summaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c1
-rw-r--r--src/backend/parser/gram.y60
-rw-r--r--src/backend/parser/parse_clause.c32
-rw-r--r--src/backend/parser/parse_expr.c65
-rw-r--r--src/backend/parser/parse_relation.c8
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);