explain.c 7.7 KB
Newer Older
1 2 3
/*-------------------------------------------------------------------------
 *
 * explain.c--
4
 *	  Explain the query execution plan
5 6 7 8 9
 *
 * Copyright (c) 1994-5, Regents of the University of California
 *
 *
 * IDENTIFICATION
M
 
Marc G. Fournier 已提交
10
 *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.28 1998/12/14 05:18:43 scrappy Exp $
11 12 13
 *
 *-------------------------------------------------------------------------
 */
14
#include <stdio.h>
15
#include <string.h>
16

M
Marc G. Fournier 已提交
17 18 19
#include <postgres.h>

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

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

40
static char *Explain_PlanToString(Plan *plan, ExplainState *es);
41
static void printLongNotice(const char * header, const char * message);
B
Bruce Momjian 已提交
42 43
static void ExplainOneQuery(Query *query, bool verbose, CommandDest dest);

44 45 46

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

56 57
	if (IsAbortedTransactionBlockState())
	{
58
		char	   *tag = "*ABORT STATE*";
59

60 61 62 63 64 65 66
		EndCommand(tag, dest);

		elog(NOTICE, "(transaction aborted): %s",
			 "queries ignored until END");

		return;
	}
67

B
Bruce Momjian 已提交
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	/* Rewrite through rule system */
	rewritten = QueryRewrite(query);

	/* In the case of an INSTEAD NOTHING, tell at least that */
	if (rewritten == NIL)
	{
		elog(NOTICE, "query rewrites to nothing");
		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)
{
91
	char	   *s;
B
Bruce Momjian 已提交
92 93 94
	Plan	   *plan;
	ExplainState *es;

95 96
	/* plan the queries (XXX we've ignored rewrite!!) */
	plan = planner(query);
97

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

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

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

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

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

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

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

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

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

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

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

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

	switch (nodeTag(plan))
	{
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
		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;
		case T_Temp:
			pname = "Temp Scan";
			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;
		case T_Tee:
			pname = "Tee";
			break;
		default:
			pname = "";
			break;
208 209 210 211 212
	}

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

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

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

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

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

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

V
Vadim B. Mikheev 已提交
286 287 288
	/* subPlan-s */
	if (plan->subPlan)
	{
289 290 291
		List	   *saved_rtable = es->rtable;
		List	   *lst;

V
Vadim B. Mikheev 已提交
292 293 294
		for (i = 0; i < indent; i++)
			appendStringInfo(str, "  ");
		appendStringInfo(str, "  SubPlan\n");
295
		foreach(lst, plan->subPlan)
V
Vadim B. Mikheev 已提交
296
		{
297
			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
V
Vadim B. Mikheev 已提交
298 299 300
			for (i = 0; i < indent; i++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");
301
			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 4, es);
V
Vadim B. Mikheev 已提交
302 303
		}
		es->rtable = saved_rtable;
304
	}
305 306 307 308 309 310

	if (nodeTag(plan) == T_Append)
	{
		List	   *saved_rtable = es->rtable;
		List	   *lst;
		int			whichplan = 0;
311
		Append	   *appendplan = (Append *) plan;
312 313 314

		foreach(lst, appendplan->appendplans)
		{
315
			Plan	   *subnode = (Plan *) lfirst(lst);
316 317 318 319 320 321 322 323 324 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++)
				appendStringInfo(str, "  ");
			appendStringInfo(str, "    ->  ");

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

			whichplan++;
		}
		es->rtable = saved_rtable;
	}
339
	return;
340 341
}

342
static char *
343
Explain_PlanToString(Plan *plan, ExplainState *es)
344
{
345 346
	StringInfo	str;
	char	   *s;
347 348 349 350 351 352 353 354 355 356

	if (plan == NULL)
		return "";
	Assert(plan != NULL);
	str = makeStringInfo();
	explain_outNode(str, plan, 0, es);
	s = str->data;
	pfree(str);

	return s;
357
}
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

/*
 * 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;
	}
}