From 4d5dcb8783d92a2e2a37ea37bbf346545cd09337 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Fri, 22 Mar 2024 09:42:23 -0400
Subject: [PATCH v10 14/17] Push BitmapHeapScan prefetch code into heapam.c

In preparation for transitioning to using the streaming read API for
prefetching [1], move all of the BitmapHeapScanState members related to
prefetching and the functions for accessing them into the
HeapScanDescData and TableScanDescData. Members that still need to be
accessed in BitmapHeapNext() could not be moved into heap AM-specific
code. Specifically, parallel iterator setup requires several components
which seem odd to pass to the table AM API.

[1] https://www.postgresql.org/message-id/flat/CA%2BhUKGJkOiOCa%2Bmag4BF%2BzHo7qo%3Do9CFheB8%3Dg6uT5TUm2gkvA%40mail.gmail.com
---
 src/backend/access/heap/heapam.c          |  26 ++
 src/backend/access/heap/heapam_handler.c  | 262 +++++++++++++++++
 src/backend/executor/nodeBitmapHeapscan.c | 341 ++--------------------
 src/include/access/heapam.h               |  17 ++
 src/include/access/relscan.h              |   8 +
 src/include/access/tableam.h              |  26 +-
 src/include/nodes/execnodes.h             |  14 -
 7 files changed, 355 insertions(+), 339 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6b2863391f..3d92fb5135 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -948,8 +948,16 @@ 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_base.blockno = InvalidBlockNumber;
+
 	scan->rs_vmbuffer = InvalidBuffer;
 	scan->rs_empty_tuples_pending = 0;
+	scan->pvmbuffer = InvalidBuffer;
+
+	scan->pfblockno = InvalidBlockNumber;
+	scan->prefetch_target = -1;
+	scan->prefetch_pages = 0;
 
 	/*
 	 * Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
@@ -1032,6 +1040,12 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
 			scan->rs_base.rs_flags &= ~SO_ALLOW_PAGEMODE;
 	}
 
+	scan->rs_base.blockno = InvalidBlockNumber;
+
+	scan->pfblockno = InvalidBlockNumber;
+	scan->prefetch_target = -1;
+	scan->prefetch_pages = 0;
+
 	/*
 	 * unpin scan buffers
 	 */
@@ -1044,6 +1058,12 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
 		scan->rs_vmbuffer = InvalidBuffer;
 	}
 
+	if (BufferIsValid(scan->pvmbuffer))
+	{
+		ReleaseBuffer(scan->pvmbuffer);
+		scan->pvmbuffer = InvalidBuffer;
+	}
+
 	/*
 	 * reinitialize scan descriptor
 	 */
@@ -1069,6 +1089,12 @@ heap_endscan(TableScanDesc sscan)
 		scan->rs_vmbuffer = InvalidBuffer;
 	}
 
+	if (BufferIsValid(scan->pvmbuffer))
+	{
+		ReleaseBuffer(scan->pvmbuffer);
+		scan->pvmbuffer = InvalidBuffer;
+	}
+
 	/*
 	 * decrement relation reference count and free scan descriptor storage
 	 */
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 105137396b..867061325c 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -55,6 +55,9 @@ static bool SampleHeapTupleVisible(TableScanDesc scan, Buffer buffer,
 								   OffsetNumber tupoffset);
 
 static BlockNumber heapam_scan_get_blocks_done(HeapScanDesc hscan);
+static inline void BitmapAdjustPrefetchIterator(HeapScanDesc scan);
+static inline void BitmapAdjustPrefetchTarget(HeapScanDesc scan);
+static inline void BitmapPrefetch(HeapScanDesc scan);
 
 static const TableAmRoutine heapam_methods;
 
@@ -2112,6 +2115,73 @@ heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
  * ------------------------------------------------------------------------
  */
 
