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