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