]> granicus.if.org Git - postgresql/blob - src/backend/commands/explain.c
Update copyright for the year 2010.
[postgresql] / src / backend / commands / explain.c
1 /*-------------------------------------------------------------------------
2  *
3  * explain.c
4  *        Explain query execution plans
5  *
6  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994-5, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.198 2010/01/02 16:57:37 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "access/xact.h"
17 #include "catalog/pg_constraint.h"
18 #include "catalog/pg_type.h"
19 #include "commands/defrem.h"
20 #include "commands/explain.h"
21 #include "commands/prepare.h"
22 #include "commands/trigger.h"
23 #include "executor/instrument.h"
24 #include "optimizer/clauses.h"
25 #include "optimizer/planner.h"
26 #include "optimizer/var.h"
27 #include "parser/parsetree.h"
28 #include "rewrite/rewriteHandler.h"
29 #include "tcop/tcopprot.h"
30 #include "utils/builtins.h"
31 #include "utils/guc.h"
32 #include "utils/lsyscache.h"
33 #include "utils/tuplesort.h"
34 #include "utils/snapmgr.h"
35 #include "utils/xml.h"
36
37
38 /* Hook for plugins to get control in ExplainOneQuery() */
39 ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
40
41 /* Hook for plugins to get control in explain_get_index_name() */
42 explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
43
44
45 /* OR-able flags for ExplainXMLTag() */
46 #define X_OPENING 0
47 #define X_CLOSING 1
48 #define X_CLOSE_IMMEDIATE 2
49 #define X_NOWHITESPACE 4
50
51 static void ExplainOneQuery(Query *query, ExplainState *es,
52                                 const char *queryString, ParamListInfo params);
53 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
54                                 ExplainState *es);
55 static double elapsed_time(instr_time *starttime);
56 static void ExplainNode(Plan *plan, PlanState *planstate,
57                                 Plan *outer_plan,
58                                 const char *relationship, const char *plan_name,
59                                 ExplainState *es);
60 static void show_plan_tlist(Plan *plan, ExplainState *es);
61 static void show_qual(List *qual, const char *qlabel, Plan *plan,
62                   Plan *outer_plan, bool useprefix, ExplainState *es);
63 static void show_scan_qual(List *qual, const char *qlabel,
64                            Plan *scan_plan, Plan *outer_plan,
65                            ExplainState *es);
66 static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
67                                 ExplainState *es);
68 static void show_sort_keys(Plan *sortplan, ExplainState *es);
69 static void show_sort_info(SortState *sortstate, ExplainState *es);
70 static const char *explain_get_index_name(Oid indexId);
71 static void ExplainScanTarget(Scan *plan, ExplainState *es);
72 static void ExplainMemberNodes(List *plans, PlanState **planstate,
73                                    Plan *outer_plan, ExplainState *es);
74 static void ExplainSubPlans(List *plans, const char *relationship,
75                                                         ExplainState *es);
76 static void ExplainPropertyList(const char *qlabel, List *data,
77                                                                 ExplainState *es);
78 static void ExplainProperty(const char *qlabel, const char *value,
79                                                         bool numeric, ExplainState *es);
80 #define ExplainPropertyText(qlabel, value, es)  \
81         ExplainProperty(qlabel, value, false, es)
82 static void ExplainPropertyInteger(const char *qlabel, int value,
83                                                                    ExplainState *es);
84 static void ExplainPropertyLong(const char *qlabel, long value,
85                                                                 ExplainState *es);
86 static void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
87                                                                  ExplainState *es);
88 static void ExplainOpenGroup(const char *objtype, const char *labelname,
89                                  bool labeled, ExplainState *es);
90 static void ExplainCloseGroup(const char *objtype, const char *labelname,
91                                  bool labeled, ExplainState *es);
92 static void ExplainDummyGroup(const char *objtype, const char *labelname,
93                                                           ExplainState *es);
94 static void ExplainXMLTag(const char *tagname, int flags, ExplainState *es);
95 static void ExplainJSONLineEnding(ExplainState *es);
96 static void ExplainYAMLLineStarting(ExplainState *es);
97 static void escape_json(StringInfo buf, const char *str);
98 static void escape_yaml(StringInfo buf, const char *str);
99
100
101 /*
102  * ExplainQuery -
103  *        execute an EXPLAIN command
104  */
105 void
106 ExplainQuery(ExplainStmt *stmt, const char *queryString,
107                          ParamListInfo params, DestReceiver *dest)
108 {
109         ExplainState es;
110         TupOutputState *tstate;
111         List       *rewritten;
112         ListCell   *lc;
113
114         /* Initialize ExplainState. */
115         ExplainInitState(&es);
116
117         /* Parse options list. */
118         foreach(lc, stmt->options)
119         {
120                 DefElem *opt = (DefElem *) lfirst(lc);
121
122                 if (strcmp(opt->defname, "analyze") == 0)
123                         es.analyze = defGetBoolean(opt);
124                 else if (strcmp(opt->defname, "verbose") == 0)
125                         es.verbose = defGetBoolean(opt);
126                 else if (strcmp(opt->defname, "costs") == 0)
127                         es.costs = defGetBoolean(opt);
128                 else if (strcmp(opt->defname, "buffers") == 0)
129                         es.buffers = defGetBoolean(opt);
130                 else if (strcmp(opt->defname, "format") == 0)
131                 {
132                         char   *p = defGetString(opt);
133
134                         if (strcmp(p, "text") == 0)
135                                 es.format = EXPLAIN_FORMAT_TEXT;
136                         else if (strcmp(p, "xml") == 0)
137                                 es.format = EXPLAIN_FORMAT_XML;
138                         else if (strcmp(p, "json") == 0)
139                                 es.format = EXPLAIN_FORMAT_JSON;
140                         else if (strcmp(p, "yaml") == 0)
141                                 es.format = EXPLAIN_FORMAT_YAML;
142                         else
143                                 ereport(ERROR,
144                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
145                                          errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
146                                                         opt->defname, p)));
147                 }
148                 else
149                         ereport(ERROR,
150                                         (errcode(ERRCODE_SYNTAX_ERROR),
151                                          errmsg("unrecognized EXPLAIN option \"%s\"",
152                                                         opt->defname)));
153         }
154
155         if (es.buffers && !es.analyze)
156                 ereport(ERROR,
157                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
158                          errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
159
160         /*
161          * Run parse analysis and rewrite.      Note this also acquires sufficient
162          * locks on the source table(s).
163          *
164          * Because the parser and planner tend to scribble on their input, we make
165          * a preliminary copy of the source querytree.  This prevents problems in
166          * the case that the EXPLAIN is in a portal or plpgsql function and is
167          * executed repeatedly.  (See also the same hack in DECLARE CURSOR and
168          * PREPARE.)  XXX FIXME someday.
169          */
170         rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
171                                                                                           queryString,
172                                                                                           (ParserSetupHook) setupParserWithParamList,
173                                                                                           params);
174
175         /* emit opening boilerplate */
176         ExplainBeginOutput(&es);
177
178         if (rewritten == NIL)
179         {
180                 /*
181                  * In the case of an INSTEAD NOTHING, tell at least that.  But in
182                  * non-text format, the output is delimited, so this isn't necessary.
183                  */
184                 if (es.format == EXPLAIN_FORMAT_TEXT)
185                         appendStringInfoString(es.str, "Query rewrites to nothing\n");
186         }
187         else
188         {
189                 ListCell   *l;
190
191                 /* Explain every plan */
192                 foreach(l, rewritten)
193                 {
194                         ExplainOneQuery((Query *) lfirst(l), &es, queryString, params);
195
196                         /* Separate plans with an appropriate separator */
197                         if (lnext(l) != NULL)
198                                 ExplainSeparatePlans(&es);
199                 }
200         }
201
202         /* emit closing boilerplate */
203         ExplainEndOutput(&es);
204         Assert(es.indent == 0);
205
206         /* output tuples */
207         tstate = begin_tup_output_tupdesc(dest, ExplainResultDesc(stmt));
208         if (es.format == EXPLAIN_FORMAT_TEXT)
209                 do_text_output_multiline(tstate, es.str->data);
210         else
211                 do_text_output_oneline(tstate, es.str->data);
212         end_tup_output(tstate);
213
214         pfree(es.str->data);
215 }
216
217 /*
218  * Initialize ExplainState.
219  */
220 void
221 ExplainInitState(ExplainState *es)
222 {
223         /* Set default options. */
224         memset(es, 0, sizeof(ExplainState));
225         es->costs = true;
226         /* Prepare output buffer. */
227         es->str = makeStringInfo();
228 }
229
230 /*
231  * ExplainResultDesc -
232  *        construct the result tupledesc for an EXPLAIN
233  */
234 TupleDesc
235 ExplainResultDesc(ExplainStmt *stmt)
236 {
237         TupleDesc       tupdesc;
238         ListCell   *lc;
239         bool            xml = false;
240
241         /* Check for XML format option */
242         foreach(lc, stmt->options)
243         {
244                 DefElem *opt = (DefElem *) lfirst(lc);
245
246                 if (strcmp(opt->defname, "format") == 0)
247                 {
248                         char   *p = defGetString(opt);
249
250                         xml = (strcmp(p, "xml") == 0);
251                 }
252         }
253
254         /* Need a tuple descriptor representing a single TEXT or XML column */
255         tupdesc = CreateTemplateTupleDesc(1, false);
256         TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
257                                            xml ? XMLOID : TEXTOID, -1, 0);
258         return tupdesc;
259 }
260
261 /*
262  * ExplainOneQuery -
263  *        print out the execution plan for one Query
264  */
265 static void
266 ExplainOneQuery(Query *query, ExplainState *es,
267                                 const char *queryString, ParamListInfo params)
268 {
269         /* planner will not cope with utility statements */
270         if (query->commandType == CMD_UTILITY)
271         {
272                 ExplainOneUtility(query->utilityStmt, es, queryString, params);
273                 return;
274         }
275
276         /* if an advisor plugin is present, let it manage things */
277         if (ExplainOneQuery_hook)
278                 (*ExplainOneQuery_hook) (query, es, queryString, params);
279         else
280         {
281                 PlannedStmt *plan;
282
283                 /* plan the query */
284                 plan = pg_plan_query(query, 0, params);
285
286                 /* run it (if needed) and produce output */
287                 ExplainOnePlan(plan, es, queryString, params);
288         }
289 }
290
291 /*
292  * ExplainOneUtility -
293  *        print out the execution plan for one utility statement
294  *        (In general, utility statements don't have plans, but there are some
295  *        we treat as special cases)
296  *
297  * This is exported because it's called back from prepare.c in the
298  * EXPLAIN EXECUTE case
299  */
300 void
301 ExplainOneUtility(Node *utilityStmt, ExplainState *es,
302                                   const char *queryString, ParamListInfo params)
303 {
304         if (utilityStmt == NULL)
305                 return;
306
307         if (IsA(utilityStmt, ExecuteStmt))
308                 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es,
309                                                         queryString, params);
310         else if (IsA(utilityStmt, NotifyStmt))
311         {
312                 if (es->format == EXPLAIN_FORMAT_TEXT)
313                         appendStringInfoString(es->str, "NOTIFY\n");
314                 else
315                         ExplainDummyGroup("Notify", NULL, es);
316         }
317         else
318         {
319                 if (es->format == EXPLAIN_FORMAT_TEXT)
320                         appendStringInfoString(es->str,
321                                                            "Utility statements have no plan structure\n");
322                 else
323                         ExplainDummyGroup("Utility Statement", NULL, es);
324         }
325 }
326
327 /*
328  * ExplainOnePlan -
329  *              given a planned query, execute it if needed, and then print
330  *              EXPLAIN output
331  *
332  * Since we ignore any DeclareCursorStmt that might be attached to the query,
333  * if you say EXPLAIN ANALYZE DECLARE CURSOR then we'll actually run the
334  * query.  This is different from pre-8.3 behavior but seems more useful than
335  * not running the query.  No cursor will be created, however.
336  *
337  * This is exported because it's called back from prepare.c in the
338  * EXPLAIN EXECUTE case, and because an index advisor plugin would need
339  * to call it.
340  */
341 void
342 ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
343                            const char *queryString, ParamListInfo params)
344 {
345         QueryDesc  *queryDesc;
346         instr_time      starttime;
347         double          totaltime = 0;
348         int                     eflags;
349         int                     instrument_option = 0;
350
351         if (es->analyze)
352                 instrument_option |= INSTRUMENT_TIMER;
353         if (es->buffers)
354                 instrument_option |= INSTRUMENT_BUFFERS;
355
356         /*
357          * Use a snapshot with an updated command ID to ensure this query sees
358          * results of any previously executed queries.
359          */
360         PushUpdatedSnapshot(GetActiveSnapshot());
361
362         /* Create a QueryDesc requesting no output */
363         queryDesc = CreateQueryDesc(plannedstmt, queryString,
364                                                                 GetActiveSnapshot(), InvalidSnapshot,
365                                                                 None_Receiver, params, instrument_option);
366
367         INSTR_TIME_SET_CURRENT(starttime);
368
369         /* If analyzing, we need to cope with queued triggers */
370         if (es->analyze)
371                 AfterTriggerBeginQuery();
372
373         /* Select execution options */
374         if (es->analyze)
375                 eflags = 0;                             /* default run-to-completion flags */
376         else
377                 eflags = EXEC_FLAG_EXPLAIN_ONLY;
378
379         /* call ExecutorStart to prepare the plan for execution */
380         ExecutorStart(queryDesc, eflags);
381
382         /* Execute the plan for statistics if asked for */
383         if (es->analyze)
384         {
385                 /* run the plan */
386                 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
387
388                 /* We can't clean up 'till we're done printing the stats... */
389                 totaltime += elapsed_time(&starttime);
390         }
391
392         ExplainOpenGroup("Query", NULL, true, es);
393
394         /* Create textual dump of plan tree */
395         ExplainPrintPlan(es, queryDesc);
396
397         /*
398          * If we ran the command, run any AFTER triggers it queued.  (Note this
399          * will not include DEFERRED triggers; since those don't run until end of
400          * transaction, we can't measure them.)  Include into total runtime.
401          */
402         if (es->analyze)
403         {
404                 INSTR_TIME_SET_CURRENT(starttime);
405                 AfterTriggerEndQuery(queryDesc->estate);
406                 totaltime += elapsed_time(&starttime);
407         }
408
409         /* Print info about runtime of triggers */
410         if (es->analyze)
411         {
412                 ResultRelInfo *rInfo;
413                 bool            show_relname;
414                 int                     numrels = queryDesc->estate->es_num_result_relations;
415                 List       *targrels = queryDesc->estate->es_trig_target_relations;
416                 int                     nr;
417                 ListCell   *l;
418
419                 ExplainOpenGroup("Triggers", "Triggers", false, es);
420
421                 show_relname = (numrels > 1 || targrels != NIL);
422                 rInfo = queryDesc->estate->es_result_relations;
423                 for (nr = 0; nr < numrels; rInfo++, nr++)
424                         report_triggers(rInfo, show_relname, es);
425
426                 foreach(l, targrels)
427                 {
428                         rInfo = (ResultRelInfo *) lfirst(l);
429                         report_triggers(rInfo, show_relname, es);
430                 }
431
432                 ExplainCloseGroup("Triggers", "Triggers", false, es);
433         }
434
435         /*
436          * Close down the query and free resources.  Include time for this in the
437          * total runtime (although it should be pretty minimal).
438          */
439         INSTR_TIME_SET_CURRENT(starttime);
440
441         ExecutorEnd(queryDesc);
442
443         FreeQueryDesc(queryDesc);
444
445         PopActiveSnapshot();
446
447         /* We need a CCI just in case query expanded to multiple plans */
448         if (es->analyze)
449                 CommandCounterIncrement();
450
451         totaltime += elapsed_time(&starttime);
452
453         if (es->analyze)
454         {
455                 if (es->format == EXPLAIN_FORMAT_TEXT)
456                         appendStringInfo(es->str, "Total runtime: %.3f ms\n",
457                                                          1000.0 * totaltime);
458                 else
459                         ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime,
460                                                                  3, es);
461         }
462
463         ExplainCloseGroup("Query", NULL, true, es);
464 }
465
466 /*
467  * ExplainPrintPlan -
468  *        convert a QueryDesc's plan tree to text and append it to es->str
469  *
470  * The caller should have set up the options fields of *es, as well as
471  * initializing the output buffer es->str.  Other fields in *es are
472  * initialized here.
473  *
474  * NB: will not work on utility statements
475  */
476 void
477 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
478 {
479         Assert(queryDesc->plannedstmt != NULL);
480         es->pstmt = queryDesc->plannedstmt;
481         es->rtable = queryDesc->plannedstmt->rtable;
482         ExplainNode(queryDesc->plannedstmt->planTree, queryDesc->planstate,
483                                 NULL, NULL, NULL, es);
484 }
485
486 /*
487  * report_triggers -
488  *              report execution stats for a single relation's triggers
489  */
490 static void
491 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
492 {
493         int                     nt;
494
495         if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
496                 return;
497         for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
498         {
499                 Trigger    *trig = rInfo->ri_TrigDesc->triggers + nt;
500                 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
501                 char       *relname;
502                 char       *conname = NULL;
503
504                 /* Must clean up instrumentation state */
505                 InstrEndLoop(instr);
506
507                 /*
508                  * We ignore triggers that were never invoked; they likely aren't
509                  * relevant to the current query type.
510                  */
511                 if (instr->ntuples == 0)
512                         continue;
513
514                 ExplainOpenGroup("Trigger", NULL, true, es);
515
516                 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
517                 if (OidIsValid(trig->tgconstraint))
518                         conname = get_constraint_name(trig->tgconstraint);
519
520                 /*
521                  * In text format, we avoid printing both the trigger name and the
522                  * constraint name unless VERBOSE is specified.  In non-text
523                  * formats we just print everything.
524                  */
525                 if (es->format == EXPLAIN_FORMAT_TEXT)
526                 {
527                         if (es->verbose || conname == NULL)
528                                 appendStringInfo(es->str, "Trigger %s", trig->tgname);
529                         else
530                                 appendStringInfoString(es->str, "Trigger");
531                         if (conname)
532                                 appendStringInfo(es->str, " for constraint %s", conname);
533                         if (show_relname)
534                                 appendStringInfo(es->str, " on %s", relname);
535                         appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
536                                                          1000.0 * instr->total, instr->ntuples);
537                 }
538                 else
539                 {
540                         ExplainPropertyText("Trigger Name", trig->tgname, es);
541                         if (conname)
542                                 ExplainPropertyText("Constraint Name", conname, es);
543                         ExplainPropertyText("Relation", relname, es);
544                         ExplainPropertyFloat("Time", 1000.0 * instr->total, 3, es);
545                         ExplainPropertyFloat("Calls", instr->ntuples, 0, es);
546                 }
547
548                 if (conname)
549                         pfree(conname);
550
551                 ExplainCloseGroup("Trigger", NULL, true, es);
552         }
553 }
554
555 /* Compute elapsed time in seconds since given timestamp */
556 static double
557 elapsed_time(instr_time *starttime)
558 {
559         instr_time      endtime;
560
561         INSTR_TIME_SET_CURRENT(endtime);
562         INSTR_TIME_SUBTRACT(endtime, *starttime);
563         return INSTR_TIME_GET_DOUBLE(endtime);
564 }
565
566 /*
567  * ExplainNode -
568  *        Appends a description of the Plan node to es->str
569  *
570  * planstate points to the executor state node corresponding to the plan node.
571  * We need this to get at the instrumentation data (if any) as well as the
572  * list of subplans.
573  *
574  * outer_plan, if not null, references another plan node that is the outer
575  * side of a join with the current node.  This is only interesting for
576  * deciphering runtime keys of an inner indexscan.
577  *
578  * relationship describes the relationship of this plan node to its parent
579  * (eg, "Outer", "Inner"); it can be null at top level.  plan_name is an
580  * optional name to be attached to the node.
581  *
582  * In text format, es->indent is controlled in this function since we only
583  * want it to change at Plan-node boundaries.  In non-text formats, es->indent
584  * corresponds to the nesting depth of logical output groups, and therefore
585  * is controlled by ExplainOpenGroup/ExplainCloseGroup.
586  */
587 static void
588 ExplainNode(Plan *plan, PlanState *planstate,
589                         Plan *outer_plan,
590                         const char *relationship, const char *plan_name,
591                         ExplainState *es)
592 {
593         const char *pname;                      /* node type name for text output */
594         const char *sname;                      /* node type name for non-text output */
595         const char *strategy = NULL;
596         const char *operation = NULL;
597         int                     save_indent = es->indent;
598         bool            haschildren;
599
600         Assert(plan);
601
602         switch (nodeTag(plan))
603         {
604                 case T_Result:
605                         pname = sname = "Result";
606                         break;
607                 case T_ModifyTable:
608                         sname = "ModifyTable";
609                         switch (((ModifyTable *) plan)->operation)
610                         {
611                                 case CMD_INSERT:
612                                         pname = operation = "Insert";
613                                         break;
614                                 case CMD_UPDATE:
615                                         pname = operation = "Update";
616                                         break;
617                                 case CMD_DELETE:
618                                         pname = operation = "Delete";
619                                         break;
620                                 default:
621                                         pname = "???";
622                                         break;
623                         }
624                         break;
625                 case T_Append:
626                         pname = sname = "Append";
627                         break;
628                 case T_RecursiveUnion:
629                         pname = sname = "Recursive Union";
630                         break;
631                 case T_BitmapAnd:
632                         pname = sname = "BitmapAnd";
633                         break;
634                 case T_BitmapOr:
635                         pname = sname = "BitmapOr";
636                         break;
637                 case T_NestLoop:
638                         pname = sname = "Nested Loop";
639                         break;
640                 case T_MergeJoin:
641                         pname = "Merge";                /* "Join" gets added by jointype switch */
642                         sname = "Merge Join";
643                         break;
644                 case T_HashJoin:
645                         pname = "Hash";                 /* "Join" gets added by jointype switch */
646                         sname = "Hash Join";
647                         break;
648                 case T_SeqScan:
649                         pname = sname = "Seq Scan";
650                         break;
651                 case T_IndexScan:
652                         pname = sname = "Index Scan";
653                         break;
654                 case T_BitmapIndexScan:
655                         pname = sname = "Bitmap Index Scan";
656                         break;
657                 case T_BitmapHeapScan:
658                         pname = sname = "Bitmap Heap Scan";
659                         break;
660                 case T_TidScan:
661                         pname = sname = "Tid Scan";
662                         break;
663                 case T_SubqueryScan:
664                         pname = sname = "Subquery Scan";
665                         break;
666                 case T_FunctionScan:
667                         pname = sname = "Function Scan";
668                         break;
669                 case T_ValuesScan:
670                         pname = sname = "Values Scan";
671                         break;
672                 case T_CteScan:
673                         pname = sname = "CTE Scan";
674                         break;
675                 case T_WorkTableScan:
676                         pname = sname = "WorkTable Scan";
677                         break;
678                 case T_Material:
679                         pname = sname = "Materialize";
680                         break;
681                 case T_Sort:
682                         pname = sname = "Sort";
683                         break;
684                 case T_Group:
685                         pname = sname = "Group";
686                         break;
687                 case T_Agg:
688                         sname = "Aggregate";
689                         switch (((Agg *) plan)->aggstrategy)
690                         {
691                                 case AGG_PLAIN:
692                                         pname = "Aggregate";
693                                         strategy = "Plain";
694                                         break;
695                                 case AGG_SORTED:
696                                         pname = "GroupAggregate";
697                                         strategy = "Sorted";
698                                         break;
699                                 case AGG_HASHED:
700                                         pname = "HashAggregate";
701                                         strategy = "Hashed";
702                                         break;
703                                 default:
704                                         pname = "Aggregate ???";
705                                         strategy = "???";
706                                         break;
707                         }
708                         break;
709                 case T_WindowAgg:
710                         pname = sname = "WindowAgg";
711                         break;
712                 case T_Unique:
713                         pname = sname = "Unique";
714                         break;
715                 case T_SetOp:
716                         sname = "SetOp";
717                         switch (((SetOp *) plan)->strategy)
718                         {
719                                 case SETOP_SORTED:
720                                         pname = "SetOp";
721                                         strategy = "Sorted";
722                                         break;
723                                 case SETOP_HASHED:
724                                         pname = "HashSetOp";
725                                         strategy = "Hashed";
726                                         break;
727                                 default:
728                                         pname = "SetOp ???";
729                                         strategy = "???";
730                                         break;
731                         }
732                         break;
733                 case T_LockRows:
734                         pname = sname = "LockRows";
735                         break;
736                 case T_Limit:
737                         pname = sname = "Limit";
738                         break;
739                 case T_Hash:
740                         pname = sname = "Hash";
741                         break;
742                 default:
743                         pname = sname = "???";
744                         break;
745         }
746
747         ExplainOpenGroup("Plan",
748                                          relationship ? NULL : "Plan",
749                                          true, es);
750
751         if (es->format == EXPLAIN_FORMAT_TEXT)
752         {
753                 if (plan_name)
754                 {
755                         appendStringInfoSpaces(es->str, es->indent * 2);
756                         appendStringInfo(es->str, "%s\n", plan_name);
757                         es->indent++;
758                 }
759                 if (es->indent)
760                 {
761                         appendStringInfoSpaces(es->str, es->indent * 2);
762                         appendStringInfoString(es->str, "->  ");
763                         es->indent += 2;
764                 }
765                 appendStringInfoString(es->str, pname);
766                 es->indent++;
767         }
768         else
769         {
770                 ExplainPropertyText("Node Type", sname, es);
771                 if (strategy)
772                         ExplainPropertyText("Strategy", strategy, es);
773                 if (operation)
774                         ExplainPropertyText("Operation", operation, es);
775                 if (relationship)
776                         ExplainPropertyText("Parent Relationship", relationship, es);
777                 if (plan_name)
778                         ExplainPropertyText("Subplan Name", plan_name, es);
779         }
780
781         switch (nodeTag(plan))
782         {
783                 case T_IndexScan:
784                         {
785                                 IndexScan *indexscan = (IndexScan *) plan;
786                                 const char *indexname =
787                                         explain_get_index_name(indexscan->indexid);
788
789                                 if (es->format == EXPLAIN_FORMAT_TEXT)
790                                 {
791                                         if (ScanDirectionIsBackward(indexscan->indexorderdir))
792                                                 appendStringInfoString(es->str, " Backward");
793                                         appendStringInfo(es->str, " using %s", indexname);
794                                 }
795                                 else
796                                 {
797                                         const char *scandir;
798
799                                         switch (indexscan->indexorderdir)
800                                         {
801                                                 case BackwardScanDirection:
802                                                         scandir = "Backward";
803                                                         break;
804                                                 case NoMovementScanDirection:
805                                                         scandir = "NoMovement";
806                                                         break;
807                                                 case ForwardScanDirection:
808                                                         scandir = "Forward";
809                                                         break;
810                                                 default:
811                                                         scandir = "???";
812                                                         break;
813                                         }
814                                         ExplainPropertyText("Scan Direction", scandir, es);
815                                         ExplainPropertyText("Index Name", indexname, es);
816                                 }
817                         }
818                         /* FALL THRU */
819                 case T_SeqScan:
820                 case T_BitmapHeapScan:
821                 case T_TidScan:
822                 case T_SubqueryScan:
823                 case T_FunctionScan:
824                 case T_ValuesScan:
825                 case T_CteScan:
826                 case T_WorkTableScan:
827                         ExplainScanTarget((Scan *) plan, es);
828                         break;
829                 case T_BitmapIndexScan:
830                         {
831                                 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
832                                 const char *indexname =
833                                         explain_get_index_name(bitmapindexscan->indexid);
834
835                                 if (es->format == EXPLAIN_FORMAT_TEXT)
836                                         appendStringInfo(es->str, " on %s", indexname);
837                                 else
838                                         ExplainPropertyText("Index Name", indexname, es);
839                         }
840                         break;
841                 case T_NestLoop:
842                 case T_MergeJoin:
843                 case T_HashJoin:
844                         {
845                                 const char *jointype;
846
847                                 switch (((Join *) plan)->jointype)
848                                 {
849                                         case JOIN_INNER:
850                                                 jointype = "Inner";
851                                                 break;
852                                         case JOIN_LEFT:
853                                                 jointype = "Left";
854                                                 break;
855                                         case JOIN_FULL:
856                                                 jointype = "Full";
857                                                 break;
858                                         case JOIN_RIGHT:
859                                                 jointype = "Right";
860                                                 break;
861                                         case JOIN_SEMI:
862                                                 jointype = "Semi";
863                                                 break;
864                                         case JOIN_ANTI:
865                                                 jointype = "Anti";
866                                                 break;
867                                         default:
868                                                 jointype = "???";
869                                                 break;
870                                 }
871                                 if (es->format == EXPLAIN_FORMAT_TEXT)
872                                 {
873                                         /*
874                                          * For historical reasons, the join type is interpolated
875                                          * into the node type name...
876                                          */
877                                         if (((Join *) plan)->jointype != JOIN_INNER)
878                                                 appendStringInfo(es->str, " %s Join", jointype);
879                                         else if (!IsA(plan, NestLoop))
880                                                 appendStringInfo(es->str, " Join");
881                                 }
882                                 else
883                                         ExplainPropertyText("Join Type", jointype, es);
884                         }
885                         break;
886                 case T_SetOp:
887                         {
888                                 const char *setopcmd;
889
890                                 switch (((SetOp *) plan)->cmd)
891                                 {
892                                         case SETOPCMD_INTERSECT:
893                                                 setopcmd = "Intersect";
894                                                 break;
895                                         case SETOPCMD_INTERSECT_ALL:
896                                                 setopcmd = "Intersect All";
897                                                 break;
898                                         case SETOPCMD_EXCEPT:
899                                                 setopcmd = "Except";
900                                                 break;
901                                         case SETOPCMD_EXCEPT_ALL:
902                                                 setopcmd = "Except All";
903                                                 break;
904                                         default:
905                                                 setopcmd = "???";
906                                                 break;
907                                 }
908                                 if (es->format == EXPLAIN_FORMAT_TEXT)
909                                         appendStringInfo(es->str, " %s", setopcmd);
910                                 else
911                                         ExplainPropertyText("Command", setopcmd, es);
912                         }
913                         break;
914                 default:
915                         break;
916         }
917
918         if (es->costs)
919         {
920                 if (es->format == EXPLAIN_FORMAT_TEXT)
921                 {
922                         appendStringInfo(es->str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
923                                                          plan->startup_cost, plan->total_cost,
924                                                          plan->plan_rows, plan->plan_width);
925                 }
926                 else
927                 {
928                         ExplainPropertyFloat("Startup Cost", plan->startup_cost, 2, es);
929                         ExplainPropertyFloat("Total Cost", plan->total_cost, 2, es);
930                         ExplainPropertyFloat("Plan Rows", plan->plan_rows, 0, es);
931                         ExplainPropertyInteger("Plan Width", plan->plan_width, es);
932                 }
933         }
934
935         /*
936          * We have to forcibly clean up the instrumentation state because we
937          * haven't done ExecutorEnd yet.  This is pretty grotty ...
938          */
939         if (planstate->instrument)
940                 InstrEndLoop(planstate->instrument);
941
942         if (planstate->instrument && planstate->instrument->nloops > 0)
943         {
944                 double          nloops = planstate->instrument->nloops;
945                 double          startup_sec = 1000.0 * planstate->instrument->startup / nloops;
946                 double          total_sec = 1000.0 * planstate->instrument->total / nloops;
947                 double          rows = planstate->instrument->ntuples / nloops;
948
949                 if (es->format == EXPLAIN_FORMAT_TEXT)
950                 {
951                         appendStringInfo(es->str,
952                                                          " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
953                                                          startup_sec, total_sec, rows, nloops);
954                 }
955                 else
956                 {
957                         ExplainPropertyFloat("Actual Startup Time", startup_sec, 3, es);
958                         ExplainPropertyFloat("Actual Total Time", total_sec, 3, es);
959                         ExplainPropertyFloat("Actual Rows", rows, 0, es);
960                         ExplainPropertyFloat("Actual Loops", nloops, 0, es);
961                 }
962         }
963         else if (es->analyze)
964         {
965                 if (es->format == EXPLAIN_FORMAT_TEXT)
966                         appendStringInfo(es->str, " (never executed)");
967                 else
968                 {
969                         ExplainPropertyFloat("Actual Startup Time", 0.0, 3, es);
970                         ExplainPropertyFloat("Actual Total Time", 0.0, 3, es);
971                         ExplainPropertyFloat("Actual Rows", 0.0, 0, es);
972                         ExplainPropertyFloat("Actual Loops", 0.0, 0, es);
973                 }
974         }
975
976         /* in text format, first line ends here */
977         if (es->format == EXPLAIN_FORMAT_TEXT)
978                 appendStringInfoChar(es->str, '\n');
979
980         /* target list */
981         if (es->verbose)
982                 show_plan_tlist(plan, es);
983
984         /* quals, sort keys, etc */
985         switch (nodeTag(plan))
986         {
987                 case T_IndexScan:
988                         show_scan_qual(((IndexScan *) plan)->indexqualorig,
989                                                    "Index Cond", plan, outer_plan, es);
990                         show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
991                         break;
992                 case T_BitmapIndexScan:
993                         show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
994                                                    "Index Cond", plan, outer_plan, es);
995                         break;
996                 case T_BitmapHeapScan:
997                         show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
998                                                    "Recheck Cond", plan, outer_plan, es);
999                         /* FALL THRU */
1000                 case T_SeqScan:
1001                 case T_FunctionScan:
1002                 case T_ValuesScan:
1003                 case T_CteScan:
1004                 case T_WorkTableScan:
1005                 case T_SubqueryScan:
1006                         show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
1007                         break;
1008                 case T_TidScan:
1009                         {
1010                                 /*
1011                                  * The tidquals list has OR semantics, so be sure to show it
1012                                  * as an OR condition.
1013                                  */
1014                                 List       *tidquals = ((TidScan *) plan)->tidquals;
1015
1016                                 if (list_length(tidquals) > 1)
1017                                         tidquals = list_make1(make_orclause(tidquals));
1018                                 show_scan_qual(tidquals, "TID Cond", plan, outer_plan, es);
1019                                 show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
1020                         }
1021                         break;
1022                 case T_NestLoop:
1023                         show_upper_qual(((NestLoop *) plan)->join.joinqual,
1024                                                         "Join Filter", plan, es);
1025                         show_upper_qual(plan->qual, "Filter", plan, es);
1026                         break;
1027                 case T_MergeJoin:
1028                         show_upper_qual(((MergeJoin *) plan)->mergeclauses,
1029                                                         "Merge Cond", plan, es);
1030                         show_upper_qual(((MergeJoin *) plan)->join.joinqual,
1031                                                         "Join Filter", plan, es);
1032                         show_upper_qual(plan->qual, "Filter", plan, es);
1033                         break;
1034                 case T_HashJoin:
1035                         show_upper_qual(((HashJoin *) plan)->hashclauses,
1036                                                         "Hash Cond", plan, es);
1037                         show_upper_qual(((HashJoin *) plan)->join.joinqual,
1038                                                         "Join Filter", plan, es);
1039                         show_upper_qual(plan->qual, "Filter", plan, es);
1040                         break;
1041                 case T_Agg:
1042                 case T_Group:
1043                         show_upper_qual(plan->qual, "Filter", plan, es);
1044                         break;
1045                 case T_Sort:
1046                         show_sort_keys(plan, es);
1047                         show_sort_info((SortState *) planstate, es);
1048                         break;
1049                 case T_Result:
1050                         show_upper_qual((List *) ((Result *) plan)->resconstantqual,
1051                                                         "One-Time Filter", plan, es);
1052                         show_upper_qual(plan->qual, "Filter", plan, es);
1053                         break;
1054                 default:
1055                         break;
1056         }
1057
1058         /* Show buffer usage */
1059         if (es->buffers)
1060         {
1061                 const BufferUsage *usage = &planstate->instrument->bufusage;
1062
1063                 if (es->format == EXPLAIN_FORMAT_TEXT)
1064                 {
1065                         bool    has_shared = (usage->shared_blks_hit > 0 ||
1066                                                                   usage->shared_blks_read > 0 ||
1067                                                                   usage->shared_blks_written);
1068                         bool    has_local = (usage->local_blks_hit > 0 ||
1069                                                                  usage->local_blks_read > 0 ||
1070                                                                  usage->local_blks_written);
1071                         bool    has_temp = (usage->temp_blks_read > 0 ||
1072                                                                 usage->temp_blks_written);
1073
1074                         /* Show only positive counter values. */
1075                         if (has_shared || has_local || has_temp)
1076                         {
1077                                 appendStringInfoSpaces(es->str, es->indent * 2);
1078                                 appendStringInfoString(es->str, "Buffers:");
1079
1080                                 if (has_shared)
1081                                 {
1082                                         appendStringInfoString(es->str, " shared");
1083                                         if (usage->shared_blks_hit > 0)
1084                                                 appendStringInfo(es->str, " hit=%ld",
1085                                                         usage->shared_blks_hit);
1086                                         if (usage->shared_blks_read > 0)
1087                                                 appendStringInfo(es->str, " read=%ld",
1088                                                         usage->shared_blks_read);
1089                                         if (usage->shared_blks_written > 0)
1090                                                 appendStringInfo(es->str, " written=%ld",
1091                                                         usage->shared_blks_written);
1092                                         if (has_local || has_temp)
1093                                                 appendStringInfoChar(es->str, ',');
1094                                 }
1095                                 if (has_local)
1096                                 {
1097                                         appendStringInfoString(es->str, " local");
1098                                         if (usage->local_blks_hit > 0)
1099                                                 appendStringInfo(es->str, " hit=%ld",
1100                                                         usage->local_blks_hit);
1101                                         if (usage->local_blks_read > 0)
1102                                                 appendStringInfo(es->str, " read=%ld",
1103                                                         usage->local_blks_read);
1104                                         if (usage->local_blks_written > 0)
1105                                                 appendStringInfo(es->str, " written=%ld",
1106                                                         usage->local_blks_written);
1107                                         if (has_temp)
1108                                                 appendStringInfoChar(es->str, ',');
1109                                 }
1110                                 if (has_temp)
1111                                 {
1112                                         appendStringInfoString(es->str, " temp");
1113                                         if (usage->temp_blks_read > 0)
1114                                                 appendStringInfo(es->str, " read=%ld",
1115                                                         usage->temp_blks_read);
1116                                         if (usage->temp_blks_written > 0)
1117                                                 appendStringInfo(es->str, " written=%ld",
1118                                                         usage->temp_blks_written);
1119                                 }
1120                                 appendStringInfoChar(es->str, '\n');
1121                         }
1122                 }
1123                 else
1124                 {
1125                         ExplainPropertyLong("Shared Hit Blocks", usage->shared_blks_hit, es);
1126                         ExplainPropertyLong("Shared Read Blocks", usage->shared_blks_read, es);
1127                         ExplainPropertyLong("Shared Written Blocks", usage->shared_blks_written, es);
1128                         ExplainPropertyLong("Local Hit Blocks", usage->local_blks_hit, es);
1129                         ExplainPropertyLong("Local Read Blocks", usage->local_blks_read, es);
1130                         ExplainPropertyLong("Local Written Blocks", usage->local_blks_written, es);
1131                         ExplainPropertyLong("Temp Read Blocks", usage->temp_blks_read, es);
1132                         ExplainPropertyLong("Temp Written Blocks", usage->temp_blks_written, es);
1133                 }
1134         }
1135
1136         /* Get ready to display the child plans */
1137         haschildren = plan->initPlan ||
1138                 outerPlan(plan) ||
1139                 innerPlan(plan) ||
1140                 IsA(plan, ModifyTable) ||
1141                 IsA(plan, Append) ||
1142                 IsA(plan, BitmapAnd) ||
1143                 IsA(plan, BitmapOr) ||
1144                 IsA(plan, SubqueryScan) ||
1145                 planstate->subPlan;
1146         if (haschildren)
1147                 ExplainOpenGroup("Plans", "Plans", false, es);
1148
1149         /* initPlan-s */
1150         if (plan->initPlan)
1151                 ExplainSubPlans(planstate->initPlan, "InitPlan", es);
1152
1153         /* lefttree */
1154         if (outerPlan(plan))
1155         {
1156                 /*
1157                  * Ordinarily we don't pass down our own outer_plan value to our child
1158                  * nodes, but in bitmap scan trees we must, since the bottom
1159                  * BitmapIndexScan nodes may have outer references.
1160                  */
1161                 ExplainNode(outerPlan(plan), outerPlanState(planstate),
1162                                         IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
1163                                         "Outer", NULL, es);
1164         }
1165
1166         /* righttree */
1167         if (innerPlan(plan))
1168         {
1169                 ExplainNode(innerPlan(plan), innerPlanState(planstate),
1170                                         outerPlan(plan),
1171                                         "Inner", NULL, es);
1172         }
1173
1174         /* special child plans */
1175         switch (nodeTag(plan))
1176         {
1177                 case T_ModifyTable:
1178                         ExplainMemberNodes(((ModifyTable *) plan)->plans,
1179                                                            ((ModifyTableState *) planstate)->mt_plans,
1180                                                            outer_plan, es);
1181                         break;
1182                 case T_Append:
1183                         ExplainMemberNodes(((Append *) plan)->appendplans,
1184                                                            ((AppendState *) planstate)->appendplans,
1185                                                            outer_plan, es);
1186                         break;
1187                 case T_BitmapAnd:
1188                         ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
1189                                                            ((BitmapAndState *) planstate)->bitmapplans,
1190                                                            outer_plan, es);
1191                         break;
1192                 case T_BitmapOr:
1193                         ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
1194                                                            ((BitmapOrState *) planstate)->bitmapplans,
1195                                                            outer_plan, es);
1196                         break;
1197                 case T_SubqueryScan:
1198                         {
1199                                 SubqueryScan *subqueryscan = (SubqueryScan *) plan;
1200                                 SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
1201
1202                                 ExplainNode(subqueryscan->subplan, subquerystate->subplan,
1203                                                         NULL,
1204                                                         "Subquery", NULL, es);
1205                         }
1206                         break;
1207                 default:
1208                         break;
1209         }
1210
1211         /* subPlan-s */
1212         if (planstate->subPlan)
1213                 ExplainSubPlans(planstate->subPlan, "SubPlan", es);
1214
1215         /* end of child plans */
1216         if (haschildren)
1217                 ExplainCloseGroup("Plans", "Plans", false, es);
1218
1219         /* in text format, undo whatever indentation we added */
1220         if (es->format == EXPLAIN_FORMAT_TEXT)
1221                 es->indent = save_indent;
1222
1223         ExplainCloseGroup("Plan",
1224                                           relationship ? NULL : "Plan",
1225                                           true, es);
1226 }
1227
1228 /*
1229  * Show the targetlist of a plan node
1230  */
1231 static void
1232 show_plan_tlist(Plan *plan, ExplainState *es)
1233 {
1234         List       *context;
1235         List       *result = NIL;
1236         bool            useprefix;
1237         ListCell   *lc;
1238         int                     i;
1239
1240         /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1241         if (plan->targetlist == NIL)
1242                 return;
1243         /* The tlist of an Append isn't real helpful, so suppress it */
1244         if (IsA(plan, Append))
1245                 return;
1246         /* Likewise for RecursiveUnion */
1247         if (IsA(plan, RecursiveUnion))
1248                 return;
1249
1250         /* Set up deparsing context */
1251         context = deparse_context_for_plan((Node *) plan,
1252                                                                            NULL,
1253                                                                            es->rtable,
1254                                                                            es->pstmt->subplans);
1255         useprefix = list_length(es->rtable) > 1;
1256
1257         /* Deparse each result column (we now include resjunk ones) */
1258         i = 0;
1259         foreach(lc, plan->targetlist)
1260         {
1261                 TargetEntry *tle = (TargetEntry *) lfirst(lc);
1262
1263                 result = lappend(result,
1264                                              deparse_expression((Node *) tle->expr, context,
1265                                                                                         useprefix, false));
1266         }
1267
1268         /* Print results */
1269         ExplainPropertyList("Output", result, es);
1270 }
1271
1272 /*
1273  * Show a qualifier expression
1274  *
1275  * Note: outer_plan is the referent for any OUTER vars in the scan qual;
1276  * this would be the outer side of a nestloop plan.  Pass NULL if none.
1277  */
1278 static void
1279 show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
1280                   bool useprefix, ExplainState *es)
1281 {
1282         List       *context;
1283         Node       *node;
1284         char       *exprstr;
1285
1286         /* No work if empty qual */
1287         if (qual == NIL)
1288                 return;
1289
1290         /* Convert AND list to explicit AND */
1291         node = (Node *) make_ands_explicit(qual);
1292
1293         /* Set up deparsing context */
1294         context = deparse_context_for_plan((Node *) plan,
1295                                                                            (Node *) outer_plan,
1296                                                                            es->rtable,
1297                                                                            es->pstmt->subplans);
1298
1299         /* Deparse the expression */
1300         exprstr = deparse_expression(node, context, useprefix, false);
1301
1302         /* And add to es->str */
1303         ExplainPropertyText(qlabel, exprstr, es);
1304 }
1305
1306 /*
1307  * Show a qualifier expression for a scan plan node
1308  */
1309 static void
1310 show_scan_qual(List *qual, const char *qlabel,
1311                            Plan *scan_plan, Plan *outer_plan,
1312                            ExplainState *es)
1313 {
1314         bool            useprefix;
1315
1316         useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
1317                                  es->verbose);
1318         show_qual(qual, qlabel, scan_plan, outer_plan, useprefix, es);
1319 }
1320
1321 /*
1322  * Show a qualifier expression for an upper-level plan node
1323  */
1324 static void
1325 show_upper_qual(List *qual, const char *qlabel, Plan *plan, ExplainState *es)
1326 {
1327         bool            useprefix;
1328
1329         useprefix = (list_length(es->rtable) > 1 || es->verbose);
1330         show_qual(qual, qlabel, plan, NULL, useprefix, es);
1331 }
1332
1333 /*
1334  * Show the sort keys for a Sort node.
1335  */
1336 static void
1337 show_sort_keys(Plan *sortplan, ExplainState *es)
1338 {
1339         int                     nkeys = ((Sort *) sortplan)->numCols;
1340         AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
1341         List       *context;
1342         List       *result = NIL;
1343         bool            useprefix;
1344         int                     keyno;
1345         char       *exprstr;
1346
1347         if (nkeys <= 0)
1348                 return;
1349
1350         /* Set up deparsing context */
1351         context = deparse_context_for_plan((Node *) sortplan,
1352                                                                            NULL,
1353                                                                            es->rtable,
1354                                                                            es->pstmt->subplans);
1355         useprefix = (list_length(es->rtable) > 1 || es->verbose);
1356
1357         for (keyno = 0; keyno < nkeys; keyno++)
1358         {
1359                 /* find key expression in tlist */
1360                 AttrNumber      keyresno = keycols[keyno];
1361                 TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);
1362
1363                 if (!target)
1364                         elog(ERROR, "no tlist entry for key %d", keyresno);
1365                 /* Deparse the expression, showing any top-level cast */
1366                 exprstr = deparse_expression((Node *) target->expr, context,
1367                                                                          useprefix, true);
1368                 result = lappend(result, exprstr);
1369         }
1370
1371         ExplainPropertyList("Sort Key", result, es);
1372 }
1373
1374 /*
1375  * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
1376  */
1377 static void
1378 show_sort_info(SortState *sortstate, ExplainState *es)
1379 {
1380         Assert(IsA(sortstate, SortState));
1381         if (es->analyze && sortstate->sort_Done &&
1382                 sortstate->tuplesortstate != NULL)
1383         {
1384                 Tuplesortstate  *state = (Tuplesortstate *) sortstate->tuplesortstate;
1385                 const char *sortMethod;
1386                 const char *spaceType;
1387                 long            spaceUsed;
1388
1389                 tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
1390
1391                 if (es->format == EXPLAIN_FORMAT_TEXT)
1392                 {
1393                         appendStringInfoSpaces(es->str, es->indent * 2);
1394                         appendStringInfo(es->str, "Sort Method:  %s  %s: %ldkB\n",
1395                                                          sortMethod, spaceType, spaceUsed);
1396                 }
1397                 else
1398                 {
1399                         ExplainPropertyText("Sort Method", sortMethod, es);
1400                         ExplainPropertyLong("Sort Space Used", spaceUsed, es);
1401                         ExplainPropertyText("Sort Space Type", spaceType, es);
1402                 }
1403         }
1404 }
1405
1406 /*
1407  * Fetch the name of an index in an EXPLAIN
1408  *
1409  * We allow plugins to get control here so that plans involving hypothetical
1410  * indexes can be explained.
1411  */
1412 static const char *
1413 explain_get_index_name(Oid indexId)
1414 {
1415         const char *result;
1416
1417         if (explain_get_index_name_hook)
1418                 result = (*explain_get_index_name_hook) (indexId);
1419         else
1420                 result = NULL;
1421         if (result == NULL)
1422         {
1423                 /* default behavior: look in the catalogs and quote it */
1424                 result = get_rel_name(indexId);
1425                 if (result == NULL)
1426                         elog(ERROR, "cache lookup failed for index %u", indexId);
1427                 result = quote_identifier(result);
1428         }
1429         return result;
1430 }
1431
1432 /*
1433  * Show the target of a Scan node
1434  */
1435 static void
1436 ExplainScanTarget(Scan *plan, ExplainState *es)
1437 {
1438         char       *objectname = NULL;
1439         char       *namespace = NULL;
1440         const char *objecttag = NULL;
1441         RangeTblEntry *rte;
1442
1443         if (plan->scanrelid <= 0)       /* Is this still possible? */
1444                 return;
1445         rte = rt_fetch(plan->scanrelid, es->rtable);
1446
1447         switch (nodeTag(plan))
1448         {
1449                 case T_SeqScan:
1450                 case T_IndexScan:
1451                 case T_BitmapHeapScan:
1452                 case T_TidScan:
1453                         /* Assert it's on a real relation */
1454                         Assert(rte->rtekind == RTE_RELATION);
1455                         objectname = get_rel_name(rte->relid);
1456                         if (es->verbose)
1457                                 namespace = get_namespace_name(get_rel_namespace(rte->relid));
1458                         objecttag = "Relation Name";
1459                         break;
1460                 case T_FunctionScan:
1461                         {
1462                                 Node       *funcexpr;
1463
1464                                 /* Assert it's on a RangeFunction */
1465                                 Assert(rte->rtekind == RTE_FUNCTION);
1466
1467                                 /*
1468                                  * If the expression is still a function call, we can get the
1469                                  * real name of the function.  Otherwise, punt (this can
1470                                  * happen if the optimizer simplified away the function call,
1471                                  * for example).
1472                                  */
1473                                 funcexpr = ((FunctionScan *) plan)->funcexpr;
1474                                 if (funcexpr && IsA(funcexpr, FuncExpr))
1475                                 {
1476                                         Oid                     funcid = ((FuncExpr *) funcexpr)->funcid;
1477
1478                                         objectname = get_func_name(funcid);
1479                                         if (es->verbose)
1480                                                 namespace =
1481                                                         get_namespace_name(get_func_namespace(funcid));
1482                                 }
1483                                 objecttag = "Function Name";
1484                         }
1485                         break;
1486                 case T_ValuesScan:
1487                         Assert(rte->rtekind == RTE_VALUES);
1488                         break;
1489                 case T_CteScan:
1490                         /* Assert it's on a non-self-reference CTE */
1491                         Assert(rte->rtekind == RTE_CTE);
1492                         Assert(!rte->self_reference);
1493                         objectname = rte->ctename;
1494                         objecttag = "CTE Name";
1495                         break;
1496                 case T_WorkTableScan:
1497                         /* Assert it's on a self-reference CTE */
1498                         Assert(rte->rtekind == RTE_CTE);
1499                         Assert(rte->self_reference);
1500                         objectname = rte->ctename;
1501                         objecttag = "CTE Name";
1502                         break;
1503                 default:
1504                         break;
1505         }
1506
1507         if (es->format == EXPLAIN_FORMAT_TEXT)
1508         {
1509                 appendStringInfoString(es->str, " on");
1510                 if (namespace != NULL)
1511                         appendStringInfo(es->str, " %s.%s", quote_identifier(namespace),
1512                                                          quote_identifier(objectname));
1513                 else if (objectname != NULL)
1514                         appendStringInfo(es->str, " %s", quote_identifier(objectname));
1515                 if (objectname == NULL ||
1516                         strcmp(rte->eref->aliasname, objectname) != 0)
1517                         appendStringInfo(es->str, " %s",
1518                                                          quote_identifier(rte->eref->aliasname));
1519         }
1520         else
1521         {
1522                 if (objecttag != NULL && objectname != NULL)
1523                         ExplainPropertyText(objecttag, objectname, es);
1524                 if (namespace != NULL)
1525                         ExplainPropertyText("Schema", namespace, es);
1526                 ExplainPropertyText("Alias", rte->eref->aliasname, es);
1527         }
1528 }
1529
1530 /*
1531  * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
1532  * or BitmapOr node.
1533  *
1534  * Ordinarily we don't pass down outer_plan to our child nodes, but in these
1535  * cases we must, since the node could be an "inner indexscan" in which case
1536  * outer references can appear in the child nodes.
1537  */
1538 static void
1539 ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
1540                            ExplainState *es)
1541 {
1542         ListCell   *lst;
1543         int                     j = 0;
1544
1545         foreach(lst, plans)
1546         {
1547                 Plan       *subnode = (Plan *) lfirst(lst);
1548
1549                 ExplainNode(subnode, planstate[j],
1550                                         outer_plan,
1551                                         "Member", NULL,
1552                                         es);
1553                 j++;
1554         }
1555 }
1556
1557 /*
1558  * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
1559  */
1560 static void
1561 ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
1562 {
1563         ListCell   *lst;
1564
1565         foreach(lst, plans)
1566         {
1567                 SubPlanState *sps = (SubPlanState *) lfirst(lst);
1568                 SubPlan    *sp = (SubPlan *) sps->xprstate.expr;
1569
1570                 ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
1571                                         sps->planstate,
1572                                         NULL,
1573                                         relationship, sp->plan_name,
1574                                         es);
1575         }
1576 }
1577
1578 /*
1579  * Explain a property, such as sort keys or targets, that takes the form of
1580  * a list of unlabeled items.  "data" is a list of C strings.
1581  */
1582 static void
1583 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
1584 {
1585         ListCell   *lc;
1586         bool            first = true;
1587
1588         switch (es->format)
1589         {
1590                 case EXPLAIN_FORMAT_TEXT:
1591                         appendStringInfoSpaces(es->str, es->indent * 2);
1592                         appendStringInfo(es->str, "%s: ", qlabel);
1593                         foreach(lc, data)
1594                         {
1595                                 if (!first)
1596                                         appendStringInfoString(es->str, ", ");
1597                                 appendStringInfoString(es->str, (const char *) lfirst(lc));
1598                                 first = false;
1599                         }
1600                         appendStringInfoChar(es->str, '\n');
1601                         break;
1602
1603                 case EXPLAIN_FORMAT_XML:
1604                         ExplainXMLTag(qlabel, X_OPENING, es);
1605                         foreach(lc, data)
1606                         {
1607                                 char   *str;
1608
1609                                 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
1610                                 appendStringInfoString(es->str, "<Item>");
1611                                 str = escape_xml((const char *) lfirst(lc));
1612                                 appendStringInfoString(es->str, str);
1613                                 pfree(str);
1614                                 appendStringInfoString(es->str, "</Item>\n");
1615                         }
1616                         ExplainXMLTag(qlabel, X_CLOSING, es);
1617                         break;
1618
1619                 case EXPLAIN_FORMAT_JSON:
1620                         ExplainJSONLineEnding(es);
1621                         appendStringInfoSpaces(es->str, es->indent * 2);
1622                         escape_json(es->str, qlabel);
1623                         appendStringInfoString(es->str, ": [");
1624                         foreach(lc, data)
1625                         {
1626                                 if (!first)
1627                                         appendStringInfoString(es->str, ", ");
1628                                 escape_json(es->str, (const char *) lfirst(lc));
1629                                 first = false;
1630                         }
1631                         appendStringInfoChar(es->str, ']');
1632                         break;
1633
1634                 case EXPLAIN_FORMAT_YAML:
1635                         ExplainYAMLLineStarting(es);
1636                         escape_yaml(es->str, qlabel);
1637                         appendStringInfoChar(es->str, ':');
1638                         foreach(lc, data)
1639                         {
1640                                 appendStringInfoChar(es->str, '\n');
1641                                 appendStringInfoSpaces(es->str, es->indent * 2 + 2);
1642                                 appendStringInfoString(es->str, "- ");
1643                                 escape_yaml(es->str, (const char *) lfirst(lc));
1644                         }
1645                         break;
1646         }
1647 }
1648
1649 /*
1650  * Explain a simple property.
1651  *
1652  * If "numeric" is true, the value is a number (or other value that
1653  * doesn't need quoting in JSON).
1654  *
1655  * This usually should not be invoked directly, but via one of the datatype
1656  * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
1657  */
1658 static void
1659 ExplainProperty(const char *qlabel, const char *value, bool numeric,
1660                                 ExplainState *es)
1661 {
1662         switch (es->format)
1663         {
1664                 case EXPLAIN_FORMAT_TEXT:
1665                         appendStringInfoSpaces(es->str, es->indent * 2);
1666                         appendStringInfo(es->str, "%s: %s\n", qlabel, value);
1667                         break;
1668
1669                 case EXPLAIN_FORMAT_XML:
1670                         {
1671                                 char   *str;
1672
1673                                 appendStringInfoSpaces(es->str, es->indent * 2);
1674                                 ExplainXMLTag(qlabel, X_OPENING | X_NOWHITESPACE, es);
1675                                 str = escape_xml(value);
1676                                 appendStringInfoString(es->str, str);
1677                                 pfree(str);
1678                                 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
1679                                 appendStringInfoChar(es->str, '\n');
1680                         }
1681                         break;
1682
1683                 case EXPLAIN_FORMAT_JSON:
1684                         ExplainJSONLineEnding(es);
1685                         appendStringInfoSpaces(es->str, es->indent * 2);
1686                         escape_json(es->str, qlabel);
1687                         appendStringInfoString(es->str, ": ");
1688                         if (numeric)
1689                                 appendStringInfoString(es->str, value);
1690                         else
1691                                 escape_json(es->str, value);
1692                         break;
1693
1694                 case EXPLAIN_FORMAT_YAML:
1695                         ExplainYAMLLineStarting(es);
1696                         appendStringInfo(es->str, "%s: ", qlabel);
1697                         escape_yaml(es->str, value);
1698                         break;
1699         }
1700 }
1701
1702 /*
1703  * Explain an integer-valued property.
1704  */
1705 static void
1706 ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
1707 {
1708         char    buf[32];
1709
1710         snprintf(buf, sizeof(buf), "%d", value);
1711         ExplainProperty(qlabel, buf, true, es);
1712 }
1713
1714 /*
1715  * Explain a long-integer-valued property.
1716  */
1717 static void
1718 ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
1719 {
1720         char    buf[32];
1721
1722         snprintf(buf, sizeof(buf), "%ld", value);
1723         ExplainProperty(qlabel, buf, true, es);
1724 }
1725
1726 /*
1727  * Explain a float-valued property, using the specified number of
1728  * fractional digits.
1729  */
1730 static void
1731 ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
1732                                          ExplainState *es)
1733 {
1734         char    buf[256];
1735
1736         snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
1737         ExplainProperty(qlabel, buf, true, es);
1738 }
1739
1740 /*
1741  * Open a group of related objects.
1742  *
1743  * objtype is the type of the group object, labelname is its label within
1744  * a containing object (if any).
1745  *
1746  * If labeled is true, the group members will be labeled properties,
1747  * while if it's false, they'll be unlabeled objects.
1748  */
1749 static void
1750 ExplainOpenGroup(const char *objtype, const char *labelname,
1751                                  bool labeled, ExplainState *es)
1752 {
1753         switch (es->format)
1754         {
1755                 case EXPLAIN_FORMAT_TEXT:
1756                         /* nothing to do */
1757                         break;
1758
1759                 case EXPLAIN_FORMAT_XML:
1760                         ExplainXMLTag(objtype, X_OPENING, es);
1761                         es->indent++;
1762                         break;
1763
1764                 case EXPLAIN_FORMAT_JSON:
1765                         ExplainJSONLineEnding(es);
1766                         appendStringInfoSpaces(es->str, 2 * es->indent);
1767                         if (labelname)
1768                         {
1769                                 escape_json(es->str, labelname);
1770                                 appendStringInfoString(es->str, ": ");
1771                         }
1772                         appendStringInfoChar(es->str, labeled ? '{' : '[');
1773
1774                         /*
1775                          * In JSON format, the grouping_stack is an integer list.  0 means
1776                          * we've emitted nothing at this grouping level, 1 means we've
1777                          * emitted something (and so the next item needs a comma).
1778                          * See ExplainJSONLineEnding().
1779                          */
1780                         es->grouping_stack = lcons_int(0, es->grouping_stack);
1781                         es->indent++;
1782                         break;
1783
1784                 case EXPLAIN_FORMAT_YAML:
1785
1786                         /*
1787                          * In YAML format, the grouping stack is an integer list.  0 means
1788                          * we've emitted nothing at this grouping level AND this grouping
1789                          * level is unlabelled and must be marked with "- ".  See
1790                          * ExplainYAMLLineStarting().
1791                          */
1792                         ExplainYAMLLineStarting(es);
1793                         if (labelname)
1794                         {
1795                                 escape_yaml(es->str, labelname);
1796                                 appendStringInfoChar(es->str, ':');
1797                                 es->grouping_stack = lcons_int(1, es->grouping_stack);
1798                         }
1799                         else
1800                         {
1801                                 appendStringInfoString(es->str, "- ");
1802                                 es->grouping_stack = lcons_int(0, es->grouping_stack);
1803                         }
1804                         es->indent++;
1805                         break;
1806         }
1807 }
1808
1809 /*
1810  * Close a group of related objects.
1811  * Parameters must match the corresponding ExplainOpenGroup call.
1812  */
1813 static void
1814 ExplainCloseGroup(const char *objtype, const char *labelname,
1815                                   bool labeled, ExplainState *es)
1816 {
1817         switch (es->format)
1818         {
1819                 case EXPLAIN_FORMAT_TEXT:
1820                         /* nothing to do */
1821                         break;
1822
1823                 case EXPLAIN_FORMAT_XML:
1824                         es->indent--;
1825                         ExplainXMLTag(objtype, X_CLOSING, es);
1826                         break;
1827
1828                 case EXPLAIN_FORMAT_JSON:
1829                         es->indent--;
1830                         appendStringInfoChar(es->str, '\n');
1831                         appendStringInfoSpaces(es->str, 2 * es->indent);
1832                         appendStringInfoChar(es->str, labeled ? '}' : ']');
1833                         es->grouping_stack = list_delete_first(es->grouping_stack);
1834                         break;
1835
1836                 case EXPLAIN_FORMAT_YAML:
1837                         es->indent--;
1838                         es->grouping_stack = list_delete_first(es->grouping_stack);
1839                         break;
1840         }
1841 }
1842
1843 /*
1844  * Emit a "dummy" group that never has any members.
1845  *
1846  * objtype is the type of the group object, labelname is its label within
1847  * a containing object (if any).
1848  */
1849 static void
1850 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
1851 {
1852         switch (es->format)
1853         {
1854                 case EXPLAIN_FORMAT_TEXT:
1855                         /* nothing to do */
1856                         break;
1857
1858                 case EXPLAIN_FORMAT_XML:
1859                         ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
1860                         break;
1861
1862                 case EXPLAIN_FORMAT_JSON:
1863                         ExplainJSONLineEnding(es);
1864                         appendStringInfoSpaces(es->str, 2 * es->indent);
1865                         if (labelname)
1866                         {
1867                                 escape_json(es->str, labelname);
1868                                 appendStringInfoString(es->str, ": ");
1869                         }
1870                         escape_json(es->str, objtype);
1871                         break;
1872
1873                 case EXPLAIN_FORMAT_YAML:
1874                         ExplainYAMLLineStarting(es);
1875                         if (labelname)
1876                         {
1877                                 escape_yaml(es->str, labelname);
1878                                 appendStringInfoString(es->str, ": ");
1879                         }
1880                         else
1881                         {
1882                                 appendStringInfoString(es->str, "- ");
1883                         }
1884                         escape_yaml(es->str, objtype);
1885                         break;
1886         }
1887 }
1888
1889 /*
1890  * Emit the start-of-output boilerplate.
1891  *
1892  * This is just enough different from processing a subgroup that we need
1893  * a separate pair of subroutines.
1894  */
1895 void
1896 ExplainBeginOutput(ExplainState *es)
1897 {
1898         switch (es->format)
1899         {
1900                 case EXPLAIN_FORMAT_TEXT:
1901                         /* nothing to do */
1902                         break;
1903
1904                 case EXPLAIN_FORMAT_XML:
1905                         appendStringInfoString(es->str,
1906                                                                    "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
1907                         es->indent++;
1908                         break;
1909
1910                 case EXPLAIN_FORMAT_JSON:
1911                         /* top-level structure is an array of plans */
1912                         appendStringInfoChar(es->str, '[');
1913                         es->grouping_stack = lcons_int(0, es->grouping_stack);
1914                         es->indent++;
1915                         break;
1916
1917                 case EXPLAIN_FORMAT_YAML:
1918                         es->grouping_stack = lcons_int(0, es->grouping_stack);
1919                         break;
1920         }
1921 }
1922
1923 /*
1924  * Emit the end-of-output boilerplate.
1925  */
1926 void
1927 ExplainEndOutput(ExplainState *es)
1928 {
1929         switch (es->format)
1930         {
1931                 case EXPLAIN_FORMAT_TEXT:
1932                         /* nothing to do */
1933                         break;
1934
1935                 case EXPLAIN_FORMAT_XML:
1936                         es->indent--;
1937                         appendStringInfoString(es->str, "</explain>");
1938                         break;
1939
1940                 case EXPLAIN_FORMAT_JSON:
1941                         es->indent--;
1942                         appendStringInfoString(es->str, "\n]");
1943                         es->grouping_stack = list_delete_first(es->grouping_stack);
1944                         break;
1945
1946                 case EXPLAIN_FORMAT_YAML:
1947                         es->grouping_stack = list_delete_first(es->grouping_stack);
1948                         break;
1949         }
1950 }
1951
1952 /*
1953  * Put an appropriate separator between multiple plans
1954  */
1955 void
1956 ExplainSeparatePlans(ExplainState *es)
1957 {
1958         switch (es->format)
1959         {
1960                 case EXPLAIN_FORMAT_TEXT:
1961                         /* add a blank line */
1962                         appendStringInfoChar(es->str, '\n');
1963                         break;
1964
1965                 case EXPLAIN_FORMAT_XML:
1966                 case EXPLAIN_FORMAT_JSON:
1967                 case EXPLAIN_FORMAT_YAML:
1968                         /* nothing to do */
1969                         break;
1970         }
1971 }
1972
1973 /*
1974  * Emit opening or closing XML tag.
1975  *
1976  * "flags" must contain X_OPENING, X_CLOSING, or X_CLOSE_IMMEDIATE.
1977  * Optionally, OR in X_NOWHITESPACE to suppress the whitespace we'd normally
1978  * add.
1979  *
1980  * XML tag names can't contain white space, so we replace any spaces in
1981  * "tagname" with dashes.
1982  */
1983 static void
1984 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
1985 {
1986         const char *s;
1987
1988         if ((flags & X_NOWHITESPACE) == 0)
1989                 appendStringInfoSpaces(es->str, 2 * es->indent);
1990         appendStringInfoCharMacro(es->str, '<');
1991         if ((flags & X_CLOSING) != 0)
1992                 appendStringInfoCharMacro(es->str, '/');
1993         for (s = tagname; *s; s++)
1994                 appendStringInfoCharMacro(es->str, (*s == ' ') ? '-' : *s);
1995         if ((flags & X_CLOSE_IMMEDIATE) != 0)
1996                 appendStringInfoString(es->str, " /");
1997         appendStringInfoCharMacro(es->str, '>');
1998         if ((flags & X_NOWHITESPACE) == 0)
1999                 appendStringInfoCharMacro(es->str, '\n');
2000 }
2001
2002 /*
2003  * Emit a JSON line ending.
2004  *
2005  * JSON requires a comma after each property but the last.  To facilitate this,
2006  * in JSON format, the text emitted for each property begins just prior to the
2007  * preceding line-break (and comma, if applicable).
2008  */
2009 static void
2010 ExplainJSONLineEnding(ExplainState *es)
2011 {
2012         Assert(es->format == EXPLAIN_FORMAT_JSON);
2013         if (linitial_int(es->grouping_stack) != 0)
2014                 appendStringInfoChar(es->str, ',');
2015         else
2016                 linitial_int(es->grouping_stack) = 1;
2017         appendStringInfoChar(es->str, '\n');
2018 }
2019
2020 /*
2021  * Indent a YAML line.
2022  *
2023  * YAML lines are ordinarily indented by two spaces per indentation level.
2024  * The text emitted for each property begins just prior to the preceding
2025  * line-break, except for the first property in an unlabelled group, for which
2026  * it begins immediately after the "- " that introduces the group.  The first
2027  * property of the group appears on the same line as the opening "- ".
2028  */
2029 static void
2030 ExplainYAMLLineStarting(ExplainState *es)
2031 {
2032         Assert(es->format == EXPLAIN_FORMAT_YAML);
2033         if (linitial_int(es->grouping_stack) == 0)
2034         {
2035                 linitial_int(es->grouping_stack) = 1;
2036         }
2037         else
2038         {
2039                 appendStringInfoChar(es->str, '\n');
2040                 appendStringInfoSpaces(es->str, es->indent * 2);
2041         }
2042 }
2043
2044 /*
2045  * Produce a JSON string literal, properly escaping characters in the text.
2046  */
2047 static void
2048 escape_json(StringInfo buf, const char *str)
2049 {
2050         const char *p;
2051
2052         appendStringInfoCharMacro(buf, '\"');
2053         for (p = str; *p; p++)
2054         {
2055                 switch (*p)
2056                 {
2057                         case '\b':
2058                                 appendStringInfoString(buf, "\\b");
2059                                 break;
2060                         case '\f':
2061                                 appendStringInfoString(buf, "\\f");
2062                                 break;
2063                         case '\n':
2064                                 appendStringInfoString(buf, "\\n");
2065                                 break;
2066                         case '\r':
2067                                 appendStringInfoString(buf, "\\r");
2068                                 break;
2069                         case '\t':
2070                                 appendStringInfoString(buf, "\\t");
2071                                 break;
2072                         case '"':
2073                                 appendStringInfoString(buf, "\\\"");
2074                                 break;
2075                         case '\\':
2076                                 appendStringInfoString(buf, "\\\\");
2077                                 break;
2078                         default:
2079                                 if ((unsigned char) *p < ' ')
2080                                         appendStringInfo(buf, "\\u%04x", (int) *p);
2081                                 else
2082                                         appendStringInfoCharMacro(buf, *p);
2083                                 break;
2084                 }
2085         }
2086         appendStringInfoCharMacro(buf, '\"');
2087 }
2088
2089 /*
2090  * YAML is a superset of JSON: if we find quotable characters, we call
2091  * escape_json.  If not, we emit the property unquoted for better readability.
2092  */
2093 static void
2094 escape_yaml(StringInfo buf, const char *str)
2095 {
2096         const char *p;
2097
2098         for (p = str; *p; p++)
2099         {
2100                 if ((unsigned char) *p < ' ' || strchr("\"\\\b\f\n\r\t", *p))
2101                 {
2102                         escape_json(buf, str);
2103                         return;
2104                 }
2105         }
2106
2107         appendStringInfo(buf, "%s", str);
2108 }