mirror of
https://github.com/postgres/postgres.git
synced 2025-04-27 22:56:53 +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:
parent
74bdf965a6
commit
c6e81aeef3
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user