From f9845bb8bf9a501c7c0d19e8faf394e8a42cc6d8 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Mon, 25 Mar 2019 22:45:22 +1300
Subject: [PATCH 2/2] Use 64 bit transaction IDs for the transaction stack.

Provide GetTopFullTransactionId() and GetCurrentFullTransactionId().
The intended users of these interfaces are table access methods that
use xids for visibility checks but don't want to have to "freeze"
tuples.

Discussion: https://postgr.es/m/CAA4eK1%2BMv%2Bmb0HFfWM9Srtc6MVe160WFurXV68iAFMcagRZ0dQ%40mail.gmail.com
---
 src/backend/access/transam/varsup.c |  24 +++--
 src/backend/access/transam/xact.c   | 160 +++++++++++++++++++---------
 src/include/access/transam.h        |   4 +
 src/include/access/xact.h           |   5 +
 4 files changed, 136 insertions(+), 57 deletions(-)

diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index db141d286dc..0286eb99b60 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -35,7 +35,8 @@ VariableCache ShmemVariableCache = NULL;
 
 
 /*
- * Allocate the next XID for a new transaction or subtransaction.
+ * Allocate the next FullTransactionId for a new transaction or
+ * subtransaction.
  *
  * The new XID is also stored into MyPgXact before returning.
  *
@@ -44,9 +45,10 @@ VariableCache ShmemVariableCache = NULL;
  * does something.  So it is safe to do a database lookup if we want to
  * issue a warning about XID wrap.
  */
