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[] */
{
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
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
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.
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;
}
/* ----------------------------------------------------------------
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.
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;
}
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;
/*
/* 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);
}
/*