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