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