Yeb Havinga <yebhavinga@gmail.com> writes:
> Will the new referenced expression printing also be used when printing
> subplans?
> If yes, I do not have to submit the latest version of a patch I made for
> subplan argument printing (discussed earlier in this thread
> http://archives.postgresql.org/pgsql-hackers/2010-02/msg01602.php)
Oh, I had forgotten that patch was still in progress. I think it's
unnecessary given what I'm fooling with. The attached patch needs
some more testing, but what I get with it is for example
regression=# explain (verbose) select (select oid from pg_class a where
regression(# a.oid = b.relfilenode) from pg_class b;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Seq Scan on pg_catalog.pg_class b (cost=0.00..5556.81 rows=669 width=4)
Output: (SubPlan 1)
SubPlan 1
-> Index Scan using pg_class_oid_index on pg_catalog.pg_class a (cost=0.00..8.27 rows=1 width=4)
Output: a.oid
Index Cond: (a.oid = b.relfilenode)
(6 rows)
(this is the first example in the above-referenced thread).
regards, tom lane
Index: src/backend/commands/explain.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/explain.c,v
retrieving revision 1.206
diff -c -r1.206 explain.c
*** src/backend/commands/explain.c 10 Jun 2010 01:26:30 -0000 1.206
--- src/backend/commands/explain.c 13 Jul 2010 18:13:25 -0000
***************
*** 54,80 ****
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,
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_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);
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 ExplainPropertyList(const char *qlabel, List *data,
ExplainState *es);
static void ExplainProperty(const char *qlabel, const char *value,
--- 54,83 ----
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
static double elapsed_time(instr_time *starttime);
! static void ExplainNode(PlanState *planstate, List *ancestors,
const char *relationship, const char *plan_name,
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,
! 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 **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,491 ****
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
es->rtable = queryDesc->plannedstmt->rtable;
! ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
! NULL, NULL, NULL, es);
}
/*
--- 487,493 ----
Assert(queryDesc->plannedstmt != NULL);
es->pstmt = queryDesc->plannedstmt;
es->rtable = queryDesc->plannedstmt->rtable;
! ExplainNode(queryDesc->planstate, NIL, NULL, NULL, es);
}
/*
***************
*** 585,615 ****
/*
* ExplainNode -
! * Appends a description of the Plan node 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.
! *
! * 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.
*
* 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
* 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,
const char *relationship, const char *plan_name,
ExplainState *es)
{
const char *pname; /* node type name for text output */
const char *sname; /* node type name for non-text output */
const char *strategy = NULL;
--- 587,616 ----
/*
* ExplainNode -
! * Appends a description of a plan tree to es->str
*
! * 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.
! *
! * 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
* corresponds to the nesting depth of logical output groups, and therefore
* is controlled by ExplainOpenGroup/ExplainCloseGroup.
*/
static void
! 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,624 ****
int save_indent = es->indent;
bool haschildren;
- Assert(plan);
-
switch (nodeTag(plan))
{
case T_Result:
--- 618,623 ----
***************
*** 999,1021 ****
/* target list */
if (es->verbose)
! show_plan_tlist(plan, 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);
break;
case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
! "Index Cond", plan, outer_plan, es);
break;
case T_BitmapHeapScan:
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
! "Recheck Cond", plan, outer_plan, es);
/* FALL THRU */
case T_SeqScan:
case T_FunctionScan:
--- 998,1020 ----
/* target list */
if (es->verbose)
! show_plan_tlist(planstate, ancestors, es);
/* quals, sort keys, etc */
switch (nodeTag(plan))
{
case T_IndexScan:
show_scan_qual(((IndexScan *) plan)->indexqualorig,
! "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", planstate, ancestors, es);
break;
case T_BitmapHeapScan:
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
! "Recheck Cond", planstate, ancestors, es);
/* FALL THRU */
case T_SeqScan:
case T_FunctionScan:
***************
*** 1023,1029 ****
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
! show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
break;
case T_TidScan:
{
--- 1022,1028 ----
case T_CteScan:
case T_WorkTableScan:
case T_SubqueryScan:
! show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_TidScan:
{
***************
*** 1035,1075 ****
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);
}
break;
case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual,
! "Join Filter", plan, es);
! show_upper_qual(plan->qual, "Filter", plan, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
! "Merge Cond", plan, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
! "Join Filter", plan, es);
! show_upper_qual(plan->qual, "Filter", plan, es);
break;
case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses,
! "Hash Cond", plan, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
! "Join Filter", plan, es);
! show_upper_qual(plan->qual, "Filter", plan, es);
break;
case T_Agg:
case T_Group:
! show_upper_qual(plan->qual, "Filter", plan, es);
break;
case T_Sort:
! show_sort_keys(plan, 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);
break;
case T_Hash:
show_hash_info((HashState *) planstate, es);
--- 1034,1074 ----
if (list_length(tidquals) > 1)
tidquals = list_make1(make_orclause(tidquals));
! 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", planstate, ancestors, es);
! show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses,
! "Merge Cond", planstate, ancestors, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual,
! "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", planstate, ancestors, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual,
! "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", planstate, ancestors, es);
break;
case T_Sort:
! 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", planstate, ancestors, es);
! show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
break;
case T_Hash:
show_hash_info((HashState *) planstate, es);
***************
*** 1169,1198 ****
if (haschildren)
ExplainOpenGroup("Plans", "Plans", false, es);
/* initPlan-s */
if (plan->initPlan)
! ExplainSubPlans(planstate->initPlan, "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,
"Outer", NULL, es);
- }
/* righttree */
! if (innerPlan(plan))
! {
! ExplainNode(innerPlan(plan), innerPlanState(planstate),
! outerPlan(plan),
"Inner", NULL, es);
- }
/* special child plans */
switch (nodeTag(plan))
--- 1168,1189 ----
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, ancestors, "InitPlan", es);
/* lefttree */
! if (outerPlanState(planstate))
! ExplainNode(outerPlanState(planstate), ancestors,
"Outer", NULL, es);
/* righttree */
! if (innerPlanState(planstate))
! ExplainNode(innerPlanState(planstate), ancestors,
"Inner", NULL, es);
/* special child plans */
switch (nodeTag(plan))
***************
*** 1200,1231 ****
case T_ModifyTable:
ExplainMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
! outer_plan, es);
break;
case T_Append:
ExplainMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
! outer_plan, es);
break;
case T_BitmapAnd:
ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
((BitmapAndState *) planstate)->bitmapplans,
! outer_plan, es);
break;
case T_BitmapOr:
ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
((BitmapOrState *) planstate)->bitmapplans,
! outer_plan, es);
break;
case T_SubqueryScan:
! {
! SubqueryScan *subqueryscan = (SubqueryScan *) plan;
! SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
!
! ExplainNode(subqueryscan->subplan, subquerystate->subplan,
! NULL,
! "Subquery", NULL, es);
! }
break;
default:
break;
--- 1191,1216 ----
case T_ModifyTable:
ExplainMemberNodes(((ModifyTable *) plan)->plans,
((ModifyTableState *) planstate)->mt_plans,
! ancestors, es);
break;
case T_Append:
ExplainMemberNodes(((Append *) plan)->appendplans,
((AppendState *) planstate)->appendplans,
! ancestors, es);
break;
case T_BitmapAnd:
ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
((BitmapAndState *) planstate)->bitmapplans,
! ancestors, es);
break;
case T_BitmapOr:
ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
((BitmapOrState *) planstate)->bitmapplans,
! ancestors, es);
break;
case T_SubqueryScan:
! ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
! "Subquery", NULL, es);
break;
default:
break;
***************
*** 1233,1241 ****
/* subPlan-s */
if (planstate->subPlan)
! ExplainSubPlans(planstate->subPlan, "SubPlan", es);
/* end of child plans */
if (haschildren)
ExplainCloseGroup("Plans", "Plans", false, es);
--- 1218,1227 ----
/* subPlan-s */
if (planstate->subPlan)
! ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
/* end of child plans */
+ ancestors = list_delete_first(ancestors);
if (haschildren)
ExplainCloseGroup("Plans", "Plans", false, es);
***************
*** 1252,1259 ****
* Show the targetlist of a plan node
*/
static void
! show_plan_tlist(Plan *plan, ExplainState *es)
{
List *context;
List *result = NIL;
bool useprefix;
--- 1238,1246 ----
* Show the targetlist of a plan node
*/
static void
! show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
{
+ Plan *plan = planstate->plan;
List *context;
List *result = NIL;
bool useprefix;
***************
*** 1271,1280 ****
return;
/* Set up deparsing context */
! context = deparse_context_for_plan((Node *) plan,
! NULL,
! es->rtable,
! es->pstmt->subplans);
useprefix = list_length(es->rtable) > 1;
/* Deparse each result column (we now include resjunk ones) */
--- 1258,1266 ----
return;
/* Set up deparsing context */
! 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,1305 ****
/*
* 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,
bool useprefix, ExplainState *es)
{
List *context;
--- 1280,1289 ----
/*
* Show a qualifier expression
*/
static void
! show_qual(List *qual, const char *qlabel,
! PlanState *planstate, List *ancestors,
bool useprefix, ExplainState *es)
{
List *context;
***************
*** 1314,1323 ****
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);
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
--- 1298,1306 ----
node = (Node *) make_ands_explicit(qual);
/* Set up deparsing context */
! context = deparse_context_for_planstate((Node *) planstate,
! ancestors,
! es->rtable);
/* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false);
***************
*** 1331,1366 ****
*/
static void
show_scan_qual(List *qual, const char *qlabel,
! Plan *scan_plan, Plan *outer_plan,
ExplainState *es)
{
bool useprefix;
! useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
! es->verbose);
! show_qual(qual, qlabel, scan_plan, outer_plan, 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)
{
bool useprefix;
useprefix = (list_length(es->rtable) > 1 || es->verbose);
! show_qual(qual, qlabel, plan, NULL, useprefix, es);
}
/*
* Show the sort keys for a Sort node.
*/
static void
! show_sort_keys(Plan *sortplan, ExplainState *es)
{
! int nkeys = ((Sort *) sortplan)->numCols;
! AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
List *context;
List *result = NIL;
bool useprefix;
--- 1314,1351 ----
*/
static void
show_scan_qual(List *qual, const char *qlabel,
! PlanState *planstate, List *ancestors,
ExplainState *es)
{
bool useprefix;
! 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,
! PlanState *planstate, List *ancestors,
! ExplainState *es)
{
bool useprefix;
useprefix = (list_length(es->rtable) > 1 || es->verbose);
! show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
}
/*
* Show the sort keys for a Sort node.
*/
static void
! show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
{
! Sort *plan = (Sort *) sortstate->ss.ps.plan;
! int nkeys = plan->numCols;
! AttrNumber *keycols = plan->sortColIdx;
List *context;
List *result = NIL;
bool useprefix;
***************
*** 1371,1387 ****
return;
/* Set up deparsing context */
! context = deparse_context_for_plan((Node *) sortplan,
! NULL,
! es->rtable,
! es->pstmt->subplans);
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);
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
--- 1356,1372 ----
return;
/* Set up deparsing context */
! 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(plan->plan.targetlist,
! keyresno);
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
***************
*** 1596,1629 ****
* 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.
*/
static void
! ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
! ExplainState *es)
{
! ListCell *lst;
! int j = 0;
! foreach(lst, plans)
! {
! Plan *subnode = (Plan *) lfirst(lst);
!
! ExplainNode(subnode, planstate[j],
! outer_plan,
! "Member", NULL,
! es);
! j++;
! }
}
/*
* Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
*/
static void
! ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
{
ListCell *lst;
--- 1581,1613 ----
* Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
* or BitmapOr node.
*
! * 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 **planstates,
! List *ancestors, ExplainState *es)
{
! int nplans = list_length(plans);
! int 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, List *ancestors,
! const char *relationship, ExplainState *es)
{
ListCell *lst;
***************
*** 1632,1642 ****
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);
}
}
--- 1616,1623 ----
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
! ExplainNode(sps->planstate, ancestors,
! relationship, sp->plan_name, es);
}
}
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.327
diff -c -r1.327 ruleutils.c
*** src/backend/utils/adt/ruleutils.c 9 Jul 2010 21:11:47 -0000 1.327
--- src/backend/utils/adt/ruleutils.c 13 Jul 2010 18:13:25 -0000
***************
*** 102,117 ****
* 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.
*/
typedef struct
{
List *rtable; /* List of RangeTblEntry nodes */
List *ctes; /* List of CommonTableExpr nodes */
! List *subplans; /* List of subplans, in plan-tree case */
Plan *outer_plan; /* OUTER subplan, or NULL if none */
Plan *inner_plan; /* INNER subplan, or NULL if none */
} deparse_namespace;
--- 102,124 ----
* The rangetable is the list of actual RTEs from the query tree, and the
* cte list is the list of actual CTEs.
*
! * 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 */
! /* 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,159 ****
--- 161,175 ----
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,193 ****
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 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,
--- 199,211 ----
static void get_rule_windowclause(Query *query, deparse_context *context);
static void get_rule_windowspec(WindowClause *wc, List *targetList,
deparse_context *context);
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,634 ****
newrte->inFromCl = true;
/* Build two-element rtable */
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;
--- 643,651 ----
newrte->inFromCl = true;
/* Build two-element rtable */
+ memset(&dpns, 0, sizeof(dpns));
dpns.rtable = list_make2(oldrte, newrte);
dpns.ctes = NIL;
/* Set up context with one-deep namespace stack */
context.buf = &buf;
***************
*** 2072,2078 ****
deparse_namespace *dpns;
RangeTblEntry *rte;
! dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
--- 2089,2095 ----
deparse_namespace *dpns;
RangeTblEntry *rte;
! dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
/* Build a minimal RTE for the rel */
rte = makeNode(RangeTblEntry);
***************
*** 2085,2147 ****
/* 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
*
* 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.)
*
! * 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.
*
* 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_namespace *dpns;
! dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
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;
! /* Return a one-deep namespace stack */
! return list_make1(dpns);
}
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
--- 2102,2292 ----
/* Build one-element rtable */
dpns->rtable = list_make1(rte);
dpns->ctes = NIL;
/* Return a one-deep namespace stack */
return list_make1(dpns);
}
/*
! * 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 PlanState node. Then OUTER and INNER references can be resolved
! * by drilling down into the left and right child plans.
*
! * 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.
*/
List *
! deparse_context_for_planstate(Node *planstate, List *ancestors,
! List *rtable)
{
deparse_namespace *dpns;
! dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
+ /* Initialize fields that stay the same across the whole plan tree */
dpns->rtable = rtable;
dpns->ctes = NIL;
!
! /* 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,2294 ****
context.varprefix = (list_length(query->rtable) != 1);
context.prettyFlags = prettyFlags;
context.indentLevel = PRETTYINDENT_STD;
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
- dpns.subplans = NIL;
- dpns.outer_plan = dpns.inner_plan = NULL;
get_rule_expr(qual, &context, false);
}
--- 2430,2439 ----
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;
get_rule_expr(qual, &context, false);
}
***************
*** 2432,2441 ****
context.prettyFlags = prettyFlags;
context.indentLevel = startIndent;
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
- dpns.subplans = NIL;
- dpns.outer_plan = dpns.inner_plan = NULL;
switch (query->commandType)
{
--- 2577,2585 ----
context.prettyFlags = prettyFlags;
context.indentLevel = startIndent;
+ memset(&dpns, 0, sizeof(dpns));
dpns.rtable = query->rtable;
dpns.ctes = query->cteList;
switch (query->commandType)
{
***************
*** 3481,3537 ****
/*
- * 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)
--- 3625,3630 ----
***************
*** 3576,3592 ****
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
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);
/*
* Force parentheses because our caller probably assumed a Var is a
--- 3669,3682 ----
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
! 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);
! push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
***************
*** 3598,3621 ****
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return NULL;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
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);
/*
* Force parentheses because our caller probably assumed a Var is a
--- 3688,3707 ----
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
! 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);
! push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
***************
*** 3627,3634 ****
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return NULL;
}
else
--- 3713,3719 ----
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! pop_child_plan(dpns, &save_dpns);
return NULL;
}
else
***************
*** 3653,3669 ****
dpns->inner_plan)
{
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
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);
/*
* Force parentheses because our caller probably assumed a Var is a
--- 3738,3751 ----
dpns->inner_plan)
{
TargetEntry *tle;
! 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);
! push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
***************
*** 3675,3682 ****
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return NULL;
}
--- 3757,3763 ----
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
! pop_child_plan(dpns, &save_dpns);
return NULL;
}
***************
*** 3827,3834 ****
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
const char *result;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
--- 3908,3914 ----
else if (var->varno == OUTER && dpns->outer_plan)
{
TargetEntry *tle;
! deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
***************
*** 3836,3857 ****
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);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return result;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
const char *result;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
--- 3916,3933 ----
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
Assert(netlevelsup == 0);
! push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! pop_child_plan(dpns, &save_dpns);
return result;
}
else if (var->varno == INNER && dpns->inner_plan)
{
TargetEntry *tle;
! deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
***************
*** 3859,3873 ****
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);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return result;
}
else
--- 3935,3946 ----
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
Assert(netlevelsup == 0);
! push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! pop_child_plan(dpns, &save_dpns);
return result;
}
else
***************
*** 3926,3935 ****
deparse_namespace mydpns;
const char *result;
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);
--- 3999,4007 ----
deparse_namespace mydpns;
const char *result;
+ memset(&mydpns, 0, sizeof(mydpns));
mydpns.rtable = rte->subquery->rtable;
mydpns.ctes = rte->subquery->cteList;
context->namespaces = lcons(&mydpns,
context->namespaces);
***************
*** 3954,3961 ****
* look into the child plan's tlist instead.
*/
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
const char *result;
if (!dpns->inner_plan)
--- 4026,4032 ----
* look into the child plan's tlist instead.
*/
TargetEntry *tle;
! deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
***************
*** 3967,3981 ****
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);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return result;
}
}
--- 4038,4049 ----
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
! push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! pop_child_plan(dpns, &save_dpns);
return result;
}
}
***************
*** 4049,4058 ****
deparse_namespace mydpns;
const char *result;
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);
--- 4117,4125 ----
deparse_namespace mydpns;
const char *result;
+ memset(&mydpns, 0, sizeof(mydpns));
mydpns.rtable = ctequery->rtable;
mydpns.ctes = ctequery->cteList;
new_nslist = list_copy_tail(context->namespaces,
ctelevelsup);
***************
*** 4076,4083 ****
* can look into the subplan's tlist instead.
*/
TargetEntry *tle;
! Plan *save_outer;
! Plan *save_inner;
const char *result;
if (!dpns->inner_plan)
--- 4143,4149 ----
* can look into the subplan's tlist instead.
*/
TargetEntry *tle;
! deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
***************
*** 4089,4103 ****
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);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! dpns->outer_plan = save_outer;
! dpns->inner_plan = save_inner;
return result;
}
}
--- 4155,4166 ----
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
Assert(netlevelsup == 0);
! push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
! pop_child_plan(dpns, &save_dpns);
return result;
}
}
***************
*** 4160,4165 ****
--- 4223,4387 ----
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 an initPlan.
+ */
+ foreach(lc2, ps->initPlan)
+ {
+ SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+ SubPlan *subplan = (SubPlan *) sstate->xprstate.expr;
+ ListCell *lc3;
+ ListCell *lc4;
+
+ if (child_ps != sstate->planstate)
+ continue;
+
+ /* Matched initPlan, 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 an initPlan. */
+ in_same_plan_level = false;
+ break;
+ }
+
+ /*
+ * Likewise for subPlans.
+ */
+ 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;
+ }
+
+ /* 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,4502 ****
break;
case T_Param:
! appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
break;
case T_Aggref:
--- 4718,4724 ----
break;
case T_Param:
! get_parameter((Param *) node, context);
break;
case T_Aggref:
Index: src/include/utils/builtins.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.350
diff -c -r1.350 builtins.h
*** src/include/utils/builtins.h 6 Jul 2010 19:19:00 -0000 1.350
--- src/include/utils/builtins.h 13 Jul 2010 18:13:25 -0000
***************
*** 609,616 ****
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 const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *qualifier,
const char *ident);
--- 609,616 ----
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_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);