Commit 8af12bca authored by Tom Lane's avatar Tom Lane

Assorted minor refactoring in EXPLAIN.

This is believed to not change the output at all, with one known exception:
"Subquery Scan foo" becomes "Subquery Scan on foo".  (We can fix that if
anyone complains, but it would be a wart, because the old code was clearly
inconsistent.)  The main intention is to remove duplicate coding and
provide a cleaner base for subsequent EXPLAIN patching.

Robert Haas
parent a7e58786
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.186 2009/06/11 14:48:55 momjian Exp $ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.187 2009/07/24 21:08:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,6 +42,7 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL; ...@@ -42,6 +42,7 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
typedef struct ExplainState typedef struct ExplainState
{ {
StringInfo str; /* output buffer */
/* options */ /* options */
bool printTList; /* print plan targetlists */ bool printTList; /* print plan targetlists */
bool printAnalyze; /* print actual times */ bool printAnalyze; /* print actual times */
...@@ -56,23 +57,23 @@ static void ExplainOneQuery(Query *query, ExplainStmt *stmt, ...@@ -56,23 +57,23 @@ static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
StringInfo buf); StringInfo buf);
static double elapsed_time(instr_time *starttime); static double elapsed_time(instr_time *starttime);
static void explain_outNode(StringInfo str, static void ExplainNode(Plan *plan, PlanState *planstate,
Plan *plan, PlanState *planstate, Plan *outer_plan, int indent, ExplainState *es);
Plan *outer_plan, static void show_plan_tlist(Plan *plan, int indent, ExplainState *es);
int indent, ExplainState *es); static void show_qual(List *qual, const char *qlabel, Plan *plan,
static void show_plan_tlist(Plan *plan, Plan *outer_plan, int indent, bool useprefix, ExplainState *es);
StringInfo str, int indent, ExplainState *es);
static void show_scan_qual(List *qual, const char *qlabel, static void show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *scan_plan, Plan *outer_plan, Plan *scan_plan, Plan *outer_plan,
StringInfo str, int indent, ExplainState *es); int indent, ExplainState *es);
static void show_upper_qual(List *qual, const char *qlabel, Plan *plan, static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
StringInfo str, int indent, ExplainState *es); int indent, ExplainState *es);
static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, static void show_sort_keys(Plan *sortplan, int indent, ExplainState *es);
const char *qlabel, static void show_sort_info(SortState *sortstate, int indent, ExplainState *es);
StringInfo str, int indent, ExplainState *es);
static void show_sort_info(SortState *sortstate,
StringInfo str, int indent, ExplainState *es);
static const char *explain_get_index_name(Oid indexId); 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, int indent, ExplainState *es);
static void ExplainSubPlans(List *plans, int indent, ExplainState *es);
/* /*
...@@ -347,14 +348,14 @@ ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc, ...@@ -347,14 +348,14 @@ ExplainPrintPlan(StringInfo str, QueryDesc *queryDesc,
Assert(queryDesc->plannedstmt != NULL); Assert(queryDesc->plannedstmt != NULL);
memset(&es, 0, sizeof(es)); memset(&es, 0, sizeof(es));
es.str = str;
es.printTList = verbose; es.printTList = verbose;
es.printAnalyze = analyze; es.printAnalyze = analyze;
es.pstmt = queryDesc->plannedstmt; es.pstmt = queryDesc->plannedstmt;
es.rtable = queryDesc->plannedstmt->rtable; es.rtable = queryDesc->plannedstmt->rtable;
explain_outNode(str, ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
queryDesc->plannedstmt->planTree, queryDesc->planstate, NULL, 0, &es);
NULL, 0, &es);
} }
/* /*
...@@ -414,8 +415,8 @@ elapsed_time(instr_time *starttime) ...@@ -414,8 +415,8 @@ elapsed_time(instr_time *starttime)
} }
/* /*
* explain_outNode - * ExplainNode -
* converts a Plan node into ascii string and appends it to 'str' * converts a Plan node into ascii string and appends it to es->str
* *
* planstate points to the executor state node corresponding to the plan node. * 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 * We need this to get at the instrumentation data (if any) as well as the
...@@ -424,19 +425,27 @@ elapsed_time(instr_time *starttime) ...@@ -424,19 +425,27 @@ elapsed_time(instr_time *starttime)
* outer_plan, if not null, references another plan node that is the outer * 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 * side of a join with the current node. This is only interesting for
* deciphering runtime keys of an inner indexscan. * deciphering runtime keys of an inner indexscan.
*
* If indent is positive, we indent the plan output accordingly and put "->"
* in front of it. This should only happen for child plan nodes.
*/ */
static void static void
explain_outNode(StringInfo str, ExplainNode(Plan *plan, PlanState *planstate,
Plan *plan, PlanState *planstate, Plan *outer_plan,
Plan *outer_plan, int indent, ExplainState *es)
int indent, ExplainState *es)
{ {
const char *pname; const char *pname;
int i;
if (indent)
{
Assert(indent >= 2);
appendStringInfoSpaces(es->str, 2 * indent - 4);
appendStringInfoString(es->str, "-> ");
}
if (plan == NULL) if (plan == NULL)
{ {
appendStringInfoChar(str, '\n'); appendStringInfoChar(es->str, '\n');
return; return;
} }
...@@ -656,141 +665,34 @@ explain_outNode(StringInfo str, ...@@ -656,141 +665,34 @@ explain_outNode(StringInfo str,
break; break;
} }
appendStringInfoString(str, pname); appendStringInfoString(es->str, pname);
switch (nodeTag(plan)) switch (nodeTag(plan))
{ {
case T_IndexScan: case T_IndexScan:
if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir)) if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir))
appendStringInfoString(str, " Backward"); appendStringInfoString(es->str, " Backward");
appendStringInfo(str, " using %s", appendStringInfo(es->str, " using %s",
explain_get_index_name(((IndexScan *) plan)->indexid)); explain_get_index_name(((IndexScan *) plan)->indexid));
/* FALL THRU */ /* FALL THRU */
case T_SeqScan: case T_SeqScan:
case T_BitmapHeapScan: case T_BitmapHeapScan:
case T_TidScan: case T_TidScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
char *relname;
/* Assume it's on a real relation */
Assert(rte->rtekind == RTE_RELATION);
/* We only show the rel name, not schema name */
relname = get_rel_name(rte->relid);
appendStringInfo(str, " on %s",
quote_identifier(relname));
if (strcmp(rte->eref->aliasname, relname) != 0)
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break;
case T_BitmapIndexScan:
appendStringInfo(str, " on %s",
explain_get_index_name(((BitmapIndexScan *) plan)->indexid));
break;
case T_SubqueryScan: case T_SubqueryScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break;
case T_FunctionScan: case T_FunctionScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
Node *funcexpr;
char *proname;
/* Assert it's on a RangeFunction */
Assert(rte->rtekind == RTE_FUNCTION);
/*
* If the expression is still a function call, we can get the
* real name of the function. Otherwise, punt (this can
* happen if the optimizer simplified away the function call,
* for example).
*/
funcexpr = ((FunctionScan *) plan)->funcexpr;
if (funcexpr && IsA(funcexpr, FuncExpr))
{
Oid funcid = ((FuncExpr *) funcexpr)->funcid;
/* We only show the func name, not schema name */
proname = get_func_name(funcid);
}
else
proname = rte->eref->aliasname;
appendStringInfo(str, " on %s",
quote_identifier(proname));
if (strcmp(rte->eref->aliasname, proname) != 0)
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break;
case T_ValuesScan: case T_ValuesScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
char *valsname;
/* Assert it's on a values rte */
Assert(rte->rtekind == RTE_VALUES);
valsname = rte->eref->aliasname;
appendStringInfo(str, " on %s",
quote_identifier(valsname));
}
break;
case T_CteScan: case T_CteScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
/* Assert it's on a non-self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(!rte->self_reference);
appendStringInfo(str, " on %s",
quote_identifier(rte->ctename));
if (strcmp(rte->eref->aliasname, rte->ctename) != 0)
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break;
case T_WorkTableScan: case T_WorkTableScan:
if (((Scan *) plan)->scanrelid > 0) ExplainScanTarget((Scan *) plan, es);
{ break;
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid, case T_BitmapIndexScan:
es->rtable); appendStringInfo(es->str, " on %s",
explain_get_index_name(((BitmapIndexScan *) plan)->indexid));
/* Assert it's on a self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(rte->self_reference);
appendStringInfo(str, " on %s",
quote_identifier(rte->ctename));
if (strcmp(rte->eref->aliasname, rte->ctename) != 0)
appendStringInfo(str, " %s",
quote_identifier(rte->eref->aliasname));
}
break; break;
default: default:
break; break;
} }
appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)", appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->startup_cost, plan->total_cost, plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width); plan->plan_rows, plan->plan_width);
...@@ -805,67 +707,46 @@ explain_outNode(StringInfo str, ...@@ -805,67 +707,46 @@ explain_outNode(StringInfo str,
{ {
double nloops = planstate->instrument->nloops; double nloops = planstate->instrument->nloops;
appendStringInfo(str, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)", appendStringInfo(es->str,
" (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1000.0 * planstate->instrument->startup / nloops, 1000.0 * planstate->instrument->startup / nloops,
1000.0 * planstate->instrument->total / nloops, 1000.0 * planstate->instrument->total / nloops,
planstate->instrument->ntuples / nloops, planstate->instrument->ntuples / nloops,
planstate->instrument->nloops); planstate->instrument->nloops);
} }
else if (es->printAnalyze) else if (es->printAnalyze)
appendStringInfo(str, " (never executed)"); appendStringInfoString(es->str, " (never executed)");
appendStringInfoChar(str, '\n'); appendStringInfoChar(es->str, '\n');
/* target list */ /* target list */
if (es->printTList) if (es->printTList)
show_plan_tlist(plan, str, indent, es); show_plan_tlist(plan, indent, es);
/* quals, sort keys, etc */ /* quals, sort keys, etc */
switch (nodeTag(plan)) switch (nodeTag(plan))
{ {
case T_IndexScan: case T_IndexScan:
show_scan_qual(((IndexScan *) plan)->indexqualorig, show_scan_qual(((IndexScan *) plan)->indexqualorig,
"Index Cond", "Index Cond", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
break; break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig, show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond", "Index Cond", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
break; break;
case T_BitmapHeapScan: case T_BitmapHeapScan:
/* XXX do we want to show this in production? */
show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig, show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
"Recheck Cond", "Recheck Cond", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
/* FALL THRU */ /* FALL THRU */
case T_SeqScan: case T_SeqScan:
case T_FunctionScan: case T_FunctionScan:
case T_ValuesScan: case T_ValuesScan:
case T_CteScan: case T_CteScan:
case T_WorkTableScan: case T_WorkTableScan:
show_scan_qual(plan->qual,
"Filter",
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
break;
case T_SubqueryScan: case T_SubqueryScan:
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
break; break;
case T_TidScan: case T_TidScan:
{ {
...@@ -878,69 +759,42 @@ explain_outNode(StringInfo str, ...@@ -878,69 +759,42 @@ explain_outNode(StringInfo str,
if (list_length(tidquals) > 1) if (list_length(tidquals) > 1)
tidquals = list_make1(make_orclause(tidquals)); tidquals = list_make1(make_orclause(tidquals));
show_scan_qual(tidquals, show_scan_qual(tidquals,
"TID Cond", "TID Cond", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
show_scan_qual(plan->qual, show_scan_qual(plan->qual,
"Filter", "Filter", plan, outer_plan, indent, es);
((Scan *) plan)->scanrelid,
plan, outer_plan,
str, indent, es);
} }
break; break;
case T_NestLoop: case T_NestLoop:
show_upper_qual(((NestLoop *) plan)->join.joinqual, show_upper_qual(((NestLoop *) plan)->join.joinqual,
"Join Filter", plan, "Join Filter", plan, indent, es);
str, indent, es); show_upper_qual(plan->qual, "Filter", plan, indent, es);
show_upper_qual(plan->qual,
"Filter", plan,
str, indent, es);
break; break;
case T_MergeJoin: case T_MergeJoin:
show_upper_qual(((MergeJoin *) plan)->mergeclauses, show_upper_qual(((MergeJoin *) plan)->mergeclauses,
"Merge Cond", plan, "Merge Cond", plan, indent, es);
str, indent, es);
show_upper_qual(((MergeJoin *) plan)->join.joinqual, show_upper_qual(((MergeJoin *) plan)->join.joinqual,
"Join Filter", plan, "Join Filter", plan, indent, es);
str, indent, es); show_upper_qual(plan->qual, "Filter", plan, indent, es);
show_upper_qual(plan->qual,
"Filter", plan,
str, indent, es);
break; break;
case T_HashJoin: case T_HashJoin:
show_upper_qual(((HashJoin *) plan)->hashclauses, show_upper_qual(((HashJoin *) plan)->hashclauses,
"Hash Cond", plan, "Hash Cond", plan, indent, es);
str, indent, es);
show_upper_qual(((HashJoin *) plan)->join.joinqual, show_upper_qual(((HashJoin *) plan)->join.joinqual,
"Join Filter", plan, "Join Filter", plan, indent, es);
str, indent, es); show_upper_qual(plan->qual, "Filter", plan, indent, es);
show_upper_qual(plan->qual,
"Filter", plan,
str, indent, es);
break; break;
case T_Agg: case T_Agg:
case T_Group: case T_Group:
show_upper_qual(plan->qual, show_upper_qual(plan->qual, "Filter", plan, indent, es);
"Filter", plan,
str, indent, es);
break; break;
case T_Sort: case T_Sort:
show_sort_keys(plan, show_sort_keys(plan, indent, es);
((Sort *) plan)->numCols, show_sort_info((SortState *) planstate, indent, es);
((Sort *) plan)->sortColIdx,
"Sort Key",
str, indent, es);
show_sort_info((SortState *) planstate,
str, indent, es);
break; break;
case T_Result: case T_Result:
show_upper_qual((List *) ((Result *) plan)->resconstantqual, show_upper_qual((List *) ((Result *) plan)->resconstantqual,
"One-Time Filter", plan, "One-Time Filter", plan, indent, es);
str, indent, es); show_upper_qual(plan->qual, "Filter", plan, indent, es);
show_upper_qual(plan->qual,
"Filter", plan,
str, indent, es);
break; break;
default: default:
break; break;
...@@ -948,183 +802,69 @@ explain_outNode(StringInfo str, ...@@ -948,183 +802,69 @@ explain_outNode(StringInfo str,
/* initPlan-s */ /* initPlan-s */
if (plan->initPlan) if (plan->initPlan)
{ ExplainSubPlans(planstate->initPlan, indent, es);
ListCell *lst;
foreach(lst, planstate->initPlan)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " %s\n", sp->plan_name);
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str,
exec_subplan_get_plan(es->pstmt, sp),
sps->planstate,
NULL,
indent + 4, es);
}
}
/* lefttree */ /* lefttree */
if (outerPlan(plan)) if (outerPlan(plan))
{ {
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
/* /*
* Ordinarily we don't pass down our own outer_plan value to our child * 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 * nodes, but in bitmap scan trees we must, since the bottom
* BitmapIndexScan nodes may have outer references. * BitmapIndexScan nodes may have outer references.
*/ */
explain_outNode(str, outerPlan(plan), ExplainNode(outerPlan(plan), outerPlanState(planstate),
outerPlanState(planstate), IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
IsA(plan, BitmapHeapScan) ? outer_plan : NULL, indent + 3, es);
indent + 3, es);
} }
/* righttree */ /* righttree */
if (innerPlan(plan)) if (innerPlan(plan))
{ {
for (i = 0; i < indent; i++) ExplainNode(innerPlan(plan), innerPlanState(planstate),
appendStringInfo(str, " "); outerPlan(plan), indent + 3, es);
appendStringInfo(str, " -> ");
explain_outNode(str, innerPlan(plan),
innerPlanState(planstate),
outerPlan(plan),
indent + 3, es);
}
if (IsA(plan, Append))
{
Append *appendplan = (Append *) plan;
AppendState *appendstate = (AppendState *) planstate;
ListCell *lst;
int j;
j = 0;
foreach(lst, appendplan->appendplans)
{
Plan *subnode = (Plan *) lfirst(lst);
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
/*
* Ordinarily we don't pass down our own outer_plan value to our
* child nodes, but in an Append we must, since we might be
* looking at an appendrel indexscan with outer references from
* the member scans.
*/
explain_outNode(str, subnode,
appendstate->appendplans[j],
outer_plan,
indent + 3, es);
j++;
}
} }
if (IsA(plan, BitmapAnd)) /* special child plans */
{ switch (nodeTag(plan))
BitmapAnd *bitmapandplan = (BitmapAnd *) plan;
BitmapAndState *bitmapandstate = (BitmapAndState *) planstate;
ListCell *lst;
int j;
j = 0;
foreach(lst, bitmapandplan->bitmapplans)
{
Plan *subnode = (Plan *) lfirst(lst);
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, subnode,
bitmapandstate->bitmapplans[j],
outer_plan, /* pass down same outer plan */
indent + 3, es);
j++;
}
}
if (IsA(plan, BitmapOr))
{ {
BitmapOr *bitmaporplan = (BitmapOr *) plan; case T_Append:
BitmapOrState *bitmaporstate = (BitmapOrState *) planstate; ExplainMemberNodes(((Append *) plan)->appendplans,
ListCell *lst; ((AppendState *) planstate)->appendplans,
int j; outer_plan, indent, es);
break;
j = 0; case T_BitmapAnd:
foreach(lst, bitmaporplan->bitmapplans) ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
{ ((BitmapAndState *) planstate)->bitmapplans,
Plan *subnode = (Plan *) lfirst(lst); outer_plan, indent, es);
break;
for (i = 0; i < indent; i++) case T_BitmapOr:
appendStringInfo(str, " "); ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
appendStringInfo(str, " -> "); ((BitmapOrState *) planstate)->bitmapplans,
outer_plan, indent, es);
explain_outNode(str, subnode, break;
bitmaporstate->bitmapplans[j], case T_SubqueryScan:
outer_plan, /* pass down same outer plan */ {
indent + 3, es); SubqueryScan *subqueryscan = (SubqueryScan *) plan;
j++; SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
}
}
if (IsA(plan, SubqueryScan)) ExplainNode(subqueryscan->subplan, subquerystate->subplan,
{ NULL, indent + 3, es);
SubqueryScan *subqueryscan = (SubqueryScan *) plan; }
SubqueryScanState *subquerystate = (SubqueryScanState *) planstate; break;
Plan *subnode = subqueryscan->subplan; default:
break;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, subnode,
subquerystate->subplan,
NULL,
indent + 3, es);
} }
/* subPlan-s */ /* subPlan-s */
if (planstate->subPlan) if (planstate->subPlan)
{ ExplainSubPlans(planstate->subPlan, indent, es);
ListCell *lst;
foreach(lst, planstate->subPlan)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " %s\n", sp->plan_name);
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str,
exec_subplan_get_plan(es->pstmt, sp),
sps->planstate,
NULL,
indent + 4, es);
}
}
} }
/* /*
* Show the targetlist of a plan node * Show the targetlist of a plan node
*/ */
static void static void
show_plan_tlist(Plan *plan, show_plan_tlist(Plan *plan, int indent, ExplainState *es)
StringInfo str, int indent, ExplainState *es)
{ {
List *context; List *context;
bool useprefix; bool useprefix;
...@@ -1149,9 +889,8 @@ show_plan_tlist(Plan *plan, ...@@ -1149,9 +889,8 @@ show_plan_tlist(Plan *plan,
useprefix = list_length(es->rtable) > 1; useprefix = list_length(es->rtable) > 1;
/* Emit line prefix */ /* Emit line prefix */
for (i = 0; i < indent; i++) appendStringInfoSpaces(es->str, indent * 2);
appendStringInfo(str, " "); appendStringInfoString(es->str, " Output: ");
appendStringInfo(str, " Output: ");
/* Deparse each non-junk result column */ /* Deparse each non-junk result column */
i = 0; i = 0;
...@@ -1162,31 +901,28 @@ show_plan_tlist(Plan *plan, ...@@ -1162,31 +901,28 @@ show_plan_tlist(Plan *plan,
if (tle->resjunk) if (tle->resjunk)
continue; continue;
if (i++ > 0) if (i++ > 0)
appendStringInfo(str, ", "); appendStringInfoString(es->str, ", ");
appendStringInfoString(str, appendStringInfoString(es->str,
deparse_expression((Node *) tle->expr, context, deparse_expression((Node *) tle->expr, context,
useprefix, false)); useprefix, false));
} }
appendStringInfoChar(str, '\n'); appendStringInfoChar(es->str, '\n');
} }
/* /*
* Show a qualifier expression for a scan plan node * Show a qualifier expression
* *
* Note: outer_plan is the referent for any OUTER vars in the scan qual; * 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. * this would be the outer side of a nestloop plan. Pass NULL if none.
*/ */
static void static void
show_scan_qual(List *qual, const char *qlabel, show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
int scanrelid, Plan *scan_plan, Plan *outer_plan, int indent, bool useprefix, ExplainState *es)
StringInfo str, int indent, ExplainState *es)
{ {
List *context; List *context;
bool useprefix;
Node *node; Node *node;
char *exprstr; char *exprstr;
int i;
/* No work if empty qual */ /* No work if empty qual */
if (qual == NIL) if (qual == NIL)
...@@ -1196,75 +932,64 @@ show_scan_qual(List *qual, const char *qlabel, ...@@ -1196,75 +932,64 @@ show_scan_qual(List *qual, const char *qlabel,
node = (Node *) make_ands_explicit(qual); node = (Node *) make_ands_explicit(qual);
/* Set up deparsing context */ /* Set up deparsing context */
context = deparse_context_for_plan((Node *) scan_plan, context = deparse_context_for_plan((Node *) plan,
(Node *) outer_plan, (Node *) outer_plan,
es->rtable, es->rtable,
es->pstmt->subplans); es->pstmt->subplans);
useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan));
/* Deparse the expression */ /* Deparse the expression */
exprstr = deparse_expression(node, context, useprefix, false); exprstr = deparse_expression(node, context, useprefix, false);
/* And add to str */ /* And add to es->str */
for (i = 0; i < indent; i++) appendStringInfoSpaces(es->str, indent * 2);
appendStringInfo(str, " "); appendStringInfo(es->str, " %s: %s\n", qlabel, exprstr);
appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
} }
/* /*
* Show a qualifier expression for an upper-level plan node * Show a qualifier expression for a scan plan node
*/ */
static void static void
show_upper_qual(List *qual, const char *qlabel, Plan *plan, show_scan_qual(List *qual, const char *qlabel,
StringInfo str, int indent, ExplainState *es) Plan *scan_plan, Plan *outer_plan,
int indent, ExplainState *es)
{ {
List *context;
bool useprefix; bool useprefix;
Node *node;
char *exprstr;
int i;
/* No work if empty qual */
if (qual == NIL)
return;
/* Set up deparsing context */ useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan));
context = deparse_context_for_plan((Node *) plan, show_qual(qual, qlabel, scan_plan, outer_plan, indent, useprefix, es);
NULL, }
es->rtable,
es->pstmt->subplans);
useprefix = list_length(es->rtable) > 1;
/* Deparse the expression */ /*
node = (Node *) make_ands_explicit(qual); * Show a qualifier expression for an upper-level plan node
exprstr = deparse_expression(node, context, useprefix, false); */
static void
show_upper_qual(List *qual, const char *qlabel, Plan *plan,
int indent, ExplainState *es)
{
bool useprefix;
/* And add to str */ useprefix = (list_length(es->rtable) > 1);
for (i = 0; i < indent; i++) show_qual(qual, qlabel, plan, NULL, indent, useprefix, es);
appendStringInfo(str, " ");
appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
} }
/* /*
* Show the sort keys for a Sort node. * Show the sort keys for a Sort node.
*/ */
static void static void
show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, show_sort_keys(Plan *sortplan, int indent, ExplainState *es)
const char *qlabel,
StringInfo str, int indent, ExplainState *es)
{ {
int nkeys = ((Sort *) sortplan)->numCols;
AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
List *context; List *context;
bool useprefix; bool useprefix;
int keyno; int keyno;
char *exprstr; char *exprstr;
int i;
if (nkeys <= 0) if (nkeys <= 0)
return; return;
for (i = 0; i < indent; i++) appendStringInfoSpaces(es->str, indent * 2);
appendStringInfo(str, " "); appendStringInfoString(es->str, " Sort Key: ");
appendStringInfo(str, " %s: ", qlabel);
/* Set up deparsing context */ /* Set up deparsing context */
context = deparse_context_for_plan((Node *) sortplan, context = deparse_context_for_plan((Node *) sortplan,
...@@ -1284,33 +1009,30 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols, ...@@ -1284,33 +1009,30 @@ show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
/* Deparse the expression, showing any top-level cast */ /* Deparse the expression, showing any top-level cast */
exprstr = deparse_expression((Node *) target->expr, context, exprstr = deparse_expression((Node *) target->expr, context,
useprefix, true); useprefix, true);
/* And add to str */ /* And add to es->str */
if (keyno > 0) if (keyno > 0)
appendStringInfo(str, ", "); appendStringInfoString(es->str, ", ");
appendStringInfoString(str, exprstr); appendStringInfoString(es->str, exprstr);
} }
appendStringInfo(str, "\n"); appendStringInfoChar(es->str, '\n');
} }
/* /*
* If it's EXPLAIN ANALYZE, show tuplesort explain info for a sort node * If it's EXPLAIN ANALYZE, show tuplesort explain info for a sort node
*/ */
static void static void
show_sort_info(SortState *sortstate, show_sort_info(SortState *sortstate, int indent, ExplainState *es)
StringInfo str, int indent, ExplainState *es)
{ {
Assert(IsA(sortstate, SortState)); Assert(IsA(sortstate, SortState));
if (es->printAnalyze && sortstate->sort_Done && if (es->printAnalyze && sortstate->sort_Done &&
sortstate->tuplesortstate != NULL) sortstate->tuplesortstate != NULL)
{ {
char *sortinfo; char *sortinfo;
int i;
sortinfo = tuplesort_explain((Tuplesortstate *) sortstate->tuplesortstate); sortinfo = tuplesort_explain((Tuplesortstate *) sortstate->tuplesortstate);
for (i = 0; i < indent; i++) appendStringInfoSpaces(es->str, indent * 2);
appendStringInfo(str, " "); appendStringInfo(es->str, " %s\n", sortinfo);
appendStringInfo(str, " %s\n", sortinfo);
pfree(sortinfo); pfree(sortinfo);
} }
} }
...@@ -1340,3 +1062,119 @@ explain_get_index_name(Oid indexId) ...@@ -1340,3 +1062,119 @@ explain_get_index_name(Oid indexId)
} }
return result; return result;
} }
/*
* Show the target of a Scan node
*/
static void
ExplainScanTarget(Scan *plan, ExplainState *es)
{
char *objectname = NULL;
RangeTblEntry *rte;
if (plan->scanrelid <= 0) /* Is this still possible? */
return;
rte = rt_fetch(plan->scanrelid, es->rtable);
switch (nodeTag(plan))
{
case T_SeqScan:
case T_IndexScan:
case T_BitmapHeapScan:
case T_TidScan:
/* Assert it's on a real relation */
Assert(rte->rtekind == RTE_RELATION);
objectname = get_rel_name(rte->relid);
break;
case T_FunctionScan:
{
Node *funcexpr;
/* Assert it's on a RangeFunction */
Assert(rte->rtekind == RTE_FUNCTION);
/*
* If the expression is still a function call, we can get the
* real name of the function. Otherwise, punt (this can
* happen if the optimizer simplified away the function call,
* for example).
*/
funcexpr = ((FunctionScan *) plan)->funcexpr;
if (funcexpr && IsA(funcexpr, FuncExpr))
{
Oid funcid = ((FuncExpr *) funcexpr)->funcid;
objectname = get_func_name(funcid);
}
}
break;
case T_ValuesScan:
Assert(rte->rtekind == RTE_VALUES);
break;
case T_CteScan:
/* Assert it's on a non-self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(!rte->self_reference);
objectname = rte->ctename;
break;
case T_WorkTableScan:
/* Assert it's on a self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
Assert(rte->self_reference);
objectname = rte->ctename;
break;
default:
break;
}
appendStringInfoString(es->str, " on");
if (objectname != NULL)
appendStringInfo(es->str, " %s", quote_identifier(objectname));
if (objectname == NULL || strcmp(rte->eref->aliasname, objectname) != 0)
appendStringInfo(es->str, " %s",
quote_identifier(rte->eref->aliasname));
}
/*
* Explain the constituent plans of an 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,
int indent, ExplainState *es)
{
ListCell *lst;
int j = 0;
foreach(lst, plans)
{
Plan *subnode = (Plan *) lfirst(lst);
ExplainNode(subnode, planstate[j],
outer_plan, indent + 3, es);
j++;
}
}
/*
* Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
*/
static void
ExplainSubPlans(List *plans, int indent, ExplainState *es)
{
ListCell *lst;
foreach(lst, plans)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
appendStringInfoSpaces(es->str, indent * 2);
appendStringInfo(es->str, " %s\n", sp->plan_name);
ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
sps->planstate, NULL, indent + 4, es);
}
}
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.50 2009/01/01 17:23:42 momjian Exp $ * $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.51 2009/07/24 21:08:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -186,6 +186,26 @@ appendStringInfoChar(StringInfo str, char ch) ...@@ -186,6 +186,26 @@ appendStringInfoChar(StringInfo str, char ch)
str->data[str->len] = '\0'; str->data[str->len] = '\0';
} }
/*
* appendStringInfoSpaces
*
* Append the specified number of spaces to a buffer.
*/
void
appendStringInfoSpaces(StringInfo str, int count)
{
if (count > 0)
{
/* Make more room if needed */
enlargeStringInfo(str, count);
/* OK, append the spaces */
while (--count >= 0)
str->data[str->len++] = ' ';
str->data[str->len] = '\0';
}
}
/* /*
* appendBinaryStringInfo * appendBinaryStringInfo
* *
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.303 2009/07/16 06:33:44 petere Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.304 2009/07/24 21:08:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -187,7 +187,6 @@ static RangeTblEntry *find_rte_by_refname(const char *refname, ...@@ -187,7 +187,6 @@ static RangeTblEntry *find_rte_by_refname(const char *refname,
deparse_context *context); deparse_context *context);
static const char *get_simple_binary_op_name(OpExpr *expr); static const char *get_simple_binary_op_name(OpExpr *expr);
static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
static void appendStringInfoSpaces(StringInfo buf, int count);
static void appendContextKeyword(deparse_context *context, const char *str, static void appendContextKeyword(deparse_context *context, const char *str,
int indentBefore, int indentAfter, int indentPlus); int indentBefore, int indentAfter, int indentPlus);
static void get_rule_expr(Node *node, deparse_context *context, static void get_rule_expr(Node *node, deparse_context *context,
...@@ -4172,16 +4171,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) ...@@ -4172,16 +4171,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
} }
/*
* appendStringInfoSpaces - append spaces to buffer
*/
static void
appendStringInfoSpaces(StringInfo buf, int count)
{
while (count-- > 0)
appendStringInfoChar(buf, ' ');
}
/* /*
* appendContextKeyword - append a keyword to buffer * appendContextKeyword - append a keyword to buffer
* *
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/lib/stringinfo.h,v 1.36 2009/01/01 17:23:59 momjian Exp $ * $PostgreSQL: pgsql/src/include/lib/stringinfo.h,v 1.37 2009/07/24 21:08:42 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -131,6 +131,12 @@ extern void appendStringInfoChar(StringInfo str, char ch); ...@@ -131,6 +131,12 @@ extern void appendStringInfoChar(StringInfo str, char ch);
appendStringInfoChar(str, ch) : \ appendStringInfoChar(str, ch) : \
(void)((str)->data[(str)->len] = (ch), (str)->data[++(str)->len] = '\0')) (void)((str)->data[(str)->len] = (ch), (str)->data[++(str)->len] = '\0'))
/*------------------------
* appendStringInfoSpaces
* Append a given number of spaces to str.
*/
extern void appendStringInfoSpaces(StringInfo str, int count);
/*------------------------ /*------------------------
* appendBinaryStringInfo * appendBinaryStringInfo
* Append arbitrary binary data to a StringInfo, allocating more space * Append arbitrary binary data to a StringInfo, allocating more space
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment