From b584717e9de72c1ac084698453667cb301551e36 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Tue, 21 Mar 2023 14:40:43 -0700
Subject: [PATCH v1] Record which PRUNE records are from VACUUM.

---
 src/include/access/heapam.h            |  2 +-
 src/include/access/heapam_xlog.h       |  8 +++++++-
 src/backend/access/heap/pruneheap.c    | 25 +++++++++++++++----------
 src/backend/access/rmgrdesc/heapdesc.c |  3 +++
 4 files changed, 26 insertions(+), 12 deletions(-)

diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index faf502651..2eedd7efd 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -289,7 +289,7 @@ extern int	heap_page_prune(Relation relation, Buffer buffer,
 							TransactionId old_snap_xmin,
 							TimestampTz old_snap_ts,
 							int *nnewlpdead,
-							OffsetNumber *off_loc);
+							OffsetNumber *off_loc_vacuum);
 extern void heap_page_prune_execute(Buffer buffer,
 									OffsetNumber *redirected, int nredirected,
 									OffsetNumber *nowdead, int ndead,
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index a2c67d1cd..3372d1938 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -48,7 +48,7 @@
  * We ran out of opcodes, so heapam.c now has a second RmgrId.  These opcodes
  * are associated with RM_HEAP2_ID, but are not logically different from
  * the ones above associated with RM_HEAP_ID.  XLOG_HEAP_OPMASK applies to
- * these, too.
+ * these, too.  The BYVACUUM bit is used in place of RM_HEAP_ID's init bit.
  */
 #define XLOG_HEAP2_REWRITE		0x00
 #define XLOG_HEAP2_PRUNE		0x10
@@ -59,6 +59,12 @@
 #define XLOG_HEAP2_LOCK_UPDATED 0x60
 #define XLOG_HEAP2_NEW_CID		0x70
 
+/*
+ * RM_HEAP2_ID operations that take place during VACUUM set the BYVACUUM bit
+ * as instrumentation.  Only set in XLOG_HEAP2_PRUNE records, for now.
+ */
+#define XLOG_HEAP2_BYVACUUM		0x80
+
 /*
  * xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available.
  */
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index 4e65cbcad..5f4ef384e 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -257,8 +257,8 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
  * Sets *nnewlpdead for caller, indicating the number of items that were
  * newly set LP_DEAD during prune operation.
  *
- * off_loc is the offset location required by the caller to use in error
- * callback.
+ * off_loc_vacuum is the offset location required by VACUUM caller to use in
+ * error callback.  Opportunistic pruning caller should pass NULL.
  *
  * Returns the number of tuples deleted from the page during this call.
  */
@@ -268,7 +268,7 @@ heap_page_prune(Relation relation, Buffer buffer,
 				TransactionId old_snap_xmin,
 				TimestampTz old_snap_ts,
 				int *nnewlpdead,
-				OffsetNumber *off_loc)
+				OffsetNumber *off_loc_vacuum)
 {
 	int			ndeleted = 0;
 	Page		page = BufferGetPage(buffer);
@@ -345,8 +345,8 @@ heap_page_prune(Relation relation, Buffer buffer,
 		 * Set the offset number so that we can display it along with any
 		 * error that occurred while processing this tuple.
 		 */
-		if (off_loc)
-			*off_loc = offnum;
+		if (off_loc_vacuum)
+			*off_loc_vacuum = offnum;
 
 		prstate.htsv[offnum] = heap_prune_satisfies_vacuum(&prstate, &tup,
 														   buffer);
@@ -364,8 +364,8 @@ heap_page_prune(Relation relation, Buffer buffer,
 			continue;
 
 		/* see preceding loop */
-		if (off_loc)
-			*off_loc = offnum;
+		if (off_loc_vacuum)
+			*off_loc_vacuum = offnum;
 
 		/* Nothing to do if slot is empty or already dead */
 		itemid = PageGetItemId(page, offnum);
@@ -377,8 +377,8 @@ heap_page_prune(Relation relation, Buffer buffer,
 	}
 
 	/* Clear the offset information once we have processed the given page. */
-	if (off_loc)
-		*off_loc = InvalidOffsetNumber;
+	if (off_loc_vacuum)
+		*off_loc_vacuum = InvalidOffsetNumber;
 
 	/* Any error while applying the changes is critical */
 	START_CRIT_SECTION();
@@ -416,6 +416,7 @@ heap_page_prune(Relation relation, Buffer buffer,
 		if (RelationNeedsWAL(relation))
 		{
 			xl_heap_prune xlrec;
+			uint8		info = XLOG_HEAP2_PRUNE;
 			XLogRecPtr	recptr;
 
 			xlrec.snapshotConflictHorizon = prstate.snapshotConflictHorizon;
@@ -445,7 +446,11 @@ heap_page_prune(Relation relation, Buffer buffer,
 				XLogRegisterBufData(0, (char *) prstate.nowunused,
 									prstate.nunused * sizeof(OffsetNumber));
 
-			recptr = XLogInsert(RM_HEAP2_ID, XLOG_HEAP2_PRUNE);
+			/* Set bit indicating pruning by VACUUM where appropriate */
+			if (off_loc_vacuum)
+				info |= XLOG_HEAP2_BYVACUUM;
+
+			recptr = XLogInsert(RM_HEAP2_ID, info);
 
 			PageSetLSN(BufferGetPage(buffer), recptr);
 		}
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index 628f7e821..9231b7411 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -235,6 +235,9 @@ heap2_identify(uint8 info)
 		case XLOG_HEAP2_PRUNE:
 			id = "PRUNE";
 			break;
+		case XLOG_HEAP2_PRUNE | XLOG_HEAP2_BYVACUUM:
+			id = "PRUNE+BYVACUUM";
+			break;
 		case XLOG_HEAP2_VACUUM:
 			id = "VACUUM";
 			break;
-- 
2.39.2

