aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c501
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: