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
*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
, 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
))
432 case T_RecursiveUnion
:
433 pname
= "Recursive Union";
442 switch (((NestLoop
*) plan
)->join
.jointype
)
445 pname
= "Nested Loop";
448 pname
= "Nested Loop Left Join";
451 pname
= "Nested Loop Full Join";
454 pname
= "Nested Loop Right Join";
457 pname
= "Nested Loop Semi Join";
460 pname
= "Nested Loop Anti Join";
463 pname
= "Nested Loop ??? Join";
468 switch (((MergeJoin
*) plan
)->join
.jointype
)
471 pname
= "Merge Join";
474 pname
= "Merge Left Join";
477 pname
= "Merge Full Join";
480 pname
= "Merge Right Join";
483 pname
= "Merge Semi Join";
486 pname
= "Merge Anti Join";
489 pname
= "Merge ??? Join";
494 switch (((HashJoin
*) plan
)->join
.jointype
)
500 pname
= "Hash Left Join";
503 pname
= "Hash Full Join";
506 pname
= "Hash Right Join";
509 pname
= "Hash Semi Join";
512 pname
= "Hash Anti Join";
515 pname
= "Hash ??? Join";
523 pname
= "Index Scan";
525 case T_BitmapIndexScan
:
526 pname
= "Bitmap Index Scan";
528 case T_BitmapHeapScan
:
529 pname
= "Bitmap Heap Scan";
535 pname
= "Subquery Scan";
538 pname
= "Function Scan";
541 pname
= "Values Scan";
546 case T_WorkTableScan
:
547 pname
= "WorkTable Scan";
550 pname
= "Materialize";
559 switch (((Agg
*) plan
)->aggstrategy
)
565 pname
= "GroupAggregate";
568 pname
= "HashAggregate";
571 pname
= "Aggregate ???";
579 switch (((SetOp
*) plan
)->strategy
)
582 switch (((SetOp
*) plan
)->cmd
)
584 case SETOPCMD_INTERSECT
:
585 pname
= "SetOp Intersect";
587 case SETOPCMD_INTERSECT_ALL
:
588 pname
= "SetOp Intersect All";
590 case SETOPCMD_EXCEPT
:
591 pname
= "SetOp Except";
593 case SETOPCMD_EXCEPT_ALL
:
594 pname
= "SetOp Except All";
602 switch (((SetOp
*) plan
)->cmd
)
604 case SETOPCMD_INTERSECT
:
605 pname
= "HashSetOp Intersect";
607 case SETOPCMD_INTERSECT_ALL
:
608 pname
= "HashSetOp Intersect All";
610 case SETOPCMD_EXCEPT
:
611 pname
= "HashSetOp Except";
613 case SETOPCMD_EXCEPT_ALL
:
614 pname
= "HashSetOp Except All";
617 pname
= "HashSetOp ???";
637 appendStringInfoString(str
, pname
);
638 switch (nodeTag(plan
))
641 if (ScanDirectionIsBackward(((IndexScan
*) plan
)->indexorderdir
))
642 appendStringInfoString(str
, " Backward");
643 appendStringInfo(str
, " using %s",
644 explain_get_index_name(((IndexScan
*) plan
)->indexid
));
647 case T_BitmapHeapScan
:
649 if (((Scan
*) plan
)->scanrelid
> 0)
651 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
655 /* Assume it's on a real relation */
656 Assert(rte
->rtekind
== RTE_RELATION
);
658 /* We only show the rel name, not schema name */
659 relname
= get_rel_name(rte
->relid
);
661 appendStringInfo(str
, " on %s",
662 quote_identifier(relname
));
663 if (strcmp(rte
->eref
->aliasname
, relname
) != 0)
664 appendStringInfo(str
, " %s",
665 quote_identifier(rte
->eref
->aliasname
));
668 case T_BitmapIndexScan
:
669 appendStringInfo(str
, " on %s",
670 explain_get_index_name(((BitmapIndexScan
*) plan
)->indexid
));
673 if (((Scan
*) plan
)->scanrelid
> 0)
675 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
678 appendStringInfo(str
, " %s",
679 quote_identifier(rte
->eref
->aliasname
));
683 if (((Scan
*) plan
)->scanrelid
> 0)
685 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
690 /* Assert it's on a RangeFunction */
691 Assert(rte
->rtekind
== RTE_FUNCTION
);
694 * If the expression is still a function call, we can get the
695 * real name of the function. Otherwise, punt (this can
696 * happen if the optimizer simplified away the function call,
699 funcexpr
= ((FunctionScan
*) plan
)->funcexpr
;
700 if (funcexpr
&& IsA(funcexpr
, FuncExpr
))
702 Oid funcid
= ((FuncExpr
*) funcexpr
)->funcid
;
704 /* We only show the func name, not schema name */
705 proname
= get_func_name(funcid
);
708 proname
= rte
->eref
->aliasname
;
710 appendStringInfo(str
, " on %s",
711 quote_identifier(proname
));
712 if (strcmp(rte
->eref
->aliasname
, proname
) != 0)
713 appendStringInfo(str
, " %s",
714 quote_identifier(rte
->eref
->aliasname
));
718 if (((Scan
*) plan
)->scanrelid
> 0)
720 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
724 /* Assert it's on a values rte */
725 Assert(rte
->rtekind
== RTE_VALUES
);
727 valsname
= rte
->eref
->aliasname
;
729 appendStringInfo(str
, " on %s",
730 quote_identifier(valsname
));
734 if (((Scan
*) plan
)->scanrelid
> 0)
736 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
739 /* Assert it's on a non-self-reference CTE */
740 Assert(rte
->rtekind
== RTE_CTE
);
741 Assert(!rte
->self_reference
);
743 appendStringInfo(str
, " on %s",
744 quote_identifier(rte
->ctename
));
745 if (strcmp(rte
->eref
->aliasname
, rte
->ctename
) != 0)
746 appendStringInfo(str
, " %s",
747 quote_identifier(rte
->eref
->aliasname
));
750 case T_WorkTableScan
:
751 if (((Scan
*) plan
)->scanrelid
> 0)
753 RangeTblEntry
*rte
= rt_fetch(((Scan
*) plan
)->scanrelid
,
756 /* Assert it's on a self-reference CTE */
757 Assert(rte
->rtekind
== RTE_CTE
);
758 Assert(rte
->self_reference
);
760 appendStringInfo(str
, " on %s",
761 quote_identifier(rte
->ctename
));
762 if (strcmp(rte
->eref
->aliasname
, rte
->ctename
) != 0)
763 appendStringInfo(str
, " %s",
764 quote_identifier(rte
->eref
->aliasname
));
771 appendStringInfo(str
, " (cost=%.2f..%.2f rows=%.0f width=%d)",
772 plan
->startup_cost
, plan
->total_cost
,
773 plan
->plan_rows
, plan
->plan_width
);
776 * We have to forcibly clean up the instrumentation state because we
777 * haven't done ExecutorEnd yet. This is pretty grotty ...
779 if (planstate
->instrument
)
780 InstrEndLoop(planstate
->instrument
);
782 if (planstate
->instrument
&& planstate
->instrument
->nloops
> 0)
784 double nloops
= planstate
->instrument
->nloops
;
786 appendStringInfo(str
, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
787 1000.0 * planstate
->instrument
->startup
/ nloops
,
788 1000.0 * planstate
->instrument
->total
/ nloops
,
789 planstate
->instrument
->ntuples
/ nloops
,
790 planstate
->instrument
->nloops
);
792 else if (es
->printAnalyze
)
793 appendStringInfo(str
, " (never executed)");
794 appendStringInfoChar(str
, '\n');
798 show_plan_tlist(plan
, str
, indent
, es
);
800 /* quals, sort keys, etc */
801 switch (nodeTag(plan
))
804 show_scan_qual(((IndexScan
*) plan
)->indexqualorig
,
806 ((Scan
*) plan
)->scanrelid
,
809 show_scan_qual(plan
->qual
,
811 ((Scan
*) plan
)->scanrelid
,
815 case T_BitmapIndexScan
:
816 show_scan_qual(((BitmapIndexScan
*) plan
)->indexqualorig
,
818 ((Scan
*) plan
)->scanrelid
,
822 case T_BitmapHeapScan
:
823 /* XXX do we want to show this in production? */
824 show_scan_qual(((BitmapHeapScan
*) plan
)->bitmapqualorig
,
826 ((Scan
*) plan
)->scanrelid
,
834 case T_WorkTableScan
:
835 show_scan_qual(plan
->qual
,
837 ((Scan
*) plan
)->scanrelid
,
842 show_scan_qual(plan
->qual
,
844 ((Scan
*) plan
)->scanrelid
,
851 * The tidquals list has OR semantics, so be sure to show it
852 * as an OR condition.
854 List
*tidquals
= ((TidScan
*) plan
)->tidquals
;
856 if (list_length(tidquals
) > 1)
857 tidquals
= list_make1(make_orclause(tidquals
));
858 show_scan_qual(tidquals
,
860 ((Scan
*) plan
)->scanrelid
,
863 show_scan_qual(plan
->qual
,
865 ((Scan
*) plan
)->scanrelid
,
871 show_upper_qual(((NestLoop
*) plan
)->join
.joinqual
,
874 show_upper_qual(plan
->qual
,
879 show_upper_qual(((MergeJoin
*) plan
)->mergeclauses
,
882 show_upper_qual(((MergeJoin
*) plan
)->join
.joinqual
,
885 show_upper_qual(plan
->qual
,
890 show_upper_qual(((HashJoin
*) plan
)->hashclauses
,
893 show_upper_qual(((HashJoin
*) plan
)->join
.joinqual
,
896 show_upper_qual(plan
->qual
,
902 show_upper_qual(plan
->qual
,
908 ((Sort
*) plan
)->numCols
,
909 ((Sort
*) plan
)->sortColIdx
,
912 show_sort_info((SortState
*) planstate
,
916 show_upper_qual((List
*) ((Result
*) plan
)->resconstantqual
,
917 "One-Time Filter", plan
,
919 show_upper_qual(plan
->qual
,
932 for (i
= 0; i
< indent
; i
++)
933 appendStringInfo(str
, " ");
934 appendStringInfo(str
, " InitPlan\n");
935 foreach(lst
, planstate
->initPlan
)
937 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
938 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
940 for (i
= 0; i
< indent
; i
++)
941 appendStringInfo(str
, " ");
942 appendStringInfo(str
, " -> ");
944 exec_subplan_get_plan(es
->pstmt
, sp
),
954 for (i
= 0; i
< indent
; i
++)
955 appendStringInfo(str
, " ");
956 appendStringInfo(str
, " -> ");
959 * Ordinarily we don't pass down our own outer_plan value to our child
960 * nodes, but in bitmap scan trees we must, since the bottom
961 * BitmapIndexScan nodes may have outer references.
963 explain_outNode(str
, outerPlan(plan
),
964 outerPlanState(planstate
),
965 IsA(plan
, BitmapHeapScan
) ? outer_plan
: NULL
,
972 for (i
= 0; i
< indent
; i
++)
973 appendStringInfo(str
, " ");
974 appendStringInfo(str
, " -> ");
975 explain_outNode(str
, innerPlan(plan
),
976 innerPlanState(planstate
),
981 if (IsA(plan
, Append
))
983 Append
*appendplan
= (Append
*) plan
;
984 AppendState
*appendstate
= (AppendState
*) planstate
;
989 foreach(lst
, appendplan
->appendplans
)
991 Plan
*subnode
= (Plan
*) lfirst(lst
);
993 for (i
= 0; i
< indent
; i
++)
994 appendStringInfo(str
, " ");
995 appendStringInfo(str
, " -> ");
998 * Ordinarily we don't pass down our own outer_plan value to our
999 * child nodes, but in an Append we must, since we might be
1000 * looking at an appendrel indexscan with outer references from
1003 explain_outNode(str
, subnode
,
1004 appendstate
->appendplans
[j
],
1011 if (IsA(plan
, BitmapAnd
))
1013 BitmapAnd
*bitmapandplan
= (BitmapAnd
*) plan
;
1014 BitmapAndState
*bitmapandstate
= (BitmapAndState
*) planstate
;
1019 foreach(lst
, bitmapandplan
->bitmapplans
)
1021 Plan
*subnode
= (Plan
*) lfirst(lst
);
1023 for (i
= 0; i
< indent
; i
++)
1024 appendStringInfo(str
, " ");
1025 appendStringInfo(str
, " -> ");
1027 explain_outNode(str
, subnode
,
1028 bitmapandstate
->bitmapplans
[j
],
1029 outer_plan
, /* pass down same outer plan */
1035 if (IsA(plan
, BitmapOr
))
1037 BitmapOr
*bitmaporplan
= (BitmapOr
*) plan
;
1038 BitmapOrState
*bitmaporstate
= (BitmapOrState
*) planstate
;
1043 foreach(lst
, bitmaporplan
->bitmapplans
)
1045 Plan
*subnode
= (Plan
*) lfirst(lst
);
1047 for (i
= 0; i
< indent
; i
++)
1048 appendStringInfo(str
, " ");
1049 appendStringInfo(str
, " -> ");
1051 explain_outNode(str
, subnode
,
1052 bitmaporstate
->bitmapplans
[j
],
1053 outer_plan
, /* pass down same outer plan */
1059 if (IsA(plan
, SubqueryScan
))
1061 SubqueryScan
*subqueryscan
= (SubqueryScan
*) plan
;
1062 SubqueryScanState
*subquerystate
= (SubqueryScanState
*) planstate
;
1063 Plan
*subnode
= subqueryscan
->subplan
;
1065 for (i
= 0; i
< indent
; i
++)
1066 appendStringInfo(str
, " ");
1067 appendStringInfo(str
, " -> ");
1069 explain_outNode(str
, subnode
,
1070 subquerystate
->subplan
,
1076 if (planstate
->subPlan
)
1080 for (i
= 0; i
< indent
; i
++)
1081 appendStringInfo(str
, " ");
1082 appendStringInfo(str
, " SubPlan\n");
1083 foreach(lst
, planstate
->subPlan
)
1085 SubPlanState
*sps
= (SubPlanState
*) lfirst(lst
);
1086 SubPlan
*sp
= (SubPlan
*) sps
->xprstate
.expr
;
1088 for (i
= 0; i
< indent
; i
++)
1089 appendStringInfo(str
, " ");
1090 appendStringInfo(str
, " -> ");
1091 explain_outNode(str
,
1092 exec_subplan_get_plan(es
->pstmt
, sp
),
1101 * Show the targetlist of a plan node
1104 show_plan_tlist(Plan
*plan
,
1105 StringInfo str
, int indent
, ExplainState
*es
)
1112 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1113 if (plan
->targetlist
== NIL
)
1115 /* The tlist of an Append isn't real helpful, so suppress it */
1116 if (IsA(plan
, Append
))
1118 /* Likewise for RecursiveUnion */
1119 if (IsA(plan
, RecursiveUnion
))
1122 /* Set up deparsing context */
1123 context
= deparse_context_for_plan((Node
*) plan
,
1126 es
->pstmt
->subplans
);
1127 useprefix
= list_length(es
->rtable
) > 1;
1129 /* Emit line prefix */
1130 for (i
= 0; i
< indent
; i
++)
1131 appendStringInfo(str
, " ");
1132 appendStringInfo(str
, " Output: ");
1134 /* Deparse each non-junk result column */
1136 foreach(lc
, plan
->targetlist
)
1138 TargetEntry
*tle
= (TargetEntry
*) lfirst(lc
);
1143 appendStringInfo(str
, ", ");
1144 appendStringInfoString(str
,
1145 deparse_expression((Node
*) tle
->expr
, context
,
1149 appendStringInfoChar(str
, '\n');
1153 * Show a qualifier expression for a scan plan node
1155 * Note: outer_plan is the referent for any OUTER vars in the scan qual;
1156 * this would be the outer side of a nestloop plan. Pass NULL if none.
1159 show_scan_qual(List
*qual
, const char *qlabel
,
1160 int scanrelid
, Plan
*scan_plan
, Plan
*outer_plan
,
1161 StringInfo str
, int indent
, ExplainState
*es
)
1169 /* No work if empty qual */
1173 /* Convert AND list to explicit AND */
1174 node
= (Node
*) make_ands_explicit(qual
);
1176 /* Set up deparsing context */
1177 context
= deparse_context_for_plan((Node
*) scan_plan
,
1178 (Node
*) outer_plan
,
1180 es
->pstmt
->subplans
);
1181 useprefix
= (outer_plan
!= NULL
|| IsA(scan_plan
, SubqueryScan
));
1183 /* Deparse the expression */
1184 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1186 /* And add to str */
1187 for (i
= 0; i
< indent
; i
++)
1188 appendStringInfo(str
, " ");
1189 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1193 * Show a qualifier expression for an upper-level plan node
1196 show_upper_qual(List
*qual
, const char *qlabel
, Plan
*plan
,
1197 StringInfo str
, int indent
, ExplainState
*es
)
1205 /* No work if empty qual */
1209 /* Set up deparsing context */
1210 context
= deparse_context_for_plan((Node
*) plan
,
1213 es
->pstmt
->subplans
);
1214 useprefix
= list_length(es
->rtable
) > 1;
1216 /* Deparse the expression */
1217 node
= (Node
*) make_ands_explicit(qual
);
1218 exprstr
= deparse_expression(node
, context
, useprefix
, false);
1220 /* And add to str */
1221 for (i
= 0; i
< indent
; i
++)
1222 appendStringInfo(str
, " ");
1223 appendStringInfo(str
, " %s: %s\n", qlabel
, exprstr
);
1227 * Show the sort keys for a Sort node.
1230 show_sort_keys(Plan
*sortplan
, int nkeys
, AttrNumber
*keycols
,
1232 StringInfo str
, int indent
, ExplainState
*es
)
1243 for (i
= 0; i
< indent
; i
++)
1244 appendStringInfo(str
, " ");
1245 appendStringInfo(str
, " %s: ", qlabel
);
1247 /* Set up deparsing context */
1248 context
= deparse_context_for_plan((Node
*) sortplan
,
1251 es
->pstmt
->subplans
);
1252 useprefix
= list_length(es
->rtable
) > 1;
1254 for (keyno
= 0; keyno
< nkeys
; keyno
++)
1256 /* find key expression in tlist */
1257 AttrNumber keyresno
= keycols
[keyno
];
1258 TargetEntry
*target
= get_tle_by_resno(sortplan
->targetlist
, keyresno
);
1261 elog(ERROR
, "no tlist entry for key %d", keyresno
);
1262 /* Deparse the expression, showing any top-level cast */
1263 exprstr
= deparse_expression((Node
*) target
->expr
, context
,
1265 /* And add to str */
1267 appendStringInfo(str
, ", ");
1268 appendStringInfoString(str
, exprstr
);
1271 appendStringInfo(str
, "\n");
1275 * If it's EXPLAIN ANALYZE, show tuplesort explain info for a sort node
1278 show_sort_info(SortState
*sortstate
,
1279 StringInfo str
, int indent
, ExplainState
*es
)
1281 Assert(IsA(sortstate
, SortState
));
1282 if (es
->printAnalyze
&& sortstate
->sort_Done
&&
1283 sortstate
->tuplesortstate
!= NULL
)
1288 sortinfo
= tuplesort_explain((Tuplesortstate
*) sortstate
->tuplesortstate
);
1289 for (i
= 0; i
< indent
; i
++)
1290 appendStringInfo(str
, " ");
1291 appendStringInfo(str
, " %s\n", sortinfo
);
1297 * Fetch the name of an index in an EXPLAIN
1299 * We allow plugins to get control here so that plans involving hypothetical
1300 * indexes can be explained.
1303 explain_get_index_name(Oid indexId
)
1307 if (explain_get_index_name_hook
)
1308 result
= (*explain_get_index_name_hook
) (indexId
);
1313 /* default behavior: look in the catalogs and quote it */
1314 result
= get_rel_name(indexId
);
1316 elog(ERROR
, "cache lookup failed for index %u", indexId
);
1317 result
= quote_identifier(result
);