LCOV - differential code coverage report
Current view: top level - src/backend/access/heap - heapam_indexscan.c (source / functions) Coverage Total Hit UNC GNC
Current: bed3ffbf9d952be6c7d739d068cdce44c046dfb7 vs 574581b50ac9c63dd9e4abebb731a3b67e5b50f6 Lines: 97.9 % 96 94 2 94
Current Date: 2026-05-05 10:23:31 +0900 Functions: 100.0 % 5 5 5
Baseline: lcov-20260505-025707-baseline Branches: 81.2 % 64 52 12 52
Baseline Date: 2026-05-05 10:27:06 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 97.9 % 96 94 2 94
Function coverage date bins:
(30,360] days: 100.0 % 5 5 5
Branch coverage date bins:
(30,360] days: 81.2 % 64 52 12 52

 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/heapam.h"
                                 18                 :                : #include "access/relscan.h"
                                 19                 :                : #include "storage/predicate.h"
                                 20                 :                : 
                                 21                 :                : 
                                 22                 :                : /* ------------------------------------------------------------------------
                                 23                 :                :  * Index Scan Callbacks for heap AM
                                 24                 :                :  * ------------------------------------------------------------------------
                                 25                 :                :  */
                                 26                 :                : 
                                 27                 :                : IndexFetchTableData *
   31 pg@bowt.ie                 28                 :GNC    16791746 : heapam_index_fetch_begin(Relation rel, uint32 flags)
                                 29                 :                : {
                                 30                 :       16791746 :     IndexFetchHeapData *hscan = palloc0_object(IndexFetchHeapData);
                                 31                 :                : 
                                 32                 :       16791746 :     hscan->xs_base.rel = rel;
                                 33                 :       16791746 :     hscan->xs_base.flags = flags;
                                 34                 :       16791746 :     hscan->xs_cbuf = InvalidBuffer;
                                 35                 :       16791746 :     hscan->xs_blk = InvalidBlockNumber;
                                 36                 :       16791746 :     hscan->xs_vmbuffer = InvalidBuffer;
                                 37                 :                : 
                                 38                 :       16791746 :     return &hscan->xs_base;
                                 39                 :                : }
                                 40                 :                : 
                                 41                 :                : void
                                 42                 :       14216786 : heapam_index_fetch_reset(IndexFetchTableData *scan)
                                 43                 :                : {
                                 44                 :                :     /*
                                 45                 :                :      * Resets are a no-op.
                                 46                 :                :      *
                                 47                 :                :      * Deliberately avoid dropping pins now held in xs_cbuf and xs_vmbuffer.
                                 48                 :                :      * This saves cycles during certain tight nested loop joins (it can avoid
                                 49                 :                :      * repeated pinning and unpinning of the same buffer across rescans).
                                 50                 :                :      */
                                 51                 :       14216786 : }
                                 52                 :                : 
                                 53                 :                : void
                                 54                 :       16790496 : heapam_index_fetch_end(IndexFetchTableData *scan)
                                 55                 :                : {
                                 56                 :       16790496 :     IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
                                 57                 :                : 
                                 58                 :                :     /* drop pin if there's a pinned heap page */
                                 59         [ +  + ]:       16790496 :     if (BufferIsValid(hscan->xs_cbuf))
                                 60                 :       13902431 :         ReleaseBuffer(hscan->xs_cbuf);
                                 61                 :                : 
                                 62                 :                :     /* drop pin if there's a pinned visibility map page */
                                 63         [ +  + ]:       16790496 :     if (BufferIsValid(hscan->xs_vmbuffer))
                                 64                 :          54287 :         ReleaseBuffer(hscan->xs_vmbuffer);
                                 65                 :                : 
                                 66                 :       16790496 :     pfree(hscan);
                                 67                 :       16790496 : }
                                 68                 :                : 
                                 69                 :                : /*
                                 70                 :                :  *  heap_hot_search_buffer  - search HOT chain for tuple satisfying snapshot
                                 71                 :                :  *
                                 72                 :                :  * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
                                 73                 :                :  * of a HOT chain), and buffer is the buffer holding this tuple.  We search
                                 74                 :                :  * for the first chain member satisfying the given snapshot.  If one is
                                 75                 :                :  * found, we update *tid to reference that tuple's offset number, and
                                 76                 :                :  * return true.  If no match, return false without modifying *tid.
                                 77                 :                :  *
                                 78                 :                :  * heapTuple is a caller-supplied buffer.  When a match is found, we return
                                 79                 :                :  * the tuple here, in addition to updating *tid.  If no match is found, the
                                 80                 :                :  * contents of this buffer on return are undefined.
                                 81                 :                :  *
                                 82                 :                :  * If all_dead is not NULL, we check non-visible tuples to see if they are
                                 83                 :                :  * globally dead; *all_dead is set true if all members of the HOT chain
                                 84                 :                :  * are vacuumable, false if not.
                                 85                 :                :  *
                                 86                 :                :  * Unlike heap_fetch, the caller must already have pin and (at least) share
                                 87                 :                :  * lock on the buffer; it is still pinned/locked at exit.
                                 88                 :                :  */
                                 89                 :                : bool
                                 90                 :       27476137 : heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
                                 91                 :                :                        Snapshot snapshot, HeapTuple heapTuple,
                                 92                 :                :                        bool *all_dead, bool first_call)
                                 93                 :                : {
                                 94                 :       27476137 :     Page        page = BufferGetPage(buffer);
                                 95                 :       27476137 :     TransactionId prev_xmax = InvalidTransactionId;
                                 96                 :                :     BlockNumber blkno;
                                 97                 :                :     OffsetNumber offnum;
                                 98                 :                :     bool        at_chain_start;
                                 99                 :                :     bool        valid;
                                100                 :                :     bool        skip;
                                101                 :       27476137 :     GlobalVisState *vistest = NULL;
                                102                 :                : 
                                103                 :                :     /* If this is not the first call, previous call returned a (live!) tuple */
                                104         [ +  + ]:       27476137 :     if (all_dead)
                                105                 :       23464427 :         *all_dead = first_call;
                                106                 :                : 
                                107                 :       27476137 :     blkno = ItemPointerGetBlockNumber(tid);
                                108                 :       27476137 :     offnum = ItemPointerGetOffsetNumber(tid);
                                109                 :       27476137 :     at_chain_start = first_call;
                                110                 :       27476137 :     skip = !first_call;
                                111                 :                : 
                                112                 :                :     /* XXX: we should assert that a snapshot is pushed or registered */
                                113         [ -  + ]:       27476137 :     Assert(TransactionIdIsValid(RecentXmin));
                                114         [ +  - ]:       27476137 :     Assert(BufferGetBlockNumber(buffer) == blkno);
                                115                 :                : 
                                116                 :                :     /* Scan through possible multiple members of HOT-chain */
                                117                 :                :     for (;;)
                                118                 :        1904680 :     {
                                119                 :                :         ItemId      lp;
                                120                 :                : 
                                121                 :                :         /* check for bogus TID */
                                122   [ +  -  +  - ]:       29380817 :         if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
                                123                 :                :             break;
                                124                 :                : 
                                125                 :       29380817 :         lp = PageGetItemId(page, offnum);
                                126                 :                : 
                                127                 :                :         /* check for unused, dead, or redirected items */
                                128         [ +  + ]:       29380817 :         if (!ItemIdIsNormal(lp))
                                129                 :                :         {
                                130                 :                :             /* We should only see a redirect at start of chain */
                                131   [ +  +  +  - ]:         972606 :             if (ItemIdIsRedirected(lp) && at_chain_start)
                                132                 :                :             {
                                133                 :                :                 /* Follow the redirect */
                                134                 :         552499 :                 offnum = ItemIdGetRedirect(lp);
                                135                 :         552499 :                 at_chain_start = false;
                                136                 :         552499 :                 continue;
                                137                 :                :             }
                                138                 :                :             /* else must be end of chain */
                                139                 :         420107 :             break;
                                140                 :                :         }
                                141                 :                : 
                                142                 :                :         /*
                                143                 :                :          * Update heapTuple to point to the element of the HOT chain we're
                                144                 :                :          * currently investigating. Having t_self set correctly is important
                                145                 :                :          * because the SSI checks and the *Satisfies routine for historical
                                146                 :                :          * MVCC snapshots need the correct tid to decide about the visibility.
                                147                 :                :          */
                                148                 :       28408211 :         heapTuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
                                149                 :       28408211 :         heapTuple->t_len = ItemIdGetLength(lp);
                                150                 :       28408211 :         heapTuple->t_tableOid = RelationGetRelid(relation);
                                151                 :       28408211 :         ItemPointerSet(&heapTuple->t_self, blkno, offnum);
                                152                 :                : 
                                153                 :                :         /*
                                154                 :                :          * Shouldn't see a HEAP_ONLY tuple at chain start.
                                155                 :                :          */
                                156   [ +  +  -  + ]:       28408211 :         if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
   31 pg@bowt.ie                157                 :UNC           0 :             break;
                                158                 :                : 
                                159                 :                :         /*
                                160                 :                :          * The xmin should match the previous xmax value, else chain is
                                161                 :                :          * broken.
                                162                 :                :          */
   31 pg@bowt.ie                163   [ +  +  -  + ]:GNC    29760392 :         if (TransactionIdIsValid(prev_xmax) &&
                                164                 :        1352181 :             !TransactionIdEquals(prev_xmax,
                                165                 :                :                                  HeapTupleHeaderGetXmin(heapTuple->t_data)))
   31 pg@bowt.ie                166                 :UNC           0 :             break;
                                167                 :                : 
                                168                 :                :         /*
                                169                 :                :          * When first_call is true (and thus, skip is initially false) we'll
                                170                 :                :          * return the first tuple we find.  But on later passes, heapTuple
                                171                 :                :          * will initially be pointing to the tuple we returned last time.
                                172                 :                :          * Returning it again would be incorrect (and would loop forever), so
                                173                 :                :          * we skip it and return the next match we find.
                                174                 :                :          */
   31 pg@bowt.ie                175         [ +  + ]:GNC    28408211 :         if (!skip)
                                176                 :                :         {
                                177                 :                :             /* If it's visible per the snapshot, we must return it */
                                178                 :       28307802 :             valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
                                179                 :       28307802 :             HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
                                180                 :                :                                                 buffer, snapshot);
                                181                 :                : 
                                182         [ +  + ]:       28307797 :             if (valid)
                                183                 :                :             {
                                184                 :       18815416 :                 ItemPointerSetOffsetNumber(tid, offnum);
                                185                 :       18815416 :                 PredicateLockTID(relation, &heapTuple->t_self, snapshot,
                                186                 :       18815416 :                                  HeapTupleHeaderGetXmin(heapTuple->t_data));
                                187         [ +  + ]:       18815416 :                 if (all_dead)
                                188                 :       15172756 :                     *all_dead = false;
                                189                 :       18815416 :                 return true;
                                190                 :                :             }
                                191                 :                :         }
                                192                 :        9592790 :         skip = false;
                                193                 :                : 
                                194                 :                :         /*
                                195                 :                :          * If we can't see it, maybe no one else can either.  At caller
                                196                 :                :          * request, check whether all chain members are dead to all
                                197                 :                :          * transactions.
                                198                 :                :          *
                                199                 :                :          * Note: if you change the criterion here for what is "dead", fix the
                                200                 :                :          * planner's get_actual_variable_range() function to match.
                                201                 :                :          */
                                202   [ +  +  +  + ]:        9592790 :         if (all_dead && *all_dead)
                                203                 :                :         {
                                204         [ +  + ]:        8562330 :             if (!vistest)
                                205                 :        8404060 :                 vistest = GlobalVisTestFor(relation);
                                206                 :                : 
                                207         [ +  + ]:        8562330 :             if (!HeapTupleIsSurelyDead(heapTuple, vistest))
                                208                 :        8111280 :                 *all_dead = false;
                                209                 :                :         }
                                210                 :                : 
                                211                 :                :         /*
                                212                 :                :          * Check to see if HOT chain continues past this tuple; if so fetch
                                213                 :                :          * the next offnum and loop around.
                                214                 :                :          */
                                215         [ +  + ]:        9592790 :         if (HeapTupleIsHotUpdated(heapTuple))
                                216                 :                :         {
                                217         [ -  + ]:        1352181 :             Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
                                218                 :                :                    blkno);
                                219                 :        1352181 :             offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
                                220                 :        1352181 :             at_chain_start = false;
                                221                 :        1352181 :             prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
                                222                 :                :         }
                                223                 :                :         else
                                224                 :        8240609 :             break;              /* end of chain */
                                225                 :                : 
                                226                 :                :     }
                                227                 :                : 
                                228                 :        8660716 :     return false;
                                229                 :                : }
                                230                 :                : 
                                231                 :                : bool
                                232                 :       23464832 : heapam_index_fetch_tuple(struct IndexFetchTableData *scan,
                                233                 :                :                          ItemPointer tid,
                                234                 :                :                          Snapshot snapshot,
                                235                 :                :                          TupleTableSlot *slot,
                                236                 :                :                          bool *heap_continue, bool *all_dead)
                                237                 :                : {
                                238                 :       23464832 :     IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan;
                                239                 :       23464832 :     BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
                                240                 :                :     bool        got_heap_tuple;
                                241                 :                : 
                                242         [ -  + ]:       23464832 :     Assert(TTS_IS_BUFFERTUPLE(slot));
                                243                 :                : 
                                244                 :                :     /* We can skip the buffer-switching logic if we're on the same page. */
                                245         [ +  + ]:       23464832 :     if (hscan->xs_blk != ItemPointerGetBlockNumber(tid))
                                246                 :                :     {
                                247         [ -  + ]:       16095389 :         Assert(!*heap_continue);
                                248                 :                : 
                                249                 :                :         /* Remember this buffer's block number for next time */
                                250                 :       16095389 :         hscan->xs_blk = ItemPointerGetBlockNumber(tid);
                                251                 :                : 
                                252         [ +  + ]:       16095389 :         if (BufferIsValid(hscan->xs_cbuf))
                                253                 :        2191756 :             ReleaseBuffer(hscan->xs_cbuf);
                                254                 :                : 
                                255                 :       16095389 :         hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk);
                                256                 :                : 
                                257                 :                :         /*
                                258                 :                :          * Prune page when it is pinned for the first time
                                259                 :                :          */
                                260                 :       16095386 :         heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf,
                                261                 :                :                             &hscan->xs_vmbuffer,
                                262                 :       16095386 :                             hscan->xs_base.flags & SO_HINT_REL_READ_ONLY);
                                263                 :                :     }
                                264                 :                : 
                                265         [ -  + ]:       23464829 :     Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk);
                                266         [ -  + ]:       23464829 :     Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid));
                                267                 :                : 
                                268                 :                :     /* Obtain share-lock on the buffer so we can examine visibility */
                                269                 :       23464829 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE);
                                270                 :       23464829 :     got_heap_tuple = heap_hot_search_buffer(tid,
                                271                 :                :                                             hscan->xs_base.rel,
                                272                 :                :                                             hscan->xs_cbuf,
                                273                 :                :                                             snapshot,
                                274                 :                :                                             &bslot->base.tupdata,
                                275                 :                :                                             all_dead,
                                276                 :       23464829 :                                             !*heap_continue);
                                277                 :       23464827 :     bslot->base.tupdata.t_self = *tid;
                                278                 :       23464827 :     LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_UNLOCK);
                                279                 :                : 
                                280         [ +  + ]:       23464827 :     if (got_heap_tuple)
                                281                 :                :     {
                                282                 :                :         /*
                                283                 :                :          * Only in a non-MVCC snapshot can more than one member of the HOT
                                284                 :                :          * chain be visible.
                                285                 :                :          */
                                286   [ +  +  +  + ]:       15173158 :         *heap_continue = !IsMVCCLikeSnapshot(snapshot);
                                287                 :                : 
                                288                 :       15173158 :         slot->tts_tableOid = RelationGetRelid(scan->rel);
                                289                 :       15173158 :         ExecStoreBufferHeapTuple(&bslot->base.tupdata, slot, hscan->xs_cbuf);
                                290                 :                :     }
                                291                 :                :     else
                                292                 :                :     {
                                293                 :                :         /* We've reached the end of the HOT chain. */
                                294                 :        8291669 :         *heap_continue = false;
                                295                 :                :     }
                                296                 :                : 
                                297                 :       23464827 :     return got_heap_tuple;
                                298                 :                : }
        

Generated by: LCOV version 2.5.0-beta