diff options
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 193 |
1 files changed, 96 insertions, 97 deletions
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; } |