From 9c54cfb4937f49301793f1ec39dd8526baa8139d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 2 Apr 2002 05:11:55 +0000
Subject: [PATCH] Fix CLOG truncation code to not do the Wrong Thing when there
 are already wrapped-around databases.  The unvacuumed databases might be
 fine, or they might not, but things will definitely not be fine if we remove
 the wrong CLOG segments.  Per trouble report from Gary Wolfe, 1-Apr-2002.

---
 src/backend/commands/vacuum.c | 58 +++++++++++++++++++++++++++--------
 1 file changed, 45 insertions(+), 13 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index fff0320eca2..ed3effab651 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.221 2002/04/02 01:03:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.222 2002/04/02 05:11:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -557,10 +557,15 @@ vac_update_dbstats(Oid dbid,
 static void
 vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
 {
+	TransactionId myXID;
 	Relation	relation;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
 	int32		age;
+	bool		vacuumAlreadyWrapped = false;
+	bool		frozenAlreadyWrapped = false;
+
+	myXID = GetCurrentTransactionId();
 
 	relation = heap_openr(DatabaseRelationName, AccessShareLock);
 
@@ -575,28 +580,55 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
 		if (!dbform->datallowconn)
 			continue;
 
-		if (TransactionIdIsNormal(dbform->datvacuumxid) &&
-			TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
-			vacuumXID = dbform->datvacuumxid;
-		if (TransactionIdIsNormal(dbform->datfrozenxid) &&
-			TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
-			frozenXID = dbform->datfrozenxid;
+		if (TransactionIdIsNormal(dbform->datvacuumxid))
+		{
+			if (TransactionIdPrecedes(myXID, dbform->datvacuumxid))
+				vacuumAlreadyWrapped = true;
+			else if (TransactionIdPrecedes(dbform->datvacuumxid, vacuumXID))
+				vacuumXID = dbform->datvacuumxid;
+		}
+		if (TransactionIdIsNormal(dbform->datfrozenxid))
+		{
+			if (TransactionIdPrecedes(myXID, dbform->datfrozenxid))
+				frozenAlreadyWrapped = true;
+			else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
+				frozenXID = dbform->datfrozenxid;
+		}
 	}
 
 	heap_endscan(scan);
 
 	heap_close(relation, AccessShareLock);
 
+	/*
+	 * Do not truncate CLOG if we seem to have suffered wraparound already;
+	 * the computed minimum XID might be bogus.
+	 */
+	if (vacuumAlreadyWrapped)
+	{
+		elog(WARNING, "Some databases have not been vacuumed in over 2 billion transactions."
+			 "\n\tYou may have already suffered transaction-wraparound data loss.");
+		return;
+	}
+
 	/* Truncate CLOG to the oldest vacuumxid */
 	TruncateCLOG(vacuumXID);
 
 	/* Give warning about impending wraparound problems */
-	age = (int32) (GetCurrentTransactionId() - frozenXID);
-	if (age > (int32) ((MaxTransactionId >> 3) * 3))
-		elog(WARNING, "Some databases have not been vacuumed in %d transactions."
-			 "\n\tBetter vacuum them within %d transactions,"
-			 "\n\tor you may have a wraparound failure.",
-			 age, (int32) (MaxTransactionId >> 1) - age);
+	if (frozenAlreadyWrapped)
+	{
+		elog(WARNING, "Some databases have not been vacuumed in over 1 billion transactions."
+			 "\n\tBetter vacuum them soon, or you may have a wraparound failure.");
+	}
+	else
+	{
+		age = (int32) (myXID - frozenXID);
+		if (age > (int32) ((MaxTransactionId >> 3) * 3))
+			elog(WARNING, "Some databases have not been vacuumed in %d transactions."
+				 "\n\tBetter vacuum them within %d transactions,"
+				 "\n\tor you may have a wraparound failure.",
+				 age, (int32) (MaxTransactionId >> 1) - age);
+	}
 }
 
 
-- 
GitLab