PostgreSQL Source Code  git master
explain.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * explain.c
4  * Explain query execution plans
5  *
6  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994-5, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/commands/explain.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
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"
23 #include "jit/jit.h"
24 #include "nodes/extensible.h"
25 #include "nodes/makefuncs.h"
26 #include "nodes/nodeFuncs.h"
27 #include "parser/analyze.h"
28 #include "parser/parsetree.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "storage/bufmgr.h"
31 #include "tcop/tcopprot.h"
32 #include "utils/builtins.h"
33 #include "utils/guc_tables.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"
42 
43 
44 /* Hook for plugins to get control in ExplainOneQuery() */
46 
47 /* Hook for plugins to get control in explain_get_index_name() */
49 
50 
51 /* OR-able flags for ExplainXMLTag() */
52 #define X_OPENING 0
53 #define X_CLOSING 1
54 #define X_CLOSE_IMMEDIATE 2
55 #define X_NOWHITESPACE 4
56 
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 ExplainPrintJIT(ExplainState *es, int jit_flags,
62  JitInstrumentation *ji);
63 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
64  ExplainState *es);
65 static double elapsed_time(instr_time *starttime);
66 static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
67 static void ExplainNode(PlanState *planstate, List *ancestors,
68  const char *relationship, const char *plan_name,
69  ExplainState *es);
70 static void show_plan_tlist(PlanState *planstate, List *ancestors,
71  ExplainState *es);
72 static void show_expression(Node *node, const char *qlabel,
73  PlanState *planstate, List *ancestors,
74  bool useprefix, ExplainState *es);
75 static void show_qual(List *qual, const char *qlabel,
76  PlanState *planstate, List *ancestors,
77  bool useprefix, ExplainState *es);
78 static void show_scan_qual(List *qual, const char *qlabel,
79  PlanState *planstate, List *ancestors,
80  ExplainState *es);
81 static void show_upper_qual(List *qual, const char *qlabel,
82  PlanState *planstate, List *ancestors,
83  ExplainState *es);
84 static void show_sort_keys(SortState *sortstate, List *ancestors,
85  ExplainState *es);
86 static void show_incremental_sort_keys(IncrementalSortState *incrsortstate,
87  List *ancestors, ExplainState *es);
88 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
89  ExplainState *es);
90 static void show_agg_keys(AggState *astate, List *ancestors,
91  ExplainState *es);
92 static void show_grouping_sets(PlanState *planstate, Agg *agg,
93  List *ancestors, ExplainState *es);
94 static void show_grouping_set_keys(PlanState *planstate,
95  Agg *aggnode, Sort *sortnode,
96  List *context, bool useprefix,
97  List *ancestors, ExplainState *es);
98 static void show_group_keys(GroupState *gstate, List *ancestors,
99  ExplainState *es);
100 static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
101  int nkeys, int nPresortedKeys, AttrNumber *keycols,
102  Oid *sortOperators, Oid *collations, bool *nullsFirst,
103  List *ancestors, ExplainState *es);
104 static void show_sortorder_options(StringInfo buf, Node *sortexpr,
105  Oid sortOperator, Oid collation, bool nullsFirst);
106 static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
107  List *ancestors, ExplainState *es);
108 static void show_sort_info(SortState *sortstate, ExplainState *es);
109 static void show_incremental_sort_info(IncrementalSortState *incrsortstate,
110  ExplainState *es);
111 static void show_hash_info(HashState *hashstate, ExplainState *es);
112 static void show_memoize_info(MemoizeState *mstate, List *ancestors,
113  ExplainState *es);
114 static void show_hashagg_info(AggState *hashstate, ExplainState *es);
115 static void show_tidbitmap_info(BitmapHeapScanState *planstate,
116  ExplainState *es);
117 static void show_instrumentation_count(const char *qlabel, int which,
118  PlanState *planstate, ExplainState *es);
119 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
120 static void show_eval_params(Bitmapset *bms_params, ExplainState *es);
121 static const char *explain_get_index_name(Oid indexId);
122 static void show_buffer_usage(ExplainState *es, const BufferUsage *usage,
123  bool planning);
124 static void show_wal_usage(ExplainState *es, const WalUsage *usage);
125 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
126  ExplainState *es);
127 static void ExplainScanTarget(Scan *plan, ExplainState *es);
128 static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
129 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
130 static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
131  ExplainState *es);
132 static void ExplainMemberNodes(PlanState **planstates, int nplans,
133  List *ancestors, ExplainState *es);
134 static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es);
135 static void ExplainSubPlans(List *plans, List *ancestors,
136  const char *relationship, ExplainState *es);
137 static void ExplainCustomChildren(CustomScanState *css,
138  List *ancestors, ExplainState *es);
139 static ExplainWorkersState *ExplainCreateWorkersState(int num_workers);
140 static void ExplainOpenWorker(int n, ExplainState *es);
141 static void ExplainCloseWorker(int n, ExplainState *es);
142 static void ExplainFlushWorkersState(ExplainState *es);
143 static void ExplainProperty(const char *qlabel, const char *unit,
144  const char *value, bool numeric, ExplainState *es);
145 static void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname,
146  bool labeled, int depth, ExplainState *es);
147 static void ExplainSaveGroup(ExplainState *es, int depth, int *state_save);
148 static void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save);
149 static void ExplainDummyGroup(const char *objtype, const char *labelname,
150  ExplainState *es);
151 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
152 static void ExplainIndentText(ExplainState *es);
153 static void ExplainJSONLineEnding(ExplainState *es);
154 static void ExplainYAMLLineStarting(ExplainState *es);
155 static void escape_yaml(StringInfo buf, const char *str);
156 
157 
158 
159 /*
160  * ExplainQuery -
161  * execute an EXPLAIN command
162  */
163 void
166 {
168  TupOutputState *tstate;
169  JumbleState *jstate = NULL;
170  Query *query;
171  List *rewritten;
172  ListCell *lc;
173  bool timing_set = false;
174  bool summary_set = false;
175 
176  /* Parse options list. */
177  foreach(lc, stmt->options)
178  {
179  DefElem *opt = (DefElem *) lfirst(lc);
180 
181  if (strcmp(opt->defname, "analyze") == 0)
182  es->analyze = defGetBoolean(opt);
183  else if (strcmp(opt->defname, "verbose") == 0)
184  es->verbose = defGetBoolean(opt);
185  else if (strcmp(opt->defname, "costs") == 0)
186  es->costs = defGetBoolean(opt);
187  else if (strcmp(opt->defname, "buffers") == 0)
188  es->buffers = defGetBoolean(opt);
189  else if (strcmp(opt->defname, "wal") == 0)
190  es->wal = defGetBoolean(opt);
191  else if (strcmp(opt->defname, "settings") == 0)
192  es->settings = defGetBoolean(opt);
193  else if (strcmp(opt->defname, "timing") == 0)
194  {
195  timing_set = true;
196  es->timing = defGetBoolean(opt);
197  }
198  else if (strcmp(opt->defname, "summary") == 0)
199  {
200  summary_set = true;
201  es->summary = defGetBoolean(opt);
202  }
203  else if (strcmp(opt->defname, "format") == 0)
204  {
205  char *p = defGetString(opt);
206 
207  if (strcmp(p, "text") == 0)
209  else if (strcmp(p, "xml") == 0)
211  else if (strcmp(p, "json") == 0)
213  else if (strcmp(p, "yaml") == 0)
215  else
216  ereport(ERROR,
217  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
218  errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
219  opt->defname, p),
220  parser_errposition(pstate, opt->location)));
221  }
222  else
223  ereport(ERROR,
224  (errcode(ERRCODE_SYNTAX_ERROR),
225  errmsg("unrecognized EXPLAIN option \"%s\"",
226  opt->defname),
227  parser_errposition(pstate, opt->location)));
228  }
229 
230  if (es->wal && !es->analyze)
231  ereport(ERROR,
232  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
233  errmsg("EXPLAIN option WAL requires ANALYZE")));
234 
235  /* if the timing was not set explicitly, set default value */
236  es->timing = (timing_set) ? es->timing : es->analyze;
237 
238  /* check that timing is used with EXPLAIN ANALYZE */
239  if (es->timing && !es->analyze)
240  ereport(ERROR,
241  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
242  errmsg("EXPLAIN option TIMING requires ANALYZE")));
243 
244  /* if the summary was not set explicitly, set default value */
245  es->summary = (summary_set) ? es->summary : es->analyze;
246 
247  query = castNode(Query, stmt->query);
248  if (IsQueryIdEnabled())
249  jstate = JumbleQuery(query, pstate->p_sourcetext);
250 
252  (*post_parse_analyze_hook) (pstate, query, jstate);
253 
254  /*
255  * Parse analysis was done already, but we still have to run the rule
256  * rewriter. We do not do AcquireRewriteLocks: we assume the query either
257  * came straight from the parser, or suitable locks were acquired by
258  * plancache.c.
259  */
260  rewritten = QueryRewrite(castNode(Query, stmt->query));
261 
262  /* emit opening boilerplate */
263  ExplainBeginOutput(es);
264 
265  if (rewritten == NIL)
266  {
267  /*
268  * In the case of an INSTEAD NOTHING, tell at least that. But in
269  * non-text format, the output is delimited, so this isn't necessary.
270  */
271  if (es->format == EXPLAIN_FORMAT_TEXT)
272  appendStringInfoString(es->str, "Query rewrites to nothing\n");
273  }
274  else
275  {
276  ListCell *l;
277 
278  /* Explain every plan */
279  foreach(l, rewritten)
280  {
282  CURSOR_OPT_PARALLEL_OK, NULL, es,
283  pstate->p_sourcetext, params, pstate->p_queryEnv);
284 
285  /* Separate plans with an appropriate separator */
286  if (lnext(rewritten, l) != NULL)
288  }
289  }
290 
291  /* emit closing boilerplate */
292  ExplainEndOutput(es);
293  Assert(es->indent == 0);
294 
295  /* output tuples */
297  &TTSOpsVirtual);
298  if (es->format == EXPLAIN_FORMAT_TEXT)
299  do_text_output_multiline(tstate, es->str->data);
300  else
301  do_text_output_oneline(tstate, es->str->data);
302  end_tup_output(tstate);
303 
304  pfree(es->str->data);
305 }
306 
307 /*
308  * Create a new ExplainState struct initialized with default options.
309  */
310 ExplainState *
312 {
313  ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
314 
315  /* Set default options (most fields can be left as zeroes). */
316  es->costs = true;
317  /* Prepare output buffer. */
318  es->str = makeStringInfo();
319 
320  return es;
321 }
322 
323 /*
324  * ExplainResultDesc -
325  * construct the result tupledesc for an EXPLAIN
326  */
327 TupleDesc
329 {
330  TupleDesc tupdesc;
331  ListCell *lc;
332  Oid result_type = TEXTOID;
333 
334  /* Check for XML format option */
335  foreach(lc, stmt->options)
336  {
337  DefElem *opt = (DefElem *) lfirst(lc);
338 
339  if (strcmp(opt->defname, "format") == 0)
340  {
341  char *p = defGetString(opt);
342 
343  if (strcmp(p, "xml") == 0)
344  result_type = XMLOID;
345  else if (strcmp(p, "json") == 0)
346  result_type = JSONOID;
347  else
348  result_type = TEXTOID;
349  /* don't "break", as ExplainQuery will use the last value */
350  }
351  }
352 
353  /* Need a tuple descriptor representing a single TEXT or XML column */
354  tupdesc = CreateTemplateTupleDesc(1);
355  TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
356  result_type, -1, 0);
357  return tupdesc;
358 }
359 
360 /*
361  * ExplainOneQuery -
362  * print out the execution plan for one Query
363  *
364  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
365  */
366 static void
367 ExplainOneQuery(Query *query, int cursorOptions,
368  IntoClause *into, ExplainState *es,
369  const char *queryString, ParamListInfo params,
370  QueryEnvironment *queryEnv)
371 {
372  /* planner will not cope with utility statements */
373  if (query->commandType == CMD_UTILITY)
374  {
375  ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
376  queryEnv);
377  return;
378  }
379 
380  /* if an advisor plugin is present, let it manage things */
382  (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
383  queryString, params, queryEnv);
384  else
385  {
386  PlannedStmt *plan;
387  instr_time planstart,
388  planduration;
389  BufferUsage bufusage_start,
390  bufusage;
391 
392  if (es->buffers)
393  bufusage_start = pgBufferUsage;
394  INSTR_TIME_SET_CURRENT(planstart);
395 
396  /* plan the query */
397  plan = pg_plan_query(query, queryString, cursorOptions, params);
398 
399  INSTR_TIME_SET_CURRENT(planduration);
400  INSTR_TIME_SUBTRACT(planduration, planstart);
401 
402  /* calc differences of buffer counters. */
403  if (es->buffers)
404  {
405  memset(&bufusage, 0, sizeof(BufferUsage));
406  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
407  }
408 
409  /* run it (if needed) and produce output */
410  ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
411  &planduration, (es->buffers ? &bufusage : NULL));
412  }
413 }
414 
415 /*
416  * ExplainOneUtility -
417  * print out the execution plan for one utility statement
418  * (In general, utility statements don't have plans, but there are some
419  * we treat as special cases)
420  *
421  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
422  *
423  * This is exported because it's called back from prepare.c in the
424  * EXPLAIN EXECUTE case. In that case, we'll be dealing with a statement
425  * that's in the plan cache, so we have to ensure we don't modify it.
426  */
427 void
429  const char *queryString, ParamListInfo params,
430  QueryEnvironment *queryEnv)
431 {
432  if (utilityStmt == NULL)
433  return;
434 
435  if (IsA(utilityStmt, CreateTableAsStmt))
436  {
437  /*
438  * We have to rewrite the contained SELECT and then pass it back to
439  * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
440  */
441  CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
442  List *rewritten;
443 
444  /*
445  * Check if the relation exists or not. This is done at this stage to
446  * avoid query planning or execution.
447  */
448  if (CreateTableAsRelExists(ctas))
449  {
450  if (ctas->objtype == OBJECT_TABLE)
451  ExplainDummyGroup("CREATE TABLE AS", NULL, es);
452  else if (ctas->objtype == OBJECT_MATVIEW)
453  ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
454  else
455  elog(ERROR, "unexpected object type: %d",
456  (int) ctas->objtype);
457  return;
458  }
459 
460  rewritten = QueryRewrite(castNode(Query, copyObject(ctas->query)));
461  Assert(list_length(rewritten) == 1);
462  ExplainOneQuery(linitial_node(Query, rewritten),
463  CURSOR_OPT_PARALLEL_OK, ctas->into, es,
464  queryString, params, queryEnv);
465  }
466  else if (IsA(utilityStmt, DeclareCursorStmt))
467  {
468  /*
469  * Likewise for DECLARE CURSOR.
470  *
471  * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
472  * actually run the query. This is different from pre-8.3 behavior
473  * but seems more useful than not running the query. No cursor will
474  * be created, however.
475  */
476  DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
477  List *rewritten;
478 
479  rewritten = QueryRewrite(castNode(Query, copyObject(dcs->query)));
480  Assert(list_length(rewritten) == 1);
481  ExplainOneQuery(linitial_node(Query, rewritten),
482  dcs->options, NULL, es,
483  queryString, params, queryEnv);
484  }
485  else if (IsA(utilityStmt, ExecuteStmt))
486  ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
487  queryString, params, queryEnv);
488  else if (IsA(utilityStmt, NotifyStmt))
489  {
490  if (es->format == EXPLAIN_FORMAT_TEXT)
491  appendStringInfoString(es->str, "NOTIFY\n");
492  else
493  ExplainDummyGroup("Notify", NULL, es);
494  }
495  else
496  {
497  if (es->format == EXPLAIN_FORMAT_TEXT)
499  "Utility statements have no plan structure\n");
500  else
501  ExplainDummyGroup("Utility Statement", NULL, es);
502  }
503 }
504 
505 /*
506  * ExplainOnePlan -
507  * given a planned query, execute it if needed, and then print
508  * EXPLAIN output
509  *
510  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
511  * in which case executing the query should result in creating that table.
512  *
513  * This is exported because it's called back from prepare.c in the
514  * EXPLAIN EXECUTE case, and because an index advisor plugin would need
515  * to call it.
516  */
517 void
519  const char *queryString, ParamListInfo params,
520  QueryEnvironment *queryEnv, const instr_time *planduration,
521  const BufferUsage *bufusage)
522 {
524  QueryDesc *queryDesc;
525  instr_time starttime;
526  double totaltime = 0;
527  int eflags;
528  int instrument_option = 0;
529 
530  Assert(plannedstmt->commandType != CMD_UTILITY);
531 
532  if (es->analyze && es->timing)
533  instrument_option |= INSTRUMENT_TIMER;
534  else if (es->analyze)
535  instrument_option |= INSTRUMENT_ROWS;
536 
537  if (es->buffers)
538  instrument_option |= INSTRUMENT_BUFFERS;
539  if (es->wal)
540  instrument_option |= INSTRUMENT_WAL;
541 
542  /*
543  * We always collect timing for the entire statement, even when node-level
544  * timing is off, so we don't look at es->timing here. (We could skip
545  * this if !es->summary, but it's hardly worth the complication.)
546  */
547  INSTR_TIME_SET_CURRENT(starttime);
548 
549  /*
550  * Use a snapshot with an updated command ID to ensure this query sees
551  * results of any previously executed queries.
552  */
555 
556  /*
557  * Normally we discard the query's output, but if explaining CREATE TABLE
558  * AS, we'd better use the appropriate tuple receiver.
559  */
560  if (into)
562  else
564 
565  /* Create a QueryDesc for the query */
566  queryDesc = CreateQueryDesc(plannedstmt, queryString,
568  dest, params, queryEnv, instrument_option);
569 
570  /* Select execution options */
571  if (es->analyze)
572  eflags = 0; /* default run-to-completion flags */
573  else
574  eflags = EXEC_FLAG_EXPLAIN_ONLY;
575  if (into)
576  eflags |= GetIntoRelEFlags(into);
577 
578  /* call ExecutorStart to prepare the plan for execution */
579  ExecutorStart(queryDesc, eflags);
580 
581  /* Execute the plan for statistics if asked for */
582  if (es->analyze)
583  {
584  ScanDirection dir;
585 
586  /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
587  if (into && into->skipData)
589  else
590  dir = ForwardScanDirection;
591 
592  /* run the plan */
593  ExecutorRun(queryDesc, dir, 0L, true);
594 
595  /* run cleanup too */
596  ExecutorFinish(queryDesc);
597 
598  /* We can't run ExecutorEnd 'till we're done printing the stats... */
599  totaltime += elapsed_time(&starttime);
600  }
601 
602  ExplainOpenGroup("Query", NULL, true, es);
603 
604  /* Create textual dump of plan tree */
605  ExplainPrintPlan(es, queryDesc);
606 
607  /*
608  * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
609  * the queryid in any of the EXPLAIN plans to keep stable the results
610  * generated by regression test suites.
611  */
612  if (es->verbose && plannedstmt->queryId != UINT64CONST(0) &&
614  {
615  /*
616  * Output the queryid as an int64 rather than a uint64 so we match
617  * what would be seen in the BIGINT pg_stat_statements.queryid column.
618  */
619  ExplainPropertyInteger("Query Identifier", NULL, (int64)
620  plannedstmt->queryId, es);
621  }
622 
623  /* Show buffer usage in planning */
624  if (bufusage)
625  {
626  ExplainOpenGroup("Planning", "Planning", true, es);
627  show_buffer_usage(es, bufusage, true);
628  ExplainCloseGroup("Planning", "Planning", true, es);
629  }
630 
631  if (es->summary && planduration)
632  {
633  double plantime = INSTR_TIME_GET_DOUBLE(*planduration);
634 
635  ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
636  }
637 
638  /* Print info about runtime of triggers */
639  if (es->analyze)
640  ExplainPrintTriggers(es, queryDesc);
641 
642  /*
643  * Print info about JITing. Tied to es->costs because we don't want to
644  * display this in regression tests, as it'd cause output differences
645  * depending on build options. Might want to separate that out from COSTS
646  * at a later stage.
647  */
648  if (es->costs)
649  ExplainPrintJITSummary(es, queryDesc);
650 
651  /*
652  * Close down the query and free resources. Include time for this in the
653  * total execution time (although it should be pretty minimal).
654  */
655  INSTR_TIME_SET_CURRENT(starttime);
656 
657  ExecutorEnd(queryDesc);
658 
659  FreeQueryDesc(queryDesc);
660 
662 
663  /* We need a CCI just in case query expanded to multiple plans */
664  if (es->analyze)
666 
667  totaltime += elapsed_time(&starttime);
668 
669  /*
670  * We only report execution time if we actually ran the query (that is,
671  * the user specified ANALYZE), and if summary reporting is enabled (the
672  * user can set SUMMARY OFF to not have the timing information included in
673  * the output). By default, ANALYZE sets SUMMARY to true.
674  */
675  if (es->summary && es->analyze)
676  ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
677  es);
678 
679  ExplainCloseGroup("Query", NULL, true, es);
680 }
681 
682 /*
683  * ExplainPrintSettings -
684  * Print summary of modified settings affecting query planning.
685  */
686 static void
688 {
689  int num;
690  struct config_generic **gucs;
691 
692  /* bail out if information about settings not requested */
693  if (!es->settings)
694  return;
695 
696  /* request an array of relevant settings */
697  gucs = get_explain_guc_options(&num);
698 
699  if (es->format != EXPLAIN_FORMAT_TEXT)
700  {
701  ExplainOpenGroup("Settings", "Settings", true, es);
702 
703  for (int i = 0; i < num; i++)
704  {
705  char *setting;
706  struct config_generic *conf = gucs[i];
707 
708  setting = GetConfigOptionByName(conf->name, NULL, true);
709 
710  ExplainPropertyText(conf->name, setting, es);
711  }
712 
713  ExplainCloseGroup("Settings", "Settings", true, es);
714  }
715  else
716  {
718 
719  /* In TEXT mode, print nothing if there are no options */
720  if (num <= 0)
721  return;
722 
724 
725  for (int i = 0; i < num; i++)
726  {
727  char *setting;
728  struct config_generic *conf = gucs[i];
729 
730  if (i > 0)
731  appendStringInfoString(&str, ", ");
732 
733  setting = GetConfigOptionByName(conf->name, NULL, true);
734 
735  if (setting)
736  appendStringInfo(&str, "%s = '%s'", conf->name, setting);
737  else
738  appendStringInfo(&str, "%s = NULL", conf->name);
739  }
740 
741  ExplainPropertyText("Settings", str.data, es);
742  }
743 }
744 
745 /*
746  * ExplainPrintPlan -
747  * convert a QueryDesc's plan tree to text and append it to es->str
748  *
749  * The caller should have set up the options fields of *es, as well as
750  * initializing the output buffer es->str. Also, output formatting state
751  * such as the indent level is assumed valid. Plan-tree-specific fields
752  * in *es are initialized here.
753  *
754  * NB: will not work on utility statements
755  */
756 void
758 {
759  Bitmapset *rels_used = NULL;
760  PlanState *ps;
761 
762  /* Set up ExplainState fields associated with this plan tree */
763  Assert(queryDesc->plannedstmt != NULL);
764  es->pstmt = queryDesc->plannedstmt;
765  es->rtable = queryDesc->plannedstmt->rtable;
766  ExplainPreScanNode(queryDesc->planstate, &rels_used);
769  es->rtable_names);
770  es->printed_subplans = NULL;
771 
772  /*
773  * Sometimes we mark a Gather node as "invisible", which means that it's
774  * not to be displayed in EXPLAIN output. The purpose of this is to allow
775  * running regression tests with force_parallel_mode=regress to get the
776  * same results as running the same tests with force_parallel_mode=off.
777  * Such marking is currently only supported on a Gather at the top of the
778  * plan. We skip that node, and we must also hide per-worker detail data
779  * further down in the plan tree.
780  */
781  ps = queryDesc->planstate;
782  if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
783  {
784  ps = outerPlanState(ps);
785  es->hide_workers = true;
786  }
787  ExplainNode(ps, NIL, NULL, NULL, es);
788 
789  /*
790  * If requested, include information about GUC parameters with values that
791  * don't match the built-in defaults.
792  */
794 }
795 
796 /*
797  * ExplainPrintTriggers -
798  * convert a QueryDesc's trigger statistics to text and append it to
799  * es->str
800  *
801  * The caller should have set up the options fields of *es, as well as
802  * initializing the output buffer es->str. Other fields in *es are
803  * initialized here.
804  */
805 void
807 {
808  ResultRelInfo *rInfo;
809  bool show_relname;
810  List *resultrels;
811  List *routerels;
812  List *targrels;
813  ListCell *l;
814 
815  resultrels = queryDesc->estate->es_opened_result_relations;
816  routerels = queryDesc->estate->es_tuple_routing_result_relations;
817  targrels = queryDesc->estate->es_trig_target_relations;
818 
819  ExplainOpenGroup("Triggers", "Triggers", false, es);
820 
821  show_relname = (list_length(resultrels) > 1 ||
822  routerels != NIL || targrels != NIL);
823  foreach(l, resultrels)
824  {
825  rInfo = (ResultRelInfo *) lfirst(l);
826  report_triggers(rInfo, show_relname, es);
827  }
828 
829  foreach(l, routerels)
830  {
831  rInfo = (ResultRelInfo *) lfirst(l);
832  report_triggers(rInfo, show_relname, es);
833  }
834 
835  foreach(l, targrels)
836  {
837  rInfo = (ResultRelInfo *) lfirst(l);
838  report_triggers(rInfo, show_relname, es);
839  }
840 
841  ExplainCloseGroup("Triggers", "Triggers", false, es);
842 }
843 
844 /*
845  * ExplainPrintJITSummary -
846  * Print summarized JIT instrumentation from leader and workers
847  */
848 void
850 {
851  JitInstrumentation ji = {0};
852 
853  if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
854  return;
855 
856  /*
857  * Work with a copy instead of modifying the leader state, since this
858  * function may be called twice
859  */
860  if (queryDesc->estate->es_jit)
861  InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
862 
863  /* If this process has done JIT in parallel workers, merge stats */
864  if (queryDesc->estate->es_jit_worker_instr)
865  InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
866 
867  ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
868 }
869 
870 /*
871  * ExplainPrintJIT -
872  * Append information about JITing to es->str.
873  */
874 static void
876 {
877  instr_time total_time;
878 
879  /* don't print information if no JITing happened */
880  if (!ji || ji->created_functions == 0)
881  return;
882 
883  /* calculate total time */
884  INSTR_TIME_SET_ZERO(total_time);
885  INSTR_TIME_ADD(total_time, ji->generation_counter);
886  INSTR_TIME_ADD(total_time, ji->inlining_counter);
887  INSTR_TIME_ADD(total_time, ji->optimization_counter);
888  INSTR_TIME_ADD(total_time, ji->emission_counter);
889 
890  ExplainOpenGroup("JIT", "JIT", true, es);
891 
892  /* for higher density, open code the text output format */
893  if (es->format == EXPLAIN_FORMAT_TEXT)
894  {
895  ExplainIndentText(es);
896  appendStringInfoString(es->str, "JIT:\n");
897  es->indent++;
898 
899  ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
900 
901  ExplainIndentText(es);
902  appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
903  "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
904  "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
905  "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
906  "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
907 
908  if (es->analyze && es->timing)
909  {
910  ExplainIndentText(es);
911  appendStringInfo(es->str,
912  "Timing: %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
913  "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
914  "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
915  "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
916  "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
917  "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
918  }
919 
920  es->indent--;
921  }
922  else
923  {
924  ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
925 
926  ExplainOpenGroup("Options", "Options", true, es);
927  ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
928  ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
929  ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
930  ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
931  ExplainCloseGroup("Options", "Options", true, es);
932 
933  if (es->analyze && es->timing)
934  {
935  ExplainOpenGroup("Timing", "Timing", true, es);
936 
937  ExplainPropertyFloat("Generation", "ms",
939  3, es);
940  ExplainPropertyFloat("Inlining", "ms",
942  3, es);
943  ExplainPropertyFloat("Optimization", "ms",
945  3, es);
946  ExplainPropertyFloat("Emission", "ms",
948  3, es);
949  ExplainPropertyFloat("Total", "ms",
950  1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
951  3, es);
952 
953  ExplainCloseGroup("Timing", "Timing", true, es);
954  }
955  }
956 
957  ExplainCloseGroup("JIT", "JIT", true, es);
958 }
959 
960 /*
961  * ExplainQueryText -
962  * add a "Query Text" node that contains the actual text of the query
963  *
964  * The caller should have set up the options fields of *es, as well as
965  * initializing the output buffer es->str.
966  *
967  */
968 void
970 {
971  if (queryDesc->sourceText)
972  ExplainPropertyText("Query Text", queryDesc->sourceText, es);
973 }
974 
975 /*
976  * report_triggers -
977  * report execution stats for a single relation's triggers
978  */
979 static void
980 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
981 {
982  int nt;
983 
984  if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
985  return;
986  for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
987  {
988  Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
989  Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
990  char *relname;
991  char *conname = NULL;
992 
993  /* Must clean up instrumentation state */
994  InstrEndLoop(instr);
995 
996  /*
997  * We ignore triggers that were never invoked; they likely aren't
998  * relevant to the current query type.
999  */
1000  if (instr->ntuples == 0)
1001  continue;
1002 
1003  ExplainOpenGroup("Trigger", NULL, true, es);
1004 
1006  if (OidIsValid(trig->tgconstraint))
1007  conname = get_constraint_name(trig->tgconstraint);
1008 
1009  /*
1010  * In text format, we avoid printing both the trigger name and the
1011  * constraint name unless VERBOSE is specified. In non-text formats
1012  * we just print everything.
1013  */
1014  if (es->format == EXPLAIN_FORMAT_TEXT)
1015  {
1016  if (es->verbose || conname == NULL)
1017  appendStringInfo(es->str, "Trigger %s", trig->tgname);
1018  else
1019  appendStringInfoString(es->str, "Trigger");
1020  if (conname)
1021  appendStringInfo(es->str, " for constraint %s", conname);
1022  if (show_relname)
1023  appendStringInfo(es->str, " on %s", relname);
1024  if (es->timing)
1025  appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
1026  1000.0 * instr->total, instr->ntuples);
1027  else
1028  appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
1029  }
1030  else
1031  {
1032  ExplainPropertyText("Trigger Name", trig->tgname, es);
1033  if (conname)
1034  ExplainPropertyText("Constraint Name", conname, es);
1035  ExplainPropertyText("Relation", relname, es);
1036  if (es->timing)
1037  ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
1038  es);
1039  ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
1040  }
1041 
1042  if (conname)
1043  pfree(conname);
1044 
1045  ExplainCloseGroup("Trigger", NULL, true, es);
1046  }
1047 }
1048 
1049 /* Compute elapsed time in seconds since given timestamp */
1050 static double
1052 {
1053  instr_time endtime;
1054 
1055  INSTR_TIME_SET_CURRENT(endtime);
1056  INSTR_TIME_SUBTRACT(endtime, *starttime);
1057  return INSTR_TIME_GET_DOUBLE(endtime);
1058 }
1059 
1060 /*
1061  * ExplainPreScanNode -
1062  * Prescan the planstate tree to identify which RTEs are referenced
1063  *
1064  * Adds the relid of each referenced RTE to *rels_used. The result controls
1065  * which RTEs are assigned aliases by select_rtable_names_for_explain.
1066  * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
1067  * that never appear in the EXPLAIN output (such as inheritance parents).
1068  */
1069 static bool
1070 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
1071 {
1072  Plan *plan = planstate->plan;
1073 
1074  switch (nodeTag(plan))
1075  {
1076  case T_SeqScan:
1077  case T_SampleScan:
1078  case T_IndexScan:
1079  case T_IndexOnlyScan:
1080  case T_BitmapHeapScan:
1081  case T_TidScan:
1082  case T_TidRangeScan:
1083  case T_SubqueryScan:
1084  case T_FunctionScan:
1085  case T_TableFuncScan:
1086  case T_ValuesScan:
1087  case T_CteScan:
1088  case T_NamedTuplestoreScan:
1089  case T_WorkTableScan:
1090  *rels_used = bms_add_member(*rels_used,
1091  ((Scan *) plan)->scanrelid);
1092  break;
1093  case T_ForeignScan:
1094  *rels_used = bms_add_members(*rels_used,
1095  ((ForeignScan *) plan)->fs_relids);
1096  break;
1097  case T_CustomScan:
1098  *rels_used = bms_add_members(*rels_used,
1099  ((CustomScan *) plan)->custom_relids);
1100  break;
1101  case T_ModifyTable:
1102  *rels_used = bms_add_member(*rels_used,
1103  ((ModifyTable *) plan)->nominalRelation);
1104  if (((ModifyTable *) plan)->exclRelRTI)
1105  *rels_used = bms_add_member(*rels_used,
1106  ((ModifyTable *) plan)->exclRelRTI);
1107  break;
1108  case T_Append:
1109  *rels_used = bms_add_members(*rels_used,
1110  ((Append *) plan)->apprelids);
1111  break;
1112  case T_MergeAppend:
1113  *rels_used = bms_add_members(*rels_used,
1114  ((MergeAppend *) plan)->apprelids);
1115  break;
1116  default:
1117  break;
1118  }
1119 
1120  return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
1121 }
1122 
1123 /*
1124  * ExplainNode -
1125  * Appends a description of a plan tree to es->str
1126  *
1127  * planstate points to the executor state node for the current plan node.
1128  * We need to work from a PlanState node, not just a Plan node, in order to
1129  * get at the instrumentation data (if any) as well as the list of subplans.
1130  *
1131  * ancestors is a list of parent Plan and SubPlan nodes, most-closely-nested
1132  * first. These are needed in order to interpret PARAM_EXEC Params.
1133  *
1134  * relationship describes the relationship of this plan node to its parent
1135  * (eg, "Outer", "Inner"); it can be null at top level. plan_name is an
1136  * optional name to be attached to the node.
1137  *
1138  * In text format, es->indent is controlled in this function since we only
1139  * want it to change at plan-node boundaries (but a few subroutines will
1140  * transiently increment it). In non-text formats, es->indent corresponds
1141  * to the nesting depth of logical output groups, and therefore is controlled
1142  * by ExplainOpenGroup/ExplainCloseGroup.
1143  */
1144 static void
1145 ExplainNode(PlanState *planstate, List *ancestors,
1146  const char *relationship, const char *plan_name,
1147  ExplainState *es)
1148 {
1149  Plan *plan = planstate->plan;
1150  const char *pname; /* node type name for text output */
1151  const char *sname; /* node type name for non-text output */
1152  const char *strategy = NULL;
1153  const char *partialmode = NULL;
1154  const char *operation = NULL;
1155  const char *custom_name = NULL;
1156  ExplainWorkersState *save_workers_state = es->workers_state;
1157  int save_indent = es->indent;
1158  bool haschildren;
1159 
1160  /*
1161  * Prepare per-worker output buffers, if needed. We'll append the data in
1162  * these to the main output string further down.
1163  */
1164  if (planstate->worker_instrument && es->analyze && !es->hide_workers)
1166  else
1167  es->workers_state = NULL;
1168 
1169  /* Identify plan node type, and print generic details */
1170  switch (nodeTag(plan))
1171  {
1172  case T_Result:
1173  pname = sname = "Result";
1174  break;
1175  case T_ProjectSet:
1176  pname = sname = "ProjectSet";
1177  break;
1178  case T_ModifyTable:
1179  sname = "ModifyTable";
1180  switch (((ModifyTable *) plan)->operation)
1181  {
1182  case CMD_INSERT:
1183  pname = operation = "Insert";
1184  break;
1185  case CMD_UPDATE:
1186  pname = operation = "Update";
1187  break;
1188  case CMD_DELETE:
1189  pname = operation = "Delete";
1190  break;
1191  case CMD_MERGE:
1192  pname = operation = "Merge";
1193  break;
1194  default:
1195  pname = "???";
1196  break;
1197  }
1198  break;
1199  case T_Append:
1200  pname = sname = "Append";
1201  break;
1202  case T_MergeAppend:
1203  pname = sname = "Merge Append";
1204  break;
1205  case T_RecursiveUnion:
1206  pname = sname = "Recursive Union";
1207  break;
1208  case T_BitmapAnd:
1209  pname = sname = "BitmapAnd";
1210  break;
1211  case T_BitmapOr:
1212  pname = sname = "BitmapOr";
1213  break;
1214  case T_NestLoop:
1215  pname = sname = "Nested Loop";
1216  break;
1217  case T_MergeJoin:
1218  pname = "Merge"; /* "Join" gets added by jointype switch */
1219  sname = "Merge Join";
1220  break;
1221  case T_HashJoin:
1222  pname = "Hash"; /* "Join" gets added by jointype switch */
1223  sname = "Hash Join";
1224  break;
1225  case T_SeqScan:
1226  pname = sname = "Seq Scan";
1227  break;
1228  case T_SampleScan:
1229  pname = sname = "Sample Scan";
1230  break;
1231  case T_Gather:
1232  pname = sname = "Gather";
1233  break;
1234  case T_GatherMerge:
1235  pname = sname = "Gather Merge";
1236  break;
1237  case T_IndexScan:
1238  pname = sname = "Index Scan";
1239  break;
1240  case T_IndexOnlyScan:
1241  pname = sname = "Index Only Scan";
1242  break;
1243  case T_BitmapIndexScan:
1244  pname = sname = "Bitmap Index Scan";
1245  break;
1246  case T_BitmapHeapScan:
1247  pname = sname = "Bitmap Heap Scan";
1248  break;
1249  case T_TidScan:
1250  pname = sname = "Tid Scan";
1251  break;
1252  case T_TidRangeScan:
1253  pname = sname = "Tid Range Scan";
1254  break;
1255  case T_SubqueryScan:
1256  pname = sname = "Subquery Scan";
1257  break;
1258  case T_FunctionScan:
1259  pname = sname = "Function Scan";
1260  break;
1261  case T_TableFuncScan:
1262  pname = sname = "Table Function Scan";
1263  break;
1264  case T_ValuesScan:
1265  pname = sname = "Values Scan";
1266  break;
1267  case T_CteScan:
1268  pname = sname = "CTE Scan";
1269  break;
1270  case T_NamedTuplestoreScan:
1271  pname = sname = "Named Tuplestore Scan";
1272  break;
1273  case T_WorkTableScan:
1274  pname = sname = "WorkTable Scan";
1275  break;
1276  case T_ForeignScan:
1277  sname = "Foreign Scan";
1278  switch (((ForeignScan *) plan)->operation)
1279  {
1280  case CMD_SELECT:
1281  pname = "Foreign Scan";
1282  operation = "Select";
1283  break;
1284  case CMD_INSERT:
1285  pname = "Foreign Insert";
1286  operation = "Insert";
1287  break;
1288  case CMD_UPDATE:
1289  pname = "Foreign Update";
1290  operation = "Update";
1291  break;
1292  case CMD_DELETE:
1293  pname = "Foreign Delete";
1294  operation = "Delete";
1295  break;
1296  default:
1297  pname = "???";
1298  break;
1299  }
1300  break;
1301  case T_CustomScan:
1302  sname = "Custom Scan";
1303  custom_name = ((CustomScan *) plan)->methods->CustomName;
1304  if (custom_name)
1305  pname = psprintf("Custom Scan (%s)", custom_name);
1306  else
1307  pname = sname;
1308  break;
1309  case T_Material:
1310  pname = sname = "Materialize";
1311  break;
1312  case T_Memoize:
1313  pname = sname = "Memoize";
1314  break;
1315  case T_Sort:
1316  pname = sname = "Sort";
1317  break;
1318  case T_IncrementalSort:
1319  pname = sname = "Incremental Sort";
1320  break;
1321  case T_Group:
1322  pname = sname = "Group";
1323  break;
1324  case T_Agg:
1325  {
1326  Agg *agg = (Agg *) plan;
1327 
1328  sname = "Aggregate";
1329  switch (agg->aggstrategy)
1330  {
1331  case AGG_PLAIN:
1332  pname = "Aggregate";
1333  strategy = "Plain";
1334  break;
1335  case AGG_SORTED:
1336  pname = "GroupAggregate";
1337  strategy = "Sorted";
1338  break;
1339  case AGG_HASHED:
1340  pname = "HashAggregate";
1341  strategy = "Hashed";
1342  break;
1343  case AGG_MIXED:
1344  pname = "MixedAggregate";
1345  strategy = "Mixed";
1346  break;
1347  default:
1348  pname = "Aggregate ???";
1349  strategy = "???";
1350  break;
1351  }
1352 
1353  if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
1354  {
1355  partialmode = "Partial";
1356  pname = psprintf("%s %s", partialmode, pname);
1357  }
1358  else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
1359  {
1360  partialmode = "Finalize";
1361  pname = psprintf("%s %s", partialmode, pname);
1362  }
1363  else
1364  partialmode = "Simple";
1365  }
1366  break;
1367  case T_WindowAgg:
1368  pname = sname = "WindowAgg";
1369  break;
1370  case T_Unique:
1371  pname = sname = "Unique";
1372  break;
1373  case T_SetOp:
1374  sname = "SetOp";
1375  switch (((SetOp *) plan)->strategy)
1376  {
1377  case SETOP_SORTED:
1378  pname = "SetOp";
1379  strategy = "Sorted";
1380  break;
1381  case SETOP_HASHED:
1382  pname = "HashSetOp";
1383  strategy = "Hashed";
1384  break;
1385  default:
1386  pname = "SetOp ???";
1387  strategy = "???";
1388  break;
1389  }
1390  break;
1391  case T_LockRows:
1392  pname = sname = "LockRows";
1393  break;
1394  case T_Limit:
1395  pname = sname = "Limit";
1396  break;
1397  case T_Hash:
1398  pname = sname = "Hash";
1399  break;
1400  default:
1401  pname = sname = "???";
1402  break;
1403  }
1404 
1405  ExplainOpenGroup("Plan",
1406  relationship ? NULL : "Plan",
1407  true, es);
1408 
1409  if (es->format == EXPLAIN_FORMAT_TEXT)
1410  {
1411  if (plan_name)
1412  {
1413  ExplainIndentText(es);
1414  appendStringInfo(es->str, "%s\n", plan_name);
1415  es->indent++;
1416  }
1417  if (es->indent)
1418  {
1419  ExplainIndentText(es);
1420  appendStringInfoString(es->str, "-> ");
1421  es->indent += 2;
1422  }
1423  if (plan->parallel_aware)
1424  appendStringInfoString(es->str, "Parallel ");
1425  if (plan->async_capable)
1426  appendStringInfoString(es->str, "Async ");
1427  appendStringInfoString(es->str, pname);
1428  es->indent++;
1429  }
1430  else
1431  {
1432  ExplainPropertyText("Node Type", sname, es);
1433  if (strategy)
1434  ExplainPropertyText("Strategy", strategy, es);
1435  if (partialmode)
1436  ExplainPropertyText("Partial Mode", partialmode, es);
1437  if (operation)
1438  ExplainPropertyText("Operation", operation, es);
1439  if (relationship)
1440  ExplainPropertyText("Parent Relationship", relationship, es);
1441  if (plan_name)
1442  ExplainPropertyText("Subplan Name", plan_name, es);
1443  if (custom_name)
1444  ExplainPropertyText("Custom Plan Provider", custom_name, es);
1445  ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
1446  ExplainPropertyBool("Async Capable", plan->async_capable, es);
1447  }
1448 
1449  switch (nodeTag(plan))
1450  {
1451  case T_SeqScan:
1452  case T_SampleScan:
1453  case T_BitmapHeapScan:
1454  case T_TidScan:
1455  case T_TidRangeScan:
1456  case T_SubqueryScan:
1457  case T_FunctionScan:
1458  case T_TableFuncScan:
1459  case T_ValuesScan:
1460  case T_CteScan:
1461  case T_WorkTableScan:
1462  ExplainScanTarget((Scan *) plan, es);
1463  break;
1464  case T_ForeignScan:
1465  case T_CustomScan:
1466  if (((Scan *) plan)->scanrelid > 0)
1467  ExplainScanTarget((Scan *) plan, es);
1468  break;
1469  case T_IndexScan:
1470  {
1471  IndexScan *indexscan = (IndexScan *) plan;
1472 
1473  ExplainIndexScanDetails(indexscan->indexid,
1474  indexscan->indexorderdir,
1475  es);
1476  ExplainScanTarget((Scan *) indexscan, es);
1477  }
1478  break;
1479  case T_IndexOnlyScan:
1480  {
1481  IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
1482 
1483  ExplainIndexScanDetails(indexonlyscan->indexid,
1484  indexonlyscan->indexorderdir,
1485  es);
1486  ExplainScanTarget((Scan *) indexonlyscan, es);
1487  }
1488  break;
1489  case T_BitmapIndexScan:
1490  {
1491  BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
1492  const char *indexname =
1493  explain_get_index_name(bitmapindexscan->indexid);
1494 
1495  if (es->format == EXPLAIN_FORMAT_TEXT)
1496  appendStringInfo(es->str, " on %s",
1497  quote_identifier(indexname));
1498  else
1499  ExplainPropertyText("Index Name", indexname, es);
1500  }
1501  break;
1502  case T_ModifyTable:
1503  ExplainModifyTarget((ModifyTable *) plan, es);
1504  break;
1505  case T_NestLoop:
1506  case T_MergeJoin:
1507  case T_HashJoin:
1508  {
1509  const char *jointype;
1510 
1511  switch (((Join *) plan)->jointype)
1512  {
1513  case JOIN_INNER:
1514  jointype = "Inner";
1515  break;
1516  case JOIN_LEFT:
1517  jointype = "Left";
1518  break;
1519  case JOIN_FULL:
1520  jointype = "Full";
1521  break;
1522  case JOIN_RIGHT:
1523  jointype = "Right";
1524  break;
1525  case JOIN_SEMI:
1526  jointype = "Semi";
1527  break;
1528  case JOIN_ANTI:
1529  jointype = "Anti";
1530  break;
1531  default:
1532  jointype = "???";
1533  break;
1534  }
1535  if (es->format == EXPLAIN_FORMAT_TEXT)
1536  {
1537  /*
1538  * For historical reasons, the join type is interpolated
1539  * into the node type name...
1540  */
1541  if (((Join *) plan)->jointype != JOIN_INNER)
1542  appendStringInfo(es->str, " %s Join", jointype);
1543  else if (!IsA(plan, NestLoop))
1544  appendStringInfoString(es->str, " Join");
1545  }
1546  else
1547  ExplainPropertyText("Join Type", jointype, es);
1548  }
1549  break;
1550  case T_SetOp:
1551  {
1552  const char *setopcmd;
1553 
1554  switch (((SetOp *) plan)->cmd)
1555  {
1556  case SETOPCMD_INTERSECT:
1557  setopcmd = "Intersect";
1558  break;
1560  setopcmd = "Intersect All";
1561  break;
1562  case SETOPCMD_EXCEPT:
1563  setopcmd = "Except";
1564  break;
1565  case SETOPCMD_EXCEPT_ALL:
1566  setopcmd = "Except All";
1567  break;
1568  default:
1569  setopcmd = "???";
1570  break;
1571  }
1572  if (es->format == EXPLAIN_FORMAT_TEXT)
1573  appendStringInfo(es->str, " %s", setopcmd);
1574  else
1575  ExplainPropertyText("Command", setopcmd, es);
1576  }
1577  break;
1578  default:
1579  break;
1580  }
1581 
1582  if (es->costs)
1583  {
1584  if (es->format == EXPLAIN_FORMAT_TEXT)
1585  {
1586  appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
1587  plan->startup_cost, plan->total_cost,
1588  plan->plan_rows, plan->plan_width);
1589  }
1590  else
1591  {
1592  ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
1593  2, es);
1594  ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
1595  2, es);
1596  ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
1597  0, es);
1598  ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
1599  es);
1600  }
1601  }
1602 
1603  /*
1604  * We have to forcibly clean up the instrumentation state because we
1605  * haven't done ExecutorEnd yet. This is pretty grotty ...
1606  *
1607  * Note: contrib/auto_explain could cause instrumentation to be set up
1608  * even though we didn't ask for it here. Be careful not to print any
1609  * instrumentation results the user didn't ask for. But we do the
1610  * InstrEndLoop call anyway, if possible, to reduce the number of cases
1611  * auto_explain has to contend with.
1612  */
1613  if (planstate->instrument)
1614  InstrEndLoop(planstate->instrument);
1615 
1616  if (es->analyze &&
1617  planstate->instrument && planstate->instrument->nloops > 0)
1618  {
1619  double nloops = planstate->instrument->nloops;
1620  double startup_ms = 1000.0 * planstate->instrument->startup / nloops;
1621  double total_ms = 1000.0 * planstate->instrument->total / nloops;
1622  double rows = planstate->instrument->ntuples / nloops;
1623 
1624  if (es->format == EXPLAIN_FORMAT_TEXT)
1625  {
1626  if (es->timing)
1627  appendStringInfo(es->str,
1628  " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1629  startup_ms, total_ms, rows, nloops);
1630  else
1631  appendStringInfo(es->str,
1632  " (actual rows=%.0f loops=%.0f)",
1633  rows, nloops);
1634  }
1635  else
1636  {
1637  if (es->timing)
1638  {
1639  ExplainPropertyFloat("Actual Startup Time", "ms", startup_ms,
1640  3, es);
1641  ExplainPropertyFloat("Actual Total Time", "ms", total_ms,
1642  3, es);
1643  }
1644  ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1645  ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1646  }
1647  }
1648  else if (es->analyze)
1649  {
1650  if (es->format == EXPLAIN_FORMAT_TEXT)
1651  appendStringInfoString(es->str, " (never executed)");
1652  else
1653  {
1654  if (es->timing)
1655  {
1656  ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
1657  ExplainPropertyFloat("Actual Total Time", "ms", 0.0, 3, es);
1658  }
1659  ExplainPropertyFloat("Actual Rows", NULL, 0.0, 0, es);
1660  ExplainPropertyFloat("Actual Loops", NULL, 0.0, 0, es);
1661  }
1662  }
1663 
1664  /* in text format, first line ends here */
1665  if (es->format == EXPLAIN_FORMAT_TEXT)
1666  appendStringInfoChar(es->str, '\n');
1667 
1668  /* prepare per-worker general execution details */
1669  if (es->workers_state && es->verbose)
1670  {
1671  WorkerInstrumentation *w = planstate->worker_instrument;
1672 
1673  for (int n = 0; n < w->num_workers; n++)
1674  {
1675  Instrumentation *instrument = &w->instrument[n];
1676  double nloops = instrument->nloops;
1677  double startup_ms;
1678  double total_ms;
1679  double rows;
1680 
1681  if (nloops <= 0)
1682  continue;
1683  startup_ms = 1000.0 * instrument->startup / nloops;
1684  total_ms = 1000.0 * instrument->total / nloops;
1685  rows = instrument->ntuples / nloops;
1686 
1687  ExplainOpenWorker(n, es);
1688 
1689  if (es->format == EXPLAIN_FORMAT_TEXT)
1690  {
1691  ExplainIndentText(es);
1692  if (es->timing)
1693  appendStringInfo(es->str,
1694  "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
1695  startup_ms, total_ms, rows, nloops);
1696  else
1697  appendStringInfo(es->str,
1698  "actual rows=%.0f loops=%.0f\n",
1699  rows, nloops);
1700  }
1701  else
1702  {
1703  if (es->timing)
1704  {
1705  ExplainPropertyFloat("Actual Startup Time", "ms",
1706  startup_ms, 3, es);
1707  ExplainPropertyFloat("Actual Total Time", "ms",
1708  total_ms, 3, es);
1709  }
1710  ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1711  ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1712  }
1713 
1714  ExplainCloseWorker(n, es);
1715  }
1716  }
1717 
1718  /* target list */
1719  if (es->verbose)
1720  show_plan_tlist(planstate, ancestors, es);
1721 
1722  /* unique join */
1723  switch (nodeTag(plan))
1724  {
1725  case T_NestLoop:
1726  case T_MergeJoin:
1727  case T_HashJoin:
1728  /* try not to be too chatty about this in text mode */
1729  if (es->format != EXPLAIN_FORMAT_TEXT ||
1730  (es->verbose && ((Join *) plan)->inner_unique))
1731  ExplainPropertyBool("Inner Unique",
1732  ((Join *) plan)->inner_unique,
1733  es);
1734  break;
1735  default:
1736  break;
1737  }
1738 
1739  /* quals, sort keys, etc */
1740  switch (nodeTag(plan))
1741  {
1742  case T_IndexScan:
1743  show_scan_qual(((IndexScan *) plan)->indexqualorig,
1744  "Index Cond", planstate, ancestors, es);
1745  if (((IndexScan *) plan)->indexqualorig)
1746  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1747  planstate, es);
1748  show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
1749  "Order By", planstate, ancestors, es);
1750  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1751  if (plan->qual)
1752  show_instrumentation_count("Rows Removed by Filter", 1,
1753  planstate, es);
1754  break;
1755  case T_IndexOnlyScan:
1756  show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
1757  "Index Cond", planstate, ancestors, es);
1758  if (((IndexOnlyScan *) plan)->recheckqual)
1759  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1760  planstate, es);
1761  show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
1762  "Order By", planstate, ancestors, es);
1763  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1764  if (plan->qual)
1765  show_instrumentation_count("Rows Removed by Filter", 1,
1766  planstate, es);
1767  if (es->analyze)
1768  ExplainPropertyFloat("Heap Fetches", NULL,
1769  planstate->instrument->ntuples2, 0, es);
1770  break;
1771  case T_BitmapIndexScan:
1772  show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
1773  "Index Cond", planstate, ancestors, es);
1774  break;
1775  case T_BitmapHeapScan:
1776  show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
1777  "Recheck Cond", planstate, ancestors, es);
1778  if (((BitmapHeapScan *) plan)->bitmapqualorig)
1779  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1780  planstate, es);
1781  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1782  if (plan->qual)
1783  show_instrumentation_count("Rows Removed by Filter", 1,
1784  planstate, es);
1785  if (es->analyze)
1786  show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
1787  break;
1788  case T_SampleScan:
1789  show_tablesample(((SampleScan *) plan)->tablesample,
1790  planstate, ancestors, es);
1791  /* fall through to print additional fields the same as SeqScan */
1792  /* FALLTHROUGH */
1793  case T_SeqScan:
1794  case T_ValuesScan:
1795  case T_CteScan:
1796  case T_NamedTuplestoreScan:
1797  case T_WorkTableScan:
1798  case T_SubqueryScan:
1799  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1800  if (plan->qual)
1801  show_instrumentation_count("Rows Removed by Filter", 1,
1802  planstate, es);
1803  break;
1804  case T_Gather:
1805  {
1806  Gather *gather = (Gather *) plan;
1807 
1808  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1809  if (plan->qual)
1810  show_instrumentation_count("Rows Removed by Filter", 1,
1811  planstate, es);
1812  ExplainPropertyInteger("Workers Planned", NULL,
1813  gather->num_workers, es);
1814 
1815  /* Show params evaluated at gather node */
1816  if (gather->initParam)
1817  show_eval_params(gather->initParam, es);
1818 
1819  if (es->analyze)
1820  {
1821  int nworkers;
1822 
1823  nworkers = ((GatherState *) planstate)->nworkers_launched;
1824  ExplainPropertyInteger("Workers Launched", NULL,
1825  nworkers, es);
1826  }
1827 
1828  if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
1829  ExplainPropertyBool("Single Copy", gather->single_copy, es);
1830  }
1831  break;
1832  case T_GatherMerge:
1833  {
1834  GatherMerge *gm = (GatherMerge *) plan;
1835 
1836  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1837  if (plan->qual)
1838  show_instrumentation_count("Rows Removed by Filter", 1,
1839  planstate, es);
1840  ExplainPropertyInteger("Workers Planned", NULL,
1841  gm->num_workers, es);
1842 
1843  /* Show params evaluated at gather-merge node */
1844  if (gm->initParam)
1845  show_eval_params(gm->initParam, es);
1846 
1847  if (es->analyze)
1848  {
1849  int nworkers;
1850 
1851  nworkers = ((GatherMergeState *) planstate)->nworkers_launched;
1852  ExplainPropertyInteger("Workers Launched", NULL,
1853  nworkers, es);
1854  }
1855  }
1856  break;
1857  case T_FunctionScan:
1858  if (es->verbose)
1859  {
1860  List *fexprs = NIL;
1861  ListCell *lc;
1862 
1863  foreach(lc, ((FunctionScan *) plan)->functions)
1864  {
1865  RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
1866 
1867  fexprs = lappend(fexprs, rtfunc->funcexpr);
1868  }
1869  /* We rely on show_expression to insert commas as needed */
1870  show_expression((Node *) fexprs,
1871  "Function Call", planstate, ancestors,
1872  es->verbose, es);
1873  }
1874  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1875  if (plan->qual)
1876  show_instrumentation_count("Rows Removed by Filter", 1,
1877  planstate, es);
1878  break;
1879  case T_TableFuncScan:
1880  if (es->verbose)
1881  {
1882  TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
1883 
1884  show_expression((Node *) tablefunc,
1885  "Table Function Call", planstate, ancestors,
1886  es->verbose, es);
1887  }
1888  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1889  if (plan->qual)
1890  show_instrumentation_count("Rows Removed by Filter", 1,
1891  planstate, es);
1892  break;
1893  case T_TidScan:
1894  {
1895  /*
1896  * The tidquals list has OR semantics, so be sure to show it
1897  * as an OR condition.
1898  */
1899  List *tidquals = ((TidScan *) plan)->tidquals;
1900 
1901  if (list_length(tidquals) > 1)
1902  tidquals = list_make1(make_orclause(tidquals));
1903  show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
1904  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1905  if (plan->qual)
1906  show_instrumentation_count("Rows Removed by Filter", 1,
1907  planstate, es);
1908  }
1909  break;
1910  case T_TidRangeScan:
1911  {
1912  /*
1913  * The tidrangequals list has AND semantics, so be sure to
1914  * show it as an AND condition.
1915  */
1916  List *tidquals = ((TidRangeScan *) plan)->tidrangequals;
1917 
1918  if (list_length(tidquals) > 1)
1919  tidquals = list_make1(make_andclause(tidquals));
1920  show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
1921  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1922  if (plan->qual)
1923  show_instrumentation_count("Rows Removed by Filter", 1,
1924  planstate, es);
1925  }
1926  break;
1927  case T_ForeignScan:
1928  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1929  if (plan->qual)
1930  show_instrumentation_count("Rows Removed by Filter", 1,
1931  planstate, es);
1932  show_foreignscan_info((ForeignScanState *) planstate, es);
1933  break;
1934  case T_CustomScan:
1935  {
1936  CustomScanState *css = (CustomScanState *) planstate;
1937 
1938  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1939  if (plan->qual)
1940  show_instrumentation_count("Rows Removed by Filter", 1,
1941  planstate, es);
1942  if (css->methods->ExplainCustomScan)
1943  css->methods->ExplainCustomScan(css, ancestors, es);
1944  }
1945  break;
1946  case T_NestLoop:
1947  show_upper_qual(((NestLoop *) plan)->join.joinqual,
1948  "Join Filter", planstate, ancestors, es);
1949  if (((NestLoop *) plan)->join.joinqual)
1950  show_instrumentation_count("Rows Removed by Join Filter", 1,
1951  planstate, es);
1952  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1953  if (plan->qual)
1954  show_instrumentation_count("Rows Removed by Filter", 2,
1955  planstate, es);
1956  break;
1957  case T_MergeJoin:
1958  show_upper_qual(((MergeJoin *) plan)->mergeclauses,
1959  "Merge Cond", planstate, ancestors, es);
1960  show_upper_qual(((MergeJoin *) plan)->join.joinqual,
1961  "Join Filter", planstate, ancestors, es);
1962  if (((MergeJoin *) plan)->join.joinqual)
1963  show_instrumentation_count("Rows Removed by Join Filter", 1,
1964  planstate, es);
1965  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1966  if (plan->qual)
1967  show_instrumentation_count("Rows Removed by Filter", 2,
1968  planstate, es);
1969  break;
1970  case T_HashJoin:
1971  show_upper_qual(((HashJoin *) plan)->hashclauses,
1972  "Hash Cond", planstate, ancestors, es);
1973  show_upper_qual(((HashJoin *) plan)->join.joinqual,
1974  "Join Filter", planstate, ancestors, es);
1975  if (((HashJoin *) plan)->join.joinqual)
1976  show_instrumentation_count("Rows Removed by Join Filter", 1,
1977  planstate, es);
1978  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1979  if (plan->qual)
1980  show_instrumentation_count("Rows Removed by Filter", 2,
1981  planstate, es);
1982  break;
1983  case T_Agg:
1984  show_agg_keys(castNode(AggState, planstate), ancestors, es);
1985  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1986  show_hashagg_info((AggState *) planstate, es);
1987  if (plan->qual)
1988  show_instrumentation_count("Rows Removed by Filter", 1,
1989  planstate, es);
1990  break;
1991  case T_WindowAgg:
1992  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
1993  if (plan->qual)
1994  show_instrumentation_count("Rows Removed by Filter", 1,
1995  planstate, es);
1996  show_upper_qual(((WindowAgg *) plan)->runConditionOrig,
1997  "Run Condition", planstate, ancestors, es);
1998  break;
1999  case T_Group:
2000  show_group_keys(castNode(GroupState, planstate), ancestors, es);
2001  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2002  if (plan->qual)
2003  show_instrumentation_count("Rows Removed by Filter", 1,
2004  planstate, es);
2005  break;
2006  case T_Sort:
2007  show_sort_keys(castNode(SortState, planstate), ancestors, es);
2008  show_sort_info(castNode(SortState, planstate), es);
2009  break;
2010  case T_IncrementalSort:
2012  ancestors, es);
2014  es);
2015  break;
2016  case T_MergeAppend:
2018  ancestors, es);
2019  break;
2020  case T_Result:
2021  show_upper_qual((List *) ((Result *) plan)->resconstantqual,
2022  "One-Time Filter", planstate, ancestors, es);
2023  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2024  if (plan->qual)
2025  show_instrumentation_count("Rows Removed by Filter", 1,
2026  planstate, es);
2027  break;
2028  case T_ModifyTable:
2029  show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
2030  es);
2031  break;
2032  case T_Hash:
2033  show_hash_info(castNode(HashState, planstate), es);
2034  break;
2035  case T_Memoize:
2036  show_memoize_info(castNode(MemoizeState, planstate), ancestors,
2037  es);
2038  break;
2039  default:
2040  break;
2041  }
2042 
2043  /*
2044  * Prepare per-worker JIT instrumentation. As with the overall JIT
2045  * summary, this is printed only if printing costs is enabled.
2046  */
2047  if (es->workers_state && es->costs && es->verbose)
2048  {
2050 
2051  if (w)
2052  {
2053  for (int n = 0; n < w->num_workers; n++)
2054  {
2055  ExplainOpenWorker(n, es);
2056  ExplainPrintJIT(es, planstate->state->es_jit_flags,
2057  &w->jit_instr[n]);
2058  ExplainCloseWorker(n, es);
2059  }
2060  }
2061  }
2062 
2063  /* Show buffer/WAL usage */
2064  if (es->buffers && planstate->instrument)
2065  show_buffer_usage(es, &planstate->instrument->bufusage, false);
2066  if (es->wal && planstate->instrument)
2067  show_wal_usage(es, &planstate->instrument->walusage);
2068 
2069  /* Prepare per-worker buffer/WAL usage */
2070  if (es->workers_state && (es->buffers || es->wal) && es->verbose)
2071  {
2072  WorkerInstrumentation *w = planstate->worker_instrument;
2073 
2074  for (int n = 0; n < w->num_workers; n++)
2075  {
2076  Instrumentation *instrument = &w->instrument[n];
2077  double nloops = instrument->nloops;
2078 
2079  if (nloops <= 0)
2080  continue;
2081 
2082  ExplainOpenWorker(n, es);
2083  if (es->buffers)
2084  show_buffer_usage(es, &instrument->bufusage, false);
2085  if (es->wal)
2086  show_wal_usage(es, &instrument->walusage);
2087  ExplainCloseWorker(n, es);
2088  }
2089  }
2090 
2091  /* Show per-worker details for this plan node, then pop that stack */
2092  if (es->workers_state)
2094  es->workers_state = save_workers_state;
2095 
2096  /*
2097  * If partition pruning was done during executor initialization, the
2098  * number of child plans we'll display below will be less than the number
2099  * of subplans that was specified in the plan. To make this a bit less
2100  * mysterious, emit an indication that this happened. Note that this
2101  * field is emitted now because we want it to be a property of the parent
2102  * node; it *cannot* be emitted within the Plans sub-node we'll open next.
2103  */
2104  switch (nodeTag(plan))
2105  {
2106  case T_Append:
2107  ExplainMissingMembers(((AppendState *) planstate)->as_nplans,
2108  list_length(((Append *) plan)->appendplans),
2109  es);
2110  break;
2111  case T_MergeAppend:
2112  ExplainMissingMembers(((MergeAppendState *) planstate)->ms_nplans,
2113  list_length(((MergeAppend *) plan)->mergeplans),
2114  es);
2115  break;
2116  default:
2117  break;
2118  }
2119 
2120  /* Get ready to display the child plans */
2121  haschildren = planstate->initPlan ||
2122  outerPlanState(planstate) ||
2123  innerPlanState(planstate) ||
2124  IsA(plan, Append) ||
2125  IsA(plan, MergeAppend) ||
2126  IsA(plan, BitmapAnd) ||
2127  IsA(plan, BitmapOr) ||
2128  IsA(plan, SubqueryScan) ||
2129  (IsA(planstate, CustomScanState) &&
2130  ((CustomScanState *) planstate)->custom_ps != NIL) ||
2131  planstate->subPlan;
2132  if (haschildren)
2133  {
2134  ExplainOpenGroup("Plans", "Plans", false, es);
2135  /* Pass current Plan as head of ancestors list for children */
2136  ancestors = lcons(plan, ancestors);
2137  }
2138 
2139  /* initPlan-s */
2140  if (planstate->initPlan)
2141  ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
2142 
2143  /* lefttree */
2144  if (outerPlanState(planstate))
2145  ExplainNode(outerPlanState(planstate), ancestors,
2146  "Outer", NULL, es);
2147 
2148  /* righttree */
2149  if (innerPlanState(planstate))
2150  ExplainNode(innerPlanState(planstate), ancestors,
2151  "Inner", NULL, es);
2152 
2153  /* special child plans */
2154  switch (nodeTag(plan))
2155  {
2156  case T_Append:
2157  ExplainMemberNodes(((AppendState *) planstate)->appendplans,
2158  ((AppendState *) planstate)->as_nplans,
2159  ancestors, es);
2160  break;
2161  case T_MergeAppend:
2162  ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
2163  ((MergeAppendState *) planstate)->ms_nplans,
2164  ancestors, es);
2165  break;
2166  case T_BitmapAnd:
2167  ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
2168  ((BitmapAndState *) planstate)->nplans,
2169  ancestors, es);
2170  break;
2171  case T_BitmapOr:
2172  ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
2173  ((BitmapOrState *) planstate)->nplans,
2174  ancestors, es);
2175  break;
2176  case T_SubqueryScan:
2177  ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
2178  "Subquery", NULL, es);
2179  break;
2180  case T_CustomScan:
2181  ExplainCustomChildren((CustomScanState *) planstate,
2182  ancestors, es);
2183  break;
2184  default:
2185  break;
2186  }
2187 
2188  /* subPlan-s */
2189  if (planstate->subPlan)
2190  ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
2191 
2192  /* end of child plans */
2193  if (haschildren)
2194  {
2195  ancestors = list_delete_first(ancestors);
2196  ExplainCloseGroup("Plans", "Plans", false, es);
2197  }
2198 
2199  /* in text format, undo whatever indentation we added */
2200  if (es->format == EXPLAIN_FORMAT_TEXT)
2201  es->indent = save_indent;
2202 
2203  ExplainCloseGroup("Plan",
2204  relationship ? NULL : "Plan",
2205  true, es);
2206 }
2207 
2208 /*
2209  * Show the targetlist of a plan node
2210  */
2211 static void
2212 show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
2213 {
2214  Plan *plan = planstate->plan;
2215  List *context;
2216  List *result = NIL;
2217  bool useprefix;
2218  ListCell *lc;
2219 
2220  /* No work if empty tlist (this occurs eg in bitmap indexscans) */
2221  if (plan->targetlist == NIL)
2222  return;
2223  /* The tlist of an Append isn't real helpful, so suppress it */
2224  if (IsA(plan, Append))
2225  return;
2226  /* Likewise for MergeAppend and RecursiveUnion */
2227  if (IsA(plan, MergeAppend))
2228  return;
2229  if (IsA(plan, RecursiveUnion))
2230  return;
2231 
2232  /*
2233  * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
2234  *
2235  * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
2236  * might contain subplan output expressions that are confusing in this
2237  * context. The tlist for a ForeignScan that executes a direct UPDATE/
2238  * DELETE always contains "junk" target columns to identify the exact row
2239  * to update or delete, which would be confusing in this context. So, we
2240  * suppress it in all the cases.
2241  */
2242  if (IsA(plan, ForeignScan) &&
2243  ((ForeignScan *) plan)->operation != CMD_SELECT)
2244  return;
2245 
2246  /* Set up deparsing context */
2248  plan,
2249  ancestors);
2250  useprefix = list_length(es->rtable) > 1;
2251 
2252  /* Deparse each result column (we now include resjunk ones) */
2253  foreach(lc, plan->targetlist)
2254  {
2255  TargetEntry *tle = (TargetEntry *) lfirst(lc);
2256 
2257  result = lappend(result,
2258  deparse_expression((Node *) tle->expr, context,
2259  useprefix, false));
2260  }
2261 
2262  /* Print results */
2263  ExplainPropertyList("Output", result, es);
2264 }
2265 
2266 /*
2267  * Show a generic expression
2268  */
2269 static void
2270 show_expression(Node *node, const char *qlabel,
2271  PlanState *planstate, List *ancestors,
2272  bool useprefix, ExplainState *es)
2273 {
2274  List *context;
2275  char *exprstr;
2276 
2277  /* Set up deparsing context */
2279  planstate->plan,
2280  ancestors);
2281 
2282  /* Deparse the expression */
2283  exprstr = deparse_expression(node, context, useprefix, false);
2284 
2285  /* And add to es->str */
2286  ExplainPropertyText(qlabel, exprstr, es);
2287 }
2288 
2289 /*
2290  * Show a qualifier expression (which is a List with implicit AND semantics)
2291  */
2292 static void
2293 show_qual(List *qual, const char *qlabel,
2294  PlanState *planstate, List *ancestors,
2295  bool useprefix, ExplainState *es)
2296 {
2297  Node *node;
2298 
2299  /* No work if empty qual */
2300  if (qual == NIL)
2301  return;
2302 
2303  /* Convert AND list to explicit AND */
2304  node = (Node *) make_ands_explicit(qual);
2305 
2306  /* And show it */
2307  show_expression(node, qlabel, planstate, ancestors, useprefix, es);
2308 }
2309 
2310 /*
2311  * Show a qualifier expression for a scan plan node
2312  */
2313 static void
2314 show_scan_qual(List *qual, const char *qlabel,
2315  PlanState *planstate, List *ancestors,
2316  ExplainState *es)
2317 {
2318  bool useprefix;
2319 
2320  useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
2321  show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2322 }
2323 
2324 /*
2325  * Show a qualifier expression for an upper-level plan node
2326  */
2327 static void
2328 show_upper_qual(List *qual, const char *qlabel,
2329  PlanState *planstate, List *ancestors,
2330  ExplainState *es)
2331 {
2332  bool useprefix;
2333 
2334  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2335  show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2336 }
2337 
2338 /*
2339  * Show the sort keys for a Sort node.
2340  */
2341 static void
2342 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
2343 {
2344  Sort *plan = (Sort *) sortstate->ss.ps.plan;
2345 
2346  show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2347  plan->numCols, 0, plan->sortColIdx,
2348  plan->sortOperators, plan->collations,
2349  plan->nullsFirst,
2350  ancestors, es);
2351 }
2352 
2353 /*
2354  * Show the sort keys for a IncrementalSort node.
2355  */
2356 static void
2358  List *ancestors, ExplainState *es)
2359 {
2360  IncrementalSort *plan = (IncrementalSort *) incrsortstate->ss.ps.plan;
2361 
2362  show_sort_group_keys((PlanState *) incrsortstate, "Sort Key",
2363  plan->sort.numCols, plan->nPresortedCols,
2364  plan->sort.sortColIdx,
2365  plan->sort.sortOperators, plan->sort.collations,
2366  plan->sort.nullsFirst,
2367  ancestors, es);
2368 }
2369 
2370 /*
2371  * Likewise, for a MergeAppend node.
2372  */
2373 static void
2375  ExplainState *es)
2376 {
2377  MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
2378 
2379  show_sort_group_keys((PlanState *) mstate, "Sort Key",
2380  plan->numCols, 0, plan->sortColIdx,
2381  plan->sortOperators, plan->collations,
2382  plan->nullsFirst,
2383  ancestors, es);
2384 }
2385 
2386 /*
2387  * Show the grouping keys for an Agg node.
2388  */
2389 static void
2390 show_agg_keys(AggState *astate, List *ancestors,
2391  ExplainState *es)
2392 {
2393  Agg *plan = (Agg *) astate->ss.ps.plan;
2394 
2395  if (plan->numCols > 0 || plan->groupingSets)
2396  {
2397  /* The key columns refer to the tlist of the child plan */
2398  ancestors = lcons(plan, ancestors);
2399 
2400  if (plan->groupingSets)
2401  show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2402  else
2403  show_sort_group_keys(outerPlanState(astate), "Group Key",
2404  plan->numCols, 0, plan->grpColIdx,
2405  NULL, NULL, NULL,
2406  ancestors, es);
2407 
2408  ancestors = list_delete_first(ancestors);
2409  }
2410 }
2411 
2412 static void
2414  List *ancestors, ExplainState *es)
2415 {
2416  List *context;
2417  bool useprefix;
2418  ListCell *lc;
2419 
2420  /* Set up deparsing context */
2422  planstate->plan,
2423  ancestors);
2424  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2425 
2426  ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
2427 
2428  show_grouping_set_keys(planstate, agg, NULL,
2429  context, useprefix, ancestors, es);
2430 
2431  foreach(lc, agg->chain)
2432  {
2433  Agg *aggnode = lfirst(lc);
2434  Sort *sortnode = (Sort *) aggnode->plan.lefttree;
2435 
2436  show_grouping_set_keys(planstate, aggnode, sortnode,
2437  context, useprefix, ancestors, es);
2438  }
2439 
2440  ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
2441 }
2442 
2443 static void
2445  Agg *aggnode, Sort *sortnode,
2446  List *context, bool useprefix,
2447  List *ancestors, ExplainState *es)
2448 {
2449  Plan *plan = planstate->plan;
2450  char *exprstr;
2451  ListCell *lc;
2452  List *gsets = aggnode->groupingSets;
2453  AttrNumber *keycols = aggnode->grpColIdx;
2454  const char *keyname;
2455  const char *keysetname;
2456 
2457  if (aggnode->aggstrategy == AGG_HASHED || aggnode->aggstrategy == AGG_MIXED)
2458  {
2459  keyname = "Hash Key";
2460  keysetname = "Hash Keys";
2461  }
2462  else
2463  {
2464  keyname = "Group Key";
2465  keysetname = "Group Keys";
2466  }
2467 
2468  ExplainOpenGroup("Grouping Set", NULL, true, es);
2469 
2470  if (sortnode)
2471  {
2472  show_sort_group_keys(planstate, "Sort Key",
2473  sortnode->numCols, 0, sortnode->sortColIdx,
2474  sortnode->sortOperators, sortnode->collations,
2475  sortnode->nullsFirst,
2476  ancestors, es);
2477  if (es->format == EXPLAIN_FORMAT_TEXT)
2478  es->indent++;
2479  }
2480 
2481  ExplainOpenGroup(keysetname, keysetname, false, es);
2482 
2483  foreach(lc, gsets)
2484  {
2485  List *result = NIL;
2486  ListCell *lc2;
2487 
2488  foreach(lc2, (List *) lfirst(lc))
2489  {
2490  Index i = lfirst_int(lc2);
2491  AttrNumber keyresno = keycols[i];
2492  TargetEntry *target = get_tle_by_resno(plan->targetlist,
2493  keyresno);
2494 
2495  if (!target)
2496  elog(ERROR, "no tlist entry for key %d", keyresno);
2497  /* Deparse the expression, showing any top-level cast */
2498  exprstr = deparse_expression((Node *) target->expr, context,
2499  useprefix, true);
2500 
2501  result = lappend(result, exprstr);
2502  }
2503 
2504  if (!result && es->format == EXPLAIN_FORMAT_TEXT)
2505  ExplainPropertyText(keyname, "()", es);
2506  else
2507  ExplainPropertyListNested(keyname, result, es);
2508  }
2509 
2510  ExplainCloseGroup(keysetname, keysetname, false, es);
2511 
2512  if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
2513  es->indent--;
2514 
2515  ExplainCloseGroup("Grouping Set", NULL, true, es);
2516 }
2517 
2518 /*
2519  * Show the grouping keys for a Group node.
2520  */
2521 static void
2522 show_group_keys(GroupState *gstate, List *ancestors,
2523  ExplainState *es)
2524 {
2525  Group *plan = (Group *) gstate->ss.ps.plan;
2526 
2527  /* The key columns refer to the tlist of the child plan */
2528  ancestors = lcons(plan, ancestors);
2529  show_sort_group_keys(outerPlanState(gstate), "Group Key",
2530  plan->numCols, 0, plan->grpColIdx,
2531  NULL, NULL, NULL,
2532  ancestors, es);
2533  ancestors = list_delete_first(ancestors);
2534 }
2535 
2536 /*
2537  * Common code to show sort/group keys, which are represented in plan nodes
2538  * as arrays of targetlist indexes. If it's a sort key rather than a group
2539  * key, also pass sort operators/collations/nullsFirst arrays.
2540  */
2541 static void
2542 show_sort_group_keys(PlanState *planstate, const char *qlabel,
2543  int nkeys, int nPresortedKeys, AttrNumber *keycols,
2544  Oid *sortOperators, Oid *collations, bool *nullsFirst,
2545  List *ancestors, ExplainState *es)
2546 {
2547  Plan *plan = planstate->plan;
2548  List *context;
2549  List *result = NIL;
2550  List *resultPresorted = NIL;
2551  StringInfoData sortkeybuf;
2552  bool useprefix;
2553  int keyno;
2554 
2555  if (nkeys <= 0)
2556  return;
2557 
2558  initStringInfo(&sortkeybuf);
2559 
2560  /* Set up deparsing context */
2562  plan,
2563  ancestors);
2564  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2565 
2566  for (keyno = 0; keyno < nkeys; keyno++)
2567  {
2568  /* find key expression in tlist */
2569  AttrNumber keyresno = keycols[keyno];
2570  TargetEntry *target = get_tle_by_resno(plan->targetlist,
2571  keyresno);
2572  char *exprstr;
2573 
2574  if (!target)
2575  elog(ERROR, "no tlist entry for key %d", keyresno);
2576  /* Deparse the expression, showing any top-level cast */
2577  exprstr = deparse_expression((Node *) target->expr, context,
2578  useprefix, true);
2579  resetStringInfo(&sortkeybuf);
2580  appendStringInfoString(&sortkeybuf, exprstr);
2581  /* Append sort order information, if relevant */
2582  if (sortOperators != NULL)
2583  show_sortorder_options(&sortkeybuf,
2584  (Node *) target->expr,
2585  sortOperators[keyno],
2586  collations[keyno],
2587  nullsFirst[keyno]);
2588  /* Emit one property-list item per sort key */
2589  result = lappend(result, pstrdup(sortkeybuf.data));
2590  if (keyno < nPresortedKeys)
2591  resultPresorted = lappend(resultPresorted, exprstr);
2592  }
2593 
2594  ExplainPropertyList(qlabel, result, es);
2595  if (nPresortedKeys > 0)
2596  ExplainPropertyList("Presorted Key", resultPresorted, es);
2597 }
2598 
2599 /*
2600  * Append nondefault characteristics of the sort ordering of a column to buf
2601  * (collation, direction, NULLS FIRST/LAST)
2602  */
2603 static void
2605  Oid sortOperator, Oid collation, bool nullsFirst)
2606 {
2607  Oid sortcoltype = exprType(sortexpr);
2608  bool reverse = false;
2609  TypeCacheEntry *typentry;
2610 
2611  typentry = lookup_type_cache(sortcoltype,
2613 
2614  /*
2615  * Print COLLATE if it's not default for the column's type. There are
2616  * some cases where this is redundant, eg if expression is a column whose
2617  * declared collation is that collation, but it's hard to distinguish that
2618  * here (and arguably, printing COLLATE explicitly is a good idea anyway
2619  * in such cases).
2620  */
2621  if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
2622  {
2623  char *collname = get_collation_name(collation);
2624 
2625  if (collname == NULL)
2626  elog(ERROR, "cache lookup failed for collation %u", collation);
2627  appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2628  }
2629 
2630  /* Print direction if not ASC, or USING if non-default sort operator */
2631  if (sortOperator == typentry->gt_opr)
2632  {
2633  appendStringInfoString(buf, " DESC");
2634  reverse = true;
2635  }
2636  else if (sortOperator != typentry->lt_opr)
2637  {
2638  char *opname = get_opname(sortOperator);
2639 
2640  if (opname == NULL)
2641  elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2642  appendStringInfo(buf, " USING %s", opname);
2643  /* Determine whether operator would be considered ASC or DESC */
2644  (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2645  }
2646 
2647  /* Add NULLS FIRST/LAST only if it wouldn't be default */
2648  if (nullsFirst && !reverse)
2649  {
2650  appendStringInfoString(buf, " NULLS FIRST");
2651  }
2652  else if (!nullsFirst && reverse)
2653  {
2654  appendStringInfoString(buf, " NULLS LAST");
2655  }
2656 }
2657 
2658 /*
2659  * Show TABLESAMPLE properties
2660  */
2661 static void
2663  List *ancestors, ExplainState *es)
2664 {
2665  List *context;
2666  bool useprefix;
2667  char *method_name;
2668  List *params = NIL;
2669  char *repeatable;
2670  ListCell *lc;
2671 
2672  /* Set up deparsing context */
2674  planstate->plan,
2675  ancestors);
2676  useprefix = list_length(es->rtable) > 1;
2677 
2678  /* Get the tablesample method name */
2679  method_name = get_func_name(tsc->tsmhandler);
2680 
2681  /* Deparse parameter expressions */
2682  foreach(lc, tsc->args)
2683  {
2684  Node *arg = (Node *) lfirst(lc);
2685 
2686  params = lappend(params,
2688  useprefix, false));
2689  }
2690  if (tsc->repeatable)
2691  repeatable = deparse_expression((Node *) tsc->repeatable, context,
2692  useprefix, false);
2693  else
2694  repeatable = NULL;
2695 
2696  /* Print results */
2697  if (es->format == EXPLAIN_FORMAT_TEXT)
2698  {
2699  bool first = true;
2700 
2701  ExplainIndentText(es);
2702  appendStringInfo(es->str, "Sampling: %s (", method_name);
2703  foreach(lc, params)
2704  {
2705  if (!first)
2706  appendStringInfoString(es->str, ", ");
2707  appendStringInfoString(es->str, (const char *) lfirst(lc));
2708  first = false;
2709  }
2710  appendStringInfoChar(es->str, ')');
2711  if (repeatable)
2712  appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
2713  appendStringInfoChar(es->str, '\n');
2714  }
2715  else
2716  {
2717  ExplainPropertyText("Sampling Method", method_name, es);
2718  ExplainPropertyList("Sampling Parameters", params, es);
2719  if (repeatable)
2720  ExplainPropertyText("Repeatable Seed", repeatable, es);
2721  }
2722 }
2723 
2724 /*
2725  * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
2726  */
2727 static void
2729 {
2730  if (!es->analyze)
2731  return;
2732 
2733  if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
2734  {
2737  const char *sortMethod;
2738  const char *spaceType;
2739  int64 spaceUsed;
2740 
2741  tuplesort_get_stats(state, &stats);
2742  sortMethod = tuplesort_method_name(stats.sortMethod);
2743  spaceType = tuplesort_space_type_name(stats.spaceType);
2744  spaceUsed = stats.spaceUsed;
2745 
2746  if (es->format == EXPLAIN_FORMAT_TEXT)
2747  {
2748  ExplainIndentText(es);
2749  appendStringInfo(es->str, "Sort Method: %s %s: " INT64_FORMAT "kB\n",
2750  sortMethod, spaceType, spaceUsed);
2751  }
2752  else
2753  {
2754  ExplainPropertyText("Sort Method", sortMethod, es);
2755  ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
2756  ExplainPropertyText("Sort Space Type", spaceType, es);
2757  }
2758  }
2759 
2760  /*
2761  * You might think we should just skip this stanza entirely when
2762  * es->hide_workers is true, but then we'd get no sort-method output at
2763  * all. We have to make it look like worker 0's data is top-level data.
2764  * This is easily done by just skipping the OpenWorker/CloseWorker calls.
2765  * Currently, we don't worry about the possibility that there are multiple
2766  * workers in such a case; if there are, duplicate output fields will be
2767  * emitted.
2768  */
2769  if (sortstate->shared_info != NULL)
2770  {
2771  int n;
2772 
2773  for (n = 0; n < sortstate->shared_info->num_workers; n++)
2774  {
2775  TuplesortInstrumentation *sinstrument;
2776  const char *sortMethod;
2777  const char *spaceType;
2778  int64 spaceUsed;
2779 
2780  sinstrument = &sortstate->shared_info->sinstrument[n];
2781  if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
2782  continue; /* ignore any unfilled slots */
2783  sortMethod = tuplesort_method_name(sinstrument->sortMethod);
2784  spaceType = tuplesort_space_type_name(sinstrument->spaceType);
2785  spaceUsed = sinstrument->spaceUsed;
2786 
2787  if (es->workers_state)
2788  ExplainOpenWorker(n, es);
2789 
2790  if (es->format == EXPLAIN_FORMAT_TEXT)
2791  {
2792  ExplainIndentText(es);
2793  appendStringInfo(es->str,
2794  "Sort Method: %s %s: " INT64_FORMAT "kB\n",
2795  sortMethod, spaceType, spaceUsed);
2796  }
2797  else
2798  {
2799  ExplainPropertyText("Sort Method", sortMethod, es);
2800  ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
2801  ExplainPropertyText("Sort Space Type", spaceType, es);
2802  }
2803 
2804  if (es->workers_state)
2805  ExplainCloseWorker(n, es);
2806  }
2807  }
2808 }
2809 
2810 /*
2811  * Incremental sort nodes sort in (a potentially very large number of) batches,
2812  * so EXPLAIN ANALYZE needs to roll up the tuplesort stats from each batch into
2813  * an intelligible summary.
2814  *
2815  * This function is used for both a non-parallel node and each worker in a
2816  * parallel incremental sort node.
2817  */
2818 static void
2820  const char *groupLabel, bool indent, ExplainState *es)
2821 {
2822  ListCell *methodCell;
2823  List *methodNames = NIL;
2824 
2825  /* Generate a list of sort methods used across all groups. */
2826  for (int bit = 0; bit < NUM_TUPLESORTMETHODS; bit++)
2827  {
2828  TuplesortMethod sortMethod = (1 << bit);
2829 
2830  if (groupInfo->sortMethods & sortMethod)
2831  {
2832  const char *methodName = tuplesort_method_name(sortMethod);
2833 
2834  methodNames = lappend(methodNames, unconstify(char *, methodName));
2835  }
2836  }
2837 
2838  if (es->format == EXPLAIN_FORMAT_TEXT)
2839  {
2840  if (indent)
2841  appendStringInfoSpaces(es->str, es->indent * 2);
2842  appendStringInfo(es->str, "%s Groups: " INT64_FORMAT " Sort Method", groupLabel,
2843  groupInfo->groupCount);
2844  /* plural/singular based on methodNames size */
2845  if (list_length(methodNames) > 1)
2846  appendStringInfoString(es->str, "s: ");
2847  else
2848  appendStringInfoString(es->str, ": ");
2849  foreach(methodCell, methodNames)
2850  {
2851  appendStringInfoString(es->str, (char *) methodCell->ptr_value);
2852  if (foreach_current_index(methodCell) < list_length(methodNames) - 1)
2853  appendStringInfoString(es->str, ", ");
2854  }
2855 
2856  if (groupInfo->maxMemorySpaceUsed > 0)
2857  {
2858  int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
2859  const char *spaceTypeName;
2860 
2862  appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
2863  spaceTypeName, avgSpace,
2864  spaceTypeName, groupInfo->maxMemorySpaceUsed);
2865  }
2866 
2867  if (groupInfo->maxDiskSpaceUsed > 0)
2868  {
2869  int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
2870 
2871  const char *spaceTypeName;
2872 
2874  appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
2875  spaceTypeName, avgSpace,
2876  spaceTypeName, groupInfo->maxDiskSpaceUsed);
2877  }
2878  }
2879  else
2880  {
2881  StringInfoData groupName;
2882 
2883  initStringInfo(&groupName);
2884  appendStringInfo(&groupName, "%s Groups", groupLabel);
2885  ExplainOpenGroup("Incremental Sort Groups", groupName.data, true, es);
2886  ExplainPropertyInteger("Group Count", NULL, groupInfo->groupCount, es);
2887 
2888  ExplainPropertyList("Sort Methods Used", methodNames, es);
2889 
2890  if (groupInfo->maxMemorySpaceUsed > 0)
2891  {
2892  int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
2893  const char *spaceTypeName;
2894  StringInfoData memoryName;
2895 
2897  initStringInfo(&memoryName);
2898  appendStringInfo(&memoryName, "Sort Space %s", spaceTypeName);
2899  ExplainOpenGroup("Sort Space", memoryName.data, true, es);
2900 
2901  ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
2902  ExplainPropertyInteger("Peak Sort Space Used", "kB",
2903  groupInfo->maxMemorySpaceUsed, es);
2904 
2905  ExplainCloseGroup("Sort Space", memoryName.data, true, es);
2906  }
2907  if (groupInfo->maxDiskSpaceUsed > 0)
2908  {
2909  int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
2910  const char *spaceTypeName;
2911  StringInfoData diskName;
2912 
2914  initStringInfo(&diskName);
2915  appendStringInfo(&diskName, "Sort Space %s", spaceTypeName);
2916  ExplainOpenGroup("Sort Space", diskName.data, true, es);
2917 
2918  ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
2919  ExplainPropertyInteger("Peak Sort Space Used", "kB",
2920  groupInfo->maxDiskSpaceUsed, es);
2921 
2922  ExplainCloseGroup("Sort Space", diskName.data, true, es);
2923  }
2924 
2925  ExplainCloseGroup("Incremental Sort Groups", groupName.data, true, es);
2926  }
2927 }
2928 
2929 /*
2930  * If it's EXPLAIN ANALYZE, show tuplesort stats for an incremental sort node
2931  */
2932 static void
2934  ExplainState *es)
2935 {
2936  IncrementalSortGroupInfo *fullsortGroupInfo;
2937  IncrementalSortGroupInfo *prefixsortGroupInfo;
2938 
2939  fullsortGroupInfo = &incrsortstate->incsort_info.fullsortGroupInfo;
2940 
2941  if (!es->analyze)
2942  return;
2943 
2944  /*
2945  * Since we never have any prefix groups unless we've first sorted a full
2946  * groups and transitioned modes (copying the tuples into a prefix group),
2947  * we don't need to do anything if there were 0 full groups.
2948  *
2949  * We still have to continue after this block if there are no full groups,
2950  * though, since it's possible that we have workers that did real work
2951  * even if the leader didn't participate.
2952  */
2953  if (fullsortGroupInfo->groupCount > 0)
2954  {
2955  show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort", true, es);
2956  prefixsortGroupInfo = &incrsortstate->incsort_info.prefixsortGroupInfo;
2957  if (prefixsortGroupInfo->groupCount > 0)
2958  {
2959  if (es->format == EXPLAIN_FORMAT_TEXT)
2960  appendStringInfoChar(es->str, '\n');
2961  show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
2962  }
2963  if (es->format == EXPLAIN_FORMAT_TEXT)
2964  appendStringInfoChar(es->str, '\n');
2965  }
2966 
2967  if (incrsortstate->shared_info != NULL)
2968  {
2969  int n;
2970  bool indent_first_line;
2971 
2972  for (n = 0; n < incrsortstate->shared_info->num_workers; n++)
2973  {
2974  IncrementalSortInfo *incsort_info =
2975  &incrsortstate->shared_info->sinfo[n];
2976 
2977  /*
2978  * If a worker hasn't processed any sort groups at all, then
2979  * exclude it from output since it either didn't launch or didn't
2980  * contribute anything meaningful.
2981  */
2982  fullsortGroupInfo = &incsort_info->fullsortGroupInfo;
2983 
2984  /*
2985  * Since we never have any prefix groups unless we've first sorted
2986  * a full groups and transitioned modes (copying the tuples into a
2987  * prefix group), we don't need to do anything if there were 0
2988  * full groups.
2989  */
2990  if (fullsortGroupInfo->groupCount == 0)
2991  continue;
2992 
2993  if (es->workers_state)
2994  ExplainOpenWorker(n, es);
2995 
2996  indent_first_line = es->workers_state == NULL || es->verbose;
2997  show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort",
2998  indent_first_line, es);
2999  prefixsortGroupInfo = &incsort_info->prefixsortGroupInfo;
3000  if (prefixsortGroupInfo->groupCount > 0)
3001  {
3002  if (es->format == EXPLAIN_FORMAT_TEXT)
3003  appendStringInfoChar(es->str, '\n');
3004  show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
3005  }
3006  if (es->format == EXPLAIN_FORMAT_TEXT)
3007  appendStringInfoChar(es->str, '\n');
3008 
3009  if (es->workers_state)
3010  ExplainCloseWorker(n, es);
3011  }
3012  }
3013 }
3014 
3015 /*
3016  * Show information on hash buckets/batches.
3017  */
3018 static void
3020 {
3021  HashInstrumentation hinstrument = {0};
3022 
3023  /*
3024  * Collect stats from the local process, even when it's a parallel query.
3025  * In a parallel query, the leader process may or may not have run the
3026  * hash join, and even if it did it may not have built a hash table due to
3027  * timing (if it started late it might have seen no tuples in the outer
3028  * relation and skipped building the hash table). Therefore we have to be
3029  * prepared to get instrumentation data from all participants.
3030  */
3031  if (hashstate->hinstrument)
3032  memcpy(&hinstrument, hashstate->hinstrument,
3033  sizeof(HashInstrumentation));
3034 
3035  /*
3036  * Merge results from workers. In the parallel-oblivious case, the
3037  * results from all participants should be identical, except where
3038  * participants didn't run the join at all so have no data. In the
3039  * parallel-aware case, we need to consider all the results. Each worker
3040  * may have seen a different subset of batches and we want to report the
3041  * highest memory usage across all batches. We take the maxima of other
3042  * values too, for the same reasons as in ExecHashAccumInstrumentation.
3043  */
3044  if (hashstate->shared_info)
3045  {
3046  SharedHashInfo *shared_info = hashstate->shared_info;
3047  int i;
3048 
3049  for (i = 0; i < shared_info->num_workers; ++i)
3050  {
3051  HashInstrumentation *worker_hi = &shared_info->hinstrument[i];
3052 
3053  hinstrument.nbuckets = Max(hinstrument.nbuckets,
3054  worker_hi->nbuckets);
3055  hinstrument.nbuckets_original = Max(hinstrument.nbuckets_original,
3056  worker_hi->nbuckets_original);
3057  hinstrument.nbatch = Max(hinstrument.nbatch,
3058  worker_hi->nbatch);
3059  hinstrument.nbatch_original = Max(hinstrument.nbatch_original,
3060  worker_hi->nbatch_original);
3061  hinstrument.space_peak = Max(hinstrument.space_peak,
3062  worker_hi->space_peak);
3063  }
3064  }
3065 
3066  if (hinstrument.nbatch > 0)
3067  {
3068  long spacePeakKb = (hinstrument.space_peak + 1023) / 1024;
3069 
3070  if (es->format != EXPLAIN_FORMAT_TEXT)
3071  {
3072  ExplainPropertyInteger("Hash Buckets", NULL,
3073  hinstrument.nbuckets, es);
3074  ExplainPropertyInteger("Original Hash Buckets", NULL,
3075  hinstrument.nbuckets_original, es);
3076  ExplainPropertyInteger("Hash Batches", NULL,
3077  hinstrument.nbatch, es);
3078  ExplainPropertyInteger("Original Hash Batches", NULL,
3079  hinstrument.nbatch_original, es);
3080  ExplainPropertyInteger("Peak Memory Usage", "kB",
3081  spacePeakKb, es);
3082  }
3083  else if (hinstrument.nbatch_original != hinstrument.nbatch ||
3084  hinstrument.nbuckets_original != hinstrument.nbuckets)
3085  {
3086  ExplainIndentText(es);
3087  appendStringInfo(es->str,
3088  "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: %ldkB\n",
3089  hinstrument.nbuckets,
3090  hinstrument.nbuckets_original,
3091  hinstrument.nbatch,
3092  hinstrument.nbatch_original,
3093  spacePeakKb);
3094  }
3095  else
3096  {
3097  ExplainIndentText(es);
3098  appendStringInfo(es->str,
3099  "Buckets: %d Batches: %d Memory Usage: %ldkB\n",
3100  hinstrument.nbuckets, hinstrument.nbatch,
3101  spacePeakKb);
3102  }
3103  }
3104 }
3105 
3106 /*
3107  * Show information on memoize hits/misses/evictions and memory usage.
3108  */
3109 static void
3111 {
3112  Plan *plan = ((PlanState *) mstate)->plan;
3113  ListCell *lc;
3114  List *context;
3115  StringInfoData keystr;
3116  char *separator = "";
3117  bool useprefix;
3118  int64 memPeakKb;
3119 
3120  initStringInfo(&keystr);
3121 
3122  /*
3123  * It's hard to imagine having a memoize node with fewer than 2 RTEs, but
3124  * let's just keep the same useprefix logic as elsewhere in this file.
3125  */
3126  useprefix = list_length(es->rtable) > 1 || es->verbose;
3127 
3128  /* Set up deparsing context */
3130  plan,
3131  ancestors);
3132 
3133  foreach(lc, ((Memoize *) plan)->param_exprs)
3134  {
3135  Node *expr = (Node *) lfirst(lc);
3136 
3138 
3140  useprefix, false));
3141  separator = ", ";
3142  }
3143 
3144  if (es->format != EXPLAIN_FORMAT_TEXT)
3145  {
3146  ExplainPropertyText("Cache Key", keystr.data, es);
3147  ExplainPropertyText("Cache Mode", mstate->binary_mode ? "binary" : "logical", es);
3148  }
3149  else
3150  {
3151  ExplainIndentText(es);
3152  appendStringInfo(es->str, "Cache Key: %s\n", keystr.data);
3153  ExplainIndentText(es);
3154  appendStringInfo(es->str, "Cache Mode: %s\n", mstate->binary_mode ? "binary" : "logical");
3155  }
3156 
3157  pfree(keystr.data);
3158 
3159  if (!es->analyze)
3160  return;
3161 
3162  if (mstate->stats.cache_misses > 0)
3163  {
3164  /*
3165  * mem_peak is only set when we freed memory, so we must use mem_used
3166  * when mem_peak is 0.
3167  */
3168  if (mstate->stats.mem_peak > 0)
3169  memPeakKb = (mstate->stats.mem_peak + 1023) / 1024;
3170  else
3171  memPeakKb = (mstate->mem_used + 1023) / 1024;
3172 
3173  if (es->format != EXPLAIN_FORMAT_TEXT)
3174  {
3175  ExplainPropertyInteger("Cache Hits", NULL, mstate->stats.cache_hits, es);
3176  ExplainPropertyInteger("Cache Misses", NULL, mstate->stats.cache_misses, es);
3177  ExplainPropertyInteger("Cache Evictions", NULL, mstate->stats.cache_evictions, es);
3178  ExplainPropertyInteger("Cache Overflows", NULL, mstate->stats.cache_overflows, es);
3179  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3180  }
3181  else
3182  {
3183  ExplainIndentText(es);
3184  appendStringInfo(es->str,
3185  "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3186  mstate->stats.cache_hits,
3187  mstate->stats.cache_misses,
3188  mstate->stats.cache_evictions,
3189  mstate->stats.cache_overflows,
3190  memPeakKb);
3191  }
3192  }
3193 
3194  if (mstate->shared_info == NULL)
3195  return;
3196 
3197  /* Show details from parallel workers */
3198  for (int n = 0; n < mstate->shared_info->num_workers; n++)
3199  {
3201 
3202  si = &mstate->shared_info->sinstrument[n];
3203 
3204  /*
3205  * Skip workers that didn't do any work. We needn't bother checking
3206  * for cache hits as a miss will always occur before a cache hit.
3207  */
3208  if (si->cache_misses == 0)
3209  continue;
3210 
3211  if (es->workers_state)
3212  ExplainOpenWorker(n, es);
3213 
3214  /*
3215  * Since the worker's MemoizeState.mem_used field is unavailable to
3216  * us, ExecEndMemoize will have set the
3217  * MemoizeInstrumentation.mem_peak field for us. No need to do the
3218  * zero checks like we did for the serial case above.
3219  */
3220  memPeakKb = (si->mem_peak + 1023) / 1024;
3221 
3222  if (es->format == EXPLAIN_FORMAT_TEXT)
3223  {
3224  ExplainIndentText(es);
3225  appendStringInfo(es->str,
3226  "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3227  si->cache_hits, si->cache_misses,
3229  memPeakKb);
3230  }
3231  else
3232  {
3233  ExplainPropertyInteger("Cache Hits", NULL,
3234  si->cache_hits, es);
3235  ExplainPropertyInteger("Cache Misses", NULL,
3236  si->cache_misses, es);
3237  ExplainPropertyInteger("Cache Evictions", NULL,
3238  si->cache_evictions, es);
3239  ExplainPropertyInteger("Cache Overflows", NULL,
3240  si->cache_overflows, es);
3241  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3242  es);
3243  }
3244 
3245  if (es->workers_state)
3246  ExplainCloseWorker(n, es);
3247  }
3248 }
3249 
3250 /*
3251  * Show information on hash aggregate memory usage and batches.
3252  */
3253 static void
3255 {
3256  Agg *agg = (Agg *) aggstate->ss.ps.plan;
3257  int64 memPeakKb = (aggstate->hash_mem_peak + 1023) / 1024;
3258 
3259  if (agg->aggstrategy != AGG_HASHED &&
3260  agg->aggstrategy != AGG_MIXED)
3261  return;
3262 
3263  if (es->format != EXPLAIN_FORMAT_TEXT)
3264  {
3265 
3266  if (es->costs)
3267  ExplainPropertyInteger("Planned Partitions", NULL,
3268  aggstate->hash_planned_partitions, es);
3269 
3270  /*
3271  * During parallel query the leader may have not helped out. We
3272  * detect this by checking how much memory it used. If we find it
3273  * didn't do any work then we don't show its properties.
3274  */
3275  if (es->analyze && aggstate->hash_mem_peak > 0)
3276  {
3277  ExplainPropertyInteger("HashAgg Batches", NULL,
3278  aggstate->hash_batches_used, es);
3279  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3280  ExplainPropertyInteger("Disk Usage", "kB",
3281  aggstate->hash_disk_used, es);
3282  }
3283  }
3284  else
3285  {
3286  bool gotone = false;
3287 
3288  if (es->costs && aggstate->hash_planned_partitions > 0)
3289  {
3290  ExplainIndentText(es);
3291  appendStringInfo(es->str, "Planned Partitions: %d",
3292  aggstate->hash_planned_partitions);
3293  gotone = true;
3294  }
3295 
3296  /*
3297  * During parallel query the leader may have not helped out. We
3298  * detect this by checking how much memory it used. If we find it
3299  * didn't do any work then we don't show its properties.
3300  */
3301  if (es->analyze && aggstate->hash_mem_peak > 0)
3302  {
3303  if (!gotone)
3304  ExplainIndentText(es);
3305  else
3306  appendStringInfoString(es->str, " ");
3307 
3308  appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3309  aggstate->hash_batches_used, memPeakKb);
3310  gotone = true;
3311 
3312  /* Only display disk usage if we spilled to disk */
3313  if (aggstate->hash_batches_used > 1)
3314  {
3315  appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3316  aggstate->hash_disk_used);
3317  }
3318  }
3319 
3320  if (gotone)
3321  appendStringInfoChar(es->str, '\n');
3322  }
3323 
3324  /* Display stats for each parallel worker */
3325  if (es->analyze && aggstate->shared_info != NULL)
3326  {
3327  for (int n = 0; n < aggstate->shared_info->num_workers; n++)
3328  {
3329  AggregateInstrumentation *sinstrument;
3330  uint64 hash_disk_used;
3331  int hash_batches_used;
3332 
3333  sinstrument = &aggstate->shared_info->sinstrument[n];
3334  /* Skip workers that didn't do anything */
3335  if (sinstrument->hash_mem_peak == 0)
3336  continue;
3337  hash_disk_used = sinstrument->hash_disk_used;
3338  hash_batches_used = sinstrument->hash_batches_used;
3339  memPeakKb = (sinstrument->hash_mem_peak + 1023) / 1024;
3340 
3341  if (es->workers_state)
3342  ExplainOpenWorker(n, es);
3343 
3344  if (es->format == EXPLAIN_FORMAT_TEXT)
3345  {
3346  ExplainIndentText(es);
3347 
3348  appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3349  hash_batches_used, memPeakKb);
3350 
3351  /* Only display disk usage if we spilled to disk */
3352  if (hash_batches_used > 1)
3353  appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3354  hash_disk_used);
3355  appendStringInfoChar(es->str, '\n');
3356  }
3357  else
3358  {
3359  ExplainPropertyInteger("HashAgg Batches", NULL,
3360  hash_batches_used, es);
3361  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3362  es);
3363  ExplainPropertyInteger("Disk Usage", "kB", hash_disk_used, es);
3364  }
3365 
3366  if (es->workers_state)
3367  ExplainCloseWorker(n, es);
3368  }
3369  }
3370 }
3371 
3372 /*
3373  * If it's EXPLAIN ANALYZE, show exact/lossy pages for a BitmapHeapScan node
3374  */
3375 static void
3377 {
3378  if (es->format != EXPLAIN_FORMAT_TEXT)
3379  {
3380  ExplainPropertyInteger("Exact Heap Blocks", NULL,
3381  planstate->exact_pages, es);
3382  ExplainPropertyInteger("Lossy Heap Blocks", NULL,
3383  planstate->lossy_pages, es);
3384  }
3385  else
3386  {
3387  if (planstate->exact_pages > 0 || planstate->lossy_pages > 0)
3388  {
3389  ExplainIndentText(es);
3390  appendStringInfoString(es->str, "Heap Blocks:");
3391  if (planstate->exact_pages > 0)
3392  appendStringInfo(es->str, " exact=%ld", planstate->exact_pages);
3393  if (planstate->lossy_pages > 0)
3394  appendStringInfo(es->str, " lossy=%ld", planstate->lossy_pages);
3395  appendStringInfoChar(es->str, '\n');
3396  }
3397  }
3398 }
3399 
3400 /*
3401  * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
3402  *
3403  * "which" identifies which instrumentation counter to print
3404  */
3405 static void
3406 show_instrumentation_count(const char *qlabel, int which,
3407  PlanState *planstate, ExplainState *es)
3408 {
3409  double nfiltered;
3410  double nloops;
3411 
3412  if (!es->analyze || !planstate->instrument)
3413  return;
3414 
3415  if (which == 2)
3416  nfiltered = planstate->instrument->nfiltered2;
3417  else
3418  nfiltered = planstate->instrument->nfiltered1;
3419  nloops = planstate->instrument->nloops;
3420 
3421  /* In text mode, suppress zero counts; they're not interesting enough */
3422  if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
3423  {
3424  if (nloops > 0)
3425  ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
3426  else
3427  ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
3428  }
3429 }
3430 
3431 /*
3432  * Show extra information for a ForeignScan node.
3433  */
3434 static void
3436 {
3437  FdwRoutine *fdwroutine = fsstate->fdwroutine;
3438 
3439  /* Let the FDW emit whatever fields it wants */
3440  if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
3441  {
3442  if (fdwroutine->ExplainDirectModify != NULL)
3443  fdwroutine->ExplainDirectModify(fsstate, es);
3444  }
3445  else
3446  {
3447  if (fdwroutine->ExplainForeignScan != NULL)
3448  fdwroutine->ExplainForeignScan(fsstate, es);
3449  }
3450 }
3451 
3452 /*
3453  * Show initplan params evaluated at Gather or Gather Merge node.
3454  */
3455 static void
3457 {
3458  int paramid = -1;
3459  List *params = NIL;
3460 
3461  Assert(bms_params);
3462 
3463  while ((paramid = bms_next_member(bms_params, paramid)) >= 0)
3464  {
3465  char param[32];
3466 
3467  snprintf(param, sizeof(param), "$%d", paramid);
3468  params = lappend(params, pstrdup(param));
3469  }
3470 
3471  if (params)
3472  ExplainPropertyList("Params Evaluated", params, es);
3473 }
3474 
3475 /*
3476  * Fetch the name of an index in an EXPLAIN
3477  *
3478  * We allow plugins to get control here so that plans involving hypothetical
3479  * indexes can be explained.
3480  *
3481  * Note: names returned by this function should be "raw"; the caller will
3482  * apply quoting if needed. Formerly the convention was to do quoting here,
3483  * but we don't want that in non-text output formats.
3484  */
3485 static const char *
3487 {
3488  const char *result;
3489 
3491  result = (*explain_get_index_name_hook) (indexId);
3492  else
3493  result = NULL;
3494  if (result == NULL)
3495  {
3496  /* default behavior: look it up in the catalogs */
3497  result = get_rel_name(indexId);
3498  if (result == NULL)
3499  elog(ERROR, "cache lookup failed for index %u", indexId);
3500  }
3501  return result;
3502 }
3503 
3504 /*
3505  * Show buffer usage details.
3506  */
3507 static void
3509 {
3510  if (es->format == EXPLAIN_FORMAT_TEXT)
3511  {
3512  bool has_shared = (usage->shared_blks_hit > 0 ||
3513  usage->shared_blks_read > 0 ||
3514  usage->shared_blks_dirtied > 0 ||
3515  usage->shared_blks_written > 0);
3516  bool has_local = (usage->local_blks_hit > 0 ||
3517  usage->local_blks_read > 0 ||
3518  usage->local_blks_dirtied > 0 ||
3519  usage->local_blks_written > 0);
3520  bool has_temp = (usage->temp_blks_read > 0 ||
3521  usage->temp_blks_written > 0);
3522  bool has_timing = (!INSTR_TIME_IS_ZERO(usage->blk_read_time) ||
3523  !INSTR_TIME_IS_ZERO(usage->blk_write_time));
3524  bool has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
3525  !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
3526  bool show_planning = (planning && (has_shared ||
3527  has_local || has_temp || has_timing ||
3528  has_temp_timing));
3529 
3530  if (show_planning)
3531  {
3532  ExplainIndentText(es);
3533  appendStringInfoString(es->str, "Planning:\n");
3534  es->indent++;
3535  }
3536 
3537  /* Show only positive counter values. */
3538  if (has_shared || has_local || has_temp)
3539  {
3540  ExplainIndentText(es);
3541  appendStringInfoString(es->str, "Buffers:");
3542 
3543  if (has_shared)
3544  {
3545  appendStringInfoString(es->str, " shared");
3546  if (usage->shared_blks_hit > 0)
3547  appendStringInfo(es->str, " hit=%lld",
3548  (long long) usage->shared_blks_hit);
3549  if (usage->shared_blks_read > 0)
3550  appendStringInfo(es->str, " read=%lld",
3551  (long long) usage->shared_blks_read);
3552  if (usage->shared_blks_dirtied > 0)
3553  appendStringInfo(es->str, " dirtied=%lld",
3554  (long long) usage->shared_blks_dirtied);
3555  if (usage->shared_blks_written > 0)
3556  appendStringInfo(es->str, " written=%lld",
3557  (long long) usage->shared_blks_written);
3558  if (has_local || has_temp)
3559  appendStringInfoChar(es->str, ',');
3560  }
3561  if (has_local)
3562  {
3563  appendStringInfoString(es->str, " local");
3564  if (usage->local_blks_hit > 0)
3565  appendStringInfo(es->str, " hit=%lld",
3566  (long long) usage->local_blks_hit);
3567  if (usage->local_blks_read > 0)
3568  appendStringInfo(es->str, " read=%lld",
3569  (long long) usage->local_blks_read);
3570  if (usage->local_blks_dirtied > 0)
3571  appendStringInfo(es->str, " dirtied=%lld",
3572  (long long) usage->local_blks_dirtied);
3573  if (usage->local_blks_written > 0)
3574  appendStringInfo(es->str, " written=%lld",
3575  (long long) usage->local_blks_written);
3576  if (has_temp)
3577  appendStringInfoChar(es->str, ',');
3578  }
3579  if (has_temp)
3580  {
3581  appendStringInfoString(es->str, " temp");
3582  if (usage->temp_blks_read > 0)
3583  appendStringInfo(es->str, " read=%lld",
3584  (long long) usage->temp_blks_read);
3585  if (usage->temp_blks_written > 0)
3586  appendStringInfo(es->str, " written=%lld",
3587  (long long) usage->temp_blks_written);
3588  }
3589  appendStringInfoChar(es->str, '\n');
3590  }
3591 
3592  /* As above, show only positive counter values. */
3593  if (has_timing || has_temp_timing)
3594  {
3595  ExplainIndentText(es);
3596  appendStringInfoString(es->str, "I/O Timings:");
3597 
3598  if (has_timing)
3599  {
3600  appendStringInfoString(es->str, " shared/local");
3601  if (!INSTR_TIME_IS_ZERO(usage->blk_read_time))
3602  appendStringInfo(es->str, " read=%0.3f",
3603  INSTR_TIME_GET_MILLISEC(usage->blk_read_time));
3604  if (!INSTR_TIME_IS_ZERO(usage->blk_write_time))
3605  appendStringInfo(es->str, " write=%0.3f",
3606  INSTR_TIME_GET_MILLISEC(usage->blk_write_time));
3607  if (has_temp_timing)
3608  appendStringInfoChar(es->str, ',');
3609  }
3610  if (has_temp_timing)
3611  {
3612  appendStringInfoString(es->str, " temp");
3613  if (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))
3614  appendStringInfo(es->str, " read=%0.3f",
3615  INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));
3616  if (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))
3617  appendStringInfo(es->str, " write=%0.3f",
3618  INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));
3619  }
3620  appendStringInfoChar(es->str, '\n');
3621  }
3622 
3623  if (show_planning)
3624  es->indent--;
3625  }
3626  else
3627  {
3628  ExplainPropertyInteger("Shared Hit Blocks", NULL,
3629  usage->shared_blks_hit, es);
3630  ExplainPropertyInteger("Shared Read Blocks", NULL,
3631  usage->shared_blks_read, es);
3632  ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
3633  usage->shared_blks_dirtied, es);
3634  ExplainPropertyInteger("Shared Written Blocks", NULL,
3635  usage->shared_blks_written, es);
3636  ExplainPropertyInteger("Local Hit Blocks", NULL,
3637  usage->local_blks_hit, es);
3638  ExplainPropertyInteger("Local Read Blocks", NULL,
3639  usage->local_blks_read, es);
3640  ExplainPropertyInteger("Local Dirtied Blocks", NULL,
3641  usage->local_blks_dirtied, es);
3642  ExplainPropertyInteger("Local Written Blocks", NULL,
3643  usage->local_blks_written, es);
3644  ExplainPropertyInteger("Temp Read Blocks", NULL,
3645  usage->temp_blks_read, es);
3646  ExplainPropertyInteger("Temp Written Blocks", NULL,
3647  usage->temp_blks_written, es);
3648  if (track_io_timing)
3649  {
3650  ExplainPropertyFloat("I/O Read Time", "ms",
3651  INSTR_TIME_GET_MILLISEC(usage->blk_read_time),
3652  3, es);
3653  ExplainPropertyFloat("I/O Write Time", "ms",
3654  INSTR_TIME_GET_MILLISEC(usage->blk_write_time),
3655  3, es);
3656  ExplainPropertyFloat("Temp I/O Read Time", "ms",
3657  INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),
3658  3, es);
3659  ExplainPropertyFloat("Temp I/O Write Time", "ms",
3660  INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),
3661  3, es);
3662  }
3663  }
3664 }
3665 
3666 /*
3667  * Show WAL usage details.
3668  */
3669 static void
3671 {
3672  if (es->format == EXPLAIN_FORMAT_TEXT)
3673  {
3674  /* Show only positive counter values. */
3675  if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
3676  (usage->wal_bytes > 0))
3677  {
3678  ExplainIndentText(es);
3679  appendStringInfoString(es->str, "WAL:");
3680 
3681  if (usage->wal_records > 0)
3682  appendStringInfo(es->str, " records=%lld",
3683  (long long) usage->wal_records);
3684  if (usage->wal_fpi > 0)
3685  appendStringInfo(es->str, " fpi=%lld",
3686  (long long) usage->wal_fpi);
3687  if (usage->wal_bytes > 0)
3688  appendStringInfo(es->str, " bytes=" UINT64_FORMAT,
3689  usage->wal_bytes);
3690  appendStringInfoChar(es->str, '\n');
3691  }
3692  }
3693  else
3694  {
3695  ExplainPropertyInteger("WAL Records", NULL,
3696  usage->wal_records, es);
3697  ExplainPropertyInteger("WAL FPI", NULL,
3698  usage->wal_fpi, es);
3699  ExplainPropertyUInteger("WAL Bytes", NULL,
3700  usage->wal_bytes, es);
3701  }
3702 }
3703 
3704 /*
3705  * Add some additional details about an IndexScan or IndexOnlyScan
3706  */
3707 static void
3709  ExplainState *es)
3710 {
3711  const char *indexname = explain_get_index_name(indexid);
3712 
3713  if (es->format == EXPLAIN_FORMAT_TEXT)
3714  {
3715  if (ScanDirectionIsBackward(indexorderdir))
3716  appendStringInfoString(es->str, " Backward");
3717  appendStringInfo(es->str, " using %s", quote_identifier(indexname));
3718  }
3719  else
3720  {
3721  const char *scandir;
3722 
3723  switch (indexorderdir)
3724  {
3725  case BackwardScanDirection:
3726  scandir = "Backward";
3727  break;
3729  scandir = "NoMovement";
3730  break;
3731  case ForwardScanDirection:
3732  scandir = "Forward";
3733  break;
3734  default:
3735  scandir = "???";
3736  break;
3737  }
3738  ExplainPropertyText("Scan Direction", scandir, es);
3739  ExplainPropertyText("Index Name", indexname, es);
3740  }
3741 }
3742 
3743 /*
3744  * Show the target of a Scan node
3745  */
3746 static void
3748 {
3749  ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
3750 }
3751 
3752 /*
3753  * Show the target of a ModifyTable node
3754  *
3755  * Here we show the nominal target (ie, the relation that was named in the
3756  * original query). If the actual target(s) is/are different, we'll show them
3757  * in show_modifytable_info().
3758  */
3759 static void
3761 {
3762  ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
3763 }
3764 
3765 /*
3766  * Show the target relation of a scan or modify node
3767  */
3768 static void
3770 {
3771  char *objectname = NULL;
3772  char *namespace = NULL;
3773  const char *objecttag = NULL;
3774  RangeTblEntry *rte;
3775  char *refname;
3776 
3777  rte = rt_fetch(rti, es->rtable);
3778  refname = (char *) list_nth(es->rtable_names, rti - 1);
3779  if (refname == NULL)
3780  refname = rte->eref->aliasname;
3781 
3782  switch (nodeTag(plan))
3783  {
3784  case T_SeqScan:
3785  case T_SampleScan:
3786  case T_IndexScan:
3787  case T_IndexOnlyScan:
3788  case T_BitmapHeapScan:
3789  case T_TidScan:
3790  case T_TidRangeScan:
3791  case T_ForeignScan:
3792  case T_CustomScan:
3793  case T_ModifyTable:
3794  /* Assert it's on a real relation */
3795  Assert(rte->rtekind == RTE_RELATION);
3796  objectname = get_rel_name(rte->relid);
3797  if (es->verbose)
3798  namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
3799  objecttag = "Relation Name";
3800  break;
3801  case T_FunctionScan:
3802  {
3803  FunctionScan *fscan = (FunctionScan *) plan;
3804 
3805  /* Assert it's on a RangeFunction */
3806  Assert(rte->rtekind == RTE_FUNCTION);
3807 
3808  /*
3809  * If the expression is still a function call of a single
3810  * function, we can get the real name of the function.
3811  * Otherwise, punt. (Even if it was a single function call
3812  * originally, the optimizer could have simplified it away.)
3813  */
3814  if (list_length(fscan->functions) == 1)
3815  {
3816  RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
3817 
3818  if (IsA(rtfunc->funcexpr, FuncExpr))
3819  {
3820  FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
3821  Oid funcid = funcexpr->funcid;
3822 
3823  objectname = get_func_name(funcid);
3824  if (es->verbose)
3825  namespace = get_namespace_name_or_temp(get_func_namespace(funcid));
3826  }
3827  }
3828  objecttag = "Function Name";
3829  }
3830  break;
3831  case T_TableFuncScan:
3832  Assert(rte->rtekind == RTE_TABLEFUNC);
3833  if (rte->tablefunc)
3834  if (rte->tablefunc->functype == TFT_XMLTABLE)
3835  objectname = "xmltable";
3836  else /* Must be TFT_JSON_TABLE */
3837  objectname = "json_table";
3838  else
3839  objectname = NULL;
3840  objecttag = "Table Function Name";
3841  break;
3842  case T_ValuesScan:
3843  Assert(rte->rtekind == RTE_VALUES);
3844  break;
3845  case T_CteScan:
3846  /* Assert it's on a non-self-reference CTE */
3847  Assert(rte->rtekind == RTE_CTE);
3848  Assert(!rte->self_reference);
3849  objectname = rte->ctename;
3850  objecttag = "CTE Name";
3851  break;
3852  case T_NamedTuplestoreScan:
3853  Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
3854  objectname = rte->enrname;
3855  objecttag = "Tuplestore Name";
3856  break;
3857  case T_WorkTableScan:
3858  /* Assert it's on a self-reference CTE */
3859  Assert(rte->rtekind == RTE_CTE);
3860  Assert(rte->self_reference);
3861  objectname = rte->ctename;
3862  objecttag = "CTE Name";
3863  break;
3864  default:
3865  break;
3866  }
3867 
3868  if (es->format == EXPLAIN_FORMAT_TEXT)
3869  {
3870  appendStringInfoString(es->str, " on");
3871  if (namespace != NULL)
3872  appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
3873  quote_identifier(objectname));
3874  else if (objectname != NULL)
3875  appendStringInfo(es->str, " %s", quote_identifier(objectname));
3876  if (objectname == NULL || strcmp(refname, objectname) != 0)
3877  appendStringInfo(es->str, " %s", quote_identifier(refname));
3878  }
3879  else
3880  {
3881  if (objecttag != NULL && objectname != NULL)
3882  ExplainPropertyText(objecttag, objectname, es);
3883  if (namespace != NULL)
3884  ExplainPropertyText("Schema", namespace, es);
3885  ExplainPropertyText("Alias", refname, es);
3886  }
3887 }
3888 
3889 /*
3890  * Show extra information for a ModifyTable node
3891  *
3892  * We have three objectives here. First, if there's more than one target
3893  * table or it's different from the nominal target, identify the actual
3894  * target(s). Second, give FDWs a chance to display extra info about foreign
3895  * targets. Third, show information about ON CONFLICT.
3896  */
3897 static void
3899  ExplainState *es)
3900 {
3901  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
3902  const char *operation;
3903  const char *foperation;
3904  bool labeltargets;
3905  int j;
3906  List *idxNames = NIL;
3907  ListCell *lst;
3908 
3909  switch (node->operation)
3910  {
3911  case CMD_INSERT:
3912  operation = "Insert";
3913  foperation = "Foreign Insert";
3914  break;
3915  case CMD_UPDATE:
3916  operation = "Update";
3917  foperation = "Foreign Update";
3918  break;
3919  case CMD_DELETE:
3920  operation = "Delete";
3921  foperation = "Foreign Delete";
3922  break;
3923  case CMD_MERGE:
3924  operation = "Merge";
3925  /* XXX unsupported for now, but avoid compiler noise */
3926  foperation = "Foreign Merge";
3927  break;
3928  default:
3929  operation = "???";
3930  foperation = "Foreign ???";
3931  break;
3932  }
3933 
3934  /* Should we explicitly label target relations? */
3935  labeltargets = (mtstate->mt_nrels > 1 ||
3936  (mtstate->mt_nrels == 1 &&
3937  mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation));
3938 
3939  if (labeltargets)
3940  ExplainOpenGroup("Target Tables", "Target Tables", false, es);
3941 
3942  for (j = 0; j < mtstate->mt_nrels; j++)
3943  {
3944  ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
3945  FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
3946 
3947  if (labeltargets)
3948  {
3949  /* Open a group for this target */
3950  ExplainOpenGroup("Target Table", NULL, true, es);
3951 
3952  /*
3953  * In text mode, decorate each target with operation type, so that
3954  * ExplainTargetRel's output of " on foo" will read nicely.
3955  */
3956  if (es->format == EXPLAIN_FORMAT_TEXT)
3957  {
3958  ExplainIndentText(es);
3960  fdwroutine ? foperation : operation);
3961  }
3962 
3963  /* Identify target */
3964  ExplainTargetRel((Plan *) node,
3965  resultRelInfo->ri_RangeTableIndex,
3966  es);
3967 
3968  if (es->format == EXPLAIN_FORMAT_TEXT)
3969  {
3970  appendStringInfoChar(es->str, '\n');
3971  es->indent++;
3972  }
3973  }
3974 
3975  /* Give FDW a chance if needed */
3976  if (!resultRelInfo->ri_usesFdwDirectModify &&
3977  fdwroutine != NULL &&
3978  fdwroutine->ExplainForeignModify != NULL)
3979  {
3980  List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
3981 
3982  fdwroutine->ExplainForeignModify(mtstate,
3983  resultRelInfo,
3984  fdw_private,
3985  j,
3986  es);
3987  }
3988 
3989  if (labeltargets)
3990  {
3991  /* Undo the indentation we added in text format */
3992  if (es->format == EXPLAIN_FORMAT_TEXT)
3993  es->indent--;
3994 
3995  /* Close the group */
3996  ExplainCloseGroup("Target Table", NULL, true, es);
3997  }
3998  }
3999 
4000  /* Gather names of ON CONFLICT arbiter indexes */
4001  foreach(lst, node->arbiterIndexes)
4002  {
4003  char *indexname = get_rel_name(lfirst_oid(lst));
4004 
4005  idxNames = lappend(idxNames, indexname);
4006  }
4007 
4008  if (node->onConflictAction != ONCONFLICT_NONE)
4009  {
4010  ExplainPropertyText("Conflict Resolution",
4012  "NOTHING" : "UPDATE",
4013  es);
4014 
4015  /*
4016  * Don't display arbiter indexes at all when DO NOTHING variant
4017  * implicitly ignores all conflicts
4018  */
4019  if (idxNames)
4020  ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
4021 
4022  /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
4023  if (node->onConflictWhere)
4024  {
4025  show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
4026  &mtstate->ps, ancestors, es);
4027  show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
4028  }
4029 
4030  /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
4031  if (es->analyze && mtstate->ps.instrument)
4032  {
4033  double total;
4034  double insert_path;
4035  double other_path;
4036 
4037  InstrEndLoop(outerPlanState(mtstate)->instrument);
4038 
4039  /* count the number of source rows */
4040  total = outerPlanState(mtstate)->instrument->ntuples;
4041  other_path = mtstate->ps.instrument->ntuples2;
4042  insert_path = total - other_path;
4043 
4044  ExplainPropertyFloat("Tuples Inserted", NULL,
4045  insert_path, 0, es);
4046  ExplainPropertyFloat("Conflicting Tuples", NULL,
4047  other_path, 0, es);
4048  }
4049  }
4050  else if (node->operation == CMD_MERGE)
4051  {
4052  /* EXPLAIN ANALYZE display of tuples processed */
4053  if (es->analyze && mtstate->ps.instrument)
4054  {
4055  double total;
4056  double insert_path;
4057  double update_path;
4058  double delete_path;
4059  double skipped_path;
4060 
4061  InstrEndLoop(outerPlanState(mtstate)->instrument);
4062 
4063  /* count the number of source rows */
4064  total = outerPlanState(mtstate)->instrument->ntuples;
4065  insert_path = mtstate->mt_merge_inserted;
4066  update_path = mtstate->mt_merge_updated;
4067  delete_path = mtstate->mt_merge_deleted;
4068  skipped_path = total - insert_path - update_path - delete_path;
4069  Assert(skipped_path >= 0);
4070 
4071  if (es->format == EXPLAIN_FORMAT_TEXT)
4072  {
4073  if (total > 0)
4074  {
4075  ExplainIndentText(es);
4076  appendStringInfoString(es->str, "Tuples:");
4077  if (insert_path > 0)
4078  appendStringInfo(es->str, " inserted=%.0f", insert_path);
4079  if (update_path > 0)
4080  appendStringInfo(es->str, " updated=%.0f", update_path);
4081  if (delete_path > 0)
4082  appendStringInfo(es->str, " deleted=%.0f", delete_path);
4083  if (skipped_path > 0)
4084  appendStringInfo(es->str, " skipped=%.0f", skipped_path);
4085  appendStringInfoChar(es->str, '\n');
4086  }
4087  }
4088  else
4089  {
4090  ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
4091  ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
4092  ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
4093  ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
4094  }
4095  }
4096  }
4097 
4098  if (labeltargets)
4099  ExplainCloseGroup("Target Tables", "Target Tables", false, es);
4100 }
4101 
4102 /*
4103  * Explain the constituent plans of an Append, MergeAppend,
4104  * BitmapAnd, or BitmapOr node.
4105  *
4106  * The ancestors list should already contain the immediate parent of these
4107  * plans.
4108  */
4109 static void
4110 ExplainMemberNodes(PlanState **planstates, int nplans,
4111  List *ancestors, ExplainState *es)
4112 {
4113  int j;
4114 
4115  for (j = 0; j < nplans; j++)
4116  ExplainNode(planstates[j], ancestors,
4117  "Member", NULL, es);
4118 }
4119 
4120 /*
4121  * Report about any pruned subnodes of an Append or MergeAppend node.
4122  *
4123  * nplans indicates the number of live subplans.
4124  * nchildren indicates the original number of subnodes in the Plan;
4125  * some of these may have been pruned by the run-time pruning code.
4126  */
4127 static void
4128 ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
4129 {
4130  if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
4131  ExplainPropertyInteger("Subplans Removed", NULL,
4132  nchildren - nplans, es);
4133 }
4134 
4135 /*
4136  * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
4137  *
4138  * The ancestors list should already contain the immediate parent of these
4139  * SubPlans.
4140  */
4141 static void
4142 ExplainSubPlans(List *plans, List *ancestors,
4143  const char *relationship, ExplainState *es)
4144 {
4145  ListCell *lst;
4146 
4147  foreach(lst, plans)
4148  {
4149  SubPlanState *sps = (SubPlanState *) lfirst(lst);
4150  SubPlan *sp = sps->subplan;
4151 
4152  /*
4153  * There can be multiple SubPlan nodes referencing the same physical
4154  * subplan (same plan_id, which is its index in PlannedStmt.subplans).
4155  * We should print a subplan only once, so track which ones we already
4156  * printed. This state must be global across the plan tree, since the
4157  * duplicate nodes could be in different plan nodes, eg both a bitmap
4158  * indexscan's indexqual and its parent heapscan's recheck qual. (We
4159  * do not worry too much about which plan node we show the subplan as
4160  * attached to in such cases.)
4161  */
4162  if (bms_is_member(sp->plan_id, es->printed_subplans))
4163  continue;
4165  sp->plan_id);
4166 
4167  /*
4168  * Treat the SubPlan node as an ancestor of the plan node(s) within
4169  * it, so that ruleutils.c can find the referents of subplan
4170  * parameters.
4171  */
4172  ancestors = lcons(sp, ancestors);
4173 
4174  ExplainNode(sps->planstate, ancestors,
4175  relationship, sp->plan_name, es);
4176 
4177  ancestors = list_delete_first(ancestors);
4178  }
4179 }
4180 
4181 /*
4182  * Explain a list of children of a CustomScan.
4183  */
4184 static void
4186 {
4187  ListCell *cell;
4188  const char *label =
4189  (list_length(css->custom_ps) != 1 ? "children" : "child");
4190 
4191  foreach(cell, css->custom_ps)
4192  ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
4193 }
4194 
4195 /*
4196  * Create a per-plan-node workspace for collecting per-worker data.
4197  *
4198  * Output related to each worker will be temporarily "set aside" into a
4199  * separate buffer, which we'll merge into the main output stream once
4200  * we've processed all data for the plan node. This makes it feasible to
4201  * generate a coherent sub-group of fields for each worker, even though the
4202  * code that produces the fields is in several different places in this file.
4203  * Formatting of such a set-aside field group is managed by
4204  * ExplainOpenSetAsideGroup and ExplainSaveGroup/ExplainRestoreGroup.
4205  */
4206 static ExplainWorkersState *
4208 {
4209  ExplainWorkersState *wstate;
4210 
4211  wstate = (ExplainWorkersState *) palloc(sizeof(ExplainWorkersState));
4212  wstate->num_workers = num_workers;
4213  wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
4214  wstate->worker_str = (StringInfoData *)
4215  palloc0(num_workers * sizeof(StringInfoData));
4216  wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
4217  return wstate;
4218 }
4219 
4220 /*
4221  * Begin or resume output into the set-aside group for worker N.
4222  */
4223 static void
4225 {
4226  ExplainWorkersState *wstate = es->workers_state;
4227 
4228  Assert(wstate);
4229  Assert(n >= 0 && n < wstate->num_workers);
4230 
4231  /* Save prior output buffer pointer */
4232  wstate->prev_str = es->str;
4233 
4234  if (!wstate->worker_inited[n])
4235  {
4236  /* First time through, so create the buffer for this worker */
4237  initStringInfo(&wstate->worker_str[n]);
4238  es->str = &wstate->worker_str[n];
4239 
4240  /*
4241  * Push suitable initial formatting state for this worker's field
4242  * group. We allow one extra logical nesting level, since this group
4243  * will eventually be wrapped in an outer "Workers" group.
4244  */
4245  ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
4246 
4247  /*
4248  * In non-TEXT formats we always emit a "Worker Number" field, even if
4249  * there's no other data for this worker.
4250  */
4251  if (es->format != EXPLAIN_FORMAT_TEXT)
4252  ExplainPropertyInteger("Worker Number", NULL, n, es);
4253 
4254  wstate->worker_inited[n] = true;
4255  }
4256  else
4257  {
4258  /* Resuming output for a worker we've already emitted some data for */
4259  es->str = &wstate->worker_str[n];
4260 
4261  /* Restore formatting state saved by last ExplainCloseWorker() */
4262  ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
4263  }
4264 
4265  /*
4266  * In TEXT format, prefix the first output line for this worker with
4267  * "Worker N:". Then, any additional lines should be indented one more
4268  * stop than the "Worker N" line is.
4269  */
4270  if (es->format == EXPLAIN_FORMAT_TEXT)
4271  {
4272  if (es->str->len == 0)
4273  {
4274  ExplainIndentText(es);
4275  appendStringInfo(es->str, "Worker %d: ", n);
4276  }
4277 
4278  es->indent++;
4279  }
4280 }
4281 
4282 /*
4283  * End output for worker N --- must pair with previous ExplainOpenWorker call
4284  */
4285 static void
4287 {
4288  ExplainWorkersState *wstate = es->workers_state;
4289 
4290  Assert(wstate);
4291  Assert(n >= 0 && n < wstate->num_workers);
4292  Assert(wstate->worker_inited[n]);
4293 
4294  /*
4295  * Save formatting state in case we do another ExplainOpenWorker(), then
4296  * pop the formatting stack.
4297  */
4298  ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
4299 
4300  /*
4301  * In TEXT format, if we didn't actually produce any output line(s) then
4302  * truncate off the partial line emitted by ExplainOpenWorker. (This is
4303  * to avoid bogus output if, say, show_buffer_usage chooses not to print
4304  * anything for the worker.) Also fix up the indent level.
4305  */
4306  if (es->format == EXPLAIN_FORMAT_TEXT)
4307  {
4308  while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
4309  es->str->data[--(es->str->len)] = '\0';
4310 
4311  es->indent--;
4312  }
4313 
4314  /* Restore prior output buffer pointer */
4315  es->str = wstate->prev_str;
4316 }
4317 
4318 /*
4319  * Print per-worker info for current node, then free the ExplainWorkersState.
4320  */
4321 static void
4323 {
4324  ExplainWorkersState *wstate = es->workers_state;
4325 
4326  ExplainOpenGroup("Workers", "Workers", false, es);
4327  for (int i = 0; i < wstate->num_workers; i++)
4328  {
4329  if (wstate->worker_inited[i])
4330  {
4331  /* This must match previous ExplainOpenSetAsideGroup call */
4332  ExplainOpenGroup("Worker", NULL, true, es);
4333  appendStringInfoString(es->str, wstate->worker_str[i].data);
4334  ExplainCloseGroup("Worker", NULL, true, es);
4335 
4336  pfree(wstate->worker_str[i].data);
4337  }
4338  }
4339  ExplainCloseGroup("Workers", "Workers", false, es);
4340 
4341  pfree(wstate->worker_inited);
4342  pfree(wstate->worker_str);
4343  pfree(wstate->worker_state_save);
4344  pfree(wstate);
4345 }
4346 
4347 /*
4348  * Explain a property, such as sort keys or targets, that takes the form of
4349  * a list of unlabeled items. "data" is a list of C strings.
4350  */
4351 void
4352 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
4353 {
4354  ListCell *lc;
4355  bool first = true;
4356 
4357  switch (es->format)
4358  {
4359  case EXPLAIN_FORMAT_TEXT:
4360  ExplainIndentText(es);
4361  appendStringInfo(es->str, "%s: ", qlabel);
4362  foreach(lc, data)
4363  {
4364  if (!first)
4365  appendStringInfoString(es->str, ", ");
4366  appendStringInfoString(es->str, (const char *) lfirst(lc));
4367  first = false;
4368  }
4369  appendStringInfoChar(es->str, '\n');
4370  break;
4371 
4372  case EXPLAIN_FORMAT_XML:
4373  ExplainXMLTag(qlabel, X_OPENING, es);
4374  foreach(lc, data)
4375  {
4376  char *str;
4377 
4378  appendStringInfoSpaces(es->str, es->indent * 2 + 2);
4379  appendStringInfoString(es->str, "<Item>");
4380  str = escape_xml((const char *) lfirst(lc));
4382  pfree(str);
4383  appendStringInfoString(es->str, "</Item>\n");
4384  }
4385  ExplainXMLTag(qlabel, X_CLOSING, es);
4386  break;
4387 
4388  case EXPLAIN_FORMAT_JSON:
4390  appendStringInfoSpaces(es->str, es->indent * 2);
4391  escape_json(es->str, qlabel);
4392  appendStringInfoString(es->str, ": [");
4393  foreach(lc, data)
4394  {
4395  if (!first)
4396  appendStringInfoString(es->str, ", ");
4397  escape_json(es->str, (const char *) lfirst(lc));
4398  first = false;
4399  }
4400  appendStringInfoChar(es->str, ']');
4401  break;
4402 
4403  case EXPLAIN_FORMAT_YAML:
4405  appendStringInfo(es->str, "%s: ", qlabel);
4406  foreach(lc, data)
4407  {
4408  appendStringInfoChar(es->str, '\n');
4409  appendStringInfoSpaces(es->str, es->indent * 2 + 2);
4410  appendStringInfoString(es->str, "- ");
4411  escape_yaml(es->str, (const char *) lfirst(lc));
4412  }
4413  break;
4414  }
4415 }
4416 
4417 /*
4418  * Explain a property that takes the form of a list of unlabeled items within
4419  * another list. "data" is a list of C strings.
4420  */
4421 void
4423 {
4424  ListCell *lc;
4425  bool first = true;
4426 
4427  switch (es->format)
4428  {
4429  case EXPLAIN_FORMAT_TEXT:
4430  case EXPLAIN_FORMAT_XML:
4431  ExplainPropertyList(qlabel, data, es);
4432  return;
4433 
4434  case EXPLAIN_FORMAT_JSON:
4436  appendStringInfoSpaces(es->str, es->indent * 2);
4437  appendStringInfoChar(es->str, '[');
4438  foreach(lc, data)
4439  {
4440  if (!first)
4441  appendStringInfoString(es->str, ", ");
4442  escape_json(es->str, (const char *) lfirst(lc));
4443  first = false;
4444  }
4445  appendStringInfoChar(es->str, ']');
4446  break;
4447 
4448  case EXPLAIN_FORMAT_YAML:
4450  appendStringInfoString(es->str, "- [");
4451  foreach(lc, data)
4452  {
4453  if (!first)
4454  appendStringInfoString(es->str, ", ");
4455  escape_yaml(es->str, (const char *) lfirst(lc));
4456  first = false;
4457  }
4458  appendStringInfoChar(es->str, ']');
4459  break;
4460  }
4461 }
4462 
4463 /*
4464  * Explain a simple property.
4465  *
4466  * If "numeric" is true, the value is a number (or other value that
4467  * doesn't need quoting in JSON).
4468  *
4469  * If unit is non-NULL the text format will display it after the value.
4470  *
4471  * This usually should not be invoked directly, but via one of the datatype
4472  * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
4473  */
4474 static void
4475 ExplainProperty(const char *qlabel, const char *unit, const char *value,
4476  bool numeric, ExplainState *es)
4477 {
4478  switch (es->format)
4479  {
4480  case EXPLAIN_FORMAT_TEXT:
4481  ExplainIndentText(es);
4482  if (unit)
4483  appendStringInfo(es->str, "%s: %s %s\n", qlabel, value, unit);
4484  else
4485  appendStringInfo(es->str, "%s: %s\n", qlabel, value);
4486  break;
4487 
4488  case EXPLAIN_FORMAT_XML:
4489  {
4490  char *str;
4491 
4492  appendStringInfoSpaces(es->str, es->indent * 2);
4493  ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
4494  str = escape_xml(value);
4496  pfree(str);
4497  ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
4498  appendStringInfoChar(es->str, '\n');
4499  }
4500  break;
4501 
4502  case EXPLAIN_FORMAT_JSON:
4504  appendStringInfoSpaces(es->str, es->indent * 2);
4505  escape_json(es->str, qlabel);
4506  appendStringInfoString(es->str, ": ");
4507  if (numeric)
4509  else
4510  escape_json(es->str, value);
4511  break;
4512 
4513  case EXPLAIN_FORMAT_YAML:
4515  appendStringInfo(es->str, "%s: ", qlabel);
4516  if (numeric)
4518  else
4519  escape_yaml(es->str, value);
4520  break;
4521  }
4522 }
4523 
4524 /*
4525  * Explain a string-valued property.
4526  */
4527 void
4528 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
4529 {
4530  ExplainProperty(qlabel, NULL, value, false, es);
4531 }
4532 
4533 /*
4534  * Explain an integer-valued property.
4535  */
4536 void
4537 ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value,
4538  ExplainState *es)
4539 {
4540  char buf[32];
4541 
4542  snprintf(buf, sizeof(buf), INT64_FORMAT, value);
4543  ExplainProperty(qlabel, unit, buf, true, es);
4544 }
4545 
4546 /*
4547  * Explain an unsigned integer-valued property.
4548  */
4549 void
4550 ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value,
4551  ExplainState *es)
4552 {
4553  char buf[32];
4554 
4555  snprintf(buf, sizeof(buf), UINT64_FORMAT, value);
4556  ExplainProperty(qlabel, unit, buf, true, es);
4557 }
4558 
4559 /*
4560  * Explain a float-valued property, using the specified number of
4561  * fractional digits.
4562  */
4563 void
4564 ExplainPropertyFloat(const char *qlabel, const char *unit, double value,
4565  int ndigits, ExplainState *es)
4566 {
4567  char *buf;
4568 
4569  buf = psprintf("%.*f", ndigits, value);
4570  ExplainProperty(qlabel, unit, buf, true, es);
4571  pfree(buf);
4572 }
4573 
4574 /*
4575  * Explain a bool-valued property.
4576  */
4577 void
4578 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
4579 {
4580  ExplainProperty(qlabel, NULL, value ? "true" : "false", true, es);
4581 }
4582 
4583 /*
4584  * Open a group of related objects.
4585  *
4586  * objtype is the type of the group object, labelname is its label within
4587  * a containing object (if any).
4588  *
4589  * If labeled is true, the group members will be labeled properties,
4590  * while if it's false, they'll be unlabeled objects.
4591  */
4592 void
4593 ExplainOpenGroup(const char *objtype, const char *labelname,
4594  bool labeled, ExplainState *es)
4595 {
4596  switch (es->format)
4597  {
4598  case EXPLAIN_FORMAT_TEXT:
4599  /* nothing to do */
4600  break;
4601 
4602  case EXPLAIN_FORMAT_XML:
4603  ExplainXMLTag(objtype, X_OPENING, es);
4604  es->indent++;
4605  break;
4606 
4607  case EXPLAIN_FORMAT_JSON:
4609  appendStringInfoSpaces(es->str, 2 * es->indent);
4610  if (labelname)
4611  {
4612  escape_json(es->str, labelname);
4613  appendStringInfoString(es->str, ": ");
4614  }
4615  appendStringInfoChar(es->str, labeled ? '{' : '[');
4616 
4617  /*
4618  * In JSON format, the grouping_stack is an integer list. 0 means
4619  * we've emitted nothing at this grouping level, 1 means we've
4620  * emitted something (and so the next item needs a comma). See
4621  * ExplainJSONLineEnding().
4622  */
4623  es->grouping_stack = lcons_int(0, es->grouping_stack);
4624  es->indent++;
4625  break;
4626 
4627  case EXPLAIN_FORMAT_YAML:
4628 
4629  /*
4630  * In YAML format, the grouping stack is an integer list. 0 means
4631  * we've emitted nothing at this grouping level AND this grouping
4632  * level is unlabeled and must be marked with "- ". See
4633  * ExplainYAMLLineStarting().
4634  */
4636  if (labelname)
4637  {
4638  appendStringInfo(es->str, "%s: ", labelname);
4639  es->grouping_stack = lcons_int(1, es->grouping_stack);
4640  }
4641  else
4642  {
4643  appendStringInfoString(es->str, "- ");
4644  es->grouping_stack = lcons_int(0, es->grouping_stack);
4645  }
4646  es->indent++;
4647  break;
4648  }
4649 }
4650 
4651 /*
4652  * Close a group of related objects.
4653  * Parameters must match the corresponding ExplainOpenGroup call.
4654  */
4655 void
4656 ExplainCloseGroup(const char *objtype, const char *labelname,
4657  bool labeled, ExplainState *es)
4658 {
4659  switch (es->format)
4660  {
4661  case EXPLAIN_FORMAT_TEXT:
4662  /* nothing to do */
4663  break;
4664 
4665  case EXPLAIN_FORMAT_XML:
4666  es->indent--;
4667  ExplainXMLTag(objtype, X_CLOSING, es);
4668  break;
4669 
4670  case EXPLAIN_FORMAT_JSON:
4671  es->indent--;
4672  appendStringInfoChar(es->str, '\n');
4673  appendStringInfoSpaces(es->str, 2 * es->indent);
4674  appendStringInfoChar(es->str, labeled ? '}' : ']');
4676  break;
4677 
4678  case EXPLAIN_FORMAT_YAML:
4679  es->indent--;
4681  break;
4682  }
4683 }
4684 
4685 /*
4686  * Open a group of related objects, without emitting actual data.
4687  *
4688  * Prepare the formatting state as though we were beginning a group with
4689  * the identified properties, but don't actually emit anything. Output
4690  * subsequent to this call can be redirected into a separate output buffer,
4691  * and then eventually appended to the main output buffer after doing a
4692  * regular ExplainOpenGroup call (with the same parameters).
4693  *
4694  * The extra "depth" parameter is the new group's depth compared to current.
4695  * It could be more than one, in case the eventual output will be enclosed
4696  * in additional nesting group levels. We assume we don't need to track
4697  * formatting state for those levels while preparing this group's output.
4698  *
4699  * There is no ExplainCloseSetAsideGroup --- in current usage, we always
4700  * pop this state with ExplainSaveGroup.
4701  */
4702 static void
4703 ExplainOpenSetAsideGroup(const char *objtype, const char *labelname,
4704  bool labeled, int depth, ExplainState *es)
4705 {
4706  switch (es->format)
4707  {
4708  case EXPLAIN_FORMAT_TEXT:
4709  /* nothing to do */
4710  break;
4711 
4712  case EXPLAIN_FORMAT_XML:
4713  es->indent += depth;
4714  break;
4715 
4716  case EXPLAIN_FORMAT_JSON:
4717  es->grouping_stack = lcons_int(0, es->grouping_stack);
4718  es->indent += depth;
4719  break;
4720 
4721  case EXPLAIN_FORMAT_YAML:
4722  if (labelname)
4723  es->grouping_stack = lcons_int(1, es->grouping_stack);
4724  else
4725  es->grouping_stack = lcons_int(0, es->grouping_stack);
4726  es->indent += depth;
4727  break;
4728  }
4729 }
4730 
4731 /*
4732  * Pop one level of grouping state, allowing for a re-push later.
4733  *
4734  * This is typically used after ExplainOpenSetAsideGroup; pass the
4735  * same "depth" used for that.
4736  *
4737  * This should not emit any output. If state needs to be saved,
4738  * save it at *state_save. Currently, an integer save area is sufficient
4739  * for all formats, but we might need to revisit that someday.
4740  */
4741 static void
4742 ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
4743 {
4744  switch (es->format)
4745  {
4746  case EXPLAIN_FORMAT_TEXT:
4747  /* nothing to do */
4748  break;
4749 
4750  case EXPLAIN_FORMAT_XML:
4751  es->indent -= depth;
4752  break;
4753 
4754  case EXPLAIN_FORMAT_JSON:
4755  es->indent -= depth;
4756  *state_save = linitial_int(es->grouping_stack);
4758  break;
4759 
4760  case EXPLAIN_FORMAT_YAML:
4761  es->indent -= depth;
4762  *state_save = linitial_int(es->grouping_stack);
4764  break;
4765  }
4766 }
4767 
4768 /*
4769  * Re-push one level of grouping state, undoing the effects of ExplainSaveGroup.
4770  */
4771 static void
4772 ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
4773 {
4774  switch (es->format)
4775  {
4776  case EXPLAIN_FORMAT_TEXT:
4777  /* nothing to do */
4778  break;
4779 
4780  case EXPLAIN_FORMAT_XML:
4781  es->indent += depth;
4782  break;
4783 
4784  case EXPLAIN_FORMAT_JSON:
4785  es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
4786  es->indent += depth;
4787  break;
4788 
4789  case EXPLAIN_FORMAT_YAML:
4790  es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
4791  es->indent += depth;
4792  break;
4793  }
4794 }
4795 
4796 /*
4797  * Emit a "dummy" group that never has any members.
4798  *
4799  * objtype is the type of the group object, labelname is its label within
4800  * a containing object (if any).
4801  */
4802 static void
4803 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
4804 {
4805  switch (es->format)
4806  {
4807  case EXPLAIN_FORMAT_TEXT:
4808  /* nothing to do */
4809  break;
4810 
4811  case EXPLAIN_FORMAT_XML:
4812  ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
4813  break;
4814 
4815  case EXPLAIN_FORMAT_JSON:
4817  appendStringInfoSpaces(es->str, 2 * es->indent);
4818  if (labelname)
4819  {
4820  escape_json(es->str, labelname);
4821  appendStringInfoString(es->str, ": ");
4822  }
4823  escape_json(es->str, objtype);
4824  break;
4825 
4826  case EXPLAIN_FORMAT_YAML:
4828  if (labelname)
4829  {
4830  escape_yaml(es->str, labelname);
4831  appendStringInfoString(es->str, ": ");
4832  }
4833  else
4834  {
4835  appendStringInfoString(es->str, "- ");
4836  }
4837  escape_yaml(es->str, objtype);
4838  break;
4839  }
4840 }
4841 
4842 /*
4843  * Emit the start-of-output boilerplate.
4844  *
4845  * This is just enough different from processing a subgroup that we need
4846  * a separate pair of subroutines.
4847  */
4848 void
4850 {
4851  switch (es->format)
4852  {
4853  case EXPLAIN_FORMAT_TEXT:
4854  /* nothing to do */
4855  break;
4856 
4857  case EXPLAIN_FORMAT_XML:
4859  "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
4860  es->indent++;
4861  break;
4862 
4863  case EXPLAIN_FORMAT_JSON:
4864  /* top-level structure is an array of plans */
4865  appendStringInfoChar(es->str, '[');
4866  es->grouping_stack = lcons_int(0, es->grouping_stack);
4867  es->indent++;
4868  break;
4869 
4870  case EXPLAIN_FORMAT_YAML:
4871  es->grouping_stack = lcons_int(0, es->grouping_stack);
4872  break;
4873  }
4874 }
4875 
4876 /*
4877  * Emit the end-of-output boilerplate.
4878  */
4879 void
4881 {
4882  switch (es->format)
4883  {
4884  case EXPLAIN_FORMAT_TEXT:
4885  /* nothing to do */
4886  break;
4887 
4888  case EXPLAIN_FORMAT_XML:
4889  es->indent--;
4890  appendStringInfoString(es->str, "</explain>");
4891  break;
4892 
4893  case EXPLAIN_FORMAT_JSON:
4894  es->indent--;
4895  appendStringInfoString(es->str, "\n]");
4897  break;
4898 
4899  case EXPLAIN_FORMAT_YAML:
4901  break;
4902  }
4903 }
4904 
4905 /*
4906  * Put an appropriate separator between multiple plans
4907  */
4908 void
4910 {
4911  switch (es->format)
4912  {
4913  case EXPLAIN_FORMAT_TEXT:
4914  /* add a blank line */
4915  appendStringInfoChar(es->str, '\n');
4916  break;
4917 
4918  case EXPLAIN_FORMAT_XML:
4919  case EXPLAIN_FORMAT_JSON:
4920  case EXPLAIN_FORMAT_YAML:
4921  /* nothing to do */
4922  break;
4923  }
4924 }
4925 
4926 /*
4927  * Emit opening or closing XML tag.
4928  *
4929  * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
4930  * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
4931  * add.
4932  *
4933  * XML restricts tag names more than our other output formats, eg they can't
4934  * contain white space or slashes. Replace invalid characters with dashes,
4935  * so that for example "I/O Read Time" becomes "I-O-Read-Time".
4936  */
4937 static void
4938 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
4939 {
4940  const char *s;
4941  const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
4942 
4943  if ((flags & X_NOWHITESPACE) == 0)
4944  appendStringInfoSpaces(es->str, 2 * es->indent);
4945  appendStringInfoCharMacro(es->str, '<');
4946  if ((flags & X_CLOSING) != 0)
4947  appendStringInfoCharMacro(es->str, '/');
4948  for (s = tagname; *s; s++)
4949  appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
4950  if ((flags & X_CLOSE_IMMEDIATE) != 0)
4951  appendStringInfoString(es->str, " /");
4952  appendStringInfoCharMacro(es->str, '>');
4953  if ((flags & X_NOWHITESPACE) == 0)
4954  appendStringInfoCharMacro(es->str, '\n');
4955 }
4956 
4957 /*
4958  * Indent a text-format line.
4959  *
4960  * We indent by two spaces per indentation level. However, when emitting
4961  * data for a parallel worker there might already be data on the current line
4962  * (cf. ExplainOpenWorker); in that case, don't indent any more.
4963  */
4964 static void
4966 {
4968  if (es->str->len == 0 || es->str->data[es->str->len - 1] == '\n')
4969  appendStringInfoSpaces(es->str, es->indent * 2);
4970 }
4971 
4972 /*
4973  * Emit a JSON line ending.
4974  *
4975  * JSON requires a comma after each property but the last. To facilitate this,
4976  * in JSON format, the text emitted for each property begins just prior to the
4977  * preceding line-break (and comma, if applicable).
4978  */
4979 static void
4981 {
4983  if (linitial_int(es->grouping_stack) != 0)
4984  appendStringInfoChar(es->str, ',');
4985  else
4986  linitial_int(es->grouping_stack) = 1;
4987  appendStringInfoChar(es->str, '\n');
4988 }
4989 
4990 /*
4991  * Indent a YAML line.
4992  *
4993  * YAML lines are ordinarily indented by two spaces per indentation level.
4994  * The text emitted for each property begins just prior to the preceding
4995  * line-break, except for the first property in an unlabeled group, for which
4996  * it begins immediately after the "- " that introduces the group. The first
4997  * property of the group appears on the same line as the opening "- ".
4998  */
4999 static void
5001 {
5003  if (linitial_int(es->grouping_stack) == 0)
5004  {
5005  linitial_int(es->grouping_stack) = 1;
5006  }
5007  else
5008  {
5009  appendStringInfoChar(es->str, '\n');
5010  appendStringInfoSpaces(es->str, es->indent * 2);
5011  }
5012 }
5013 
5014 /*
5015  * YAML is a superset of JSON; unfortunately, the YAML quoting rules are
5016  * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
5017  * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
5018  * Empty strings, strings with leading or trailing whitespace, and strings
5019  * containing a variety of special characters must certainly be quoted or the
5020  * output is invalid; and other seemingly harmless strings like "0xa" or
5021  * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
5022  * constant rather than a string.
5023  */
5024 static void
5026 {
5027  escape_json(buf, str);
5028 }
int16 AttrNumber
Definition: attnum.h:21
void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: prepare.c:571
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1045
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:738
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:795
bool track_io_timing
Definition: bufmgr.c:137
#define unconstify(underlying_type, expr)
Definition: c.h:1240
#define Max(x, y)
Definition: c.h:980
#define INT64_FORMAT
Definition: c.h:483
#define UINT64_FORMAT
Definition: c.h:484
unsigned int Index
Definition: c.h:549
#define OidIsValid(objectId)
Definition: c.h:710
bool CreateTableAsRelExists(CreateTableAsStmt *ctas)
Definition: createas.c:393
DestReceiver * CreateIntoRelDestReceiver(IntoClause *intoClause)
Definition: createas.c:428
int GetIntoRelEFlags(IntoClause *intoClause)
Definition: createas.c:375
bool defGetBoolean(DefElem *def)
Definition: define.c:108
char * defGetString(DefElem *def)
Definition: define.c:49
DestReceiver * None_Receiver
Definition: dest.c:96
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define ereport(elevel,...)
Definition: elog.h:143
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:461
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:401
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:131
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:300
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
void end_tup_output(TupOutputState *tstate)
Definition: execTuples.c:2333
void do_text_output_multiline(TupOutputState *tstate, const char *txt)
Definition: execTuples.c:2303
TupOutputState * begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:2255
#define outerPlanState(node)
Definition: execnodes.h:1094
#define innerPlanState(node)
Definition: execnodes.h:1093
#define do_text_output_oneline(tstate, str_to_emit)
Definition: executor.h:509
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:56
#define X_OPENING
Definition: explain.c:52
static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors, ExplainState *es)
Definition: explain.c:3898
static void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
Definition: explain.c:4803
static void show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2212
static void show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:3110
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
Definition: explain.c:4528
static void show_group_keys(GroupState *gstate, List *ancestors, ExplainState *es)
Definition: explain.c:2522
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
Definition: explain.c:3760
void ExplainOpenGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
Definition: explain.c:4593
static void show_agg_keys(AggState *astate, List *ancestors, ExplainState *es)
Definition: explain.c:2390
static void show_scan_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2314
static const char * explain_get_index_name(Oid indexId)
Definition: explain.c:3486
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir, ExplainState *es)
Definition: explain.c:3708
#define X_CLOSE_IMMEDIATE
Definition: explain.c:54
ExplainOneQuery_hook_type ExplainOneQuery_hook
Definition: explain.c:45
static void show_eval_params(Bitmapset *bms_params, ExplainState *es)
Definition: explain.c:3456
static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es)
Definition: explain.c:3406
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
Definition: explain.c:4550
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition: explain.c:1145
static void show_incremental_sort_group_info(IncrementalSortGroupInfo *groupInfo, const char *groupLabel, bool indent, ExplainState *es)
Definition: explain.c:2819
void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage)
Definition: explain.c:518
#define X_NOWHITESPACE
Definition: explain.c:55
static void ExplainMemberNodes(PlanState **planstates, int nplans, List *ancestors, ExplainState *es)
Definition: explain.c:4110
explain_get_index_name_hook_type explain_get_index_name_hook
Definition: explain.c:48
static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
Definition: explain.c:1070
static void ExplainPrintJIT(ExplainState *es, int jit_flags, JitInstrumentation *ji)
Definition: explain.c:875
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
Definition: explain.c:4537
void ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
Definition: explain.c:4422
static void ExplainYAMLLineStarting(ExplainState *es)
Definition: explain.c:5000
void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest)
Definition: explain.c:164
static void show_incremental_sort_info(IncrementalSortState *incrsortstate, ExplainState *es)
Definition: explain.c:2933
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2662
static void show_upper_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2328
static void show_sort_info(SortState *sortstate, ExplainState *es)
Definition: explain.c:2728
void ExplainSeparatePlans(ExplainState *es)
Definition: explain.c:4909
static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
Definition: explain.c:4128
void ExplainEndOutput(ExplainState *es)
Definition: explain.c:4880
void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:428
static void ExplainJSONLineEnding(ExplainState *es)
Definition: explain.c:4980
TupleDesc ExplainResultDesc(ExplainStmt *stmt)
Definition: explain.c:328
static void ExplainFlushWorkersState(ExplainState *es)
Definition: explain.c:4322
void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:849
static void show_hash_info(HashState *hashstate, ExplainState *es)
Definition: explain.c:3019
static void ExplainProperty(const char *qlabel, const char *unit, const char *value, bool numeric, ExplainState *es)
Definition: explain.c:4475
void ExplainPropertyFloat(const char *qlabel, const char *unit, double value, int ndigits, ExplainState *es)
Definition: explain.c:4564
void ExplainCloseGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
Definition: explain.c:4656
static void show_sortorder_options(StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst)
Definition: explain.c:2604
static void show_buffer_usage(ExplainState *es, const BufferUsage *usage, bool planning)
Definition: explain.c:3508
static void show_expression(Node *node, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2270
static double elapsed_time(instr_time *starttime)
Definition: explain.c:1051
static void ExplainIndentText(ExplainState *es)
Definition: explain.c:4965
void ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:969
static void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
Definition: explain.c:4772
void ExplainBeginOutput(ExplainState *es)
Definition: explain.c:4849
ExplainState * NewExplainState(void)
Definition: explain.c:311
static void show_sort_group_keys(PlanState *planstate, const char *qlabel, int nkeys, int nPresortedKeys, AttrNumber *keycols, Oid *sortOperators, Oid *collations, bool *nullsFirst, List *ancestors, ExplainState *es)
Definition: explain.c:2542
static void show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
Definition: explain.c:3376
static void ExplainSubPlans(List *plans, List *ancestors, const char *relationship, ExplainState *es)
Definition: explain.c:4142
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
Definition: explain.c:3435
static void ExplainScanTarget(Scan *plan, ExplainState *es)
Definition: explain.c:3747
void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:757
static void escape_yaml(StringInfo buf, const char *str)
Definition: explain.c:5025
static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:2374
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition: explain.c:980
static void show_grouping_sets(PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
Definition: explain.c:2413
static void ExplainPrintSettings(ExplainState *es)
Definition: explain.c:687
static void ExplainCloseWorker(int n, ExplainState *es)
Definition: explain.c:4286
static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:367
static void ExplainOpenWorker(int n, ExplainState *es)
Definition: explain.c:4224
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition: explain.c:3769
static void show_hashagg_info(AggState *hashstate, ExplainState *es)
Definition: explain.c:3254
static void ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
Definition: explain.c:4742
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
Definition: explain.c:4938
static void show_wal_usage(ExplainState *es, const WalUsage *usage)
Definition: explain.c:3670
static void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname, bool labeled, int depth, ExplainState *es)
Definition: explain.c:4703
void ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
Definition: explain.c:4578
static void show_grouping_set_keys(PlanState *planstate, Agg *aggnode, Sort *sortnode, List *context, bool useprefix, List *ancestors, ExplainState *es)
Definition: explain.c:2444
static void show_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2293
static void show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2342
static ExplainWorkersState * ExplainCreateWorkersState(int num_workers)
Definition: explain.c:4207
#define X_CLOSING
Definition: explain.c:53
void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:806
static void show_incremental_sort_keys(IncrementalSortState *incrsortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2357
static void ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
Definition: explain.c:4185
void ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
Definition: explain.c:4352
void(* ExplainOneQuery_hook_type)(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.h:65
@ EXPLAIN_FORMAT_XML
Definition: explain.h:23
@ EXPLAIN_FORMAT_YAML
Definition: explain.h:25
@ EXPLAIN_FORMAT_TEXT
Definition: explain.h:22
@ EXPLAIN_FORMAT_JSON
Definition: explain.h:24
const char *(* explain_get_index_name_hook_type)(Oid indexId)
Definition: explain.h:75
struct config_generic ** get_explain_guc_options(int *num)
Definition: guc.c:9808
char * GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
Definition: guc.c:9901
static struct @151 value
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:156
#define INSTR_TIME_ADD(x, y)
Definition: instr_time.h:158
#define INSTR_TIME_IS_ZERO(t)
Definition: instr_time.h:152
#define INSTR_