explain.c 9.8 KB
Newer Older
M
 
Marc G. Fournier 已提交
1
/*
2
 * explain.c
3
 *	  Explain the query execution plan
4
 *
5
 * Portions Copyright (c) 1996-2001, 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.68 2002/02/26 22:47:04 tgl Exp $
9 10
 *
 */
11

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

14
#include "commands/explain.h"
15
#include "executor/instrument.h"
B
Bruce Momjian 已提交
16 17
#include "lib/stringinfo.h"
#include "nodes/print.h"
18
#include "optimizer/planner.h"
B
Bruce Momjian 已提交
19
#include "parser/parsetree.h"
20
#include "rewrite/rewriteHandler.h"
21
#include "tcop/pquery.h"
B
Bruce Momjian 已提交
22
#include "utils/relcache.h"
23

24 25 26
typedef struct ExplainState
{
	/* options */
27
	bool		printCost;		/* print cost */
28
	bool		printNodes;		/* do nodeToString() instead */
29
	/* other states */
30
	List	   *rtable;			/* range table */
31
} ExplainState;
32

33 34
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
B
Bruce Momjian 已提交
35

36 37 38
/* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))

39 40 41

/*
 * ExplainQuery -
42
 *	  print out the execution plan for a given query
43 44 45
 *
 */
void
46
ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
47
{
B
Bruce Momjian 已提交
48 49
	List	   *rewritten;
	List	   *l;
50

51
	/* rewriter and planner may not work in aborted state? */
52 53 54 55
	if (IsAbortedTransactionBlockState())
	{
		elog(NOTICE, "(transaction aborted): %s",
			 "queries ignored until END");
56 57
		return;
	}
58

59
	/* rewriter will not cope with utility statements */
60 61 62
	if (query->commandType == CMD_UTILITY)
	{
		elog(NOTICE, "Utility statements have no plan structure");
63 64
		return;
	}
65

B
Bruce Momjian 已提交
66 67 68 69 70 71
	/* Rewrite through rule system */
	rewritten = QueryRewrite(query);

	/* In the case of an INSTEAD NOTHING, tell at least that */
	if (rewritten == NIL)
	{
72
		elog(NOTICE, "Query rewrites to nothing");
B
Bruce Momjian 已提交
73 74 75 76 77
		return;
	}

	/* Explain every plan */
	foreach(l, rewritten)
78
		ExplainOneQuery(lfirst(l), verbose, analyze, dest);
B
Bruce Momjian 已提交
79 80 81 82 83 84 85 86
}

/*
 * ExplainOneQuery -
 *	  print out the execution plan for one query
 *
 */
static void
87
ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
B
Bruce Momjian 已提交
88 89 90
{
	Plan	   *plan;
	ExplainState *es;
91
	double		totaltime = 0;
B
Bruce Momjian 已提交
92

93 94 95 96 97 98 99 100 101 102
	/* planner will not cope with utility statements */
	if (query->commandType == CMD_UTILITY)
	{
		if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
			elog(NOTICE, "QUERY PLAN:\n\nNOTIFY\n");
		else
			elog(NOTICE, "QUERY PLAN:\n\nUTILITY\n");
		return;
	}

103
	/* plan the query */
104
	plan = planner(query);
105

106 107 108
	/* pg_plan could have failed */
	if (plan == NULL)
		return;
B
Bruce Momjian 已提交
109

110 111 112 113 114 115 116
	/* Execute the plan for statistics if asked for */
	if (analyze)
	{
		struct timeval starttime;
		struct timeval endtime;

		/*
117 118
		 * Set up the instrumentation for the top node. This will cascade
		 * during plan initialisation
119 120 121 122
		 */
		plan->instrument = InstrAlloc();

		gettimeofday(&starttime, NULL);
123
		ProcessQuery(query, plan, None, NULL);
124 125 126
		CommandCounterIncrement();
		gettimeofday(&endtime, NULL);

127
		endtime.tv_sec -= starttime.tv_sec;
128 129 130 131 132 133 134 135 136 137
		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 已提交
138
	es = (ExplainState *) palloc(sizeof(ExplainState));
B
Bruce Momjian 已提交
139
	MemSet(es, 0, sizeof(ExplainState));
B
Bruce Momjian 已提交
140

141
	es->printCost = true;		/* default */
142

143 144
	if (verbose)
		es->printNodes = true;
B
Bruce Momjian 已提交
145

146 147 148
	es->rtable = query->rtable;

	if (es->printNodes)
149
	{
150 151
		char	   *s;

152
		s = nodeToString(plan);
153 154
		if (s)
		{
155
			elog(NOTICE, "QUERY DUMP:\n\n%s", s);
156 157 158
			pfree(s);
		}
	}
159

160 161
	if (es->printCost)
	{
162 163 164 165 166 167 168 169 170
		StringInfo	str;

		str = Explain_PlanToString(plan, es);
		if (analyze)
			appendStringInfo(str, "Total runtime: %.2f msec\n",
							 1000.0 * totaltime);
		elog(NOTICE, "QUERY PLAN:\n\n%s", str->data);
		pfree(str->data);
		pfree(str);
B
Bruce Momjian 已提交
171
	}
172

173
	if (es->printNodes)
174
		pprint(plan);			/* display in postmaster log file */
175

B
Bruce Momjian 已提交
176
	pfree(es);
177 178 179 180 181 182 183 184
}

/*****************************************************************************
 *
 *****************************************************************************/

/*
 * explain_outNode -
185
 *	  converts a Node into ascii string and append it to 'str'
186 187
 */
static void
188
explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
189
{
B
Bruce Momjian 已提交
190
	List	   *l;
191
	Relation	relation;
B
Bruce Momjian 已提交
192 193
	char	   *pname;
	int			i;
194 195 196 197 198 199 200 201 202

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

	switch (nodeTag(plan))
	{
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
		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;
224 225 226 227 228 229
		case T_TidScan:
			pname = "Tid Scan";
			break;
		case T_SubqueryScan:
			pname = "Subquery Scan";
			break;
230 231 232
		case T_Material:
			pname = "Materialize";
			break;
233 234 235 236 237 238 239 240 241 242 243 244
		case T_Sort:
			pname = "Sort";
			break;
		case T_Group:
			pname = "Group";
			break;
		case T_Agg:
			pname = "Aggregate";
			break;
		case T_Unique:
			pname = "Unique";
			break;
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
		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;
265 266 267
		case T_Limit:
			pname = "Limit";
			break;
268 269 270 271
		case T_Hash:
			pname = "Hash";
			break;
		default:
272
			pname = "???";
273
			break;
274 275 276 277 278
	}

	appendStringInfo(str, pname);
	switch (nodeTag(plan))
	{
279
		case T_IndexScan:
280
			if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
281
				appendStringInfo(str, " Backward");
282
			appendStringInfo(str, " using ");
V
Vadim B. Mikheev 已提交
283
			i = 0;
B
Bruce Momjian 已提交
284
			foreach(l, ((IndexScan *) plan)->indxid)
V
Vadim B. Mikheev 已提交
285
			{
286 287
				relation = RelationIdGetRelation(lfirsti(l));
				Assert(relation);
288 289
				appendStringInfo(str, "%s%s",
								 (++i > 1) ? ", " : "",
290
					stringStringInfo(RelationGetRelationName(relation)));
291 292
				/* drop relcache refcount from RelationIdGetRelation */
				RelationDecrementReferenceCount(relation);
V
Vadim B. Mikheev 已提交
293
			}
294
			/* FALL THRU */
295
		case T_SeqScan:
296
		case T_TidScan:
297 298
			if (((Scan *) plan)->scanrelid > 0)
			{
299 300 301 302 303
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);

				/* Assume it's on a real relation */
				Assert(rte->relname);
304

305 306
				appendStringInfo(str, " on %s",
								 stringStringInfo(rte->relname));
307 308
				if (strcmp(rte->eref->relname, rte->relname) != 0)
					appendStringInfo(str, " %s",
B
Bruce Momjian 已提交
309
								   stringStringInfo(rte->eref->relname));
310 311 312 313 314 315 316 317 318 319
			}
			break;
		case T_SubqueryScan:
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
											  es->rtable);

				appendStringInfo(str, " %s",
								 stringStringInfo(rte->eref->relname));
320 321
			}
			break;
