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