From f61559b2a337484071cb777c44a13e74f92544a0 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <m.orlov@postgrespro.ru>
Date: Fri, 25 Mar 2022 12:36:24 +0300
Subject: [PATCH v35 3/8] Use 64-bit FullTransactionId instead of Epoch:xid

NextXid in controldata is now compatible with old format Epoch:xid and the new
one. This is next step to make XIDs 64-bit.

Author: Alexander Korotkov <aekorotkov@gmail.com>
Author: Teodor Sigaev <teodor@sigaev.ru>
Author: Nikita Glukhov <n.gluhov@postgrespro.ru>
Author: Maxim Orlov <orlovmg@gmail.com>
Author: Pavel Borisov <pashkin.elfe@gmail.com>
Author: Yura Sokolov <y.sokolov@postgrespro.ru> <funny.falcon@gmail.com>
Author: Aleksander Alekseev <aleksander@timescale.com>
Reviewed-by: Aleksander Alekseev <aleksander@timescale.com>
Discussion: https://postgr.es/m/CACG%3DezZe1NQSCnfHOr78AtAZxJZeCvxrts0ygrxYwe%3DpyyjVWA%40mail.gmail.com
Discussion: https://postgr.es/m/CAJ7c6TPDOYBYrnCAeyndkBktO0WG2xSdYduTF0nxq%2BvfkmTF5Q%40mail.gmail.com
---
 contrib/amcheck/verify_heapam.c         | 75 ++++++++++---------------
 contrib/pageinspect/btreefuncs.c        |  5 +-
 src/backend/access/rmgrdesc/gistdesc.c  | 10 ++--
 src/backend/access/rmgrdesc/nbtdesc.c   | 10 ++--
 src/backend/access/rmgrdesc/xlogdesc.c  |  5 +-
 src/backend/utils/misc/pg_controldata.c |  5 +-
 src/bin/pg_controldata/pg_controldata.c |  5 +-
 src/bin/pg_upgrade/controldata.c        | 32 +++++++++--
 8 files changed, 73 insertions(+), 74 deletions(-)

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 61acfc295d..823977856a 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -743,24 +743,21 @@ check_tuple_visibility(HeapCheckContext *ctx)
 			break;
 		case XID_IN_FUTURE:
 			report_corruption(ctx,
-							  psprintf("xmin %llu equals or exceeds next valid transaction ID %u:%llu",
+							  psprintf("xmin %llu equals or exceeds next valid transaction ID %llu",
 									   (unsigned long long) xmin,
-									   EpochFromFullTransactionId(ctx->next_fxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->next_fxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->next_fxid)));
 			return false;
 		case XID_PRECEDES_CLUSTERMIN:
 			report_corruption(ctx,
-							  psprintf("xmin %llu precedes oldest valid transaction ID %u:%llu",
+							  psprintf("xmin %llu precedes oldest valid transaction ID %llu",
 									   (unsigned long long) xmin,
-									   EpochFromFullTransactionId(ctx->oldest_fxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->oldest_fxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->oldest_fxid)));
 			return false;
 		case XID_PRECEDES_RELMIN:
 			report_corruption(ctx,
-							  psprintf("xmin %llu precedes relation freeze threshold %u:%llu",
+							  psprintf("xmin %llu precedes relation freeze threshold %llu",
 									   (unsigned long long) xmin,
-									   EpochFromFullTransactionId(ctx->relfrozenfxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->relfrozenfxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->relfrozenfxid)));
 			return false;
 	}
 
@@ -784,24 +781,21 @@ check_tuple_visibility(HeapCheckContext *ctx)
 					return false;
 				case XID_IN_FUTURE:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple equals or exceeds next valid transaction ID %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple equals or exceeds next valid transaction ID %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->next_fxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->next_fxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->next_fxid)));
 					return false;
 				case XID_PRECEDES_RELMIN:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple precedes relation freeze threshold %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple precedes relation freeze threshold %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->relfrozenfxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->relfrozenfxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->relfrozenfxid)));
 					return false;
 				case XID_PRECEDES_CLUSTERMIN:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple precedes oldest valid transaction ID %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved off tuple precedes oldest valid transaction ID %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->oldest_fxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->oldest_fxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->oldest_fxid)));
 					return false;
 				case XID_BOUNDS_OK:
 					break;
