]> granicus.if.org Git - postgresql/blob - src/backend/commands/explain.c
1b861c96204944ccb93fc8cbb986c2fffff8d21e
[postgresql] / src / backend / commands / explain.c
1 /*-------------------------------------------------------------------------
2  *
3  * explain.c--
4  *        Explain the query execution plan
5  *
6  * Copyright (c) 1994-5, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.25 1998/10/21 16:21:20 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <stdio.h>
15 #include <string.h>
16
17 #include <postgres.h>
18
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>
30
31 typedef struct ExplainState
32 {
33         /* 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 ExplainOneQuery(Query *query, bool verbose, CommandDest dest);
42
43
44 /*
45  * ExplainQuery -
46  *        print out the execution plan for a given query
47  *
48  */
49 void
50 ExplainQuery(Query *query, bool verbose, CommandDest dest)
51 {
52         List    *rewritten;
53         List    *l;
54
55         if (IsAbortedTransactionBlockState())
56         {
57                 char       *tag = "*ABORT STATE*";
58
59                 EndCommand(tag, dest);
60
61                 elog(NOTICE, "(transaction aborted): %s",
62                          "queries ignored until END");
63
64                 return;
65         }
66
67         /* Rewrite through rule system */
68         rewritten = QueryRewrite(query);
69
70         /* In the case of an INSTEAD NOTHING, tell at least that */
71         if (rewritten == NIL)
72         {
73                 elog(NOTICE, "query rewrites to nothing");
74                 return;
75         }
76
77         /* Explain every plan */
78         foreach(l, rewritten)
79                 ExplainOneQuery(lfirst(l), verbose, dest);
80 }
81
82 /*
83  * ExplainOneQuery -
84  *        print out the execution plan for one query
85  *
86  */
87 static void
88 ExplainOneQuery(Query *query, bool verbose, CommandDest dest)
89 {
90         char       *s = NULL,
91                            *s2;
92         Plan       *plan;
93         ExplainState *es;
94         int                     len;
95
96         /* plan the queries (XXX we've ignored rewrite!!) */
97         plan = planner(query);
98
99         /* pg_plan could have failed */
100         if (plan == NULL)
101                 return;
102
103         es = (ExplainState *) palloc(sizeof(ExplainState));
104         MemSet(es, 0, sizeof(ExplainState));
105
106         es->printCost = true;           /* default */
107
108         if (verbose)
109                 es->printNodes = true;
110
111         es->rtable = query->rtable;
112
113         if (es->printNodes)
114                 s = nodeToString(plan);
115
116         if (es->printCost)
117         {
118                 s2 = Explain_PlanToString(plan, es);
119                 if (s == NULL)
120                         s = s2;
121                 else
122                 {
123                         strcat(s, "\n\n");
124                         strcat(s, s2);
125                 }
126         }
127
128         /* output the plan */
129         len = strlen(s);
130         elog(NOTICE, "QUERY PLAN:\n\n%.*s", ELOG_MAXLEN - 64, s);
131         len -= ELOG_MAXLEN - 64;
132         while (len > 0)
133         {
134                 s += ELOG_MAXLEN - 64;
135                 elog(NOTICE, "%.*s", ELOG_MAXLEN - 64, s);
136                 len -= ELOG_MAXLEN - 64;
137         }
138         if (es->printNodes)
139                 pprint(plan);                   /* display in postmaster log file */
140
141         pfree(es);
142 }
143
144 /*****************************************************************************
145  *
146  *****************************************************************************/
147
148 /*
149  * explain_outNode -
150  *        converts a Node into ascii string and append it to 'str'
151  */
152 static void
153 explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
154 {
155         List       *l;
156         Relation        relation;
157         char       *pname;
158         char            buf[1000];
159         int                     i;
160
161         if (plan == NULL)
162         {
163                 appendStringInfo(str, "\n");
164                 return;
165         }
166
167         switch (nodeTag(plan))
168         {
169                 case T_Result:
170                         pname = "Result";
171                         break;
172                 case T_Append:
173                         pname = "Append";
174                         break;
175                 case T_NestLoop:
176                         pname = "Nested Loop";
177                         break;
178                 case T_MergeJoin:
179                         pname = "Merge Join";
180                         break;
181                 case T_HashJoin:
182                         pname = "Hash Join";
183                         break;
184                 case T_SeqScan:
185                         pname = "Seq Scan";
186                         break;
187                 case T_IndexScan:
188                         pname = "Index Scan";
189                         break;
190                 case T_Temp:
191                         pname = "Temp Scan";
192                         break;
193                 case T_Sort:
194                         pname = "Sort";
195                         break;
196                 case T_Group:
197                         pname = "Group";
198                         break;
199                 case T_Agg:
200                         pname = "Aggregate";
201                         break;
202                 case T_Unique:
203                         pname = "Unique";
204                         break;
205                 case T_Hash:
206                         pname = "Hash";
207                         break;
208                 case T_Tee:
209                         pname = "Tee";
210                         break;
211                 default:
212                         pname = "";
213                         break;
214         }
215
216 #if 0
217         for (i = 0; i < indent; i++)
218                 appendStringInfo(str, "  ");
219 #endif
220
221         appendStringInfo(str, pname);
222         switch (nodeTag(plan))
223         {
224                 case T_IndexScan:
225                         appendStringInfo(str, " using ");
226                         l = ((IndexScan *) plan)->indxid;
227                         relation = RelationIdCacheGetRelation((int) lfirst(l));
228                         appendStringInfo(str, (RelationGetRelationName(relation))->data);
229                 case T_SeqScan:
230                         if (((Scan *) plan)->scanrelid > 0)
231                         {
232                                 RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
233
234                                 appendStringInfo(str, " on ");
235                                 if (strcmp(rte->refname, rte->relname) != 0)
236                                 {
237                                         sprintf(buf, "%s ", rte->relname);
238                                         appendStringInfo(str, buf);
239                                 }
240                                 appendStringInfo(str, rte->refname);
241                         }
242                         break;
243                 default:
244                         break;
245         }
246         if (es->printCost)
247         {
248                 sprintf(buf, "  (cost=%.2f size=%d width=%d)",
249                                 plan->cost, plan->plan_size, plan->plan_width);
250                 appendStringInfo(str, buf);
251         }
252         appendStringInfo(str, "\n");
253
254         /* initPlan-s */
255         if (plan->initPlan)
256         {
257                 List       *saved_rtable = es->rtable;
258                 List       *lst;
259
260                 for (i = 0; i < indent; i++)
261                         appendStringInfo(str, "  ");
262                 appendStringInfo(str, "  InitPlan\n");
263                 foreach(lst, plan->initPlan)
264                 {
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);
270                 }
271                 es->rtable = saved_rtable;
272         }
273
274         /* lefttree */
275         if (outerPlan(plan))
276         {
277                 for (i = 0; i < indent; i++)
278                         appendStringInfo(str, "  ");
279                 appendStringInfo(str, "  ->  ");
280                 explain_outNode(str, outerPlan(plan), indent + 3, es);
281         }
282
283         /* righttree */
284         if (innerPlan(plan))
285         {
286                 for (i = 0; i < indent; i++)
287                         appendStringInfo(str, "  ");
288                 appendStringInfo(str, "  ->  ");
289                 explain_outNode(str, innerPlan(plan), indent + 3, es);
290         }
291
292         /* subPlan-s */
293         if (plan->subPlan)
294         {
295                 List       *saved_rtable = es->rtable;
296                 List       *lst;
297
298                 for (i = 0; i < indent; i++)
299                         appendStringInfo(str, "  ");
300                 appendStringInfo(str, "  SubPlan\n");
301                 foreach(lst, plan->subPlan)
302                 {
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);
308                 }
309                 es->rtable = saved_rtable;
310         }
311
312         if (nodeTag(plan) == T_Append)
313         {
314                 List       *saved_rtable = es->rtable;
315                 List       *lst;
316                 int                     whichplan = 0;
317                 Append     *appendplan = (Append *) plan;
318
319                 foreach(lst, appendplan->appendplans)
320                 {
321                         Plan       *subnode = (Plan *) lfirst(lst);
322
323                         if (appendplan->inheritrelid > 0)
324                         {
325                                 ResTarget  *rtentry;
326
327                                 es->rtable = appendplan->inheritrtable;
328                                 rtentry = nth(whichplan, appendplan->inheritrtable);
329                                 Assert(rtentry != NULL);
330                                 rt_store(appendplan->inheritrelid, es->rtable, rtentry);
331                         }
332                         else
333                                 es->rtable = nth(whichplan, appendplan->unionrtables);
334
335                         for (i = 0; i < indent; i++)
336                                 appendStringInfo(str, "  ");
337                         appendStringInfo(str, "    ->  ");
338
339                         explain_outNode(str, subnode, indent + 4, es);
340
341                         whichplan++;
342                 }
343                 es->rtable = saved_rtable;
344         }
345         return;
346 }
347
348 static char *
349 Explain_PlanToString(Plan *plan, ExplainState *es)
350 {
351         StringInfo      str;
352         char       *s;
353
354         if (plan == NULL)
355                 return "";
356         Assert(plan != NULL);
357         str = makeStringInfo();
358         explain_outNode(str, plan, 0, es);
359         s = str->data;
360         pfree(str);
361
362         return s;
363 }