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