@@ -853,24 +847,21 @@ check_tuple_visibility(HeapCheckContext *ctx)
 					return false;
 				case XID_IN_FUTURE:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple equals or exceeds next valid transaction ID %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple equals or exceeds next valid transaction ID %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->next_fxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->next_fxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->next_fxid)));
 					return false;
 				case XID_PRECEDES_RELMIN:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple precedes relation freeze threshold %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple precedes relation freeze threshold %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->relfrozenfxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->relfrozenfxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->relfrozenfxid)));
 					return false;
 				case XID_PRECEDES_CLUSTERMIN:
 					report_corruption(ctx,
-									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple precedes oldest valid transaction ID %u:%llu",
+									  psprintf("old-style VACUUM FULL transaction ID %llu for moved in tuple precedes oldest valid transaction ID %llu",
 											   (unsigned long long) xvac,
-											   EpochFromFullTransactionId(ctx->oldest_fxid),
-											   (unsigned long long) XidFromFullTransactionId(ctx->oldest_fxid)));
+											   (unsigned long long) U64FromFullTransactionId(ctx->oldest_fxid)));
 					return false;
 				case XID_BOUNDS_OK:
 					break;
@@ -1016,24 +1007,21 @@ check_tuple_visibility(HeapCheckContext *ctx)
 				return true;
 			case XID_IN_FUTURE:
 				report_corruption(ctx,
-								  psprintf("update xid %llu equals or exceeds next valid transaction ID %u:%llu",
+								  psprintf("update xid %llu equals or exceeds next valid transaction ID %llu",
 										   (unsigned long long) xmax,
-										   EpochFromFullTransactionId(ctx->next_fxid),
-										   (unsigned long long) XidFromFullTransactionId(ctx->next_fxid)));
+										   (unsigned long long) U64FromFullTransactionId(ctx->next_fxid)));
 				return true;
 			case XID_PRECEDES_RELMIN:
 				report_corruption(ctx,
-								  psprintf("update xid %llu precedes relation freeze threshold %u:%llu",
+								  psprintf("update xid %llu precedes relation freeze threshold %llu",
 										   (unsigned long long) xmax,
-										   EpochFromFullTransactionId(ctx->relfrozenfxid),
-										   (unsigned long long) XidFromFullTransactionId(ctx->relfrozenfxid)));
+										   (unsigned long long) U64FromFullTransactionId(ctx->relfrozenfxid)));
 				return true;
 			case XID_PRECEDES_CLUSTERMIN:
 				report_corruption(ctx,
-								  psprintf("update xid %llu precedes oldest valid transaction ID %u:%llu",
+								  psprintf("update xid %llu precedes oldest valid transaction ID %llu",
 										   (unsigned long long) xmax,
-										   EpochFromFullTransactionId(ctx->oldest_fxid),
-										   (unsigned long long) XidFromFullTransactionId(ctx->oldest_fxid)));
+										   (unsigned long long) U64FromFullTransactionId(ctx->oldest_fxid)));
 				return true;
 			case XID_BOUNDS_OK:
 				break;
