LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spgscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 230 261 88.1 %
Date: 2017-09-29 15:12:54 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * spgscan.c
       4             :  *    routines for scanning SP-GiST indexes
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *          src/backend/access/spgist/spgscan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/relscan.h"
      19             : #include "access/spgist_private.h"
      20             : #include "miscadmin.h"
      21             : #include "storage/bufmgr.h"
      22             : #include "utils/datum.h"
      23             : #include "utils/memutils.h"
      24             : #include "utils/rel.h"
      25             : 
      26             : 
      27             : typedef void (*storeRes_func) (SpGistScanOpaque so, ItemPointer heapPtr,
      28             :                                Datum leafValue, bool isnull, bool recheck);
      29             : 
      30             : typedef struct ScanStackEntry
      31             : {
      32             :     Datum       reconstructedValue; /* value reconstructed from parent */
      33             :     void       *traversalValue; /* opclass-specific traverse value */
      34             :     int         level;          /* level of items on this page */
      35             :     ItemPointerData ptr;        /* block and offset to scan from */
      36             : } ScanStackEntry;
      37             : 
      38             : 
      39             : /* Free a ScanStackEntry */
      40             : static void
      41        9944 : freeScanStackEntry(SpGistScanOpaque so, ScanStackEntry *stackEntry)
      42             : {
      43       19888 :     if (!so->state.attType.attbyval &&
      44        9944 :         DatumGetPointer(stackEntry->reconstructedValue) != NULL)
      45        2400 :         pfree(DatumGetPointer(stackEntry->reconstructedValue));
      46        9944 :     if (stackEntry->traversalValue)
      47        2118 :         pfree(stackEntry->traversalValue);
      48             : 
      49        9944 :     pfree(stackEntry);
      50        9944 : }
      51             : 
      52             : /* Free the entire stack */
      53             : static void
      54         120 : freeScanStack(SpGistScanOpaque so)
      55             : {
      56             :     ListCell   *lc;
      57             : 
      58         120 :     foreach(lc, so->scanStack)
      59             :     {
      60           0 :         freeScanStackEntry(so, (ScanStackEntry *) lfirst(lc));
      61             :     }
      62         120 :     list_free(so->scanStack);
      63         120 :     so->scanStack = NIL;
      64         120 : }
      65             : 
      66             : /*
      67             :  * Initialize scanStack to search the root page, resetting
      68             :  * any previously active scan
      69             :  */
      70             : static void
      71         120 : resetSpGistScanOpaque(SpGistScanOpaque so)
      72             : {
      73             :     ScanStackEntry *startEntry;
      74             : 
      75         120 :     freeScanStack(so);
      76             : 
      77         120 :     if (so->searchNulls)
      78             :     {
      79             :         /* Stack a work item to scan the null index entries */
      80           4 :         startEntry = (ScanStackEntry *) palloc0(sizeof(ScanStackEntry));
      81           4 :         ItemPointerSet(&startEntry->ptr, SPGIST_NULL_BLKNO, FirstOffsetNumber);
      82           4 :         so->scanStack = lappend(so->scanStack, startEntry);
      83             :     }
      84             : 
      85         120 :     if (so->searchNonNulls)
      86             :     {
      87             :         /* Stack a work item to scan the non-null index entries */
      88         118 :         startEntry = (ScanStackEntry *) palloc0(sizeof(ScanStackEntry));
      89         118 :         ItemPointerSet(&startEntry->ptr, SPGIST_ROOT_BLKNO, FirstOffsetNumber);
      90         118 :         so->scanStack = lappend(so->scanStack, startEntry);
      91             :     }
      92             : 
      93         120 :     if (so->want_itup)
      94             :     {
      95             :         /* Must pfree reconstructed tuples to avoid memory leak */
      96             :         int         i;
      97             : 
      98           0 :         for (i = 0; i < so->nPtrs; i++)
      99           0 :             pfree(so->reconTups[i]);
     100             :     }
     101         120 :     so->iPtr = so->nPtrs = 0;
     102         120 : }
     103             : 
     104             : /*
     105             :  * Prepare scan keys in SpGistScanOpaque from caller-given scan keys
     106             :  *
     107             :  * Sets searchNulls, searchNonNulls, numberOfKeys, keyData fields of *so.
     108             :  *
     109             :  * The point here is to eliminate null-related considerations from what the
     110             :  * opclass consistent functions need to deal with.  We assume all SPGiST-
     111             :  * indexable operators are strict, so any null RHS value makes the scan
     112             :  * condition unsatisfiable.  We also pull out any IS NULL/IS NOT NULL
     113             :  * conditions; their effect is reflected into searchNulls/searchNonNulls.
     114             :  */
     115             : static void
     116         120 : spgPrepareScanKeys(IndexScanDesc scan)
     117             : {
     118         120 :     SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     119             :     bool        qual_ok;
     120             :     bool        haveIsNull;
     121             :     bool        haveNotNull;
     122             :     int         nkeys;
     123             :     int         i;
     124             : 
     125         120 :     if (scan->numberOfKeys <= 0)
     126             :     {
     127             :         /* If no quals, whole-index scan is required */
     128           2 :         so->searchNulls = true;
     129           2 :         so->searchNonNulls = true;
     130           2 :         so->numberOfKeys = 0;
     131         122 :         return;
     132             :     }
     133             : 
     134             :     /* Examine the given quals */
     135         118 :     qual_ok = true;
     136         118 :     haveIsNull = haveNotNull = false;
     137         118 :     nkeys = 0;
     138         236 :     for (i = 0; i < scan->numberOfKeys; i++)
     139             :     {
     140         118 :         ScanKey     skey = &scan->keyData[i];
     141             : 
     142         118 :         if (skey->sk_flags & SK_SEARCHNULL)
     143           2 :             haveIsNull = true;
     144         116 :         else if (skey->sk_flags & SK_SEARCHNOTNULL)
     145           2 :             haveNotNull = true;
     146         114 :         else if (skey->sk_flags & SK_ISNULL)
     147             :         {
     148             :             /* ordinary qual with null argument - unsatisfiable */
     149           0 :             qual_ok = false;
     150           0 :             break;
     151             :         }
     152             :         else
     153             :         {
     154             :             /* ordinary qual, propagate into so->keyData */
     155         114 :             so->keyData[nkeys++] = *skey;
     156             :             /* this effectively creates a not-null requirement */
     157         114 :             haveNotNull = true;
     158             :         }
     159             :     }
     160             : 
     161             :     /* IS NULL in combination with something else is unsatisfiable */
     162         118 :     if (haveIsNull && haveNotNull)
     163           0 :         qual_ok = false;
     164             : 
     165             :     /* Emit results */
     166         118 :     if (qual_ok)
     167             :     {
     168         118 :         so->searchNulls = haveIsNull;
     169         118 :         so->searchNonNulls = haveNotNull;
     170         118 :         so->numberOfKeys = nkeys;
     171             :     }
     172             :     else
     173             :     {
     174           0 :         so->searchNulls = false;
     175           0 :         so->searchNonNulls = false;
     176           0 :         so->numberOfKeys = 0;
     177             :     }
     178             : }
     179             : 
     180             : IndexScanDesc
     181         120 : spgbeginscan(Relation rel, int keysz, int orderbysz)
     182             : {
     183             :     IndexScanDesc scan;
     184             :     SpGistScanOpaque so;
     185             : 
     186         120 :     scan = RelationGetIndexScan(rel, keysz, 0);
     187             : 
     188         120 :     so = (SpGistScanOpaque) palloc0(sizeof(SpGistScanOpaqueData));
     189         120 :     if (keysz > 0)
     190         118 :         so->keyData = (ScanKey) palloc(sizeof(ScanKeyData) * keysz);
     191             :     else
     192           2 :         so->keyData = NULL;
     193         120 :     initSpGistState(&so->state, scan->indexRelation);
     194         120 :     so->tempCxt = AllocSetContextCreate(CurrentMemoryContext,
     195             :                                         "SP-GiST search temporary context",
     196             :                                         ALLOCSET_DEFAULT_SIZES);
     197             : 
     198             :     /* Set up indexTupDesc and xs_hitupdesc in case it's an index-only scan */
     199         120 :     so->indexTupDesc = scan->xs_hitupdesc = RelationGetDescr(rel);
     200             : 
     201         120 :     scan->opaque = so;
     202             : 
     203         120 :     return scan;
     204             : }
     205             : 
     206             : void
     207         120 : spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
     208             :           ScanKey orderbys, int norderbys)
     209             : {
     210         120 :     SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     211             : 
     212             :     /* copy scankeys into local storage */
     213         120 :     if (scankey && scan->numberOfKeys > 0)
     214             :     {
     215         118 :         memmove(scan->keyData, scankey,
     216         118 :                 scan->numberOfKeys * sizeof(ScanKeyData));
     217             :     }
     218             : 
     219             :     /* preprocess scankeys, set up the representation in *so */
     220         120 :     spgPrepareScanKeys(scan);
     221             : 
     222             :     /* set up starting stack entries */
     223         120 :     resetSpGistScanOpaque(so);
     224         120 : }
     225             : 
     226             : void
     227         120 : spgendscan(IndexScanDesc scan)
     228             : {
     229         120 :     SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     230             : 
     231         120 :     MemoryContextDelete(so->tempCxt);
     232         120 : }
     233             : 
     234             : /*
     235             :  * Test whether a leaf tuple satisfies all the scan keys
     236             :  *
     237             :  * *leafValue is set to the reconstructed datum, if provided
     238             :  * *recheck is set true if any of the operators are lossy
     239             :  */
     240             : static bool
     241      313749 : spgLeafTest(Relation index, SpGistScanOpaque so,
     242             :             SpGistLeafTuple leafTuple, bool isnull,
     243             :             int level, Datum reconstructedValue,
     244             :             void *traversalValue,
     245             :             Datum *leafValue, bool *recheck)
     246             : {
     247             :     bool        result;
     248             :     Datum       leafDatum;
     249             :     spgLeafConsistentIn in;
     250             :     spgLeafConsistentOut out;
     251             :     FmgrInfo   *procinfo;
     252             :     MemoryContext oldCtx;
     253             : 
     254      313749 :     if (isnull)
     255             :     {
     256             :         /* Should not have arrived on a nulls page unless nulls are wanted */
     257          12 :         Assert(so->searchNulls);
     258          12 :         *leafValue = (Datum) 0;
     259          12 :         *recheck = false;
     260          12 :         return true;
     261             :     }
     262             : 
     263      313737 :     leafDatum = SGLTDATUM(leafTuple, &so->state);
     264             : 
     265             :     /* use temp context for calling leaf_consistent */
     266      313737 :     oldCtx = MemoryContextSwitchTo(so->tempCxt);
     267             : 
     268      313737 :     in.scankeys = so->keyData;
     269      313737 :     in.nkeys = so->numberOfKeys;
     270      313737 :     in.reconstructedValue = reconstructedValue;
     271      313737 :     in.traversalValue = traversalValue;
     272      313737 :     in.level = level;
     273      313737 :     in.returnData = so->want_itup;
     274      313737 :     in.leafDatum = leafDatum;
     275             : 
     276      313737 :     out.leafValue = (Datum) 0;
     277      313737 :     out.recheck = false;
     278             : 
     279      313737 :     procinfo = index_getprocinfo(index, 1, SPGIST_LEAF_CONSISTENT_PROC);
     280      313737 :     result = DatumGetBool(FunctionCall2Coll(procinfo,
     281             :                                             index->rd_indcollation[0],
     282             :                                             PointerGetDatum(&in),
     283             :                                             PointerGetDatum(&out)));
     284             : 
     285      313737 :     *leafValue = out.leafValue;
     286      313737 :     *recheck = out.recheck;
     287             : 
     288      313737 :     MemoryContextSwitchTo(oldCtx);
     289             : 
     290      313737 :     return result;
     291             : }
     292             : 
     293             : /*
     294             :  * Walk the tree and report all tuples passing the scan quals to the storeRes
     295             :  * subroutine.
     296             :  *
     297             :  * If scanWholeIndex is true, we'll do just that.  If not, we'll stop at the
     298             :  * next page boundary once we have reported at least one tuple.
     299             :  */
     300             : static void
     301        2161 : spgWalk(Relation index, SpGistScanOpaque so, bool scanWholeIndex,
     302             :         storeRes_func storeRes, Snapshot snapshot)
     303             : {
     304        2161 :     Buffer      buffer = InvalidBuffer;
     305        2161 :     bool        reportedSome = false;
     306             : 
     307       14266 :     while (scanWholeIndex || !reportedSome)
     308             :     {
     309             :         ScanStackEntry *stackEntry;
     310             :         BlockNumber blkno;
     311             :         OffsetNumber offset;
     312             :         Page        page;
     313             :         bool        isnull;
     314             : 
     315             :         /* Pull next to-do item from the list */
     316       10064 :         if (so->scanStack == NIL)
     317         120 :             break;              /* there are no more pages to scan */
     318             : 
     319        9944 :         stackEntry = (ScanStackEntry *) linitial(so->scanStack);
     320        9944 :         so->scanStack = list_delete_first(so->scanStack);
     321             : 
     322             : redirect:
     323             :         /* Check for interrupts, just in case of infinite loop */
     324        9944 :         CHECK_FOR_INTERRUPTS();
     325             : 
     326        9944 :         blkno = ItemPointerGetBlockNumber(&stackEntry->ptr);
     327        9944 :         offset = ItemPointerGetOffsetNumber(&stackEntry->ptr);
     328             : 
     329        9944 :         if (buffer == InvalidBuffer)
     330             :         {
     331        2093 :             buffer = ReadBuffer(index, blkno);
     332        2093 :             LockBuffer(buffer, BUFFER_LOCK_SHARE);
     333             :         }
     334        7851 :         else if (blkno != BufferGetBlockNumber(buffer))
     335             :         {
     336        4020 :             UnlockReleaseBuffer(buffer);
     337        4020 :             buffer = ReadBuffer(index, blkno);
     338        4020 :             LockBuffer(buffer, BUFFER_LOCK_SHARE);
     339             :         }
     340             :         /* else new pointer points to the same page, no work needed */
     341             : 
     342        9944 :         page = BufferGetPage(buffer);
     343        9944 :         TestForOldSnapshot(snapshot, index, page);
     344             : 
     345        9944 :         isnull = SpGistPageStoresNulls(page) ? true : false;
     346             : 
     347        9944 :         if (SpGistPageIsLeaf(page))
     348             :         {
     349             :             SpGistLeafTuple leafTuple;
     350        7249 :             OffsetNumber max = PageGetMaxOffsetNumber(page);
     351        7249 :             Datum       leafValue = (Datum) 0;
     352        7249 :             bool        recheck = false;
     353             : 
     354        7277 :             if (SpGistBlockIsRoot(blkno))
     355             :             {
     356             :                 /* When root is a leaf, examine all its tuples */
     357         904 :                 for (offset = FirstOffsetNumber; offset <= max; offset++)
     358             :                 {
     359         876 :                     leafTuple = (SpGistLeafTuple)
     360         876 :                         PageGetItem(page, PageGetItemId(page, offset));
     361         876 :                     if (leafTuple->tupstate != SPGIST_LIVE)
     362             :                     {
     363             :                         /* all tuples on root should be live */
     364           0 :                         elog(ERROR, "unexpected SPGiST tuple state: %d",
     365             :                              leafTuple->tupstate);
     366             :                     }
     367             : 
     368         876 :                     Assert(ItemPointerIsValid(&leafTuple->heapPtr));
     369         876 :                     if (spgLeafTest(index, so,
     370             :                                     leafTuple, isnull,
     371             :                                     stackEntry->level,
     372             :                                     stackEntry->reconstructedValue,
     373             :                                     stackEntry->traversalValue,
     374             :                                     &leafValue,
     375             :                                     &recheck))
     376             :                     {
     377         162 :                         storeRes(so, &leafTuple->heapPtr,
     378             :                                  leafValue, isnull, recheck);
     379         162 :                         reportedSome = true;
     380             :                     }
     381             :                 }
     382             :             }
     383             :             else
     384             :             {
     385             :                 /* Normal case: just examine the chain we arrived at */
     386      327315 :                 while (offset != InvalidOffsetNumber)
     387             :                 {
     388      312873 :                     Assert(offset >= FirstOffsetNumber && offset <= max);
     389      312873 :                     leafTuple = (SpGistLeafTuple)
     390      312873 :                         PageGetItem(page, PageGetItemId(page, offset));
     391      312873 :                     if (leafTuple->tupstate != SPGIST_LIVE)
     392             :                     {
     393           0 :                         if (leafTuple->tupstate == SPGIST_REDIRECT)
     394             :                         {
     395             :                             /* redirection tuple should be first in chain */
     396           0 :                             Assert(offset == ItemPointerGetOffsetNumber(&stackEntry->ptr));
     397             :                             /* transfer attention to redirect point */
     398           0 :                             stackEntry->ptr = ((SpGistDeadTuple) leafTuple)->pointer;
     399           0 :                             Assert(ItemPointerGetBlockNumber(&stackEntry->ptr) != SPGIST_METAPAGE_BLKNO);
     400           0 :                             goto redirect;
     401             :                         }
     402           0 :                         if (leafTuple->tupstate == SPGIST_DEAD)
     403             :                         {
     404             :                             /* dead tuple should be first in chain */
     405           0 :                             Assert(offset == ItemPointerGetOffsetNumber(&stackEntry->ptr));
     406             :                             /* No live entries on this page */
     407           0 :                             Assert(leafTuple->nextOffset == InvalidOffsetNumber);
     408           0 :                             break;
     409             :                         }
     410             :                         /* We should not arrive at a placeholder */
     411           0 :                         elog(ERROR, "unexpected SPGiST tuple state: %d",
     412             :                              leafTuple->tupstate);
     413             :                     }
     414             : 
     415      312873 :                     Assert(ItemPointerIsValid(&leafTuple->heapPtr));
     416      312873 :                     if (spgLeafTest(index, so,
     417             :                                     leafTuple, isnull,
     418             :                                     stackEntry->level,
     419             :                                     stackEntry->reconstructedValue,
     420             :                                     stackEntry->traversalValue,
     421             :                                     &leafValue,
     422             :                                     &recheck))
     423             :                     {
     424      234565 :                         storeRes(so, &leafTuple->heapPtr,
     425             :                                  leafValue, isnull, recheck);
     426      234565 :                         reportedSome = true;
     427             :                     }
     428             : 
     429      312873 :                     offset = leafTuple->nextOffset;
     430             :                 }
     431             :             }
     432             :         }
     433             :         else                    /* page is inner */
     434             :         {
     435             :             SpGistInnerTuple innerTuple;
     436             :             spgInnerConsistentIn in;
     437             :             spgInnerConsistentOut out;
     438             :             FmgrInfo   *procinfo;
     439             :             SpGistNodeTuple *nodes;
     440             :             SpGistNodeTuple node;
     441             :             int         i;
     442             :             MemoryContext oldCtx;
     443             : 
     444        2695 :             innerTuple = (SpGistInnerTuple) PageGetItem(page,
     445             :                                                         PageGetItemId(page, offset));
     446             : 
     447        2695 :             if (innerTuple->tupstate != SPGIST_LIVE)
     448             :             {
     449           0 :                 if (innerTuple->tupstate == SPGIST_REDIRECT)
     450             :                 {
     451             :                     /* transfer attention to redirect point */
     452           0 :                     stackEntry->ptr = ((SpGistDeadTuple) innerTuple)->pointer;
     453           0 :                     Assert(ItemPointerGetBlockNumber(&stackEntry->ptr) != SPGIST_METAPAGE_BLKNO);
     454           0 :                     goto redirect;
     455             :                 }
     456           0 :                 elog(ERROR, "unexpected SPGiST tuple state: %d",
     457             :                      innerTuple->tupstate);
     458             :             }
     459             : 
     460             :             /* use temp context for calling inner_consistent */
     461        2695 :             oldCtx = MemoryContextSwitchTo(so->tempCxt);
     462             : 
     463        2695 :             in.scankeys = so->keyData;
     464        2695 :             in.nkeys = so->numberOfKeys;
     465        2695 :             in.reconstructedValue = stackEntry->reconstructedValue;
     466        2695 :             in.traversalMemoryContext = oldCtx;
     467        2695 :             in.traversalValue = stackEntry->traversalValue;
     468        2695 :             in.level = stackEntry->level;
     469        2695 :             in.returnData = so->want_itup;
     470        2695 :             in.allTheSame = innerTuple->allTheSame;
     471        2695 :             in.hasPrefix = (innerTuple->prefixSize > 0);
     472        2695 :             in.prefixDatum = SGITDATUM(innerTuple, &so->state);
     473        2695 :             in.nNodes = innerTuple->nNodes;
     474        2695 :             in.nodeLabels = spgExtractNodeLabels(&so->state, innerTuple);
     475             : 
     476             :             /* collect node pointers */
     477        2695 :             nodes = (SpGistNodeTuple *) palloc(sizeof(SpGistNodeTuple) * in.nNodes);
     478       22064 :             SGITITERATE(innerTuple, i, node)
     479             :             {
     480       19369 :                 nodes[i] = node;
     481             :             }
     482             : 
     483        2695 :             memset(&out, 0, sizeof(out));
     484             : 
     485        2695 :             if (!isnull)
     486             :             {
     487             :                 /* use user-defined inner consistent method */
     488        2695 :                 procinfo = index_getprocinfo(index, 1, SPGIST_INNER_CONSISTENT_PROC);
     489        5390 :                 FunctionCall2Coll(procinfo,
     490        2695 :                                   index->rd_indcollation[0],
     491             :                                   PointerGetDatum(&in),
     492             :                                   PointerGetDatum(&out));
     493             :             }
     494             :             else
     495             :             {
     496             :                 /* force all children to be visited */
     497           0 :                 out.nNodes = in.nNodes;
     498           0 :                 out.nodeNumbers = (int *) palloc(sizeof(int) * in.nNodes);
     499           0 :                 for (i = 0; i < in.nNodes; i++)
     500           0 :                     out.nodeNumbers[i] = i;
     501             :             }
     502             : 
     503        2695 :             MemoryContextSwitchTo(oldCtx);
     504             : 
     505             :             /* If allTheSame, they should all or none of 'em match */
     506        2695 :             if (innerTuple->allTheSame)
     507         200 :                 if (out.nNodes != 0 && out.nNodes != in.nNodes)
     508           0 :                     elog(ERROR, "inconsistent inner_consistent results for allTheSame inner tuple");
     509             : 
     510       18664 :             for (i = 0; i < out.nNodes; i++)
     511             :             {
     512       15969 :                 int         nodeN = out.nodeNumbers[i];
     513             : 
     514       15969 :                 Assert(nodeN >= 0 && nodeN < in.nNodes);
     515       15969 :                 if (ItemPointerIsValid(&nodes[nodeN]->t_tid))
     516             :                 {
     517             :                     ScanStackEntry *newEntry;
     518             : 
     519             :                     /* Create new work item for this node */
     520        9822 :                     newEntry = palloc(sizeof(ScanStackEntry));
     521        9822 :                     newEntry->ptr = nodes[nodeN]->t_tid;
     522        9822 :                     if (out.levelAdds)
     523        3532 :                         newEntry->level = stackEntry->level + out.levelAdds[i];
     524             :                     else
     525        6290 :                         newEntry->level = stackEntry->level;
     526             :                     /* Must copy value out of temp context */
     527        9822 :                     if (out.reconstructedValues)
     528        2400 :                         newEntry->reconstructedValue =
     529        4800 :                             datumCopy(out.reconstructedValues[i],
     530        2400 :                                       so->state.attType.attbyval,
     531        2400 :                                       so->state.attType.attlen);
     532             :                     else
     533        7422 :                         newEntry->reconstructedValue = (Datum) 0;
     534             : 
     535             :                     /*
     536             :                      * Elements of out.traversalValues should be allocated in
     537             :                      * in.traversalMemoryContext, which is actually a long
     538             :                      * lived context of index scan.
     539             :                      */
     540       19644 :                     newEntry->traversalValue = (out.traversalValues) ?
     541        9822 :                         out.traversalValues[i] : NULL;
     542             : 
     543        9822 :                     so->scanStack = lcons(newEntry, so->scanStack);
     544             :                 }
     545             :             }
     546             :         }
     547             : 
     548             :         /* done with this scan stack entry */
     549        9944 :         freeScanStackEntry(so, stackEntry);
     550             :         /* clear temp context before proceeding to the next one */
     551        9944 :         MemoryContextReset(so->tempCxt);
     552             :     }
     553             : 
     554        2161 :     if (buffer != InvalidBuffer)
     555        2093 :         UnlockReleaseBuffer(buffer);
     556        2161 : }
     557             : 
     558             : /* storeRes subroutine for getbitmap case */
     559             : static void
     560      127583 : storeBitmap(SpGistScanOpaque so, ItemPointer heapPtr,
     561             :             Datum leafValue, bool isnull, bool recheck)
     562             : {
     563      127583 :     tbm_add_tuples(so->tbm, heapPtr, 1, recheck);
     564      127583 :     so->ntids++;
     565      127583 : }
     566             : 
     567             : int64
     568          43 : spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     569             : {
     570          43 :     SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     571             : 
     572             :     /* Copy want_itup to *so so we don't need to pass it around separately */
     573          43 :     so->want_itup = false;
     574             : 
     575          43 :     so->tbm = tbm;
     576          43 :     so->ntids = 0;
     577             : 
     578          43 :     spgWalk(scan->indexRelation, so, true, storeBitmap, scan->xs_snapshot);
     579             : 
     580          43 :     return so->ntids;
     581             : }
     582             : 
     583             : /* storeRes subroutine for gettuple case */
     584             : static void
     585      107144 : storeGettuple(SpGistScanOpaque so, ItemPointer heapPtr,
     586             :               Datum leafValue, bool isnull, bool recheck)
     587             : {
     588      107144 :     Assert(so->nPtrs < MaxIndexTuplesPerPage);
     589      107144 :     so->heapPtrs[so->nPtrs] = *heapPtr;
     590      107144 :     so->recheck[so->nPtrs] = recheck;
     591      107144 :     if (so->want_itup)
     592             :     {
     593             :         /*
     594             :          * Reconstruct index data.  We have to copy the datum out of the temp
     595             :          * context anyway, so we may as well create the tuple here.
     596             :          */
     597      107075 :         so->reconTups[so->nPtrs] = heap_form_tuple(so->indexTupDesc,
     598             :                                                    &leafValue,
     599             :                                                    &isnull);
     600             :     }
     601      107144 :     so->nPtrs++;
     602      107144 : }
     603             : 
     604             : bool
     605      107221 : spggettuple(IndexScanDesc scan, ScanDirection dir)
     606             : {
     607      107221 :     SpGistScanOpaque so = (SpGistScanOpaque) scan->opaque;
     608             : 
     609      107221 :     if (dir != ForwardScanDirection)
     610           0 :         elog(ERROR, "SP-GiST only supports forward scan direction");
     611             : 
     612             :     /* Copy want_itup to *so so we don't need to pass it around separately */
     613      107221 :     so->want_itup = scan->xs_want_itup;
     614             : 
     615             :     for (;;)
     616             :     {
     617      109262 :         if (so->iPtr < so->nPtrs)
     618             :         {
     619             :             /* continuing to return tuples from a leaf page */
     620      107144 :             scan->xs_ctup.t_self = so->heapPtrs[so->iPtr];
     621      107144 :             scan->xs_recheck = so->recheck[so->iPtr];
     622      107144 :             scan->xs_hitup = so->reconTups[so->iPtr];
     623      107144 :             so->iPtr++;
     624      107144 :             return true;
     625             :         }
     626             : 
     627        2118 :         if (so->want_itup)
     628             :         {
     629             :             /* Must pfree reconstructed tuples to avoid memory leak */
     630             :             int         i;
     631             : 
     632      109172 :             for (i = 0; i < so->nPtrs; i++)
     633      107075 :                 pfree(so->reconTups[i]);
     634             :         }
     635        2118 :         so->iPtr = so->nPtrs = 0;
     636             : 
     637        2118 :         spgWalk(scan->indexRelation, so, false, storeGettuple,
     638             :                 scan->xs_snapshot);
     639             : 
     640        2118 :         if (so->nPtrs == 0)
     641          77 :             break;              /* must have completed scan */
     642        2041 :     }
     643             : 
     644          77 :     return false;
     645             : }
     646             : 
     647             : bool
     648         233 : spgcanreturn(Relation index, int attno)
     649             : {
     650             :     SpGistCache *cache;
     651             : 
     652             :     /* We can do it if the opclass config function says so */
     653         233 :     cache = spgGetCache(index);
     654             : 
     655         233 :     return cache->config.canReturnData;
     656             : }

Generated by: LCOV version 1.11