LCOV - differential code coverage report
Current view: top level - src/backend/access/heap - heapam_indexscan.c (source / functions) Coverage Total Hit UNC UBC GIC GNC CBC DCB
Current: f76c13edadc2b319036e0d238703ed56bb23f934 vs 9e17d25e79d4756be08b4a5521b4b58450217137 Lines: 98.2 % 164 161 1 2 1 88 72 22
Current Date: 2026-09-20 14:13:17 +0900 Functions: 100.0 % 12 12 11 1 4
Baseline: lcov-20260920-baseline Branches: 80.2 % 126 101 15 10 55 46
Baseline Date: 2026-09-20 14:13:13 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 98.9 % 89 88 1 88
(30,360] days: 97.3 % 75 73 2 1 72
Function coverage date bins:
(1,7] days: 100.0 % 11 11 11
(30,360] days: 100.0 % 1 1 1
Branch coverage date bins:
(1,7] days: 78.6 % 70 55 15 55
(30,360] days: 82.1 % 56 46 10 46

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * heapam_indexscan.c
                                  4                 :                :  *    heap table plain index scan and index-only scan code
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
                                  7                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :                :  *
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *    src/backend/access/heap/heapam_indexscan.c
                                 12                 :                :  *
                                 13                 :                :  *-------------------------------------------------------------------------
                                 14                 :                :  */
                                 15                 :                : #include "postgres.h"
                                 16                 :                : 
                                 17                 :                : #include "access/amapi.h"
                                 18                 :                : #include "access/heapam.h"
                                 19                 :                : #include "access/relscan.h"
                                 20                 :                : #include "access/tableam_indexscan.h"
                                 21                 :                : #include "access/visibilitymap.h"
                                 22                 :                : #include "pgstat.h"
                                 23                 :                : #include "storage/predicate.h"
                                 24                 :                : 
                                 25                 :                : 
                                 26                 :                : static bool heapam_index_plain_tuple_getnext_slot(IndexScanDesc scan,
                                 27                 :                :                                                   ScanDirection direction,
                                 28                 :                :                                                   TupleTableSlot *slot);
                                 29                 :                : static bool heapam_index_only_tuple_getnext_slot(IndexScanDesc scan,
                                 30                 :                :                                                  ScanDirection direction,
                                 31                 :                :                                                  TupleTableSlot *slot);
                                 32                 :                : static pg_always_inline bool heapam_index_getnext_slot(IndexScanDesc scan,
                                 33                 :                :                                                        ScanDirection direction,
                                 34                 :                :                                                        TupleTableSlot *slot,
                                 35                 :                :                                                        bool index_only);
                                 36                 :                : static pg_always_inline bool heapam_index_heap_fetch(IndexScanDesc scan,
                                 37                 :                :                                                      IndexScanHeapData *hscan,
                                 38                 :                :                                                      TupleTableSlot *slot,
                                 39                 :                :                                                      bool index_only);
                                 40                 :                : static pg_noinline bool heapam_index_only_heap_fetch(IndexScanDesc scan);
                                 41                 :                : static pg_noinline void heapam_index_kill_item(IndexScanDesc scan);
                                 42                 :                : static inline bool heapam_index_visited_pages_exceeded(IndexScanDesc scan);
                                 43                 :                : 
                                 44                 :                : /*
                                 45                 :                :  * TID-based lookup used by constraint enforcement code (e.g., unique index
                                 46                 :                :  * enforcement).
                                 47                 :                :  *
                                 48                 :                :  * This isn't actually used by index scans, but this is as good a place for it
                                 49                 :                :  * as anywhere else.
                                 50                 :                :  */
                                 51                 :                : bool
    5 pg@bowt.ie                 52                 :GNC     7610081 : heapam_fetch_tid(Relation rel, ItemPointer tid, Snapshot snapshot,
                                 53                 :                :                  bool *all_dead)
                                 54                 :                : {
                                 55                 :                :     HeapTupleData heapTuple;
                                 56                 :                :     Buffer      buf;
                                 57                 :                :     bool        found;
                                 58                 :                : 
                                 59                 :        7610081 :     buf = ReadBuffer(rel, ItemPointerGetBlockNumber(tid));
                                 60                 :        7610081 :     LockBuffer(buf, BUFFER_LOCK_SHARE);
                                 61                 :        7610081 :     found = heap_hot_search_buffer(tid, rel, buf, snapshot, &heapTuple,
                                 62                 :                :                                    all_dead, true);
                                 63                 :        7610081 :     UnlockReleaseBuffer(buf);
                                 64                 :                : 
                                 65                 :        7610081 :     return found;
                                 66                 :                : }
                                 67                 :                : 
                                 68                 :                : /* ------------------------------------------------------------------------
                                 69                 :                :  * Index Scan Callbacks for heap AM
                                 70                 :                :  * ------------------------------------------------------------------------
                                 71                 :                :  */
                                 72                 :                : 
                                 73                 :                : void
                                 74                 :        8664744 : heapam_index_scan_begin(IndexScanDesc scan, uint32 flags)
                                 75                 :                : {
                                 76                 :        8664744 :     IndexScanHeapData *hscan = palloc0_object(IndexScanHeapData);
                                 77                 :                : 
  169 pg@bowt.ie                 78                 :CBC     8664744 :     hscan->xs_cbuf = InvalidBuffer;
                                 79                 :        8664744 :     hscan->xs_blk = InvalidBlockNumber;
                                 80                 :        8664744 :     hscan->xs_vmbuffer = InvalidBuffer;
                                 81                 :                : 
                                 82                 :                :     /* Remember if scan is read-only */
    5 pg@bowt.ie                 83                 :GNC     8664744 :     hscan->xs_readonly = (flags & SO_HINT_REL_READ_ONLY) != 0;
                                 84                 :                : 
                                 85                 :                :     /* Resolve which xs_getnext_slot implementation to use for this scan */
                                 86         [ +  + ]:        8664744 :     if (scan->xs_want_itup)
                                 87                 :          90753 :         scan->xs_getnext_slot = heapam_index_only_tuple_getnext_slot;
                                 88                 :                :     else
                                 89                 :        8573991 :         scan->xs_getnext_slot = heapam_index_plain_tuple_getnext_slot;
                                 90                 :                : 
                                 91                 :                :     /* Expose heapam's private scan state through the scan's opaque pointer */
                                 92                 :        8664744 :     scan->xs_table_opaque = hscan;
  169 pg@bowt.ie                 93                 :GIC     8664744 : }
                                 94                 :                : 
                                 95                 :                : void
    5 pg@bowt.ie                 96                 :GNC     9136816 : heapam_index_scan_reset(IndexScanDesc scan)
                                 97                 :                : {
                                 98                 :        9136816 :     IndexScanHeapData *hscan = (IndexScanHeapData *) scan->xs_table_opaque;
                                 99                 :                : 
                                100                 :                :     /* Heap fetches from the last rescan don't count towards this limit */
                                101                 :        9136816 :     hscan->xs_blkswitch_count = 0;
                                102                 :                : 
                                103                 :                :     /*
                                104                 :                :      * Deliberately avoid dropping pins now held in xs_cbuf and xs_vmbuffer.
                                105                 :                :      * This saves cycles during certain tight nested loop joins (it can avoid
                                106                 :                :      * repeated pinning and unpinning of the same buffer across rescans).
                                107                 :                :      */
  169 pg@bowt.ie                108                 :CBC     9136816 : }
                                109                 :                : 
                                110                 :                : void
    5 pg@bowt.ie                111                 :GNC     8663538 : heapam_index_scan_end(IndexScanDesc scan)
                                112                 :                : {
                                113                 :        8663538 :     IndexScanHeapData *hscan = (IndexScanHeapData *) scan->xs_table_opaque;
                                114                 :                : 
                                115                 :                :     /* drop pin if there's a pinned heap page */
  169 pg@bowt.ie                116         [ +  + ]:CBC     8663538 :     if (BufferIsValid(hscan->xs_cbuf))
                                117                 :        5934696 :         ReleaseBuffer(hscan->xs_cbuf);
                                118                 :                : 
                                119                 :                :     /* drop pin if there's a pinned visibility map page */
                                120         [ +  + ]:        8663538 :     if (BufferIsValid(hscan->xs_vmbuffer))
                                121                 :         139059 :         ReleaseBuffer(hscan->xs_vmbuffer);
                                122                 :                : 
                                123                 :        8663538 :     pfree(hscan);
                                124                 :        8663538 : }
                                125                 :                : 
                                126                 :                : /*
                                127                 :                :  *  heap_hot_search_buffer  - search HOT chain for tuple satisfying snapshot
                                128                 :                :  *
                                129                 :                :  * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
                                130                 :                :  * of a HOT chain), and buffer is the buffer holding this tuple.  We search
                                131                 :                :  * for the first chain member satisfying the given snapshot.  If one is
                                132                 :                :  * found, we update *tid to reference that tuple's offset number, and
                                133                 :                :  * return true.  If no match, return false without modifying *tid.
                                134                 :                :  *
                                135                 :                :  * heapTuple is a caller-supplied buffer.  When a match is found, we return
                                136                 :                :  * the tuple here, in addition to updating *tid.  If no match is found, the
                                137                 :                :  * contents of this buffer on return are undefined.
                                138                 :                :  *
                                139                 :                :  * If all_dead is not NULL, we check non-visible tuples to see if they are
                                140                 :                :  * globally dead; *all_dead is set true if all members of the HOT chain
                                141                 :                :  * are vacuumable, false if not.
                                142                 :                :  *
                                143                 :                :  * Unlike heap_fetch, the caller must already have pin and (at least) share
                                144                 :                :  * lock on the buffer; it is still pinned/locked at exit.
                                145                 :                :  */
                                146                 :                : bool
                                147                 :       26636961 : heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
                                148                 :                :                        Snapshot snapshot, HeapTuple heapTuple,
                                149                 :                :                        bool *all_dead, bool first_call)
                                150                 :                : {
                                151                 :       26636961 :     Page        page = BufferGetPage(buffer);
                                152                 :       26636961 :     TransactionId prev_xmax = InvalidTransactionId;
                                153                 :                :     BlockNumber blkno;
                                154                 :                :     OffsetNumber offnum;
                                155                 :                :     bool        at_chain_start;
                                156                 :                :     bool        valid;
                                157                 :                :     bool        skip;
                                158                 :       26636961 :     GlobalVisState *vistest = NULL;
                                159                 :                : 
                                160                 :                :     /* If this is not the first call, previous call returned a (live!) tuple */
                                161         [ +  + ]:       26636961 :     if (all_dead)
                                162                 :       22671415 :         *all_dead = first_call;
                                163                 :                : 
                                164                 :       26636961 :     blkno = ItemPointerGetBlockNumber(tid);
                                165                 :       26636961 :     offnum = ItemPointerGetOffsetNumber(tid);
                                166                 :       26636961 :     at_chain_start = first_call;
                                167                 :       26636961 :     skip = !first_call;
                                168                 :                : 
                                169                 :                :     /* XXX: we should assert that a snapshot is pushed or registered */
                                170         [ -  + ]:       26636961 :     Assert(TransactionIdIsValid(RecentXmin));
                                171         [ +  - ]:       26636961 :     Assert(BufferGetBlockNumber(buffer) == blkno);
                                172                 :                : 
                                173                 :                :     /* Scan through possible multiple members of HOT-chain */
                                174                 :                :     for (;;)
                                175                 :        1811762 :     {
                                176                 :                :         ItemId      lp;
                                177                 :                : 
                                178                 :                :         /* check for bogus TID */
                                179   [ +  -  +  - ]:       28448723 :         if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
                                180                 :                :             break;
                                181                 :                : 
                                182                 :       28448723 :         lp = PageGetItemId(page, offnum);
                                183                 :                : 
                                184                 :                :         /* check for unused, dead, or redirected items */
                                185         [ +  + ]:       28448723 :         if (!ItemIdIsNormal(lp))
                                186                 :                :         {
                                187                 :                :             /* We should only see a redirect at start of chain */
                                188   [ +  +  +  - ]:         831157 :             if (ItemIdIsRedirected(lp) && at_chain_start)
                                189                 :                :             {
                                190                 :                :                 /* Follow the redirect */
                                191                 :         468498 :                 offnum = ItemIdGetRedirect(lp);
                                192                 :         468498 :                 at_chain_start = false;
                                193                 :         468498 :                 continue;
                                194                 :                :             }
                                195                 :                :             /* else must be end of chain */
                                196                 :         362659 :             break;
                                197                 :                :         }
                                198                 :                : 
                                199                 :                :         /*
                                200                 :                :          * Update heapTuple to point to the element of the HOT chain we're
                                201                 :                :          * currently investigating. Having t_self set correctly is important
                                202                 :                :          * because the SSI checks and the *Satisfies routine for historical
                                203                 :                :          * MVCC snapshots need the correct tid to decide about the visibility.
                                204                 :                :          */
                                205                 :       27617566 :         heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
                                206                 :       27617566 :         heapTuple->t_len = ItemIdGetLength(lp);
                                207                 :       27617566 :         heapTuple->t_tableOid = RelationGetRelid(relation);
                                208                 :       27617566 :         ItemPointerSet(&heapTuple->t_self, blkno, offnum);
                                209                 :                : 
                                210                 :                :         /*
                                211                 :                :          * Shouldn't see a HEAP_ONLY tuple at chain start.
                                212                 :                :          */
                                213   [ +  +  -  + ]:       27617566 :         if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
  169 pg@bowt.ie                214                 :UBC           0 :             break;
                                215                 :                : 
                                216                 :                :         /*
                                217                 :                :          * The xmin should match the previous xmax value, else chain is
                                218                 :                :          * broken.
                                219                 :                :          */
  169 pg@bowt.ie                220   [ +  +  -  + ]:CBC    28960830 :         if (TransactionIdIsValid(prev_xmax) &&
                                221                 :        1343264 :             !TransactionIdEquals(prev_xmax,
                                222                 :                :                                  HeapTupleHeaderGetXmin(heapTuple->t_data)))
  169 pg@bowt.ie                223                 :UBC           0 :             break;
                                224                 :                : 
                                225                 :                :         /*
                                226                 :                :          * When first_call is true (and thus, skip is initially false) we'll
                                227                 :                :          * return the first tuple we find.  But on later passes, heapTuple
                                228                 :                :          * will initially be pointing to the tuple we returned last time.
                                229                 :                :          * Returning it again would be incorrect (and would loop forever), so
                                230                 :                :          * we skip it and return the next match we find.
                                231                 :                :          */
  169 pg@bowt.ie                232         [ +  + ]:CBC    27617566 :         if (!skip)
                                233                 :                :         {
                                234                 :                :             /* If it's visible per the snapshot, we must return it */
                                235                 :       27515650 :             valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
                                236                 :       27515650 :             HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
                                237                 :                :                                                 buffer, snapshot);
                                238                 :                : 
                                239         [ +  + ]:       27515645 :             if (valid)
                                240                 :                :             {
                                241                 :       17964082 :                 ItemPointerSetOffsetNumber(tid, offnum);
                                242                 :       17964082 :                 PredicateLockTID(relation, &heapTuple->t_self, snapshot,
                                243                 :       17964082 :                                  HeapTupleHeaderGetXmin(heapTuple->t_data));
                                244         [ +  + ]:       17964082 :                 if (all_dead)
                                245                 :       14346856 :                     *all_dead = false;
                                246                 :       17964082 :                 return true;
                                247                 :                :             }
                                248                 :                :         }
                                249                 :        9653479 :         skip = false;
                                250                 :                : 
                                251                 :                :         /*
                                252                 :                :          * If we can't see it, maybe no one else can either.  At caller
                                253                 :                :          * request, check whether all chain members are dead to all
                                254                 :                :          * transactions.
                                255                 :                :          *
                                256                 :                :          * Note: if you change the criterion here for what is "dead", fix the
                                257                 :                :          * planner's get_actual_variable_range() function to match.
                                258                 :                :          */
                                259   [ +  +  +  + ]:        9653479 :         if (all_dead && *all_dead)
                                260                 :                :         {
                                261         [ +  + ]:        8605023 :             if (!vistest)
                                262                 :        8443210 :                 vistest = GlobalVisTestFor(relation);
                                263                 :                : 
                                264         [ +  + ]:        8605023 :             if (!HeapTupleIsSurelyDead(heapTuple, vistest))
                                265                 :        8136507 :                 *all_dead = false;
                                266                 :                :         }
                                267                 :                : 
                                268                 :                :         /*
                                269                 :                :          * Check to see if HOT chain continues past this tuple; if so fetch
                                270                 :                :          * the next offnum and loop around.
                                271                 :                :          */
                                272         [ +  + ]:        9653479 :         if (HeapTupleIsHotUpdated(heapTuple))
                                273                 :                :         {
                                274         [ -  + ]:        1343264 :             Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
                                275                 :                :                    blkno);
                                276                 :        1343264 :             offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
                                277                 :        1343264 :             at_chain_start = false;
                                278                 :        1343264 :             prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
                                279                 :                :         }
                                280                 :                :         else
                                281                 :        8310215 :             break;              /* end of chain */
                                282                 :                : 
                                283                 :                :     }
                                284                 :                : 
                                285                 :        8672874 :     return false;
                                286                 :                : }
                                287                 :                : 
                                288                 :                : /* xs_getnext_slot callback: amgettuple, plain index scan */
                                289                 :                : static bool
    5 pg@bowt.ie                290                 :GNC    18127223 : heapam_index_plain_tuple_getnext_slot(IndexScanDesc scan,
                                291                 :                :                                       ScanDirection direction,
                                292                 :                :                                       TupleTableSlot *slot)
                                293                 :                : {
                                294         [ -  + ]:       18127223 :     Assert(!scan->xs_want_itup);
                                295         [ -  + ]:       18127223 :     Assert(scan->indexRelation->rd_indam->amgettuple != NULL);
                                296                 :                : 
                                297                 :       18127223 :     return heapam_index_getnext_slot(scan, direction, slot, false);
                                298                 :                : }
                                299                 :                : 
                                300                 :                : /* xs_getnext_slot callback: amgettuple, index-only scan */
                                301                 :                : static bool
                                302                 :        3950887 : heapam_index_only_tuple_getnext_slot(IndexScanDesc scan,
                                303                 :                :                                      ScanDirection direction,
                                304                 :                :                                      TupleTableSlot *slot)
                                305                 :                : {
                                306         [ -  + ]:        3950887 :     Assert(scan->xs_want_itup);
                                307         [ -  + ]:        3950887 :     Assert(scan->indexRelation->rd_indam->amgettuple != NULL);
                                308                 :                : 
                                309                 :        3950887 :     return heapam_index_getnext_slot(scan, direction, slot, true);
                                310                 :                : }
                                311                 :                : 
                                312                 :                : /*
                                313                 :                :  * Common implementation for both heapam_index_*_getnext_slot variants.
                                314                 :                :  *
                                315                 :                :  * The result is true if a tuple satisfying the scan keys and the snapshot was
                                316                 :                :  * found, false otherwise.  This is per the table_index_getnext_slot
                                317                 :                :  * interface.
                                318                 :                :  *
                                319                 :                :  * The index_only parameter is a compile-time constant at each call site,
                                320                 :                :  * allowing the compiler to specialize the code for each variant.
                                321                 :                :  */
                                322                 :                : static pg_always_inline bool
                                323                 :       22078110 : heapam_index_getnext_slot(IndexScanDesc scan, ScanDirection direction,
                                324                 :                :                           TupleTableSlot *slot, bool index_only)
                                325                 :                : {
                                326         [ -  + ]:       22078110 :     Assert(TransactionIdIsValid(RecentXmin));
                                327   [ +  +  +  - ]:       22078110 :     Assert(index_only || scan->xs_visited_pages_limit == 0);
                                328                 :                : 
                                329                 :                :     for (;;)
                                330                 :         715583 :     {
                                331                 :                :         IndexScanHeapData *hscan;
                                332                 :                :         bool        all_visible;
                                333                 :                : 
                                334                 :                :         /*
                                335                 :                :          * Get the next TID from the index, unless we're still working through
                                336                 :                :          * a HOT chain (index-only scans never do that, and plain index scans
                                337                 :                :          * only do it with a non-MVCC snapshot)
                                338                 :                :          */
                                339   [ +  +  -  + ]:       22793693 :         Assert(!index_only || !scan->xs_heap_continue);
                                340   [ +  +  +  + ]:       22793693 :         if (index_only || likely(!scan->xs_heap_continue))
                                341                 :                :         {
                                342         [ +  + ]:       22691777 :             if (!tableam_index_getnext_tid(scan, direction))
                                343                 :        4359296 :                 return false;
                                344                 :                :         }
                                345                 :                : 
                                346                 :                :         /* The scan's next TID was set in scan->xs_heaptid for us */
                                347         [ -  + ]:       18434397 :         Assert(ItemPointerIsValid(&scan->xs_heaptid));
                                348                 :                : 
                                349                 :       18434397 :         hscan = (IndexScanHeapData *) scan->xs_table_opaque;
                                350                 :                : 
                                351         [ +  + ]:       18434397 :         if (!index_only)
                                352                 :                :         {
                                353                 :                :             /* Plain index scan */
                                354         [ +  + ]:       14546657 :             if (!heapam_index_heap_fetch(scan, hscan, slot, false))
                                355                 :         648568 :                 continue;       /* no visible tuple, try next index entry */
                                356                 :                :         }
                                357                 :                :         else
                                358                 :                :         {
                                359                 :                :             /*
                                360                 :                :              * Note: VM_ALL_VISIBLE does not lock the visibility map buffer,
                                361                 :                :              * so the result could be slightly stale.  See the comments above
                                362                 :                :              * visibilitymap_get_status for why this is okay.
                                363                 :                :              */
                                364                 :        3887740 :             all_visible = VM_ALL_VISIBLE(scan->heapRelation,
                                365                 :                :                                          ItemPointerGetBlockNumber(&scan->xs_heaptid),
                                366                 :                :                                          &hscan->xs_vmbuffer);
                                367                 :                : 
                                368                 :                :             /* Page isn't all-visible, so verify visibility with a heap fetch */
                                369         [ +  + ]:        3887740 :             if (unlikely(!all_visible))
                                370                 :                :             {
                                371         [ +  + ]:         515173 :                 if (!heapam_index_only_heap_fetch(scan))
                                372                 :                :                 {
                                373                 :                :                     /* No visible tuple */
                                374         [ -  + ]:          67015 :                     if (heapam_index_visited_pages_exceeded(scan))
    5 pg@bowt.ie                375                 :UNC           0 :                         return false;   /* give up */
                                376                 :                : 
    5 pg@bowt.ie                377                 :GNC       67015 :                     continue;   /* try next index entry */
                                378                 :                :                 }
                                379                 :                :             }
                                380                 :                :             else
                                381                 :                :             {
                                382                 :                :                 /*
                                383                 :                :                  * Index-only scan with all-visible item.
                                384                 :                :                  *
                                385                 :                :                  * We won't access the heap, so we'll need to take a predicate
                                386                 :                :                  * lock explicitly, as if we had.  For now we do that at page
                                387                 :                :                  * level.
                                388                 :                :                  */
                                389                 :        3372567 :                 PredicateLockPage(scan->heapRelation,
                                390                 :        3372567 :                                   ItemPointerGetBlockNumber(&scan->xs_heaptid),
                                391                 :                :                                   scan->xs_snapshot);
                                392                 :                :             }
                                393                 :                : 
                                394                 :                :             /*
                                395                 :                :              * Fill slot with data returned by the index AM (during plain
                                396                 :                :              * scans heapam_index_heap_fetch does this for us instead)
                                397                 :                :              */
                                398                 :        3820725 :             tableam_index_fill_ios_slot(scan, slot);
                                399                 :                :         }
                                400                 :                : 
                                401                 :       17718809 :         return true;
                                402                 :                :     }
                                403                 :                : 
                                404                 :                :     pg_unreachable();
                                405                 :                : 
                                406                 :                :     return false;
                                407                 :                : }
                                408                 :                : 
                                409                 :                : /*
                                410                 :                :  * Get the scan's next heap tuple.
                                411                 :                :  *
                                412                 :                :  * Returns true if a visible heap tuple associated with the index TID most
                                413                 :                :  * recently fetched by our caller in scan->xs_heaptid was found, false if no
                                414                 :                :  * more matching tuples exist.  (There can be more than one matching tuple
                                415                 :                :  * because of HOT chains, although when using an MVCC snapshot it should be
                                416                 :                :  * impossible for more than one such tuple to exist.)
                                417                 :                :  *
                                418                 :                :  * Plain index scans have us store the tuple in their slot, and its buffer
                                419                 :                :  * stays pinned until a later call here (or heapam_index_scan_end) releases
                                420                 :                :  * it.  Index-only scans just need us to verify tuple visibility, so they pass
                                421                 :                :  * a NULL slot.
                                422                 :                :  *
                                423                 :                :  * When the TID's whole HOT chain turns out to be dead, we arrange for the
                                424                 :                :  * index AM to kill its entry for the TID before returning false.
                                425                 :                :  */
                                426                 :                : static pg_always_inline bool
                                427                 :       15061830 : heapam_index_heap_fetch(IndexScanDesc scan, IndexScanHeapData *hscan,
                                428                 :                :                         TupleTableSlot *slot, bool index_only)
                                429                 :                : {
                                430                 :       15061830 :     Relation    rel = scan->heapRelation;
                                431                 :       15061830 :     ItemPointer tid = &scan->xs_heaptid;
                                432                 :       15061830 :     Snapshot    snapshot = scan->xs_snapshot;
                                433                 :                :     HeapTupleData tupdata;
                                434                 :                :     HeapTuple   heapTuple;
                                435                 :                :     bool        got_heap_tuple;
                                436                 :                :     bool        all_dead;
                                437                 :                : 
                                438         [ +  + ]:       15061830 :     if (!index_only)
                                439                 :                :     {
                                440                 :                :         /* Plain index scans have us store fetched tuple in their slot */
                                441                 :       14546657 :         BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
                                442                 :                : 
                                443         [ -  + ]:       14546657 :         Assert(TTS_IS_BUFFERTUPLE(slot));
                                444                 :       14546657 :         heapTuple = &bslot->base.tupdata;
                                445                 :                :     }
                                446                 :                :     else
                                447                 :                :     {
                                448                 :                :         /* Index-only scans only need to verify tuple visibility */
                                449         [ -  + ]:         515173 :         pg_assume(slot == NULL);
                                450                 :         515173 :         heapTuple = &tupdata;
                                451                 :                : 
                                452         [ +  + ]:         515173 :         if (scan->instrument)
                                453                 :            330 :             scan->instrument->ntabletuplefetches++;
                                454                 :                :     }
                                455                 :                : 
                                456                 :                :     /* We can skip the buffer-switching logic if we're on the same page. */
  169 pg@bowt.ie                457         [ +  + ]:CBC    15061830 :     if (hscan->xs_blk != ItemPointerGetBlockNumber(tid))
                                458                 :                :     {
    5 pg@bowt.ie                459         [ -  + ]:GNC     8039382 :         Assert(!scan->xs_heap_continue);
                                460                 :                : 
                                461                 :                :         /* Remember this buffer's block number for next time */
  169 pg@bowt.ie                462                 :CBC     8039382 :         hscan->xs_blk = ItemPointerGetBlockNumber(tid);
                                463                 :                : 
                                464                 :                :         /* We're switching to a new heap block, so count it */
    5 pg@bowt.ie                465                 :GNC     8039382 :         hscan->xs_blkswitch_count++;
                                466                 :                : 
  169 pg@bowt.ie                467         [ +  + ]:CBC     8039382 :         if (BufferIsValid(hscan->xs_cbuf))
                                468                 :        2103529 :             ReleaseBuffer(hscan->xs_cbuf);
                                469                 :                : 
    5 pg@bowt.ie                470                 :GNC     8039382 :         hscan->xs_cbuf = ReadBuffer(rel, hscan->xs_blk);
                                471                 :                : 
                                472                 :                :         /*
                                473                 :                :          * Prune page when it is pinned for the first time
                                474                 :                :          */
                                475                 :        8039379 :         heap_page_prune_opt(rel, hscan->xs_cbuf, &hscan->xs_vmbuffer,
                                476                 :        8039379 :                             hscan->xs_readonly);
                                477                 :                :     }
                                478                 :                : 
  169 pg@bowt.ie                479         [ -  + ]:CBC    15061827 :     Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk);
                                480         [ -  + ]:       15061827 :     Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid));
                                481                 :                : 
                                482                 :                :     /* Obtain share-lock on the buffer so we can examine visibility */
                                483                 :       15061827 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE);
                                484                 :       15061827 :     got_heap_tuple = heap_hot_search_buffer(tid,
                                485                 :                :                                             rel,
                                486                 :                :                                             hscan->xs_cbuf,
                                487                 :                :                                             snapshot,
                                488                 :                :                                             heapTuple,
                                489                 :                :                                             &all_dead,
    5 pg@bowt.ie                490                 :GNC    15061827 :                                             !scan->xs_heap_continue);
                                491                 :       15061825 :     heapTuple->t_self = *tid;
  169 pg@bowt.ie                492                 :CBC    15061825 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_UNLOCK);
                                493                 :                : 
                                494         [ +  + ]:       15061825 :     if (got_heap_tuple)
                                495                 :                :     {
    5 pg@bowt.ie                496         [ +  + ]:GNC    14346242 :         if (!index_only)
                                497                 :                :         {
                                498                 :                :             /*
                                499                 :                :              * Only with a non-MVCC snapshot can more than one HOT chain
                                500                 :                :              * member be visible, so only then must we keep walking the chain
                                501                 :                :              */
                                502   [ +  +  +  + ]:       13898084 :             scan->xs_heap_continue = !IsMVCCLikeSnapshot(snapshot);
                                503                 :                : 
                                504                 :       13898084 :             ExecStoreBufferHeapTuple(heapTuple, slot, hscan->xs_cbuf);
                                505                 :                : 
                                506         [ -  + ]:       13898084 :             Assert(slot->tts_tableOid == RelationGetRelid(rel));
                                507                 :                :         }
                                508                 :                :         else
                                509                 :                :         {
                                510                 :                :             /*
                                511                 :                :              * Index-only scans stop at the first visible HOT chain member.
                                512                 :                :              * With a non-MVCC snapshot a later member could also be visible,
                                513                 :                :              * but we never look.  That's fine for the only non-MVCC
                                514                 :                :              * index-only scan caller (selfuncs.c), which only needs to know
                                515                 :                :              * that some version is visible.
                                516                 :                :              */
                                517                 :         448158 :             scan->xs_heap_continue = false;
                                518                 :                :         }
                                519                 :                : 
                                520   [ +  +  -  +  :       14346242 :         pgstat_count_heap_fetch(scan->indexRelation);
                                        +  +  +  - ]
                                521                 :                :     }
                                522                 :                :     else
                                523                 :                :     {
                                524                 :                :         /* We've reached the end of the HOT chain. */
                                525                 :         715583 :         scan->xs_heap_continue = false;
                                526                 :                : 
                                527         [ +  + ]:         715583 :         if (unlikely(all_dead))
                                528                 :         256025 :             heapam_index_kill_item(scan);
                                529                 :                :     }
                                530                 :                : 
  169 pg@bowt.ie                531                 :CBC    15061825 :     return got_heap_tuple;
                                532                 :                : }
                                533                 :                : 
                                534                 :                : /*
                                535                 :                :  * Out-of-line heapam_index_heap_fetch wrapper for index-only scans.
                                536                 :                :  *
                                537                 :                :  * Index-only scans usually avoid heap fetches using the visibility map, so
                                538                 :                :  * keeping their fetch out of line keeps the frame of their getnext_slot
                                539                 :                :  * callback small.
                                540                 :                :  */
                                541                 :                : static pg_noinline bool
    5 pg@bowt.ie                542                 :GNC      515173 : heapam_index_only_heap_fetch(IndexScanDesc scan)
                                543                 :                : {
                                544                 :         515173 :     IndexScanHeapData *hscan = (IndexScanHeapData *) scan->xs_table_opaque;
                                545                 :                : 
                                546                 :         515173 :     return heapam_index_heap_fetch(scan, hscan, NULL, true);
                                547                 :                : }
                                548                 :                : 
                                549                 :                : /*
                                550                 :                :  * Called when we scanned a whole HOT chain and found only dead tuples:
                                551                 :                :  * arrange for the index AM to kill its entry for that TID.  We do not do this
                                552                 :                :  * when in recovery because it may violate MVCC to do so.  See comments in
                                553                 :                :  * RelationGetIndexScan().
                                554                 :                :  */
                                555                 :                : static pg_noinline void
                                556                 :         256025 : heapam_index_kill_item(IndexScanDesc scan)
                                557                 :                : {
                                558         [ +  + ]:         256025 :     if (scan->xactStartedInRecovery)
                                559                 :          22285 :         return;
                                560                 :                : 
                                561                 :                :     /*
                                562                 :                :      * Tell amgettuple-based index AM to kill its entry for that TID.  The
                                563                 :                :      * next tableam_index_getnext_tid call will pass that along to the index
                                564                 :                :      * AM, before unsetting the flag again.
                                565                 :                :      */
                                566                 :         233740 :     scan->kill_prior_tuple = true;
                                567                 :                : }
                                568                 :                : 
                                569                 :                : /*
                                570                 :                :  * Did an index-only scan switch heap pages more times than the caller's
                                571                 :                :  * visited-pages limit allows?
                                572                 :                :  *
                                573                 :                :  * Caller passes scan rather than hscan to avoiding keeping hscan live across
                                574                 :                :  * heap fetches.
                                575                 :                :  */
                                576                 :                : static inline bool
                                577                 :          67015 : heapam_index_visited_pages_exceeded(IndexScanDesc scan)
                                578                 :                : {
                                579                 :                :     IndexScanHeapData *hscan;
                                580                 :                : 
                                581         [ +  + ]:          67015 :     if (likely(scan->xs_visited_pages_limit == 0))
                                582                 :          37882 :         return false;
                                583                 :                : 
                                584                 :          29133 :     hscan = (IndexScanHeapData *) scan->xs_table_opaque;
                                585                 :                : 
                                586                 :          29133 :     return hscan->xs_blkswitch_count > scan->xs_visited_pages_limit;
                                587                 :                : }
        

Generated by: LCOV version 2.0-1