explain.c 20.0 KB
Newer Older
M
 
Marc G. Fournier 已提交
1
/*
2
 * explain.c
3
 *	  Explain the query execution plan
4
 *
B
Bruce Momjian 已提交
5
 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
6
 * Portions Copyright (c) 1994-5, Regents of the University of California
7
 *
8
 * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.88 2002/09/19 22:48:33 tgl Exp $
9 10
 *
 */
11

12
#include "postgres.h"
M
Marc G. Fournier 已提交
13

14
#include "access/genam.h"
15 16
#include "access/heapam.h"
#include "catalog/pg_type.h"
17
#include "commands/explain.h"
18
#include "executor/executor.h"
19
#include "executor/instrument.h"
B
Bruce Momjian 已提交
20 21
#include "lib/stringinfo.h"
#include "nodes/print.h"
22
#include "optimizer/clauses.h"
23
#include "optimizer/planner.h"
24
#include "optimizer/var.h"
B
Bruce Momjian 已提交
25
#include "parser/parsetree.h"
26
#include "rewrite/rewriteHandler.h"
27
#include "tcop/pquery.h"
28
#include "utils/builtins.h"
29
#include "utils/guc.h"
30
#include "utils/lsyscache.h"
31

32

33 34 35
typedef struct ExplainState
{
	/* options */
36
	bool		printCost;		/* print cost */
37
	bool		printNodes;		/* do nodeToString() instead */
38
	/* other states */
39
	List	   *rtable;			/* range table */
40
} ExplainState;
41

42
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
43
static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
B
Bruce Momjian 已提交
44
				TupOutputState *tstate);
45
static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
B
Bruce Momjian 已提交
46
				int indent, ExplainState *es);
47
static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
B
Bruce Momjian 已提交
48 49
			   int scanrelid, Plan *outer_plan,
			   StringInfo str, int indent, ExplainState *es);
50
static void show_upper_qual(List *qual, const char *qlabel,
B
Bruce Momjian 已提交
51 52 53
				const char *outer_name, int outer_varno, Plan *outer_plan,
				const char *inner_name, int inner_varno, Plan *inner_plan,
				StringInfo str, int indent, ExplainState *es);
54
static void show_sort_keys(List *tlist, int nkeys, const char *qlabel,
B
Bruce Momjian 已提交
55
			   StringInfo str, int indent, ExplainState *es);
56
static Node *make_ors_ands_explicit(List *orclauses);
57 58 59

/*
 * ExplainQuery -
60
 *	  execute an EXPLAIN command
61 62
 */
void
63
ExplainQuery(ExplainStmt *stmt, CommandDest dest)
64
{
65
	Query	   *query = stmt->query;
66 67
	TupOutputState *tstate;
	TupleDesc	tupdesc;
B
Bruce Momjian 已提交
68 69
	List	   *rewritten;
	List	   *l;
70

71
	/* need a tuple descriptor representing a single TEXT column */
72
	tupdesc = CreateTemplateTupleDesc(1, false);
73 74 75 76 77
	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
					   TEXTOID, -1, 0, false);

	/* prepare for projection of tuples */
	tstate = begin_tup_output_tupdesc(dest, tupdesc);
78

79 80
	if (query->commandType == CMD_UTILITY)
	{
81
		/* rewriter will not cope with utility statements */
82
		do_text_output_oneline(tstate, "Utility statements have no plan structure");
83
	}
84
	else
B
Bruce Momjian 已提交
85
	{
86 87 88 89 90 91
		/* Rewrite through rule system */
		rewritten = QueryRewrite(query);

		if (rewritten == NIL)
		{
			/* In the case of an INSTEAD NOTHING, tell at least that */
92
			do_text_output_oneline(tstate, "Query rewrites to nothing");
93 94 95 96 97 98 99 100 101
		}
		else
		{
			/* Explain every plan */
			foreach(l, rewritten)
			{
				ExplainOneQuery(lfirst(l), stmt, tstate);
				/* put a blank line between plans */
				if (lnext(l) != NIL)
102
					do_text_output_oneline(tstate, "");
103 104
			}
		}
B
Bruce Momjian 已提交
105 106
	}

107
	end_tup_output(tstate);
B
Bruce Momjian 已提交
108 109 110 111 112 113 114
}