+/*
+ *	BitmapAdjustPrefetchIterator - Adjust the prefetch iterator
+ *
+ *	We keep track of how far the prefetch iterator is ahead of the main
+ *	iterator in prefetch_pages. For each block the main iterator returns, we
+ *	decrement prefetch_pages.
+ */
+static inline void
+BitmapAdjustPrefetchIterator(HeapScanDesc scan)
+{
+#ifdef USE_PREFETCH
+	BitmapHeapIterator *prefetch_iterator = scan->rs_base.rs_pf_bhs_iterator;
+	ParallelBitmapHeapState *pstate = scan->rs_base.bm_parallel;
+	TBMIterateResult tbmpre;
+
+	if (pstate == NULL)
+	{
+		if (scan->prefetch_pages > 0)
+		{
+			/* The main iterator has closed the distance by one page */
+			scan->prefetch_pages--;
+		}
+		else if (prefetch_iterator)
+		{
+			/* Do not let the prefetch iterator get behind the main one */
+			bhs_iterate(prefetch_iterator, &tbmpre);
+			scan->pfblockno = tbmpre.blockno;
+		}
+		return;
+	}
+
+	/*
+	 * Adjusting the prefetch iterator before invoking
+	 * table_scan_bitmap_next_block() keeps prefetch distance higher across
+	 * the parallel workers.
+	 */
+	if (scan->rs_base.prefetch_maximum > 0)
+	{
+		SpinLockAcquire(&pstate->mutex);
+		if (pstate->prefetch_pages > 0)
+		{
+			pstate->prefetch_pages--;
+			SpinLockRelease(&pstate->mutex);
+		}
+		else
+		{
+			/* Release the mutex before iterating */
+			SpinLockRelease(&pstate->mutex);
+
+			/*
+			 * In case of shared mode, we can not ensure that the current
+			 * blockno of the main iterator and that of the prefetch iterator
+			 * are same.  It's possible that whatever blockno we are
+			 * prefetching will be processed by another process.  Therefore,
+			 * we don't validate the blockno here as we do in non-parallel
+			 * case.
+			 */
+			if (prefetch_iterator)
+			{
+				bhs_iterate(prefetch_iterator, &tbmpre);
+				scan->pfblockno = tbmpre.blockno;
+			}
+		}
+	}
+#endif							/* USE_PREFETCH */
+}
+
 static bool
 heapam_scan_bitmap_next_block(TableScanDesc scan,
 							  bool *recheck, BlockNumber *blockno,
@@ -2130,6 +2200,8 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	*blockno = InvalidBlockNumber;
 	*recheck = true;
 
+	BitmapAdjustPrefetchIterator(hscan);
+
 	do
 	{
 		CHECK_FOR_INTERRUPTS();
@@ -2270,6 +2342,18 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	else
 		(*exact_pages)++;
 
+	/*
+	 * If serial, we can error out if the the prefetch block doesn't stay
+	 * ahead of the current block.
+	 */
+	if (scan->bm_parallel == NULL &&
+		scan->rs_pf_bhs_iterator &&
+		hscan->pfblockno > hscan->rs_base.blockno)
+		elog(ERROR, "prefetch and main iterators are out of sync");
+
+	/* Adjust the prefetch target */
+	BitmapAdjustPrefetchTarget(hscan);
+
 	/*
 	 * Return true to indicate that a valid block was found and the bitmap is
 	 * not exhausted. If there are no visible tuples on this page,
@@ -2280,6 +2364,154 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
 	return true;
 }
 
+/*
+ * BitmapAdjustPrefetchTarget - Adjust the prefetch target
+ *
+ * Increase prefetch target if it's not yet at the max.  Note that
+ * we will increase it to zero after fetching the very first
+ * page/tuple, then to one after the second tuple is fetched, then
+ * it doubles as later pages are fetched.
+ */
+static inline void
+BitmapAdjustPrefetchTarget(HeapScanDesc scan)
+{
+#ifdef USE_PREFETCH
+	ParallelBitmapHeapState *pstate = scan->rs_base.bm_parallel;
+	int			prefetch_maximum = scan->rs_base.prefetch_maximum;
+
+	if (pstate == NULL)
+	{
+		if (scan->prefetch_target >= prefetch_maximum)
+			 /* don't increase any further */ ;
+		else if (scan->prefetch_target >= prefetch_maximum / 2)
+			scan->prefetch_target = prefetch_maximum;
+		else if (scan->prefetch_target > 0)
+			scan->prefetch_target *= 2;
+		else
+			scan->prefetch_target++;
+		return;
+	}
+
+	/* Do an unlocked check first to save spinlock acquisitions. */
+	if (pstate->prefetch_target < prefetch_maximum)
+	{
+		SpinLockAcquire(&pstate->mutex);
+		if (pstate->prefetch_target >= prefetch_maximum)
+			 /* don't increase any further */ ;
+		else if (pstate->prefetch_target >= prefetch_maximum / 2)
+			pstate->prefetch_target = prefetch_maximum;
+		else if (pstate->prefetch_target > 0)
+			pstate->prefetch_target *= 2;
+		else
+			pstate->prefetch_target++;
+		SpinLockRelease(&pstate->mutex);
+	}
+#endif							/* USE_PREFETCH */
+}
+
+
+/*
+ * BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
+ */
+static inline void
+BitmapPrefetch(HeapScanDesc scan)
+{
+#ifdef USE_PREFETCH
+	ParallelBitmapHeapState *pstate = scan->rs_base.bm_parallel;
+	BitmapHeapIterator *prefetch_iterator = scan->rs_base.rs_pf_bhs_iterator;
+
+	if (pstate == NULL)
+	{
+		if (prefetch_iterator)
+		{
+			while (scan->prefetch_pages < scan->prefetch_target)
+			{
+				TBMIterateResult tbmpre;
+				bool		skip_fetch;
+
+				bhs_iterate(prefetch_iterator, &tbmpre);
+
+				if (!BlockNumberIsValid(tbmpre.blockno))
+				{
+					/* No more pages to prefetch */
+					bhs_end_iterate(prefetch_iterator);
+					scan->rs_base.rs_pf_bhs_iterator = NULL;
+					break;
+				}
+				scan->prefetch_pages++;
+				scan->pfblockno = tbmpre.blockno;
+
+				/*
+				 * If we expect not to have to actually read this heap page,
+				 * skip this prefetch call, but continue to run the prefetch
+				 * logic normally.  (Would it be better not to increment
+				 * prefetch_pages?)
+				 */
+				skip_fetch = (!(scan->rs_base.rs_flags & SO_NEED_TUPLE) &&
+							  !tbmpre.recheck &&
+							  VM_ALL_VISIBLE(scan->rs_base.rs_rd,
+											 tbmpre.blockno,
+											 &scan->pvmbuffer));
+
+				if (!skip_fetch)
+					PrefetchBuffer(scan->rs_base.rs_rd, MAIN_FORKNUM, tbmpre.blockno);
+			}
+		}
+
+		return;
+	}
+
+	if (pstate->prefetch_pages < pstate->prefetch_target)
+	{
+		if (prefetch_iterator)
+		{
+			while (1)
+			{
+				TBMIterateResult tbmpre;
+				bool		do_prefetch = false;
+				bool		skip_fetch;
+
+				/*
+				 * Recheck under the mutex. If some other process has already
+				 * done enough prefetching then we need not to do anything.
+				 */
+				SpinLockAcquire(&pstate->mutex);
+				if (pstate->prefetch_pages < pstate->prefetch_target)
+				{
+					pstate->prefetch_pages++;
+					do_prefetch = true;
+				}
+				SpinLockRelease(&pstate->mutex);
+
+				if (!do_prefetch)
+					return;
+
+				bhs_iterate(prefetch_iterator, &tbmpre);
+				if (!BlockNumberIsValid(tbmpre.blockno))
+				{
+					/* No more pages to prefetch */
+					bhs_end_iterate(prefetch_iterator);
+					scan->rs_base.rs_pf_bhs_iterator = NULL;
+					break;
+				}
+
+				scan->pfblockno = tbmpre.blockno;
+
+				/* As above, skip prefetch if we expect not to need page */
+				skip_fetch = (!(scan->rs_base.rs_flags & SO_NEED_TUPLE) &&
+							  !tbmpre.recheck &&
+							  VM_ALL_VISIBLE(scan->rs_base.rs_rd,
+											 tbmpre.blockno,
+											 &scan->pvmbuffer));
+
+				if (!skip_fetch)
+					PrefetchBuffer(scan->rs_base.rs_rd, MAIN_FORKNUM, tbmpre.blockno);
+			}
+		}
+	}
+#endif							/* USE_PREFETCH */
+}
+
 static bool
 heapam_scan_bitmap_next_tuple(TableScanDesc scan,
 							  TupleTableSlot *slot)
@@ -2305,6 +2537,36 @@ heapam_scan_bitmap_next_tuple(TableScanDesc scan,
 	if (hscan->rs_cindex < 0 || hscan->rs_cindex >= hscan->rs_ntuples)
 		return false;
 
+#ifdef USE_PREFETCH
+
+	/*
+	 * Try to prefetch at least a few pages even before we get to the second
+	 * page if we don't stop reading after the first tuple.
+	 */
+	if (!scan->bm_parallel)
+	{
+		if (hscan->prefetch_target < scan->prefetch_maximum)
+			hscan->prefetch_target++;
+	}
+	else if (scan->bm_parallel->prefetch_target < scan->prefetch_maximum)
+	{
+		/* take spinlock while updating shared state */
+		SpinLockAcquire(&scan->bm_parallel->mutex);
+		if (scan->bm_parallel->prefetch_target < scan->prefetch_maximum)
+			scan->bm_parallel->prefetch_target++;
+		SpinLockRelease(&scan->bm_parallel->mutex);
+	}
+
+	/*
+	 * We issue prefetch requests *after* fetching the current page to try to
+	 * avoid having prefetching interfere with the main I/O. Also, this should
+	 * happen only when we have determined there is still something to do on
+	 * the current page, else we may uselessly prefetch the same page we are
+	 * just about to request for real.
+	 */
+	BitmapPrefetch(hscan);
+#endif							/* USE_PREFETCH */
+
 	targoffset = hscan->rs_vistuples[hscan->rs_cindex];
 	page = BufferGetPage(hscan->rs_cbuf);
 	lp = PageGetItemId(page, targoffset);
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 78f79aafff..187b288e68 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -51,10 +51,6 @@
 
 static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
 static inline void BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate);
-static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node);
-static inline void BitmapAdjustPrefetchTarget(BitmapHeapScanState *node);
-static inline void BitmapPrefetch(BitmapHeapScanState *node,
-								  TableScanDesc scan);
 static bool BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate);
 static BitmapHeapIterator *bhs_begin_iterate(TIDBitmap *tbm,
 											 dsa_pointer shared_area,
@@ -122,7 +118,6 @@ BitmapHeapNext(BitmapHeapScanState *node)
 	TableScanDesc scan;
 	TIDBitmap  *tbm;
 	TupleTableSlot *slot;
-	ParallelBitmapHeapState *pstate = node->pstate;
 	dsa_area   *dsa = node->ss.ps.state->es_query_dsa;
 
 	/*
@@ -142,7 +137,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 	 * prefetching.  node->prefetch_pages tracks exactly how many pages ahead
 	 * the prefetch iterator is.  Also, node->prefetch_target tracks the
 	 * desired prefetch distance, which starts small and increases up to the
-	 * node->prefetch_maximum.  This is to avoid doing a lot of prefetching in
+	 * scan->prefetch_maximum.  This is to avoid doing a lot of prefetching in
 	 * a scan that stops after a few tuples because of a LIMIT.
 	 */
 	if (!node->initialized)
@@ -154,7 +149,16 @@ BitmapHeapNext(BitmapHeapScanState *node)
 		bool		init_shared_state = node->pstate ?
 			BitmapShouldInitializeSharedState(node->pstate) : false;
 
-		if (!pstate || init_shared_state)
+		/*
+		 * Maximum number of prefetches for the tablespace if configured,
+		 * otherwise the current value of the effective_io_concurrency GUC.
+		 */
+		int			pf_maximum = 0;
+#ifdef USE_PREFETCH
+		pf_maximum = get_tablespace_io_concurrency(node->ss.ss_currentRelation->rd_rel->reltablespace);
+#endif
+
+		if (!node->pstate || init_shared_state)
 		{
 			tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
 
@@ -169,23 +173,16 @@ BitmapHeapNext(BitmapHeapScanState *node)
 				 * dsa_pointer of the iterator state which will be used by
 				 * multiple processes to iterate jointly.
 				 */
-				pstate->tbmiterator = tbm_prepare_shared_iterate(tbm);
+				node->pstate->tbmiterator = tbm_prepare_shared_iterate(tbm);
 #ifdef USE_PREFETCH
-				if (node->prefetch_maximum > 0)
+				if (pf_maximum > 0)
 				{
-					pstate->prefetch_iterator =
+					node->pstate->prefetch_iterator =
 						tbm_prepare_shared_iterate(tbm);
-
-					/*
-					 * We don't need the mutex here as we haven't yet woke up
-					 * others.
-					 */
-					pstate->prefetch_pages = 0;
-					pstate->prefetch_target = -1;
 				}
 #endif
 				/* We have initialized the shared state so wake up others. */
-				BitmapDoneInitializingSharedState(pstate);
+				BitmapDoneInitializingSharedState(node->pstate);
 			}
 		}
 
