Skip to content

Commit 327fa51

Browse files
committed
MySQL: Support CROSS JOIN <table> [ON | USING] ...
1 parent befc95e commit 327fa51

File tree

7 files changed

+63
-6
lines changed

7 files changed

+63
-6
lines changed

src/ast/query.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,7 +2333,11 @@ impl fmt::Display for Join {
23332333
self.relation,
23342334
suffix(constraint)
23352335
)),
2336-
JoinOperator::CrossJoin => f.write_fmt(format_args!("CROSS JOIN {}", self.relation)),
2336+
JoinOperator::CrossJoin(constraint) => f.write_fmt(format_args!(
2337+
"CROSS JOIN {}{}",
2338+
self.relation,
2339+
suffix(constraint)
2340+
)),
23372341
JoinOperator::Semi(constraint) => f.write_fmt(format_args!(
23382342
"{}SEMI JOIN {}{}",
23392343
prefix(constraint),
@@ -2400,7 +2404,8 @@ pub enum JoinOperator {
24002404
Right(JoinConstraint),
24012405
RightOuter(JoinConstraint),
24022406
FullOuter(JoinConstraint),
2403-
CrossJoin,
2407+
/// CROSS (constraint is non-standard)
2408+
CrossJoin(JoinConstraint),
24042409
/// SEMI (non-standard)
24052410
Semi(JoinConstraint),
24062411
/// LEFT SEMI (non-standard)

src/ast/spans.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ impl Spanned for JoinOperator {
22262226
JoinOperator::Right(join_constraint) => join_constraint.span(),
22272227
JoinOperator::RightOuter(join_constraint) => join_constraint.span(),
22282228
JoinOperator::FullOuter(join_constraint) => join_constraint.span(),
2229-
JoinOperator::CrossJoin => Span::empty(),
2229+
JoinOperator::CrossJoin(join_constraint) => join_constraint.span(),
22302230
JoinOperator::LeftSemi(join_constraint) => join_constraint.span(),
22312231
JoinOperator::RightSemi(join_constraint) => join_constraint.span(),
22322232
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
@@ -13317,15 +13317,24 @@ impl<'a> Parser<'a> {
1331713317
let global = self.parse_keyword(Keyword::GLOBAL);
1331813318
let join = if self.parse_keyword(Keyword::CROSS) {
1331913319
let join_operator = if self.parse_keyword(Keyword::JOIN) {
13320-
JoinOperator::CrossJoin
13320+
JoinOperator::CrossJoin(JoinConstraint::None)
1332113321
} else if self.parse_keyword(Keyword::APPLY) {
1332213322
// MSSQL extension, similar to CROSS JOIN LATERAL
1332313323
JoinOperator::CrossApply
1332413324
} else {
1332513325
return self.expected("JOIN or APPLY after CROSS", self.peek_token());
1332613326
};
13327+
let relation = self.parse_table_factor()?;
13328+
let join_operator = if matches!(join_operator, JoinOperator::CrossJoin(_))
13329+
&& self.dialect.supports_cross_join_constraint()
13330+
{
13331+
let constraint = self.parse_join_constraint(false)?;
13332+
JoinOperator::CrossJoin(constraint)
13333+
} else {
13334+
join_operator
13335+
};
1332713336
Join {
13328-
relation: self.parse_table_factor()?,
13337+
relation,
1332913338
global,
1333013339
join_operator,
1333113340
}

tests/sqlparser_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7131,7 +7131,7 @@ fn parse_cross_join() {
71317131
Join {
71327132
relation: table_from_name(ObjectName::from(vec![Ident::new("t2")])),
71337133
global: false,
7134-
join_operator: JoinOperator::CrossJoin,
7134+
join_operator: JoinOperator::CrossJoin(JoinConstraint::None),
71357135
},
71367136
only(only(select.from).joins),
71377137
);

tests/sqlparser_mysql.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4132,6 +4132,40 @@ fn parse_straight_join() {
41324132
.verified_stmt("SELECT a.*, b.* FROM table_a STRAIGHT_JOIN table_b AS b ON a.b_id = b.id");
41334133
}
41344134

4135+
#[test]
4136+
fn parse_cross_join_on() {
4137+
let sql = "SELECT * FROM t1 CROSS JOIN t2 ON a = b";
4138+
let select = mysql().verified_only_select(sql);
4139+
assert_eq!(
4140+
Join {
4141+
relation: table_from_name(ObjectName::from(vec![Ident::new("t2")])),
4142+
global: false,
4143+
join_operator: JoinOperator::CrossJoin(JoinConstraint::On(Expr::BinaryOp {
4144+
left: Box::new(Expr::Identifier(Ident::new("a"))),
4145+
op: BinaryOperator::Eq,
4146+
right: Box::new(Expr::Identifier(Ident::new("b"))),
4147+
})),
4148+
},
4149+
only(only(select.from).joins),
4150+
);
4151+
}
4152+
4153+
#[test]
4154+
fn parse_cross_join_using() {
4155+
let sql = "SELECT * FROM t1 CROSS JOIN t2 USING(a)";
4156+
let select = mysql().verified_only_select(sql);
4157+
assert_eq!(
4158+
Join {
4159+
relation: table_from_name(ObjectName::from(vec![Ident::new("t2")])),
4160+
global: false,
4161+
join_operator: JoinOperator::CrossJoin(JoinConstraint::Using(vec![ObjectName::from(
4162+
vec![Ident::new("a")]
4163+
)],)),
4164+
},
4165+
only(only(select.from).joins),
4166+
);
4167+
}
4168+
41354169
#[test]
41364170
fn mysql_foreign_key_with_index_name() {
41374171
mysql().verified_stmt(

0 commit comments

Comments
 (0)