From d07b92930105646c316db5eadd4ea23d17e30ab9 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Tue, 10 Mar 2026 14:40:35 -0400 Subject: [PATCH v18 03/19] heapam: Track heap block in IndexFetchHeapData. 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. This is preparatory work for an upcoming commit that will add index prefetching using a read stream. Delegating the release of a currently pinned buffer to ReleaseAndReadBuffer won't work anymore -- at least not when the next buffer that the scan needs to pin is one returned by read_stream_next_buffer (not a buffer returned by ReadBuffer). 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_indexscan.c | 35 +++++++++++++--------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 8f9cc2f88..addb883ce 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_indexscan.c b/src/backend/access/heap/heapam_indexscan.c index de812a462..cf9366ad4 100644 --- a/src/backend/access/heap/heapam_indexscan.c +++ b/src/backend/access/heap/heapam_indexscan.c @@ -47,6 +47,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; @@ -61,6 +62,7 @@ heapam_index_fetch_reset(IndexFetchTableData *scan) { ReleaseBuffer(hscan->xs_cbuf); hscan->xs_cbuf = InvalidBuffer; + hscan->xs_blk = InvalidBlockNumber; } if (BufferIsValid(hscan->xs_vmbuffer)) @@ -129,24 +131,29 @@ heapam_index_fetch_tuple_impl(struct IndexFetchTableData *scan, Assert(TTS_IS_BUFFERTUPLE(slot)); - /* We can skip the buffer-switching logic if we're in mid-HOT chain. */ - if (!*heap_continue) + /* 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(!*heap_continue); - 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, @@ -252,11 +259,11 @@ heapam_index_getnext_slot(IndexScanDesc scan, ScanDirection direction, */ if (unlikely(xs_visited_pages_limit > 0)) { - BlockNumber blk = ItemPointerGetBlockNumber(tid); + Assert(hscan->xs_blk == ItemPointerGetBlockNumber(tid)); - if (blk != last_visited_block) + if (hscan->xs_blk != last_visited_block) { - last_visited_block = blk; + last_visited_block = hscan->xs_blk; if (++n_visited_pages > xs_visited_pages_limit) return false; /* give up */ } -- 2.53.0