From 267ac9cbf9e8d7989486118818d751e3d224919f Mon Sep 17 00:00:00 2001
From: Vigneshwaran c <vignesh21@gmail.com>
Date: Thu, 1 Aug 2019 11:25:39 +0530
Subject: [PATCH] Store FullTransactionId in 
	 TwoPhaseFileHeader/GlobalTransactionData

	In the undo system, we use full-transaction-id for transactions. For
	rollback of prepared transactions, we were planning to use
	FullTransactionId by combining TransactionId and epoch, but as
	suggested by multiple people in undo email chain, the better
	idea is to store Full-transactionid in TwoPhaseFileHeader.
---
 src/backend/access/transam/twophase.c | 53 ++++++++++++++++++-----------------
 src/backend/access/transam/xact.c     |  4 +--
 src/include/access/transam.h          |  1 +
 src/include/access/twophase.h         |  7 +++--
 4 files changed, 35 insertions(+), 30 deletions(-)

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 477709b..f9494f2 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -163,7 +163,7 @@ typedef struct GlobalTransactionData
 	 */
 	XLogRecPtr	prepare_start_lsn;	/* XLOG offset of prepare record start */
 	XLogRecPtr	prepare_end_lsn;	/* XLOG offset of prepare record end */
-	TransactionId xid;			/* The GXACT id */
+	FullTransactionId	full_xid;		/* The GXACT full_xid */
 
 	Oid			owner;			/* ID of user that executed the xact */
 	BackendId	locking_backend;	/* backend currently working on the xact */
@@ -224,7 +224,7 @@ static void XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len);
 static char *ProcessTwoPhaseBuffer(TransactionId xid,
 								   XLogRecPtr prepare_start_lsn,
 								   bool fromdisk, bool setParent, bool setNextXid);
-static void MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid,
+static void MarkAsPreparingGuts(GlobalTransaction gxact, FullTransactionId full_xid,
 								const char *gid, TimestampTz prepared_at, Oid owner,
 								Oid databaseid);
 static void RemoveTwoPhaseFile(TransactionId xid, bool giveWarning);
@@ -370,7 +370,7 @@ PostPrepare_Twophase(void)
  *		Reserve the GID for the given transaction.
  */
 GlobalTransaction