@@ -216,19 +213,19 @@ BitmapHeapNext(BitmapHeapScanState *node)
 																	extra_flags);
 		}
 
+		scan->prefetch_maximum = pf_maximum;
+		scan->bm_parallel = node->pstate;
+
 		scan->rs_bhs_iterator = bhs_begin_iterate(tbm,
-												  pstate ? pstate->tbmiterator : InvalidDsaPointer,
+												  scan->bm_parallel ? scan->bm_parallel->tbmiterator : InvalidDsaPointer,
 												  dsa);
 
 #ifdef USE_PREFETCH
-		if (node->prefetch_maximum > 0)
+		if (scan->prefetch_maximum > 0)
 		{
-			node->pf_iterator = bhs_begin_iterate(tbm,
-												  pstate ? pstate->prefetch_iterator : InvalidDsaPointer,
-												  dsa);
-			/* Only used for serial BHS */
-			node->prefetch_pages = 0;
-			node->prefetch_target = -1;
+			scan->rs_pf_bhs_iterator = bhs_begin_iterate(tbm,
+														 scan->bm_parallel ? scan->bm_parallel->prefetch_iterator : InvalidDsaPointer,
+														 dsa);
 		}
 #endif							/* USE_PREFETCH */
 
@@ -243,36 +240,6 @@ BitmapHeapNext(BitmapHeapScanState *node)
 		{
 			CHECK_FOR_INTERRUPTS();
 
-#ifdef USE_PREFETCH
-
-			/*
-			 * Try to prefetch at least a few pages even before we get to the
-			 * second page if we don't stop reading after the first tuple.
-			 */
-			if (!pstate)
-			{
-				if (node->prefetch_target < node->prefetch_maximum)
-					node->prefetch_target++;
-			}
-			else if (pstate->prefetch_target < node->prefetch_maximum)
-			{
-				/* take spinlock while updating shared state */
-				SpinLockAcquire(&pstate->mutex);
-				if (pstate->prefetch_target < node->prefetch_maximum)
-					pstate->prefetch_target++;
-				SpinLockRelease(&pstate->mutex);
-			}
-#endif							/* USE_PREFETCH */
-
-			/*
-			 * We issue prefetch requests *after* fetching the current page to
-			 * try to avoid having prefetching interfere with the main I/O.
-			 * Also, this should happen only when we have determined there is
-			 * still something to do on the current page, else we may
-			 * uselessly prefetch the same page we are just about to request
-			 * for real.
-			 */
-			BitmapPrefetch(node, scan);
 
 			/*
 			 * If we are using lossy info, we have to recheck the qual
@@ -296,23 +263,9 @@ BitmapHeapNext(BitmapHeapScanState *node)
 
 new_page:
 
-		BitmapAdjustPrefetchIterator(node);
-
-		if (!table_scan_bitmap_next_block(scan, &node->recheck, &node->blockno,
+		if (!table_scan_bitmap_next_block(scan, &node->recheck, &scan->blockno,
 										  &node->lossy_pages, &node->exact_pages))
 			break;
-
-		/*
-		 * If serial, we can error out if the the prefetch block doesn't stay
-		 * ahead of the current block.
-		 */
-		if (node->pstate == NULL &&
-			node->pf_iterator &&
-			node->pfblockno > node->blockno)
-			elog(ERROR, "prefetch and main iterators are out of sync");
-
-		/* Adjust the prefetch target */
-		BitmapAdjustPrefetchTarget(node);
 	}
 
 	/*
@@ -336,219 +289,6 @@ BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate)
 	ConditionVariableBroadcast(&pstate->cv);
 }
 
-/*
- *	BitmapAdjustPrefetchIterator - Adjust the prefetch iterator
- *
- *	We keep track of how far the prefetch iterator is ahead of the main
- *	iterator in prefetch_pages. For each block the main iterator returns, we
- *	decrement prefetch_pages.
- */
-static inline void
-BitmapAdjustPrefetchIterator(BitmapHeapScanState *node)
-{
-#ifdef USE_PREFETCH
-	ParallelBitmapHeapState *pstate = node->pstate;
-	BitmapHeapIterator *prefetch_iterator = node->pf_iterator;
-	TBMIterateResult tbmpre;
-
-	if (pstate == NULL)
-	{
-		if (node->prefetch_pages > 0)
-		{
-			/* The main iterator has closed the distance by one page */
-			node->prefetch_pages--;
-		}
-		else if (prefetch_iterator)
-		{
-			/* Do not let the prefetch iterator get behind the main one */
-			bhs_iterate(prefetch_iterator, &tbmpre);
-			node->pfblockno = tbmpre.blockno;
-		}
-		return;
-	}
-
-	/*
-	 * Adjusting the prefetch iterator before invoking
-	 * table_scan_bitmap_next_block() keeps prefetch distance higher across
-	 * the parallel workers.
-	 */
-	if (node->prefetch_maximum > 0)
-	{
-		SpinLockAcquire(&pstate->mutex);
-		if (pstate->prefetch_pages > 0)
-		{
-			pstate->prefetch_pages--;
-			SpinLockRelease(&pstate->mutex);
-		}
-		else
-		{
-			/* Release the mutex before iterating */
-			SpinLockRelease(&pstate->mutex);
-
-			/*
-			 * In case of shared mode, we can not ensure that the current
-			 * blockno of the main iterator and that of the prefetch iterator
-			 * are same.  It's possible that whatever blockno we are
-			 * prefetching will be processed by another process.  Therefore,
-			 * we don't validate the blockno here as we do in non-parallel
-			 * case.
-			 */
-			if (prefetch_iterator)
-			{
-				bhs_iterate(prefetch_iterator, &tbmpre);
-				node->pfblockno = tbmpre.blockno;
-			}
-		}
-	}
-#endif							/* USE_PREFETCH */
-}
-
-/*
- * BitmapAdjustPrefetchTarget - Adjust the prefetch target
- *
- * Increase prefetch target if it's not yet at the max.  Note that
- * we will increase it to zero after fetching the very first
- * page/tuple, then to one after the second tuple is fetched, then
- * it doubles as later pages are fetched.
- */
-static inline void
-BitmapAdjustPrefetchTarget(BitmapHeapScanState *node)
-{
-#ifdef USE_PREFETCH
-	ParallelBitmapHeapState *pstate = node->pstate;
-
-	if (pstate == NULL)
-	{
-		if (node->prefetch_target >= node->prefetch_maximum)
-			 /* don't increase any further */ ;
-		else if (node->prefetch_target >= node->prefetch_maximum / 2)
-			node->prefetch_target = node->prefetch_maximum;
-		else if (node->prefetch_target > 0)
-			node->prefetch_target *= 2;
-		else
-			node->prefetch_target++;
-		return;
-	}
-
-	/* Do an unlocked check first to save spinlock acquisitions. */
-	if (pstate->prefetch_target < node->prefetch_maximum)
-	{
-		SpinLockAcquire(&pstate->mutex);
-		if (pstate->prefetch_target >= node->prefetch_maximum)
-			 /* don't increase any further */ ;
-		else if (pstate->prefetch_target >= node->prefetch_maximum / 2)
-			pstate->prefetch_target = node->prefetch_maximum;
-		else if (pstate->prefetch_target > 0)
-			pstate->prefetch_target *= 2;
-		else
-			pstate->prefetch_target++;
-		SpinLockRelease(&pstate->mutex);
-	}
-#endif							/* USE_PREFETCH */
-}
-
-/*
- * BitmapPrefetch - Prefetch, if prefetch_pages are behind prefetch_target
- */
-static inline void
-BitmapPrefetch(BitmapHeapScanState *node, TableScanDesc scan)
-{
-#ifdef USE_PREFETCH
-	ParallelBitmapHeapState *pstate = node->pstate;
-	BitmapHeapIterator *prefetch_iterator = node->pf_iterator;
-
-	if (pstate == NULL)
-	{
-		if (prefetch_iterator)
-		{
-			while (node->prefetch_pages < node->prefetch_target)
-			{
-				TBMIterateResult tbmpre;
-				bool		skip_fetch;
-
-				bhs_iterate(prefetch_iterator, &tbmpre);
-
-				if (!BlockNumberIsValid(tbmpre.blockno))
-				{
-					/* No more pages to prefetch */
-					bhs_end_iterate(prefetch_iterator);
-					node->pf_iterator = NULL;
-					break;
-				}
-				node->prefetch_pages++;
-				node->pfblockno = tbmpre.blockno;
-
-				/*
-				 * If we expect not to have to actually read this heap page,
-				 * skip this prefetch call, but continue to run the prefetch
-				 * logic normally.  (Would it be better not to increment
-				 * prefetch_pages?)
-				 */
-				skip_fetch = (!(scan->rs_flags & SO_NEED_TUPLE) &&
-							  !tbmpre.recheck &&
-							  VM_ALL_VISIBLE(node->ss.ss_currentRelation,
-											 tbmpre.blockno,
-											 &node->pvmbuffer));
-
-				if (!skip_fetch)
-					PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre.blockno);
-			}
-		}
-
-		return;
-	}
-
-	if (pstate->prefetch_pages < pstate->prefetch_target)
-	{
-		if (prefetch_iterator)
-		{
-			while (1)
-			{
-				TBMIterateResult tbmpre;
-				bool		do_prefetch = false;
-				bool		skip_fetch;
-
-				/*
-				 * Recheck under the mutex. If some other process has already
-				 * done enough prefetching then we need not to do anything.
-				 */
-				SpinLockAcquire(&pstate->mutex);
-				if (pstate->prefetch_pages < pstate->prefetch_target)
-				{
-					pstate->prefetch_pages++;
-					do_prefetch = true;
-				}
-				SpinLockRelease(&pstate->mutex);
-
-				if (!do_prefetch)
-					return;
-
-				bhs_iterate(prefetch_iterator, &tbmpre);
-				if (!BlockNumberIsValid(tbmpre.blockno))
-				{
-					/* No more pages to prefetch */
-					bhs_end_iterate(prefetch_iterator);
-					node->pf_iterator = NULL;
-					break;
-				}
-
-				node->pfblockno = tbmpre.blockno;
-
-				/* As above, skip prefetch if we expect not to need page */
-				skip_fetch = (!(scan->rs_flags & SO_NEED_TUPLE) &&
-							  !tbmpre.recheck &&
-							  VM_ALL_VISIBLE(node->ss.ss_currentRelation,
-											 tbmpre.blockno,
-											 &node->pvmbuffer));
-
-				if (!skip_fetch)
-					PrefetchBuffer(scan->rs_rd, MAIN_FORKNUM, tbmpre.blockno);
-			}
-		}
-	}
-#endif							/* USE_PREFETCH */
-}
-
 /*
  * BitmapHeapRecheck -- access method routine to recheck a tuple in EvalPlanQual
  */
