v22-0016-Remove-table-AM-callback-scan_bitmap_next_block.patch

text/x-patch

Filename: v22-0016-Remove-table-AM-callback-scan_bitmap_next_block.patch
Type: text/x-patch
Part: 15
Message: Re: BitmapHeapScan streaming read user and prelim refactoring

Patch

Format: format-patch
Series: patch v22-0016
Subject: Remove table AM callback scan_bitmap_next_block
File+
src/backend/access/heap/heapam_handler.c 37 21
src/backend/access/table/tableamapi.c 0 3
src/backend/executor/nodeBitmapHeapscan.c 28 35
src/backend/optimizer/util/plancat.c 1 1
src/include/access/tableam.h 22 66
From 52629bc9e45a7ba13e5714c381fc0c55b68ed025 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Tue, 21 May 2024 18:02:38 -0400
Subject: [PATCH v22 16/19] Remove table AM callback scan_bitmap_next_block

Non block-based table AMs might find implementing bitmap table scan
somewhat easier by removing table_scan_bitmap_next_block(). Now bitmap
table scan code only calls table_scan_bitmap_next_tuple().

This commit turns the heap AM implementation of scan_bitmap_next_block()
into a local helper in heapam_handler.c.
---
 src/backend/access/heap/heapam_handler.c  | 58 +++++++++------
 src/backend/access/table/tableamapi.c     |  3 -
 src/backend/executor/nodeBitmapHeapscan.c | 63 ++++++++--------
 src/backend/optimizer/util/plancat.c      |  2 +-
 src/include/access/tableam.h              | 88 ++++++-----------------
 5 files changed, 88 insertions(+), 126 deletions(-)

diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index e06054369d2..a97d835c815 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2334,18 +2334,22 @@ BitmapAdjustPrefetchTarget(BitmapHeapScanDesc *scan)
 #endif							/* USE_PREFETCH */
 }
 
-
-/* ------------------------------------------------------------------------
- * Executor related callbacks for the heap AM
- * ------------------------------------------------------------------------
+/*
+ * Prepare to fetch / check / return tuples as part of a bitmap heap scan.
+ * `scan` needs to have been started via heap_beginscan_bm(). Returns false if
+ * there are no more blocks in the bitmap, true otherwise. `lossy_pages` is
+ * incremented if the block's representation in the bitmap is lossy; otherwise,
+ * `exact_pages` is incremented.
+ *
+ * `recheck` is set to indicate whether or not the tuples from this block
+ * should be rechecked. Tuples from lossy pages will always need to be
+ * rechecked, but some non-lossy pages' tuples may also require recheck.
  */
-
 static bool
