From 2fb76f871d25a9a6967d700a5dccbe28473c0fc2 Mon Sep 17 00:00:00 2001 From: Melanie Plageman Date: Sat, 27 Jan 2024 18:39:37 -0500 Subject: [PATCH v8 1/3] Split heapgetpage into two parts heapgetpage(), a per-block utility function used in heap scans, read a passed-in block into a buffer and then, if page-at-a-time processing was enabled, pruned the page and built an array of its visible tuples. This was used for sequential and sample scans. Future commits will add support for read streams. The streaming read API will read in the blocks specified by a callback, but any significant per-page processing should be done synchronously on the buffer yielded by the streaming read API. To support this, separate the logic in heapgetpage() to read a block into a buffer from that which prunes the page and builds an array of the visible tuples. The former is now heapfetchbuf() and the latter is now heapbuildvis(). Future commits will push the logic for selecting the next block into heapfetchbuf() in cases when streaming reads are not supported (such as backwards sequential scans). Because this logic differs for sample scans and sequential scans, inline the code to read the block into a buffer for sample scans. This has the added benefit of allowing for a bit of refactoring in heapam_scan_sample_next_block(), including unpinning the previous buffer before invoking the callback to select the next block. --- src/backend/access/heap/heapam.c | 74 ++++++++++++++---------- src/backend/access/heap/heapam_handler.c | 40 +++++++++---- src/include/access/heapam.h | 2 +- 3 files changed, 72 insertions(+), 44 deletions(-) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index a9d5b109a5e..bf2b9b19e72 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -360,17 +360,18 @@ heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlk } /* - * heapgetpage - subroutine for heapgettup() + * heapbuildvis - Utility function for heap scans. * - * This routine reads and pins the specified page of the relation. - * In page-at-a-time mode it performs additional work, namely determining - * which tuples on the page are visible. + * Given a page residing in a buffer saved in the scan descriptor, prune the + * page and determine which of its tuples are all visible, saving their offsets + * in an array in the scan descriptor. */ void -heapgetpage(TableScanDesc sscan, BlockNumber block) +heapbuildvis(TableScanDesc sscan) { HeapScanDesc scan = (HeapScanDesc) sscan; - Buffer buffer; + Buffer buffer = scan->rs_cbuf; + BlockNumber block = scan->rs_cblock; Snapshot snapshot; Page page; int lines; @@ -378,31 +379,8 @@ heapgetpage(TableScanDesc sscan, BlockNumber block) OffsetNumber lineoff; bool all_visible; - Assert(block < scan->rs_nblocks); + Assert(BufferGetBlockNumber(buffer) == block); - /* release previous scan buffer, if any */ - if (BufferIsValid(scan->rs_cbuf)) - { - ReleaseBuffer(scan->rs_cbuf); - scan->rs_cbuf = InvalidBuffer; - } - - /* - * Be sure to check for interrupts at least once per page. Checks at - * higher code levels won't be able to stop a seqscan that encounters many - * pages' worth of consecutive dead tuples. - */ - CHECK_FOR_INTERRUPTS(); - - /* read page using selected strategy */ - scan->rs_cbuf = ReadBufferExtended(scan->rs_base.rs_rd, MAIN_FORKNUM, block, - RBM_NORMAL, scan->rs_strategy); - scan->rs_cblock = block; - - if (!(scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)) - return; - - buffer = scan->rs_cbuf; snapshot = scan->rs_base.rs_snapshot; /* @@ -475,6 +453,37 @@ heapgetpage(TableScanDesc sscan, BlockNumber block) scan->rs_ntuples = ntup; } +/* + * heapfetchbuf - subroutine for heapgettup() + * + * This routine reads the specified block of the relation into a buffer and + * returns with that pinned buffer saved in the scan descriptor. + */ +static inline void +heapfetchbuf(HeapScanDesc scan, BlockNumber block) +{ + Assert(block < scan->rs_nblocks); + + /* release previous scan buffer, if any */ + if (BufferIsValid(scan->rs_cbuf)) + { + ReleaseBuffer(scan->rs_cbuf); + scan->rs_cbuf = InvalidBuffer; + } + + /* + * Be sure to check for interrupts at least once per page. Checks at + * higher code levels won't be able to stop a seqscan that encounters many + * pages' worth of consecutive dead tuples. + */ + CHECK_FOR_INTERRUPTS(); + + /* read page using selected strategy */ + scan->rs_cbuf = ReadBufferExtended(scan->rs_base.rs_rd, MAIN_FORKNUM, block, + RBM_NORMAL, scan->rs_strategy); + scan->rs_cblock = block; +} + /* * heapgettup_initial_block - return the first BlockNumber to scan * @@ -748,7 +757,7 @@ heapgettup(HeapScanDesc scan, */ while (block != InvalidBlockNumber) { - heapgetpage((TableScanDesc) scan, block); + heapfetchbuf(scan, block); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE); page = heapgettup_start_page(scan, dir, &linesleft, &lineoff); continue_page: @@ -869,7 +878,8 @@ heapgettup_pagemode(HeapScanDesc scan, */ while (block != InvalidBlockNumber) { - heapgetpage((TableScanDesc) scan, block); + heapfetchbuf(scan, block); + heapbuildvis((TableScanDesc) scan); page = BufferGetPage(scan->rs_cbuf); linesleft = scan->rs_ntuples; lineindex = ScanDirectionIsForward(dir) ? 0 : linesleft - 1; diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index 0952d4a98eb..f4f670e9b24 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -2352,11 +2352,14 @@ heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate) if (hscan->rs_nblocks == 0) return false; - if (tsm->NextSampleBlock) + if (BufferIsValid(hscan->rs_cbuf)) { - blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks); - hscan->rs_cblock = blockno; + ReleaseBuffer(hscan->rs_cbuf); + hscan->rs_cbuf = InvalidBuffer; } + + if (tsm->NextSampleBlock) + blockno = tsm->NextSampleBlock(scanstate, hscan->rs_nblocks); else { /* scanning table sequentially */ @@ -2398,20 +2401,35 @@ heapam_scan_sample_next_block(TableScanDesc scan, SampleScanState *scanstate) } } - if (!BlockNumberIsValid(blockno)) + hscan->rs_cblock = blockno; + + if (!BlockNumberIsValid(hscan->rs_cblock)) { - if (BufferIsValid(hscan->rs_cbuf)) - ReleaseBuffer(hscan->rs_cbuf); - hscan->rs_cbuf = InvalidBuffer; - hscan->rs_cblock = InvalidBlockNumber; hscan->rs_inited = false; - return false; } - heapgetpage(scan, blockno); - hscan->rs_inited = true; + Assert(hscan->rs_cblock < hscan->rs_nblocks); + + /* + * We may scan multiple pages before finding tuples to yield or finishing + * the scan. Since we want to check for interrupts at least once per page, + * do so here. + */ + CHECK_FOR_INTERRUPTS(); + + /* Read page using selected strategy */ + hscan->rs_cbuf = ReadBufferExtended(hscan->rs_base.rs_rd, MAIN_FORKNUM, + hscan->rs_cblock, RBM_NORMAL, hscan->rs_strategy); + /* + * If pagemode is allowed, prune the page and build an array of visible + * tuple offsets. + */ + if (hscan->rs_base.rs_flags & SO_ALLOW_PAGEMODE) + heapbuildvis(scan); + + hscan->rs_inited = true; return true; } diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index a307fb5f245..4d324c78e5b 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -267,7 +267,7 @@ extern TableScanDesc heap_beginscan(Relation relation, Snapshot snapshot, uint32 flags); extern void heap_setscanlimits(TableScanDesc sscan, BlockNumber startBlk, BlockNumber numBlks); -extern void heapgetpage(TableScanDesc sscan, BlockNumber block); +extern void heapbuildvis(TableScanDesc sscan); extern void heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params, bool allow_strat, bool allow_sync, bool allow_pagemode); extern void heap_endscan(TableScanDesc sscan); -- 2.40.1