Allow to use the GLOBAL keyword before the join operator (#1353)

This commit is contained in:
hulk 2024-07-31 04:30:46 +08:00 committed by GitHub
parent bc15f7b4ce
commit f96658006f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 81 additions and 8 deletions

View file

@ -1537,6 +1537,9 @@ impl Display for TableVersion {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Join { pub struct Join {
pub relation: TableFactor, 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, pub join_operator: JoinOperator,
} }
@ -1563,6 +1566,10 @@ impl fmt::Display for Join {
} }
Suffix(constraint) Suffix(constraint)
} }
if self.global {
write!(f, " GLOBAL")?;
}
match &self.join_operator { match &self.join_operator {
JoinOperator::Inner(constraint) => write!( JoinOperator::Inner(constraint) => write!(
f, f,

View file

@ -850,6 +850,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::USING, Keyword::USING,
Keyword::CLUSTER, Keyword::CLUSTER,
Keyword::DISTRIBUTE, Keyword::DISTRIBUTE,
Keyword::GLOBAL,
// for MSSQL-specific OUTER APPLY (seems reserved in most dialects) // for MSSQL-specific OUTER APPLY (seems reserved in most dialects)
Keyword::OUTER, Keyword::OUTER,
Keyword::SET, Keyword::SET,

View file

@ -9015,6 +9015,7 @@ impl<'a> Parser<'a> {
// a table alias. // a table alias.
let mut joins = vec![]; let mut joins = vec![];
loop { loop {
let global = self.parse_keyword(Keyword::GLOBAL);
let join = if self.parse_keyword(Keyword::CROSS) { let join = if self.parse_keyword(Keyword::CROSS) {
let join_operator = if self.parse_keyword(Keyword::JOIN) { let join_operator = if self.parse_keyword(Keyword::JOIN) {
JoinOperator::CrossJoin JoinOperator::CrossJoin
@ -9026,6 +9027,7 @@ impl<'a> Parser<'a> {
}; };
Join { Join {
relation: self.parse_table_factor()?, relation: self.parse_table_factor()?,
global,
join_operator, join_operator,
} }
} else if self.parse_keyword(Keyword::OUTER) { } else if self.parse_keyword(Keyword::OUTER) {
@ -9033,6 +9035,7 @@ impl<'a> Parser<'a> {
self.expect_keyword(Keyword::APPLY)?; self.expect_keyword(Keyword::APPLY)?;
Join { Join {
relation: self.parse_table_factor()?, relation: self.parse_table_factor()?,
global,
join_operator: JoinOperator::OuterApply, join_operator: JoinOperator::OuterApply,
} }
} else if self.parse_keyword(Keyword::ASOF) { } else if self.parse_keyword(Keyword::ASOF) {
@ -9042,6 +9045,7 @@ impl<'a> Parser<'a> {
let match_condition = self.parse_parenthesized(Self::parse_expr)?; let match_condition = self.parse_parenthesized(Self::parse_expr)?;
Join { Join {
relation, relation,
global,
join_operator: JoinOperator::AsOf { join_operator: JoinOperator::AsOf {
match_condition, match_condition,
constraint: self.parse_join_constraint(false)?, constraint: self.parse_join_constraint(false)?,
@ -9127,6 +9131,7 @@ impl<'a> Parser<'a> {
let join_constraint = self.parse_join_constraint(natural)?; let join_constraint = self.parse_join_constraint(natural)?;
Join { Join {
relation, relation,
global,
join_operator: join_operator_type(join_constraint), join_operator: join_operator_type(join_constraint),
} }
}; };

View file

@ -331,6 +331,7 @@ pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> Ta
pub fn join(relation: TableFactor) -> Join { pub fn join(relation: TableFactor) -> Join {
Join { Join {
relation, relation,
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural), join_operator: JoinOperator::Inner(JoinConstraint::Natural),
} }
} }

View file

@ -1557,6 +1557,7 @@ fn parse_join_constraint_unnest_alias() {
with_offset_alias: None, with_offset_alias: None,
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())), left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq, op: BinaryOperator::Eq,

View file

@ -5600,6 +5600,7 @@ fn parse_implicit_join() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural), join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}], }],
}, },
@ -5623,6 +5624,7 @@ fn parse_implicit_join() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural), join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}], }],
}, },
@ -5646,6 +5648,7 @@ fn parse_cross_join() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::CrossJoin, join_operator: JoinOperator::CrossJoin,
}, },
only(only(select.from).joins), only(only(select.from).joins),
@ -5657,6 +5660,7 @@ fn parse_joins_on() {
fn join_with_constraint( fn join_with_constraint(
relation: impl Into<String>, relation: impl Into<String>,
alias: Option<TableAlias>, alias: Option<TableAlias>,
global: bool,
f: impl Fn(JoinConstraint) -> JoinOperator, f: impl Fn(JoinConstraint) -> JoinOperator,
) -> Join { ) -> Join {
Join { Join {
@ -5669,6 +5673,7 @@ fn parse_joins_on() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global,
join_operator: f(JoinConstraint::On(Expr::BinaryOp { join_operator: f(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())), left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq, op: BinaryOperator::Eq,
@ -5682,6 +5687,7 @@ fn parse_joins_on() {
vec![join_with_constraint( vec![join_with_constraint(
"t2", "t2",
table_alias("foo"), table_alias("foo"),
false,
JoinOperator::Inner, JoinOperator::Inner,
)] )]
); );
@ -5692,35 +5698,80 @@ fn parse_joins_on() {
// Test parsing of different join operators // Test parsing of different join operators
assert_eq!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT SEMI JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT ANTI JOIN t2 ON c1 = c2").from).joins, 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!( assert_eq!(
only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2").from).joins, 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
)]
); );
} }
@ -5741,6 +5792,7 @@ fn parse_joins_using() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: f(JoinConstraint::Using(vec!["c1".into()])), join_operator: f(JoinConstraint::Using(vec!["c1".into()])),
} }
} }
@ -5805,6 +5857,7 @@ fn parse_natural_join() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: f(JoinConstraint::Natural), join_operator: f(JoinConstraint::Natural),
} }
} }
@ -6073,6 +6126,7 @@ fn parse_derived_tables() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::Natural), join_operator: JoinOperator::Inner(JoinConstraint::Natural),
}], }],
}), }),
@ -6983,6 +7037,7 @@ fn lateral_function() {
], ],
alias: None, alias: None,
}, },
global: false,
join_operator: JoinOperator::LeftOuter(JoinConstraint::None), join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
}], }],
}], }],

