Commit 08f8d478 authored by Tom Lane's avatar Tom Lane

Do parse analysis of an EXPLAIN's contained statement during the normal

parse analysis phase, rather than at execution time.  This makes parameter
handling work the same as it does in ordinary plannable queries, and in
particular fixes the incompatibility that Pavel pointed out with plpgsql's
new handling of variable references.  plancache.c gets a little bit
grottier, but the alternatives seem worse.
parent 00b5cceb
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * 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, ...@@ -158,19 +158,19 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
errmsg("EXPLAIN option BUFFERS requires ANALYZE"))); errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
/* /*
* Run parse analysis and rewrite. Note this also acquires sufficient * Parse analysis was done already, but we still have to run the rule
* locks on the source table(s). * 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 * 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 * 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 * executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday. * PREPARE.) XXX FIXME someday.
*/ */
rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query), Assert(IsA(stmt->query, Query));
queryString, rewritten = QueryRewrite((Query *) copyObject(stmt->query));
(ParserSetupHook) setupParserWithParamList,
params);
/* emit opening boilerplate */ /* emit opening boilerplate */
ExplainBeginOutput(&es); ExplainBeginOutput(&es);
...@@ -248,6 +248,7 @@ ExplainResultDesc(ExplainStmt *stmt) ...@@ -248,6 +248,7 @@ ExplainResultDesc(ExplainStmt *stmt)
char *p = defGetString(opt); char *p = defGetString(opt);
xml = (strcmp(p, "xml") == 0); xml = (strcmp(p, "xml") == 0);
/* don't "break", as ExplainQuery will use the last value */
} }
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -75,47 +75,3 @@ copyParamList(ParamListInfo from)
return retval; 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);
}
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1905,14 +1905,15 @@ record_plan_function_dependency(PlannerGlobal *glob, Oid funcid)
/* /*
* extract_query_dependencies * extract_query_dependencies
* Given a list of not-yet-planned queries (i.e. Query nodes), * Given a not-yet-planned query or queries (i.e. a Query node or list
* extract their dependencies just as set_plan_references would do. * of Query nodes), extract dependencies just as set_plan_references
* would do.
* *
* This is needed by plancache.c to handle invalidation of cached unplanned * This is needed by plancache.c to handle invalidation of cached unplanned
* queries. * queries.
*/ */
void void
extract_query_dependencies(List *queries, extract_query_dependencies(Node *query,
List **relationOids, List **relationOids,
List **invalItems) List **invalItems)
{ {
...@@ -1924,7 +1925,7 @@ extract_query_dependencies(List *queries, ...@@ -1924,7 +1925,7 @@ extract_query_dependencies(List *queries,
glob.relationOids = NIL; glob.relationOids = NIL;
glob.invalItems = NIL; glob.invalItems = NIL;
(void) extract_query_dependencies_walker((Node *) queries, &glob); (void) extract_query_dependencies_walker(query, &glob);
*relationOids = glob.relationOids; *relationOids = glob.relationOids;
*invalItems = glob.invalItems; *invalItems = glob.invalItems;
...@@ -1943,6 +1944,19 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context) ...@@ -1943,6 +1944,19 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context)
Query *query = (Query *) node; Query *query = (Query *) node;
ListCell *lc; 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 */ /* Collect relation OIDs in this Query's rtable */
foreach(lc, query->rtable) foreach(lc, query->rtable)
{ {
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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) ...@@ -257,16 +257,12 @@ analyze_requires_snapshot(Node *parseTree)
break; break;
case T_ExplainStmt: case T_ExplainStmt:
/* yes, because we must analyze the contained statement */
/*
* We only need a snapshot in varparams case, but it doesn't seem
* worth complicating this function's API to distinguish that.
*/
result = true; result = true;
break; break;
default: default:
/* utility statements don't have any active parse analysis */ /* other utility statements don't have any real parse analysis */
result = false; result = false;
break; break;
} }
...@@ -1993,29 +1989,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) ...@@ -1993,29 +1989,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
* transformExplainStmt - * transformExplainStmt -
* transform an EXPLAIN Statement * transform an EXPLAIN Statement
* *
* EXPLAIN is just like other utility statements in that we emit it as a * EXPLAIN is like other utility statements in that we emit it as a
* CMD_UTILITY Query node with no transformation of the raw parse tree. * CMD_UTILITY Query node; however, we must first transform the contained
* However, if p_coerce_param_hook is set, it could be that the client is * query. We used to postpone that until execution, but it's really necessary
* expecting us to resolve parameter types in something like * to do it during the normal parse analysis phase to ensure that side effects
* EXPLAIN SELECT * FROM tab WHERE col = $1 * of parser hooks happen at the expected time.
* 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.)
*/ */
static Query * static Query *
transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
{ {
Query *result; Query *result;
if (pstate->p_coerce_param_hook != NULL) /* transform contained query */
{ stmt->query = (Node *) transformStmt(pstate, stmt->query);
/* Since parse analysis scribbles on its input, copy the tree first! */
(void) transformStmt(pstate, copyObject(stmt->query));
}
/* Now return the untransformed command as a utility Query */ /* represent the command as a utility Query */
result = makeNode(Query); result = makeNode(Query);
result->commandType = CMD_UTILITY; result->commandType = CMD_UTILITY;
result->utilityStmt = (Node *) stmt; result->utilityStmt = (Node *) stmt;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -2438,6 +2438,7 @@ GetCommandLogLevel(Node *parsetree)
if (strcmp(opt->defname, "analyze") == 0) if (strcmp(opt->defname, "analyze") == 0)
analyze = defGetBoolean(opt); analyze = defGetBoolean(opt);
/* don't "break", as explain.c will use the last value */
} }
if (analyze) if (analyze)
return GetCommandLogLevel(stmt->query); return GetCommandLogLevel(stmt->query);
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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, ...@@ -359,13 +359,27 @@ StoreCachedPlan(CachedPlanSource *plansource,
plan->context = plan_context; plan->context = plan_context;
if (plansource->fully_planned) 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; 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 else
{ {
/* Use the planner machinery to extract dependencies */ /* Use the planner machinery to extract dependencies */
extract_query_dependencies(stmt_list, extract_query_dependencies((Node *) stmt_list,
&plan->relationOids, &plan->relationOids,
&plan->invalItems); &plan->invalItems);
} }
...@@ -685,7 +699,24 @@ AcquireExecutorLocks(List *stmt_list, bool acquire) ...@@ -685,7 +699,24 @@ AcquireExecutorLocks(List *stmt_list, bool acquire)
Assert(!IsA(plannedstmt, Query)); Assert(!IsA(plannedstmt, Query));
if (!IsA(plannedstmt, PlannedStmt)) 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; rt_index = 0;
foreach(lc2, plannedstmt->rtable) foreach(lc2, plannedstmt->rtable)
...@@ -739,6 +770,19 @@ AcquirePlannerLocks(List *stmt_list, bool acquire) ...@@ -739,6 +770,19 @@ AcquirePlannerLocks(List *stmt_list, bool acquire)
Query *query = (Query *) lfirst(lc); Query *query = (Query *) lfirst(lc);
Assert(IsA(query, Query)); 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); ScanQueryForLocks(query, acquire);
} }
} }
...@@ -752,6 +796,9 @@ ScanQueryForLocks(Query *parsetree, bool acquire) ...@@ -752,6 +796,9 @@ ScanQueryForLocks(Query *parsetree, bool acquire)
ListCell *lc; ListCell *lc;
int rt_index; int rt_index;
/* Shouldn't get called on utility commands */
Assert(parsetree->commandType != CMD_UTILITY);
/* /*
* First, process RTEs of the current query level. * First, process RTEs of the current query level.
*/ */
...@@ -942,7 +989,16 @@ PlanCacheRelCallback(Datum arg, Oid relid) ...@@ -942,7 +989,16 @@ PlanCacheRelCallback(Datum arg, Oid relid)
/* No work if it's already invalidated */ /* No work if it's already invalidated */
if (!plan || plan->dead) if (!plan || plan->dead)
continue; 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 */ /* Have to check the per-PlannedStmt relid lists */
ListCell *lc2; ListCell *lc2;
...@@ -963,13 +1019,6 @@ PlanCacheRelCallback(Datum arg, Oid relid) ...@@ -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) ...@@ -992,15 +1041,34 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
{ {
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1); CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
CachedPlan *plan = plansource->plan; CachedPlan *plan = plansource->plan;
ListCell *lc2;
/* No work if it's already invalidated */ /* No work if it's already invalidated */
if (!plan || plan->dead) if (!plan || plan->dead)
continue; 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 */ PlanInvalItem *item = (PlanInvalItem *) lfirst(lc2);
ListCell *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) foreach(lc2, plan->stmt_list)
{ {
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
...@@ -1027,26 +1095,6 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) ...@@ -1027,26 +1095,6 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
break; /* out of stmt_list scan */ 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) ...@@ -1086,7 +1134,9 @@ ResetPlanCache(void)
* aborted transactions when we can't revalidate them (cf bug #5269). * aborted transactions when we can't revalidate them (cf bug #5269).
* In general there is no point in invalidating utility statements * In general there is no point in invalidating utility statements
* since they have no plans anyway. So mark it dead only if it * 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) if (plan->fully_planned)
{ {
...@@ -1096,7 +1146,8 @@ ResetPlanCache(void) ...@@ -1096,7 +1146,8 @@ ResetPlanCache(void)
PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2); PlannedStmt *plannedstmt = (PlannedStmt *) lfirst(lc2);
Assert(!IsA(plannedstmt, Query)); Assert(!IsA(plannedstmt, Query));
if (IsA(plannedstmt, PlannedStmt)) if (IsA(plannedstmt, PlannedStmt) ||
IsA(plannedstmt, ExplainStmt))
{ {
/* non-utility statement, so invalidate */ /* non-utility statement, so invalidate */
plan->dead = true; plan->dead = true;
...@@ -1112,7 +1163,8 @@ ResetPlanCache(void) ...@@ -1112,7 +1163,8 @@ ResetPlanCache(void)
Query *query = (Query *) lfirst(lc2); Query *query = (Query *) lfirst(lc2);
Assert(IsA(query, Query)); Assert(IsA(query, Query));
if (query->commandType != CMD_UTILITY) if (query->commandType != CMD_UTILITY ||
IsA(query->utilityStmt, ExplainStmt))
{ {
/* non-utility statement, so invalidate */ /* non-utility statement, so invalidate */
plan->dead = true; plan->dead = true;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -103,7 +103,4 @@ typedef struct ParamExecData
/* Functions found in src/backend/nodes/params.c */ /* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from); extern ParamListInfo copyParamList(ParamListInfo from);
extern void setupParserWithParamList(struct ParseState *pstate,
ParamListInfo params);
#endif /* PARAMS_H */ #endif /* PARAMS_H */
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -2260,12 +2260,16 @@ typedef struct VacuumStmt
/* ---------------------- /* ----------------------
* Explain Statement * 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 typedef struct ExplainStmt
{ {
NodeTag type; NodeTag type;
Node *query; /* the query (as a raw parse tree) */ Node *query; /* the query (see comments above) */
List *options; /* list of DefElem nodes */ List *options; /* list of DefElem nodes */
} ExplainStmt; } ExplainStmt;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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); ...@@ -122,7 +122,7 @@ extern void fix_opfuncids(Node *node);
extern void set_opfuncid(OpExpr *opexpr); extern void set_opfuncid(OpExpr *opexpr);
extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr); extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
extern void record_plan_function_dependency(PlannerGlobal *glob, Oid funcid); 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 **relationOids,
List **invalItems); List **invalItems);
......
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