explain.c 7.7 KB
Newer Older
M
 
Marc G. Fournier 已提交
1
/*
2
 * explain.c
3
 *	  Explain the query execution plan
4 5 6
 *
 * Copyright (c) 1994-5, Regents of the University of California
 *
7
 *	  $Id: explain.c,v 1.36 1999/05/09 23:31:45 tgl Exp $
8 9
 *
 */
10
#include <stdio.h>
11
#include <string.h>
12

M
Marc G. Fournier 已提交
13 14 15
#include <postgres.h>

#include <nodes/plannodes.h>
16
#include <nodes/print.h>
M
Marc G. Fournier 已提交
17 18 19
#include <tcop/tcopprot.h>
#include <lib/stringinfo.h>
#include <commands/explain.h>
20
#include <parser/parsetree.h>
21
#include <parser/parse_node.h>
M
Marc G. Fournier 已提交
22 23
#include <optimizer/planner.h>
#include <access/xact.h>
24
#include <utils/relcache.h>
B
Bruce Momjian 已提交
25
#include <rewrite/rewriteHandler.h>
26

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

36
static char *Explain_PlanToString(Plan *plan, ExplainState *es);
37
static void printLongNotice(const char * header, const char * message);
B
Bruce Momjian 已提交
38 39
static void ExplainOneQuery(Query *query, bool verbose, CommandDest dest);

40 41 42

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

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

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

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

	/* In the case of an INSTEAD NOTHING, tell at least that */
	if (rewritten == NIL)
	{
73
		elog(NOTICE, "Query rewrites to nothing");
B
Bruce Momjian 已提交
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
		return;
	}

	/* Explain every plan */
	foreach(l, rewritten)
		ExplainOneQuery(lfirst(l), verbose, dest);
}

/*
 * ExplainOneQuery -
 *	  print out the execution plan for one query
 *
 */
static void
ExplainOneQuery(Query *query, bool verbose, CommandDest dest)
{
90
	char	   *s;
B
Bruce Momjian 已提交
91 92 93
	Plan	   *plan;
	ExplainState *es;

94
	/* plan the query */
95
	plan = planner(query);
96

97 98 99
	/* pg_plan could have failed */
	if (plan == NULL)
		return;
B
Bruce Momjian 已提交
100

B
Bruce Momjian 已提交
101
	es = (ExplainState *) palloc(sizeof(ExplainState));
B
Bruce Momjian 已提交
102
	MemSet(es, 0, sizeof(ExplainState));
B
Bruce Momjian 已提交
103

104
	es->printCost = true;		/* default */
105

106 107
	if (verbose)
		es->printNodes = true;
B
Bruce Momjian 已提交
108

109 110 111
	es->rtable = query->rtable;

	if (es->printNodes)
112
	{
113
		s = nodeToString(plan);
114 115 116 117 118 119
		if (s)
		{
			printLongNotice("QUERY DUMP:\n\n", s);
			pfree(s);
		}
	}
120

121 122
	if (es->printCost)
	{
123 124
		s = Explain_PlanToString(plan, es);
		if (s)
125
		{
126 127
			printLongNotice("QUERY PLAN:\n\n", s);
			pfree(s);
128
		}
B
Bruce Momjian 已提交
129
	}
130

131
	if (es->printNodes)
132
		pprint(plan);			/* display in postmaster log file */
133

B
Bruce Momjian 已提交
134
	pfree(es);
135 136 137 138 139 140 141 142
}

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

/*
 * explain_outNode -
143
 *	  converts a Node into ascii string and append it to 'str'
144 145
 */
static void
146
explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
147
{
M
 
Marc G. Fournier 已提交
148
	List			*l;
149
	Relation	relation;
150
	char			*pname;
M
 
Marc G. Fournier 已提交
151
	int				i;
152 153 154 155 156 157 158 159 160

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

	switch (nodeTag(plan))
	{
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
		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;
182 183
		case T_Noname:
			pname = "Noname Scan";
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
			break;
		case T_Sort:
			pname = "Sort";
			break;
		case T_Group:
			pname = "Group";
			break;
		case T_Agg:
			pname = "Aggregate";
			break;
		case T_Unique:
			pname = "Unique";
			break;
		case T_Hash:
			pname = "Hash";
			break;
		default:
201
			pname = "???";
202
			break;
203 204 205 206 207
	}

	appendStringInfo(str, pname);
	switch (nodeTag(plan))
	{
208
		case T_IndexScan:
209
			appendStringInfo(str, " using ");
V
Vadim B. Mikheev 已提交
210 211 212 213 214
			i = 0;
			foreach (l, ((IndexScan *) plan)->indxid)
			{
				relation = RelationIdCacheGetRelation((int) lfirst(l));
				if (++i > 1)
M
 
Marc G. Fournier 已提交
215
				{
V
Vadim B. Mikheev 已提交
216
					appendStringInfo(str, ", ");
M
 
Marc G. Fournier 已提交
217
				}
218 219
				appendStringInfo(str, 
					stringStringInfo((RelationGetRelationName(relation))->data));
V
Vadim B. Mikheev 已提交
220
			}
221
		case T_SeqScan:
222 223 224 225
			if (((Scan *) plan)->scanrelid > 0)
			{
				RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);

B
Bruce Momjian 已提交
226 227 228
				appendStringInfo(str, " on ");
				if (strcmp(rte->refname, rte->relname) != 0)
				{
229 230
					appendStringInfo(str, "%s ",
						stringStringInfo(rte->relname));
B
Bruce Momjian 已提交
231
				}
232
				appendStringInfo(str, stringStringInfo(rte->refname));
233 234 235 236
			}
			break;
		default:
			break;
237 238 239
	}
	if (es->printCost)
	{
240
		appendStringInfo(str, "  (cost=%.2f rows=%d width=%d)",
241 242
				plan->cost, plan->plan_size, plan->plan_width);
	}
