aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c17
-rw-r--r--src/backend/nodes/params.c46
-rw-r--r--src/backend/optimizer/plan/setrefs.c24
-rw-r--r--src/backend/parser/analyze.c34
-rw-r--r--src/backend/tcop/utility.c3
-rw-r--r--src/backend/utils/cache/plancache.c128
-rw-r--r--src/include/nodes/params.h5
-rw-r--r--src/include/nodes/parsenodes.h8
-rw-r--r--src/include/optimizer/planmain.h4
9 files changed, 141 insertions, 128 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 1617dedcef8..37647e57398 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.198 2010/01/02 16:57:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.199 2010/01/15 22:36:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -158,19 +158,19 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
/*
- * Run parse analysis and rewrite. Note this also acquires sufficient
- * locks on the source table(s).
+ * Parse analysis was done already, but we still have to run the rule
+ * rewriter. We do not do AcquireRewriteLocks: we assume the query
+ * either came straight from the parser, or suitable locks were
+ * acquired by plancache.c.
*
- * Because the parser and planner tend to scribble on their input, we make
+ * Because the rewriter and planner tend to scribble on the input, we make
* a preliminary copy of the source querytree. This prevents problems in
* the case that the EXPLAIN is in a portal or plpgsql function and is
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday.
*/
- rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
- queryString,
- (ParserSetupHook) setupParserWithParamList,
- params);
+ Assert(IsA(stmt->query, Query));
+ rewritten = QueryRewrite((Query *) copyObject(stmt->query));
/* emit opening boilerplate */
ExplainBeginOutput(&es);
@@ -248,6 +248,7 @@ ExplainResultDesc(ExplainStmt *stmt)
char *p = defGetString(opt);
xml = (strcmp(p, "xml") == 0);
+ /* don't "break", as ExplainQuery will use the last value */
}
}
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index 136f40ea549..ef17a9bb321 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.13 2010/01/02 16:57:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.14 2010/01/15 22:36:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -75,47 +75,3 @@ copyParamList(ParamListInfo from)
return retval;
}
-
-/*
- * Set up the parser to treat the given list of run-time parameters
- * as available external parameters during parsing of a new query.
- *
- * Note that the parser doesn't actually care about the *values* of the given
- * parameters, only about their *types*. Also, the code that originally
- * provided the ParamListInfo may have provided a setupHook, which should
- * override applying parse_fixed_parameters().
- */
-void
-setupParserWithParamList(struct ParseState *pstate,
- ParamListInfo params)
-{
- if (params == NULL) /* no params, nothing to do */
- return;
-
- /* If there is a parserSetup hook, it gets to do this */
- if (params->parserSetup != NULL)
- {
- (*params->parserSetup) (pstate, params->parserSetupArg);
- return;
- }
-
- /* Else, treat any available parameters as being of fixed type */
- if (params->numParams > 0)
- {
- Oid *ptypes;
- int i;
-
- ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
- for (i = 0; i < params->numParams; i++)
- {
- ParamExternData *prm = &params->params[i];
-
- /* give hook a chance in case parameter is dynamic */
- if (!OidIsValid(prm->ptype) && params->paramFetch != NULL)
- (*params->paramFetch) (params, i+1);
-
- ptypes[i] = prm->ptype;
- }
- parse_fixed_parameters(pstate, ptypes, params->numParams);
- }
-}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 2c33e6acb98..aa4fd4e1ebe 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.156 2010/01/02 16:57:47 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.157 2010/01/15 22:36:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1905,14 +1905,15 @@ record_plan_function_dependency(PlannerGlobal *glob, Oid funcid)
/*
* extract_query_dependencies
- * Given a list of not-yet-planned queries (i.e. Query nodes),
- * extract their dependencies just as set_plan_references would do.
+ * Given a not-yet-planned query or queries (i.e. a Query node or list
+ * of Query nodes), extract dependencies just as set_plan_references
+ * would do.
*
* This is needed by plancache.c to handle invalidation of cached unplanned
* queries.
*/
void
-extract_query_dependencies(List *queries,
+extract_query_dependencies(Node *query,
List **relationOids,
List **invalItems)
{
@@ -1924,7 +1925,7 @@ extract_query_dependencies(List *queries,
glob.relationOids = NIL;
glob.invalItems = NIL;
- (void) extract_query_dependencies_walker((Node *) queries, &glob);
+ (void) extract_query_dependencies_walker(query, &glob);
*relationOids = glob.relationOids;
*invalItems = glob.invalItems;
@@ -1943,6 +1944,19 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context)
Query *query = (Query *) node;
ListCell *lc;
+ if (query->commandType == CMD_UTILITY)
+ {
+ /* Ignore utility statements, except EXPLAIN */
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ query = (Query *) ((ExplainStmt *) query->utilityStmt)->query;
+ Assert(IsA(query, Query));
+ Assert(query->commandType != CMD_UTILITY);
+ }
+ else
+ return false;
+ }
+
/* Collect relation OIDs in this Query's rtable */
foreach(lc, query->rtable)
{
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a0e565b187c..efa4e47b1a4 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.399 2010/01/02 16:57:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.400 2010/01/15 22:36:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -257,16 +257,12 @@ analyze_requires_snapshot(Node *parseTree)
break;
case T_ExplainStmt:
-
- /*
- * We only need a snapshot in varparams case, but it doesn't seem
- * worth complicating this function's API to distinguish that.
- */
+ /* yes, because we must analyze the contained statement */
result = true;
break;
default:
- /* utility statements don't have any active parse analysis */
+ /* other utility statements don't have any real parse analysis */
result = false;
break;
}
@@ -1993,29 +1989,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
* transformExplainStmt -
* transform an EXPLAIN Statement
*
- * EXPLAIN is just like other utility statements in that we emit it as a
- * CMD_UTILITY Query node with no transformation of the raw parse tree.
- * However, if p_coerce_param_hook is set, it could be that the client is
- * expecting us to resolve parameter types in something like
- * EXPLAIN SELECT * FROM tab WHERE col = $1
- * To deal with such cases, we run parse analysis and throw away the result;
- * this is a bit grotty but not worth contorting the rest of the system for.
- * (The approach we use for DECLARE CURSOR won't work because the statement
- * being explained isn't necessarily a SELECT, and in particular might rewrite
- * to multiple parsetrees.)
+ * EXPLAIN is like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node; however, we must first transform the contained
+ * query. We used to postpone that until execution, but it's really necessary
+ * to do it during the normal parse analysis phase to ensure that side effects
+ * of parser hooks happen at the expected time.
*/
static Query *
transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
{
Query *result;
- if (pstate->p_coerce_param_hook != NULL)
- {
- /* Since parse analysis scribbles on its input, copy the tree first! */
- (void) transformStmt(pstate, copyObject(stmt->query));
- }
+ /* transform contained query */
+ stmt->query = (Node *) transformStmt(pstate, stmt->query);
- /* Now return the untransformed command as a utility Query */
+ /* represent the command as a utility Query */
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index acacbec094a..96b0aa735f4 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.328 2010/01/06 03:04:01 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.329 2010/01/15 22:36:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2438,6 +2438,7 @@ GetCommandLogLevel(Node *parsetree)
if (strcmp(opt->defname, "analyze") == 0)
analyze = defGetBoolean(opt);
+ /* don't "break", as explain.c will use the last value */
}
if (analyze)
return GetCommandLogLevel(stmt->query);
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index ffa117b66cd..114cd9b9756 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.33 2010/01/13 16:56:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.34 2010/01/15 22:36:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -359,13 +359,27 @@ StoreCachedPlan(CachedPlanSource *plansource,
plan->context = plan_context;
if (plansource->fully_planned)
{
- /* Planner already extracted dependencies, we don't have to */
+ /*
+ * Planner already extracted dependencies, we don't have to ...
+ * except in the case of EXPLAIN. We assume here that EXPLAIN
+ * can't appear in a list with other commands.
+ */
plan->relationOids = plan->invalItems = NIL;
+
+ if (list_length(stmt_list) == 1 &&
+ IsA(linitial(stmt_list), ExplainStmt))
+ {
+ ExplainStmt *estmt = (ExplainStmt *) linitial(stmt_list);
+
+ extract_query_dependencies(estmt->query,
+ &plan->relationOids,
+ &plan->invalItems);
+ }
}
else
{
/* Use the planner machinery to extract dependencies */
- extract_query_dependencies(stmt_list,
+ extract_query_dependencies((Node *) stmt_list,
&plan->relationOids,
&plan->invalItems);
}
@@ -685,7 +699,24 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
Assert(!IsA(plannedstmt, Query));
if (!IsA(plannedstmt, PlannedStmt))
- continue; /* Ignore utility statements */
+ {
+ /*
+ * Ignore utility statements, except EXPLAIN which contains a
+ * parsed-but-not-planned query. Note: it's okay to use
+ * ScanQueryForLocks, even though the query hasn't been through
+ * rule rewriting, because rewriting doesn't change the query
+ * representation.
+ */
+ if (IsA(plannedstmt, ExplainStmt))
+ {
+ Query *query;
+
+ query = (Query *) ((ExplainStmt *) plannedstmt)->query;
+ Assert(IsA(query, Query));
+ ScanQueryForLocks(query, acquire);
+ }
+ continue;
+ }
rt_index = 0;
foreach(lc2, plannedstmt->rtable)
@@ -739,6 +770,19 @@ AcquirePlannerLocks(List *stmt_list, bool acquire)
Query *query = (Query *) lfirst(lc);
Assert(IsA(query, Query));
+
+ if (query->commandType == CMD_UTILITY)
+ {
+ /* Ignore utility statements, except EXPLAIN */
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ query = (Query *) ((ExplainStmt *) query->utilityStmt)->query;
+ Assert(IsA(query, Query));
+ ScanQueryForLocks(query, acquire);
+ }
+ continue;
+ }
+
ScanQueryForLocks(query, acquire);
}
}
@@ -752,6 +796,9 @@ ScanQueryForLocks(Query *parsetree, bool acquire)
ListCell *lc;
int rt_index;
+ /* Shouldn't get called on utility commands */
+ Assert(parsetree->commandType != CMD_UTILITY);
+
/*
* First, process RTEs of the current query level.
*/
@@ -942,7 +989,16 @@ PlanCacheRelCallback(Datum arg, Oid relid)
/* No work if it's already invalidated */
if (!plan || plan->dead)
continue;
- if (plan->fully_planned)
+
+ /*
+ * Check the list we built ourselves; this covers unplanned cases
+ * including EXPLAIN.
+ */
+ if ((relid == InvalidOid) ? plan->relationOids != NIL :
+ list_member_oid(plan->relationOids, relid))
+ plan->dead = true;
+
+ if (plan->fully_planned && !plan->dead)
{
/* Have to check the per-PlannedStmt relid lists */
ListCell *lc2;
@@ -963,13 +1019,6 @@ PlanCacheRelCallback(Datum arg, Oid relid)
}
}
}
- else
- {
- /* Otherwise check the single list we built ourselves */
- if ((relid == InvalidOid) ? plan->relationOids != NIL :
- list_member_oid(plan->relationOids, relid))
- plan->dead = true;
- }
}
}
@@ -992,15 +1041,34 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
CachedPlan *plan = plansource->plan;
+ ListCell *lc2;
/* No work if it's already invalidated */
if (!plan || plan->dead)
continue;
- if (plan->fully_planned)
+
+ /*
+ * Check the list we built ourselves; this covers unplanned cases
+ * including EXPLAIN.
+ */
+ foreach(lc2, plan->invalItems)
{
- /* Have to check the per-PlannedStmt inval-item lists */
- ListCell *lc2;
+ PlanInvalItem *item = (PlanInvalItem *) lfirst(lc2);
+ if (item->cacheId != cacheid)
+ continue;
+ if (tuplePtr == NULL ||
+ ItemPointerEquals(tuplePtr, &item->tupleId))
+ {
+ /* Invalidate the plan! */
+ plan->dead = true;
+ break;
+ }
+ }
+
+ if (plan->fully_planned && !plan->dead)
+ {
+ /* Have to check the per-PlannedStmt inval-item lists */
foreach(lc2, plan->stmt_list)
{
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
@@ -1027,26 +1095,6 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
break; /* out of stmt_list scan */
}
}
- else
- {
- /* Otherwise check the single list we built ourselves */
- ListCell *lc2;
-
- foreach(lc2, plan->invalItems)
- {
- PlanInvalItem *item = (PlanInvalItem *) lfirst(lc2);
-
- if (item->cacheId != cacheid)
- continue;
- if (tuplePtr == NULL ||
- ItemPointerEquals(tuplePtr, &item->tupleId))
- {
- /* Invalidate the plan! */
- plan->dead = true;
- break;
- }
- }
- }
}
}
@@ -1086,7 +1134,9 @@ ResetPlanCache(void)
* aborted transactions when we can't revalidate them (cf bug #5269).
* In general there is no point in invalidating utility statements
* since they have no plans anyway. So mark it dead only if it
- * contains at least one non-utility statement.
+ * contains at least one non-utility statement. (EXPLAIN counts as
+ * a non-utility statement, though, since it contains an analyzed
+ * query that might have dependencies.)
*/
if (plan->fully_planned)
{
@@ -1096,7 +1146,8 @@ ResetPlanCache(void)
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
Assert(!IsA(plannedstmt, Query));
- if (IsA(plannedstmt, PlannedStmt))
+ if (IsA(plannedstmt, PlannedStmt) ||
+ IsA(plannedstmt, ExplainStmt))
{
/* non-utility statement, so invalidate */
plan->dead = true;
@@ -1112,7 +1163,8 @@ ResetPlanCache(void)
Query *query = (Query *) lfirst(lc2);
Assert(IsA(query, Query));
- if (query->commandType != CMD_UTILITY)
+ if (query->commandType != CMD_UTILITY ||
+ IsA(query->utilityStmt, ExplainStmt))
{
/* non-utility statement, so invalidate */
plan->dead = true;
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 93b2c03cee9..12ef269e610 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.40 2010/01/02 16:58:04 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.41 2010/01/15 22:36:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -103,7 +103,4 @@ typedef struct ParamExecData
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
-extern void setupParserWithParamList(struct ParseState *pstate,
- ParamListInfo params);
-
#endif /* PARAMS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 9a0bb8eec3f..a03597c9f37 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.423 2010/01/06 05:31:14 itagaki Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.424 2010/01/15 22:36:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2260,12 +2260,16 @@ typedef struct VacuumStmt
/* ----------------------
* Explain Statement
+ *
+ * The "query" field is either a raw parse tree (SelectStmt, InsertStmt, etc)
+ * or a Query node if parse analysis has been done. Note that rewriting and
+ * planning of the query are always postponed until execution of EXPLAIN.
* ----------------------
*/
typedef struct ExplainStmt
{
NodeTag type;
- Node *query; /* the query (as a raw parse tree) */
+ Node *query; /* the query (see comments above) */
List *options; /* list of DefElem nodes */
} ExplainStmt;
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 7e59cd4a6be..024142250ac 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.123 2010/01/02 16:58:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planmain.h,v 1.124 2010/01/15 22:36:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -122,7 +122,7 @@ extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr);
extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
extern void record_plan_function_dependency(PlannerGlobal *glob, Oid funcid);
-extern void extract_query_dependencies(List *queries,
+extern void extract_query_dependencies(Node *query,
List **relationOids,
List **invalItems);