1 /*-------------------------------------------------------------------------
4 * Explain query execution plans
6 * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994-5, Regents of the University of California
10 * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.198 2010/01/02 16:57:37 momjian Exp $
12 *-------------------------------------------------------------------------
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"
38 /* Hook for plugins to get control in ExplainOneQuery() */
39 ExplainOneQuery_hook_type ExplainOneQuery_hook = NULL;
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;
45 /* OR-able flags for ExplainXMLTag() */
48 #define X_CLOSE_IMMEDIATE 2
49 #define X_NOWHITESPACE 4
51 static void ExplainOneQuery(Query *query, ExplainState *es,
52 const char *queryString, ParamListInfo params);
53 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
55 static double elapsed_time(instr_time *starttime);
56 static void ExplainNode(Plan *plan, PlanState *planstate,
58 const char *relationship, const char *plan_name,
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,
66 static void show_upper_qual(List *qual, const char *qlabel, Plan *plan,
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,
76 static void ExplainPropertyList(const char *qlabel, List *data,
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,
84 static void ExplainPropertyLong(const char *qlabel, long value,
86 static void ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
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,
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);
103 * execute an EXPLAIN command
106 ExplainQuery(ExplainStmt *stmt, const char *queryString,
107 ParamListInfo params, DestReceiver *dest)
110 TupOutputState *tstate;
114 /* Initialize ExplainState. */
115 ExplainInitState(&es);
117 /* Parse options list. */
118 foreach(lc, stmt->options)
120 DefElem *opt = (DefElem *) lfirst(lc);
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)
132 char *p = defGetString(opt);
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;
144 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
145 errmsg("unrecognized value for EXPLAIN option \"%s\": \"%s\"",
150 (errcode(ERRCODE_SYNTAX_ERROR),
151 errmsg("unrecognized EXPLAIN option \"%s\"",
155 if (es.buffers && !es.analyze)
157 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
158 errmsg("EXPLAIN option BUFFERS requires ANALYZE")));
161 * Run parse analysis and rewrite. Note this also acquires sufficient
162 * locks on the source table(s).
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.
170 rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
172 (ParserSetupHook) setupParserWithParamList,
175 /* emit opening boilerplate */
176 ExplainBeginOutput(&es);
178 if (rewritten == NIL)
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.
184 if (es.format == EXPLAIN_FORMAT_TEXT)
185 appendStringInfoString(es.str, "Query rewrites to nothing\n");
191 /* Explain every plan */
192 foreach(l, rewritten)
194 ExplainOneQuery((Query *) lfirst(l), &es, queryString, params);
196 /* Separate plans with an appropriate separator */
197 if (lnext(l) != NULL)
198 ExplainSeparatePlans(&es);
202 /* emit closing boilerplate */
203 ExplainEndOutput(&es);
204 Assert(es.indent == 0);
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);
211 do_text_output_oneline(tstate, es.str->data);
212 end_tup_output(tstate);
218 * Initialize ExplainState.
221 ExplainInitState(ExplainState *es)
223 /* Set default options. */
224 memset(es, 0, sizeof(ExplainState));
226 /* Prepare output buffer. */
227 es->str = makeStringInfo();
231 * ExplainResultDesc -
232 * construct the result tupledesc for an EXPLAIN
235 ExplainResultDesc(ExplainStmt *stmt)
241 /* Check for XML format option */
242 foreach(lc, stmt->options)
244 DefElem *opt = (DefElem *) lfirst(lc);
246 if (strcmp(opt->defname, "format") == 0)
248 char *p = defGetString(opt);
250 xml = (strcmp(p, "xml") == 0);
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);
263 * print out the execution plan for one Query
266 ExplainOneQuery(Query *query, ExplainState *es,
267 const char *queryString, ParamListInfo params)
269 /* planner will not cope with utility statements */
270 if (query->commandType == CMD_UTILITY)
272 ExplainOneUtility(query->utilityStmt, es, queryString, params);
276 /* if an advisor plugin is present, let it manage things */
277 if (ExplainOneQuery_hook)
278 (*ExplainOneQuery_hook) (query, es, queryString, params);
284 plan = pg_plan_query(query, 0, params);
286 /* run it (if needed) and produce output */
287 ExplainOnePlan(plan, es, queryString, params);
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)
297 * This is exported because it's called back from prepare.c in the
298 * EXPLAIN EXECUTE case
301 ExplainOneUtility(Node *utilityStmt, ExplainState *es,
302 const char *queryString, ParamListInfo params)
304 if (utilityStmt == NULL)
307 if (IsA(utilityStmt, ExecuteStmt))
308 ExplainExecuteQuery((ExecuteStmt *) utilityStmt, es,
309 queryString, params);
310 else if (IsA(utilityStmt, NotifyStmt))
312 if (es->format == EXPLAIN_FORMAT_TEXT)
313 appendStringInfoString(es->str, "NOTIFY\n");
315 ExplainDummyGroup("Notify", NULL, es);
319 if (es->format == EXPLAIN_FORMAT_TEXT)
320 appendStringInfoString(es->str,
321 "Utility statements have no plan structure\n");
323 ExplainDummyGroup("Utility Statement", NULL, es);
329 * given a planned query, execute it if needed, and then print
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.
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
342 ExplainOnePlan(PlannedStmt *plannedstmt, ExplainState *es,
343 const char *queryString, ParamListInfo params)
345 QueryDesc *queryDesc;
346 instr_time starttime;
347 double totaltime = 0;
349 int instrument_option = 0;
352 instrument_option |= INSTRUMENT_TIMER;
354 instrument_option |= INSTRUMENT_BUFFERS;
357 * Use a snapshot with an updated command ID to ensure this query sees
358 * results of any previously executed queries.
360 PushUpdatedSnapshot(GetActiveSnapshot());
362 /* Create a QueryDesc requesting no output */
363 queryDesc = CreateQueryDesc(plannedstmt, queryString,
364 GetActiveSnapshot(), InvalidSnapshot,
365 None_Receiver, params, instrument_option);
367 INSTR_TIME_SET_CURRENT(starttime);
369 /* If analyzing, we need to cope with queued triggers */
371 AfterTriggerBeginQuery();
373 /* Select execution options */
375 eflags = 0; /* default run-to-completion flags */
377 eflags = EXEC_FLAG_EXPLAIN_ONLY;
379 /* call ExecutorStart to prepare the plan for execution */
380 ExecutorStart(queryDesc, eflags);
382 /* Execute the plan for statistics if asked for */
386 ExecutorRun(queryDesc, ForwardScanDirection, 0L);
388 /* We can't clean up 'till we're done printing the stats... */
389 totaltime += elapsed_time(&starttime);
392 ExplainOpenGroup("Query", NULL, true, es);
394 /* Create textual dump of plan tree */
395 ExplainPrintPlan(es, queryDesc);
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.
404 INSTR_TIME_SET_CURRENT(starttime);
405 AfterTriggerEndQuery(queryDesc->estate);
406 totaltime += elapsed_time(&starttime);
409 /* Print info about runtime of triggers */
412 ResultRelInfo *rInfo;
414 int numrels = queryDesc->estate->es_num_result_relations;
415 List *targrels = queryDesc->estate->es_trig_target_relations;
419 ExplainOpenGroup("Triggers", "Triggers", false, es);
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);
428 rInfo = (ResultRelInfo *) lfirst(l);
429 report_triggers(rInfo, show_relname, es);
432 ExplainCloseGroup("Triggers", "Triggers", false, es);
436 * Close down the query and free resources. Include time for this in the
437 * total runtime (although it should be pretty minimal).
439 INSTR_TIME_SET_CURRENT(starttime);
441 ExecutorEnd(queryDesc);
443 FreeQueryDesc(queryDesc);
447 /* We need a CCI just in case query expanded to multiple plans */
449 CommandCounterIncrement();
451 totaltime += elapsed_time(&starttime);
455 if (es->format == EXPLAIN_FORMAT_TEXT)
456 appendStringInfo(es->str, "Total runtime: %.3f ms\n",
459 ExplainPropertyFloat("Total Runtime", 1000.0 * totaltime,
463 ExplainCloseGroup("Query", NULL, true, es);
468 * convert a QueryDesc's plan tree to text and append it to es->str
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
474 * NB: will not work on utility statements
477 ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc)
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);
488 * report execution stats for a single relation's triggers
491 report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
495 if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
497 for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
499 Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
500 Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
502 char *conname = NULL;
504 /* Must clean up instrumentation state */
508 * We ignore triggers that were never invoked; they likely aren't
509 * relevant to the current query type.
511 if (instr->ntuples == 0)
514 ExplainOpenGroup("Trigger", NULL, true, es);
516 relname = RelationGetRelationName(rInfo->ri_RelationDesc);
517 if (OidIsValid(trig->tgconstraint))
518 conname = get_constraint_name(trig->tgconstraint);
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.
525 if (es->format == EXPLAIN_FORMAT_TEXT)
527 if (es->verbose || conname == NULL)
528 appendStringInfo(es->str, "Trigger %s", trig->tgname);
530 appendStringInfoString(es->str, "Trigger");
532 appendStringInfo(es->str, " for constraint %s", conname);
534 appendStringInfo(es->str, " on %s", relname);
535 appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
536 1000.0 * instr->total, instr->ntuples);
540 ExplainPropertyText("Trigger Name", trig->tgname, es);
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);
551 ExplainCloseGroup("Trigger", NULL, true, es);
555 /* Compute elapsed time in seconds since given timestamp */
557 elapsed_time(instr_time *starttime)
561 INSTR_TIME_SET_CURRENT(endtime);
562 INSTR_TIME_SUBTRACT(endtime, *starttime);
563 return INSTR_TIME_GET_DOUBLE(endtime);
568 * Appends a description of the Plan node to es->str
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
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.
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.
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.
588 ExplainNode(Plan *plan, PlanState *planstate,
590 const char *relationship, const char *plan_name,
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;
602 switch (nodeTag(plan))
605 pname = sname = "Result";
608 sname = "ModifyTable";
609 switch (((ModifyTable *) plan)->operation)
612 pname = operation = "Insert";
615 pname = operation = "Update";
618 pname = operation = "Delete";
626 pname = sname = "Append";
628 case T_RecursiveUnion:
629 pname = sname = "Recursive Union";
632 pname = sname = "BitmapAnd";
635 pname = sname = "BitmapOr";
638 pname = sname = "Nested Loop";
641 pname = "Merge"; /* "Join" gets added by jointype switch */
642 sname = "Merge Join";
645 pname = "Hash"; /* "Join" gets added by jointype switch */
649 pname = sname = "Seq Scan";
652 pname = sname = "Index Scan";
654 case T_BitmapIndexScan:
655 pname = sname = "Bitmap Index Scan";
657 case T_BitmapHeapScan:
658 pname = sname = "Bitmap Heap Scan";
661 pname = sname = "Tid Scan";
664 pname = sname = "Subquery Scan";
667 pname = sname = "Function Scan";
670 pname = sname = "Values Scan";
673 pname = sname = "CTE Scan";
675 case T_WorkTableScan:
676 pname = sname = "WorkTable Scan";
679 pname = sname = "Materialize";
682 pname = sname = "Sort";
685 pname = sname = "Group";
689 switch (((Agg *) plan)->aggstrategy)
696 pname = "GroupAggregate";
700 pname = "HashAggregate";
704 pname = "Aggregate ???";
710 pname = sname = "WindowAgg";
713 pname = sname = "Unique";
717 switch (((SetOp *) plan)->strategy)
734 pname = sname = "LockRows";
737 pname = sname = "Limit";
740 pname = sname = "Hash";
743 pname = sname = "???";
747 ExplainOpenGroup("Plan",
748 relationship ? NULL : "Plan",
751 if (es->format == EXPLAIN_FORMAT_TEXT)
755 appendStringInfoSpaces(es->str, es->indent * 2);
756 appendStringInfo(es->str, "%s\n", plan_name);
761 appendStringInfoSpaces(es->str, es->indent * 2);
762 appendStringInfoString(es->str, "-> ");
765 appendStringInfoString(es->str, pname);
770 ExplainPropertyText("Node Type", sname, es);
772 ExplainPropertyText("Strategy", strategy, es);
774 ExplainPropertyText("Operation", operation, es);
776 ExplainPropertyText("Parent Relationship", relationship, es);
778 ExplainPropertyText("Subplan Name", plan_name, es);
781 switch (nodeTag(plan))
785 IndexScan *indexscan = (IndexScan *) plan;
786 const char *indexname =
787 explain_get_index_name(indexscan->indexid);
789 if (es->format == EXPLAIN_FORMAT_TEXT)
791 if (ScanDirectionIsBackward(indexscan->indexorderdir))
792 appendStringInfoString(es->str, " Backward");
793 appendStringInfo(es->str, " using %s", indexname);
799 switch (indexscan->indexorderdir)
801 case BackwardScanDirection:
802 scandir = "Backward";
804 case NoMovementScanDirection:
805 scandir = "NoMovement";
807 case ForwardScanDirection:
814 ExplainPropertyText("Scan Direction", scandir, es);
815 ExplainPropertyText("Index Name", indexname, es);
820 case T_BitmapHeapScan:
826 case T_WorkTableScan:
827 ExplainScanTarget((Scan *) plan, es);
829 case T_BitmapIndexScan:
831 BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
832 const char *indexname =
833 explain_get_index_name(bitmapindexscan->indexid);
835 if (es->format == EXPLAIN_FORMAT_TEXT)
836 appendStringInfo(es->str, " on %s", indexname);
838 ExplainPropertyText("Index Name", indexname, es);
845 const char *jointype;
847 switch (((Join *) plan)->jointype)
871 if (es->format == EXPLAIN_FORMAT_TEXT)
874 * For historical reasons, the join type is interpolated
875 * into the node type name...
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");
883 ExplainPropertyText("Join Type", jointype, es);
888 const char *setopcmd;
890 switch (((SetOp *) plan)->cmd)
892 case SETOPCMD_INTERSECT:
893 setopcmd = "Intersect";
895 case SETOPCMD_INTERSECT_ALL:
896 setopcmd = "Intersect All";
898 case SETOPCMD_EXCEPT:
901 case SETOPCMD_EXCEPT_ALL:
902 setopcmd = "Except All";
908 if (es->format == EXPLAIN_FORMAT_TEXT)
909 appendStringInfo(es->str, " %s", setopcmd);
911 ExplainPropertyText("Command", setopcmd, es);
920 if (es->format == EXPLAIN_FORMAT_TEXT)
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);
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);
936 * We have to forcibly clean up the instrumentation state because we
937 * haven't done ExecutorEnd yet. This is pretty grotty ...
939 if (planstate->instrument)
940 InstrEndLoop(planstate->instrument);
942 if (planstate->instrument && planstate->instrument->nloops > 0)
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;
949 if (es->format == EXPLAIN_FORMAT_TEXT)
951 appendStringInfo(es->str,
952 " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
953 startup_sec, total_sec, rows, nloops);
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);
963 else if (es->analyze)
965 if (es->format == EXPLAIN_FORMAT_TEXT)
966 appendStringInfo(es->str, " (never executed)");
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);
976 /* in text format, first line ends here */
977 if (es->format == EXPLAIN_FORMAT_TEXT)
978 appendStringInfoChar(es->str, '\n');
982 show_plan_tlist(plan, es);
984 /* quals, sort keys, etc */
985 switch (nodeTag(plan))
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);
992 case T_BitmapIndexScan:
993 show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
994 "Index Cond", plan, outer_plan, es);
996 case T_BitmapHeapScan:
997 show_scan_qual(((BitmapHeapScan *) plan)->bitmapqualorig,
998 "Recheck Cond", plan, outer_plan, es);
1001 case T_FunctionScan:
1004 case T_WorkTableScan:
1005 case T_SubqueryScan:
1006 show_scan_qual(plan->qual, "Filter", plan, outer_plan, es);
1011 * The tidquals list has OR semantics, so be sure to show it
1012 * as an OR condition.
1014 List *tidquals = ((TidScan *) plan)->tidquals;
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);
1023 show_upper_qual(((NestLoop *) plan)->join.joinqual,
1024 "Join Filter", plan, es);
1025 show_upper_qual(plan->qual, "Filter", plan, es);
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);
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);
1043 show_upper_qual(plan->qual, "Filter", plan, es);
1046 show_sort_keys(plan, es);
1047 show_sort_info((SortState *) planstate, es);
1050 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
1051 "One-Time Filter", plan, es);
1052 show_upper_qual(plan->qual, "Filter", plan, es);
1058 /* Show buffer usage */
1061 const BufferUsage *usage = &planstate->instrument->bufusage;
1063 if (es->format == EXPLAIN_FORMAT_TEXT)
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);
1074 /* Show only positive counter values. */
1075 if (has_shared || has_local || has_temp)
1077 appendStringInfoSpaces(es->str, es->indent * 2);
1078 appendStringInfoString(es->str, "Buffers:");
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, ',');
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);
1108 appendStringInfoChar(es->str, ',');
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);
1120 appendStringInfoChar(es->str, '\n');
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);
1136 /* Get ready to display the child plans */
1137 haschildren = plan->initPlan ||
1140 IsA(plan, ModifyTable) ||
1141 IsA(plan, Append) ||
1142 IsA(plan, BitmapAnd) ||
1143 IsA(plan, BitmapOr) ||
1144 IsA(plan, SubqueryScan) ||
1147 ExplainOpenGroup("Plans", "Plans", false, es);
1151 ExplainSubPlans(planstate->initPlan, "InitPlan", es);
1154 if (outerPlan(plan))
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.
1161 ExplainNode(outerPlan(plan), outerPlanState(planstate),
1162 IsA(plan, BitmapHeapScan) ? outer_plan : NULL,
1167 if (innerPlan(plan))
1169 ExplainNode(innerPlan(plan), innerPlanState(planstate),
1174 /* special child plans */
1175 switch (nodeTag(plan))
1178 ExplainMemberNodes(((ModifyTable *) plan)->plans,
1179 ((ModifyTableState *) planstate)->mt_plans,
1183 ExplainMemberNodes(((Append *) plan)->appendplans,
1184 ((AppendState *) planstate)->appendplans,
1188 ExplainMemberNodes(((BitmapAnd *) plan)->bitmapplans,
1189 ((BitmapAndState *) planstate)->bitmapplans,
1193 ExplainMemberNodes(((BitmapOr *) plan)->bitmapplans,
1194 ((BitmapOrState *) planstate)->bitmapplans,
1197 case T_SubqueryScan:
1199 SubqueryScan *subqueryscan = (SubqueryScan *) plan;
1200 SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
1202 ExplainNode(subqueryscan->subplan, subquerystate->subplan,
1204 "Subquery", NULL, es);
1212 if (planstate->subPlan)
1213 ExplainSubPlans(planstate->subPlan, "SubPlan", es);
1215 /* end of child plans */
1217 ExplainCloseGroup("Plans", "Plans", false, es);
1219 /* in text format, undo whatever indentation we added */
1220 if (es->format == EXPLAIN_FORMAT_TEXT)
1221 es->indent = save_indent;
1223 ExplainCloseGroup("Plan",
1224 relationship ? NULL : "Plan",
1229 * Show the targetlist of a plan node
1232 show_plan_tlist(Plan *plan, ExplainState *es)
1240 /* No work if empty tlist (this occurs eg in bitmap indexscans) */
1241 if (plan->targetlist == NIL)
1243 /* The tlist of an Append isn't real helpful, so suppress it */
1244 if (IsA(plan, Append))
1246 /* Likewise for RecursiveUnion */
1247 if (IsA(plan, RecursiveUnion))
1250 /* Set up deparsing context */
1251 context = deparse_context_for_plan((Node *) plan,
1254 es->pstmt->subplans);
1255 useprefix = list_length(es->rtable) > 1;
1257 /* Deparse each result column (we now include resjunk ones) */
1259 foreach(lc, plan->targetlist)
1261 TargetEntry *tle = (TargetEntry *) lfirst(lc);
1263 result = lappend(result,
1264 deparse_expression((Node *) tle->expr, context,
1269 ExplainPropertyList("Output", result, es);
1273 * Show a qualifier expression
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.
1279 show_qual(List *qual, const char *qlabel, Plan *plan, Plan *outer_plan,
1280 bool useprefix, ExplainState *es)
1286 /* No work if empty qual */
1290 /* Convert AND list to explicit AND */
1291 node = (Node *) make_ands_explicit(qual);
1293 /* Set up deparsing context */
1294 context = deparse_context_for_plan((Node *) plan,
1295 (Node *) outer_plan,
1297 es->pstmt->subplans);
1299 /* Deparse the expression */
1300 exprstr = deparse_expression(node, context, useprefix, false);
1302 /* And add to es->str */
1303 ExplainPropertyText(qlabel, exprstr, es);
1307 * Show a qualifier expression for a scan plan node
1310 show_scan_qual(List *qual, const char *qlabel,
1311 Plan *scan_plan, Plan *outer_plan,
1316 useprefix = (outer_plan != NULL || IsA(scan_plan, SubqueryScan) ||
1318 show_qual(qual, qlabel, scan_plan, outer_plan, useprefix, es);
1322 * Show a qualifier expression for an upper-level plan node
1325 show_upper_qual(List *qual, const char *qlabel, Plan *plan, ExplainState *es)
1329 useprefix = (list_length(es->rtable) > 1 || es->verbose);
1330 show_qual(qual, qlabel, plan, NULL, useprefix, es);
1334 * Show the sort keys for a Sort node.
1337 show_sort_keys(Plan *sortplan, ExplainState *es)
1339 int nkeys = ((Sort *) sortplan)->numCols;
1340 AttrNumber *keycols = ((Sort *) sortplan)->sortColIdx;
1350 /* Set up deparsing context */
1351 context = deparse_context_for_plan((Node *) sortplan,
1354 es->pstmt->subplans);
1355 useprefix = (list_length(es->rtable) > 1 || es->verbose);
1357 for (keyno = 0; keyno < nkeys; keyno++)
1359 /* find key expression in tlist */
1360 AttrNumber keyresno = keycols[keyno];
1361 TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);
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,
1368 result = lappend(result, exprstr);
1371 ExplainPropertyList("Sort Key", result, es);
1375 * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
1378 show_sort_info(SortState *sortstate, ExplainState *es)
1380 Assert(IsA(sortstate, SortState));
1381 if (es->analyze && sortstate->sort_Done &&
1382 sortstate->tuplesortstate != NULL)
1384 Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
1385 const char *sortMethod;
1386 const char *spaceType;
1389 tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
1391 if (es->format == EXPLAIN_FORMAT_TEXT)
1393 appendStringInfoSpaces(es->str, es->indent * 2);
1394 appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n",
1395 sortMethod, spaceType, spaceUsed);
1399 ExplainPropertyText("Sort Method", sortMethod, es);
1400 ExplainPropertyLong("Sort Space Used", spaceUsed, es);
1401 ExplainPropertyText("Sort Space Type", spaceType, es);
1407 * Fetch the name of an index in an EXPLAIN
1409 * We allow plugins to get control here so that plans involving hypothetical
1410 * indexes can be explained.
1413 explain_get_index_name(Oid indexId)
1417 if (explain_get_index_name_hook)
1418 result = (*explain_get_index_name_hook) (indexId);
1423 /* default behavior: look in the catalogs and quote it */
1424 result = get_rel_name(indexId);
1426 elog(ERROR, "cache lookup failed for index %u", indexId);
1427 result = quote_identifier(result);
1433 * Show the target of a Scan node
1436 ExplainScanTarget(Scan *plan, ExplainState *es)
1438 char *objectname = NULL;
1439 char *namespace = NULL;
1440 const char *objecttag = NULL;
1443 if (plan->scanrelid <= 0) /* Is this still possible? */
1445 rte = rt_fetch(plan->scanrelid, es->rtable);
1447 switch (nodeTag(plan))
1451 case T_BitmapHeapScan:
1453 /* Assert it's on a real relation */
1454 Assert(rte->rtekind == RTE_RELATION);
1455 objectname = get_rel_name(rte->relid);
1457 namespace = get_namespace_name(get_rel_namespace(rte->relid));
1458 objecttag = "Relation Name";
1460 case T_FunctionScan:
1464 /* Assert it's on a RangeFunction */
1465 Assert(rte->rtekind == RTE_FUNCTION);
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,
1473 funcexpr = ((FunctionScan *) plan)->funcexpr;
1474 if (funcexpr && IsA(funcexpr, FuncExpr))
1476 Oid funcid = ((FuncExpr *) funcexpr)->funcid;
1478 objectname = get_func_name(funcid);
1481 get_namespace_name(get_func_namespace(funcid));
1483 objecttag = "Function Name";
1487 Assert(rte->rtekind == RTE_VALUES);
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";
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";
1507 if (es->format == EXPLAIN_FORMAT_TEXT)
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));
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);
1531 * Explain the constituent plans of a ModifyTable, Append, BitmapAnd,
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.
1539 ExplainMemberNodes(List *plans, PlanState **planstate, Plan *outer_plan,
1547 Plan *subnode = (Plan *) lfirst(lst);
1549 ExplainNode(subnode, planstate[j],
1558 * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
1561 ExplainSubPlans(List *plans, const char *relationship, ExplainState *es)
1567 SubPlanState *sps = (SubPlanState *) lfirst(lst);
1568 SubPlan *sp = (SubPlan *) sps->xprstate.expr;
1570 ExplainNode(exec_subplan_get_plan(es->pstmt, sp),
1573 relationship, sp->plan_name,
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.
1583 ExplainPropertyList(const char *qlabel, List *data, ExplainState *es)
1590 case EXPLAIN_FORMAT_TEXT:
1591 appendStringInfoSpaces(es->str, es->indent * 2);
1592 appendStringInfo(es->str, "%s: ", qlabel);
1596 appendStringInfoString(es->str, ", ");
1597 appendStringInfoString(es->str, (const char *) lfirst(lc));
1600 appendStringInfoChar(es->str, '\n');
1603 case EXPLAIN_FORMAT_XML:
1604 ExplainXMLTag(qlabel, X_OPENING, es);
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);
1614 appendStringInfoString(es->str, "</Item>\n");
1616 ExplainXMLTag(qlabel, X_CLOSING, es);
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, ": [");
1627 appendStringInfoString(es->str, ", ");
1628 escape_json(es->str, (const char *) lfirst(lc));
1631 appendStringInfoChar(es->str, ']');
1634 case EXPLAIN_FORMAT_YAML:
1635 ExplainYAMLLineStarting(es);
1636 escape_yaml(es->str, qlabel);
1637 appendStringInfoChar(es->str, ':');
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));
1650 * Explain a simple property.
1652 * If "numeric" is true, the value is a number (or other value that
1653 * doesn't need quoting in JSON).
1655 * This usually should not be invoked directly, but via one of the datatype
1656 * specific routines ExplainPropertyText, ExplainPropertyInteger, etc.
1659 ExplainProperty(const char *qlabel, const char *value, bool numeric,
1664 case EXPLAIN_FORMAT_TEXT:
1665 appendStringInfoSpaces(es->str, es->indent * 2);
1666 appendStringInfo(es->str, "%s: %s\n", qlabel, value);
1669 case EXPLAIN_FORMAT_XML:
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);
1678 ExplainXMLTag(qlabel, X_CLOSING | X_NOWHITESPACE, es);
1679 appendStringInfoChar(es->str, '\n');
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, ": ");
1689 appendStringInfoString(es->str, value);
1691 escape_json(es->str, value);
1694 case EXPLAIN_FORMAT_YAML:
1695 ExplainYAMLLineStarting(es);
1696 appendStringInfo(es->str, "%s: ", qlabel);
1697 escape_yaml(es->str, value);
1703 * Explain an integer-valued property.
1706 ExplainPropertyInteger(const char *qlabel, int value, ExplainState *es)
1710 snprintf(buf, sizeof(buf), "%d", value);
1711 ExplainProperty(qlabel, buf, true, es);
1715 * Explain a long-integer-valued property.
1718 ExplainPropertyLong(const char *qlabel, long value, ExplainState *es)
1722 snprintf(buf, sizeof(buf), "%ld", value);
1723 ExplainProperty(qlabel, buf, true, es);
1727 * Explain a float-valued property, using the specified number of
1728 * fractional digits.
1731 ExplainPropertyFloat(const char *qlabel, double value, int ndigits,
1736 snprintf(buf, sizeof(buf), "%.*f", ndigits, value);
1737 ExplainProperty(qlabel, buf, true, es);
1741 * Open a group of related objects.
1743 * objtype is the type of the group object, labelname is its label within
1744 * a containing object (if any).
1746 * If labeled is true, the group members will be labeled properties,
1747 * while if it's false, they'll be unlabeled objects.
1750 ExplainOpenGroup(const char *objtype, const char *labelname,
1751 bool labeled, ExplainState *es)
1755 case EXPLAIN_FORMAT_TEXT:
1759 case EXPLAIN_FORMAT_XML:
1760 ExplainXMLTag(objtype, X_OPENING, es);
1764 case EXPLAIN_FORMAT_JSON:
1765 ExplainJSONLineEnding(es);
1766 appendStringInfoSpaces(es->str, 2 * es->indent);
1769 escape_json(es->str, labelname);
1770 appendStringInfoString(es->str, ": ");
1772 appendStringInfoChar(es->str, labeled ? '{' : '[');
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().
1780 es->grouping_stack = lcons_int(0, es->grouping_stack);
1784 case EXPLAIN_FORMAT_YAML:
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().
1792 ExplainYAMLLineStarting(es);
1795 escape_yaml(es->str, labelname);
1796 appendStringInfoChar(es->str, ':');
1797 es->grouping_stack = lcons_int(1, es->grouping_stack);
1801 appendStringInfoString(es->str, "- ");
1802 es->grouping_stack = lcons_int(0, es->grouping_stack);
1810 * Close a group of related objects.
1811 * Parameters must match the corresponding ExplainOpenGroup call.
1814 ExplainCloseGroup(const char *objtype, const char *labelname,
1815 bool labeled, ExplainState *es)
1819 case EXPLAIN_FORMAT_TEXT:
1823 case EXPLAIN_FORMAT_XML:
1825 ExplainXMLTag(objtype, X_CLOSING, es);
1828 case EXPLAIN_FORMAT_JSON:
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);
1836 case EXPLAIN_FORMAT_YAML:
1838 es->grouping_stack = list_delete_first(es->grouping_stack);
1844 * Emit a "dummy" group that never has any members.
1846 * objtype is the type of the group object, labelname is its label within
1847 * a containing object (if any).
1850 ExplainDummyGroup(const char *objtype, const char *labelname, ExplainState *es)
1854 case EXPLAIN_FORMAT_TEXT:
1858 case EXPLAIN_FORMAT_XML:
1859 ExplainXMLTag(objtype, X_CLOSE_IMMEDIATE, es);
1862 case EXPLAIN_FORMAT_JSON:
1863 ExplainJSONLineEnding(es);
1864 appendStringInfoSpaces(es->str, 2 * es->indent);
1867 escape_json(es->str, labelname);
1868 appendStringInfoString(es->str, ": ");
1870 escape_json(es->str, objtype);
1873 case EXPLAIN_FORMAT_YAML:
1874 ExplainYAMLLineStarting(es);
1877 escape_yaml(es->str, labelname);
1878 appendStringInfoString(es->str, ": ");
1882 appendStringInfoString(es->str, "- ");
1884 escape_yaml(es->str, objtype);
1890 * Emit the start-of-output boilerplate.
1892 * This is just enough different from processing a subgroup that we need
1893 * a separate pair of subroutines.
1896 ExplainBeginOutput(ExplainState *es)
1900 case EXPLAIN_FORMAT_TEXT:
1904 case EXPLAIN_FORMAT_XML:
1905 appendStringInfoString(es->str,
1906 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n");
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);
1917 case EXPLAIN_FORMAT_YAML:
1918 es->grouping_stack = lcons_int(0, es->grouping_stack);
1924 * Emit the end-of-output boilerplate.
1927 ExplainEndOutput(ExplainState *es)
1931 case EXPLAIN_FORMAT_TEXT:
1935 case EXPLAIN_FORMAT_XML:
1937 appendStringInfoString(es->str, "</explain>");
1940 case EXPLAIN_FORMAT_JSON:
1942 appendStringInfoString(es->str, "\n]");
1943 es->grouping_stack = list_delete_first(es->grouping_stack);
1946 case EXPLAIN_FORMAT_YAML:
1947 es->grouping_stack = list_delete_first(es->grouping_stack);
1953 * Put an appropriate separator between multiple plans
1956 ExplainSeparatePlans(ExplainState *es)
1960 case EXPLAIN_FORMAT_TEXT:
1961 /* add a blank line */
1962 appendStringInfoChar(es->str, '\n');
1965 case EXPLAIN_FORMAT_XML:
1966 case EXPLAIN_FORMAT_JSON:
1967 case EXPLAIN_FORMAT_YAML:
1974 * Emit opening or closing XML tag.
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
1980 * XML tag names can't contain white space, so we replace any spaces in
1981 * "tagname" with dashes.
1984 ExplainXMLTag(const char *tagname, int flags, ExplainState *es)
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');
2003 * Emit a JSON line ending.
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).
2010 ExplainJSONLineEnding(ExplainState *es)
2012 Assert(es->format == EXPLAIN_FORMAT_JSON);
2013 if (linitial_int(es->grouping_stack) != 0)
2014 appendStringInfoChar(es->str, ',');
2016 linitial_int(es->grouping_stack) = 1;
2017 appendStringInfoChar(es->str, '\n');
2021 * Indent a YAML line.
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 "- ".
2030 ExplainYAMLLineStarting(ExplainState *es)
2032 Assert(es->format == EXPLAIN_FORMAT_YAML);
2033 if (linitial_int(es->grouping_stack) == 0)
2035 linitial_int(es->grouping_stack) = 1;
2039 appendStringInfoChar(es->str, '\n');
2040 appendStringInfoSpaces(es->str, es->indent * 2);
2045 * Produce a JSON string literal, properly escaping characters in the text.
2048 escape_json(StringInfo buf, const char *str)
2052 appendStringInfoCharMacro(buf, '\"');
2053 for (p = str; *p; p++)
2058 appendStringInfoString(buf, "\\b");
2061 appendStringInfoString(buf, "\\f");
2064 appendStringInfoString(buf, "\\n");
2067 appendStringInfoString(buf, "\\r");
2070 appendStringInfoString(buf, "\\t");
2073 appendStringInfoString(buf, "\\\"");
2076 appendStringInfoString(buf, "\\\\");
2079 if ((unsigned char) *p < ' ')
2080 appendStringInfo(buf, "\\u%04x", (int) *p);
2082 appendStringInfoCharMacro(buf, *p);
2086 appendStringInfoCharMacro(buf, '\"');
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.
2094 escape_yaml(StringInfo buf, const char *str)
2098 for (p = str; *p; p++)
2100 if ((unsigned char) *p < ' ' || strchr("\"\\\b\f\n\r\t", *p))
2102 escape_json(buf, str);
2107 appendStringInfo(buf, "%s", str);