/*
 * ExplainOneQuery -
 *	  print out the execution plan for one query
 */
static void
115
ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
B
Bruce Momjian 已提交
116 117 118
{
	Plan	   *plan;
	ExplainState *es;
119
	double		totaltime = 0;
B
Bruce Momjian 已提交
120

121 122 123 124
	/* planner will not cope with utility statements */
	if (query->commandType == CMD_UTILITY)
	{
		if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
125
			do_text_output_oneline(tstate, "NOTIFY");
126
		else
127
			do_text_output_oneline(tstate, "UTILITY");
128 129 130
		return;
	}

131
	/* plan the query */
132
	plan = planner(query);
133

134 135 136
	/* pg_plan could have failed */
	if (plan == NULL)
		return;
B
Bruce Momjian 已提交
137

138
	/* Execute the plan for statistics if asked for */
139
	if (stmt->analyze)
140 141 142 143 144
	{
		struct timeval starttime;
		struct timeval endtime;

		/*
145 146
		 * Set up the instrumentation for the top node. This will cascade
		 * during plan initialisation
147 148 149 150
		 */
		plan->instrument = InstrAlloc();

		gettimeofday(&starttime, NULL);
151
		ProcessQuery(query, plan, None, NULL);
152 153 154
		CommandCounterIncrement();
		gettimeofday(&endtime, NULL);

155
		endtime.tv_sec -= starttime.tv_sec;
156 157 158 159 160 161 162 163 164 165
		endtime.tv_usec -= starttime.tv_usec;
		while (endtime.tv_usec < 0)
		{
			endtime.tv_usec += 1000000;
			endtime.tv_sec--;
		}
		totaltime = (double) endtime.tv_sec +
			(double) endtime.tv_usec / 1000000.0;
	}

B
Bruce Momjian 已提交
166
	es = (ExplainState *) palloc(sizeof(ExplainState));
B
Bruce Momjian 已提交
167
	MemSet(es, 0, sizeof(ExplainState));
B
Bruce Momjian 已提交
168

169
	es->printCost = true;		/* default */
170

171
	if (stmt->verbose)
172
		es->printNodes = true;
B
Bruce Momjian 已提交
173

174 175 176
	es->rtable = query->rtable;

	if (es->printNodes)
177
	{
178
		char	   *s;
179
		char	   *f;
180

181
		s = nodeToString(plan);
182 183
		if (s)
		{
184 185 186 187
			if (Explain_pretty_print)
				f = pretty_format_node_dump(s);
			else
				f = format_node_dump(s);
188
			pfree(s);
189 190 191
			do_text_output_multiline(tstate, f);
			pfree(f);
			if (es->printCost)
B
Bruce Momjian 已提交
192
				do_text_output_oneline(tstate, "");		/* separator line */
193 194
		}
	}
195

196 197
	if (es->printCost)
	{
198 199 200
		StringInfo	str;

		str = Explain_PlanToString(plan, es);
201
		if (stmt->analyze)
202 203
			appendStringInfo(str, "Total runtime: %.2f msec\n",
							 1000.0 * totaltime);
204
		do_text_output_multiline(tstate, str->data);
205 206
		pfree(str->data);
		pfree(str);
B
Bruce Momjian 已提交
207
	}
208

B
Bruce Momjian 已提交
209
	pfree(es);
210 211 212 213 214
}


/*
 * explain_outNode -
215 216 217 218 219
 *	  converts a Plan node into ascii string and appends it to 'str'
 *
 * outer_plan, if not null, references another plan node that is the outer
 * side of a join with the current node.  This is only interesting for
 * deciphering runtime keys of an inner indexscan.
220 221
 */
static void
222 223
explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
				int indent, ExplainState *es)
