From 2f2aac4054db86b02d780801e94cf48897ee2280 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Sat, 6 Jan 2024 16:22:17 -0500
Subject: [PATCH v2 04/17] Add reference to VacuumCutoffs in HeapPageFreeze

Future commits will move opportunistic freezing into the main path of
pruning in heap_page_prune(). Because on-access pruning will not do
opportunistic freezing, it is cleaner to keep the visibility information
required for calling heap_prepare_freeze_tuple() inside of the
HeapPageFreeze structure itself by saving a reference to VacuumCutoffs.
---
 src/backend/access/heap/heapam.c     | 67 ++++++++++++++--------------
 src/backend/access/heap/vacuumlazy.c |  3 +-
 src/include/access/heapam.h          |  2 +-
 3 files changed, 36 insertions(+), 36 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 34bc60f625f..7261c4988d7 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -6023,7 +6023,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
  */
 static TransactionId
 FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
-				  const struct VacuumCutoffs *cutoffs, uint16 *flags,
+				  uint16 *flags,
 				  HeapPageFreeze *pagefrz)
 {
 	TransactionId newxmax;
@@ -6049,12 +6049,12 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 		pagefrz->freeze_required = true;
 		return InvalidTransactionId;
 	}
-	else if (MultiXactIdPrecedes(multi, cutoffs->relminmxid))
+	else if (MultiXactIdPrecedes(multi, pagefrz->cutoffs->relminmxid))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_CORRUPTED),
 				 errmsg_internal("found multixact %u from before relminmxid %u",
-								 multi, cutoffs->relminmxid)));
-	else if (MultiXactIdPrecedes(multi, cutoffs->OldestMxact))
+								 multi, pagefrz->cutoffs->relminmxid)));
+	else if (MultiXactIdPrecedes(multi, pagefrz->cutoffs->OldestMxact))
 	{
 		TransactionId update_xact;
 
@@ -6069,7 +6069,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg_internal("multixact %u from before multi freeze cutoff %u found to be still running",
-									 multi, cutoffs->OldestMxact)));
+									 multi, pagefrz->cutoffs->OldestMxact)));
 
 		if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
 		{
@@ -6080,13 +6080,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 
 		/* replace multi with single XID for its updater? */
 		update_xact = MultiXactIdGetUpdateXid(multi, t_infomask);
-		if (TransactionIdPrecedes(update_xact, cutoffs->relfrozenxid))
+		if (TransactionIdPrecedes(update_xact, pagefrz->cutoffs->relfrozenxid))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg_internal("multixact %u contains update XID %u from before relfrozenxid %u",
 									 multi, update_xact,
-									 cutoffs->relfrozenxid)));
-		else if (TransactionIdPrecedes(update_xact, cutoffs->OldestXmin))
+									 pagefrz->cutoffs->relfrozenxid)));
+		else if (TransactionIdPrecedes(update_xact, pagefrz->cutoffs->OldestXmin))
 		{
 			/*
 			 * Updater XID has to have aborted (otherwise the tuple would have
@@ -6098,7 +6098,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 						(errcode(ERRCODE_DATA_CORRUPTED),
 						 errmsg_internal("multixact %u contains committed update XID %u from before removable cutoff %u",
 										 multi, update_xact,
-										 cutoffs->OldestXmin)));
+										 pagefrz->cutoffs->OldestXmin)));
 			*flags |= FRM_INVALIDATE_XMAX;
 			pagefrz->freeze_required = true;
 			return InvalidTransactionId;
@@ -6150,9 +6150,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 	{
 		TransactionId xid = members[i].xid;
 
-		Assert(!TransactionIdPrecedes(xid, cutoffs->relfrozenxid));
+		Assert(!TransactionIdPrecedes(xid, pagefrz->cutoffs->relfrozenxid));
 
-		if (TransactionIdPrecedes(xid, cutoffs->FreezeLimit))
+		if (TransactionIdPrecedes(xid, pagefrz->cutoffs->FreezeLimit))
 		{
 			/* Can't violate the FreezeLimit postcondition */
 			need_replace = true;
@@ -6164,7 +6164,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 
 	/* Can't violate the MultiXactCutoff postcondition, either */
 	if (!need_replace)
-		need_replace = MultiXactIdPrecedes(multi, cutoffs->MultiXactCutoff);
+		need_replace = MultiXactIdPrecedes(multi, pagefrz->cutoffs->MultiXactCutoff);
 
 	if (!need_replace)
 	{
@@ -6203,7 +6203,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 		TransactionId xid = members[i].xid;
 		MultiXactStatus mstatus = members[i].status;
 
-		Assert(!TransactionIdPrecedes(xid, cutoffs->relfrozenxid));
+		Assert(!TransactionIdPrecedes(xid, pagefrz->cutoffs->relfrozenxid));
 
 		if (!ISUPDATE_from_mxstatus(mstatus))
 		{
@@ -6214,12 +6214,12 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 			if (TransactionIdIsCurrentTransactionId(xid) ||
 				TransactionIdIsInProgress(xid))
 			{
-				if (TransactionIdPrecedes(xid, cutoffs->OldestXmin))
+				if (TransactionIdPrecedes(xid, pagefrz->cutoffs->OldestXmin))
 					ereport(ERROR,
 							(errcode(ERRCODE_DATA_CORRUPTED),
 							 errmsg_internal("multixact %u contains running locker XID %u from before removable cutoff %u",
 											 multi, xid,
-											 cutoffs->OldestXmin)));
+											 pagefrz->cutoffs->OldestXmin)));
 				newmembers[nnewmembers++] = members[i];
 				has_lockers = true;
 			}
