Commit 708f82f1 authored by Tom Lane's avatar Tom Lane

Fix bug noted by Bruce: FETCH in an already-aborted transaction block

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.
parent 30d4f582
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -61,7 +61,6 @@ ProcedureCreate(char *procedureName,
Oid typeObjectId; Oid typeObjectId;
List *x; List *x;
List *querytree_list; List *querytree_list;
List *plan_list;
Oid typev[FUNC_MAX_ARGS]; Oid typev[FUNC_MAX_ARGS];
Oid relid; Oid relid;
Oid toid; Oid toid;
...@@ -222,9 +221,8 @@ ProcedureCreate(char *procedureName, ...@@ -222,9 +221,8 @@ ProcedureCreate(char *procedureName,
if (strcmp(languageName, "sql") == 0) if (strcmp(languageName, "sql") == 0)
{ {
plan_list = pg_parse_and_plan(prosrc, typev, parameterCount, querytree_list = pg_parse_and_rewrite(prosrc, typev, parameterCount,
&querytree_list, dest, FALSE); FALSE);
/* typecheck return value */ /* typecheck return value */
pg_checkretval(typeObjectId, querytree_list); pg_checkretval(typeObjectId, querytree_list);
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -84,7 +84,6 @@ init_execution_state(FunctionCachePtr fcache,
execution_state *nextes; execution_state *nextes;
execution_state *preves; execution_state *preves;
List *queryTree_list, List *queryTree_list,
*planTree_list,
*qtl_item; *qtl_item;
int nargs = fcache->nargs; int nargs = fcache->nargs;
...@@ -92,15 +91,17 @@ init_execution_state(FunctionCachePtr fcache, ...@@ -92,15 +91,17 @@ init_execution_state(FunctionCachePtr fcache,
nextes = newes; nextes = newes;
preves = (execution_state *) NULL; preves = (execution_state *) NULL;
planTree_list = pg_parse_and_plan(fcache->src, fcache->argOidVect, queryTree_list = pg_parse_and_rewrite(fcache->src, fcache->argOidVect,
nargs, &queryTree_list, None, FALSE); nargs, FALSE);
foreach(qtl_item, queryTree_list) foreach(qtl_item, queryTree_list)
{ {
Query *queryTree = lfirst(qtl_item); Query *queryTree = lfirst(qtl_item);
Plan *planTree = lfirst(planTree_list); Plan *planTree;
EState *estate; EState *estate;
planTree = pg_plan_query(queryTree);
if (!nextes) if (!nextes)
nextes = (execution_state *) palloc(sizeof(execution_state)); nextes = (execution_state *) palloc(sizeof(execution_state));
if (preves) if (preves)
...@@ -141,8 +142,6 @@ init_execution_state(FunctionCachePtr fcache, ...@@ -141,8 +142,6 @@ init_execution_state(FunctionCachePtr fcache,
nextes->estate = estate; nextes->estate = estate;
preves = nextes; preves = nextes;
nextes = (execution_state *) NULL; nextes = (execution_state *) NULL;
planTree_list = lnext(planTree_list);
} }
return newes; return newes;
...@@ -151,15 +150,12 @@ init_execution_state(FunctionCachePtr fcache, ...@@ -151,15 +150,12 @@ init_execution_state(FunctionCachePtr fcache,
static TupleDesc static TupleDesc
postquel_start(execution_state *es) postquel_start(execution_state *es)
{ {
#ifdef FUNC_UTIL_PATCH
/* /*
* Do nothing for utility commands. (create, destroy...) DZ - * Do nothing for utility commands. (create, destroy...) DZ -
* 30-8-1996 * 30-8-1996
*/ */
if (es->qd->operation == CMD_UTILITY) if (es->qd->operation == CMD_UTILITY)
return (TupleDesc) NULL; return (TupleDesc) NULL;
#endif
return ExecutorStart(es->qd, es->estate); return ExecutorStart(es->qd, es->estate);
} }
...@@ -168,12 +164,10 @@ postquel_getnext(execution_state *es) ...@@ -168,12 +164,10 @@ postquel_getnext(execution_state *es)
{ {
int feature; int feature;
#ifdef FUNC_UTIL_PATCH
if (es->qd->operation == CMD_UTILITY) if (es->qd->operation == CMD_UTILITY)
{ {
/* /*
* Process an utility command. (create, destroy...) DZ - * Process a utility command. (create, destroy...) DZ -
* 30-8-1996 * 30-8-1996
*/ */
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
...@@ -181,7 +175,6 @@ postquel_getnext(execution_state *es) ...@@ -181,7 +175,6 @@ postquel_getnext(execution_state *es)
CommandCounterIncrement(); CommandCounterIncrement();
return (TupleTableSlot *) NULL; return (TupleTableSlot *) NULL;
} }
#endif
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
...@@ -191,15 +184,12 @@ postquel_getnext(execution_state *es) ...@@ -191,15 +184,12 @@ postquel_getnext(execution_state *es)
static void static void
postquel_end(execution_state *es) postquel_end(execution_state *es)
{ {
#ifdef FUNC_UTIL_PATCH
/* /*
* Do nothing for utility commands. (create, destroy...) DZ - * Do nothing for utility commands. (create, destroy...) DZ -
* 30-8-1996 * 30-8-1996
*/ */
if (es->qd->operation == CMD_UTILITY) if (es->qd->operation == CMD_UTILITY)
return; return;
#endif
ExecutorEnd(es->qd, es->estate); ExecutorEnd(es->qd, es->estate);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* spi.c * spi.c
* Server Programming Interface * 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) ...@@ -622,7 +622,6 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
List *queryTree_list; List *queryTree_list;
List *planTree_list; List *planTree_list;
List *queryTree_list_item; List *queryTree_list_item;
List *ptlist;
QueryDesc *qdesc; QueryDesc *qdesc;
Query *queryTree; Query *queryTree;
Plan *planTree; Plan *planTree;
...@@ -645,18 +644,20 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan) ...@@ -645,18 +644,20 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
nargs = plan->nargs; nargs = plan->nargs;
argtypes = plan->argtypes; 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; _SPI_current->qtlist = queryTree_list;
planTree_list = NIL;
foreach(queryTree_list_item, queryTree_list) foreach(queryTree_list_item, queryTree_list)
{ {
queryTree = (Query *) lfirst(queryTree_list_item); queryTree = (Query *) lfirst(queryTree_list_item);
planTree = lfirst(planTree_list); islastquery = (lnext(queryTree_list_item) == NIL);
planTree_list = lnext(planTree_list);
islastquery = (planTree_list == NIL); /* assume lists are same planTree = pg_plan_query(queryTree);
* len */ planTree_list = lappend(planTree_list, planTree);
if (queryTree->commandType == CMD_UTILITY) if (queryTree->commandType == CMD_UTILITY)
{ {
...@@ -707,7 +708,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan) ...@@ -707,7 +708,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
} }
plan->qtlist = queryTree_list; plan->qtlist = queryTree_list;
plan->ptlist = ptlist; plan->ptlist = planTree_list;
return res; return res;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -377,22 +377,23 @@ ReadCommand(StringInfo inBuf) ...@@ -377,22 +377,23 @@ ReadCommand(StringInfo inBuf)
return result; 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 * List *
pg_parse_and_plan(char *query_string, /* string to execute */ pg_parse_and_rewrite(char *query_string, /* string to execute */
Oid *typev, /* argument types */ Oid *typev, /* argument types */
int nargs, /* number of arguments */ int nargs, /* number of arguments */
List **queryListP, /* returned pointer to the parse bool aclOverride)
* trees */
CommandDest dest, /* where results should go */
bool aclOverride)
{ {
List *querytree_list = NIL; List *querytree_list;
List *plan_list = NIL;
List *querytree_list_item; List *querytree_list_item;
Query *querytree; Query *querytree;
Plan *plan;
List *new_list; List *new_list;
List *rewritten;
if (DebugPrintQuery) if (DebugPrintQuery)
{ {
...@@ -448,15 +449,16 @@ pg_parse_and_plan(char *query_string, /* string to execute */ ...@@ -448,15 +449,16 @@ pg_parse_and_plan(char *query_string, /* string to execute */
else else
{ {
/* rewrite regular queries */ /* rewrite regular queries */
rewritten = QueryRewrite(querytree); List *rewritten = QueryRewrite(querytree);
new_list = nconc(new_list, rewritten); new_list = nconc(new_list, rewritten);
} }
} }
querytree_list = new_list; querytree_list = new_list;
/* /* ----------------
* Override ACL checking if requested * (3) If ACL override is requested, mark queries for no ACL check.
* ----------------
*/ */
if (aclOverride) if (aclOverride)
{ {
...@@ -503,87 +505,54 @@ pg_parse_and_plan(char *query_string, /* string to execute */ ...@@ -503,87 +505,54 @@ pg_parse_and_plan(char *query_string, /* string to execute */
} }
} }
foreach(querytree_list_item, querytree_list) return 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)
{
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", /* Utility commands have no plans. */
"queries ignored until END"); if (querytree->commandType == CMD_UTILITY)
return NULL;
if (queryListP) if (ShowPlannerStats)
*queryListP = NIL; ResetUsage();
return NIL;
}
if (ShowPlannerStats) /* call that optimizer */
ResetUsage(); plan = planner(querytree);
/* call that optimizer */ if (ShowPlannerStats)
plan = planner(querytree); {
fprintf(stderr, "! Planner Stats:\n");
ShowUsage();
}
if (ShowPlannerStats) /* ----------------
{ * Print plan if debugging.
fprintf(stderr, "! Planner Stats:\n"); * ----------------
ShowUsage(); */
} if (DebugPrintPlan || DebugPPrintPlan)
plan_list = lappend(plan_list, plan); {
#ifdef INDEXSCAN_PATCH if (DebugPPrintPlan)
/* ---------------- {
* Print plan if debugging. TPRINTF(TRACE_PRETTY_PLAN, "plan:");
* This has been moved here to get debugging output nodeDisplay(plan);
* 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
} }
/*
* 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 else
plan_list = lappend(plan_list, NULL); {
TPRINTF(TRACE_PLAN, "plan:");
printf("\n%s\n\n", nodeToString(plan));
}
} }
if (queryListP) return plan;
*queryListP = querytree_list;
return plan_list;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* pg_exec_query() * pg_exec_query()
* *
...@@ -620,39 +589,31 @@ pg_exec_query_dest(char *query_string, /* string to execute */ ...@@ -620,39 +589,31 @@ pg_exec_query_dest(char *query_string, /* string to execute */
* of superusers */ * of superusers */
{ {
List *querytree_list; List *querytree_list;
List *plan_list;
Query *querytree;
Plan *plan;
int j;
/* plan the queries */ /* parse and rewrite the queries */
plan_list = pg_parse_and_plan(query_string, NULL, 0, querytree_list = pg_parse_and_rewrite(query_string, NULL, 0,
&querytree_list, dest, aclOverride); aclOverride);
/* if we got a cancel signal whilst planning, quit */
if (QueryCancel)
CancelQuery();
/* OK, do it to it! */
/* /*
* NOTE: we do not use "foreach" here because we want to be sure the * 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 * need to do that because VACUUM has a nasty little habit of doing
* CommitTransactionCommand at startup, and that will release the * 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 * solution --- currently, the code will crash if someone submits
* "vacuum; something-else" in a single query string. But memory * "vacuum; something-else" in a single query string. But memory
* allocation needs redesigned anyway, so this will have to do for * allocation needs redesigned anyway, so this will have to do for
* now. * now.
*/ */
while (querytree_list) while (querytree_list)
{ {
querytree = (Query *) lfirst(querytree_list); Query *querytree = (Query *) lfirst(querytree_list);
querytree_list = lnext(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) if (querytree->commandType == CMD_UTILITY)
{ {
...@@ -668,42 +629,38 @@ pg_exec_query_dest(char *query_string, /* string to execute */ ...@@ -668,42 +629,38 @@ pg_exec_query_dest(char *query_string, /* string to execute */
else if (Verbose) else if (Verbose)
TPRINTF(TRACE_VERBOSE, "ProcessUtility"); 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); ProcessUtility(querytree->utilityStmt, dest);
} }
else else
{ {
#ifdef INDEXSCAN_PATCH Plan *plan;
int j;
/* /* If aborted transaction, quit now */
* Print moved in pg_parse_and_plan. DZ - 27-8-1996 if (IsAbortedTransactionBlockState())
*/
#else
/* ----------------
* print plan if debugging
* ----------------
*/
if (DebugPrintPlan || DebugPPrintPlan)
{ {
if (DebugPPrintPlan) /* ----------------
{ * the EndCommand() stuff is to tell the frontend
TPRINTF(TRACE_PRETTY_PLAN, "plan:"); * that the command ended. -cim 6/1/90
nodeDisplay(plan); * ----------------
} */
else char *tag = "*ABORT STATE*";
{
TPRINTF(TRACE_PLAN, "plan:"); elog(NOTICE, "current transaction is aborted, "
printf("\n%s\n\n", nodeToString(plan)); "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(); SetQuerySnapshot();
/* /*
...@@ -1505,7 +1462,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[]) ...@@ -1505,7 +1462,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
if (!IsUnderPostmaster) if (!IsUnderPostmaster)
{ {
puts("\nPOSTGRES backend interactive interface "); 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");
} }
/* /*
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -136,6 +136,8 @@ ProcessUtility(Node *parsetree,
PS_SET_STATUS(commandTag = (stmt->ismove) ? "MOVE" : "FETCH"); PS_SET_STATUS(commandTag = (stmt->ismove) ? "MOVE" : "FETCH");
CHECK_IF_ABORTED(); CHECK_IF_ABORTED();
SetQuerySnapshot();
forward = (bool) (stmt->direction == FORWARD); forward = (bool) (stmt->direction == FORWARD);
/* /*
...@@ -255,6 +257,9 @@ ProcessUtility(Node *parsetree, ...@@ -255,6 +257,9 @@ ProcessUtility(Node *parsetree,
PS_SET_STATUS(commandTag = "COPY"); PS_SET_STATUS(commandTag = "COPY");
CHECK_IF_ABORTED(); CHECK_IF_ABORTED();
if (stmt->direction != FROM)
SetQuerySnapshot();
DoCopy(stmt->relname, DoCopy(stmt->relname,
stmt->binary, stmt->binary,
stmt->oids, stmt->oids,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * 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 * OLD COMMENTS
* This file was created so that other c files could get the two * This file was created so that other c files could get the two
...@@ -29,12 +29,15 @@ extern bool InError; ...@@ -29,12 +29,15 @@ extern bool InError;
extern bool ExitAfterAbort; extern bool ExitAfterAbort;
#ifndef BOOTSTRAP_INCLUDE #ifndef BOOTSTRAP_INCLUDE
extern List *pg_parse_and_plan(char *query_string, Oid *typev, int nargs,
List **queryListP, CommandDest dest, extern List *pg_parse_and_rewrite(char *query_string,
bool aclOverride); 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_acl_override(char *query_string);
extern void extern void pg_exec_query_dest(char *query_string,
pg_exec_query_dest(char *query_string, CommandDest dest, bool aclOverride); CommandDest dest,
bool aclOverride);
#endif /* BOOTSTRAP_INCLUDE */ #endif /* BOOTSTRAP_INCLUDE */
......
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