224
{
B
Bruce Momjian 已提交
225 226 227
	List	   *l;
	char	   *pname;
	int			i;
228 229 230 231 232 233 234 235 236

	if (plan == NULL)
	{
		appendStringInfo(str, "\n");
		return;
	}

	switch (nodeTag(plan))
	{
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
		case T_Result:
			pname = "Result";
			break;
		case T_Append:
			pname = "Append";
			break;
		case T_NestLoop:
			pname = "Nested Loop";
			break;
		case T_MergeJoin:
			pname = "Merge Join";
			break;
		case T_HashJoin:
			pname = "Hash Join";
			break;
		case T_SeqScan:
			pname = "Seq Scan";
			break;
		case T_IndexScan:
			pname = "Index Scan";
			break;
258 259 260 261 262 263
		case T_TidScan:
			pname = "Tid Scan";
			break;
		case T_SubqueryScan:
			pname = "Subquery Scan";
			break;
264 265 266
		case T_FunctionScan:
			pname = "Function Scan";
			break;
267 268 269
		case T_Material:
			pname = "Materialize";
			break;
270 271 272 273 274 275 276 277 278 279 280 281
		case T_Sort:
			pname = "Sort";
			break;
		case T_Group:
			pname = "Group";
			break;
		case T_Agg:
			pname = "Aggregate";
			break;
		case T_Unique:
			pname = "Unique";
			break;
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
		case T_SetOp:
			switch (((SetOp *) plan)->cmd)
			{
				case SETOPCMD_INTERSECT:
					pname = "SetOp Intersect";
					break;
				case SETOPCMD_INTERSECT_ALL:
					pname = "SetOp Intersect All";
					break;
				case SETOPCMD_EXCEPT:
					pname = "SetOp Except";
					break;
				case SETOPCMD_EXCEPT_ALL:
					pname = "SetOp Except All";
					break;
				default:
					pname = "SetOp ???";
					break;
			}
			break;
302 303 304
		case T_Limit:
			pname = "Limit";
			break;
305 306 307 308
		case T_Hash:
			pname = "Hash";
			break;
		default:
309
			pname = "???";
310
			break;
311 312 313 314 315
	}

	appendStringInfo(str, pname);
	switch (nodeTag(plan))
	{
316
		case T_IndexScan:
317
			if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
318
				appendStringInfo(str, " Backward");
319
			appendStringInfo(str, " using ");
V
Vadim B. Mikheev 已提交
320
			i = 0;
B
Bruce Momjian 已提交
321
			foreach(l, ((IndexScan *) plan)->indxid)
V
Vadim B. Mikheev 已提交
322
			{
323 324 325
				Relation	relation;

				relation = index_open(lfirsti(l));
326 327
				appendStringInfo(str, "%s%s",
								 (++i > 1) ? ", " : "",
B
Bruce Momjian 已提交
328
					quote_identifier(RelationGetRelationName(relation)));
329
				index_close(relation);
V
Vadim B. Mikheev 已提交
330
			}
331
			/* FALL THRU */
332
		case T_SeqScan:
333
		case T_TidScan:
334 335
			if (((Scan *) plan)->scanrelid > 0)
			{
336 337
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);
B
Bruce Momjian 已提交
338
				char	   *relname;
339 340

				/* Assume it's on a real relation */
341
				Assert(rte->rtekind == RTE_RELATION);
342 343 344

				/* We only show the rel name, not schema name */
				relname = get_rel_name(rte->relid);
345

346
				appendStringInfo(str, " on %s",
347
								 quote_identifier(relname));
348
				if (strcmp(rte->eref->aliasname, relname) != 0)
349
					appendStringInfo(str, " %s",
B
Bruce Momjian 已提交
350
								 quote_identifier(rte->eref->aliasname));
351 352 353 354 355 356 357 358 359
			}
			break;
		case T_SubqueryScan:
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);

				appendStringInfo(str, " %s",
360
								 quote_identifier(rte->eref->aliasname));
361 362
			}
			break;
363 364 365 366 367
		case T_FunctionScan:
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);
B
Bruce Momjian 已提交
368 369 370 371
				Expr	   *expr;
				Func	   *funcnode;
				Oid			funcid;
				char	   *proname;
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

				/* Assert it's on a RangeFunction */
				Assert(rte->rtekind == RTE_FUNCTION);

				expr = (Expr *) rte->funcexpr;
				funcnode = (Func *) expr->oper;
				funcid = funcnode->funcid;

				/* We only show the func name, not schema name */
				proname = get_func_name(funcid);

				appendStringInfo(str, " on %s",
								 quote_identifier(proname));
				if (strcmp(rte->eref->aliasname, proname) != 0)
					appendStringInfo(str, " %s",
B
Bruce Momjian 已提交
387
								 quote_identifier(rte->eref->aliasname));
388 389
			}
			break;
390 391
		default:
			break;
