diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2010-07-13 20:57:19 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2010-07-13 20:57:19 +0000 |
commit | 1cc29fe7c60ba643c114979dbe588d3a38005449 (patch) | |
tree | 432913ae6834cea94e9dced0dfdfab2329b0a090 /src/backend/utils/adt/ruleutils.c | |
parent | 4504a1bc017ff006c129dfce3e691f0d9b29a0c3 (diff) | |
download | postgresql-1cc29fe7c60ba643c114979dbe588d3a38005449.tar.gz postgresql-1cc29fe7c60ba643c114979dbe588d3a38005449.zip |
Teach EXPLAIN to print PARAM_EXEC Params as the referenced expressions,
rather than just $N. This brings the display of nestloop-inner-indexscan
plans back to where it's been, and incidentally improves the display of
SubPlan parameters as well. In passing, simplify the EXPLAIN code by
having it deal primarily in the PlanState tree rather than separately
searching Plan and PlanState trees. This is noticeably cleaner for
subplans, and about a wash elsewhere.
One small difference from previous behavior is that EXPLAIN will no longer
qualify local variable references in inner-indexscan plan nodes, since it
no longer sees such nodes as possibly referencing multiple tables. Vars
referenced through PARAM_EXEC Params are still forcibly qualified, though,
so I don't think the display is any more confusing than before. Adjust a
couple of examples in the documentation to match this behavior.
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 501 |
1 files changed, 356 insertions, 145 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8b99c95070d..7f1ec8766c6 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * 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 $ * *------------------------------------------------------------------------- */ @@ -102,16 +102,23 @@ typedef struct * 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; @@ -154,6 +161,15 @@ static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname, 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, @@ -183,11 +199,13 @@ static void get_rule_orderby(List *orderList, List *targetList, 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, @@ -625,10 +643,9 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty) 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; @@ -2072,7 +2089,7 @@ deparse_context_for(const char *aliasname, Oid relid) 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); @@ -2085,63 +2102,191 @@ deparse_context_for(const char *aliasname, Oid relid) /* 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 @@ -2285,10 +2430,10 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, 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); } @@ -2432,10 +2577,9 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace, 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) { @@ -3481,57 +3625,6 @@ get_utility_query_def(Query *query, deparse_context *context) /* - * 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. * * In some cases (currently only when recursing into an unnamed join) @@ -3576,17 +3669,14 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context) 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 @@ -3598,24 +3688,20 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context) 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 @@ -3627,8 +3713,7 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context) 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 @@ -3653,17 +3738,14 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context) 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 @@ -3675,8 +3757,7 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context) 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; } @@ -3827,8 +3908,7 @@ get_name_for_var_field(Var *var, int fieldno, 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); @@ -3836,22 +3916,18 @@ get_name_for_var_field(Var *var, int fieldno, 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); @@ -3859,15 +3935,12 @@ get_name_for_var_field(Var *var, int fieldno, 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 @@ -3926,10 +3999,9 @@ get_name_for_var_field(Var *var, int fieldno, 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); @@ -3954,8 +4026,7 @@ get_name_for_var_field(Var *var, int fieldno, * 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) @@ -3967,15 +4038,12 @@ get_name_for_var_field(Var *var, int fieldno, 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; } } @@ -4049,10 +4117,9 @@ get_name_for_var_field(Var *var, int fieldno, 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); @@ -4076,8 +4143,7 @@ get_name_for_var_field(Var *var, int fieldno, * 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) @@ -4089,15 +4155,12 @@ get_name_for_var_field(Var *var, int fieldno, 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; } } @@ -4160,6 +4223,154 @@ find_rte_by_refname(const char *refname, deparse_context *context) 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 @@ -4496,7 +4707,7 @@ get_rule_expr(Node *node, deparse_context *context, break; case T_Param: - appendStringInfo(buf, "$%d", ((Param *) node)->paramid); + get_parameter((Param *) node, context); break; case T_Aggref: |