3 * Explain the query execution plan
5 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
6 * Portions Copyright (c) 1994-5, Regents of the University of California
8 * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.86 2002/09/02 01:05:04 tgl Exp $
14 #include "access/genam.h"
15 #include "access/heapam.h"
16 #include "catalog/pg_type.h"
17 #include "commands/explain.h"
18 #include "executor/executor.h"
19 #include "executor/instrument.h"
20 #include "lib/stringinfo.h"
21 #include "nodes/print.h"
22 #include "optimizer/clauses.h"
23 #include "optimizer/planner.h"
24 #include "optimizer/var.h"
25 #include "parser/parsetree.h"
26 #include "rewrite/rewriteHandler.h"
27 #include "tcop/pquery.h"
28 #include "utils/builtins.h"
29 #include "utils/guc.h"
30 #include "utils/lsyscache.h"
33 typedef struct ExplainState
36 bool printCost; /* print cost */
37 bool printNodes; /* do nodeToString() instead */
39 List *rtable; /* range table */
42 static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
43 static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
44 TupOutputState *tstate);
45 static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
46 int indent, ExplainState *es);
47 static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
48 int scanrelid, Plan *outer_plan,
49 StringInfo str, int indent, ExplainState *es);
50 static void show_upper_qual(List *qual, const char *qlabel,
51 const char *outer_name, int outer_varno, Plan *outer_plan,
52 const char *inner_name, int inner_varno, Plan *inner_plan,
53 StringInfo str, int indent, ExplainState *es);
54 static void show_sort_keys(List *tlist, int nkeys, const char *qlabel,
55 StringInfo str, int indent, ExplainState *es);
56 static Node *make_ors_ands_explicit(List *orclauses);
60 * execute an EXPLAIN command
63 ExplainQuery(ExplainStmt *stmt, CommandDest dest)
65 Query *query = stmt->query;
66 TupOutputState *tstate;
71 /* need a tuple descriptor representing a single TEXT column */
72 tupdesc = CreateTemplateTupleDesc(1, false);
73 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
74 TEXTOID, -1, 0, false);
76 /* prepare for projection of tuples */
77 tstate = begin_tup_output_tupdesc(dest, tupdesc);
79 if (query->commandType == CMD_UTILITY)
81 /* rewriter will not cope with utility statements */
82 do_text_output_oneline(tstate, "Utility statements have no plan structure");
86 /* Rewrite through rule system */
87 rewritten = QueryRewrite(query);
91 /* In the case of an INSTEAD NOTHING, tell at least that */
92 do_text_output_oneline(tstate, "Query rewrites to nothing");
96 /* Explain every plan */
99 ExplainOneQuery(lfirst(l), stmt, tstate);
100 /* put a blank line between plans */
102 do_text_output_oneline(tstate, "");
107 end_tup_output(tstate);
112 * print out the execution plan for one query
115 ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
119 double totaltime = 0;
121 /* planner will not cope with utility statements */
122 if (query->commandType == CMD_UTILITY)
124 if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
125 do_text_output_oneline(tstate, "NOTIFY");
127 do_text_output_oneline(tstate, "UTILITY");
132 plan = planner(query);
134 /* pg_plan could have failed */
138 /* Execute the plan for statistics if asked for */
141 struct timeval starttime;
142 struct timeval endtime;
145 * Set up the instrumentation for the top node. This will cascade
146 * during plan initialisation
148 plan->instrument = InstrAlloc();
150 gettimeofday(&starttime, NULL);
151 ProcessQuery(query, plan, None, NULL);
152 CommandCounterIncrement();
153 gettimeofday(&endtime, NULL);
155 endtime.tv_sec -= starttime.tv_sec;
156 endtime.tv_usec -= starttime.tv_usec;
157 while (endtime.tv_usec < 0)
159 endtime.tv_usec += 1000000;
162 totaltime = (double) endtime.tv_sec +
163 (double) endtime.tv_usec / 1000000.0;
166 es = (ExplainState *) palloc(sizeof(ExplainState));
167 MemSet(es, 0, sizeof(ExplainState));
169 es->printCost = true; /* default */
172 es->printNodes = true;
174 es->rtable = query->rtable;
181 s = nodeToString(plan);
184 if (Explain_pretty_print)
185 f = pretty_format_node_dump(s);
187 f = format_node_dump(s);
189 do_text_output_multiline(tstate, f);
192 do_text_output_oneline(tstate, ""); /* separator line */
200 str = Explain_PlanToString(plan, es);
202 appendStringInfo(str, "Total runtime: %.2f msec\n",
204 do_text_output_multiline(tstate, str->data);
215 * converts a Plan node into ascii string and appends it to 'str'
217 * outer_plan, if not null, references another plan node that is the outer
218 * side of a join with the current node. This is only interesting for
219 * deciphering runtime keys of an inner indexscan.
222 explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
223 int indent, ExplainState *es)
231 appendStringInfo(str, "\n");
235 switch (nodeTag(plan))
244 pname = "Nested Loop";
247 pname = "Merge Join";
256 pname = "Index Scan";
262 pname = "Subquery Scan";
265 pname = "Function Scan";
268 pname = "Materialize";
283 switch (((SetOp *) plan)->cmd)
285 case SETOPCMD_INTERSECT:
286 pname = "SetOp Intersect";
288 case SETOPCMD_INTERSECT_ALL:
289 pname = "SetOp Intersect All";
291 case SETOPCMD_EXCEPT:
292 pname = "SetOp Except";
294 case SETOPCMD_EXCEPT_ALL:
295 pname = "SetOp Except All";
313 appendStringInfo(str, pname);
314 switch (nodeTag(plan))
317 if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
318 appendStringInfo(str, " Backward");
319 appendStringInfo(str, " using ");
321 foreach(l, ((IndexScan *) plan)->indxid)
325 relation = index_open(lfirsti(l));
326 appendStringInfo(str, "%s%s",
327 (++i > 1) ? ", " : "",
328 quote_identifier(RelationGetRelationName(relation)));
329 index_close(relation);
334 if (((Scan *) plan)->scanrelid > 0)
336 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
340 /* Assume it's on a real relation */
341 Assert(rte->rtekind == RTE_RELATION);
343 /* We only show the rel name, not schema name */
344 relname = get_rel_name(rte->relid);
346 appendStringInfo(str, " on %s",
347 quote_identifier(relname));
348 if (strcmp(rte->eref->aliasname, relname) != 0)
349 appendStringInfo(str, " %s",
350 quote_identifier(rte->eref->aliasname));
354 if (((Scan *) plan)->scanrelid > 0)
356 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
359 appendStringInfo(str, " %s",
360 quote_identifier(rte->eref->aliasname));
364 if (((Scan *) plan)->scanrelid > 0)
366 RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
373 /* Assert it's on a RangeFunction */
374 Assert(rte->rtekind == RTE_FUNCTION);
376 expr = (Expr *) rte->funcexpr;
377 funcnode = (Func *) expr->oper;
378 funcid = funcnode->funcid;
380 /* We only show the func name, not schema name */
381 proname = get_func_name(funcid);
383 appendStringInfo(str, " on %s",
384 quote_identifier(proname));
385 if (strcmp(rte->eref->aliasname, proname) != 0)
386 appendStringInfo(str, " %s",
387 quote_identifier(rte->eref->aliasname));
395 appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
396 plan->startup_cost, plan->total_cost,
397 plan->plan_rows, plan->plan_width);
399 if (plan->instrument && plan->instrument->nloops > 0)
401 double nloops = plan->instrument->nloops;
403 appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
404 1000.0 * plan->instrument->startup / nloops,
405 1000.0 * plan->instrument->total / nloops,
406 plan->instrument->ntuples / nloops,
407 plan->instrument->nloops);
410 appendStringInfo(str, "\n");
412 /* quals, sort keys, etc */
413 switch (nodeTag(plan))
416 show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
418 ((Scan *) plan)->scanrelid,
421 show_scan_qual(plan->qual, false,
423 ((Scan *) plan)->scanrelid,
430 show_scan_qual(plan->qual, false,
432 ((Scan *) plan)->scanrelid,
437 show_upper_qual(((NestLoop *) plan)->join.joinqual,
439 "outer", OUTER, outerPlan(plan),
440 "inner", INNER, innerPlan(plan),
442 show_upper_qual(plan->qual,
444 "outer", OUTER, outerPlan(plan),
445 "inner", INNER, innerPlan(plan),
449 show_upper_qual(((MergeJoin *) plan)->mergeclauses,
451 "outer", OUTER, outerPlan(plan),
452 "inner", INNER, innerPlan(plan),
454 show_upper_qual(((MergeJoin *) plan)->join.joinqual,
456 "outer", OUTER, outerPlan(plan),
457 "inner", INNER, innerPlan(plan),
459 show_upper_qual(plan->qual,
461 "outer", OUTER, outerPlan(plan),
462 "inner", INNER, innerPlan(plan),
466 show_upper_qual(((HashJoin *) plan)->hashclauses,
468 "outer", OUTER, outerPlan(plan),
469 "inner", INNER, innerPlan(plan),
471 show_upper_qual(((HashJoin *) plan)->join.joinqual,
473 "outer", OUTER, outerPlan(plan),
474 "inner", INNER, innerPlan(plan),
476 show_upper_qual(plan->qual,
478 "outer", OUTER, outerPlan(plan),
479 "inner", INNER, innerPlan(plan),
483 show_upper_qual(plan->qual,
485 "subplan", 1, ((SubqueryScan *) plan)->subplan,
491 show_upper_qual(plan->qual,
493 "subplan", 0, outerPlan(plan),
498 show_sort_keys(plan->targetlist, ((Sort *) plan)->keycount,
503 show_upper_qual((List *) ((Result *) plan)->resconstantqual,
505 "subplan", OUTER, outerPlan(plan),
508 show_upper_qual(plan->qual,
510 "subplan", OUTER, outerPlan(plan),
521 List *saved_rtable = es->rtable;
524 for (i = 0; i < indent; i++)
525 appendStringInfo(str, " ");
526 appendStringInfo(str, " InitPlan\n");
527 foreach(lst, plan->initPlan)
529 es->rtable = ((SubPlan *) lfirst(lst))->rtable;
530 for (i = 0; i < indent; i++)
531 appendStringInfo(str, " ");
532 appendStringInfo(str, " -> ");
533 explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
536 es->rtable = saved_rtable;
542 for (i = 0; i < indent; i++)
543 appendStringInfo(str, " ");
544 appendStringInfo(str, " -> ");
545 explain_outNode(str, outerPlan(plan), NULL, indent + 3, es);
551 for (i = 0; i < indent; i++)
552 appendStringInfo(str, " ");
553 appendStringInfo(str, " -> ");
554 explain_outNode(str, innerPlan(plan), outerPlan(plan),
558 if (IsA(plan, Append))
560 Append *appendplan = (Append *) plan;
563 foreach(lst, appendplan->appendplans)
565 Plan *subnode = (Plan *) lfirst(lst);
567 for (i = 0; i < indent; i++)
568 appendStringInfo(str, " ");
569 appendStringInfo(str, " -> ");
571 explain_outNode(str, subnode, NULL, indent + 3, es);
575 if (IsA(plan, SubqueryScan))
577 SubqueryScan *subqueryscan = (SubqueryScan *) plan;
578 Plan *subnode = subqueryscan->subplan;
579 RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
581 List *saved_rtable = es->rtable;
583 Assert(rte->rtekind == RTE_SUBQUERY);
584 es->rtable = rte->subquery->rtable;
586 for (i = 0; i < indent; i++)
587 appendStringInfo(str, " ");
588 appendStringInfo(str, " -> ");
590 explain_outNode(str, subnode, NULL, indent + 3, es);
592 es->rtable = saved_rtable;
598 List *saved_rtable = es->rtable;
601 for (i = 0; i < indent; i++)
602 appendStringInfo(str, " ");
603 appendStringInfo(str, " SubPlan\n");
604 foreach(lst, plan->subPlan)
606 es->rtable = ((SubPlan *) lfirst(lst))->rtable;
607 for (i = 0; i < indent; i++)
608 appendStringInfo(str, " ");
609 appendStringInfo(str, " -> ");
610 explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
613 es->rtable = saved_rtable;
618 Explain_PlanToString(Plan *plan, ExplainState *es)
620 StringInfo str = makeStringInfo();
623 explain_outNode(str, plan, NULL, 0, es);
628 * Show a qualifier expression for a scan plan node
631 show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
632 int scanrelid, Plan *outer_plan,
633 StringInfo str, int indent, ExplainState *es)
643 /* No work if empty qual */
648 if (lfirst(qual) == NIL && lnext(qual) == NIL)
652 /* Fix qual --- indexqual requires different processing */
654 node = make_ors_ands_explicit(qual);
656 node = (Node *) make_ands_explicit(qual);
658 /* Generate deparse context */
659 Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
660 rte = rt_fetch(scanrelid, es->rtable);
661 scancontext = deparse_context_for_rte(rte);
664 * If we have an outer plan that is referenced by the qual, add it to
665 * the deparse context. If not, don't (so that we don't force prefixes
670 if (intMember(OUTER, pull_varnos(node)))
671 outercontext = deparse_context_for_subplan("outer",
672 outer_plan->targetlist,
680 context = deparse_context_for_plan(scanrelid, scancontext,
684 /* Deparse the expression */
685 exprstr = deparse_expression(node, context, (outercontext != NULL));
688 for (i = 0; i < indent; i++)
689 appendStringInfo(str, " ");
690 appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
694 * Show a qualifier expression for an upper-level plan node
697 show_upper_qual(List *qual, const char *qlabel,
698 const char *outer_name, int outer_varno, Plan *outer_plan,
699 const char *inner_name, int inner_varno, Plan *inner_plan,
700 StringInfo str, int indent, ExplainState *es)
709 /* No work if empty qual */
713 /* Generate deparse context */
715 outercontext = deparse_context_for_subplan(outer_name,
716 outer_plan->targetlist,
721 innercontext = deparse_context_for_subplan(inner_name,
722 inner_plan->targetlist,
726 context = deparse_context_for_plan(outer_varno, outercontext,
727 inner_varno, innercontext,
730 /* Deparse the expression */
731 node = (Node *) make_ands_explicit(qual);
732 exprstr = deparse_expression(node, context, (inner_plan != NULL));
735 for (i = 0; i < indent; i++)
736 appendStringInfo(str, " ");
737 appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
741 * Show the sort keys for a Sort node.
744 show_sort_keys(List *tlist, int nkeys, const char *qlabel,
745 StringInfo str, int indent, ExplainState *es)
757 for (i = 0; i < indent; i++)
758 appendStringInfo(str, " ");
759 appendStringInfo(str, " %s: ", qlabel);
762 * In this routine we expect that the plan node's tlist has not been
763 * processed by set_plan_references(). Normally, any Vars will contain
764 * valid varnos referencing the actual rtable. But we might instead be
765 * looking at a dummy tlist generated by prepunion.c; if there are
766 * Vars with zero varno, use the tlist itself to determine their names.
768 if (intMember(0, pull_varnos((Node *) tlist)))
772 outercontext = deparse_context_for_subplan("sort",
775 context = deparse_context_for_plan(0, outercontext,
782 context = deparse_context_for_plan(0, NULL,
785 useprefix = length(es->rtable) > 1;
788 for (keyno = 1; keyno <= nkeys; keyno++)
790 /* find key expression in tlist */
793 TargetEntry *target = (TargetEntry *) lfirst(tl);
795 if (target->resdom->reskey == keyno)
797 /* Deparse the expression */
798 exprstr = deparse_expression(target->expr, context, useprefix);
801 appendStringInfo(str, ", ");
802 appendStringInfo(str, "%s", exprstr);
807 elog(ERROR, "show_sort_keys: no tlist entry for key %d", keyno);
810 appendStringInfo(str, "\n");
814 * Indexscan qual lists have an implicit OR-of-ANDs structure. Make it
815 * explicit so deparsing works properly.
818 make_ors_ands_explicit(List *orclauses)
820 if (orclauses == NIL)
821 return NULL; /* probably can't happen */
822 else if (lnext(orclauses) == NIL)
823 return (Node *) make_ands_explicit(lfirst(orclauses));
829 foreach(orptr, orclauses)
831 args = lappend(args, make_ands_explicit(lfirst(orptr)));
834 return (Node *) make_orclause(args);