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