1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

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.
This commit is contained in:
Tom Lane
2006-04-08 18:49:52 +00:00
parent 74bdf965a6
commit c6e81aeef3
3 changed files with 113 additions and 127 deletions

View File

@ -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,
@ -1424,90 +1422,32 @@ deparse_context_for_plan(int outer_varno, Node *outercontext,
return list_make1(dpns);
}
/*
* 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;
}