From c6e81aeef39fd546976c5d90e484db0ddb2536e7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 8 Apr 2006 18:49:52 +0000 Subject: [PATCH] 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. --- src/backend/commands/explain.c | 41 +++----- src/backend/utils/adt/ruleutils.c | 193 +++++++++++++++++++------------------- src/include/utils/builtins.h | 6 +- 3 files changed, 113 insertions(+), 127 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e6492720b6..9c71cfeea8 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 f8daab6eea..902af40d9f 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 47553c5cf2..a46f187bcb 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); -- 2.11.0