]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
PortalRun must guard against the possibility that the portal it's
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Oct 2004 21:52:15 +0000 (21:52 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 4 Oct 2004 21:52:15 +0000 (21:52 +0000)
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.

src/backend/access/transam/xact.c
src/backend/tcop/pquery.c

index 17db7dd78d52f2126b669b1c9e88ade46b35ae23..321a86f30c22f6c33ac05098d603ba19330771b3 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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);
 static void AbortTransaction(void);
 static void AtAbort_Memory(void);
 static void AtCleanup_Memory(void);
+static void AtAbort_ResourceOwner(void);
 static void AtCommit_LocalCache(void);
 static void AtCommit_Memory(void);
 static void AtStart_Cache(void);
@@ -229,6 +230,7 @@ static void PopTransaction(void);
 
 static void AtSubAbort_Memory(void);
 static void AtSubCleanup_Memory(void);
+static void AtSubAbort_ResourceOwner(void);
 static void AtSubCommit_Memory(void);
 static void AtSubStart_Memory(void);
 static void AtSubStart_ResourceOwner(void);
@@ -1103,7 +1105,6 @@ AtAbort_Memory(void)
                MemoryContextSwitchTo(TopMemoryContext);
 }
 
-
 /*
  * AtSubAbort_Memory
  */
@@ -1115,6 +1116,33 @@ AtSubAbort_Memory(void)
        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
  */
@@ -1598,8 +1626,9 @@ AbortTransaction(void)
         */
        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_ResourceOwner();
 
        /*
         * Reset user id which might have been changed transiently.  We cannot
@@ -3338,6 +3367,7 @@ AbortSubTransaction(void)
         * do abort processing
         */
        AtSubAbort_Memory();
+       AtSubAbort_ResourceOwner();
 
        /*
         * We can skip all this stuff if the subxact failed before creating
index 5a1c8b4867b39c36bec0c6f7551bc6beda0e8e30..2c4c2f3c7e5d6122dce0f583def04b824ecd1081 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
                  char *completionTag)
 {
        bool            result;
+       ResourceOwner saveTopTransactionResourceOwner;
+       MemoryContext saveTopTransactionContext;
        Portal          saveActivePortal;
        Snapshot        saveActiveSnapshot;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
        MemoryContext saveQueryContext;
-       MemoryContext oldContext;
+       MemoryContext saveMemoryContext;
 
        AssertArg(PortalIsValid(portal));
 
@@ -523,12 +525,26 @@ PortalRun(Portal portal, long count,
 
        /*
         * 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;
        saveActiveSnapshot = ActiveSnapshot;
        saveResourceOwner = CurrentResourceOwner;
        savePortalContext = PortalContext;
        saveQueryContext = QueryContext;
+       saveMemoryContext = CurrentMemoryContext;
        PG_TRY();
        {
                ActivePortal = portal;
@@ -537,7 +553,7 @@ PortalRun(Portal portal, long count,
                PortalContext = PortalGetHeapMemory(portal);
                QueryContext = portal->queryContext;
 
-               oldContext = MemoryContextSwitchTo(PortalContext);
+               MemoryContextSwitchTo(PortalContext);
 
                switch (portal->strategy)
                {
@@ -620,9 +636,16 @@ PortalRun(Portal portal, long count,
                portal->status = PORTAL_FAILED;
 
                /* Restore global vars and propagate error */
+               if (saveMemoryContext == saveTopTransactionContext)
+                       MemoryContextSwitchTo(TopTransactionContext);
+               else
+                       MemoryContextSwitchTo(saveMemoryContext);
                ActivePortal = saveActivePortal;
                ActiveSnapshot = saveActiveSnapshot;
-               CurrentResourceOwner = saveResourceOwner;
+               if (saveResourceOwner == saveTopTransactionResourceOwner)
+                       CurrentResourceOwner = TopTransactionResourceOwner;
+               else
+                       CurrentResourceOwner = saveResourceOwner;
                PortalContext = savePortalContext;
                QueryContext = saveQueryContext;
 
@@ -630,11 +653,16 @@ PortalRun(Portal portal, long count,
        }
        PG_END_TRY();
 
-       MemoryContextSwitchTo(oldContext);
-
+       if (saveMemoryContext == saveTopTransactionContext)
+               MemoryContextSwitchTo(TopTransactionContext);
+       else
+               MemoryContextSwitchTo(saveMemoryContext);
        ActivePortal = saveActivePortal;
        ActiveSnapshot = saveActiveSnapshot;
-       CurrentResourceOwner = saveResourceOwner;
+       if (saveResourceOwner == saveTopTransactionResourceOwner)
+               CurrentResourceOwner = TopTransactionResourceOwner;
+       else
+               CurrentResourceOwner = saveResourceOwner;
        PortalContext = savePortalContext;
        QueryContext = saveQueryContext;