@@ -1078,24 +1066,21 @@ check_tuple_visibility(HeapCheckContext *ctx)
 	{
 		case XID_IN_FUTURE:
 			report_corruption(ctx,
-							  psprintf("xmax %llu equals or exceeds next valid transaction ID %u:%llu",
+							  psprintf("xmax %llu equals or exceeds next valid transaction ID %llu",
 									   (unsigned long long) xmax,
-									   EpochFromFullTransactionId(ctx->next_fxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->next_fxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->next_fxid)));
 			return false;		/* corrupt */
 		case XID_PRECEDES_RELMIN:
 			report_corruption(ctx,
-							  psprintf("xmax %llu precedes relation freeze threshold %u:%llu",
+							  psprintf("xmax %llu precedes relation freeze threshold %llu",
 									   (unsigned long long) xmax,
-									   EpochFromFullTransactionId(ctx->relfrozenfxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->relfrozenfxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->relfrozenfxid)));
 			return false;		/* corrupt */
 		case XID_PRECEDES_CLUSTERMIN:
 			report_corruption(ctx,
-							  psprintf("xmax %llu precedes oldest valid transaction ID %u:%llu",
+							  psprintf("xmax %llu precedes oldest valid transaction ID %llu",
 									   (unsigned long long) xmax,
-									   EpochFromFullTransactionId(ctx->oldest_fxid),
-									   (unsigned long long) XidFromFullTransactionId(ctx->oldest_fxid)));
+									   (unsigned long long) U64FromFullTransactionId(ctx->oldest_fxid)));
 			return false;		/* corrupt */
 		case XID_BOUNDS_OK:
 		case XID_INVALID:
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index fbda52c2f0..33ef9f2f45 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -125,9 +125,8 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
 		{
 			FullTransactionId safexid = BTPageGetDeleteXid(page);
 
-			elog(DEBUG2, "deleted page from block %u has safexid %u:%llu",
-				 blkno, EpochFromFullTransactionId(safexid),
-				 (unsigned long long) XidFromFullTransactionId(safexid));
+			elog(DEBUG2, "deleted page from block %u has safexid %llu",
+				 blkno, (unsigned long long) U64FromFullTransactionId(safexid));
 		}
 		else
 			elog(DEBUG2, "deleted page from block %u has safexid %u",
diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c
index 1e89c97d6f..27f949a38d 100644
--- a/src/backend/access/rmgrdesc/gistdesc.c
+++ b/src/backend/access/rmgrdesc/gistdesc.c
@@ -26,11 +26,10 @@ out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
 static void
 out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec)
 {
-	appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u:%llu",
+	appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %llu",
 					 xlrec->node.spcNode, xlrec->node.dbNode,
 					 xlrec->node.relNode, xlrec->block,
-					 EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
-					 (unsigned long long) XidFromFullTransactionId(xlrec->latestRemovedFullXid));
+					 (unsigned long long) U64FromFullTransactionId(xlrec->latestRemovedFullXid));
 }
 
 static void
