diff --git a/doc/src/sgml/spgist.sgml b/doc/src/sgml/spgist.sgml index 70e0e9ff503dedefd9c28c0ade7f09a77fea825f..dcc3cc2d7331247fe3b3cb998188ed08083ff98a 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 4bb8dfa150907243370fafad92f76b20b10c90ef..207c32776c73ca2f78746c0861af3e37d3351842 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 e11d1a35e3af58fadc67cc7ae803ead98d3b3934..d5c1b4f454c375f57237c87838a0c98aef44eb2f 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 0be6e55ad305d82d61ff03e362b1eb983c4bbcde..e40d8b0e76577eaf78793fc73188cc78e757315a 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 748265ecba7dbae82ed6e933b52f222d93daae6b..e158ca1aae7e1aa909f04c229673b0d9430c8fb8 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 b60379784256a5c732a034498f14053b0bf437c5..ab37d482636f023318b85f5f3fc02dbbadf43de0 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 c6bf07a9424e11ffedfab504128fc8f6e75875d0..aced35c84414f56c52184a45f69ad46ab6c010ce 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; } @@ -153,46 +191,6 @@ SpGistNewBuffer(Relation index) return buffer; } -/* - * 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 * @@ -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 df6fec6cf455939421e929542bc9840bbb7f50d5..d2f0a72c36bc005fe7dab7d6fa8fead7e954d49b 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 5c57799f09c12b9c9afa380b466a7d19f42abe95..ec6d2d07fc7664e89e2ba75e548966e7b65dcede 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 86cee2de94269be38afce4fa56794feb6f7d259a..36198b8edd56a417c176a1da45ee416e79ee9088 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)