@@ -594,22 +334,12 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
 	if (node->ss.ss_currentScanDesc)
 		table_rescan(node->ss.ss_currentScanDesc, NULL);
 
-	/* release bitmaps and buffers if any */
-	if (node->pf_iterator)
-	{
-		bhs_end_iterate(node->pf_iterator);
-		node->pf_iterator = NULL;
-	}
+	/* release bitmaps if any */
 	if (node->tbm)
 		tbm_free(node->tbm);
-	if (node->pvmbuffer != InvalidBuffer)
-		ReleaseBuffer(node->pvmbuffer);
 	node->tbm = NULL;
 	node->initialized = false;
-	node->pvmbuffer = InvalidBuffer;
 	node->recheck = true;
-	node->blockno = InvalidBlockNumber;
-	node->pfblockno = InvalidBlockNumber;
 
 	ExecScanReScan(&node->ss);
 
@@ -648,14 +378,10 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
 		table_endscan(scanDesc);
 
 	/*
-	 * release bitmaps and buffers if any
+	 * release bitmaps if any
 	 */
-	if (node->pf_iterator)
-		bhs_end_iterate(node->pf_iterator);
 	if (node->tbm)
 		tbm_free(node->tbm);
-	if (node->pvmbuffer != InvalidBuffer)
-		ReleaseBuffer(node->pvmbuffer);
 }
 
 /* ----------------------------------------------------------------
@@ -688,17 +414,11 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.ExecProcNode = ExecBitmapHeapScan;
 
 	scanstate->tbm = NULL;
-	scanstate->pvmbuffer = InvalidBuffer;
 	scanstate->exact_pages = 0;
 	scanstate->lossy_pages = 0;
-	scanstate->pf_iterator = NULL;
-	scanstate->prefetch_pages = 0;
-	scanstate->prefetch_target = 0;
 	scanstate->initialized = false;
 	scanstate->pstate = NULL;
 	scanstate->recheck = true;
-	scanstate->blockno = InvalidBlockNumber;
-	scanstate->pfblockno = InvalidBlockNumber;
 
 	/*
 	 * Miscellaneous initialization
@@ -738,13 +458,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->bitmapqualorig =
 		ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
 
-	/*
-	 * Maximum number of prefetches for the tablespace if configured,
-	 * otherwise the current value of the effective_io_concurrency GUC.
-	 */
-	scanstate->prefetch_maximum =
-		get_tablespace_io_concurrency(currentRelation->rd_rel->reltablespace);
-
 	scanstate->ss.ss_currentRelation = currentRelation;
 
 	/*
@@ -828,7 +541,7 @@ ExecBitmapHeapInitializeDSM(BitmapHeapScanState *node,
 	/* Initialize the mutex */
 	SpinLockInit(&pstate->mutex);
 	pstate->prefetch_pages = 0;
