LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistget.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 79.3 % 358 284
Test Date: 2026-01-26 10:56:24 Functions: 87.5 % 8 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 65.1 % 192 125

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

Generated by: LCOV version 2.3.2-1