From eac4e11efc7c19091b8076f6193b90aa3989b19e Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Fri, 5 Apr 2024 19:30:43 -0400
Subject: [PATCH v16 11/18] Add BitmapHeapScanDesc and scan functions

Future commits will push all prefetching related code into the heap AM
to eliminate a layering violation. Move all of the members used to
manage prefetching for bitmap heap scan from the BitmapHeapScanState
into a new scan descriptor BitmapHeapScanDesc.

With this new scan descriptor, add all of the relevant functions for
beginning, rescanning, and ending a bitmap heap scan. There is a lot of
bitmap-specific logic to do there now, so it makes sense for bitmap heap
scan to have its own.

Author: Melanie Plageman
Discussion: https://postgr.es/m/CAAKRu_ZwCwWFeL_H3ia26bP2e7HiKLWt0ZmGXPVwPO6uXq0vaA%40mail.gmail.com
---
 src/backend/access/heap/heapam.c          | 98 +++++++++++++++++++----
 src/backend/access/heap/heapam_handler.c  | 42 +++++-----
 src/backend/executor/nodeBitmapHeapscan.c | 47 +++++------
 src/include/access/heapam.h               | 36 ++++++---
 src/include/access/tableam.h              | 45 +++++++++--
 src/tools/pgindent/typedefs.list          |  1 +
 6 files changed, 193 insertions(+), 76 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 10f2faaa60..82faf447e9 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -13,8 +13,11 @@
  *
  * INTERFACE ROUTINES
  *		heap_beginscan	- begin relation scan
+ *		heap_beginscan_bm - begin bitmap heap scan
  *		heap_rescan		- restart a relation scan
+ *		heap_rescan_bm  - restart a bitmap heap scan
  *		heap_endscan	- end relation scan
+ *		heap_endscan_bm - end a bitmap heap scan
  *		heap_getnext	- retrieve next tuple in scan
  *		heap_fetch		- retrieve tuple with given tid
  *		heap_insert		- insert tuple into a relation
@@ -938,6 +941,43 @@ continue_page:
  * ----------------------------------------------------------------
  */
 
+TableScanDesc
+heap_beginscan_bm(Relation relation, Snapshot snapshot, uint32 flags)
+{
+	BitmapHeapScanDesc scan;
+
+	/*
+	 * increment relation ref count while scanning relation
+	 *
+	 * This is just to make really sure the relcache entry won't go away while
+	 * the scan has a pointer to it.  Caller should be holding the rel open
+	 * anyway, so this is redundant in all normal scenarios...
+	 */
+	RelationIncrementReferenceCount(relation);
+	scan = (BitmapHeapScanDesc) palloc(sizeof(BitmapHeapScanDescData));
+
+	scan->heap_common.rs_base.rs_rd = relation;
+	scan->heap_common.rs_base.rs_snapshot = snapshot;
+	scan->heap_common.rs_base.rs_nkeys = 0;
+	scan->heap_common.rs_base.rs_flags = flags;
+	scan->heap_common.rs_base.rs_parallel = NULL;
+	scan->heap_common.rs_strategy = NULL;
+
+	Assert(snapshot && IsMVCCSnapshot(snapshot));
+
+	/* we only need to set this up once */
+	scan->heap_common.rs_ctup.t_tableOid = RelationGetRelid(relation);
+
+	scan->heap_common.rs_parallelworkerdata = NULL;
+	scan->heap_common.rs_base.rs_key = NULL;
+
+	initscan(&scan->heap_common, NULL, false);
+
+	scan->vmbuffer = InvalidBuffer;
+
+	return (TableScanDesc) scan;
+}
+
 
 TableScanDesc
 heap_beginscan(Relation relation, Snapshot snapshot,
@@ -967,8 +1007,6 @@ heap_beginscan(Relation relation, Snapshot snapshot,
 	scan->rs_base.rs_flags = flags;
 	scan->rs_base.rs_parallel = parallel_scan;
 	scan->rs_strategy = NULL;	/* set in initscan */
-	scan->rs_vmbuffer = InvalidBuffer;
-	scan->rs_empty_tuples_pending = 0;
 
 	/*
 	 * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
@@ -1026,6 +1064,29 @@ heap_beginscan(Relation relation, Snapshot snapshot,
 	return (TableScanDesc) scan;
 }
 
+/*
+ * Cleanup BitmapHeapScan table state
+ */
+void
+heap_endscan_bm(TableScanDesc sscan)
+{
+	BitmapHeapScanDesc scan = (BitmapHeapScanDesc) sscan;
+
+	if (BufferIsValid(scan->heap_common.rs_cbuf))
+		ReleaseBuffer(scan->heap_common.rs_cbuf);
+
+	if (BufferIsValid(scan->vmbuffer))
+		ReleaseBuffer(scan->vmbuffer);
+
+	/*
+	 * decrement relation reference count and free scan descriptor storage
+	 */
+	RelationDecrementReferenceCount(scan->heap_common.rs_base.rs_rd);
+
+	pfree(scan);
+}
+
+
 void
 heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
 			bool allow_strat, bool allow_sync, bool allow_pagemode)
@@ -1057,12 +1118,6 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
 	if (BufferIsValid(scan->rs_cbuf))
 		ReleaseBuffer(scan->rs_cbuf);
 
-	if (BufferIsValid(scan->rs_vmbuffer))
-	{
-		ReleaseBuffer(scan->rs_vmbuffer);
-		scan->rs_vmbuffer = InvalidBuffer;
-	}
-
 	/*
 	 * reinitialize scan descriptor
 	 */
@@ -1082,12 +1137,6 @@ heap_endscan(TableScanDesc sscan)
 	if (BufferIsValid(scan->rs_cbuf))
 		ReleaseBuffer(scan->rs_cbuf);
 
-	if (BufferIsValid(scan->rs_vmbuffer))
-	{
-		ReleaseBuffer(scan->rs_vmbuffer);
-		scan->rs_vmbuffer = InvalidBuffer;
-	}
-
 	/*
 	 * decrement relation reference count and free scan descriptor storage
 	 */
@@ -1334,6 +1383,27 @@ heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction,
 	return true;
 }
 