-	pstate->prefetch_target = 0;
+	pstate->prefetch_target = -1;
 	pstate->state = BM_INITIAL;
 
 	ConditionVariableInit(&pstate->cv);
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index cef54e2d5d..29fdd55893 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -82,6 +82,23 @@ typedef struct HeapScanDescData
 	Buffer		rs_vmbuffer;
 	int			rs_empty_tuples_pending;
 
+	/*
+	 * These fields only used for prefetching in bitmap table scans
+	 */
+
+	/* buffer for visibility-map lookups of prefetched pages */
+	Buffer		pvmbuffer;
+
+	/*
+	 * These fields only used in serial BHS
+	 */
+	/* Current target for prefetch distance */
+	int			prefetch_target;
+	/* # pages prefetch iterator is ahead of current */
+	int			prefetch_pages;
+	/* used to validate prefetch block stays ahead of current block  */
+	BlockNumber pfblockno;
+
 	/* 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 */
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index fb22f305bf..7938b741d6 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -26,6 +26,7 @@
 struct ParallelTableScanDescData;
 
 struct BitmapHeapIterator;
+struct ParallelBitmapHeapState;
 
 /*
  * Generic descriptor for table scans. This is the base-class for table scans,
@@ -45,6 +46,13 @@ typedef struct TableScanDescData
 
 	/* Only used for Bitmap table scans */
 	struct BitmapHeapIterator *rs_bhs_iterator;
