]> granicus.if.org Git - postgresql/commitdiff
Fix EXPLAIN so that it can drill down through multiple levels of subplan
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 8 Apr 2006 18:49:52 +0000 (18:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 8 Apr 2006 18:49:52 +0000 (18:49 +0000)
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
src/backend/utils/adt/ruleutils.c
src/include/utils/builtins.h

index e6492720b6a6acf04121705fb0ab4a232d3a9a3b..9c71cfeea89b83625aa2f3a6fea355660e84efb8 100644 (file)
@@ -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);
index f8daab6eeaf234eec3448bd1657a70d15bda14b5..902af40d9fcf199883bec772c8eff589cb60bbad 100644 (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;
        }
index 47553c5cf291dcc4c169b48c4d37982267dd0a0b..a46f187bcbfe08f9311a46ec34a6a12155884cce 100644 (file)
@@ -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);