LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistget.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 90.6 % 265 240
Test Date: 2026-07-14 02:15:45 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 78.7 % 164 129

             Branch data     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                 :         396 : gistkillitems(IndexScanDesc scan)
      40                 :             : {
      41                 :         396 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
      42                 :             :     Buffer      buffer;
      43                 :             :     Page        page;
      44                 :             :     OffsetNumber offnum;
      45                 :             :     ItemId      iid;
      46                 :             :     int         i;
      47                 :         396 :     bool        killedsomething = false;
      48                 :             : 
      49                 :             :     Assert(so->curBlkno != InvalidBlockNumber);
      50                 :             :     Assert(XLogRecPtrIsValid(so->curPageLSN));
      51                 :             :     Assert(so->killedItems != NULL);
      52                 :             : 
      53                 :         396 :     buffer = ReadBuffer(scan->indexRelation, so->curBlkno);
      54         [ -  + ]:         396 :     if (!BufferIsValid(buffer))
      55                 :           0 :         return;
      56                 :             : 
      57                 :         396 :     LockBuffer(buffer, GIST_SHARE);
      58                 :         396 :     gistcheckpage(scan->indexRelation, buffer);
      59                 :         396 :     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         [ +  + ]:         396 :     if (BufferGetLSNAtomic(buffer) != so->curPageLSN)
      67                 :         102 :         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         [ +  + ]:         604 :     for (i = 0; i < so->numKilled; i++)
      76                 :             :     {
      77         [ +  + ]:         310 :         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         [ -  + ]:         294 :             if (!BufferBeginSetHintBits(buffer))
      85                 :           0 :                 goto unlock;
      86                 :             :         }
      87                 :             : 
      88                 :         310 :         offnum = so->killedItems[i];
      89                 :         310 :         iid = PageGetItemId(page, offnum);
      90                 :         310 :         ItemIdMarkDead(iid);
      91                 :         310 :         killedsomething = true;
      92                 :             :     }
      93                 :             : 
      94         [ -  + ]:         294 :     if (killedsomething)
      95                 :             :     {
      96                 :         294 :         GistMarkPageHasGarbage(page);
      97                 :         294 :         BufferFinishSetHintBits(buffer, true, true);
      98                 :             :     }
      99                 :             : 
     100                 :           0 : unlock:
     101                 :         396 :     UnlockReleaseBuffer(buffer);
     102                 :             : 
     103                 :             :     /*
     104                 :             :      * Always reset the scan state, so we don't look for same items on other
     105                 :             :      * pages.
     106                 :             :      */
     107                 :         396 :     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                 :     1322374 : gistindex_keytest(IndexScanDesc scan,
     135                 :             :                   IndexTuple tuple,
     136                 :             :                   Page page,
     137                 :             :                   OffsetNumber offset,
     138                 :             :                   bool *recheck_p,
     139                 :             :                   bool *recheck_distances_p)
     140                 :             : {
     141                 :     1322374 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     142                 :     1322374 :     GISTSTATE  *giststate = so->giststate;
     143                 :     1322374 :     ScanKey     key = scan->keyData;
     144                 :     1322374 :     int         keySize = scan->numberOfKeys;
     145                 :             :     IndexOrderByDistance *distance_p;
     146                 :     1322374 :     Relation    r = scan->indexRelation;
     147                 :             : 
     148                 :     1322374 :     *recheck_p = false;
     149                 :     1322374 :     *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         [ -  + ]:     1322374 :     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         [ +  + ]:     2042365 :     while (keySize > 0)
     172                 :             :     {
     173                 :             :         Datum       datum;
     174                 :             :         bool        isNull;
     175                 :             : 
     176                 :     1275341 :         datum = index_getattr(tuple,
     177                 :     1275341 :                               key->sk_attno,
     178                 :             :                               giststate->leafTupdesc,
     179                 :             :                               &isNull);
     180                 :             : 
     181         [ +  + ]:     1275341 :         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                 :      555350 :                     return false;
     193                 :             :             }
     194                 :             :             else
     195                 :             :             {
     196                 :             :                 Assert(key->sk_flags & SK_SEARCHNOTNULL);
     197         [ +  + ]:          44 :                 if (isNull)
     198                 :           4 :                     return false;
     199                 :             :             }
     200                 :             :         }
     201         [ +  + ]:     1245409 :         else if (isNull)
     202                 :             :         {
     203                 :        2047 :             return false;
     204                 :             :         }
     205                 :             :         else
     206                 :             :         {
     207                 :             :             Datum       test;
     208                 :             :             bool        recheck;
     209                 :             :             GISTENTRY   de;
     210                 :             : 
     211                 :     1243362 :             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                 :     1243362 :             recheck = true;
     229                 :             : 
     230                 :     2486724 :             test = FunctionCall5Coll(&key->sk_func,
     231                 :             :                                      key->sk_collation,
     232                 :             :                                      PointerGetDatum(&de),
     233                 :             :                                      key->sk_argument,
     234                 :     1243362 :                                      UInt16GetDatum(key->sk_strategy),
     235                 :             :                                      ObjectIdGetDatum(key->sk_subtype),
     236                 :             :                                      PointerGetDatum(&recheck));
     237                 :             : 
     238         [ +  + ]:     1243362 :             if (!DatumGetBool(test))
     239                 :      525882 :                 return false;
     240                 :      717480 :             *recheck_p |= recheck;
     241                 :             :         }
     242                 :             : 
     243                 :      719991 :         key++;
     244                 :      719991 :         keySize--;
     245                 :             :     }
     246                 :             : 
     247                 :             :     /* OK, it passes --- now let's compute the distances */
     248                 :      767024 :     key = scan->orderByData;
     249                 :      767024 :     distance_p = so->distances;
     250                 :      767024 :     keySize = scan->numberOfOrderBys;
     251         [ +  + ]:      784549 :     while (keySize > 0)
     252                 :             :     {
     253                 :             :         Datum       datum;
     254                 :             :         bool        isNull;
     255                 :             : 
     256                 :       17525 :         datum = index_getattr(tuple,
     257                 :       17525 :                               key->sk_attno,
     258                 :             :                               giststate->leafTupdesc,
     259                 :             :                               &isNull);
     260                 :             : 
     261   [ +  -  +  + ]:       17525 :         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                 :       17457 :             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                 :       17457 :             recheck = false;
     294                 :       34914 :             dist = FunctionCall5Coll(&key->sk_func,
     295                 :             :                                      key->sk_collation,
     296                 :             :                                      PointerGetDatum(&de),
     297                 :             :                                      key->sk_argument,
     298                 :       17457 :                                      UInt16GetDatum(key->sk_strategy),
     299                 :             :                                      ObjectIdGetDatum(key->sk_subtype),
     300                 :             :                                      PointerGetDatum(&recheck));
     301                 :       17457 :             *recheck_distances_p |= recheck;
     302                 :       17457 :             distance_p->value = DatumGetFloat8(dist);
     303                 :       17457 :             distance_p->isnull = false;
     304                 :             :         }
     305                 :             : 
     306                 :       17525 :         key++;
     307                 :       17525 :         distance_p++;
     308                 :       17525 :         keySize--;
     309                 :             :     }
     310                 :             : 
     311                 :      767024 :     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                 :       45773 : gistScanPage(IndexScanDesc scan, GISTSearchItem *pageItem,
     338                 :             :              IndexOrderByDistance *myDistances, TIDBitmap *tbm, int64 *ntids)
     339                 :             : {
     340                 :       45773 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     341                 :       45773 :     GISTSTATE  *giststate = so->giststate;
     342                 :       45773 :     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                 :       45773 :     buffer = ReadBuffer(scan->indexRelation, pageItem->blkno);
     353                 :       45773 :     LockBuffer(buffer, GIST_SHARE);
     354                 :       45773 :     PredicateLockPage(r, BufferGetBlockNumber(buffer), scan->xs_snapshot);
     355                 :       45773 :     gistcheckpage(scan->indexRelation, buffer);
     356                 :       45773 :     page = BufferGetPage(buffer);
     357                 :       45773 :     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         [ +  + ]:       45773 :     if (XLogRecPtrIsValid(pageItem->data.parentlsn) &&
     366   [ +  -  -  + ]:       78806 :         (GistFollowRight(page) ||
     367                 :       39403 :          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         [ -  + ]:       45773 :     if (GistPageIsDeleted(page))
     401                 :             :     {
     402                 :           0 :         UnlockReleaseBuffer(buffer);
     403                 :           0 :         return;
     404                 :             :     }
     405                 :             : 
     406                 :       45773 :     so->nPageData = so->curPageData = 0;
     407                 :       45773 :     scan->xs_hitup = NULL;       /* might point into pageDataCxt */
     408         [ +  + ]:       45773 :     if (so->pageDataCxt)
     409                 :        4105 :         MemoryContextReset(so->pageDataCxt);
     410                 :             : 
     411                 :             :     /*
     412                 :             :      * Save the current page's block number for a possible gistkillitems()
     413                 :             :      * call later.  We also save its LSN, so that we know whether it is safe
     414                 :             :      * to apply the LP_DEAD hints to the page later.  This allows us to drop
     415                 :             :      * the pin for MVCC scans, which allows vacuum to avoid blocking.
     416                 :             :      */
     417                 :       45773 :     so->curBlkno = pageItem->blkno;
     418                 :       45773 :     so->curPageLSN = BufferGetLSNAtomic(buffer);
     419                 :             : 
     420                 :             :     /*
     421                 :             :      * check all tuples on page
     422                 :             :      */
     423                 :       45773 :     maxoff = PageGetMaxOffsetNumber(page);
     424         [ +  + ]:     1371066 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     425                 :             :     {
     426                 :     1325293 :         ItemId      iid = PageGetItemId(page, i);
     427                 :             :         IndexTuple  it;
     428                 :             :         bool        match;
     429                 :             :         bool        recheck;
     430                 :             :         bool        recheck_distances;
     431                 :             : 
     432                 :             :         /*
     433                 :             :          * If the scan specifies not to return killed tuples, then we treat a
     434                 :             :          * killed tuple as not passing the qual.
     435                 :             :          */
     436   [ +  -  +  + ]:     1325293 :         if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
     437                 :      558269 :             continue;
     438                 :             : 
     439                 :     1322374 :         it = (IndexTuple) PageGetItem(page, iid);
     440                 :             : 
     441                 :             :         /*
     442                 :             :          * Must call gistindex_keytest in tempCxt, and clean up any leftover
     443                 :             :          * junk afterward.
     444                 :             :          */
     445                 :     1322374 :         oldcxt = MemoryContextSwitchTo(so->giststate->tempCxt);
     446                 :             : 
     447                 :     1322374 :         match = gistindex_keytest(scan, it, page, i,
     448                 :             :                                   &recheck, &recheck_distances);
     449                 :             : 
     450                 :     1322374 :         MemoryContextSwitchTo(oldcxt);
     451                 :     1322374 :         MemoryContextReset(so->giststate->tempCxt);
     452                 :             : 
     453                 :             :         /* Ignore tuple if it doesn't match */
     454         [ +  + ]:     1322374 :         if (!match)
     455                 :      555350 :             continue;
     456                 :             : 
     457   [ +  +  +  + ]:      767024 :         if (tbm && GistPageIsLeaf(page))
     458                 :             :         {
     459                 :             :             /*
     460                 :             :              * getbitmap scan, so just push heap tuple TIDs into the bitmap
     461                 :             :              * without worrying about ordering
     462                 :             :              */
     463                 :      197554 :             tbm_add_tuples(tbm, &it->t_tid, 1, recheck);
     464                 :      197554 :             (*ntids)++;
     465                 :             :         }
     466   [ +  +  +  + ]:      569470 :         else if (scan->numberOfOrderBys == 0 && GistPageIsLeaf(page))
     467                 :             :         {
     468                 :             :             /*
     469                 :             :              * Non-ordered scan, so report tuples in so->pageData[]
     470                 :             :              */
     471                 :      512725 :             so->pageData[so->nPageData].heapPtr = it->t_tid;
     472                 :      512725 :             so->pageData[so->nPageData].recheck = recheck;
     473                 :      512725 :             so->pageData[so->nPageData].offnum = i;
     474                 :             : 
     475                 :             :             /*
     476                 :             :              * In an index-only scan, also fetch the data from the tuple.  The
     477                 :             :              * reconstructed tuples are stored in pageDataCxt.
     478                 :             :              */
     479         [ +  + ]:      512725 :             if (scan->xs_want_itup)
     480                 :             :             {
     481                 :      355645 :                 oldcxt = MemoryContextSwitchTo(so->pageDataCxt);
     482                 :      711290 :                 so->pageData[so->nPageData].recontup =
     483                 :      355645 :                     gistFetchTuple(giststate, r, it);
     484                 :      355645 :                 MemoryContextSwitchTo(oldcxt);
     485                 :             :             }
     486                 :      512725 :             so->nPageData++;
     487                 :             :         }
     488                 :             :         else
     489                 :             :         {
     490                 :             :             /*
     491                 :             :              * Must push item into search queue.  We get here for any lower
     492                 :             :              * index page, and also for heap tuples if doing an ordered
     493                 :             :              * search.
     494                 :             :              */
     495                 :             :             GISTSearchItem *item;
     496                 :       56745 :             int         nOrderBys = scan->numberOfOrderBys;
     497                 :             : 
     498                 :       56745 :             oldcxt = MemoryContextSwitchTo(so->queueCxt);
     499                 :             : 
     500                 :             :             /* Create new GISTSearchItem for this item */
     501                 :       56745 :             item = palloc(SizeOfGISTSearchItem(scan->numberOfOrderBys));
     502                 :             : 
     503         [ +  + ]:       56745 :             if (GistPageIsLeaf(page))
     504                 :             :             {
     505                 :             :                 /* Creating heap-tuple GISTSearchItem */
     506                 :       15359 :                 item->blkno = InvalidBlockNumber;
     507                 :       15359 :                 item->data.heap.heapPtr = it->t_tid;
     508                 :       15359 :                 item->data.heap.recheck = recheck;
     509                 :       15359 :                 item->data.heap.recheckDistances = recheck_distances;
     510                 :             : 
     511                 :             :                 /*
     512                 :             :                  * In an index-only scan, also fetch the data from the tuple.
     513                 :             :                  */
     514         [ +  + ]:       15359 :                 if (scan->xs_want_itup)
     515                 :        7235 :                     item->data.heap.recontup = gistFetchTuple(giststate, r, it);
     516                 :             :             }
     517                 :             :             else
     518                 :             :             {
     519                 :             :                 /* Creating index-page GISTSearchItem */
     520                 :       41386 :                 item->blkno = ItemPointerGetBlockNumber(&it->t_tid);
     521                 :             : 
     522                 :             :                 /*
     523                 :             :                  * LSN of current page is lsn of parent page for child. We
     524                 :             :                  * only have a shared lock, so we need to get the LSN
     525                 :             :                  * atomically.
     526                 :             :                  */
     527                 :       41386 :                 item->data.parentlsn = BufferGetLSNAtomic(buffer);
     528                 :             :             }
     529                 :             : 
     530                 :             :             /* Insert it into the queue using new distance data */
     531                 :       56745 :             memcpy(item->distances, so->distances,
     532                 :             :                    sizeof(item->distances[0]) * nOrderBys);
     533                 :             : 
     534                 :       56745 :             pairingheap_add(so->queue, &item->phNode);
     535                 :             : 
     536                 :       56745 :             MemoryContextSwitchTo(oldcxt);
     537                 :             :         }
     538                 :             :     }
     539                 :             : 
     540                 :       45773 :     UnlockReleaseBuffer(buffer);
     541                 :             : }
     542                 :             : 
     543                 :             : /*
     544                 :             :  * Extract next item (in order) from search queue
     545                 :             :  *
     546                 :             :  * Returns a GISTSearchItem or NULL.  Caller must pfree item when done with it.
     547                 :             :  */
     548                 :             : static GISTSearchItem *
     549                 :       46228 : getNextGISTSearchItem(GISTScanOpaque so)
     550                 :             : {
     551                 :             :     GISTSearchItem *item;
     552                 :             : 
     553         [ +  + ]:       46228 :     if (!pairingheap_is_empty(so->queue))
     554                 :             :     {
     555                 :       40166 :         item = (GISTSearchItem *) pairingheap_remove_first(so->queue);
     556                 :             :     }
     557                 :             :     else
     558                 :             :     {
     559                 :             :         /* Done when both heaps are empty */
     560                 :        6062 :         item = NULL;
     561                 :             :     }
     562                 :             : 
     563                 :             :     /* Return item; caller is responsible to pfree it */
     564                 :       46228 :     return item;
     565                 :             : }
     566                 :             : 
     567                 :             : /*
     568                 :             :  * Fetch next heap tuple in an ordered search
     569                 :             :  */
     570                 :             : static bool
     571                 :         791 : getNextNearest(IndexScanDesc scan)
     572                 :             : {
     573                 :         791 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     574                 :         791 :     bool        res = false;
     575                 :             : 
     576         [ +  + ]:         791 :     if (scan->xs_hitup)
     577                 :             :     {
     578                 :             :         /* free previously returned tuple */
     579                 :         518 :         pfree(scan->xs_hitup);
     580                 :         518 :         scan->xs_hitup = NULL;
     581                 :             :     }
     582                 :             : 
     583                 :             :     do
     584                 :             :     {
     585                 :         974 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     586                 :             : 
     587         [ +  + ]:         974 :         if (!item)
     588                 :          28 :             break;
     589                 :             : 
     590         [ +  + ]:         946 :         if (GISTSearchItemIsHeap(*item))
     591                 :             :         {
     592                 :             :             /* found a heap item at currently minimal distance */
     593                 :         763 :             scan->xs_heaptid = item->data.heap.heapPtr;
     594                 :         763 :             scan->xs_recheck = item->data.heap.recheck;
     595                 :             : 
     596                 :         763 :             index_store_float8_orderby_distances(scan, so->orderByTypes,
     597                 :         763 :                                                  item->distances,
     598                 :         763 :                                                  item->data.heap.recheckDistances);
     599                 :             : 
     600                 :             :             /* in an index-only scan, also return the reconstructed tuple. */
     601         [ +  + ]:         763 :             if (scan->xs_want_itup)
     602                 :         556 :                 scan->xs_hitup = item->data.heap.recontup;
     603                 :         763 :             res = true;
     604                 :             :         }
     605                 :             :         else
     606                 :             :         {
     607                 :             :             /* visit an index page, extract its items into queue */
     608         [ -  + ]:         183 :             CHECK_FOR_INTERRUPTS();
     609                 :             : 
     610                 :         183 :             gistScanPage(scan, item, item->distances, NULL, NULL);
     611                 :             :         }
     612                 :             : 
     613                 :         946 :         pfree(item);
     614         [ +  + ]:         946 :     } while (!res);
     615                 :             : 
     616                 :         791 :     return res;
     617                 :             : }
     618                 :             : 
     619                 :             : /*
     620                 :             :  * gistgettuple() -- Get the next tuple in the scan
     621                 :             :  */
     622                 :             : bool
     623                 :      518146 : gistgettuple(IndexScanDesc scan, ScanDirection dir)
     624                 :             : {
     625                 :      518146 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     626                 :             : 
     627         [ -  + ]:      518146 :     if (dir != ForwardScanDirection)
     628         [ #  # ]:           0 :         elog(ERROR, "GiST only supports forward scan direction");
     629                 :             : 
     630         [ -  + ]:      518146 :     if (!so->qual_ok)
     631                 :           0 :         return false;
     632                 :             : 
     633         [ +  + ]:      518146 :     if (so->firstCall)
     634                 :             :     {
     635                 :             :         /* Begin the scan by processing the root page */
     636                 :             :         GISTSearchItem fakeItem;
     637                 :             : 
     638   [ +  +  +  +  :        5147 :         pgstat_count_index_scan(scan->indexRelation);
                   +  + ]
     639         [ +  + ]:        5147 :         if (scan->instrument)
     640                 :           8 :             scan->instrument->nsearches++;
     641                 :             : 
     642                 :        5147 :         so->firstCall = false;
     643                 :        5147 :         so->curPageData = so->nPageData = 0;
     644                 :        5147 :         scan->xs_hitup = NULL;
     645         [ +  + ]:        5147 :         if (so->pageDataCxt)
     646                 :         466 :             MemoryContextReset(so->pageDataCxt);
     647                 :             : 
     648                 :        5147 :         fakeItem.blkno = GIST_ROOT_BLKNO;
     649                 :        5147 :         memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     650                 :        5147 :         gistScanPage(scan, &fakeItem, NULL, NULL, NULL);
     651                 :             :     }
     652                 :             : 
     653         [ +  + ]:      518146 :     if (scan->numberOfOrderBys > 0)
     654                 :             :     {
     655                 :             :         /* Must fetch tuples in strict distance order */
     656                 :         791 :         return getNextNearest(scan);
     657                 :             :     }
     658                 :             :     else
     659                 :             :     {
     660                 :             :         /* Fetch tuples index-page-at-a-time */
     661                 :             :         for (;;)
     662                 :             :         {
     663         [ +  + ]:      522830 :             if (so->curPageData < so->nPageData)
     664                 :             :             {
     665   [ +  +  +  - ]:      512544 :                 if (scan->kill_prior_tuple && so->curPageData > 0)
     666                 :             :                 {
     667                 :             : 
     668         [ +  + ]:         406 :                     if (so->killedItems == NULL)
     669                 :             :                     {
     670                 :             :                         MemoryContext oldCxt =
     671                 :         386 :                             MemoryContextSwitchTo(so->giststate->scanCxt);
     672                 :             : 
     673                 :         386 :                         so->killedItems =
     674                 :         386 :                             (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     675                 :             :                                                     * sizeof(OffsetNumber));
     676                 :             : 
     677                 :         386 :                         MemoryContextSwitchTo(oldCxt);
     678                 :             :                     }
     679         [ +  - ]:         406 :                     if (so->numKilled < MaxIndexTuplesPerPage)
     680                 :         406 :                         so->killedItems[so->numKilled++] =
     681                 :         406 :                             so->pageData[so->curPageData - 1].offnum;
     682                 :             :                 }
     683                 :             :                 /* continuing to return tuples from a leaf page */
     684                 :      512544 :                 scan->xs_heaptid = so->pageData[so->curPageData].heapPtr;
     685                 :      512544 :                 scan->xs_recheck = so->pageData[so->curPageData].recheck;
     686                 :             : 
     687                 :             :                 /* in an index-only scan, also return the reconstructed tuple */
     688         [ +  + ]:      512544 :                 if (scan->xs_want_itup)
     689                 :      355645 :                     scan->xs_hitup = so->pageData[so->curPageData].recontup;
     690                 :             : 
     691                 :      512544 :                 so->curPageData++;
     692                 :             : 
     693                 :      512544 :                 return true;
     694                 :             :             }
     695                 :             : 
     696                 :             :             /*
     697                 :             :              * Check the last returned tuple and add it to killedItems if
     698                 :             :              * necessary
     699                 :             :              */
     700         [ +  + ]:       10286 :             if (scan->kill_prior_tuple
     701         [ +  - ]:          20 :                 && so->curPageData > 0
     702         [ +  - ]:          20 :                 && so->curPageData == so->nPageData)
     703                 :             :             {
     704                 :             : 
     705         [ +  + ]:          20 :                 if (so->killedItems == NULL)
     706                 :             :                 {
     707                 :             :                     MemoryContext oldCxt =
     708                 :          19 :                         MemoryContextSwitchTo(so->giststate->scanCxt);
     709                 :             : 
     710                 :          19 :                     so->killedItems =
     711                 :          19 :                         (OffsetNumber *) palloc(MaxIndexTuplesPerPage
     712                 :             :                                                 * sizeof(OffsetNumber));
     713                 :             : 
     714                 :          19 :                     MemoryContextSwitchTo(oldCxt);
     715                 :             :                 }
     716         [ +  - ]:          20 :                 if (so->numKilled < MaxIndexTuplesPerPage)
     717                 :          20 :                     so->killedItems[so->numKilled++] =
     718                 :          20 :                         so->pageData[so->curPageData - 1].offnum;
     719                 :             :             }
     720                 :             :             /* find and process the next index page */
     721                 :             :             do
     722                 :             :             {
     723                 :             :                 GISTSearchItem *item;
     724                 :             : 
     725   [ +  -  +  + ]:       12367 :                 if ((so->curBlkno != InvalidBlockNumber) && (so->numKilled > 0))
     726                 :         396 :                     gistkillitems(scan);
     727                 :             : 
     728                 :       12367 :                 item = getNextGISTSearchItem(so);
     729                 :             : 
     730         [ +  + ]:       12367 :                 if (!item)
     731                 :        4811 :                     return false;
     732                 :             : 
     733         [ -  + ]:        7556 :                 CHECK_FOR_INTERRUPTS();
     734                 :             : 
     735                 :             :                 /*
     736                 :             :                  * While scanning a leaf page, ItemPointers of matching heap
     737                 :             :                  * tuples are stored in so->pageData.  If there are any on
     738                 :             :                  * this page, we fall out of the inner "do" and loop around to
     739                 :             :                  * return them.
     740                 :             :                  */
     741                 :        7556 :                 gistScanPage(scan, item, item->distances, NULL, NULL);
     742                 :             : 
     743                 :        7556 :                 pfree(item);
     744         [ +  + ]:        7556 :             } while (so->nPageData == 0);
     745                 :             :         }
     746                 :             :     }
     747                 :             : }
     748                 :             : 
     749                 :             : /*
     750                 :             :  * gistgetbitmap() -- Get a bitmap of all heap tuple locations
     751                 :             :  */
     752                 :             : int64
     753                 :        1223 : gistgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
     754                 :             : {
     755                 :        1223 :     GISTScanOpaque so = (GISTScanOpaque) scan->opaque;
     756                 :        1223 :     int64       ntids = 0;
     757                 :             :     GISTSearchItem fakeItem;
     758                 :             : 
     759         [ -  + ]:        1223 :     if (!so->qual_ok)
     760                 :           0 :         return 0;
     761                 :             : 
     762   [ +  +  +  -  :        1223 :     pgstat_count_index_scan(scan->indexRelation);
                   +  - ]
     763         [ -  + ]:        1223 :     if (scan->instrument)
     764                 :           0 :         scan->instrument->nsearches++;
     765                 :             : 
     766                 :             :     /* Begin the scan by processing the root page */
     767                 :        1223 :     so->curPageData = so->nPageData = 0;
     768                 :        1223 :     scan->xs_hitup = NULL;
     769         [ -  + ]:        1223 :     if (so->pageDataCxt)
     770                 :           0 :         MemoryContextReset(so->pageDataCxt);
     771                 :             : 
     772                 :        1223 :     fakeItem.blkno = GIST_ROOT_BLKNO;
     773                 :        1223 :     memset(&fakeItem.data.parentlsn, 0, sizeof(GistNSN));
     774                 :        1223 :     gistScanPage(scan, &fakeItem, NULL, tbm, &ntids);
     775                 :             : 
     776                 :             :     /*
     777                 :             :      * While scanning a leaf page, ItemPointers of matching heap tuples will
     778                 :             :      * be stored directly into tbm, so we don't need to deal with them here.
     779                 :             :      */
     780                 :             :     for (;;)
     781                 :       31664 :     {
     782                 :       32887 :         GISTSearchItem *item = getNextGISTSearchItem(so);
     783                 :             : 
     784         [ +  + ]:       32887 :         if (!item)
     785                 :        1223 :             break;
     786                 :             : 
     787         [ -  + ]:       31664 :         CHECK_FOR_INTERRUPTS();
     788                 :             : 
     789                 :       31664 :         gistScanPage(scan, item, item->distances, tbm, &ntids);
     790                 :             : 
     791                 :       31664 :         pfree(item);
     792                 :             :     }
     793                 :             : 
     794                 :        1223 :     return ntids;
     795                 :             : }
     796                 :             : 
     797                 :             : /*
     798                 :             :  * Can we do index-only scans on the given index column?
     799                 :             :  *
     800                 :             :  * Opclasses that implement a fetch function support index-only scans.
     801                 :             :  * Opclasses without compression functions also support index-only scans.
     802                 :             :  * Included attributes always can be fetched for index-only scans.
     803                 :             :  */
     804                 :             : bool
     805                 :        9002 : gistcanreturn(Relation index, int attno)
     806                 :             : {
     807   [ +  +  +  + ]:       17899 :     if (attno > IndexRelationGetNumberOfKeyAttributes(index) ||
     808         [ +  + ]:       17061 :         OidIsValid(index_getprocid(index, attno, GIST_FETCH_PROC)) ||
     809                 :        8164 :         !OidIsValid(index_getprocid(index, attno, GIST_COMPRESS_PROC)))
     810                 :        6790 :         return true;
     811                 :             :     else
     812                 :        2212 :         return false;
     813                 :             : }
        

Generated by: LCOV version 2.0-1