-MarkAsPreparing(TransactionId xid, const char *gid,
+MarkAsPreparing(FullTransactionId full_xid, const char *gid,
 				TimestampTz prepared_at, Oid owner, Oid databaseid)
 {
 	GlobalTransaction gxact;
@@ -421,7 +421,7 @@ MarkAsPreparing(TransactionId xid, const char *gid,
 	gxact = TwoPhaseState->freeGXacts;
 	TwoPhaseState->freeGXacts = gxact->next;
 
-	MarkAsPreparingGuts(gxact, xid, gid, prepared_at, owner, databaseid);
+	MarkAsPreparingGuts(gxact, full_xid, gid, prepared_at, owner, databaseid);
 
 	gxact->ondisk = false;
 
@@ -444,7 +444,7 @@ MarkAsPreparing(TransactionId xid, const char *gid,
  * Note: This function should be called with appropriate locks held.
  */
 static void
-MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
+MarkAsPreparingGuts(GlobalTransaction gxact, FullTransactionId full_xid, const char *gid,
 					TimestampTz prepared_at, Oid owner, Oid databaseid)
 {
 	PGPROC	   *proc;
@@ -463,8 +463,8 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
 	SHMQueueElemInit(&(proc->links));
 	proc->waitStatus = STATUS_OK;
 	/* We set up the gxact's VXID as InvalidBackendId/XID */
-	proc->lxid = (LocalTransactionId) xid;
-	pgxact->xid = xid;
+	proc->lxid = (LocalTransactionId) XidFromFullTransactionId(full_xid);
+	pgxact->xid = XidFromFullTransactionId(full_xid);
 	pgxact->xmin = InvalidTransactionId;
 	pgxact->delayChkpt = false;
 	pgxact->vacuumFlags = 0;
@@ -485,7 +485,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
 	pgxact->nxids = 0;
 
 	gxact->prepared_at = prepared_at;
-	gxact->xid = xid;
+	gxact->full_xid = full_xid;
 	gxact->owner = owner;
 	gxact->locking_backend = MyBackendId;
 	gxact->valid = false;
@@ -915,7 +915,7 @@ typedef struct TwoPhaseFileHeader
 {
 	uint32		magic;			/* format identifier */
 	uint32		total_len;		/* actual file length */
-	TransactionId xid;			/* original transaction XID */
+	FullTransactionId full_xid;			/* original full transaction XID */
 	Oid			database;		/* OID of database it was in */
 	TimestampTz prepared_at;	/* time of preparation */
 	Oid			owner;			/* user running the transaction */
@@ -1004,8 +1004,6 @@ void
 StartPrepare(GlobalTransaction gxact)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
-	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
-	TransactionId xid = pgxact->xid;
 	TwoPhaseFileHeader hdr;
 	TransactionId *children;
 	RelFileNode *commitrels;
@@ -1028,7 +1026,7 @@ StartPrepare(GlobalTransaction gxact)
 	/* Create header */
 	hdr.magic = TWOPHASE_MAGIC;
 	hdr.total_len = 0;			/* EndPrepare will fill this in */
-	hdr.xid = xid;
+	hdr.full_xid = gxact->full_xid;
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
@@ -1346,7 +1344,7 @@ ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
 
 	parsed->origin_lsn = hdr->origin_lsn;
 	parsed->origin_timestamp = hdr->origin_timestamp;
-	parsed->twophase_xid = hdr->xid;
+	parsed->twophase_xid = XidFromFullTransactionId(hdr->full_xid);
 	parsed->dbId = hdr->database;
 	parsed->nsubxacts = hdr->nsubxacts;
 	parsed->nrels = hdr->ncommitrels;
@@ -1442,7 +1440,8 @@ StandbyTransactionIdIsPrepared(TransactionId xid)
 
 	/* Check header also */
 	hdr = (TwoPhaseFileHeader *) buf;
-	result = TransactionIdEquals(hdr->xid, xid);
+	result = TransactionIdEquals(
+		XidFromFullTransactionId(hdr->full_xid), xid);
 	pfree(buf);
 
 	return result;
@@ -1493,7 +1492,8 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * Disassemble the header area
 	 */
 	hdr = (TwoPhaseFileHeader *) buf;
-	Assert(TransactionIdEquals(hdr->xid, xid));
+	Assert(TransactionIdEquals(XidFromFullTransactionId(
+		hdr->full_xid), xid));
 	bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader));
 	bufptr += MAXALIGN(hdr->gidlen);
 	children = (TransactionId *) bufptr;
@@ -1791,7 +1791,8 @@ CheckPointTwoPhase(XLogRecPtr redo_horizon)
 			int			len;
 
 			XlogReadTwoPhaseData(gxact->prepare_start_lsn, &buf, &len);
-			RecreateTwoPhaseFile(gxact->xid, buf, len);
+			RecreateTwoPhaseFile(
+				XidFromFullTransactionId(gxact->full_xid), buf, len);
 			gxact->ondisk = true;
 			gxact->prepare_start_lsn = InvalidXLogRecPtr;
 			gxact->prepare_end_lsn = InvalidXLogRecPtr;
@@ -1911,7 +1912,7 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
 
 		Assert(gxact->inredo);
 
-		xid = gxact->xid;
+		xid = XidFromFullTransactionId(gxact->full_xid);
 
 		buf = ProcessTwoPhaseBuffer(xid,
 									gxact->prepare_start_lsn,
@@ -1986,7 +1987,7 @@ StandbyRecoverPreparedTransactions(void)
 
 		Assert(gxact->inredo);
 
-		xid = gxact->xid;
+		xid = XidFromFullTransactionId(gxact->full_xid);
 
 		buf = ProcessTwoPhaseBuffer(xid,
 									gxact->prepare_start_lsn,
@@ -2029,7 +2030,7 @@ RecoverPreparedTransactions(void)
 		TransactionId *subxids;
 		const char *gid;
 
-		xid = gxact->xid;
+		xid = XidFromFullTransactionId(gxact->full_xid);
 
 		/*
 		 * Reconstruct subtrans state for the transaction --- needed because
@@ -2050,7 +2051,7 @@ RecoverPreparedTransactions(void)
 				(errmsg("recovering prepared transaction %u from shared memory", xid)));
 
 		hdr = (TwoPhaseFileHeader *) buf;
-		Assert(TransactionIdEquals(hdr->xid, xid));
+		Assert(FullTransactionIdEquals(hdr->full_xid, gxact->full_xid));
 		bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader));
 		gid = (const char *) bufptr;
 		bufptr += MAXALIGN(hdr->gidlen);
@@ -2064,7 +2065,7 @@ RecoverPreparedTransactions(void)
 		 * Recreate its GXACT and dummy PGPROC. But, check whether it was
 		 * added in redo and already has a shmem entry for it.
 		 */
-		MarkAsPreparingGuts(gxact, xid, gid,
+		MarkAsPreparingGuts(gxact, gxact->full_xid, gid,
 							hdr->prepared_at,
 							hdr->owner, hdr->database);
 
@@ -2185,7 +2186,8 @@ ProcessTwoPhaseBuffer(TransactionId xid,
 
 	/* Deconstruct header */
 	hdr = (TwoPhaseFileHeader *) buf;
-	if (!TransactionIdEquals(hdr->xid, xid))
+	if (!TransactionIdEquals(XidFromFullTransactionId(
+		hdr->full_xid), xid))
 	{
 		if (fromdisk)
 			ereport(ERROR,
@@ -2426,7 +2428,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
 	gxact->prepared_at = hdr->prepared_at;
 	gxact->prepare_start_lsn = start_lsn;
 	gxact->prepare_end_lsn = end_lsn;
-	gxact->xid = hdr->xid;
+	gxact->full_xid = hdr->full_xid;
 	gxact->owner = hdr->owner;
 	gxact->locking_backend = InvalidBackendId;
 	gxact->valid = false;
@@ -2445,7 +2447,8 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
 						   false /* backward */ , false /* WAL */ );
 	}
 
-	elog(DEBUG2, "added 2PC data in shared memory for transaction %u", gxact->xid);
+	elog(DEBUG2, "added 2PC data in shared memory for transaction %u",
+		XidFromFullTransactionId(gxact->full_xid));
 }
 
 /*
@@ -2471,7 +2474,7 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
 	{
 		gxact = TwoPhaseState->prepXacts[i];
 
-		if (gxact->xid == xid)
+		if (XidFromFullTransactionId(gxact->full_xid) == xid)
 		{
 			Assert(gxact->inredo);
 			found = true;
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 1bbaeee..0b03156 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -2413,8 +2413,8 @@ PrepareTransaction(void)
 	 * Reserve the GID for this transaction. This could fail if the requested
 	 * GID is invalid or already in use.
 	 */
-	gxact = MarkAsPreparing(xid, prepareGID, prepared_at,
-							GetUserId(), MyDatabaseId);
+	gxact = MarkAsPreparing(GetCurrentFullTransactionId(), prepareGID,
+				prepared_at, GetUserId(), MyDatabaseId);
 	prepareGID = NULL;
 
 	/*
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052..1128326 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -48,6 +48,7 @@
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
+#define FullTransactionIdEquals(a, b)   ((a).value == (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..6be6e7c 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -37,9 +37,10 @@ extern void PostPrepare_Twophase(void);
 extern PGPROC *TwoPhaseGetDummyProc(TransactionId xid, bool lock_held);
 extern BackendId TwoPhaseGetDummyBackendId(TransactionId xid, bool lock_held);
 
-extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
-										 TimestampTz prepared_at,
-										 Oid owner, Oid databaseid);
+extern GlobalTransaction MarkAsPreparing(FullTransactionId full_xid,
+									 const char *gid,
+									 TimestampTz prepared_at,
+									 Oid owner, Oid databaseid);
 
 extern void StartPrepare(GlobalTransaction gxact);
 extern void EndPrepare(GlobalTransaction gxact);
-- 
1.8.3.1

