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