LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistget.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.2 % 265 239
Test Date: 2026-04-17 16:16:21 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * gistget.c
       4              :  *    fetch tuples from a GiST scan.
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/access/gist/gistget.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : #include "postgres.h"
      16              : 
      17              : #include "access/genam.h"
      18              : #include "access/gist_private.h"
      19              : #include "access/relscan.h"
      20              : #include "executor/instrument_node.h"
      21              : #include "lib/pairingheap.h"
      22              : #include "miscadmin.h"
      23              : #include "pgstat.h"
      24              : #include "storage/predicate.h"
      25              : #include "utils/float.h"
      26              : #include "utils/memutils.h"
      27              : #include "utils/rel.h"
      28              : 
      29              : /*
      30              :  * gistkillitems() -- set LP_DEAD state for items an indexscan caller has
      31              :  * told us were killed.
      32              :  *
      33              :  * We re-read page here, so it's important to check page LSN. If the page
      34              :  * has been modified since the last read (as determined by LSN), we cannot
      35              :  * flag any entries because it is possible that the old entry was vacuumed
      36              :  * away and the TID was re-used by a completely different heap tuple.
      37              :  */
      38              : static void
      39            1 : gistkillitems(IndexScanDesc scan)
      40              : {
      41            1 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
      42              :     Buffer      buffer;
      43              :     Page        page;
      44              :     OffsetNumber offnum;
      45              :     ItemId      iid;
      46              :     int         i;
      47            1 :     bool        killedsomething = false;
      48              : 
      49              :     Assert(so->curBlkno != InvalidBlockNumber);
      50              :     Assert(XLogRecPtrIsValid(so->curPageLSN));
      51              :     Assert(so->killedItems != NULL);
      52              : 
      53            1 :     buffer = ReadBuffer(scan->indexRelation, so->curBlkno);
      54            1 :     if (!BufferIsValid(buffer))
      55            0 :         return;
      56              : 
      57            1 :     LockBuffer(buffer, GIST_SHARE);
      58            1 :     gistcheckpage(scan->indexRelation, buffer);
      59            1 :     page = BufferGetPage(buffer);
      60              : 
      61              :     /*
      62              :      * If page LSN differs it means that the page was modified since the last
      63              :      * read. killedItems could be not valid so LP_DEAD hints applying is not
      64              :      * safe.
      65              :      */
      66            1 :     if (BufferGetLSNAtomic(buffer) != so->curPageLSN)
      67            0 :         goto unlock;
      68              : 
      69              :     Assert(GistPageIsLeaf(page));
      70              : 
      71              :     /*
      72              :      * Mark all killedItems as dead. We need no additional recheck, because,
      73              :      * if page was modified, curPageLSN must have changed.
      74              :      */
      75            2 :     for (i = 0; i < so->numKilled; i++)
      76              :     {
      77            1 :         if (!killedsomething)
      78              :         {
      79              :             /*
      80              :              * Use the hint bit infrastructure to check if we can update the
      81              :              * page while just holding a share lock. If we are not allowed,
      82              :              * there's no point continuing.
      83              :              */
      84            1 :             if (!BufferBeginSetHintBits(buffer))
      85            0 :                 goto unlock;
      86              :         }
      87              : 
      88            1 :         offnum = so->killedItems[i];
      89            1 :         iid = PageGetItemId(page, offnum);
      90            1 :         ItemIdMarkDead(iid);
      91            1 :         killedsomething = true;
      92              :     }
      93              : 
      94            1 :     if (killedsomething)
      95              :     {
      96            1 :         GistMarkPageHasGarbage(page);
      97            1 :         BufferFinishSetHintBits(buffer, true, true);
      98              :     }
      99              : 
     100            0 : unlock:
     101            1 :     UnlockReleaseBuffer(buffer);
     102              : 
     103              :     /*
     104              :      * Always reset the scan state, so we don't look for same items on other
     105              :      * pages.
     106              :      */
     107            1 :     so->numKilled = 0;
     108              : }
     109              : 
     110              : /*
     111              :  * gistindex_keytest() -- does this index tuple satisfy the scan key(s)?
     112              :  *
     113              :  * The index tuple might represent either a heap tuple or a lower index page,
     114              :  * depending on whether the containing page is a leaf page or not.
     115              :  *
     116              :  * On success return for a heap tuple, *recheck_p is set to indicate whether
     117              :  * the quals need to be rechecked.  We recheck if any of the consistent()
     118              :  * functions request it.  recheck is not interesting when examining a non-leaf
     119              :  * entry, since we must visit the lower index page if there's any doubt.
     120              :  * Similarly, *recheck_distances_p is set to indicate whether the distances
     121              :  * need to be rechecked, and it is also ignored for non-leaf entries.
     122              :  *
     123              :  * If we are doing an ordered scan, so->distances[] is filled with distance
     124              :  * data from the distance() functions before returning success.
     125              :  *
     126              :  * We must decompress the key in the IndexTuple before passing it to the
     127              :  * sk_funcs (which actually are the opclass Consistent or Distance methods).
     128              :  *
     129              :  * Note that this function is always invoked in a short-lived memory context,
     130              :  * so we don't need to worry about cleaning up allocated memory, either here
     131              :  * or in the implementation of any Consistent or Distance methods.
     132              :  */
     133              : static bool
     134      1326093 : gistindex_keytest(IndexScanDesc scan,
     135              :                   IndexTuple tuple,
     136              :                   Page page,
     137              :                   OffsetNumber offset,
     138              :                   bool *recheck_p,
     139              :                   bool *recheck_distances_p)
     140              : {
     141      1326093 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     142      1326093 :     GISTSTATE  *giststate = so->giststate;
     143      1326093 :     ScanKey     key = scan->keyData;
     144      1326093 :     int         keySize = scan->numberOfKeys;
     145              :     IndexOrderByDistance *distance_p;
     146      1326093 :     Relation    r = scan->indexRelation;
     147              : 
     148      1326093 :     *recheck_p = false;
     149      1326093 :     *recheck_distances_p = false;
     150              : 
     151              :     /*
     152              :      * If it's a leftover invalid tuple from pre-9.1, treat it as a match with
     153              :      * minimum possible distances.  This means we'll always follow it to the
     154              :      * referenced page.
     155              :      */
     156      1326093 :     if (GistTupleIsInvalid(tuple))
     157              :     {
     158              :         int         i;
     159              : 
     160            0 :         if (GistPageIsLeaf(page))   /* shouldn't happen */
     161            0 :             elog(ERROR, "invalid GiST tuple found on leaf page");
     162            0 :         for (i = 0; i < scan->numberOfOrderBys; i++)
     163              :         {
     164            0 :             so->distances[i].value = -get_float8_infinity();
     165            0 :             so->distances[i].isnull = false;
     166              :         }
     167            0 :         return true;
     168              :     }
     169              : 
     170              :     /* Check whether it matches according to the Consistent functions */
     171      2047660 :     while (keySize > 0)
     172              :     {
     173              :         Datum       datum;
     174              :         bool        isNull;
     175              : 
     176      1279706 :         datum = index_getattr(tuple,
     177      1279706 :                               key->sk_attno,
     178              :                               giststate->leafTupdesc,
     179              :                               &isNull);
     180              : 
     181      1279706 :         if (key->sk_flags & SK_ISNULL)
     182              :         {
     183              :             /*
     184              :              * On non-leaf page we can't conclude that child hasn't NULL
     185              :              * values because of assumption in GiST: union (VAL, NULL) is VAL.
     186              :              * But if on non-leaf page key IS NULL, then all children are
     187              :              * NULL.
     188              :              */
     189        29932 :             if (key->sk_flags & SK_SEARCHNULL)
     190              :             {
     191        29888 :                 if (GistPageIsLeaf(page) && !isNull)
     192       558139 :                     return false;
     193              :             }
     194              :             else
     195              :             {
     196              :                 Assert(key->sk_flags & SK_SEARCHNOTNULL);
     197           44 :                 if (isNull)
     198            4 :                     return false;
     199              :             }
     200              :         }
     201      1249774 :         else if (isNull)
     202              :         {
     203         2379 :             return false;
     204              :         }
     205              :         else
     206              :         {
     207              :             Datum       test;
     208              :             bool        recheck;
     209              :             GISTENTRY   de;
     210              : 
     211      1247395 :             gistdentryinit(giststate, key->sk_attno - 1, &de,
     212              :                            datum, r, page, offset,
     213              :                            false, isNull);
     214              : 
     215              :             /*
     216              :              * Call the Consistent function to evaluate the test.  The
     217              :              * arguments are the index datum (as a GISTENTRY*), the comparison
     218              :              * datum, the comparison operator's strategy number and subtype
     219              :              * from pg_amop, and the recheck flag.
     220              :              *
     221              :              * (Presently there's no need to pass the subtype since it'll
     222              :              * always be zero, but might as well pass it for possible future
     223              :              * use.)
     224              :              *
     225              :              * We initialize the recheck flag to true (the safest assumption)
     226              :              * in case the Consistent function forgets to set it.
     227              :              */
     228      1247395 :             recheck = true;
     229              : 
     230      2494790 :             test = FunctionCall5Coll(&key->sk_func,
     231              :                                      key->sk_collation,
     232              :                                      PointerGetDatum(&de),
     233              :                                      key->sk_argument,
     234      1247395 :                                      UInt16GetDatum(key->sk_strategy),
     235              :                                      ObjectIdGetDatum(key->sk_subtype),
     236              :                                      PointerGetDatum(&recheck));
     237              : 
     238      1247395 :             if (!DatumGetBool(test))
     239       528339 :                 return false;
     240       719056 :             *recheck_p |= recheck;
     241              :         }
     242              : 
     243       721567 :         key++;
     244       721567 :         keySize--;
     245              :     }
     246              : 
     247              :     /* OK, it passes --- now let's compute the distances */
     248       767954 :     key = scan->orderByData;
     249       767954 :     distance_p = so->distances;
     250       767954 :     keySize = scan->numberOfOrderBys;
     251       785770 :     while (keySize > 0)
     252              :     {
     253              :         Datum       datum;
     254              :         bool        isNull;
     255              : 
     256        17816 :         datum = index_getattr(tuple,
     257        17816 :                               key->sk_attno,
     258              :                               giststate->leafTupdesc,
     259              :                               &isNull);
     260              : 
     261        17816 :         if ((key->sk_flags & SK_ISNULL) || isNull)
     262              :         {
     263              :             /* Assume distance computes as null */
     264           68 :             distance_p->value = 0.0;
     265           68 :             distance_p->isnull = true;
     266              :         }
     267              :         else
     268              :         {
     269              :             Datum       dist;
     270              :             bool        recheck;
     271              :             GISTENTRY   de;
     272              : 
     273        17748 :             gistdentryinit(giststate, key->sk_attno - 1, &de,
     274              :                            datum, r, page, offset,
     275              :                            false, isNull);
     276              : 
     277              :             /*
     278              :              * Call the Distance function to evaluate the distance.  The
     279              :              * arguments are the index datum (as a GISTENTRY*), the comparison
     280              :              * datum, the ordering operator's strategy number and subtype from
     281              :              * pg_amop, and the recheck flag.
     282              :              *
     283              :              * (Presently there's no need to pass the subtype since it'll
     284              :              * always be zero, but might as well pass it for possible future
     285              :              * use.)
     286              :              *
     287              :              * If the function sets the recheck flag, the returned distance is
     288              :              * a lower bound on the true distance and needs to be rechecked.
     289              :              * We initialize the flag to 'false'.  This flag was added in
     290              :              * version 9.5; distance functions written before that won't know
     291              :              * about the flag, but are expected to never be lossy.
     292              :              */
     293        17748 :             recheck = false;
     294        35496 :             dist = FunctionCall5Coll(&key->sk_func,
     295              :                                      key->sk_collation,
     296              :                                      PointerGetDatum(&de),
     297              :                                      key->sk_argument,
     298        17748 :                                      UInt16GetDatum(key->sk_strategy),
     299              :                                      ObjectIdGetDatum(key->sk_subtype),
     300              :                                      PointerGetDatum(&recheck));
     301        17748 :             *recheck_distances_p |= recheck;
     302        17748 :             distance_p->value = DatumGetFloat8(dist);
     303        17748 :             distance_p->isnull = false;
     304              :         }
     305              : 
     306        17816 :         key++;
     307        17816 :         distance_p++;
     308        17816 :         keySize--;
     309              :     }
     310              : 
     311       767954 :     return true;
     312              : }
     313              : 
     314              : /*
     315              :  * Scan all items on the GiST index page identified by *pageItem, and insert
     316              :  * them into the queue (or directly to output areas)
     317              :  *
     318              :  * scan: index scan we are executing
     319              :  * pageItem: search queue item identifying an index page to scan
     320              :  * myDistances: distances array associated with pageItem, or NULL at the root
     321              :  * tbm: if not NULL, gistgetbitmap's output bitmap
     322              :  * ntids: if not NULL, gistgetbitmap's output tuple counter
     323              :  *
     324              :  * If tbm/ntids aren't NULL, we are doing an amgetbitmap scan, and heap
     325              :  * tuples should be reported directly into the bitmap.  If they are NULL,
     326              :  * we're doing a plain or ordered indexscan.  For a plain indexscan, heap
     327              :  * tuple TIDs are returned into so->pageData[].  For an ordered indexscan,
     328              :  * heap tuple TIDs are pushed into individual search queue items.  In an
     329              :  * index-only scan, reconstructed index tuples are returned along with the
     330              :  * TIDs.
     331              :  *
     332              :  * If we detect that the index page has split since we saw its downlink
     333              :  * in the parent, we push its new right sibling onto the queue so the
     334              :  * sibling will be processed next.
     335              :  */
     336              : static void
     337        45688 : gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem,
     338              :              IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
     339              : {
     340        45688 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     341        45688 :     GISTSTATE  *giststate = so->giststate;
     342        45688 :     Relation    r = scan->indexRelation;
     343              :     Buffer      buffer;
     344              :     Page        page;
     345              :     GISTPageOpaque opaque;
     346              :     OffsetNumber maxoff;
     347              :     OffsetNumber i;
     348              :     MemoryContext oldcxt;
     349              : 
     350              :     Assert(!GISTSearchItemIsHeap(*pageItem));
     351              : 
     352        45688 :     buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
     353        45688 :     LockBuffer(buffer, GIST_SHARE);
     354        45688 :     PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
     355        45688 :     gistcheckpage(scan->indexRelation, buffer);
     356        45688 :     page = BufferGetPage(buffer);
     357        45688 :     opaque = GistPageGetOpaque(page);
     358              : 
     359              :     /*
     360              :      * Check if we need to follow the rightlink. We need to follow it if the
     361              :      * page was concurrently split since we visited the parent (in which case
     362              :      * parentlsn < nsn), or if the system crashed after a page split but
     363              :      * before the downlink was inserted into the parent.
     364              :      */
     365        45688 :     if (XLogRecPtrIsValid(pageItem->data.parentlsn) &&
     366        78684 :         (GistFollowRight(page) ||
     367        39342 :          pageItem->data.parentlsn < GistPageGetNSN(page)) &&
     368            0 :         opaque->rightlink != InvalidBlockNumber /* sanity check */ )
     369              :     {
     370              :         /* There was a page split, follow right link to add pages */
     371              :         GISTSearchItem *item;
     372              : 
     373              :         /* This can't happen when starting at the root */
     374              :         Assert(myDistances != NULL);
     375              : 
     376            0 :         oldcxt = MemoryContextSwitchTo(so->queueCxt);
     377              : 
     378              :         /* Create new GISTSearchItem for the right sibling index page */
     379            0 :         item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));
     380            0 :         item->blkno = opaque->rightlink;
     381            0 :         item->data.parentlsn = pageItem->data.parentlsn;
     382              : 
     383              :         /* Insert it into the queue using same distances as for this page */
     384            0 :         memcpy(item->distances, myDistances,
     385            0 :                sizeof(item->distances[0]) * scan->numberOfOrderBys);
     386              : 
     387            0 :         pairingheap_add(so->queue, &item->phNode);
     388              : 
     389            0 :         MemoryContextSwitchTo(oldcxt);
     390              :     }
     391              : 
     392              :     /*
     393              :      * Check if the page was deleted after we saw the downlink. There's
     394              :      * nothing of interest on a deleted page. Note that we must do this after
     395              :      * checking the NSN for concurrent splits! It's possible that the page
     396              :      * originally contained some tuples that are visible to us, but was split
     397              :      * so that all the visible tuples were moved to another page, and then
     398              :      * this page was deleted.
     399              :      */
     400        45688 :     if (GistPageIsDeleted(page))
     401              :     {
     402            0 :         UnlockReleaseBuffer(buffer);
     403            0 :         return;
     404              :     }
     405              : 
     406        45688 :     so->nPageData = so->curPageData = 0;
     407        45688 :     scan->xs_hitup = NULL;       /* might point into pageDataCxt */
     408        45688 :     if (so->pageDataCxt)
     409         4105 :         MemoryContextReset(so->pageDataCxt);
     410              : 
     411              :     /*
     412              :      * We save the LSN of the page as we read it, so that we know whether it
     413              :      * is safe to apply LP_DEAD hints to the page later. This allows us to
     414              :      * drop the pin for MVCC scans, which allows vacuum to avoid blocking.
     415              :      */
     416        45688 :     so->curPageLSN = BufferGetLSNAtomic(buffer);
     417              : 
     418              :     /*
     419              :      * check all tuples on page
     420              :      */
     421        45688 :     maxoff = PageGetMaxOffsetNumber(page);
     422      1371782 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     423              :     {
     424      1326094 :         ItemId      iid = PageGetItemId(page, i);
     425              :         IndexTuple  it;
     426              :         bool        match;
     427              :         bool        recheck;
     428              :         bool        recheck_distances;
     429              : 
     430              :         /*
     431              :          * If the scan specifies not to return killed tuples, then we treat a
     432              :          * killed tuple as not passing the qual.
     433              :          */
     434      1326094 :         if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
     435       558140 :             continue;
     436              : 
     437      1326093 :         it = (IndexTuple) PageGetItem(page, iid);
     438              : 
     439              :         /*
     440              :          * Must call gistindex_keytest in tempCxt, and clean up any leftover
     441              :          * junk afterward.
     442              :          */
     443      1326093 :         oldcxt = MemoryContextSwitchTo(so->giststate->tempCxt);
     444              : 
     445      1326093 :         match = gistindex_keytest(scan, it, page, i,
     446              :                                   &recheck, &recheck_distances);
     447              : 
     448      1326093 :         MemoryContextSwitchTo(oldcxt);
     449      1326093 :         MemoryContextReset(so->giststate->tempCxt);
     450              : 
     451              :         /* Ignore tuple if it doesn't match */
     452      1326093 :         if (!match)
     453       558139 :             continue;
     454              : 
     455       767954 :         if (tbm && GistPageIsLeaf(page))
     456              :         {
     457              :             /*
     458              :              * getbitmap scan, so just push heap tuple TIDs into the bitmap
     459              :              * without worrying about ordering
     460              :              */
     461       197655 :             tbm_add_tuples(tbm, &it->t_tid, 1, recheck);
     462       197655 :             (*ntids)++;
     463              :         }
     464       570299 :         else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
     465              :         {
     466              :             /*
     467              :              * Non-ordered scan, so report tuples in so->pageData[]
     468              :              */
     469       513327 :             so->pageData[so->nPageData].heapPtr = it->t_tid;
     470       513327 :             so->pageData[so->nPageData].recheck = recheck;
     471       513327 :             so->pageData[so->nPageData].offnum = i;
     472              : 
     473              :             /*
     474              :              * In an index-only scan, also fetch the data from the tuple.  The
     475              :              * reconstructed tuples are stored in pageDataCxt.
     476              :              */
     477       513327 :             if (scan->xs_want_itup)
     478              :             {
     479       355626 :                 oldcxt = MemoryContextSwitchTo(so->pageDataCxt);
     480       711252 :                 so->pageData[so->nPageData].recontup =
     481       355626 :                     gistFetchTuple(giststate, r, it);
     482       355626 :                 MemoryContextSwitchTo(oldcxt);
     483              :             }
     484       513327 :             so->nPageData++;
     485              :         }
     486              :         else
     487              :         {
     488              :             /*
     489              :              * Must push item into search queue.  We get here for any lower
     490              :              * index page, and also for heap tuples if doing an ordered
     491              :              * search.
     492              :              */
     493              :             GISTSearchItem *item;
     494        56972 :             int         nOrderBys = scan->numberOfOrderBys;
     495              : 
     496        56972 :             oldcxt = MemoryContextSwitchTo(so->queueCxt);
     497              : 
     498              :             /* Create new GISTSearchItem for this item */
     499        56972 :             item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));
     500              : 
     501        56972 :             if (GistPageIsLeaf(page))
     502              :             {
     503              :                 /* Creating heap-tuple GISTSearchItem */
     504        15660 :                 item->blkno = InvalidBlockNumber;
     505        15660 :                 item->data.heap.heapPtr = it->t_tid;
     506        15660 :                 item->data.heap.recheck = recheck;
     507        15660 :                 item->data.heap.recheckDistances = recheck_distances;
     508              : 
     509              :                 /*
     510              :                  * In an index-only scan, also fetch the data from the tuple.
     511              :                  */
     512        15660 :                 if (scan->xs_want_itup)
     513         7292 :                     item->data.heap.recontup = gistFetchTuple(giststate, r, it);
     514              :             }
     515              :             else
     516              :             {
     517              :                 /* Creating index-page GISTSearchItem */
     518        41312 :                 item->blkno = ItemPointerGetBlockNumber(&it->t_tid);
     519              : 
     520              :                 /*
     521              :                  * LSN of current page is lsn of parent page for child. We
     522              :                  * only have a shared lock, so we need to get the LSN
     523              :                  * atomically.
     524              :                  */
     525        41312 :                 item->data.parentlsn = BufferGetLSNAtomic(buffer);
     526              :             }
     527              : 
     528              :             /* Insert it into the queue using new distance data */
     529        56972 :             memcpy(item->distances, so->distances,
     530              :                    sizeof(item->distances[0]) * nOrderBys);
     531              : 
     532        56972 :             pairingheap_add(so->queue, &item->phNode);
     533              : 
     534        56972 :             MemoryContextSwitchTo(oldcxt);
     535              :         }
     536              :     }
     537              : 
     538        45688 :     UnlockReleaseBuffer(buffer);
     539              : }
     540              : 
     541              : /*
     542              :  * Extract next item (in order) from search queue
     543              :  *
     544              :  * Returns a GISTSearchItem or NULL.  Caller must pfree item when done with it.
     545              :  */
     546              : static GISTSearchItem *
     547        46147 : getNextGISTSearchItem(GISTScanOpaque so)
     548              : {
     549              :     GISTSearchItem *item;
     550              : 
     551        46147 :     if (!pairingheap_is_empty(so->queue))
     552              :     {
     553        40105 :         item = (GISTSearchItem *) pairingheap_remove_first(so->queue);
     554              :     }
     555              :     else
     556              :     {
     557              :         /* Done when both heaps are empty */
     558         6042 :         item = NULL;
     559              :     }
     560              : 
     561              :     /* Return item; caller is responsible to pfree it */
     562        46147 :     return item;
     563              : }
     564              : 
     565              : /*
     566              :  * Fetch next heap tuple in an ordered search
     567              :  */
     568              : static bool
     569          791 : getNextNearest(IndexScanDesc scan)
     570              : {
     571          791 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     572          791 :     bool        res = false;
     573              : 
     574          791 :     if (scan->xs_hitup)
     575              :     {
     576              :         /* free previously returned tuple */
     577          518 :         pfree(scan->xs_hitup);
     578          518 :         scan->xs_hitup = NULL;
     579              :     }
     580              : 
     581              :     do
     582              :     {
     583          977 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     584              : 
     585          977 :         if (!item)
     586           28 :             break;
     587              : 
     588          949 :         if (GISTSearchItemIsHeap(*item))
     589              :         {
     590              :             /* found a heap item at currently minimal distance */
     591          763 :             scan->xs_heaptid = item->data.heap.heapPtr;
     592          763 :             scan->xs_recheck = item->data.heap.recheck;
     593              : 
     594          763 :             index_store_float8_orderby_distances(scan, so->orderByTypes,
     595          763 :                                                  item->distances,
     596          763 :                                                  item->data.heap.recheckDistances);
     597              : 
     598              :             /* in an index-only scan, also return the reconstructed tuple. */
     599          763 :             if (scan->xs_want_itup)
     600          556 :                 scan->xs_hitup = item->data.heap.recontup;
     601          763 :             res = true;
     602              :         }
     603              :         else
     604              :         {
     605              :             /* visit an index page, extract its items into queue */
     606          186 :             CHECK_FOR_INTERRUPTS();
     607              : 
     608          186 :             gistScanPage(scan, item, item->distances, NULL, NULL);
     609              :         }
     610              : 
     611          949 :         pfree(item);
     612          949 :     } while (!res);
     613              : 
     614          791 :     return res;
     615              : }
     616              : 
     617              : /*
     618              :  * gistgettuple() -- Get the next tuple in the scan
     619              :  */
     620              : bool
     621       518734 : gistgettuple(IndexScanDesc scan, ScanDirection dir)
     622              : {
     623       518734 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     624              : 
     625       518734 :     if (dir != ForwardScanDirection)
     626            0 :         elog(ERROR, "GiST only supports forward scan direction");
     627              : 
     628       518734 :     if (!so->qual_ok)
     629            0 :         return false;
     630              : 
     631       518734 :     if (so->firstCall)
     632              :     {
     633              :         /* Begin the scan by processing the root page */
     634              :         GISTSearchItem fakeItem;
     635              : 
     636         5127 :         pgstat_count_index_scan(scan->indexRelation);
     637         5127 :         if (scan->instrument)
     638            8 :             scan->instrument->nsearches++;
     639              : 
     640         5127 :         so->firstCall = false;
     641         5127 :         so->curPageData = so->nPageData = 0;
     642         5127 :         scan->xs_hitup = NULL;
     643         5127 :         if (so->pageDataCxt)
     644          466 :             MemoryContextReset(so->pageDataCxt);
     645              : 
     646         5127 :         fakeItem.blkno = GIST_ROOT_BLKNO;
     647         5127 :         memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     648         5127 :         gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
     649              :     }
     650              : 
     651       518734 :     if (scan->numberOfOrderBys > 0)
     652              :     {
     653              :         /* Must fetch tuples in strict distance order */
     654          791 :         return getNextNearest(scan);
     655              :     }
     656              :     else
     657              :     {
     658              :         /* Fetch tuples index-page-at-a-time */
     659              :         for (;;)
     660              :         {
     661       523425 :             if (so->curPageData < so->nPageData)
     662              :             {
     663       513148 :                 if (scan->kill_prior_tuple && so->curPageData > 0)
     664              :                 {
     665              : 
     666         1101 :                     if (so->killedItems == NULL)
     667              :                     {
     668              :                         MemoryContext oldCxt =
     669          908 :                             MemoryContextSwitchTo(so->giststate->scanCxt);
     670              : 
     671          908 :                         so->killedItems =
     672          908 :                             (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     673              :                                                     * sizeof(OffsetNumber));
     674              : 
     675          908 :                         MemoryContextSwitchTo(oldCxt);
     676              :                     }
     677         1101 :                     if (so->numKilled < MaxIndexTuplesPerPage)
     678         1101 :                         so->killedItems[so->numKilled++] =
     679         1101 :                             so->pageData[so->curPageData - 1].offnum;
     680              :                 }
     681              :                 /* continuing to return tuples from a leaf page */
     682       513148 :                 scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;
     683       513148 :                 scan->xs_recheck = so->pageData[so->curPageData].recheck;
     684              : 
     685              :                 /* in an index-only scan, also return the reconstructed tuple */
     686       513148 :                 if (scan->xs_want_itup)
     687       355626 :                     scan->xs_hitup = so->pageData[so->curPageData].recontup;
     688              : 
     689       513148 :                 so->curPageData++;
     690              : 
     691       513148 :                 return true;
     692              :             }
     693              : 
     694              :             /*
     695              :              * Check the last returned tuple and add it to killedItems if
     696              :              * necessary
     697              :              */
     698        10277 :             if (scan->kill_prior_tuple
     699           29 :                 && so->curPageData > 0
     700           29 :                 && so->curPageData == so->nPageData)
     701              :             {
     702              : 
     703           29 :                 if (so->killedItems == NULL)
     704              :                 {
     705              :                     MemoryContext oldCxt =
     706           14 :                         MemoryContextSwitchTo(so->giststate->scanCxt);
     707              : 
     708           14 :                     so->killedItems =
     709           14 :                         (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     710              :                                                 * sizeof(OffsetNumber));
     711              : 
     712           14 :                     MemoryContextSwitchTo(oldCxt);
     713              :                 }
     714           29 :                 if (so->numKilled < MaxIndexTuplesPerPage)
     715           29 :                     so->killedItems[so->numKilled++] =
     716           29 :                         so->pageData[so->curPageData - 1].offnum;
     717              :             }
     718              :             /* find and process the next index page */
     719              :             do
     720              :             {
     721              :                 GISTSearchItem *item;
     722              : 
     723        12152 :                 if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
     724            1 :                     gistkillitems(scan);
     725              : 
     726        12152 :                 item = getNextGISTSearchItem(so);
     727              : 
     728        12152 :                 if (!item)
     729         4795 :                     return false;
     730              : 
     731         7357 :                 CHECK_FOR_INTERRUPTS();
     732              : 
     733              :                 /* save current item BlockNumber for next gistkillitems() call */
     734         7357 :                 so->curBlkno = item->blkno;
     735              : 
     736              :                 /*
     737              :                  * While scanning a leaf page, ItemPointers of matching heap
     738              :                  * tuples are stored in so->pageData.  If there are any on
     739              :                  * this page, we fall out of the inner "do" and loop around to
     740              :                  * return them.
     741              :                  */
     742         7357 :                 gistScanPage(scan, item, item->distances, NULL, NULL);
     743              : 
     744         7357 :                 pfree(item);
     745         7357 :             } while (so->nPageData == 0);
     746              :         }
     747              :     }
     748              : }
     749              : 
     750              : /*
     751              :  * gistgetbitmap() -- Get a bitmap of all heap tuple locations
     752              :  */
     753              : int64
     754         1219 : gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     755              : {
     756         1219 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     757         1219 :     int64       ntids = 0;
     758              :     GISTSearchItem fakeItem;
     759              : 
     760         1219 :     if (!so->qual_ok)
     761            0 :         return 0;
     762              : 
     763         1219 :     pgstat_count_index_scan(scan->indexRelation);
     764         1219 :     if (scan->instrument)
     765            0 :         scan->instrument->nsearches++;
     766              : 
     767              :     /* Begin the scan by processing the root page */
     768         1219 :     so->curPageData = so->nPageData = 0;
     769         1219 :     scan->xs_hitup = NULL;
     770         1219 :     if (so->pageDataCxt)
     771            0 :         MemoryContextReset(so->pageDataCxt);
     772              : 
     773         1219 :     fakeItem.blkno = GIST_ROOT_BLKNO;
     774         1219 :     memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     775         1219 :     gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
     776              : 
     777              :     /*
     778              :      * While scanning a leaf page, ItemPointers of matching heap tuples will
     779              :      * be stored directly into tbm, so we don't need to deal with them here.
     780              :      */
     781              :     for (;;)
     782        31799 :     {
     783        33018 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     784              : 
     785        33018 :         if (!item)
     786         1219 :             break;
     787              : 
     788        31799 :         CHECK_FOR_INTERRUPTS();
     789              : 
     790        31799 :         gistScanPage(scan, item, item->distances, tbm, &ntids);
     791              : 
     792        31799 :         pfree(item);
     793              :     }
     794              : 
     795         1219 :     return ntids;
     796              : }
     797              : 
     798              : /*
     799              :  * Can we do index-only scans on the given index column?
     800              :  *
     801              :  * Opclasses that implement a fetch function support index-only scans.
     802              :  * Opclasses without compression functions also support index-only scans.
     803              :  * Included attributes always can be fetched for index-only scans.
     804              :  */
     805              : bool
     806         8921 : gistcanreturn(Relation index, int attno)
     807              : {
     808        17737 :     if (attno > IndexRelationGetNumberOfKeyAttributes(index) ||
     809        16913 :         OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
     810         8097 :         !OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
     811         6732 :         return true;
     812              :     else
     813         2189 :         return false;
     814              : }
        

Generated by: LCOV version 2.0-1