explain.c 22.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.96 2002/12/12 15:49:24 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 38
	bool		printNodes;		/* do nodeToString() too */
	bool		printAnalyze;	/* print actual times */
39
	/* other states */
40
	List	   *rtable;			/* range table */
41
} ExplainState;
42

43
static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
B
Bruce Momjian 已提交
44
				TupOutputState *tstate);
45 46 47 48 49
static double elapsed_time(struct timeval *starttime);
static void explain_outNode(StringInfo str,
							Plan *plan, PlanState *planstate,
							Plan *outer_plan,
							int indent, ExplainState *es);
50
static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
B
Bruce Momjian 已提交
51 52
			   int scanrelid, Plan *outer_plan,
			   StringInfo str, int indent, ExplainState *es);
53
static void show_upper_qual(List *qual, const char *qlabel,
B
Bruce Momjian 已提交
54 55 56
				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);
57
static void show_sort_keys(List *tlist, int nkeys, const char *qlabel,
B
Bruce Momjian 已提交
58
			   StringInfo str, int indent, ExplainState *es);
59
static Node *make_ors_ands_explicit(List *orclauses);
60 61 62

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

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

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

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

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

110
	end_tup_output(tstate);
B
Bruce Momjian 已提交
111 112 113 114 115 116 117
}

/*
 * ExplainOneQuery -
 *	  print out the execution plan for one query
 */
static void
118
ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
B
Bruce Momjian 已提交
119 120
{
	Plan	   *plan;
121
	QueryDesc  *queryDesc;
B
Bruce Momjian 已提交
122
	ExplainState *es;
123
	StringInfo	str;
124
	double		totaltime = 0;
125
	struct timeval starttime;
B
Bruce Momjian 已提交
126

127 128 129 130
	/* planner will not cope with utility statements */
	if (query->commandType == CMD_UTILITY)
	{
		if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
131
			do_text_output_oneline(tstate, "NOTIFY");
132
		else
133
			do_text_output_oneline(tstate, "UTILITY");
134 135 136
		return;
	}

137
	/* plan the query */
138
	plan = planner(query);
139

140 141 142
	/* pg_plan could have failed */
	if (plan == NULL)
		return;
B
Bruce Momjian 已提交
143

144 145 146 147 148 149 150 151 152 153 154 155
	/* We don't support DECLARE CURSOR here */
	Assert(!query->isPortal);

	gettimeofday(&starttime, NULL);

	/* Create a QueryDesc requesting no output */
	queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL,
								stmt->analyze);

	/* call ExecutorStart to prepare the plan for execution */
	ExecutorStart(queryDesc);

156
	/* Execute the plan for statistics if asked for */
157
	if (stmt->analyze)
158
	{
159 160
		/* run the plan */
		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
161

162
		/* We can't clean up 'till we're done printing the stats... */
163

164
		totaltime += elapsed_time(&starttime);
165 166
	}

167
	es = (ExplainState *) palloc0(sizeof(ExplainState));
B
Bruce Momjian 已提交
168

169
	es->printCost = true;		/* default */
170 171
	es->printNodes = stmt->verbose;
	es->printAnalyze = stmt->analyze;
172 173 174
	es->rtable = query->rtable;

	if (es->printNodes)
175
	{
176
		char	   *s;
177
		char	   *f;
178

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

194 195
	str = makeStringInfo();

196 197
	if (es->printCost)
	{
198 199 200 201 202 203 204 205 206
		explain_outNode(str, plan, queryDesc->planstate,
						NULL, 0, es);
	}

	/*
	 * Close down the query and free resources.  Include time for this
	 * in the total runtime.
	 */
	gettimeofday(&starttime, NULL);
207

208 209 210 211 212 213 214
	ExecutorEnd(queryDesc);
	CommandCounterIncrement();

	totaltime += elapsed_time(&starttime);

	if (es->printCost)
	{
215
		if (stmt->analyze)
216 217
			appendStringInfo(str, "Total runtime: %.2f msec\n",
							 1000.0 * totaltime);
218
		do_text_output_multiline(tstate, str->data);
B
Bruce Momjian 已提交
219
	}
220

221 222
	pfree(str->data);
	pfree(str);
B
Bruce Momjian 已提交
223
	pfree(es);
224 225
}

