diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 8e6aef3..eb51783 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -188,6 +188,8 @@ 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 isCachedSubXact; + struct TransactionStateData *cachedSubXact; struct TransactionStateData *parent; /* back link to parent */ } TransactionStateData; @@ -320,7 +322,7 @@ static void CommitSubTransaction(void); static void AbortSubTransaction(void); static void CleanupSubTransaction(void); static void PushTransaction(void); -static void PopTransaction(void); +static void PopTransaction(bool free); static void AtSubAbort_Memory(void); static void AtSubCleanup_Memory(void); @@ -334,6 +336,8 @@ static void ShowTransactionStateRec(const char *str, TransactionState state); static const char *BlockStateAsString(TBlockState blockState); static const char *TransStateAsString(TransState state); +static void ReleaseCurrentCachedSubTransaction(void); +static void RollbackAndReleaseCurrentCachedSubTransaction(void); /* ---------------------------------------------------------------- * transaction state accessors @@ -2745,6 +2749,8 @@ CommitTransactionCommand(void) { TransactionState s = CurrentTransactionState; + ReleaseCurrentCachedSubTransaction(); + switch (s->blockState) { /* @@ -2985,6 +2991,8 @@ AbortCurrentTransaction(void) { TransactionState s = CurrentTransactionState; + RollbackAndReleaseCurrentCachedSubTransaction(); + switch (s->blockState) { case TBLOCK_DEFAULT: @@ -4132,6 +4140,47 @@ RollbackToSavepoint(const char *name) BlockStateAsString(xact->blockState)); } +static void +RestoreSubTransactionState(TransactionState subxact) +{ + CurrentTransactionState = subxact; + + CurTransactionContext = subxact->curTransactionContext; + MemoryContextSwitchTo(CurTransactionContext); + CurTransactionResourceOwner = subxact->curTransactionOwner; + CurrentResourceOwner = subxact->curTransactionOwner; +} + +static void +ReleaseCurrentCachedSubTransaction(void) +{ + TransactionState s = CurrentTransactionState; + + if (s->cachedSubXact) + { + RestoreSubTransactionState(s->cachedSubXact); + ReleaseCurrentCachedSubTransaction(); + MemoryContextSwitchTo(CurTransactionContext); + CommitSubTransaction(); + Assert(s == CurrentTransactionState); + s->cachedSubXact = NULL; + } +} + +static void +RollbackAndReleaseCurrentCachedSubTransaction(void) +{ + TransactionState s = CurrentTransactionState; + + if (s->cachedSubXact) + { + RestoreSubTransactionState(s->cachedSubXact); + RollbackAndReleaseCurrentSubTransaction(); + Assert(s == CurrentTransactionState); + s->cachedSubXact = NULL; + } +} + /* * BeginInternalSubTransaction * This is the same as DefineSavepoint except it allows TBLOCK_STARTED, @@ -4142,8 +4191,8 @@ RollbackToSavepoint(const char *name) * CommitTransactionCommand/StartTransactionCommand instead of expecting * the caller to do it. */ -void -BeginInternalSubTransaction(const char *name) +static void +BeginInternalSubTransactionInternal(const char *name, bool cached) { TransactionState s = CurrentTransactionState; @@ -4170,10 +4219,31 @@ BeginInternalSubTransaction(const char *name) case TBLOCK_END: case TBLOCK_PREPARE: case TBLOCK_SUBINPROGRESS: + if (s->cachedSubXact) + { + TransactionState subxact = s->cachedSubXact; + + s->cachedSubXact = NULL; + + Assert(subxact->subTransactionId == currentSubTransactionId); + if (subxact->subTransactionId == currentSubTransactionId) + { + /* reuse cached subtransaction */ + RestoreSubTransactionState(subxact); + return; + } + else + { + ReleaseCurrentCachedSubTransaction(); + } + } + /* Normal subtransaction start */ PushTransaction(); s = CurrentTransactionState; /* changed by push */ + s->isCachedSubXact = cached; + /* * Savepoint names, like the TransactionState block itself, live * in TopTransactionContext. @@ -4206,6 +4276,18 @@ BeginInternalSubTransaction(const char *name) StartTransactionCommand(); } +void +BeginInternalSubTransaction(const char *name) +{ + BeginInternalSubTransactionInternal(name, false); +} + +void +BeginInternalCachedSubTransaction(const char *name) +{ + BeginInternalSubTransactionInternal(name, true); +} + /* * ReleaseCurrentSubTransaction * @@ -4234,8 +4316,40 @@ ReleaseCurrentSubTransaction(void) elog(ERROR, "ReleaseCurrentSubTransaction: unexpected state %s", BlockStateAsString(s->blockState)); Assert(s->state == TRANS_INPROGRESS); - MemoryContextSwitchTo(CurTransactionContext); - CommitSubTransaction(); + + ReleaseCurrentCachedSubTransaction(); + + if (s->isCachedSubXact && + !TransactionIdIsValid(s->transactionId) && + !s->cachedSubXact /*& + (!s->curTransactionOwner || + (!s->curTransactionOwner->firstchild && + s->curTransactionOwner->nlocks <= 0))*/) + { + if (s->curTransactionOwner) + { + ResourceOwnerRelease(s->curTransactionOwner, + RESOURCE_RELEASE_BEFORE_LOCKS, + true, false); + ResourceOwnerRelease(s->curTransactionOwner, + RESOURCE_RELEASE_LOCKS, + true, false); + ResourceOwnerRelease(s->curTransactionOwner, + RESOURCE_RELEASE_AFTER_LOCKS, + true, false); + } + + Assert(!s->parent->cachedSubXact); + s->parent->cachedSubXact = s; + + PopTransaction(false); + } + else + { + MemoryContextSwitchTo(CurTransactionContext); + CommitSubTransaction(); + } + s = CurrentTransactionState; /* changed by pop */ Assert(s->state == TRANS_INPROGRESS); } @@ -4291,6 +4405,8 @@ RollbackAndReleaseCurrentSubTransaction(void) break; } + RollbackAndReleaseCurrentCachedSubTransaction(); + /* * Abort the current subtransaction, if needed. */ @@ -4654,7 +4770,7 @@ CommitSubTransaction(void) s->state = TRANS_DEFAULT; - PopTransaction(); + PopTransaction(true); } /* @@ -4831,7 +4947,7 @@ CleanupSubTransaction(void) s->state = TRANS_DEFAULT; - PopTransaction(); + PopTransaction(true); } /* @@ -4901,11 +5017,11 @@ PushTransaction(void) * if it has a local pointer to it after calling this function. */ static void -PopTransaction(void) +PopTransaction(bool free) { TransactionState s = CurrentTransactionState; - if (s->state != TRANS_DEFAULT) + if (free && s->state != TRANS_DEFAULT) elog(WARNING, "PopTransaction while in %s state", TransStateAsString(s->state)); @@ -4922,6 +5038,9 @@ PopTransaction(void) CurTransactionResourceOwner = s->parent->curTransactionOwner; CurrentResourceOwner = s->parent->curTransactionOwner; + if (!free) + return; + /* Free the old child structure */ if (s->name) pfree(s->name); diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 1ca6d7b..c13143e 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -4531,7 +4531,7 @@ ExecEvalJson(ExprState *state, ExprEvalStep *op, ExprContext *econtext) MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; - BeginInternalSubTransaction(NULL); + BeginInternalCachedSubTransaction(NULL); /* Want to execute expressions inside function's memory context */ MemoryContextSwitchTo(oldcontext); diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 083e879..8f7be9a 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -380,6 +380,7 @@ extern void ReleaseSavepoint(const char *name); extern void DefineSavepoint(const char *name); extern void RollbackToSavepoint(const char *name); extern void BeginInternalSubTransaction(const char *name); +extern void BeginInternalCachedSubTransaction(const char *name); extern void ReleaseCurrentSubTransaction(void); extern void RollbackAndReleaseCurrentSubTransaction(void); extern bool IsSubTransaction(void);