This is an automated email from the ASF dual-hosted git repository.

iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 44df6d6f Add support for qualified column names in JOIN ... USING 
(#1663)
44df6d6f is described below

commit 44df6d6f92e8bba1cbc6d4b57c99f8f126b786fd
Author: Yoav Cohen <[email protected]>
AuthorDate: Sun Jan 19 11:43:45 2025 +0100

    Add support for qualified column names in JOIN ... USING (#1663)
---
 src/ast/query.rs          |  2 +-
 src/ast/spans.rs          |  2 +-
 src/parser/mod.rs         | 35 +++++++++++++++++++++++++++++++----
 tests/sqlparser_common.rs |  3 ++-
 4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/src/ast/query.rs b/src/ast/query.rs
index 9bcdc2e7..66c70b46 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -2050,7 +2050,7 @@ pub enum JoinOperator {
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub enum JoinConstraint {
     On(Expr),
-    Using(Vec<Ident>),
+    Using(Vec<ObjectName>),
     Natural,
     None,
 }
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 2a5a75b4..1ddd47d7 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -2008,7 +2008,7 @@ impl Spanned for JoinConstraint {
     fn span(&self) -> Span {
         match self {
             JoinConstraint::On(expr) => expr.span(),
-            JoinConstraint::Using(vec) => union_spans(vec.iter().map(|i| 
i.span)),
+            JoinConstraint::Using(vec) => union_spans(vec.iter().map(|i| 
i.span())),
             JoinConstraint::Natural => Span::empty(),
             JoinConstraint::None => Span::empty(),
         }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 5cacfda9..a3adb023 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -9336,18 +9336,45 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Parse a parenthesized comma-separated list of unqualified, possibly 
quoted identifiers
+    /// Parses a parenthesized comma-separated list of unqualified, possibly 
quoted identifiers.
+    /// For example: `(col1, "col 2", ...)`
     pub fn parse_parenthesized_column_list(
         &mut self,
         optional: IsOptional,
         allow_empty: bool,
     ) -> Result<Vec<Ident>, ParserError> {
+        self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| 
p.parse_identifier())
+    }
+
+    /// Parses a parenthesized comma-separated list of qualified, possibly 
quoted identifiers.
+    /// For example: `(db1.sc1.tbl1.col1, db1.sc1.tbl1."col 2", ...)`
+    pub fn parse_parenthesized_qualified_column_list(
+        &mut self,
+        optional: IsOptional,
+        allow_empty: bool,
+    ) -> Result<Vec<ObjectName>, ParserError> {
+        self.parse_parenthesized_column_list_inner(optional, allow_empty, |p| {
+            p.parse_object_name(true)
+        })
+    }
+
+    /// Parses a parenthesized comma-separated list of columns using
+    /// the provided function to parse each element.
+    fn parse_parenthesized_column_list_inner<F, T>(
+        &mut self,
+        optional: IsOptional,
+        allow_empty: bool,
+        mut f: F,
+    ) -> Result<Vec<T>, ParserError>
+    where
+        F: FnMut(&mut Parser) -> Result<T, ParserError>,
+    {
         if self.consume_token(&Token::LParen) {
             if allow_empty && self.peek_token().token == Token::RParen {
                 self.next_token();
                 Ok(vec![])
             } else {
-                let cols = self.parse_comma_separated(|p| 
p.parse_identifier())?;
+                let cols = self.parse_comma_separated(|p| f(p))?;
                 self.expect_token(&Token::RParen)?;
                 Ok(cols)
             }
@@ -9358,7 +9385,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a parenthesized comma-separated list of table alias column 
definitions.
+    /// Parses a parenthesized comma-separated list of table alias column 
definitions.
     fn parse_table_alias_column_defs(&mut self) -> 
Result<Vec<TableAliasColumnDef>, ParserError> {
         if self.consume_token(&Token::LParen) {
             let cols = self.parse_comma_separated(|p| {
@@ -11853,7 +11880,7 @@ impl<'a> Parser<'a> {
             let constraint = self.parse_expr()?;
             Ok(JoinConstraint::On(constraint))
         } else if self.parse_keyword(Keyword::USING) {
-            let columns = self.parse_parenthesized_column_list(Mandatory, 
false)?;
+            let columns = 
self.parse_parenthesized_qualified_column_list(Mandatory, false)?;
             Ok(JoinConstraint::Using(columns))
         } else {
             Ok(JoinConstraint::None)
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 49588a58..7271d637 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -6541,7 +6541,7 @@ fn parse_joins_using() {
                 sample: None,
             },
             global: false,
-            join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
+            join_operator: 
f(JoinConstraint::Using(vec![ObjectName(vec!["c1".into()])])),
         }
     }
     // Test parsing of aliases
@@ -6598,6 +6598,7 @@ fn parse_joins_using() {
         only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 
USING(c1)").from).joins,
         vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
     );
+    verified_stmt("SELECT * FROM tbl1 AS t1 JOIN tbl2 AS t2 USING(t2.col1)");
 }
 
 #[test]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to