diff options
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 92 |
1 files changed, 53 insertions, 39 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index cecb5427271..762be4e846b 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -164,10 +164,10 @@ typedef struct * since they just inherit column names from their input RTEs, and we can't * rename the columns at the join level. Most of the time this isn't an issue * because we don't need to reference the join's output columns as such; we - * can reference the input columns instead. That approach fails for merged - * FULL JOIN USING columns, however, so when we have one of those in an - * unnamed join, we have to make that column's alias globally unique across - * the whole query to ensure it can be referenced unambiguously. + * can reference the input columns instead. That approach can fail for merged + * JOIN USING columns, however, so when we have one of those in an unnamed + * join, we have to make that column's alias globally unique across the whole + * query to ensure it can be referenced unambiguously. * * Another problem is that a JOIN USING clause requires the columns to be * merged to have the same aliases in both input RTEs. To handle that, we do @@ -301,7 +301,7 @@ static bool refname_is_unique(char *refname, deparse_namespace *dpns, static void set_deparse_for_query(deparse_namespace *dpns, Query *query, List *parent_namespaces); static void set_simple_column_names(deparse_namespace *dpns); -static bool has_unnamed_full_join_using(Node *jtnode); +static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode); static void set_using_names(deparse_namespace *dpns, Node *jtnode); static void set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte, @@ -2570,7 +2570,7 @@ set_deparse_for_query(deparse_namespace *dpns, Query *query, { /* Detect whether global uniqueness of USING names is needed */ dpns->unique_using = - has_unnamed_full_join_using((Node *) query->jointree); + has_dangerous_join_using(dpns, (Node *) query->jointree); /* * Select names for columns merged by USING, via a recursive pass over @@ -2630,25 +2630,26 @@ set_simple_column_names(deparse_namespace *dpns) } /* - * has_unnamed_full_join_using: search jointree for unnamed FULL JOIN USING + * has_dangerous_join_using: search jointree for unnamed JOIN USING * - * Merged columns of a FULL JOIN USING act differently from either of the - * input columns, so they have to be referenced as columns of the JOIN not - * as columns of either input. And this is problematic if the join is - * unnamed (alias-less): we cannot qualify the column's name with an RTE - * name, since there is none. (Forcibly assigning an alias to the join is - * not a solution, since that will prevent legal references to tables below - * the join.) To ensure that every column in the query is unambiguously - * referenceable, we must assign such merged columns names that are globally - * unique across the whole query, aliasing other columns out of the way as - * necessary. + * Merged columns of a JOIN USING may act differently from either of the input + * columns, either because they are merged with COALESCE (in a FULL JOIN) or + * because an implicit coercion of the underlying input column is required. + * In such a case the column must be referenced as a column of the JOIN not as + * a column of either input. And this is problematic if the join is unnamed + * (alias-less): we cannot qualify the column's name with an RTE name, since + * there is none. (Forcibly assigning an alias to the join is not a solution, + * since that will prevent legal references to tables below the join.) + * To ensure that every column in the query is unambiguously referenceable, + * we must assign such merged columns names that are globally unique across + * the whole query, aliasing other columns out of the way as necessary. * * Because the ensuing re-aliasing is fairly damaging to the readability of * the query, we don't do this unless we have to. So, we must pre-scan * the join tree to see if we have to, before starting set_using_names(). */ static bool -has_unnamed_full_join_using(Node *jtnode) +has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode) { if (IsA(jtnode, RangeTblRef)) { @@ -2661,7 +2662,7 @@ has_unnamed_full_join_using(Node *jtnode) foreach(lc, f->fromlist) { - if (has_unnamed_full_join_using((Node *) lfirst(lc))) + if (has_dangerous_join_using(dpns, (Node *) lfirst(lc))) return true; } } @@ -2669,16 +2670,30 @@ has_unnamed_full_join_using(Node *jtnode) { JoinExpr *j = (JoinExpr *) jtnode; - /* Is it an unnamed FULL JOIN with USING? */ - if (j->alias == NULL && - j->jointype == JOIN_FULL && - j->usingClause) - return true; + /* Is it an unnamed JOIN with USING? */ + if (j->alias == NULL && j->usingClause) + { + /* + * Yes, so check each join alias var to see if any of them are not + * simple references to underlying columns. If so, we have a + * dangerous situation and must pick unique aliases. + */ + RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable); + ListCell *lc; + + foreach(lc, jrte->joinaliasvars) + { + Var *aliasvar = (Var *) lfirst(lc); + + if (aliasvar != NULL && !IsA(aliasvar, Var)) + return true; + } + } /* Nope, but inspect children */ - if (has_unnamed_full_join_using(j->larg)) + if (has_dangerous_join_using(dpns, j->larg)) return true; - if (has_unnamed_full_join_using(j->rarg)) + if (has_dangerous_join_using(dpns, j->rarg)) return true; } else @@ -2768,16 +2783,16 @@ set_using_names(deparse_namespace *dpns, Node *jtnode) * * If dpns->unique_using is TRUE, we force all USING names to be * unique across the whole query level. In principle we'd only need - * the names of USING columns in unnamed full joins to be globally - * unique, but to safely assign all USING names in a single pass, we - * have to enforce the same uniqueness rule for all of them. However, - * if a USING column's name has been pushed down from the parent, we - * should use it as-is rather than making a uniqueness adjustment. - * This is necessary when we're at an unnamed join, and it creates no - * risk of ambiguity. Also, if there's a user-written output alias - * for a merged column, we prefer to use that rather than the input - * name; this simplifies the logic and seems likely to lead to less - * aliasing overall. + * the names of dangerous USING columns to be globally unique, but to + * safely assign all USING names in a single pass, we have to enforce + * the same uniqueness rule for all of them. However, if a USING + * column's name has been pushed down from the parent, we should use + * it as-is rather than making a uniqueness adjustment. This is + * necessary when we're at an unnamed join, and it creates no risk of + * ambiguity. Also, if there's a user-written output alias for a + * merged column, we prefer to use that rather than the input name; + * this simplifies the logic and seems likely to lead to less aliasing + * overall. * * If dpns->unique_using is FALSE, we only need USING names to be * unique within their own join RTE. We still need to honor @@ -5344,9 +5359,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) * If it's a simple reference to one of the input vars, then recursively * print the name of that var instead. When it's not a simple reference, * we have to just print the unqualified join column name. (This can only - * happen with columns that were merged by USING or NATURAL clauses in a - * FULL JOIN; we took pains previously to make the unqualified column name - * unique in such cases.) + * happen with "dangerous" merged columns in a JOIN USING; we took pains + * previously to make the unqualified column name unique in such cases.) * * This wouldn't work in decompiling plan trees, because we don't store * joinaliasvars lists after planning; but a plan tree should never |