1 /*-------------------------------------------------------------------------
4 * Explain query execution plans
6 * Portions Copyright (c) 1996-2019, 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_type.h"
18 #include "commands/createas.h"
19 #include "commands/defrem.h"
20 #include "commands/prepare.h"
21 #include "executor/nodeHash.h"
22 #include "foreign/fdwapi.h"
24 #include "nodes/extensible.h"
25 #include "nodes/makefuncs.h"
26 #include "nodes/nodeFuncs.h"
27 #include "optimizer/clauses.h"
28 #include "optimizer/planmain.h"
29 #include "parser/parsetree.h"
30 #include "rewrite/rewriteHandler.h"
31 #include "storage/bufmgr.h"
32 #include "tcop/tcopprot.h"
33 #include "utils/builtins.h"
34 #include "utils/json.h"
35 #include "utils/lsyscache.h"
36 #include "utils/rel.h"
37 #include "utils/ruleutils.h"
38 #include "utils/snapmgr.h"
39 #include "utils/tuplesort.h"
40 #include "utils/typcache.h"
41 #include "utils/xml.h"
44 /* Hook for plugins to get control in ExplainOneQuery() */
45 ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
47 /* Hook for plugins to get control in explain_get_index_name() */
48 explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
51 /* OR-able flags for ExplainXMLTag() */
54 #define X_CLOSE_IMMEDIATE 2
55 #define X_NOWHITESPACE 4
57 static void ExplainOneQuery(Query *query, int cursorOptions,
58 IntoClause *into, ExplainState *es,
59 const char *queryString, ParamListInfo params,
60 QueryEnvironment *queryEnv);
61 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
63 static double elapsed_time(instr_time *starttime);
64 static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
65 static void ExplainNode(PlanState *planstate, List *ancestors,
66 const char *relationship, const char *plan_name,
68 static void show_plan_tlist(PlanState *planstate, List *ancestors,
70 static void show_expression(Node *node, const char *qlabel,
71 PlanState *planstate, List *ancestors,
72 bool useprefix, ExplainState *es);
73 static void show_qual(List *qual, const char *qlabel,
74 PlanState *planstate, List *ancestors,
75 bool useprefix, ExplainState *es);
76 static void show_scan_qual(List *qual, const char *qlabel,
77 PlanState *planstate, List *ancestors,
79 static void show_upper_qual(List *qual, const char *qlabel,
80 PlanState *planstate, List *ancestors,
82 static void show_sort_keys(SortState *sortstate, List *ancestors,
84 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
86 static void show_agg_keys(AggState *astate, List *ancestors,
88 static void show_grouping_sets(PlanState *planstate, Agg *agg,
89 List *ancestors, ExplainState *es);
90 static void show_grouping_set_keys(PlanState *planstate,
91 Agg *aggnode, Sort *sortnode,
92 List *context, bool useprefix,
93 List *ancestors, ExplainState *es);
94 static void show_group_keys(GroupState *gstate, List *ancestors,
96 static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
97 int nkeys, AttrNumber *keycols,
98 Oid *sortOperators, Oid *collations, bool *nullsFirst,
99 List *ancestors, ExplainState *es);
100 static void show_sortorder_options(StringInfo buf, Node *sortexpr,
101 Oid sortOperator, Oid collation, bool nullsFirst);
102 static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
103 List *ancestors, ExplainState *es);
104 static void show_sort_info(SortState *sortstate, ExplainState *es);
105 static void show_hash_info(HashState *hashstate, ExplainState *es);
106 static void show_tidbitmap_info(BitmapHeapScanState *planstate,
108 static void show_instrumentation_count(const char *qlabel, int which,
109 PlanState *planstate, ExplainState *es);
110 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
111 static void show_eval_params(Bitmapset *bms_params, ExplainState *es);
112 static const char *explain_get_index_name(Oid indexId);
113 static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
114 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
116 static void ExplainScanTarget(Scan *plan, ExplainState *es);
117 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
118 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
119 static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
121 static void ExplainMemberNodes(PlanState **planstates, int nsubnodes,
122 int nplans, List *ancestors, ExplainState *es);
123 static void ExplainSubPlans(List *plans, List *ancestors,
124 const char *relationship, ExplainState *es);
125 static void ExplainCustomChildren(CustomScanState *css,
126 List *ancestors, ExplainState *es);
127 static void ExplainProperty(const char *qlabel, const char *unit,
128 const char *value, bool numeric, ExplainState *es);
129 static void ExplainDummyGroup(const char *objtype, const char *labelname,
131 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
132 static void ExplainJSONLineEnding(ExplainState *es);
133 static void ExplainYAMLLineStarting(ExplainState *es);
134 static void escape_yaml(StringInfo buf, const char *str);
140 * execute an EXPLAIN command
143 ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
144 ParamListInfo params, QueryEnvironment *queryEnv,
147 ExplainState *es = NewExplainState();
148 TupOutputState *tstate;
151 bool timing_set = false;
152 bool summary_set = false;
154 /* Parse options list. */
155 foreach(lc, stmt->options)
157 DefElem *opt = (DefElem *) lfirst(lc);
159 if (strcmp(opt->defname, "analyze") == 0)
160 es->analyze = defGetBoolean(opt);
161 else if (strcmp(opt->defname, "verbose") == 0)
162 es->verbose = defGetBoolean(opt);
163 else if (strcmp(opt->defname, "costs") == 0)
164 es->costs = defGetBoolean(opt);
165 else if (strcmp(opt->defname, "buffers") == 0)
166 es->buffers = defGetBoolean(opt);
167 else if (strcmp(opt->defname, "timing") == 0)
170 es->timing = defGetBoolean(opt);
172 else if (strcmp(opt->defname, "summary") == 0)
175 es->summary = defGetBoolean(opt);
177 else if (strcmp(opt->defname, "format") == 0)
179 char *p = defGetString(opt);
181 if (strcmp(p, "text") == 0)
182 es->format = EXPLAIN_FORMAT_TEXT;
183 else if (strcmp(p, "xml") == 0)
184 es->format = EXPLAIN_FORMAT_XML;
185 else if (strcmp(p, "json") == 0)
186 es->format = EXPLAIN_FORMAT_JSON;
187 else if (strcmp(p, "yaml") == 0)
188 es->format = EXPLAIN_FORMAT_YAML;
191 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
192 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
194 parser_errposition(pstate, opt->location)));
198 (errcode(ERRCODE_SYNTAX_ERROR),
199 errmsg("unrecognized EXPLAIN option \"%s\"",
201 parser_errposition(pstate, opt->location)));
204 if (es->buffers && !es->analyze)
206 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
207 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
209 /* if the timing was not set explicitly, set default value */
210 es->timing = (timing_set) ? es->timing : es->analyze;
212 /* check that timing is used with EXPLAIN ANALYZE */
213 if (es->timing && !es->analyze)
215 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
216 errmsg("EXPLAIN option TIMING requires ANALYZE")));
218 /* if the summary was not set explicitly, set default value */
219 es->summary = (summary_set) ? es->summary : es->analyze;
222 * Parse analysis was done already, but we still have to run the rule
223 * rewriter. We do not do AcquireRewriteLocks: we assume the query either
224 * came straight from the parser, or suitable locks were acquired by
227 * Because the rewriter and planner tend to scribble on the input, we make
228 * a preliminary copy of the source querytree. This prevents problems in
229 * the case that the EXPLAIN is in a portal or plpgsql function and is
230 * executed repeatedly. (See also the same hack in DECLARE CURSOR and
231 * PREPARE.) XXX FIXME someday.
233 rewritten = QueryRewrite(castNode(Query, copyObject(stmt->query)));
235 /* emit opening boilerplate */
236 ExplainBeginOutput(es);
238 if (rewritten == NIL)
241 * In the case of an INSTEAD NOTHING, tell at least that. But in
242 * non-text format, the output is delimited, so this isn't necessary.
244 if (es->format == EXPLAIN_FORMAT_TEXT)
245 appendStringInfoString(es->str, "Query rewrites to nothing\n");
251 /* Explain every plan */
252 foreach(l, rewritten)
254 ExplainOneQuery(lfirst_node(Query, l),
255 CURSOR_OPT_PARALLEL_OK, NULL, es,
256 queryString, params, queryEnv);
258 /* Separate plans with an appropriate separator */
259 if (lnext(l) != NULL)
260 ExplainSeparatePlans(es);
264 /* emit closing boilerplate */
265 ExplainEndOutput(es);
266 Assert(es->indent == 0);
269 tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt),
271 if (es->format == EXPLAIN_FORMAT_TEXT)
272 do_text_output_multiline(tstate, es->str->data);
274 do_text_output_oneline(tstate, es->str->data);
275 end_tup_output(tstate);
277 pfree(es->str->data);
281 * Create a new ExplainState struct initialized with default options.
284 NewExplainState(void)
286 ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
288 /* Set default options (most fields can be left as zeroes). */
290 /* Prepare output buffer. */
291 es->str = makeStringInfo();
297 * ExplainResultDesc -
298 * construct the result tupledesc for an EXPLAIN
301 ExplainResultDesc(ExplainStmt *stmt)
305 Oid result_type = TEXTOID;
307 /* Check for XML format option */
308 foreach(lc, stmt->options)
310 DefElem *opt = (DefElem *) lfirst(lc);
312 if (strcmp(opt->defname, "format") == 0)
314 char *p = defGetString(opt);
316 if (strcmp(p, "xml") == 0)
317 result_type = XMLOID;
318 else if (strcmp(p, "json") == 0)
319 result_type = JSONOID;
321 result_type = TEXTOID;
322 /* don't "break", as ExplainQuery will use the last value */
326 /* Need a tuple descriptor representing a single TEXT or XML column */
327 tupdesc = CreateTemplateTupleDesc(1);
328 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
335 * print out the execution plan for one Query
337 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
340 ExplainOneQuery(Query *query, int cursorOptions,
341 IntoClause *into, ExplainState *es,
342 const char *queryString, ParamListInfo params,
343 QueryEnvironment *queryEnv)
345 /* planner will not cope with utility statements */
346 if (query->commandType == CMD_UTILITY)
348 ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
353 /* if an advisor plugin is present, let it manage things */
354 if (ExplainOneQuery_hook)
355 (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
356 queryString, params, queryEnv);
360 instr_time planstart,
363 INSTR_TIME_SET_CURRENT(planstart);
366 plan = pg_plan_query(query, cursorOptions, params);
368 INSTR_TIME_SET_CURRENT(planduration);
369 INSTR_TIME_SUBTRACT(planduration, planstart);
371 /* run it (if needed) and produce output */
372 ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
378 * ExplainOneUtility -
379 * print out the execution plan for one utility statement
380 * (In general, utility statements don't have plans, but there are some
381 * we treat as special cases)
383 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
385 * This is exported because it's called back from prepare.c in the
386 * EXPLAIN EXECUTE case.
389 ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
390 const char *queryString, ParamListInfo params,
391 QueryEnvironment *queryEnv)
393 if (utilityStmt == NULL)
396 if (IsA(utilityStmt, CreateTableAsStmt))
399 * We have to rewrite the contained SELECT and then pass it back to
400 * ExplainOneQuery. It's probably not really necessary to copy the
401 * contained parsetree another time, but let's be safe.
403 CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
406 rewritten = QueryRewrite(castNode(Query, copyObject(ctas->query)));
407 Assert(list_length(rewritten) == 1);
408 ExplainOneQuery(linitial_node(Query, rewritten),
409 CURSOR_OPT_PARALLEL_OK, ctas->into, es,
410 queryString, params, queryEnv);
412 else if (IsA(utilityStmt, DeclareCursorStmt))
415 * Likewise for DECLARE CURSOR.
417 * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
418 * actually run the query. This is different from pre-8.3 behavior
419 * but seems more useful than not running the query. No cursor will
420 * be created, however.
422 DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
425 rewritten = QueryRewrite(castNode(Query, copyObject(dcs->query)));
426 Assert(list_length(rewritten) == 1);
427 ExplainOneQuery(linitial_node(Query, rewritten),
428 dcs->options, NULL, es,
429 queryString, params, queryEnv);
431 else if (IsA(utilityStmt, ExecuteStmt))
432 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
433 queryString, params, queryEnv);
434 else if (IsA(utilityStmt, NotifyStmt))
436 if (es->format == EXPLAIN_FORMAT_TEXT)
437 appendStringInfoString(es->str, "NOTIFY\n");
439 ExplainDummyGroup("Notify", NULL, es);
443 if (es->format == EXPLAIN_FORMAT_TEXT)
444 appendStringInfoString(es->str,
445 "Utility statements have no plan structure\n");
447 ExplainDummyGroup("Utility Statement", NULL, es);
453 * given a planned query, execute it if needed, and then print
456 * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
457 * in which case executing the query should result in creating that table.
459 * This is exported because it's called back from prepare.c in the
460 * EXPLAIN EXECUTE case, and because an index advisor plugin would need
464 ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
465 const char *queryString, ParamListInfo params,
466 QueryEnvironment *queryEnv, const instr_time *planduration)
469 QueryDesc *queryDesc;
470 instr_time starttime;
471 double totaltime = 0;
473 int instrument_option = 0;
475 Assert(plannedstmt->commandType != CMD_UTILITY);
477 if (es->analyze && es->timing)
478 instrument_option |= INSTRUMENT_TIMER;
479 else if (es->analyze)
480 instrument_option |= INSTRUMENT_ROWS;
483 instrument_option |= INSTRUMENT_BUFFERS;
486 * We always collect timing for the entire statement, even when node-level
487 * timing is off, so we don't look at es->timing here. (We could skip
488 * this if !es->summary, but it's hardly worth the complication.)
490 INSTR_TIME_SET_CURRENT(starttime);
493 * Use a snapshot with an updated command ID to ensure this query sees
494 * results of any previously executed queries.
496 PushCopiedSnapshot(GetActiveSnapshot());
497 UpdateActiveSnapshotCommandId();
500 * Normally we discard the query's output, but if explaining CREATE TABLE
501 * AS, we'd better use the appropriate tuple receiver.
504 dest = CreateIntoRelDestReceiver(into);
506 dest = None_Receiver;
508 /* Create a QueryDesc for the query */
509 queryDesc = CreateQueryDesc(plannedstmt, queryString,
510 GetActiveSnapshot(), InvalidSnapshot,
511 dest, params, queryEnv, instrument_option);
513 /* Select execution options */
515 eflags = 0; /* default run-to-completion flags */
517 eflags = EXEC_FLAG_EXPLAIN_ONLY;
519 eflags |= GetIntoRelEFlags(into);
521 /* call ExecutorStart to prepare the plan for execution */
522 ExecutorStart(queryDesc, eflags);
524 /* Execute the plan for statistics if asked for */
529 /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
530 if (into && into->skipData)
531 dir = NoMovementScanDirection;
533 dir = ForwardScanDirection;
536 ExecutorRun(queryDesc, dir, 0L, true);
538 /* run cleanup too */
539 ExecutorFinish(queryDesc);
541 /* We can't run ExecutorEnd 'till we're done printing the stats... */
542 totaltime += elapsed_time(&starttime);
545 ExplainOpenGroup("Query", NULL, true, es);
547 /* Create textual dump of plan tree */
548 ExplainPrintPlan(es, queryDesc);
550 if (es->summary && planduration)
552 double plantime = INSTR_TIME_GET_DOUBLE(*planduration);
554 ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
557 /* Print info about runtime of triggers */
559 ExplainPrintTriggers(es, queryDesc);
562 * Print info about JITing. Tied to es->costs because we don't want to
563 * display this in regression tests, as it'd cause output differences
564 * depending on build options. Might want to separate that out from COSTS
568 ExplainPrintJITSummary(es, queryDesc);
571 * Close down the query and free resources. Include time for this in the
572 * total execution time (although it should be pretty minimal).
574 INSTR_TIME_SET_CURRENT(starttime);
576 ExecutorEnd(queryDesc);
578 FreeQueryDesc(queryDesc);
582 /* We need a CCI just in case query expanded to multiple plans */
584 CommandCounterIncrement();
586 totaltime += elapsed_time(&starttime);
589 * We only report execution time if we actually ran the query (that is,
590 * the user specified ANALYZE), and if summary reporting is enabled (the
591 * user can set SUMMARY OFF to not have the timing information included in
592 * the output). By default, ANALYZE sets SUMMARY to true.
594 if (es->summary && es->analyze)
595 ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
598 ExplainCloseGroup("Query", NULL, true, es);
603 * convert a QueryDesc's plan tree to text and append it to es->str
605 * The caller should have set up the options fields of *es, as well as
606 * initializing the output buffer es->str. Also, output formatting state
607 * such as the indent level is assumed valid. Plan-tree-specific fields
608 * in *es are initialized here.
610 * NB: will not work on utility statements
613 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
615 Bitmapset *rels_used = NULL;
618 /* Set up ExplainState fields associated with this plan tree */
619 Assert(queryDesc->plannedstmt != NULL);
620 es->pstmt = queryDesc->plannedstmt;
621 es->rtable = queryDesc->plannedstmt->rtable;
622 ExplainPreScanNode(queryDesc->planstate, &rels_used);
623 es->rtable_names = select_rtable_names_for_explain(es->rtable, rels_used);
624 es->deparse_cxt = deparse_context_for_plan_rtable(es->rtable,
626 es->printed_subplans = NULL;
629 * Sometimes we mark a Gather node as "invisible", which means that it's
630 * not displayed in EXPLAIN output. The purpose of this is to allow
631 * running regression tests with force_parallel_mode=regress to get the
632 * same results as running the same tests with force_parallel_mode=off.
634 ps = queryDesc->planstate;
635 if (IsA(ps, GatherState) &&((Gather *) ps->plan)->invisible)
636 ps = outerPlanState(ps);
637 ExplainNode(ps, NIL, NULL, NULL, es);
641 * ExplainPrintTriggers -
642 * convert a QueryDesc's trigger statistics to text and append it to
645 * The caller should have set up the options fields of *es, as well as
646 * initializing the output buffer es->str. Other fields in *es are
650 ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
652 ResultRelInfo *rInfo;
654 int numrels = queryDesc->estate->es_num_result_relations;
655 int numrootrels = queryDesc->estate->es_num_root_result_relations;
661 routerels = queryDesc->estate->es_tuple_routing_result_relations;
662 targrels = queryDesc->estate->es_trig_target_relations;
664 ExplainOpenGroup("Triggers", "Triggers", false, es);
666 show_relname = (numrels > 1 || numrootrels > 0 ||
667 routerels != NIL || targrels != NIL);
668 rInfo = queryDesc->estate->es_result_relations;
669 for (nr = 0; nr < numrels; rInfo++, nr++)
670 report_triggers(rInfo, show_relname, es);
672 rInfo = queryDesc->estate->es_root_result_relations;
673 for (nr = 0; nr < numrootrels; rInfo++, nr++)
674 report_triggers(rInfo, show_relname, es);
676 foreach(l, routerels)
678 rInfo = (ResultRelInfo *) lfirst(l);
679 report_triggers(rInfo, show_relname, es);
684 rInfo = (ResultRelInfo *) lfirst(l);
685 report_triggers(rInfo, show_relname, es);
688 ExplainCloseGroup("Triggers", "Triggers", false, es);
692 * ExplainPrintJITSummary -
693 * Print summarized JIT instrumentation from leader and workers
696 ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
698 JitInstrumentation ji = {0};
700 if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
704 * Work with a copy instead of modifying the leader state, since this
705 * function may be called twice
707 if (queryDesc->estate->es_jit)
708 InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
710 /* If this process has done JIT in parallel workers, merge stats */
711 if (queryDesc->estate->es_jit_worker_instr)
712 InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
714 ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji, -1);
719 * Append information about JITing to es->str.
721 * Can be used to print the JIT instrumentation of the backend (worker_num =
722 * -1) or that of a specific worker (worker_num = ...).
725 ExplainPrintJIT(ExplainState *es, int jit_flags,
726 JitInstrumentation *ji, int worker_num)
728 instr_time total_time;
729 bool for_workers = (worker_num >= 0);
731 /* don't print information if no JITing happened */
732 if (!ji || ji->created_functions == 0)
735 /* calculate total time */
736 INSTR_TIME_SET_ZERO(total_time);
737 INSTR_TIME_ADD(total_time, ji->generation_counter);
738 INSTR_TIME_ADD(total_time, ji->inlining_counter);
739 INSTR_TIME_ADD(total_time, ji->optimization_counter);
740 INSTR_TIME_ADD(total_time, ji->emission_counter);
742 ExplainOpenGroup("JIT", "JIT", true, es);
744 /* for higher density, open code the text output format */
745 if (es->format == EXPLAIN_FORMAT_TEXT)
747 appendStringInfoSpaces(es->str, es->indent * 2);
749 appendStringInfo(es->str, "JIT for worker %u:\n", worker_num);
751 appendStringInfo(es->str, "JIT:\n");
754 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
756 appendStringInfoSpaces(es->str, es->indent * 2);
757 appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
758 "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
759 "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
760 "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
761 "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
763 if (es->analyze && es->timing)
765 appendStringInfoSpaces(es->str, es->indent * 2);
766 appendStringInfo(es->str,
767 "Timing: %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
768 "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
769 "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
770 "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
771 "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
772 "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
779 ExplainPropertyInteger("Worker Number", NULL, worker_num, es);
780 ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
782 ExplainOpenGroup("Options", "Options", true, es);
783 ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
784 ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
785 ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
786 ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
787 ExplainCloseGroup("Options", "Options", true, es);
789 if (es->analyze && es->timing)
791 ExplainOpenGroup("Timing", "Timing", true, es);
793 ExplainPropertyFloat("Generation", "ms",
794 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
796 ExplainPropertyFloat("Inlining", "ms",
797 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
799 ExplainPropertyFloat("Optimization", "ms",
800 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
802 ExplainPropertyFloat("Emission", "ms",
803 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
805 ExplainPropertyFloat("Total", "ms",
806 1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
809 ExplainCloseGroup("Timing", "Timing", true, es);
813 ExplainCloseGroup("JIT", "JIT", true, es);
818 * add a "Query Text" node that contains the actual text of the query
820 * The caller should have set up the options fields of *es, as well as
821 * initializing the output buffer es->str.
825 ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
827 if (queryDesc->sourceText)
828 ExplainPropertyText("Query Text", queryDesc->sourceText, es);
833 * report execution stats for a single relation's triggers
836 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
840 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
842 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
844 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
845 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
847 char *conname = NULL;
849 /* Must clean up instrumentation state */
853 * We ignore triggers that were never invoked; they likely aren't
854 * relevant to the current query type.
856 if (instr->ntuples == 0)
859 ExplainOpenGroup("Trigger", NULL, true, es);
861 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
862 if (OidIsValid(trig->tgconstraint))
863 conname = get_constraint_name(trig->tgconstraint);
866 * In text format, we avoid printing both the trigger name and the
867 * constraint name unless VERBOSE is specified. In non-text formats
868 * we just print everything.
870 if (es->format == EXPLAIN_FORMAT_TEXT)
872 if (es->verbose || conname == NULL)
873 appendStringInfo(es->str, "Trigger %s", trig->tgname);
875 appendStringInfoString(es->str, "Trigger");
877 appendStringInfo(es->str, " for constraint %s", conname);
879 appendStringInfo(es->str, " on %s", relname);
881 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
882 1000.0 * instr->total, instr->ntuples);
884 appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
888 ExplainPropertyText("Trigger Name", trig->tgname, es);
890 ExplainPropertyText("Constraint Name", conname, es);
891 ExplainPropertyText("Relation", relname, es);
893 ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
895 ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
901 ExplainCloseGroup("Trigger", NULL, true, es);
905 /* Compute elapsed time in seconds since given timestamp */
907 elapsed_time(instr_time *starttime)
911 INSTR_TIME_SET_CURRENT(endtime);
912 INSTR_TIME_SUBTRACT(endtime, *starttime);
913 return INSTR_TIME_GET_DOUBLE(endtime);
917 * ExplainPreScanNode -
918 * Prescan the planstate tree to identify which RTEs are referenced
920 * Adds the relid of each referenced RTE to *rels_used. The result controls
921 * which RTEs are assigned aliases by select_rtable_names_for_explain.
922 * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
923 * that never appear in the EXPLAIN output (such as inheritance parents).
926 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
928 Plan *plan = planstate->plan;
930 switch (nodeTag(plan))
935 case T_IndexOnlyScan:
936 case T_BitmapHeapScan:
940 case T_TableFuncScan:
943 case T_NamedTuplestoreScan:
944 case T_WorkTableScan:
945 *rels_used = bms_add_member(*rels_used,
946 ((Scan *) plan)->scanrelid);
949 *rels_used = bms_add_members(*rels_used,
950 ((ForeignScan *) plan)->fs_relids);
953 *rels_used = bms_add_members(*rels_used,
954 ((CustomScan *) plan)->custom_relids);
957 *rels_used = bms_add_member(*rels_used,
958 ((ModifyTable *) plan)->nominalRelation);
959 if (((ModifyTable *) plan)->exclRelRTI)
960 *rels_used = bms_add_member(*rels_used,
961 ((ModifyTable *) plan)->exclRelRTI);
967 return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
972 * Appends a description of a plan tree to es->str
974 * planstate points to the executor state node for the current plan node.
975 * We need to work from a PlanState node, not just a Plan node, in order to
976 * get at the instrumentation data (if any) as well as the list of subplans.
978 * ancestors is a list of parent PlanState nodes, most-closely-nested first.
979 * These are needed in order to interpret PARAM_EXEC Params.
981 * relationship describes the relationship of this plan node to its parent
982 * (eg, "Outer", "Inner"); it can be null at top level. plan_name is an
983 * optional name to be attached to the node.
985 * In text format, es->indent is controlled in this function since we only
986 * want it to change at plan-node boundaries. In non-text formats, es->indent
987 * corresponds to the nesting depth of logical output groups, and therefore
988 * is controlled by ExplainOpenGroup/ExplainCloseGroup.
991 ExplainNode(PlanState *planstate, List *ancestors,
992 const char *relationship, const char *plan_name,
995 Plan *plan = planstate->plan;
996 const char *pname; /* node type name for text output */
997 const char *sname; /* node type name for non-text output */
998 const char *strategy = NULL;
999 const char *partialmode = NULL;
1000 const char *operation = NULL;
1001 const char *custom_name = NULL;
1002 int save_indent = es->indent;
1005 switch (nodeTag(plan))
1008 pname = sname = "Result";
1011 pname = sname = "ProjectSet";
1014 sname = "ModifyTable";
1015 switch (((ModifyTable *) plan)->operation)
1018 pname = operation = "Insert";
1021 pname = operation = "Update";
1024 pname = operation = "Delete";
1032 pname = sname = "Append";
1035 pname = sname = "Merge Append";
1037 case T_RecursiveUnion:
1038 pname = sname = "Recursive Union";
1041 pname = sname = "BitmapAnd";
1044 pname = sname = "BitmapOr";
1047 pname = sname = "Nested Loop";
1050 pname = "Merge"; /* "Join" gets added by jointype switch */
1051 sname = "Merge Join";
1054 pname = "Hash"; /* "Join" gets added by jointype switch */
1055 sname = "Hash Join";
1058 pname = sname = "Seq Scan";
1061 pname = sname = "Sample Scan";
1064 pname = sname = "Gather";
1067 pname = sname = "Gather Merge";
1070 pname = sname = "Index Scan";
1072 case T_IndexOnlyScan:
1073 pname = sname = "Index Only Scan";
1075 case T_BitmapIndexScan:
1076 pname = sname = "Bitmap Index Scan";
1078 case T_BitmapHeapScan:
1079 pname = sname = "Bitmap Heap Scan";
1082 pname = sname = "Tid Scan";
1084 case T_SubqueryScan:
1085 pname = sname = "Subquery Scan";
1087 case T_FunctionScan:
1088 pname = sname = "Function Scan";
1090 case T_TableFuncScan:
1091 pname = sname = "Table Function Scan";
1094 pname = sname = "Values Scan";
1097 pname = sname = "CTE Scan";
1099 case T_NamedTuplestoreScan:
1100 pname = sname = "Named Tuplestore Scan";
1102 case T_WorkTableScan:
1103 pname = sname = "WorkTable Scan";
1106 sname = "Foreign Scan";
1107 switch (((ForeignScan *) plan)->operation)
1110 pname = "Foreign Scan";
1111 operation = "Select";
1114 pname = "Foreign Insert";
1115 operation = "Insert";
1118 pname = "Foreign Update";
1119 operation = "Update";
1122 pname = "Foreign Delete";
1123 operation = "Delete";
1131 sname = "Custom Scan";
1132 custom_name = ((CustomScan *) plan)->methods->CustomName;
1134 pname = psprintf("Custom Scan (%s)", custom_name);
1139 pname = sname = "Materialize";
1142 pname = sname = "Sort";
1145 pname = sname = "Group";
1149 Agg *agg = (Agg *) plan;
1151 sname = "Aggregate";
1152 switch (agg->aggstrategy)
1155 pname = "Aggregate";
1159 pname = "GroupAggregate";
1160 strategy = "Sorted";
1163 pname = "HashAggregate";
1164 strategy = "Hashed";
1167 pname = "MixedAggregate";
1171 pname = "Aggregate ???";
1176 if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
1178 partialmode = "Partial";
1179 pname = psprintf("%s %s", partialmode, pname);
1181 else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
1183 partialmode = "Finalize";
1184 pname = psprintf("%s %s", partialmode, pname);
1187 partialmode = "Simple";
1191 pname = sname = "WindowAgg";
1194 pname = sname = "Unique";
1198 switch (((SetOp *) plan)->strategy)
1202 strategy = "Sorted";
1205 pname = "HashSetOp";
1206 strategy = "Hashed";
1209 pname = "SetOp ???";
1215 pname = sname = "LockRows";
1218 pname = sname = "Limit";
1221 pname = sname = "Hash";
1224 pname = sname = "???";
1228 ExplainOpenGroup("Plan",
1229 relationship ? NULL : "Plan",
1232 if (es->format == EXPLAIN_FORMAT_TEXT)
1236 appendStringInfoSpaces(es->str, es->indent * 2);
1237 appendStringInfo(es->str, "%s\n", plan_name);
1242 appendStringInfoSpaces(es->str, es->indent * 2);
1243 appendStringInfoString(es->str, "-> ");
1246 if (plan->parallel_aware)
1247 appendStringInfoString(es->str, "Parallel ");
1248 appendStringInfoString(es->str, pname);
1253 ExplainPropertyText("Node Type", sname, es);
1255 ExplainPropertyText("Strategy", strategy, es);
1257 ExplainPropertyText("Partial Mode", partialmode, es);
1259 ExplainPropertyText("Operation", operation, es);
1261 ExplainPropertyText("Parent Relationship", relationship, es);
1263 ExplainPropertyText("Subplan Name", plan_name, es);
1265 ExplainPropertyText("Custom Plan Provider", custom_name, es);
1266 ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
1269 switch (nodeTag(plan))
1273 case T_BitmapHeapScan:
1275 case T_SubqueryScan:
1276 case T_FunctionScan:
1277 case T_TableFuncScan:
1280 case T_WorkTableScan:
1281 ExplainScanTarget((Scan *) plan, es);
1285 if (((Scan *) plan)->scanrelid > 0)
1286 ExplainScanTarget((Scan *) plan, es);
1290 IndexScan *indexscan = (IndexScan *) plan;
1292 ExplainIndexScanDetails(indexscan->indexid,
1293 indexscan->indexorderdir,
1295 ExplainScanTarget((Scan *) indexscan, es);
1298 case T_IndexOnlyScan:
1300 IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
1302 ExplainIndexScanDetails(indexonlyscan->indexid,
1303 indexonlyscan->indexorderdir,
1305 ExplainScanTarget((Scan *) indexonlyscan, es);
1308 case T_BitmapIndexScan:
1310 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
1311 const char *indexname =
1312 explain_get_index_name(bitmapindexscan->indexid);
1314 if (es->format == EXPLAIN_FORMAT_TEXT)
1315 appendStringInfo(es->str, " on %s", indexname);
1317 ExplainPropertyText("Index Name", indexname, es);
1321 ExplainModifyTarget((ModifyTable *) plan, es);
1327 const char *jointype;
1329 switch (((Join *) plan)->jointype)
1353 if (es->format == EXPLAIN_FORMAT_TEXT)
1356 * For historical reasons, the join type is interpolated
1357 * into the node type name...
1359 if (((Join *) plan)->jointype != JOIN_INNER)
1360 appendStringInfo(es->str, " %s Join", jointype);
1361 else if (!IsA(plan, NestLoop))
1362 appendStringInfoString(es->str, " Join");
1365 ExplainPropertyText("Join Type", jointype, es);
1370 const char *setopcmd;
1372 switch (((SetOp *) plan)->cmd)
1374 case SETOPCMD_INTERSECT:
1375 setopcmd = "Intersect";
1377 case SETOPCMD_INTERSECT_ALL:
1378 setopcmd = "Intersect All";
1380 case SETOPCMD_EXCEPT:
1381 setopcmd = "Except";
1383 case SETOPCMD_EXCEPT_ALL:
1384 setopcmd = "Except All";
1390 if (es->format == EXPLAIN_FORMAT_TEXT)
1391 appendStringInfo(es->str, " %s", setopcmd);
1393 ExplainPropertyText("Command", setopcmd, es);
1402 if (es->format == EXPLAIN_FORMAT_TEXT)
1404 appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
1405 plan->startup_cost, plan->total_cost,
1406 plan->plan_rows, plan->plan_width);
1410 ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
1412 ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
1414 ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
1416 ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
1422 * We have to forcibly clean up the instrumentation state because we
1423 * haven't done ExecutorEnd yet. This is pretty grotty ...
1425 * Note: contrib/auto_explain could cause instrumentation to be set up
1426 * even though we didn't ask for it here. Be careful not to print any
1427 * instrumentation results the user didn't ask for. But we do the
1428 * InstrEndLoop call anyway, if possible, to reduce the number of cases
1429 * auto_explain has to contend with.
1431 if (planstate->instrument)
1432 InstrEndLoop(planstate->instrument);
1435 planstate->instrument && planstate->instrument->nloops > 0)
1437 double nloops = planstate->instrument->nloops;
1438 double startup_ms = 1000.0 * planstate->instrument->startup / nloops;
1439 double total_ms = 1000.0 * planstate->instrument->total / nloops;
1440 double rows = planstate->instrument->ntuples / nloops;
1442 if (es->format == EXPLAIN_FORMAT_TEXT)
1445 appendStringInfo(es->str,
1446 " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1447 startup_ms, total_ms, rows, nloops);
1449 appendStringInfo(es->str,
1450 " (actual rows=%.0f loops=%.0f)",
1457 ExplainPropertyFloat("Actual Startup Time", "s", startup_ms,
1459 ExplainPropertyFloat("Actual Total Time", "s", total_ms,
1462 ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1463 ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1466 else if (es->analyze)
1468 if (es->format == EXPLAIN_FORMAT_TEXT)
1469 appendStringInfoString(es->str, " (never executed)");
1474 ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
1475 ExplainPropertyFloat("Actual Total Time", "ms", 0.0, 3, es);
1477 ExplainPropertyFloat("Actual Rows", NULL, 0.0, 0, es);
1478 ExplainPropertyFloat("Actual Loops", NULL, 0.0, 0, es);
1482 /* in text format, first line ends here */
1483 if (es->format == EXPLAIN_FORMAT_TEXT)
1484 appendStringInfoChar(es->str, '\n');
1488 show_plan_tlist(planstate, ancestors, es);
1491 switch (nodeTag(plan))
1496 /* try not to be too chatty about this in text mode */
1497 if (es->format != EXPLAIN_FORMAT_TEXT ||
1498 (es->verbose && ((Join *) plan)->inner_unique))
1499 ExplainPropertyBool("Inner Unique",
1500 ((Join *) plan)->inner_unique,
1507 /* quals, sort keys, etc */
1508 switch (nodeTag(plan))
1511 show_scan_qual(((IndexScan *) plan)->indexqualorig,
1512 "Index Cond", planstate, ancestors, es);
1513 if (((IndexScan *) plan)->indexqualorig)
1514 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1516 show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
1517 "Order By", planstate, ancestors, es);
1518 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1520 show_instrumentation_count("Rows Removed by Filter", 1,
1523 case T_IndexOnlyScan:
1524 show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
1525 "Index Cond", planstate, ancestors, es);
1526 if (((IndexOnlyScan *) plan)->indexqual)
1527 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1529 show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
1530 "Order By", planstate, ancestors, es);
1531 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1533 show_instrumentation_count("Rows Removed by Filter", 1,
1536 ExplainPropertyFloat("Heap Fetches", NULL,
1537 planstate->instrument->ntuples2, 0, es);
1539 case T_BitmapIndexScan:
1540 show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
1541 "Index Cond", planstate, ancestors, es);
1543 case T_BitmapHeapScan:
1544 show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
1545 "Recheck Cond", planstate, ancestors, es);
1546 if (((BitmapHeapScan *) plan)->bitmapqualorig)
1547 show_instrumentation_count("Rows Removed by Index Recheck", 2,
1549 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1551 show_instrumentation_count("Rows Removed by Filter", 1,
1554 show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
1557 show_tablesample(((SampleScan *) plan)->tablesample,
1558 planstate, ancestors, es);
1559 /* fall through to print additional fields the same as SeqScan */
1564 case T_NamedTuplestoreScan:
1565 case T_WorkTableScan:
1566 case T_SubqueryScan:
1567 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1569 show_instrumentation_count("Rows Removed by Filter", 1,
1574 Gather *gather = (Gather *) plan;
1576 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1578 show_instrumentation_count("Rows Removed by Filter", 1,
1580 ExplainPropertyInteger("Workers Planned", NULL,
1581 gather->num_workers, es);
1583 /* Show params evaluated at gather node */
1584 if (gather->initParam)
1585 show_eval_params(gather->initParam, es);
1591 nworkers = ((GatherState *) planstate)->nworkers_launched;
1592 ExplainPropertyInteger("Workers Launched", NULL,
1597 * Print per-worker Jit instrumentation. Use same conditions
1598 * as for the leader's JIT instrumentation, see comment there.
1600 if (es->costs && es->verbose &&
1601 outerPlanState(planstate)->worker_jit_instrument)
1603 PlanState *child = outerPlanState(planstate);
1605 SharedJitInstrumentation *w = child->worker_jit_instrument;
1607 for (n = 0; n < w->num_workers; ++n)
1609 ExplainPrintJIT(es, child->state->es_jit_flags,
1610 &w->jit_instr[n], n);
1614 if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
1615 ExplainPropertyBool("Single Copy", gather->single_copy, es);
1620 GatherMerge *gm = (GatherMerge *) plan;
1622 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1624 show_instrumentation_count("Rows Removed by Filter", 1,
1626 ExplainPropertyInteger("Workers Planned", NULL,
1627 gm->num_workers, es);
1629 /* Show params evaluated at gather-merge node */
1631 show_eval_params(gm->initParam, es);
1637 nworkers = ((GatherMergeState *) planstate)->nworkers_launched;
1638 ExplainPropertyInteger("Workers Launched", NULL,
1643 case T_FunctionScan:
1649 foreach(lc, ((FunctionScan *) plan)->functions)
1651 RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
1653 fexprs = lappend(fexprs, rtfunc->funcexpr);
1655 /* We rely on show_expression to insert commas as needed */
1656 show_expression((Node *) fexprs,
1657 "Function Call", planstate, ancestors,
1660 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1662 show_instrumentation_count("Rows Removed by Filter", 1,
1665 case T_TableFuncScan:
1668 TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
1670 show_expression((Node *) tablefunc,
1671 "Table Function Call", planstate, ancestors,
1674 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1676 show_instrumentation_count("Rows Removed by Filter", 1,
1682 * The tidquals list has OR semantics, so be sure to show it
1683 * as an OR condition.
1685 List *tidquals = ((TidScan *) plan)->tidquals;
1687 if (list_length(tidquals) > 1)
1688 tidquals = list_make1(make_orclause(tidquals));
1689 show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
1690 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1692 show_instrumentation_count("Rows Removed by Filter", 1,
1697 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1699 show_instrumentation_count("Rows Removed by Filter", 1,
1701 show_foreignscan_info((ForeignScanState *) planstate, es);
1705 CustomScanState *css = (CustomScanState *) planstate;
1707 show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1709 show_instrumentation_count("Rows Removed by Filter", 1,
1711 if (css->methods->ExplainCustomScan)
1712 css->methods->ExplainCustomScan(css, ancestors, es);
1716 show_upper_qual(((NestLoop *) plan)->join.joinqual,
1717 "Join Filter", planstate, ancestors, es);
1718 if (((NestLoop *) plan)->join.joinqual)
1719 show_instrumentation_count("Rows Removed by Join Filter", 1,
1721 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1723 show_instrumentation_count("Rows Removed by Filter", 2,
1727 show_upper_qual(((MergeJoin *) plan)->mergeclauses,
1728 "Merge Cond", planstate, ancestors, es);
1729 show_upper_qual(((MergeJoin *) plan)->join.joinqual,
1730 "Join Filter", planstate, ancestors, es);
1731 if (((MergeJoin *) plan)->join.joinqual)
1732 show_instrumentation_count("Rows Removed by Join Filter", 1,
1734 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1736 show_instrumentation_count("Rows Removed by Filter", 2,
1740 show_upper_qual(((HashJoin *) plan)->hashclauses,
1741 "Hash Cond", planstate, ancestors, es);
1742 show_upper_qual(((HashJoin *) plan)->join.joinqual,
1743 "Join Filter", planstate, ancestors, es);
1744 if (((HashJoin *) plan)->join.joinqual)
1745 show_instrumentation_count("Rows Removed by Join Filter", 1,
1747 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1749 show_instrumentation_count("Rows Removed by Filter", 2,
1753 show_agg_keys(castNode(AggState, planstate), ancestors, es);
1754 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1756 show_instrumentation_count("Rows Removed by Filter", 1,
1760 show_group_keys(castNode(GroupState, planstate), ancestors, es);
1761 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1763 show_instrumentation_count("Rows Removed by Filter", 1,
1767 show_sort_keys(castNode(SortState, planstate), ancestors, es);
1768 show_sort_info(castNode(SortState, planstate), es);
1771 show_merge_append_keys(castNode(MergeAppendState, planstate),
1775 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
1776 "One-Time Filter", planstate, ancestors, es);
1777 show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1779 show_instrumentation_count("Rows Removed by Filter", 1,
1783 show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
1787 show_hash_info(castNode(HashState, planstate), es);
1793 /* Show buffer usage */
1794 if (es->buffers && planstate->instrument)
1795 show_buffer_usage(es, &planstate->instrument->bufusage);
1797 /* Show worker detail */
1798 if (es->analyze && es->verbose && planstate->worker_instrument)
1800 WorkerInstrumentation *w = planstate->worker_instrument;
1801 bool opened_group = false;
1804 for (n = 0; n < w->num_workers; ++n)
1806 Instrumentation *instrument = &w->instrument[n];
1807 double nloops = instrument->nloops;
1814 startup_ms = 1000.0 * instrument->startup / nloops;
1815 total_ms = 1000.0 * instrument->total / nloops;
1816 rows = instrument->ntuples / nloops;
1818 if (es->format == EXPLAIN_FORMAT_TEXT)
1820 appendStringInfoSpaces(es->str, es->indent * 2);
1821 appendStringInfo(es->str, "Worker %d: ", n);
1823 appendStringInfo(es->str,
1824 "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
1825 startup_ms, total_ms, rows, nloops);
1827 appendStringInfo(es->str,
1828 "actual rows=%.0f loops=%.0f\n",
1832 show_buffer_usage(es, &instrument->bufusage);
1839 ExplainOpenGroup("Workers", "Workers", false, es);
1840 opened_group = true;
1842 ExplainOpenGroup("Worker", NULL, true, es);
1843 ExplainPropertyInteger("Worker Number", NULL, n, es);
1847 ExplainPropertyFloat("Actual Startup Time", "ms",
1849 ExplainPropertyFloat("Actual Total Time", "ms",
1852 ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1853 ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1856 show_buffer_usage(es, &instrument->bufusage);
1858 ExplainCloseGroup("Worker", NULL, true, es);
1863 ExplainCloseGroup("Workers", "Workers", false, es);
1866 /* Get ready to display the child plans */
1867 haschildren = planstate->initPlan ||
1868 outerPlanState(planstate) ||
1869 innerPlanState(planstate) ||
1870 IsA(plan, ModifyTable) ||
1871 IsA(plan, Append) ||
1872 IsA(plan, MergeAppend) ||
1873 IsA(plan, BitmapAnd) ||
1874 IsA(plan, BitmapOr) ||
1875 IsA(plan, SubqueryScan) ||
1876 (IsA(planstate, CustomScanState) &&
1877 ((CustomScanState *) planstate)->custom_ps != NIL) ||
1881 ExplainOpenGroup("Plans", "Plans", false, es);
1882 /* Pass current PlanState as head of ancestors list for children */
1883 ancestors = lcons(planstate, ancestors);
1887 if (planstate->initPlan)
1888 ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
1891 if (outerPlanState(planstate))
1892 ExplainNode(outerPlanState(planstate), ancestors,
1896 if (innerPlanState(planstate))
1897 ExplainNode(innerPlanState(planstate), ancestors,
1900 /* special child plans */
1901 switch (nodeTag(plan))
1904 ExplainMemberNodes(((ModifyTableState *) planstate)->mt_plans,
1905 ((ModifyTableState *) planstate)->mt_nplans,
1906 list_length(((ModifyTable *) plan)->plans),
1910 ExplainMemberNodes(((AppendState *) planstate)->appendplans,
1911 ((AppendState *) planstate)->as_nplans,
1912 list_length(((Append *) plan)->appendplans),
1916 ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
1917 ((MergeAppendState *) planstate)->ms_nplans,
1918 list_length(((MergeAppend *) plan)->mergeplans),
1922 ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
1923 ((BitmapAndState *) planstate)->nplans,
1924 list_length(((BitmapAnd *) plan)->bitmapplans),
1928 ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
1929 ((BitmapOrState *) planstate)->nplans,
1930 list_length(((BitmapOr *) plan)->bitmapplans),
1933 case T_SubqueryScan:
1934 ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
1935 "Subquery", NULL, es);
1938 ExplainCustomChildren((CustomScanState *) planstate,
1946 if (planstate->subPlan)
1947 ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
1949 /* end of child plans */
1952 ancestors = list_delete_first(ancestors);
1953 ExplainCloseGroup("Plans", "Plans", false, es);
1956 /* in text format, undo whatever indentation we added */
1957 if (es->format == EXPLAIN_FORMAT_TEXT)
1958 es->indent = save_indent;
1960 ExplainCloseGroup("Plan",
1961 relationship ? NULL : "Plan",
1966 * Show the targetlist of a plan node
1969 show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
1971 Plan *plan = planstate->plan;
1977 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1978 if (plan->targetlist == NIL)
1980 /* The tlist of an Append isn't real helpful, so suppress it */
1981 if (IsA(plan, Append))
1983 /* Likewise for MergeAppend and RecursiveUnion */
1984 if (IsA(plan, MergeAppend))
1986 if (IsA(plan, RecursiveUnion))
1990 * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
1992 * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
1993 * might contain subplan output expressions that are confusing in this
1994 * context. The tlist for a ForeignScan that executes a direct UPDATE/
1995 * DELETE always contains "junk" target columns to identify the exact row
1996 * to update or delete, which would be confusing in this context. So, we
1997 * suppress it in all the cases.
1999 if (IsA(plan, ForeignScan) &&
2000 ((ForeignScan *) plan)->operation != CMD_SELECT)
2003 /* Set up deparsing context */
2004 context = set_deparse_context_planstate(es->deparse_cxt,
2007 useprefix = list_length(es->rtable) > 1;
2009 /* Deparse each result column (we now include resjunk ones) */
2010 foreach(lc, plan->targetlist)
2012 TargetEntry *tle = (TargetEntry *) lfirst(lc);
2014 result = lappend(result,
2015 deparse_expression((Node *) tle->expr, context,
2020 ExplainPropertyList("Output", result, es);
2024 * Show a generic expression
2027 show_expression(Node *node, const char *qlabel,
2028 PlanState *planstate, List *ancestors,
2029 bool useprefix, ExplainState *es)
2034 /* Set up deparsing context */
2035 context = set_deparse_context_planstate(es->deparse_cxt,
2039 /* Deparse the expression */
2040 exprstr = deparse_expression(node, context, useprefix, false);
2042 /* And add to es->str */
2043 ExplainPropertyText(qlabel, exprstr, es);
2047 * Show a qualifier expression (which is a List with implicit AND semantics)
2050 show_qual(List *qual, const char *qlabel,
2051 PlanState *planstate, List *ancestors,
2052 bool useprefix, ExplainState *es)
2056 /* No work if empty qual */
2060 /* Convert AND list to explicit AND */
2061 node = (Node *) make_ands_explicit(qual);
2064 show_expression(node, qlabel, planstate, ancestors, useprefix, es);
2068 * Show a qualifier expression for a scan plan node
2071 show_scan_qual(List *qual, const char *qlabel,
2072 PlanState *planstate, List *ancestors,
2077 useprefix = (IsA(planstate->plan, SubqueryScan) ||es->verbose);
2078 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2082 * Show a qualifier expression for an upper-level plan node
2085 show_upper_qual(List *qual, const char *qlabel,
2086 PlanState *planstate, List *ancestors,
2091 useprefix = (list_length(es->rtable) > 1 || es->verbose);
2092 show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2096 * Show the sort keys for a Sort node.
2099 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
2101 Sort *plan = (Sort *) sortstate->ss.ps.plan;
2103 show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2104 plan->numCols, plan->sortColIdx,
2105 plan->sortOperators, plan->collations,
2111 * Likewise, for a MergeAppend node.
2114 show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
2117 MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
2119 show_sort_group_keys((PlanState *) mstate, "Sort Key",
2120 plan->numCols, plan->sortColIdx,
2121 plan->sortOperators, plan->collations,
2127 * Show the grouping keys for an Agg node.
2130 show_agg_keys(AggState *astate, List *ancestors,
2133 Agg *plan = (Agg *) astate->ss.ps.plan;
2135 if (plan->numCols > 0 || plan->groupingSets)
2137 /* The key columns refer to the tlist of the child plan */
2138 ancestors = lcons(astate, ancestors);
2140 if (plan->groupingSets)
2141 show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2143 show_sort_group_keys(outerPlanState(astate), "Group Key",
2144 plan->numCols, plan->grpColIdx,
2148 ancestors = list_delete_first(ancestors);
2153 show_grouping_sets(PlanState *planstate, Agg *agg,
2154 List *ancestors, ExplainState *es)
2160 /* Set up deparsing context */
2161 context = set_deparse_context_planstate(es->deparse_cxt,
2164 useprefix = (list_length(es->rtable) > 1 || es->verbose);
2166 ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
2168 show_grouping_set_keys(planstate, agg, NULL,
2169 context, useprefix, ancestors, es);
2171 foreach(lc, agg->chain)
2173 Agg *aggnode = lfirst(lc);
2174 Sort *sortnode = (Sort *) aggnode->plan.lefttree;
2176 show_grouping_set_keys(planstate, aggnode, sortnode,
2177 context, useprefix, ancestors, es);
2180 ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
2184 show_grouping_set_keys(PlanState *planstate,
2185 Agg *aggnode, Sort *sortnode,
2186 List *context, bool useprefix,
2187 List *ancestors, ExplainState *es)
2189 Plan *plan = planstate->plan;
2192 List *gsets = aggnode->groupingSets;
2193 AttrNumber *keycols = aggnode->grpColIdx;
2194 const char *keyname;
2195 const char *keysetname;
2197 if (aggnode->aggstrategy == AGG_HASHED || aggnode->aggstrategy == AGG_MIXED)
2199 keyname = "Hash Key";
2200 keysetname = "Hash Keys";
2204 keyname = "Group Key";
2205 keysetname = "Group Keys";
2208 ExplainOpenGroup("Grouping Set", NULL, true, es);
2212 show_sort_group_keys(planstate, "Sort Key",
2213 sortnode->numCols, sortnode->sortColIdx,
2214 sortnode->sortOperators, sortnode->collations,
2215 sortnode->nullsFirst,
2217 if (es->format == EXPLAIN_FORMAT_TEXT)
2221 ExplainOpenGroup(keysetname, keysetname, false, es);
2228 foreach(lc2, (List *) lfirst(lc))
2230 Index i = lfirst_int(lc2);
2231 AttrNumber keyresno = keycols[i];
2232 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2236 elog(ERROR, "no tlist entry for key %d", keyresno);
2237 /* Deparse the expression, showing any top-level cast */
2238 exprstr = deparse_expression((Node *) target->expr, context,
2241 result = lappend(result, exprstr);
2244 if (!result && es->format == EXPLAIN_FORMAT_TEXT)
2245 ExplainPropertyText(keyname, "()", es);
2247 ExplainPropertyListNested(keyname, result, es);
2250 ExplainCloseGroup(keysetname, keysetname, false, es);
2252 if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
2255 ExplainCloseGroup("Grouping Set", NULL, true, es);
2259 * Show the grouping keys for a Group node.
2262 show_group_keys(GroupState *gstate, List *ancestors,
2265 Group *plan = (Group *) gstate->ss.ps.plan;
2267 /* The key columns refer to the tlist of the child plan */
2268 ancestors = lcons(gstate, ancestors);
2269 show_sort_group_keys(outerPlanState(gstate), "Group Key",
2270 plan->numCols, plan->grpColIdx,
2273 ancestors = list_delete_first(ancestors);
2277 * Common code to show sort/group keys, which are represented in plan nodes
2278 * as arrays of targetlist indexes. If it's a sort key rather than a group
2279 * key, also pass sort operators/collations/nullsFirst arrays.
2282 show_sort_group_keys(PlanState *planstate, const char *qlabel,
2283 int nkeys, AttrNumber *keycols,
2284 Oid *sortOperators, Oid *collations, bool *nullsFirst,
2285 List *ancestors, ExplainState *es)
2287 Plan *plan = planstate->plan;
2290 StringInfoData sortkeybuf;
2297 initStringInfo(&sortkeybuf);
2299 /* Set up deparsing context */
2300 context = set_deparse_context_planstate(es->deparse_cxt,
2303 useprefix = (list_length(es->rtable) > 1 || es->verbose);
2305 for (keyno = 0; keyno < nkeys; keyno++)
2307 /* find key expression in tlist */
2308 AttrNumber keyresno = keycols[keyno];
2309 TargetEntry *target = get_tle_by_resno(plan->targetlist,
2314 elog(ERROR, "no tlist entry for key %d", keyresno);
2315 /* Deparse the expression, showing any top-level cast */
2316 exprstr = deparse_expression((Node *) target->expr, context,
2318 resetStringInfo(&sortkeybuf);
2319 appendStringInfoString(&sortkeybuf, exprstr);
2320 /* Append sort order information, if relevant */
2321 if (sortOperators != NULL)
2322 show_sortorder_options(&sortkeybuf,
2323 (Node *) target->expr,
2324 sortOperators[keyno],
2327 /* Emit one property-list item per sort key */
2328 result = lappend(result, pstrdup(sortkeybuf.data));
2331 ExplainPropertyList(qlabel, result, es);
2335 * Append nondefault characteristics of the sort ordering of a column to buf
2336 * (collation, direction, NULLS FIRST/LAST)
2339 show_sortorder_options(StringInfo buf, Node *sortexpr,
2340 Oid sortOperator, Oid collation, bool nullsFirst)
2342 Oid sortcoltype = exprType(sortexpr);
2343 bool reverse = false;
2344 TypeCacheEntry *typentry;
2346 typentry = lookup_type_cache(sortcoltype,
2347 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
2350 * Print COLLATE if it's not default for the column's type. There are
2351 * some cases where this is redundant, eg if expression is a column whose
2352 * declared collation is that collation, but it's hard to distinguish that
2353 * here (and arguably, printing COLLATE explicitly is a good idea anyway
2356 if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
2358 char *collname = get_collation_name(collation);
2360 if (collname == NULL)
2361 elog(ERROR, "cache lookup failed for collation %u", collation);
2362 appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2365 /* Print direction if not ASC, or USING if non-default sort operator */
2366 if (sortOperator == typentry->gt_opr)
2368 appendStringInfoString(buf, " DESC");
2371 else if (sortOperator != typentry->lt_opr)
2373 char *opname = get_opname(sortOperator);
2376 elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2377 appendStringInfo(buf, " USING %s", opname);
2378 /* Determine whether operator would be considered ASC or DESC */
2379 (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2382 /* Add NULLS FIRST/LAST only if it wouldn't be default */
2383 if (nullsFirst && !reverse)
2385 appendStringInfoString(buf, " NULLS FIRST");
2387 else if (!nullsFirst && reverse)
2389 appendStringInfoString(buf, " NULLS LAST");
2394 * Show TABLESAMPLE properties
2397 show_tablesample(TableSampleClause *tsc, PlanState *planstate,
2398 List *ancestors, ExplainState *es)
2407 /* Set up deparsing context */
2408 context = set_deparse_context_planstate(es->deparse_cxt,
2411 useprefix = list_length(es->rtable) > 1;
2413 /* Get the tablesample method name */
2414 method_name = get_func_name(tsc->tsmhandler);
2416 /* Deparse parameter expressions */
2417 foreach(lc, tsc->args)
2419 Node *arg = (Node *) lfirst(lc);
2421 params = lappend(params,
2422 deparse_expression(arg, context,
2425 if (tsc->repeatable)
2426 repeatable = deparse_expression((Node *) tsc->repeatable, context,
2432 if (es->format == EXPLAIN_FORMAT_TEXT)
2436 appendStringInfoSpaces(es->str, es->indent * 2);
2437 appendStringInfo(es->str, "Sampling: %s (", method_name);
2441 appendStringInfoString(es->str, ", ");
2442 appendStringInfoString(es->str, (const char *) lfirst(lc));
2445 appendStringInfoChar(es->str, ')');
2447 appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
2448 appendStringInfoChar(es->str, '\n');
2452 ExplainPropertyText("Sampling Method", method_name, es);
2453 ExplainPropertyList("Sampling Parameters", params, es);
2455 ExplainPropertyText("Repeatable Seed", repeatable, es);
2460 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
2463 show_sort_info(SortState *sortstate, ExplainState *es)
2468 if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
2470 Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
2471 TuplesortInstrumentation stats;
2472 const char *sortMethod;
2473 const char *spaceType;
2476 tuplesort_get_stats(state, &stats);
2477 sortMethod = tuplesort_method_name(stats.sortMethod);
2478 spaceType = tuplesort_space_type_name(stats.spaceType);
2479 spaceUsed = stats.spaceUsed;
2481 if (es->format == EXPLAIN_FORMAT_TEXT)
2483 appendStringInfoSpaces(es->str, es->indent * 2);
2484 appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n",
2485 sortMethod, spaceType, spaceUsed);
2489 ExplainPropertyText("Sort Method", sortMethod, es);
2490 ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
2491 ExplainPropertyText("Sort Space Type", spaceType, es);
2495 if (sortstate->shared_info != NULL)
2498 bool opened_group = false;
2500 for (n = 0; n < sortstate->shared_info->num_workers; n++)
2502 TuplesortInstrumentation *sinstrument;
2503 const char *sortMethod;
2504 const char *spaceType;
2507 sinstrument = &sortstate->shared_info->sinstrument[n];
2508 if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
2509 continue; /* ignore any unfilled slots */
2510 sortMethod = tuplesort_method_name(sinstrument->sortMethod);
2511 spaceType = tuplesort_space_type_name(sinstrument->spaceType);
2512 spaceUsed = sinstrument->spaceUsed;
2514 if (es->format == EXPLAIN_FORMAT_TEXT)
2516 appendStringInfoSpaces(es->str, es->indent * 2);
2517 appendStringInfo(es->str,
2518 "Worker %d: Sort Method: %s %s: %ldkB\n",
2519 n, sortMethod, spaceType, spaceUsed);
2525 ExplainOpenGroup("Workers", "Workers", false, es);
2526 opened_group = true;
2528 ExplainOpenGroup("Worker", NULL, true, es);
2529 ExplainPropertyInteger("Worker Number", NULL, n, es);
2530 ExplainPropertyText("Sort Method", sortMethod, es);
2531 ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
2532 ExplainPropertyText("Sort Space Type", spaceType, es);
2533 ExplainCloseGroup("Worker", NULL, true, es);
2537 ExplainCloseGroup("Workers", "Workers", false, es);
2542 * Show information on hash buckets/batches.
2545 show_hash_info(HashState *hashstate, ExplainState *es)
2547 HashInstrumentation hinstrument = {0};
2550 * In a parallel query, the leader process may or may not have run the
2551 * hash join, and even if it did it may not have built a hash table due to
2552 * timing (if it started late it might have seen no tuples in the outer
2553 * relation and skipped building the hash table). Therefore we have to be
2554 * prepared to get instrumentation data from all participants.
2556 if (hashstate->hashtable)
2557 ExecHashGetInstrumentation(&hinstrument, hashstate->hashtable);
2560 * Merge results from workers. In the parallel-oblivious case, the
2561 * results from all participants should be identical, except where
2562 * participants didn't run the join at all so have no data. In the
2563 * parallel-aware case, we need to consider all the results. Each worker
2564 * may have seen a different subset of batches and we want to find the
2565 * highest memory usage for any one batch across all batches.
2567 if (hashstate->shared_info)
2569 SharedHashInfo *shared_info = hashstate->shared_info;
2572 for (i = 0; i < shared_info->num_workers; ++i)
2574 HashInstrumentation *worker_hi = &shared_info->hinstrument[i];
2576 if (worker_hi->nbatch > 0)
2579 * Every participant should agree on the buckets, so to be
2580 * sure we have a value we'll just overwrite each time.
2582 hinstrument.nbuckets = worker_hi->nbuckets;
2583 hinstrument.nbuckets_original = worker_hi->nbuckets_original;
2586 * Normally every participant should agree on the number of
2587 * batches too, but it's possible for a backend that started
2588 * late and missed the whole join not to have the final nbatch
2589 * number. So we'll take the largest number.
2591 hinstrument.nbatch = Max(hinstrument.nbatch, worker_hi->nbatch);
2592 hinstrument.nbatch_original = worker_hi->nbatch_original;
2595 * In a parallel-aware hash join, for now we report the
2596 * maximum peak memory reported by any worker.
2598 hinstrument.space_peak =
2599 Max(hinstrument.space_peak, worker_hi->space_peak);
2604 if (hinstrument.nbatch > 0)
2606 long spacePeakKb = (hinstrument.space_peak + 1023) / 1024;
2608 if (es->format != EXPLAIN_FORMAT_TEXT)
2610 ExplainPropertyInteger("Hash Buckets", NULL,
2611 hinstrument.nbuckets, es);
2612 ExplainPropertyInteger("Original Hash Buckets", NULL,
2613 hinstrument.nbuckets_original, es);
2614 ExplainPropertyInteger("Hash Batches", NULL,
2615 hinstrument.nbatch, es);
2616 ExplainPropertyInteger("Original Hash Batches", NULL,
2617 hinstrument.nbatch_original, es);
2618 ExplainPropertyInteger("Peak Memory Usage", "kB",
2621 else if (hinstrument.nbatch_original != hinstrument.nbatch ||
2622 hinstrument.nbuckets_original != hinstrument.nbuckets)
2624 appendStringInfoSpaces(es->str, es->indent * 2);
2625 appendStringInfo(es->str,
2626 "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: %ldkB\n",
2627 hinstrument.nbuckets,
2628 hinstrument.nbuckets_original,
2630 hinstrument.nbatch_original,
2635 appendStringInfoSpaces(es->str, es->indent * 2);
2636 appendStringInfo(es->str,
2637 "Buckets: %d Batches: %d Memory Usage: %ldkB\n",
2638 hinstrument.nbuckets, hinstrument.nbatch,
2645 * If it's EXPLAIN ANALYZE, show exact/lossy pages for a BitmapHeapScan node
2648 show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
2650 if (es->format != EXPLAIN_FORMAT_TEXT)
2652 ExplainPropertyInteger("Exact Heap Blocks", NULL,
2653 planstate->exact_pages, es);
2654 ExplainPropertyInteger("Lossy Heap Blocks", NULL,
2655 planstate->lossy_pages, es);
2659 if (planstate->exact_pages > 0 || planstate->lossy_pages > 0)
2661 appendStringInfoSpaces(es->str, es->indent * 2);
2662 appendStringInfoString(es->str, "Heap Blocks:");
2663 if (planstate->exact_pages > 0)
2664 appendStringInfo(es->str, " exact=%ld", planstate->exact_pages);
2665 if (planstate->lossy_pages > 0)
2666 appendStringInfo(es->str, " lossy=%ld", planstate->lossy_pages);
2667 appendStringInfoChar(es->str, '\n');
2673 * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
2675 * "which" identifies which instrumentation counter to print
2678 show_instrumentation_count(const char *qlabel, int which,
2679 PlanState *planstate, ExplainState *es)
2684 if (!es->analyze || !planstate->instrument)
2688 nfiltered = planstate->instrument->nfiltered2;
2690 nfiltered = planstate->instrument->nfiltered1;
2691 nloops = planstate->instrument->nloops;
2693 /* In text mode, suppress zero counts; they're not interesting enough */
2694 if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
2697 ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
2699 ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
2704 * Show extra information for a ForeignScan node.
2707 show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
2709 FdwRoutine *fdwroutine = fsstate->fdwroutine;
2711 /* Let the FDW emit whatever fields it wants */
2712 if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
2714 if (fdwroutine->ExplainDirectModify != NULL)
2715 fdwroutine->ExplainDirectModify(fsstate, es);
2719 if (fdwroutine->ExplainForeignScan != NULL)
2720 fdwroutine->ExplainForeignScan(fsstate, es);
2725 * Show initplan params evaluated at Gather or Gather Merge node.
2728 show_eval_params(Bitmapset *bms_params, ExplainState *es)
2735 while ((paramid = bms_next_member(bms_params, paramid)) >= 0)
2739 snprintf(param, sizeof(param), "$%d", paramid);
2740 params = lappend(params, pstrdup(param));
2744 ExplainPropertyList("Params Evaluated", params, es);
2748 * Fetch the name of an index in an EXPLAIN
2750 * We allow plugins to get control here so that plans involving hypothetical
2751 * indexes can be explained.
2754 explain_get_index_name(Oid indexId)
2758 if (explain_get_index_name_hook)
2759 result = (*explain_get_index_name_hook) (indexId);
2764 /* default behavior: look in the catalogs and quote it */
2765 result = get_rel_name(indexId);
2767 elog(ERROR, "cache lookup failed for index %u", indexId);
2768 result = quote_identifier(result);
2774 * Show buffer usage details.
2777 show_buffer_usage(ExplainState *es, const BufferUsage *usage)
2779 if (es->format == EXPLAIN_FORMAT_TEXT)
2781 bool has_shared = (usage->shared_blks_hit > 0 ||
2782 usage->shared_blks_read > 0 ||
2783 usage->shared_blks_dirtied > 0 ||
2784 usage->shared_blks_written > 0);
2785 bool has_local = (usage->local_blks_hit > 0 ||
2786 usage->local_blks_read > 0 ||
2787 usage->local_blks_dirtied > 0 ||
2788 usage->local_blks_written > 0);
2789 bool has_temp = (usage->temp_blks_read > 0 ||
2790 usage->temp_blks_written > 0);
2791 bool has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
2792 !INSTR_TIME_IS_ZERO(usage->blk_write_time));
2794 /* Show only positive counter values. */
2795 if (has_shared || has_local || has_temp)
2797 appendStringInfoSpaces(es->str, es->indent * 2);
2798 appendStringInfoString(es->str, "Buffers:");
2802 appendStringInfoString(es->str, " shared");
2803 if (usage->shared_blks_hit > 0)
2804 appendStringInfo(es->str, " hit=%ld",
2805 usage->shared_blks_hit);
2806 if (usage->shared_blks_read > 0)
2807 appendStringInfo(es->str, " read=%ld",
2808 usage->shared_blks_read);
2809 if (usage->shared_blks_dirtied > 0)
2810 appendStringInfo(es->str, " dirtied=%ld",
2811 usage->shared_blks_dirtied);
2812 if (usage->shared_blks_written > 0)
2813 appendStringInfo(es->str, " written=%ld",
2814 usage->shared_blks_written);
2815 if (has_local || has_temp)
2816 appendStringInfoChar(es->str, ',');
2820 appendStringInfoString(es->str, " local");
2821 if (usage->local_blks_hit > 0)
2822 appendStringInfo(es->str, " hit=%ld",
2823 usage->local_blks_hit);
2824 if (usage->local_blks_read > 0)
2825 appendStringInfo(es->str, " read=%ld",
2826 usage->local_blks_read);
2827 if (usage->local_blks_dirtied > 0)
2828 appendStringInfo(es->str, " dirtied=%ld",
2829 usage->local_blks_dirtied);
2830 if (usage->local_blks_written > 0)
2831 appendStringInfo(es->str, " written=%ld",
2832 usage->local_blks_written);
2834 appendStringInfoChar(es->str, ',');
2838 appendStringInfoString(es->str, " temp");
2839 if (usage->temp_blks_read > 0)
2840 appendStringInfo(es->str, " read=%ld",
2841 usage->temp_blks_read);
2842 if (usage->temp_blks_written > 0)
2843 appendStringInfo(es->str, " written=%ld",
2844 usage->temp_blks_written);
2846 appendStringInfoChar(es->str, '\n');
2849 /* As above, show only positive counter values. */
2852 appendStringInfoSpaces(es->str, es->indent * 2);
2853 appendStringInfoString(es->str, "I/O Timings:");
2854 if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
2855 appendStringInfo(es->str, " read=%0.3f",
2856 INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
2857 if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
2858 appendStringInfo(es->str, " write=%0.3f",
2859 INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
2860 appendStringInfoChar(es->str, '\n');
2865 ExplainPropertyInteger("Shared Hit Blocks", NULL,
2866 usage->shared_blks_hit, es);
2867 ExplainPropertyInteger("Shared Read Blocks", NULL,
2868 usage->shared_blks_read, es);
2869 ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
2870 usage->shared_blks_dirtied, es);
2871 ExplainPropertyInteger("Shared Written Blocks", NULL,
2872 usage->shared_blks_written, es);
2873 ExplainPropertyInteger("Local Hit Blocks", NULL,
2874 usage->local_blks_hit, es);
2875 ExplainPropertyInteger("Local Read Blocks", NULL,
2876 usage->local_blks_read, es);
2877 ExplainPropertyInteger("Local Dirtied Blocks", NULL,
2878 usage->local_blks_dirtied, es);
2879 ExplainPropertyInteger("Local Written Blocks", NULL,
2880 usage->local_blks_written, es);
2881 ExplainPropertyInteger("Temp Read Blocks", NULL,
2882 usage->temp_blks_read, es);
2883 ExplainPropertyInteger("Temp Written Blocks", NULL,
2884 usage->temp_blks_written, es);
2885 if (track_io_timing)
2887 ExplainPropertyFloat("I/O Read Time", "ms",
2888 INSTR_TIME_GET_MILLISEC(usage->blk_read_time),
2890 ExplainPropertyFloat("I/O Write Time", "ms",
2891 INSTR_TIME_GET_MILLISEC(usage->blk_write_time),
2898 * Add some additional details about an IndexScan or IndexOnlyScan
2901 ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
2904 const char *indexname = explain_get_index_name(indexid);
2906 if (es->format == EXPLAIN_FORMAT_TEXT)
2908 if (ScanDirectionIsBackward(indexorderdir))
2909 appendStringInfoString(es->str, " Backward");
2910 appendStringInfo(es->str, " using %s", indexname);
2914 const char *scandir;
2916 switch (indexorderdir)
2918 case BackwardScanDirection:
2919 scandir = "Backward";
2921 case NoMovementScanDirection:
2922 scandir = "NoMovement";
2924 case ForwardScanDirection:
2925 scandir = "Forward";
2931 ExplainPropertyText("Scan Direction", scandir, es);
2932 ExplainPropertyText("Index Name", indexname, es);
2937 * Show the target of a Scan node
2940 ExplainScanTarget(Scan *plan, ExplainState *es)
2942 ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
2946 * Show the target of a ModifyTable node
2948 * Here we show the nominal target (ie, the relation that was named in the
2949 * original query). If the actual target(s) is/are different, we'll show them
2950 * in show_modifytable_info().
2953 ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
2955 ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
2959 * Show the target relation of a scan or modify node
2962 ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
2964 char *objectname = NULL;
2965 char *namespace = NULL;
2966 const char *objecttag = NULL;
2970 rte = rt_fetch(rti, es->rtable);
2971 refname = (char *) list_nth(es->rtable_names, rti - 1);
2972 if (refname == NULL)
2973 refname = rte->eref->aliasname;
2975 switch (nodeTag(plan))
2980 case T_IndexOnlyScan:
2981 case T_BitmapHeapScan:
2986 /* Assert it's on a real relation */
2987 Assert(rte->rtekind == RTE_RELATION);
2988 objectname = get_rel_name(rte->relid);
2990 namespace = get_namespace_name(get_rel_namespace(rte->relid));
2991 objecttag = "Relation Name";
2993 case T_FunctionScan:
2995 FunctionScan *fscan = (FunctionScan *) plan;
2997 /* Assert it's on a RangeFunction */
2998 Assert(rte->rtekind == RTE_FUNCTION);
3001 * If the expression is still a function call of a single
3002 * function, we can get the real name of the function.
3003 * Otherwise, punt. (Even if it was a single function call
3004 * originally, the optimizer could have simplified it away.)
3006 if (list_length(fscan->functions) == 1)
3008 RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
3010 if (IsA(rtfunc->funcexpr, FuncExpr))
3012 FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
3013 Oid funcid = funcexpr->funcid;
3015 objectname = get_func_name(funcid);
3018 get_namespace_name(get_func_namespace(funcid));
3021 objecttag = "Function Name";
3024 case T_TableFuncScan:
3025 Assert(rte->rtekind == RTE_TABLEFUNC);
3026 objectname = "xmltable";
3027 objecttag = "Table Function Name";
3030 Assert(rte->rtekind == RTE_VALUES);
3033 /* Assert it's on a non-self-reference CTE */
3034 Assert(rte->rtekind == RTE_CTE);
3035 Assert(!rte->self_reference);
3036 objectname = rte->ctename;
3037 objecttag = "CTE Name";
3039 case T_NamedTuplestoreScan:
3040 Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
3041 objectname = rte->enrname;
3042 objecttag = "Tuplestore Name";
3044 case T_WorkTableScan:
3045 /* Assert it's on a self-reference CTE */
3046 Assert(rte->rtekind == RTE_CTE);
3047 Assert(rte->self_reference);
3048 objectname = rte->ctename;
3049 objecttag = "CTE Name";
3055 if (es->format == EXPLAIN_FORMAT_TEXT)
3057 appendStringInfoString(es->str, " on");
3058 if (namespace != NULL)
3059 appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
3060 quote_identifier(objectname));
3061 else if (objectname != NULL)
3062 appendStringInfo(es->str, " %s", quote_identifier(objectname));
3063 if (objectname == NULL || strcmp(refname, objectname) != 0)
3064 appendStringInfo(es->str, " %s", quote_identifier(refname));
3068 if (objecttag != NULL && objectname != NULL)
3069 ExplainPropertyText(objecttag, objectname, es);
3070 if (namespace != NULL)
3071 ExplainPropertyText("Schema", namespace, es);
3072 ExplainPropertyText("Alias", refname, es);
3077 * Show extra information for a ModifyTable node
3079 * We have three objectives here. First, if there's more than one target
3080 * table or it's different from the nominal target, identify the actual
3081 * target(s). Second, give FDWs a chance to display extra info about foreign
3082 * targets. Third, show information about ON CONFLICT.
3085 show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
3088 ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3089 const char *operation;
3090 const char *foperation;
3093 List *idxNames = NIL;
3096 switch (node->operation)
3099 operation = "Insert";
3100 foperation = "Foreign Insert";
3103 operation = "Update";
3104 foperation = "Foreign Update";
3107 operation = "Delete";
3108 foperation = "Foreign Delete";
3112 foperation = "Foreign ???";
3116 /* Should we explicitly label target relations? */
3117 labeltargets = (mtstate->mt_nplans > 1 ||
3118 (mtstate->mt_nplans == 1 &&
3119 mtstate->resultRelInfo->ri_RangeTableIndex != node->nominalRelation));
3122 ExplainOpenGroup("Target Tables", "Target Tables", false, es);
3124 for (j = 0; j < mtstate->mt_nplans; j++)
3126 ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
3127 FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
3131 /* Open a group for this target */
3132 ExplainOpenGroup("Target Table", NULL, true, es);
3135 * In text mode, decorate each target with operation type, so that
3136 * ExplainTargetRel's output of " on foo" will read nicely.
3138 if (es->format == EXPLAIN_FORMAT_TEXT)
3140 appendStringInfoSpaces(es->str, es->indent * 2);
3141 appendStringInfoString(es->str,
3142 fdwroutine ? foperation : operation);
3145 /* Identify target */
3146 ExplainTargetRel((Plan *) node,
3147 resultRelInfo->ri_RangeTableIndex,
3150 if (es->format == EXPLAIN_FORMAT_TEXT)
3152 appendStringInfoChar(es->str, '\n');
3157 /* Give FDW a chance if needed */
3158 if (!resultRelInfo->ri_usesFdwDirectModify &&
3159 fdwroutine != NULL &&
3160 fdwroutine->ExplainForeignModify != NULL)
3162 List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
3164 fdwroutine->ExplainForeignModify(mtstate,
3173 /* Undo the indentation we added in text format */
3174 if (es->format == EXPLAIN_FORMAT_TEXT)
3177 /* Close the group */
3178 ExplainCloseGroup("Target Table", NULL, true, es);
3182 /* Gather names of ON CONFLICT arbiter indexes */
3183 foreach(lst, node->arbiterIndexes)
3185 char *indexname = get_rel_name(lfirst_oid(lst));
3187 idxNames = lappend(idxNames, indexname);
3190 if (node->onConflictAction != ONCONFLICT_NONE)
3192 ExplainPropertyText("Conflict Resolution",
3193 node->onConflictAction == ONCONFLICT_NOTHING ?
3194 "NOTHING" : "UPDATE",
3198 * Don't display arbiter indexes at all when DO NOTHING variant
3199 * implicitly ignores all conflicts
3202 ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
3204 /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
3205 if (node->onConflictWhere)
3207 show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
3208 &mtstate->ps, ancestors, es);
3209 show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
3212 /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
3213 if (es->analyze && mtstate->ps.instrument)
3219 InstrEndLoop(mtstate->mt_plans[0]->instrument);
3221 /* count the number of source rows */
3222 total = mtstate->mt_plans[0]->instrument->ntuples;
3223 other_path = mtstate->ps.instrument->ntuples2;
3224 insert_path = total - other_path;
3226 ExplainPropertyFloat("Tuples Inserted", NULL,
3227 insert_path, 0, es);
3228 ExplainPropertyFloat("Conflicting Tuples", NULL,
3234 ExplainCloseGroup("Target Tables", "Target Tables", false, es);
3238 * Explain the constituent plans of a ModifyTable, Append, MergeAppend,
3239 * BitmapAnd, or BitmapOr node.
3241 * The ancestors list should already contain the immediate parent of these
3244 * nsubnodes indicates the number of items in the planstates array.
3245 * nplans indicates the original number of subnodes in the Plan, some of these
3246 * may have been pruned by the run-time pruning code.
3249 ExplainMemberNodes(PlanState **planstates, int nsubnodes, int nplans,
3250 List *ancestors, ExplainState *es)
3255 * The number of subnodes being lower than the number of subplans that was
3256 * specified in the plan means that some subnodes have been ignored per
3257 * instruction for the partition pruning code during the executor
3258 * initialization. To make this a bit less mysterious, we'll indicate
3259 * here that this has happened.
3261 if (nsubnodes < nplans)
3262 ExplainPropertyInteger("Subplans Removed", NULL, nplans - nsubnodes, es);
3264 for (j = 0; j < nsubnodes; j++)
3265 ExplainNode(planstates[j], ancestors,
3266 "Member", NULL, es);
3270 * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
3272 * The ancestors list should already contain the immediate parent of these
3276 ExplainSubPlans(List *plans, List *ancestors,
3277 const char *relationship, ExplainState *es)
3283 SubPlanState *sps = (SubPlanState *) lfirst(lst);
3284 SubPlan *sp = sps->subplan;
3287 * There can be multiple SubPlan nodes referencing the same physical
3288 * subplan (same plan_id, which is its index in PlannedStmt.subplans).
3289 * We should print a subplan only once, so track which ones we already
3290 * printed. This state must be global across the plan tree, since the
3291 * duplicate nodes could be in different plan nodes, eg both a bitmap
3292 * indexscan's indexqual and its parent heapscan's recheck qual. (We
3293 * do not worry too much about which plan node we show the subplan as
3294 * attached to in such cases.)
3296 if (bms_is_member(sp->plan_id, es->printed_subplans))
3298 es->printed_subplans = bms_add_member(es->printed_subplans,
3301 ExplainNode(sps->planstate, ancestors,
3302 relationship, sp->plan_name, es);
3307 * Explain a list of children of a CustomScan.
3310 ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
3314 (list_length(css->custom_ps) != 1 ? "children" : "child");
3316 foreach(cell, css->custom_ps)
3317 ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
3321 * Explain a property, such as sort keys or targets, that takes the form of
3322 * a list of unlabeled items. "data" is a list of C strings.
3325 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
3332 case EXPLAIN_FORMAT_TEXT:
3333 appendStringInfoSpaces(es->str, es->indent * 2);
3334 appendStringInfo(es->str, "%s: ", qlabel);
3338 appendStringInfoString(es->str, ", ");
3339 appendStringInfoString(es->str, (const char *) lfirst(lc));
3342 appendStringInfoChar(es->str, '\n');
3345 case EXPLAIN_FORMAT_XML:
3346 ExplainXMLTag(qlabel, X_OPENING, es);
3351 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
3352 appendStringInfoString(es->str, "<Item>");
3353 str = escape_xml((const char *) lfirst(lc));
3354 appendStringInfoString(es->str, str);
3356 appendStringInfoString(es->str, "</Item>\n");
3358 ExplainXMLTag(qlabel, X_CLOSING, es);
3361 case EXPLAIN_FORMAT_JSON:
3362 ExplainJSONLineEnding(es);
3363 appendStringInfoSpaces(es->str, es->indent * 2);
3364 escape_json(es->str, qlabel);
3365 appendStringInfoString(es->str, ": [");
3369 appendStringInfoString(es->str, ", ");
3370 escape_json(es->str, (const char *) lfirst(lc));
3373 appendStringInfoChar(es->str, ']');
3376 case EXPLAIN_FORMAT_YAML:
3377 ExplainYAMLLineStarting(es);
3378 appendStringInfo(es->str, "%s: ", qlabel);
3381 appendStringInfoChar(es->str, '\n');
3382 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
3383 appendStringInfoString(es->str, "- ");
3384 escape_yaml(es->str, (const char *) lfirst(lc));
3391 * Explain a property that takes the form of a list of unlabeled items within
3392 * another list. "data" is a list of C strings.
3395 ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
3402 case EXPLAIN_FORMAT_TEXT:
3403 case EXPLAIN_FORMAT_XML:
3404 ExplainPropertyList(qlabel, data, es);
3407 case EXPLAIN_FORMAT_JSON:
3408 ExplainJSONLineEnding(es);
3409 appendStringInfoSpaces(es->str, es->indent * 2);
3410 appendStringInfoChar(es->str, '[');
3414 appendStringInfoString(es->str, ", ");
3415 escape_json(es->str, (const char *) lfirst(lc));
3418 appendStringInfoChar(es->str, ']');
3421 case EXPLAIN_FORMAT_YAML:
3422 ExplainYAMLLineStarting(es);
3423 appendStringInfoString(es->str, "- [");
3427 appendStringInfoString(es->str, ", ");
3428 escape_yaml(es->str, (const char *) lfirst(lc));
3431 appendStringInfoChar(es->str, ']');
3437 * Explain a simple property.
3439 * If "numeric" is true, the value is a number (or other value that
3440 * doesn't need quoting in JSON).
3442 * If unit is non-NULL the text format will display it after the value.
3444 * This usually should not be invoked directly, but via one of the datatype
3445 * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
3448 ExplainProperty(const char *qlabel, const char *unit, const char *value,
3449 bool numeric, ExplainState *es)
3453 case EXPLAIN_FORMAT_TEXT:
3454 appendStringInfoSpaces(es->str, es->indent * 2);
3456 appendStringInfo(es->str, "%s: %s %s\n", qlabel, value, unit);
3458 appendStringInfo(es->str, "%s: %s\n", qlabel, value);
3461 case EXPLAIN_FORMAT_XML:
3465 appendStringInfoSpaces(es->str, es->indent * 2);
3466 ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
3467 str = escape_xml(value);
3468 appendStringInfoString(es->str, str);
3470 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
3471 appendStringInfoChar(es->str, '\n');
3475 case EXPLAIN_FORMAT_JSON:
3476 ExplainJSONLineEnding(es);
3477 appendStringInfoSpaces(es->str, es->indent * 2);
3478 escape_json(es->str, qlabel);
3479 appendStringInfoString(es->str, ": ");
3481 appendStringInfoString(es->str, value);
3483 escape_json(es->str, value);
3486 case EXPLAIN_FORMAT_YAML:
3487 ExplainYAMLLineStarting(es);
3488 appendStringInfo(es->str, "%s: ", qlabel);
3490 appendStringInfoString(es->str, value);
3492 escape_yaml(es->str, value);
3498 * Explain a string-valued property.
3501 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
3503 ExplainProperty(qlabel, NULL, value, false, es);
3507 * Explain an integer-valued property.
3510 ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value,
3515 snprintf(buf, sizeof(buf), INT64_FORMAT, value);
3516 ExplainProperty(qlabel, unit, buf, true, es);
3520 * Explain a float-valued property, using the specified number of
3521 * fractional digits.
3524 ExplainPropertyFloat(const char *qlabel, const char *unit, double value,
3525 int ndigits, ExplainState *es)
3529 buf = psprintf("%.*f", ndigits, value);
3530 ExplainProperty(qlabel, unit, buf, true, es);
3535 * Explain a bool-valued property.
3538 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
3540 ExplainProperty(qlabel, NULL, value ? "true" : "false", true, es);
3544 * Open a group of related objects.
3546 * objtype is the type of the group object, labelname is its label within
3547 * a containing object (if any).
3549 * If labeled is true, the group members will be labeled properties,
3550 * while if it's false, they'll be unlabeled objects.
3553 ExplainOpenGroup(const char *objtype, const char *labelname,
3554 bool labeled, ExplainState *es)
3558 case EXPLAIN_FORMAT_TEXT:
3562 case EXPLAIN_FORMAT_XML:
3563 ExplainXMLTag(objtype, X_OPENING, es);
3567 case EXPLAIN_FORMAT_JSON:
3568 ExplainJSONLineEnding(es);
3569 appendStringInfoSpaces(es->str, 2 * es->indent);
3572 escape_json(es->str, labelname);
3573 appendStringInfoString(es->str, ": ");
3575 appendStringInfoChar(es->str, labeled ? '{' : '[');
3578 * In JSON format, the grouping_stack is an integer list. 0 means
3579 * we've emitted nothing at this grouping level, 1 means we've
3580 * emitted something (and so the next item needs a comma). See
3581 * ExplainJSONLineEnding().
3583 es->grouping_stack = lcons_int(0, es->grouping_stack);
3587 case EXPLAIN_FORMAT_YAML:
3590 * In YAML format, the grouping stack is an integer list. 0 means
3591 * we've emitted nothing at this grouping level AND this grouping
3592 * level is unlabelled and must be marked with "- ". See
3593 * ExplainYAMLLineStarting().
3595 ExplainYAMLLineStarting(es);
3598 appendStringInfo(es->str, "%s: ", labelname);
3599 es->grouping_stack = lcons_int(1, es->grouping_stack);
3603 appendStringInfoString(es->str, "- ");
3604 es->grouping_stack = lcons_int(0, es->grouping_stack);
3612 * Close a group of related objects.
3613 * Parameters must match the corresponding ExplainOpenGroup call.
3616 ExplainCloseGroup(const char *objtype, const char *labelname,
3617 bool labeled, ExplainState *es)
3621 case EXPLAIN_FORMAT_TEXT:
3625 case EXPLAIN_FORMAT_XML:
3627 ExplainXMLTag(objtype, X_CLOSING, es);
3630 case EXPLAIN_FORMAT_JSON:
3632 appendStringInfoChar(es->str, '\n');
3633 appendStringInfoSpaces(es->str, 2 * es->indent);
3634 appendStringInfoChar(es->str, labeled ? '}' : ']');
3635 es->grouping_stack = list_delete_first(es->grouping_stack);
3638 case EXPLAIN_FORMAT_YAML:
3640 es->grouping_stack = list_delete_first(es->grouping_stack);
3646 * Emit a "dummy" group that never has any members.
3648 * objtype is the type of the group object, labelname is its label within
3649 * a containing object (if any).
3652 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
3656 case EXPLAIN_FORMAT_TEXT:
3660 case EXPLAIN_FORMAT_XML:
3661 ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
3664 case EXPLAIN_FORMAT_JSON:
3665 ExplainJSONLineEnding(es);
3666 appendStringInfoSpaces(es->str, 2 * es->indent);
3669 escape_json(es->str, labelname);
3670 appendStringInfoString(es->str, ": ");
3672 escape_json(es->str, objtype);
3675 case EXPLAIN_FORMAT_YAML:
3676 ExplainYAMLLineStarting(es);
3679 escape_yaml(es->str, labelname);
3680 appendStringInfoString(es->str, ": ");
3684 appendStringInfoString(es->str, "- ");
3686 escape_yaml(es->str, objtype);
3692 * Emit the start-of-output boilerplate.
3694 * This is just enough different from processing a subgroup that we need
3695 * a separate pair of subroutines.
3698 ExplainBeginOutput(ExplainState *es)
3702 case EXPLAIN_FORMAT_TEXT:
3706 case EXPLAIN_FORMAT_XML:
3707 appendStringInfoString(es->str,
3708 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
3712 case EXPLAIN_FORMAT_JSON:
3713 /* top-level structure is an array of plans */
3714 appendStringInfoChar(es->str, '[');
3715 es->grouping_stack = lcons_int(0, es->grouping_stack);
3719 case EXPLAIN_FORMAT_YAML:
3720 es->grouping_stack = lcons_int(0, es->grouping_stack);
3726 * Emit the end-of-output boilerplate.
3729 ExplainEndOutput(ExplainState *es)
3733 case EXPLAIN_FORMAT_TEXT:
3737 case EXPLAIN_FORMAT_XML:
3739 appendStringInfoString(es->str, "</explain>");
3742 case EXPLAIN_FORMAT_JSON:
3744 appendStringInfoString(es->str, "\n]");
3745 es->grouping_stack = list_delete_first(es->grouping_stack);
3748 case EXPLAIN_FORMAT_YAML:
3749 es->grouping_stack = list_delete_first(es->grouping_stack);
3755 * Put an appropriate separator between multiple plans
3758 ExplainSeparatePlans(ExplainState *es)
3762 case EXPLAIN_FORMAT_TEXT:
3763 /* add a blank line */
3764 appendStringInfoChar(es->str, '\n');
3767 case EXPLAIN_FORMAT_XML:
3768 case EXPLAIN_FORMAT_JSON:
3769 case EXPLAIN_FORMAT_YAML:
3776 * Emit opening or closing XML tag.
3778 * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
3779 * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
3782 * XML restricts tag names more than our other output formats, eg they can't
3783 * contain white space or slashes. Replace invalid characters with dashes,
3784 * so that for example "I/O Read Time" becomes "I-O-Read-Time".
3787 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
3790 const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
3792 if ((flags & X_NOWHITESPACE) == 0)
3793 appendStringInfoSpaces(es->str, 2 * es->indent);
3794 appendStringInfoCharMacro(es->str, '<');
3795 if ((flags & X_CLOSING) != 0)
3796 appendStringInfoCharMacro(es->str, '/');
3797 for (s = tagname; *s; s++)
3798 appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
3799 if ((flags & X_CLOSE_IMMEDIATE) != 0)
3800 appendStringInfoString(es->str, " /");
3801 appendStringInfoCharMacro(es->str, '>');
3802 if ((flags & X_NOWHITESPACE) == 0)
3803 appendStringInfoCharMacro(es->str, '\n');
3807 * Emit a JSON line ending.
3809 * JSON requires a comma after each property but the last. To facilitate this,
3810 * in JSON format, the text emitted for each property begins just prior to the
3811 * preceding line-break (and comma, if applicable).
3814 ExplainJSONLineEnding(ExplainState *es)
3816 Assert(es->format == EXPLAIN_FORMAT_JSON);
3817 if (linitial_int(es->grouping_stack) != 0)
3818 appendStringInfoChar(es->str, ',');
3820 linitial_int(es->grouping_stack) = 1;
3821 appendStringInfoChar(es->str, '\n');
3825 * Indent a YAML line.
3827 * YAML lines are ordinarily indented by two spaces per indentation level.
3828 * The text emitted for each property begins just prior to the preceding
3829 * line-break, except for the first property in an unlabelled group, for which
3830 * it begins immediately after the "- " that introduces the group. The first
3831 * property of the group appears on the same line as the opening "- ".
3834 ExplainYAMLLineStarting(ExplainState *es)
3836 Assert(es->format == EXPLAIN_FORMAT_YAML);
3837 if (linitial_int(es->grouping_stack) == 0)
3839 linitial_int(es->grouping_stack) = 1;
3843 appendStringInfoChar(es->str, '\n');
3844 appendStringInfoSpaces(es->str, es->indent * 2);
3849 * YAML is a superset of JSON; unfortunately, the YAML quoting rules are
3850 * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
3851 * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
3852 * Empty strings, strings with leading or trailing whitespace, and strings
3853 * containing a variety of special characters must certainly be quoted or the
3854 * output is invalid; and other seemingly harmless strings like "0xa" or
3855 * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
3856 * constant rather than a string.
3859 escape_yaml(StringInfo buf, const char *str)
3861 escape_json(buf, str);