PostgreSQL Source Code  git master
explain.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * explain.c
4  * Explain query execution plans
5  *
6  * Portions Copyright (c) 1996-2024, 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_type.h"
18 #include "commands/createas.h"
19 #include "commands/defrem.h"
20 #include "commands/prepare.h"
21 #include "foreign/fdwapi.h"
22 #include "jit/jit.h"
23 #include "libpq/pqformat.h"
24 #include "nodes/extensible.h"
25 #include "nodes/makefuncs.h"
26 #include "nodes/nodeFuncs.h"
27 #include "parser/analyze.h"
28 #include "parser/parsetree.h"
29 #include "rewrite/rewriteHandler.h"
30 #include "storage/bufmgr.h"
31 #include "tcop/tcopprot.h"
32 #include "utils/builtins.h"
33 #include "utils/guc_tables.h"
34 #include "utils/json.h"
35 #include "utils/lsyscache.h"
36 #include "utils/rel.h"
37 #include "utils/ruleutils.h"
38 #include "utils/snapmgr.h"
39 #include "utils/tuplesort.h"
40 #include "utils/typcache.h"
41 #include "utils/xml.h"
42 
43 
44 /* Hook for plugins to get control in ExplainOneQuery() */
46 
47 /* Hook for plugins to get control in explain_get_index_name() */
49 
50 
51 /* Instrumentation data for SERIALIZE option */
52 typedef struct SerializeMetrics
53 {
54  uint64 bytesSent; /* # of bytes serialized */
55  instr_time timeSpent; /* time spent serializing */
56  BufferUsage bufferUsage; /* buffers accessed during serialization */
58 
59 /* OR-able flags for ExplainXMLTag() */
60 #define X_OPENING 0
61 #define X_CLOSING 1
62 #define X_CLOSE_IMMEDIATE 2
63 #define X_NOWHITESPACE 4
64 
65 static void ExplainOneQuery(Query *query, int cursorOptions,
66  IntoClause *into, ExplainState *es,
67  const char *queryString, ParamListInfo params,
68  QueryEnvironment *queryEnv);
69 static void ExplainPrintJIT(ExplainState *es, int jit_flags,
70  JitInstrumentation *ji);
71 static void ExplainPrintSerialize(ExplainState *es,
72  SerializeMetrics *metrics);
73 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
74  ExplainState *es);
75 static double elapsed_time(instr_time *starttime);
76 static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
77 static void ExplainNode(PlanState *planstate, List *ancestors,
78  const char *relationship, const char *plan_name,
79  ExplainState *es);
80 static void show_plan_tlist(PlanState *planstate, List *ancestors,
81  ExplainState *es);
82 static void show_expression(Node *node, const char *qlabel,
83  PlanState *planstate, List *ancestors,
84  bool useprefix, ExplainState *es);
85 static void show_qual(List *qual, const char *qlabel,
86  PlanState *planstate, List *ancestors,
87  bool useprefix, ExplainState *es);
88 static void show_scan_qual(List *qual, const char *qlabel,
89  PlanState *planstate, List *ancestors,
90  ExplainState *es);
91 static void show_upper_qual(List *qual, const char *qlabel,
92  PlanState *planstate, List *ancestors,
93  ExplainState *es);
94 static void show_sort_keys(SortState *sortstate, List *ancestors,
95  ExplainState *es);
96 static void show_incremental_sort_keys(IncrementalSortState *incrsortstate,
97  List *ancestors, ExplainState *es);
98 static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
99  ExplainState *es);
100 static void show_agg_keys(AggState *astate, List *ancestors,
101  ExplainState *es);
102 static void show_grouping_sets(PlanState *planstate, Agg *agg,
103  List *ancestors, ExplainState *es);
104 static void show_grouping_set_keys(PlanState *planstate,
105  Agg *aggnode, Sort *sortnode,
106  List *context, bool useprefix,
107  List *ancestors, ExplainState *es);
108 static void show_group_keys(GroupState *gstate, List *ancestors,
109  ExplainState *es);
110 static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
111  int nkeys, int nPresortedKeys, AttrNumber *keycols,
112  Oid *sortOperators, Oid *collations, bool *nullsFirst,
113  List *ancestors, ExplainState *es);
114 static void show_sortorder_options(StringInfo buf, Node *sortexpr,
115  Oid sortOperator, Oid collation, bool nullsFirst);
116 static void show_tablesample(TableSampleClause *tsc, PlanState *planstate,
117  List *ancestors, ExplainState *es);
118 static void show_sort_info(SortState *sortstate, ExplainState *es);
119 static void show_incremental_sort_info(IncrementalSortState *incrsortstate,
120  ExplainState *es);
121 static void show_hash_info(HashState *hashstate, ExplainState *es);
122 static void show_memoize_info(MemoizeState *mstate, List *ancestors,
123  ExplainState *es);
124 static void show_hashagg_info(AggState *aggstate, ExplainState *es);
125 static void show_tidbitmap_info(BitmapHeapScanState *planstate,
126  ExplainState *es);
127 static void show_instrumentation_count(const char *qlabel, int which,
128  PlanState *planstate, ExplainState *es);
129 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
130 static const char *explain_get_index_name(Oid indexId);
131 static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage);
132 static void show_buffer_usage(ExplainState *es, const BufferUsage *usage);
133 static void show_wal_usage(ExplainState *es, const WalUsage *usage);
134 static void show_memory_counters(ExplainState *es,
135  const MemoryContextCounters *mem_counters);
136 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
137  ExplainState *es);
138 static void ExplainScanTarget(Scan *plan, ExplainState *es);
140 static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
141 static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
142  ExplainState *es);
143 static void ExplainMemberNodes(PlanState **planstates, int nplans,
144  List *ancestors, ExplainState *es);
145 static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es);
146 static void ExplainSubPlans(List *plans, List *ancestors,
147  const char *relationship, ExplainState *es);
148 static void ExplainCustomChildren(CustomScanState *css,
149  List *ancestors, ExplainState *es);
150 static ExplainWorkersState *ExplainCreateWorkersState(int num_workers);
151 static void ExplainOpenWorker(int n, ExplainState *es);
152 static void ExplainCloseWorker(int n, ExplainState *es);
153 static void ExplainFlushWorkersState(ExplainState *es);
154 static void ExplainProperty(const char *qlabel, const char *unit,
155  const char *value, bool numeric, ExplainState *es);
156 static void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname,
157  bool labeled, int depth, ExplainState *es);
158 static void ExplainSaveGroup(ExplainState *es, int depth, int *state_save);
159 static void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save);
160 static void ExplainDummyGroup(const char *objtype, const char *labelname,
161  ExplainState *es);
162 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
163 static void ExplainIndentText(ExplainState *es);
164 static void ExplainJSONLineEnding(ExplainState *es);
165 static void ExplainYAMLLineStarting(ExplainState *es);
166 static void escape_yaml(StringInfo buf, const char *str);
168 
169 
170 
171 /*
172  * ExplainQuery -
173  * execute an EXPLAIN command
174  */
175 void
178 {
180  TupOutputState *tstate;
181  JumbleState *jstate = NULL;
182  Query *query;
183  List *rewritten;
184  ListCell *lc;
185  bool timing_set = false;
186  bool summary_set = false;
187 
188  /* Parse options list. */
189  foreach(lc, stmt->options)
190  {
191  DefElem *opt = (DefElem *) lfirst(lc);
192 
193  if (strcmp(opt->defname, "analyze") == 0)
194  es->analyze = defGetBoolean(opt);
195  else if (strcmp(opt->defname, "verbose") == 0)
196  es->verbose = defGetBoolean(opt);
197  else if (strcmp(opt->defname, "costs") == 0)
198  es->costs = defGetBoolean(opt);
199  else if (strcmp(opt->defname, "buffers") == 0)
200  es->buffers = defGetBoolean(opt);
201  else if (strcmp(opt->defname, "wal") == 0)
202  es->wal = defGetBoolean(opt);
203  else if (strcmp(opt->defname, "settings") == 0)
204  es->settings = defGetBoolean(opt);
205  else if (strcmp(opt->defname, "generic_plan") == 0)
206  es->generic = defGetBoolean(opt);
207  else if (strcmp(opt->defname, "timing") == 0)
208  {
209  timing_set = true;
210  es->timing = defGetBoolean(opt);
211  }
212  else if (strcmp(opt->defname, "summary") == 0)
213  {
214  summary_set = true;
215  es->summary = defGetBoolean(opt);
216  }
217  else if (strcmp(opt->defname, "memory") == 0)
218  es->memory = defGetBoolean(opt);
219  else if (strcmp(opt->defname, "serialize") == 0)
220  {
221  if (opt->arg)
222  {
223  char *p = defGetString(opt);
224 
225  if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0)
227  else if (strcmp(p, "text") == 0)
229  else if (strcmp(p, "binary") == 0)
231  else
232  ereport(ERROR,
233  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
234  errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
235  opt->defname, p),
236  parser_errposition(pstate, opt->location)));
237  }
238  else
239  {
240  /* SERIALIZE without an argument is taken as 'text' */
242  }
243  }
244  else if (strcmp(opt->defname, "format") == 0)
245  {
246  char *p = defGetString(opt);
247 
248  if (strcmp(p, "text") == 0)
250  else if (strcmp(p, "xml") == 0)
252  else if (strcmp(p, "json") == 0)
254  else if (strcmp(p, "yaml") == 0)
256  else
257  ereport(ERROR,
258  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
259  errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
260  opt->defname, p),
261  parser_errposition(pstate, opt->location)));
262  }
263  else
264  ereport(ERROR,
265  (errcode(ERRCODE_SYNTAX_ERROR),
266  errmsg("unrecognized EXPLAIN option \"%s\"",
267  opt->defname),
268  parser_errposition(pstate, opt->location)));
269  }
270 
271  /* check that WAL is used with EXPLAIN ANALYZE */
272  if (es->wal && !es->analyze)
273  ereport(ERROR,
274  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
275  errmsg("EXPLAIN option WAL requires ANALYZE")));
276 
277  /* if the timing was not set explicitly, set default value */
278  es->timing = (timing_set) ? es->timing : es->analyze;
279 
280  /* check that timing is used with EXPLAIN ANALYZE */
281  if (es->timing && !es->analyze)
282  ereport(ERROR,
283  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
284  errmsg("EXPLAIN option TIMING requires ANALYZE")));
285 
286  /* check that serialize is used with EXPLAIN ANALYZE */
287  if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze)
288  ereport(ERROR,
289  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
290  errmsg("EXPLAIN option SERIALIZE requires ANALYZE")));
291 
292  /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */
293  if (es->generic && es->analyze)
294  ereport(ERROR,
295  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
296  errmsg("EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together")));
297 
298  /* if the summary was not set explicitly, set default value */
299  es->summary = (summary_set) ? es->summary : es->analyze;
300 
301  query = castNode(Query, stmt->query);
302  if (IsQueryIdEnabled())
303  jstate = JumbleQuery(query);
304 
306  (*post_parse_analyze_hook) (pstate, query, jstate);
307 
308  /*
309  * Parse analysis was done already, but we still have to run the rule
310  * rewriter. We do not do AcquireRewriteLocks: we assume the query either
311  * came straight from the parser, or suitable locks were acquired by
312  * plancache.c.
313  */
314  rewritten = QueryRewrite(castNode(Query, stmt->query));
315 
316  /* emit opening boilerplate */
317  ExplainBeginOutput(es);
318 
319  if (rewritten == NIL)
320  {
321  /*
322  * In the case of an INSTEAD NOTHING, tell at least that. But in
323  * non-text format, the output is delimited, so this isn't necessary.
324  */
325  if (es->format == EXPLAIN_FORMAT_TEXT)
326  appendStringInfoString(es->str, "Query rewrites to nothing\n");
327  }
328  else
329  {
330  ListCell *l;
331 
332  /* Explain every plan */
333  foreach(l, rewritten)
334  {
336  CURSOR_OPT_PARALLEL_OK, NULL, es,
337  pstate->p_sourcetext, params, pstate->p_queryEnv);
338 
339  /* Separate plans with an appropriate separator */
340  if (lnext(rewritten, l) != NULL)
342  }
343  }
344 
345  /* emit closing boilerplate */
346  ExplainEndOutput(es);
347  Assert(es->indent == 0);
348 
349  /* output tuples */
351  &TTSOpsVirtual);
352  if (es->format == EXPLAIN_FORMAT_TEXT)
353  do_text_output_multiline(tstate, es->str->data);
354  else
355  do_text_output_oneline(tstate, es->str->data);
356  end_tup_output(tstate);
357 
358  pfree(es->str->data);
359 }
360 
361 /*
362  * Create a new ExplainState struct initialized with default options.
363  */
364 ExplainState *
366 {
367  ExplainState *es = (ExplainState *) palloc0(sizeof(ExplainState));
368 
369  /* Set default options (most fields can be left as zeroes). */
370  es->costs = true;
371  /* Prepare output buffer. */
372  es->str = makeStringInfo();
373 
374  return es;
375 }
376 
377 /*
378  * ExplainResultDesc -
379  * construct the result tupledesc for an EXPLAIN
380  */
381 TupleDesc
383 {
384  TupleDesc tupdesc;
385  ListCell *lc;
386  Oid result_type = TEXTOID;
387 
388  /* Check for XML format option */
389  foreach(lc, stmt->options)
390  {
391  DefElem *opt = (DefElem *) lfirst(lc);
392 
393  if (strcmp(opt->defname, "format") == 0)
394  {
395  char *p = defGetString(opt);
396 
397  if (strcmp(p, "xml") == 0)
398  result_type = XMLOID;
399  else if (strcmp(p, "json") == 0)
400  result_type = JSONOID;
401  else
402  result_type = TEXTOID;
403  /* don't "break", as ExplainQuery will use the last value */
404  }
405  }
406 
407  /* Need a tuple descriptor representing a single TEXT or XML column */
408  tupdesc = CreateTemplateTupleDesc(1);
409  TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
410  result_type, -1, 0);
411  return tupdesc;
412 }
413 
414 /*
415  * ExplainOneQuery -
416  * print out the execution plan for one Query
417  *
418  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
419  */
420 static void
421 ExplainOneQuery(Query *query, int cursorOptions,
422  IntoClause *into, ExplainState *es,
423  const char *queryString, ParamListInfo params,
424  QueryEnvironment *queryEnv)
425 {
426  /* planner will not cope with utility statements */
427  if (query->commandType == CMD_UTILITY)
428  {
429  ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
430  queryEnv);
431  return;
432  }
433 
434  /* if an advisor plugin is present, let it manage things */
436  (*ExplainOneQuery_hook) (query, cursorOptions, into, es,
437  queryString, params, queryEnv);
438  else
439  standard_ExplainOneQuery(query, cursorOptions, into, es,
440  queryString, params, queryEnv);
441 }
442 
443 /*
444  * standard_ExplainOneQuery -
445  * print out the execution plan for one Query, without calling a hook.
446  */
447 void
448 standard_ExplainOneQuery(Query *query, int cursorOptions,
449  IntoClause *into, ExplainState *es,
450  const char *queryString, ParamListInfo params,
451  QueryEnvironment *queryEnv)
452 {
453  PlannedStmt *plan;
454  instr_time planstart,
455  planduration;
456  BufferUsage bufusage_start,
457  bufusage;
458  MemoryContextCounters mem_counters;
459  MemoryContext planner_ctx = NULL;
460  MemoryContext saved_ctx = NULL;
461 
462  if (es->memory)
463  {
464  /*
465  * Create a new memory context to measure planner's memory consumption
466  * accurately. Note that if the planner were to be modified to use a
467  * different memory context type, here we would be changing that to
468  * AllocSet, which might be undesirable. However, we don't have a way
469  * to create a context of the same type as another, so we pray and
470  * hope that this is OK.
471  */
473  "explain analyze planner context",
475  saved_ctx = MemoryContextSwitchTo(planner_ctx);
476  }
477 
478  if (es->buffers)
479  bufusage_start = pgBufferUsage;
480  INSTR_TIME_SET_CURRENT(planstart);
481 
482  /* plan the query */
483  plan = pg_plan_query(query, queryString, cursorOptions, params);
484 
485  INSTR_TIME_SET_CURRENT(planduration);
486  INSTR_TIME_SUBTRACT(planduration, planstart);
487 
488  if (es->memory)
489  {
490  MemoryContextSwitchTo(saved_ctx);
491  MemoryContextMemConsumed(planner_ctx, &mem_counters);
492  }
493 
494  /* calc differences of buffer counters. */
495  if (es->buffers)
496  {
497  memset(&bufusage, 0, sizeof(BufferUsage));
498  BufferUsageAccumDiff(&bufusage, &pgBufferUsage, &bufusage_start);
499  }
500 
501  /* run it (if needed) and produce output */
502  ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
503  &planduration, (es->buffers ? &bufusage : NULL),
504  es->memory ? &mem_counters : NULL);
505 }
506 
507 /*
508  * ExplainOneUtility -
509  * print out the execution plan for one utility statement
510  * (In general, utility statements don't have plans, but there are some
511  * we treat as special cases)
512  *
513  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt.
514  *
515  * This is exported because it's called back from prepare.c in the
516  * EXPLAIN EXECUTE case. In that case, we'll be dealing with a statement
517  * that's in the plan cache, so we have to ensure we don't modify it.
518  */
519 void
521  const char *queryString, ParamListInfo params,
522  QueryEnvironment *queryEnv)
523 {
524  if (utilityStmt == NULL)
525  return;
526 
527  if (IsA(utilityStmt, CreateTableAsStmt))
528  {
529  /*
530  * We have to rewrite the contained SELECT and then pass it back to
531  * ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
532  */
533  CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
534  List *rewritten;
535 
536  /*
537  * Check if the relation exists or not. This is done at this stage to
538  * avoid query planning or execution.
539  */
540  if (CreateTableAsRelExists(ctas))
541  {
542  if (ctas->objtype == OBJECT_TABLE)
543  ExplainDummyGroup("CREATE TABLE AS", NULL, es);
544  else if (ctas->objtype == OBJECT_MATVIEW)
545  ExplainDummyGroup("CREATE MATERIALIZED VIEW", NULL, es);
546  else
547  elog(ERROR, "unexpected object type: %d",
548  (int) ctas->objtype);
549  return;
550  }
551 
552  rewritten = QueryRewrite(castNode(Query, copyObject(ctas->query)));
553  Assert(list_length(rewritten) == 1);
554  ExplainOneQuery(linitial_node(Query, rewritten),
555  CURSOR_OPT_PARALLEL_OK, ctas->into, es,
556  queryString, params, queryEnv);
557  }
558  else if (IsA(utilityStmt, DeclareCursorStmt))
559  {
560  /*
561  * Likewise for DECLARE CURSOR.
562  *
563  * Notice that if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll
564  * actually run the query. This is different from pre-8.3 behavior
565  * but seems more useful than not running the query. No cursor will
566  * be created, however.
567  */
568  DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
569  List *rewritten;
570 
571  rewritten = QueryRewrite(castNode(Query, copyObject(dcs->query)));
572  Assert(list_length(rewritten) == 1);
573  ExplainOneQuery(linitial_node(Query, rewritten),
574  dcs->options, NULL, es,
575  queryString, params, queryEnv);
576  }
577  else if (IsA(utilityStmt, ExecuteStmt))
578  ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
579  queryString, params, queryEnv);
580  else if (IsA(utilityStmt, NotifyStmt))
581  {
582  if (es->format == EXPLAIN_FORMAT_TEXT)
583  appendStringInfoString(es->str, "NOTIFY\n");
584  else
585  ExplainDummyGroup("Notify", NULL, es);
586  }
587  else
588  {
589  if (es->format == EXPLAIN_FORMAT_TEXT)
591  "Utility statements have no plan structure\n");
592  else
593  ExplainDummyGroup("Utility Statement", NULL, es);
594  }
595 }
596 
597 /*
598  * ExplainOnePlan -
599  * given a planned query, execute it if needed, and then print
600  * EXPLAIN output
601  *
602  * "into" is NULL unless we are explaining the contents of a CreateTableAsStmt,
603  * in which case executing the query should result in creating that table.
604  *
605  * This is exported because it's called back from prepare.c in the
606  * EXPLAIN EXECUTE case, and because an index advisor plugin would need
607  * to call it.
608  */
609 void
611  const char *queryString, ParamListInfo params,
612  QueryEnvironment *queryEnv, const instr_time *planduration,
613  const BufferUsage *bufusage,
614  const MemoryContextCounters *mem_counters)
615 {
617  QueryDesc *queryDesc;
618  instr_time starttime;
619  double totaltime = 0;
620  int eflags;
621  int instrument_option = 0;
622  SerializeMetrics serializeMetrics = {0};
623 
624  Assert(plannedstmt->commandType != CMD_UTILITY);
625 
626  if (es->analyze && es->timing)
627  instrument_option |= INSTRUMENT_TIMER;
628  else if (es->analyze)
629  instrument_option |= INSTRUMENT_ROWS;
630 
631  if (es->buffers)
632  instrument_option |= INSTRUMENT_BUFFERS;
633  if (es->wal)
634  instrument_option |= INSTRUMENT_WAL;
635 
636  /*
637  * We always collect timing for the entire statement, even when node-level
638  * timing is off, so we don't look at es->timing here. (We could skip
639  * this if !es->summary, but it's hardly worth the complication.)
640  */
641  INSTR_TIME_SET_CURRENT(starttime);
642 
643  /*
644  * Use a snapshot with an updated command ID to ensure this query sees
645  * results of any previously executed queries.
646  */
649 
650  /*
651  * We discard the output if we have no use for it. If we're explaining
652  * CREATE TABLE AS, we'd better use the appropriate tuple receiver, while
653  * the SERIALIZE option requires its own tuple receiver. (If you specify
654  * SERIALIZE while explaining CREATE TABLE AS, you'll see zeroes for the
655  * results, which is appropriate since no data would have gone to the
656  * client.)
657  */
658  if (into)
660  else if (es->serialize != EXPLAIN_SERIALIZE_NONE)
662  else
664 
665  /* Create a QueryDesc for the query */
666  queryDesc = CreateQueryDesc(plannedstmt, queryString,
668  dest, params, queryEnv, instrument_option);
669 
670  /* Select execution options */
671  if (es->analyze)
672  eflags = 0; /* default run-to-completion flags */
673  else
674  eflags = EXEC_FLAG_EXPLAIN_ONLY;
675  if (es->generic)
676  eflags |= EXEC_FLAG_EXPLAIN_GENERIC;
677  if (into)
678  eflags |= GetIntoRelEFlags(into);
679 
680  /* call ExecutorStart to prepare the plan for execution */
681  ExecutorStart(queryDesc, eflags);
682 
683  /* Execute the plan for statistics if asked for */
684  if (es->analyze)
685  {
686  ScanDirection dir;
687 
688  /* EXPLAIN ANALYZE CREATE TABLE AS WITH NO DATA is weird */
689  if (into && into->skipData)
691  else
692  dir = ForwardScanDirection;
693 
694  /* run the plan */
695  ExecutorRun(queryDesc, dir, 0, true);
696 
697  /* run cleanup too */
698  ExecutorFinish(queryDesc);
699 
700  /* We can't run ExecutorEnd 'till we're done printing the stats... */
701  totaltime += elapsed_time(&starttime);
702  }
703 
704  /* grab serialization metrics before we destroy the DestReceiver */
706  serializeMetrics = GetSerializationMetrics(dest);
707 
708  /* call the DestReceiver's destroy method even during explain */
709  dest->rDestroy(dest);
710 
711  ExplainOpenGroup("Query", NULL, true, es);
712 
713  /* Create textual dump of plan tree */
714  ExplainPrintPlan(es, queryDesc);
715 
716  /* Show buffer and/or memory usage in planning */
717  if (peek_buffer_usage(es, bufusage) || mem_counters)
718  {
719  ExplainOpenGroup("Planning", "Planning", true, es);
720 
721  if (es->format == EXPLAIN_FORMAT_TEXT)
722  {
723  ExplainIndentText(es);
724  appendStringInfoString(es->str, "Planning:\n");
725  es->indent++;
726  }
727 
728  if (bufusage)
729  show_buffer_usage(es, bufusage);
730 
731  if (mem_counters)
732  show_memory_counters(es, mem_counters);
733 
734  if (es->format == EXPLAIN_FORMAT_TEXT)
735  es->indent--;
736 
737  ExplainCloseGroup("Planning", "Planning", true, es);
738  }
739 
740  if (es->summary && planduration)
741  {
742  double plantime = INSTR_TIME_GET_DOUBLE(*planduration);
743 
744  ExplainPropertyFloat("Planning Time", "ms", 1000.0 * plantime, 3, es);
745  }
746 
747  /* Print info about runtime of triggers */
748  if (es->analyze)
749  ExplainPrintTriggers(es, queryDesc);
750 
751  /*
752  * Print info about JITing. Tied to es->costs because we don't want to
753  * display this in regression tests, as it'd cause output differences
754  * depending on build options. Might want to separate that out from COSTS
755  * at a later stage.
756  */
757  if (es->costs)
758  ExplainPrintJITSummary(es, queryDesc);
759 
760  /* Print info about serialization of output */
762  ExplainPrintSerialize(es, &serializeMetrics);
763 
764  /*
765  * Close down the query and free resources. Include time for this in the
766  * total execution time (although it should be pretty minimal).
767  */
768  INSTR_TIME_SET_CURRENT(starttime);
769 
770  ExecutorEnd(queryDesc);
771 
772  FreeQueryDesc(queryDesc);
773 
775 
776  /* We need a CCI just in case query expanded to multiple plans */
777  if (es->analyze)
779 
780  totaltime += elapsed_time(&starttime);
781 
782  /*
783  * We only report execution time if we actually ran the query (that is,
784  * the user specified ANALYZE), and if summary reporting is enabled (the
785  * user can set SUMMARY OFF to not have the timing information included in
786  * the output). By default, ANALYZE sets SUMMARY to true.
787  */
788  if (es->summary && es->analyze)
789  ExplainPropertyFloat("Execution Time", "ms", 1000.0 * totaltime, 3,
790  es);
791 
792  ExplainCloseGroup("Query", NULL, true, es);
793 }
794 
795 /*
796  * ExplainPrintSettings -
797  * Print summary of modified settings affecting query planning.
798  */
799 static void
801 {
802  int num;
803  struct config_generic **gucs;
804 
805  /* bail out if information about settings not requested */
806  if (!es->settings)
807  return;
808 
809  /* request an array of relevant settings */
810  gucs = get_explain_guc_options(&num);
811 
812  if (es->format != EXPLAIN_FORMAT_TEXT)
813  {
814  ExplainOpenGroup("Settings", "Settings", true, es);
815 
816  for (int i = 0; i < num; i++)
817  {
818  char *setting;
819  struct config_generic *conf = gucs[i];
820 
821  setting = GetConfigOptionByName(conf->name, NULL, true);
822 
823  ExplainPropertyText(conf->name, setting, es);
824  }
825 
826  ExplainCloseGroup("Settings", "Settings", true, es);
827  }
828  else
829  {
831 
832  /* In TEXT mode, print nothing if there are no options */
833  if (num <= 0)
834  return;
835 
837 
838  for (int i = 0; i < num; i++)
839  {
840  char *setting;
841  struct config_generic *conf = gucs[i];
842 
843  if (i > 0)
844  appendStringInfoString(&str, ", ");
845 
846  setting = GetConfigOptionByName(conf->name, NULL, true);
847 
848  if (setting)
849  appendStringInfo(&str, "%s = '%s'", conf->name, setting);
850  else
851  appendStringInfo(&str, "%s = NULL", conf->name);
852  }
853 
854  ExplainPropertyText("Settings", str.data, es);
855  }
856 }
857 
858 /*
859  * ExplainPrintPlan -
860  * convert a QueryDesc's plan tree to text and append it to es->str
861  *
862  * The caller should have set up the options fields of *es, as well as
863  * initializing the output buffer es->str. Also, output formatting state
864  * such as the indent level is assumed valid. Plan-tree-specific fields
865  * in *es are initialized here.
866  *
867  * NB: will not work on utility statements
868  */
869 void
871 {
872  Bitmapset *rels_used = NULL;
873  PlanState *ps;
874 
875  /* Set up ExplainState fields associated with this plan tree */
876  Assert(queryDesc->plannedstmt != NULL);
877  es->pstmt = queryDesc->plannedstmt;
878  es->rtable = queryDesc->plannedstmt->rtable;
879  ExplainPreScanNode(queryDesc->planstate, &rels_used);
882  es->rtable_names);
883  es->printed_subplans = NULL;
884 
885  /*
886  * Sometimes we mark a Gather node as "invisible", which means that it's
887  * not to be displayed in EXPLAIN output. The purpose of this is to allow
888  * running regression tests with debug_parallel_query=regress to get the
889  * same results as running the same tests with debug_parallel_query=off.
890  * Such marking is currently only supported on a Gather at the top of the
891  * plan. We skip that node, and we must also hide per-worker detail data
892  * further down in the plan tree.
893  */
894  ps = queryDesc->planstate;
895  if (IsA(ps, GatherState) && ((Gather *) ps->plan)->invisible)
896  {
897  ps = outerPlanState(ps);
898  es->hide_workers = true;
899  }
900  ExplainNode(ps, NIL, NULL, NULL, es);
901 
902  /*
903  * If requested, include information about GUC parameters with values that
904  * don't match the built-in defaults.
905  */
907 
908  /*
909  * COMPUTE_QUERY_ID_REGRESS means COMPUTE_QUERY_ID_AUTO, but we don't show
910  * the queryid in any of the EXPLAIN plans to keep stable the results
911  * generated by regression test suites.
912  */
913  if (es->verbose && queryDesc->plannedstmt->queryId != UINT64CONST(0) &&
915  {
916  /*
917  * Output the queryid as an int64 rather than a uint64 so we match
918  * what would be seen in the BIGINT pg_stat_statements.queryid column.
919  */
920  ExplainPropertyInteger("Query Identifier", NULL, (int64)
921  queryDesc->plannedstmt->queryId, es);
922  }
923 }
924 
925 /*
926  * ExplainPrintTriggers -
927  * convert a QueryDesc's trigger statistics to text and append it to
928  * es->str
929  *
930  * The caller should have set up the options fields of *es, as well as
931  * initializing the output buffer es->str. Other fields in *es are
932  * initialized here.
933  */
934 void
936 {
937  ResultRelInfo *rInfo;
938  bool show_relname;
939  List *resultrels;
940  List *routerels;
941  List *targrels;
942  ListCell *l;
943 
944  resultrels = queryDesc->estate->es_opened_result_relations;
945  routerels = queryDesc->estate->es_tuple_routing_result_relations;
946  targrels = queryDesc->estate->es_trig_target_relations;
947 
948  ExplainOpenGroup("Triggers", "Triggers", false, es);
949 
950  show_relname = (list_length(resultrels) > 1 ||
951  routerels != NIL || targrels != NIL);
952  foreach(l, resultrels)
953  {
954  rInfo = (ResultRelInfo *) lfirst(l);
955  report_triggers(rInfo, show_relname, es);
956  }
957 
958  foreach(l, routerels)
959  {
960  rInfo = (ResultRelInfo *) lfirst(l);
961  report_triggers(rInfo, show_relname, es);
962  }
963 
964  foreach(l, targrels)
965  {
966  rInfo = (ResultRelInfo *) lfirst(l);
967  report_triggers(rInfo, show_relname, es);
968  }
969 
970  ExplainCloseGroup("Triggers", "Triggers", false, es);
971 }
972 
973 /*
974  * ExplainPrintJITSummary -
975  * Print summarized JIT instrumentation from leader and workers
976  */
977 void
979 {
980  JitInstrumentation ji = {0};
981 
982  if (!(queryDesc->estate->es_jit_flags & PGJIT_PERFORM))
983  return;
984 
985  /*
986  * Work with a copy instead of modifying the leader state, since this
987  * function may be called twice
988  */
989  if (queryDesc->estate->es_jit)
990  InstrJitAgg(&ji, &queryDesc->estate->es_jit->instr);
991 
992  /* If this process has done JIT in parallel workers, merge stats */
993  if (queryDesc->estate->es_jit_worker_instr)
994  InstrJitAgg(&ji, queryDesc->estate->es_jit_worker_instr);
995 
996  ExplainPrintJIT(es, queryDesc->estate->es_jit_flags, &ji);
997 }
998 
999 /*
1000  * ExplainPrintJIT -
1001  * Append information about JITing to es->str.
1002  */
1003 static void
1005 {
1006  instr_time total_time;
1007 
1008  /* don't print information if no JITing happened */
1009  if (!ji || ji->created_functions == 0)
1010  return;
1011 
1012  /* calculate total time */
1013  INSTR_TIME_SET_ZERO(total_time);
1014  /* don't add deform_counter, it's included in generation_counter */
1015  INSTR_TIME_ADD(total_time, ji->generation_counter);
1016  INSTR_TIME_ADD(total_time, ji->inlining_counter);
1017  INSTR_TIME_ADD(total_time, ji->optimization_counter);
1018  INSTR_TIME_ADD(total_time, ji->emission_counter);
1019 
1020  ExplainOpenGroup("JIT", "JIT", true, es);
1021 
1022  /* for higher density, open code the text output format */
1023  if (es->format == EXPLAIN_FORMAT_TEXT)
1024  {
1025  ExplainIndentText(es);
1026  appendStringInfoString(es->str, "JIT:\n");
1027  es->indent++;
1028 
1029  ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
1030 
1031  ExplainIndentText(es);
1032  appendStringInfo(es->str, "Options: %s %s, %s %s, %s %s, %s %s\n",
1033  "Inlining", jit_flags & PGJIT_INLINE ? "true" : "false",
1034  "Optimization", jit_flags & PGJIT_OPT3 ? "true" : "false",
1035  "Expressions", jit_flags & PGJIT_EXPR ? "true" : "false",
1036  "Deforming", jit_flags & PGJIT_DEFORM ? "true" : "false");
1037 
1038  if (es->analyze && es->timing)
1039  {
1040  ExplainIndentText(es);
1041  appendStringInfo(es->str,
1042  "Timing: %s %.3f ms (%s %.3f ms), %s %.3f ms, %s %.3f ms, %s %.3f ms, %s %.3f ms\n",
1043  "Generation", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->generation_counter),
1044  "Deform", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
1045  "Inlining", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->inlining_counter),
1046  "Optimization", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->optimization_counter),
1047  "Emission", 1000.0 * INSTR_TIME_GET_DOUBLE(ji->emission_counter),
1048  "Total", 1000.0 * INSTR_TIME_GET_DOUBLE(total_time));
1049  }
1050 
1051  es->indent--;
1052  }
1053  else
1054  {
1055  ExplainPropertyInteger("Functions", NULL, ji->created_functions, es);
1056 
1057  ExplainOpenGroup("Options", "Options", true, es);
1058  ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
1059  ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
1060  ExplainPropertyBool("Expressions", jit_flags & PGJIT_EXPR, es);
1061  ExplainPropertyBool("Deforming", jit_flags & PGJIT_DEFORM, es);
1062  ExplainCloseGroup("Options", "Options", true, es);
1063 
1064  if (es->analyze && es->timing)
1065  {
1066  ExplainOpenGroup("Timing", "Timing", true, es);
1067 
1068  ExplainOpenGroup("Generation", "Generation", true, es);
1069  ExplainPropertyFloat("Deform", "ms",
1070  1000.0 * INSTR_TIME_GET_DOUBLE(ji->deform_counter),
1071  3, es);
1072  ExplainPropertyFloat("Total", "ms",
1074  3, es);
1075  ExplainCloseGroup("Generation", "Generation", true, es);
1076 
1077  ExplainPropertyFloat("Inlining", "ms",
1079  3, es);
1080  ExplainPropertyFloat("Optimization", "ms",
1082  3, es);
1083  ExplainPropertyFloat("Emission", "ms",
1085  3, es);
1086  ExplainPropertyFloat("Total", "ms",
1087  1000.0 * INSTR_TIME_GET_DOUBLE(total_time),
1088  3, es);
1089 
1090  ExplainCloseGroup("Timing", "Timing", true, es);
1091  }
1092  }
1093 
1094  ExplainCloseGroup("JIT", "JIT", true, es);
1095 }
1096 
1097 /*
1098  * ExplainPrintSerialize -
1099  * Append information about query output volume to es->str.
1100  */
1101 static void
1103 {
1104  const char *format;
1105 
1106  /* We shouldn't get called for EXPLAIN_SERIALIZE_NONE */
1107  if (es->serialize == EXPLAIN_SERIALIZE_TEXT)
1108  format = "text";
1109  else
1110  {
1112  format = "binary";
1113  }
1114 
1115  ExplainOpenGroup("Serialization", "Serialization", true, es);
1116 
1117  if (es->format == EXPLAIN_FORMAT_TEXT)
1118  {
1119  ExplainIndentText(es);
1120  if (es->timing)
1121  appendStringInfo(es->str, "Serialization: time=%.3f ms output=" UINT64_FORMAT "kB format=%s\n",
1122  1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1123  (metrics->bytesSent + 512) / 1024,
1124  format);
1125  else
1126  appendStringInfo(es->str, "Serialization: output=" UINT64_FORMAT "kB format=%s\n",
1127  (metrics->bytesSent + 512) / 1024,
1128  format);
1129 
1130  if (es->buffers && peek_buffer_usage(es, &metrics->bufferUsage))
1131  {
1132  es->indent++;
1133  show_buffer_usage(es, &metrics->bufferUsage);
1134  es->indent--;
1135  }
1136  }
1137  else
1138  {
1139  if (es->timing)
1140  ExplainPropertyFloat("Time", "ms",
1141  1000.0 * INSTR_TIME_GET_DOUBLE(metrics->timeSpent),
1142  3, es);
1143  ExplainPropertyUInteger("Output Volume", "kB",
1144  (metrics->bytesSent + 512) / 1024, es);
1145  ExplainPropertyText("Format", format, es);
1146  if (es->buffers)
1147  show_buffer_usage(es, &metrics->bufferUsage);
1148  }
1149 
1150  ExplainCloseGroup("Serialization", "Serialization", true, es);
1151 }
1152 
1153 /*
1154  * ExplainQueryText -
1155  * add a "Query Text" node that contains the actual text of the query
1156  *
1157  * The caller should have set up the options fields of *es, as well as
1158  * initializing the output buffer es->str.
1159  *
1160  */
1161 void
1163 {
1164  if (queryDesc->sourceText)
1165  ExplainPropertyText("Query Text", queryDesc->sourceText, es);
1166 }
1167 
1168 /*
1169  * ExplainQueryParameters -
1170  * add a "Query Parameters" node that describes the parameters of the query
1171  *
1172  * The caller should have set up the options fields of *es, as well as
1173  * initializing the output buffer es->str.
1174  *
1175  */
1176 void
1178 {
1179  char *str;
1180 
1181  /* This check is consistent with errdetail_params() */
1182  if (params == NULL || params->numParams <= 0 || maxlen == 0)
1183  return;
1184 
1185  str = BuildParamLogString(params, NULL, maxlen);
1186  if (str && str[0] != '\0')
1187  ExplainPropertyText("Query Parameters", str, es);
1188 }
1189 
1190 /*
1191  * report_triggers -
1192  * report execution stats for a single relation's triggers
1193  */
1194 static void
1195 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
1196 {
1197  int nt;
1198 
1199  if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
1200  return;
1201  for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
1202  {
1203  Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
1204  Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
1205  char *relname;
1206  char *conname = NULL;
1207 
1208  /* Must clean up instrumentation state */
1209  InstrEndLoop(instr);
1210 
1211  /*
1212  * We ignore triggers that were never invoked; they likely aren't
1213  * relevant to the current query type.
1214  */
1215  if (instr->ntuples == 0)
1216  continue;
1217 
1218  ExplainOpenGroup("Trigger", NULL, true, es);
1219 
1221  if (OidIsValid(trig->tgconstraint))
1222  conname = get_constraint_name(trig->tgconstraint);
1223 
1224  /*
1225  * In text format, we avoid printing both the trigger name and the
1226  * constraint name unless VERBOSE is specified. In non-text formats
1227  * we just print everything.
1228  */
1229  if (es->format == EXPLAIN_FORMAT_TEXT)
1230  {
1231  if (es->verbose || conname == NULL)
1232  appendStringInfo(es->str, "Trigger %s", trig->tgname);
1233  else
1234  appendStringInfoString(es->str, "Trigger");
1235  if (conname)
1236  appendStringInfo(es->str, " for constraint %s", conname);
1237  if (show_relname)
1238  appendStringInfo(es->str, " on %s", relname);
1239  if (es->timing)
1240  appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
1241  1000.0 * instr->total, instr->ntuples);
1242  else
1243  appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
1244  }
1245  else
1246  {
1247  ExplainPropertyText("Trigger Name", trig->tgname, es);
1248  if (conname)
1249  ExplainPropertyText("Constraint Name", conname, es);
1250  ExplainPropertyText("Relation", relname, es);
1251  if (es->timing)
1252  ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
1253  es);
1254  ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
1255  }
1256 
1257  if (conname)
1258  pfree(conname);
1259 
1260  ExplainCloseGroup("Trigger", NULL, true, es);
1261  }
1262 }
1263 
1264 /* Compute elapsed time in seconds since given timestamp */
1265 static double
1267 {
1268  instr_time endtime;
1269 
1270  INSTR_TIME_SET_CURRENT(endtime);
1271  INSTR_TIME_SUBTRACT(endtime, *starttime);
1272  return INSTR_TIME_GET_DOUBLE(endtime);
1273 }
1274 
1275 /*
1276  * ExplainPreScanNode -
1277  * Prescan the planstate tree to identify which RTEs are referenced
1278  *
1279  * Adds the relid of each referenced RTE to *rels_used. The result controls
1280  * which RTEs are assigned aliases by select_rtable_names_for_explain.
1281  * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
1282  * that never appear in the EXPLAIN output (such as inheritance parents).
1283  */
1284 static bool
1285 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
1286 {
1287  Plan *plan = planstate->plan;
1288 
1289  switch (nodeTag(plan))
1290  {
1291  case T_SeqScan:
1292  case T_SampleScan:
1293  case T_IndexScan:
1294  case T_IndexOnlyScan:
1295  case T_BitmapHeapScan:
1296  case T_TidScan:
1297  case T_TidRangeScan:
1298  case T_SubqueryScan:
1299  case T_FunctionScan:
1300  case T_TableFuncScan:
1301  case T_ValuesScan:
1302  case T_CteScan:
1303  case T_NamedTuplestoreScan:
1304  case T_WorkTableScan:
1305  *rels_used = bms_add_member(*rels_used,
1306  ((Scan *) plan)->scanrelid);
1307  break;
1308  case T_ForeignScan:
1309  *rels_used = bms_add_members(*rels_used,
1310  ((ForeignScan *) plan)->fs_base_relids);
1311  break;
1312  case T_CustomScan:
1313  *rels_used = bms_add_members(*rels_used,
1314  ((CustomScan *) plan)->custom_relids);
1315  break;
1316  case T_ModifyTable:
1317  *rels_used = bms_add_member(*rels_used,
1318  ((ModifyTable *) plan)->nominalRelation);
1319  if (((ModifyTable *) plan)->exclRelRTI)
1320  *rels_used = bms_add_member(*rels_used,
1321  ((ModifyTable *) plan)->exclRelRTI);
1322  break;
1323  case T_Append:
1324  *rels_used = bms_add_members(*rels_used,
1325  ((Append *) plan)->apprelids);
1326  break;
1327  case T_MergeAppend:
1328  *rels_used = bms_add_members(*rels_used,
1329  ((MergeAppend *) plan)->apprelids);
1330  break;
1331  default:
1332  break;
1333  }
1334 
1335  return planstate_tree_walker(planstate, ExplainPreScanNode, rels_used);
1336 }
1337 
1338 /*
1339  * ExplainNode -
1340  * Appends a description of a plan tree to es->str
1341  *
1342  * planstate points to the executor state node for the current plan node.
1343  * We need to work from a PlanState node, not just a Plan node, in order to
1344  * get at the instrumentation data (if any) as well as the list of subplans.
1345  *
1346  * ancestors is a list of parent Plan and SubPlan nodes, most-closely-nested
1347  * first. These are needed in order to interpret PARAM_EXEC Params.
1348  *
1349  * relationship describes the relationship of this plan node to its parent
1350  * (eg, "Outer", "Inner"); it can be null at top level. plan_name is an
1351  * optional name to be attached to the node.
1352  *
1353  * In text format, es->indent is controlled in this function since we only
1354  * want it to change at plan-node boundaries (but a few subroutines will
1355  * transiently increment it). In non-text formats, es->indent corresponds
1356  * to the nesting depth of logical output groups, and therefore is controlled
1357  * by ExplainOpenGroup/ExplainCloseGroup.
1358  */
1359 static void
1360 ExplainNode(PlanState *planstate, List *ancestors,
1361  const char *relationship, const char *plan_name,
1362  ExplainState *es)
1363 {
1364  Plan *plan = planstate->plan;
1365  const char *pname; /* node type name for text output */
1366  const char *sname; /* node type name for non-text output */
1367  const char *strategy = NULL;
1368  const char *partialmode = NULL;
1369  const char *operation = NULL;
1370  const char *custom_name = NULL;
1371  ExplainWorkersState *save_workers_state = es->workers_state;
1372  int save_indent = es->indent;
1373  bool haschildren;
1374 
1375  /*
1376  * Prepare per-worker output buffers, if needed. We'll append the data in
1377  * these to the main output string further down.
1378  */
1379  if (planstate->worker_instrument && es->analyze && !es->hide_workers)
1381  else
1382  es->workers_state = NULL;
1383 
1384  /* Identify plan node type, and print generic details */
1385  switch (nodeTag(plan))
1386  {
1387  case T_Result:
1388  pname = sname = "Result";
1389  break;
1390  case T_ProjectSet:
1391  pname = sname = "ProjectSet";
1392  break;
1393  case T_ModifyTable:
1394  sname = "ModifyTable";
1395  switch (((ModifyTable *) plan)->operation)
1396  {
1397  case CMD_INSERT:
1398  pname = operation = "Insert";
1399  break;
1400  case CMD_UPDATE:
1401  pname = operation = "Update";
1402  break;
1403  case CMD_DELETE:
1404  pname = operation = "Delete";
1405  break;
1406  case CMD_MERGE:
1407  pname = operation = "Merge";
1408  break;
1409  default:
1410  pname = "???";
1411  break;
1412  }
1413  break;
1414  case T_Append:
1415  pname = sname = "Append";
1416  break;
1417  case T_MergeAppend:
1418  pname = sname = "Merge Append";
1419  break;
1420  case T_RecursiveUnion:
1421  pname = sname = "Recursive Union";
1422  break;
1423  case T_BitmapAnd:
1424  pname = sname = "BitmapAnd";
1425  break;
1426  case T_BitmapOr:
1427  pname = sname = "BitmapOr";
1428  break;
1429  case T_NestLoop:
1430  pname = sname = "Nested Loop";
1431  break;
1432  case T_MergeJoin:
1433  pname = "Merge"; /* "Join" gets added by jointype switch */
1434  sname = "Merge Join";
1435  break;
1436  case T_HashJoin:
1437  pname = "Hash"; /* "Join" gets added by jointype switch */
1438  sname = "Hash Join";
1439  break;
1440  case T_SeqScan:
1441  pname = sname = "Seq Scan";
1442  break;
1443  case T_SampleScan:
1444  pname = sname = "Sample Scan";
1445  break;
1446  case T_Gather:
1447  pname = sname = "Gather";
1448  break;
1449  case T_GatherMerge:
1450  pname = sname = "Gather Merge";
1451  break;
1452  case T_IndexScan:
1453  pname = sname = "Index Scan";
1454  break;
1455  case T_IndexOnlyScan:
1456  pname = sname = "Index Only Scan";
1457  break;
1458  case T_BitmapIndexScan:
1459  pname = sname = "Bitmap Index Scan";
1460  break;
1461  case T_BitmapHeapScan:
1462  pname = sname = "Bitmap Heap Scan";
1463  break;
1464  case T_TidScan:
1465  pname = sname = "Tid Scan";
1466  break;
1467  case T_TidRangeScan:
1468  pname = sname = "Tid Range Scan";
1469  break;
1470  case T_SubqueryScan:
1471  pname = sname = "Subquery Scan";
1472  break;
1473  case T_FunctionScan:
1474  pname = sname = "Function Scan";
1475  break;
1476  case T_TableFuncScan:
1477  pname = sname = "Table Function Scan";
1478  break;
1479  case T_ValuesScan:
1480  pname = sname = "Values Scan";
1481  break;
1482  case T_CteScan:
1483  pname = sname = "CTE Scan";
1484  break;
1485  case T_NamedTuplestoreScan:
1486  pname = sname = "Named Tuplestore Scan";
1487  break;
1488  case T_WorkTableScan:
1489  pname = sname = "WorkTable Scan";
1490  break;
1491  case T_ForeignScan:
1492  sname = "Foreign Scan";
1493  switch (((ForeignScan *) plan)->operation)
1494  {
1495  case CMD_SELECT:
1496  pname = "Foreign Scan";
1497  operation = "Select";
1498  break;
1499  case CMD_INSERT:
1500  pname = "Foreign Insert";
1501  operation = "Insert";
1502  break;
1503  case CMD_UPDATE:
1504  pname = "Foreign Update";
1505  operation = "Update";
1506  break;
1507  case CMD_DELETE:
1508  pname = "Foreign Delete";
1509  operation = "Delete";
1510  break;
1511  default:
1512  pname = "???";
1513  break;
1514  }
1515  break;
1516  case T_CustomScan:
1517  sname = "Custom Scan";
1518  custom_name = ((CustomScan *) plan)->methods->CustomName;
1519  if (custom_name)
1520  pname = psprintf("Custom Scan (%s)", custom_name);
1521  else
1522  pname = sname;
1523  break;
1524  case T_Material:
1525  pname = sname = "Materialize";
1526  break;
1527  case T_Memoize:
1528  pname = sname = "Memoize";
1529  break;
1530  case T_Sort:
1531  pname = sname = "Sort";
1532  break;
1533  case T_IncrementalSort:
1534  pname = sname = "Incremental Sort";
1535  break;
1536  case T_Group:
1537  pname = sname = "Group";
1538  break;
1539  case T_Agg:
1540  {
1541  Agg *agg = (Agg *) plan;
1542 
1543  sname = "Aggregate";
1544  switch (agg->aggstrategy)
1545  {
1546  case AGG_PLAIN:
1547  pname = "Aggregate";
1548  strategy = "Plain";
1549  break;
1550  case AGG_SORTED:
1551  pname = "GroupAggregate";
1552  strategy = "Sorted";
1553  break;
1554  case AGG_HASHED:
1555  pname = "HashAggregate";
1556  strategy = "Hashed";
1557  break;
1558  case AGG_MIXED:
1559  pname = "MixedAggregate";
1560  strategy = "Mixed";
1561  break;
1562  default:
1563  pname = "Aggregate ???";
1564  strategy = "???";
1565  break;
1566  }
1567 
1568  if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
1569  {
1570  partialmode = "Partial";
1571  pname = psprintf("%s %s", partialmode, pname);
1572  }
1573  else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
1574  {
1575  partialmode = "Finalize";
1576  pname = psprintf("%s %s", partialmode, pname);
1577  }
1578  else
1579  partialmode = "Simple";
1580  }
1581  break;
1582  case T_WindowAgg:
1583  pname = sname = "WindowAgg";
1584  break;
1585  case T_Unique:
1586  pname = sname = "Unique";
1587  break;
1588  case T_SetOp:
1589  sname = "SetOp";
1590  switch (((SetOp *) plan)->strategy)
1591  {
1592  case SETOP_SORTED:
1593  pname = "SetOp";
1594  strategy = "Sorted";
1595  break;
1596  case SETOP_HASHED:
1597  pname = "HashSetOp";
1598  strategy = "Hashed";
1599  break;
1600  default:
1601  pname = "SetOp ???";
1602  strategy = "???";
1603  break;
1604  }
1605  break;
1606  case T_LockRows:
1607  pname = sname = "LockRows";
1608  break;
1609  case T_Limit:
1610  pname = sname = "Limit";
1611  break;
1612  case T_Hash:
1613  pname = sname = "Hash";
1614  break;
1615  default:
1616  pname = sname = "???";
1617  break;
1618  }
1619 
1620  ExplainOpenGroup("Plan",
1621  relationship ? NULL : "Plan",
1622  true, es);
1623 
1624  if (es->format == EXPLAIN_FORMAT_TEXT)
1625  {
1626  if (plan_name)
1627  {
1628  ExplainIndentText(es);
1629  appendStringInfo(es->str, "%s\n", plan_name);
1630  es->indent++;
1631  }
1632  if (es->indent)
1633  {
1634  ExplainIndentText(es);
1635  appendStringInfoString(es->str, "-> ");
1636  es->indent += 2;
1637  }
1638  if (plan->parallel_aware)
1639  appendStringInfoString(es->str, "Parallel ");
1640  if (plan->async_capable)
1641  appendStringInfoString(es->str, "Async ");
1642  appendStringInfoString(es->str, pname);
1643  es->indent++;
1644  }
1645  else
1646  {
1647  ExplainPropertyText("Node Type", sname, es);
1648  if (strategy)
1649  ExplainPropertyText("Strategy", strategy, es);
1650  if (partialmode)
1651  ExplainPropertyText("Partial Mode", partialmode, es);
1652  if (operation)
1653  ExplainPropertyText("Operation", operation, es);
1654  if (relationship)
1655  ExplainPropertyText("Parent Relationship", relationship, es);
1656  if (plan_name)
1657  ExplainPropertyText("Subplan Name", plan_name, es);
1658  if (custom_name)
1659  ExplainPropertyText("Custom Plan Provider", custom_name, es);
1660  ExplainPropertyBool("Parallel Aware", plan->parallel_aware, es);
1661  ExplainPropertyBool("Async Capable", plan->async_capable, es);
1662  }
1663 
1664  switch (nodeTag(plan))
1665  {
1666  case T_SeqScan:
1667  case T_SampleScan:
1668  case T_BitmapHeapScan:
1669  case T_TidScan:
1670  case T_TidRangeScan:
1671  case T_SubqueryScan:
1672  case T_FunctionScan:
1673  case T_TableFuncScan:
1674  case T_ValuesScan:
1675  case T_CteScan:
1676  case T_WorkTableScan:
1677  ExplainScanTarget((Scan *) plan, es);
1678  break;
1679  case T_ForeignScan:
1680  case T_CustomScan:
1681  if (((Scan *) plan)->scanrelid > 0)
1682  ExplainScanTarget((Scan *) plan, es);
1683  break;
1684  case T_IndexScan:
1685  {
1686  IndexScan *indexscan = (IndexScan *) plan;
1687 
1688  ExplainIndexScanDetails(indexscan->indexid,
1689  indexscan->indexorderdir,
1690  es);
1691  ExplainScanTarget((Scan *) indexscan, es);
1692  }
1693  break;
1694  case T_IndexOnlyScan:
1695  {
1696  IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
1697 
1698  ExplainIndexScanDetails(indexonlyscan->indexid,
1699  indexonlyscan->indexorderdir,
1700  es);
1701  ExplainScanTarget((Scan *) indexonlyscan, es);
1702  }
1703  break;
1704  case T_BitmapIndexScan:
1705  {
1706  BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
1707  const char *indexname =
1708  explain_get_index_name(bitmapindexscan->indexid);
1709 
1710  if (es->format == EXPLAIN_FORMAT_TEXT)
1711  appendStringInfo(es->str, " on %s",
1712  quote_identifier(indexname));
1713  else
1714  ExplainPropertyText("Index Name", indexname, es);
1715  }
1716  break;
1717  case T_ModifyTable:
1719  break;
1720  case T_NestLoop:
1721  case T_MergeJoin:
1722  case T_HashJoin:
1723  {
1724  const char *jointype;
1725 
1726  switch (((Join *) plan)->jointype)
1727  {
1728  case JOIN_INNER:
1729  jointype = "Inner";
1730  break;
1731  case JOIN_LEFT:
1732  jointype = "Left";
1733  break;
1734  case JOIN_FULL:
1735  jointype = "Full";
1736  break;
1737  case JOIN_RIGHT:
1738  jointype = "Right";
1739  break;
1740  case JOIN_SEMI:
1741  jointype = "Semi";
1742  break;
1743  case JOIN_ANTI:
1744  jointype = "Anti";
1745  break;
1746  case JOIN_RIGHT_ANTI:
1747  jointype = "Right Anti";
1748  break;
1749  default:
1750  jointype = "???";
1751  break;
1752  }
1753  if (es->format == EXPLAIN_FORMAT_TEXT)
1754  {
1755  /*
1756  * For historical reasons, the join type is interpolated
1757  * into the node type name...
1758  */
1759  if (((Join *) plan)->jointype != JOIN_INNER)
1760  appendStringInfo(es->str, " %s Join", jointype);
1761  else if (!IsA(plan, NestLoop))
1762  appendStringInfoString(es->str, " Join");
1763  }
1764  else
1765  ExplainPropertyText("Join Type", jointype, es);
1766  }
1767  break;
1768  case T_SetOp:
1769  {
1770  const char *setopcmd;
1771 
1772  switch (((SetOp *) plan)->cmd)
1773  {
1774  case SETOPCMD_INTERSECT:
1775  setopcmd = "Intersect";
1776  break;
1778  setopcmd = "Intersect All";
1779  break;
1780  case SETOPCMD_EXCEPT:
1781  setopcmd = "Except";
1782  break;
1783  case SETOPCMD_EXCEPT_ALL:
1784  setopcmd = "Except All";
1785  break;
1786  default:
1787  setopcmd = "???";
1788  break;
1789  }
1790  if (es->format == EXPLAIN_FORMAT_TEXT)
1791  appendStringInfo(es->str, " %s", setopcmd);
1792  else
1793  ExplainPropertyText("Command", setopcmd, es);
1794  }
1795  break;
1796  default:
1797  break;
1798  }
1799 
1800  if (es->costs)
1801  {
1802  if (es->format == EXPLAIN_FORMAT_TEXT)
1803  {
1804  appendStringInfo(es->str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
1805  plan->startup_cost, plan->total_cost,
1806  plan->plan_rows, plan->plan_width);
1807  }
1808  else
1809  {
1810  ExplainPropertyFloat("Startup Cost", NULL, plan->startup_cost,
1811  2, es);
1812  ExplainPropertyFloat("Total Cost", NULL, plan->total_cost,
1813  2, es);
1814  ExplainPropertyFloat("Plan Rows", NULL, plan->plan_rows,
1815  0, es);
1816  ExplainPropertyInteger("Plan Width", NULL, plan->plan_width,
1817  es);
1818  }
1819  }
1820 
1821  /*
1822  * We have to forcibly clean up the instrumentation state because we
1823  * haven't done ExecutorEnd yet. This is pretty grotty ...
1824  *
1825  * Note: contrib/auto_explain could cause instrumentation to be set up
1826  * even though we didn't ask for it here. Be careful not to print any
1827  * instrumentation results the user didn't ask for. But we do the
1828  * InstrEndLoop call anyway, if possible, to reduce the number of cases
1829  * auto_explain has to contend with.
1830  */
1831  if (planstate->instrument)
1832  InstrEndLoop(planstate->instrument);
1833 
1834  if (es->analyze &&
1835  planstate->instrument && planstate->instrument->nloops > 0)
1836  {
1837  double nloops = planstate->instrument->nloops;
1838  double startup_ms = 1000.0 * planstate->instrument->startup / nloops;
1839  double total_ms = 1000.0 * planstate->instrument->total / nloops;
1840  double rows = planstate->instrument->ntuples / nloops;
1841 
1842  if (es->format == EXPLAIN_FORMAT_TEXT)
1843  {
1844  if (es->timing)
1845  appendStringInfo(es->str,
1846  " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
1847  startup_ms, total_ms, rows, nloops);
1848  else
1849  appendStringInfo(es->str,
1850  " (actual rows=%.0f loops=%.0f)",
1851  rows, nloops);
1852  }
1853  else
1854  {
1855  if (es->timing)
1856  {
1857  ExplainPropertyFloat("Actual Startup Time", "ms", startup_ms,
1858  3, es);
1859  ExplainPropertyFloat("Actual Total Time", "ms", total_ms,
1860  3, es);
1861  }
1862  ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1863  ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1864  }
1865  }
1866  else if (es->analyze)
1867  {
1868  if (es->format == EXPLAIN_FORMAT_TEXT)
1869  appendStringInfoString(es->str, " (never executed)");
1870  else
1871  {
1872  if (es->timing)
1873  {
1874  ExplainPropertyFloat("Actual Startup Time", "ms", 0.0, 3, es);
1875  ExplainPropertyFloat("Actual Total Time", "ms", 0.0, 3, es);
1876  }
1877  ExplainPropertyFloat("Actual Rows", NULL, 0.0, 0, es);
1878  ExplainPropertyFloat("Actual Loops", NULL, 0.0, 0, es);
1879  }
1880  }
1881 
1882  /* in text format, first line ends here */
1883  if (es->format == EXPLAIN_FORMAT_TEXT)
1884  appendStringInfoChar(es->str, '\n');
1885 
1886  /* prepare per-worker general execution details */
1887  if (es->workers_state && es->verbose)
1888  {
1889  WorkerInstrumentation *w = planstate->worker_instrument;
1890 
1891  for (int n = 0; n < w->num_workers; n++)
1892  {
1893  Instrumentation *instrument = &w->instrument[n];
1894  double nloops = instrument->nloops;
1895  double startup_ms;
1896  double total_ms;
1897  double rows;
1898 
1899  if (nloops <= 0)
1900  continue;
1901  startup_ms = 1000.0 * instrument->startup / nloops;
1902  total_ms = 1000.0 * instrument->total / nloops;
1903  rows = instrument->ntuples / nloops;
1904 
1905  ExplainOpenWorker(n, es);
1906 
1907  if (es->format == EXPLAIN_FORMAT_TEXT)
1908  {
1909  ExplainIndentText(es);
1910  if (es->timing)
1911  appendStringInfo(es->str,
1912  "actual time=%.3f..%.3f rows=%.0f loops=%.0f\n",
1913  startup_ms, total_ms, rows, nloops);
1914  else
1915  appendStringInfo(es->str,
1916  "actual rows=%.0f loops=%.0f\n",
1917  rows, nloops);
1918  }
1919  else
1920  {
1921  if (es->timing)
1922  {
1923  ExplainPropertyFloat("Actual Startup Time", "ms",
1924  startup_ms, 3, es);
1925  ExplainPropertyFloat("Actual Total Time", "ms",
1926  total_ms, 3, es);
1927  }
1928  ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
1929  ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
1930  }
1931 
1932  ExplainCloseWorker(n, es);
1933  }
1934  }
1935 
1936  /* target list */
1937  if (es->verbose)
1938  show_plan_tlist(planstate, ancestors, es);
1939 
1940  /* unique join */
1941  switch (nodeTag(plan))
1942  {
1943  case T_NestLoop:
1944  case T_MergeJoin:
1945  case T_HashJoin:
1946  /* try not to be too chatty about this in text mode */
1947  if (es->format != EXPLAIN_FORMAT_TEXT ||
1948  (es->verbose && ((Join *) plan)->inner_unique))
1949  ExplainPropertyBool("Inner Unique",
1950  ((Join *) plan)->inner_unique,
1951  es);
1952  break;
1953  default:
1954  break;
1955  }
1956 
1957  /* quals, sort keys, etc */
1958  switch (nodeTag(plan))
1959  {
1960  case T_IndexScan:
1961  show_scan_qual(((IndexScan *) plan)->indexqualorig,
1962  "Index Cond", planstate, ancestors, es);
1963  if (((IndexScan *) plan)->indexqualorig)
1964  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1965  planstate, es);
1966  show_scan_qual(((IndexScan *) plan)->indexorderbyorig,
1967  "Order By", planstate, ancestors, es);
1968  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1969  if (plan->qual)
1970  show_instrumentation_count("Rows Removed by Filter", 1,
1971  planstate, es);
1972  break;
1973  case T_IndexOnlyScan:
1974  show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
1975  "Index Cond", planstate, ancestors, es);
1976  if (((IndexOnlyScan *) plan)->recheckqual)
1977  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1978  planstate, es);
1979  show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
1980  "Order By", planstate, ancestors, es);
1981  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
1982  if (plan->qual)
1983  show_instrumentation_count("Rows Removed by Filter", 1,
1984  planstate, es);
1985  if (es->analyze)
1986  ExplainPropertyFloat("Heap Fetches", NULL,
1987  planstate->instrument->ntuples2, 0, es);
1988  break;
1989  case T_BitmapIndexScan:
1990  show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
1991  "Index Cond", planstate, ancestors, es);
1992  break;
1993  case T_BitmapHeapScan:
1994  show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
1995  "Recheck Cond", planstate, ancestors, es);
1996  if (((BitmapHeapScan *) plan)->bitmapqualorig)
1997  show_instrumentation_count("Rows Removed by Index Recheck", 2,
1998  planstate, es);
1999  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2000  if (plan->qual)
2001  show_instrumentation_count("Rows Removed by Filter", 1,
2002  planstate, es);
2003  if (es->analyze)
2004  show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
2005  break;
2006  case T_SampleScan:
2007  show_tablesample(((SampleScan *) plan)->tablesample,
2008  planstate, ancestors, es);
2009  /* fall through to print additional fields the same as SeqScan */
2010  /* FALLTHROUGH */
2011  case T_SeqScan:
2012  case T_ValuesScan:
2013  case T_CteScan:
2014  case T_NamedTuplestoreScan:
2015  case T_WorkTableScan:
2016  case T_SubqueryScan:
2017  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2018  if (plan->qual)
2019  show_instrumentation_count("Rows Removed by Filter", 1,
2020  planstate, es);
2021  break;
2022  case T_Gather:
2023  {
2024  Gather *gather = (Gather *) plan;
2025 
2026  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2027  if (plan->qual)
2028  show_instrumentation_count("Rows Removed by Filter", 1,
2029  planstate, es);
2030  ExplainPropertyInteger("Workers Planned", NULL,
2031  gather->num_workers, es);
2032 
2033  if (es->analyze)
2034  {
2035  int nworkers;
2036 
2037  nworkers = ((GatherState *) planstate)->nworkers_launched;
2038  ExplainPropertyInteger("Workers Launched", NULL,
2039  nworkers, es);
2040  }
2041 
2042  if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
2043  ExplainPropertyBool("Single Copy", gather->single_copy, es);
2044  }
2045  break;
2046  case T_GatherMerge:
2047  {
2048  GatherMerge *gm = (GatherMerge *) plan;
2049 
2050  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2051  if (plan->qual)
2052  show_instrumentation_count("Rows Removed by Filter", 1,
2053  planstate, es);
2054  ExplainPropertyInteger("Workers Planned", NULL,
2055  gm->num_workers, es);
2056 
2057  if (es->analyze)
2058  {
2059  int nworkers;
2060 
2061  nworkers = ((GatherMergeState *) planstate)->nworkers_launched;
2062  ExplainPropertyInteger("Workers Launched", NULL,
2063  nworkers, es);
2064  }
2065  }
2066  break;
2067  case T_FunctionScan:
2068  if (es->verbose)
2069  {
2070  List *fexprs = NIL;
2071  ListCell *lc;
2072 
2073  foreach(lc, ((FunctionScan *) plan)->functions)
2074  {
2075  RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
2076 
2077  fexprs = lappend(fexprs, rtfunc->funcexpr);
2078  }
2079  /* We rely on show_expression to insert commas as needed */
2080  show_expression((Node *) fexprs,
2081  "Function Call", planstate, ancestors,
2082  es->verbose, es);
2083  }
2084  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2085  if (plan->qual)
2086  show_instrumentation_count("Rows Removed by Filter", 1,
2087  planstate, es);
2088  break;
2089  case T_TableFuncScan:
2090  if (es->verbose)
2091  {
2092  TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
2093 
2094  show_expression((Node *) tablefunc,
2095  "Table Function Call", planstate, ancestors,
2096  es->verbose, es);
2097  }
2098  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2099  if (plan->qual)
2100  show_instrumentation_count("Rows Removed by Filter", 1,
2101  planstate, es);
2102  break;
2103  case T_TidScan:
2104  {
2105  /*
2106  * The tidquals list has OR semantics, so be sure to show it
2107  * as an OR condition.
2108  */
2109  List *tidquals = ((TidScan *) plan)->tidquals;
2110 
2111  if (list_length(tidquals) > 1)
2112  tidquals = list_make1(make_orclause(tidquals));
2113  show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
2114  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2115  if (plan->qual)
2116  show_instrumentation_count("Rows Removed by Filter", 1,
2117  planstate, es);
2118  }
2119  break;
2120  case T_TidRangeScan:
2121  {
2122  /*
2123  * The tidrangequals list has AND semantics, so be sure to
2124  * show it as an AND condition.
2125  */
2126  List *tidquals = ((TidRangeScan *) plan)->tidrangequals;
2127 
2128  if (list_length(tidquals) > 1)
2129  tidquals = list_make1(make_andclause(tidquals));
2130  show_scan_qual(tidquals, "TID Cond", planstate, ancestors, es);
2131  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2132  if (plan->qual)
2133  show_instrumentation_count("Rows Removed by Filter", 1,
2134  planstate, es);
2135  }
2136  break;
2137  case T_ForeignScan:
2138  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2139  if (plan->qual)
2140  show_instrumentation_count("Rows Removed by Filter", 1,
2141  planstate, es);
2142  show_foreignscan_info((ForeignScanState *) planstate, es);
2143  break;
2144  case T_CustomScan:
2145  {
2146  CustomScanState *css = (CustomScanState *) planstate;
2147 
2148  show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
2149  if (plan->qual)
2150  show_instrumentation_count("Rows Removed by Filter", 1,
2151  planstate, es);
2152  if (css->methods->ExplainCustomScan)
2153  css->methods->ExplainCustomScan(css, ancestors, es);
2154  }
2155  break;
2156  case T_NestLoop:
2157  show_upper_qual(((NestLoop *) plan)->join.joinqual,
2158  "Join Filter", planstate, ancestors, es);
2159  if (((NestLoop *) plan)->join.joinqual)
2160  show_instrumentation_count("Rows Removed by Join Filter", 1,
2161  planstate, es);
2162  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2163  if (plan->qual)
2164  show_instrumentation_count("Rows Removed by Filter", 2,
2165  planstate, es);
2166  break;
2167  case T_MergeJoin:
2168  show_upper_qual(((MergeJoin *) plan)->mergeclauses,
2169  "Merge Cond", planstate, ancestors, es);
2170  show_upper_qual(((MergeJoin *) plan)->join.joinqual,
2171  "Join Filter", planstate, ancestors, es);
2172  if (((MergeJoin *) plan)->join.joinqual)
2173  show_instrumentation_count("Rows Removed by Join Filter", 1,
2174  planstate, es);
2175  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2176  if (plan->qual)
2177  show_instrumentation_count("Rows Removed by Filter", 2,
2178  planstate, es);
2179  break;
2180  case T_HashJoin:
2181  show_upper_qual(((HashJoin *) plan)->hashclauses,
2182  "Hash Cond", planstate, ancestors, es);
2183  show_upper_qual(((HashJoin *) plan)->join.joinqual,
2184  "Join Filter", planstate, ancestors, es);
2185  if (((HashJoin *) plan)->join.joinqual)
2186  show_instrumentation_count("Rows Removed by Join Filter", 1,
2187  planstate, es);
2188  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2189  if (plan->qual)
2190  show_instrumentation_count("Rows Removed by Filter", 2,
2191  planstate, es);
2192  break;
2193  case T_Agg:
2194  show_agg_keys(castNode(AggState, planstate), ancestors, es);
2195  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2196  show_hashagg_info((AggState *) planstate, es);
2197  if (plan->qual)
2198  show_instrumentation_count("Rows Removed by Filter", 1,
2199  planstate, es);
2200  break;
2201  case T_WindowAgg:
2202  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2203  if (plan->qual)
2204  show_instrumentation_count("Rows Removed by Filter", 1,
2205  planstate, es);
2206  show_upper_qual(((WindowAgg *) plan)->runConditionOrig,
2207  "Run Condition", planstate, ancestors, es);
2208  break;
2209  case T_Group:
2210  show_group_keys(castNode(GroupState, planstate), ancestors, es);
2211  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2212  if (plan->qual)
2213  show_instrumentation_count("Rows Removed by Filter", 1,
2214  planstate, es);
2215  break;
2216  case T_Sort:
2217  show_sort_keys(castNode(SortState, planstate), ancestors, es);
2218  show_sort_info(castNode(SortState, planstate), es);
2219  break;
2220  case T_IncrementalSort:
2222  ancestors, es);
2224  es);
2225  break;
2226  case T_MergeAppend:
2228  ancestors, es);
2229  break;
2230  case T_Result:
2231  show_upper_qual((List *) ((Result *) plan)->resconstantqual,
2232  "One-Time Filter", planstate, ancestors, es);
2233  show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
2234  if (plan->qual)
2235  show_instrumentation_count("Rows Removed by Filter", 1,
2236  planstate, es);
2237  break;
2238  case T_ModifyTable:
2239  show_modifytable_info(castNode(ModifyTableState, planstate), ancestors,
2240  es);
2241  break;
2242  case T_Hash:
2243  show_hash_info(castNode(HashState, planstate), es);
2244  break;
2245  case T_Memoize:
2246  show_memoize_info(castNode(MemoizeState, planstate), ancestors,
2247  es);
2248  break;
2249  default:
2250  break;
2251  }
2252 
2253  /*
2254  * Prepare per-worker JIT instrumentation. As with the overall JIT
2255  * summary, this is printed only if printing costs is enabled.
2256  */
2257  if (es->workers_state && es->costs && es->verbose)
2258  {
2260 
2261  if (w)
2262  {
2263  for (int n = 0; n < w->num_workers; n++)
2264  {
2265  ExplainOpenWorker(n, es);
2266  ExplainPrintJIT(es, planstate->state->es_jit_flags,
2267  &w->jit_instr[n]);
2268  ExplainCloseWorker(n, es);
2269  }
2270  }
2271  }
2272 
2273  /* Show buffer/WAL usage */
2274  if (es->buffers && planstate->instrument)
2275  show_buffer_usage(es, &planstate->instrument->bufusage);
2276  if (es->wal && planstate->instrument)
2277  show_wal_usage(es, &planstate->instrument->walusage);
2278 
2279  /* Prepare per-worker buffer/WAL usage */
2280  if (es->workers_state && (es->buffers || es->wal) && es->verbose)
2281  {
2282  WorkerInstrumentation *w = planstate->worker_instrument;
2283 
2284  for (int n = 0; n < w->num_workers; n++)
2285  {
2286  Instrumentation *instrument = &w->instrument[n];
2287  double nloops = instrument->nloops;
2288 
2289  if (nloops <= 0)
2290  continue;
2291 
2292  ExplainOpenWorker(n, es);
2293  if (es->buffers)
2294  show_buffer_usage(es, &instrument->bufusage);
2295  if (es->wal)
2296  show_wal_usage(es, &instrument->walusage);
2297  ExplainCloseWorker(n, es);
2298  }
2299  }
2300 
2301  /* Show per-worker details for this plan node, then pop that stack */
2302  if (es->workers_state)
2304  es->workers_state = save_workers_state;
2305 
2306  /*
2307  * If partition pruning was done during executor initialization, the
2308  * number of child plans we'll display below will be less than the number
2309  * of subplans that was specified in the plan. To make this a bit less
2310  * mysterious, emit an indication that this happened. Note that this
2311  * field is emitted now because we want it to be a property of the parent
2312  * node; it *cannot* be emitted within the Plans sub-node we'll open next.
2313  */
2314  switch (nodeTag(plan))
2315  {
2316  case T_Append:
2317  ExplainMissingMembers(((AppendState *) planstate)->as_nplans,
2318  list_length(((Append *) plan)->appendplans),
2319  es);
2320  break;
2321  case T_MergeAppend:
2322  ExplainMissingMembers(((MergeAppendState *) planstate)->ms_nplans,
2323  list_length(((MergeAppend *) plan)->mergeplans),
2324  es);
2325  break;
2326  default:
2327  break;
2328  }
2329 
2330  /* Get ready to display the child plans */
2331  haschildren = planstate->initPlan ||
2332  outerPlanState(planstate) ||
2333  innerPlanState(planstate) ||
2334  IsA(plan, Append) ||
2335  IsA(plan, MergeAppend) ||
2336  IsA(plan, BitmapAnd) ||
2337  IsA(plan, BitmapOr) ||
2338  IsA(plan, SubqueryScan) ||
2339  (IsA(planstate, CustomScanState) &&
2340  ((CustomScanState *) planstate)->custom_ps != NIL) ||
2341  planstate->subPlan;
2342  if (haschildren)
2343  {
2344  ExplainOpenGroup("Plans", "Plans", false, es);
2345  /* Pass current Plan as head of ancestors list for children */
2346  ancestors = lcons(plan, ancestors);
2347  }
2348 
2349  /* initPlan-s */
2350  if (planstate->initPlan)
2351  ExplainSubPlans(planstate->initPlan, ancestors, "InitPlan", es);
2352 
2353  /* lefttree */
2354  if (outerPlanState(planstate))
2355  ExplainNode(outerPlanState(planstate), ancestors,
2356  "Outer", NULL, es);
2357 
2358  /* righttree */
2359  if (innerPlanState(planstate))
2360  ExplainNode(innerPlanState(planstate), ancestors,
2361  "Inner", NULL, es);
2362 
2363  /* special child plans */
2364  switch (nodeTag(plan))
2365  {
2366  case T_Append:
2367  ExplainMemberNodes(((AppendState *) planstate)->appendplans,
2368  ((AppendState *) planstate)->as_nplans,
2369  ancestors, es);
2370  break;
2371  case T_MergeAppend:
2372  ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
2373  ((MergeAppendState *) planstate)->ms_nplans,
2374  ancestors, es);
2375  break;
2376  case T_BitmapAnd:
2377  ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
2378  ((BitmapAndState *) planstate)->nplans,
2379  ancestors, es);
2380  break;
2381  case T_BitmapOr:
2382  ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
2383  ((BitmapOrState *) planstate)->nplans,
2384  ancestors, es);
2385  break;
2386  case T_SubqueryScan:
2387  ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
2388  "Subquery", NULL, es);
2389  break;
2390  case T_CustomScan:
2391  ExplainCustomChildren((CustomScanState *) planstate,
2392  ancestors, es);
2393  break;
2394  default:
2395  break;
2396  }
2397 
2398  /* subPlan-s */
2399  if (planstate->subPlan)
2400  ExplainSubPlans(planstate->subPlan, ancestors, "SubPlan", es);
2401 
2402  /* end of child plans */
2403  if (haschildren)
2404  {
2405  ancestors = list_delete_first(ancestors);
2406  ExplainCloseGroup("Plans", "Plans", false, es);
2407  }
2408 
2409  /* in text format, undo whatever indentation we added */
2410  if (es->format == EXPLAIN_FORMAT_TEXT)
2411  es->indent = save_indent;
2412 
2413  ExplainCloseGroup("Plan",
2414  relationship ? NULL : "Plan",
2415  true, es);
2416 }
2417 
2418 /*
2419  * Show the targetlist of a plan node
2420  */
2421 static void
2422 show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
2423 {
2424  Plan *plan = planstate->plan;
2425  List *context;
2426  List *result = NIL;
2427  bool useprefix;
2428  ListCell *lc;
2429 
2430  /* No work if empty tlist (this occurs eg in bitmap indexscans) */
2431  if (plan->targetlist == NIL)
2432  return;
2433  /* The tlist of an Append isn't real helpful, so suppress it */
2434  if (IsA(plan, Append))
2435  return;
2436  /* Likewise for MergeAppend and RecursiveUnion */
2437  if (IsA(plan, MergeAppend))
2438  return;
2439  if (IsA(plan, RecursiveUnion))
2440  return;
2441 
2442  /*
2443  * Likewise for ForeignScan that executes a direct INSERT/UPDATE/DELETE
2444  *
2445  * Note: the tlist for a ForeignScan that executes a direct INSERT/UPDATE
2446  * might contain subplan output expressions that are confusing in this
2447  * context. The tlist for a ForeignScan that executes a direct UPDATE/
2448  * DELETE always contains "junk" target columns to identify the exact row
2449  * to update or delete, which would be confusing in this context. So, we
2450  * suppress it in all the cases.
2451  */
2452  if (IsA(plan, ForeignScan) &&
2453  ((ForeignScan *) plan)->operation != CMD_SELECT)
2454  return;
2455 
2456  /* Set up deparsing context */
2458  plan,
2459  ancestors);
2460  useprefix = list_length(es->rtable) > 1;
2461 
2462  /* Deparse each result column (we now include resjunk ones) */
2463  foreach(lc, plan->targetlist)
2464  {
2465  TargetEntry *tle = (TargetEntry *) lfirst(lc);
2466 
2467  result = lappend(result,
2468  deparse_expression((Node *) tle->expr, context,
2469  useprefix, false));
2470  }
2471 
2472  /* Print results */
2473  ExplainPropertyList("Output", result, es);
2474 }
2475 
2476 /*
2477  * Show a generic expression
2478  */
2479 static void
2480 show_expression(Node *node, const char *qlabel,
2481  PlanState *planstate, List *ancestors,
2482  bool useprefix, ExplainState *es)
2483 {
2484  List *context;
2485  char *exprstr;
2486 
2487  /* Set up deparsing context */
2489  planstate->plan,
2490  ancestors);
2491 
2492  /* Deparse the expression */
2493  exprstr = deparse_expression(node, context, useprefix, false);
2494 
2495  /* And add to es->str */
2496  ExplainPropertyText(qlabel, exprstr, es);
2497 }
2498 
2499 /*
2500  * Show a qualifier expression (which is a List with implicit AND semantics)
2501  */
2502 static void
2503 show_qual(List *qual, const char *qlabel,
2504  PlanState *planstate, List *ancestors,
2505  bool useprefix, ExplainState *es)
2506 {
2507  Node *node;
2508 
2509  /* No work if empty qual */
2510  if (qual == NIL)
2511  return;
2512 
2513  /* Convert AND list to explicit AND */
2514  node = (Node *) make_ands_explicit(qual);
2515 
2516  /* And show it */
2517  show_expression(node, qlabel, planstate, ancestors, useprefix, es);
2518 }
2519 
2520 /*
2521  * Show a qualifier expression for a scan plan node
2522  */
2523 static void
2524 show_scan_qual(List *qual, const char *qlabel,
2525  PlanState *planstate, List *ancestors,
2526  ExplainState *es)
2527 {
2528  bool useprefix;
2529 
2530  useprefix = (IsA(planstate->plan, SubqueryScan) || es->verbose);
2531  show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2532 }
2533 
2534 /*
2535  * Show a qualifier expression for an upper-level plan node
2536  */
2537 static void
2538 show_upper_qual(List *qual, const char *qlabel,
2539  PlanState *planstate, List *ancestors,
2540  ExplainState *es)
2541 {
2542  bool useprefix;
2543 
2544  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2545  show_qual(qual, qlabel, planstate, ancestors, useprefix, es);
2546 }
2547 
2548 /*
2549  * Show the sort keys for a Sort node.
2550  */
2551 static void
2552 show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
2553 {
2554  Sort *plan = (Sort *) sortstate->ss.ps.plan;
2555 
2556  show_sort_group_keys((PlanState *) sortstate, "Sort Key",
2557  plan->numCols, 0, plan->sortColIdx,
2558  plan->sortOperators, plan->collations,
2559  plan->nullsFirst,
2560  ancestors, es);
2561 }
2562 
2563 /*
2564  * Show the sort keys for an IncrementalSort node.
2565  */
2566 static void
2568  List *ancestors, ExplainState *es)
2569 {
2570  IncrementalSort *plan = (IncrementalSort *) incrsortstate->ss.ps.plan;
2571 
2572  show_sort_group_keys((PlanState *) incrsortstate, "Sort Key",
2573  plan->sort.numCols, plan->nPresortedCols,
2574  plan->sort.sortColIdx,
2575  plan->sort.sortOperators, plan->sort.collations,
2576  plan->sort.nullsFirst,
2577  ancestors, es);
2578 }
2579 
2580 /*
2581  * Likewise, for a MergeAppend node.
2582  */
2583 static void
2585  ExplainState *es)
2586 {
2587  MergeAppend *plan = (MergeAppend *) mstate->ps.plan;
2588 
2589  show_sort_group_keys((PlanState *) mstate, "Sort Key",
2590  plan->numCols, 0, plan->sortColIdx,
2591  plan->sortOperators, plan->collations,
2592  plan->nullsFirst,
2593  ancestors, es);
2594 }
2595 
2596 /*
2597  * Show the grouping keys for an Agg node.
2598  */
2599 static void
2600 show_agg_keys(AggState *astate, List *ancestors,
2601  ExplainState *es)
2602 {
2603  Agg *plan = (Agg *) astate->ss.ps.plan;
2604 
2605  if (plan->numCols > 0 || plan->groupingSets)
2606  {
2607  /* The key columns refer to the tlist of the child plan */
2608  ancestors = lcons(plan, ancestors);
2609 
2610  if (plan->groupingSets)
2611  show_grouping_sets(outerPlanState(astate), plan, ancestors, es);
2612  else
2613  show_sort_group_keys(outerPlanState(astate), "Group Key",
2614  plan->numCols, 0, plan->grpColIdx,
2615  NULL, NULL, NULL,
2616  ancestors, es);
2617 
2618  ancestors = list_delete_first(ancestors);
2619  }
2620 }
2621 
2622 static void
2624  List *ancestors, ExplainState *es)
2625 {
2626  List *context;
2627  bool useprefix;
2628  ListCell *lc;
2629 
2630  /* Set up deparsing context */
2632  planstate->plan,
2633  ancestors);
2634  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2635 
2636  ExplainOpenGroup("Grouping Sets", "Grouping Sets", false, es);
2637 
2638  show_grouping_set_keys(planstate, agg, NULL,
2639  context, useprefix, ancestors, es);
2640 
2641  foreach(lc, agg->chain)
2642  {
2643  Agg *aggnode = lfirst(lc);
2644  Sort *sortnode = (Sort *) aggnode->plan.lefttree;
2645 
2646  show_grouping_set_keys(planstate, aggnode, sortnode,
2647  context, useprefix, ancestors, es);
2648  }
2649 
2650  ExplainCloseGroup("Grouping Sets", "Grouping Sets", false, es);
2651 }
2652 
2653 static void
2655  Agg *aggnode, Sort *sortnode,
2656  List *context, bool useprefix,
2657  List *ancestors, ExplainState *es)
2658 {
2659  Plan *plan = planstate->plan;
2660  char *exprstr;
2661  ListCell *lc;
2662  List *gsets = aggnode->groupingSets;
2663  AttrNumber *keycols = aggnode->grpColIdx;
2664  const char *keyname;
2665  const char *keysetname;
2666 
2667  if (aggnode->aggstrategy == AGG_HASHED || aggnode->aggstrategy == AGG_MIXED)
2668  {
2669  keyname = "Hash Key";
2670  keysetname = "Hash Keys";
2671  }
2672  else
2673  {
2674  keyname = "Group Key";
2675  keysetname = "Group Keys";
2676  }
2677 
2678  ExplainOpenGroup("Grouping Set", NULL, true, es);
2679 
2680  if (sortnode)
2681  {
2682  show_sort_group_keys(planstate, "Sort Key",
2683  sortnode->numCols, 0, sortnode->sortColIdx,
2684  sortnode->sortOperators, sortnode->collations,
2685  sortnode->nullsFirst,
2686  ancestors, es);
2687  if (es->format == EXPLAIN_FORMAT_TEXT)
2688  es->indent++;
2689  }
2690 
2691  ExplainOpenGroup(keysetname, keysetname, false, es);
2692 
2693  foreach(lc, gsets)
2694  {
2695  List *result = NIL;
2696  ListCell *lc2;
2697 
2698  foreach(lc2, (List *) lfirst(lc))
2699  {
2700  Index i = lfirst_int(lc2);
2701  AttrNumber keyresno = keycols[i];
2702  TargetEntry *target = get_tle_by_resno(plan->targetlist,
2703  keyresno);
2704 
2705  if (!target)
2706  elog(ERROR, "no tlist entry for key %d", keyresno);
2707  /* Deparse the expression, showing any top-level cast */
2708  exprstr = deparse_expression((Node *) target->expr, context,
2709  useprefix, true);
2710 
2711  result = lappend(result, exprstr);
2712  }
2713 
2714  if (!result && es->format == EXPLAIN_FORMAT_TEXT)
2715  ExplainPropertyText(keyname, "()", es);
2716  else
2717  ExplainPropertyListNested(keyname, result, es);
2718  }
2719 
2720  ExplainCloseGroup(keysetname, keysetname, false, es);
2721 
2722  if (sortnode && es->format == EXPLAIN_FORMAT_TEXT)
2723  es->indent--;
2724 
2725  ExplainCloseGroup("Grouping Set", NULL, true, es);
2726 }
2727 
2728 /*
2729  * Show the grouping keys for a Group node.
2730  */
2731 static void
2732 show_group_keys(GroupState *gstate, List *ancestors,
2733  ExplainState *es)
2734 {
2735  Group *plan = (Group *) gstate->ss.ps.plan;
2736 
2737  /* The key columns refer to the tlist of the child plan */
2738  ancestors = lcons(plan, ancestors);
2739  show_sort_group_keys(outerPlanState(gstate), "Group Key",
2740  plan->numCols, 0, plan->grpColIdx,
2741  NULL, NULL, NULL,
2742  ancestors, es);
2743  ancestors = list_delete_first(ancestors);
2744 }
2745 
2746 /*
2747  * Common code to show sort/group keys, which are represented in plan nodes
2748  * as arrays of targetlist indexes. If it's a sort key rather than a group
2749  * key, also pass sort operators/collations/nullsFirst arrays.
2750  */
2751 static void
2752 show_sort_group_keys(PlanState *planstate, const char *qlabel,
2753  int nkeys, int nPresortedKeys, AttrNumber *keycols,
2754  Oid *sortOperators, Oid *collations, bool *nullsFirst,
2755  List *ancestors, ExplainState *es)
2756 {
2757  Plan *plan = planstate->plan;
2758  List *context;
2759  List *result = NIL;
2760  List *resultPresorted = NIL;
2761  StringInfoData sortkeybuf;
2762  bool useprefix;
2763  int keyno;
2764 
2765  if (nkeys <= 0)
2766  return;
2767 
2768  initStringInfo(&sortkeybuf);
2769 
2770  /* Set up deparsing context */
2772  plan,
2773  ancestors);
2774  useprefix = (list_length(es->rtable) > 1 || es->verbose);
2775 
2776  for (keyno = 0; keyno < nkeys; keyno++)
2777  {
2778  /* find key expression in tlist */
2779  AttrNumber keyresno = keycols[keyno];
2780  TargetEntry *target = get_tle_by_resno(plan->targetlist,
2781  keyresno);
2782  char *exprstr;
2783 
2784  if (!target)
2785  elog(ERROR, "no tlist entry for key %d", keyresno);
2786  /* Deparse the expression, showing any top-level cast */
2787  exprstr = deparse_expression((Node *) target->expr, context,
2788  useprefix, true);
2789  resetStringInfo(&sortkeybuf);
2790  appendStringInfoString(&sortkeybuf, exprstr);
2791  /* Append sort order information, if relevant */
2792  if (sortOperators != NULL)
2793  show_sortorder_options(&sortkeybuf,
2794  (Node *) target->expr,
2795  sortOperators[keyno],
2796  collations[keyno],
2797  nullsFirst[keyno]);
2798  /* Emit one property-list item per sort key */
2799  result = lappend(result, pstrdup(sortkeybuf.data));
2800  if (keyno < nPresortedKeys)
2801  resultPresorted = lappend(resultPresorted, exprstr);
2802  }
2803 
2804  ExplainPropertyList(qlabel, result, es);
2805  if (nPresortedKeys > 0)
2806  ExplainPropertyList("Presorted Key", resultPresorted, es);
2807 }
2808 
2809 /*
2810  * Append nondefault characteristics of the sort ordering of a column to buf
2811  * (collation, direction, NULLS FIRST/LAST)
2812  */
2813 static void
2815  Oid sortOperator, Oid collation, bool nullsFirst)
2816 {
2817  Oid sortcoltype = exprType(sortexpr);
2818  bool reverse = false;
2819  TypeCacheEntry *typentry;
2820 
2821  typentry = lookup_type_cache(sortcoltype,
2823 
2824  /*
2825  * Print COLLATE if it's not default for the column's type. There are
2826  * some cases where this is redundant, eg if expression is a column whose
2827  * declared collation is that collation, but it's hard to distinguish that
2828  * here (and arguably, printing COLLATE explicitly is a good idea anyway
2829  * in such cases).
2830  */
2831  if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
2832  {
2833  char *collname = get_collation_name(collation);
2834 
2835  if (collname == NULL)
2836  elog(ERROR, "cache lookup failed for collation %u", collation);
2837  appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
2838  }
2839 
2840  /* Print direction if not ASC, or USING if non-default sort operator */
2841  if (sortOperator == typentry->gt_opr)
2842  {
2843  appendStringInfoString(buf, " DESC");
2844  reverse = true;
2845  }
2846  else if (sortOperator != typentry->lt_opr)
2847  {
2848  char *opname = get_opname(sortOperator);
2849 
2850  if (opname == NULL)
2851  elog(ERROR, "cache lookup failed for operator %u", sortOperator);
2852  appendStringInfo(buf, " USING %s", opname);
2853  /* Determine whether operator would be considered ASC or DESC */
2854  (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
2855  }
2856 
2857  /* Add NULLS FIRST/LAST only if it wouldn't be default */
2858  if (nullsFirst && !reverse)
2859  {
2860  appendStringInfoString(buf, " NULLS FIRST");
2861  }
2862  else if (!nullsFirst && reverse)
2863  {
2864  appendStringInfoString(buf, " NULLS LAST");
2865  }
2866 }
2867 
2868 /*
2869  * Show TABLESAMPLE properties
2870  */
2871 static void
2873  List *ancestors, ExplainState *es)
2874 {
2875  List *context;
2876  bool useprefix;
2877  char *method_name;
2878  List *params = NIL;
2879  char *repeatable;
2880  ListCell *lc;
2881 
2882  /* Set up deparsing context */
2884  planstate->plan,
2885  ancestors);
2886  useprefix = list_length(es->rtable) > 1;
2887 
2888  /* Get the tablesample method name */
2889  method_name = get_func_name(tsc->tsmhandler);
2890 
2891  /* Deparse parameter expressions */
2892  foreach(lc, tsc->args)
2893  {
2894  Node *arg = (Node *) lfirst(lc);
2895 
2896  params = lappend(params,
2898  useprefix, false));
2899  }
2900  if (tsc->repeatable)
2901  repeatable = deparse_expression((Node *) tsc->repeatable, context,
2902  useprefix, false);
2903  else
2904  repeatable = NULL;
2905 
2906  /* Print results */
2907  if (es->format == EXPLAIN_FORMAT_TEXT)
2908  {
2909  bool first = true;
2910 
2911  ExplainIndentText(es);
2912  appendStringInfo(es->str, "Sampling: %s (", method_name);
2913  foreach(lc, params)
2914  {
2915  if (!first)
2916  appendStringInfoString(es->str, ", ");
2917  appendStringInfoString(es->str, (const char *) lfirst(lc));
2918  first = false;
2919  }
2920  appendStringInfoChar(es->str, ')');
2921  if (repeatable)
2922  appendStringInfo(es->str, " REPEATABLE (%s)", repeatable);
2923  appendStringInfoChar(es->str, '\n');
2924  }
2925  else
2926  {
2927  ExplainPropertyText("Sampling Method", method_name, es);
2928  ExplainPropertyList("Sampling Parameters", params, es);
2929  if (repeatable)
2930  ExplainPropertyText("Repeatable Seed", repeatable, es);
2931  }
2932 }
2933 
2934 /*
2935  * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
2936  */
2937 static void
2939 {
2940  if (!es->analyze)
2941  return;
2942 
2943  if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
2944  {
2947  const char *sortMethod;
2948  const char *spaceType;
2949  int64 spaceUsed;
2950 
2951  tuplesort_get_stats(state, &stats);
2952  sortMethod = tuplesort_method_name(stats.sortMethod);
2953  spaceType = tuplesort_space_type_name(stats.spaceType);
2954  spaceUsed = stats.spaceUsed;
2955 
2956  if (es->format == EXPLAIN_FORMAT_TEXT)
2957  {
2958  ExplainIndentText(es);
2959  appendStringInfo(es->str, "Sort Method: %s %s: " INT64_FORMAT "kB\n",
2960  sortMethod, spaceType, spaceUsed);
2961  }
2962  else
2963  {
2964  ExplainPropertyText("Sort Method", sortMethod, es);
2965  ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
2966  ExplainPropertyText("Sort Space Type", spaceType, es);
2967  }
2968  }
2969 
2970  /*
2971  * You might think we should just skip this stanza entirely when
2972  * es->hide_workers is true, but then we'd get no sort-method output at
2973  * all. We have to make it look like worker 0's data is top-level data.
2974  * This is easily done by just skipping the OpenWorker/CloseWorker calls.
2975  * Currently, we don't worry about the possibility that there are multiple
2976  * workers in such a case; if there are, duplicate output fields will be
2977  * emitted.
2978  */
2979  if (sortstate->shared_info != NULL)
2980  {
2981  int n;
2982 
2983  for (n = 0; n < sortstate->shared_info->num_workers; n++)
2984  {
2985  TuplesortInstrumentation *sinstrument;
2986  const char *sortMethod;
2987  const char *spaceType;
2988  int64 spaceUsed;
2989 
2990  sinstrument = &sortstate->shared_info->sinstrument[n];
2991  if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS)
2992  continue; /* ignore any unfilled slots */
2993  sortMethod = tuplesort_method_name(sinstrument->sortMethod);
2994  spaceType = tuplesort_space_type_name(sinstrument->spaceType);
2995  spaceUsed = sinstrument->spaceUsed;
2996 
2997  if (es->workers_state)
2998  ExplainOpenWorker(n, es);
2999 
3000  if (es->format == EXPLAIN_FORMAT_TEXT)
3001  {
3002  ExplainIndentText(es);
3003  appendStringInfo(es->str,
3004  "Sort Method: %s %s: " INT64_FORMAT "kB\n",
3005  sortMethod, spaceType, spaceUsed);
3006  }
3007  else
3008  {
3009  ExplainPropertyText("Sort Method", sortMethod, es);
3010  ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es);
3011  ExplainPropertyText("Sort Space Type", spaceType, es);
3012  }
3013 
3014  if (es->workers_state)
3015  ExplainCloseWorker(n, es);
3016  }
3017  }
3018 }
3019 
3020 /*
3021  * Incremental sort nodes sort in (a potentially very large number of) batches,
3022  * so EXPLAIN ANALYZE needs to roll up the tuplesort stats from each batch into
3023  * an intelligible summary.
3024  *
3025  * This function is used for both a non-parallel node and each worker in a
3026  * parallel incremental sort node.
3027  */
3028 static void
3030  const char *groupLabel, bool indent, ExplainState *es)
3031 {
3032  ListCell *methodCell;
3033  List *methodNames = NIL;
3034 
3035  /* Generate a list of sort methods used across all groups. */
3036  for (int bit = 0; bit < NUM_TUPLESORTMETHODS; bit++)
3037  {
3038  TuplesortMethod sortMethod = (1 << bit);
3039 
3040  if (groupInfo->sortMethods & sortMethod)
3041  {
3042  const char *methodName = tuplesort_method_name(sortMethod);
3043 
3044  methodNames = lappend(methodNames, unconstify(char *, methodName));
3045  }
3046  }
3047 
3048  if (es->format == EXPLAIN_FORMAT_TEXT)
3049  {
3050  if (indent)
3051  appendStringInfoSpaces(es->str, es->indent * 2);
3052  appendStringInfo(es->str, "%s Groups: " INT64_FORMAT " Sort Method", groupLabel,
3053  groupInfo->groupCount);
3054  /* plural/singular based on methodNames size */
3055  if (list_length(methodNames) > 1)
3056  appendStringInfoString(es->str, "s: ");
3057  else
3058  appendStringInfoString(es->str, ": ");
3059  foreach(methodCell, methodNames)
3060  {
3061  appendStringInfoString(es->str, (char *) methodCell->ptr_value);
3062  if (foreach_current_index(methodCell) < list_length(methodNames) - 1)
3063  appendStringInfoString(es->str, ", ");
3064  }
3065 
3066  if (groupInfo->maxMemorySpaceUsed > 0)
3067  {
3068  int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
3069  const char *spaceTypeName;
3070 
3072  appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
3073  spaceTypeName, avgSpace,
3074  spaceTypeName, groupInfo->maxMemorySpaceUsed);
3075  }
3076 
3077  if (groupInfo->maxDiskSpaceUsed > 0)
3078  {
3079  int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
3080 
3081  const char *spaceTypeName;
3082 
3084  appendStringInfo(es->str, " Average %s: " INT64_FORMAT "kB Peak %s: " INT64_FORMAT "kB",
3085  spaceTypeName, avgSpace,
3086  spaceTypeName, groupInfo->maxDiskSpaceUsed);
3087  }
3088  }
3089  else
3090  {
3091  StringInfoData groupName;
3092 
3093  initStringInfo(&groupName);
3094  appendStringInfo(&groupName, "%s Groups", groupLabel);
3095  ExplainOpenGroup("Incremental Sort Groups", groupName.data, true, es);
3096  ExplainPropertyInteger("Group Count", NULL, groupInfo->groupCount, es);
3097 
3098  ExplainPropertyList("Sort Methods Used", methodNames, es);
3099 
3100  if (groupInfo->maxMemorySpaceUsed > 0)
3101  {
3102  int64 avgSpace = groupInfo->totalMemorySpaceUsed / groupInfo->groupCount;
3103  const char *spaceTypeName;
3104  StringInfoData memoryName;
3105 
3107  initStringInfo(&memoryName);
3108  appendStringInfo(&memoryName, "Sort Space %s", spaceTypeName);
3109  ExplainOpenGroup("Sort Space", memoryName.data, true, es);
3110 
3111  ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
3112  ExplainPropertyInteger("Peak Sort Space Used", "kB",
3113  groupInfo->maxMemorySpaceUsed, es);
3114 
3115  ExplainCloseGroup("Sort Space", memoryName.data, true, es);
3116  }
3117  if (groupInfo->maxDiskSpaceUsed > 0)
3118  {
3119  int64 avgSpace = groupInfo->totalDiskSpaceUsed / groupInfo->groupCount;
3120  const char *spaceTypeName;
3121  StringInfoData diskName;
3122 
3124  initStringInfo(&diskName);
3125  appendStringInfo(&diskName, "Sort Space %s", spaceTypeName);
3126  ExplainOpenGroup("Sort Space", diskName.data, true, es);
3127 
3128  ExplainPropertyInteger("Average Sort Space Used", "kB", avgSpace, es);
3129  ExplainPropertyInteger("Peak Sort Space Used", "kB",
3130  groupInfo->maxDiskSpaceUsed, es);
3131 
3132  ExplainCloseGroup("Sort Space", diskName.data, true, es);
3133  }
3134 
3135  ExplainCloseGroup("Incremental Sort Groups", groupName.data, true, es);
3136  }
3137 }
3138 
3139 /*
3140  * If it's EXPLAIN ANALYZE, show tuplesort stats for an incremental sort node
3141  */
3142 static void
3144  ExplainState *es)
3145 {
3146  IncrementalSortGroupInfo *fullsortGroupInfo;
3147  IncrementalSortGroupInfo *prefixsortGroupInfo;
3148 
3149  fullsortGroupInfo = &incrsortstate->incsort_info.fullsortGroupInfo;
3150 
3151  if (!es->analyze)
3152  return;
3153 
3154  /*
3155  * Since we never have any prefix groups unless we've first sorted a full
3156  * groups and transitioned modes (copying the tuples into a prefix group),
3157  * we don't need to do anything if there were 0 full groups.
3158  *
3159  * We still have to continue after this block if there are no full groups,
3160  * though, since it's possible that we have workers that did real work
3161  * even if the leader didn't participate.
3162  */
3163  if (fullsortGroupInfo->groupCount > 0)
3164  {
3165  show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort", true, es);
3166  prefixsortGroupInfo = &incrsortstate->incsort_info.prefixsortGroupInfo;
3167  if (prefixsortGroupInfo->groupCount > 0)
3168  {
3169  if (es->format == EXPLAIN_FORMAT_TEXT)
3170  appendStringInfoChar(es->str, '\n');
3171  show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
3172  }
3173  if (es->format == EXPLAIN_FORMAT_TEXT)
3174  appendStringInfoChar(es->str, '\n');
3175  }
3176 
3177  if (incrsortstate->shared_info != NULL)
3178  {
3179  int n;
3180  bool indent_first_line;
3181 
3182  for (n = 0; n < incrsortstate->shared_info->num_workers; n++)
3183  {
3184  IncrementalSortInfo *incsort_info =
3185  &incrsortstate->shared_info->sinfo[n];
3186 
3187  /*
3188  * If a worker hasn't processed any sort groups at all, then
3189  * exclude it from output since it either didn't launch or didn't
3190  * contribute anything meaningful.
3191  */
3192  fullsortGroupInfo = &incsort_info->fullsortGroupInfo;
3193 
3194  /*
3195  * Since we never have any prefix groups unless we've first sorted
3196  * a full groups and transitioned modes (copying the tuples into a
3197  * prefix group), we don't need to do anything if there were 0
3198  * full groups.
3199  */
3200  if (fullsortGroupInfo->groupCount == 0)
3201  continue;
3202 
3203  if (es->workers_state)
3204  ExplainOpenWorker(n, es);
3205 
3206  indent_first_line = es->workers_state == NULL || es->verbose;
3207  show_incremental_sort_group_info(fullsortGroupInfo, "Full-sort",
3208  indent_first_line, es);
3209  prefixsortGroupInfo = &incsort_info->prefixsortGroupInfo;
3210  if (prefixsortGroupInfo->groupCount > 0)
3211  {
3212  if (es->format == EXPLAIN_FORMAT_TEXT)
3213  appendStringInfoChar(es->str, '\n');
3214  show_incremental_sort_group_info(prefixsortGroupInfo, "Pre-sorted", true, es);
3215  }
3216  if (es->format == EXPLAIN_FORMAT_TEXT)
3217  appendStringInfoChar(es->str, '\n');
3218 
3219  if (es->workers_state)
3220  ExplainCloseWorker(n, es);
3221  }
3222  }
3223 }
3224 
3225 /*
3226  * Show information on hash buckets/batches.
3227  */
3228 static void
3230 {
3231  HashInstrumentation hinstrument = {0};
3232 
3233  /*
3234  * Collect stats from the local process, even when it's a parallel query.
3235  * In a parallel query, the leader process may or may not have run the
3236  * hash join, and even if it did it may not have built a hash table due to
3237  * timing (if it started late it might have seen no tuples in the outer
3238  * relation and skipped building the hash table). Therefore we have to be
3239  * prepared to get instrumentation data from all participants.
3240  */
3241  if (hashstate->hinstrument)
3242  memcpy(&hinstrument, hashstate->hinstrument,
3243  sizeof(HashInstrumentation));
3244 
3245  /*
3246  * Merge results from workers. In the parallel-oblivious case, the
3247  * results from all participants should be identical, except where
3248  * participants didn't run the join at all so have no data. In the
3249  * parallel-aware case, we need to consider all the results. Each worker
3250  * may have seen a different subset of batches and we want to report the
3251  * highest memory usage across all batches. We take the maxima of other
3252  * values too, for the same reasons as in ExecHashAccumInstrumentation.
3253  */
3254  if (hashstate->shared_info)
3255  {
3256  SharedHashInfo *shared_info = hashstate->shared_info;
3257  int i;
3258 
3259  for (i = 0; i < shared_info->num_workers; ++i)
3260  {
3261  HashInstrumentation *worker_hi = &shared_info->hinstrument[i];
3262 
3263  hinstrument.nbuckets = Max(hinstrument.nbuckets,
3264  worker_hi->nbuckets);
3265  hinstrument.nbuckets_original = Max(hinstrument.nbuckets_original,
3266  worker_hi->nbuckets_original);
3267  hinstrument.nbatch = Max(hinstrument.nbatch,
3268  worker_hi->nbatch);
3269  hinstrument.nbatch_original = Max(hinstrument.nbatch_original,
3270  worker_hi->nbatch_original);
3271  hinstrument.space_peak = Max(hinstrument.space_peak,
3272  worker_hi->space_peak);
3273  }
3274  }
3275 
3276  if (hinstrument.nbatch > 0)
3277  {
3278  long spacePeakKb = (hinstrument.space_peak + 1023) / 1024;
3279 
3280  if (es->format != EXPLAIN_FORMAT_TEXT)
3281  {
3282  ExplainPropertyInteger("Hash Buckets", NULL,
3283  hinstrument.nbuckets, es);
3284  ExplainPropertyInteger("Original Hash Buckets", NULL,
3285  hinstrument.nbuckets_original, es);
3286  ExplainPropertyInteger("Hash Batches", NULL,
3287  hinstrument.nbatch, es);
3288  ExplainPropertyInteger("Original Hash Batches", NULL,
3289  hinstrument.nbatch_original, es);
3290  ExplainPropertyInteger("Peak Memory Usage", "kB",
3291  spacePeakKb, es);
3292  }
3293  else if (hinstrument.nbatch_original != hinstrument.nbatch ||
3294  hinstrument.nbuckets_original != hinstrument.nbuckets)
3295  {
3296  ExplainIndentText(es);
3297  appendStringInfo(es->str,
3298  "Buckets: %d (originally %d) Batches: %d (originally %d) Memory Usage: %ldkB\n",
3299  hinstrument.nbuckets,
3300  hinstrument.nbuckets_original,
3301  hinstrument.nbatch,
3302  hinstrument.nbatch_original,
3303  spacePeakKb);
3304  }
3305  else
3306  {
3307  ExplainIndentText(es);
3308  appendStringInfo(es->str,
3309  "Buckets: %d Batches: %d Memory Usage: %ldkB\n",
3310  hinstrument.nbuckets, hinstrument.nbatch,
3311  spacePeakKb);
3312  }
3313  }
3314 }
3315 
3316 /*
3317  * Show information on memoize hits/misses/evictions and memory usage.
3318  */
3319 static void
3321 {
3322  Plan *plan = ((PlanState *) mstate)->plan;
3323  ListCell *lc;
3324  List *context;
3325  StringInfoData keystr;
3326  char *separator = "";
3327  bool useprefix;
3328  int64 memPeakKb;
3329 
3330  initStringInfo(&keystr);
3331 
3332  /*
3333  * It's hard to imagine having a memoize node with fewer than 2 RTEs, but
3334  * let's just keep the same useprefix logic as elsewhere in this file.
3335  */
3336  useprefix = list_length(es->rtable) > 1 || es->verbose;
3337 
3338  /* Set up deparsing context */
3340  plan,
3341  ancestors);
3342 
3343  foreach(lc, ((Memoize *) plan)->param_exprs)
3344  {
3345  Node *expr = (Node *) lfirst(lc);
3346 
3348 
3350  useprefix, false));
3351  separator = ", ";
3352  }
3353 
3354  if (es->format != EXPLAIN_FORMAT_TEXT)
3355  {
3356  ExplainPropertyText("Cache Key", keystr.data, es);
3357  ExplainPropertyText("Cache Mode", mstate->binary_mode ? "binary" : "logical", es);
3358  }
3359  else
3360  {
3361  ExplainIndentText(es);
3362  appendStringInfo(es->str, "Cache Key: %s\n", keystr.data);
3363  ExplainIndentText(es);
3364  appendStringInfo(es->str, "Cache Mode: %s\n", mstate->binary_mode ? "binary" : "logical");
3365  }
3366 
3367  pfree(keystr.data);
3368 
3369  if (!es->analyze)
3370  return;
3371 
3372  if (mstate->stats.cache_misses > 0)
3373  {
3374  /*
3375  * mem_peak is only set when we freed memory, so we must use mem_used
3376  * when mem_peak is 0.
3377  */
3378  if (mstate->stats.mem_peak > 0)
3379  memPeakKb = (mstate->stats.mem_peak + 1023) / 1024;
3380  else
3381  memPeakKb = (mstate->mem_used + 1023) / 1024;
3382 
3383  if (es->format != EXPLAIN_FORMAT_TEXT)
3384  {
3385  ExplainPropertyInteger("Cache Hits", NULL, mstate->stats.cache_hits, es);
3386  ExplainPropertyInteger("Cache Misses", NULL, mstate->stats.cache_misses, es);
3387  ExplainPropertyInteger("Cache Evictions", NULL, mstate->stats.cache_evictions, es);
3388  ExplainPropertyInteger("Cache Overflows", NULL, mstate->stats.cache_overflows, es);
3389  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3390  }
3391  else
3392  {
3393  ExplainIndentText(es);
3394  appendStringInfo(es->str,
3395  "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3396  mstate->stats.cache_hits,
3397  mstate->stats.cache_misses,
3398  mstate->stats.cache_evictions,
3399  mstate->stats.cache_overflows,
3400  memPeakKb);
3401  }
3402  }
3403 
3404  if (mstate->shared_info == NULL)
3405  return;
3406 
3407  /* Show details from parallel workers */
3408  for (int n = 0; n < mstate->shared_info->num_workers; n++)
3409  {
3411 
3412  si = &mstate->shared_info->sinstrument[n];
3413 
3414  /*
3415  * Skip workers that didn't do any work. We needn't bother checking
3416  * for cache hits as a miss will always occur before a cache hit.
3417  */
3418  if (si->cache_misses == 0)
3419  continue;
3420 
3421  if (es->workers_state)
3422  ExplainOpenWorker(n, es);
3423 
3424  /*
3425  * Since the worker's MemoizeState.mem_used field is unavailable to
3426  * us, ExecEndMemoize will have set the
3427  * MemoizeInstrumentation.mem_peak field for us. No need to do the
3428  * zero checks like we did for the serial case above.
3429  */
3430  memPeakKb = (si->mem_peak + 1023) / 1024;
3431 
3432  if (es->format == EXPLAIN_FORMAT_TEXT)
3433  {
3434  ExplainIndentText(es);
3435  appendStringInfo(es->str,
3436  "Hits: " UINT64_FORMAT " Misses: " UINT64_FORMAT " Evictions: " UINT64_FORMAT " Overflows: " UINT64_FORMAT " Memory Usage: " INT64_FORMAT "kB\n",
3437  si->cache_hits, si->cache_misses,
3439  memPeakKb);
3440  }
3441  else
3442  {
3443  ExplainPropertyInteger("Cache Hits", NULL,
3444  si->cache_hits, es);
3445  ExplainPropertyInteger("Cache Misses", NULL,
3446  si->cache_misses, es);
3447  ExplainPropertyInteger("Cache Evictions", NULL,
3448  si->cache_evictions, es);
3449  ExplainPropertyInteger("Cache Overflows", NULL,
3450  si->cache_overflows, es);
3451  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3452  es);
3453  }
3454 
3455  if (es->workers_state)
3456  ExplainCloseWorker(n, es);
3457  }
3458 }
3459 
3460 /*
3461  * Show information on hash aggregate memory usage and batches.
3462  */
3463 static void
3465 {
3466  Agg *agg = (Agg *) aggstate->ss.ps.plan;
3467  int64 memPeakKb = (aggstate->hash_mem_peak + 1023) / 1024;
3468 
3469  if (agg->aggstrategy != AGG_HASHED &&
3470  agg->aggstrategy != AGG_MIXED)
3471  return;
3472 
3473  if (es->format != EXPLAIN_FORMAT_TEXT)
3474  {
3475  if (es->costs)
3476  ExplainPropertyInteger("Planned Partitions", NULL,
3477  aggstate->hash_planned_partitions, es);
3478 
3479  /*
3480  * During parallel query the leader may have not helped out. We
3481  * detect this by checking how much memory it used. If we find it
3482  * didn't do any work then we don't show its properties.
3483  */
3484  if (es->analyze && aggstate->hash_mem_peak > 0)
3485  {
3486  ExplainPropertyInteger("HashAgg Batches", NULL,
3487  aggstate->hash_batches_used, es);
3488  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb, es);
3489  ExplainPropertyInteger("Disk Usage", "kB",
3490  aggstate->hash_disk_used, es);
3491  }
3492  }
3493  else
3494  {
3495  bool gotone = false;
3496 
3497  if (es->costs && aggstate->hash_planned_partitions > 0)
3498  {
3499  ExplainIndentText(es);
3500  appendStringInfo(es->str, "Planned Partitions: %d",
3501  aggstate->hash_planned_partitions);
3502  gotone = true;
3503  }
3504 
3505  /*
3506  * During parallel query the leader may have not helped out. We
3507  * detect this by checking how much memory it used. If we find it
3508  * didn't do any work then we don't show its properties.
3509  */
3510  if (es->analyze && aggstate->hash_mem_peak > 0)
3511  {
3512  if (!gotone)
3513  ExplainIndentText(es);
3514  else
3515  appendStringInfoSpaces(es->str, 2);
3516 
3517  appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3518  aggstate->hash_batches_used, memPeakKb);
3519  gotone = true;
3520 
3521  /* Only display disk usage if we spilled to disk */
3522  if (aggstate->hash_batches_used > 1)
3523  {
3524  appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3525  aggstate->hash_disk_used);
3526  }
3527  }
3528 
3529  if (gotone)
3530  appendStringInfoChar(es->str, '\n');
3531  }
3532 
3533  /* Display stats for each parallel worker */
3534  if (es->analyze && aggstate->shared_info != NULL)
3535  {
3536  for (int n = 0; n < aggstate->shared_info->num_workers; n++)
3537  {
3538  AggregateInstrumentation *sinstrument;
3539  uint64 hash_disk_used;
3540  int hash_batches_used;
3541 
3542  sinstrument = &aggstate->shared_info->sinstrument[n];
3543  /* Skip workers that didn't do anything */
3544  if (sinstrument->hash_mem_peak == 0)
3545  continue;
3546  hash_disk_used = sinstrument->hash_disk_used;
3547  hash_batches_used = sinstrument->hash_batches_used;
3548  memPeakKb = (sinstrument->hash_mem_peak + 1023) / 1024;
3549 
3550  if (es->workers_state)
3551  ExplainOpenWorker(n, es);
3552 
3553  if (es->format == EXPLAIN_FORMAT_TEXT)
3554  {
3555  ExplainIndentText(es);
3556 
3557  appendStringInfo(es->str, "Batches: %d Memory Usage: " INT64_FORMAT "kB",
3558  hash_batches_used, memPeakKb);
3559 
3560  /* Only display disk usage if we spilled to disk */
3561  if (hash_batches_used > 1)
3562  appendStringInfo(es->str, " Disk Usage: " UINT64_FORMAT "kB",
3563  hash_disk_used);
3564  appendStringInfoChar(es->str, '\n');
3565  }
3566  else
3567  {
3568  ExplainPropertyInteger("HashAgg Batches", NULL,
3569  hash_batches_used, es);
3570  ExplainPropertyInteger("Peak Memory Usage", "kB", memPeakKb,
3571  es);
3572  ExplainPropertyInteger("Disk Usage", "kB", hash_disk_used, es);
3573  }
3574 
3575  if (es->workers_state)
3576  ExplainCloseWorker(n, es);
3577  }
3578  }
3579 }
3580 
3581 /*
3582  * If it's EXPLAIN ANALYZE, show exact/lossy pages for a BitmapHeapScan node
3583  */
3584 static void
3586 {
3587  if (es->format != EXPLAIN_FORMAT_TEXT)
3588  {
3589  ExplainPropertyInteger("Exact Heap Blocks", NULL,
3590  planstate->exact_pages, es);
3591  ExplainPropertyInteger("Lossy Heap Blocks", NULL,
3592  planstate->lossy_pages, es);
3593  }
3594  else
3595  {
3596  if (planstate->exact_pages > 0 || planstate->lossy_pages > 0)
3597  {
3598  ExplainIndentText(es);
3599  appendStringInfoString(es->str, "Heap Blocks:");
3600  if (planstate->exact_pages > 0)
3601  appendStringInfo(es->str, " exact=%ld", planstate->exact_pages);
3602  if (planstate->lossy_pages > 0)
3603  appendStringInfo(es->str, " lossy=%ld", planstate->lossy_pages);
3604  appendStringInfoChar(es->str, '\n');
3605  }
3606  }
3607 }
3608 
3609 /*
3610  * If it's EXPLAIN ANALYZE, show instrumentation information for a plan node
3611  *
3612  * "which" identifies which instrumentation counter to print
3613  */
3614 static void
3615 show_instrumentation_count(const char *qlabel, int which,
3616  PlanState *planstate, ExplainState *es)
3617 {
3618  double nfiltered;
3619  double nloops;
3620 
3621  if (!es->analyze || !planstate->instrument)
3622  return;
3623 
3624  if (which == 2)
3625  nfiltered = planstate->instrument->nfiltered2;
3626  else
3627  nfiltered = planstate->instrument->nfiltered1;
3628  nloops = planstate->instrument->nloops;
3629 
3630  /* In text mode, suppress zero counts; they're not interesting enough */
3631  if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
3632  {
3633  if (nloops > 0)
3634  ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
3635  else
3636  ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
3637  }
3638 }
3639 
3640 /*
3641  * Show extra information for a ForeignScan node.
3642  */
3643 static void
3645 {
3646  FdwRoutine *fdwroutine = fsstate->fdwroutine;
3647 
3648  /* Let the FDW emit whatever fields it wants */
3649  if (((ForeignScan *) fsstate->ss.ps.plan)->operation != CMD_SELECT)
3650  {
3651  if (fdwroutine->ExplainDirectModify != NULL)
3652  fdwroutine->ExplainDirectModify(fsstate, es);
3653  }
3654  else
3655  {
3656  if (fdwroutine->ExplainForeignScan != NULL)
3657  fdwroutine->ExplainForeignScan(fsstate, es);
3658  }
3659 }
3660 
3661 /*
3662  * Fetch the name of an index in an EXPLAIN
3663  *
3664  * We allow plugins to get control here so that plans involving hypothetical
3665  * indexes can be explained.
3666  *
3667  * Note: names returned by this function should be "raw"; the caller will
3668  * apply quoting if needed. Formerly the convention was to do quoting here,
3669  * but we don't want that in non-text output formats.
3670  */
3671 static const char *
3673 {
3674  const char *result;
3675 
3677  result = (*explain_get_index_name_hook) (indexId);
3678  else
3679  result = NULL;
3680  if (result == NULL)
3681  {
3682  /* default behavior: look it up in the catalogs */
3683  result = get_rel_name(indexId);
3684  if (result == NULL)
3685  elog(ERROR, "cache lookup failed for index %u", indexId);
3686  }
3687  return result;
3688 }
3689 
3690 /*
3691  * Return whether show_buffer_usage would have anything to print, if given
3692  * the same 'usage' data. Note that when the format is anything other than
3693  * text, we print even if the counters are all zeroes.
3694  */
3695 static bool
3697 {
3698  bool has_shared;
3699  bool has_local;
3700  bool has_temp;
3701  bool has_shared_timing;
3702  bool has_local_timing;
3703  bool has_temp_timing;
3704 
3705  if (usage == NULL)
3706  return false;
3707 
3708  if (es->format != EXPLAIN_FORMAT_TEXT)
3709  return true;
3710 
3711  has_shared = (usage->shared_blks_hit > 0 ||
3712  usage->shared_blks_read > 0 ||
3713  usage->shared_blks_dirtied > 0 ||
3714  usage->shared_blks_written > 0);
3715  has_local = (usage->local_blks_hit > 0 ||
3716  usage->local_blks_read > 0 ||
3717  usage->local_blks_dirtied > 0 ||
3718  usage->local_blks_written > 0);
3719  has_temp = (usage->temp_blks_read > 0 ||
3720  usage->temp_blks_written > 0);
3721  has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
3722  !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
3723  has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
3724  !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
3725  has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
3726  !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
3727 
3728  return has_shared || has_local || has_temp || has_shared_timing ||
3729  has_local_timing || has_temp_timing;
3730 }
3731 
3732 /*
3733  * Show buffer usage details. This better be sync with peek_buffer_usage.
3734  */
3735 static void
3737 {
3738  if (es->format == EXPLAIN_FORMAT_TEXT)
3739  {
3740  bool has_shared = (usage->shared_blks_hit > 0 ||
3741  usage->shared_blks_read > 0 ||
3742  usage->shared_blks_dirtied > 0 ||
3743  usage->shared_blks_written > 0);
3744  bool has_local = (usage->local_blks_hit > 0 ||
3745  usage->local_blks_read > 0 ||
3746  usage->local_blks_dirtied > 0 ||
3747  usage->local_blks_written > 0);
3748  bool has_temp = (usage->temp_blks_read > 0 ||
3749  usage->temp_blks_written > 0);
3750  bool has_shared_timing = (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time) ||
3751  !INSTR_TIME_IS_ZERO(usage->shared_blk_write_time));
3752  bool has_local_timing = (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time) ||
3753  !INSTR_TIME_IS_ZERO(usage->local_blk_write_time));
3754  bool has_temp_timing = (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time) ||
3755  !INSTR_TIME_IS_ZERO(usage->temp_blk_write_time));
3756 
3757  /* Show only positive counter values. */
3758  if (has_shared || has_local || has_temp)
3759  {
3760  ExplainIndentText(es);
3761  appendStringInfoString(es->str, "Buffers:");
3762 
3763  if (has_shared)
3764  {
3765  appendStringInfoString(es->str, " shared");
3766  if (usage->shared_blks_hit > 0)
3767  appendStringInfo(es->str, " hit=%lld",
3768  (long long) usage->shared_blks_hit);
3769  if (usage->shared_blks_read > 0)
3770  appendStringInfo(es->str, " read=%lld",
3771  (long long) usage->shared_blks_read);
3772  if (usage->shared_blks_dirtied > 0)
3773  appendStringInfo(es->str, " dirtied=%lld",
3774  (long long) usage->shared_blks_dirtied);
3775  if (usage->shared_blks_written > 0)
3776  appendStringInfo(es->str, " written=%lld",
3777  (long long) usage->shared_blks_written);
3778  if (has_local || has_temp)
3779  appendStringInfoChar(es->str, ',');
3780  }
3781  if (has_local)
3782  {
3783  appendStringInfoString(es->str, " local");
3784  if (usage->local_blks_hit > 0)
3785  appendStringInfo(es->str, " hit=%lld",
3786  (long long) usage->local_blks_hit);
3787  if (usage->local_blks_read > 0)
3788  appendStringInfo(es->str, " read=%lld",
3789  (long long) usage->local_blks_read);
3790  if (usage->local_blks_dirtied > 0)
3791  appendStringInfo(es->str, " dirtied=%lld",
3792  (long long) usage->local_blks_dirtied);
3793  if (usage->local_blks_written > 0)
3794  appendStringInfo(es->str, " written=%lld",
3795  (long long) usage->local_blks_written);
3796  if (has_temp)
3797  appendStringInfoChar(es->str, ',');
3798  }
3799  if (has_temp)
3800  {
3801  appendStringInfoString(es->str, " temp");
3802  if (usage->temp_blks_read > 0)
3803  appendStringInfo(es->str, " read=%lld",
3804  (long long) usage->temp_blks_read);
3805  if (usage->temp_blks_written > 0)
3806  appendStringInfo(es->str, " written=%lld",
3807  (long long) usage->temp_blks_written);
3808  }
3809  appendStringInfoChar(es->str, '\n');
3810  }
3811 
3812  /* As above, show only positive counter values. */
3813  if (has_shared_timing || has_local_timing || has_temp_timing)
3814  {
3815  ExplainIndentText(es);
3816  appendStringInfoString(es->str, "I/O Timings:");
3817 
3818  if (has_shared_timing)
3819  {
3820  appendStringInfoString(es->str, " shared");
3821  if (!INSTR_TIME_IS_ZERO(usage->shared_blk_read_time))
3822  appendStringInfo(es->str, " read=%0.3f",
3823  INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time));
3824  if (!INSTR_TIME_IS_ZERO(usage->shared_blk_write_time))
3825  appendStringInfo(es->str, " write=%0.3f",
3826  INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time));
3827  if (has_local_timing || has_temp_timing)
3828  appendStringInfoChar(es->str, ',');
3829  }
3830  if (has_local_timing)
3831  {
3832  appendStringInfoString(es->str, " local");
3833  if (!INSTR_TIME_IS_ZERO(usage->local_blk_read_time))
3834  appendStringInfo(es->str, " read=%0.3f",
3835  INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time));
3836  if (!INSTR_TIME_IS_ZERO(usage->local_blk_write_time))
3837  appendStringInfo(es->str, " write=%0.3f",
3838  INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time));
3839  if (has_temp_timing)
3840  appendStringInfoChar(es->str, ',');
3841  }
3842  if (has_temp_timing)
3843  {
3844  appendStringInfoString(es->str, " temp");
3845  if (!INSTR_TIME_IS_ZERO(usage->temp_blk_read_time))
3846  appendStringInfo(es->str, " read=%0.3f",
3847  INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time));
3848  if (!INSTR_TIME_IS_ZERO(usage->temp_blk_write_time))
3849  appendStringInfo(es->str, " write=%0.3f",
3850  INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time));
3851  }
3852  appendStringInfoChar(es->str, '\n');
3853  }
3854  }
3855  else
3856  {
3857  ExplainPropertyInteger("Shared Hit Blocks", NULL,
3858  usage->shared_blks_hit, es);
3859  ExplainPropertyInteger("Shared Read Blocks", NULL,
3860  usage->shared_blks_read, es);
3861  ExplainPropertyInteger("Shared Dirtied Blocks", NULL,
3862  usage->shared_blks_dirtied, es);
3863  ExplainPropertyInteger("Shared Written Blocks", NULL,
3864  usage->shared_blks_written, es);
3865  ExplainPropertyInteger("Local Hit Blocks", NULL,
3866  usage->local_blks_hit, es);
3867  ExplainPropertyInteger("Local Read Blocks", NULL,
3868  usage->local_blks_read, es);
3869  ExplainPropertyInteger("Local Dirtied Blocks", NULL,
3870  usage->local_blks_dirtied, es);
3871  ExplainPropertyInteger("Local Written Blocks", NULL,
3872  usage->local_blks_written, es);
3873  ExplainPropertyInteger("Temp Read Blocks", NULL,
3874  usage->temp_blks_read, es);
3875  ExplainPropertyInteger("Temp Written Blocks", NULL,
3876  usage->temp_blks_written, es);
3877  if (track_io_timing)
3878  {
3879  ExplainPropertyFloat("Shared I/O Read Time", "ms",
3880  INSTR_TIME_GET_MILLISEC(usage->shared_blk_read_time),
3881  3, es);
3882  ExplainPropertyFloat("Shared I/O Write Time", "ms",
3883  INSTR_TIME_GET_MILLISEC(usage->shared_blk_write_time),
3884  3, es);
3885  ExplainPropertyFloat("Local I/O Read Time", "ms",
3886  INSTR_TIME_GET_MILLISEC(usage->local_blk_read_time),
3887  3, es);
3888  ExplainPropertyFloat("Local I/O Write Time", "ms",
3889  INSTR_TIME_GET_MILLISEC(usage->local_blk_write_time),
3890  3, es);
3891  ExplainPropertyFloat("Temp I/O Read Time", "ms",
3892  INSTR_TIME_GET_MILLISEC(usage->temp_blk_read_time),
3893  3, es);
3894  ExplainPropertyFloat("Temp I/O Write Time", "ms",
3895  INSTR_TIME_GET_MILLISEC(usage->temp_blk_write_time),
3896  3, es);
3897  }
3898  }
3899 }
3900 
3901 /*
3902  * Show WAL usage details.
3903  */
3904 static void
3906 {
3907  if (es->format == EXPLAIN_FORMAT_TEXT)
3908  {
3909  /* Show only positive counter values. */
3910  if ((usage->wal_records > 0) || (usage->wal_fpi > 0) ||
3911  (usage->wal_bytes > 0))
3912  {
3913  ExplainIndentText(es);
3914  appendStringInfoString(es->str, "WAL:");
3915 
3916  if (usage->wal_records > 0)
3917  appendStringInfo(es->str, " records=%lld",
3918  (long long) usage->wal_records);
3919  if (usage->wal_fpi > 0)
3920  appendStringInfo(es->str, " fpi=%lld",
3921  (long long) usage->wal_fpi);
3922  if (usage->wal_bytes > 0)
3923  appendStringInfo(es->str, " bytes=" UINT64_FORMAT,
3924  usage->wal_bytes);
3925  appendStringInfoChar(es->str, '\n');
3926  }
3927  }
3928  else
3929  {
3930  ExplainPropertyInteger("WAL Records", NULL,
3931  usage->wal_records, es);
3932  ExplainPropertyInteger("WAL FPI", NULL,
3933  usage->wal_fpi, es);
3934  ExplainPropertyUInteger("WAL Bytes", NULL,
3935  usage->wal_bytes, es);
3936  }
3937 }
3938 
3939 /*
3940  * Show memory usage details.
3941  */
3942 static void
3944 {
3945  if (es->format == EXPLAIN_FORMAT_TEXT)
3946  {
3947  ExplainIndentText(es);
3948  appendStringInfo(es->str,
3949  "Memory: used=%lld bytes allocated=%lld bytes",
3950  (long long) (mem_counters->totalspace - mem_counters->freespace),
3951  (long long) mem_counters->totalspace);
3952  appendStringInfoChar(es->str, '\n');
3953  }
3954  else
3955  {
3956  ExplainPropertyInteger("Memory Used", "bytes",
3957  mem_counters->totalspace - mem_counters->freespace,
3958  es);
3959  ExplainPropertyInteger("Memory Allocated", "bytes",
3960  mem_counters->totalspace, es);
3961  }
3962 }
3963 
3964 
3965 /*
3966  * Add some additional details about an IndexScan or IndexOnlyScan
3967  */
3968 static void
3970  ExplainState *es)
3971 {
3972  const char *indexname = explain_get_index_name(indexid);
3973 
3974  if (es->format == EXPLAIN_FORMAT_TEXT)
3975  {
3976  if (ScanDirectionIsBackward(indexorderdir))
3977  appendStringInfoString(es->str, " Backward");
3978  appendStringInfo(es->str, " using %s", quote_identifier(indexname));
3979  }
3980  else
3981  {
3982  const char *scandir;
3983 
3984  switch (indexorderdir)
3985  {
3986  case BackwardScanDirection:
3987  scandir = "Backward";
3988  break;
3989  case ForwardScanDirection:
3990  scandir = "Forward";
3991  break;
3992  default:
3993  scandir = "???";
3994  break;
3995  }
3996  ExplainPropertyText("Scan Direction", scandir, es);
3997  ExplainPropertyText("Index Name", indexname, es);
3998  }
3999 }
4000 
4001 /*
4002  * Show the target of a Scan node
4003  */
4004 static void
4006 {
4007  ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
4008 }
4009 
4010 /*
4011  * Show the target of a ModifyTable node
4012  *
4013  * Here we show the nominal target (ie, the relation that was named in the
4014  * original query). If the actual target(s) is/are different, we'll show them
4015  * in show_modifytable_info().
4016  */
4017 static void
4019 {
4020  ExplainTargetRel((Plan *) plan, plan->nominalRelation, es);
4021 }
4022 
4023 /*
4024  * Show the target relation of a scan or modify node
4025  */
4026 static void
4028 {
4029  char *objectname = NULL;
4030  char *namespace = NULL;
4031  const char *objecttag = NULL;
4032  RangeTblEntry *rte;
4033  char *refname;
4034 
4035  rte = rt_fetch(rti, es->rtable);
4036  refname = (char *) list_nth(es->rtable_names, rti - 1);
4037  if (refname == NULL)
4038  refname = rte->eref->aliasname;
4039 
4040  switch (nodeTag(plan))
4041  {
4042  case T_SeqScan:
4043  case T_SampleScan:
4044  case T_IndexScan:
4045  case T_IndexOnlyScan:
4046  case T_BitmapHeapScan:
4047  case T_TidScan:
4048  case T_TidRangeScan:
4049  case T_ForeignScan:
4050  case T_CustomScan:
4051  case T_ModifyTable:
4052  /* Assert it's on a real relation */
4053  Assert(rte->rtekind == RTE_RELATION);
4054  objectname = get_rel_name(rte->relid);
4055  if (es->verbose)
4056  namespace = get_namespace_name_or_temp(get_rel_namespace(rte->relid));
4057  objecttag = "Relation Name";
4058  break;
4059  case T_FunctionScan:
4060  {
4061  FunctionScan *fscan = (FunctionScan *) plan;
4062 
4063  /* Assert it's on a RangeFunction */
4064  Assert(rte->rtekind == RTE_FUNCTION);
4065 
4066  /*
4067  * If the expression is still a function call of a single
4068  * function, we can get the real name of the function.
4069  * Otherwise, punt. (Even if it was a single function call
4070  * originally, the optimizer could have simplified it away.)
4071  */
4072  if (list_length(fscan->functions) == 1)
4073  {
4074  RangeTblFunction *rtfunc = (RangeTblFunction *) linitial(fscan->functions);
4075 
4076  if (IsA(rtfunc->funcexpr, FuncExpr))
4077  {
4078  FuncExpr *funcexpr = (FuncExpr *) rtfunc->funcexpr;
4079  Oid funcid = funcexpr->funcid;
4080 
4081  objectname = get_func_name(funcid);
4082  if (es->verbose)
4083  namespace = get_namespace_name_or_temp(get_func_namespace(funcid));
4084  }
4085  }
4086  objecttag = "Function Name";
4087  }
4088  break;
4089  case T_TableFuncScan:
4090  {
4091  TableFunc *tablefunc = ((TableFuncScan *) plan)->tablefunc;
4092 
4093  Assert(rte->rtekind == RTE_TABLEFUNC);
4094  switch (tablefunc->functype)
4095  {
4096  case TFT_XMLTABLE:
4097  objectname = "xmltable";
4098  break;
4099  case TFT_JSON_TABLE:
4100  objectname = "json_table";
4101  break;
4102  default:
4103  elog(ERROR, "invalid TableFunc type %d",
4104  (int) tablefunc->functype);
4105  }
4106  objecttag = "Table Function Name";
4107  }
4108  break;
4109  case T_ValuesScan:
4110  Assert(rte->rtekind == RTE_VALUES);
4111  break;
4112  case T_CteScan:
4113  /* Assert it's on a non-self-reference CTE */
4114  Assert(rte->rtekind == RTE_CTE);
4115  Assert(!rte->self_reference);
4116  objectname = rte->ctename;
4117  objecttag = "CTE Name";
4118  break;
4119  case T_NamedTuplestoreScan:
4120  Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
4121  objectname = rte->enrname;
4122  objecttag = "Tuplestore Name";
4123  break;
4124  case T_WorkTableScan:
4125  /* Assert it's on a self-reference CTE */
4126  Assert(rte->rtekind == RTE_CTE);
4127  Assert(rte->self_reference);
4128  objectname = rte->ctename;
4129  objecttag = "CTE Name";
4130  break;
4131  default:
4132  break;
4133  }
4134 
4135  if (es->format == EXPLAIN_FORMAT_TEXT)
4136  {
4137  appendStringInfoString(es->str, " on");
4138  if (namespace != NULL)
4139  appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
4140  quote_identifier(objectname));
4141  else if (objectname != NULL)
4142  appendStringInfo(es->str, " %s", quote_identifier(objectname));
4143  if (objectname == NULL || strcmp(refname, objectname) != 0)
4144  appendStringInfo(es->str, " %s", quote_identifier(refname));
4145  }
4146  else
4147  {
4148  if (objecttag != NULL && objectname != NULL)
4149  ExplainPropertyText(objecttag, objectname, es);
4150  if (namespace != NULL)
4151  ExplainPropertyText("Schema", namespace, es);
4152  ExplainPropertyText("Alias", refname, es);
4153  }
4154 }
4155 
4156 /*
4157  * Show extra information for a ModifyTable node
4158  *
4159  * We have three objectives here. First, if there's more than one target
4160  * table or it's different from the nominal target, identify the actual
4161  * target(s). Second, give FDWs a chance to display extra info about foreign
4162  * targets. Third, show information about ON CONFLICT.
4163  */
4164 static void
4166  ExplainState *es)
4167 {
4168  ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
4169  const char *operation;
4170  const char *foperation;
4171  bool labeltargets;
4172  int j;
4173  List *idxNames = NIL;
4174  ListCell *lst;
4175 
4176  switch (node->operation)
4177  {
4178  case CMD_INSERT:
4179  operation = "Insert";
4180  foperation = "Foreign Insert";
4181  break;
4182  case CMD_UPDATE:
4183  operation = "Update";
4184  foperation = "Foreign Update";
4185  break;
4186  case CMD_DELETE:
4187  operation = "Delete";
4188  foperation = "Foreign Delete";
4189  break;
4190  case CMD_MERGE:
4191  operation = "Merge";
4192  /* XXX unsupported for now, but avoid compiler noise */
4193  foperation = "Foreign Merge";
4194  break;
4195  default:
4196  operation = "???";
4197  foperation = "Foreign ???";
4198  break;
4199  }
4200 
4201  /* Should we explicitly label target relations? */
4202  labeltargets = (mtstate->mt_nrels > 1 ||
4203  (mtstate->mt_nrels == 1 &&
4204  mtstate->resultRelInfo[0].ri_RangeTableIndex != node->nominalRelation));
4205 
4206  if (labeltargets)
4207  ExplainOpenGroup("Target Tables", "Target Tables", false, es);
4208 
4209  for (j = 0; j < mtstate->mt_nrels; j++)
4210  {
4211  ResultRelInfo *resultRelInfo = mtstate->resultRelInfo + j;
4212  FdwRoutine *fdwroutine = resultRelInfo->ri_FdwRoutine;
4213 
4214  if (labeltargets)
4215  {
4216  /* Open a group for this target */
4217  ExplainOpenGroup("Target Table", NULL, true, es);
4218 
4219  /*
4220  * In text mode, decorate each target with operation type, so that
4221  * ExplainTargetRel's output of " on foo" will read nicely.
4222  */
4223  if (es->format == EXPLAIN_FORMAT_TEXT)
4224  {
4225  ExplainIndentText(es);
4227  fdwroutine ? foperation : operation);
4228  }
4229 
4230  /* Identify target */
4231  ExplainTargetRel((Plan *) node,
4232  resultRelInfo->ri_RangeTableIndex,
4233  es);
4234 
4235  if (es->format == EXPLAIN_FORMAT_TEXT)
4236  {
4237  appendStringInfoChar(es->str, '\n');
4238  es->indent++;
4239  }
4240  }
4241 
4242  /* Give FDW a chance if needed */
4243  if (!resultRelInfo->ri_usesFdwDirectModify &&
4244  fdwroutine != NULL &&
4245  fdwroutine->ExplainForeignModify != NULL)
4246  {
4247  List *fdw_private = (List *) list_nth(node->fdwPrivLists, j);
4248 
4249  fdwroutine->ExplainForeignModify(mtstate,
4250  resultRelInfo,
4251  fdw_private,
4252  j,
4253  es);
4254  }
4255 
4256  if (labeltargets)
4257  {
4258  /* Undo the indentation we added in text format */
4259  if (es->format == EXPLAIN_FORMAT_TEXT)
4260  es->indent--;
4261 
4262  /* Close the group */
4263  ExplainCloseGroup("Target Table", NULL, true, es);
4264  }
4265  }
4266 
4267  /* Gather names of ON CONFLICT arbiter indexes */
4268  foreach(lst, node->arbiterIndexes)
4269  {
4270  char *indexname = get_rel_name(lfirst_oid(lst));
4271 
4272  idxNames = lappend(idxNames, indexname);
4273  }
4274 
4275  if (node->onConflictAction != ONCONFLICT_NONE)
4276  {
4277  ExplainPropertyText("Conflict Resolution",
4279  "NOTHING" : "UPDATE",
4280  es);
4281 
4282  /*
4283  * Don't display arbiter indexes at all when DO NOTHING variant
4284  * implicitly ignores all conflicts
4285  */
4286  if (idxNames)
4287  ExplainPropertyList("Conflict Arbiter Indexes", idxNames, es);
4288 
4289  /* ON CONFLICT DO UPDATE WHERE qual is specially displayed */
4290  if (node->onConflictWhere)
4291  {
4292  show_upper_qual((List *) node->onConflictWhere, "Conflict Filter",
4293  &mtstate->ps, ancestors, es);
4294  show_instrumentation_count("Rows Removed by Conflict Filter", 1, &mtstate->ps, es);
4295  }
4296 
4297  /* EXPLAIN ANALYZE display of actual outcome for each tuple proposed */
4298  if (es->analyze && mtstate->ps.instrument)
4299  {
4300  double total;
4301  double insert_path;
4302  double other_path;
4303 
4304  InstrEndLoop(outerPlanState(mtstate)->instrument);
4305 
4306  /* count the number of source rows */
4307  total = outerPlanState(mtstate)->instrument->ntuples;
4308  other_path = mtstate->ps.instrument->ntuples2;
4309  insert_path = total - other_path;
4310 
4311  ExplainPropertyFloat("Tuples Inserted", NULL,
4312  insert_path, 0, es);
4313  ExplainPropertyFloat("Conflicting Tuples", NULL,
4314  other_path, 0, es);
4315  }
4316  }
4317  else if (node->operation == CMD_MERGE)
4318  {
4319  /* EXPLAIN ANALYZE display of tuples processed */
4320  if (es->analyze && mtstate->ps.instrument)
4321  {
4322  double total;
4323  double insert_path;
4324  double update_path;
4325  double delete_path;
4326  double skipped_path;
4327 
4328  InstrEndLoop(outerPlanState(mtstate)->instrument);
4329 
4330  /* count the number of source rows */
4331  total = outerPlanState(mtstate)->instrument->ntuples;
4332  insert_path = mtstate->mt_merge_inserted;
4333  update_path = mtstate->mt_merge_updated;
4334  delete_path = mtstate->mt_merge_deleted;
4335  skipped_path = total - insert_path - update_path - delete_path;
4336  Assert(skipped_path >= 0);
4337 
4338  if (es->format == EXPLAIN_FORMAT_TEXT)
4339  {
4340  if (total > 0)
4341  {
4342  ExplainIndentText(es);
4343  appendStringInfoString(es->str, "Tuples:");
4344  if (insert_path > 0)
4345  appendStringInfo(es->str, " inserted=%.0f", insert_path);
4346  if (update_path > 0)
4347  appendStringInfo(es->str, " updated=%.0f", update_path);
4348  if (delete_path > 0)
4349  appendStringInfo(es->str, " deleted=%.0f", delete_path);
4350  if (skipped_path > 0)
4351  appendStringInfo(es->str, " skipped=%.0f", skipped_path);
4352  appendStringInfoChar(es->str, '\n');
4353  }
4354  }
4355  else
4356  {
4357  ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
4358  ExplainPropertyFloat("Tuples Updated", NULL, update_path, 0, es);
4359  ExplainPropertyFloat("Tuples Deleted", NULL, delete_path, 0, es);
4360  ExplainPropertyFloat("Tuples Skipped", NULL, skipped_path, 0, es);
4361  }
4362  }
4363  }
4364 
4365  if (labeltargets)
4366  ExplainCloseGroup("Target Tables", "Target Tables", false, es);
4367 }
4368 
4369 /*
4370  * Explain the constituent plans of an Append, MergeAppend,
4371  * BitmapAnd, or BitmapOr node.
4372  *
4373  * The ancestors list should already contain the immediate parent of these
4374  * plans.
4375  */
4376 static void
4377 ExplainMemberNodes(PlanState **planstates, int nplans,
4378  List *ancestors, ExplainState *es)
4379 {
4380  int j;
4381 
4382  for (j = 0; j < nplans; j++)
4383  ExplainNode(planstates[j], ancestors,
4384  "Member", NULL, es);
4385 }
4386 
4387 /*
4388  * Report about any pruned subnodes of an Append or MergeAppend node.
4389  *
4390  * nplans indicates the number of live subplans.
4391  * nchildren indicates the original number of subnodes in the Plan;
4392  * some of these may have been pruned by the run-time pruning code.
4393  */
4394 static void
4395 ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
4396 {
4397  if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
4398  ExplainPropertyInteger("Subplans Removed", NULL,
4399  nchildren - nplans, es);
4400 }
4401 
4402 /*
4403  * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
4404  *
4405  * The ancestors list should already contain the immediate parent of these
4406  * SubPlans.
4407  */
4408 static void
4409 ExplainSubPlans(List *plans, List *ancestors,
4410  const char *relationship, ExplainState *es)
4411 {
4412  ListCell *lst;
4413 
4414  foreach(lst, plans)
4415  {
4416  SubPlanState *sps = (SubPlanState *) lfirst(lst);
4417  SubPlan *sp = sps->subplan;
4418 
4419  /*
4420  * There can be multiple SubPlan nodes referencing the same physical
4421  * subplan (same plan_id, which is its index in PlannedStmt.subplans).
4422  * We should print a subplan only once, so track which ones we already
4423  * printed. This state must be global across the plan tree, since the
4424  * duplicate nodes could be in different plan nodes, eg both a bitmap
4425  * indexscan's indexqual and its parent heapscan's recheck qual. (We
4426  * do not worry too much about which plan node we show the subplan as
4427  * attached to in such cases.)
4428  */
4429  if (bms_is_member(sp->plan_id, es->printed_subplans))
4430  continue;
4432  sp->plan_id);
4433 
4434  /*
4435  * Treat the SubPlan node as an ancestor of the plan node(s) within
4436  * it, so that ruleutils.c can find the referents of subplan
4437  * parameters.
4438  */
4439  ancestors = lcons(sp, ancestors);
4440 
4441  ExplainNode(sps->planstate, ancestors,
4442  relationship, sp->plan_name, es);
4443 
4444  ancestors = list_delete_first(ancestors);
4445  }
4446 }
4447 
4448 /*
4449  * Explain a list of children of a CustomScan.
4450  */
4451 static void
4453 {
4454  ListCell *cell;
4455  const char *label =
4456  (list_length(css->custom_ps) != 1 ? "children" : "child");
4457 
4458  foreach(cell, css->custom_ps)
4459  ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
4460 }
4461 
4462 /*
4463  * Create a per-plan-node workspace for collecting per-worker data.
4464  *
4465  * Output related to each worker will be temporarily "set aside" into a
4466  * separate buffer, which we'll merge into the main output stream once
4467  * we've processed all data for the plan node. This makes it feasible to
4468  * generate a coherent sub-group of fields for each worker, even though the
4469  * code that produces the fields is in several different places in this file.
4470  * Formatting of such a set-aside field group is managed by
4471  * ExplainOpenSetAsideGroup and ExplainSaveGroup/ExplainRestoreGroup.
4472  */
4473 static ExplainWorkersState *
4475 {
4476  ExplainWorkersState *wstate;
4477 
4478  wstate = (ExplainWorkersState *) palloc(sizeof(ExplainWorkersState));
4479  wstate->num_workers = num_workers;
4480  wstate->worker_inited = (bool *) palloc0(num_workers * sizeof(bool));
4481  wstate->worker_str = (StringInfoData *)
4482  palloc0(num_workers * sizeof(StringInfoData));
4483  wstate->worker_state_save = (int *) palloc(num_workers * sizeof(int));
4484  return wstate;
4485 }
4486 
4487 /*
4488  * Begin or resume output into the set-aside group for worker N.
4489  */
4490 static void
4492 {
4493  ExplainWorkersState *wstate = es->workers_state;
4494 
4495  Assert(wstate);
4496  Assert(n >= 0 && n < wstate->num_workers);
4497 
4498  /* Save prior output buffer pointer */
4499  wstate->prev_str = es->str;
4500 
4501  if (!wstate->worker_inited[n])
4502  {
4503  /* First time through, so create the buffer for this worker */
4504  initStringInfo(&wstate->worker_str[n]);
4505  es->str = &wstate->worker_str[n];
4506 
4507  /*
4508  * Push suitable initial formatting state for this worker's field
4509  * group. We allow one extra logical nesting level, since this group
4510  * will eventually be wrapped in an outer "Workers" group.
4511  */
4512  ExplainOpenSetAsideGroup("Worker", NULL, true, 2, es);
4513 
4514  /*
4515  * In non-TEXT formats we always emit a "Worker Number" field, even if
4516  * there's no other data for this worker.
4517  */
4518  if (es->format != EXPLAIN_FORMAT_TEXT)
4519  ExplainPropertyInteger("Worker Number", NULL, n, es);
4520 
4521  wstate->worker_inited[n] = true;
4522  }
4523  else
4524  {
4525  /* Resuming output for a worker we've already emitted some data for */
4526  es->str = &wstate->worker_str[n];
4527 
4528  /* Restore formatting state saved by last ExplainCloseWorker() */
4529  ExplainRestoreGroup(es, 2, &wstate->worker_state_save[n]);
4530  }
4531 
4532  /*
4533  * In TEXT format, prefix the first output line for this worker with
4534  * "Worker N:". Then, any additional lines should be indented one more
4535  * stop than the "Worker N" line is.
4536  */
4537  if (es->format == EXPLAIN_FORMAT_TEXT)
4538  {
4539  if (es->str->len == 0)
4540  {
4541  ExplainIndentText(es);
4542  appendStringInfo(es->str, "Worker %d: ", n);
4543  }
4544 
4545  es->indent++;
4546  }
4547 }
4548 
4549 /*
4550  * End output for worker N --- must pair with previous ExplainOpenWorker call
4551  */
4552 static void
4554 {
4555  ExplainWorkersState *wstate = es->workers_state;
4556 
4557  Assert(wstate);
4558  Assert(n >= 0 && n < wstate->num_workers);
4559  Assert(wstate->worker_inited[n]);
4560 
4561  /*
4562  * Save formatting state in case we do another ExplainOpenWorker(), then
4563  * pop the formatting stack.
4564  */
4565  ExplainSaveGroup(es, 2, &wstate->worker_state_save[n]);
4566 
4567  /*
4568  * In TEXT format, if we didn't actually produce any output line(s) then
4569  * truncate off the partial line emitted by ExplainOpenWorker. (This is
4570  * to avoid bogus output if, say, show_buffer_usage chooses not to print
4571  * anything for the worker.) Also fix up the indent level.
4572  */
4573  if (es->format == EXPLAIN_FORMAT_TEXT)
4574  {
4575  while (es->str->len > 0 && es->str->data[es->str->len - 1] != '\n')
4576  es->str->data[--(es->str->len)] = '\0';
4577 
4578  es->indent--;
4579  }
4580 
4581  /* Restore prior output buffer pointer */
4582  es->str = wstate->prev_str;
4583 }
4584 
4585 /*
4586  * Print per-worker info for current node, then free the ExplainWorkersState.
4587  */
4588 static void
4590 {
4591  ExplainWorkersState *wstate = es->workers_state;
4592 
4593  ExplainOpenGroup("Workers", "Workers", false, es);
4594  for (int i = 0; i < wstate->num_workers; i++)
4595  {
4596  if (wstate->worker_inited[i])
4597  {
4598  /* This must match previous ExplainOpenSetAsideGroup call */
4599  ExplainOpenGroup("Worker", NULL, true, es);
4600  appendStringInfoString(es->str, wstate->worker_str[i].data);
4601  ExplainCloseGroup("Worker", NULL, true, es);
4602 
4603  pfree(wstate->worker_str[i].data);
4604  }
4605  }
4606  ExplainCloseGroup("Workers", "Workers", false, es);
4607 
4608  pfree(wstate->worker_inited);
4609  pfree(wstate->worker_str);
4610  pfree(wstate->worker_state_save);
4611  pfree(wstate);
4612 }
4613 
4614 /*
4615  * Explain a property, such as sort keys or targets, that takes the form of
4616  * a list of unlabeled items. "data" is a list of C strings.
4617  */
4618 void
4619 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
4620 {
4621  ListCell *lc;
4622  bool first = true;
4623 
4624  switch (es->format)
4625  {
4626  case EXPLAIN_FORMAT_TEXT:
4627  ExplainIndentText(es);
4628  appendStringInfo(es->str, "%s: ", qlabel);
4629  foreach(lc, data)
4630  {
4631  if (!first)
4632  appendStringInfoString(es->str, ", ");
4633  appendStringInfoString(es->str, (const char *) lfirst(lc));
4634  first = false;
4635  }
4636  appendStringInfoChar(es->str, '\n');
4637  break;
4638 
4639  case EXPLAIN_FORMAT_XML:
4640  ExplainXMLTag(qlabel, X_OPENING, es);
4641  foreach(lc, data)
4642  {
4643  char *str;
4644 
4645  appendStringInfoSpaces(es->str, es->indent * 2 + 2);
4646  appendStringInfoString(es->str, "<Item>");
4647  str = escape_xml((const char *) lfirst(lc));
4649  pfree(str);
4650  appendStringInfoString(es->str, "</Item>\n");
4651  }
4652  ExplainXMLTag(qlabel, X_CLOSING, es);
4653  break;
4654 
4655  case EXPLAIN_FORMAT_JSON:
4657  appendStringInfoSpaces(es->str, es->indent * 2);
4658  escape_json(es->str, qlabel);
4659  appendStringInfoString(es->str, ": [");
4660  foreach(lc, data)
4661  {
4662  if (!first)
4663  appendStringInfoString(es->str, ", ");
4664  escape_json(es->str, (const char *) lfirst(lc));
4665  first = false;
4666  }
4667  appendStringInfoChar(es->str, ']');
4668  break;
4669 
4670  case EXPLAIN_FORMAT_YAML:
4672  appendStringInfo(es->str, "%s: ", qlabel);
4673  foreach(lc, data)
4674  {
4675  appendStringInfoChar(es->str, '\n');
4676  appendStringInfoSpaces(es->str, es->indent * 2 + 2);
4677  appendStringInfoString(es->str, "- ");
4678  escape_yaml(es->str, (const char *) lfirst(lc));
4679  }
4680  break;
4681  }
4682 }
4683 
4684 /*
4685  * Explain a property that takes the form of a list of unlabeled items within
4686  * another list. "data" is a list of C strings.
4687  */
4688 void
4690 {
4691  ListCell *lc;
4692  bool first = true;
4693 
4694  switch (es->format)
4695  {
4696  case EXPLAIN_FORMAT_TEXT:
4697  case EXPLAIN_FORMAT_XML:
4698  ExplainPropertyList(qlabel, data, es);
4699  return;
4700 
4701  case EXPLAIN_FORMAT_JSON:
4703  appendStringInfoSpaces(es->str, es->indent * 2);
4704  appendStringInfoChar(es->str, '[');
4705  foreach(lc, data)
4706  {
4707  if (!first)
4708  appendStringInfoString(es->str, ", ");
4709  escape_json(es->str, (const char *) lfirst(lc));
4710  first = false;
4711  }
4712  appendStringInfoChar(es->str, ']');
4713  break;
4714 
4715  case EXPLAIN_FORMAT_YAML:
4717  appendStringInfoString(es->str, "- [");
4718  foreach(lc, data)
4719  {
4720  if (!first)
4721  appendStringInfoString(es->str, ", ");
4722  escape_yaml(es->str, (const char *) lfirst(lc));
4723  first = false;
4724  }
4725  appendStringInfoChar(es->str, ']');
4726  break;
4727  }
4728 }
4729 
4730 /*
4731  * Explain a simple property.
4732  *
4733  * If "numeric" is true, the value is a number (or other value that
4734  * doesn't need quoting in JSON).
4735  *
4736  * If unit is non-NULL the text format will display it after the value.
4737  *
4738  * This usually should not be invoked directly, but via one of the datatype
4739  * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
4740  */
4741 static void
4742 ExplainProperty(const char *qlabel, const char *unit, const char *value,
4743  bool numeric, ExplainState *es)
4744 {
4745  switch (es->format)
4746  {
4747  case EXPLAIN_FORMAT_TEXT:
4748  ExplainIndentText(es);
4749  if (unit)
4750  appendStringInfo(es->str, "%s: %s %s\n", qlabel, value, unit);
4751  else
4752  appendStringInfo(es->str, "%s: %s\n", qlabel, value);
4753  break;
4754 
4755  case EXPLAIN_FORMAT_XML:
4756  {
4757  char *str;
4758 
4759  appendStringInfoSpaces(es->str, es->indent * 2);
4760  ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
4761  str = escape_xml(value);
4763  pfree(str);
4764  ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
4765  appendStringInfoChar(es->str, '\n');
4766  }
4767  break;
4768 
4769  case EXPLAIN_FORMAT_JSON:
4771  appendStringInfoSpaces(es->str, es->indent * 2);
4772  escape_json(es->str, qlabel);
4773  appendStringInfoString(es->str, ": ");
4774  if (numeric)
4776  else
4777  escape_json(es->str, value);
4778  break;
4779 
4780  case EXPLAIN_FORMAT_YAML:
4782  appendStringInfo(es->str, "%s: ", qlabel);
4783  if (numeric)
4785  else
4786  escape_yaml(es->str, value);
4787  break;
4788  }
4789 }
4790 
4791 /*
4792  * Explain a string-valued property.
4793  */
4794 void
4795 ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
4796 {
4797  ExplainProperty(qlabel, NULL, value, false, es);
4798 }
4799 
4800 /*
4801  * Explain an integer-valued property.
4802  */
4803 void
4804 ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value,
4805  ExplainState *es)
4806 {
4807  char buf[32];
4808 
4809  snprintf(buf, sizeof(buf), INT64_FORMAT, value);
4810  ExplainProperty(qlabel, unit, buf, true, es);
4811 }
4812 
4813 /*
4814  * Explain an unsigned integer-valued property.
4815  */
4816 void
4817 ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value,
4818  ExplainState *es)
4819 {
4820  char buf[32];
4821 
4822  snprintf(buf, sizeof(buf), UINT64_FORMAT, value);
4823  ExplainProperty(qlabel, unit, buf, true, es);
4824 }
4825 
4826 /*
4827  * Explain a float-valued property, using the specified number of
4828  * fractional digits.
4829  */
4830 void
4831 ExplainPropertyFloat(const char *qlabel, const char *unit, double value,
4832  int ndigits, ExplainState *es)
4833 {
4834  char *buf;
4835 
4836  buf = psprintf("%.*f", ndigits, value);
4837  ExplainProperty(qlabel, unit, buf, true, es);
4838  pfree(buf);
4839 }
4840 
4841 /*
4842  * Explain a bool-valued property.
4843  */
4844 void
4845 ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
4846 {
4847  ExplainProperty(qlabel, NULL, value ? "true" : "false", true, es);
4848 }
4849 
4850 /*
4851  * Open a group of related objects.
4852  *
4853  * objtype is the type of the group object, labelname is its label within
4854  * a containing object (if any).
4855  *
4856  * If labeled is true, the group members will be labeled properties,
4857  * while if it's false, they'll be unlabeled objects.
4858  */
4859 void
4860 ExplainOpenGroup(const char *objtype, const char *labelname,
4861  bool labeled, ExplainState *es)
4862 {
4863  switch (es->format)
4864  {
4865  case EXPLAIN_FORMAT_TEXT:
4866  /* nothing to do */
4867  break;
4868 
4869  case EXPLAIN_FORMAT_XML:
4870  ExplainXMLTag(objtype, X_OPENING, es);
4871  es->indent++;
4872  break;
4873 
4874  case EXPLAIN_FORMAT_JSON:
4876  appendStringInfoSpaces(es->str, 2 * es->indent);
4877  if (labelname)
4878  {
4879  escape_json(es->str, labelname);
4880  appendStringInfoString(es->str, ": ");
4881  }
4882  appendStringInfoChar(es->str, labeled ? '{' : '[');
4883 
4884  /*
4885  * In JSON format, the grouping_stack is an integer list. 0 means
4886  * we've emitted nothing at this grouping level, 1 means we've
4887  * emitted something (and so the next item needs a comma). See
4888  * ExplainJSONLineEnding().
4889  */
4890  es->grouping_stack = lcons_int(0, es->grouping_stack);
4891  es->indent++;
4892  break;
4893 
4894  case EXPLAIN_FORMAT_YAML:
4895 
4896  /*
4897  * In YAML format, the grouping stack is an integer list. 0 means
4898  * we've emitted nothing at this grouping level AND this grouping
4899  * level is unlabeled and must be marked with "- ". See
4900  * ExplainYAMLLineStarting().
4901  */
4903  if (labelname)
4904  {
4905  appendStringInfo(es->str, "%s: ", labelname);
4906  es->grouping_stack = lcons_int(1, es->grouping_stack);
4907  }
4908  else
4909  {
4910  appendStringInfoString(es->str, "- ");
4911  es->grouping_stack = lcons_int(0, es->grouping_stack);
4912  }
4913  es->indent++;
4914  break;
4915  }
4916 }
4917 
4918 /*
4919  * Close a group of related objects.
4920  * Parameters must match the corresponding ExplainOpenGroup call.
4921  */
4922 void
4923 ExplainCloseGroup(const char *objtype, const char *labelname,
4924  bool labeled, ExplainState *es)
4925 {
4926  switch (es->format)
4927  {
4928  case EXPLAIN_FORMAT_TEXT:
4929  /* nothing to do */
4930  break;
4931 
4932  case EXPLAIN_FORMAT_XML:
4933  es->indent--;
4934  ExplainXMLTag(objtype, X_CLOSING, es);
4935  break;
4936 
4937  case EXPLAIN_FORMAT_JSON:
4938  es->indent--;
4939  appendStringInfoChar(es->str, '\n');
4940  appendStringInfoSpaces(es->str, 2 * es->indent);
4941  appendStringInfoChar(es->str, labeled ? '}' : ']');
4943  break;
4944 
4945  case EXPLAIN_FORMAT_YAML:
4946  es->indent--;
4948  break;
4949  }
4950 }
4951 
4952 /*
4953  * Open a group of related objects, without emitting actual data.
4954  *
4955  * Prepare the formatting state as though we were beginning a group with
4956  * the identified properties, but don't actually emit anything. Output
4957  * subsequent to this call can be redirected into a separate output buffer,
4958  * and then eventually appended to the main output buffer after doing a
4959  * regular ExplainOpenGroup call (with the same parameters).
4960  *
4961  * The extra "depth" parameter is the new group's depth compared to current.
4962  * It could be more than one, in case the eventual output will be enclosed
4963  * in additional nesting group levels. We assume we don't need to track
4964  * formatting state for those levels while preparing this group's output.
4965  *
4966  * There is no ExplainCloseSetAsideGroup --- in current usage, we always
4967  * pop this state with ExplainSaveGroup.
4968  */
4969 static void
4970 ExplainOpenSetAsideGroup(const char *objtype, const char *labelname,
4971  bool labeled, int depth, ExplainState *es)
4972 {
4973  switch (es->format)
4974  {
4975  case EXPLAIN_FORMAT_TEXT:
4976  /* nothing to do */
4977  break;
4978 
4979  case EXPLAIN_FORMAT_XML:
4980  es->indent += depth;
4981  break;
4982 
4983  case EXPLAIN_FORMAT_JSON:
4984  es->grouping_stack = lcons_int(0, es->grouping_stack);
4985  es->indent += depth;
4986  break;
4987 
4988  case EXPLAIN_FORMAT_YAML:
4989  if (labelname)
4990  es->grouping_stack = lcons_int(1, es->grouping_stack);
4991  else
4992  es->grouping_stack = lcons_int(0, es->grouping_stack);
4993  es->indent += depth;
4994  break;
4995  }
4996 }
4997 
4998 /*
4999  * Pop one level of grouping state, allowing for a re-push later.
5000  *
5001  * This is typically used after ExplainOpenSetAsideGroup; pass the
5002  * same "depth" used for that.
5003  *
5004  * This should not emit any output. If state needs to be saved,
5005  * save it at *state_save. Currently, an integer save area is sufficient
5006  * for all formats, but we might need to revisit that someday.
5007  */
5008 static void
5009 ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
5010 {
5011  switch (es->format)
5012  {
5013  case EXPLAIN_FORMAT_TEXT:
5014  /* nothing to do */
5015  break;
5016 
5017  case EXPLAIN_FORMAT_XML:
5018  es->indent -= depth;
5019  break;
5020 
5021  case EXPLAIN_FORMAT_JSON:
5022  es->indent -= depth;
5023  *state_save = linitial_int(es->grouping_stack);
5025  break;
5026 
5027  case EXPLAIN_FORMAT_YAML:
5028  es->indent -= depth;
5029  *state_save = linitial_int(es->grouping_stack);
5031  break;
5032  }
5033 }
5034 
5035 /*
5036  * Re-push one level of grouping state, undoing the effects of ExplainSaveGroup.
5037  */
5038 static void
5039 ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
5040 {
5041  switch (es->format)
5042  {
5043  case EXPLAIN_FORMAT_TEXT:
5044  /* nothing to do */
5045  break;
5046 
5047  case EXPLAIN_FORMAT_XML:
5048  es->indent += depth;
5049  break;
5050 
5051  case EXPLAIN_FORMAT_JSON:
5052  es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
5053  es->indent += depth;
5054  break;
5055 
5056  case EXPLAIN_FORMAT_YAML:
5057  es->grouping_stack = lcons_int(*state_save, es->grouping_stack);
5058  es->indent += depth;
5059  break;
5060  }
5061 }
5062 
5063 /*
5064  * Emit a "dummy" group that never has any members.
5065  *
5066  * objtype is the type of the group object, labelname is its label within
5067  * a containing object (if any).
5068  */
5069 static void
5070 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
5071 {
5072  switch (es->format)
5073  {
5074  case EXPLAIN_FORMAT_TEXT:
5075  /* nothing to do */
5076  break;
5077 
5078  case EXPLAIN_FORMAT_XML:
5079  ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
5080  break;
5081 
5082  case EXPLAIN_FORMAT_JSON:
5084  appendStringInfoSpaces(es->str, 2 * es->indent);
5085  if (labelname)
5086  {
5087  escape_json(es->str, labelname);
5088  appendStringInfoString(es->str, ": ");
5089  }
5090  escape_json(es->str, objtype);
5091  break;
5092 
5093  case EXPLAIN_FORMAT_YAML:
5095  if (labelname)
5096  {
5097  escape_yaml(es->str, labelname);
5098  appendStringInfoString(es->str, ": ");
5099  }
5100  else
5101  {
5102  appendStringInfoString(es->str, "- ");
5103  }
5104  escape_yaml(es->str, objtype);
5105  break;
5106  }
5107 }
5108 
5109 /*
5110  * Emit the start-of-output boilerplate.
5111  *
5112  * This is just enough different from processing a subgroup that we need
5113  * a separate pair of subroutines.
5114  */
5115 void
5117 {
5118  switch (es->format)
5119  {
5120  case EXPLAIN_FORMAT_TEXT:
5121  /* nothing to do */
5122  break;
5123 
5124  case EXPLAIN_FORMAT_XML:
5126  "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
5127  es->indent++;
5128  break;
5129 
5130  case EXPLAIN_FORMAT_JSON:
5131  /* top-level structure is an array of plans */
5132  appendStringInfoChar(es->str, '[');
5133  es->grouping_stack = lcons_int(0, es->grouping_stack);
5134  es->indent++;
5135  break;
5136 
5137  case EXPLAIN_FORMAT_YAML:
5138  es->grouping_stack = lcons_int(0, es->grouping_stack);
5139  break;
5140  }
5141 }
5142 
5143 /*
5144  * Emit the end-of-output boilerplate.
5145  */
5146 void
5148 {
5149  switch (es->format)
5150  {
5151  case EXPLAIN_FORMAT_TEXT:
5152  /* nothing to do */
5153  break;
5154 
5155  case EXPLAIN_FORMAT_XML:
5156  es->indent--;
5157  appendStringInfoString(es->str, "</explain>");
5158  break;
5159 
5160  case EXPLAIN_FORMAT_JSON:
5161  es->indent--;
5162  appendStringInfoString(es->str, "\n]");
5164  break;
5165 
5166  case EXPLAIN_FORMAT_YAML:
5168  break;
5169  }
5170 }
5171 
5172 /*
5173  * Put an appropriate separator between multiple plans
5174  */
5175 void
5177 {
5178  switch (es->format)
5179  {
5180  case EXPLAIN_FORMAT_TEXT:
5181  /* add a blank line */
5182  appendStringInfoChar(es->str, '\n');
5183  break;
5184 
5185  case EXPLAIN_FORMAT_XML:
5186  case EXPLAIN_FORMAT_JSON:
5187  case EXPLAIN_FORMAT_YAML:
5188  /* nothing to do */
5189  break;
5190  }
5191 }
5192 
5193 /*
5194  * Emit opening or closing XML tag.
5195  *
5196  * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
5197  * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
5198  * add.
5199  *
5200  * XML restricts tag names more than our other output formats, eg they can't
5201  * contain white space or slashes. Replace invalid characters with dashes,
5202  * so that for example "I/O Read Time" becomes "I-O-Read-Time".
5203  */
5204 static void
5205 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
5206 {
5207  const char *s;
5208  const char *valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
5209 
5210  if ((flags & X_NOWHITESPACE) == 0)
5211  appendStringInfoSpaces(es->str, 2 * es->indent);
5212  appendStringInfoCharMacro(es->str, '<');
5213  if ((flags & X_CLOSING) != 0)
5214  appendStringInfoCharMacro(es->str, '/');
5215  for (s = tagname; *s; s++)
5216  appendStringInfoChar(es->str, strchr(valid, *s) ? *s : '-');
5217  if ((flags & X_CLOSE_IMMEDIATE) != 0)
5218  appendStringInfoString(es->str, " /");
5219  appendStringInfoCharMacro(es->str, '>');
5220  if ((flags & X_NOWHITESPACE) == 0)
5221  appendStringInfoCharMacro(es->str, '\n');
5222 }
5223 
5224 /*
5225  * Indent a text-format line.
5226  *
5227  * We indent by two spaces per indentation level. However, when emitting
5228  * data for a parallel worker there might already be data on the current line
5229  * (cf. ExplainOpenWorker); in that case, don't indent any more.
5230  */
5231 static void
5233 {
5235  if (es->str->len == 0 || es->str->data[es->str->len - 1] == '\n')
5236  appendStringInfoSpaces(es->str, es->indent * 2);
5237 }
5238 
5239 /*
5240  * Emit a JSON line ending.
5241  *
5242  * JSON requires a comma after each property but the last. To facilitate this,
5243  * in JSON format, the text emitted for each property begins just prior to the
5244  * preceding line-break (and comma, if applicable).
5245  */
5246 static void
5248 {
5250  if (linitial_int(es->grouping_stack) != 0)
5251  appendStringInfoChar(es->str, ',');
5252  else
5253  linitial_int(es->grouping_stack) = 1;
5254  appendStringInfoChar(es->str, '\n');
5255 }
5256 
5257 /*
5258  * Indent a YAML line.
5259  *
5260  * YAML lines are ordinarily indented by two spaces per indentation level.
5261  * The text emitted for each property begins just prior to the preceding
5262  * line-break, except for the first property in an unlabeled group, for which
5263  * it begins immediately after the "- " that introduces the group. The first
5264  * property of the group appears on the same line as the opening "- ".
5265  */
5266 static void
5268 {
5270  if (linitial_int(es->grouping_stack) == 0)
5271  {
5272  linitial_int(es->grouping_stack) = 1;
5273  }
5274  else
5275  {
5276  appendStringInfoChar(es->str, '\n');
5277  appendStringInfoSpaces(es->str, es->indent * 2);
5278  }
5279 }
5280 
5281 /*
5282  * YAML is a superset of JSON; unfortunately, the YAML quoting rules are
5283  * ridiculously complicated -- as documented in sections 5.3 and 7.3.3 of
5284  * http://yaml.org/spec/1.2/spec.html -- so we chose to just quote everything.
5285  * Empty strings, strings with leading or trailing whitespace, and strings
5286  * containing a variety of special characters must certainly be quoted or the
5287  * output is invalid; and other seemingly harmless strings like "0xa" or
5288  * "true" must be quoted, lest they be interpreted as a hexadecimal or Boolean
5289  * constant rather than a string.
5290  */
5291 static void
5293 {
5294  escape_json(buf, str);
5295 }
5296 
5297 
5298 /*
5299  * DestReceiver functions for SERIALIZE option
5300  *
5301  * A DestReceiver for query tuples, that serializes passed rows into RowData
5302  * messages while measuring the resources expended and total serialized size,
5303  * while never sending the data to the client. This allows measuring the
5304  * overhead of deTOASTing and datatype out/sendfuncs, which are not otherwise
5305  * exercisable without actually hitting the network.
5306  */
5308 {
5310  ExplainState *es; /* this EXPLAIN statement's ExplainState */
5311  int8 format; /* text or binary, like pq wire protocol */
5312  TupleDesc attrinfo; /* the output tuple desc */
5313  int nattrs; /* current number of columns */
5314  FmgrInfo *finfos; /* precomputed call info for output fns */
5315  MemoryContext tmpcontext; /* per-row temporary memory context */
5316  StringInfoData buf; /* buffer to hold the constructed message */
5317  SerializeMetrics metrics; /* collected metrics */
5319 
5320 /*
5321  * Get the function lookup info that we'll need for output.
5322  *
5323  * This is a subset of what printtup_prepare_info() does. We don't need to
5324  * cope with format choices varying across columns, so it's slightly simpler.
5325  */
5326 static void
5328  TupleDesc typeinfo, int nattrs)
5329 {
5330  /* get rid of any old data */
5331  if (receiver->finfos)
5332  pfree(receiver->finfos);
5333  receiver->finfos = NULL;
5334 
5335  receiver->attrinfo = typeinfo;
5336  receiver->nattrs = nattrs;
5337  if (nattrs <= 0)
5338  return;
5339 
5340  receiver->finfos = (FmgrInfo *) palloc0(nattrs * sizeof(FmgrInfo));
5341 
5342  for (int i = 0; i < nattrs; i++)
5343  {
5344  FmgrInfo *finfo = receiver->finfos + i;
5345  Form_pg_attribute attr = TupleDescAttr(typeinfo, i);
5346  Oid typoutput;
5347  Oid typsend;
5348  bool typisvarlena;
5349 
5350  if (receiver->format == 0)
5351  {
5352  /* wire protocol format text */
5353  getTypeOutputInfo(attr->atttypid,
5354  &typoutput,
5355  &typisvarlena);
5356  fmgr_info(typoutput, finfo);
5357  }
5358  else if (receiver->format == 1)
5359  {
5360  /* wire protocol format binary */
5361  getTypeBinaryOutputInfo(attr->atttypid,
5362  &typsend,
5363  &typisvarlena);
5364  fmgr_info(typsend, finfo);
5365  }
5366  else
5367  ereport(ERROR,
5368  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5369  errmsg("unsupported format code: %d", receiver->format)));
5370  }
5371 }
5372 
5373 /*
5374  * serializeAnalyzeReceive - collect tuples for EXPLAIN (SERIALIZE)
5375  *
5376  * This should match printtup() in printtup.c as closely as possible,
5377  * except for the addition of measurement code.
5378  */
5379 static bool
5381 {
5382  TupleDesc typeinfo = slot->tts_tupleDescriptor;
5383  SerializeDestReceiver *myState = (SerializeDestReceiver *) self;
5384  MemoryContext oldcontext;
5385  StringInfo buf = &myState->buf;
5386  int natts = typeinfo->natts;
5387  instr_time start,
5388  end;
5389  BufferUsage instr_start;
5390 
5391  /* only measure time, buffers if requested */
5392  if (myState->es->timing)
5394  if (myState->es->buffers)
5395  instr_start = pgBufferUsage;
5396 
5397  /* Set or update my derived attribute info, if needed */
5398  if (myState->attrinfo != typeinfo || myState->nattrs != natts)
5399  serialize_prepare_info(myState, typeinfo, natts);
5400 
5401  /* Make sure the tuple is fully deconstructed */
5402  slot_getallattrs(slot);
5403 
5404  /* Switch into per-row context so we can recover memory below */
5405  oldcontext = MemoryContextSwitchTo(myState->tmpcontext);
5406 
5407  /*
5408  * Prepare a DataRow message (note buffer is in per-query context)
5409  *
5410  * Note that we fill a StringInfo buffer the same as printtup() does, so
5411  * as to capture the costs of manipulating the strings accurately.
5412  */
5413  pq_beginmessage_reuse(buf, 'D');
5414 
5415  pq_sendint16(buf, natts);
5416 
5417  /*
5418  * send the attributes of this tuple
5419  */
5420  for (int i = 0; i < natts; i++)
5421  {
5422  FmgrInfo *finfo = myState->finfos + i;
5423  Datum attr = slot->tts_values[i];
5424 
5425  if (slot->tts_isnull[i])
5426  {
5427  pq_sendint32(buf, -1);
5428  continue;
5429  }
5430 
5431  if (myState->format == 0)
5432  {
5433  /* Text output */
5434  char *outputstr;
5435 
5436  outputstr = OutputFunctionCall(finfo, attr);
5437  pq_sendcountedtext(buf, outputstr, strlen(outputstr));
5438  }
5439  else
5440  {
5441  /* Binary output */
5442  bytea *outputbytes;
5443 
5444  outputbytes = SendFunctionCall(finfo, attr);
5445  pq_sendint32(buf, VARSIZE(outputbytes) - VARHDRSZ);
5446  pq_sendbytes(buf, VARDATA(outputbytes),
5447  VARSIZE(outputbytes) - VARHDRSZ);
5448  }
5449  }
5450 
5451  /*
5452  * We mustn't call pq_endmessage_reuse(), since that would actually send
5453  * the data to the client. Just count the data, instead. We can leave
5454  * the buffer alone; it'll be reset on the next iteration (as would also
5455  * happen in printtup()).
5456  */
5457  myState->metrics.bytesSent += buf->len;
5458 
5459  /* Return to caller's context, and flush row's temporary memory */
5460  MemoryContextSwitchTo(oldcontext);
5461  MemoryContextReset(myState->tmpcontext);
5462 
5463  /* Update timing data */
5464  if (myState->es->timing)
5465  {
5467  INSTR_TIME_ACCUM_DIFF(myState->metrics.timeSpent, end, start);
5468  }
5469 
5470  /* Update buffer metrics */
5471  if (myState->es->buffers)
5473  &pgBufferUsage,
5474  &instr_start);
5475 
5476  return true;
5477 }
5478 
5479 /*
5480  * serializeAnalyzeStartup - start up the serializeAnalyze receiver
5481  */
5482 static void
5483 serializeAnalyzeStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
5484 {
5485  SerializeDestReceiver *receiver = (SerializeDestReceiver *) self;
5486 
5487  Assert(receiver->es != NULL);
5488 
5489  switch (receiver->es->serialize)
5490  {
5492  Assert(false);
5493  break;
5495  receiver->format = 0; /* wire protocol format text */
5496  break;
5498  receiver->format = 1; /* wire protocol format binary */
5499  break;
5500  }
5501 
5502  /* Create per-row temporary memory context */
5504  "SerializeTupleReceive",
5506 
5507  /* The output buffer is re-used across rows, as in printtup.c */
5508  initStringInfo(&receiver->buf);
5509 
5510  /* Initialize results counters */
5511  memset(&receiver->metrics, 0, sizeof(SerializeMetrics));
5513 }
5514 
5515 /*
5516  * serializeAnalyzeShutdown - shut down the serializeAnalyze receiver
5517  */
5518 static void
5520 {
5521  SerializeDestReceiver *receiver = (SerializeDestReceiver *) self;
5522 
5523  if (receiver->finfos)
5524  pfree(receiver->finfos);
5525  receiver->finfos = NULL;
5526 
5527  if (receiver->buf.data)
5528  pfree(receiver->buf.data);
5529  receiver->buf.data = NULL;
5530 
5531  if (receiver->tmpcontext)
5532  MemoryContextDelete(receiver->tmpcontext);
5533  receiver->tmpcontext = NULL;
5534 }
5535 
5536 /*
5537  * serializeAnalyzeDestroy - destroy the serializeAnalyze receiver
5538  */
5539 static void
5541 {
5542  pfree(self);
5543 }
5544 
5545 /*
5546  * Build a DestReceiver for EXPLAIN (SERIALIZE) instrumentation.
5547  */
5548 DestReceiver *
5550 {
5551  SerializeDestReceiver *self;
5552 
5554 
5555  self->pub.receiveSlot = serializeAnalyzeReceive;
5556  self->pub.rStartup = serializeAnalyzeStartup;
5557  self->pub.rShutdown = serializeAnalyzeShutdown;
5558  self->pub.rDestroy = serializeAnalyzeDestroy;
5559  self->pub.mydest = DestExplainSerialize;
5560 
5561  self->es = es;
5562 
5563  return (DestReceiver *) self;
5564 }
5565 
5566 /*
5567  * GetSerializationMetrics - collect metrics
5568  *
5569  * We have to be careful here since the receiver could be an IntoRel
5570  * receiver if the subject statement is CREATE TABLE AS. In that
5571  * case, return all-zeroes stats.
5572  */
5573 static SerializeMetrics
5575 {
5576  SerializeMetrics empty;
5577 
5578  if (dest->mydest == DestExplainSerialize)
5579  return ((SerializeDestReceiver *) dest)->metrics;
5580 
5581  memset(&empty, 0, sizeof(SerializeMetrics));
5583 
5584  return empty;
5585 }
int16 AttrNumber
Definition: attnum.h:21
void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: prepare.c:568
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:917
bool track_io_timing
Definition: bufmgr.c:142
#define unconstify(underlying_type, expr)
Definition: c.h:1245
signed char int8
Definition: c.h:492
#define Max(x, y)
Definition: c.h:998
#define INT64_FORMAT
Definition: c.h:548
#define VARHDRSZ
Definition: c.h:692
#define Assert(condition)
Definition: c.h:858
#define UINT64_FORMAT
Definition: c.h:549
unsigned int Index
Definition: c.h:614
#define OidIsValid(objectId)
Definition: c.h:775
bool CreateTableAsRelExists(CreateTableAsStmt *ctas)
Definition: createas.c:388
DestReceiver * CreateIntoRelDestReceiver(IntoClause *intoClause)
Definition: createas.c:435
int GetIntoRelEFlags(IntoClause *intoClause)
Definition: createas.c:370
bool defGetBoolean(DefElem *def)
Definition: define.c:107
char * defGetString(DefElem *def)
Definition: define.c:48
DestReceiver * None_Receiver
Definition: dest.c:96
@ DestExplainSerialize
Definition: dest.h:99
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
void ExecutorEnd(QueryDesc *queryDesc)
Definition: execMain.c:467
void ExecutorFinish(QueryDesc *queryDesc)
Definition: execMain.c:407
void ExecutorStart(QueryDesc *queryDesc, int eflags)
Definition: execMain.c:124
void ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once)
Definition: execMain.c:297
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
void end_tup_output(TupOutputState *tstate)
Definition: execTuples.c:2420
void do_text_output_multiline(TupOutputState *tstate, const char *txt)
Definition: execTuples.c:2390
TupOutputState * begin_tup_output_tupdesc(DestReceiver *dest, TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:2342
#define outerPlanState(node)
Definition: execnodes.h:1213
#define innerPlanState(node)
Definition: execnodes.h:1212
#define EXEC_FLAG_EXPLAIN_GENERIC
Definition: executor.h:66
#define do_text_output_oneline(tstate, str_to_emit)
Definition: executor.h:522
#define EXEC_FLAG_EXPLAIN_ONLY
Definition: executor.h:65
#define X_OPENING
Definition: explain.c:60
static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors, ExplainState *es)
Definition: explain.c:4165
static void serializeAnalyzeStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
Definition: explain.c:5483
static void ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
Definition: explain.c:5070
static void show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2422
static void show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:3320
void ExplainPropertyText(const char *qlabel, const char *value, ExplainState *es)
Definition: explain.c:4795
static void show_group_keys(GroupState *gstate, List *ancestors, ExplainState *es)
Definition: explain.c:2732
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es)
Definition: explain.c:4018
void ExplainOpenGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
Definition: explain.c:4860
static void show_agg_keys(AggState *astate, List *ancestors, ExplainState *es)
Definition: explain.c:2600
static void show_hashagg_info(AggState *aggstate, ExplainState *es)
Definition: explain.c:3464
static void show_scan_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2524
static const char * explain_get_index_name(Oid indexId)
Definition: explain.c:3672
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir, ExplainState *es)
Definition: explain.c:3969
#define X_CLOSE_IMMEDIATE
Definition: explain.c:62
void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters)
Definition: explain.c:610
ExplainOneQuery_hook_type ExplainOneQuery_hook
Definition: explain.c:45
static void show_instrumentation_count(const char *qlabel, int which, PlanState *planstate, ExplainState *es)
Definition: explain.c:3615
void ExplainPropertyUInteger(const char *qlabel, const char *unit, uint64 value, ExplainState *es)
Definition: explain.c:4817
static void ExplainNode(PlanState *planstate, List *ancestors, const char *relationship, const char *plan_name, ExplainState *es)
Definition: explain.c:1360
static void show_incremental_sort_group_info(IncrementalSortGroupInfo *groupInfo, const char *groupLabel, bool indent, ExplainState *es)
Definition: explain.c:3029
#define X_NOWHITESPACE
Definition: explain.c:63
static void ExplainMemberNodes(PlanState **planstates, int nplans, List *ancestors, ExplainState *es)
Definition: explain.c:4377
explain_get_index_name_hook_type explain_get_index_name_hook
Definition: explain.c:48
static bool ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
Definition: explain.c:1285
static void ExplainPrintJIT(ExplainState *es, int jit_flags, JitInstrumentation *ji)
Definition: explain.c:1004
void ExplainPropertyInteger(const char *qlabel, const char *unit, int64 value, ExplainState *es)
Definition: explain.c:4804
void ExplainPropertyListNested(const char *qlabel, List *data, ExplainState *es)
Definition: explain.c:4689
static void ExplainYAMLLineStarting(ExplainState *es)
Definition: explain.c:5267
DestReceiver * CreateExplainSerializeDestReceiver(ExplainState *es)
Definition: explain.c:5549
void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, ParamListInfo params, DestReceiver *dest)
Definition: explain.c:176
static void show_incremental_sort_info(IncrementalSortState *incrsortstate, ExplainState *es)
Definition: explain.c:3143
static void serialize_prepare_info(SerializeDestReceiver *receiver, TupleDesc typeinfo, int nattrs)
Definition: explain.c:5327
struct SerializeMetrics SerializeMetrics
static void show_tablesample(TableSampleClause *tsc, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2872
static void show_upper_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, ExplainState *es)
Definition: explain.c:2538
static void show_sort_info(SortState *sortstate, ExplainState *es)
Definition: explain.c:2938
void ExplainSeparatePlans(ExplainState *es)
Definition: explain.c:5176
static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
Definition: explain.c:4395
void ExplainEndOutput(ExplainState *es)
Definition: explain.c:5147
void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:520
static void serializeAnalyzeDestroy(DestReceiver *self)
Definition: explain.c:5540
static void ExplainJSONLineEnding(ExplainState *es)
Definition: explain.c:5247
TupleDesc ExplainResultDesc(ExplainStmt *stmt)
Definition: explain.c:382
static void ExplainFlushWorkersState(ExplainState *es)
Definition: explain.c:4589
static bool peek_buffer_usage(ExplainState *es, const BufferUsage *usage)
Definition: explain.c:3696
void ExplainPrintJITSummary(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:978
static void show_hash_info(HashState *hashstate, ExplainState *es)
Definition: explain.c:3229
static void ExplainProperty(const char *qlabel, const char *unit, const char *value, bool numeric, ExplainState *es)
Definition: explain.c:4742
void ExplainPropertyFloat(const char *qlabel, const char *unit, double value, int ndigits, ExplainState *es)
Definition: explain.c:4831
void ExplainCloseGroup(const char *objtype, const char *labelname, bool labeled, ExplainState *es)
Definition: explain.c:4923
static void show_sortorder_options(StringInfo buf, Node *sortexpr, Oid sortOperator, Oid collation, bool nullsFirst)
Definition: explain.c:2814
static void show_expression(Node *node, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2480
static double elapsed_time(instr_time *starttime)
Definition: explain.c:1266
static void ExplainIndentText(ExplainState *es)
Definition: explain.c:5232
void ExplainQueryText(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:1162
static bool serializeAnalyzeReceive(TupleTableSlot *slot, DestReceiver *self)
Definition: explain.c:5380
static void ExplainRestoreGroup(ExplainState *es, int depth, int *state_save)
Definition: explain.c:5039
void ExplainBeginOutput(ExplainState *es)
Definition: explain.c:5116
ExplainState * NewExplainState(void)
Definition: explain.c:365
static void show_sort_group_keys(PlanState *planstate, const char *qlabel, int nkeys, int nPresortedKeys, AttrNumber *keycols, Oid *sortOperators, Oid *collations, bool *nullsFirst, List *ancestors, ExplainState *es)
Definition: explain.c:2752
static void show_tidbitmap_info(BitmapHeapScanState *planstate, ExplainState *es)
Definition: explain.c:3585
static void show_memory_counters(ExplainState *es, const MemoryContextCounters *mem_counters)
Definition: explain.c:3943
static void ExplainSubPlans(List *plans, List *ancestors, const char *relationship, ExplainState *es)
Definition: explain.c:4409
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
Definition: explain.c:3644
static void ExplainScanTarget(Scan *plan, ExplainState *es)
Definition: explain.c:4005
void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:870
static void escape_yaml(StringInfo buf, const char *str)
Definition: explain.c:5292
static void show_merge_append_keys(MergeAppendState *mstate, List *ancestors, ExplainState *es)
Definition: explain.c:2584
static void report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
Definition: explain.c:1195
void ExplainQueryParameters(ExplainState *es, ParamListInfo params, int maxlen)
Definition: explain.c:1177
static void ExplainPrintSerialize(ExplainState *es, SerializeMetrics *metrics)
Definition: explain.c:1102
static void show_grouping_sets(PlanState *planstate, Agg *agg, List *ancestors, ExplainState *es)
Definition: explain.c:2623
static void ExplainPrintSettings(ExplainState *es)
Definition: explain.c:800
static void ExplainCloseWorker(int n, ExplainState *es)
Definition: explain.c:4553
static void ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:421
static void ExplainOpenWorker(int n, ExplainState *es)
Definition: explain.c:4491
struct SerializeDestReceiver SerializeDestReceiver
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
Definition: explain.c:4027
static void ExplainSaveGroup(ExplainState *es, int depth, int *state_save)
Definition: explain.c:5009
static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
Definition: explain.c:5205
static void show_wal_usage(ExplainState *es, const WalUsage *usage)
Definition: explain.c:3905
static void ExplainOpenSetAsideGroup(const char *objtype, const char *labelname, bool labeled, int depth, ExplainState *es)
Definition: explain.c:4970
void ExplainPropertyBool(const char *qlabel, bool value, ExplainState *es)
Definition: explain.c:4845
static void show_grouping_set_keys(PlanState *planstate, Agg *aggnode, Sort *sortnode, List *context, bool useprefix, List *ancestors, ExplainState *es)
Definition: explain.c:2654
static void show_qual(List *qual, const char *qlabel, PlanState *planstate, List *ancestors, bool useprefix, ExplainState *es)
Definition: explain.c:2503
static void show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2552
static ExplainWorkersState * ExplainCreateWorkersState(int num_workers)
Definition: explain.c:4474
#define X_CLOSING
Definition: explain.c:61
void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
Definition: explain.c:935
void standard_ExplainOneQuery(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.c:448
static void show_buffer_usage(ExplainState *es, const BufferUsage *usage)
Definition: explain.c:3736
static void serializeAnalyzeShutdown(DestReceiver *self)
Definition: explain.c:5519
static void show_incremental_sort_keys(IncrementalSortState *incrsortstate, List *ancestors, ExplainState *es)
Definition: explain.c:2567
static void ExplainCustomChildren(CustomScanState *css, List *ancestors, ExplainState *es)
Definition: explain.c:4452
void ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
Definition: explain.c:4619
static SerializeMetrics GetSerializationMetrics(DestReceiver *dest)
Definition: explain.c:5574
@ EXPLAIN_SERIALIZE_TEXT
Definition: explain.h:23
@ EXPLAIN_SERIALIZE_NONE
Definition: explain.h:22
@ EXPLAIN_SERIALIZE_BINARY
Definition: explain.h:24
void(* ExplainOneQuery_hook_type)(Query *query, int cursorOptions, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv)
Definition: explain.h:75
@ EXPLAIN_FORMAT_XML
Definition: explain.h:30
@ EXPLAIN_FORMAT_YAML
Definition: explain.h:32
@ EXPLAIN_FORMAT_TEXT
Definition: explain.h:29
@ EXPLAIN_FORMAT_JSON
Definition: explain.h:31
const char *(* explain_get_index_name_hook_type)(Oid indexId)
Definition: explain.h:85
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:127
bytea * SendFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1744
char * OutputFunctionCall(FmgrInfo *flinfo, Datum val)
Definition: fmgr.c:1683
struct config_generic ** get_explain_guc_options(int *num)
Definition: guc.c:5284
char * GetConfigOptionByName(const char *name, const char **varname, bool missing_ok)
Definition: guc.c:5385
return str start
const char * str
#define stmt
Definition: indent_codes.h:59
struct parser_state ps
static struct @155 value
#define INSTR_TIME_SET_CURRENT(t)
Definition: instr_time.h:122
#define INSTR_TIME_ADD(x, y)
Definition: instr_time.h:178
#define INSTR_TIME_IS_ZERO(t)
Definition: instr_time.h:169
#define INSTR_TIME_GET_DOUBLE(t)
Definition: instr_time.h:188
#define INSTR_TIME_SUBTRACT(x, y)
Definition: instr_time.h:181
#define INSTR_TIME_GET_MILLISEC(t)
Definition: instr_time.h:191
#define INSTR_TIME_SET_ZERO(t)
Definition: instr_time.h:172
#define INSTR_TIME_ACCUM_DIFF(x, y, z)
Definition: instr_time.h:184
void InstrEndLoop(Instrumentation *instr)
Definition: instrument.c:140
BufferUsage pgBufferUsage
Definition: instrument.c:20
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition: instrument.c:248
@ INSTRUMENT_TIMER
Definition: instrument.h:61
@ INSTRUMENT_BUFFERS
Definition: instrument.h:62
@ INSTRUMENT_WAL
Definition: instrument.h:64
@ INSTRUMENT_ROWS
Definition: instrument.h:63
int j
Definition: isn.c:74
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
Definition: jit.c:182
#define PGJIT_OPT3
Definition: jit.h:21
#define PGJIT_EXPR
Definition: jit.h:23
#define PGJIT_DEFORM
Definition: jit.h:24
#define PGJIT_INLINE
Definition: jit.h:22
#define PGJIT_PERFORM
Definition: jit.h:20
void escape_json(StringInfo buf, const char *str)
Definition: json.c:1549
List * lcons_int(int datum, List *list)
Definition: list.c:513
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_delete_first(List *list)
Definition: list.c:943
List * lcons(void *datum, List *list)
Definition: list.c:495
char * get_opname(Oid opno)
Definition: lsyscache.c:1310
void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
Definition: lsyscache.c:2973
char * get_namespace_name_or_temp(Oid nspid)
Definition: lsyscache.c:3390
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition: lsyscache.c:2907
Oid get_equality_op_for_ordering_op(Oid opno, bool *reverse)
Definition: lsyscache.c:267
Oid get_rel_namespace(Oid relid)
Definition: lsyscache.c:1952
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3056
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1608
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1035
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1081
Oid get_func_namespace(Oid funcid)
Definition: lsyscache.c:1632
Expr * make_andclause(List *andclauses)
Definition: makefuncs.c:654
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:726
Expr * make_orclause(List *orclauses)
Definition: makefuncs.c:670
void MemoryContextMemConsumed(MemoryContext context, MemoryContextCounters *consumed)
Definition: mcxt.c:786
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
char * pstrdup(const char *in)
Definition: mcxt.c:1695
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
void * palloc(Size size)
Definition: mcxt.c:1316
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
#define planstate_tree_walker(ps, w, c)
Definition: nodeFuncs.h:177
@ SETOPCMD_EXCEPT
Definition: nodes.h:399
@ SETOPCMD_EXCEPT_ALL
Definition: nodes.h:400
@ SETOPCMD_INTERSECT_ALL
Definition: nodes.h:398
@ SETOPCMD_INTERSECT
Definition: nodes.h:397
@ SETOP_HASHED
Definition: nodes.h:406
@ SETOP_SORTED
Definition: nodes.h:405
#define DO_AGGSPLIT_SKIPFINAL(as)
Definition: nodes.h:385
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define copyObject(obj)
Definition: nodes.h:224
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define DO_AGGSPLIT_COMBINE(as)
Definition: nodes.h:384
@ ONCONFLICT_NONE
Definition: nodes.h:417
@ ONCONFLICT_NOTHING
Definition: nodes.h:418
@ CMD_MERGE
Definition: nodes.h:269
@ CMD_UTILITY
Definition: nodes.h:270
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
@ CMD_SELECT
Definition: nodes.h:265
@ AGG_SORTED
Definition: nodes.h:354
@ AGG_HASHED
Definition: nodes.h:355
@ AGG_MIXED
Definition: nodes.h:356
@ AGG_PLAIN
Definition: nodes.h:353
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
@ JOIN_SEMI
Definition: nodes.h:307
@ JOIN_FULL
Definition: nodes.h:295
@ JOIN_INNER
Definition: nodes.h:293
@ JOIN_RIGHT
Definition: nodes.h:296
@ JOIN_LEFT
Definition: nodes.h:294
@ JOIN_RIGHT_ANTI
Definition: nodes.h:309
@ JOIN_ANTI
Definition: nodes.h:308
char * BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
Definition: params.c:335
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
@ RTE_CTE
Definition: parsenodes.h:1034
@ RTE_NAMEDTUPLESTORE
Definition: parsenodes.h:1035
@ RTE_VALUES
Definition: parsenodes.h:1033
@ RTE_FUNCTION
Definition: parsenodes.h:1031
@ RTE_TABLEFUNC
Definition: parsenodes.h:1032
@ RTE_RELATION
Definition: parsenodes.h:1028
@ OBJECT_MATVIEW
Definition: parsenodes.h:2286
@ OBJECT_TABLE
Definition: parsenodes.h:2304
#define CURSOR_OPT_PARALLEL_OK
Definition: parsenodes.h:3302
post_parse_analyze_hook_type post_parse_analyze_hook
Definition: analyze.c:58
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void * arg
static char format
static char * label
NameData relname
Definition: pg_class.h:38
const void * data
#define lfirst(lc)
Definition: pg_list.h:172
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define linitial_node(type, l)
Definition: pg_list.h:181
#define NIL
Definition: pg_list.h:68
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define lfirst_int(lc)
Definition: pg_list.h:173
#define list_make1(x1)
Definition: pg_list.h:212
#define linitial_int(l)
Definition: pg_list.h:179
#define linitial(l)
Definition: pg_list.h:178
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define plan(x)
Definition: pg_regress.c:162
static char * buf
Definition: pg_test_fsync.c:73
#define snprintf
Definition: port.h:238
PlannedStmt * pg_plan_query(Query *querytree, const char *query_string, int cursorOptions, ParamListInfo boundParams)
Definition: postgres.c:886
uintptr_t Datum
Definition: postgres.h:64
unsigned int Oid
Definition: postgres_ext.h:31
void pq_sendbytes(StringInfo buf, const void *data, int datalen)
Definition: pqformat.c:126
void pq_beginmessage_reuse(StringInfo buf, char msgtype)
Definition: pqformat.c:109
void pq_sendcountedtext(StringInfo buf, const char *str, int slen)
Definition: pqformat.c:142
static void pq_sendint32(StringInfo buf, uint32 i)
Definition: pqformat.h:144
static void pq_sendint16(StringInfo buf, uint16 i)
Definition: pqformat.h:136
void FreeQueryDesc(QueryDesc *qdesc)
Definition: pquery.c:105
QueryDesc * CreateQueryDesc(PlannedStmt *plannedstmt, const char *sourceText, Snapshot snapshot, Snapshot crosscheck_snapshot, DestReceiver *dest, ParamListInfo params, QueryEnvironment *queryEnv, int instrument_options)
Definition: pquery.c:67
@ TFT_XMLTABLE
Definition: primnodes.h:99
@ TFT_JSON_TABLE
Definition: primnodes.h:100
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
@ COMPUTE_QUERY_ID_REGRESS
Definition: queryjumble.h:59
static bool IsQueryIdEnabled(void)
Definition: queryjumble.h:77
JumbleState * JumbleQuery(Query *query)
int compute_query_id
tree context
Definition: radixtree.h:1833
MemoryContextSwitchTo(old_ctx)
static const struct fns functions
Definition: regcomp.c:356
#define RelationGetRelationName(relation)
Definition: rel.h:539
List * QueryRewrite(Query *parsetree)
char * deparse_expression(Node *expr, List *dpcontext, bool forceprefix, bool showimplicit)
Definition: ruleutils.c:3625
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12623
List * select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
Definition: ruleutils.c:3822
List * set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
Definition: ruleutils.c:3799
List * deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
Definition: ruleutils.c:3730
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
ScanDirection
Definition: sdir.h:25
@ NoMovementScanDirection
Definition: sdir.h:27
@ BackwardScanDirection
Definition: sdir.h:26
@ ForwardScanDirection
Definition: sdir.h:28
void UpdateActiveSnapshotCommandId(void)
Definition: snapmgr.c:712
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
void PushCopiedSnapshot(Snapshot snapshot)
Definition: snapmgr.c:700
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:770
#define InvalidSnapshot
Definition: snapshot.h:123
StringInfo makeStringInfo(void)
Definition: stringinfo.c:41
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:212
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
#define appendStringInfoCharMacro(str, ch)
Definition: stringinfo.h:204
ScanState ss
Definition: execnodes.h:2459
uint64 hash_disk_used
Definition: execnodes.h:2519
int hash_planned_partitions
Definition: execnodes.h:2513
SharedAggInfo * shared_info
Definition: execnodes.h:2530
Size hash_mem_peak
Definition: execnodes.h:2516
int hash_batches_used
Definition: execnodes.h:2520
Definition: plannodes.h:997
AggSplit aggsplit
Definition: plannodes.h:1004
List * chain
Definition: plannodes.h:1031
List * groupingSets
Definition: plannodes.h:1028
Plan plan
Definition: plannodes.h:998
AggStrategy aggstrategy
Definition: plannodes.h:1001
IntoClause * into
Definition: parsenodes.h:3904
ObjectType objtype
Definition: parsenodes.h:3905
void(* ExplainCustomScan)(CustomScanState *node, List *ancestors, ExplainState *es)
Definition: extensible.h:155
const struct CustomExecMethods * methods
Definition: execnodes.h:2065
List * custom_ps
Definition: execnodes.h:2063
char * defname
Definition: parsenodes.h:815
ParseLoc location
Definition: parsenodes.h:819
Node * arg
Definition: parsenodes.h:816
List * es_tuple_routing_result_relations
Definition: execnodes.h:655
struct JitContext * es_jit
Definition: execnodes.h:716
struct JitInstrumentation * es_jit_worker_instr
Definition: execnodes.h:717
List * es_trig_target_relations
Definition: execnodes.h:658
int es_jit_flags
Definition: execnodes.h:715
List * es_opened_result_relations
Definition: execnodes.h:645
bool verbose
Definition: explain.h:48
bool settings
Definition: explain.h:56
Bitmapset * printed_subplans
Definition: explain.h:68
bool hide_workers
Definition: explain.h:69
bool timing
Definition: explain.h:53
List * grouping_stack
Definition: explain.h:62
List * rtable_names
Definition: explain.h:66
ExplainWorkersState * workers_state
Definition: explain.h:71
bool analyze
Definition: explain.h:49
StringInfo str
Definition: explain.h:46
ExplainFormat format
Definition: explain.h:59
bool wal
Definition: explain.h:52
bool generic
Definition: explain.h:57
List * rtable
Definition: explain.h:65
PlannedStmt * pstmt
Definition: explain.h:64
bool memory
Definition: explain.h:55
bool summary
Definition: explain.h:54
ExplainSerializeOption serialize
Definition: explain.h:58
bool costs
Definition: explain.h:50
bool buffers
Definition: explain.h:51
int indent
Definition: explain.h:61
List * deparse_cxt
Definition: explain.h:67
StringInfoData * worker_str
Definition: explain.h:39
bool * worker_inited
Definition: explain.h:38
int * worker_state_save
Definition: explain.h:40
StringInfo prev_str
Definition: explain.h:41
ExplainForeignScan_function ExplainForeignScan
Definition: fdwapi.h:252
ExplainForeignModify_function ExplainForeignModify
Definition: fdwapi.h:253
ExplainDirectModify_function ExplainDirectModify
Definition: fdwapi.h:254
Definition: fmgr.h:57
struct FdwRoutine * fdwroutine
Definition: execnodes.h:2039
ScanState ss
Definition: execnodes.h:2034
Oid funcid
Definition: primnodes.h:720
List * functions
Definition: plannodes.h:612
int num_workers
Definition: plannodes.h:1160
int num_workers
Definition: plannodes.h:1143
bool single_copy
Definition: plannodes.h:1145
ScanState ss
Definition: execnodes.h:2412
SharedHashInfo * shared_info
Definition: execnodes.h:2750
HashInstrumentation * hinstrument
Definition: execnodes.h:2757
IncrementalSortGroupInfo prefixsortGroupInfo
Definition: execnodes.h:2358
IncrementalSortGroupInfo fullsortGroupInfo
Definition: execnodes.h:2357
SharedIncrementalSortInfo * shared_info
Definition: execnodes.h:2403
IncrementalSortInfo incsort_info
Definition: execnodes.h:2397
ScanDirection indexorderdir
Definition: plannodes.h:500
ScanDirection indexorderdir
Definition: plannodes.h:458
Oid indexid
Definition: plannodes.h:452
double nfiltered2
Definition: instrument.h:90
double nfiltered1
Definition: instrument.h:89
double startup
Definition: instrument.h:84
WalUsage walusage
Definition: instrument.h:92
double ntuples
Definition: instrument.h:86
BufferUsage bufusage
Definition: instrument.h:91
double ntuples2
Definition: instrument.h:87
bool skipData
Definition: primnodes.h:170
JitInstrumentation instr
Definition: jit.h:64
instr_time generation_counter
Definition: jit.h:33
size_t created_functions
Definition: jit.h:30
instr_time optimization_counter
Definition: jit.h:42
instr_time deform_counter
Definition: jit.h:36
instr_time emission_counter
Definition: jit.h:45
instr_time inlining_counter
Definition: jit.h:39
Definition: pg_list.h:54
uint64 mem_used
Definition: execnodes.h:2278
SharedMemoizeInfo * shared_info
Definition: execnodes.h:2293
MemoizeInstrumentation stats
Definition: execnodes.h:2292
bool binary_mode
Definition: execnodes.h:2290
PlanState ps
Definition: execnodes.h:1483
ResultRelInfo * resultRelInfo
Definition: execnodes.h:1360
double mt_merge_deleted
Definition: execnodes.h:1415
double mt_merge_inserted
Definition: execnodes.h:1413
double mt_merge_updated
Definition: execnodes.h:1414
PlanState ps
Definition: execnodes.h:1355
Index nominalRelation
Definition: plannodes.h:234
List * arbiterIndexes
Definition: plannodes.h:246
CmdType operation
Definition: plannodes.h:232
List * fdwPrivLists
Definition: plannodes.h:241
Node * onConflictWhere
Definition: plannodes.h:249
OnConflictAction onConflictAction
Definition: plannodes.h:245
Definition: nodes.h:129
QueryEnvironment * p_queryEnv
Definition: parse_node.h:220
const char * p_sourcetext
Definition: parse_node.h:193
struct SharedJitInstrumentation * worker_jit_instrument
Definition: execnodes.h:1131
Instrumentation * instrument
Definition: execnodes.h:1127
Plan * plan
Definition: execnodes.h:1117
List * subPlan
Definition: execnodes.h:1144
EState * state
Definition: execnodes.h:1119
WorkerInstrumentation * worker_instrument
Definition: execnodes.h:1128
List * initPlan
Definition: execnodes.h:1142
struct Plan * lefttree
Definition: plannodes.h:154
CmdType commandType
Definition: plannodes.h:52
List * rtable
Definition: plannodes.h:72
uint64 queryId
Definition: plannodes.h:54
const char * sourceText
Definition: execdesc.h:38
EState * estate
Definition: execdesc.h:48
PlannedStmt * plannedstmt
Definition: execdesc.h:37
PlanState * planstate
Definition: execdesc.h:49
CmdType commandType
Definition: parsenodes.h:121
Node * utilityStmt
Definition: parsenodes.h:136
RTEKind rtekind
Definition: parsenodes.h:1057
Instrumentation * ri_TrigInstrument
Definition: execnodes.h:495
Relation ri_RelationDesc
Definition: execnodes.h:456
TriggerDesc * ri_TrigDesc
Definition: execnodes.h:486
Index ri_RangeTableIndex
Definition: execnodes.h:453
struct FdwRoutine * ri_FdwRoutine
Definition: execnodes.h:503
bool ri_usesFdwDirectModify
Definition: execnodes.h:509
PlanState ps
Definition: execnodes.h:1564
ExplainState * es
Definition: explain.c:5310
TupleDesc attrinfo
Definition: explain.c:5312
SerializeMetrics metrics
Definition: explain.c:5317
FmgrInfo * finfos
Definition: explain.c:5314
StringInfoData buf
Definition: explain.c:5316
MemoryContext tmpcontext
Definition: explain.c:5315
DestReceiver pub
Definition: explain.c:5309
uint64 bytesSent
Definition: explain.c:54
instr_time timeSpent
Definition: explain.c:55
BufferUsage bufferUsage
Definition: explain.c:56
AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2435
HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2731
IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2368
JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER]
Definition: jit.h:54
MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2254
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2319
bool sort_Done
Definition: execnodes.h:2332
void * tuplesortstate
Definition: execnodes.h:2335
SharedSortInfo * shared_info
Definition: execnodes.h:2338
ScanState ss
Definition: execnodes.h:2328
int numCols
Definition: plannodes.h:936
struct PlanState * planstate
Definition: execnodes.h:961
SubPlan * subplan
Definition: execnodes.h:960
int plan_id
Definition: primnodes.h:1040
char * plan_name
Definition: primnodes.h:1042
TableFuncType functype
Definition: primnodes.h:113
Expr * expr
Definition: primnodes.h:2162
int numtriggers
Definition: reltrigger.h:50
Trigger * triggers
Definition: reltrigger.h:49
Oid tgconstraint
Definition: reltrigger.h:35
char * tgname
Definition: reltrigger.h:27
TupleDesc tts_tupleDescriptor
Definition: tuptable.h:123
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
TuplesortMethod sortMethod
Definition: tuplesort.h:112
TuplesortSpaceType spaceType
Definition: tuplesort.h:113
Instrumentation instrument[FLEXIBLE_ARRAY_MEMBER]
Definition: instrument.h:98
const char * name
Definition: guc_tables.h:156
Definition: regguts.h:323
Definition: c.h:687
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:67
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:651
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
const char * tuplesort_space_type_name(TuplesortSpaceType t)
Definition: tuplesort.c:2604
void tuplesort_get_stats(Tuplesortstate *state, TuplesortInstrumentation *stats)
Definition: tuplesort.c:2537
const char * tuplesort_method_name(TuplesortMethod m)
Definition: tuplesort.c:2581
#define NUM_TUPLESORTMETHODS
Definition: tuplesort.h:84
@ SORT_SPACE_TYPE_DISK
Definition: tuplesort.h:88
@ SORT_SPACE_TYPE_MEMORY
Definition: tuplesort.h:89
TuplesortMethod
Definition: tuplesort.h:76
@ SORT_TYPE_STILL_IN_PROGRESS
Definition: tuplesort.h:77
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:346
#define TYPECACHE_GT_OPR
Definition: typcache.h:139
#define TYPECACHE_LT_OPR
Definition: typcache.h:138
void * ptr_value
Definition: pg_list.h:47
static void usage(const char *progname)
Definition: vacuumlo.c:414
#define VARDATA(PTR)
Definition: varatt.h:278
#define VARSIZE(PTR)
Definition: varatt.h:279
Datum bit(PG_FUNCTION_ARGS)
Definition: varbit.c:391
void CommandCounterIncrement(void)
Definition: xact.c:1097
char * escape_xml(const char *str)
Definition: xml.c:2632