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