Commit 4c77cbb2 authored by Tom Lane's avatar Tom Lane

PortalRun must guard against the possibility that the portal it's

running contains VACUUM or a similar command that will internally start
and commit transactions.  In such a case, the original caller values of
CurrentMemoryContext and CurrentResourceOwner will point to objects that
will be destroyed by the internal commit.  We must restore these pointers
to point to the newly-manufactured transaction context and resource owner,
rather than possibly pointing to deleted memory.
Also tweak xact.c so that AbortTransaction and AbortSubTransaction
forcibly restore a sane value for CurrentResourceOwner, much as they
have always done for CurrentMemoryContext.  I'm not certain this is
necessary but I'm feeling paranoid today.
Responds to Sean Chittenden's bug report of 4-Oct.
parent ee7de3d6
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.190 2004/09/16 20:17:16 tgl Exp $ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.191 2004/10/04 21:52:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -205,6 +205,7 @@ static void AssignSubTransactionId(TransactionState s); ...@@ -205,6 +205,7 @@ static void AssignSubTransactionId(TransactionState s);
static void AbortTransaction(void); static void AbortTransaction(void);
static void AtAbort_Memory(void); static void AtAbort_Memory(void);
static void AtCleanup_Memory(void); static void AtCleanup_Memory(void);
static void AtAbort_ResourceOwner(void);
static void AtCommit_LocalCache(void); static void AtCommit_LocalCache(void);
static void AtCommit_Memory(void); static void AtCommit_Memory(void);
static void AtStart_Cache(void); static void AtStart_Cache(void);
...@@ -229,6 +230,7 @@ static void PopTransaction(void); ...@@ -229,6 +230,7 @@ static void PopTransaction(void);
static void AtSubAbort_Memory(void); static void AtSubAbort_Memory(void);
static void AtSubCleanup_Memory(void); static void AtSubCleanup_Memory(void);
static void AtSubAbort_ResourceOwner(void);
static void AtSubCommit_Memory(void); static void AtSubCommit_Memory(void);
static void AtSubStart_Memory(void); static void AtSubStart_Memory(void);
static void AtSubStart_ResourceOwner(void); static void AtSubStart_ResourceOwner(void);
...@@ -1103,7 +1105,6 @@ AtAbort_Memory(void) ...@@ -1103,7 +1105,6 @@ AtAbort_Memory(void)
MemoryContextSwitchTo(TopMemoryContext); MemoryContextSwitchTo(TopMemoryContext);
} }
/* /*
* AtSubAbort_Memory * AtSubAbort_Memory
*/ */
...@@ -1115,6 +1116,33 @@ AtSubAbort_Memory(void) ...@@ -1115,6 +1116,33 @@ AtSubAbort_Memory(void)
MemoryContextSwitchTo(TopTransactionContext); MemoryContextSwitchTo(TopTransactionContext);
} }
/*
* AtAbort_ResourceOwner
*/
static void
AtAbort_ResourceOwner(void)
{
/*
* Make sure we have a valid ResourceOwner, if possible (else it
* will be NULL, which is OK)
*/
CurrentResourceOwner = TopTransactionResourceOwner;
}
/*
* AtSubAbort_ResourceOwner
*/
static void
AtSubAbort_ResourceOwner(void)
{
TransactionState s = CurrentTransactionState;
/* Make sure we have a valid ResourceOwner */
CurrentResourceOwner = s->curTransactionOwner;
}
/* /*
* AtSubAbort_childXids * AtSubAbort_childXids
*/ */
...@@ -1598,8 +1626,9 @@ AbortTransaction(void) ...@@ -1598,8 +1626,9 @@ AbortTransaction(void)
*/ */
s->state = TRANS_ABORT; s->state = TRANS_ABORT;
/* Make sure we are in a valid memory context */ /* Make sure we have a valid memory context and resource owner */
AtAbort_Memory(); AtAbort_Memory();
AtAbort_ResourceOwner();
/* /*
* Reset user id which might have been changed transiently. We cannot * Reset user id which might have been changed transiently. We cannot
...@@ -3338,6 +3367,7 @@ AbortSubTransaction(void) ...@@ -3338,6 +3367,7 @@ AbortSubTransaction(void)
* do abort processing * do abort processing
*/ */
AtSubAbort_Memory(); AtSubAbort_Memory();
AtSubAbort_ResourceOwner();
/* /*
* We can skip all this stuff if the subxact failed before creating * We can skip all this stuff if the subxact failed before creating
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.87 2004/09/13 20:07:05 tgl Exp $ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.88 2004/10/04 21:52:15 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -491,12 +491,14 @@ PortalRun(Portal portal, long count, ...@@ -491,12 +491,14 @@ PortalRun(Portal portal, long count,
char *completionTag) char *completionTag)
{ {
bool result; bool result;
ResourceOwner saveTopTransactionResourceOwner;
MemoryContext saveTopTransactionContext;
Portal saveActivePortal; Portal saveActivePortal;
Snapshot saveActiveSnapshot; Snapshot saveActiveSnapshot;
ResourceOwner saveResourceOwner; ResourceOwner saveResourceOwner;
MemoryContext savePortalContext; MemoryContext savePortalContext;
MemoryContext saveQueryContext; MemoryContext saveQueryContext;
MemoryContext oldContext; MemoryContext saveMemoryContext;
AssertArg(PortalIsValid(portal)); AssertArg(PortalIsValid(portal));
...@@ -523,12 +525,26 @@ PortalRun(Portal portal, long count, ...@@ -523,12 +525,26 @@ PortalRun(Portal portal, long count,
/* /*
* Set up global portal context pointers. * Set up global portal context pointers.
*/ *
* We have to play a special game here to support utility commands like
* VACUUM and CLUSTER, which internally start and commit transactions.
* When we are called to execute such a command, CurrentResourceOwner
* will be pointing to the TopTransactionResourceOwner --- which will
* be destroyed and replaced in the course of the internal commit and
* restart. So we need to be prepared to restore it as pointing to
* the exit-time TopTransactionResourceOwner. (Ain't that ugly? This
* idea of internally starting whole new transactions is not good.)
* CurrentMemoryContext has a similar problem, but the other pointers
* we save here will be NULL or pointing to longer-lived objects.
*/
saveTopTransactionResourceOwner = TopTransactionResourceOwner;
saveTopTransactionContext = TopTransactionContext;
saveActivePortal = ActivePortal; saveActivePortal = ActivePortal;
saveActiveSnapshot = ActiveSnapshot; saveActiveSnapshot = ActiveSnapshot;
saveResourceOwner = CurrentResourceOwner; saveResourceOwner = CurrentResourceOwner;
savePortalContext = PortalContext; savePortalContext = PortalContext;
saveQueryContext = QueryContext; saveQueryContext = QueryContext;
saveMemoryContext = CurrentMemoryContext;
PG_TRY(); PG_TRY();
{ {
ActivePortal = portal; ActivePortal = portal;
...@@ -537,7 +553,7 @@ PortalRun(Portal portal, long count, ...@@ -537,7 +553,7 @@ PortalRun(Portal portal, long count,
PortalContext = PortalGetHeapMemory(portal); PortalContext = PortalGetHeapMemory(portal);
QueryContext = portal->queryContext; QueryContext = portal->queryContext;
oldContext = MemoryContextSwitchTo(PortalContext); MemoryContextSwitchTo(PortalContext);
switch (portal->strategy) switch (portal->strategy)
{ {
...@@ -620,8 +636,15 @@ PortalRun(Portal portal, long count, ...@@ -620,8 +636,15 @@ PortalRun(Portal portal, long count,
portal->status = PORTAL_FAILED; portal->status = PORTAL_FAILED;
/* Restore global vars and propagate error */ /* Restore global vars and propagate error */
if (saveMemoryContext == saveTopTransactionContext)
MemoryContextSwitchTo(TopTransactionContext);
else
MemoryContextSwitchTo(saveMemoryContext);
ActivePortal = saveActivePortal; ActivePortal = saveActivePortal;
ActiveSnapshot = saveActiveSnapshot; ActiveSnapshot = saveActiveSnapshot;
if (saveResourceOwner == saveTopTransactionResourceOwner)
CurrentResourceOwner = TopTransactionResourceOwner;
else
CurrentResourceOwner = saveResourceOwner; CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext; PortalContext = savePortalContext;
QueryContext = saveQueryContext; QueryContext = saveQueryContext;
...@@ -630,10 +653,15 @@ PortalRun(Portal portal, long count, ...@@ -630,10 +653,15 @@ PortalRun(Portal portal, long count,
} }
PG_END_TRY(); PG_END_TRY();
MemoryContextSwitchTo(oldContext); if (saveMemoryContext == saveTopTransactionContext)
MemoryContextSwitchTo(TopTransactionContext);
else
MemoryContextSwitchTo(saveMemoryContext);
ActivePortal = saveActivePortal; ActivePortal = saveActivePortal;
ActiveSnapshot = saveActiveSnapshot; ActiveSnapshot = saveActiveSnapshot;
if (saveResourceOwner == saveTopTransactionResourceOwner)
CurrentResourceOwner = TopTransactionResourceOwner;
else
CurrentResourceOwner = saveResourceOwner; CurrentResourceOwner = saveResourceOwner;
PortalContext = savePortalContext; PortalContext = savePortalContext;
QueryContext = saveQueryContext; QueryContext = saveQueryContext;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment