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
This commit is contained in:
parent
a7e587863c
commit
8af12bca3b
@ -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;
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
* 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,
|
|||||||
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)
|
case T_SubqueryScan:
|
||||||
{
|
case T_FunctionScan:
|
||||||
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
|
case T_ValuesScan:
|
||||||
es->rtable);
|
case T_CteScan:
|
||||||
char *relname;
|
case T_WorkTableScan:
|
||||||
|
ExplainScanTarget((Scan *) plan, es);
|
||||||
/* 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;
|
break;
|
||||||
case T_BitmapIndexScan:
|
case T_BitmapIndexScan:
|
||||||
appendStringInfo(str, " on %s",
|
appendStringInfo(es->str, " on %s",
|
||||||
explain_get_index_name(((BitmapIndexScan *) plan)->indexid));
|
explain_get_index_name(((BitmapIndexScan *) plan)->indexid));
|
||||||
break;
|
break;
|
||||||
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:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
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:
|
|
||||||
if (((Scan *) plan)->scanrelid > 0)
|
|
||||||
{
|
|
||||||
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
|
|
||||||
es->rtable);
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
/* 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))
|
/* special child plans */
|
||||||
|
switch (nodeTag(plan))
|
||||||
{
|
{
|
||||||
Append *appendplan = (Append *) plan;
|
case T_Append:
|
||||||
AppendState *appendstate = (AppendState *) planstate;
|
ExplainMemberNodes(((Append *) plan)->appendplans,
|
||||||
ListCell *lst;
|
((AppendState *) planstate)->appendplans,
|
||||||
int j;
|
outer_plan, indent, es);
|
||||||
|
break;
|
||||||
|
case T_BitmapAnd:
|
||||||
|
ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
|
||||||
|
((BitmapAndState *) planstate)->bitmapplans,
|
||||||
|
outer_plan, indent, es);
|
||||||
|
break;
|
||||||
|
case T_BitmapOr:
|
||||||
|
ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
|
||||||
|
((BitmapOrState *) planstate)->bitmapplans,
|
||||||
|
outer_plan, indent, es);
|
||||||
|
break;
|
||||||
|
case T_SubqueryScan:
|
||||||
|
{
|
||||||
|
SubqueryScan *subqueryscan = (SubqueryScan *) plan;
|
||||||
|
SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
|
||||||
|
|
||||||
j = 0;
|
ExplainNode(subqueryscan->subplan, subquerystate->subplan,
|
||||||
foreach(lst, appendplan->appendplans)
|
NULL, indent + 3, es);
|
||||||
{
|
}
|
||||||
Plan *subnode = (Plan *) lfirst(lst);
|
break;
|
||||||
|
default:
|
||||||
for (i = 0; i < indent; i++)
|
break;
|
||||||
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))
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
BitmapOrState *bitmaporstate = (BitmapOrState *) planstate;
|
|
||||||
ListCell *lst;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
foreach(lst, bitmaporplan->bitmapplans)
|
|
||||||
{
|
|
||||||
Plan *subnode = (Plan *) lfirst(lst);
|
|
||||||
|
|
||||||
for (i = 0; i < indent; i++)
|
|
||||||
appendStringInfo(str, " ");
|
|
||||||
appendStringInfo(str, " -> ");
|
|
||||||
|
|
||||||
explain_outNode(str, subnode,
|
|
||||||
bitmaporstate->bitmapplans[j],
|
|
||||||
outer_plan, /* pass down same outer plan */
|
|
||||||
indent + 3, es);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsA(plan, SubqueryScan))
|
|
||||||
{
|
|
||||||
SubqueryScan *subqueryscan = (SubqueryScan *) plan;
|
|
||||||
SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
|
|
||||||
Plan *subnode = subqueryscan->subplan;
|
|
||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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,19 +932,31 @@ 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 a scan plan node
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
show_scan_qual(List *qual, const char *qlabel,
|
||||||
|
Plan *scan_plan, Plan *outer_plan,
|
||||||
|
int indent, ExplainState *es)
|
||||||
|
{
|
||||||
|
bool useprefix;
|
||||||
|
|
||||||
|
useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan));
|
||||||
|
show_qual(qual, qlabel, scan_plan, outer_plan, indent, useprefix, es);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1216,55 +964,32 @@ show_scan_qual(List *qual, const char *qlabel,
|
|||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
show_upper_qual(List *qual, const char *qlabel, Plan *plan,
|
show_upper_qual(List *qual, const char *qlabel, Plan *plan,
|
||||||
StringInfo str, int indent, ExplainState *es)
|
int indent, ExplainState *es)
|
||||||
{
|
{
|
||||||
List *context;
|
|
||||||
bool useprefix;
|
bool useprefix;
|
||||||
Node *node;
|
|
||||||
char *exprstr;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* No work if empty qual */
|
useprefix = (list_length(es->rtable) > 1);
|
||||||
if (qual == NIL)
|
show_qual(qual, qlabel, plan, NULL, indent, useprefix, es);
|
||||||
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 the expression */
|
|
||||||
node = (Node *) make_ands_explicit(qual);
|
|
||||||
exprstr = deparse_expression(node, context, useprefix, false);
|
|
||||||
|
|
||||||
/* And add to str */
|
|
||||||
for (i = 0; i < indent; i++)
|
|
||||||
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,
|
|||||||
/* 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)
|
|||||||
}
|
}
|
||||||
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 @@
|
|||||||
* 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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 @@
|
|||||||
* 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);
|
|||||||
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user