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

Generated by: LCOV version 2.0-1