+	struct BitmapHeapIterator *rs_pf_bhs_iterator;
+
+	/* maximum value for prefetch_target */
+	int			prefetch_maximum;
+	struct ParallelBitmapHeapState *bm_parallel;
+	/* used to validate BHS prefetch and current block stay in sync */
+	BlockNumber blockno;
 
 	/*
 	 * Information about type and behaviour of the scan, a bitmask of members
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index edc54ecffe..284ea3d864 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -800,17 +800,6 @@ typedef struct TableAmRoutine
 	 * lossy_pages is incremented if the block's representation in the bitmap
 	 * is lossy, otherwise, exact_pages is incremented.
 	 *
-	 * XXX: Currently this may only be implemented if the AM uses md.c as its
-	 * storage manager, and uses ItemPointer->ip_blkid in a manner that maps
-	 * blockids directly to the underlying storage. nodeBitmapHeapscan.c
-	 * performs prefetching directly using that interface.  This probably
-	 * needs to be rectified at a later point.
-	 *
-	 * XXX: Currently this may only be implemented if the AM uses the
-	 * visibilitymap, as nodeBitmapHeapscan.c unconditionally accesses it to
-	 * perform prefetching.  This probably needs to be rectified at a later
-	 * point.
-	 *
 	 * Optional callback, but either both scan_bitmap_next_block and
 	 * scan_bitmap_next_tuple need to exist, or neither.
 	 */
