aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/explain.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/explain.c')
-rw-r--r--src/backend/commands/explain.c220
1 files changed, 214 insertions, 6 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 319a9c83d82..e230ba598a0 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
* 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.70 2002/03/06 06:09:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.71 2002/03/12 00:51:35 tgl Exp $
*
*/
@@ -15,12 +15,15 @@
#include "executor/instrument.h"
#include "lib/stringinfo.h"
#include "nodes/print.h"
+#include "optimizer/clauses.h"
#include "optimizer/planner.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
#include "tcop/pquery.h"
+#include "utils/builtins.h"
#include "utils/relcache.h"
+
typedef struct ExplainState
{
/* options */
@@ -32,6 +35,14 @@ typedef struct ExplainState
static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest);
+static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+ int scanrelid,
+ StringInfo str, int indent, ExplainState *es);
+static void show_upper_qual(List *qual, const char *qlabel,
+ const char *outer_name, int outer_varno, Plan *outer_plan,
+ const char *inner_name, int inner_varno, Plan *inner_plan,
+ StringInfo str, int indent, ExplainState *es);
+static Node *make_ors_ands_explicit(List *orclauses);
/* Convert a null string pointer into "<>" */
#define stringStringInfo(s) (((s) == NULL) ? "<>" : (s))
@@ -40,7 +51,6 @@ static void ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDes
/*
* ExplainQuery -
* print out the execution plan for a given query
- *
*/
void
ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -81,7 +91,6 @@ ExplainQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
/*
* ExplainOneQuery -
* print out the execution plan for one query
- *
*/
static void
ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
@@ -176,9 +185,6 @@ ExplainOneQuery(Query *query, bool verbose, bool analyze, CommandDest dest)
pfree(es);
}
-/*****************************************************************************
- *
- *****************************************************************************/
/*
* explain_outNode -
@@ -341,6 +347,90 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
}
appendStringInfo(str, "\n");
+ /* quals */
+ switch (nodeTag(plan))
+ {
+ case T_IndexScan:
+ show_scan_qual(((IndexScan *) plan)->indxqualorig, true,
+ "indxqual",
+ ((Scan *) plan)->scanrelid,
+ str, indent, es);
+ show_scan_qual(plan->qual, false, "qual",
+ ((Scan *) plan)->scanrelid,
+ str, indent, es);
+ break;
+ case T_SeqScan:
+ case T_TidScan:
+ show_scan_qual(plan->qual, false, "qual",
+ ((Scan *) plan)->scanrelid,
+ str, indent, es);
+ break;
+ case T_NestLoop:
+ show_upper_qual(((NestLoop *) plan)->join.joinqual, "joinqual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ show_upper_qual(plan->qual, "qual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ break;
+ case T_MergeJoin:
+ show_upper_qual(((MergeJoin *) plan)->mergeclauses, "merge",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ show_upper_qual(((MergeJoin *) plan)->join.joinqual, "joinqual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ show_upper_qual(plan->qual, "qual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ break;
+ case T_HashJoin:
+ show_upper_qual(((HashJoin *) plan)->hashclauses, "hash",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ show_upper_qual(((HashJoin *) plan)->join.joinqual, "joinqual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ show_upper_qual(plan->qual, "qual",
+ "outer", OUTER, outerPlan(plan),
+ "inner", INNER, innerPlan(plan),
+ str, indent, es);
+ break;
+ case T_SubqueryScan:
+ show_upper_qual(plan->qual, "qual",
+ "subplan", 1, ((SubqueryScan *) plan)->subplan,
+ "", 0, NULL,
+ str, indent, es);
+ break;
+ case T_Agg:
+ case T_Group:
+ show_upper_qual(plan->qual, "qual",
+ "subplan", 0, outerPlan(plan),
+ "", 0, NULL,
+ str, indent, es);
+ break;
+ case T_Result:
+ show_upper_qual((List *) ((Result *) plan)->resconstantqual,
+ "constqual",
+ "subplan", OUTER, outerPlan(plan),
+ "", 0, NULL,
+ str, indent, es);
+ show_upper_qual(plan->qual, "qual",
+ "subplan", OUTER, outerPlan(plan),
+ "", 0, NULL,
+ str, indent, es);
+ break;
+ default:
+ break;
+ }
+
/* initPlan-s */
if (plan->initPlan)
{
@@ -448,3 +538,121 @@ Explain_PlanToString(Plan *plan, ExplainState *es)
explain_outNode(str, plan, 0, es);
return str;
}
+
+/*
+ * Show a qualifier expression for a scan plan node
+ */
+static void
+show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
+ int scanrelid,
+ StringInfo str, int indent, ExplainState *es)
+{
+ RangeTblEntry *rte;
+ List *context;
+ Node *node;
+ char *exprstr;
+ int i;
+
+ /* No work if empty qual */
+ if (qual == NIL)
+ return;
+ if (is_or_qual)
+ {
+ if (lfirst(qual) == NIL && lnext(qual) == NIL)
+ return;
+ }
+
+ /* Generate deparse context */
+ Assert(scanrelid > 0 && scanrelid <= length(es->rtable));
+ rte = rt_fetch(scanrelid, es->rtable);
+
+ /* Assume it's on a real relation */
+ Assert(rte->relname);
+
+ context = deparse_context_for(rte->relname, rte->relid);
+
+ /* Fix qual --- indexqual requires different processing */
+ if (is_or_qual)
+ node = make_ors_ands_explicit(qual);
+ else
+ node = (Node *) make_ands_explicit(qual);
+
+ /* Deparse the expression */
+ exprstr = deparse_expression(node, context, false);
+
+ /* And add to str */
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
+}
+
+/*
+ * Show a qualifier expression for an upper-level plan node
+ */
+static void
+show_upper_qual(List *qual, const char *qlabel,
+ const char *outer_name, int outer_varno, Plan *outer_plan,
+ const char *inner_name, int inner_varno, Plan *inner_plan,
+ StringInfo str, int indent, ExplainState *es)
+{
+ List *context;
+ Node *outercontext;
+ Node *innercontext;
+ Node *node;
+ char *exprstr;
+ int i;
+
+ /* No work if empty qual */
+ if (qual == NIL)
+ return;
+
+ /* Generate deparse context */
+ if (outer_plan)
+ outercontext = deparse_context_for_subplan(outer_name,
+ outer_plan->targetlist,
+ es->rtable);
+ else
+ outercontext = NULL;
+ if (inner_plan)
+ innercontext = deparse_context_for_subplan(inner_name,
+ inner_plan->targetlist,
+ es->rtable);
+ else
+ innercontext = NULL;
+ context = deparse_context_for_plan(outer_varno, outercontext,
+ inner_varno, innercontext);
+
+ /* Deparse the expression */
+ node = (Node *) make_ands_explicit(qual);
+ exprstr = deparse_expression(node, context, (inner_plan != NULL));
+
+ /* And add to str */
+ for (i = 0; i < indent; i++)
+ appendStringInfo(str, " ");
+ appendStringInfo(str, " %s: %s\n", qlabel, exprstr);
+}
+
+/*
+ * Indexscan qual lists have an implicit OR-of-ANDs structure. Make it
+ * explicit so deparsing works properly.
+ */
+static Node *
+make_ors_ands_explicit(List *orclauses)
+{
+ if (orclauses == NIL)
+ return NULL; /* probably can't happen */
+ else if (lnext(orclauses) == NIL)
+ return (Node *) make_ands_explicit(lfirst(orclauses));
+ else
+ {
+ List *args = NIL;
+ List *orptr;
+
+ foreach(orptr, orclauses)
+ {
+ args = lappend(args, make_ands_explicit(lfirst(orptr)));
+ }
+
+ return (Node *) make_orclause(args);
+ }
+}