243
	appendStringInfo(str, "\n");
244

V
Vadim B. Mikheev 已提交
245 246 247
	/* initPlan-s */
	if (plan->initPlan)
	{
248 249 250
		List	   *saved_rtable = es->rtable;
		List	   *lst;

M
 
Marc G. Fournier 已提交
251 252
		for (i = 0; i < indent; i++) 
		{
V
Vadim B. Mikheev 已提交
253
			appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
254
		}
V
Vadim B. Mikheev 已提交
255
		appendStringInfo(str, "  InitPlan\n");
256
		foreach(lst, plan->initPlan)
V
Vadim B. Mikheev 已提交
257
		{
258
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
V
Vadim B. Mikheev 已提交
259
			for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
260
			{
V
Vadim B. Mikheev 已提交
261
				appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
262
			}
V
Vadim B. Mikheev 已提交
263
			appendStringInfo(str, "    ->  ");
B
Bruce Momjian 已提交
264
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 2, es);
V
Vadim B. Mikheev 已提交
265 266 267
		}
		es->rtable = saved_rtable;
	}
268 269 270 271 272

	/* lefttree */
	if (outerPlan(plan))
	{
		for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
273
		{
274
			appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
275
		}
V
Vadim B. Mikheev 已提交
276 277
		appendStringInfo(str, "  ->  ");
		explain_outNode(str, outerPlan(plan), indent + 3, es);
278
	}
279 280 281 282 283

	/* righttree */
	if (innerPlan(plan))
	{
		for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
284
		{
285
			appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
286
		}
V
Vadim B. Mikheev 已提交
287 288 289
		appendStringInfo(str, "  ->  ");
		explain_outNode(str, innerPlan(plan), indent + 3, es);
	}
290

V
Vadim B. Mikheev 已提交
291 292 293
	/* subPlan-s */
	if (plan->subPlan)
	{
294 295 296
		List	   *saved_rtable = es->rtable;
		List	   *lst;

V
Vadim B. Mikheev 已提交
297
		for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
298
		{
V
Vadim B. Mikheev 已提交
299
			appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
300
		}
V
Vadim B. Mikheev 已提交
301
		appendStringInfo(str, "  SubPlan\n");
302
		foreach(lst, plan->subPlan)
V
Vadim B. Mikheev 已提交
303
		{
304
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
V
Vadim B. Mikheev 已提交
305
			for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
306
			{
V
Vadim B. Mikheev 已提交
307
				appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
308
			}
V
Vadim B. Mikheev 已提交
309
			appendStringInfo(str, "    ->  ");
310
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 4, es);
V
Vadim B. Mikheev 已提交
311 312
		}
		es->rtable = saved_rtable;
313
	}
314 315 316 317 318 319

	if (nodeTag(plan) == T_Append)
	{
		List	   *saved_rtable = es->rtable;
		List	   *lst;
		int			whichplan = 0;
320
		Append	   *appendplan = (Append *) plan;
321 322 323

		foreach(lst, appendplan->appendplans)
		{
324
			Plan	   *subnode = (Plan *) lfirst(lst);
325 326 327 328 329 330 331 332 333 334 335 336 337 338

			if (appendplan->inheritrelid > 0)
			{
				ResTarget  *rtentry;

				es->rtable = appendplan->inheritrtable;
				rtentry = nth(whichplan, appendplan->inheritrtable);
				Assert(rtentry != NULL);
				rt_store(appendplan->inheritrelid, es->rtable, rtentry);
			}
			else
				es->rtable = nth(whichplan, appendplan->unionrtables);

			for (i = 0; i < indent; i++)
M
 
Marc G. Fournier 已提交
339
			{
340
				appendStringInfo(str, "  ");
M
 
Marc G. Fournier 已提交
341
			}
342 343 344 345 346 347 348 349
			appendStringInfo(str, "    ->  ");

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

			whichplan++;
		}
		es->rtable = saved_rtable;
	}
350
	return;
351 352
}

353
static char *
354
Explain_PlanToString(Plan *plan, ExplainState *es)
355
{
356
	StringInfoData	str;
357

358 359 360 361 362
	/* see stringinfo.h for an explanation of this maneuver */
	initStringInfo(&str);
	if (plan != NULL)
		explain_outNode(&str, plan, 0, es);
	return str.data;
363
}
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382

/*
 * Print a message that might exceed the size of the elog message buffer.
 * This is a crock ... there shouldn't be an upper limit to what you can elog().
 */
static void
printLongNotice(const char * header, const char * message)
{
	int		len = strlen(message);

	elog(NOTICE, "%.20s%.*s", header, ELOG_MAXLEN - 64, message);
	len -= ELOG_MAXLEN - 64;
	while (len > 0)
	{
		message += ELOG_MAXLEN - 64;
		elog(NOTICE, "%.*s", ELOG_MAXLEN - 64, message);
		len -= ELOG_MAXLEN - 64;
	}
}