From e7cf691b3d5b08f82ac224de482f59480b211a12 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Tue, 10 Mar 2026 14:40:35 -0400 Subject: [PATCH v17 02/18] 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. Author: Peter Geoghegan Reviewed-By: Andres Freund Discussion: https://postgr.es/m/CAH2-Wz=g=JTSyDB4UtB5su2ZcvsS7VbP+ZMvvaG6ABoCb+s8Lw@mail.gmail.com --- src/include/access/heapam.h | 5 ++-- src/backend/access/heap/heapam_handler.c | 29 +++++++++++++++--------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 305ecc31a..ecf1ba1cf 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -124,10 +124,11 @@ typedef struct IndexFetchHeapData IndexFetchTableData xs_base; /* AM independent part of the descriptor */ /* - * Current heap buffer in scan, if any. NB: if xs_cbuf is not - * InvalidBuffer, we hold a pin on that buffer. + * Current heap buffer in scan (and its block number), if any. NB: if + * xs_blk is not InvalidBlockNumber, we hold a pin in xs_cbuf. */ Buffer xs_cbuf; + BlockNumber xs_blk; /* Current heap block's corresponding page in the visibility map */ Buffer xs_vmbuffer; diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 253a735b6..cfe4689a8 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -86,6 +86,7 @@ heapam_index_fetch_begin(Relation rel) hscan->xs_base.rel = rel; hscan->xs_cbuf = InvalidBuffer; + hscan->xs_blk = InvalidBlockNumber; hscan->xs_vmbuffer = InvalidBuffer; return &hscan->xs_base; @@ -107,6 +108,7 @@ heapam_index_fetch_reset(IndexFetchTableData *scan) ReleaseBuffer(hscan->xs_vmbuffer); hscan->xs_vmbuffer = InvalidBuffer; } + hscan->xs_blk = InvalidBlockNumber; } static void @@ -132,24 +134,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, - &hscan->xs_vmbuffer); + heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf, + &hscan->xs_vmbuffer); } + 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