From 60b36cd8b4d9e2de125690a7fcfbab7330c12287 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 18 Jun 2025 12:42:19 -0400
Subject: [PATCH v4 05/13] Use xl_heap_prune record for setting empty pages
 all-visible

As part of a project to eliminate xl_heap_visible records, eliminate
their usage in phase I vacuum of empty pages.
---
 src/backend/access/heap/pruneheap.c  | 14 +++++--
 src/backend/access/heap/vacuumlazy.c | 55 ++++++++++++++++++----------
 src/include/access/heapam.h          |  1 +
 3 files changed, 47 insertions(+), 23 deletions(-)

diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 68ecf50848b..2724cf7f64f 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -836,6 +836,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
 				conflict_xid = prstate.latest_xid_removed;
 
 			log_heap_prune_and_freeze(relation, buffer,
+									  false,
 									  InvalidBuffer, 0, false,
 									  conflict_xid,
 									  true, reason,
@@ -2052,6 +2053,9 @@ heap_log_freeze_plan(HeapTupleFreeze *tuples, int ntuples,
  * case, vmbuffer should already have been updated and marked dirty and should
  * still be pinned and locked.
  *
+ * force_heap_fpi indicates that a full page image of the heap block should be
+ * forced.
+ *
  * set_pd_all_vis indicates that we set PD_ALL_VISIBLE and thus should update
  * the page LSN when checksums/wal_log_hints are enabled even if we did not
  * prune or freeze tuples on the page.
@@ -2062,6 +2066,7 @@ heap_log_freeze_plan(HeapTupleFreeze *tuples, int ntuples,
  */
 void
 log_heap_prune_and_freeze(Relation relation, Buffer buffer,
+						  bool force_heap_fpi,
 						  Buffer vmbuffer,
 						  uint8 vmflags,
 						  bool set_pd_all_vis,
@@ -2090,13 +2095,16 @@ log_heap_prune_and_freeze(Relation relation, Buffer buffer,
 	xlrec.flags = 0;
 	regbuf_flags = REGBUF_STANDARD;
 
+	if (force_heap_fpi)
+		regbuf_flags |= REGBUF_FORCE_IMAGE;
+
 	/*
 	 * We can avoid an FPI if the only modification we are making to the heap
 	 * page is to set PD_ALL_VISIBLE and checksums/wal_log_hints are disabled.
 	 */
-	if (!do_prune &&
-		nfrozen == 0 &&
-		(!set_pd_all_vis || !XLogHintBitIsNeeded()))
+	else if (!do_prune &&
+			 nfrozen == 0 &&
+			 (!set_pd_all_vis || !XLogHintBitIsNeeded()))
 		regbuf_flags |= REGBUF_NO_IMAGE;
 
 	/*
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 460cdbd8417..d9e195269d2 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -1878,33 +1878,47 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
 		 */
 		if (!PageIsAllVisible(page))
 		{
+			uint8		new_vmbits = VISIBILITYMAP_ALL_VISIBLE |
+				VISIBILITYMAP_ALL_FROZEN;
+
 			START_CRIT_SECTION();
 
-			/* mark buffer dirty before writing a WAL record */
+			PageSetAllVisible(page);
 			MarkBufferDirty(buf);
 
-			/*
-			 * It's possible that another backend has extended the heap,
-			 * initialized the page, and then failed to WAL-log the page due
-			 * to an ERROR.  Since heap extension is not WAL-logged, recovery
-			 * might try to replay our record setting the page all-visible and
-			 * find that the page isn't initialized, which will cause a PANIC.
-			 * To prevent that, check whether the page has been previously
-			 * WAL-logged, and if not, do that now.
-			 */
-			if (RelationNeedsWAL(vacrel->rel) &&
-				PageGetLSN(page) == InvalidXLogRecPtr)
-				log_newpage_buffer(buf, true);
+			LockBuffer(vmbuffer, BUFFER_LOCK_EXCLUSIVE);
+			visibilitymap_set_vmbyte(vacrel->rel, blkno,
+									 vmbuffer, new_vmbits);
+
+			if (RelationNeedsWAL(vacrel->rel))
+			{
+				/*
+				 * It's possible that another backend has extended the heap,
+				 * initialized the page, and then failed to WAL-log the page
+				 * due to an ERROR.  Since heap extension is not WAL-logged,
+				 * recovery might try to replay our record setting the page
+				 * all-visible and find that the page isn't initialized, which
+				 * will cause a PANIC. To prevent that, if the page hasn't
+				 * been previously WAL-logged, force a heap FPI.
+				 */
+				log_heap_prune_and_freeze(vacrel->rel, buf,
+										  PageGetLSN(page) == InvalidXLogRecPtr,
+										  vmbuffer,
+										  new_vmbits,
+										  true,
+										  InvalidTransactionId,
+										  false, PRUNE_VACUUM_SCAN,
+										  NULL, 0,
+										  NULL, 0,
+										  NULL, 0,
+										  NULL, 0);
+			}
 
-			PageSetAllVisible(page);
-			visibilitymap_set(vacrel->rel, blkno, buf,
-							  InvalidXLogRecPtr,
-							  vmbuffer, InvalidTransactionId,
-							  VISIBILITYMAP_ALL_VISIBLE |
-							  VISIBILITYMAP_ALL_FROZEN);
 			END_CRIT_SECTION();
 
-			/* Count the newly all-frozen pages for logging */
+			LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
+
+			/* Count the newly all-frozen pages for logging. */
 			vacrel->vm_new_visible_pages++;
 			vacrel->vm_new_visible_frozen_pages++;
 		}
@@ -2918,6 +2932,7 @@ lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, Buffer buffer,
 	if (RelationNeedsWAL(vacrel->rel))
 	{
 		log_heap_prune_and_freeze(vacrel->rel, buffer,
+								  false,
 								  vmbuffer,
 								  vmflags,
 								  set_pd_all_vis,
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 8b47295efa2..e7129a644a1 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -394,6 +394,7 @@ extern void heap_page_prune_execute(Buffer buffer, bool lp_truncate_only,
 									OffsetNumber *nowunused, int nunused);
 extern void heap_get_root_tuples(Page page, OffsetNumber *root_offsets);
 extern void log_heap_prune_and_freeze(Relation relation, Buffer buffer,
+									  bool force_heap_fpi,
 									  Buffer vmbuffer,
 									  uint8 vmflags,
 									  bool vm_modified_heap_page,
-- 
2.43.0

