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.
This commit is contained in:
Tom Lane 2010-07-13 20:57:19 +00:00
parent 4504a1bc01
commit 1cc29fe7c6
5 changed files with 484 additions and 289 deletions

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/perform.sgml,v 1.82 2010/06/28 22:46:11 momjian Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/perform.sgml,v 1.83 2010/07/13 20:57:19 tgl Exp $ -->
<chapter id="performance-tips">
<title>Performance Tips</title>
@ -316,7 +316,7 @@ WHERE t1.unique1 &lt; 100 AND t1.unique2 = t2.unique2;
-&gt; Bitmap Index Scan on tenk1_unique1 (cost=0.00..2.37 rows=106 width=0)
Index Cond: (unique1 &lt; 100)
-&gt; Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..3.01 rows=1 width=244)
Index Cond: (t2.unique2 = t1.unique2)
Index Cond: (unique2 = t1.unique2)
</programlisting>
</para>
@ -329,7 +329,7 @@ WHERE t1.unique1 &lt; 100 AND t1.unique2 = t2.unique2;
so it doesn't affect the row count of the outer scan. For the inner (lower) scan, the
<literal>unique2</> value of the current outer-scan row is plugged into
the inner index scan to produce an index condition like
<literal>t2.unique2 = <replaceable>constant</replaceable></literal>.
<literal>unique2 = <replaceable>constant</replaceable></literal>.
So we get the same inner-scan plan and costs that we'd get from, say,
<literal>EXPLAIN SELECT * FROM tenk2 WHERE unique2 = 42</literal>. The
costs of the loop node are then set on the basis of the cost of the outer
@ -405,7 +405,7 @@ WHERE t1.unique1 &lt; 100 AND t1.unique2 = t2.unique2;
-&gt; Bitmap Index Scan on tenk1_unique1 (cost=0.00..2.37 rows=106 width=0) (actual time=0.546..0.546 rows=100 loops=1)
Index Cond: (unique1 &lt; 100)
-&gt; Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..3.01 rows=1 width=244) (actual time=0.067..0.078 rows=1 loops=100)
Index Cond: (t2.unique2 = t1.unique2)
Index Cond: (unique2 = t1.unique2)
Total runtime: 14.452 ms
</screen>

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/planstats.sgml,v 1.9 2007/12/28 21:03:31 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/planstats.sgml,v 1.10 2010/07/13 20:57:19 tgl Exp $ -->
<chapter id="planner-stats-details">
<title>How the Planner Uses Statistics</title>
@ -353,7 +353,7 @@ WHERE t1.unique1 &lt; 50 AND t1.unique2 = t2.unique2;
-&gt; Bitmap Index Scan on tenk1_unique1 (cost=0.00..4.63 rows=50 width=0)
Index Cond: (unique1 &lt; 50)
-&gt; Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..6.27 rows=1 width=244)
Index Cond: (t2.unique2 = t1.unique2)
Index Cond: (unique2 = t1.unique2)
</programlisting>
The restriction on <structname>tenk1</structname>,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.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 $
*
*-------------------------------------------------------------------------
*/
@ -54,27 +54,30 @@ static void ExplainOneQuery(Query *query, ExplainState *es,
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,
@ -484,8 +487,7 @@ ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
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);
}
/*
@ -585,31 +587,30 @@ elapsed_time(instr_time *starttime)
/*
* 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;
@ -617,8 +618,6 @@ ExplainNode(Plan *plan, PlanState *planstate,
int save_indent = es->indent;
bool haschildren;
Assert(plan);
switch (nodeTag(plan))
{
case T_Result:
@ -999,23 +998,23 @@ ExplainNode(Plan *plan, PlanState *planstate,
/* 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:
@ -1023,7 +1022,7 @@ ExplainNode(Plan *plan, PlanState *planstate,
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:
{
@ -1035,41 +1034,41 @@ ExplainNode(Plan *plan, PlanState *planstate,
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);
@ -1157,9 +1156,9 @@ ExplainNode(Plan *plan, PlanState *planstate,
}
/* 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) ||
@ -1167,32 +1166,25 @@ ExplainNode(Plan *plan, PlanState *planstate,
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))
@ -1200,32 +1192,26 @@ ExplainNode(Plan *plan, PlanState *planstate,
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;
@ -1233,11 +1219,14 @@ ExplainNode(Plan *plan, PlanState *planstate,
/* 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)
@ -1252,8 +1241,9 @@ ExplainNode(Plan *plan, PlanState *planstate,
* 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;
@ -1271,10 +1261,9 @@ show_plan_tlist(Plan *plan, ExplainState *es)
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) */
@ -1294,12 +1283,10 @@ show_plan_tlist(Plan *plan, ExplainState *es)
/*
* 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;
@ -1314,10 +1301,9 @@ show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
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);
@ -1331,36 +1317,38 @@ show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
*/
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;
@ -1371,17 +1359,17 @@ show_sort_keys(Plan *sortplan, ExplainState *es)
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);
@ -1596,34 +1584,33 @@ ExplainScanTarget(Scan *plan, ExplainState *es)
* 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;
int nplans = list_length(plans);
int j;
foreach(lst, plans)
{
Plan *subnode = (Plan *) lfirst(lst);
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;
@ -1632,11 +1619,8 @@ ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
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);
}
}

View File

@ -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 up outer_plan and inner_plan from the Plan node (this includes
* various special cases for particular Plan types).
*/
push_plan(dpns, (Plan *) plan);
/*
* If outer_plan is given, that overrides whatever we got from the plan.
*/
if (outer_plan)
dpns->outer_plan = (Plan *) outer_plan;
/* 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;
/*
* 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(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;
/*
* 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(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);
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)
{
@ -3480,57 +3624,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.
*
@ -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:

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.350 2010/07/06 19:19:00 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.351 2010/07/13 20:57:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -609,8 +609,8 @@ extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit);
extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(Node *plan, Node *outer_plan,
List *rtable, List *subplans);
extern List *deparse_context_for_planstate(Node *planstate, List *ancestors,
List *rtable);
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *qualifier,
const char *ident);