-TransactionId
-GetNewTransactionId(bool isSubXact)
+FullTransactionId
+GetNewFullTransactionId(bool isSubXact)
 {
+	FullTransactionId full_xid;
 	TransactionId xid;
 
 	/*
@@ -64,7 +66,7 @@ GetNewTransactionId(bool isSubXact)
 	{
 		Assert(!isSubXact);
 		MyPgXact->xid = BootstrapTransactionId;
-		return BootstrapTransactionId;
+		return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId);
 	}
 
 	/* safety check, we should never get this far in a HS standby */
@@ -73,7 +75,8 @@ GetNewTransactionId(bool isSubXact)
 
 	LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
 
-	xid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+	full_xid = ShmemVariableCache->nextFullXid;
+	xid = XidFromFullTransactionId(full_xid);
 
 	/*----------
 	 * Check to see if it's safe to assign another XID.  This protects against
@@ -232,7 +235,16 @@ GetNewTransactionId(bool isSubXact)
 
 	LWLockRelease(XidGenLock);
 
-	return xid;
+	return full_xid;
+}
+
+/*
+ * Allocate the next FullTransactionId, but return only the XID part.
+ */
+TransactionId
+GetNewTransactionId(bool isSubXact)
+{
+	return XidFromFullTransactionId(GetNewFullTransactionId(isSubXact));
 }
 
 /*
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 9b100050597..fa5787c67f0 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -105,7 +105,7 @@ int			synchronous_commit = SYNCHRONOUS_COMMIT_ON;
  * The XIDs are stored sorted in numerical order (not logical order) to make
  * lookups as fast as possible.
  */
-TransactionId XactTopTransactionId = InvalidTransactionId;
+FullTransactionId XactTopFullTransactionId = {InvalidTransactionId};
 int			nParallelCurrentXids = 0;
 TransactionId *ParallelCurrentXids;
 
@@ -171,7 +171,7 @@ typedef enum TBlockState
  */
 typedef struct TransactionStateData
 {
-	TransactionId transactionId;	/* my XID, or Invalid if none */
+	FullTransactionId fullTransactionId;	/* my FullTransactionId */
 	SubTransactionId subTransactionId;	/* my subxact ID */
 	char	   *name;			/* savepoint name, if any */
 	int			savepointLevel; /* savepoint level */
@@ -372,9 +372,9 @@ IsAbortedTransactionBlockState(void)
 TransactionId
 GetTopTransactionId(void)
 {
-	if (!TransactionIdIsValid(XactTopTransactionId))
+	if (!FullTransactionIdIsValid(XactTopFullTransactionId))
 		AssignTransactionId(&TopTransactionStateData);
-	return XactTopTransactionId;
+	return XidFromFullTransactionId(XactTopFullTransactionId);
 }
 
 /*
@@ -387,7 +387,7 @@ GetTopTransactionId(void)
 TransactionId
 GetTopTransactionIdIfAny(void)
 {
-	return XactTopTransactionId;
+	return XidFromFullTransactionId(XactTopFullTransactionId);
 }
 
 /*
@@ -400,11 +400,7 @@ GetTopTransactionIdIfAny(void)
 TransactionId
 GetCurrentTransactionId(void)
 {
-	TransactionState s = CurrentTransactionState;
-
-	if (!TransactionIdIsValid(s->transactionId))
-		AssignTransactionId(s);
-	return s->transactionId;
+	return XidFromFullTransactionId(GetCurrentFullTransactionId());
 }
 
 /*
@@ -417,7 +413,66 @@ GetCurrentTransactionId(void)
 TransactionId
 GetCurrentTransactionIdIfAny(void)
 {
-	return CurrentTransactionState->transactionId;
+	return XidFromFullTransactionId(GetCurrentFullTransactionIdIfAny());
+}
+
+/*
+ *	GetTopFullTransactionId
+ *
+ * This will return the XID of the main transaction, assigning one if
+ * it's not yet set.  Be careful to call this only inside a valid xact.
+ */
+FullTransactionId
+GetTopFullTransactionId(void)
+{
+	if (!FullTransactionIdIsValid(XactTopFullTransactionId))
+		AssignTransactionId(&TopTransactionStateData);
+	return XactTopFullTransactionId;
+}
+
+/*
+ *	GetTopFullTransactionIdIfAny
+ *
+ * This will return the FullTransactionId of the main transaction, if one is
+ * assigned.  It will return a value for which FullTransactionIdIsValid()
+ * returns false if we are not currently inside a transaction, or inside a
+ * transaction that hasn't yet been assigned an XID.
+ */
+FullTransactionId
+GetTopFullTransactionIdIfAny(void)
+{
+	return XactTopFullTransactionId;
+}
+
+/*
+ *	GetCurrentFullTransactionId
+ *
+ * This will return the FullTransactionId of the current transaction (main or
+ * sub transaction), assigning one if it's not yet set.  Be careful to call
+ * this only inside a valid xact.
+ */
+FullTransactionId
+GetCurrentFullTransactionId(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	if (!FullTransactionIdIsValid(s->fullTransactionId))
+		AssignTransactionId(s);
+	return s->fullTransactionId;
+}
+
+/*
+ *	GetCurrentFullTransactionIdIfAny
+ *
+ * This will return the FullTransactionId of the current sub xact, if one is
+ * assigned.  It will return a value for which FullTransactionIdIsValid()
+ * returns false if we are not currently inside a transaction, or inside a
+ * transaction that hasn't been assigned an XID yet.
+ */
+FullTransactionId
+GetCurrentFullTransactionIdIfAny(void)
+{
+	return CurrentTransactionState->fullTransactionId;
 }
 
 /*
@@ -428,7 +483,7 @@ GetCurrentTransactionIdIfAny(void)
 void
 MarkCurrentTransactionIdLoggedIfAny(void)
 {
-	if (TransactionIdIsValid(CurrentTransactionState->transactionId))
+	if (FullTransactionIdIsValid(CurrentTransactionState->fullTransactionId))
 		CurrentTransactionState->didLogXid = true;
 }
 
@@ -477,7 +532,7 @@ AssignTransactionId(TransactionState s)
 	bool		log_unknown_top = false;
 
 	/* Assert that caller didn't screw up */
-	Assert(!TransactionIdIsValid(s->transactionId));
+	Assert(!FullTransactionIdIsValid(s->fullTransactionId));
 	Assert(s->state == TRANS_INPROGRESS);
 
 	/*
@@ -493,14 +548,14 @@ AssignTransactionId(TransactionState s)
 	 * if we're at the bottom of a huge stack of subtransactions none of which
 	 * have XIDs yet.
 	 */
-	if (isSubXact && !TransactionIdIsValid(s->parent->transactionId))
+	if (isSubXact && !FullTransactionIdIsValid(s->parent->fullTransactionId))
 	{
 		TransactionState p = s->parent;
 		TransactionState *parents;
 		size_t		parentOffset = 0;
 
 		parents = palloc(sizeof(TransactionState) * s->nestingLevel);
-		while (p != NULL && !TransactionIdIsValid(p->transactionId))
+		while (p != NULL && !FullTransactionIdIsValid(p->fullTransactionId))
 		{
 			parents[parentOffset++] = p;
 			p = p->parent;
@@ -538,19 +593,20 @@ AssignTransactionId(TransactionState s)
 	 * PG_PROC, the subtrans entry is needed to ensure that other backends see
 	 * the Xid as "running".  See GetNewTransactionId.
 	 */
-	s->transactionId = GetNewTransactionId(isSubXact);
+	s->fullTransactionId = GetNewFullTransactionId(isSubXact);
 	if (!isSubXact)
-		XactTopTransactionId = s->transactionId;
+		XactTopFullTransactionId = s->fullTransactionId;
 
 	if (isSubXact)
-		SubTransSetParent(s->transactionId, s->parent->transactionId);
+		SubTransSetParent(XidFromFullTransactionId(s->fullTransactionId),
+						  XidFromFullTransactionId(s->parent->fullTransactionId));
 
 	/*
 	 * If it's a top-level transaction, the predicate locking system needs to
 	 * be told about it too.
 	 */
 	if (!isSubXact)
-		RegisterPredicateLockingXid(s->transactionId);
+		RegisterPredicateLockingXid(XidFromFullTransactionId(s->fullTransactionId));
 
 	/*
 	 * Acquire lock on the transaction XID.  (We assume this cannot block.) We
@@ -560,7 +616,7 @@ AssignTransactionId(TransactionState s)
 	currentOwner = CurrentResourceOwner;
 	CurrentResourceOwner = s->curTransactionOwner;
 
-	XactLockTableInsert(s->transactionId);
+	XactLockTableInsert(XidFromFullTransactionId(s->fullTransactionId));
 
 	CurrentResourceOwner = currentOwner;
 
@@ -584,7 +640,7 @@ AssignTransactionId(TransactionState s)
 	 */
 	if (isSubXact && XLogStandbyInfoActive())
 	{
-		unreportedXids[nUnreportedXids] = s->transactionId;
+		unreportedXids[nUnreportedXids] = XidFromFullTransactionId(s->fullTransactionId);
 		nUnreportedXids++;
 
 		/*
@@ -832,9 +888,9 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 
 		if (s->state == TRANS_ABORT)
 			continue;
-		if (!TransactionIdIsValid(s->transactionId))
+		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
-		if (TransactionIdEquals(xid, s->transactionId))
+		if (TransactionIdEquals(xid, XidFromFullTransactionId(s->fullTransactionId)))
 			return true;
 		/* As the childXids array is ordered, we can use binary search */
 		low = 0;
@@ -1495,7 +1551,7 @@ AtSubCommit_childXids(void)
 	 * all XIDs already in the array belong to subtransactions started and
 	 * subcommitted before us, so their XIDs must precede ours.
 	 */
-	s->parent->childXids[s->parent->nChildXids] = s->transactionId;
+	s->parent->childXids[s->parent->nChildXids] = XidFromFullTransactionId(s->fullTransactionId);
 
 	if (s->nChildXids > 0)
 		memcpy(&s->parent->childXids[s->parent->nChildXids + 1],
@@ -1809,7 +1865,7 @@ StartTransaction(void)
 	s = &TopTransactionStateData;
 	CurrentTransactionState = s;
 
-	Assert(XactTopTransactionId == InvalidTransactionId);
+	Assert(XidFromFullTransactionId(XactTopFullTransactionId) == InvalidTransactionId);
 
 	/* check the current transaction state */
 	Assert(s->state == TRANS_DEFAULT);
@@ -1821,7 +1877,7 @@ StartTransaction(void)
 	 * flags are fetched below.
 	 */
 	s->state = TRANS_START;
-	s->transactionId = InvalidTransactionId;	/* until assigned */
+	s->fullTransactionId = InvalidFullTransactionId;	/* until assigned */
 
 	/*
 	 * initialize current transaction state fields
@@ -2165,7 +2221,7 @@ CommitTransaction(void)
 
 	AtCommit_Memory();
 
-	s->transactionId = InvalidTransactionId;
+	s->fullTransactionId = InvalidFullTransactionId;
 	s->subTransactionId = InvalidSubTransactionId;
 	s->nestingLevel = 0;
 	s->gucNestLevel = 0;
@@ -2173,7 +2229,7 @@ CommitTransaction(void)
 	s->nChildXids = 0;
 	s->maxChildXids = 0;
 
-	XactTopTransactionId = InvalidTransactionId;
+	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
 	/*
@@ -2448,7 +2504,7 @@ PrepareTransaction(void)
 
 	AtCommit_Memory();
 
-	s->transactionId = InvalidTransactionId;
+	s->fullTransactionId = InvalidFullTransactionId;
 	s->subTransactionId = InvalidSubTransactionId;
 	s->nestingLevel = 0;
 	s->gucNestLevel = 0;
@@ -2456,7 +2512,7 @@ PrepareTransaction(void)
 	s->nChildXids = 0;
 	s->maxChildXids = 0;
 
-	XactTopTransactionId = InvalidTransactionId;
+	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
 	/*
@@ -2686,7 +2742,7 @@ CleanupTransaction(void)
 
 	AtCleanup_Memory();			/* and transaction memory */
 
-	s->transactionId = InvalidTransactionId;
+	s->fullTransactionId = InvalidFullTransactionId;
 	s->subTransactionId = InvalidSubTransactionId;
 	s->nestingLevel = 0;
 	s->gucNestLevel = 0;
@@ -2695,7 +2751,7 @@ CleanupTransaction(void)
 	s->maxChildXids = 0;
 	s->parallelModeLevel = 0;
 
-	XactTopTransactionId = InvalidTransactionId;
+	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
 	/*
@@ -4693,7 +4749,7 @@ CommitSubTransaction(void)
 	 */
 
 	/* Post-commit cleanup */
-	if (TransactionIdIsValid(s->transactionId))
+	if (FullTransactionIdIsValid(s->fullTransactionId))
 		AtSubCommit_childXids();
 	AfterTriggerEndSubXact(true);
 	AtSubCommit_Portals(s->subTransactionId,
@@ -4718,8 +4774,8 @@ CommitSubTransaction(void)
 	 * The only lock we actually release here is the subtransaction XID lock.
 	 */
 	CurrentResourceOwner = s->curTransactionOwner;
-	if (TransactionIdIsValid(s->transactionId))
-		XactLockTableDelete(s->transactionId);
+	if (FullTransactionIdIsValid(s->fullTransactionId))
+		XactLockTableDelete(XidFromFullTransactionId(s->fullTransactionId));
 
 	/*
 	 * Other locks should get transferred to their parent resource owner.
@@ -4872,7 +4928,7 @@ AbortSubTransaction(void)
 		(void) RecordTransactionAbort(true);
 
 		/* Post-abort cleanup */
-		if (TransactionIdIsValid(s->transactionId))
+		if (FullTransactionIdIsValid(s->fullTransactionId))
 			AtSubAbort_childXids();
 
 		CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId,
@@ -4985,7 +5041,7 @@ PushTransaction(void)
 	 * We can now stack a minimally valid subtransaction without fear of
 	 * failure.
 	 */
-	s->transactionId = InvalidTransactionId;	/* until assigned */
+	s->fullTransactionId = InvalidFullTransactionId;	/* until assigned */
 	s->subTransactionId = currentSubTransactionId;
 	s->parent = p;
 	s->nestingLevel = p->nestingLevel + 1;
@@ -5052,12 +5108,12 @@ Size
 EstimateTransactionStateSpace(void)
 {
 	TransactionState s;
-	Size		nxids = 6;		/* iso level, deferrable, top & current XID,
+	Size		nxids = 8;		/* iso level, deferrable, top & current XID,
 								 * command counter, XID count */
 
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (TransactionIdIsValid(s->transactionId))
+		if (FullTransactionIdIsValid(s->fullTransactionId))
 			nxids = add_size(nxids, 1);
 		nxids = add_size(nxids, s->nChildXids);
 	}
@@ -5073,7 +5129,7 @@ EstimateTransactionStateSpace(void)
  *
  * We need to save and restore XactDeferrable, XactIsoLevel, and the XIDs
  * associated with this transaction.  The first eight bytes of the result
- * contain XactDeferrable and XactIsoLevel; the next twelve bytes contain the
+ * contain XactDeferrable and XactIsoLevel; the next 18 bytes contain the
  * XID of the top-level transaction, the XID of the current transaction
  * (or, in each case, InvalidTransactionId if none), and the current command
  * counter.  After that, the next 4 bytes contain a count of how many
@@ -5093,8 +5149,10 @@ SerializeTransactionState(Size maxsize, char *start_address)
 
 	result[c++] = (TransactionId) XactIsoLevel;
 	result[c++] = (TransactionId) XactDeferrable;
-	result[c++] = XactTopTransactionId;
-	result[c++] = CurrentTransactionState->transactionId;
+	result[c++] = EpochFromFullTransactionId(XactTopFullTransactionId);
+	result[c++] = XidFromFullTransactionId(XactTopFullTransactionId);
+	result[c++] = EpochFromFullTransactionId(CurrentTransactionState->fullTransactionId);
+	result[c++] = XidFromFullTransactionId(CurrentTransactionState->fullTransactionId);
 	result[c++] = (TransactionId) currentCommandId;
 	Assert(maxsize >= c * sizeof(TransactionId));
 
@@ -5118,7 +5176,7 @@ SerializeTransactionState(Size maxsize, char *start_address)
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (TransactionIdIsValid(s->transactionId))
+		if (FullTransactionIdIsValid(s->fullTransactionId))
 			nxids = add_size(nxids, 1);
 		nxids = add_size(nxids, s->nChildXids);
 	}
@@ -5128,8 +5186,8 @@ SerializeTransactionState(Size maxsize, char *start_address)
 	workspace = palloc(nxids * sizeof(TransactionId));
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (TransactionIdIsValid(s->transactionId))
-			workspace[i++] = s->transactionId;
+		if (FullTransactionIdIsValid(s->fullTransactionId))
+			workspace[i++] = XidFromFullTransactionId(s->fullTransactionId);
 		memcpy(&workspace[i], s->childXids,
 			   s->nChildXids * sizeof(TransactionId));
 		i += s->nChildXids;
@@ -5159,11 +5217,11 @@ StartParallelWorkerTransaction(char *tstatespace)
 
 	XactIsoLevel = (int) tstate[0];
 	XactDeferrable = (bool) tstate[1];
-	XactTopTransactionId = tstate[2];
-	CurrentTransactionState->transactionId = tstate[3];
-	currentCommandId = tstate[4];
-	nParallelCurrentXids = (int) tstate[5];
-	ParallelCurrentXids = &tstate[6];
+	XactTopFullTransactionId = FullTransactionIdFromEpochAndXid(tstate[2], tstate[3]);
+	CurrentTransactionState->fullTransactionId = FullTransactionIdFromEpochAndXid(tstate[4], tstate[5]);
+	currentCommandId = tstate[6];
+	nParallelCurrentXids = (int) tstate[7];
+	ParallelCurrentXids = &tstate[8];
 
 	CurrentTransactionState->blockState = TBLOCK_PARALLEL_INPROGRESS;
 }
@@ -5222,7 +5280,7 @@ ShowTransactionStateRec(const char *str, TransactionState s)
 							 PointerIsValid(s->name) ? s->name : "unnamed",
 							 BlockStateAsString(s->blockState),
 							 TransStateAsString(s->state),
-							 (unsigned int) s->transactionId,
+							 (unsigned int) XidFromFullTransactionId(s->fullTransactionId),
 							 (unsigned int) s->subTransactionId,
 							 (unsigned int) currentCommandId,
 							 currentCommandIdUsed ? " (used)" : "",
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 5715f0c5217..614565a47b8 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,9 @@
 #define U64FromFullTransactionId(x)		((x).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdPrecedesOrEquals(a, b)	((a).value <= (b).value)
+#define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
+#define InvalidFullTransactionId \
+	FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
 
 /*
  * A 64 bit value that contains an epoch and a TransactionId.  This is
@@ -223,6 +226,7 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 
 /* in transam/varsup.c */
 extern TransactionId GetNewTransactionId(bool isSubXact);
+extern FullTransactionId GetNewFullTransactionId(bool isSubXact);
 extern void AdvanceNextFullTransactionIdPastXid(TransactionId xid);
 extern FullTransactionId ReadNextFullTransactionId(void);
 extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index e8579dcd478..b550343c4db 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
 #include "nodes/pg_list.h"
@@ -355,6 +356,10 @@ extern TransactionId GetCurrentTransactionId(void);
 extern TransactionId GetCurrentTransactionIdIfAny(void);
 extern TransactionId GetStableLatestTransactionId(void);
 extern SubTransactionId GetCurrentSubTransactionId(void);
+extern FullTransactionId GetTopFullTransactionId(void);
+extern FullTransactionId GetTopFullTransactionIdIfAny(void);
+extern FullTransactionId GetCurrentFullTransactionId(void);
+extern FullTransactionId GetCurrentFullTransactionIdIfAny(void);
 extern void MarkCurrentTransactionIdLoggedIfAny(void);
 extern bool SubTransactionIsActive(SubTransactionId subxid);
 extern CommandId GetCurrentCommandId(bool used);
-- 
2.21.0