226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
/* Compute elapsed time in seconds since given gettimeofday() timestamp */
static double
elapsed_time(struct timeval *starttime)
{
	struct timeval endtime;

	gettimeofday(&endtime, NULL);

	endtime.tv_sec -= starttime->tv_sec;
	endtime.tv_usec -= starttime->tv_usec;
	while (endtime.tv_usec < 0)
	{
		endtime.tv_usec += 1000000;
		endtime.tv_sec--;
	}
	return (double) endtime.tv_sec +
		(double) endtime.tv_usec / 1000000.0;
}
244 245 246

/*
 * explain_outNode -
247 248
 *	  converts a Plan node into ascii string and appends it to 'str'
 *
249 250 251 252
 * planstate points to the executor state node corresponding to the plan node.
 * We need this to get at the instrumentation data (if any) as well as the
 * list of subplans.
 *
253 254 255
 * 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.
256 257
 */
static void
258 259 260
explain_outNode(StringInfo str,
				Plan *plan, PlanState *planstate,
				Plan *outer_plan,
261
				int indent, ExplainState *es)
262
{
B
Bruce Momjian 已提交
263 264 265
	List	   *l;
	char	   *pname;
	int			i;
266 267 268 269 270 271 272 273 274

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

	switch (nodeTag(plan))
	{
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
		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;
296 297 298 299 300 301
		case T_TidScan:
			pname = "Tid Scan";
			break;
		case T_SubqueryScan:
			pname = "Subquery Scan";
			break;
302 303 304
		case T_FunctionScan:
			pname = "Function Scan";
			break;
305 306 307
		case T_Material:
			pname = "Materialize";
			break;
308 309 310 311 312 313 314
		case T_Sort:
			pname = "Sort";
			break;
		case T_Group:
			pname = "Group";
			break;
		case T_Agg:
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
			switch (((Agg *) plan)->aggstrategy)
			{
				case AGG_PLAIN:
					pname = "Aggregate";
					break;
				case AGG_SORTED:
					pname = "GroupAggregate";
					break;
				case AGG_HASHED:
					pname = "HashAggregate";
					break;
				default:
					pname = "Aggregate ???";
					break;
			}
330 331 332 333
			break;
		case T_Unique:
			pname = "Unique";
			break;
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
		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;
354 355 356
		case T_Limit:
			pname = "Limit";
			break;
357 358 359 360
		case T_Hash:
			pname = "Hash";
			break;
		default:
361
			pname = "???";
362
			break;
363 364 365 366 367
	}

	appendStringInfo(str, pname);
	switch (nodeTag(plan))
	{
368
		case T_IndexScan:
369
			if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
370
				appendStringInfo(str, " Backward");
371
			appendStringInfo(str, " using ");
V
Vadim B. Mikheev 已提交
372
			i = 0;
B
Bruce Momjian 已提交
373
			foreach(l, ((IndexScan *) plan)->indxid)
V
Vadim B. Mikheev 已提交
374
			{
375 376 377
				Relation	relation;

				relation = index_open(lfirsti(l));
378 379
				appendStringInfo(str, "%s%s",
								 (++i > 1) ? ", " : "",
B
Bruce Momjian 已提交
380
					quote_identifier(RelationGetRelationName(relation)));
381
				index_close(relation);
V
Vadim B. Mikheev 已提交
382
			}
383
			/* FALL THRU */
384
		case T_SeqScan:
385
		case T_TidScan:
386 387
			if (((Scan *) plan)->scanrelid > 0)
			{
388 389
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);
B
Bruce Momjian 已提交
390
				char	   *relname;
391 392

				/* Assume it's on a real relation */
393
				Assert(rte->rtekind == RTE_RELATION);
394 395 396

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

398
				appendStringInfo(str, " on %s",
399
								 quote_identifier(relname));
400
				if (strcmp(rte->eref->aliasname, relname) != 0)
401
					appendStringInfo(str, " %s",
B
Bruce Momjian 已提交
402
								 quote_identifier(rte->eref->aliasname));
403 404 405 406 407 408 409 410 411
			}
			break;
		case T_SubqueryScan:
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);

				appendStringInfo(str, " %s",
412
								 quote_identifier(rte->eref->aliasname));
