Support SEMI/ANTI JOIN syntax (#723)

This commit is contained in:
mingmwang 2022-12-01 01:57:45 +08:00 committed by GitHub
parent 1f22ea74ae
commit 96bca38fae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 118 additions and 7 deletions

View file

@ -611,6 +611,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),
}
@ -625,6 +653,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)

View file

@ -77,6 +77,7 @@ define_keywords!(
ALTER,
ANALYZE,
AND,
ANTI,
ANY,
APPLY,
ARCHIVE,
@ -491,6 +492,7 @@ define_keywords!(
SEARCH,
SECOND,
SELECT,
SEMI,
SENSITIVE,
SEQUENCE,
SEQUENCEFILE,

View file

@ -4830,16 +4830,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!(),
}
}
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());

View file

@ -3868,6 +3868,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)]
@ -3917,6 +3933,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)]