@@ -51,9 +50,8 @@ out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
 static void
 out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec)
 {
-	appendStringInfo(buf, "deleteXid %u:%llu; downlink %u",
-					 EpochFromFullTransactionId(xlrec->deleteXid),
-					 (unsigned long long) XidFromFullTransactionId(xlrec->deleteXid),
+	appendStringInfo(buf, "deleteXid %llu; downlink %u",
+					 (unsigned long long) U64FromFullTransactionId(xlrec->deleteXid),
 					 xlrec->downlinkOffset);
 }
 
diff --git a/src/backend/access/rmgrdesc/nbtdesc.c b/src/backend/access/rmgrdesc/nbtdesc.c
index a55b69dedf..cd980053ab 100644
--- a/src/backend/access/rmgrdesc/nbtdesc.c
+++ b/src/backend/access/rmgrdesc/nbtdesc.c
@@ -81,10 +81,9 @@ btree_desc(StringInfo buf, XLogReaderState *record)
 			{
 				xl_btree_unlink_page *xlrec = (xl_btree_unlink_page *) rec;
 
-				appendStringInfo(buf, "left %u; right %u; level %u; safexid %u:%llu; ",
+				appendStringInfo(buf, "left %u; right %u; level %u; safexid %llu; ",
 								 xlrec->leftsib, xlrec->rightsib, xlrec->level,
-								 EpochFromFullTransactionId(xlrec->safexid),
-								 (unsigned long long) XidFromFullTransactionId(xlrec->safexid));
+								 (unsigned long long) U64FromFullTransactionId(xlrec->safexid));
 				appendStringInfo(buf, "leafleft %u; leafright %u; leaftopparent %u",
 								 xlrec->leafleftsib, xlrec->leafrightsib,
 								 xlrec->leaftopparent);
@@ -101,11 +100,10 @@ btree_desc(StringInfo buf, XLogReaderState *record)
 			{
 				xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
 
-				appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid %u:%llu",
+				appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid %llu",
 								 xlrec->node.spcNode, xlrec->node.dbNode,
 								 xlrec->node.relNode,
-								 EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
-								 (unsigned long long) XidFromFullTransactionId(xlrec->latestRemovedFullXid));
+								 (unsigned long long) U64FromFullTransactionId(xlrec->latestRemovedFullXid));
 				break;
 			}
 		case XLOG_BTREE_META_CLEANUP:
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 59f08139c9..230b85fc69 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -45,7 +45,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 		CheckPoint *checkpoint = (CheckPoint *) rec;
 
 		appendStringInfo(buf, "redo %X/%X; "
-						 "tli %u; prev tli %u; fpw %s; xid %u:%llu; oid %u; multi %llu; offset %u; "
+						 "tli %u; prev tli %u; fpw %s; xid %llu; oid %u; multi %llu; offset %u; "
 						 "oldest xid %llu in DB %u; oldest multi %llu in DB %u; "
 						 "oldest/newest commit timestamp xid: %llu/%llu; "
 						 "oldest running xid %llu; %s",
@@ -53,8 +53,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->PrevTimeLineID,
 						 checkpoint->fullPageWrites ? "true" : "false",
-						 EpochFromFullTransactionId(checkpoint->nextXid),
-						 (unsigned long long) XidFromFullTransactionId(checkpoint->nextXid),
+						 (unsigned long long) U64FromFullTransactionId(checkpoint->nextXid),
 						 checkpoint->nextOid,
 						 (unsigned long long) checkpoint->nextMulti,
 						 checkpoint->nextMultiOffset,
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 88f6f33ef5..4ab4a0a701 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -164,9 +164,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
 	values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
 	nulls[5] = false;
 
-	values[6] = CStringGetTextDatum(psprintf("%u:%llu",
-											 EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
-											 (unsigned long long) XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid)));
+	values[6] = CStringGetTextDatum(psprintf("%llu",
+											 (unsigned long long) U64FromFullTransactionId(ControlFile->checkPointCopy.nextXid)));
 	nulls[6] = false;
 
 	values[7] = ObjectIdGetDatum(ControlFile->checkPointCopy.nextOid);
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index 2c10322355..a8a46d5bf0 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -247,9 +247,8 @@ main(int argc, char *argv[])
 		   ControlFile->checkPointCopy.PrevTimeLineID);
 	printf(_("Latest checkpoint's full_page_writes: %s\n"),
 		   ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
-	printf(_("Latest checkpoint's NextXID:          %u:%llu\n"),
-		   EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
-		   (unsigned long long) XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid));
+	printf(_("Latest checkpoint's NextXID:          %llu\n"),
+		   (unsigned long long) U64FromFullTransactionId(ControlFile->checkPointCopy.nextXid));
 	printf(_("Latest checkpoint's NextOID:          %u\n"),
 		   ControlFile->checkPointCopy.nextOid);
 	printf(_("Latest checkpoint's NextMultiXactId:  %llu\n"),
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 41b8f69b8c..2b1b1a3435 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -8,6 +8,7 @@
  */
 
 #include "postgres_fe.h"
+#include "access/transam.h"
 
 #include <ctype.h>
 
@@ -263,13 +264,22 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL)
 		{
+			FullTransactionId		xid;
+
 			p = strchr(p, ':');
 
 			if (p == NULL || strlen(p) <= 1)
 				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
 
 			p++;				/* remove ':' char */
-			cluster->controldata.chkpnt_nxtepoch = str2uint(p);
+
+			/*
+			 * NextXID representation in controldata file changed from Epoch:Xid
+			 * to 64-bit FullTransactionId representation as a part of making
+			 * xids 64-bit in the future. Here we support both controldata
+			 * formats.
+			 */
+			xid.value = strtou64(p, NULL, 10);
 
 			/*
 			 * Delimiter changed from '/' to ':' in 9.6.  We don't test for
@@ -284,11 +294,23 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 			else
 				p = NULL;
 
-			if (p == NULL || strlen(p) <= 1)
-				pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+			if (p == NULL)
+			{
+				/* FullTransactionId representation */
+				cluster->controldata.chkpnt_nxtxid = XidFromFullTransactionId(xid);
+				cluster->controldata.chkpnt_nxtepoch = EpochFromFullTransactionId(xid);
+			}
+			else
+			{
+				if (strlen(p) <= 1)
+					pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+				/* Epoch:Xid representation */
+				p++;				/* remove '/' or ':' char */
+				cluster->controldata.chkpnt_nxtxid = str2uint(p);
+				cluster->controldata.chkpnt_nxtepoch = (TransactionId) XidFromFullTransactionId(xid);
+			}
 
-			p++;				/* remove '/' or ':' char */
-			cluster->controldata.chkpnt_nxtxid = str2uint(p);
 			got_xid = true;
 		}
 		else if ((p = strstr(bufin, "Latest checkpoint's NextOID:")) != NULL)
-- 
2.30.2

