Skip to content

Commit

Permalink
Add support for qualified column names in JOIN ... USING (#1663)
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavcloud authored Jan 19, 2025
1 parent e9498d5 commit 44df6d6
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
}
Expand Down
35 changes: 31 additions & 4 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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| {
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit 44df6d6

Please sign in to comment.