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