From: Peter Geoghegan Date: Sat, 4 Apr 2026 15:45:33 +0000 (-0400) Subject: heapam: Track heap block in IndexFetchHeapData. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7d09595e46ff513ae0a23ae80e58871a321174f;p=thirdparty%2Fpostgresql.git 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 --- diff --git a/src/backend/access/heap/heapam_indexscan.c b/src/backend/access/heap/heapam_indexscan.c index c36b804d1e3..bbd8a165ddc 100644 --- a/src/backend/access/heap/heapam_indexscan.c +++ b/src/backend/access/heap/heapam_indexscan.c @@ -32,6 +32,7 @@ heapam_index_fetch_begin(Relation rel, uint32 flags) hscan->xs_base.rel = rel; hscan->xs_base.flags = flags; hscan->xs_cbuf = InvalidBuffer; + hscan->xs_blk = InvalidBlockNumber; hscan->xs_vmbuffer = InvalidBuffer; return &hscan->xs_base; @@ -46,6 +47,7 @@ heapam_index_fetch_reset(IndexFetchTableData *scan) { ReleaseBuffer(hscan->xs_cbuf); hscan->xs_cbuf = InvalidBuffer; + hscan->xs_blk = InvalidBlockNumber; } if (BufferIsValid(hscan->xs_vmbuffer)) @@ -240,25 +242,30 @@ 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 (!*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, - hscan->xs_base.flags & SO_HINT_REL_READ_ONLY); + heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf, + &hscan->xs_vmbuffer, + hscan->xs_base.flags & SO_HINT_REL_READ_ONLY); } + 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, diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index cc90c821be5..5176478c295 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -122,10 +122,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;