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