@@ -6277,11 +6277,11 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
 		 * We determined that updater must be kept -- add it to pending new
 		 * members list
 		 */
-		if (TransactionIdPrecedes(xid, cutoffs->OldestXmin))
+		if (TransactionIdPrecedes(xid, pagefrz->cutoffs->OldestXmin))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg_internal("multixact %u contains committed update XID %u from before removable cutoff %u",
-									 multi, xid, cutoffs->OldestXmin)));
+									 multi, xid, pagefrz->cutoffs->OldestXmin)));
 		newmembers[nnewmembers++] = members[i];
 	}
 
@@ -6373,7 +6373,6 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
  */
 bool
 heap_prepare_freeze_tuple(HeapTupleHeader tuple,
-						  const struct VacuumCutoffs *cutoffs,
 						  HeapPageFreeze *pagefrz,
 						  HeapTupleFreeze *frz, bool *totally_frozen)
 {
@@ -6401,14 +6400,14 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 		xmin_already_frozen = true;
 	else
 	{
-		if (TransactionIdPrecedes(xid, cutoffs->relfrozenxid))
+		if (TransactionIdPrecedes(xid, pagefrz->cutoffs->relfrozenxid))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg_internal("found xmin %u from before relfrozenxid %u",
-									 xid, cutoffs->relfrozenxid)));
+									 xid, pagefrz->cutoffs->relfrozenxid)));
 
 		/* Will set freeze_xmin flags in freeze plan below */
-		freeze_xmin = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
+		freeze_xmin = TransactionIdPrecedes(xid, pagefrz->cutoffs->OldestXmin);
 
 		/* Verify that xmin committed if and when freeze plan is executed */
 		if (freeze_xmin)
@@ -6422,8 +6421,8 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 	xid = HeapTupleHeaderGetXvac(tuple);
 	if (TransactionIdIsNormal(xid))
 	{
-		Assert(TransactionIdPrecedesOrEquals(cutoffs->relfrozenxid, xid));
-		Assert(TransactionIdPrecedes(xid, cutoffs->OldestXmin));
+		Assert(TransactionIdPrecedesOrEquals(pagefrz->cutoffs->relfrozenxid, xid));
+		Assert(TransactionIdPrecedes(xid, pagefrz->cutoffs->OldestXmin));
 
 		/*
 		 * For Xvac, we always freeze proactively.  This allows totally_frozen
@@ -6448,8 +6447,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 		 * perform no-op xmax processing.  The only constraint is that the
 		 * FreezeLimit/MultiXactCutoff postcondition must never be violated.
 		 */
-		newxmax = FreezeMultiXactId(xid, tuple->t_infomask, cutoffs,
-									&flags, pagefrz);
+		newxmax = FreezeMultiXactId(xid, tuple->t_infomask, &flags, pagefrz);
 
 		if (flags & FRM_NOOP)
 		{
@@ -6472,7 +6470,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 			 * (This repeats work from FreezeMultiXactId, but allows "no
 			 * freeze" tracker maintenance to happen in only one place.)
 			 */
-			Assert(!MultiXactIdPrecedes(newxmax, cutoffs->MultiXactCutoff));
+			Assert(!MultiXactIdPrecedes(newxmax, pagefrz->cutoffs->MultiXactCutoff));
 			Assert(MultiXactIdIsValid(newxmax) && xid == newxmax);
 		}
 		else if (flags & FRM_RETURN_IS_XID)
@@ -6481,7 +6479,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 			 * xmax will become an updater Xid (original MultiXact's updater
 			 * member Xid will be carried forward as a simple Xid in Xmax).
 			 */
-			Assert(!TransactionIdPrecedes(newxmax, cutoffs->OldestXmin));
+			Assert(!TransactionIdPrecedes(newxmax, pagefrz->cutoffs->OldestXmin));
 
 			/*
 			 * NB -- some of these transformations are only valid because we
@@ -6505,7 +6503,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 			 * xmax is an old MultiXactId that we have to replace with a new
 			 * MultiXactId, to carry forward two or more original member XIDs.
 			 */
-			Assert(!MultiXactIdPrecedes(newxmax, cutoffs->OldestMxact));
+			Assert(!MultiXactIdPrecedes(newxmax, pagefrz->cutoffs->OldestMxact));
 
 			/*
 			 * We can't use GetMultiXactIdHintBits directly on the new multi
@@ -6540,14 +6538,14 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 	else if (TransactionIdIsNormal(xid))
 	{
 		/* Raw xmax is normal XID */
-		if (TransactionIdPrecedes(xid, cutoffs->relfrozenxid))
+		if (TransactionIdPrecedes(xid, pagefrz->cutoffs->relfrozenxid))
 			ereport(ERROR,
 					(errcode(ERRCODE_DATA_CORRUPTED),
 					 errmsg_internal("found xmax %u from before relfrozenxid %u",
-									 xid, cutoffs->relfrozenxid)));
+									 xid, pagefrz->cutoffs->relfrozenxid)));
 
 		/* Will set freeze_xmax flags in freeze plan below */