@@ -960,6 +949,9 @@ table_beginscan_bm(Relation rel, Snapshot snapshot,
 
 	result = rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
 	result->rs_bhs_iterator = NULL;
+	result->rs_pf_bhs_iterator = NULL;
+	result->prefetch_maximum = 0;
+	result->bm_parallel = NULL;
 	return result;
 }
 
@@ -1024,6 +1016,12 @@ table_endscan(TableScanDesc scan)
 	{
 		bhs_end_iterate(scan->rs_bhs_iterator);
 		scan->rs_bhs_iterator = NULL;
+
+		if (scan->rs_pf_bhs_iterator)
+		{
+			bhs_end_iterate(scan->rs_pf_bhs_iterator);
+			scan->rs_pf_bhs_iterator = NULL;
+		}
 	}
 
 	scan->rs_rd->rd_tableam->scan_end(scan);
@@ -1040,6 +1038,12 @@ table_rescan(TableScanDesc scan,
 	{
 		bhs_end_iterate(scan->rs_bhs_iterator);
 		scan->rs_bhs_iterator = NULL;
+
+		if (scan->rs_pf_bhs_iterator)
+		{
+			bhs_end_iterate(scan->rs_pf_bhs_iterator);
+			scan->rs_pf_bhs_iterator = NULL;
+		}
 	}
 
 	scan->rs_rd->rd_tableam->scan_rescan(scan, key, false, false, false, false);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 52cedd1b35..60916bf0d0 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1785,18 +1785,11 @@ struct BitmapHeapIterator;
  *
  *		bitmapqualorig	   execution state for bitmapqualorig expressions
  *		tbm				   bitmap obtained from child index scan(s)
- *		pvmbuffer		   buffer for visibility-map lookups of prefetched pages
  *		exact_pages		   total number of exact pages retrieved
  *		lossy_pages		   total number of lossy pages retrieved
- *		pf_iterator        for prefetching ahead of current page
- *		prefetch_pages	   # pages prefetch iterator is ahead of current
- *		prefetch_target    current target prefetch distance
- *		prefetch_maximum   maximum value for prefetch_target
  *		initialized		   is node is ready to iterate
  *		pstate			   shared state for parallel bitmap scan
  *		recheck			   do current page's tuples need recheck
- *		blockno			   used to validate pf and current block in sync
- *		pfblockno		   used to validate pf stays ahead of current block
  * ----------------
  */
 typedef struct BitmapHeapScanState
@@ -1804,18 +1797,11 @@ typedef struct BitmapHeapScanState
 	ScanState	ss;				/* its first field is NodeTag */
 	ExprState  *bitmapqualorig;
 	TIDBitmap  *tbm;
-	Buffer		pvmbuffer;
 	long		exact_pages;
 	long		lossy_pages;
-	int			prefetch_pages;
-	int			prefetch_target;
-	int			prefetch_maximum;
 	bool		initialized;
-	struct BitmapHeapIterator *pf_iterator;
 	ParallelBitmapHeapState *pstate;
 	bool		recheck;
-	BlockNumber blockno;
-	BlockNumber pfblockno;
 } BitmapHeapScanState;
 
 /* ----------------
-- 
2.40.1

