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