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