1 /*-------------------------------------------------------------------------
4 * Explain query execution plans
6 * Portions Copyright (c) 1996-2008, 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
*outer_plan
, Plan
*inner_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
, params
, stmt
, 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
, ParamListInfo params
,
222 ExplainStmt
*stmt
, TupOutputState
*tstate
)
224 QueryDesc
*queryDesc
;
225 instr_time starttime
;
226 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
,
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 es
= (ExplainState
*) palloc0(sizeof(ExplainState
));
270 es
->printTList
= stmt
->verbose
;
271 es
->printAnalyze
= stmt
->analyze
;
272 es
->pstmt
= queryDesc
->plannedstmt
;
273 es
->rtable
= queryDesc
->plannedstmt
->rtable
;
275 initStringInfo(&buf
);
276 explain_outNode(&buf
,
277 queryDesc
->plannedstmt
->planTree
, queryDesc
->planstate
,
281 * If we ran the command, run any AFTER triggers it queued. (Note this
282 * will not include DEFERRED triggers; since those don't run until end of
283 * transaction, we can't measure them.) Include into total runtime.
287 INSTR_TIME_SET_CURRENT(starttime
);
288 AfterTriggerEndQuery(queryDesc
->estate
);
289 totaltime
+= elapsed_time(&starttime
);
292 /* Print info about runtime of triggers */
293 if (es
->printAnalyze
)
295 ResultRelInfo
*rInfo
;
297 int numrels
= queryDesc
->estate
->es_num_result_relations
;
298 List
*targrels
= queryDesc
->estate
->es_trig_target_relations
;
302 show_relname
= (numrels
> 1 || targrels
!= NIL
);
303 rInfo
= queryDesc
->estate
->es_result_relations
;
304 for (nr
= 0; nr
< numrels
; rInfo
++, nr
++)
305 report_triggers(rInfo
, show_relname
, &buf
);
309 rInfo
= (ResultRelInfo
*) lfirst(l
);
310 report_triggers(rInfo
, show_relname
, &buf
);
315 * Close down the query and free resources. Include time for this in the
316 * total runtime (although it should be pretty minimal).
318 INSTR_TIME_SET_CURRENT(starttime
);
320 ExecutorEnd(queryDesc
);
322 FreeQueryDesc(queryDesc
);
326 /* We need a CCI just in case query expanded to multiple plans */
328 CommandCounterIncrement();
330 totaltime
+= elapsed_time(&starttime
);
333 appendStringInfo(&buf
, "Total runtime: %.3f ms\n",
335 do_text_output_multiline(tstate
, buf
.data
);
343 * report execution stats for a single relation's triggers
346 report_triggers(ResultRelInfo
*rInfo
, bool show_relname
, StringInfo buf
)
350 if (!rInfo
->ri_TrigDesc
|| !rInfo
->ri_TrigInstrument
)
352 for (nt
= 0; nt
< rInfo
->ri_TrigDesc
->numtriggers
; nt
++)
354 Trigger
*trig
= rInfo
->ri_TrigDesc
->triggers
+ nt
;
355 Instrumentation
*instr
= rInfo
->ri_TrigInstrument
+ nt
;
358 /* Must clean up instrumentation state */
362 * We ignore triggers that were never invoked; they likely aren't
363 * relevant to the current query type.
365 if (instr
->ntuples
== 0)
368 if (OidIsValid(trig
->tgconstraint
) &&
369 (conname
= get_constraint_name(trig
->tgconstraint
)) != NULL
)
371 appendStringInfo(buf
, "Trigger for constraint %s", conname
);
375 appendStringInfo(buf
, "Trigger %s", trig
->tgname
);
378 appendStringInfo(buf
, " on %s",
379 RelationGetRelationName(rInfo
->ri_RelationDesc
));
381 appendStringInfo(buf
, ": time=%.3f calls=%.0f\n",
382 1000.0 * instr
->total
, instr
->ntuples
);
386 /* Compute elapsed time in seconds since given timestamp */
388 elapsed_time(instr_time
*starttime
)
392 INSTR_TIME_SET_CURRENT(endtime
);
393 INSTR_TIME_SUBTRACT(endtime
, *starttime
);
394 return INSTR_TIME_GET_DOUBLE(endtime
);
399 * converts a Plan node into ascii string and appends it to 'str'
401 * planstate points to the executor state node corresponding to the plan node.
402 * We need this to get at the instrumentation data (if any) as well as the
405 * outer_plan, if not null, references another plan node that is the outer
406 * side of a join with the current node. This is only interesting for
407 * deciphering runtime keys of an inner indexscan.
410 explain_outNode(StringInfo str
,
411 Plan
*plan
, PlanState
*planstate
,
413 int indent
, ExplainState
*es
)
420 appendStringInfoChar(str
, '\n');
424 switch (nodeTag(plan
))
439 switch (((NestLoop
*) plan
)->join
.jointype
)
442 pname
= "Nested Loop";
445 pname
= "Nested Loop Left Join";
448 pname
= "Nested Loop Full Join";
451 pname
= "Nested Loop Right Join";
454 pname
= "Nested Loop Semi Join";
457 pname
= "Nested Loop Anti Join";
460 pname
= "Nested Loop ??? Join";
465 switch (((MergeJoin
*) plan
)->join
.jointype
)
468 pname
= "Merge Join";
471 pname
= "Merge Left Join";
474 pname
= "Merge Full Join";
477 pname
= "Merge Right Join";
480 pname
= "Merge Semi Join";
483 pname
= "Merge Anti Join";
486 pname
= "Merge ??? Join";
491 switch (((HashJoin
*) plan
)->join
.jointype
)
497 pname
= "Hash Left Join";
500 pname
= "Hash Full Join";
503 pname
= "Hash Right Join";
506 pname
= "Hash Semi Join";
509 pname
= "Hash Anti Join";
512 pname
= "Hash ??? Join";
520 pname
= "Index Scan";
522 case T_BitmapIndexScan
:
523 pname
= "Bitmap Index Scan";
525 case T_BitmapHeapScan
:
526 pname
= "Bitmap Heap Scan";
532 pname
= "Subquery Scan";
535 pname
= "Function Scan";
538 pname
= "Values Scan";
541 pname
= "Materialize";
550 switch (((Agg
*) plan
)->aggstrategy
)
556 pname
= "GroupAggregate";
559 pname
= "HashAggregate";
562 pname
= "Aggregate ???";
570 switch (((SetOp
*) plan
)->strategy
)
573 switch (((SetOp
*) plan
)->cmd
)
575 case SETOPCMD_INTERSECT
:
576 pname
= "SetOp Intersect";
578 case SETOPCMD_INTERSECT_ALL
:
579 pname
= "SetOp Intersect All";
581 case SETOPCMD_EXCEPT
:
582 pname
= "SetOp Except";
584 case SETOPCMD_EXCEPT_ALL
:
585 pname
= "SetOp Except All";
593 switch (((SetOp
*) plan
)->cmd
)
595 case SETOPCMD_INTERSECT
:
596 pname
= "HashSetOp Intersect";
598 case SETOPCMD_INTERSECT_ALL
:
599 pname
= "HashSetOp Intersect All";
601 case SETOPCMD_EXCEPT
:
602 pname
= "HashSetOp Except";
604 case SETOPCMD_EXCEPT_ALL
:
605 pname
= "HashSetOp Except All";
608 pname
= "HashSetOp ???";
628 appendStringInfoString(str
, pname
);
629 switch (nodeTag(plan
))
632 if (ScanDirectionIsBackward(((IndexScan
*) plan
)->indexorderdir
))
633 appendStringInfoString(str
, " Backward");
634 appendStringInfo(str
, " using %s",
635 explain_get_index_name(((IndexScan
*) plan
)->indexid
));
638 case T_BitmapHeapScan
:
640 if (((Scan
*) plan
)->scanrelid
> 0)
642 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
646 /* Assume it's on a real relation */
647 Assert(rte
->rtekind
== RTE_RELATION
);
649 /* We only show the rel name, not schema name */
650 relname
= get_rel_name(rte
->relid
);
652 appendStringInfo(str
, " on %s",
653 quote_identifier(relname
));
654 if (strcmp(rte
->eref
->aliasname
, relname
) != 0)
655 appendStringInfo(str
, " %s",
656 quote_identifier(rte
->eref
->aliasname
));
659 case T_BitmapIndexScan
:
660 appendStringInfo(str
, " on %s",
661 explain_get_index_name(((BitmapIndexScan
*) plan
)->indexid
));
664 if (((Scan
*) plan
)->scanrelid
> 0)
666 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
669 appendStringInfo(str
, " %s",
670 quote_identifier(rte
->eref
->aliasname
));
674 if (((Scan
*) plan
)->scanrelid
> 0)
676 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
681 /* Assert it's on a RangeFunction */
682 Assert(rte
->rtekind
== RTE_FUNCTION
);
685 * If the expression is still a function call, we can get the
686 * real name of the function. Otherwise, punt (this can
687 * happen if the optimizer simplified away the function call,
690 funcexpr
= ((FunctionScan
*) plan
)->funcexpr
;
691 if (funcexpr
&& IsA(funcexpr
, FuncExpr
))
693 Oid funcid
= ((FuncExpr
*) funcexpr
)->funcid
;
695 /* We only show the func name, not schema name */
696 proname
= get_func_name(funcid
);
699 proname
= rte
->eref
->aliasname
;
701 appendStringInfo(str
, " on %s",
702 quote_identifier(proname
));
703 if (strcmp(rte
->eref
->aliasname
, proname
) != 0)
704 appendStringInfo(str
, " %s",
705 quote_identifier(rte
->eref
->aliasname
));
709 if (((Scan
*) plan
)->scanrelid
> 0)
711 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
715 /* Assert it's on a values rte */
716 Assert(rte
->rtekind
== RTE_VALUES
);
718 valsname
= rte
->eref
->aliasname
;
720 appendStringInfo(str
, " on %s",
721 quote_identifier(valsname
));
728 appendStringInfo(str
, " (cost=%.2f..%.2f rows=%.0f width=%d)",
729 plan
->startup_cost
, plan
->total_cost
,
730 plan
->plan_rows
, plan
->plan_width
);
733 * We have to forcibly clean up the instrumentation state because we
734 * haven't done ExecutorEnd yet. This is pretty grotty ...
736 if (planstate
->instrument
)
737 InstrEndLoop(planstate
->instrument
);
739 if (planstate
->instrument
&& planstate
->instrument
->nloops
> 0)
741 double nloops
= planstate
->instrument
->nloops
;
743 appendStringInfo(str
, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
744 1000.0 * planstate
->instrument
->startup
/ nloops
,
745 1000.0 * planstate
->instrument
->total
/ nloops
,
746 planstate
->instrument
->ntuples
/ nloops
,
747 planstate
->instrument
->nloops
);
749 else if (es
->printAnalyze
)
750 appendStringInfo(str
, " (never executed)");
751 appendStringInfoChar(str
, '\n');
755 show_plan_tlist(plan
, str
, indent
, es
);
757 /* quals, sort keys, etc */
758 switch (nodeTag(plan
))
761 show_scan_qual(((IndexScan
*) plan
)->indexqualorig
,
763 ((Scan
*) plan
)->scanrelid
,
766 show_scan_qual(plan
->qual
,
768 ((Scan
*) plan
)->scanrelid
,
772 case T_BitmapIndexScan
:
773 show_scan_qual(((BitmapIndexScan
*) plan
)->indexqualorig
,
775 ((Scan
*) plan
)->scanrelid
,
779 case T_BitmapHeapScan
:
780 /* XXX do we want to show this in production? */
781 show_scan_qual(((BitmapHeapScan
*) plan
)->bitmapqualorig
,
783 ((Scan
*) plan
)->scanrelid
,
790 show_scan_qual(plan
->qual
,
792 ((Scan
*) plan
)->scanrelid
,
797 show_scan_qual(plan
->qual
,
799 ((Scan
*) plan
)->scanrelid
,
801 ((SubqueryScan
*) plan
)->subplan
,
807 * The tidquals list has OR semantics, so be sure to show it
808 * as an OR condition.
810 List
*tidquals
= ((TidScan
*) plan
)->tidquals
;
812 if (list_length(tidquals
) > 1)
813 tidquals
= list_make1(make_orclause(tidquals
));
814 show_scan_qual(tidquals
,
816 ((Scan
*) plan
)->scanrelid
,
819 show_scan_qual(plan
->qual
,
821 ((Scan
*) plan
)->scanrelid
,
827 show_upper_qual(((NestLoop
*) plan
)->join
.joinqual
,
830 show_upper_qual(plan
->qual
,
835 show_upper_qual(((MergeJoin
*) plan
)->mergeclauses
,
838 show_upper_qual(((MergeJoin
*) plan
)->join
.joinqual
,
841 show_upper_qual(plan
->qual
,
846 show_upper_qual(((HashJoin
*) plan
)->hashclauses
,
849 show_upper_qual(((HashJoin
*) plan
)->join
.joinqual
,
852 show_upper_qual(plan
->qual
,
858 show_upper_qual(plan
->qual
,
864 ((Sort
*) plan
)->numCols
,
865 ((Sort
*) plan
)->sortColIdx
,
868 show_sort_info((SortState
*) planstate
,
872 show_upper_qual((List
*) ((Result
*) plan
)->resconstantqual
,
873 "One-Time Filter", plan
,
875 show_upper_qual(plan
->qual
,
888 for (i
= 0; i
< indent
; i
++)
889 appendStringInfo(str
, " ");
890 appendStringInfo(str
, " InitPlan\n");
891 foreach(lst
, planstate
->initPlan
)
893 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
894 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
896 for (i
= 0; i
< indent
; i
++)
897 appendStringInfo(str
, " ");
898 appendStringInfo(str
, " -> ");
900 exec_subplan_get_plan(es
->pstmt
, sp
),
910 for (i
= 0; i
< indent
; i
++)
911 appendStringInfo(str
, " ");
912 appendStringInfo(str
, " -> ");
915 * Ordinarily we don't pass down our own outer_plan value to our child
916 * nodes, but in bitmap scan trees we must, since the bottom
917 * BitmapIndexScan nodes may have outer references.
919 explain_outNode(str
, outerPlan(plan
),
920 outerPlanState(planstate
),
921 IsA(plan
, BitmapHeapScan
) ? outer_plan
: NULL
,
928 for (i
= 0; i
< indent
; i
++)
929 appendStringInfo(str
, " ");
930 appendStringInfo(str
, " -> ");
931 explain_outNode(str
, innerPlan(plan
),
932 innerPlanState(planstate
),
937 if (IsA(plan
, Append
))
939 Append
*appendplan
= (Append
*) plan
;
940 AppendState
*appendstate
= (AppendState
*) planstate
;
945 foreach(lst
, appendplan
->appendplans
)
947 Plan
*subnode
= (Plan
*) lfirst(lst
);
949 for (i
= 0; i
< indent
; i
++)
950 appendStringInfo(str
, " ");
951 appendStringInfo(str
, " -> ");
954 * Ordinarily we don't pass down our own outer_plan value to our
955 * child nodes, but in an Append we must, since we might be
956 * looking at an appendrel indexscan with outer references from
959 explain_outNode(str
, subnode
,
960 appendstate
->appendplans
[j
],
967 if (IsA(plan
, BitmapAnd
))
969 BitmapAnd
*bitmapandplan
= (BitmapAnd
*) plan
;
970 BitmapAndState
*bitmapandstate
= (BitmapAndState
*) planstate
;
975 foreach(lst
, bitmapandplan
->bitmapplans
)
977 Plan
*subnode
= (Plan
*) lfirst(lst
);
979 for (i
= 0; i
< indent
; i
++)
980 appendStringInfo(str
, " ");
981 appendStringInfo(str
, " -> ");
983 explain_outNode(str
, subnode
,
984 bitmapandstate
->bitmapplans
[j
],
985 outer_plan
, /* pass down same outer plan */
991 if (IsA(plan
, BitmapOr
))
993 BitmapOr
*bitmaporplan
= (BitmapOr
*) plan
;
994 BitmapOrState
*bitmaporstate
= (BitmapOrState
*) planstate
;
999 foreach(lst
, bitmaporplan
->bitmapplans
)
1001 Plan
*subnode
= (Plan
*) lfirst(lst
);
1003 for (i
= 0; i
< indent
; i
++)
1004 appendStringInfo(str
, " ");
1005 appendStringInfo(str
, " -> ");
1007 explain_outNode(str
, subnode
,
1008 bitmaporstate
->bitmapplans
[j
],
1009 outer_plan
, /* pass down same outer plan */
1015 if (IsA(plan
, SubqueryScan
))
1017 SubqueryScan
*subqueryscan
= (SubqueryScan
*) plan
;
1018 SubqueryScanState
*subquerystate
= (SubqueryScanState
*) planstate
;
1019 Plan
*subnode
= subqueryscan
->subplan
;
1021 for (i
= 0; i
< indent
; i
++)
1022 appendStringInfo(str
, " ");
1023 appendStringInfo(str
, " -> ");
1025 explain_outNode(str
, subnode
,
1026 subquerystate
->subplan
,
1032 if (planstate
->subPlan
)
1036 for (i
= 0; i
< indent
; i
++)
1037 appendStringInfo(str
, " ");
1038 appendStringInfo(str
, " SubPlan\n");
1039 foreach(lst
, planstate
->subPlan
)
1041 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
1042 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
1044 for (i
= 0; i
< indent
; i
++)
1045 appendStringInfo(str
, " ");
1046 appendStringInfo(str
, " -> ");
1047 explain_outNode(str
,
1048 exec_subplan_get_plan(es
->pstmt
, sp
),
1057 * Show the targetlist of a plan node
1060 show_plan_tlist(Plan
*plan
,
1061 StringInfo str
, int indent
, ExplainState
*es
)
1068 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1069 if (plan
->targetlist
== NIL
)
1071 /* The tlist of an Append isn't real helpful, so suppress it */
1072 if (IsA(plan
, Append
))
1075 /* Set up deparsing context */
1076 context
= deparse_context_for_plan((Node
*) outerPlan(plan
),
1077 (Node
*) innerPlan(plan
),
1079 useprefix
= list_length(es
->rtable
) > 1;
1081 /* Emit line prefix */
1082 for (i
= 0; i
< indent
; i
++)
1083 appendStringInfo(str
, " ");
1084 appendStringInfo(str
, " Output: ");
1086 /* Deparse each non-junk result column */
1088 foreach(lc
, plan
->targetlist
)
1090 TargetEntry
*tle
= (TargetEntry
*) lfirst(lc
);
1095 appendStringInfo(str
, ", ");
1096 appendStringInfoString(str
,
1097 deparse_expression((Node
*) tle
->expr
, context
,
1101 appendStringInfoChar(str
, '\n');
1105 * Show a qualifier expression for a scan plan node
1107 * Note: outer_plan is the referent for any OUTER vars in the scan qual;
1108 * this would be the outer side of a nestloop plan. inner_plan should be
1109 * NULL except for a SubqueryScan plan node, where it should be the subplan.
1112 show_scan_qual(List
*qual
, const char *qlabel
,
1113 int scanrelid
, Plan
*outer_plan
, Plan
*inner_plan
,
1114 StringInfo str
, int indent
, ExplainState
*es
)
1122 /* No work if empty qual */
1126 /* Convert AND list to explicit AND */
1127 node
= (Node
*) make_ands_explicit(qual
);
1129 /* Set up deparsing context */
1130 context
= deparse_context_for_plan((Node
*) outer_plan
,
1131 (Node
*) inner_plan
,
1133 useprefix
= (outer_plan
!= NULL
|| inner_plan
!= NULL
);
1135 /* Deparse the expression */
1136 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1138 /* And add to str */
1139 for (i
= 0; i
< indent
; i
++)
1140 appendStringInfo(str
, " ");
1141 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1145 * Show a qualifier expression for an upper-level plan node
1148 show_upper_qual(List
*qual
, const char *qlabel
, Plan
*plan
,
1149 StringInfo str
, int indent
, ExplainState
*es
)
1157 /* No work if empty qual */
1161 /* Set up deparsing context */
1162 context
= deparse_context_for_plan((Node
*) outerPlan(plan
),
1163 (Node
*) innerPlan(plan
),
1165 useprefix
= list_length(es
->rtable
) > 1;
1167 /* Deparse the expression */
1168 node
= (Node
*) make_ands_explicit(qual
);
1169 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1171 /* And add to str */
1172 for (i
= 0; i
< indent
; i
++)
1173 appendStringInfo(str
, " ");
1174 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1178 * Show the sort keys for a Sort node.
1181 show_sort_keys(Plan
*sortplan
, int nkeys
, AttrNumber
*keycols
,
1183 StringInfo str
, int indent
, ExplainState
*es
)
1194 for (i
= 0; i
< indent
; i
++)
1195 appendStringInfo(str
, " ");
1196 appendStringInfo(str
, " %s: ", qlabel
);
1198 /* Set up deparsing context */
1199 context
= deparse_context_for_plan((Node
*) outerPlan(sortplan
),
1200 NULL
, /* Sort has no innerPlan */
1202 useprefix
= list_length(es
->rtable
) > 1;
1204 for (keyno
= 0; keyno
< nkeys
; keyno
++)
1206 /* find key expression in tlist */
1207 AttrNumber keyresno
= keycols
[keyno
];
1208 TargetEntry
*target
= get_tle_by_resno(sortplan
->targetlist
, keyresno
);
1211 elog(ERROR
, "no tlist entry for key %d", keyresno
);
1212 /* Deparse the expression, showing any top-level cast */
1213 exprstr
= deparse_expression((Node
*) target
->expr
, context
,
1215 /* And add to str */
1217 appendStringInfo(str
, ", ");
1218 appendStringInfoString(str
, exprstr
);
1221 appendStringInfo(str
, "\n");
1225 * If it's EXPLAIN ANALYZE, show tuplesort explain info for a sort node
1228 show_sort_info(SortState
*sortstate
,
1229 StringInfo str
, int indent
, ExplainState
*es
)
1231 Assert(IsA(sortstate
, SortState
));
1232 if (es
->printAnalyze
&& sortstate
->sort_Done
&&
1233 sortstate
->tuplesortstate
!= NULL
)
1238 sortinfo
= tuplesort_explain((Tuplesortstate
*) sortstate
->tuplesortstate
);
1239 for (i
= 0; i
< indent
; i
++)
1240 appendStringInfo(str
, " ");
1241 appendStringInfo(str
, " %s\n", sortinfo
);
1247 * Fetch the name of an index in an EXPLAIN
1249 * We allow plugins to get control here so that plans involving hypothetical
1250 * indexes can be explained.
1253 explain_get_index_name(Oid indexId
)
1257 if (explain_get_index_name_hook
)
1258 result
= (*explain_get_index_name_hook
) (indexId
);
1263 /* default behavior: look in the catalogs and quote it */
1264 result
= get_rel_name(indexId
);
1266 elog(ERROR
, "cache lookup failed for index %u", indexId
);
1267 result
= quote_identifier(result
);