]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Fix bug noted by Bruce: FETCH in an already-aborted transaction block
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Apr 2000 21:44:40 +0000 (21:44 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 4 Apr 2000 21:44:40 +0000 (21:44 +0000)
would crash, due to premature invocation of SetQuerySnapshot().  Clean
up problems with handling of multiple queries by splitting
pg_parse_and_plan into two routines.  The old code would not, for
example, do the right thing with END; SELECT... submitted in one query
string when it had been in transaction abort state, because it'd decide
to skip planning the SELECT before it had executed the END.  New
arrangement is simpler and doesn't force caller to plan if only
parse+rewrite is needed.

src/backend/catalog/pg_proc.c
src/backend/executor/functions.c
src/backend/executor/spi.c
src/backend/tcop/postgres.c
src/backend/tcop/utility.c
src/include/tcop/tcopprot.h

index c1633ecd8346db393988a6d8eb7c14e4719dbfd2..6557335d994b0bd8dfb3695d3a518556dc2e7caf 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.40 2000/01/26 05:56:11 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.41 2000/04/04 21:44:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,7 +61,6 @@ ProcedureCreate(char *procedureName,
        Oid                     typeObjectId;
        List       *x;
        List       *querytree_list;
-       List       *plan_list;
        Oid                     typev[FUNC_MAX_ARGS];
        Oid                     relid;
        Oid                     toid;
@@ -222,9 +221,8 @@ ProcedureCreate(char *procedureName,
 
        if (strcmp(languageName, "sql") == 0)
        {
-               plan_list = pg_parse_and_plan(prosrc, typev, parameterCount,
-                                                                         &querytree_list, dest, FALSE);
-
+               querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount,
+                                                                                         FALSE);
                /* typecheck return value */
                pg_checkretval(typeObjectId, querytree_list);
        }
index 5a7f4c0889fb72aae4ff3b8b1781e594c9f73c80..3854978029b2393a7f5c1f0f3b59d905164c027d 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.31 2000/01/26 05:56:22 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.32 2000/04/04 21:44:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -84,7 +84,6 @@ init_execution_state(FunctionCachePtr fcache,
        execution_state *nextes;
        execution_state *preves;
        List       *queryTree_list,
-                          *planTree_list,
                           *qtl_item;
        int                     nargs = fcache->nargs;
 
@@ -92,15 +91,17 @@ init_execution_state(FunctionCachePtr fcache,
        nextes = newes;
        preves = (execution_state *) NULL;
 
-       planTree_list = pg_parse_and_plan(fcache->src, fcache->argOidVect,
-                                                                       nargs, &queryTree_list, None, FALSE);
+       queryTree_list = pg_parse_and_rewrite(fcache->src, fcache->argOidVect,
+                                                                                 nargs, FALSE);
 
        foreach(qtl_item, queryTree_list)
        {
                Query      *queryTree = lfirst(qtl_item);
-               Plan       *planTree = lfirst(planTree_list);
+               Plan       *planTree;
                EState     *estate;
 
+               planTree = pg_plan_query(queryTree);
+
                if (!nextes)
                        nextes = (execution_state *) palloc(sizeof(execution_state));
                if (preves)
@@ -141,8 +142,6 @@ init_execution_state(FunctionCachePtr fcache,
                nextes->estate = estate;
                preves = nextes;
                nextes = (execution_state *) NULL;
-
-               planTree_list = lnext(planTree_list);
        }
 
        return newes;
@@ -151,15 +150,12 @@ init_execution_state(FunctionCachePtr fcache,
 static TupleDesc
 postquel_start(execution_state *es)
 {
-#ifdef FUNC_UTIL_PATCH
-
        /*
         * Do nothing for utility commands. (create, destroy...)  DZ -
         * 30-8-1996
         */
        if (es->qd->operation == CMD_UTILITY)
                return (TupleDesc) NULL;
-#endif
        return ExecutorStart(es->qd, es->estate);
 }
 
@@ -168,12 +164,10 @@ postquel_getnext(execution_state *es)
 {
        int                     feature;
 
-#ifdef FUNC_UTIL_PATCH
        if (es->qd->operation == CMD_UTILITY)
        {
-
                /*
-                * Process an utility command. (create, destroy...)  DZ -
+                * Process a utility command. (create, destroy...)  DZ -
                 * 30-8-1996
                 */
                ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
@@ -181,7 +175,6 @@ postquel_getnext(execution_state *es)
                        CommandCounterIncrement();
                return (TupleTableSlot *) NULL;
        }
-#endif
 
        feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
 
@@ -191,15 +184,12 @@ postquel_getnext(execution_state *es)
 static void
 postquel_end(execution_state *es)
 {
-#ifdef FUNC_UTIL_PATCH
-
        /*
         * Do nothing for utility commands. (create, destroy...)  DZ -
         * 30-8-1996
         */
        if (es->qd->operation == CMD_UTILITY)
                return;
-#endif
        ExecutorEnd(es->qd, es->estate);
 }
 
index 7c65c66814f863bdaa590c88af77936c27d0e3ea..016d15ae8ac56a0c6a4b38aeda401240ed7160d2 100644 (file)
@@ -3,7 +3,7 @@
  * spi.c
  *                             Server Programming Interface
  *
- * $Id: spi.c,v 1.44 1999/12/16 22:19:44 wieck Exp $
+ * $Id: spi.c,v 1.45 2000/04/04 21:44:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -622,7 +622,6 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
        List       *queryTree_list;
        List       *planTree_list;
        List       *queryTree_list_item;
-       List       *ptlist;
        QueryDesc  *qdesc;
        Query      *queryTree;
        Plan       *planTree;
@@ -645,18 +644,20 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
                nargs = plan->nargs;
                argtypes = plan->argtypes;
        }
-       ptlist = planTree_list =
-               pg_parse_and_plan(src, argtypes, nargs, &queryTree_list, None, FALSE);
+
+       queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs, FALSE);
 
        _SPI_current->qtlist = queryTree_list;
 
+       planTree_list = NIL;
+
        foreach(queryTree_list_item, queryTree_list)
        {
                queryTree = (Query *) lfirst(queryTree_list_item);
-               planTree = lfirst(planTree_list);
-               planTree_list = lnext(planTree_list);
-               islastquery = (planTree_list == NIL);   /* assume lists are same
-                                                                                                * len */
+               islastquery = (lnext(queryTree_list_item) == NIL);
+
+               planTree = pg_plan_query(queryTree);
+               planTree_list = lappend(planTree_list, planTree);
 
                if (queryTree->commandType == CMD_UTILITY)
                {
@@ -707,7 +708,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
        }
 
        plan->qtlist = queryTree_list;
-       plan->ptlist = ptlist;
+       plan->ptlist = planTree_list;
 
        return res;
 }
index a70418637f729ae4c2225996a2591893155f2485..65b6bfbd6c0016036ea44629b694b7097261d0bc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.148 2000/03/23 23:16:48 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.149 2000/04/04 21:44:39 tgl Exp $
  *
  * NOTES
  *       this is the "main" module of the postgres backend and
@@ -377,22 +377,23 @@ ReadCommand(StringInfo inBuf)
        return result;
 }
 
+
+/*
+ * Parse a query string and pass it through the rewriter.
+ *
+ * A list of Query nodes is returned, since the string might contain
+ * multiple queries and/or the rewriter might expand one query to several.
+ */
 List *
-pg_parse_and_plan(char *query_string,  /* string to execute */
-                                 Oid *typev,   /* argument types */
-                                 int nargs,    /* number of arguments */
-                                 List **queryListP,    /* returned pointer to the parse
-                                                                                * trees */
-                                 CommandDest dest,             /* where results should go */
-                                 bool aclOverride)
+pg_parse_and_rewrite(char *query_string,       /* string to execute */
+                                        Oid *typev,                    /* argument types */
+                                        int nargs,                             /* number of arguments */
+                                        bool aclOverride)
 {
-       List       *querytree_list = NIL;
-       List       *plan_list = NIL;
+       List       *querytree_list;
        List       *querytree_list_item;
        Query      *querytree;
-       Plan       *plan;
        List       *new_list;
-       List       *rewritten;
 
        if (DebugPrintQuery)
        {
@@ -448,15 +449,16 @@ pg_parse_and_plan(char *query_string,     /* string to execute */
                else
                {
                        /* rewrite regular queries */
-                       rewritten = QueryRewrite(querytree);
+                       List *rewritten = QueryRewrite(querytree);
                        new_list = nconc(new_list, rewritten);
                }
        }
 
        querytree_list = new_list;
 
-       /*
-        * Override ACL checking if requested
+       /* ----------------
+        *      (3) If ACL override is requested, mark queries for no ACL check.
+        * ----------------
         */
        if (aclOverride)
        {
@@ -503,87 +505,54 @@ pg_parse_and_plan(char *query_string,     /* string to execute */
                }
        }
 
-       foreach(querytree_list_item, querytree_list)
-       {
-               querytree = (Query *) lfirst(querytree_list_item);
-
-               /*
-                * For each query that isn't a utility invocation, generate a
-                * plan.
-                */
-
-               if (querytree->commandType != CMD_UTILITY)
-               {
+       return querytree_list;
+}
 
-                       if (IsAbortedTransactionBlockState())
-                       {
-                               /* ----------------
-                                *       the EndCommand() stuff is to tell the frontend
-                                *       that the command ended. -cim 6/1/90
-                                * ----------------
-                                */
-                               char       *tag = "*ABORT STATE*";
 
-                               EndCommand(tag, dest);
+/* Generate a plan for a single query. */
+Plan *
+pg_plan_query(Query *querytree)
+{
+       Plan       *plan;
 
-                               elog(NOTICE, "(transaction aborted): %s",
-                                        "queries ignored until END");
+       /* Utility commands have no plans. */
+       if (querytree->commandType == CMD_UTILITY)
+               return NULL;
 
-                               if (queryListP)
-                                       *queryListP = NIL;
-                               return NIL;
-                       }
+       if (ShowPlannerStats)
+               ResetUsage();
 
-                       if (ShowPlannerStats)
-                               ResetUsage();
+       /* call that optimizer */
+       plan = planner(querytree);
 
-                       /* call that optimizer */
-                       plan = planner(querytree);
+       if (ShowPlannerStats)
+       {
+               fprintf(stderr, "! Planner Stats:\n");
+               ShowUsage();
+       }
 
-                       if (ShowPlannerStats)
-                       {
-                               fprintf(stderr, "! Planner Stats:\n");
-                               ShowUsage();
-                       }
-                       plan_list = lappend(plan_list, plan);
-#ifdef INDEXSCAN_PATCH
-                       /* ----------------
-                        *      Print plan if debugging.
-                        *      This has been moved here to get debugging output
-                        *      also for queries in functions.  DZ - 27-8-1996
-                        * ----------------
-                        */
-                       if (DebugPrintPlan || DebugPPrintPlan)
-                       {
-                               if (DebugPPrintPlan)
-                               {
-                                       TPRINTF(TRACE_PRETTY_PLAN, "plan:");
-                                       nodeDisplay(plan);
-                               }
-                               else
-                               {
-                                       TPRINTF(TRACE_PLAN, "plan:");
-                                       printf("\n%s\n\n", nodeToString(plan));
-                               }
-                       }
-#endif
+       /* ----------------
+        *      Print plan if debugging.
+        * ----------------
+        */
+       if (DebugPrintPlan || DebugPPrintPlan)
+       {
+               if (DebugPPrintPlan)
+               {
+                       TPRINTF(TRACE_PRETTY_PLAN, "plan:");
+                       nodeDisplay(plan);
                }
-
-               /*
-                * If the command is an utility append a null plan. This is needed
-                * to keep the plan_list aligned with the querytree_list or the
-                * function executor will crash.  DZ - 30-8-1996
-                */
                else
-                       plan_list = lappend(plan_list, NULL);
+               {
+                       TPRINTF(TRACE_PLAN, "plan:");
+                       printf("\n%s\n\n", nodeToString(plan));
+               }
        }
 
-       if (queryListP)
-               *queryListP = querytree_list;
-
-       return plan_list;
+       return plan;
 }
 
+
 /* ----------------------------------------------------------------
  *             pg_exec_query()
  *
@@ -620,39 +589,31 @@ pg_exec_query_dest(char *query_string,    /* string to execute */
                                                                                 * of superusers */
 {
        List       *querytree_list;
-       List       *plan_list;
-       Query      *querytree;
-       Plan       *plan;
-       int                     j;
 
-       /* plan the queries */
-       plan_list = pg_parse_and_plan(query_string, NULL, 0,
-                                                                 &querytree_list, dest, aclOverride);
-
-       /* if we got a cancel signal whilst planning, quit */
-       if (QueryCancel)
-               CancelQuery();
-
-       /* OK, do it to it! */
+       /* parse and rewrite the queries */
+       querytree_list = pg_parse_and_rewrite(query_string, NULL, 0,
+                                                                                 aclOverride);
 
        /*
         * NOTE: we do not use "foreach" here because we want to be sure the
-        * list pointers have been advanced before the query is executed. We
+        * list pointer has been advanced before the query is executed. We
         * need to do that because VACUUM has a nasty little habit of doing
         * CommitTransactionCommand at startup, and that will release the
-        * memory holding our parse/plan lists :-(.  This needs a better
+        * memory holding our parse list :-(.  This needs a better
         * solution --- currently, the code will crash if someone submits
         * "vacuum; something-else" in a single query string.  But memory
         * allocation needs redesigned anyway, so this will have to do for
         * now.
         */
-
        while (querytree_list)
        {
-               querytree = (Query *) lfirst(querytree_list);
+               Query      *querytree = (Query *) lfirst(querytree_list);
+
                querytree_list = lnext(querytree_list);
-               plan = (Plan *) lfirst(plan_list);
-               plan_list = lnext(plan_list);
+
+               /* if we got a cancel signal in parsing or prior command, quit */
+               if (QueryCancel)
+                       CancelQuery();
 
                if (querytree->commandType == CMD_UTILITY)
                {
@@ -668,42 +629,38 @@ pg_exec_query_dest(char *query_string,    /* string to execute */
                        else if (Verbose)
                                TPRINTF(TRACE_VERBOSE, "ProcessUtility");
 
-                       /*
-                        * We have to set query SnapShot in the case of FETCH or COPY TO.
-                        */
-                       if (nodeTag(querytree->utilityStmt) == T_FetchStmt ||
-                               (nodeTag(querytree->utilityStmt) == T_CopyStmt && 
-                               ((CopyStmt *)(querytree->utilityStmt))->direction != FROM))
-                               SetQuerySnapshot();
                        ProcessUtility(querytree->utilityStmt, dest);
                }
                else
                {
-#ifdef INDEXSCAN_PATCH
+                       Plan       *plan;
+                       int                     j;
 
-                       /*
-                        * Print moved in pg_parse_and_plan.    DZ - 27-8-1996
-                        */
-#else
-                       /* ----------------
-                        *      print plan if debugging
-                        * ----------------
-                        */
-                       if (DebugPrintPlan || DebugPPrintPlan)
+                       /* If aborted transaction, quit now */
+                       if (IsAbortedTransactionBlockState())
                        {
-                               if (DebugPPrintPlan)
-                               {
-                                       TPRINTF(TRACE_PRETTY_PLAN, "plan:");
-                                       nodeDisplay(plan);
-                               }
-                               else
-                               {
-                                       TPRINTF(TRACE_PLAN, "plan:");
-                                       printf("\n%s\n\n", nodeToString(plan));
-                               }
+                               /* ----------------
+                                *       the EndCommand() stuff is to tell the frontend
+                                *       that the command ended. -cim 6/1/90
+                                * ----------------
+                                */
+                               char       *tag = "*ABORT STATE*";
+
+                               elog(NOTICE, "current transaction is aborted, "
+                                        "queries ignored until end of transaction block");
+
+                               EndCommand(tag, dest);
+
+                               break;
                        }
-#endif
 
+                       plan = pg_plan_query(querytree);
+
+                       /* if we got a cancel signal whilst planning, quit */
+                       if (QueryCancel)
+                               CancelQuery();
+
+                       /* Initialize snapshot state for query */
                        SetQuerySnapshot();
 
                        /*
@@ -1505,7 +1462,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
        if (!IsUnderPostmaster)
        {
                puts("\nPOSTGRES backend interactive interface ");
-               puts("$Revision: 1.148 $ $Date: 2000/03/23 23:16:48 $\n");
+               puts("$Revision: 1.149 $ $Date: 2000/04/04 21:44:39 $\n");
        }
 
        /*
index 62ad6d8f9571bd780b49c74ecfd8009857c68f28..d3ba5b319dd7a8072726d61ba698eb1bb02b20bd 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.84 2000/02/19 02:29:07 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.85 2000/04/04 21:44:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,6 +136,8 @@ ProcessUtility(Node *parsetree,
                                PS_SET_STATUS(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
                                CHECK_IF_ABORTED();
 
+                               SetQuerySnapshot();
+
                                forward = (bool) (stmt->direction == FORWARD);
 
                                /*
@@ -255,6 +257,9 @@ ProcessUtility(Node *parsetree,
                                PS_SET_STATUS(commandTag = "COPY");
                                CHECK_IF_ABORTED();
 
+                               if (stmt->direction != FROM)
+                                       SetQuerySnapshot();
+
                                DoCopy(stmt->relname,
                                           stmt->binary,
                                           stmt->oids,
index 283e517ccce18d726f88cbc183a90501d56a4500..8255770068fe80e73c0ff6b78b758b2ff354fdc4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tcopprot.h,v 1.25 2000/02/20 14:28:28 petere Exp $
+ * $Id: tcopprot.h,v 1.26 2000/04/04 21:44:37 tgl Exp $
  *
  * OLD COMMENTS
  *       This file was created so that other c files could get the two
@@ -29,12 +29,15 @@ extern bool InError;
 extern bool    ExitAfterAbort;
 
 #ifndef BOOTSTRAP_INCLUDE
-extern List *pg_parse_and_plan(char *query_string, Oid *typev, int nargs,
-                                 List **queryListP, CommandDest dest,
-                                 bool aclOverride);
+
+extern List *pg_parse_and_rewrite(char *query_string,
+                                                                 Oid *typev, int nargs,
+                                                                 bool aclOverride);
+extern Plan *pg_plan_query(Query *querytree);
 extern void pg_exec_query_acl_override(char *query_string);
-extern void
-                       pg_exec_query_dest(char *query_string, CommandDest dest, bool aclOverride);
+extern void pg_exec_query_dest(char *query_string,
+                                                          CommandDest dest,
+                                                          bool aclOverride);
 
 #endif  /* BOOTSTRAP_INCLUDE */