322 323
		default:
			break;
324 325 326
	}
	if (es->printCost)
	{
327 328 329
		appendStringInfo(str, "  (cost=%.2f..%.2f rows=%.0f width=%d)",
						 plan->startup_cost, plan->total_cost,
						 plan->plan_rows, plan->plan_width);
330

331
		if (plan->instrument && plan->instrument->nloops > 0)
332
		{
333
			double		nloops = plan->instrument->nloops;
334 335 336 337 338 339 340

			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);
		}
341
	}
342
	appendStringInfo(str, "\n");
343

V
Vadim B. Mikheev 已提交
344 345 346
	/* initPlan-s */
	if (plan->initPlan)
	{
347 348 349
		List	   *saved_rtable = es->rtable;
		List	   *lst;

B
Bruce Momjian 已提交
350
		for (i = 0; i < indent; i++)
V
Vadim B. Mikheev 已提交
351 352
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  InitPlan\n");
353
		foreach(lst, plan->initPlan)
V
Vadim B. Mikheev 已提交
354
		{
355
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
V
Vadim B. Mikheev 已提交
356 357 358
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
359 360
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
							indent + 4, es);
V
Vadim B. Mikheev 已提交
361 362 363
		}
		es->rtable = saved_rtable;
	}
364 365 366 367 368 369

	/* lefttree */
	if (outerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
370 371
		appendStringInfo(str, "  ->  ");
		explain_outNode(str, outerPlan(plan), indent + 3, es);
372
	}
373 374 375 376 377 378

	/* righttree */
	if (innerPlan(plan))
	{
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
V
Vadim B. Mikheev 已提交
379 380 381
		appendStringInfo(str, "  ->  ");
		explain_outNode(str, innerPlan(plan), indent + 3, es);
	}
382

383
	if (IsA(plan, Append))
384
	{
385 386
		Append	   *appendplan = (Append *) plan;
		List	   *lst;
387 388 389

		foreach(lst, appendplan->appendplans)
		{
390
			Plan	   *subnode = (Plan *) lfirst(lst);
391 392 393

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

396
			explain_outNode(str, subnode, indent + 3, es);
397 398
		}
	}
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

	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;

		Assert(rte->subquery != NULL);
		es->rtable = rte->subquery->rtable;

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

		explain_outNode(str, subnode, indent + 3, es);

		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, "    ->  ");
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
							indent + 4, es);
		}
		es->rtable = saved_rtable;
	}
440 441
}

442
static StringInfo
443
Explain_PlanToString(Plan *plan, ExplainState *es)
444
{
445
	StringInfo	str = makeStringInfo();
446

447
	if (plan != NULL)
448 449
		explain_outNode(str, plan, 0, es);
	return str;
450
}