When joining tables with USING, the listed columns are merged and no
longer belong to either the left or the right side.  That means they can
no longer be qualified which can often be an inconvenience.


SELECT a.x, b.y, z FROM a INNER JOIN b USING (z);


The SQL standard provides a workaround for this by allowing an alias on
the join clause. (<join correlation name> in section 7.10)


SELECT j.x, j.y, j.z FROM a INNER JOIN b USING (z) AS j;


Attached is a patch (based on 517bf2d910) adding this feature.

-- 

Vik Fearing

diff --git a/doc/src/sgml/queries.sgml b/doc/src/sgml/queries.sgml
index 22252556be..e4b17698d9 100644
--- a/doc/src/sgml/queries.sgml
+++ b/doc/src/sgml/queries.sgml
@@ -248,7 +248,7 @@ FROM <replaceable>table_reference</replaceable> <optional>, <replaceable>table_r
       <listitem>
 <synopsis>
 <replaceable>T1</replaceable> { <optional>INNER</optional> | { LEFT | RIGHT | FULL } <optional>OUTER</optional> } JOIN <replaceable>T2</replaceable> ON <replaceable>boolean_expression</replaceable>
-<replaceable>T1</replaceable> { <optional>INNER</optional> | { LEFT | RIGHT | FULL } <optional>OUTER</optional> } JOIN <replaceable>T2</replaceable> USING ( <replaceable>join column list</replaceable> )
+<replaceable>T1</replaceable> { <optional>INNER</optional> | { LEFT | RIGHT | FULL } <optional>OUTER</optional> } JOIN <replaceable>T2</replaceable> USING ( <replaceable>join column list</replaceable> ) [ AS <replaceable>alias</replaceable> ]
 <replaceable>T1</replaceable> NATURAL { <optional>INNER</optional> | { LEFT | RIGHT | FULL } <optional>OUTER</optional> } JOIN <replaceable>T2</replaceable>
 </synopsis>
 
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 06d611b64c..fcb474aaee 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -620,7 +620,7 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
      </varlistentry>
 
      <varlistentry>
-      <term><literal>USING ( <replaceable class="parameter">join_column</replaceable> [, ...] )</literal></term>
+      <term><literal>USING ( <replaceable class="parameter">join_column</replaceable> [, ...] ) [ AS <replaceable>alias</replaceable> ]</literal></term>
       <listitem>
        <para>
         A clause of the form <literal>USING ( a, b, ... )</literal> is
@@ -629,6 +629,8 @@ TABLE [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ]
         <literal>USING</literal> implies that only one of each pair of
         equivalent columns will be included in the join output, not
         both.
+        An alias may be provided to reference these columns.  In this
+        case, the <literal>AS</literal> keyword is required.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3f67aaf30e..34a4e0fa57 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -12004,7 +12004,11 @@ 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->alias = lsecond_node(Alias, castNode(List, $5));
+					}
 					else
 						n->quals = $5; /* ON clause */
 					$$ = n;
@@ -12018,7 +12022,11 @@ 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->alias = lsecond_node(Alias, castNode(List, $4));
+					}
 					else
 						n->quals = $4; /* ON clause */
 					$$ = n;
@@ -12126,9 +12134,14 @@ join_outer: OUTER_P									{ $$ = NULL; }
  *	ON expr allows more general qualifications.
  *
  * We return USING as a List node, while an ON-expr will not be a List.
+ *
+ * Since the USING clause merges the columns, they no longer belong to either
+ * the left or the right table.  SQL allows an alias to be assigned to the JOIN
+ * so that the columns can be qualified.
  */
 
-join_qual:	USING '(' name_list ')'					{ $$ = (Node *) $3; }
+join_qual:	USING '(' name_list ')'					{ $$ = (Node *) (list_make2($3, NULL)); }
+			| USING '(' name_list ')' AS ColId		{ $$ = (Node *) (list_make2($3, makeAlias($6, NIL))); }
 			| ON a_expr								{ $$ = $2; }
 		;
 
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index b58d560163..9694688077 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -1621,6 +1621,30 @@ SELECT '' AS "xxx", *
      | 4 | 1 | one   | 2
 (4 rows)
 
+-- Test naming the join result, first with postgres syntax...
+SELECT '' AS "xxx", *
+  FROM (J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (d, b) USING (b)) AS j
+  ORDER BY j.b, j.c, j.d;
+ xxx | b | a |   c   | d 
+-----+---+---+-------+---
+     | 0 | 5 | five  |  
+     | 0 |   | zero  |  
+     | 2 | 3 | three | 2
+     | 4 | 1 | one   | 2
+(4 rows)
+
+-- ...then with standard SQL syntax.
+SELECT '' AS "xxx", *
+  FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (d, b) USING (b) AS j
+  ORDER BY j.b, j.c, j.d;
+ xxx | b | a |   c   | d 
+-----+---+---+-------+---
+     | 0 | 5 | five  |  
+     | 0 |   | zero  |  
+     | 2 | 3 | three | 2
+     | 4 | 1 | one   | 2
+(4 rows)
+
 --
 -- NATURAL JOIN
 -- Inner equi-join on all columns with the same name
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 57481d0411..aa8a5771c5 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -126,6 +126,16 @@ SELECT '' AS "xxx", *
   FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (a, b) USING (b)
   ORDER BY b, t1.a;
 
+-- Test naming the join result, first with postgres syntax...
+SELECT '' AS "xxx", *
+  FROM (J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (d, b) USING (b)) AS j
+  ORDER BY j.b, j.c, j.d;
+
+-- ...then with standard SQL syntax.
+SELECT '' AS "xxx", *
+  FROM J1_TBL t1 (a, b, c) JOIN J2_TBL t2 (d, b) USING (b) AS j
+  ORDER BY j.b, j.c, j.d;
+
 
 --
 -- NATURAL JOIN

Reply via email to