413 414
			}
			break;
415 416 417 418 419
		case T_FunctionScan:
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);
B
Bruce Momjian 已提交
420
				char	   *proname;
421 422 423 424

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

425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
				/*
				 * If the expression is still a function call, we can get
				 * the real name of the function.  Otherwise, punt (this
				 * can happen if the optimizer simplified away the function
				 * call, for example).
				 */
				if (rte->funcexpr && IsA(rte->funcexpr, FuncExpr))
				{
					FuncExpr   *funcexpr = (FuncExpr *) rte->funcexpr;
					Oid			funcid = funcexpr->funcid;

					/* We only show the func name, not schema name */
					proname = get_func_name(funcid);
				}
				else
					proname = rte->eref->aliasname;
441 442 443 444 445

				appendStringInfo(str, " on %s",
								 quote_identifier(proname));
				if (strcmp(rte->eref->aliasname, proname) != 0)
					appendStringInfo(str, " %s",
B
Bruce Momjian 已提交
446
								 quote_identifier(rte->eref->aliasname));
447 448
			}
			break;
449 450
		default:
			break;
451 452 453
	}
	if (es->printCost)
	{
454 455 456
		appendStringInfo(str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
						 plan->startup_cost, plan->total_cost,
						 plan->plan_rows, plan->plan_width);
457

458 459 460 461 462 463 464
		/*
		 * We have to forcibly clean up the instrumentation state because
		 * we haven't done ExecutorEnd yet.  This is pretty grotty ...
		 */
		InstrEndLoop(planstate->instrument);

		if (planstate->instrument && planstate->instrument->nloops > 0)
465
		{
466
			double		nloops = planstate->instrument->nloops;
467 468

			appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
469 470 471 472
							 1000.0 * planstate->instrument->startup / nloops,
							 1000.0 * planstate->instrument->total / nloops,
							 planstate->instrument->ntuples / nloops,
							 planstate->instrument->nloops);
473
		}
474
		else if (es->printAnalyze)
475 476
		{
			appendStringInfo(str, " (never executed)");
477
		}
478
	}
479
	appendStringInfo(str, "\n");
480

481
	/* quals, sort keys, etc */
482 483 484 485
	switch (nodeTag(plan))
	{
		case T_IndexScan:
			show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
486
						   "Index Cond",
487
						   ((Scan *) plan)->scanrelid,
488
						   outer_plan,
489
						   str, indent, es);
490 491
			show_scan_qual(plan->qual, false,
						   "Filter",
492
						   ((Scan *) plan)->scanrelid,
493
						   outer_plan,
494 495 496 497
						   str, indent, es);
			break;
		case T_SeqScan:
		case T_TidScan:
498
		case T_SubqueryScan:
499
		case T_FunctionScan:
500 501
			show_scan_qual(plan->qual, false,
						   "Filter",
502
						   ((Scan *) plan)->scanrelid,
503
						   outer_plan,
504 505 506
						   str, indent, es);
			break;
		case T_NestLoop:
