1 /*-------------------------------------------------------------------------
4 * Explain query execution plans
6 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
10 * src/backend/commands/explain.c
12 *-------------------------------------------------------------------------
16 #include "access/xact.h"
17 #include "catalog/pg_collation.h"
18 #include "catalog/pg_type.h"
19 #include "commands/createas.h"
20 #include "commands/defrem.h"
21 #include "commands/prepare.h"
22 #include "executor/hashjoin.h"
23 #include "foreign/fdwapi.h"
24 #include "nodes/extensible.h"
25 #include "nodes/nodeFuncs.h"
26 #include "optimizer/clauses.h"
27 #include "optimizer/planmain.h"
28 #include "parser/parsetree.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "storage/bufmgr.h"
31 #include "tcop/tcopprot.h"
32 #include "utils/builtins.h"
33 #include "utils/json.h"
34 #include "utils/lsyscache.h"
35 #include "utils/rel.h"
36 #include "utils/ruleutils.h"
37 #include "utils/snapmgr.h"
38 #include "utils/tuplesort.h"
39 #include "utils/typcache.h"
40 #include "utils/xml.h"
43 /* Hook for plugins to get control in ExplainOneQuery() */
44 ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
46 /* Hook for plugins to get control in explain_get_index_name() */
47 explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
50 /* OR-able flags for ExplainXMLTag() */
53 #define X_CLOSE_IMMEDIATE 2
54 #define X_NOWHITESPACE 4
56 static void ExplainOneQuery(Query *query, int cursorOptions,
57 IntoClause *into, ExplainState *es,
58 const char *queryString, ParamListInfo params);
59 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
61 static double elapsed_time(instr_time *starttime);
62 static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
63 static void ExplainNode(PlanState *planstate, List *ancestors,
64 const char *relationship, const char *plan_name,
66 static void show_plan_tlist(PlanState *planstate, List *ancestors,
68 static void show_expression(Node *node, const char *qlabel,
69 PlanState *planstate, List *ancestors,
70 bool useprefix, ExplainState *es);
71 static void show_qual(List *qual, const char *qlabel,
72 PlanState *planstate, List *ancestors,
73 bool useprefix, ExplainState *es);
74 static void show_scan_qual(List *qual, const char *qlabel,
75 PlanState *planstate, List *ancestors,
77 static void show_upper_qual(List *qual, const char *qlabel,
78 PlanState *planstate, List *ancestors,
80 static void show_sort_keys(SortState *sortstate, List *ancestors,
82 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
84 static void show_agg_keys(AggState *astate, List *ancestors,
86 static void show_grouping_sets(PlanState *planstate, Agg *agg,
87 List *ancestors, ExplainState *es);
88 static void show_grouping_set_keys(PlanState *planstate,
89 Agg *aggnode, Sort *sortnode,
90 List *context, bool useprefix,
91 List *ancestors, ExplainState *es);
92 static void show_group_keys(GroupState *gstate, List *ancestors,
94 static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
95 int nkeys, AttrNumber *keycols,
96 Oid *sortOperators, Oid *collations, bool *nullsFirst,
97 List *ancestors, ExplainState *es);
98 static void show_sortorder_options(StringInfo buf, Node *sortexpr,
99 Oid sortOperator, Oid collation, bool nullsFirst);
100 static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
101 List *ancestors, ExplainState *es);
102 static void show_sort_info(SortState *sortstate, ExplainState *es);
103 static void show_hash_info(HashState *hashstate, ExplainState *es);
104 static void show_tidbitmap_info(BitmapHeapScanState *planstate,
106 static void show_instrumentation_count(const char *qlabel, int which,
107 PlanState *planstate, ExplainState *es);
108 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
109 static const char *explain_get_index_name(Oid indexId);
110 static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
111 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
113 static void ExplainScanTarget(Scan *plan, ExplainState *es);
114 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
115 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
116 static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
118 static void ExplainMemberNodes(List *plans, PlanState **planstates,
119 List *ancestors, ExplainState *es);
120 static void ExplainSubPlans(List *plans, List *ancestors,
121 const char *relationship, ExplainState *es);
122 static void ExplainCustomChildren(CustomScanState *css,
123 List *ancestors, ExplainState *es);
124 static void ExplainProperty(const char *qlabel, const char *value,
125 bool numeric, ExplainState *es);
126 static void ExplainOpenGroup(const char *objtype, const char *labelname,
127 bool labeled, ExplainState *es);
128 static void ExplainCloseGroup(const char *objtype, const char *labelname,
129 bool labeled, ExplainState *es);
130 static void ExplainDummyGroup(const char *objtype, const char *labelname,
132 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
133 static void ExplainJSONLineEnding(ExplainState *es);
134 static void ExplainYAMLLineStarting(ExplainState *es);
135 static void escape_yaml(StringInfo buf, const char *str);
141 * execute an EXPLAIN command
144 ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
145 ParamListInfo params, DestReceiver *dest)
147 ExplainState *es = NewExplainState();
148 TupOutputState *tstate;
151 bool timing_set = false;
153 /* Parse options list. */
154 foreach(lc, stmt->options)
156 DefElem *opt = (DefElem *) lfirst(lc);
158 if (strcmp(opt->defname, "analyze") == 0)
159 es->analyze = defGetBoolean(opt);
160 else if (strcmp(opt->defname, "verbose") == 0)
161 es->verbose = defGetBoolean(opt);
162 else if (strcmp(opt->defname, "costs") == 0)
163 es->costs = defGetBoolean(opt);
164 else if (strcmp(opt->defname, "buffers") == 0)
165 es->buffers = defGetBoolean(opt);
166 else if (strcmp(opt->defname, "timing") == 0)
169 es->timing = defGetBoolean(opt);
171 else if (strcmp(opt->defname, "format") == 0)
173 char *p = defGetString(opt);
175 if (strcmp(p, "text") == 0)
176 es->format = EXPLAIN_FORMAT_TEXT;
177 else if (strcmp(p, "xml") == 0)
178 es->format = EXPLAIN_FORMAT_XML;
179 else if (strcmp(p, "json") == 0)
180 es->format = EXPLAIN_FORMAT_JSON;
181 else if (strcmp(p, "yaml") == 0)
182 es->format = EXPLAIN_FORMAT_YAML;
185 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
186 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
188 parser_errposition(pstate, opt->location)));
192 (errcode(ERRCODE_SYNTAX_ERROR),
193 errmsg("unrecognized EXPLAIN option \"%s\"",
195 parser_errposition(pstate, opt->location)));
198 if (es->buffers && !es->analyze)
200 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
201 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
203 /* if the timing was not set explicitly, set default value */
204 es->timing = (timing_set) ? es->timing : es->analyze;
206 /* check that timing is used with EXPLAIN ANALYZE */
207 if (es->timing && !es->analyze)
209 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
210 errmsg("EXPLAIN option TIMING requires ANALYZE")));
212 /* currently, summary option is not exposed to users; just set it */
213 es->summary = es->analyze;
216 * Parse analysis was done already, but we still have to run the rule
217 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
218 * came straight from the parser, or suitable locks were acquired by
221 * Because the rewriter and planner tend to scribble on the input, we make
222 * a preliminary copy of the source querytree. This prevents problems in
223 * the case that the EXPLAIN is in a portal or plpgsql function and is
224 * executed repeatedly. (See also the same hack in DECLARE CURSOR and
225 * PREPARE.) XXX FIXME someday.
227 rewritten = QueryRewrite(castNode(Query, copyObject(stmt->query)));
229 /* emit opening boilerplate */
230 ExplainBeginOutput(es);
232 if (rewritten == NIL)
235 * In the case of an INSTEAD NOTHING, tell at least that. But in
236 * non-text format, the output is delimited, so this isn't necessary.
238 if (es->format == EXPLAIN_FORMAT_TEXT)
239 appendStringInfoString(es->str, "Query rewrites to nothing\n");
245 /* Explain every plan */
246 foreach(l, rewritten)
248 ExplainOneQuery(castNode(Query, lfirst(l)),
249 CURSOR_OPT_PARALLEL_OK, NULL, es,
250 queryString, params);
252 /* Separate plans with an appropriate separator */
253 if (lnext(l) != NULL)
254 ExplainSeparatePlans(es);
258 /* emit closing boilerplate */
259 ExplainEndOutput(es);
260 Assert(es->indent == 0);
263 tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
264 if (es->format == EXPLAIN_FORMAT_TEXT)
265 do_text_output_multiline(tstate, es->str->data);
267 do_text_output_oneline(tstate, es->str->data);
268 end_tup_output(tstate);
270 pfree(es->str->data);
274 * Create a new ExplainState struct initialized with default options.
277 NewExplainState(void)
279 ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
281 /* Set default options (most fields can be left as zeroes). */
283 /* Prepare output buffer. */
284 es->str = makeStringInfo();
290 * ExplainResultDesc -
291 * construct the result tupledesc for an EXPLAIN
294 ExplainResultDesc(ExplainStmt *stmt)
298 Oid result_type = TEXTOID;
300 /* Check for XML format option */
301 foreach(lc, stmt->options)
303 DefElem *opt = (DefElem *) lfirst(lc);
305 if (strcmp(opt->defname, "format") == 0)
307 char *p = defGetString(opt);
309 if (strcmp(p, "xml") == 0)
310 result_type = XMLOID;
311 else if (strcmp(p, "json") == 0)
312 result_type = JSONOID;
314 result_type = TEXTOID;
315 /* don't "break", as ExplainQuery will use the last value */
319 /* Need a tuple descriptor representing a single TEXT or XML column */
320 tupdesc = CreateTemplateTupleDesc(1, false);
321 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
328 * print out the execution plan for one Query
330 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
333 ExplainOneQuery(Query *query, int cursorOptions,
334 IntoClause *into, ExplainState *es,
335 const char *queryString, ParamListInfo params)
337 /* planner will not cope with utility statements */
338 if (query->commandType == CMD_UTILITY)
340 ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
344 /* if an advisor plugin is present, let it manage things */
345 if (ExplainOneQuery_hook)
346 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
347 queryString, params);
351 instr_time planstart,
354 INSTR_TIME_SET_CURRENT(planstart);
357 plan = pg_plan_query(query, cursorOptions, params);
359 INSTR_TIME_SET_CURRENT(planduration);
360 INSTR_TIME_SUBTRACT(planduration, planstart);
362 /* run it (if needed) and produce output */
363 ExplainOnePlan(plan, into, es, queryString, params, &planduration);
368 * ExplainOneUtility -
369 * print out the execution plan for one utility statement
370 * (In general, utility statements don't have plans, but there are some
371 * we treat as special cases)
373 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
375 * This is exported because it's called back from prepare.c in the
376 * EXPLAIN EXECUTE case.
379 ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
380 const char *queryString, ParamListInfo params)
382 if (utilityStmt == NULL)
385 if (IsA(utilityStmt, CreateTableAsStmt))
388 * We have to rewrite the contained SELECT and then pass it back to
389 * ExplainOneQuery. It's probably not really necessary to copy the
390 * contained parsetree another time, but let's be safe.
392 * Like ExecCreateTableAs, disallow parallelism in the plan.
394 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
397 rewritten = QueryRewrite(castNode(Query, copyObject(ctas->query)));
398 Assert(list_length(rewritten) == 1);
399 ExplainOneQuery(castNode(Query, linitial(rewritten)),
401 queryString, params);
403 else if (IsA(utilityStmt, DeclareCursorStmt))
406 * Likewise for DECLARE CURSOR.
408 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
409 * actually run the query. This is different from pre-8.3 behavior
410 * but seems more useful than not running the query. No cursor will
411 * be created, however.
413 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
416 rewritten = QueryRewrite(castNode(Query, copyObject(dcs->query)));
417 Assert(list_length(rewritten) == 1);
418 ExplainOneQuery(castNode(Query, linitial(rewritten)),
419 dcs->options, NULL, es,
420 queryString, params);
422 else if (IsA(utilityStmt, ExecuteStmt))
423 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
424 queryString, params);
425 else if (IsA(utilityStmt, NotifyStmt))
427 if (es->format == EXPLAIN_FORMAT_TEXT)
428 appendStringInfoString(es->str, "NOTIFY\n");
430 ExplainDummyGroup("Notify", NULL, es);
434 if (es->format == EXPLAIN_FORMAT_TEXT)
435 appendStringInfoString(es->str,
436 "Utility statements have no plan structure\n");
438 ExplainDummyGroup("Utility Statement", NULL, es);
444 * given a planned query, execute it if needed, and then print
447 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
448 * in which case executing the query should result in creating that table.
450 * This is exported because it's called back from prepare.c in the
451 * EXPLAIN EXECUTE case, and because an index advisor plugin would need
455 ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
456 const char *queryString, ParamListInfo params,
457 const instr_time *planduration)
460 QueryDesc *queryDesc;
461 instr_time starttime;
462 double totaltime = 0;
464 int instrument_option = 0;
466 Assert(plannedstmt->commandType != CMD_UTILITY);
468 if (es->analyze && es->timing)
469 instrument_option |= INSTRUMENT_TIMER;
470 else if (es->analyze)
471 instrument_option |= INSTRUMENT_ROWS;
474 instrument_option |= INSTRUMENT_BUFFERS;
477 * We always collect timing for the entire statement, even when node-level
478 * timing is off, so we don't look at es->timing here. (We could skip
479 * this if !es->summary, but it's hardly worth the complication.)
481 INSTR_TIME_SET_CURRENT(starttime);
484 * Use a snapshot with an updated command ID to ensure this query sees
485 * results of any previously executed queries.
487 PushCopiedSnapshot(GetActiveSnapshot());
488 UpdateActiveSnapshotCommandId();
491 * Normally we discard the query's output, but if explaining CREATE TABLE
492 * AS, we'd better use the appropriate tuple receiver.
495 dest = CreateIntoRelDestReceiver(into);
497 dest = None_Receiver;
499 /* Create a QueryDesc for the query */
500 queryDesc = CreateQueryDesc(plannedstmt, queryString,
501 GetActiveSnapshot(), InvalidSnapshot,
502 dest, params, instrument_option);
504 /* Select execution options */
506 eflags = 0; /* default run-to-completion flags */
508 eflags = EXEC_FLAG_EXPLAIN_ONLY;
510 eflags |= GetIntoRelEFlags(into);
512 /* call ExecutorStart to prepare the plan for execution */
513 ExecutorStart(queryDesc, eflags);
515 /* Execute the plan for statistics if asked for */
520 /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
521 if (into && into->skipData)
522 dir = NoMovementScanDirection;
524 dir = ForwardScanDirection;
527 ExecutorRun(queryDesc, dir, 0L);
529 /* run cleanup too */
530 ExecutorFinish(queryDesc);
532 /* We can't run ExecutorEnd 'till we're done printing the stats... */
533 totaltime += elapsed_time(&starttime);
536 ExplainOpenGroup("Query", NULL, true, es);
538 /* Create textual dump of plan tree */
539 ExplainPrintPlan(es, queryDesc);
541 if (es->summary && planduration)
543 double plantime = INSTR_TIME_GET_DOUBLE(*planduration);
545 if (es->format == EXPLAIN_FORMAT_TEXT)
546 appendStringInfo(es->str, "Planning time: %.3f ms\n",
549 ExplainPropertyFloat("Planning Time", 1000.0 * plantime, 3, es);
552 /* Print info about runtime of triggers */
554 ExplainPrintTriggers(es, queryDesc);
557 * Close down the query and free resources. Include time for this in the
558 * total execution time (although it should be pretty minimal).
560 INSTR_TIME_SET_CURRENT(starttime);
562 ExecutorEnd(queryDesc);
564 FreeQueryDesc(queryDesc);
568 /* We need a CCI just in case query expanded to multiple plans */
570 CommandCounterIncrement();
572 totaltime += elapsed_time(&starttime);
576 if (es->format == EXPLAIN_FORMAT_TEXT)
577 appendStringInfo(es->str, "Execution time: %.3f ms\n",
580 ExplainPropertyFloat("Execution Time", 1000.0 * totaltime,
584 ExplainCloseGroup("Query", NULL, true, es);
589 * convert a QueryDesc's plan tree to text and append it to es->str
591 * The caller should have set up the options fields of *es, as well as
592 * initializing the output buffer es->str. Also, output formatting state
593 * such as the indent level is assumed valid. Plan-tree-specific fields
594 * in *es are initialized here.
596 * NB: will not work on utility statements
599 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
601 Bitmapset *rels_used = NULL;
604 /* Set up ExplainState fields associated with this plan tree */
605 Assert(queryDesc->plannedstmt != NULL);
606 es->pstmt = queryDesc->plannedstmt;
607 es->rtable = queryDesc->plannedstmt->rtable;
608 ExplainPreScanNode(queryDesc->planstate, &rels_used);
609 es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
610 es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable,
612 es->printed_subplans = NULL;
615 * Sometimes we mark a Gather node as "invisible", which means that it's
616 * not displayed in EXPLAIN output. The purpose of this is to allow
617 * running regression tests with force_parallel_mode=regress to get the
618 * same results as running the same tests with force_parallel_mode=off.
620 ps = queryDesc->planstate;
621 if (IsA(ps, GatherState) &&((Gather *) ps->plan)->invisible)
622 ps = outerPlanState(ps);
623 ExplainNode(ps, NIL, NULL, NULL, es);
627 * ExplainPrintTriggers -
628 * convert a QueryDesc's trigger statistics to text and append it to
631 * The caller should have set up the options fields of *es, as well as
632 * initializing the output buffer es->str. Other fields in *es are
636 ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
638 ResultRelInfo *rInfo;
640 int numrels = queryDesc->estate->es_num_result_relations;
641 List *targrels = queryDesc->estate->es_trig_target_relations;
645 ExplainOpenGroup("Triggers", "Triggers", false, es);
647 show_relname = (numrels > 1 || targrels != NIL);
648 rInfo = queryDesc->estate->es_result_relations;
649 for (nr = 0; nr < numrels; rInfo++, nr++)
650 report_triggers(rInfo, show_relname, es);
654 rInfo = (ResultRelInfo *) lfirst(l);
655 report_triggers(rInfo, show_relname, es);
658 ExplainCloseGroup("Triggers", "Triggers", false, es);
663 * add a "Query Text" node that contains the actual text of the query
665 * The caller should have set up the options fields of *es, as well as
666 * initializing the output buffer es->str.
670 ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
672 if (queryDesc->sourceText)
673 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
678 * report execution stats for a single relation's triggers
681 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
685 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
687 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
689 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
690 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
692 char *conname = NULL;
694 /* Must clean up instrumentation state */
698 * We ignore triggers that were never invoked; they likely aren't
699 * relevant to the current query type.
701 if (instr->ntuples == 0)
704 ExplainOpenGroup("Trigger", NULL, true, es);
706 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
707 if (OidIsValid(trig->tgconstraint))
708 conname = get_constraint_name(trig->tgconstraint);
711 * In text format, we avoid printing both the trigger name and the
712 * constraint name unless VERBOSE is specified. In non-text formats
713 * we just print everything.
715 if (es->format == EXPLAIN_FORMAT_TEXT)
717 if (es->verbose || conname == NULL)
718 appendStringInfo(es->str, "Trigger %s", trig->tgname);
720 appendStringInfoString(es->str, "Trigger");
722 appendStringInfo(es->str, " for constraint %s", conname);
724 appendStringInfo(es->str, " on %s", relname);
726 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
727 1000.0 * instr->total, instr->ntuples);
729 appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
733 ExplainPropertyText("Trigger Name", trig->tgname, es);
735 ExplainPropertyText("Constraint Name", conname, es);
736 ExplainPropertyText("Relation", relname, es);
738 ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
739 ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
745 ExplainCloseGroup("Trigger", NULL, true, es);
749 /* Compute elapsed time in seconds since given timestamp */
751 elapsed_time(instr_time *starttime)
755 INSTR_TIME_SET_CURRENT(endtime);
756 INSTR_TIME_SUBTRACT(endtime, *starttime);
757 return INSTR_TIME_GET_DOUBLE(endtime);
761 * ExplainPreScanNode -
762 * Prescan the planstate tree to identify which RTEs are referenced
764 * Adds the relid of each referenced RTE to *rels_used. The result controls
765 * which RTEs are assigned aliases by select_rtable_names_for_explain.
766 * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
767 * that never appear in the EXPLAIN output (such as inheritance parents).
770 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
772 Plan *plan = planstate->plan;
774 switch (nodeTag(plan))
779 case T_IndexOnlyScan:
780 case T_BitmapHeapScan:
786 case T_WorkTableScan:
787 *rels_used = bms_add_member(*rels_used,
788 ((Scan *) plan)->scanrelid);
791 *rels_used = bms_add_members(*rels_used,
792 ((ForeignScan *) plan)->fs_relids);
795 *rels_used = bms_add_members(*rels_used,
796 ((CustomScan *) plan)->custom_relids);
799 *rels_used = bms_add_member(*rels_used,
800 ((ModifyTable *) plan)->nominalRelation);
801 if (((ModifyTable *) plan)->exclRelRTI)
802 *rels_used = bms_add_member(*rels_used,
803 ((ModifyTable *) plan)->exclRelRTI);
809 return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
814 * Appends a description of a plan tree to es->str
816 * planstate points to the executor state node for the current plan node.
817 * We need to work from a PlanState node, not just a Plan node, in order to
818 * get at the instrumentation data (if any) as well as the list of subplans.
820 * ancestors is a list of parent PlanState nodes, most-closely-nested first.
821 * These are needed in order to interpret PARAM_EXEC Params.
823 * relationship describes the relationship of this plan node to its parent
824 * (eg, "Outer", "Inner"); it can be null at top level. plan_name is an
825 * optional name to be attached to the node.
827 * In text format, es->indent is controlled in this function since we only
828 * want it to change at plan-node boundaries. In non-text formats, es->indent
829 * corresponds to the nesting depth of logical output groups, and therefore
830 * is controlled by ExplainOpenGroup/ExplainCloseGroup.
833 ExplainNode(PlanState *planstate, List *ancestors,
834 const char *relationship, const char *plan_name,
837 Plan *plan = planstate->plan;
838 const char *pname; /* node type name for text output */
839 const char *sname; /* node type name for non-text output */
840 const char *strategy = NULL;
841 const char *partialmode = NULL;
842 const char *operation = NULL;
843 const char *custom_name = NULL;
844 int save_indent = es->indent;
847 switch (nodeTag(plan))
850 pname = sname = "Result";
853 pname = sname = "ProjectSet";
856 sname = "ModifyTable";
857 switch (((ModifyTable *) plan)->operation)
860 pname = operation = "Insert";
863 pname = operation = "Update";
866 pname = operation = "Delete";
874 pname = sname = "Append";
877 pname = sname = "Merge Append";
879 case T_RecursiveUnion:
880 pname = sname = "Recursive Union";
883 pname = sname = "BitmapAnd";
886 pname = sname = "BitmapOr";
889 pname = sname = "Nested Loop";
892 pname = "Merge"; /* "Join" gets added by jointype switch */
893 sname = "Merge Join";
896 pname = "Hash"; /* "Join" gets added by jointype switch */
900 pname = sname = "Seq Scan";
903 pname = sname = "Sample Scan";
906 pname = sname = "Gather";
909 pname = sname = "Index Scan";
911 case T_IndexOnlyScan:
912 pname = sname = "Index Only Scan";
914 case T_BitmapIndexScan:
915 pname = sname = "Bitmap Index Scan";
917 case T_BitmapHeapScan:
918 pname = sname = "Bitmap Heap Scan";
921 pname = sname = "Tid Scan";
924 pname = sname = "Subquery Scan";
927 pname = sname = "Function Scan";
930 pname = sname = "Values Scan";
933 pname = sname = "CTE Scan";
935 case T_WorkTableScan:
936 pname = sname = "WorkTable Scan";
939 sname = "Foreign Scan";
940 switch (((ForeignScan *) plan)->operation)
943 pname = "Foreign Scan";
944 operation = "Select";
947 pname = "Foreign Insert";
948 operation = "Insert";
951 pname = "Foreign Update";
952 operation = "Update";
955 pname = "Foreign Delete";
956 operation = "Delete";
964 sname = "Custom Scan";
965 custom_name = ((CustomScan *) plan)->methods->CustomName;
967 pname = psprintf("Custom Scan (%s)", custom_name);
972 pname = sname = "Materialize";
975 pname = sname = "Sort";
978 pname = sname = "Group";
982 Agg *agg = (Agg *) plan;
985 switch (agg->aggstrategy)
992 pname = "GroupAggregate";
996 pname = "HashAggregate";
1000 pname = "Aggregate ???";
1005 if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
1007 partialmode = "Partial";
1008 pname = psprintf("%s %s", partialmode, pname);
1010 else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
1012 partialmode = "Finalize";
1013 pname = psprintf("%s %s", partialmode, pname);
1016 partialmode = "Simple";
1020 pname = sname = "WindowAgg";
1023 pname = sname = "Unique";
1027 switch (((SetOp *) plan)->strategy)
1031 strategy = "Sorted";
1034 pname = "HashSetOp";
1035 strategy = "Hashed";
1038 pname = "SetOp ???";
1044 pname = sname = "LockRows";
1047 pname = sname = "Limit";
1050 pname = sname = "Hash";
1053 pname = sname = "???";
1057 ExplainOpenGroup("Plan",
1058 relationship ? NULL : "Plan",
1061 if (es->format == EXPLAIN_FORMAT_TEXT)
1065 appendStringInfoSpaces(es->str, es->indent * 2);
1066 appendStringInfo(es->str, "%s\n", plan_name);
1071 appendStringInfoSpaces(es->str, es->indent * 2);
1072 appendStringInfoString(es->str, "-> ");
1075 if (plan->parallel_aware)
1076 appendStringInfoString(es->str, "Parallel ");
1077 appendStringInfoString(es->str, pname);
1082 ExplainPropertyText("Node Type", sname, es);
1084 ExplainPropertyText("Strategy", strategy, es);
1086 ExplainPropertyText("Partial Mode", partialmode, es);
1088 ExplainPropertyText("Operation", operation, es);
1090 ExplainPropertyText("Parent Relationship", relationship, es);
1092 ExplainPropertyText("Subplan Name", plan_name, es);
1094 ExplainPropertyText("Custom Plan Provider", custom_name, es);
1095 ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
1098 switch (nodeTag(plan))
1102 case T_BitmapHeapScan:
1104 case T_SubqueryScan:
1105 case T_FunctionScan:
1108 case T_WorkTableScan:
1109 ExplainScanTarget((Scan *) plan, es);
1113 if (((Scan *) plan)->scanrelid > 0)
1114 ExplainScanTarget((Scan *) plan, es);
1118 IndexScan *indexscan = (IndexScan *) plan;
1120 ExplainIndexScanDetails(indexscan->indexid,
1121 indexscan->indexorderdir,
1123 ExplainScanTarget((Scan *) indexscan, es);
1126 case T_IndexOnlyScan:
1128 IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
1130 ExplainIndexScanDetails(indexonlyscan->indexid,
1131 indexonlyscan->indexorderdir,
1133 ExplainScanTarget((Scan *) indexonlyscan, es);
1136 case T_BitmapIndexScan:
1138 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
1139 const char *indexname =
1140 explain_get_index_name(bitmapindexscan->indexid);
1142 if (es->format == EXPLAIN_FORMAT_TEXT)
1143 appendStringInfo(es->str, " on %s", indexname);
1145 ExplainPropertyText("Index Name", indexname, es);
1149 ExplainModifyTarget((ModifyTable *) plan, es);
1155 const char *jointype;
1157 switch (((Join *) plan)->jointype)
1181 if (es->format == EXPLAIN_FORMAT_TEXT)
1184 * For historical reasons, the join type is interpolated
1185 * into the node type name...
1187 if (((Join *) plan)->jointype != JOIN_INNER)
1188 appendStringInfo(es->str, " %s Join", jointype);
1189 else if (!IsA(plan, NestLoop))
1190 appendStringInfoString(es->str, " Join");
1193 ExplainPropertyText("Join Type", jointype, es);
1198 const char *setopcmd;
1200 switch (((SetOp *) plan)->cmd)
1202 case SETOPCMD_INTERSECT:
1203 setopcmd = "Intersect";
1205 case SETOPCMD_INTERSECT_ALL:
1206 setopcmd = "Intersect All";
1208 case SETOPCMD_EXCEPT:
1209 setopcmd = "Except";
1211 case SETOPCMD_EXCEPT_ALL:
1212 setopcmd = "Except All";
1218 if (es->format == EXPLAIN_FORMAT_TEXT)
1219 appendStringInfo(es->str, " %s", setopcmd);
1221 ExplainPropertyText("Command", setopcmd, es);
1230 if (es->format == EXPLAIN_FORMAT_TEXT)
1232 appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
1233 plan->startup_cost, plan->total_cost,
1234 plan->plan_rows, plan->plan_width);
1238 ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
1239 ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
1240 ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
1241 ExplainPropertyInteger("Plan Width", plan->plan_width, es);
1246 * We have to forcibly clean up the instrumentation state because we
1247 * haven't done ExecutorEnd yet. This is pretty grotty ...
1249 * Note: contrib/auto_explain could cause instrumentation to be set up
1250 * even though we didn't ask for it here. Be careful not to print any
1251 * instrumentation results the user didn't ask for. But we do the
1252 * InstrEndLoop call anyway, if possible, to reduce the number of cases
1253 * auto_explain has to contend with.
1255 if (planstate->instrument)
1256 InstrEndLoop(planstate->instrument);
1259 planstate->instrument && planstate->instrument->nloops > 0)
1261 double nloops = planstate->instrument->nloops;
1262 double startup_sec = 1000.0 * planstate->instrument->startup / nloops;
1263 double total_sec = 1000.0 * planstate->instrument->total / nloops;
1264 double rows = planstate->instrument->ntuples / nloops;
1266 if (es->format == EXPLAIN_FORMAT_TEXT)
1269 appendStringInfo(es->str,
1270 " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1271 startup_sec, total_sec, rows, nloops);
1273 appendStringInfo(es->str,
1274 " (actual rows=%.0f loops=%.0f)",
1281 ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
1282 ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
1284 ExplainPropertyFloat("Actual Rows", rows, 0, es);
1285 ExplainPropertyFloat("Actual Loops", nloops, 0, es);
1288 else if (es->analyze)
1290 if (es->format == EXPLAIN_FORMAT_TEXT)
1291 appendStringInfoString(es->str, " (never executed)");
1296 ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
1297 ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
1299 ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
1300 ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
1304 /* in text format, first line ends here */
1305 if (es->format == EXPLAIN_FORMAT_TEXT)
1306 appendStringInfoChar(es->str, '\n');
1310 show_plan_tlist(planstate, ancestors, es);
1312 /* quals, sort keys, etc */
1313 switch (nodeTag(plan))
1316 show_scan_qual(((IndexScan *) plan)->indexqualorig,
1317 "Index Cond", planstate, ancestors, es);
1318 if (((IndexScan *) plan)->indexqualorig)
1319 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1321 show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
1322 "Order By", planstate, ancestors, es);
1323 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1325 show_instrumentation_count("Rows Removed by Filter", 1,
1328 case T_IndexOnlyScan:
1329 show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
1330 "Index Cond", planstate, ancestors, es);
1331 if (((IndexOnlyScan *) plan)->indexqual)
1332 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1334 show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
1335 "Order By", planstate, ancestors, es);
1336 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1338 show_instrumentation_count("Rows Removed by Filter", 1,
1341 ExplainPropertyLong("Heap Fetches",
1342 ((IndexOnlyScanState *) planstate)->ioss_HeapFetches, es);
1344 case T_BitmapIndexScan:
1345 show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
1346 "Index Cond", planstate, ancestors, es);
1348 case T_BitmapHeapScan:
1349 show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
1350 "Recheck Cond", planstate, ancestors, es);
1351 if (((BitmapHeapScan *) plan)->bitmapqualorig)
1352 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1354 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1356 show_instrumentation_count("Rows Removed by Filter", 1,
1359 show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
1362 show_tablesample(((SampleScan *) plan)->tablesample,
1363 planstate, ancestors, es);
1364 /* FALL THRU to print additional fields the same as SeqScan */
1368 case T_WorkTableScan:
1369 case T_SubqueryScan:
1370 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1372 show_instrumentation_count("Rows Removed by Filter", 1,
1377 Gather *gather = (Gather *) plan;
1379 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1381 show_instrumentation_count("Rows Removed by Filter", 1,
1383 ExplainPropertyInteger("Workers Planned",
1384 gather->num_workers, es);
1389 nworkers = ((GatherState *) planstate)->nworkers_launched;
1390 ExplainPropertyInteger("Workers Launched",
1393 if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
1394 ExplainPropertyBool("Single Copy", gather->single_copy, es);
1397 case T_FunctionScan:
1403 foreach(lc, ((FunctionScan *) plan)->functions)
1405 RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
1407 fexprs = lappend(fexprs, rtfunc->funcexpr);
1409 /* We rely on show_expression to insert commas as needed */
1410 show_expression((Node *) fexprs,
1411 "Function Call", planstate, ancestors,
1414 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1416 show_instrumentation_count("Rows Removed by Filter", 1,
1422 * The tidquals list has OR semantics, so be sure to show it
1423 * as an OR condition.
1425 List *tidquals = ((TidScan *) plan)->tidquals;
1427 if (list_length(tidquals) > 1)
1428 tidquals = list_make1(make_orclause(tidquals));
1429 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
1430 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1432 show_instrumentation_count("Rows Removed by Filter", 1,
1437 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1439 show_instrumentation_count("Rows Removed by Filter", 1,
1441 show_foreignscan_info((ForeignScanState *) planstate, es);
1445 CustomScanState *css = (CustomScanState *) planstate;
1447 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1449 show_instrumentation_count("Rows Removed by Filter", 1,
1451 if (css->methods->ExplainCustomScan)
1452 css->methods->ExplainCustomScan(css, ancestors, es);
1456 show_upper_qual(((NestLoop *) plan)->join.joinqual,
1457 "Join Filter", planstate, ancestors, es);
1458 if (((NestLoop *) plan)->join.joinqual)
1459 show_instrumentation_count("Rows Removed by Join Filter", 1,
1461 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1463 show_instrumentation_count("Rows Removed by Filter", 2,
1467 show_upper_qual(((MergeJoin *) plan)->mergeclauses,
1468 "Merge Cond", planstate, ancestors, es);
1469 show_upper_qual(((MergeJoin *) plan)->join.joinqual,
1470 "Join Filter", planstate, ancestors, es);
1471 if (((MergeJoin *) plan)->join.joinqual)
1472 show_instrumentation_count("Rows Removed by Join Filter", 1,
1474 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1476 show_instrumentation_count("Rows Removed by Filter", 2,
1480 show_upper_qual(((HashJoin *) plan)->hashclauses,
1481 "Hash Cond", planstate, ancestors, es);
1482 show_upper_qual(((HashJoin *) plan)->join.joinqual,
1483 "Join Filter", planstate, ancestors, es);
1484 if (((HashJoin *) plan)->join.joinqual)
1485 show_instrumentation_count("Rows Removed by Join Filter", 1,
1487 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1489 show_instrumentation_count("Rows Removed by Filter", 2,
1493 show_agg_keys(castNode(AggState, planstate), ancestors, es);
1494 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1496 show_instrumentation_count("Rows Removed by Filter", 1,
1500 show_group_keys(castNode(GroupState, planstate), ancestors, es);
1501 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1503 show_instrumentation_count("Rows Removed by Filter", 1,
1507 show_sort_keys(castNode(SortState, planstate), ancestors, es);
1508 show_sort_info(castNode(SortState, planstate), es);
1511 show_merge_append_keys(castNode(MergeAppendState, planstate),
1515 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
1516 "One-Time Filter", planstate, ancestors, es);
1517 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1519 show_instrumentation_count("Rows Removed by Filter", 1,
1523 show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
1527 show_hash_info(castNode(HashState, planstate), es);
1533 /* Show buffer usage */
1534 if (es->buffers && planstate->instrument)
1535 show_buffer_usage(es, &planstate->instrument->bufusage);
1537 /* Show worker detail */
1538 if (es->analyze && es->verbose && planstate->worker_instrument)
1540 WorkerInstrumentation *w = planstate->worker_instrument;
1541 bool opened_group = false;
1544 for (n = 0; n < w->num_workers; ++n)
1546 Instrumentation *instrument = &w->instrument[n];
1547 double nloops = instrument->nloops;
1554 startup_sec = 1000.0 * instrument->startup / nloops;
1555 total_sec = 1000.0 * instrument->total / nloops;
1556 rows = instrument->ntuples / nloops;
1558 if (es->format == EXPLAIN_FORMAT_TEXT)
1560 appendStringInfoSpaces(es->str, es->indent * 2);
1561 appendStringInfo(es->str, "Worker %d: ", n);
1563 appendStringInfo(es->str,
1564 "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
1565 startup_sec, total_sec, rows, nloops);
1567 appendStringInfo(es->str,
1568 "actual rows=%.0f loops=%.0f\n",
1572 show_buffer_usage(es, &instrument->bufusage);
1579 ExplainOpenGroup("Workers", "Workers", false, es);
1580 opened_group = true;
1582 ExplainOpenGroup("Worker", NULL, true, es);
1583 ExplainPropertyInteger("Worker Number", n, es);
1587 ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
1588 ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
1590 ExplainPropertyFloat("Actual Rows", rows, 0, es);
1591 ExplainPropertyFloat("Actual Loops", nloops, 0, es);
1594 show_buffer_usage(es, &instrument->bufusage);
1596 ExplainCloseGroup("Worker", NULL, true, es);
1601 ExplainCloseGroup("Workers", "Workers", false, es);
1604 /* Get ready to display the child plans */
1605 haschildren = planstate->initPlan ||
1606 outerPlanState(planstate) ||
1607 innerPlanState(planstate) ||
1608 IsA(plan, ModifyTable) ||
1609 IsA(plan, Append) ||
1610 IsA(plan, MergeAppend) ||
1611 IsA(plan, BitmapAnd) ||
1612 IsA(plan, BitmapOr) ||
1613 IsA(plan, SubqueryScan) ||
1614 (IsA(planstate, CustomScanState) &&
1615 ((CustomScanState *) planstate)->custom_ps != NIL) ||
1619 ExplainOpenGroup("Plans", "Plans", false, es);
1620 /* Pass current PlanState as head of ancestors list for children */
1621 ancestors = lcons(planstate, ancestors);
1625 if (planstate->initPlan)
1626 ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
1629 if (outerPlanState(planstate))
1630 ExplainNode(outerPlanState(planstate), ancestors,
1634 if (innerPlanState(planstate))
1635 ExplainNode(innerPlanState(planstate), ancestors,
1638 /* special child plans */
1639 switch (nodeTag(plan))
1642 ExplainMemberNodes(((ModifyTable *) plan)->plans,
1643 ((ModifyTableState *) planstate)->mt_plans,
1647 ExplainMemberNodes(((Append *) plan)->appendplans,
1648 ((AppendState *) planstate)->appendplans,
1652 ExplainMemberNodes(((MergeAppend *) plan)->mergeplans,
1653 ((MergeAppendState *) planstate)->mergeplans,
1657 ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
1658 ((BitmapAndState *) planstate)->bitmapplans,
1662 ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
1663 ((BitmapOrState *) planstate)->bitmapplans,
1666 case T_SubqueryScan:
1667 ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
1668 "Subquery", NULL, es);
1671 ExplainCustomChildren((CustomScanState *) planstate,
1679 if (planstate->subPlan)
1680 ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
1682 /* end of child plans */
1685 ancestors = list_delete_first(ancestors);
1686 ExplainCloseGroup("Plans", "Plans", false, es);
1689 /* in text format, undo whatever indentation we added */
1690 if (es->format == EXPLAIN_FORMAT_TEXT)
1691 es->indent = save_indent;
1693 ExplainCloseGroup("Plan",
1694 relationship ? NULL : "Plan",
1699 * Show the targetlist of a plan node
1702 show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
1704 Plan *plan = planstate->plan;
1710 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1711 if (plan->targetlist == NIL)
1713 /* The tlist of an Append isn't real helpful, so suppress it */
1714 if (IsA(plan, Append))
1716 /* Likewise for MergeAppend and RecursiveUnion */
1717 if (IsA(plan, MergeAppend))
1719 if (IsA(plan, RecursiveUnion))
1723 * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
1725 * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
1726 * might contain subplan output expressions that are confusing in this
1727 * context. The tlist for a ForeignScan that executes a direct UPDATE/
1728 * DELETE always contains "junk" target columns to identify the exact row
1729 * to update or delete, which would be confusing in this context. So, we
1730 * suppress it in all the cases.
1732 if (IsA(plan, ForeignScan) &&
1733 ((ForeignScan *) plan)->operation != CMD_SELECT)
1736 /* Set up deparsing context */
1737 context = set_deparse_context_planstate(es->deparse_cxt,
1740 useprefix = list_length(es->rtable) > 1;
1742 /* Deparse each result column (we now include resjunk ones) */
1743 foreach(lc, plan->targetlist)
1745 TargetEntry *tle = (TargetEntry *) lfirst(lc);
1747 result = lappend(result,
1748 deparse_expression((Node *) tle->expr, context,
1753 ExplainPropertyList("Output", result, es);
1757 * Show a generic expression
1760 show_expression(Node *node, const char *qlabel,
1761 PlanState *planstate, List *ancestors,
1762 bool useprefix, ExplainState *es)
1767 /* Set up deparsing context */
1768 context = set_deparse_context_planstate(es->deparse_cxt,
1772 /* Deparse the expression */
1773 exprstr = deparse_expression(node, context, useprefix, false);
1775 /* And add to es->str */
1776 ExplainPropertyText(qlabel, exprstr, es);
1780 * Show a qualifier expression (which is a List with implicit AND semantics)
1783 show_qual(List *qual, const char *qlabel,
1784 PlanState *planstate, List *ancestors,
1785 bool useprefix, ExplainState *es)
1789 /* No work if empty qual */
1793 /* Convert AND list to explicit AND */
1794 node = (Node *) make_ands_explicit(qual);
1797 show_expression(node, qlabel, planstate, ancestors, useprefix, es);
1801 * Show a qualifier expression for a scan plan node
1804 show_scan_qual(List *qual, const char *qlabel,
1805 PlanState *planstate, List *ancestors,
1810 useprefix = (IsA(planstate->plan, SubqueryScan) ||es->verbose);
1811 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
1815 * Show a qualifier expression for an upper-level plan node
1818 show_upper_qual(List *qual, const char *qlabel,
1819 PlanState *planstate, List *ancestors,
1824 useprefix = (list_length(es->rtable) > 1 || es->verbose);
1825 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
1829 * Show the sort keys for a Sort node.
1832 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
1834 Sort *plan = (Sort *) sortstate->ss.ps.plan;
1836 show_sort_group_keys((PlanState *) sortstate, "Sort Key",
1837 plan->numCols, plan->sortColIdx,
1838 plan->sortOperators, plan->collations,
1844 * Likewise, for a MergeAppend node.
1847 show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
1850 MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
1852 show_sort_group_keys((PlanState *) mstate, "Sort Key",
1853 plan->numCols, plan->sortColIdx,
1854 plan->sortOperators, plan->collations,
1860 * Show the grouping keys for an Agg node.
1863 show_agg_keys(AggState *astate, List *ancestors,
1866 Agg *plan = (Agg *) astate->ss.ps.plan;
1868 if (plan->numCols > 0 || plan->groupingSets)
1870 /* The key columns refer to the tlist of the child plan */
1871 ancestors = lcons(astate, ancestors);
1873 if (plan->groupingSets)
1874 show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
1876 show_sort_group_keys(outerPlanState(astate), "Group Key",
1877 plan->numCols, plan->grpColIdx,
1881 ancestors = list_delete_first(ancestors);
1886 show_grouping_sets(PlanState *planstate, Agg *agg,
1887 List *ancestors, ExplainState *es)
1893 /* Set up deparsing context */
1894 context = set_deparse_context_planstate(es->deparse_cxt,
1897 useprefix = (list_length(es->rtable) > 1 || es->verbose);
1899 ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
1901 show_grouping_set_keys(planstate, agg, NULL,
1902 context, useprefix, ancestors, es);
1904 foreach(lc, agg->chain)
1906 Agg *aggnode = lfirst(lc);
1907 Sort *sortnode = (Sort *) aggnode->plan.lefttree;
1909 show_grouping_set_keys(planstate, aggnode, sortnode,
1910 context, useprefix, ancestors, es);
1913 ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
1917 show_grouping_set_keys(PlanState *planstate,
1918 Agg *aggnode, Sort *sortnode,
1919 List *context, bool useprefix,
1920 List *ancestors, ExplainState *es)
1922 Plan *plan = planstate->plan;
1925 List *gsets = aggnode->groupingSets;
1926 AttrNumber *keycols = aggnode->grpColIdx;
1928 ExplainOpenGroup("Grouping Set", NULL, true, es);
1932 show_sort_group_keys(planstate, "Sort Key",
1933 sortnode->numCols, sortnode->sortColIdx,
1934 sortnode->sortOperators, sortnode->collations,
1935 sortnode->nullsFirst,
1937 if (es->format == EXPLAIN_FORMAT_TEXT)
1941 ExplainOpenGroup("Group Keys", "Group Keys", false, es);
1948 foreach(lc2, (List *) lfirst(lc))
1950 Index i = lfirst_int(lc2);
1951 AttrNumber keyresno = keycols[i];
1952 TargetEntry *target = get_tle_by_resno(plan->targetlist,
1956 elog(ERROR, "no tlist entry for key %d", keyresno);
1957 /* Deparse the expression, showing any top-level cast */
1958 exprstr = deparse_expression((Node *) target->expr, context,
1961 result = lappend(result, exprstr);
1964 if (!result && es->format == EXPLAIN_FORMAT_TEXT)
1965 ExplainPropertyText("Group Key", "()", es);
1967 ExplainPropertyListNested("Group Key", result, es);
1970 ExplainCloseGroup("Group Keys", "Group Keys", false, es);
1972 if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
1975 ExplainCloseGroup("Grouping Set", NULL, true, es);
1979 * Show the grouping keys for a Group node.
1982 show_group_keys(GroupState *gstate, List *ancestors,
1985 Group *plan = (Group *) gstate->ss.ps.plan;
1987 /* The key columns refer to the tlist of the child plan */
1988 ancestors = lcons(gstate, ancestors);
1989 show_sort_group_keys(outerPlanState(gstate), "Group Key",
1990 plan->numCols, plan->grpColIdx,
1993 ancestors = list_delete_first(ancestors);
1997 * Common code to show sort/group keys, which are represented in plan nodes
1998 * as arrays of targetlist indexes. If it's a sort key rather than a group
1999 * key, also pass sort operators/collations/nullsFirst arrays.
2002 show_sort_group_keys(PlanState *planstate, const char *qlabel,
2003 int nkeys, AttrNumber *keycols,
2004 Oid *sortOperators, Oid *collations, bool *nullsFirst,
2005 List *ancestors, ExplainState *es)
2007 Plan *plan = planstate->plan;
2010 StringInfoData sortkeybuf;
2017 initStringInfo(&sortkeybuf);
2019 /* Set up deparsing context */
2020 context = set_deparse_context_planstate(es->deparse_cxt,
2023 useprefix = (list_length(es->rtable) > 1 || es->verbose);
2025 for (keyno = 0; keyno < nkeys; keyno++)
2027 /* find key expression in tlist */
2028 AttrNumber keyresno = keycols[keyno];
2029 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2034 elog(ERROR, "no tlist entry for key %d", keyresno);
2035 /* Deparse the expression, showing any top-level cast */
2036 exprstr = deparse_expression((Node *) target->expr, context,
2038 resetStringInfo(&sortkeybuf);
2039 appendStringInfoString(&sortkeybuf, exprstr);
2040 /* Append sort order information, if relevant */
2041 if (sortOperators != NULL)
2042 show_sortorder_options(&sortkeybuf,
2043 (Node *) target->expr,
2044 sortOperators[keyno],
2047 /* Emit one property-list item per sort key */
2048 result = lappend(result, pstrdup(sortkeybuf.data));
2051 ExplainPropertyList(qlabel, result, es);
2055 * Append nondefault characteristics of the sort ordering of a column to buf
2056 * (collation, direction, NULLS FIRST/LAST)
2059 show_sortorder_options(StringInfo buf, Node *sortexpr,
2060 Oid sortOperator, Oid collation, bool nullsFirst)
2062 Oid sortcoltype = exprType(sortexpr);
2063 bool reverse = false;
2064 TypeCacheEntry *typentry;
2066 typentry = lookup_type_cache(sortcoltype,
2067 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
2070 * Print COLLATE if it's not default. There are some cases where this is
2071 * redundant, eg if expression is a column whose declared collation is
2072 * that collation, but it's hard to distinguish that here.
2074 if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID)
2076 char *collname = get_collation_name(collation);
2078 if (collname == NULL)
2079 elog(ERROR, "cache lookup failed for collation %u", collation);
2080 appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2083 /* Print direction if not ASC, or USING if non-default sort operator */
2084 if (sortOperator == typentry->gt_opr)
2086 appendStringInfoString(buf, " DESC");
2089 else if (sortOperator != typentry->lt_opr)
2091 char *opname = get_opname(sortOperator);
2094 elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2095 appendStringInfo(buf, " USING %s", opname);
2096 /* Determine whether operator would be considered ASC or DESC */
2097 (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2100 /* Add NULLS FIRST/LAST only if it wouldn't be default */
2101 if (nullsFirst && !reverse)
2103 appendStringInfoString(buf, " NULLS FIRST");
2105 else if (!nullsFirst && reverse)
2107 appendStringInfoString(buf, " NULLS LAST");
2112 * Show TABLESAMPLE properties
2115 show_tablesample(TableSampleClause *tsc, PlanState *planstate,
2116 List *ancestors, ExplainState *es)
2125 /* Set up deparsing context */
2126 context = set_deparse_context_planstate(es->deparse_cxt,
2129 useprefix = list_length(es->rtable) > 1;
2131 /* Get the tablesample method name */
2132 method_name = get_func_name(tsc->tsmhandler);
2134 /* Deparse parameter expressions */
2135 foreach(lc, tsc->args)
2137 Node *arg = (Node *) lfirst(lc);
2139 params = lappend(params,
2140 deparse_expression(arg, context,
2143 if (tsc->repeatable)
2144 repeatable = deparse_expression((Node *) tsc->repeatable, context,
2150 if (es->format == EXPLAIN_FORMAT_TEXT)
2154 appendStringInfoSpaces(es->str, es->indent * 2);
2155 appendStringInfo(es->str, "Sampling: %s (", method_name);
2159 appendStringInfoString(es->str, ", ");
2160 appendStringInfoString(es->str, (const char *) lfirst(lc));
2163 appendStringInfoChar(es->str, ')');
2165 appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
2166 appendStringInfoChar(es->str, '\n');
2170 ExplainPropertyText("Sampling Method", method_name, es);
2171 ExplainPropertyList("Sampling Parameters", params, es);
2173 ExplainPropertyText("Repeatable Seed", repeatable, es);
2178 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
2181 show_sort_info(SortState *sortstate, ExplainState *es)
2183 if (es->analyze && sortstate->sort_Done &&
2184 sortstate->tuplesortstate != NULL)
2186 Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
2187 const char *sortMethod;
2188 const char *spaceType;
2191 tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
2193 if (es->format == EXPLAIN_FORMAT_TEXT)
2195 appendStringInfoSpaces(es->str, es->indent * 2);
2196 appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n",
2197 sortMethod, spaceType, spaceUsed);
2201 ExplainPropertyText("Sort Method", sortMethod, es);
2202 ExplainPropertyLong("Sort Space Used", spaceUsed, es);
2203 ExplainPropertyText("Sort Space Type", spaceType, es);
2209 * Show information on hash buckets/batches.
2212 show_hash_info(HashState *hashstate, ExplainState *es)
2214 HashJoinTable hashtable;
2216 hashtable = hashstate->hashtable;
2220 long spacePeakKb = (hashtable->spacePeak + 1023) / 1024;
2222 if (es->format != EXPLAIN_FORMAT_TEXT)
2224 ExplainPropertyLong("Hash Buckets", hashtable->nbuckets, es);
2225 ExplainPropertyLong("Original Hash Buckets",
2226 hashtable->nbuckets_original, es);
2227 ExplainPropertyLong("Hash Batches", hashtable->nbatch, es);
2228 ExplainPropertyLong("Original Hash Batches",
2229 hashtable->nbatch_original, es);
2230 ExplainPropertyLong("Peak Memory Usage", spacePeakKb, es);
2232 else if (hashtable->nbatch_original != hashtable->nbatch ||
2233 hashtable->nbuckets_original != hashtable->nbuckets)
2235 appendStringInfoSpaces(es->str, es->indent * 2);
2236 appendStringInfo(es->str,
2237 "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: %ldkB\n",
2238 hashtable->nbuckets,
2239 hashtable->nbuckets_original,
2241 hashtable->nbatch_original,
2246 appendStringInfoSpaces(es->str, es->indent * 2);
2247 appendStringInfo(es->str,
2248 "Buckets: %d Batches: %d Memory Usage: %ldkB\n",
2249 hashtable->nbuckets, hashtable->nbatch,
2256 * If it's EXPLAIN ANALYZE, show exact/lossy pages for a BitmapHeapScan node
2259 show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
2261 if (es->format != EXPLAIN_FORMAT_TEXT)
2263 ExplainPropertyLong("Exact Heap Blocks", planstate->exact_pages, es);
2264 ExplainPropertyLong("Lossy Heap Blocks", planstate->lossy_pages, es);
2268 if (planstate->exact_pages > 0 || planstate->lossy_pages > 0)
2270 appendStringInfoSpaces(es->str, es->indent * 2);
2271 appendStringInfoString(es->str, "Heap Blocks:");
2272 if (planstate->exact_pages > 0)
2273 appendStringInfo(es->str, " exact=%ld", planstate->exact_pages);
2274 if (planstate->lossy_pages > 0)
2275 appendStringInfo(es->str, " lossy=%ld", planstate->lossy_pages);
2276 appendStringInfoChar(es->str, '\n');
2282 * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
2284 * "which" identifies which instrumentation counter to print
2287 show_instrumentation_count(const char *qlabel, int which,
2288 PlanState *planstate, ExplainState *es)
2293 if (!es->analyze || !planstate->instrument)
2297 nfiltered = planstate->instrument->nfiltered2;
2299 nfiltered = planstate->instrument->nfiltered1;
2300 nloops = planstate->instrument->nloops;
2302 /* In text mode, suppress zero counts; they're not interesting enough */
2303 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
2306 ExplainPropertyFloat(qlabel, nfiltered / nloops, 0, es);
2308 ExplainPropertyFloat(qlabel, 0.0, 0, es);
2313 * Show extra information for a ForeignScan node.
2316 show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
2318 FdwRoutine *fdwroutine = fsstate->fdwroutine;
2320 /* Let the FDW emit whatever fields it wants */
2321 if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
2323 if (fdwroutine->ExplainDirectModify != NULL)
2324 fdwroutine->ExplainDirectModify(fsstate, es);
2328 if (fdwroutine->ExplainForeignScan != NULL)
2329 fdwroutine->ExplainForeignScan(fsstate, es);
2334 * Fetch the name of an index in an EXPLAIN
2336 * We allow plugins to get control here so that plans involving hypothetical
2337 * indexes can be explained.
2340 explain_get_index_name(Oid indexId)
2344 if (explain_get_index_name_hook)
2345 result = (*explain_get_index_name_hook) (indexId);
2350 /* default behavior: look in the catalogs and quote it */
2351 result = get_rel_name(indexId);
2353 elog(ERROR, "cache lookup failed for index %u", indexId);
2354 result = quote_identifier(result);
2360 * Show buffer usage details.
2363 show_buffer_usage(ExplainState *es, const BufferUsage *usage)
2365 if (es->format == EXPLAIN_FORMAT_TEXT)
2367 bool has_shared = (usage->shared_blks_hit > 0 ||
2368 usage->shared_blks_read > 0 ||
2369 usage->shared_blks_dirtied > 0 ||
2370 usage->shared_blks_written > 0);
2371 bool has_local = (usage->local_blks_hit > 0 ||
2372 usage->local_blks_read > 0 ||
2373 usage->local_blks_dirtied > 0 ||
2374 usage->local_blks_written > 0);
2375 bool has_temp = (usage->temp_blks_read > 0 ||
2376 usage->temp_blks_written > 0);
2377 bool has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
2378 !INSTR_TIME_IS_ZERO(usage->blk_write_time));
2380 /* Show only positive counter values. */
2381 if (has_shared || has_local || has_temp)
2383 appendStringInfoSpaces(es->str, es->indent * 2);
2384 appendStringInfoString(es->str, "Buffers:");
2388 appendStringInfoString(es->str, " shared");
2389 if (usage->shared_blks_hit > 0)
2390 appendStringInfo(es->str, " hit=%ld",
2391 usage->shared_blks_hit);
2392 if (usage->shared_blks_read > 0)
2393 appendStringInfo(es->str, " read=%ld",
2394 usage->shared_blks_read);
2395 if (usage->shared_blks_dirtied > 0)
2396 appendStringInfo(es->str, " dirtied=%ld",
2397 usage->shared_blks_dirtied);
2398 if (usage->shared_blks_written > 0)
2399 appendStringInfo(es->str, " written=%ld",
2400 usage->shared_blks_written);
2401 if (has_local || has_temp)
2402 appendStringInfoChar(es->str, ',');
2406 appendStringInfoString(es->str, " local");
2407 if (usage->local_blks_hit > 0)
2408 appendStringInfo(es->str, " hit=%ld",
2409 usage->local_blks_hit);
2410 if (usage->local_blks_read > 0)
2411 appendStringInfo(es->str, " read=%ld",
2412 usage->local_blks_read);
2413 if (usage->local_blks_dirtied > 0)
2414 appendStringInfo(es->str, " dirtied=%ld",
2415 usage->local_blks_dirtied);
2416 if (usage->local_blks_written > 0)
2417 appendStringInfo(es->str, " written=%ld",
2418 usage->local_blks_written);
2420 appendStringInfoChar(es->str, ',');
2424 appendStringInfoString(es->str, " temp");
2425 if (usage->temp_blks_read > 0)
2426 appendStringInfo(es->str, " read=%ld",
2427 usage->temp_blks_read);
2428 if (usage->temp_blks_written > 0)
2429 appendStringInfo(es->str, " written=%ld",
2430 usage->temp_blks_written);
2432 appendStringInfoChar(es->str, '\n');
2435 /* As above, show only positive counter values. */
2438 appendStringInfoSpaces(es->str, es->indent * 2);
2439 appendStringInfoString(es->str, "I/O Timings:");
2440 if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
2441 appendStringInfo(es->str, " read=%0.3f",
2442 INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
2443 if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
2444 appendStringInfo(es->str, " write=%0.3f",
2445 INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
2446 appendStringInfoChar(es->str, '\n');
2451 ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
2452 ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
2453 ExplainPropertyLong("Shared Dirtied Blocks", usage->shared_blks_dirtied, es);
2454 ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
2455 ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
2456 ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
2457 ExplainPropertyLong("Local Dirtied Blocks", usage->local_blks_dirtied, es);
2458 ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
2459 ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
2460 ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
2461 if (track_io_timing)
2463 ExplainPropertyFloat("I/O Read Time", INSTR_TIME_GET_MILLISEC(usage->blk_read_time), 3, es);
2464 ExplainPropertyFloat("I/O Write Time", INSTR_TIME_GET_MILLISEC(usage->blk_write_time), 3, es);
2470 * Add some additional details about an IndexScan or IndexOnlyScan
2473 ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
2476 const char *indexname = explain_get_index_name(indexid);
2478 if (es->format == EXPLAIN_FORMAT_TEXT)
2480 if (ScanDirectionIsBackward(indexorderdir))
2481 appendStringInfoString(es->str, " Backward");
2482 appendStringInfo(es->str, " using %s", indexname);
2486 const char *scandir;
2488 switch (indexorderdir)
2490 case BackwardScanDirection:
2491 scandir = "Backward";
2493 case NoMovementScanDirection:
2494 scandir = "NoMovement";
2496 case ForwardScanDirection:
2497 scandir = "Forward";
2503 ExplainPropertyText("Scan Direction", scandir, es);
2504 ExplainPropertyText("Index Name", indexname, es);
2509 * Show the target of a Scan node
2512 ExplainScanTarget(Scan *plan, ExplainState *es)
2514 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
2518 * Show the target of a ModifyTable node
2520 * Here we show the nominal target (ie, the relation that was named in the
2521 * original query). If the actual target(s) is/are different, we'll show them
2522 * in show_modifytable_info().
2525 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
2527 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
2531 * Show the target relation of a scan or modify node
2534 ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
2536 char *objectname = NULL;
2537 char *namespace = NULL;
2538 const char *objecttag = NULL;
2542 rte = rt_fetch(rti, es->rtable);
2543 refname = (char *) list_nth(es->rtable_names, rti - 1);
2544 if (refname == NULL)
2545 refname = rte->eref->aliasname;
2547 switch (nodeTag(plan))
2552 case T_IndexOnlyScan:
2553 case T_BitmapHeapScan:
2558 /* Assert it's on a real relation */
2559 Assert(rte->rtekind == RTE_RELATION);
2560 objectname = get_rel_name(rte->relid);
2562 namespace = get_namespace_name(get_rel_namespace(rte->relid));
2563 objecttag = "Relation Name";
2565 case T_FunctionScan:
2567 FunctionScan *fscan = (FunctionScan *) plan;
2569 /* Assert it's on a RangeFunction */
2570 Assert(rte->rtekind == RTE_FUNCTION);
2573 * If the expression is still a function call of a single
2574 * function, we can get the real name of the function.
2575 * Otherwise, punt. (Even if it was a single function call
2576 * originally, the optimizer could have simplified it away.)
2578 if (list_length(fscan->functions) == 1)
2580 RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
2582 if (IsA(rtfunc->funcexpr, FuncExpr))
2584 FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
2585 Oid funcid = funcexpr->funcid;
2587 objectname = get_func_name(funcid);
2590 get_namespace_name(get_func_namespace(funcid));
2593 objecttag = "Function Name";
2597 Assert(rte->rtekind == RTE_VALUES);
2600 /* Assert it's on a non-self-reference CTE */
2601 Assert(rte->rtekind == RTE_CTE);
2602 Assert(!rte->self_reference);
2603 objectname = rte->ctename;
2604 objecttag = "CTE Name";
2606 case T_WorkTableScan:
2607 /* Assert it's on a self-reference CTE */
2608 Assert(rte->rtekind == RTE_CTE);
2609 Assert(rte->self_reference);
2610 objectname = rte->ctename;
2611 objecttag = "CTE Name";
2617 if (es->format == EXPLAIN_FORMAT_TEXT)
2619 appendStringInfoString(es->str, " on");
2620 if (namespace != NULL)
2621 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
2622 quote_identifier(objectname));
2623 else if (objectname != NULL)
2624 appendStringInfo(es->str, " %s", quote_identifier(objectname));
2625 if (objectname == NULL || strcmp(refname, objectname) != 0)
2626 appendStringInfo(es->str, " %s", quote_identifier(refname));
2630 if (objecttag != NULL && objectname != NULL)
2631 ExplainPropertyText(objecttag, objectname, es);
2632 if (namespace != NULL)
2633 ExplainPropertyText("Schema", namespace, es);
2634 ExplainPropertyText("Alias", refname, es);
2639 * Show extra information for a ModifyTable node
2641 * We have three objectives here. First, if there's more than one target
2642 * table or it's different from the nominal target, identify the actual
2643 * target(s). Second, give FDWs a chance to display extra info about foreign
2644 * targets. Third, show information about ON CONFLICT.
2647 show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
2650 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
2651 const char *operation;
2652 const char *foperation;
2655 List *idxNames = NIL;
2658 switch (node->operation)
2661 operation = "Insert";
2662 foperation = "Foreign Insert";
2665 operation = "Update";
2666 foperation = "Foreign Update";
2669 operation = "Delete";
2670 foperation = "Foreign Delete";
2674 foperation = "Foreign ???";
2678 /* Should we explicitly label target relations? */
2679 labeltargets = (mtstate->mt_nplans > 1 ||
2680 (mtstate->mt_nplans == 1 &&
2681 mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation));
2684 ExplainOpenGroup("Target Tables", "Target Tables", false, es);
2686 for (j = 0; j < mtstate->mt_nplans; j++)
2688 ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
2689 FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
2693 /* Open a group for this target */
2694 ExplainOpenGroup("Target Table", NULL, true, es);
2697 * In text mode, decorate each target with operation type, so that
2698 * ExplainTargetRel's output of " on foo" will read nicely.
2700 if (es->format == EXPLAIN_FORMAT_TEXT)
2702 appendStringInfoSpaces(es->str, es->indent * 2);
2703 appendStringInfoString(es->str,
2704 fdwroutine ? foperation : operation);
2707 /* Identify target */
2708 ExplainTargetRel((Plan *) node,
2709 resultRelInfo->ri_RangeTableIndex,
2712 if (es->format == EXPLAIN_FORMAT_TEXT)
2714 appendStringInfoChar(es->str, '\n');
2719 /* Give FDW a chance if needed */
2720 if (!resultRelInfo->ri_usesFdwDirectModify &&
2721 fdwroutine != NULL &&
2722 fdwroutine->ExplainForeignModify != NULL)
2724 List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
2726 fdwroutine->ExplainForeignModify(mtstate,
2735 /* Undo the indentation we added in text format */
2736 if (es->format == EXPLAIN_FORMAT_TEXT)
2739 /* Close the group */
2740 ExplainCloseGroup("Target Table", NULL, true, es);
2744 /* Gather names of ON CONFLICT arbiter indexes */
2745 foreach(lst, node->arbiterIndexes)
2747 char *indexname = get_rel_name(lfirst_oid(lst));
2749 idxNames = lappend(idxNames, indexname);
2752 if (node->onConflictAction != ONCONFLICT_NONE)
2754 ExplainProperty("Conflict Resolution",
2755 node->onConflictAction == ONCONFLICT_NOTHING ?
2756 "NOTHING" : "UPDATE",
2760 * Don't display arbiter indexes at all when DO NOTHING variant
2761 * implicitly ignores all conflicts
2764 ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
2766 /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
2767 if (node->onConflictWhere)
2769 show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
2770 &mtstate->ps, ancestors, es);
2771 show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
2774 /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
2775 if (es->analyze && mtstate->ps.instrument)
2781 InstrEndLoop(mtstate->mt_plans[0]->instrument);
2783 /* count the number of source rows */
2784 total = mtstate->mt_plans[0]->instrument->ntuples;
2785 other_path = mtstate->ps.instrument->nfiltered2;
2786 insert_path = total - other_path;
2788 ExplainPropertyFloat("Tuples Inserted", insert_path, 0, es);
2789 ExplainPropertyFloat("Conflicting Tuples", other_path, 0, es);
2794 ExplainCloseGroup("Target Tables", "Target Tables", false, es);
2798 * Explain the constituent plans of a ModifyTable, Append, MergeAppend,
2799 * BitmapAnd, or BitmapOr node.
2801 * The ancestors list should already contain the immediate parent of these
2804 * Note: we don't actually need to examine the Plan list members, but
2805 * we need the list in order to determine the length of the PlanState array.
2808 ExplainMemberNodes(List *plans, PlanState **planstates,
2809 List *ancestors, ExplainState *es)
2811 int nplans = list_length(plans);
2814 for (j = 0; j < nplans; j++)
2815 ExplainNode(planstates[j], ancestors,
2816 "Member", NULL, es);
2820 * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
2822 * The ancestors list should already contain the immediate parent of these
2826 ExplainSubPlans(List *plans, List *ancestors,
2827 const char *relationship, ExplainState *es)
2833 SubPlanState *sps = (SubPlanState *) lfirst(lst);
2834 SubPlan *sp = (SubPlan *) sps->xprstate.expr;
2837 * There can be multiple SubPlan nodes referencing the same physical
2838 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
2839 * We should print a subplan only once, so track which ones we already
2840 * printed. This state must be global across the plan tree, since the
2841 * duplicate nodes could be in different plan nodes, eg both a bitmap
2842 * indexscan's indexqual and its parent heapscan's recheck qual. (We
2843 * do not worry too much about which plan node we show the subplan as
2844 * attached to in such cases.)
2846 if (bms_is_member(sp->plan_id, es->printed_subplans))
2848 es->printed_subplans = bms_add_member(es->printed_subplans,
2851 ExplainNode(sps->planstate, ancestors,
2852 relationship, sp->plan_name, es);
2857 * Explain a list of children of a CustomScan.
2860 ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
2864 (list_length(css->custom_ps) != 1 ? "children" : "child");
2866 foreach(cell, css->custom_ps)
2867 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
2871 * Explain a property, such as sort keys or targets, that takes the form of
2872 * a list of unlabeled items. "data" is a list of C strings.
2875 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
2882 case EXPLAIN_FORMAT_TEXT:
2883 appendStringInfoSpaces(es->str, es->indent * 2);
2884 appendStringInfo(es->str, "%s: ", qlabel);
2888 appendStringInfoString(es->str, ", ");
2889 appendStringInfoString(es->str, (const char *) lfirst(lc));
2892 appendStringInfoChar(es->str, '\n');
2895 case EXPLAIN_FORMAT_XML:
2896 ExplainXMLTag(qlabel, X_OPENING, es);
2901 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
2902 appendStringInfoString(es->str, "<Item>");
2903 str = escape_xml((const char *) lfirst(lc));
2904 appendStringInfoString(es->str, str);
2906 appendStringInfoString(es->str, "</Item>\n");
2908 ExplainXMLTag(qlabel, X_CLOSING, es);
2911 case EXPLAIN_FORMAT_JSON:
2912 ExplainJSONLineEnding(es);
2913 appendStringInfoSpaces(es->str, es->indent * 2);
2914 escape_json(es->str, qlabel);
2915 appendStringInfoString(es->str, ": [");
2919 appendStringInfoString(es->str, ", ");
2920 escape_json(es->str, (const char *) lfirst(lc));
2923 appendStringInfoChar(es->str, ']');
2926 case EXPLAIN_FORMAT_YAML:
2927 ExplainYAMLLineStarting(es);
2928 appendStringInfo(es->str, "%s: ", qlabel);
2931 appendStringInfoChar(es->str, '\n');
2932 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
2933 appendStringInfoString(es->str, "- ");
2934 escape_yaml(es->str, (const char *) lfirst(lc));
2941 * Explain a property that takes the form of a list of unlabeled items within
2942 * another list. "data" is a list of C strings.
2945 ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
2952 case EXPLAIN_FORMAT_TEXT:
2953 case EXPLAIN_FORMAT_XML:
2954 ExplainPropertyList(qlabel, data, es);
2957 case EXPLAIN_FORMAT_JSON:
2958 ExplainJSONLineEnding(es);
2959 appendStringInfoSpaces(es->str, es->indent * 2);
2960 appendStringInfoChar(es->str, '[');
2964 appendStringInfoString(es->str, ", ");
2965 escape_json(es->str, (const char *) lfirst(lc));
2968 appendStringInfoChar(es->str, ']');
2971 case EXPLAIN_FORMAT_YAML:
2972 ExplainYAMLLineStarting(es);
2973 appendStringInfoString(es->str, "- [");
2977 appendStringInfoString(es->str, ", ");
2978 escape_yaml(es->str, (const char *) lfirst(lc));
2981 appendStringInfoChar(es->str, ']');
2987 * Explain a simple property.
2989 * If "numeric" is true, the value is a number (or other value that
2990 * doesn't need quoting in JSON).
2992 * This usually should not be invoked directly, but via one of the datatype
2993 * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
2996 ExplainProperty(const char *qlabel, const char *value, bool numeric,
3001 case EXPLAIN_FORMAT_TEXT:
3002 appendStringInfoSpaces(es->str, es->indent * 2);
3003 appendStringInfo(es->str, "%s: %s\n", qlabel, value);
3006 case EXPLAIN_FORMAT_XML:
3010 appendStringInfoSpaces(es->str, es->indent * 2);
3011 ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
3012 str = escape_xml(value);
3013 appendStringInfoString(es->str, str);
3015 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
3016 appendStringInfoChar(es->str, '\n');
3020 case EXPLAIN_FORMAT_JSON:
3021 ExplainJSONLineEnding(es);
3022 appendStringInfoSpaces(es->str, es->indent * 2);
3023 escape_json(es->str, qlabel);
3024 appendStringInfoString(es->str, ": ");
3026 appendStringInfoString(es->str, value);
3028 escape_json(es->str, value);
3031 case EXPLAIN_FORMAT_YAML:
3032 ExplainYAMLLineStarting(es);
3033 appendStringInfo(es->str, "%s: ", qlabel);
3035 appendStringInfoString(es->str, value);
3037 escape_yaml(es->str, value);
3043 * Explain a string-valued property.
3046 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
3048 ExplainProperty(qlabel, value, false, es);
3052 * Explain an integer-valued property.
3055 ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
3059 snprintf(buf, sizeof(buf), "%d", value);
3060 ExplainProperty(qlabel, buf, true, es);
3064 * Explain a long-integer-valued property.
3067 ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
3071 snprintf(buf, sizeof(buf), "%ld", value);
3072 ExplainProperty(qlabel, buf, true, es);
3076 * Explain a float-valued property, using the specified number of
3077 * fractional digits.
3080 ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
3085 snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
3086 ExplainProperty(qlabel, buf, true, es);
3090 * Explain a bool-valued property.
3093 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
3095 ExplainProperty(qlabel, value ? "true" : "false", true, es);
3099 * Open a group of related objects.
3101 * objtype is the type of the group object, labelname is its label within
3102 * a containing object (if any).
3104 * If labeled is true, the group members will be labeled properties,
3105 * while if it's false, they'll be unlabeled objects.
3108 ExplainOpenGroup(const char *objtype, const char *labelname,
3109 bool labeled, ExplainState *es)
3113 case EXPLAIN_FORMAT_TEXT:
3117 case EXPLAIN_FORMAT_XML:
3118 ExplainXMLTag(objtype, X_OPENING, es);
3122 case EXPLAIN_FORMAT_JSON:
3123 ExplainJSONLineEnding(es);
3124 appendStringInfoSpaces(es->str, 2 * es->indent);
3127 escape_json(es->str, labelname);
3128 appendStringInfoString(es->str, ": ");
3130 appendStringInfoChar(es->str, labeled ? '{' : '[');
3133 * In JSON format, the grouping_stack is an integer list. 0 means
3134 * we've emitted nothing at this grouping level, 1 means we've
3135 * emitted something (and so the next item needs a comma). See
3136 * ExplainJSONLineEnding().
3138 es->grouping_stack = lcons_int(0, es->grouping_stack);
3142 case EXPLAIN_FORMAT_YAML:
3145 * In YAML format, the grouping stack is an integer list. 0 means
3146 * we've emitted nothing at this grouping level AND this grouping
3147 * level is unlabelled and must be marked with "- ". See
3148 * ExplainYAMLLineStarting().
3150 ExplainYAMLLineStarting(es);
3153 appendStringInfo(es->str, "%s: ", labelname);
3154 es->grouping_stack = lcons_int(1, es->grouping_stack);
3158 appendStringInfoString(es->str, "- ");
3159 es->grouping_stack = lcons_int(0, es->grouping_stack);
3167 * Close a group of related objects.
3168 * Parameters must match the corresponding ExplainOpenGroup call.
3171 ExplainCloseGroup(const char *objtype, const char *labelname,
3172 bool labeled, ExplainState *es)
3176 case EXPLAIN_FORMAT_TEXT:
3180 case EXPLAIN_FORMAT_XML:
3182 ExplainXMLTag(objtype, X_CLOSING, es);
3185 case EXPLAIN_FORMAT_JSON:
3187 appendStringInfoChar(es->str, '\n');
3188 appendStringInfoSpaces(es->str, 2 * es->indent);
3189 appendStringInfoChar(es->str, labeled ? '}' : ']');
3190 es->grouping_stack = list_delete_first(es->grouping_stack);
3193 case EXPLAIN_FORMAT_YAML:
3195 es->grouping_stack = list_delete_first(es->grouping_stack);
3201 * Emit a "dummy" group that never has any members.
3203 * objtype is the type of the group object, labelname is its label within
3204 * a containing object (if any).
3207 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
3211 case EXPLAIN_FORMAT_TEXT:
3215 case EXPLAIN_FORMAT_XML:
3216 ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
3219 case EXPLAIN_FORMAT_JSON:
3220 ExplainJSONLineEnding(es);
3221 appendStringInfoSpaces(es->str, 2 * es->indent);
3224 escape_json(es->str, labelname);
3225 appendStringInfoString(es->str, ": ");
3227 escape_json(es->str, objtype);
3230 case EXPLAIN_FORMAT_YAML:
3231 ExplainYAMLLineStarting(es);
3234 escape_yaml(es->str, labelname);
3235 appendStringInfoString(es->str, ": ");
3239 appendStringInfoString(es->str, "- ");
3241 escape_yaml(es->str, objtype);
3247 * Emit the start-of-output boilerplate.
3249 * This is just enough different from processing a subgroup that we need
3250 * a separate pair of subroutines.
3253 ExplainBeginOutput(ExplainState *es)
3257 case EXPLAIN_FORMAT_TEXT:
3261 case EXPLAIN_FORMAT_XML:
3262 appendStringInfoString(es->str,
3263 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
3267 case EXPLAIN_FORMAT_JSON:
3268 /* top-level structure is an array of plans */
3269 appendStringInfoChar(es->str, '[');
3270 es->grouping_stack = lcons_int(0, es->grouping_stack);
3274 case EXPLAIN_FORMAT_YAML:
3275 es->grouping_stack = lcons_int(0, es->grouping_stack);
3281 * Emit the end-of-output boilerplate.
3284 ExplainEndOutput(ExplainState *es)
3288 case EXPLAIN_FORMAT_TEXT:
3292 case EXPLAIN_FORMAT_XML:
3294 appendStringInfoString(es->str, "</explain>");
3297 case EXPLAIN_FORMAT_JSON:
3299 appendStringInfoString(es->str, "\n]");
3300 es->grouping_stack = list_delete_first(es->grouping_stack);
3303 case EXPLAIN_FORMAT_YAML:
3304 es->grouping_stack = list_delete_first(es->grouping_stack);
3310 * Put an appropriate separator between multiple plans
3313 ExplainSeparatePlans(ExplainState *es)
3317 case EXPLAIN_FORMAT_TEXT:
3318 /* add a blank line */
3319 appendStringInfoChar(es->str, '\n');
3322 case EXPLAIN_FORMAT_XML:
3323 case EXPLAIN_FORMAT_JSON:
3324 case EXPLAIN_FORMAT_YAML:
3331 * Emit opening or closing XML tag.
3333 * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
3334 * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
3337 * XML restricts tag names more than our other output formats, eg they can't
3338 * contain white space or slashes. Replace invalid characters with dashes,
3339 * so that for example "I/O Read Time" becomes "I-O-Read-Time".
3342 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
3345 const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
3347 if ((flags & X_NOWHITESPACE) == 0)
3348 appendStringInfoSpaces(es->str, 2 * es->indent);
3349 appendStringInfoCharMacro(es->str, '<');
3350 if ((flags & X_CLOSING) != 0)
3351 appendStringInfoCharMacro(es->str, '/');
3352 for (s = tagname; *s; s++)
3353 appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
3354 if ((flags & X_CLOSE_IMMEDIATE) != 0)
3355 appendStringInfoString(es->str, " /");
3356 appendStringInfoCharMacro(es->str, '>');
3357 if ((flags & X_NOWHITESPACE) == 0)
3358 appendStringInfoCharMacro(es->str, '\n');
3362 * Emit a JSON line ending.
3364 * JSON requires a comma after each property but the last. To facilitate this,
3365 * in JSON format, the text emitted for each property begins just prior to the
3366 * preceding line-break (and comma, if applicable).
3369 ExplainJSONLineEnding(ExplainState *es)
3371 Assert(es->format == EXPLAIN_FORMAT_JSON);
3372 if (linitial_int(es->grouping_stack) != 0)
3373 appendStringInfoChar(es->str, ',');
3375 linitial_int(es->grouping_stack) = 1;
3376 appendStringInfoChar(es->str, '\n');
3380 * Indent a YAML line.
3382 * YAML lines are ordinarily indented by two spaces per indentation level.
3383 * The text emitted for each property begins just prior to the preceding
3384 * line-break, except for the first property in an unlabelled group, for which
3385 * it begins immediately after the "- " that introduces the group. The first
3386 * property of the group appears on the same line as the opening "- ".
3389 ExplainYAMLLineStarting(ExplainState *es)
3391 Assert(es->format == EXPLAIN_FORMAT_YAML);
3392 if (linitial_int(es->grouping_stack) == 0)
3394 linitial_int(es->grouping_stack) = 1;
3398 appendStringInfoChar(es->str, '\n');
3399 appendStringInfoSpaces(es->str, es->indent * 2);
3404 * YAML is a superset of JSON; unfortunately, the YAML quoting rules are
3405 * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
3406 * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
3407 * Empty strings, strings with leading or trailing whitespace, and strings
3408 * containing a variety of special characters must certainly be quoted or the
3409 * output is invalid; and other seemingly harmless strings like "0xa" or
3410 * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
3411 * constant rather than a string.
3414 escape_yaml(StringInfo buf, const char *str)
3416 escape_json(buf, str);