diff options
Diffstat (limited to 'src/backend/access/transam/xact.c')
-rw-r--r-- | src/backend/access/transam/xact.c | 101 |
1 files changed, 72 insertions, 29 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 9bda1aa6bc6..d119ab909dc 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -200,6 +200,7 @@ typedef struct TransactionStateData int gucNestLevel; /* GUC context nesting depth */ MemoryContext curTransactionContext; /* my xact-lifetime context */ ResourceOwner curTransactionOwner; /* my query resources */ + MemoryContext priorContext; /* CurrentMemoryContext before xact started */ TransactionId *childXids; /* subcommitted child XIDs, in XID order */ int nChildXids; /* # of subcommitted child XIDs */ int maxChildXids; /* allocated size of childXids[] */ @@ -1175,6 +1176,11 @@ AtStart_Memory(void) TransactionState s = CurrentTransactionState; /* + * Remember the memory context that was active prior to transaction start. + */ + s->priorContext = CurrentMemoryContext; + + /* * If this is the first time through, create a private context for * AbortTransaction to work in. By reserving some space now, we can * insulate AbortTransaction from out-of-memory scenarios. Like @@ -1190,17 +1196,15 @@ AtStart_Memory(void) 32 * 1024); /* - * We shouldn't have a transaction context already. - */ - Assert(TopTransactionContext == NULL); - - /* - * Create a toplevel context for the transaction. + * Likewise, if this is the first time through, create a top-level context + * for transaction-local data. This context will be reset at transaction + * end, and then re-used in later transactions. */ - TopTransactionContext = - AllocSetContextCreate(TopMemoryContext, - "TopTransactionContext", - ALLOCSET_DEFAULT_SIZES); + if (TopTransactionContext == NULL) + TopTransactionContext = + AllocSetContextCreate(TopMemoryContext, + "TopTransactionContext", + ALLOCSET_DEFAULT_SIZES); /* * In a top-level transaction, CurTransactionContext is the same as @@ -1252,6 +1256,11 @@ AtSubStart_Memory(void) Assert(CurTransactionContext != NULL); /* + * Remember the context that was active prior to subtransaction start. + */ + s->priorContext = CurrentMemoryContext; + + /* * Create a CurTransactionContext, which will be used to hold data that * survives subtransaction commit but disappears on subtransaction abort. * We make it a child of the immediate parent's CurTransactionContext. @@ -1576,20 +1585,30 @@ AtCCI_LocalCache(void) static void AtCommit_Memory(void) { + TransactionState s = CurrentTransactionState; + /* - * Now that we're "out" of a transaction, have the system allocate things - * in the top memory context instead of per-transaction contexts. + * Return to the memory context that was current before we started the + * transaction. (In principle, this could not be any of the contexts we + * are about to delete. If it somehow is, assertions in mcxt.c will + * complain.) */ - MemoryContextSwitchTo(TopMemoryContext); + MemoryContextSwitchTo(s->priorContext); /* - * Release all transaction-local memory. + * Release all transaction-local memory. TopTransactionContext survives + * but becomes empty; any sub-contexts go away. */ Assert(TopTransactionContext != NULL); - MemoryContextDelete(TopTransactionContext); - TopTransactionContext = NULL; + MemoryContextReset(TopTransactionContext); + + /* + * Clear these pointers as a pro-forma matter. (Notionally, while + * TopTransactionContext still exists, it's currently not associated with + * this TransactionState struct.) + */ CurTransactionContext = NULL; - CurrentTransactionState->curTransactionContext = NULL; + s->curTransactionContext = NULL; } /* ---------------------------------------------------------------- @@ -1942,13 +1961,18 @@ AtSubAbort_childXids(void) static void AtCleanup_Memory(void) { - Assert(CurrentTransactionState->parent == NULL); + TransactionState s = CurrentTransactionState; + + /* Should be at top level */ + Assert(s->parent == NULL); /* - * Now that we're "out" of a transaction, have the system allocate things - * in the top memory context instead of per-transaction contexts. + * Return to the memory context that was current before we started the + * transaction. (In principle, this could not be any of the contexts we + * are about to delete. If it somehow is, assertions in mcxt.c will + * complain.) */ - MemoryContextSwitchTo(TopMemoryContext); + MemoryContextSwitchTo(s->priorContext); /* * Clear the special abort context for next time. @@ -1957,13 +1981,20 @@ AtCleanup_Memory(void) MemoryContextReset(TransactionAbortContext); /* - * Release all transaction-local memory. + * Release all transaction-local memory, the same as in AtCommit_Memory, + * except we must cope with the possibility that we didn't get as far as + * creating TopTransactionContext. */ if (TopTransactionContext != NULL) - MemoryContextDelete(TopTransactionContext); - TopTransactionContext = NULL; + MemoryContextReset(TopTransactionContext); + + /* + * Clear these pointers as a pro-forma matter. (Notionally, while + * TopTransactionContext still exists, it's currently not associated with + * this TransactionState struct.) + */ CurTransactionContext = NULL; - CurrentTransactionState->curTransactionContext = NULL; + s->curTransactionContext = NULL; } @@ -1982,8 +2013,15 @@ AtSubCleanup_Memory(void) Assert(s->parent != NULL); - /* Make sure we're not in an about-to-be-deleted context */ - MemoryContextSwitchTo(s->parent->curTransactionContext); + /* + * Return to the memory context that was current before we started the + * subtransaction. (In principle, this could not be any of the contexts + * we are about to delete. If it somehow is, assertions in mcxt.c will + * complain.) + */ + MemoryContextSwitchTo(s->priorContext); + + /* Update CurTransactionContext (might not be same as priorContext) */ CurTransactionContext = s->parent->curTransactionContext; /* @@ -4904,8 +4942,13 @@ AbortOutOfAnyTransaction(void) /* Should be out of all subxacts now */ Assert(s->parent == NULL); - /* If we didn't actually have anything to do, revert to TopMemoryContext */ - AtCleanup_Memory(); + /* + * Revert to TopMemoryContext, to ensure we exit in a well-defined state + * whether there were any transactions to close or not. (Callers that + * don't intend to exit soon should switch to some other context to avoid + * long-term memory leaks.) + */ + MemoryContextSwitchTo(TopMemoryContext); } /* |