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