From 7d2c63423d18f16fd5d4c21e49a4980f78cde69a Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Wed, 30 Mar 2022 18:59:41 -0700
Subject: [PATCH v14 3/4] PANIC on relfrozenxid from the future.

This should be made into a WARNING later on.
---
 src/backend/commands/vacuum.c | 78 +++++++++++++++++++++++++++--------
 1 file changed, 60 insertions(+), 18 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index deec4887b..40b6a723b 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1340,7 +1340,11 @@ vac_update_relstats(Relation relation,
 	Relation	rd;
 	HeapTuple	ctup;
 	Form_pg_class pgcform;
-	bool		dirty;
+	bool		dirty,
+				relfrozenxid_warn,
+				relminmxid_warn;
+	TransactionId oldrelfrozenxid;
+	MultiXactId oldrelminmxid;
 
 	rd = table_open(RelationRelationId, RowExclusiveLock);
 
@@ -1406,32 +1410,57 @@ vac_update_relstats(Relation relation,
 	 * This should match vac_update_datfrozenxid() concerning what we consider
 	 * to be "in the future".
 	 */
+	relfrozenxid_warn = false;
 	if (frozenxid_updated)
 		*frozenxid_updated = false;
-	if (TransactionIdIsNormal(frozenxid) &&
-		pgcform->relfrozenxid != frozenxid &&
-		(TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid) ||
-		 TransactionIdPrecedes(ReadNextTransactionId(),
-							   pgcform->relfrozenxid)))
+	if (TransactionIdIsNormal(frozenxid) && pgcform->relfrozenxid != frozenxid)
 	{
-		if (frozenxid_updated)
-			*frozenxid_updated = true;
-		pgcform->relfrozenxid = frozenxid;
-		dirty = true;
+		bool	update = false;
+
+		if (TransactionIdPrecedes(pgcform->relfrozenxid, frozenxid))
+			update = true;
+		else if (TransactionIdPrecedes(ReadNextTransactionId(),
+									   pgcform->relfrozenxid))
+		{
+			relfrozenxid_warn = true;
+			oldrelfrozenxid = pgcform->relfrozenxid;
+			update = true;
+		}
+
+		if (update)
+		{
+			if (frozenxid_updated)
+				*frozenxid_updated = true;
+			pgcform->relfrozenxid = frozenxid;
+			dirty = true;
+		}
 	}
 
 	/* Similarly for relminmxid */
+	relminmxid_warn = false;
 	if (minmulti_updated)
 		*minmulti_updated = false;
-	if (MultiXactIdIsValid(minmulti) &&
-		pgcform->relminmxid != minmulti &&
-		(MultiXactIdPrecedes(pgcform->relminmxid, minmulti) ||
-		 MultiXactIdPrecedes(ReadNextMultiXactId(), pgcform->relminmxid)))
+	if (MultiXactIdIsValid(minmulti) && pgcform->relminmxid != minmulti)
 	{
-		if (minmulti_updated)
-			*minmulti_updated = true;
-		pgcform->relminmxid = minmulti;
-		dirty = true;
+		bool	update = false;
+
+		if (MultiXactIdPrecedes(pgcform->relminmxid, minmulti))
+			update = true;
+		else if (MultiXactIdPrecedes(ReadNextMultiXactId(),
+									 pgcform->relminmxid))
+		{
+			relminmxid_warn = true;
+			oldrelminmxid = pgcform->relminmxid;
+			update = true;
+		}
+
+		if (update)
+		{
+			if (minmulti_updated)
+				*minmulti_updated = true;
+			pgcform->relminmxid = minmulti;
+			dirty = true;
+		}
 	}
 
 	/* If anything changed, write out the tuple. */
@@ -1439,6 +1468,19 @@ vac_update_relstats(Relation relation,
 		heap_inplace_update(rd, ctup);
 
 	table_close(rd, RowExclusiveLock);
+
+	if (relfrozenxid_warn)
+		ereport(PANIC,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg_internal("overwrote invalid pg_class.relfrozenxid value %u with new value %u in table \"%s\"",
+								 oldrelfrozenxid, frozenxid,
+								 RelationGetRelationName(relation))));
+	if (relminmxid_warn)
+		ereport(PANIC,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg_internal("overwrote invalid pg_class.relminmxid value %u with new value %u in table \"%s\"",
+								 oldrelminmxid, minmulti,
+								 RelationGetRelationName(relation))));
 }
 
 
-- 
2.32.0

