postgresql/src/backend/commands/explain.c

397 lines
8.4 KiB
C
Raw Normal View History

/*
* explain.c
* Explain the query execution plan
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.63 2001/01/24 19:42:52 momjian Exp $
*
*/
#include "postgres.h"
1996-11-06 09:21:43 +01:00
#include "commands/explain.h"
1999-07-16 07:00:38 +02:00
#include "lib/stringinfo.h"
#include "nodes/print.h"
#include "optimizer/planner.h"
1999-07-16 07:00:38 +02:00
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
1999-07-16 07:00:38 +02:00
#include "utils/relcache.h"
typedef struct ExplainState
{
/* options */
bool printCost; /* print cost */
bool printNodes; /* do nodeToString() instead */
/* other states */
List *rtable; /* range table */
} ExplainState;
static char *Explain_PlanToString(Plan *plan, ExplainState *es);
static void ExplainOneQuery(Query *query, bool verbose, CommandDest dest);
/* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
/*
* ExplainQuery -
* print out the execution plan for a given query
*
*/
void
ExplainQuery(Query *query, bool verbose, CommandDest dest)
{
1999-05-25 18:15:34 +02:00
List *rewritten;
List *l;
/* rewriter and planner may not work in aborted state? */
if (IsAbortedTransactionBlockState())
{
elog(NOTICE, "(transaction aborted): %s",
"queries ignored until END");
return;
}
/* rewriter and planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
{
elog(NOTICE, "Utility statements have no plan structure");
return;
}
/* 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)
{
char *s;
Plan *plan;
ExplainState *es;
/* plan the query */
plan = planner(query);
/* pg_plan could have failed */
if (plan == NULL)
return;
1996-12-29 20:31:16 +01:00
1997-12-29 06:13:57 +01:00
es = (ExplainState *) palloc(sizeof(ExplainState));
1997-09-18 22:22:58 +02:00
MemSet(es, 0, sizeof(ExplainState));
1996-12-29 20:31:16 +01:00
es->printCost = true; /* default */
if (verbose)
es->printNodes = true;
1996-12-29 01:53:20 +01:00
es->rtable = query->rtable;
if (es->printNodes)
{
s = nodeToString(plan);
if (s)
{
elog(NOTICE, "QUERY DUMP:\n\n%s", s);
pfree(s);
}
}
if (es->printCost)
{
s = Explain_PlanToString(plan, es);
if (s)
{
elog(NOTICE, "QUERY PLAN:\n\n%s", s);
pfree(s);
}
1996-12-29 01:53:20 +01:00
}
if (es->printNodes)
pprint(plan); /* display in postmaster log file */
1997-12-29 06:13:57 +01:00
pfree(es);
}
/*****************************************************************************
*
*****************************************************************************/
/*
* explain_outNode -
* converts a Node into ascii string and append it to 'str'
*/
static void
explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
{
1999-05-25 18:15:34 +02:00
List *l;
Relation relation;
1999-05-25 18:15:34 +02:00
char *pname;
int i;
if (plan == NULL)
{
appendStringInfo(str, "\n");
return;
}
switch (nodeTag(plan))
{
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_TidScan:
pname = "Tid Scan";
break;
case T_SubqueryScan:
pname = "Subquery Scan";
break;
case T_Material:
pname = "Materialize";
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_SetOp:
switch (((SetOp *) plan)->cmd)
{
case SETOPCMD_INTERSECT:
pname = "SetOp Intersect";
break;
case SETOPCMD_INTERSECT_ALL:
pname = "SetOp Intersect All";
break;
case SETOPCMD_EXCEPT:
pname = "SetOp Except";
break;
case SETOPCMD_EXCEPT_ALL:
pname = "SetOp Except All";
break;
default:
pname = "SetOp ???";
break;
}
break;
case T_Limit:
pname = "Limit";
break;
case T_Hash:
pname = "Hash";
break;
default:
pname = "???";
break;
}
appendStringInfo(str, pname);
switch (nodeTag(plan))
{
case T_IndexScan:
if (ScanDirectionIsBackward(((IndexScan *) plan)->indxorderdir))
appendStringInfo(str, " Backward");
appendStringInfo(str, " using ");
i = 0;
1999-05-25 18:15:34 +02:00
foreach(l, ((IndexScan *) plan)->indxid)
{
relation = RelationIdGetRelation(lfirsti(l));
Assert(relation);
appendStringInfo(str, "%s%s",
(++i > 1) ? ", " : "",
stringStringInfo(RelationGetRelationName(relation)));
/* drop relcache refcount from RelationIdGetRelation */
RelationDecrementReferenceCount(relation);
}
/* FALL THRU */
case T_SeqScan:
case T_TidScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
/* Assume it's on a real relation */
Assert(rte->relname);
appendStringInfo(str, " on %s",
stringStringInfo(rte->relname));
if (strcmp(rte->eref->relname, rte->relname) != 0)
appendStringInfo(str, " %s",
stringStringInfo(rte->eref->relname));
}
break;
case T_SubqueryScan:
if (((Scan *) plan)->scanrelid > 0)
{
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
es->rtable);
appendStringInfo(str, " %s",
stringStringInfo(rte->eref->relname));
}
break;
default:
break;
}
if (es->printCost)
{
appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width);
}
appendStringInfo(str, "\n");
1998-02-13 04:21:30 +01:00
/* initPlan-s */
if (plan->initPlan)
{
List *saved_rtable = es->rtable;
List *lst;
1999-05-25 18:15:34 +02:00
for (i = 0; i < indent; i++)
1998-02-13 04:21:30 +01:00
appendStringInfo(str, " ");
appendStringInfo(str, " InitPlan\n");
foreach(lst, plan->initPlan)
1998-02-13 04:21:30 +01:00
{
es->rtable = ((SubPlan *) lfirst(lst))->rtable;
1998-02-13 04:21:30 +01:00
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
indent + 4, es);
1998-02-13 04:21:30 +01:00
}
es->rtable = saved_rtable;
}
/* lefttree */
if (outerPlan(plan))
{
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
1998-02-13 04:21:30 +01:00
appendStringInfo(str, " -> ");
explain_outNode(str, outerPlan(plan), indent + 3, es);
}
/* righttree */
if (innerPlan(plan))
{
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
1998-02-13 04:21:30 +01:00
appendStringInfo(str, " -> ");
explain_outNode(str, innerPlan(plan), indent + 3, es);
}
if (IsA(plan, Append))
{
Append *appendplan = (Append *) plan;
List *lst;
foreach(lst, appendplan->appendplans)
{
Plan *subnode = (Plan *) lfirst(lst);
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, subnode, indent + 3, es);
}
}
if (IsA(plan, SubqueryScan))
{
SubqueryScan *subqueryscan = (SubqueryScan *) plan;
Plan *subnode = subqueryscan->subplan;
RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
es->rtable);
List *saved_rtable = es->rtable;
Assert(rte->subquery != NULL);
es->rtable = rte->subquery->rtable;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, subnode, indent + 3, es);
es->rtable = saved_rtable;
}
/* subPlan-s */
if (plan->subPlan)
{
List *saved_rtable = es->rtable;
List *lst;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " SubPlan\n");
foreach(lst, plan->subPlan)
{
es->rtable = ((SubPlan *) lfirst(lst))->rtable;
for (i = 0; i < indent; i++)
appendStringInfo(str, " ");
appendStringInfo(str, " -> ");
explain_outNode(str, ((SubPlan *) lfirst(lst))->plan,
indent + 4, es);
}
es->rtable = saved_rtable;
}
}
static char *
Explain_PlanToString(Plan *plan, ExplainState *es)
{
1999-05-25 18:15:34 +02:00
StringInfoData str;
/* see stringinfo.h for an explanation of this maneuver */
initStringInfo(&str);
if (plan != NULL)
explain_outNode(&str, plan, 0, es);
return str.data;
}