507
			show_upper_qual(((NestLoop *) plan)->join.joinqual,
508
							"Join Filter",
509 510 511
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
512 513
			show_upper_qual(plan->qual,
							"Filter",
514 515 516 517 518
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_MergeJoin:
519 520
			show_upper_qual(((MergeJoin *) plan)->mergeclauses,
							"Merge Cond",
521 522 523
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
524
			show_upper_qual(((MergeJoin *) plan)->join.joinqual,
525
							"Join Filter",
526 527 528
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
529 530
			show_upper_qual(plan->qual,
							"Filter",
531 532 533 534 535
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_HashJoin:
536 537
			show_upper_qual(((HashJoin *) plan)->hashclauses,
							"Hash Cond",
538 539 540
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
541
			show_upper_qual(((HashJoin *) plan)->join.joinqual,
542
							"Join Filter",
543 544 545
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
546 547
			show_upper_qual(plan->qual,
							"Filter",
548 549 550 551 552 553
							"outer", OUTER, outerPlan(plan),
							"inner", INNER, innerPlan(plan),
							str, indent, es);
			break;
		case T_Agg:
		case T_Group:
554 555
			show_upper_qual(plan->qual,
							"Filter",
556 557 558 559
							"subplan", 0, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
			break;
560 561 562 563 564
		case T_Sort:
			show_sort_keys(plan->targetlist, ((Sort *) plan)->keycount,
						   "Sort Key",
						   str, indent, es);
			break;
565 566
		case T_Result:
			show_upper_qual((List *) ((Result *) plan)->resconstantqual,
567
							"One-Time Filter",
568 569 570
							"subplan", OUTER, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
571 572
			show_upper_qual(plan->qual,
							"Filter",
573 574 575 576 577 578 579 580
							"subplan", OUTER, outerPlan(plan),
							"", 0, NULL,
							str, indent, es);
			break;
		default:
			break;
	}

V
Vadim B. Mikheev 已提交
581 582 583
	/* initPlan-s */
	if (plan->initPlan)
	{
584
		List	   *saved_rtable = es->rtable;
585
		List	   *pslist = planstate->initPlan;
586 587
		List	   *lst;

B
Bruce Momjian 已提交
588
		for (i = 0; i < indent; i++)
V
Vadim B. Mikheev 已提交
589 590
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  InitPlan\n");
591
		foreach(lst, plan->initPlan)
V
Vadim B. Mikheev 已提交
592
		{
593
			SubPlanExpr  *subplan = (SubPlanExpr *) lfirst(lst);
594 595 596
			SubPlanState *subplanstate = (SubPlanState *) lfirst(pslist);

			es->rtable = subplan->rtable;
V
Vadim B. Mikheev 已提交
597 598 599
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
600 601 602
			explain_outNode(str, subplan->plan,
							subplanstate->planstate,
							NULL,
603
							indent + 4, es);
604
			pslist = lnext(pslist);
V
Vadim B. Mikheev 已提交
605 606 607
		}
		es->rtable = saved_rtable;
	}
608 609 610 611 612 613

	/* lefttree */
	if (outerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
614
		appendStringInfo(str, "  ->  ");
615 616 617 618
		explain_outNode(str, outerPlan(plan),
						outerPlanState(planstate),
						NULL,
						indent + 3, es);
619
	}
620 621 622 623 624 625

	/* righttree */
	if (innerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
626
		appendStringInfo(str, "  ->  ");
627 628 629
		explain_outNode(str, innerPlan(plan),
						innerPlanState(planstate),
						outerPlan(plan),
630
						indent + 3, es);
V
Vadim B. Mikheev 已提交
631
	}
632

633
	if (IsA(plan, Append))
634
	{
635
		Append	   *appendplan = (Append *) plan;
636
		AppendState *appendstate = (AppendState *) planstate;
637
		List	   *lst;
638
		int			j;
639

640
		j = 0;
641 642
		foreach(lst, appendplan->appendplans)
		{
643
			Plan	   *subnode = (Plan *) lfirst(lst);
644 645 646

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

649 650 651 652 653
			explain_outNode(str, subnode,
							appendstate->appendplans[j],
							NULL,
							indent + 3, es);
			j++;
654 655
		}
	}
656 657 658 659

	if (IsA(plan, SubqueryScan))
	{
		SubqueryScan *subqueryscan = (SubqueryScan *) plan;
660
		SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
661 662 663 664 665
		Plan	   *subnode = subqueryscan->subplan;
		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
									  es->rtable);
		List	   *saved_rtable = es->rtable;

666
		Assert(rte->rtekind == RTE_SUBQUERY);
667 668 669 670 671 672
		es->rtable = rte->subquery->rtable;

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

673 674 675 676
		explain_outNode(str, subnode,
						subquerystate->subplan,
						NULL,
						indent + 3, es);
677 678 679 680 681

		es->rtable = saved_rtable;
	}

	/* subPlan-s */
682
	if (planstate->subPlan)
683 684 685 686 687 688 689
	{
		List	   *saved_rtable = es->rtable;
		List	   *lst;

		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  SubPlan\n");
690
		foreach(lst, planstate->subPlan)
691
		{
692
			SubPlanState *sps = (SubPlanState *) lfirst(lst);
693
			SubPlanExpr *sp = (SubPlanExpr *) sps->ps.plan;
694 695

			es->rtable = sp->rtable;
696 697 698
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
699 700 701
			explain_outNode(str, sp->plan,
							sps->planstate,
							NULL,
702 703 704 705
							indent + 4, es);
		}
		es->rtable = saved_rtable;
	}
706 707
}

708 709 710 711 712
/*
 * Show a qualifier expression for a scan plan node
 */
static void
show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
713
			   int scanrelid, Plan *outer_plan,
714 715 716
			   StringInfo str, int indent, ExplainState *es)
{
	RangeTblEntry *rte;
717 718
	Node	   *scancontext;
	Node	   *outercontext;
719 720 721 722 723 724 725 726 727 728 729 730 731 732
	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;
	}

733 734 735 736 737 738
	/* Fix qual --- indexqual requires different processing */
	if (is_or_qual)
		node = make_ors_ands_explicit(qual);
	else
		node = (Node *) make_ands_explicit(qual);

739 740 741
	/* Generate deparse context */
	Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
	rte = rt_fetch(scanrelid, es->rtable);
742
	scancontext = deparse_context_for_rte(rte);
743 744 745

	/*
	 * If we have an outer plan that is referenced by the qual, add it to
B
Bruce Momjian 已提交
746 747
	 * the deparse context.  If not, don't (so that we don't force
	 * prefixes unnecessarily).
748 749 750 751 752
	 */
	if (outer_plan)
	{
		if (intMember(OUTER, pull_varnos(node)))
			outercontext = deparse_context_for_subplan("outer",
B
Bruce Momjian 已提交
753
												  outer_plan->targetlist,
754 755 756 757
													   es->rtable);
		else
			outercontext = NULL;
	}
758
	else
759 760 761
		outercontext = NULL;

	context = deparse_context_for_plan(scanrelid, scancontext,
762 763
									   OUTER, outercontext,
									   NIL);
764 765

	/* Deparse the expression */
766
	exprstr = deparse_expression(node, context, (outercontext != NULL), false);
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807

	/* 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,
808 809
									   inner_varno, innercontext,
									   NIL);
810 811 812

	/* Deparse the expression */
	node = (Node *) make_ands_explicit(qual);
813
	exprstr = deparse_expression(node, context, (inner_plan != NULL), false);
814 815 816 817 818 819 820

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

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
/*
 * 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 已提交
844 845 846 847 848
	 * 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.
849
	 */
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
	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;
	}
869 870 871 872 873 874 875 876 877 878

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

			if (target->resdom->reskey == keyno)
			{
879
				/* Deparse the expression, showing any top-level cast */
880
				exprstr = deparse_expression((Node *) target->expr, context,
881
											 useprefix, true);
882 883 884 885 886 887 888 889 890 891 892 893 894 895
				/* 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");
}

896
/*
B
Bruce Momjian 已提交
897
 * Indexscan qual lists have an implicit OR-of-ANDs structure.	Make it
898 899 900 901 902 903 904 905 906 907 908
 * 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 已提交
909 910
		List	   *args = NIL;
		List	   *orptr;
911 912 913 914 915 916 917

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

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