1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-15 19:21:59 +03:00

Transaction chaining

Add command variants COMMIT AND CHAIN and ROLLBACK AND CHAIN, which
start new transactions with the same transaction characteristics as the
just finished one, per SQL standard.

Support for transaction chaining in PL/pgSQL is also added.  This
functionality is especially useful when running COMMIT in a loop in
PL/pgSQL.

Reviewed-by: Fabien COELHO <coelho@cri.ensmp.fr>
Discussion: https://www.postgresql.org/message-id/flat/28536681-324b-10dc-ade8-ab46f7645a5a@2ndquadrant.com
This commit is contained in:
Peter Eisentraut
2019-03-24 10:33:14 +01:00
parent b2db277057
commit 280a408b48
26 changed files with 601 additions and 38 deletions

View File

@ -190,6 +190,7 @@ typedef struct TransactionStateData
bool startedInRecovery; /* did we start in recovery? */
bool didLogXid; /* has xid been included in WAL record? */
int parallelModeLevel; /* Enter/ExitParallelMode counter */
bool chain; /* start a new block after this one */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
@ -2775,6 +2776,36 @@ StartTransactionCommand(void)
MemoryContextSwitchTo(CurTransactionContext);
}
/*
* Simple system for saving and restoring transaction characteristics
* (isolation level, read only, deferrable). We need this for transaction
* chaining, so that we can set the characteristics of the new transaction to
* be the same as the previous one. (We need something like this because the
* GUC system resets the characteristics at transaction end, so for example
* just skipping the reset in StartTransaction() won't work.)
*/
static int save_XactIsoLevel;
static bool save_XactReadOnly;
static bool save_XactDeferrable;
void
SaveTransactionCharacteristics(void)
{
save_XactIsoLevel = XactIsoLevel;
save_XactReadOnly = XactReadOnly;
save_XactDeferrable = XactDeferrable;
}
void
RestoreTransactionCharacteristics(void)
{
XactIsoLevel = save_XactIsoLevel;
XactReadOnly = save_XactReadOnly;
XactDeferrable = save_XactDeferrable;
}
/*
* CommitTransactionCommand
*/
@ -2783,6 +2814,9 @@ CommitTransactionCommand(void)
{
TransactionState s = CurrentTransactionState;
if (s->chain)
SaveTransactionCharacteristics();
switch (s->blockState)
{
/*
@ -2834,6 +2868,13 @@ CommitTransactionCommand(void)
case TBLOCK_END:
CommitTransaction();
s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break;
/*
@ -2853,6 +2894,13 @@ CommitTransactionCommand(void)
case TBLOCK_ABORT_END:
CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break;
/*
@ -2864,6 +2912,13 @@ CommitTransactionCommand(void)
AbortTransaction();
CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
if (s->chain)
{
StartTransaction();
s->blockState = TBLOCK_INPROGRESS;
s->chain = false;
RestoreTransactionCharacteristics();
}
break;
/*
@ -3521,7 +3576,7 @@ PrepareTransactionBlock(const char *gid)
bool result;
/* Set up to commit the current transaction */
result = EndTransactionBlock();
result = EndTransactionBlock(false);
/* If successful, change outer tblock state to PREPARE */
if (result)
@ -3567,7 +3622,7 @@ PrepareTransactionBlock(const char *gid)
* resource owner, etc while executing inside a Portal.
*/
bool
EndTransactionBlock(void)
EndTransactionBlock(bool chain)
{
TransactionState s = CurrentTransactionState;
bool result = false;
@ -3693,6 +3748,13 @@ EndTransactionBlock(void)
break;
}
Assert(s->blockState == TBLOCK_STARTED ||
s->blockState == TBLOCK_END ||
s->blockState == TBLOCK_ABORT_END ||
s->blockState == TBLOCK_ABORT_PENDING);
s->chain = chain;
return result;
}
@ -3703,7 +3765,7 @@ EndTransactionBlock(void)
* As above, we don't actually do anything here except change blockState.
*/
void
UserAbortTransactionBlock(void)
UserAbortTransactionBlock(bool chain)
{
TransactionState s = CurrentTransactionState;
@ -3801,6 +3863,11 @@ UserAbortTransactionBlock(void)
BlockStateAsString(s->blockState));
break;
}
Assert(s->blockState == TBLOCK_ABORT_END ||
s->blockState == TBLOCK_ABORT_PENDING);
s->chain = chain;
}
/*