From 1b6681be37c6f10fde0a961837ea7f29e6cd067e Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Tue, 10 Mar 2026 14:40:35 -0400 Subject: [PATCH v14 02/16] heapam: Track heap block in IndexFetchHeapData using xs_blk Add an explicit BlockNumber field (xs_blk) to IndexFetchHeapData that tracks which heap block is currently pinned in xs_cbuf. heapam_index_fetch_tuple now uses xs_blk to determine when buffer switching is needed, replacing the previous approach that compared buffer identities via ReleaseAndReadBuffer on every non-HOT-chain call. The new approach skips the buffer-switching path entirely when the next TID is on the same heap page, which also means that heap_page_prune_opt is called exactly once per block (when the block is first pinned). This is preparatory work for an upcoming commit that will need xs_blk to manage buffer pin transfers between the scan and the executor slot. --- src/include/access/heapam.h | 1 + src/backend/access/heap/heapam_handler.c | 28 +++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index ad993c073..a859c90f4 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -117,6 +117,7 @@ typedef struct IndexFetchHeapData IndexFetchTableData xs_base; /* AM independent part of the descriptor */ Buffer xs_cbuf; /* current heap buffer in scan, if any */ + BlockNumber xs_blk; /* xs_cbuf's block number, if any */ /* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ } IndexFetchHeapData; diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 2d1ee9ac9..e15a4ef24 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -85,6 +85,7 @@ heapam_index_fetch_begin(Relation rel) hscan->xs_base.rel = rel; hscan->xs_cbuf = InvalidBuffer; + hscan->xs_blk = InvalidBlockNumber; return &hscan->xs_base; } @@ -99,6 +100,7 @@ heapam_index_fetch_reset(IndexFetchTableData *scan) ReleaseBuffer(hscan->xs_cbuf); hscan->xs_cbuf = InvalidBuffer; } + hscan->xs_blk = InvalidBlockNumber; } static void @@ -124,23 +126,29 @@ heapam_index_fetch_tuple(struct IndexFetchTableData *scan, Assert(TTS_IS_BUFFERTUPLE(slot)); - /* We can skip the buffer-switching logic if we're in mid-HOT chain. */ - if (!*call_again) + /* We can skip the buffer-switching logic if we're on the same page. */ + if (hscan->xs_blk != ItemPointerGetBlockNumber(tid)) { - /* Switch to correct buffer if we don't have it already */ - Buffer prev_buf = hscan->xs_cbuf; + Assert(!*call_again); - hscan->xs_cbuf = ReleaseAndReadBuffer(hscan->xs_cbuf, - hscan->xs_base.rel, - ItemPointerGetBlockNumber(tid)); + /* Remember this buffer's block number for next time */ + hscan->xs_blk = ItemPointerGetBlockNumber(tid); + + if (BufferIsValid(hscan->xs_cbuf)) + ReleaseBuffer(hscan->xs_cbuf); + + hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk); /* - * Prune page, but only if we weren't already on this page + * Prune page when it is pinned for the first time */ - if (prev_buf != hscan->xs_cbuf) - heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf); + heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf); } + Assert(BufferIsValid(hscan->xs_cbuf)); + Assert(BufferGetBlockNumber(hscan->xs_cbuf) == hscan->xs_blk); + Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid)); + /* Obtain share-lock on the buffer so we can examine visibility */ LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE); got_heap_tuple = heap_hot_search_buffer(tid, -- 2.53.0