* 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;
*/
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 */
typedef TransactionStateData *TransactionState;
+/*
+ * Serialized representation used to transmit transaction state to parallel
+ * workers though shared memory.
+ */
+typedef struct SerializedTransactionState
+{
+ int xactIsoLevel;
+ bool xactDeferrable;
+ FullTransactionId topFullTransactionId;
+ FullTransactionId currentFullTransactionId;
+ CommandId currentCommandId;
+ int nParallelCurrentXids;
+ TransactionId parallelCurrentXids[FLEXIBLE_ARRAY_MEMBER];
+} SerializedTransactionState;
+
+/* The size of SerializedTransactionState, not including the final array. */
+#define SerializedTransactionStateHeaderSize \
+ offsetof(SerializedTransactionState, parallelCurrentXids)
+
/*
* CurrentTransactionState always points to the current transaction state
* block. It will point to TopTransactionStateData when not in a
TransactionId
GetTopTransactionId(void)
{
- if (!TransactionIdIsValid(XactTopTransactionId))
+ if (!FullTransactionIdIsValid(XactTopFullTransactionId))
AssignTransactionId(&TopTransactionStateData);
- return XactTopTransactionId;
+ return XidFromFullTransactionId(XactTopFullTransactionId);
}
/*
TransactionId
GetTopTransactionIdIfAny(void)
{
- return XactTopTransactionId;
+ return XidFromFullTransactionId(XactTopFullTransactionId);
}
/*
{
TransactionState s = CurrentTransactionState;
- if (!TransactionIdIsValid(s->transactionId))
+ if (!FullTransactionIdIsValid(s->fullTransactionId))
AssignTransactionId(s);
- return s->transactionId;
+ return XidFromFullTransactionId(s->fullTransactionId);
}
/*
TransactionId
GetCurrentTransactionIdIfAny(void)
{
- return CurrentTransactionState->transactionId;
+ return XidFromFullTransactionId(CurrentTransactionState->fullTransactionId);
+}
+
+/*
+ * GetTopFullTransactionId
+ *
+ * This will return the FullTransactionId 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 InvalidFullTransactionId if we are not currently
+ * inside a transaction, or inside a transaction that hasn't yet been assigned
+ * one.
+ */
+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 InvalidFullTransactionId if we are not currently
+ * inside a transaction, or inside a transaction that hasn't been assigned one
+ * yet.
+ */
+FullTransactionId
+GetCurrentFullTransactionIdIfAny(void)
+{
+ return CurrentTransactionState->fullTransactionId;
}
/*
void
MarkCurrentTransactionIdLoggedIfAny(void)
{
- if (TransactionIdIsValid(CurrentTransactionState->transactionId))
+ if (FullTransactionIdIsValid(CurrentTransactionState->fullTransactionId))
CurrentTransactionState->didLogXid = true;
}
/*
* AssignTransactionId
*
- * Assigns a new permanent XID to the given TransactionState.
+ * Assigns a new permanent FullTransactionId to the given TransactionState.
* We do not assign XIDs to transactions until/unless this is called.
* Also, any parent TransactionStates that don't yet have XIDs are assigned
* one; this maintains the invariant that a child transaction has an XID
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);
/*
* 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;
log_unknown_top = true;
/*
- * Generate a new Xid and record it in PG_PROC and pg_subtrans.
+ * Generate a new FullTransactionId and record its xid in PG_PROC and
+ * pg_subtrans.
*
* NB: we must make the subtrans entry BEFORE the Xid appears anywhere in
* shared storage other than PG_PROC; because if there's no room for it in
* 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 = GetNewTransactionId(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
currentOwner = CurrentResourceOwner;
CurrentResourceOwner = s->curTransactionOwner;
- XactLockTableInsert(s->transactionId);
+ XactLockTableInsert(XidFromFullTransactionId(s->fullTransactionId));
CurrentResourceOwner = currentOwner;
*/
if (isSubXact && XLogStandbyInfoActive())
{
- unreportedXids[nUnreportedXids] = s->transactionId;
+ unreportedXids[nUnreportedXids] = XidFromFullTransactionId(s->fullTransactionId);
nUnreportedXids++;
/*
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;
* 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],
s = &TopTransactionStateData;
CurrentTransactionState = s;
- Assert(XactTopTransactionId == InvalidTransactionId);
+ Assert(!FullTransactionIdIsValid(XactTopFullTransactionId));
/* check the current transaction state */
Assert(s->state == TRANS_DEFAULT);
* flags are fetched below.
*/
s->state = TRANS_START;
- s->transactionId = InvalidTransactionId; /* until assigned */
+ s->fullTransactionId = InvalidFullTransactionId; /* until assigned */
/*
* initialize current transaction state fields
AtCommit_Memory();
- s->transactionId = InvalidTransactionId;
+ s->fullTransactionId = InvalidFullTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->nChildXids = 0;
s->maxChildXids = 0;
- XactTopTransactionId = InvalidTransactionId;
+ XactTopFullTransactionId = InvalidFullTransactionId;
nParallelCurrentXids = 0;
/*
AtCommit_Memory();
- s->transactionId = InvalidTransactionId;
+ s->fullTransactionId = InvalidFullTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->nChildXids = 0;
s->maxChildXids = 0;
- XactTopTransactionId = InvalidTransactionId;
+ XactTopFullTransactionId = InvalidFullTransactionId;
nParallelCurrentXids = 0;
/*
AtCleanup_Memory(); /* and transaction memory */
- s->transactionId = InvalidTransactionId;
+ s->fullTransactionId = InvalidFullTransactionId;
s->subTransactionId = InvalidSubTransactionId;
s->nestingLevel = 0;
s->gucNestLevel = 0;
s->maxChildXids = 0;
s->parallelModeLevel = 0;
- XactTopTransactionId = InvalidTransactionId;
+ XactTopFullTransactionId = InvalidFullTransactionId;
nParallelCurrentXids = 0;
/*
*/
/* Post-commit cleanup */
- if (TransactionIdIsValid(s->transactionId))
+ if (FullTransactionIdIsValid(s->fullTransactionId))
AtSubCommit_childXids();
AfterTriggerEndSubXact(true);
AtSubCommit_Portals(s->subTransactionId,
* 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.
(void) RecordTransactionAbort(true);
/* Post-abort cleanup */
- if (TransactionIdIsValid(s->transactionId))
+ if (FullTransactionIdIsValid(s->fullTransactionId))
AtSubAbort_childXids();
CallSubXactCallbacks(SUBXACT_EVENT_ABORT_SUB, s->subTransactionId,
* 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;
EstimateTransactionStateSpace(void)
{
TransactionState s;
- Size nxids = 6; /* iso level, deferrable, top & current XID,
- * command counter, XID count */
+ Size nxids = 0;
+ Size size = SerializedTransactionStateHeaderSize;
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);
}
- nxids = add_size(nxids, nParallelCurrentXids);
- return mul_size(nxids, sizeof(TransactionId));
+ return add_size(size, sizeof(SerializedTransactionState) * nxids);
}
/*
* needed by a parallel worker.
*
* 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
- * 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
- * additional XIDs follow; this is followed by all of those XIDs one after
- * another. We emit the XIDs in sorted order for the convenience of the
- * receiving process.
+ * associated with this transaction. These are serialized into a
+ * caller-supplied buffer big enough to hold the number of bytes reported by
+ * EstimateTransactionStateSpace(). We emit the XIDs in sorted order for the
+ * convenience of the receiving process.
*/
void
SerializeTransactionState(Size maxsize, char *start_address)
TransactionState s;
Size nxids = 0;
Size i = 0;
- Size c = 0;
TransactionId *workspace;
- TransactionId *result = (TransactionId *) start_address;
+ SerializedTransactionState *result;
+
+ result = (SerializedTransactionState *) start_address;
- result[c++] = (TransactionId) XactIsoLevel;
- result[c++] = (TransactionId) XactDeferrable;
- result[c++] = XactTopTransactionId;
- result[c++] = CurrentTransactionState->transactionId;
- result[c++] = (TransactionId) currentCommandId;
- Assert(maxsize >= c * sizeof(TransactionId));
+ result->xactIsoLevel = XactIsoLevel;
+ result->xactDeferrable = XactDeferrable;
+ result->topFullTransactionId = XactTopFullTransactionId;
+ result->currentFullTransactionId =
+ CurrentTransactionState->fullTransactionId;
+ result->currentCommandId = currentCommandId;
/*
* If we're running in a parallel worker and launching a parallel worker
*/
if (nParallelCurrentXids > 0)
{
- result[c++] = nParallelCurrentXids;
- Assert(maxsize >= (nParallelCurrentXids + c) * sizeof(TransactionId));
- memcpy(&result[c], ParallelCurrentXids,
+ result->nParallelCurrentXids = nParallelCurrentXids;
+ memcpy(&result->parallelCurrentXids[0], ParallelCurrentXids,
nParallelCurrentXids * sizeof(TransactionId));
return;
}
*/
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);
}
- Assert((c + 1 + nxids) * sizeof(TransactionId) <= maxsize);
+ Assert(SerializedTransactionStateHeaderSize + nxids * sizeof(TransactionId)
+ <= maxsize);
/* Copy them to our scratch space. */
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;
qsort(workspace, nxids, sizeof(TransactionId), xidComparator);
/* Copy data into output area. */
- result[c++] = (TransactionId) nxids;
- memcpy(&result[c], workspace, nxids * sizeof(TransactionId));
+ result->nParallelCurrentXids = nxids;
+ memcpy(&result->parallelCurrentXids[0], workspace,
+ nxids * sizeof(TransactionId));
}
/*
void
StartParallelWorkerTransaction(char *tstatespace)
{
- TransactionId *tstate = (TransactionId *) tstatespace;
+ SerializedTransactionState *tstate;
Assert(CurrentTransactionState->blockState == TBLOCK_DEFAULT);
StartTransaction();
- 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];
+ tstate = (SerializedTransactionState *) tstatespace;
+ XactIsoLevel = tstate->xactIsoLevel;
+ XactDeferrable = tstate->xactDeferrable;
+ XactTopFullTransactionId = tstate->topFullTransactionId;
+ CurrentTransactionState->fullTransactionId =
+ tstate->currentFullTransactionId;
+ currentCommandId = tstate->currentCommandId;
+ nParallelCurrentXids = tstate->nParallelCurrentXids;
+ ParallelCurrentXids = &tstate->parallelCurrentXids[0];
CurrentTransactionState->blockState = TBLOCK_PARALLEL_INPROGRESS;
}
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)" : "",