Skip to content

SUPPORT SEMI/ANTI JOIN syntax #723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,34 @@ impl fmt::Display for Join {
suffix(constraint)
),
JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
JoinOperator::LeftSemi(constraint) => write!(
f,
" {}LEFT SEMI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::RightSemi(constraint) => write!(
f,
" {}RIGHT SEMI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::LeftAnti(constraint) => write!(
f,
" {}LEFT ANTI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::RightAnti(constraint) => write!(
f,
" {}RIGHT ANTI JOIN {}{}",
prefix(constraint),
self.relation,
suffix(constraint)
),
JoinOperator::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
}
Expand All @@ -573,6 +601,14 @@ pub enum JoinOperator {
RightOuter(JoinConstraint),
FullOuter(JoinConstraint),
CrossJoin,
/// LEFT SEMI (non-standard)
LeftSemi(JoinConstraint),
/// RIGHT SEMI (non-standard)
RightSemi(JoinConstraint),
/// LEFT ANTI (non-standard)
LeftAnti(JoinConstraint),
/// RIGHT ANTI (non-standard)
RightAnti(JoinConstraint),
/// CROSS APPLY (non-standard)
CrossApply,
/// OUTER APPLY (non-standard)
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ define_keywords!(
ALTER,
ANALYZE,
AND,
ANTI,
ANY,
APPLY,
ARCHIVE,
Expand Down Expand Up @@ -486,6 +487,7 @@ define_keywords!(
SEARCH,
SECOND,
SELECT,
SEMI,
SENSITIVE,
SEQUENCE,
SEQUENCEFILE,
Expand Down
55 changes: 48 additions & 7 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4746,16 +4746,57 @@ impl<'a> Parser<'a> {
self.expect_keyword(Keyword::JOIN)?;
JoinOperator::Inner
}
kw @ Keyword::LEFT | kw @ Keyword::RIGHT | kw @ Keyword::FULL => {
kw @ Keyword::LEFT | kw @ Keyword::RIGHT => {
let _ = self.next_token();
let join_type = self.parse_one_of_keywords(&[
Keyword::OUTER,
Keyword::SEMI,
Keyword::ANTI,
Keyword::JOIN,
]);
match join_type {
Some(Keyword::OUTER) => {
self.expect_keyword(Keyword::JOIN)?;
match kw {
Keyword::LEFT => JoinOperator::LeftOuter,
Keyword::RIGHT => JoinOperator::RightOuter,
_ => unreachable!(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mingmwang for this and all the next unreachable!(). Even if this is really unreachable, please try to add an error here, or handle it in a different way. Being unsafe is not really good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the unreachable!() is used in other places in the code base. I just follow the same
code style.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it can have only two values, we can also make it a boolean value. I plan to merge this PR and then I will submit a follow on to remove the use of unreachable!

}
}
Some(Keyword::SEMI) => {
self.expect_keyword(Keyword::JOIN)?;
match kw {
Keyword::LEFT => JoinOperator::LeftSemi,
Keyword::RIGHT => JoinOperator::RightSemi,
_ => unreachable!(),
}
}
Some(Keyword::ANTI) => {
self.expect_keyword(Keyword::JOIN)?;
match kw {
Keyword::LEFT => JoinOperator::LeftAnti,
Keyword::RIGHT => JoinOperator::RightAnti,
_ => unreachable!(),
}
}
Some(Keyword::JOIN) => match kw {
Keyword::LEFT => JoinOperator::LeftOuter,
Keyword::RIGHT => JoinOperator::RightOuter,
_ => unreachable!(),
},
_ => {
return Err(ParserError::ParserError(format!(
"expected OUTER, SEMI, ANTI or JOIN after {:?}",
kw
)))
}
}
}
Keyword::FULL => {
let _ = self.next_token();
let _ = self.parse_keyword(Keyword::OUTER);
self.expect_keyword(Keyword::JOIN)?;
match kw {
Keyword::LEFT => JoinOperator::LeftOuter,
Keyword::RIGHT => JoinOperator::RightOuter,
Keyword::FULL => JoinOperator::FullOuter,
_ => unreachable!(),
}
JoinOperator::FullOuter
}
Keyword::OUTER => {
return self.expected("LEFT, RIGHT, or FULL", self.peek_token());
Expand Down
32 changes: 32 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3686,6 +3686,22 @@ fn parse_joins_on() {
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 ON c1 = c2").from).joins,
vec![join_with_constraint("t2", None, 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)]
);
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)]
);
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)]
);
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)]
);
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)]
Expand Down Expand Up @@ -3735,6 +3751,22 @@ fn parse_joins_using() {
only(&verified_only_select("SELECT * FROM t1 RIGHT JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightOuter)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::LeftSemi)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT SEMI JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightSemi)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::LeftAnti)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 RIGHT ANTI JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::RightAnti)]
);
assert_eq!(
only(&verified_only_select("SELECT * FROM t1 FULL JOIN t2 USING(c1)").from).joins,
vec![join_with_constraint("t2", None, JoinOperator::FullOuter)]
Expand Down