392 393 394
	}
	if (es->printCost)
	{
395 396 397
		appendStringInfo(str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
						 plan->startup_cost, plan->total_cost,
						 plan->plan_rows, plan->plan_width);
398

399
		if (plan->instrument && plan->instrument->nloops > 0)
400
		{
401
			double		nloops = plan->instrument->nloops;
402 403 404 405 406 407 408

			appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
							 1000.0 * plan->instrument->startup / nloops,
							 1000.0 * plan->instrument->total / nloops,
							 plan->instrument->ntuples / nloops,
							 plan->instrument->nloops);
		}
409
	}
410
	appendStringInfo(str, "\n");
411

412
	/* quals, sort keys, etc */
413 414 415 416
	switch (nodeTag(plan))
	{
		case T_IndexScan:
			show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
417
						   "Index Cond",
418
						   ((Scan *) plan)->scanrelid,
419
						   outer_plan,
420
						   str, indent, es);
421 422
			show_scan_qual(plan->qual, false,
						   "Filter",
423
						   ((Scan *) plan)->scanrelid,
424
						   outer_plan,
425 426 427 428
						   str, indent, es);
			break;
		case T_SeqScan:
		case T_TidScan:
429
		case T_FunctionScan:
430 431
			show_scan_qual(plan->qual, false,
						   "Filter",
432
						   ((Scan *) plan)->scanrelid,
433
						   outer_plan,
434 435 436
						   str, indent, es);
			break;
		case T_NestLoop:
