Skip to content

Commit 96bca38

Browse files
authored
Support SEMI/ANTI JOIN syntax (apache#723)
1 parent 1f22ea7 commit 96bca38

File tree

4 files changed

+118
-7
lines changed

4 files changed

+118
-7
lines changed

src/ast/query.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,34 @@ impl fmt::Display for Join {
611611
suffix(constraint)
612612
),
613613
JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
614+
JoinOperator::LeftSemi(constraint) => write!(
615+
f,
616+
" {}LEFT SEMI JOIN {}{}",
617+
prefix(constraint),
618+
self.relation,
619+
suffix(constraint)
620+
),
621+
JoinOperator::RightSemi(constraint) => write!(
622+
f,
623+
" {}RIGHT SEMI JOIN {}{}",
624+
prefix(constraint),
625+
self.relation,
626+
suffix(constraint)
627+
),
628+
JoinOperator::LeftAnti(constraint) => write!(
629+
f,
630+
" {}LEFT ANTI JOIN {}{}",
631+
prefix(constraint),
632+
self.relation,
633+
suffix(constraint)
634+
),
635+
JoinOperator::RightAnti(constraint) => write!(
636+
f,
637+
" {}RIGHT ANTI JOIN {}{}",
638+
prefix(constraint),
639+
self.relation,
640+
suffix(constraint)
641+
),
614642
JoinOperator::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
615643
JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
616644
}
@@ -625,6 +653,14 @@ pub enum JoinOperator {
625653
RightOuter(JoinConstraint),
626654
FullOuter(JoinConstraint),
627655
CrossJoin,
656+
/// LEFT SEMI (non-standard)
657+
LeftSemi(JoinConstraint),
658+
/// RIGHT SEMI (non-standard)
659+
RightSemi(JoinConstraint),
660+
/// LEFT ANTI (non-standard)
661+
LeftAnti(JoinConstraint),
662+
/// RIGHT ANTI (non-standard)
663+
RightAnti(JoinConstraint),
628664
/// CROSS APPLY (non-standard)
629665
CrossApply,
630666
/// OUTER APPLY (non-standard)

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ define_keywords!(
7777
ALTER,
7878
ANALYZE,
7979
AND,
80+
ANTI,
8081
ANY,
8182
APPLY,
8283
ARCHIVE,
@@ -491,6 +492,7 @@ define_keywords!(
491492
SEARCH,
492493
SECOND,
493494
SELECT,
495+
SEMI,
494496
SENSITIVE,
495497
SEQUENCE,
496498
SEQUENCEFILE,

src/parser.rs

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4830,16 +4830,57 @@ impl<'a> Parser<'a> {
48304830
self.expect_keyword(Keyword::JOIN)?;
48314831
JoinOperator::Inner
48324832
}
4833-
kw @ Keyword::LEFT | kw @ Keyword::RIGHT | kw @ Keyword::FULL => {
4833+
kw @ Keyword::LEFT | kw @ Keyword::RIGHT => {
4834+
let _ = self.next_token();
4835+
let join_type = self.parse_one_of_keywords(&[
4836+
Keyword::OUTER,
4837+
Keyword::SEMI,
4838+
Keyword::ANTI,
4839+
Keyword::JOIN,
4840+
]);
4841+
match join_type {
4842+
Some(Keyword::OUTER) => {
4843+
self.expect_keyword(Keyword::JOIN)?;
4844+
match kw {
4845+
Keyword::LEFT => JoinOperator::LeftOuter,
4846+
Keyword::RIGHT => JoinOperator::RightOuter,
4847+
_ => unreachable!(),
4848+
}
4849+
}
4850+
Some(Keyword::SEMI) => {
4851+
self.expect_keyword(Keyword::JOIN)?;
4852+
match kw {
4853+
Keyword::LEFT => JoinOperator::LeftSemi,
4854+
Keyword::RIGHT => JoinOperator::RightSemi,
4855+
_ => unreachable!(),
4856+
}
4857+
}
4858+
Some(Keyword::ANTI) => {
4859+
self.expect_keyword(Keyword::JOIN)?;
4860+
match kw {
4861+
Keyword::LEFT => JoinOperator::LeftAnti,
4862+
Keyword::RIGHT => JoinOperator::RightAnti,
4863+
_ => unreachable!(),
4864+
}
4865+
}
4866+
Some(Keyword::JOIN) => match kw {
4867+
Keyword::LEFT => JoinOperator::LeftOuter,
4868+
Keyword::RIGHT => JoinOperator::RightOuter,
4869+
_ => unreachable!(),
4870+
},
4871+
_ => {
4872+
return Err(ParserError::ParserError(format!(
4873+
"expected OUTER, SEMI, ANTI or JOIN after {:?}",
4874+
kw
4875+
)))
4876+
}
4877+
}
4878+
}
4879+
Keyword::FULL => {
48344880
let _ = self.next_token();
48354881
let _ = self.parse_keyword(Keyword::OUTER);
48364882
self.expect_keyword(Keyword::JOIN)?;
4837-
match kw {
4838-
Keyword::LEFT => JoinOperator::LeftOuter,
4839-
Keyword::RIGHT => JoinOperator::RightOuter,
4840-
Keyword::FULL => JoinOperator::FullOuter,
4841-
_ => unreachable!(),
4842-
}
4883+
JoinOperator::FullOuter
48434884
}
48444885
Keyword::OUTER => {
48454886
return self.expected("LEFT, RIGHT, or FULL", self.peek_token());

tests/sqlparser_common.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,6 +3868,22 @@ fn parse_joins_on() {
38683868
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2").from).joins,
38693869
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
38703870
);
3871+
assert_eq!(
3872+
only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 ON c1 = c2").from).joins,
3873+
vec![join_with_constraint("t2", None, JoinOperator::LeftSemi)]
3874+
);
3875+
assert_eq!(
3876+
only(&verified_only_select("SELECT * FROM t1 RIGHT SEMI JOIN t2 ON c1 = c2").from).joins,
3877+
vec![join_with_constraint("t2", None, JoinOperator::RightSemi)]
3878+
);
3879+
assert_eq!(
3880+
only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 ON c1 = c2").from).joins,
3881+
vec![join_with_constraint("t2", None, JoinOperator::LeftAnti)]
3882+
);
3883+
assert_eq!(
3884+
only(&verified_only_select("SELECT * FROM t1 RIGHT ANTI JOIN t2 ON c1 = c2").from).joins,
3885+
vec![join_with_constraint("t2", None, JoinOperator::RightAnti)]
3886+
);
38713887
assert_eq!(
38723888
only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 ON c1 = c2").from).joins,
38733889
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
@@ -3917,6 +3933,22 @@ fn parse_joins_using() {
39173933
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)").from).joins,
39183934
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
39193935
);
3936+
assert_eq!(
3937+
only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 USING(c1)").from).joins,
3938+
vec![join_with_constraint("t2", None, JoinOperator::LeftSemi)]
3939+
);
3940+
assert_eq!(
3941+
only(&verified_only_select("SELECT * FROM t1 RIGHT SEMI JOIN t2 USING(c1)").from).joins,
3942+
vec![join_with_constraint("t2", None, JoinOperator::RightSemi)]
3943+
);
3944+
assert_eq!(
3945+
only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 USING(c1)").from).joins,
3946+
vec![join_with_constraint("t2", None, JoinOperator::LeftAnti)]
3947+
);
3948+
assert_eq!(
3949+
only(&verified_only_select("SELECT * FROM t1 RIGHT ANTI JOIN t2 USING(c1)").from).joins,
3950+
vec![join_with_constraint("t2", None, JoinOperator::RightAnti)]
3951+
);
39203952
assert_eq!(
39213953
only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 USING(c1)").from).joins,
39223954
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]

0 commit comments

Comments
 (0)