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