437
			show_upper_qual(((NestLoop *) plan)->join.joinqual,
438
							"Join Filter",
439 440 441
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
442 443
			show_upper_qual(plan->qual,
							"Filter",
444 445 446 447 448
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_MergeJoin:
449 450
			show_upper_qual(((MergeJoin *) plan)->mergeclauses,
							"Merge Cond",
451 452 453
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
454
			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
455
							"Join Filter",
456 457 458
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
459 460
			show_upper_qual(plan->qual,
							"Filter",
461 462 463 464 465
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_HashJoin:
466 467
			show_upper_qual(((HashJoin *) plan)->hashclauses,
							"Hash Cond",
468 469 470
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
471
			show_upper_qual(((HashJoin *) plan)->join.joinqual,
472
							"Join Filter",
473 474 475
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
476 477
			show_upper_qual(plan->qual,
							"Filter",
478 479 480 481 482
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_SubqueryScan:
483 484
			show_upper_qual(plan->qual,
							"Filter",
B
Bruce Momjian 已提交
485
						  "subplan", 1, ((SubqueryScan *) plan)->subplan,
486 487 488 489 490
							"", 0, NULL,
							str, indent, es);
			break;
		case T_Agg:
		case T_Group:
491 492
			show_upper_qual(plan->qual,
							"Filter",
493 494 495 496
							"subplan", 0, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
			break;
497 498 499 500 501
		case T_Sort:
			show_sort_keys(plan->targetlist, ((Sort *) plan)->keycount,
						   "Sort Key",
						   str, indent, es);
			break;
502 503
		case T_Result:
			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
504
							"One-Time Filter",
505 506 507
							"subplan", OUTER, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
508 509
			show_upper_qual(plan->qual,
							"Filter",
510 511 512 513 514 515 516 517
							"subplan", OUTER, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
			break;
		default:
			break;
	}

V
Vadim B. Mikheev 已提交
518 519 520
	/* initPlan-s */
	if (plan->initPlan)
	{
521 522 523
		List	   *saved_rtable = es->rtable;
		List	   *lst;

B
Bruce Momjian 已提交
524
		for (i = 0; i < indent; i++)
V
Vadim B. Mikheev 已提交
525 526
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  InitPlan\n");
527
		foreach(lst, plan->initPlan)
V
Vadim B. Mikheev 已提交
528
		{
529
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
V
Vadim B. Mikheev 已提交
530 531 532
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
533
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
534
							indent + 4, es);
V
Vadim B. Mikheev 已提交
535 536 537
		}
		es->rtable = saved_rtable;
	}
538 539 540 541 542 543

	/* lefttree */
	if (outerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
544
		appendStringInfo(str, "  ->  ");
545
		explain_outNode(str, outerPlan(plan), NULL, indent + 3, es);
546
	}
547 548 549 550 551 552

	/* righttree */
	if (innerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
553
		appendStringInfo(str, "  ->  ");
554 555
		explain_outNode(str, innerPlan(plan), outerPlan(plan),
						indent + 3, es);
V
Vadim B. Mikheev 已提交
556
	}
557

558
	if (IsA(plan, Append))
559
	{
560 561
		Append	   *appendplan = (Append *) plan;
		List	   *lst;
562 563 564

		foreach(lst, appendplan->appendplans)
		{
565
			Plan	   *subnode = (Plan *) lfirst(lst);
566 567 568

			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
569
			appendStringInfo(str, "  ->  ");
570

571
			explain_outNode(str, subnode, NULL, indent + 3, es);
572 573
		}
	}
574 575 576 577 578 579 580 581 582

	if (IsA(plan, SubqueryScan))
	{
		SubqueryScan *subqueryscan = (SubqueryScan *) plan;
		Plan	   *subnode = subqueryscan->subplan;
		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
									  es->rtable);
		List	   *saved_rtable = es->rtable;

583
		Assert(rte->rtekind == RTE_SUBQUERY);
584 585 586 587 588 589
		es->rtable = rte->subquery->rtable;

		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  ->  ");

590
		explain_outNode(str, subnode, NULL, indent + 3, es);
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609

		es->rtable = saved_rtable;
	}

	/* subPlan-s */
	if (plan->subPlan)
	{
		List	   *saved_rtable = es->rtable;
		List	   *lst;

		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  SubPlan\n");
		foreach(lst, plan->subPlan)
		{
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
610
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
611 612 613 614
							indent + 4, es);
		}
		es->rtable = saved_rtable;
	}
615 616
}

617
static StringInfo
618
Explain_PlanToString(Plan *plan, ExplainState *es)
619
{
620
	StringInfo	str = makeStringInfo();
621

622
	if (plan != NULL)
623
		explain_outNode(str, plan, NULL, 0, es);
624
	return str;
625
}
626 627 628 629 630 631

/*
 * Show a qualifier expression for a scan plan node
 */
static void
show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
632
			   int scanrelid, Plan *outer_plan,
633 634 635
			   StringInfo str, int indent, ExplainState *es)
{
	RangeTblEntry *rte;
636 637
	Node	   *scancontext;
	Node	   *outercontext;
638 639 640 641 642 643 644 645 646 647 648 649 650 651
	List	   *context;
	Node	   *node;
	char	   *exprstr;
	int			i;

	/* No work if empty qual */
	if (qual == NIL)
		return;
	if (is_or_qual)
	{
		if (lfirst(qual) == NIL && lnext(qual) == NIL)
			return;
	}

652 653 654 655 656 657
	/* Fix qual --- indexqual requires different processing */
	if (is_or_qual)
		node = make_ors_ands_explicit(qual);
	else
		node = (Node *) make_ands_explicit(qual);

658 659 660
	/* Generate deparse context */
	Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
	rte = rt_fetch(scanrelid, es->rtable);
661
	scancontext = deparse_context_for_rte(rte);
662 663 664

	/*
	 * If we have an outer plan that is referenced by the qual, add it to
B
Bruce Momjian 已提交
665 666
	 * the deparse context.  If not, don't (so that we don't force
	 * prefixes unnecessarily).
667 668 669 670 671
	 */
	if (outer_plan)
	{
		if (intMember(OUTER, pull_varnos(node)))
			outercontext = deparse_context_for_subplan("outer",
B
Bruce Momjian 已提交
672
												  outer_plan->targetlist,
673 674 675 676
													   es->rtable);
		else
			outercontext = NULL;
	}
677
	else
678 679 680
		outercontext = NULL;

	context = deparse_context_for_plan(scanrelid, scancontext,
681 682
									   OUTER, outercontext,
									   NIL);
683 684

	/* Deparse the expression */
685
	exprstr = deparse_expression(node, context, (outercontext != NULL), false);
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726

	/* And add to str */
	for (i = 0; i < indent; i++)
		appendStringInfo(str, "  ");
	appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
}

/*
 * Show a qualifier expression for an upper-level plan node
 */
static void
show_upper_qual(List *qual, const char *qlabel,
				const char *outer_name, int outer_varno, Plan *outer_plan,
				const char *inner_name, int inner_varno, Plan *inner_plan,
				StringInfo str, int indent, ExplainState *es)
{
	List	   *context;
	Node	   *outercontext;
	Node	   *innercontext;
	Node	   *node;
	char	   *exprstr;
	int			i;

	/* No work if empty qual */
	if (qual == NIL)
		return;

	/* Generate deparse context */
	if (outer_plan)
		outercontext = deparse_context_for_subplan(outer_name,
												   outer_plan->targetlist,
												   es->rtable);
	else
		outercontext = NULL;
	if (inner_plan)
		innercontext = deparse_context_for_subplan(inner_name,
												   inner_plan->targetlist,
												   es->rtable);
	else
		innercontext = NULL;
	context = deparse_context_for_plan(outer_varno, outercontext,
727 728
									   inner_varno, innercontext,
									   NIL);
729 730 731

	/* Deparse the expression */
	node = (Node *) make_ands_explicit(qual);
732
	exprstr = deparse_expression(node, context, (inner_plan != NULL), false);
733 734 735 736 737 738 739

	/* And add to str */
	for (i = 0; i < indent; i++)
		appendStringInfo(str, "  ");
	appendStringInfo(str, "  %s: %s\n", qlabel, exprstr);
}

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
/*
 * Show the sort keys for a Sort node.
 */
static void
show_sort_keys(List *tlist, int nkeys, const char *qlabel,
			   StringInfo str, int indent, ExplainState *es)
{
	List	   *context;
	bool		useprefix;
	int			keyno;
	List	   *tl;
	char	   *exprstr;
	int			i;

	if (nkeys <= 0)
		return;

	for (i = 0; i < indent; i++)
		appendStringInfo(str, "  ");
	appendStringInfo(str, "  %s: ", qlabel);

	/*
	 * In this routine we expect that the plan node's tlist has not been
B
Bruce Momjian 已提交
763 764 765 766 767
	 * processed by set_plan_references().	Normally, any Vars will
	 * contain valid varnos referencing the actual rtable.	But we might
	 * instead be looking at a dummy tlist generated by prepunion.c; if
	 * there are Vars with zero varno, use the tlist itself to determine
	 * their names.
768
	 */
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
	if (intMember(0, pull_varnos((Node *) tlist)))
	{
		Node	   *outercontext;

		outercontext = deparse_context_for_subplan("sort",
												   tlist,
												   es->rtable);
		context = deparse_context_for_plan(0, outercontext,
										   0, NULL,
										   NIL);
		useprefix = false;
	}
	else
	{
		context = deparse_context_for_plan(0, NULL,
										   0, NULL,
										   es->rtable);
		useprefix = length(es->rtable) > 1;
	}
788 789 790 791 792 793 794 795 796 797

	for (keyno = 1; keyno <= nkeys; keyno++)
	{
		/* find key expression in tlist */
		foreach(tl, tlist)
		{
			TargetEntry *target = (TargetEntry *) lfirst(tl);

			if (target->resdom->reskey == keyno)
			{
798 799 800
				/* Deparse the expression, showing any top-level cast */
				exprstr = deparse_expression(target->expr, context,
											 useprefix, true);
801 802 803 804 805 806 807 808 809 810 811 812 813 814
				/* And add to str */
				if (keyno > 1)
					appendStringInfo(str, ", ");
				appendStringInfo(str, "%s", exprstr);
				break;
			}
		}
		if (tl == NIL)
			elog(ERROR, "show_sort_keys: no tlist entry for key %d", keyno);
	}

	appendStringInfo(str, "\n");
}

815
/*
B
Bruce Momjian 已提交
816
 * Indexscan qual lists have an implicit OR-of-ANDs structure.	Make it
817 818 819 820 821 822 823 824 825 826 827
 * explicit so deparsing works properly.
 */
static Node *
make_ors_ands_explicit(List *orclauses)
{
	if (orclauses == NIL)
		return NULL;			/* probably can't happen */
	else if (lnext(orclauses) == NIL)
		return (Node *) make_ands_explicit(lfirst(orclauses));
	else
	{
B
Bruce Momjian 已提交
828 829
		List	   *args = NIL;
		List	   *orptr;
830 831 832 833 834 835 836

		foreach(orptr, orclauses)
			args = lappend(args, make_ands_explicit(lfirst(orptr)));

		return (Node *) make_orclause(args);
	}
}