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