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