On 2020-11-10 16:15, Georgios Kokolatos wrote:
I noticed that this patch fails on the cfbot.
For this, I changed the status to: 'Waiting on Author'.
Cheers,
//Georgios
The new status of this patch is: Waiting on Author
Here is a rebased and lightly retouched patch.
--
Peter Eisentraut
2ndQuadrant, an EDB company
https://www.2ndquadrant.com/
From 5b736c0033d4e41a4ca186101f892fbd00ff3528 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Sat, 14 Nov 2020 09:25:33 +0100
Subject: [PATCH v4] Allow an alias to be attached to a JOIN ... USING
This allows something like
SELECT ... FROM t1 JOIN t2 USING (a, b, c) AS x
where x has the columns a, b, c and unlike a regular alias it does not
hide the range variables of the tables being joined t1 and t2.
Per SQL:2016 feature F404 "Range variable for common column names".
Discussion:
https://www.postgresql.org/message-id/flat/454638cf-d563-ab76-a585-256442806...@2ndquadrant.com
---
doc/src/sgml/ref/select.sgml | 11 ++++-
src/backend/catalog/sql_features.txt | 2 +-
src/backend/nodes/copyfuncs.c | 2 +
src/backend/nodes/equalfuncs.c | 2 +
src/backend/nodes/outfuncs.c | 2 +
src/backend/nodes/readfuncs.c | 2 +
src/backend/parser/analyze.c | 1 +
src/backend/parser/gram.y | 48 +++++++++++++++++----
src/backend/parser/parse_clause.c | 21 ++++++++-
src/backend/parser/parse_relation.c | 45 +++++++++++++++++++-
src/backend/utils/adt/ruleutils.c | 4 ++
src/include/nodes/parsenodes.h | 7 +++
src/include/nodes/primnodes.h | 1 +
src/include/parser/parse_node.h | 1 +
src/include/parser/parse_relation.h | 1 +
src/test/regress/expected/create_view.out | 52 ++++++++++++++++++++++-
src/test/regress/expected/join.out | 31 ++++++++++++++
src/test/regress/sql/create_view.sql | 11 +++++
src/test/regress/sql/join.sql | 8 ++++
19 files changed, 239 insertions(+), 13 deletions(-)
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 472b7cae81..c6e449d71e 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -59,7 +59,7 @@
[ LATERAL ] <replaceable class="parameter">function_name</replaceable> ( [
<replaceable class="parameter">argument</replaceable> [, ...] ] ) AS (
<replaceable class="parameter">column_definition</replaceable> [, ...] )
[ LATERAL ] ROWS FROM( <replaceable
class="parameter">function_name</replaceable> ( [ <replaceable
class="parameter">argument</replaceable> [, ...] ] ) [ AS ( <replaceable
class="parameter">column_definition</replaceable> [, ...] ) ] [, ...] )
[ WITH ORDINALITY ] [ [ AS ] <replaceable
class="parameter">alias</replaceable> [ ( <replaceable
class="parameter">column_alias</replaceable> [, ...] ) ] ]
- <replaceable class="parameter">from_item</replaceable> [ NATURAL ]
<replaceable class="parameter">join_type</replaceable> <replaceable
class="parameter">from_item</replaceable> [ ON <replaceable
class="parameter">join_condition</replaceable> | USING ( <replaceable
class="parameter">join_column</replaceable> [, ...] ) ]
+ <replaceable class="parameter">from_item</replaceable> [ NATURAL ]
<replaceable class="parameter">join_type</replaceable> <replaceable
class="parameter">from_item</replaceable> [ ON <replaceable
class="parameter">join_condition</replaceable> | USING ( <replaceable
class="parameter">join_column</replaceable> [, ...] ) [ AS <replaceable
class="parameter">join_using_alias</replaceable> ] ]
<phrase>and <replaceable class="parameter">grouping_element</replaceable> can
be one of:</phrase>
@@ -633,6 +633,15 @@ <title><literal>FROM</literal> Clause</title>
equivalent columns will be included in the join output, not
both.
</para>
+
+ <para>
+ If a <replaceable class="parameter">join_using_alias</replaceable> is
+ specified, it gives a correlation name to the join columns. Only the
+ join columns in the <literal>USING</literal> clause are addressable by
+ this name. Unlike an <replaceable
+ class="parameter">alias</replaceable>, this does not hide the names of
+ the joined tables from the rest of the query.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/catalog/sql_features.txt
b/src/backend/catalog/sql_features.txt
index b6e58e8493..69c24e7369 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -264,7 +264,7 @@ F401 Extended joined table 02 FULL OUTER JOIN
YES
F401 Extended joined table 04 CROSS JOIN YES
F402 Named column joins for LOBs, arrays, and multisets
YES
F403 Partitioned joined tables NO
-F404 Range variable for common column names NO
+F404 Range variable for common column names YES
F411 Time zone specification YES differences regarding
literal interpretation
F421 National character YES
F431 Read-only scrollable cursors YES
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 3031c52991..3582f48084 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2206,6 +2206,7 @@ _copyJoinExpr(const JoinExpr *from)
COPY_NODE_FIELD(rarg);
COPY_NODE_FIELD(usingClause);
COPY_NODE_FIELD(quals);
+ COPY_NODE_FIELD(join_using_alias);
COPY_NODE_FIELD(alias);
COPY_SCALAR_FIELD(rtindex);
@@ -2415,6 +2416,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
COPY_NODE_FIELD(joinaliasvars);
COPY_NODE_FIELD(joinleftcols);
COPY_NODE_FIELD(joinrightcols);
+ COPY_NODE_FIELD(join_using_alias);
COPY_NODE_FIELD(functions);
COPY_SCALAR_FIELD(funcordinality);
COPY_NODE_FIELD(tablefunc);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 9aa853748d..5cd5ec3c64 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -788,6 +788,7 @@ _equalJoinExpr(const JoinExpr *a, const JoinExpr *b)
COMPARE_NODE_FIELD(rarg);
COMPARE_NODE_FIELD(usingClause);
COMPARE_NODE_FIELD(quals);
+ COMPARE_NODE_FIELD(join_using_alias);
COMPARE_NODE_FIELD(alias);
COMPARE_SCALAR_FIELD(rtindex);
@@ -2672,6 +2673,7 @@ _equalRangeTblEntry(const RangeTblEntry *a, const
RangeTblEntry *b)
COMPARE_NODE_FIELD(joinaliasvars);
COMPARE_NODE_FIELD(joinleftcols);
COMPARE_NODE_FIELD(joinrightcols);
+ COMPARE_NODE_FIELD(join_using_alias);
COMPARE_NODE_FIELD(functions);
COMPARE_SCALAR_FIELD(funcordinality);
COMPARE_NODE_FIELD(tablefunc);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 4504b1503b..a9a1f85267 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1679,6 +1679,7 @@ _outJoinExpr(StringInfo str, const JoinExpr *node)
WRITE_NODE_FIELD(rarg);
WRITE_NODE_FIELD(usingClause);
WRITE_NODE_FIELD(quals);
+ WRITE_NODE_FIELD(join_using_alias);
WRITE_NODE_FIELD(alias);
WRITE_INT_FIELD(rtindex);
}
@@ -3106,6 +3107,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry
*node)
WRITE_NODE_FIELD(joinaliasvars);
WRITE_NODE_FIELD(joinleftcols);
WRITE_NODE_FIELD(joinrightcols);
+ WRITE_NODE_FIELD(join_using_alias);
break;
case RTE_FUNCTION:
WRITE_NODE_FIELD(functions);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ab7b535caa..79561c4e21 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1303,6 +1303,7 @@ _readJoinExpr(void)
READ_NODE_FIELD(rarg);
READ_NODE_FIELD(usingClause);
READ_NODE_FIELD(quals);
+ READ_NODE_FIELD(join_using_alias);
READ_NODE_FIELD(alias);
READ_INT_FIELD(rtindex);
@@ -1405,6 +1406,7 @@ _readRangeTblEntry(void)
READ_NODE_FIELD(joinaliasvars);
READ_NODE_FIELD(joinleftcols);
READ_NODE_FIELD(joinrightcols);
+ READ_NODE_FIELD(join_using_alias);
break;
case RTE_FUNCTION:
READ_NODE_FIELD(functions);
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 084e00f73d..711a1c8dba 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1733,6 +1733,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 051f1f1d49..0f947cdf87 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -498,7 +498,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
@@ -11982,9 +11982,14 @@ 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
@@ -11996,9 +12001,14 @@ 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
@@ -12055,6 +12065,21 @@ opt_alias_clause: alias_clause
{ $$ = $1; }
| /*EMPTY*/
{ $$ = NULL; }
;
+/*
+ * 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;
+ }
+ | /*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.
@@ -12099,15 +12124,22 @@ 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.
*/
-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 ea4a1f5aeb..cc87f93894 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -1267,6 +1267,13 @@ transformFromClauseItem(ParseState *pstate, Node *n,
j->usingClause = rlist;
}
+ /*
+ * 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.
*/
@@ -1464,6 +1471,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
res_colvars,
l_colnos,
r_colnos,
+
j->join_using_alias,
j->alias,
true);
@@ -1517,10 +1525,21 @@ transformFromClauseItem(ParseState *pstate, Node *n,
* The join RTE itself is always made visible for unqualified
column
* names. It's visible as a relation name only if it has an
alias.
*/
- nsitem->p_rel_visible = (j->alias != NULL);
+ nsitem->p_rel_visible = (j->alias != NULL ||
j->join_using_alias != NULL);
nsitem->p_cols_visible = true;
nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true;
+ nsitem->p_join_using_alias = (j->join_using_alias != NULL);
+
+ /*
+ * Check the JOIN/USING alias for namespace conflicts against
the
+ * subtrees (per SQL standard).
+ */
+ if (j->join_using_alias)
+ {
+ checkNameSpaceConflicts(pstate, list_make1(nsitem),
l_namespace);
+ checkNameSpaceConflicts(pstate, list_make1(nsitem),
r_namespace);
+ }
*top_nsitem = nsitem;
*namespace = lappend(my_namespace, nsitem);
diff --git a/src/backend/parser/parse_relation.c
b/src/backend/parser/parse_relation.c
index a56bd86181..614cef5bed 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -671,6 +671,37 @@ scanNSItemForColumn(ParseState *pstate, ParseNamespaceItem
*nsitem,
int attnum;
Var *var;
+ /*
+ * If this is a JOIN/USING alias, then check that the column is part of
+ * the USING column list. If so, let scanRTEForColumn() below do the
main
+ * work.
+ */
+ if (nsitem->p_join_using_alias)
+ {
+ ListCell *c;
+ bool found = false;
+
+ foreach(c, rte->join_using_alias->colnames)
+ {
+ const char *attcolname = strVal(lfirst(c));
+
+ if (strcmp(attcolname, colname) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ ereport(ERROR,
+
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("column reference \"%s\" is
invalid",
+ colname),
+ errdetail("The range variable \"%s\"
only contains columns in the USING clause.",
+
rte->join_using_alias->aliasname),
+ parser_errposition(pstate, location)));
+ }
+
/*
* Scan the RTE's column names (or aliases) for a match. Complain if
* multiple matches.
@@ -1265,6 +1296,7 @@ buildNSItemFromTupleDesc(RangeTblEntry *rte, Index
rtindex, TupleDesc tupdesc)
nsitem->p_cols_visible = true;
nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true;
+ nsitem->p_join_using_alias = false;
return nsitem;
}
@@ -1326,6 +1358,7 @@ buildNSItemFromLists(RangeTblEntry *rte, Index rtindex,
nsitem->p_cols_visible = true;
nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true;
+ nsitem->p_join_using_alias = false;
return nsitem;
}
@@ -2135,6 +2168,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
List *aliasvars,
List *leftcols,
List *rightcols,
+ Alias *join_using_alias,
Alias *alias,
bool inFromCl)
{
@@ -2163,9 +2197,16 @@ 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);
+ if (alias)
+ eref = copyObject(alias);
+ else if (join_using_alias)
+ eref = copyObject(join_using_alias);
+ else
+ eref = makeAlias("unnamed_join", NIL);
+
numaliases = list_length(eref->colnames);
/* fill in any unspecified alias columns */
@@ -2211,6 +2252,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
nsitem->p_cols_visible = true;
nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true;
+ nsitem->p_join_using_alias = false;
return nsitem;
}
@@ -2511,6 +2553,7 @@ addNSItemToQuery(ParseState *pstate, ParseNamespaceItem
*nsitem,
nsitem->p_cols_visible = addToVarNameSpace;
nsitem->p_lateral_only = false;
nsitem->p_lateral_ok = true;
+ nsitem->p_join_using_alias = false;
pstate->p_namespace = lappend(pstate->p_namespace, nsitem);
}
}
diff --git a/src/backend/utils/adt/ruleutils.c
b/src/backend/utils/adt/ruleutils.c
index c2c6df2a4f..db5e9d294c 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10582,6 +10582,10 @@ get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context)
appendStringInfoString(buf,
quote_identifier(colname));
}
appendStringInfoChar(buf, ')');
+
+ if (j->join_using_alias)
+ appendStringInfo(buf, " AS %s",
+
quote_identifier(j->join_using_alias->aliasname));
}
else if (j->quals)
{
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7ef9b0eac0..52494d37f9 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1058,6 +1058,13 @@ typedef struct RangeTblEntry
List *joinleftcols; /* left-side input column numbers */
List *joinrightcols; /* right-side input column numbers */
+ /*
+ * join_using_alias is an alias clause attached directly to JOIN/USING.
+ * It is different from the alias field (below) in that it does not hide
+ * the range variables of the tables being joined.
+ */
+ Alias *join_using_alias;
+
/*
* Fields valid for a function RTE (else NIL/zero):
*
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 5b190bb99b..a87f239f3d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1498,6 +1498,7 @@ typedef struct JoinExpr
Node *rarg; /* right subtree */
List *usingClause; /* USING clause, if any (list of
String) */
Node *quals; /* qualifiers on join, if any */
+ Alias *join_using_alias; /* alias attached to USING clause */
Alias *alias; /* user-written alias clause,
if any */
int rtindex; /* RT index assigned
for join, or 0 */
} JoinExpr;
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index d25819aa28..7c17f3c46e 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -262,6 +262,7 @@ struct ParseNamespaceItem
bool p_cols_visible; /* Column names visible as unqualified
refs? */
bool p_lateral_only; /* Is only visible to LATERAL
expressions? */
bool p_lateral_ok; /* If so, does join type allow use? */
+ bool p_join_using_alias; /* Is it a JOIN/USING alias? */
};
/*
diff --git a/src/include/parser/parse_relation.h
b/src/include/parser/parse_relation.h
index 93f94466a0..bde59315d8 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -89,6 +89,7 @@ extern ParseNamespaceItem
*addRangeTableEntryForJoin(ParseState *pstate,
List *aliasvars,
List *leftcols,
List *rightcols,
+
Alias *joinalias,
Alias *alias,
bool inFromCl);
extern ParseNamespaceItem *addRangeTableEntryForCTE(ParseState *pstate,
diff --git a/src/test/regress/expected/create_view.out
b/src/test/regress/expected/create_view.out
index b234d2d4f9..33e6c04066 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -805,6 +805,51 @@ View definition:
(tbl3
CROSS JOIN tbl4) same;
+create table tbl1a (a int, c int);
+create view view_of_joins_2a as select * from tbl1 join tbl1a using (a);
+create view view_of_joins_2b as select * from tbl1 join tbl1a using (a) as x;
+create view view_of_joins_2c as select * from (tbl1 join tbl1a using (a)) as y;
+create view view_of_joins_2d as select * from (tbl1 join tbl1a using (a) as x)
as y;
+select pg_get_viewdef('view_of_joins_2a', true);
+ pg_get_viewdef
+----------------------------
+ SELECT tbl1.a, +
+ tbl1.b, +
+ tbl1a.c +
+ FROM tbl1 +
+ JOIN tbl1a USING (a);
+(1 row)
+
+select pg_get_viewdef('view_of_joins_2b', true);
+ pg_get_viewdef
+---------------------------------
+ SELECT tbl1.a, +
+ tbl1.b, +
+ tbl1a.c +
+ FROM tbl1 +
+ JOIN tbl1a USING (a) AS x;
+(1 row)
+
+select pg_get_viewdef('view_of_joins_2c', true);
+ pg_get_viewdef
+-------------------------------
+ SELECT y.a, +
+ y.b, +
+ y.c +
+ FROM (tbl1 +
+ JOIN tbl1a USING (a)) y;
+(1 row)
+
+select pg_get_viewdef('view_of_joins_2d', true);
+ pg_get_viewdef
+------------------------------------
+ SELECT y.a, +
+ y.b, +
+ y.c +
+ FROM (tbl1 +
+ JOIN tbl1a USING (a) AS x) y;
+(1 row)
+
-- Test view decompilation in the face of column addition/deletion/renaming
create table tt2 (a int, b int, c int);
create table tt3 (ax int8, b int2, c numeric);
@@ -1943,7 +1988,7 @@ drop cascades to view aliased_view_2
drop cascades to view aliased_view_3
drop cascades to view aliased_view_4
DROP SCHEMA testviewschm2 CASCADE;
-NOTICE: drop cascades to 68 other objects
+NOTICE: drop cascades to 73 other objects
DETAIL: drop cascades to table t1
drop cascades to view temporal1
drop cascades to view temporal2
@@ -1968,6 +2013,11 @@ drop cascades to view unspecified_types
drop cascades to table tt1
drop cascades to table tx1
drop cascades to view view_of_joins
+drop cascades to table tbl1a
+drop cascades to view view_of_joins_2a
+drop cascades to view view_of_joins_2b
+drop cascades to view view_of_joins_2c
+drop cascades to view view_of_joins_2d
drop cascades to table tt2
drop cascades to table tt3
drop cascades to table tt4
diff --git a/src/test/regress/expected/join.out
b/src/test/regress/expected/join.out
index 6c9a5e26dd..4b14fa32c5 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -1621,6 +1621,37 @@ SELECT '' AS "xxx", *
| 4 | 1 | one | 2
(4 rows)
+-- test join using aliases
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) WHERE J1_TBL.t = 'one'; -- ok
+ i | j | t | k
+---+---+-----+----
+ 1 | 4 | one | -1
+(1 row)
+
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; -- ok
+ i | j | t | k
+---+---+-----+----
+ 1 | 4 | one | -1
+(1 row)
+
+SELECT * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t = 'one'; --
error
+ERROR: invalid reference to FROM-clause entry for table "j1_tbl"
+LINE 1: ... * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t =...
+ ^
+HINT: There is an entry for table "j1_tbl", but it cannot be referenced from
this part of the query.
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.i = 1; -- ok
+ i | j | t | k
+---+---+-----+----
+ 1 | 4 | one | -1
+(1 row)
+
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one'; -- error
+ERROR: column reference "t" is invalid
+LINE 1: ...CT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one...
+ ^
+DETAIL: The range variable "x" only contains columns in the USING clause.
+SELECT * FROM J1_TBL a1 JOIN J2_TBL a2 USING (i) AS a1; -- error
+ERROR: table name "a1" specified more than once
--
-- NATURAL JOIN
-- Inner equi-join on all columns with the same name
diff --git a/src/test/regress/sql/create_view.sql
b/src/test/regress/sql/create_view.sql
index 6d4dd53965..c9a9c31278 100644
--- a/src/test/regress/sql/create_view.sql
+++ b/src/test/regress/sql/create_view.sql
@@ -328,6 +328,17 @@ CREATE VIEW aliased_view_4 AS
\d+ view_of_joins
+create table tbl1a (a int, c int);
+create view view_of_joins_2a as select * from tbl1 join tbl1a using (a);
+create view view_of_joins_2b as select * from tbl1 join tbl1a using (a) as x;
+create view view_of_joins_2c as select * from (tbl1 join tbl1a using (a)) as y;
+create view view_of_joins_2d as select * from (tbl1 join tbl1a using (a) as x)
as y;
+
+select pg_get_viewdef('view_of_joins_2a', true);
+select pg_get_viewdef('view_of_joins_2b', true);
+select pg_get_viewdef('view_of_joins_2c', true);
+select pg_get_viewdef('view_of_joins_2d', true);
+
-- Test view decompilation in the face of column addition/deletion/renaming
create table tt2 (a int, b int, c int);
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index dd60d6a1f3..0c73ce8728 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -126,6 +126,14 @@ CREATE TABLE J2_TBL (
FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b)
ORDER BY b, t1.a;
+-- test join using aliases
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) WHERE J1_TBL.t = 'one'; -- ok
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE J1_TBL.t = 'one'; -- ok
+SELECT * FROM (J1_TBL JOIN J2_TBL USING (i)) AS x WHERE J1_TBL.t = 'one'; --
error
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.i = 1; -- ok
+SELECT * FROM J1_TBL JOIN J2_TBL USING (i) AS x WHERE x.t = 'one'; -- error
+SELECT * FROM J1_TBL a1 JOIN J2_TBL a2 USING (i) AS a1; -- error
+
--
-- NATURAL JOIN
base-commit: 788dd0b839fc9f2c85caf86014105afdb60ed5e3
--
2.29.2