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