-heapam_scan_bitmap_next_block(BitmapTableScanDesc *sscan,
+heapam_scan_bitmap_next_block(BitmapHeapScanDesc *scan,
 							  bool *recheck,
 							  long *lossy_pages, long *exact_pages)
 {
-	BitmapHeapScanDesc *scan = (BitmapHeapScanDesc *) sscan;
 	BlockNumber block;
 	Buffer		buffer;
 	Snapshot	snapshot;
@@ -2515,9 +2519,16 @@ heapam_scan_bitmap_next_block(BitmapTableScanDesc *sscan,
 	return true;
 }
 
+
+/* ------------------------------------------------------------------------
+ * Executor related callbacks for the heap AM
+ * ------------------------------------------------------------------------
+ */
+
 static bool
 heapam_scan_bitmap_next_tuple(BitmapTableScanDesc *sscan,
-							  TupleTableSlot *slot)
+							  TupleTableSlot *slot, bool *recheck,
+							  long *lossy_pages, long *exact_pages)
 {
 	BitmapHeapScanDesc *scan = (BitmapHeapScanDesc *) sscan;
 	ParallelBitmapHeapState *pstate = sscan->pstate;
@@ -2525,22 +2536,28 @@ heapam_scan_bitmap_next_tuple(BitmapTableScanDesc *sscan,
 	Page		page;
 	ItemId		lp;
 
-	if (scan->empty_tuples_pending > 0)
+	/*
+	 * Out of range?  If so, nothing more to look at on this page
+	 */
+	while (scan->vis_idx < 0 || scan->vis_idx >= scan->vis_ntuples)
 	{
 		/*
-		 * If we don't have to fetch the tuple, just return nulls.
+		 * Emit empty tuples before advancing to the next block
 		 */
-		ExecStoreAllNullTuple(slot);
-		scan->empty_tuples_pending--;
-		BitmapPrefetch(scan);
-		return true;
-	}
+		if (scan->empty_tuples_pending > 0)
+		{
+			/*
+			 * If we don't have to fetch the tuple, just return nulls.
+			 */
+			ExecStoreAllNullTuple(slot);
+			scan->empty_tuples_pending--;
+			BitmapPrefetch(scan);
+			return true;
+		}
 
-	/*
-	 * Out of range?  If so, nothing more to look at on this page
-	 */
-	if (scan->vis_idx < 0 || scan->vis_idx >= scan->vis_ntuples)
-		return false;
+		if (!heapam_scan_bitmap_next_block(scan, recheck, lossy_pages, exact_pages))
+			return false;
+	}
 
 #ifdef USE_PREFETCH
 
@@ -2941,7 +2958,6 @@ static const TableAmRoutine heapam_methods = {
 
 	.relation_estimate_size = heapam_estimate_rel_size,
 
-	.scan_bitmap_next_block = heapam_scan_bitmap_next_block,
 	.scan_bitmap_next_tuple = heapam_scan_bitmap_next_tuple,
 	.scan_sample_next_block = heapam_scan_sample_next_block,
 	.scan_sample_next_tuple = heapam_scan_sample_next_tuple
diff --git a/src/backend/access/table/tableamapi.c b/src/backend/access/table/tableamapi.c
index e9b598256fb..a47527d490a 100644
--- a/src/backend/access/table/tableamapi.c
+++ b/src/backend/access/table/tableamapi.c
@@ -91,9 +91,6 @@ GetTableAmRoutine(Oid amhandler)
 
 	Assert(routine->relation_estimate_size != NULL);
 
-	/* optional, but one callback implies presence of the other */
-	Assert((routine->scan_bitmap_next_block == NULL) ==
-		   (routine->scan_bitmap_next_tuple == NULL));
 	Assert(routine->scan_sample_next_block != NULL);
 	Assert(routine->scan_sample_next_tuple != NULL);
 
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index e581e727f7e..badc4b98dc5 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -185,56 +185,49 @@ BitmapTableScanSetup(BitmapHeapScanState *node)
 static TupleTableSlot *
 BitmapHeapNext(BitmapHeapScanState *node)
 {
-	ExprContext *econtext = node->ss.ps.ps_ExprContext;
-	TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
-	BitmapTableScanDesc *scan = node->scandesc;
+	ExprContext *econtext;
+	TupleTableSlot *slot;
+	BitmapTableScanDesc *scan;
 
 	/*
 	 * If we haven't yet performed the underlying index scan, do it, and begin
 	 * the iteration over the bitmap.
 	 */
 	if (!node->initialized)
-	{
 		BitmapTableScanSetup(node);
-		scan = node->scandesc;
-		goto new_page;
-	}
 
-	for (;;)
+	scan = node->scandesc;
+	econtext = node->ss.ps.ps_ExprContext;
+	slot = node->ss.ss_ScanTupleSlot;
+
+	while (table_scan_bitmap_next_tuple(scan, slot,
+										&node->recheck,
+										&node->lossy_pages, &node->exact_pages))
 	{
-		while (table_scan_bitmap_next_tuple(scan, slot))
-		{
-			/*
-			 * Continuing in previously obtained page.
-			 */
+		/*
+		 * Continuing in previously obtained page.
+		 */
 
-			CHECK_FOR_INTERRUPTS();
+		CHECK_FOR_INTERRUPTS();
 
-			/*
-			 * If we are using lossy info, we have to recheck the qual
-			 * conditions at every tuple.
-			 */
-			if (node->recheck)
+		/*
+		 * If we are using lossy info, we have to recheck the qual conditions
+		 * at every tuple.
+		 */
+		if (node->recheck)
+		{
+			econtext->ecxt_scantuple = slot;
+			if (!ExecQualAndReset(node->bitmapqualorig, econtext))
 			{
-				econtext->ecxt_scantuple = slot;
-				if (!ExecQualAndReset(node->bitmapqualorig, econtext))
-				{
-					/* Fails recheck, so drop it and loop back for another */
-					InstrCountFiltered2(node, 1);
-					ExecClearTuple(slot);
-					continue;
-				}
+				/* Fails recheck, so drop it and loop back for another */
+				InstrCountFiltered2(node, 1);
+				ExecClearTuple(slot);
+				continue;
 			}
-
-			/* OK to return this tuple */
-			return slot;
 		}
 
-new_page:
-
-		if (!table_scan_bitmap_next_block(scan, &node->recheck,
-										  &node->lossy_pages, &node->exact_pages))
-			break;
+		/* OK to return this tuple */
+		return slot;
 	}
 
 	/*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 775955363ef..c11ff3a1bae 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -324,7 +324,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 				info->amcanparallel = amroutine->amcanparallel;
 				info->amhasgettuple = (amroutine->amgettuple != NULL);
 				info->amhasgetbitmap = amroutine->amgetbitmap != NULL &&
-					relation->rd_tableam->scan_bitmap_next_block != NULL;
+					relation->rd_tableam->scan_bitmap_next_tuple != NULL;
 				info->amcanmarkpos = (amroutine->ammarkpos != NULL &&
 									  amroutine->amrestrpos != NULL);
 				info->amcostestimate = amroutine->amcostestimate;
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index a00b9bd296a..d496d68f1c2 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -808,37 +808,20 @@ typedef struct TableAmRoutine
 	 */
 
 	/*
-	 * Prepare to fetch / check / return tuples from as part of a bitmap table
-	 * scan. `scan` was started via table_beginscan_bm(). Return false if the
-	 * bitmap is exhausted and true otherwise.
-	 *
-	 * This will typically read and pin a block, and do the necessary work to
-	 * allow scan_bitmap_next_tuple() to return tuples (e.g. depending on the
-	 * table AM, it might make sense to perform tuple visibility checks at
-	 * this time).
+	 * Fetch the next tuple of a bitmap table scan into `slot` and return true
+	 * if a visible tuple was found, false otherwise.
 	 *
 	 * `lossy_pages` is incremented if the bitmap is lossy for the selected
-	 * block; otherwise, `exact_pages` is incremented.
-	 *
-	 * Prefetching future blocks indicated in the bitmap is left to the table
-	 * AM.
+	 * page; otherwise, `exact_pages` is incremented. This is tracked for
+	 * display in EXPLAIN ANALYZE output.
 	 *
-	 * Optional callback, but either both scan_bitmap_next_block and
-	 * scan_bitmap_next_tuple need to exist, or neither.
-	 */
-	bool		(*scan_bitmap_next_block) (BitmapTableScanDesc *scan,
-										   bool *recheck,
-										   long *lossy_pages, long *exact_pages);
-
-	/*
-	 * Fetch the next tuple of a bitmap table scan into `slot` and return true
-	 * if a visible tuple was found, false otherwise.
+	 * Prefetching additional data from the bitmap is left to the table AM.
 	 *
-	 * Optional callback, but either both scan_bitmap_next_block and
-	 * scan_bitmap_next_tuple need to exist, or neither.
+	 * This is an optional callback.
 	 */
 	bool		(*scan_bitmap_next_tuple) (BitmapTableScanDesc *scan,
-										   TupleTableSlot *slot);
+										   TupleTableSlot *slot, bool *recheck,
+										   long *lossy_pages, long *exact_pages);
 
 	/*
 	 * Prepare to fetch tuples from the next block in a sample scan. Return
@@ -1982,54 +1965,26 @@ table_relation_estimate_size(Relation rel, int32 *attr_widths,
  * ----------------------------------------------------------------------------
  */
 
+
 /*
- * Prepare to fetch / check / return tuples as part of a bitmap table scan.
- * `scan` needs to have been started via table_beginscan_bm(). Returns false if
- * there are no more blocks in the bitmap, true otherwise. `lossy_pages` is
- * incremented if the block's representation in the bitmap is lossy; otherwise,
- * `exact_pages` is incremented.
- *
- * `recheck` is set by the table AM to indicate whether or not the tuples
- * from this block should be rechecked. Tuples from lossy pages will always
- * need to be rechecked, but some non-lossy pages' tuples may also require
- * recheck.
+ * Fetch / check / return tuples as part of a bitmap table scan. `scan` needs
+ * to have been started via table_beginscan_bm(). Fetch the next tuple of a
+ * bitmap table scan into `slot` and return true if a visible tuple was found,
+ * false otherwise.
  *
- * `blockno` is only used in bitmap table scan code to validate that the
- * prefetch block is staying ahead of the current block.
+ * `recheck` is set by the table AM to indicate whether or not the tuple in
+ * `slot` should be rechecked. Tuples from lossy pages will always need to be
+ * rechecked, but some non-lossy pages' tuples may also require recheck.
  *
- * Note, this is an optionally implemented function, therefore should only be
- * used after verifying the presence (at plan time or such).
+ * `lossy_pages` is incremented if the block's representation in the bitmap is
+ * lossy; otherwise, `exact_pages` is incremented.
  */
 static inline bool
-table_scan_bitmap_next_block(BitmapTableScanDesc *scan,
+table_scan_bitmap_next_tuple(BitmapTableScanDesc *scan,
+							 TupleTableSlot *slot,
 							 bool *recheck,
 							 long *lossy_pages,
 							 long *exact_pages)
-{
-	/*
-	 * We don't expect direct calls to table_scan_bitmap_next_block with valid
-	 * CheckXidAlive for catalog or regular tables.  See detailed comments in
-	 * xact.c where these variables are declared.
-	 */
-	if (unlikely(TransactionIdIsValid(CheckXidAlive) && !bsysscan))
-		elog(ERROR, "unexpected table_scan_bitmap_next_block call during logical decoding");
-
-	return scan->rel->rd_tableam->scan_bitmap_next_block(scan,
-														 recheck,
-														 lossy_pages, exact_pages);
-}
-
-/*
- * Fetch the next tuple of a bitmap table scan into `slot` and return true if
- * a visible tuple was found, false otherwise.
- * table_scan_bitmap_next_block() needs to previously have selected a
- * block (i.e. returned true), and no previous
- * table_scan_bitmap_next_tuple() for the same block may have
- * returned false.
- */
-static inline bool
-table_scan_bitmap_next_tuple(BitmapTableScanDesc *scan,
-							 TupleTableSlot *slot)
 {
 	/*
 	 * We don't expect direct calls to table_scan_bitmap_next_tuple with valid
@@ -2040,7 +1995,8 @@ table_scan_bitmap_next_tuple(BitmapTableScanDesc *scan,
 		elog(ERROR, "unexpected table_scan_bitmap_next_tuple call during logical decoding");
 
 	return scan->rel->rd_tableam->scan_bitmap_next_tuple(scan,
-														 slot);
+														 slot, recheck,
+														 lossy_pages, exact_pages);
 }
 
 /*
-- 
2.34.1