1 /*-------------------------------------------------------------------------
4 * Explain query execution plans
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
12 *-------------------------------------------------------------------------
16 #include "access/xact.h"
17 #include "catalog/pg_constraint.h"
18 #include "catalog/pg_type.h"
19 #include "commands/explain.h"
20 #include "commands/prepare.h"
21 #include "commands/trigger.h"
22 #include "executor/instrument.h"
23 #include "optimizer/clauses.h"
24 #include "optimizer/planner.h"
25 #include "optimizer/var.h"
26 #include "parser/parsetree.h"
27 #include "rewrite/rewriteHandler.h"
28 #include "tcop/tcopprot.h"
29 #include "utils/builtins.h"
30 #include "utils/guc.h"
31 #include "utils/lsyscache.h"
32 #include "utils/tuplesort.h"
33 #include "utils/snapmgr.h"
36 /* Hook for plugins to get control in ExplainOneQuery() */
37 ExplainOneQuery_hook_type ExplainOneQuery_hook
= NULL
;
39 /* Hook for plugins to get control in explain_get_index_name() */
40 explain_get_index_name_hook_type explain_get_index_name_hook
= NULL
;
43 typedef struct ExplainState
46 bool printTList
; /* print plan targetlists */
47 bool printAnalyze
; /* print actual times */
49 PlannedStmt
*pstmt
; /* top of plan */
50 List
*rtable
; /* range table */
53 static void ExplainOneQuery(Query
*query
, ExplainStmt
*stmt
,
54 const char *queryString
,
55 ParamListInfo params
, TupOutputState
*tstate
);
56 static void report_triggers(ResultRelInfo
*rInfo
, bool show_relname
,
58 static double elapsed_time(instr_time
*starttime
);
59 static void explain_outNode(StringInfo str
,
60 Plan
*plan
, PlanState
*planstate
,
62 int indent
, ExplainState
*es
);
63 static void show_plan_tlist(Plan
*plan
,
64 StringInfo str
, int indent
, ExplainState
*es
);
65 static void show_scan_qual(List
*qual
, const char *qlabel
,
66 int scanrelid
, Plan
*scan_plan
, Plan
*outer_plan
,
67 StringInfo str
, int indent
, ExplainState
*es
);
68 static void show_upper_qual(List
*qual
, const char *qlabel
, Plan
*plan
,
69 StringInfo str
, int indent
, ExplainState
*es
);
70 static void show_sort_keys(Plan
*sortplan
, int nkeys
, AttrNumber
*keycols
,
72 StringInfo str
, int indent
, ExplainState
*es
);
73 static void show_sort_info(SortState
*sortstate
,
74 StringInfo str
, int indent
, ExplainState
*es
);
75 static const char *explain_get_index_name(Oid indexId
);
80 * execute an EXPLAIN command
83 ExplainQuery(ExplainStmt
*stmt
, const char *queryString
,
84 ParamListInfo params
, DestReceiver
*dest
)
88 TupOutputState
*tstate
;
92 /* Convert parameter type data to the form parser wants */
93 getParamListTypes(params
, ¶m_types
, &num_params
);
96 * Run parse analysis and rewrite. Note this also acquires sufficient
97 * locks on the source table(s).
99 * Because the parser and planner tend to scribble on their input, we make
100 * a preliminary copy of the source querytree. This prevents problems in
101 * the case that the EXPLAIN is in a portal or plpgsql function and is
102 * executed repeatedly. (See also the same hack in DECLARE CURSOR and
103 * PREPARE.) XXX FIXME someday.
105 rewritten
= pg_analyze_and_rewrite((Node
*) copyObject(stmt
->query
),
106 queryString
, param_types
, num_params
);
108 /* prepare for projection of tuples */
109 tstate
= begin_tup_output_tupdesc(dest
, ExplainResultDesc(stmt
));
111 if (rewritten
== NIL
)
113 /* In the case of an INSTEAD NOTHING, tell at least that */
114 do_text_output_oneline(tstate
, "Query rewrites to nothing");
118 /* Explain every plan */
119 foreach(l
, rewritten
)
121 ExplainOneQuery((Query
*) lfirst(l
), stmt
,
122 queryString
, params
, tstate
);
123 /* put a blank line between plans */
124 if (lnext(l
) != NULL
)
125 do_text_output_oneline(tstate
, "");
129 end_tup_output(tstate
);
133 * ExplainResultDesc -
134 * construct the result tupledesc for an EXPLAIN
137 ExplainResultDesc(ExplainStmt
*stmt
)
141 /* need a tuple descriptor representing a single TEXT column */
142 tupdesc
= CreateTemplateTupleDesc(1, false);
143 TupleDescInitEntry(tupdesc
, (AttrNumber
) 1, "QUERY PLAN",
150 * print out the execution plan for one Query
153 ExplainOneQuery(Query
*query
, ExplainStmt
*stmt
, const char *queryString
,
154 ParamListInfo params
, TupOutputState
*tstate
)
156 /* planner will not cope with utility statements */
157 if (query
->commandType
== CMD_UTILITY
)
159 ExplainOneUtility(query
->utilityStmt
, stmt
,
160 queryString
, params
, tstate
);
164 /* if an advisor plugin is present, let it manage things */
165 if (ExplainOneQuery_hook
)
166 (*ExplainOneQuery_hook
) (query
, stmt
, queryString
, params
, tstate
);
172 plan
= pg_plan_query(query
, 0, params
);
174 /* run it (if needed) and produce output */
175 ExplainOnePlan(plan
, stmt
, queryString
, params
, tstate
);
180 * ExplainOneUtility -
181 * print out the execution plan for one utility statement
182 * (In general, utility statements don't have plans, but there are some
183 * we treat as special cases)
185 * This is exported because it's called back from prepare.c in the
186 * EXPLAIN EXECUTE case
189 ExplainOneUtility(Node
*utilityStmt
, ExplainStmt
*stmt
,
190 const char *queryString
, ParamListInfo params
,
191 TupOutputState
*tstate
)
193 if (utilityStmt
== NULL
)
196 if (IsA(utilityStmt
, ExecuteStmt
))
197 ExplainExecuteQuery((ExecuteStmt
*) utilityStmt
, stmt
,
198 queryString
, params
, tstate
);
199 else if (IsA(utilityStmt
, NotifyStmt
))
200 do_text_output_oneline(tstate
, "NOTIFY");
202 do_text_output_oneline(tstate
,
203 "Utility statements have no plan structure");
208 * given a planned query, execute it if needed, and then print
211 * Since we ignore any DeclareCursorStmt that might be attached to the query,
212 * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
213 * query. This is different from pre-8.3 behavior but seems more useful than
214 * not running the query. No cursor will be created, however.
216 * This is exported because it's called back from prepare.c in the
217 * EXPLAIN EXECUTE case, and because an index advisor plugin would need
221 ExplainOnePlan(PlannedStmt
*plannedstmt
, ExplainStmt
*stmt
,
222 const char *queryString
, ParamListInfo params
,
223 TupOutputState
*tstate
)
225 QueryDesc
*queryDesc
;
226 instr_time starttime
;
227 double totaltime
= 0;
232 * Use a snapshot with an updated command ID to ensure this query sees
233 * results of any previously executed queries.
235 PushUpdatedSnapshot(GetActiveSnapshot());
237 /* Create a QueryDesc requesting no output */
238 queryDesc
= CreateQueryDesc(plannedstmt
, queryString
,
239 GetActiveSnapshot(), InvalidSnapshot
,
240 None_Receiver
, params
,
243 INSTR_TIME_SET_CURRENT(starttime
);
245 /* If analyzing, we need to cope with queued triggers */
247 AfterTriggerBeginQuery();
249 /* Select execution options */
251 eflags
= 0; /* default run-to-completion flags */
253 eflags
= EXEC_FLAG_EXPLAIN_ONLY
;
255 /* call ExecutorStart to prepare the plan for execution */
256 ExecutorStart(queryDesc
, eflags
);
258 /* Execute the plan for statistics if asked for */
262 ExecutorRun(queryDesc
, ForwardScanDirection
, 0L);
264 /* We can't clean up 'till we're done printing the stats... */
265 totaltime
+= elapsed_time(&starttime
);
268 /* Create textual dump of plan tree */
269 initStringInfo(&buf
);
270 ExplainPrintPlan(&buf
, queryDesc
, stmt
->analyze
, stmt
->verbose
);
273 * If we ran the command, run any AFTER triggers it queued. (Note this
274 * will not include DEFERRED triggers; since those don't run until end of
275 * transaction, we can't measure them.) Include into total runtime.
279 INSTR_TIME_SET_CURRENT(starttime
);
280 AfterTriggerEndQuery(queryDesc
->estate
);
281 totaltime
+= elapsed_time(&starttime
);
284 /* Print info about runtime of triggers */
287 ResultRelInfo
*rInfo
;
289 int numrels
= queryDesc
->estate
->es_num_result_relations
;
290 List
*targrels
= queryDesc
->estate
->es_trig_target_relations
;
294 show_relname
= (numrels
> 1 || targrels
!= NIL
);
295 rInfo
= queryDesc
->estate
->es_result_relations
;
296 for (nr
= 0; nr
< numrels
; rInfo
++, nr
++)
297 report_triggers(rInfo
, show_relname
, &buf
);
301 rInfo
= (ResultRelInfo
*) lfirst(l
);
302 report_triggers(rInfo
, show_relname
, &buf
);
307 * Close down the query and free resources. Include time for this in the
308 * total runtime (although it should be pretty minimal).
310 INSTR_TIME_SET_CURRENT(starttime
);
312 ExecutorEnd(queryDesc
);
314 FreeQueryDesc(queryDesc
);
318 /* We need a CCI just in case query expanded to multiple plans */
320 CommandCounterIncrement();
322 totaltime
+= elapsed_time(&starttime
);
325 appendStringInfo(&buf
, "Total runtime: %.3f ms\n",
327 do_text_output_multiline(tstate
, buf
.data
);
334 * convert a QueryDesc's plan tree to text and append it to 'str'
336 * 'analyze' means to include runtime instrumentation results
337 * 'verbose' means a verbose printout (currently, it shows targetlists)
339 * NB: will not work on utility statements
342 ExplainPrintPlan(StringInfo str
, QueryDesc
*queryDesc
,
343 bool analyze
, bool verbose
)
347 Assert(queryDesc
->plannedstmt
!= NULL
);
349 memset(&es
, 0, sizeof(es
));
350 es
.printTList
= verbose
;
351 es
.printAnalyze
= analyze
;
352 es
.pstmt
= queryDesc
->plannedstmt
;
353 es
.rtable
= queryDesc
->plannedstmt
->rtable
;
356 queryDesc
->plannedstmt
->planTree
, queryDesc
->planstate
,
362 * report execution stats for a single relation's triggers
365 report_triggers(ResultRelInfo
*rInfo
, bool show_relname
, StringInfo buf
)
369 if (!rInfo
->ri_TrigDesc
|| !rInfo
->ri_TrigInstrument
)
371 for (nt
= 0; nt
< rInfo
->ri_TrigDesc
->numtriggers
; nt
++)
373 Trigger
*trig
= rInfo
->ri_TrigDesc
->triggers
+ nt
;
374 Instrumentation
*instr
= rInfo
->ri_TrigInstrument
+ nt
;
377 /* Must clean up instrumentation state */
381 * We ignore triggers that were never invoked; they likely aren't
382 * relevant to the current query type.
384 if (instr
->ntuples
== 0)
387 if (OidIsValid(trig
->tgconstraint
) &&
388 (conname
= get_constraint_name(trig
->tgconstraint
)) != NULL
)
390 appendStringInfo(buf
, "Trigger for constraint %s", conname
);
394 appendStringInfo(buf
, "Trigger %s", trig
->tgname
);
397 appendStringInfo(buf
, " on %s",
398 RelationGetRelationName(rInfo
->ri_RelationDesc
));
400 appendStringInfo(buf
, ": time=%.3f calls=%.0f\n",
401 1000.0 * instr
->total
, instr
->ntuples
);
405 /* Compute elapsed time in seconds since given timestamp */
407 elapsed_time(instr_time
*starttime
)
411 INSTR_TIME_SET_CURRENT(endtime
);
412 INSTR_TIME_SUBTRACT(endtime
, *starttime
);
413 return INSTR_TIME_GET_DOUBLE(endtime
);
418 * converts a Plan node into ascii string and appends it to 'str'
420 * planstate points to the executor state node corresponding to the plan node.
421 * We need this to get at the instrumentation data (if any) as well as the
424 * outer_plan, if not null, references another plan node that is the outer
425 * side of a join with the current node. This is only interesting for
426 * deciphering runtime keys of an inner indexscan.
429 explain_outNode(StringInfo str
,
430 Plan
*plan
, PlanState
*planstate
,
432 int indent
, ExplainState
*es
)
439 appendStringInfoChar(str
, '\n');
443 switch (nodeTag(plan
))
451 case T_RecursiveUnion
:
452 pname
= "Recursive Union";
461 switch (((NestLoop
*) plan
)->join
.jointype
)
464 pname
= "Nested Loop";
467 pname
= "Nested Loop Left Join";
470 pname
= "Nested Loop Full Join";
473 pname
= "Nested Loop Right Join";
476 pname
= "Nested Loop Semi Join";
479 pname
= "Nested Loop Anti Join";
482 pname
= "Nested Loop ??? Join";
487 switch (((MergeJoin
*) plan
)->join
.jointype
)
490 pname
= "Merge Join";
493 pname
= "Merge Left Join";
496 pname
= "Merge Full Join";
499 pname
= "Merge Right Join";
502 pname
= "Merge Semi Join";
505 pname
= "Merge Anti Join";
508 pname
= "Merge ??? Join";
513 switch (((HashJoin
*) plan
)->join
.jointype
)
519 pname
= "Hash Left Join";
522 pname
= "Hash Full Join";
525 pname
= "Hash Right Join";
528 pname
= "Hash Semi Join";
531 pname
= "Hash Anti Join";
534 pname
= "Hash ??? Join";
542 pname
= "Index Scan";
544 case T_BitmapIndexScan
:
545 pname
= "Bitmap Index Scan";
547 case T_BitmapHeapScan
:
548 pname
= "Bitmap Heap Scan";
554 pname
= "Subquery Scan";
557 pname
= "Function Scan";
560 pname
= "Values Scan";
565 case T_WorkTableScan
:
566 pname
= "WorkTable Scan";
569 pname
= "Materialize";
578 switch (((Agg
*) plan
)->aggstrategy
)
584 pname
= "GroupAggregate";
587 pname
= "HashAggregate";
590 pname
= "Aggregate ???";
601 switch (((SetOp
*) plan
)->strategy
)
604 switch (((SetOp
*) plan
)->cmd
)
606 case SETOPCMD_INTERSECT
:
607 pname
= "SetOp Intersect";
609 case SETOPCMD_INTERSECT_ALL
:
610 pname
= "SetOp Intersect All";
612 case SETOPCMD_EXCEPT
:
613 pname
= "SetOp Except";
615 case SETOPCMD_EXCEPT_ALL
:
616 pname
= "SetOp Except All";
624 switch (((SetOp
*) plan
)->cmd
)
626 case SETOPCMD_INTERSECT
:
627 pname
= "HashSetOp Intersect";
629 case SETOPCMD_INTERSECT_ALL
:
630 pname
= "HashSetOp Intersect All";
632 case SETOPCMD_EXCEPT
:
633 pname
= "HashSetOp Except";
635 case SETOPCMD_EXCEPT_ALL
:
636 pname
= "HashSetOp Except All";
639 pname
= "HashSetOp ???";
659 appendStringInfoString(str
, pname
);
660 switch (nodeTag(plan
))
663 if (ScanDirectionIsBackward(((IndexScan
*) plan
)->indexorderdir
))
664 appendStringInfoString(str
, " Backward");
665 appendStringInfo(str
, " using %s",
666 explain_get_index_name(((IndexScan
*) plan
)->indexid
));
669 case T_BitmapHeapScan
:
671 if (((Scan
*) plan
)->scanrelid
> 0)
673 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
677 /* Assume it's on a real relation */
678 Assert(rte
->rtekind
== RTE_RELATION
);
680 /* We only show the rel name, not schema name */
681 relname
= get_rel_name(rte
->relid
);
683 appendStringInfo(str
, " on %s",
684 quote_identifier(relname
));
685 if (strcmp(rte
->eref
->aliasname
, relname
) != 0)
686 appendStringInfo(str
, " %s",
687 quote_identifier(rte
->eref
->aliasname
));
690 case T_BitmapIndexScan
:
691 appendStringInfo(str
, " on %s",
692 explain_get_index_name(((BitmapIndexScan
*) plan
)->indexid
));
695 if (((Scan
*) plan
)->scanrelid
> 0)
697 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
700 appendStringInfo(str
, " %s",
701 quote_identifier(rte
->eref
->aliasname
));
705 if (((Scan
*) plan
)->scanrelid
> 0)
707 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
712 /* Assert it's on a RangeFunction */
713 Assert(rte
->rtekind
== RTE_FUNCTION
);
716 * If the expression is still a function call, we can get the
717 * real name of the function. Otherwise, punt (this can
718 * happen if the optimizer simplified away the function call,
721 funcexpr
= ((FunctionScan
*) plan
)->funcexpr
;
722 if (funcexpr
&& IsA(funcexpr
, FuncExpr
))
724 Oid funcid
= ((FuncExpr
*) funcexpr
)->funcid
;
726 /* We only show the func name, not schema name */
727 proname
= get_func_name(funcid
);
730 proname
= rte
->eref
->aliasname
;
732 appendStringInfo(str
, " on %s",
733 quote_identifier(proname
));
734 if (strcmp(rte
->eref
->aliasname
, proname
) != 0)
735 appendStringInfo(str
, " %s",
736 quote_identifier(rte
->eref
->aliasname
));
740 if (((Scan
*) plan
)->scanrelid
> 0)
742 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
746 /* Assert it's on a values rte */
747 Assert(rte
->rtekind
== RTE_VALUES
);
749 valsname
= rte
->eref
->aliasname
;
751 appendStringInfo(str
, " on %s",
752 quote_identifier(valsname
));
756 if (((Scan
*) plan
)->scanrelid
> 0)
758 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
761 /* Assert it's on a non-self-reference CTE */
762 Assert(rte
->rtekind
== RTE_CTE
);
763 Assert(!rte
->self_reference
);
765 appendStringInfo(str
, " on %s",
766 quote_identifier(rte
->ctename
));
767 if (strcmp(rte
->eref
->aliasname
, rte
->ctename
) != 0)
768 appendStringInfo(str
, " %s",
769 quote_identifier(rte
->eref
->aliasname
));
772 case T_WorkTableScan
:
773 if (((Scan
*) plan
)->scanrelid
> 0)
775 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
778 /* Assert it's on a self-reference CTE */
779 Assert(rte
->rtekind
== RTE_CTE
);
780 Assert(rte
->self_reference
);
782 appendStringInfo(str
, " on %s",
783 quote_identifier(rte
->ctename
));
784 if (strcmp(rte
->eref
->aliasname
, rte
->ctename
) != 0)
785 appendStringInfo(str
, " %s",
786 quote_identifier(rte
->eref
->aliasname
));
793 appendStringInfo(str
, " (cost=%.2f..%.2f rows=%.0f width=%d)",
794 plan
->startup_cost
, plan
->total_cost
,
795 plan
->plan_rows
, plan
->plan_width
);
798 * We have to forcibly clean up the instrumentation state because we
799 * haven't done ExecutorEnd yet. This is pretty grotty ...
801 if (planstate
->instrument
)
802 InstrEndLoop(planstate
->instrument
);
804 if (planstate
->instrument
&& planstate
->instrument
->nloops
> 0)
806 double nloops
= planstate
->instrument
->nloops
;
808 appendStringInfo(str
, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
809 1000.0 * planstate
->instrument
->startup
/ nloops
,
810 1000.0 * planstate
->instrument
->total
/ nloops
,
811 planstate
->instrument
->ntuples
/ nloops
,
812 planstate
->instrument
->nloops
);
814 else if (es
->printAnalyze
)
815 appendStringInfo(str
, " (never executed)");
816 appendStringInfoChar(str
, '\n');
820 show_plan_tlist(plan
, str
, indent
, es
);
822 /* quals, sort keys, etc */
823 switch (nodeTag(plan
))
826 show_scan_qual(((IndexScan
*) plan
)->indexqualorig
,
828 ((Scan
*) plan
)->scanrelid
,
831 show_scan_qual(plan
->qual
,
833 ((Scan
*) plan
)->scanrelid
,
837 case T_BitmapIndexScan
:
838 show_scan_qual(((BitmapIndexScan
*) plan
)->indexqualorig
,
840 ((Scan
*) plan
)->scanrelid
,
844 case T_BitmapHeapScan
:
845 /* XXX do we want to show this in production? */
846 show_scan_qual(((BitmapHeapScan
*) plan
)->bitmapqualorig
,
848 ((Scan
*) plan
)->scanrelid
,
856 case T_WorkTableScan
:
857 show_scan_qual(plan
->qual
,
859 ((Scan
*) plan
)->scanrelid
,
864 show_scan_qual(plan
->qual
,
866 ((Scan
*) plan
)->scanrelid
,
873 * The tidquals list has OR semantics, so be sure to show it
874 * as an OR condition.
876 List
*tidquals
= ((TidScan
*) plan
)->tidquals
;
878 if (list_length(tidquals
) > 1)
879 tidquals
= list_make1(make_orclause(tidquals
));
880 show_scan_qual(tidquals
,
882 ((Scan
*) plan
)->scanrelid
,
885 show_scan_qual(plan
->qual
,
887 ((Scan
*) plan
)->scanrelid
,
893 show_upper_qual(((NestLoop
*) plan
)->join
.joinqual
,
896 show_upper_qual(plan
->qual
,
901 show_upper_qual(((MergeJoin
*) plan
)->mergeclauses
,
904 show_upper_qual(((MergeJoin
*) plan
)->join
.joinqual
,
907 show_upper_qual(plan
->qual
,
912 show_upper_qual(((HashJoin
*) plan
)->hashclauses
,
915 show_upper_qual(((HashJoin
*) plan
)->join
.joinqual
,
918 show_upper_qual(plan
->qual
,
924 show_upper_qual(plan
->qual
,
930 ((Sort
*) plan
)->numCols
,
931 ((Sort
*) plan
)->sortColIdx
,
934 show_sort_info((SortState
*) planstate
,
938 show_upper_qual((List
*) ((Result
*) plan
)->resconstantqual
,
939 "One-Time Filter", plan
,
941 show_upper_qual(plan
->qual
,
954 foreach(lst
, planstate
->initPlan
)
956 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
957 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
959 for (i
= 0; i
< indent
; i
++)
960 appendStringInfo(str
, " ");
961 appendStringInfo(str
, " %s\n", sp
->plan_name
);
962 for (i
= 0; i
< indent
; i
++)
963 appendStringInfo(str
, " ");
964 appendStringInfo(str
, " -> ");
966 exec_subplan_get_plan(es
->pstmt
, sp
),
976 for (i
= 0; i
< indent
; i
++)
977 appendStringInfo(str
, " ");
978 appendStringInfo(str
, " -> ");
981 * Ordinarily we don't pass down our own outer_plan value to our child
982 * nodes, but in bitmap scan trees we must, since the bottom
983 * BitmapIndexScan nodes may have outer references.
985 explain_outNode(str
, outerPlan(plan
),
986 outerPlanState(planstate
),
987 IsA(plan
, BitmapHeapScan
) ? outer_plan
: NULL
,
994 for (i
= 0; i
< indent
; i
++)
995 appendStringInfo(str
, " ");
996 appendStringInfo(str
, " -> ");
997 explain_outNode(str
, innerPlan(plan
),
998 innerPlanState(planstate
),
1003 if (IsA(plan
, Append
))
1005 Append
*appendplan
= (Append
*) plan
;
1006 AppendState
*appendstate
= (AppendState
*) planstate
;
1011 foreach(lst
, appendplan
->appendplans
)
1013 Plan
*subnode
= (Plan
*) lfirst(lst
);
1015 for (i
= 0; i
< indent
; i
++)
1016 appendStringInfo(str
, " ");
1017 appendStringInfo(str
, " -> ");
1020 * Ordinarily we don't pass down our own outer_plan value to our
1021 * child nodes, but in an Append we must, since we might be
1022 * looking at an appendrel indexscan with outer references from
1025 explain_outNode(str
, subnode
,
1026 appendstate
->appendplans
[j
],
1033 if (IsA(plan
, BitmapAnd
))
1035 BitmapAnd
*bitmapandplan
= (BitmapAnd
*) plan
;
1036 BitmapAndState
*bitmapandstate
= (BitmapAndState
*) planstate
;
1041 foreach(lst
, bitmapandplan
->bitmapplans
)
1043 Plan
*subnode
= (Plan
*) lfirst(lst
);
1045 for (i
= 0; i
< indent
; i
++)
1046 appendStringInfo(str
, " ");
1047 appendStringInfo(str
, " -> ");
1049 explain_outNode(str
, subnode
,
1050 bitmapandstate
->bitmapplans
[j
],
1051 outer_plan
, /* pass down same outer plan */
1057 if (IsA(plan
, BitmapOr
))
1059 BitmapOr
*bitmaporplan
= (BitmapOr
*) plan
;
1060 BitmapOrState
*bitmaporstate
= (BitmapOrState
*) planstate
;
1065 foreach(lst
, bitmaporplan
->bitmapplans
)
1067 Plan
*subnode
= (Plan
*) lfirst(lst
);
1069 for (i
= 0; i
< indent
; i
++)
1070 appendStringInfo(str
, " ");
1071 appendStringInfo(str
, " -> ");
1073 explain_outNode(str
, subnode
,
1074 bitmaporstate
->bitmapplans
[j
],
1075 outer_plan
, /* pass down same outer plan */
1081 if (IsA(plan
, SubqueryScan
))
1083 SubqueryScan
*subqueryscan
= (SubqueryScan
*) plan
;
1084 SubqueryScanState
*subquerystate
= (SubqueryScanState
*) planstate
;
1085 Plan
*subnode
= subqueryscan
->subplan
;
1087 for (i
= 0; i
< indent
; i
++)
1088 appendStringInfo(str
, " ");
1089 appendStringInfo(str
, " -> ");
1091 explain_outNode(str
, subnode
,
1092 subquerystate
->subplan
,
1098 if (planstate
->subPlan
)
1102 foreach(lst
, planstate
->subPlan
)
1104 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
1105 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
1107 for (i
= 0; i
< indent
; i
++)
1108 appendStringInfo(str
, " ");
1109 appendStringInfo(str
, " %s\n", sp
->plan_name
);
1110 for (i
= 0; i
< indent
; i
++)
1111 appendStringInfo(str
, " ");
1112 appendStringInfo(str
, " -> ");
1113 explain_outNode(str
,
1114 exec_subplan_get_plan(es
->pstmt
, sp
),
1123 * Show the targetlist of a plan node
1126 show_plan_tlist(Plan
*plan
,
1127 StringInfo str
, int indent
, ExplainState
*es
)
1134 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1135 if (plan
->targetlist
== NIL
)
1137 /* The tlist of an Append isn't real helpful, so suppress it */
1138 if (IsA(plan
, Append
))
1140 /* Likewise for RecursiveUnion */
1141 if (IsA(plan
, RecursiveUnion
))
1144 /* Set up deparsing context */
1145 context
= deparse_context_for_plan((Node
*) plan
,
1148 es
->pstmt
->subplans
);
1149 useprefix
= list_length(es
->rtable
) > 1;
1151 /* Emit line prefix */
1152 for (i
= 0; i
< indent
; i
++)
1153 appendStringInfo(str
, " ");
1154 appendStringInfo(str
, " Output: ");
1156 /* Deparse each non-junk result column */
1158 foreach(lc
, plan
->targetlist
)
1160 TargetEntry
*tle
= (TargetEntry
*) lfirst(lc
);
1165 appendStringInfo(str
, ", ");
1166 appendStringInfoString(str
,
1167 deparse_expression((Node
*) tle
->expr
, context
,
1171 appendStringInfoChar(str
, '\n');
1175 * Show a qualifier expression for a scan plan node
1177 * Note: outer_plan is the referent for any OUTER vars in the scan qual;
1178 * this would be the outer side of a nestloop plan. Pass NULL if none.
1181 show_scan_qual(List
*qual
, const char *qlabel
,
1182 int scanrelid
, Plan
*scan_plan
, Plan
*outer_plan
,
1183 StringInfo str
, int indent
, ExplainState
*es
)
1191 /* No work if empty qual */
1195 /* Convert AND list to explicit AND */
1196 node
= (Node
*) make_ands_explicit(qual
);
1198 /* Set up deparsing context */
1199 context
= deparse_context_for_plan((Node
*) scan_plan
,
1200 (Node
*) outer_plan
,
1202 es
->pstmt
->subplans
);
1203 useprefix
= (outer_plan
!= NULL
|| IsA(scan_plan
, SubqueryScan
));
1205 /* Deparse the expression */
1206 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1208 /* And add to str */
1209 for (i
= 0; i
< indent
; i
++)
1210 appendStringInfo(str
, " ");
1211 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1215 * Show a qualifier expression for an upper-level plan node
1218 show_upper_qual(List
*qual
, const char *qlabel
, Plan
*plan
,
1219 StringInfo str
, int indent
, ExplainState
*es
)
1227 /* No work if empty qual */
1231 /* Set up deparsing context */
1232 context
= deparse_context_for_plan((Node
*) plan
,
1235 es
->pstmt
->subplans
);
1236 useprefix
= list_length(es
->rtable
) > 1;
1238 /* Deparse the expression */
1239 node
= (Node
*) make_ands_explicit(qual
);
1240 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1242 /* And add to str */
1243 for (i
= 0; i
< indent
; i
++)
1244 appendStringInfo(str
, " ");
1245 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1249 * Show the sort keys for a Sort node.
1252 show_sort_keys(Plan
*sortplan
, int nkeys
, AttrNumber
*keycols
,
1254 StringInfo str
, int indent
, ExplainState
*es
)
1265 for (i
= 0; i
< indent
; i
++)
1266 appendStringInfo(str
, " ");
1267 appendStringInfo(str
, " %s: ", qlabel
);
1269 /* Set up deparsing context */
1270 context
= deparse_context_for_plan((Node
*) sortplan
,
1273 es
->pstmt
->subplans
);
1274 useprefix
= list_length(es
->rtable
) > 1;
1276 for (keyno
= 0; keyno
< nkeys
; keyno
++)
1278 /* find key expression in tlist */
1279 AttrNumber keyresno
= keycols
[keyno
];
1280 TargetEntry
*target
= get_tle_by_resno(sortplan
->targetlist
, keyresno
);
1283 elog(ERROR
, "no tlist entry for key %d", keyresno
);
1284 /* Deparse the expression, showing any top-level cast */
1285 exprstr
= deparse_expression((Node
*) target
->expr
, context
,
1287 /* And add to str */
1289 appendStringInfo(str
, ", ");
1290 appendStringInfoString(str
, exprstr
);
1293 appendStringInfo(str
, "\n");
1297 * If it's EXPLAIN ANALYZE, show tuplesort explain info for a sort node
1300 show_sort_info(SortState
*sortstate
,
1301 StringInfo str
, int indent
, ExplainState
*es
)
1303 Assert(IsA(sortstate
, SortState
));
1304 if (es
->printAnalyze
&& sortstate
->sort_Done
&&
1305 sortstate
->tuplesortstate
!= NULL
)
1310 sortinfo
= tuplesort_explain((Tuplesortstate
*) sortstate
->tuplesortstate
);
1311 for (i
= 0; i
< indent
; i
++)
1312 appendStringInfo(str
, " ");
1313 appendStringInfo(str
, " %s\n", sortinfo
);
1319 * Fetch the name of an index in an EXPLAIN
1321 * We allow plugins to get control here so that plans involving hypothetical
1322 * indexes can be explained.
1325 explain_get_index_name(Oid indexId
)
1329 if (explain_get_index_name_hook
)
1330 result
= (*explain_get_index_name_hook
) (indexId
);
1335 /* default behavior: look in the catalogs and quote it */
1336 result
= get_rel_name(indexId
);
1338 elog(ERROR
, "cache lookup failed for index %u", indexId
);
1339 result
= quote_identifier(result
);