diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 9bda1aa6bc..d119ab909d 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[] */ @@ -1174,6 +1175,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 @@ -1190,17 +1196,15 @@ AtStart_Memory(void) 32 * 1024); /* - * We shouldn't have a transaction context already. + * 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. */ - Assert(TopTransactionContext == NULL); - - /* - * Create a toplevel context for the transaction. - */ - 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 @@ -1251,6 +1255,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. @@ -1576,20 +1585,30 @@ AtCCI_LocalCache(void) static void AtCommit_Memory(void) { - /* - * Now that we're "out" of a transaction, have the system allocate things - * in the top memory context instead of per-transaction contexts. - */ - MemoryContextSwitchTo(TopMemoryContext); + TransactionState s = CurrentTransactionState; /* - * Release all transaction-local memory. + * 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(s->priorContext); + + /* + * 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); } /* diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 59d1dfc7e2..754f505c13 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -440,12 +440,10 @@ IdentifySystem(void) /* syscache access needs a transaction env. */ StartTransactionCommand(); - /* make dbname live outside TX context */ - MemoryContextSwitchTo(cur); dbname = get_database_name(MyDatabaseId); + /* copy dbname out of TX context */ + dbname = MemoryContextStrdup(cur, dbname); CommitTransactionCommand(); - /* CommitTransactionCommand switches to TopMemoryContext */ - MemoryContextSwitchTo(cur); } dest = CreateDestReceiver(DestRemoteSimple); diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c index cfa2755196..a2f94b1050 100644 --- a/src/backend/tcop/backend_startup.c +++ b/src/backend/tcop/backend_startup.c @@ -196,9 +196,8 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac) * Save remote_host and remote_port in port structure (after this, they * will appear in log_line_prefix data for log messages). */ - oldcontext = MemoryContextSwitchTo(TopMemoryContext); - port->remote_host = pstrdup(remote_host); - port->remote_port = pstrdup(remote_port); + port->remote_host = MemoryContextStrdup(TopMemoryContext, remote_host); + port->remote_port = MemoryContextStrdup(TopMemoryContext, remote_port); /* And now we can issue the Log_connections message, if wanted */ if (Log_connections) @@ -230,9 +229,8 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac) strspn(remote_host, "0123456789.") < strlen(remote_host) && strspn(remote_host, "0123456789ABCDEFabcdef:") < strlen(remote_host)) { - port->remote_hostname = pstrdup(remote_host); + port->remote_hostname = MemoryContextStrdup(TopMemoryContext, remote_host); } - MemoryContextSwitchTo(oldcontext); /* * Ready to begin client interaction. We will give up and _exit(1) after diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 45a3794b8e..adf71c6902 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4418,7 +4418,7 @@ PostgresMain(const char *dbname, const char *username) * Now return to normal top-level context and clear ErrorContext for * next time. */ - MemoryContextSwitchTo(TopMemoryContext); + MemoryContextSwitchTo(MessageContext); FlushErrorState(); /* diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index b42ececbe7..bde54326c6 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -1148,7 +1148,8 @@ MemoryContextAllocationFailure(MemoryContext context, Size size, int flags) { if ((flags & MCXT_ALLOC_NO_OOM) == 0) { - MemoryContextStats(TopMemoryContext); + if (TopMemoryContext) + MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"),