+void
+heap_rescan_bm(TableScanDesc sscan)
+{
+	BitmapHeapScanDesc scan = (BitmapHeapScanDesc) sscan;
+
+	if (BufferIsValid(scan->heap_common.rs_cbuf))
+		ReleaseBuffer(scan->heap_common.rs_cbuf);
+
+	if (BufferIsValid(scan->vmbuffer))
+		ReleaseBuffer(scan->vmbuffer);
+	scan->vmbuffer = InvalidBuffer;
+
+	scan->empty_tuples_pending = 0;
+
+	/*
+	 * reinitialize heap scan descriptor
+	 */
+	initscan(&scan->heap_common, NULL, true);
+}
+
+
 /*
  *	heap_fetch		- retrieve tuple with given tid
  *
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 5e57d19d91..43721cbd42 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -2187,11 +2187,12 @@ heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
  */
 
 static bool
-heapam_scan_bitmap_next_block(TableScanDesc scan,
+heapam_scan_bitmap_next_block(TableScanDesc sscan,
 							  BlockNumber *blockno, bool *recheck,
 							  long *lossy_pages, long *exact_pages)
 {
-	HeapScanDesc hscan = (HeapScanDesc) scan;
+	BitmapHeapScanDesc scan = (BitmapHeapScanDesc) sscan;
+	HeapScanDesc hscan = &scan->heap_common;
 	BlockNumber block;
 	Buffer		buffer;
 	Snapshot	snapshot;
@@ -2208,12 +2209,12 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	{
 		CHECK_FOR_INTERRUPTS();
 
-		tbmres = bhs_iterate(&scan->rs_bhs_iterator);
+		tbmres = bhs_iterate(&sscan->rs_bhs_iterator);
 
 		if (tbmres == NULL)
 		{
 			/* no more entries in the bitmap */
-			Assert(hscan->rs_empty_tuples_pending == 0);
+			Assert(scan->empty_tuples_pending == 0);
 			return false;
 		}
 
@@ -2236,15 +2237,15 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	 * heap, the bitmap entries don't need rechecking, and all tuples on the
 	 * page are visible to our transaction.
 	 */
-	if (!(scan->rs_flags & SO_NEED_TUPLE) &&
+	if (!(hscan->rs_base.rs_flags & SO_NEED_TUPLE) &&
 		!tbmres->recheck &&
-		VM_ALL_VISIBLE(scan->rs_rd, tbmres->blockno, &hscan->rs_vmbuffer))
+		VM_ALL_VISIBLE(hscan->rs_base.rs_rd, tbmres->blockno, &scan->vmbuffer))
 	{
 		/* can't be lossy in the skip_fetch case */
 		Assert(tbmres->ntuples >= 0);
-		Assert(hscan->rs_empty_tuples_pending >= 0);
+		Assert(scan->empty_tuples_pending >= 0);
 
-		hscan->rs_empty_tuples_pending += tbmres->ntuples;
+		scan->empty_tuples_pending += tbmres->ntuples;
 
 		return true;
 	}
@@ -2255,18 +2256,18 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	 * Acquire pin on the target heap page, trading in any pin we held before.
 	 */
 	hscan->rs_cbuf = ReleaseAndReadBuffer(hscan->rs_cbuf,
-										  scan->rs_rd,
+										  hscan->rs_base.rs_rd,
 										  block);
 	hscan->rs_cblock = block;
 	buffer = hscan->rs_cbuf;
-	snapshot = scan->rs_snapshot;
+	snapshot = hscan->rs_base.rs_snapshot;
 
 	ntup = 0;
 
 	/*
 	 * Prune and repair fragmentation for the whole page, if possible.
 	 */
-	heap_page_prune_opt(scan->rs_rd, buffer);
+	heap_page_prune_opt(hscan->rs_base.rs_rd, buffer);
 
 	/*
 	 * We must hold share lock on the buffer content while examining tuple
@@ -2294,7 +2295,7 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 			HeapTupleData heapTuple;
 
 			ItemPointerSet(&tid, block, offnum);
-			if (heap_hot_search_buffer(&tid, scan->rs_rd, buffer, snapshot,
+			if (heap_hot_search_buffer(&tid, hscan->rs_base.rs_rd, buffer, snapshot,
 									   &heapTuple, NULL, true))
 				hscan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
 		}
@@ -2320,16 +2321,16 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 				continue;
 			loctup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
 			loctup.t_len = ItemIdGetLength(lp);
-			loctup.t_tableOid = scan->rs_rd->rd_id;
+			loctup.t_tableOid = hscan->rs_base.rs_rd->rd_id;
 			ItemPointerSet(&loctup.t_self, block, offnum);
 			valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
 			if (valid)
 			{
 				hscan->rs_vistuples[ntup++] = offnum;
-				PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
+				PredicateLockTID(hscan->rs_base.rs_rd, &loctup.t_self, snapshot,
 								 HeapTupleHeaderGetXmin(loctup.t_data));
 			}
-			HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
+			HeapCheckForSerializableConflictOut(valid, hscan->rs_base.rs_rd, &loctup,
 												buffer, snapshot);
 		}
 	}
@@ -2358,18 +2359,19 @@ static bool
 heapam_scan_bitmap_next_tuple(TableScanDesc scan,
 							  TupleTableSlot *slot)
 {
-	HeapScanDesc hscan = (HeapScanDesc) scan;
+	BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
+	HeapScanDesc hscan = &bscan->heap_common;
 	OffsetNumber targoffset;
 	Page		page;
 	ItemId		lp;
 
-	if (hscan->rs_empty_tuples_pending > 0)
+	if (bscan->empty_tuples_pending > 0)
 	{
 		/*
 		 * If we don't have to fetch the tuple, just return nulls.
 		 */
 		ExecStoreAllNullTuple(slot);
-		hscan->rs_empty_tuples_pending--;
+		bscan->empty_tuples_pending--;
 		return true;
 	}
 
@@ -2704,6 +2706,10 @@ static const TableAmRoutine heapam_methods = {
 	.scan_set_tidrange = heap_set_tidrange,
 	.scan_getnextslot_tidrange = heap_getnextslot_tidrange,
 
+	.scan_rescan_bm = heap_rescan_bm,
+	.scan_begin_bm = heap_beginscan_bm,
+	.scan_end_bm = heap_endscan_bm,
+
 	.parallelscan_estimate = table_block_parallelscan_estimate,
 	.parallelscan_initialize = table_block_parallelscan_initialize,
 	.parallelscan_reinitialize = table_block_parallelscan_reinitialize,
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 70b560b8ee..0b28c46d08 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -151,6 +151,8 @@ BitmapHeapNext(BitmapHeapScanState *node)
 	 */
 	if (!node->initialized)
 	{
+		uint32		extra_flags = 0;
+
 		/*
 		 * The leader will immediately come out of the function, but others
 		 * will be blocked until leader populates the TBM and wakes them up.
@@ -193,33 +195,26 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			}
 		}
 
+		/*
+		 * We can potentially skip fetching heap pages if we do not need any
+		 * columns of the table, either for checking non-indexable quals or
+		 * for returning data.  This test is a bit simplistic, as it checks
+		 * the stronger condition that there's no qual or return tlist at all.
+		 * But in most cases it's probably not worth working harder than that.
+		 */
+		if (node->ss.ps.plan->qual != NIL || node->ss.ps.plan->targetlist != NIL)
+			extra_flags |= SO_NEED_TUPLE;
+
 		/*
 		 * If this is the first scan of the underlying table, create the table
 		 * scan descriptor and begin the scan.
 		 */
-		if (!scan)
-		{
-			uint32		extra_flags = 0;
+		scan = table_beginscan_bm(node->ss.ss_currentScanDesc,
+								  node->ss.ss_currentRelation,
+								  node->ss.ps.state->es_snapshot,
+								  extra_flags);
 
-			/*
-			 * We can potentially skip fetching heap pages if we do not need
-			 * any columns of the table, either for checking non-indexable
-			 * quals or for returning data.  This test is a bit simplistic, as
-			 * it checks the stronger condition that there's no qual or return
-			 * tlist at all. But in most cases it's probably not worth working
-			 * harder than that.
-			 */
-			if (node->ss.ps.plan->qual != NIL || node->ss.ps.plan->targetlist != NIL)
-				extra_flags |= SO_NEED_TUPLE;
-
-			scan = table_beginscan_bm(node->ss.ss_currentRelation,
-									  node->ss.ps.state->es_snapshot,
-									  0,
-									  NULL,
-									  extra_flags);
-
-			node->ss.ss_currentScanDesc = scan;
-		}
+		node->ss.ss_currentScanDesc = scan;
 
 		bhs_begin_iterate(&scan->rs_bhs_iterator, tbm, dsa,
 						  pstate ?
@@ -593,10 +588,6 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
 {
 	PlanState  *outerPlan = outerPlanState(node);
 
-	/* rescan to release any page pin */
-	if (node->ss.ss_currentScanDesc)
-		table_rescan(node->ss.ss_currentScanDesc, NULL);
-
 	/* release bitmaps and buffers if any */
 	if (node->pf_iterator.exhausted)
 		bhs_end_iterate(&node->pf_iterator);
@@ -642,10 +633,10 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
 
 
 	/*
-	 * close heap scan
+	 * close bitmapheap scan
 	 */
 	if (scanDesc)
-		table_endscan(scanDesc);
+		table_endscan_bm(scanDesc);
 
 	/*
 	 * release bitmaps and buffers if any
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 750ea30852..545760aab5 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -76,22 +76,35 @@ typedef struct HeapScanDescData
 	 */
 	ParallelBlockTableScanWorkerData *rs_parallelworkerdata;
 
+	/* these fields only used in page-at-a-time mode and for bitmap scans */
+	int			rs_cindex;		/* current tuple's index in vistuples */
+	int			rs_ntuples;		/* number of visible tuples on page */
+	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
+}			HeapScanDescData;
+typedef struct HeapScanDescData *HeapScanDesc;
+
+
+typedef struct BitmapHeapScanDescData
+{
+	/* All the non-BitmapHeapScan specific members */
+	HeapScanDescData heap_common;
+
+	/*
+	 * Members common to Parallel and Serial BitmapHeapScan
+	 */
+
 	/*
 	 * These fields are only used for bitmap scans for the "skip fetch"
 	 * optimization. Bitmap scans needing no fields from the heap may skip
 	 * fetching an all visible block, instead using the number of tuples per
 	 * block reported by the bitmap to determine how many NULL-filled tuples
-	 * to return.
+	 * to return. They are common to parallel and serial BitmapHeapScans
 	 */
-	Buffer		rs_vmbuffer;
-	int			rs_empty_tuples_pending;
+	Buffer		vmbuffer;
+	int			empty_tuples_pending;
+}			BitmapHeapScanDescData;
 
-	/* these fields only used in page-at-a-time mode and for bitmap scans */
-	int			rs_cindex;		/* current tuple's index in vistuples */
-	int			rs_ntuples;		/* number of visible tuples on page */
-	OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
-}			HeapScanDescData;
-typedef struct HeapScanDescData *HeapScanDesc;
+typedef struct BitmapHeapScanDescData *BitmapHeapScanDesc;
 
 /*
  * Descriptor for fetches from heap via an index.
@@ -289,6 +302,11 @@ extern void heap_set_tidrange(TableScanDesc sscan, ItemPointer mintid,
 extern bool heap_getnextslot_tidrange(TableScanDesc sscan,
 									  ScanDirection direction,
 									  TupleTableSlot *slot);
+extern TableScanDesc heap_beginscan_bm(Relation relation, Snapshot snapshot, uint32 flags);
+
+extern void heap_endscan_bm(TableScanDesc scan);
+extern void heap_rescan_bm(TableScanDesc sscan);
+
 extern bool heap_fetch(Relation relation, Snapshot snapshot,
 					   HeapTuple tuple, Buffer *userbuf, bool keep_buf);
 extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index 618ab2b449..3b0fa0610b 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -391,6 +391,21 @@ typedef struct TableAmRoutine
 											  ScanDirection direction,
 											  TupleTableSlot *slot);
 
+	/*
+	 * TODO: add comment
+	 */
+	TableScanDesc (*scan_begin_bm) (Relation rel,
+									Snapshot snapshot,
+									uint32 flags);
+
+	void		(*scan_rescan_bm) (TableScanDesc scan);
+
+	/*
+	 * Release resources and deallocate scan. If TableScanDesc.temp_snap,
+	 * TableScanDesc.rs_snapshot needs to be unregistered.
+	 */
+	void		(*scan_end_bm) (TableScanDesc scan);
+
 	/* ------------------------------------------------------------------------
 	 * Parallel table scan related functions.
 	 * ------------------------------------------------------------------------
@@ -929,18 +944,34 @@ table_beginscan_strat(Relation rel, Snapshot snapshot,
 }
 
 /*
- * table_beginscan_bm is an alternative entry point for setting up a
- * TableScanDesc for a bitmap heap scan.  Although that scan technology is
- * really quite unlike a standard seqscan, there is just enough commonality to
- * make it worth using the same data structure.
+ * table_beginscan_bm is the entry point for setting up a TableScanDesc for a
+ * bitmap heap scan.
  */
 static inline TableScanDesc
-table_beginscan_bm(Relation rel, Snapshot snapshot,
-				   int nkeys, struct ScanKeyData *key, uint32 extra_flags)
+table_beginscan_bm(TableScanDesc scan, Relation rel, Snapshot snapshot,
+				   uint32 extra_flags)
 {
 	uint32		flags = SO_TYPE_BITMAPSCAN | SO_ALLOW_PAGEMODE | extra_flags;
 
-	return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
+	/*
+	 * If this is the first scan of the underlying table, create the table
+	 * scan descriptor and begin the scan.
+	 */
+	if (!scan)
+		scan = rel->rd_tableam->scan_begin_bm(rel, snapshot, flags);
+
+	scan->rs_rd->rd_tableam->scan_rescan_bm(scan);
+
+	return scan;
+}
+
+/*
+ * End Bitmap Table Scan
+ */
+static inline void
+table_endscan_bm(TableScanDesc scan)
+{
+	scan->rs_rd->rd_tableam->scan_end_bm(scan);
 }
 
 /*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 848ce9b30c..8369c15a82 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -263,6 +263,7 @@ BitmapHeapIterator
 BitmapHeapPath
 BitmapHeapScan
 BitmapHeapScanState
+BitmapHeapScanDesc
 BitmapIndexScan
 BitmapIndexScanState
 BitmapOr
-- 
2.40.1

