From b004cf1b2c04392decc8f27a02a1ac8c2bb133dc Mon Sep 17 00:00:00 2001 From: Ben Herzberg Date: Thu, 10 Sep 2026 17:53:34 +0300 Subject: [PATCH 1/2] Postgres: Support aliases for JOIN USING --- src/ast/query.rs | 5 +++++ src/ast/spans.rs | 6 ++++++ src/parser/mod.rs | 9 ++++++++- tests/sqlparser_postgres.rs | 8 ++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 95f980181..c302ef967 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2703,6 +2703,9 @@ impl fmt::Display for Join { JoinConstraint::Using(attrs) => { write!(f, " USING({})", display_comma_separated(attrs)) } + JoinConstraint::UsingAlias(columns, alias) => { + write!(f, " USING({}) AS {alias}", display_comma_separated(columns)) + } _ => Ok(()), } } @@ -2892,6 +2895,8 @@ pub enum JoinConstraint { On(Expr), /// `USING(...)` list of column names. Using(Vec), + /// `USING(...) AS alias` list of column names with an alias for the joined columns. + UsingAlias(Vec, Ident), /// `NATURAL` join (columns matched automatically). Natural, /// No constraint specified (e.g. `CROSS JOIN`). diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 9fa9e61ef..c6752d022 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -2308,6 +2308,12 @@ impl Spanned for JoinConstraint { match self { JoinConstraint::On(expr) => expr.span(), JoinConstraint::Using(vec) => union_spans(vec.iter().map(|i| i.span())), + JoinConstraint::UsingAlias(columns, alias) => union_spans( + columns + .iter() + .map(|i| i.span()) + .chain(iter::once(alias.span)), + ), JoinConstraint::Natural => Span::empty(), JoinConstraint::None => Span::empty(), } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6fd25b9eb..2977e62bb 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -17763,7 +17763,14 @@ impl<'a> Parser<'a> { Ok(JoinConstraint::On(constraint)) } else if self.parse_keyword(Keyword::USING) { let columns = self.parse_parenthesized_qualified_column_list(Mandatory, false)?; - Ok(JoinConstraint::Using(columns)) + if self.parse_keyword(Keyword::AS) { + Ok(JoinConstraint::UsingAlias( + columns, + self.parse_identifier()?, + )) + } else { + Ok(JoinConstraint::Using(columns)) + } } else { Ok(JoinConstraint::None) //self.expected_ref("ON, or USING after JOIN", self.peek_token_ref()) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 23e20c718..722864b20 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -10042,3 +10042,11 @@ fn parse_bitstring_literal_escaping() { pg_and_generic().verified_stmt("SELECT B''''"); pg_and_generic().verified_stmt("SELECT B'it''s'"); } + +#[test] +fn parse_join_using_alias() { + pg().one_statement_parses_to( + "SELECT * FROM t1 JOIN t2 USING (id) AS joined_cols", + "SELECT * FROM t1 JOIN t2 USING(id) AS joined_cols", + ); +} From 6a0f1c5f73006258028a0718fa2163cb1c4361b9 Mon Sep 17 00:00:00 2001 From: Ben Herzberg Date: Thu, 24 Sep 2026 18:20:37 +0300 Subject: [PATCH 2/2] Postgres: Cover JOIN USING alias spans --- tests/sqlparser_postgres.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 722864b20..2fcd93620 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -10045,8 +10045,31 @@ fn parse_bitstring_literal_escaping() { #[test] fn parse_join_using_alias() { - pg().one_statement_parses_to( - "SELECT * FROM t1 JOIN t2 USING (id) AS joined_cols", + let sql = "SELECT * FROM t1 JOIN t2 USING (id) AS joined_cols"; + let dialect = PostgreSqlDialect {}; + let mut parser = Parser::new(&dialect).try_with_sql(sql).unwrap(); + let statement = parser.parse_statement().unwrap(); + let Statement::Query(query) = statement else { + panic!("Expected a query statement"); + }; + let SetExpr::Select(select) = *query.body else { + panic!("Expected a SELECT query"); + }; + let join = &select.from[0].joins[0]; + + assert_eq!( + JoinOperator::Join(JoinConstraint::UsingAlias( + vec![ObjectName::from(vec![Ident::new("id")])], + Ident::new("joined_cols"), + )), + join.join_operator + ); + assert_eq!( + Span::new(Location::new(1, 33), Location::new(1, 51)), + join.join_operator.span() + ); + assert_eq!( "SELECT * FROM t1 JOIN t2 USING(id) AS joined_cols", + select.to_string() ); }