aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/transam/xact.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/transam/xact.c')
-rw-r--r--src/backend/access/transam/xact.c101
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);
}
/*