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