Skip to content

Commit

Permalink
Allow to use the GLOBAL keyword before the join operator (#1353)
Browse files Browse the repository at this point in the history
  • Loading branch information
git-hulk authored Jul 30, 2024
1 parent bc15f7b commit f966580
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 8 deletions.
7 changes: 7 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,9 @@ impl Display for TableVersion {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Join {
pub relation: TableFactor,
/// ClickHouse supports the optional `GLOBAL` keyword before the join operator.
/// See [ClickHouse](https://clickhouse.com/docs/en/sql-reference/statements/select/join)
pub global: bool,
pub join_operator: JoinOperator,
}

Expand All @@ -1563,6 +1566,10 @@ impl fmt::Display for Join {
}
Suffix(constraint)
}
if self.global {
write!(f, " GLOBAL")?;
}

match &self.join_operator {
JoinOperator::Inner(constraint) => write!(
f,
Expand Down
1 change: 1 addition & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::USING,
Keyword::CLUSTER,
Keyword::DISTRIBUTE,
Keyword::GLOBAL,
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
Keyword::OUTER,
Keyword::SET,
Expand Down
5 changes: 5 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9015,6 +9015,7 @@ impl<'a> Parser<'a> {
// a table alias.
let mut joins = vec![];
loop {
let global = self.parse_keyword(Keyword::GLOBAL);
let join = if self.parse_keyword(Keyword::CROSS) {
let join_operator = if self.parse_keyword(Keyword::JOIN) {
JoinOperator::CrossJoin
Expand All @@ -9026,13 +9027,15 @@ impl<'a> Parser<'a> {
};
Join {
relation: self.parse_table_factor()?,
global,
join_operator,
}
} else if self.parse_keyword(Keyword::OUTER) {
// MSSQL extension, similar to LEFT JOIN LATERAL .. ON 1=1
self.expect_keyword(Keyword::APPLY)?;
Join {
relation: self.parse_table_factor()?,
global,
join_operator: JoinOperator::OuterApply,
}
} else if self.parse_keyword(Keyword::ASOF) {
Expand All @@ -9042,6 +9045,7 @@ impl<'a> Parser<'a> {
let match_condition = self.parse_parenthesized(Self::parse_expr)?;
Join {
relation,
global,
join_operator: JoinOperator::AsOf {
match_condition,
constraint: self.parse_join_constraint(false)?,
Expand Down Expand Up @@ -9127,6 +9131,7 @@ impl<'a> Parser<'a> {
let join_constraint = self.parse_join_constraint(natural)?;
Join {
relation,
global,
join_operator: join_operator_type(join_constraint),
}
};
Expand Down
1 change: 1 addition & 0 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> Ta
pub fn join(relation: TableFactor) -> Join {
Join {
relation,
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}
}
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,7 @@ fn parse_join_constraint_unnest_alias() {
with_offset_alias: None,
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq,
Expand Down
71 changes: 63 additions & 8 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5600,6 +5600,7 @@ fn parse_implicit_join() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}],
},
Expand All @@ -5623,6 +5624,7 @@ fn parse_implicit_join() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}],
},
Expand All @@ -5646,6 +5648,7 @@ fn parse_cross_join() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::CrossJoin,
},
only(only(select.from).joins),
Expand All @@ -5657,6 +5660,7 @@ fn parse_joins_on() {
fn join_with_constraint(
relation: impl Into<String>,
alias: Option<TableAlias>,
global: bool,
f: impl Fn(JoinConstraint) -> JoinOperator,
) -> Join {
Join {
Expand All @@ -5669,6 +5673,7 @@ fn parse_joins_on() {
partitions: vec![],
with_ordinality: false,
},
global,
join_operator: f(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq,
Expand All @@ -5682,6 +5687,7 @@ fn parse_joins_on() {
vec![join_with_constraint(
"t2",
table_alias("foo"),
false,
JoinOperator::Inner,
)]
);
Expand All @@ -5692,35 +5698,80 @@ fn parse_joins_on() {
// Test parsing of different join operators
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::Inner)]
vec![join_with_constraint("t2", None, false, JoinOperator::Inner)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::LeftOuter)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::LeftOuter
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::RightOuter
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::LeftSemi)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::LeftSemi
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT SEMI JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightSemi)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::RightSemi
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::LeftAnti)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::LeftAnti
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT ANTI JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightAnti)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::RightAnti
)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
vec![join_with_constraint(
"t2",
None,
false,
JoinOperator::FullOuter
)]
);

assert_eq!(
only(&verified_only_select("SELECT * FROM t1 GLOBAL FULL JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint(
"t2",
None,
true,
JoinOperator::FullOuter
)]
);
}

Expand All @@ -5741,6 +5792,7 @@ fn parse_joins_using() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
}
}
Expand Down Expand Up @@ -5805,6 +5857,7 @@ fn parse_natural_join() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: f(JoinConstraint::Natural),
}
}
Expand Down Expand Up @@ -6073,6 +6126,7 @@ fn parse_derived_tables() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}],
}),
Expand Down Expand Up @@ -6983,6 +7037,7 @@ fn lateral_function() {
],
alias: None,
},
global: false,
join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
}],
}],
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1891,6 +1891,7 @@ fn parse_update_with_joins() {
partitions: vec![],
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::CompoundIdentifier(vec![
Ident::new("o"),
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4102,6 +4102,7 @@ fn parse_join_constraint_unnest_alias() {
with_offset_alias: None,
with_ordinality: false,
},
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq,
Expand Down
1 change: 1 addition & 0 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,7 @@ fn asof_joins() {
relation: table_with_alias("trades_unixtime", "tu"),
joins: vec![Join {
relation: table_with_alias("quotes_unixtime", "qu"),
global: false,
join_operator: JoinOperator::AsOf {
match_condition: Expr::BinaryOp {
left: Box::new(Expr::CompoundIdentifier(vec![
Expand Down

0 comments on commit f966580

Please sign in to comment.