* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.206 2010/06/10 01:26:30 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.207 2010/07/13 20:57:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
static double elapsed_time(instr_time *starttime);
-static void ExplainNode(Plan *plan, PlanState *planstate,
- Plan *outer_plan,
+static void ExplainNode(PlanState *planstate, List *ancestors,
const char *relationship, const char *plan_name,
ExplainState *es);
-static void show_plan_tlist(Plan *plan, ExplainState *es);
-static void show_qual(List *qual, const char *qlabel, Plan *plan,
- Plan *outer_plan, bool useprefix, ExplainState *es);
+static void show_plan_tlist(PlanState *planstate, List *ancestors,
+ ExplainState *es);
+static void show_qual(List *qual, const char *qlabel,
+ PlanState *planstate, List *ancestors,
+ bool useprefix, ExplainState *es);
static void show_scan_qual(List *qual, const char *qlabel,
- Plan *scan_plan, Plan *outer_plan,
- ExplainState *es);
-static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
- ExplainState *es);
-static void show_sort_keys(Plan *sortplan, ExplainState *es);
+ PlanState *planstate, List *ancestors,
+ ExplainState *es);
+static void show_upper_qual(List *qual, const char *qlabel,
+ PlanState *planstate, List *ancestors,
+ ExplainState *es);
+static void show_sort_keys(SortState *sortstate, List *ancestors,
+ ExplainState *es);
static void show_sort_info(SortState *sortstate, ExplainState *es);
static void show_hash_info(HashState *hashstate, ExplainState *es);
static const char *explain_get_index_name(Oid indexId);
static void ExplainScanTarget(Scan *plan, ExplainState *es);
-static void ExplainMemberNodes(List *plans, PlanState **planstate,
- Plan *outer_plan, ExplainState *es);
-static void ExplainSubPlans(List *plans, const char *relationship,
- ExplainState *es);
+static void ExplainMemberNodes(List *plans, PlanState **planstates,
+ List *ancestors, ExplainState *es);
+static void ExplainSubPlans(List *plans, List *ancestors,
+ const char *relationship, ExplainState *es);
static void ExplainPropertyList(const char *qlabel, List *data,
ExplainState *es);
static void ExplainProperty(const char *qlabel, const char *value,
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
es->rtable = queryDesc->plannedstmt->rtable;
- ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
- NULL, NULL, NULL, es);
+ ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
}
/*
/*
* ExplainNode -
- * Appends a description of the Plan node to es->str
+ * Appends a description of a plan tree to es->str
*
- * planstate points to the executor state node corresponding to the plan node.
- * We need this to get at the instrumentation data (if any) as well as the
- * list of subplans.
+ * planstate points to the executor state node for the current plan node.
+ * We need to work from a PlanState node, not just a Plan node, in order to
+ * get at the instrumentation data (if any) as well as the list of subplans.
*
- * outer_plan, if not null, references another plan node that is the outer
- * side of a join with the current node. This is only interesting for
- * deciphering runtime keys of an inner indexscan.
+ * ancestors is a list of parent PlanState nodes, most-closely-nested first.
+ * These are needed in order to interpret PARAM_EXEC Params.
*
* relationship describes the relationship of this plan node to its parent
* (eg, "Outer", "Inner"); it can be null at top level. plan_name is an
* optional name to be attached to the node.
*
* In text format, es->indent is controlled in this function since we only
- * want it to change at Plan-node boundaries. In non-text formats, es->indent
+ * want it to change at plan-node boundaries. In non-text formats, es->indent
* corresponds to the nesting depth of logical output groups, and therefore
* is controlled by ExplainOpenGroup/ExplainCloseGroup.
*/
static void
-ExplainNode(Plan *plan, PlanState *planstate,
- Plan *outer_plan,
+ExplainNode(PlanState *planstate, List *ancestors,
const char *relationship, const char *plan_name,
ExplainState *es)
{
+ Plan *plan = planstate->plan;
const char *pname; /* node type name for text output */
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
int save_indent = es->indent;
bool haschildren;
- Assert(plan);
-
switch (nodeTag(plan))
{
case T_Result:
/* target list */
if (es->verbose)
- show_plan_tlist(plan, es);
+ show_plan_tlist(planstate, ancestors, es);
/* quals, sort keys, etc */
switch (nodeTag(plan))
{
case T_IndexScan:
show_scan_qual(((IndexScan *) plan)->indexqualorig,
- "Index Cond", plan, outer_plan, es);
- show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
+ "Index Cond", planstate, ancestors, es);
+ show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
- "Index Cond", plan, outer_plan, es);
+ "Index Cond", planstate, ancestors, es);
break;
case T_BitmapHeapScan:
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
- "Recheck Cond", plan, outer_plan, es);
+ "Recheck Cond", planstate, ancestors, es);
/* FALL THRU */
case T_SeqScan:
case T_FunctionScan:
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
- show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
+ show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_TidScan:
{
if (list_length(tidquals) > 1)
tidquals = list_make1(make_orclause(tidquals));
- show_scan_qual(tidquals, "TID Cond", plan, outer_plan, es);
- show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
+ show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
+ show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
}
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
- "Join Filter", plan, es);
- show_upper_qual(plan->qual, "Filter", plan, es);
+ "Join Filter", planstate, ancestors, es);
+ show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
- "Merge Cond", plan, es);
+ "Merge Cond", planstate, ancestors, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
- "Join Filter", plan, es);
- show_upper_qual(plan->qual, "Filter", plan, es);
+ "Join Filter", planstate, ancestors, es);
+ show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses,
- "Hash Cond", plan, es);
+ "Hash Cond", planstate, ancestors, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
- "Join Filter", plan, es);
- show_upper_qual(plan->qual, "Filter", plan, es);
+ "Join Filter", planstate, ancestors, es);
+ show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_Agg:
case T_Group:
- show_upper_qual(plan->qual, "Filter", plan, es);
+ show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_Sort:
- show_sort_keys(plan, es);
+ show_sort_keys((SortState *) planstate, ancestors, es);
show_sort_info((SortState *) planstate, es);
break;
case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual,
- "One-Time Filter", plan, es);
- show_upper_qual(plan->qual, "Filter", plan, es);
+ "One-Time Filter", planstate, ancestors, es);
+ show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_Hash:
show_hash_info((HashState *) planstate, es);
}
/* Get ready to display the child plans */
- haschildren = plan->initPlan ||
- outerPlan(plan) ||
- innerPlan(plan) ||
+ haschildren = planstate->initPlan ||
+ outerPlanState(planstate) ||
+ innerPlanState(planstate) ||
IsA(plan, ModifyTable) ||
IsA(plan, Append) ||
IsA(plan, BitmapAnd) ||
IsA(plan, SubqueryScan) ||
planstate->subPlan;
if (haschildren)
+ {
ExplainOpenGroup("Plans", "Plans", false, es);
+ /* Pass current PlanState as head of ancestors list for children */
+ ancestors = lcons(planstate, ancestors);
+ }
/* initPlan-s */
- if (plan->initPlan)
- ExplainSubPlans(planstate->initPlan, "InitPlan", es);
+ if (planstate->initPlan)
+ ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
/* lefttree */
- if (outerPlan(plan))
- {
- /*
- * Ordinarily we don't pass down our own outer_plan value to our child
- * nodes, but in bitmap scan trees we must, since the bottom
- * BitmapIndexScan nodes may have outer references.
- */
- ExplainNode(outerPlan(plan), outerPlanState(planstate),
- IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
+ if (outerPlanState(planstate))
+ ExplainNode(outerPlanState(planstate), ancestors,
"Outer", NULL, es);
- }
/* righttree */
- if (innerPlan(plan))
- {
- ExplainNode(innerPlan(plan), innerPlanState(planstate),
- outerPlan(plan),
+ if (innerPlanState(planstate))
+ ExplainNode(innerPlanState(planstate), ancestors,
"Inner", NULL, es);
- }
/* special child plans */
switch (nodeTag(plan))
case T_ModifyTable:
ExplainMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
- outer_plan, es);
+ ancestors, es);
break;
case T_Append:
ExplainMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
- outer_plan, es);
+ ancestors, es);
break;
case T_BitmapAnd:
ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
((BitmapAndState *) planstate)->bitmapplans,
- outer_plan, es);
+ ancestors, es);
break;
case T_BitmapOr:
ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
((BitmapOrState *) planstate)->bitmapplans,
- outer_plan, es);
+ ancestors, es);
break;
case T_SubqueryScan:
- {
- SubqueryScan *subqueryscan = (SubqueryScan *) plan;
- SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
-
- ExplainNode(subqueryscan->subplan, subquerystate->subplan,
- NULL,
- "Subquery", NULL, es);
- }
+ ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
+ "Subquery", NULL, es);
break;
default:
break;
/* subPlan-s */
if (planstate->subPlan)
- ExplainSubPlans(planstate->subPlan, "SubPlan", es);
+ ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
/* end of child plans */
if (haschildren)
+ {
+ ancestors = list_delete_first(ancestors);
ExplainCloseGroup("Plans", "Plans", false, es);
+ }
/* in text format, undo whatever indentation we added */
if (es->format == EXPLAIN_FORMAT_TEXT)
* Show the targetlist of a plan node
*/
static void
-show_plan_tlist(Plan *plan, ExplainState *es)
+show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
{
+ Plan *plan = planstate->plan;
List *context;
List *result = NIL;
bool useprefix;
return;
/* Set up deparsing context */
- context = deparse_context_for_plan((Node *) plan,
- NULL,
- es->rtable,
- es->pstmt->subplans);
+ context = deparse_context_for_planstate((Node *) planstate,
+ ancestors,
+ es->rtable);
useprefix = list_length(es->rtable) > 1;
/* Deparse each result column (we now include resjunk ones) */
/*
* Show a qualifier expression
- *
- * Note: outer_plan is the referent for any OUTER vars in the scan qual;
- * this would be the outer side of a nestloop plan. Pass NULL if none.
*/
static void
-show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
+show_qual(List *qual, const char *qlabel,
+ PlanState *planstate, List *ancestors,
bool useprefix, ExplainState *es)
{
List *context;
node = (Node *) make_ands_explicit(qual);
/* Set up deparsing context */
- context = deparse_context_for_plan((Node *) plan,
- (Node *) outer_plan,
- es->rtable,
- es->pstmt->subplans);
+ context = deparse_context_for_planstate((Node *) planstate,
+ ancestors,
+ es->rtable);
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
*/
static void
show_scan_qual(List *qual, const char *qlabel,
- Plan *scan_plan, Plan *outer_plan,
+ PlanState *planstate, List *ancestors,
ExplainState *es)
{
bool useprefix;
- useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
- es->verbose);
- show_qual(qual, qlabel, scan_plan, outer_plan, useprefix, es);
+ useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
+ show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
}
/*
* Show a qualifier expression for an upper-level plan node
*/
static void
-show_upper_qual(List *qual, const char *qlabel, Plan *plan, ExplainState *es)
+show_upper_qual(List *qual, const char *qlabel,
+ PlanState *planstate, List *ancestors,
+ ExplainState *es)
{
bool useprefix;
useprefix = (list_length(es->rtable) > 1 || es->verbose);
- show_qual(qual, qlabel, plan, NULL, useprefix, es);
+ show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
}
/*
* Show the sort keys for a Sort node.
*/
static void
-show_sort_keys(Plan *sortplan, ExplainState *es)
+show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
{
- int nkeys = ((Sort *) sortplan)->numCols;
- AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
+ Sort *plan = (Sort *) sortstate->ss.ps.plan;
+ int nkeys = plan->numCols;
+ AttrNumber *keycols = plan->sortColIdx;
List *context;
List *result = NIL;
bool useprefix;
return;
/* Set up deparsing context */
- context = deparse_context_for_plan((Node *) sortplan,
- NULL,
- es->rtable,
- es->pstmt->subplans);
+ context = deparse_context_for_planstate((Node *) sortstate,
+ ancestors,
+ es->rtable);
useprefix = (list_length(es->rtable) > 1 || es->verbose);
for (keyno = 0; keyno < nkeys; keyno++)
{
/* find key expression in tlist */
AttrNumber keyresno = keycols[keyno];
- TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);
+ TargetEntry *target = get_tle_by_resno(plan->plan.targetlist,
+ keyresno);
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
* Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
* or BitmapOr node.
*
- * Ordinarily we don't pass down outer_plan to our child nodes, but in these
- * cases we must, since the node could be an "inner indexscan" in which case
- * outer references can appear in the child nodes.
+ * The ancestors list should already contain the immediate parent of these
+ * plans.
+ *
+ * Note: we don't actually need to examine the Plan list members, but
+ * we need the list in order to determine the length of the PlanState array.
*/
static void
-ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
- ExplainState *es)
+ExplainMemberNodes(List *plans, PlanState **planstates,
+ List *ancestors, ExplainState *es)
{
- ListCell *lst;
- int j = 0;
-
- foreach(lst, plans)
- {
- Plan *subnode = (Plan *) lfirst(lst);
+ int nplans = list_length(plans);
+ int j;
- ExplainNode(subnode, planstate[j],
- outer_plan,
- "Member", NULL,
- es);
- j++;
- }
+ for (j = 0; j < nplans; j++)
+ ExplainNode(planstates[j], ancestors,
+ "Member", NULL, es);
}
/*
* Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
+ *
+ * The ancestors list should already contain the immediate parent of these
+ * SubPlanStates.
*/
static void
-ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
+ExplainSubPlans(List *plans, List *ancestors,
+ const char *relationship, ExplainState *es)
{
ListCell *lst;
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
- ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
- sps->planstate,
- NULL,
- relationship, sp->plan_name,
- es);
+ ExplainNode(sps->planstate, ancestors,
+ relationship, sp->plan_name, es);
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.327 2010/07/09 21:11:47 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.328 2010/07/13 20:57:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* The rangetable is the list of actual RTEs from the query tree, and the
* cte list is the list of actual CTEs.
*
- * For deparsing plan trees, we provide for outer and inner subplan nodes.
- * The tlists of these nodes are used to resolve OUTER and INNER varnos.
- * Also, in the plan-tree case we don't have access to the parse-time CTE
- * list, so we need a list of subplans instead.
+ * When deparsing plan trees, there is always just a single item in the
+ * deparse_namespace list (since a plan tree never contains Vars with
+ * varlevelsup > 0). We store the PlanState node that is the immediate
+ * parent of the expression to be deparsed, as well as a list of that
+ * PlanState's ancestors. In addition, we store the outer and inner
+ * subplan nodes, whose targetlists are used to resolve OUTER and INNER Vars.
+ * (Note: these could be derived on-the-fly from the planstate instead.)
*/
typedef struct
{
List *rtable; /* List of RangeTblEntry nodes */
List *ctes; /* List of CommonTableExpr nodes */
- List *subplans; /* List of subplans, in plan-tree case */
+ /* Remaining fields are used only when deparsing a Plan tree: */
+ PlanState *planstate; /* immediate parent of current expression */
+ List *ancestors; /* ancestors of planstate */
+ PlanState *outer_planstate; /* OUTER subplan state, or NULL if none */
+ PlanState *inner_planstate; /* INNER subplan state, or NULL if none */
Plan *outer_plan; /* OUTER subplan, or NULL if none */
Plan *inner_plan; /* INNER subplan, or NULL if none */
} deparse_namespace;
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args, bool print_defaults);
static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
+static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
+ deparse_namespace *save_dpns);
+static void pop_child_plan(deparse_namespace *dpns,
+ deparse_namespace *save_dpns);
+static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+ deparse_namespace *save_dpns);
+static void pop_ancestor_plan(deparse_namespace *dpns,
+ deparse_namespace *save_dpns);
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
static void get_rule_windowclause(Query *query, deparse_context *context);
static void get_rule_windowspec(WindowClause *wc, List *targetList,
deparse_context *context);
-static void push_plan(deparse_namespace *dpns, Plan *subplan);
static char *get_variable(Var *var, int levelsup, bool showstar,
deparse_context *context);
static RangeTblEntry *find_rte_by_refname(const char *refname,
deparse_context *context);
+static void get_parameter(Param *param, deparse_context *context);
+static void print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+ deparse_namespace *dpns, deparse_context *context);
static const char *get_simple_binary_op_name(OpExpr *expr);
static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
static void appendContextKeyword(deparse_context *context, const char *str,
newrte->inFromCl = true;
/* Build two-element rtable */
+ memset(&dpns, 0, sizeof(dpns));
dpns.rtable = list_make2(oldrte, newrte);
dpns.ctes = NIL;
- dpns.subplans = NIL;
- dpns.outer_plan = dpns.inner_plan = NULL;
/* Set up context with one-deep namespace stack */
context.buf = &buf;
deparse_namespace *dpns;
RangeTblEntry *rte;
- dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+ dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
/* Build one-element rtable */
dpns->rtable = list_make1(rte);
dpns->ctes = NIL;
- dpns->subplans = NIL;
- dpns->outer_plan = dpns->inner_plan = NULL;
/* Return a one-deep namespace stack */
return list_make1(dpns);
}
/*
- * deparse_context_for_plan - Build deparse context for a plan node
+ * deparse_context_for_planstate - Build deparse context for a plan
*
* When deparsing an expression in a Plan tree, we might have to resolve
* OUTER or INNER references. To do this, the caller must provide the
- * parent Plan node. In the normal case of a join plan node, OUTER and
- * INNER references can be resolved by drilling down into the left and
- * right child plans. A special case is that a nestloop inner indexscan
- * might have OUTER Vars, but the outer side of the join is not a child
- * plan node. To handle such cases the outer plan node must be passed
- * separately. (Pass NULL for outer_plan otherwise.)
+ * parent PlanState node. Then OUTER and INNER references can be resolved
+ * by drilling down into the left and right child plans.
*
- * Note: plan and outer_plan really ought to be declared as "Plan *", but
- * we use "Node *" to avoid having to include plannodes.h in builtins.h.
+ * Note: planstate really ought to be declared as "PlanState *", but we use
+ * "Node *" to avoid having to include execnodes.h in builtins.h.
+ *
+ * The ancestors list is a list of the PlanState's parent PlanStates, the
+ * most-closely-nested first. This is needed to resolve PARAM_EXEC Params.
+ * Note we assume that all the PlanStates share the same rtable.
*
* The plan's rangetable list must also be passed. We actually prefer to use
* the rangetable to resolve simple Vars, but the plan inputs are necessary
* for Vars that reference expressions computed in subplan target lists.
- *
- * We also need the list of subplans associated with the Plan tree; this
- * is for resolving references to CTE subplans.
*/
List *
-deparse_context_for_plan(Node *plan, Node *outer_plan,
- List *rtable, List *subplans)
+deparse_context_for_planstate(Node *planstate, List *ancestors,
+ List *rtable)
{
deparse_namespace *dpns;
- dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+ dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
+ /* Initialize fields that stay the same across the whole plan tree */
dpns->rtable = rtable;
dpns->ctes = NIL;
- dpns->subplans = subplans;
+
+ /* Set our attention on the specific plan node passed in */
+ set_deparse_planstate(dpns, (PlanState *) planstate);
+ dpns->ancestors = ancestors;
+
+ /* Return a one-deep namespace stack */
+ return list_make1(dpns);
+}
+
+/*
+ * set_deparse_planstate: set up deparse_namespace to parse subexpressions
+ * of a given PlanState node
+ *
+ * This sets the planstate, outer_planstate, inner_planstate, outer_plan, and
+ * inner_plan fields. Caller is responsible for adjusting the ancestors list
+ * if necessary. Note that the rtable and ctes fields do not need to change
+ * when shifting attention to different plan nodes in a single plan tree.
+ */
+static void
+set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
+{
+ dpns->planstate = ps;
/*
- * Set up outer_plan and inner_plan from the Plan node (this includes
- * various special cases for particular Plan types).
+ * We special-case Append to pretend that the first child plan is the
+ * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
+ * according to one of the children, and the first one is the most natural
+ * choice. Likewise special-case ModifyTable to pretend that the first
+ * child plan is the OUTER referent; this is to support RETURNING lists
+ * containing references to non-target relations.
*/
- push_plan(dpns, (Plan *) plan);
+ if (IsA(ps, AppendState))
+ dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
+ else if (IsA(ps, ModifyTableState))
+ dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
+ else
+ dpns->outer_planstate = outerPlanState(ps);
+
+ if (dpns->outer_planstate)
+ dpns->outer_plan = dpns->outer_planstate->plan;
+ else
+ dpns->outer_plan = NULL;
/*
- * If outer_plan is given, that overrides whatever we got from the plan.
+ * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
+ * use OUTER because that could someday conflict with the normal meaning.)
+ * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
*/
- if (outer_plan)
- dpns->outer_plan = (Plan *) outer_plan;
+ if (IsA(ps, SubqueryScanState))
+ dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
+ else if (IsA(ps, CteScanState))
+ dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
+ else
+ dpns->inner_planstate = innerPlanState(ps);
- /* Return a one-deep namespace stack */
- return list_make1(dpns);
+ if (dpns->inner_planstate)
+ dpns->inner_plan = dpns->inner_planstate->plan;
+ else
+ dpns->inner_plan = NULL;
+}
+
+/*
+ * push_child_plan: temporarily transfer deparsing attention to a child plan
+ *
+ * When expanding an OUTER or INNER reference, we must adjust the deparse
+ * context in case the referenced expression itself uses OUTER/INNER. We
+ * modify the top stack entry in-place to avoid affecting levelsup issues
+ * (although in a Plan tree there really shouldn't be any).
+ *
+ * Caller must provide a local deparse_namespace variable to save the
+ * previous state for pop_child_plan.
+ */
+static void
+push_child_plan(deparse_namespace *dpns, PlanState *ps,
+ deparse_namespace *save_dpns)
+{
+ /* Save state for restoration later */
+ *save_dpns = *dpns;
+
+ /*
+ * Currently we don't bother to adjust the ancestors list, because an
+ * OUTER or INNER reference really shouldn't contain any Params that
+ * would be set by the parent node itself. If we did want to adjust it,
+ * lcons'ing dpns->planstate onto dpns->ancestors would be the appropriate
+ * thing --- and pop_child_plan would need to undo the change to the list.
+ */
+
+ /* Set attention on selected child */
+ set_deparse_planstate(dpns, ps);
}
+/*
+ * pop_child_plan: undo the effects of push_child_plan
+ */
+static void
+pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
+{
+ /* Restore fields changed by push_child_plan */
+ *dpns = *save_dpns;
+}
+
+/*
+ * push_ancestor_plan: temporarily transfer deparsing attention to an
+ * ancestor plan
+ *
+ * When expanding a Param reference, we must adjust the deparse context
+ * to match the plan node that contains the expression being printed;
+ * otherwise we'd fail if that expression itself contains a Param or
+ * OUTER/INNER variables.
+ *
+ * The target ancestor is conveniently identified by the ListCell holding it
+ * in dpns->ancestors.
+ *
+ * Caller must provide a local deparse_namespace variable to save the
+ * previous state for pop_ancestor_plan.
+ */
+static void
+push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+ deparse_namespace *save_dpns)
+{
+ PlanState *ps = (PlanState *) lfirst(ancestor_cell);
+ List *ancestors;
+
+ /* Save state for restoration later */
+ *save_dpns = *dpns;
+
+ /* Build a new ancestor list with just this node's ancestors */
+ ancestors = NIL;
+ while ((ancestor_cell = lnext(ancestor_cell)) != NULL)
+ ancestors = lappend(ancestors, lfirst(ancestor_cell));
+ dpns->ancestors = ancestors;
+
+ /* Set attention on selected ancestor */
+ set_deparse_planstate(dpns, ps);
+}
+
+/*
+ * pop_ancestor_plan: undo the effects of push_ancestor_plan
+ */
+static void
+pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
+{
+ /* Free the ancestor list made in push_ancestor_plan */
+ list_free(dpns->ancestors);
+
+ /* Restore fields changed by push_ancestor_plan */
+ *dpns = *save_dpns;
+}
+
+
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
context.varprefix = (list_length(query->rtable) != 1);
context.prettyFlags = prettyFlags;
context.indentLevel = PRETTYINDENT_STD;
+
+ memset(&dpns, 0, sizeof(dpns));
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
- dpns.subplans = NIL;
- dpns.outer_plan = dpns.inner_plan = NULL;
get_rule_expr(qual, &context, false);
}
context.prettyFlags = prettyFlags;
context.indentLevel = startIndent;
+ memset(&dpns, 0, sizeof(dpns));
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
- dpns.subplans = NIL;
- dpns.outer_plan = dpns.inner_plan = NULL;
switch (query->commandType)
{
}
-/*
- * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
- *
- * When expanding an OUTER or INNER reference, we must push new outer/inner
- * subplans in case the referenced expression itself uses OUTER/INNER. We
- * modify the top stack entry in-place to avoid affecting levelsup issues
- * (although in a Plan tree there really shouldn't be any).
- *
- * Caller must save and restore outer_plan and inner_plan around this.
- *
- * We also use this to initialize the fields during deparse_context_for_plan.
- */
-static void
-push_plan(deparse_namespace *dpns, Plan *subplan)
-{
- /*
- * We special-case Append to pretend that the first child plan is the
- * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
- * according to one of the children, and the first one is the most natural
- * choice. Likewise special-case ModifyTable to pretend that the first
- * child plan is the OUTER referent; this is to support RETURNING lists
- * containing references to non-target relations.
- */
- if (IsA(subplan, Append))
- dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
- else if (IsA(subplan, ModifyTable))
- dpns->outer_plan = (Plan *) linitial(((ModifyTable *) subplan)->plans);
- else
- dpns->outer_plan = outerPlan(subplan);
-
- /*
- * For a SubqueryScan, pretend the subplan is INNER referent. (We don't
- * use OUTER because that could someday conflict with the normal meaning.)
- * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
- */
- if (IsA(subplan, SubqueryScan))
- dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
- else if (IsA(subplan, CteScan))
- {
- int ctePlanId = ((CteScan *) subplan)->ctePlanId;
-
- if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
- dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
- else
- dpns->inner_plan = NULL;
- }
- else
- dpns->inner_plan = innerPlan(subplan);
-}
-
-
/*
* Display a Var appropriately.
*
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->outer_plan);
+ push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->inner_plan);
+ push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return NULL;
}
else
dpns->inner_plan)
{
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->inner_plan);
+ push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->outer_plan);
+ push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return result;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->inner_plan);
+ push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return result;
}
else
deparse_namespace mydpns;
const char *result;
+ memset(&mydpns, 0, sizeof(mydpns));
mydpns.rtable = rte->subquery->rtable;
mydpns.ctes = rte->subquery->cteList;
- mydpns.subplans = NIL;
- mydpns.outer_plan = mydpns.inner_plan = NULL;
context->namespaces = lcons(&mydpns,
context->namespaces);
* look into the child plan's tlist instead.
*/
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->inner_plan);
+ push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return result;
}
}
deparse_namespace mydpns;
const char *result;
+ memset(&mydpns, 0, sizeof(mydpns));
mydpns.rtable = ctequery->rtable;
mydpns.ctes = ctequery->cteList;
- mydpns.subplans = NIL;
- mydpns.outer_plan = mydpns.inner_plan = NULL;
new_nslist = list_copy_tail(context->namespaces,
ctelevelsup);
* can look into the subplan's tlist instead.
*/
TargetEntry *tle;
- Plan *save_outer;
- Plan *save_inner;
+ deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
- save_outer = dpns->outer_plan;
- save_inner = dpns->inner_plan;
- push_plan(dpns, dpns->inner_plan);
+ push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
- dpns->outer_plan = save_outer;
- dpns->inner_plan = save_inner;
+ pop_child_plan(dpns, &save_dpns);
return result;
}
}
return result;
}
+/*
+ * Display a Param appropriately.
+ */
+static void
+get_parameter(Param *param, deparse_context *context)
+{
+ /*
+ * If it's a PARAM_EXEC parameter, try to locate the expression from
+ * which the parameter was computed. This will necessarily be in some
+ * ancestor of the current expression's PlanState. Note that failing
+ * to find a referent isn't an error, since the Param might well be a
+ * subplan output rather than an input.
+ */
+ if (param->paramkind == PARAM_EXEC)
+ {
+ deparse_namespace *dpns;
+ PlanState *child_ps;
+ bool in_same_plan_level;
+ ListCell *lc;
+
+ dpns = (deparse_namespace *) linitial(context->namespaces);
+ child_ps = dpns->planstate;
+ in_same_plan_level = true;
+
+ foreach(lc, dpns->ancestors)
+ {
+ PlanState *ps = (PlanState *) lfirst(lc);
+ ListCell *lc2;
+
+ /*
+ * NestLoops transmit params to their inner child only; also,
+ * once we've crawled up out of a subplan, this couldn't
+ * possibly be the right match.
+ */
+ if (IsA(ps, NestLoopState) &&
+ child_ps == innerPlanState(ps) &&
+ in_same_plan_level)
+ {
+ NestLoop *nl = (NestLoop *) ps->plan;
+
+ foreach(lc2, nl->nestParams)
+ {
+ NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
+
+ if (nlp->paramno == param->paramid)
+ {
+ /* Found a match, so print it */
+ print_parameter_expr((Node *) nlp->paramval, lc,
+ dpns, context);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Check to see if we're crawling up from a subplan.
+ */
+ foreach(lc2, ps->subPlan)
+ {
+ SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+ SubPlan *subplan = (SubPlan *) sstate->xprstate.expr;
+ ListCell *lc3;
+ ListCell *lc4;
+
+ if (child_ps != sstate->planstate)
+ continue;
+
+ /* Matched subplan, so check its arguments */
+ forboth(lc3, subplan->parParam, lc4, subplan->args)
+ {
+ int paramid = lfirst_int(lc3);
+ Node *arg = (Node *) lfirst(lc4);
+
+ if (paramid == param->paramid)
+ {
+ /* Found a match, so print it */
+ print_parameter_expr(arg, lc, dpns, context);
+ return;
+ }
+ }
+
+ /* Keep looking, but we are emerging from a subplan. */
+ in_same_plan_level = false;
+ break;
+ }
+
+ /*
+ * Likewise check to see if we're emerging from an initplan.
+ * Initplans never have any parParams, so no need to search that
+ * list, but we need to know if we should reset
+ * in_same_plan_level.
+ */
+ foreach(lc2, ps->initPlan)
+ {
+ SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+
+ if (child_ps != sstate->planstate)
+ continue;
+
+ /* No parameters to be had here. */
+ Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+
+ /* Keep looking, but we are emerging from an initplan. */
+ in_same_plan_level = false;
+ break;
+ }
+
+ /* No luck, crawl up to next ancestor */
+ child_ps = ps;
+ }
+ }
+
+ /*
+ * Not PARAM_EXEC, or couldn't find referent: just print $N.
+ */
+ appendStringInfo(context->buf, "$%d", param->paramid);
+}
+
+/* Print a parameter reference expression found by get_parameter */
+static void
+print_parameter_expr(Node *expr, ListCell *ancestor_cell,
+ deparse_namespace *dpns, deparse_context *context)
+{
+ deparse_namespace save_dpns;
+ bool save_varprefix;
+
+ /* Switch attention to the ancestor plan node */
+ push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+
+ /*
+ * Force prefixing of Vars, since they won't belong to the relation being
+ * scanned in the original plan node.
+ */
+ save_varprefix = context->varprefix;
+ context->varprefix = true;
+
+ /*
+ * We don't need to add parentheses because a Param's expansion is
+ * (currently) always a Var or Aggref.
+ */
+ Assert(IsA(expr, Var) || IsA(expr, Aggref));
+
+ get_rule_expr(expr, context, false);
+
+ context->varprefix = save_varprefix;
+
+ pop_ancestor_plan(dpns, &save_dpns);
+}
/*
* get_simple_binary_op_name
break;
case T_Param:
- appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
+ get_parameter((Param *) node, context);
break;
case T_Aggref: