aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/explain.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-07-13 20:57:19 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2010-07-13 20:57:19 +0000
commit1cc29fe7c60ba643c114979dbe588d3a38005449 (patch)
tree432913ae6834cea94e9dced0dfdfab2329b0a090 /src/backend/commands/explain.c
parent4504a1bc017ff006c129dfce3e691f0d9b29a0c3 (diff)
downloadpostgresql-1cc29fe7c60ba643c114979dbe588d3a38005449.tar.gz
postgresql-1cc29fe7c60ba643c114979dbe588d3a38005449.zip
Teach EXPLAIN to print PARAM_EXEC Params as the referenced expressions,
rather than just $N. This brings the display of nestloop-inner-indexscan plans back to where it's been, and incidentally improves the display of SubPlan parameters as well. In passing, simplify the EXPLAIN code by having it deal primarily in the PlanState tree rather than separately searching Plan and PlanState trees. This is noticeably cleaner for subplans, and about a wash elsewhere. One small difference from previous behavior is that EXPLAIN will no longer qualify local variable references in inner-indexscan plan nodes, since it no longer sees such nodes as possibly referencing multiple tables. Vars referenced through PARAM_EXEC Params are still forcibly qualified, though, so I don't think the display is any more confusing than before. Adjust a couple of examples in the documentation to match this behavior.
Diffstat (limited to 'src/backend/commands/explain.c')
-rw-r--r--src/backend/commands/explain.c248
1 files changed, 116 insertions, 132 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e8dba94e218..c8fa693a60e 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -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;
-
- foreach(lst, plans)
- {
- Plan *subnode = (Plan *) lfirst(lst);
+ int nplans = list_length(plans);
+ int j;
- ExplainNode(subnode, planstate[j],
- outer_plan,
- "Member", NULL,
- es);
- j++;
- }
+ for (j = 0; j < nplans; j++)
+ ExplainNode(planstates[j], ancestors,
+ "Member", NULL, es);
}
/*
* Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
+ *
+ * The ancestors list should already contain the immediate parent of these
+ * SubPlanStates.
*/
static void
-ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
+ExplainSubPlans(List *plans, List *ancestors,
+ const char *relationship, ExplainState *es)
{
ListCell *lst;
@@ -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);
}
}