-		freeze_xmax = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
+		freeze_xmax = TransactionIdPrecedes(xid, pagefrz->cutoffs->OldestXmin);
 
 		/*
 		 * Verify that xmax aborted if and when freeze plan is executed,
@@ -6627,7 +6625,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
 		 * Does this tuple force caller to freeze the entire page?
 		 */
 		pagefrz->freeze_required =
-			heap_tuple_should_freeze(tuple, cutoffs,
+			heap_tuple_should_freeze(tuple, pagefrz->cutoffs,
 									 &pagefrz->NoFreezePageRelfrozenXid,
 									 &pagefrz->NoFreezePageRelminMxid);
 	}
@@ -6949,8 +6947,9 @@ heap_freeze_tuple(HeapTupleHeader tuple,
 	pagefrz.NoFreezePageRelfrozenXid = FreezeLimit;
 	pagefrz.NoFreezePageRelminMxid = MultiXactCutoff;
 
-	do_freeze = heap_prepare_freeze_tuple(tuple, &cutoffs,
-										  &pagefrz, &frz, &totally_frozen);
+	pagefrz.cutoffs = &cutoffs;
+
+	do_freeze = heap_prepare_freeze_tuple(tuple, &pagefrz, &frz, &totally_frozen);
 
 	/*
 	 * Note that because this is not a WAL-logged operation, we don't need to
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index f9892f4cd08..06e0e841582 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -1442,6 +1442,7 @@ lazy_scan_prune(LVRelState *vacrel,
 	pagefrz.FreezePageRelminMxid = vacrel->NewRelminMxid;
 	pagefrz.NoFreezePageRelfrozenXid = vacrel->NewRelfrozenXid;
 	pagefrz.NoFreezePageRelminMxid = vacrel->NewRelminMxid;
+	pagefrz.cutoffs = &vacrel->cutoffs;
 	tuples_frozen = 0;
 	lpdead_items = 0;
 	live_tuples = 0;
@@ -1587,7 +1588,7 @@ lazy_scan_prune(LVRelState *vacrel,
 		hastup = true;			/* page makes rel truncation unsafe */
 
 		/* Tuple with storage -- consider need to freeze */
-		if (heap_prepare_freeze_tuple(htup, &vacrel->cutoffs, &pagefrz,
+		if (heap_prepare_freeze_tuple(htup, &pagefrz,
 									  &frozen[tuples_frozen], &totally_frozen))
 		{
 			/* Save prepared freeze plan for later */
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4cfaf9ea46c..6823ab8b658 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -189,6 +189,7 @@ typedef struct HeapPageFreeze
 	TransactionId NoFreezePageRelfrozenXid;
 	MultiXactId NoFreezePageRelminMxid;
 
+	struct VacuumCutoffs *cutoffs;
 } HeapPageFreeze;
 
 /*
@@ -295,7 +296,6 @@ extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
 
 extern void heap_inplace_update(Relation relation, HeapTuple tuple);
 extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
-									  const struct VacuumCutoffs *cutoffs,
 									  HeapPageFreeze *pagefrz,
 									  HeapTupleFreeze *frz, bool *totally_frozen);
 extern void heap_freeze_execute_prepared(Relation rel, Buffer buffer,
-- 
2.40.1

