1 /*-------------------------------------------------------------------------
4 * Explain the query execution plan
6 * Copyright (c) 1994-5, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.25 1998/10/21 16:21:20 momjian Exp $
12 *-------------------------------------------------------------------------
19 #include <nodes/plannodes.h>
20 #include <nodes/print.h>
21 #include <tcop/tcopprot.h>
22 #include <lib/stringinfo.h>
23 #include <commands/explain.h>
24 #include <parser/parsetree.h>
25 #include <parser/parse_node.h>
26 #include <optimizer/planner.h>
27 #include <access/xact.h>
28 #include <utils/relcache.h>
29 #include <rewrite/rewriteHandler.h>
31 typedef struct ExplainState
34 bool printCost; /* print cost */
35 bool printNodes; /* do nodeToString() instead */
37 List *rtable; /* range table */
40 static char *Explain_PlanToString(Plan *plan, ExplainState *es);
41 static void ExplainOneQuery(Query *query, bool verbose, CommandDest dest);
46 * print out the execution plan for a given query
50 ExplainQuery(Query *query, bool verbose, CommandDest dest)
55 if (IsAbortedTransactionBlockState())
57 char *tag = "*ABORT STATE*";
59 EndCommand(tag, dest);
61 elog(NOTICE, "(transaction aborted): %s",
62 "queries ignored until END");
67 /* Rewrite through rule system */
68 rewritten = QueryRewrite(query);
70 /* In the case of an INSTEAD NOTHING, tell at least that */
73 elog(NOTICE, "query rewrites to nothing");
77 /* Explain every plan */
79 ExplainOneQuery(lfirst(l), verbose, dest);
84 * print out the execution plan for one query
88 ExplainOneQuery(Query *query, bool verbose, CommandDest dest)
96 /* plan the queries (XXX we've ignored rewrite!!) */
97 plan = planner(query);
99 /* pg_plan could have failed */
103 es = (ExplainState *) palloc(sizeof(ExplainState));
104 MemSet(es, 0, sizeof(ExplainState));
106 es->printCost = true; /* default */
109 es->printNodes = true;
111 es->rtable = query->rtable;
114 s = nodeToString(plan);
118 s2 = Explain_PlanToString(plan, es);
128 /* output the plan */
130 elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN - 64, s);
131 len -= ELOG_MAXLEN - 64;
134 s += ELOG_MAXLEN - 64;
135 elog(NOTICE, "%.*s", ELOG_MAXLEN - 64, s);
136 len -= ELOG_MAXLEN - 64;
139 pprint(plan); /* display in postmaster log file */
144 /*****************************************************************************
146 *****************************************************************************/
150 * converts a Node into ascii string and append it to 'str'
153 explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
163 appendStringInfo(str, "\n");
167 switch (nodeTag(plan))
176 pname = "Nested Loop";
179 pname = "Merge Join";
188 pname = "Index Scan";
217 for (i = 0; i < indent; i++)
218 appendStringInfo(str, " ");
221 appendStringInfo(str, pname);
222 switch (nodeTag(plan))
225 appendStringInfo(str, " using ");
226 l = ((IndexScan *) plan)->indxid;
227 relation = RelationIdCacheGetRelation((int) lfirst(l));
228 appendStringInfo(str, (RelationGetRelationName(relation))->data);
230 if (((Scan *) plan)->scanrelid > 0)
232 RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
234 appendStringInfo(str, " on ");
235 if (strcmp(rte->refname, rte->relname) != 0)
237 sprintf(buf, "%s ", rte->relname);
238 appendStringInfo(str, buf);
240 appendStringInfo(str, rte->refname);
248 sprintf(buf, " (cost=%.2f size=%d width=%d)",
249 plan->cost, plan->plan_size, plan->plan_width);
250 appendStringInfo(str, buf);
252 appendStringInfo(str, "\n");
257 List *saved_rtable = es->rtable;
260 for (i = 0; i < indent; i++)
261 appendStringInfo(str, " ");
262 appendStringInfo(str, " InitPlan\n");
263 foreach(lst, plan->initPlan)
265 es->rtable = ((SubPlan *) lfirst(lst))->rtable;
266 for (i = 0; i < indent; i++)
267 appendStringInfo(str, " ");
268 appendStringInfo(str, " -> ");
269 explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 2, es);
271 es->rtable = saved_rtable;
277 for (i = 0; i < indent; i++)
278 appendStringInfo(str, " ");
279 appendStringInfo(str, " -> ");
280 explain_outNode(str, outerPlan(plan), indent + 3, es);
286 for (i = 0; i < indent; i++)
287 appendStringInfo(str, " ");
288 appendStringInfo(str, " -> ");
289 explain_outNode(str, innerPlan(plan), indent + 3, es);
295 List *saved_rtable = es->rtable;
298 for (i = 0; i < indent; i++)
299 appendStringInfo(str, " ");
300 appendStringInfo(str, " SubPlan\n");
301 foreach(lst, plan->subPlan)
303 es->rtable = ((SubPlan *) lfirst(lst))->rtable;
304 for (i = 0; i < indent; i++)
305 appendStringInfo(str, " ");
306 appendStringInfo(str, " -> ");
307 explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, indent + 4, es);
309 es->rtable = saved_rtable;
312 if (nodeTag(plan) == T_Append)
314 List *saved_rtable = es->rtable;
317 Append *appendplan = (Append *) plan;
319 foreach(lst, appendplan->appendplans)
321 Plan *subnode = (Plan *) lfirst(lst);
323 if (appendplan->inheritrelid > 0)
327 es->rtable = appendplan->inheritrtable;
328 rtentry = nth(whichplan, appendplan->inheritrtable);
329 Assert(rtentry != NULL);
330 rt_store(appendplan->inheritrelid, es->rtable, rtentry);
333 es->rtable = nth(whichplan, appendplan->unionrtables);
335 for (i = 0; i < indent; i++)
336 appendStringInfo(str, " ");
337 appendStringInfo(str, " -> ");
339 explain_outNode(str, subnode, indent + 4, es);
343 es->rtable = saved_rtable;
349 Explain_PlanToString(Plan *plan, ExplainState *es)
356 Assert(plan != NULL);
357 str = makeStringInfo();
358 explain_outNode(str, plan, 0, es);