diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 1 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 60 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 32 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 65 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 8 |
5 files changed, 142 insertions, 24 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 7149724953d..5de1307570e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1744,6 +1744,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) NIL, NIL, NULL, + NULL, false); sv_namespace = pstate->p_namespace; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 7ff36bc8422..8b1bad0d794 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -509,7 +509,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <ival> sub_type opt_materialized %type <value> NumericOnly %type <list> NumericOnly_list -%type <alias> alias_clause opt_alias_clause +%type <alias> alias_clause opt_alias_clause opt_alias_clause_for_join_using %type <list> func_alias_clause %type <sortby> sortby %type <ielem> index_elem index_elem_options @@ -12144,6 +12144,7 @@ joined_table: n->larg = $1; n->rarg = $4; n->usingClause = NIL; + n->join_using_alias = NULL; n->quals = NULL; $$ = n; } @@ -12155,9 +12156,16 @@ joined_table: n->larg = $1; n->rarg = $4; if ($5 != NULL && IsA($5, List)) - n->usingClause = (List *) $5; /* USING clause */ + { + /* USING clause */ + n->usingClause = linitial_node(List, castNode(List, $5)); + n->join_using_alias = lsecond_node(Alias, castNode(List, $5)); + } else - n->quals = $5; /* ON clause */ + { + /* ON clause */ + n->quals = $5; + } $$ = n; } | table_ref JOIN table_ref join_qual @@ -12169,9 +12177,16 @@ joined_table: n->larg = $1; n->rarg = $3; if ($4 != NULL && IsA($4, List)) - n->usingClause = (List *) $4; /* USING clause */ + { + /* USING clause */ + n->usingClause = linitial_node(List, castNode(List, $4)); + n->join_using_alias = lsecond_node(Alias, castNode(List, $4)); + } else - n->quals = $4; /* ON clause */ + { + /* ON clause */ + n->quals = $4; + } $$ = n; } | table_ref NATURAL join_type JOIN table_ref @@ -12182,6 +12197,7 @@ joined_table: n->larg = $1; n->rarg = $5; n->usingClause = NIL; /* figure out which columns later... */ + n->join_using_alias = NULL; n->quals = NULL; /* fill later */ $$ = n; } @@ -12194,6 +12210,7 @@ joined_table: n->larg = $1; n->rarg = $4; n->usingClause = NIL; /* figure out which columns later... */ + n->join_using_alias = NULL; n->quals = NULL; /* fill later */ $$ = n; } @@ -12229,6 +12246,22 @@ opt_alias_clause: alias_clause { $$ = $1; } ; /* + * The alias clause after JOIN ... USING only accepts the AS ColId spelling, + * per SQL standard. (The grammar could parse the other variants, but they + * don't seem to be useful, and it might lead to parser problems in the + * future.) + */ +opt_alias_clause_for_join_using: + AS ColId + { + $$ = makeNode(Alias); + $$->aliasname = $2; + /* the column name list will be inserted later */ + } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* * func_alias_clause can include both an Alias and a coldeflist, so we make it * return a 2-element list that gets disassembled by calling production. */ @@ -12272,15 +12305,24 @@ opt_outer: OUTER_P /* JOIN qualification clauses * Possibilities are: - * USING ( column list ) allows only unqualified column names, + * USING ( column list ) [ AS alias ] + * allows only unqualified column names, * which must match between tables. * ON expr allows more general qualifications. * - * We return USING as a List node, while an ON-expr will not be a List. + * We return USING as a two-element List (the first item being a sub-List + * of the common column names, and the second either an Alias item or NULL). + * An ON-expr will not be a List, so it can be told apart that way. */ -join_qual: USING '(' name_list ')' { $$ = (Node *) $3; } - | ON a_expr { $$ = $2; } +join_qual: USING '(' name_list ')' opt_alias_clause_for_join_using + { + $$ = (Node *) list_make2($3, $5); + } + | ON a_expr + { + $$ = $2; + } ; diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 5dfea460216..af80aa45936 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -1266,6 +1266,13 @@ transformFromClauseItem(ParseState *pstate, Node *n, } /* + * If a USING clause alias was specified, save the USING columns as + * its column list. + */ + if (j->join_using_alias) + j->join_using_alias->colnames = j->usingClause; + + /* * Now transform the join qualifications, if any. */ l_colnos = NIL; @@ -1460,6 +1467,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, res_colvars, l_colnos, r_colnos, + j->join_using_alias, j->alias, true); @@ -1494,6 +1502,30 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(list_length(pstate->p_joinexprs) == j->rtindex); /* + * If the join has a USING alias, build a ParseNamespaceItem for that + * and add it to the list of nsitems in the join's input. + */ + if (j->join_using_alias) + { + ParseNamespaceItem *jnsitem; + + jnsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); + jnsitem->p_names = j->join_using_alias; + jnsitem->p_rte = nsitem->p_rte; + jnsitem->p_rtindex = nsitem->p_rtindex; + /* no need to copy the first N columns, just use res_nscolumns */ + jnsitem->p_nscolumns = res_nscolumns; + /* set default visibility flags; might get changed later */ + jnsitem->p_rel_visible = true; + jnsitem->p_cols_visible = true; + jnsitem->p_lateral_only = false; + jnsitem->p_lateral_ok = true; + /* Per SQL, we must check for alias conflicts */ + checkNameSpaceConflicts(pstate, list_make1(jnsitem), my_namespace); + my_namespace = lappend(my_namespace, jnsitem); + } + + /* * Prepare returned namespace list. If the JOIN has an alias then it * hides the contained RTEs completely; otherwise, the contained RTEs * are still visible as table names, but are not visible for diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 03373d551fc..f928c323113 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2512,26 +2512,61 @@ static Node * transformWholeRowRef(ParseState *pstate, ParseNamespaceItem *nsitem, int sublevels_up, int location) { - Var *result; - /* - * Build the appropriate referencing node. Note that if the RTE is a - * function returning scalar, we create just a plain reference to the - * function value, not a composite containing a single column. This is - * pretty inconsistent at first sight, but it's what we've done - * historically. One argument for it is that "rel" and "rel.*" mean the - * same thing for composite relations, so why not for scalar functions... + * Build the appropriate referencing node. Normally this can be a + * whole-row Var, but if the nsitem is a JOIN USING alias then it contains + * only a subset of the columns of the underlying join RTE, so that will + * not work. Instead we immediately expand the reference into a RowExpr. + * Since the JOIN USING's common columns are fully determined at this + * point, there seems no harm in expanding it now rather than during + * planning. + * + * Note that if the RTE is a function returning scalar, we create just a + * plain reference to the function value, not a composite containing a + * single column. This is pretty inconsistent at first sight, but it's + * what we've done historically. One argument for it is that "rel" and + * "rel.*" mean the same thing for composite relations, so why not for + * scalar functions... */ - result = makeWholeRowVar(nsitem->p_rte, nsitem->p_rtindex, - sublevels_up, true); + if (nsitem->p_names == nsitem->p_rte->eref) + { + Var *result; - /* location is not filled in by makeWholeRowVar */ - result->location = location; + result = makeWholeRowVar(nsitem->p_rte, nsitem->p_rtindex, + sublevels_up, true); - /* mark relation as requiring whole-row SELECT access */ - markVarForSelectPriv(pstate, result); + /* location is not filled in by makeWholeRowVar */ + result->location = location; - return (Node *) result; + /* mark relation as requiring whole-row SELECT access */ + markVarForSelectPriv(pstate, result); + + return (Node *) result; + } + else + { + RowExpr *rowexpr; + List *fields; + + /* + * We want only as many columns as are listed in p_names->colnames, + * and we should use those names not whatever possibly-aliased names + * are in the RTE. We needn't worry about marking the RTE for SELECT + * access, as the common columns are surely so marked already. + */ + expandRTE(nsitem->p_rte, nsitem->p_rtindex, + sublevels_up, location, false, + NULL, &fields); + rowexpr = makeNode(RowExpr); + rowexpr->args = list_truncate(fields, + list_length(nsitem->p_names->colnames)); + rowexpr->row_typeid = RECORDOID; + rowexpr->row_format = COERCE_IMPLICIT_CAST; + rowexpr->colnames = copyObject(nsitem->p_names->colnames); + rowexpr->location = location; + + return (Node *) rowexpr; + } } /* diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index b12416535ed..d451f055f72 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -753,6 +753,12 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem *nsitem, * else return InvalidAttrNumber. * If the name proves ambiguous within this RTE, raise error. * + * Actually, we only search the names listed in "eref". This can be either + * rte->eref, in which case we are indeed searching all the column names, + * or for a join it can be rte->join_using_alias, in which case we are only + * considering the common column names (which are the first N columns of the + * join, so everything works). + * * pstate and location are passed only for error-reporting purposes. * * Side effect: if fuzzystate is non-NULL, check non-system columns @@ -2134,6 +2140,7 @@ addRangeTableEntryForJoin(ParseState *pstate, List *aliasvars, List *leftcols, List *rightcols, + Alias *join_using_alias, Alias *alias, bool inFromCl) { @@ -2162,6 +2169,7 @@ addRangeTableEntryForJoin(ParseState *pstate, rte->joinaliasvars = aliasvars; rte->joinleftcols = leftcols; rte->joinrightcols = rightcols; + rte->join_using_alias = join_using_alias; rte->alias = alias; eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL); |