aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/spgist.sgml15
-rw-r--r--src/backend/access/spgist/spgdoinsert.c9
-rw-r--r--src/backend/access/spgist/spgkdtreeproc.c1
-rw-r--r--src/backend/access/spgist/spgquadtreeproc.c4
-rw-r--r--src/backend/access/spgist/spgscan.c97
-rw-r--r--src/backend/access/spgist/spgtextproc.c13
-rw-r--r--src/backend/access/spgist/spgutils.c166
-rw-r--r--src/include/access/spgist.h4
-rw-r--r--src/include/access/spgist_private.h31
-rw-r--r--src/test/regress/expected/create_index.out118
10 files changed, 286 insertions, 172 deletions
diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml
index 70e0e9ff503..dcc3cc2d733 100644
--- a/doc/src/sgml/spgist.sgml
+++ b/doc/src/sgml/spgist.sgml
@@ -145,6 +145,7 @@ typedef struct spgConfigOut
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
+ bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
</programlisting>
@@ -159,6 +160,8 @@ typedef struct spgConfigOut
<structfield>prefixType</> can be set to <literal>VOIDOID</>.
Likewise, for operator classes that do not use node labels,
<structfield>labelType</> can be set to <literal>VOIDOID</>.
+ <structfield>canReturnData</> should be set true if the operator class
+ is capable of reconstructing the originally-supplied index value.
<structfield>longValuesOK</> should be set true only when the
<structfield>attType</> is of variable length and the operator
class is capable of segmenting long values by repeated suffixing
@@ -441,6 +444,7 @@ typedef struct spgInnerConsistentIn
Datum reconstructedValue; /* value reconstructed at parent */
int level; /* current level (counting from zero) */
+ bool returnData; /* original data must be returned? */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
@@ -467,6 +471,9 @@ typedef struct spgInnerConsistentOut
parent level.
<structfield>level</> is the current inner tuple's level, starting at
zero for the root level.
+ <structfield>returnData</> is <literal>true</> if reconstructed data is
+ required for this query; this will only be so if the
+ <function>config</> function asserted <structfield>canReturnData</>.
<structfield>allTheSame</> is true if the current inner tuple is
marked <quote>all-the-same</>; in this case all the nodes have the
same label (if any) and so either all or none of them match the query
@@ -525,12 +532,14 @@ typedef struct spgLeafConsistentIn
Datum reconstructedValue; /* value reconstructed at parent */
int level; /* current level (counting from zero) */
+ bool returnData; /* original data must be returned? */
Datum leafDatum; /* datum in leaf tuple */
} spgLeafConsistentIn;
typedef struct spgLeafConsistentOut
{
+ Datum leafValue; /* reconstructed original data, if any */
bool recheck; /* set true if operator must be rechecked */
} spgLeafConsistentOut;
</programlisting>
@@ -543,6 +552,9 @@ typedef struct spgLeafConsistentOut
parent level.
<structfield>level</> is the current leaf tuple's level, starting at
zero for the root level.
+ <structfield>returnData</> is <literal>true</> if reconstructed data is
+ required for this query; this will only be so if the
+ <function>config</> function asserted <structfield>canReturnData</>.
<structfield>leafDatum</> is the key value stored in the current
leaf tuple.
</para>
@@ -550,6 +562,9 @@ typedef struct spgLeafConsistentOut
<para>
The function must return <literal>true</> if the leaf tuple matches the
query, or <literal>false</> if not. In the <literal>true</> case,
+ if <structfield>returnData</> is <literal>true</> then
+ <structfield>leafValue</> must be set to the value originally supplied
+ to be indexed for this leaf tuple. Also,
<structfield>recheck</> may be set to <literal>true</> if the match
is uncertain and so the operator must be re-applied to the actual heap
tuple to verify the match.
diff --git a/src/backend/access/spgist/spgdoinsert.c b/src/backend/access/spgist/spgdoinsert.c
index 4bb8dfa1509..207c32776c7 100644
--- a/src/backend/access/spgist/spgdoinsert.c
+++ b/src/backend/access/spgist/spgdoinsert.c
@@ -15,6 +15,7 @@
#include "postgres.h"
+#include "access/genam.h"
#include "access/spgist_private.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@@ -678,6 +679,7 @@ doPickSplit(Relation index, SpGistState *state,
bool insertedNew = false;
spgPickSplitIn in;
spgPickSplitOut out;
+ FmgrInfo *procinfo;
bool includeNew;
int i,
max,
@@ -816,7 +818,8 @@ doPickSplit(Relation index, SpGistState *state,
*/
memset(&out, 0, sizeof(out));
- FunctionCall2Coll(&state->picksplitFn,
+ procinfo = index_getprocinfo(index, 1, SPGIST_PICKSPLIT_PROC);
+ FunctionCall2Coll(procinfo,
index->rd_indcollation[0],
PointerGetDatum(&in),
PointerGetDatum(&out));
@@ -1944,6 +1947,7 @@ spgdoinsert(Relation index, SpGistState *state,
SpGistInnerTuple innerTuple;
spgChooseIn in;
spgChooseOut out;
+ FmgrInfo *procinfo;
/*
* spgAddNode and spgSplitTuple cases will loop back to here to
@@ -1968,7 +1972,8 @@ spgdoinsert(Relation index, SpGistState *state,
memset(&out, 0, sizeof(out));
- FunctionCall2Coll(&state->chooseFn,
+ procinfo = index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC);
+ FunctionCall2Coll(procinfo,
index->rd_indcollation[0],
PointerGetDatum(&in),
PointerGetDatum(&out));
diff --git a/src/backend/access/spgist/spgkdtreeproc.c b/src/backend/access/spgist/spgkdtreeproc.c
index e11d1a35e3a..d5c1b4f454c 100644
--- a/src/backend/access/spgist/spgkdtreeproc.c
+++ b/src/backend/access/spgist/spgkdtreeproc.c
@@ -30,6 +30,7 @@ spg_kd_config(PG_FUNCTION_ARGS)
cfg->prefixType = FLOAT8OID;
cfg->labelType = VOIDOID; /* we don't need node labels */
+ cfg->canReturnData = true;
cfg->longValuesOK = false;
PG_RETURN_VOID();
}
diff --git a/src/backend/access/spgist/spgquadtreeproc.c b/src/backend/access/spgist/spgquadtreeproc.c
index 0be6e55ad30..e40d8b0e765 100644
--- a/src/backend/access/spgist/spgquadtreeproc.c
+++ b/src/backend/access/spgist/spgquadtreeproc.c
@@ -30,6 +30,7 @@ spg_quad_config(PG_FUNCTION_ARGS)
cfg->prefixType = POINTOID;
cfg->labelType = VOIDOID; /* we don't need node labels */
+ cfg->canReturnData = true;
cfg->longValuesOK = false;
PG_RETURN_VOID();
}
@@ -324,6 +325,9 @@ spg_quad_leaf_consistent(PG_FUNCTION_ARGS)
/* all tests are exact */
out->recheck = false;
+ /* leafDatum is what it is... */
+ out->leafValue = in->leafDatum;
+
switch (in->strategy)
{
case RTLeftStrategyNumber:
diff --git a/src/backend/access/spgist/spgscan.c b/src/backend/access/spgist/spgscan.c
index 748265ecba7..e158ca1aae7 100644
--- a/src/backend/access/spgist/spgscan.c
+++ b/src/backend/access/spgist/spgscan.c
@@ -55,7 +55,10 @@ freeScanStack(SpGistScanOpaque so)
so->scanStack = NIL;
}
-/* Initialize scanStack with a single entry for the root page */
+/*
+ * Initialize scanStack with a single entry for the root page, resetting
+ * any previously active scan
+ */
static void
resetSpGistScanOpaque(SpGistScanOpaque so)
{
@@ -65,7 +68,16 @@ resetSpGistScanOpaque(SpGistScanOpaque so)
freeScanStack(so);
so->scanStack = list_make1(startEntry);
- so->nPtrs = so->iPtr = 0;
+
+ if (so->want_itup)
+ {
+ /* Must pfree IndexTuples to avoid memory leak */
+ int i;
+
+ for (i = 0; i < so->nPtrs; i++)
+ pfree(so->indexTups[i]);
+ }
+ so->iPtr = so->nPtrs = 0;
}
Datum
@@ -87,6 +99,10 @@ spgbeginscan(PG_FUNCTION_ARGS)
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
resetSpGistScanOpaque(so);
+
+ /* Set up indexTupDesc and xs_itupdesc in case it's an index-only scan */
+ so->indexTupDesc = scan->xs_itupdesc = RelationGetDescr(rel);
+
scan->opaque = so;
PG_RETURN_POINTER(scan);
@@ -138,28 +154,35 @@ spgrestrpos(PG_FUNCTION_ARGS)
/*
* Test whether a leaf datum satisfies all the scan keys
*
+ * *leafValue is set to the reconstructed datum, if provided
* *recheck is set true if any of the operators are lossy
*/
static bool
-spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
+spgLeafTest(Relation index, SpGistScanOpaque so, Datum leafDatum,
int level, Datum reconstructedValue,
- bool *recheck)
+ Datum *leafValue, bool *recheck)
{
bool result = true;
spgLeafConsistentIn in;
spgLeafConsistentOut out;
+ FmgrInfo *procinfo;
MemoryContext oldCtx;
int i;
+ *leafValue = (Datum) 0;
*recheck = false;
/* set up values that are the same for all quals */
in.reconstructedValue = reconstructedValue;
in.level = level;
+ in.returnData = so->want_itup;
in.leafDatum = leafDatum;
- /* Apply each leaf consistent function, working in the temp context */
+ /* Apply each leaf consistency check, working in the temp context */
oldCtx = MemoryContextSwitchTo(so->tempCxt);
+
+ procinfo = index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC);
+
for (i = 0; i < so->numberOfKeys; i++)
{
ScanKey skey = &so->keyData[i];
@@ -174,12 +197,14 @@ spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
in.strategy = skey->sk_strategy;
in.query = skey->sk_argument;
+ out.leafValue = (Datum) 0;
out.recheck = false;
- result = DatumGetBool(FunctionCall2Coll(&so->state.leafConsistentFn,
+ result = DatumGetBool(FunctionCall2Coll(procinfo,
skey->sk_collation,
PointerGetDatum(&in),
PointerGetDatum(&out)));
+ *leafValue = out.leafValue;
*recheck |= out.recheck;
if (!result)
break;
@@ -198,7 +223,7 @@ spgLeafTest(SpGistScanOpaque so, Datum leafDatum,
*/
static void
spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
- void (*storeRes) (SpGistScanOpaque, ItemPointer, bool))
+ void (*storeRes) (SpGistScanOpaque, ItemPointer, Datum, bool))
{
Buffer buffer = InvalidBuffer;
bool reportedSome = false;
@@ -243,6 +268,7 @@ redirect:
{
SpGistLeafTuple leafTuple;
OffsetNumber max = PageGetMaxOffsetNumber(page);
+ Datum leafValue = (Datum) 0;
bool recheck = false;
if (blkno == SPGIST_HEAD_BLKNO)
@@ -260,13 +286,14 @@ redirect:
}
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
- if (spgLeafTest(so,
+ if (spgLeafTest(index, so,
SGLTDATUM(leafTuple, &so->state),
stackEntry->level,
stackEntry->reconstructedValue,
+ &leafValue,
&recheck))
{
- storeRes(so, &leafTuple->heapPtr, recheck);
+ storeRes(so, &leafTuple->heapPtr, leafValue, recheck);
reportedSome = true;
}
}
@@ -304,13 +331,14 @@ redirect:
}
Assert(ItemPointerIsValid(&leafTuple->heapPtr));
- if (spgLeafTest(so,
+ if (spgLeafTest(index, so,
SGLTDATUM(leafTuple, &so->state),
stackEntry->level,
stackEntry->reconstructedValue,
+ &leafValue,
&recheck))
{
- storeRes(so, &leafTuple->heapPtr, recheck);
+ storeRes(so, &leafTuple->heapPtr, leafValue, recheck);
reportedSome = true;
}
@@ -374,6 +402,7 @@ redirect:
{
spgInnerConsistentIn in;
spgInnerConsistentOut out;
+ FmgrInfo *procinfo;
SpGistNodeTuple *nodes;
int *andMap;
int *levelAdds;
@@ -388,6 +417,7 @@ redirect:
/* set up values that are the same for all scankeys */
in.reconstructedValue = stackEntry->reconstructedValue;
in.level = stackEntry->level;
+ in.returnData = so->want_itup;
in.allTheSame = innerTuple->allTheSame;
in.hasPrefix = (innerTuple->prefixSize > 0);
in.prefixDatum = SGITDATUM(innerTuple, &so->state);
@@ -405,6 +435,8 @@ redirect:
levelAdds = (int *) palloc0(sizeof(int) * in.nNodes);
reconstructedValues = (Datum *) palloc0(sizeof(Datum) * in.nNodes);
+ procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
+
for (j = 0; j < so->numberOfKeys; j++)
{
ScanKey skey = &so->keyData[j];
@@ -421,7 +453,7 @@ redirect:
memset(&out, 0, sizeof(out));
- FunctionCall2Coll(&so->state.innerConsistentFn,
+ FunctionCall2Coll(procinfo,
skey->sk_collation,
PointerGetDatum(&in),
PointerGetDatum(&out));
@@ -490,7 +522,8 @@ redirect:
/* storeRes subroutine for getbitmap case */
static void
-storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr, bool recheck)
+storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
+ Datum leafValue, bool recheck)
{
tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
so->ntids++;
@@ -506,6 +539,8 @@ spggetbitmap(PG_FUNCTION_ARGS)
/* Copy scankey to *so so we don't need to pass it around separately */
so->numberOfKeys = scan->numberOfKeys;
so->keyData = scan->keyData;
+ /* Ditto for the want_itup flag */
+ so->want_itup = false;
so->tbm = tbm;
so->ntids = 0;
@@ -517,11 +552,24 @@ spggetbitmap(PG_FUNCTION_ARGS)
/* storeRes subroutine for gettuple case */
static void
-storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr, bool recheck)
+storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
+ Datum leafValue, bool recheck)
{
Assert(so->nPtrs < MaxIndexTuplesPerPage);
so->heapPtrs[so->nPtrs] = *heapPtr;
so->recheck[so->nPtrs] = recheck;
+ if (so->want_itup)
+ {
+ /*
+ * Reconstruct desired IndexTuple. We have to copy the datum out of
+ * the temp context anyway, so we may as well create the tuple here.
+ */
+ bool isnull = false;
+
+ so->indexTups[so->nPtrs] = index_form_tuple(so->indexTupDesc,
+ &leafValue,
+ &isnull);
+ }
so->nPtrs++;
}
@@ -538,6 +586,8 @@ spggettuple(PG_FUNCTION_ARGS)
/* Copy scankey to *so so we don't need to pass it around separately */
so->numberOfKeys = scan->numberOfKeys;
so->keyData = scan->keyData;
+ /* Ditto for the want_itup flag */
+ so->want_itup = scan->xs_want_itup;
for (;;)
{
@@ -546,11 +596,21 @@ spggettuple(PG_FUNCTION_ARGS)
/* continuing to return tuples from a leaf page */
scan->xs_ctup.t_self = so->heapPtrs[so->iPtr];
scan->xs_recheck = so->recheck[so->iPtr];
+ scan->xs_itup = so->indexTups[so->iPtr];
so->iPtr++;
PG_RETURN_BOOL(true);
}
+ if (so->want_itup)
+ {
+ /* Must pfree IndexTuples to avoid memory leak */
+ int i;
+
+ for (i = 0; i < so->nPtrs; i++)
+ pfree(so->indexTups[i]);
+ }
so->iPtr = so->nPtrs = 0;
+
spgWalk(scan->indexRelation, so, false, storeGettuple);
if (so->nPtrs == 0)
@@ -563,6 +623,11 @@ spggettuple(PG_FUNCTION_ARGS)
Datum
spgcanreturn(PG_FUNCTION_ARGS)
{
- /* Not implemented yet */
- PG_RETURN_BOOL(false);
+ Relation index = (Relation) PG_GETARG_POINTER(0);
+ SpGistCache *cache;
+
+ /* We can do it if the opclass config function says so */
+ cache = spgGetCache(index);
+
+ PG_RETURN_BOOL(cache->config.canReturnData);
}
diff --git a/src/backend/access/spgist/spgtextproc.c b/src/backend/access/spgist/spgtextproc.c
index b6037978425..ab37d482636 100644
--- a/src/backend/access/spgist/spgtextproc.c
+++ b/src/backend/access/spgist/spgtextproc.c
@@ -51,6 +51,7 @@ spg_text_config(PG_FUNCTION_ARGS)
cfg->prefixType = TEXTOID;
cfg->labelType = CHAROID;
+ cfg->canReturnData = true;
cfg->longValuesOK = true; /* suffixing will shorten long values */
PG_RETURN_VOID();
}
@@ -521,7 +522,10 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
queryLen = VARSIZE_ANY_EXHDR(query);
- /* For equality, we needn't reconstruct fullValue if not same length */
+ /*
+ * For an equality check, we needn't reconstruct fullValue if not same
+ * length; it can't match
+ */
if (strategy == BTEqualStrategyNumber && queryLen != fullLen)
PG_RETURN_BOOL(false);
@@ -529,15 +533,20 @@ spg_text_leaf_consistent(PG_FUNCTION_ARGS)
if (VARSIZE_ANY_EXHDR(leafValue) == 0 && level > 0)
{
fullValue = VARDATA(reconstrValue);
+ out->leafValue = PointerGetDatum(reconstrValue);
}
else
{
- fullValue = palloc(fullLen);
+ text *fullText = palloc(VARHDRSZ + fullLen);
+
+ SET_VARSIZE(fullText, VARHDRSZ + fullLen);
+ fullValue = VARDATA(fullText);
if (level)
memcpy(fullValue, VARDATA(reconstrValue), level);
if (VARSIZE_ANY_EXHDR(leafValue) > 0)
memcpy(fullValue + level, VARDATA_ANY(leafValue),
VARSIZE_ANY_EXHDR(leafValue));
+ out->leafValue = PointerGetDatum(fullText);
}
/* Run the appropriate type of comparison */
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index c6bf07a9424..aced35c8441 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -34,51 +34,88 @@ fillTypeDesc(SpGistTypeDesc *desc, Oid type)
get_typlenbyval(type, &desc->attlen, &desc->attbyval);
}
+/*
+ * Fetch local cache of AM-specific info about the index, initializing it
+ * if necessary
+ */
+SpGistCache *
+spgGetCache(Relation index)
+{
+ SpGistCache *cache;
+
+ if (index->rd_amcache == NULL)
+ {
+ Oid atttype;
+ spgConfigIn in;
+ FmgrInfo *procinfo;
+ Buffer metabuffer;
+ SpGistMetaPageData *metadata;
+
+ cache = MemoryContextAllocZero(index->rd_indexcxt,
+ sizeof(SpGistCache));
+
+ /* SPGiST doesn't support multi-column indexes */
+ Assert(index->rd_att->natts == 1);
+
+ /*
+ * Get the actual data type of the indexed column from the index
+ * tupdesc. We pass this to the opclass config function so that
+ * polymorphic opclasses are possible.
+ */
+ atttype = index->rd_att->attrs[0]->atttypid;
+
+ /* Call the config function to get config info for the opclass */
+ in.attType = atttype;
+
+ procinfo = index_getprocinfo(index, 1, SPGIST_CONFIG_PROC);
+ FunctionCall2Coll(procinfo,
+ index->rd_indcollation[0],
+ PointerGetDatum(&in),
+ PointerGetDatum(&cache->config));
+
+ /* Get the information we need about each relevant datatype */
+ fillTypeDesc(&cache->attType, atttype);
+ fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
+ fillTypeDesc(&cache->attLabelType, cache->config.labelType);
+
+ /* Last, get the lastUsedPages data from the metapage */
+ metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
+ LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
+
+ metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
+
+ if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
+ elog(ERROR, "index \"%s\" is not an SP-GiST index",
+ RelationGetRelationName(index));
+
+ cache->lastUsedPages = metadata->lastUsedPages;
+
+ UnlockReleaseBuffer(metabuffer);
+
+ index->rd_amcache = (void *) cache;
+ }
+ else
+ {
+ /* assume it's up to date */
+ cache = (SpGistCache *) index->rd_amcache;
+ }
+
+ return cache;
+}
+
/* Initialize SpGistState for working with the given index */
void
initSpGistState(SpGistState *state, Relation index)
{
- Oid atttype;
- spgConfigIn in;
+ SpGistCache *cache;
- /* SPGiST doesn't support multi-column indexes */
- Assert(index->rd_att->natts == 1);
+ /* Get cached static information about index */
+ cache = spgGetCache(index);
- /*
- * Get the actual data type of the indexed column from the index tupdesc.
- * We pass this to the opclass config function so that polymorphic
- * opclasses are possible.
- */
- atttype = index->rd_att->attrs[0]->atttypid;
-
- /* Get the config info for the opclass */
- in.attType = atttype;
-
- memset(&state->config, 0, sizeof(state->config));
-
- FunctionCall2Coll(index_getprocinfo(index, 1, SPGIST_CONFIG_PROC),
- index->rd_indcollation[0],
- PointerGetDatum(&in),
- PointerGetDatum(&state->config));
-
- /* Get the information we need about each relevant datatype */
- fillTypeDesc(&state->attType, atttype);
- fillTypeDesc(&state->attPrefixType, state->config.prefixType);
- fillTypeDesc(&state->attLabelType, state->config.labelType);
-
- /* Get lookup info for opclass support procs */
- fmgr_info_copy(&(state->chooseFn),
- index_getprocinfo(index, 1, SPGIST_CHOOSE_PROC),
- CurrentMemoryContext);
- fmgr_info_copy(&(state->picksplitFn),
- index_getprocinfo(index, 1, SPGIST_PICKSPLIT_PROC),
- CurrentMemoryContext);
- fmgr_info_copy(&(state->innerConsistentFn),
- index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC),
- CurrentMemoryContext);
- fmgr_info_copy(&(state->leafConsistentFn),
- index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC),
- CurrentMemoryContext);
+ state->config = cache->config;
+ state->attType = cache->attType;
+ state->attPrefixType = cache->attPrefixType;
+ state->attLabelType = cache->attLabelType;
/* Make workspace for constructing dead tuples */
state->deadTupleStorage = palloc0(SGDTSIZE);
@@ -86,6 +123,7 @@ initSpGistState(SpGistState *state, Relation index)
/* Set XID to use in redirection tuples */
state->myXid = GetTopTransactionIdIfAny();
+ /* Assume we're not in an index build (spgbuild will override) */
state->isBuild = false;
}
@@ -154,46 +192,6 @@ SpGistNewBuffer(Relation index)
}
/*
- * Fetch local cache of lastUsedPages info, initializing it from the metapage
- * if necessary
- */
-static SpGistCache *
-spgGetCache(Relation index)
-{
- SpGistCache *cache;
-
- if (index->rd_amcache == NULL)
- {
- Buffer metabuffer;
- SpGistMetaPageData *metadata;
-
- cache = MemoryContextAlloc(index->rd_indexcxt,
- sizeof(SpGistCache));
-
- metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
- LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
-
- metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
-
- if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
- elog(ERROR, "index \"%s\" is not an SP-GiST index",
- RelationGetRelationName(index));
-
- *cache = metadata->lastUsedPages;
-
- UnlockReleaseBuffer(metabuffer);
-
- index->rd_amcache = cache;
- }
- else
- {
- cache = (SpGistCache *) index->rd_amcache;
- }
-
- return cache;
-}
-
-/*
* Update index metapage's lastUsedPages info from local cache, if possible
*
* Updating meta page isn't critical for index working, so
@@ -215,7 +213,7 @@ SpGistUpdateMetaPage(Relation index)
if (ConditionalLockBuffer(metabuffer))
{
metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
- metadata->lastUsedPages = *cache;
+ metadata->lastUsedPages = cache->lastUsedPages;
MarkBufferDirty(metabuffer);
UnlockReleaseBuffer(metabuffer);
@@ -229,8 +227,8 @@ SpGistUpdateMetaPage(Relation index)
/* Macro to select proper element of lastUsedPages cache depending on flags */
#define GET_LUP(c, f) (((f) & GBUF_LEAF) ? \
- &(c)->leafPage : \
- &(c)->innerPage[(f) & GBUF_PARITY_MASK])
+ &(c)->lastUsedPages.leafPage : \
+ &(c)->lastUsedPages.innerPage[(f) & GBUF_PARITY_MASK])
/*
* Allocate and initialize a new buffer of the type and parity specified by
@@ -282,8 +280,8 @@ allocNewBuffer(Relation index, int flags)
else
{
/* Page has wrong parity, record it in cache and try again */
- cache->innerPage[blkParity].blkno = blkno;
- cache->innerPage[blkParity].freeSpace =
+ cache->lastUsedPages.innerPage[blkParity].blkno = blkno;
+ cache->lastUsedPages.innerPage[blkParity].freeSpace =
PageGetExactFreeSpace(BufferGetPage(buffer));
UnlockReleaseBuffer(buffer);
}
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index df6fec6cf45..d2f0a72c36b 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -43,6 +43,7 @@ typedef struct spgConfigOut
{
Oid prefixType; /* Data type of inner-tuple prefixes */
Oid labelType; /* Data type of inner-tuple node labels */
+ bool canReturnData; /* Opclass can reconstruct original data */
bool longValuesOK; /* Opclass can cope with values > 1 page */
} spgConfigOut;
@@ -132,6 +133,7 @@ typedef struct spgInnerConsistentIn
Datum reconstructedValue; /* value reconstructed at parent */
int level; /* current level (counting from zero) */
+ bool returnData; /* original data must be returned? */
/* Data from current inner tuple */
bool allTheSame; /* tuple is marked all-the-same? */
@@ -159,12 +161,14 @@ typedef struct spgLeafConsistentIn
Datum reconstructedValue; /* value reconstructed at parent */
int level; /* current level (counting from zero) */
+ bool returnData; /* original data must be returned? */
Datum leafDatum; /* datum in leaf tuple */
} spgLeafConsistentIn;
typedef struct spgLeafConsistentOut
{
+ Datum leafValue; /* reconstructed original data, if any */
bool recheck; /* set true if operator must be rechecked */
} spgLeafConsistentOut;
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 5c57799f09c..ec6d2d07fc7 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -71,11 +71,11 @@ typedef struct SpGistLastUsedPage
int freeSpace; /* its free space (could be obsolete!) */
} SpGistLastUsedPage;
-typedef struct SpGistCache
+typedef struct SpGistLUPCache
{
SpGistLastUsedPage innerPage[3]; /* one per triple-parity group */
SpGistLastUsedPage leafPage;
-} SpGistCache;
+} SpGistLUPCache;
/*
* metapage
@@ -83,7 +83,7 @@ typedef struct SpGistCache
typedef struct SpGistMetaPageData
{
uint32 magicNumber; /* for identity cross-check */
- SpGistCache lastUsedPages; /* shared storage of last-used info */
+ SpGistLUPCache lastUsedPages; /* shared storage of last-used info */
} SpGistMetaPageData;
#define SPGIST_MAGIC_NUMBER (0xBA0BABED)
@@ -112,12 +112,6 @@ typedef struct SpGistState
SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
SpGistTypeDesc attLabelType; /* type of node label values */
- /* lookup data for the opclass support functions, except config */
- FmgrInfo chooseFn;
- FmgrInfo picksplitFn;
- FmgrInfo innerConsistentFn;
- FmgrInfo leafConsistentFn;
-
char *deadTupleStorage; /* workspace for spgFormDeadTuple */
TransactionId myXid; /* XID to use when creating a redirect tuple */
@@ -144,10 +138,13 @@ typedef struct SpGistScanOpaqueData
int64 ntids; /* number of TIDs passed to bitmap */
/* These fields are only used in amgettuple scans: */
+ bool want_itup; /* are we reconstructing tuples? */
+ TupleDesc indexTupDesc; /* if so, tuple descriptor for them */
int nPtrs; /* number of TIDs found on current page */
int iPtr; /* index for scanning through same */
ItemPointerData heapPtrs[MaxIndexTuplesPerPage]; /* TIDs from cur page */
bool recheck[MaxIndexTuplesPerPage]; /* their recheck flags */
+ IndexTuple indexTups[MaxIndexTuplesPerPage]; /* reconstructed tuples */
/*
* Note: using MaxIndexTuplesPerPage above is a bit hokey since
@@ -158,6 +155,21 @@ typedef struct SpGistScanOpaqueData
typedef SpGistScanOpaqueData *SpGistScanOpaque;
+/*
+ * This struct is what we actually keep in index->rd_amcache. It includes
+ * static configuration information as well as the lastUsedPages cache.
+ */
+typedef struct SpGistCache
+{
+ spgConfigOut config; /* filled in by opclass config method */
+
+ SpGistTypeDesc attType; /* type of input data and leaf values */
+ SpGistTypeDesc attPrefixType; /* type of inner-tuple prefix values */
+ SpGistTypeDesc attLabelType; /* type of node label values */
+
+ SpGistLUPCache lastUsedPages; /* local storage of last-used info */
+} SpGistCache;
+
/*
* SPGiST tuple types. Note: inner, leaf, and dead tuple structs
@@ -570,6 +582,7 @@ typedef struct spgxlogVacuumRedirect
#define GBUF_INNER_PARITY(x) ((x) % 3)
/* spgutils.c */
+extern SpGistCache *spgGetCache(Relation index);
extern void initSpGistState(SpGistState *state, Relation index);
extern Buffer SpGistNewBuffer(Relation index);
extern void SpGistUpdateMetaPage(Relation index);
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 86cee2de942..36198b8edd5 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -680,10 +680,10 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(3 rows)
@@ -695,11 +695,11 @@ SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
- QUERY PLAN
----------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
- Index Cond: ('(1000,1000),(200,200)'::box @> p)
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
+ Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(3 rows)
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
@@ -710,10 +710,10 @@ SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)';
- QUERY PLAN
-------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p << '(5000,4000)'::point)
(3 rows)
@@ -725,10 +725,10 @@ SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)';
- QUERY PLAN
-------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p >> '(5000,4000)'::point)
(3 rows)
@@ -740,10 +740,10 @@ SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)';
- QUERY PLAN
-------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p <^ '(5000,4000)'::point)
(3 rows)
@@ -755,10 +755,10 @@ SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)';
- QUERY PLAN
-------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p >^ '(5000,4000)'::point)
(3 rows)
@@ -770,10 +770,10 @@ SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)';
- QUERY PLAN
-------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------
Aggregate
- -> Index Scan using sp_quad_ind on quad_point_tbl
+ -> Index Only Scan using sp_quad_ind on quad_point_tbl
Index Cond: (p ~= '(4585,365)'::point)
(3 rows)
@@ -788,7 +788,7 @@ SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)';
QUERY PLAN
---------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(3 rows)
@@ -803,8 +803,8 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
QUERY PLAN
---------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
- Index Cond: ('(1000,1000),(200,200)'::box @> p)
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
+ Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(3 rows)
SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
@@ -815,10 +815,10 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
EXPLAIN (COSTS OFF)
SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)';
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p << '(5000,4000)'::point)
(3 rows)
@@ -830,10 +830,10 @@ SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)';
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p >> '(5000,4000)'::point)
(3 rows)
@@ -845,10 +845,10 @@ SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)';
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p <^ '(5000,4000)'::point)
(3 rows)
@@ -860,10 +860,10 @@ SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)';
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p >^ '(5000,4000)'::point)
(3 rows)
@@ -875,10 +875,10 @@ SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)';
- QUERY PLAN
---------------------------------------------------
+ QUERY PLAN
+-------------------------------------------------------
Aggregate
- -> Index Scan using sp_kd_ind on kd_point_tbl
+ -> Index Only Scan using sp_kd_ind on kd_point_tbl
Index Cond: (p ~= '(4585,365)'::point)
(3 rows)
@@ -890,10 +890,10 @@ SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdef';
- QUERY PLAN
--------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t = '0123456789abcdef'::text)
(3 rows)
@@ -905,10 +905,10 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdef';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcde';
- QUERY PLAN
--------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t = '0123456789abcde'::text)
(3 rows)
@@ -920,10 +920,10 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcde';
EXPLAIN (COSTS OFF)
SELECT count(*) FROM suffix_text_tbl WHERE t = '0123456789abcdefF';
- QUERY PLAN
--------------------------------------------------------
+ QUERY PLAN
+------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t = '0123456789abcdefF'::text)
(3 rows)
@@ -938,7 +938,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t < 'Aztec
QUERY PLAN
----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t < 'Aztec Ct '::text)
(3 rows)
@@ -953,7 +953,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~<~ 'Aztec
QUERY PLAN
------------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t ~<~ 'Aztec Ct '::text)
(3 rows)
@@ -968,7 +968,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t <= 'Aztec
QUERY PLAN
-----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t <= 'Aztec Ct '::text)
(3 rows)
@@ -983,7 +983,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~<=~ 'Aztec
QUERY PLAN
-------------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t ~<=~ 'Aztec Ct '::text)
(3 rows)
@@ -998,7 +998,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = 'Aztec
QUERY PLAN
----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t = 'Aztec Ct '::text)
(3 rows)
@@ -1013,7 +1013,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t = 'Worth
QUERY PLAN
----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t = 'Worth St '::text)
(3 rows)
@@ -1028,7 +1028,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t >= 'Worth
QUERY PLAN
-----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t >= 'Worth St '::text)
(3 rows)
@@ -1043,7 +1043,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~>=~ 'Worth
QUERY PLAN
-------------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t ~>=~ 'Worth St '::text)
(3 rows)
@@ -1058,7 +1058,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t > 'Worth
QUERY PLAN
----------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t > 'Worth St '::text)
(3 rows)
@@ -1073,7 +1073,7 @@ SELECT count(*) FROM suffix_text_tbl WHERE t ~>~ 'Worth
QUERY PLAN
------------------------------------------------------------------------
Aggregate
- -> Index Scan using sp_suff_ind on suffix_text_tbl
+ -> Index Only Scan using sp_suff_ind on suffix_text_tbl
Index Cond: (t ~>~ 'Worth St '::text)
(3 rows)