View file

@ -1891,6 +1891,7 @@ fn parse_update_with_joins() {
partitions: vec![], partitions: vec![],
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::CompoundIdentifier(vec![ left: Box::new(Expr::CompoundIdentifier(vec![
Ident::new("o"), Ident::new("o"),

View file

@ -4102,6 +4102,7 @@ fn parse_join_constraint_unnest_alias() {
with_offset_alias: None, with_offset_alias: None,
with_ordinality: false, with_ordinality: false,
}, },
global: false,
join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp { join_operator: JoinOperator::Inner(JoinConstraint::On(Expr::BinaryOp {
left: Box::new(Expr::Identifier("c1".into())), left: Box::new(Expr::Identifier("c1".into())),
op: BinaryOperator::Eq, op: BinaryOperator::Eq,

View file

@ -2206,6 +2206,7 @@ fn asof_joins() {
relation: table_with_alias("trades_unixtime", "tu"), relation: table_with_alias("trades_unixtime", "tu"),
joins: vec![Join { joins: vec![Join {
relation: table_with_alias("quotes_unixtime", "qu"), relation: table_with_alias("quotes_unixtime", "qu"),
global: false,
join_operator: JoinOperator::AsOf { join_operator: JoinOperator::AsOf {
match_condition: Expr::BinaryOp { match_condition: Expr::BinaryOp {
left: Box::new(Expr::CompoundIdentifier(vec![ left: Box::new(Expr::CompoundIdentifier(vec![