aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-04-08 18:49:52 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-04-08 18:49:52 +0000
commitc6e81aeef39fd546976c5d90e484db0ddb2536e7 (patch)
tree78746e33bce7278b3061460261a20c2bcad6e3ad /src
parent74bdf965a688301146b174fed5987c764517142f (diff)
downloadpostgresql-c6e81aeef39fd546976c5d90e484db0ddb2536e7.tar.gz
postgresql-c6e81aeef39fd546976c5d90e484db0ddb2536e7.zip
Fix EXPLAIN so that it can drill down through multiple levels of subplan
when trying to locate the referent of a RECORD variable. This fixes the 'record type has not been registered' failure reported by Stefan Kaltenbrunner about a month ago. A side effect of the way I chose to fix it is that most variable references in join conditions will now be properly labeled with the variable's source table name, instead of the not-too-helpful 'outer' or 'inner' we used to use.
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c41
-rw-r--r--src/backend/utils/adt/ruleutils.c193
-rw-r--r--src/include/utils/builtins.h6
3 files changed, 113 insertions, 127 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e6492720b6a..9c71cfeea89 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.146 2006/03/05 15:58:24 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.147 2006/04/08 18:49:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -58,7 +58,7 @@ 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 void show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
+static void show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
const char *qlabel,
StringInfo str, int indent, ExplainState *es);
@@ -815,7 +815,7 @@ explain_outNode(StringInfo str,
str, indent, es);
break;
case T_Sort:
- show_sort_keys(plan->targetlist,
+ show_sort_keys(plan,
((Sort *) plan)->numCols,
((Sort *) plan)->sortColIdx,
"Sort Key",
@@ -1030,8 +1030,6 @@ show_scan_qual(List *qual, const char *qlabel,
int scanrelid, Plan *outer_plan,
StringInfo str, int indent, ExplainState *es)
{
- RangeTblEntry *rte;
- Node *scancontext;
Node *outercontext;
List *context;
Node *node;
@@ -1045,11 +1043,6 @@ show_scan_qual(List *qual, const char *qlabel,
/* Convert AND list to explicit AND */
node = (Node *) make_ands_explicit(qual);
- /* Generate deparse context */
- Assert(scanrelid > 0 && scanrelid <= list_length(es->rtable));
- rte = rt_fetch(scanrelid, es->rtable);
- scancontext = deparse_context_for_rte(rte);
-
/*
* If we have an outer plan that is referenced by the qual, add it to the
* deparse context. If not, don't (so that we don't force prefixes
@@ -1061,8 +1054,7 @@ show_scan_qual(List *qual, const char *qlabel,
if (bms_is_member(OUTER, varnos))
outercontext = deparse_context_for_subplan("outer",
- outer_plan->targetlist,
- es->rtable);
+ (Node *) outer_plan);
else
outercontext = NULL;
bms_free(varnos);
@@ -1070,9 +1062,9 @@ show_scan_qual(List *qual, const char *qlabel,
else
outercontext = NULL;
- context = deparse_context_for_plan(scanrelid, scancontext,
- OUTER, outercontext,
- NIL);
+ context = deparse_context_for_plan(OUTER, outercontext,
+ 0, NULL,
+ es->rtable);
/* Deparse the expression */
exprstr = deparse_expression(node, context, (outercontext != NULL), false);
@@ -1106,19 +1098,17 @@ show_upper_qual(List *qual, const char *qlabel,
/* Generate deparse context */
if (outer_plan)
outercontext = deparse_context_for_subplan(outer_name,
- outer_plan->targetlist,
- es->rtable);
+ (Node *) outer_plan);
else
outercontext = NULL;
if (inner_plan)
innercontext = deparse_context_for_subplan(inner_name,
- inner_plan->targetlist,
- es->rtable);
+ (Node *) inner_plan);
else
innercontext = NULL;
context = deparse_context_for_plan(outer_varno, outercontext,
inner_varno, innercontext,
- NIL);
+ es->rtable);
/* Deparse the expression */
node = (Node *) make_ands_explicit(qual);
@@ -1134,7 +1124,7 @@ show_upper_qual(List *qual, const char *qlabel,
* Show the sort keys for a Sort node.
*/
static void
-show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
+show_sort_keys(Plan *sortplan, int nkeys, AttrNumber *keycols,
const char *qlabel,
StringInfo str, int indent, ExplainState *es)
{
@@ -1159,17 +1149,16 @@ show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
* looking at a dummy tlist generated by prepunion.c; if there are Vars
* with zero varno, use the tlist itself to determine their names.
*/
- varnos = pull_varnos((Node *) tlist);
+ varnos = pull_varnos((Node *) sortplan->targetlist);
if (bms_is_member(0, varnos))
{
Node *outercontext;
outercontext = deparse_context_for_subplan("sort",
- tlist,
- es->rtable);
+ (Node *) sortplan);
context = deparse_context_for_plan(0, outercontext,
0, NULL,
- NIL);
+ es->rtable);
useprefix = false;
}
else
@@ -1185,7 +1174,7 @@ show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
{
/* find key expression in tlist */
AttrNumber keyresno = keycols[keyno];
- TargetEntry *target = get_tle_by_resno(tlist, keyresno);
+ TargetEntry *target = get_tle_by_resno(sortplan->targetlist, keyresno);
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index f8daab6eeaf..902af40d9fc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.218 2006/04/04 19:35:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.219 2006/04/08 18:49:52 tgl Exp $
**********************************************************************/
#include "postgres.h"
@@ -89,7 +89,7 @@ typedef struct
* The rangetable is the list of actual RTEs from the query tree.
*
* For deparsing plan trees, we allow two special RTE entries that are not
- * part of the rtable list (mainly because they don't have consecutively
+ * part of the rtable list (partly because they don't have consecutively
* allocated varnos).
*/
typedef struct
@@ -1395,15 +1395,13 @@ deparse_context_for(const char *aliasname, Oid relid)
/*
* deparse_context_for_plan - Build deparse context for a plan node
*
- * We assume we are dealing with an upper-level plan node having either
- * one or two referenceable children (pass innercontext = NULL if only one).
- * The passed-in Nodes should be made using deparse_context_for_subplan
- * and/or deparse_context_for_relation. The resulting context will work
- * for deparsing quals, tlists, etc of the plan node.
+ * The plan node may contain references to one or two subplans or outer
+ * join plan nodes. For these, pass the varno used plus a context node
+ * made with deparse_context_for_subplan. (Pass 0/NULL for unused inputs.)
*
- * An rtable list can also be passed in case plain Vars might be seen.
- * This is not needed for true upper-level expressions, but is helpful for
- * Sort nodes and similar cases with slightly bogus targetlists.
+ * The plan's rangetable list must also be passed. We actually prefer to use
+ * the rangetable to resolve simple Vars, but the subplan inputs are needed
+ * for Vars that reference expressions computed in subplan target lists.
*/
List *
deparse_context_for_plan(int outer_varno, Node *outercontext,
@@ -1425,89 +1423,31 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
}
/*
- * deparse_context_for_rte - Build deparse context for 1 relation
- *
- * Helper routine to build one of the inputs for deparse_context_for_plan.
- *
- * The returned node is actually the given RangeTblEntry, but we declare it
- * as just Node to discourage callers from assuming anything.
- */
-Node *
-deparse_context_for_rte(RangeTblEntry *rte)
-{
- return (Node *) rte;
-}
-
-/*
* deparse_context_for_subplan - Build deparse context for a plan node
*
* Helper routine to build one of the inputs for deparse_context_for_plan.
- * Pass the tlist of the subplan node, plus the query rangetable.
+ * Pass the name to be used to reference the subplan, plus the Plan node.
+ * (subplan really ought to be declared as "Plan *", but we use "Node *"
+ * to avoid having to include plannodes.h in builtins.h.)
*
* The returned node is actually a RangeTblEntry, but we declare it as just
* Node to discourage callers from assuming anything.
*/
Node *
-deparse_context_for_subplan(const char *name, List *tlist,
- List *rtable)
+deparse_context_for_subplan(const char *name, Node *subplan)
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
- List *attrs = NIL;
- int nattrs = 0;
- int rtablelength = list_length(rtable);
- ListCell *tl;
- char buf[32];
-
- foreach(tl, tlist)
- {
- TargetEntry *tle = lfirst(tl);
-
- nattrs++;
- Assert(tle->resno == nattrs);
- if (tle->resname)
- {
- attrs = lappend(attrs, makeString(tle->resname));
- continue;
- }
- if (tle->expr && IsA(tle->expr, Var))
- {
- Var *var = (Var *) tle->expr;
-
- /* varno/varattno won't be any good, but varnoold might be */
- if (var->varnoold > 0 && var->varnoold <= rtablelength)
- {
- RangeTblEntry *varrte = rt_fetch(var->varnoold, rtable);
- AttrNumber varattnum = var->varoattno;
-
- /* need this test in case it's referencing a resjunk col */
- if (varattnum <= list_length(varrte->eref->colnames))
- {
- char *varname;
-
- varname = get_rte_attribute_name(varrte, varattnum);
- attrs = lappend(attrs, makeString(varname));
- continue;
- }
- }
- }
- /* Fallback if can't get name */
- snprintf(buf, sizeof(buf), "?column%d?", tle->resno);
- attrs = lappend(attrs, makeString(pstrdup(buf)));
- }
/*
- * We create an RTE_SPECIAL RangeTblEntry, and store the given tlist
- * in its funccoltypes field. This is a hack to make the tlist available
- * to get_name_for_var_field(). RTE_SPECIAL nodes shouldn't appear in
+ * We create an RTE_SPECIAL RangeTblEntry, and store the subplan in
+ * its funcexpr field. RTE_SPECIAL nodes shouldn't appear in
* deparse contexts otherwise.
- *
- * XXX this desperately needs redesign, as it fails to handle cases where
- * we need to drill down through multiple tlists.
*/
rte->rtekind = RTE_SPECIAL;
rte->relid = InvalidOid;
- rte->funccoltypes = tlist;
- rte->eref = makeAlias(name, attrs);
+ rte->funcexpr = subplan;
+ rte->alias = NULL;
+ rte->eref = makeAlias(name, NIL);
rte->inh = false;
rte->inFromCl = true;
@@ -2452,17 +2392,24 @@ get_utility_query_def(Query *query, deparse_context *context)
/*
* Get the RTE referenced by a (possibly nonlocal) Var.
*
+ * The appropriate attribute number is stored into *attno
+ * (do not assume that var->varattno is what to use).
+ *
* In some cases (currently only when recursing into an unnamed join)
* the Var's varlevelsup has to be interpreted with respect to a context
* above the current one; levelsup indicates the offset.
*/
static RangeTblEntry *
-get_rte_for_var(Var *var, int levelsup, deparse_context *context)
+get_rte_for_var(Var *var, int levelsup, deparse_context *context,
+ AttrNumber *attno)
{
RangeTblEntry *rte;
int netlevelsup;
deparse_namespace *dpns;
+ /* default assumption */
+ *attno = var->varattno;
+
/* Find appropriate nesting depth */
netlevelsup = var->varlevelsup + levelsup;
if (netlevelsup >= list_length(context->namespaces))
@@ -2471,9 +2418,20 @@ get_rte_for_var(Var *var, int levelsup, deparse_context *context)
dpns = (deparse_namespace *) list_nth(context->namespaces,
netlevelsup);
- /* Find the relevant RTE */
+ /*
+ * Try to find the relevant RTE in this rtable. In a plan tree, it's
+ * likely that varno is OUTER, INNER, or 0, in which case we try to
+ * use varnoold instead. If the Var references an expression computed
+ * by a subplan, varnoold will be 0, and we fall back to looking at the
+ * special subplan RTEs.
+ */
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
rte = rt_fetch(var->varno, dpns->rtable);
+ else if (var->varnoold >= 1 && var->varnoold <= list_length(dpns->rtable))
+ {
+ rte = rt_fetch(var->varnoold, dpns->rtable);
+ *attno = var->varoattno;
+ }
else if (var->varno == dpns->outer_varno)
rte = dpns->outer_rte;
else if (var->varno == dpns->inner_varno)
@@ -2509,9 +2467,10 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
char **schemaname, char **refname, char **attname)
{
RangeTblEntry *rte;
+ AttrNumber attnum;
/* Find appropriate RTE */
- rte = get_rte_for_var(var, levelsup, context);
+ rte = get_rte_for_var(var, levelsup, context, &attnum);
/* Emit results */
*schemaname = NULL; /* default assumptions */
@@ -2543,12 +2502,11 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
* variable name (this can only happen with columns that were
* merged by USING or NATURAL clauses).
*/
- if (var->varattno > 0)
+ if (attnum > 0)
{
Var *aliasvar;
- aliasvar = (Var *) list_nth(rte->joinaliasvars,
- var->varattno - 1);
+ aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
if (IsA(aliasvar, Var))
{
get_names_for_var(aliasvar,
@@ -2560,12 +2518,36 @@ get_names_for_var(Var *var, int levelsup, deparse_context *context,
/* Unnamed join has neither schemaname nor refname */
*refname = NULL;
}
+ else if (rte->rtekind == RTE_SPECIAL)
+ {
+ /*
+ * This case occurs during EXPLAIN when we are looking at a
+ * deparse context node set up by deparse_context_for_subplan().
+ * If the subplan tlist provides a name, use it, but usually
+ * we'll end up with "?columnN?".
+ */
+ List *tlist = ((Plan *) rte->funcexpr)->targetlist;
+ TargetEntry *tle = get_tle_by_resno(tlist, attnum);
+
+ if (tle && tle->resname)
+ {
+ *attname = tle->resname;
+ }
+ else
+ {
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "?column%d?", attnum);
+ *attname = pstrdup(buf);
+ }
+ return;
+ }
}
- if (var->varattno == InvalidAttrNumber)
+ if (attnum == InvalidAttrNumber)
*attname = NULL;
else
- *attname = get_rte_attribute_name(rte, var->varattno);
+ *attname = get_rte_attribute_name(rte, attnum);
}
@@ -2599,9 +2581,7 @@ get_name_for_var_field(Var *var, int fieldno,
Assert(var->vartype == RECORDOID);
/* Find appropriate RTE */
- rte = get_rte_for_var(var, levelsup, context);
-
- attnum = var->varattno;
+ rte = get_rte_for_var(var, levelsup, context, &attnum);
if (attnum == InvalidAttrNumber)
{
@@ -2675,18 +2655,37 @@ get_name_for_var_field(Var *var, int fieldno,
*/
break;
case RTE_SPECIAL:
- /*
- * This case occurs during EXPLAIN when we are looking at a
- * deparse context node set up by deparse_context_for_subplan().
- * Look into the subplan's target list to get the referenced
- * expression, and then pass it to get_expr_result_type().
- */
- if (rte->funccoltypes)
{
- TargetEntry *ste = get_tle_by_resno(rte->funccoltypes, attnum);
+ /*
+ * We are looking at a deparse context node set up by
+ * deparse_context_for_subplan(). The Var must refer to an
+ * expression computed by this subplan (or possibly one of its
+ * inputs), rather than any simple attribute of an RTE entry.
+ * Look into the subplan's target list to get the referenced
+ * expression, digging down as far as needed to find something
+ * that's not a Var, and then pass it to
+ * get_expr_result_type().
+ */
+ Plan *subplan = (Plan *) rte->funcexpr;
- if (ste != NULL)
+ for (;;)
+ {
+ TargetEntry *ste;
+
+ ste = get_tle_by_resno(subplan->targetlist,
+ ((Var *) expr)->varattno);
+ if (!ste || !ste->expr)
+ break;
expr = (Node *) ste->expr;
+ if (!IsA(expr, Var))
+ break;
+ if (((Var *) expr)->varno == INNER)
+ subplan = innerPlan(subplan);
+ else
+ subplan = outerPlan(subplan);
+ if (!subplan)
+ break;
+ }
}
break;
}
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 47553c5cf29..a46f187bcbf 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.278 2006/04/05 22:11:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.279 2006/04/08 18:49:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -526,9 +526,7 @@ extern List *deparse_context_for(const char *aliasname, Oid relid);
extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
int inner_varno, Node *innercontext,
List *rtable);
-extern Node *deparse_context_for_rte(RangeTblEntry *rte);
-extern Node *deparse_context_for_subplan(const char *name, List *tlist,
- List *rtable);
+extern Node *deparse_context_for_subplan(const char *name, Node *subplan);
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *namespace,
const char *ident);