Skip to content

Commit 6c3a67b

Browse files
rs-sacayman-sigma
authored andcommitted
MySQL: Support CROSS JOIN constraint (apache#2025)
1 parent 78facfd commit 6c3a67b

6 files changed

Lines changed: 62 additions & 6 deletions

File tree

src/ast/query.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,7 +2346,11 @@ impl fmt::Display for Join {
23462346
self.relation,
23472347
suffix(constraint)
23482348
)),
2349-
JoinOperator::CrossJoin => f.write_fmt(format_args!("CROSS JOIN {}", self.relation)),
2349+
JoinOperator::CrossJoin(constraint) => f.write_fmt(format_args!(
2350+
"CROSS JOIN {}{}",
2351+
self.relation,
2352+
suffix(constraint)
2353+
)),
23502354
JoinOperator::Semi(constraint) => f.write_fmt(format_args!(
23512355
"{}SEMI JOIN {}{}",
23522356
prefix(constraint),
@@ -2413,7 +2417,8 @@ pub enum JoinOperator {
24132417
Right(JoinConstraint),
24142418
RightOuter(JoinConstraint),
24152419
FullOuter(JoinConstraint),
2416-
CrossJoin,
2420+
/// CROSS (constraint is non-standard)
2421+
CrossJoin(JoinConstraint),
24172422
/// SEMI (non-standard)
24182423
Semi(JoinConstraint),
24192424
/// LEFT SEMI (non-standard)

src/ast/spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2235,7 +2235,7 @@ impl Spanned for JoinOperator {
22352235
JoinOperator::Right(join_constraint) => join_constraint.span(),
22362236
JoinOperator::RightOuter(join_constraint) => join_constraint.span(),
22372237
JoinOperator::FullOuter(join_constraint) => join_constraint.span(),
2238-
JoinOperator::CrossJoin => Span::empty(),
2238+
JoinOperator::CrossJoin(join_constraint) => join_constraint.span(),
22392239
JoinOperator::LeftSemi(join_constraint) => join_constraint.span(),
22402240
JoinOperator::RightSemi(join_constraint) => join_constraint.span(),
22412241
JoinOperator::LeftAnti(join_constraint) => join_constraint.span(),

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,11 @@ pub trait Dialect: Debug + Any {
311311
false
312312
}
313313

314+
/// Returns true if the dialect supports a join specification on CROSS JOIN.
315+
fn supports_cross_join_constraint(&self) -> bool {
316+
false
317+
}
318+
314319
/// Returns true if the dialect supports CONNECT BY.
315320
fn supports_connect_by(&self) -> bool {
316321
false

src/dialect/mysql.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ impl Dialect for MySqlDialect {
163163
fn supports_data_type_signed_suffix(&self) -> bool {
164164
true
165165
}
166+
167+
fn supports_cross_join_constraint(&self) -> bool {
168+
true
169+
}
166170
}
167171

168172
/// `LOCK TABLES`

src/parser/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13348,15 +13348,24 @@ impl<'a> Parser<'a> {
1334813348
let global = self.parse_keyword(Keyword::GLOBAL);
1334913349
let join = if self.parse_keyword(Keyword::CROSS) {
1335013350
let join_operator = if self.parse_keyword(Keyword::JOIN) {
13351-
JoinOperator::CrossJoin
13351+
JoinOperator::CrossJoin(JoinConstraint::None)
1335213352
} else if self.parse_keyword(Keyword::APPLY) {
1335313353
// MSSQL extension, similar to CROSS JOIN LATERAL
1335413354
JoinOperator::CrossApply
1335513355
} else {
1335613356
return self.expected("JOIN or APPLY after CROSS", self.peek_token());
1335713357
};
13358+
let relation = self.parse_table_factor()?;
13359+
let join_operator = if matches!(join_operator, JoinOperator::CrossJoin(_))
13360+
&& self.dialect.supports_cross_join_constraint()
13361+
{
13362+
let constraint = self.parse_join_constraint(false)?;
13363+
JoinOperator::CrossJoin(constraint)
13364+
} else {
13365+
join_operator
13366+
};
1335813367
Join {
13359-
relation: self.parse_table_factor()?,
13368+
relation,
1336013369
global,
1336113370
join_operator,
1336213371
}

tests/sqlparser_common.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7132,12 +7132,45 @@ fn parse_cross_join() {
71327132
Join {
71337133
relation: table_from_name(ObjectName::from(vec![Ident::new("t2")])),
71347134
global: false,
7135-
join_operator: JoinOperator::CrossJoin,
7135+
join_operator: JoinOperator::CrossJoin(JoinConstraint::None),
71367136
},
71377137
only(only(select.from).joins),
71387138
);
71397139
}
71407140

7141+
#[test]
7142+
fn parse_cross_join_constraint() {
7143+
fn join_with_constraint(constraint: JoinConstraint) -> Join {
7144+
Join {
7145+
relation: table_from_name(ObjectName::from(vec![Ident::new("t2")])),
7146+
global: false,
7147+
join_operator: JoinOperator::CrossJoin(constraint),
7148+
}
7149+
}
7150+
7151+
fn test_constraint(sql: &str, constraint: JoinConstraint) {
7152+
let dialect = all_dialects_where(|d| d.supports_cross_join_constraint());
7153+
let select = dialect.verified_only_select(sql);
7154+
assert_eq!(
7155+
join_with_constraint(constraint),
7156+
only(only(select.from).joins),
7157+
);
7158+
}
7159+
7160+
test_constraint(
7161+
"SELECT * FROM t1 CROSS JOIN t2 ON a = b",
7162+
JoinConstraint::On(Expr::BinaryOp {
7163+
left: Box::new(Expr::Identifier(Ident::new("a"))),
7164+
op: BinaryOperator::Eq,
7165+
right: Box::new(Expr::Identifier(Ident::new("b"))),
7166+
}),
7167+
);
7168+
test_constraint(
7169+
"SELECT * FROM t1 CROSS JOIN t2 USING(a)",
7170+
JoinConstraint::Using(vec![ObjectName::from(vec![Ident::new("a")])]),
7171+
);
7172+
}
7173+
71417174
#[test]
71427175
fn parse_joins_on() {
71437176
fn join_with_constraint(

0 commit comments

Comments
 (0)