From de53ae8cfc6f25c2e63f2ddc628e5b099e4da309 Mon Sep 17 00:00:00 2001 From: Subhan Date: Sat, 16 Nov 2024 14:23:09 +0000 Subject: [PATCH 1/2] support ANTI join without LEFT/RIGHT --- src/ast/query.rs | 9 +++++++++ src/keywords.rs | 1 + src/parser/mod.rs | 5 +++++ tests/sqlparser_common.rs | 8 ++++++++ 4 files changed, 23 insertions(+) diff --git a/src/ast/query.rs b/src/ast/query.rs index 60ebe3765..7956a783e 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -1708,6 +1708,13 @@ impl fmt::Display for Join { self.relation, suffix(constraint) ), + JoinOperator::Anti(constraint) => write!( + f, + " {}ANTI JOIN {}{}", + prefix(constraint), + self.relation, + suffix(constraint) + ), JoinOperator::LeftAnti(constraint) => write!( f, " {}LEFT ANTI JOIN {}{}", @@ -1750,6 +1757,8 @@ pub enum JoinOperator { LeftSemi(JoinConstraint), /// RIGHT SEMI (non-standard) RightSemi(JoinConstraint), + /// ANTI (non-standard) + Anti(JoinConstraint), /// LEFT ANTI (non-standard) LeftAnti(JoinConstraint), /// RIGHT ANTI (non-standard) diff --git a/src/keywords.rs b/src/keywords.rs index 790268219..f1391a921 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -892,6 +892,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ Keyword::CLUSTER, Keyword::DISTRIBUTE, Keyword::GLOBAL, + Keyword::ANTI, // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) Keyword::OUTER, Keyword::SET, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a583112a7..d6333bf9e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -10025,6 +10025,11 @@ impl<'a> Parser<'a> { } } } + Keyword::ANTI => { + let _ = self.next_token(); // consume ANTI + self.expect_keyword(Keyword::JOIN)?; + JoinOperator::Anti + } Keyword::FULL => { let _ = self.next_token(); // consume FULL let _ = self.parse_keyword(Keyword::OUTER); // [ OUTER ] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 2ffb5f44b..3368028bc 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6031,6 +6031,10 @@ fn parse_joins_on() { JoinOperator::RightSemi )] ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 ANTI JOIN t2 ON c1 = c2").from).joins, + vec![join_with_constraint("t2", None, false, JoinOperator::Anti)] + ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 LEFT ANTI JOIN t2 ON c1 = c2").from).joins, vec![join_with_constraint( @@ -6125,6 +6129,10 @@ fn parse_joins_using() { 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 ANTI JOIN t2 USING(c1)").from).joins, + vec![join_with_constraint("t2", None, JoinOperator::Anti)] + ); 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)] From 2ae3a0c90902675232606947a73c95a958dc8704 Mon Sep 17 00:00:00 2001 From: Subhan Date: Sat, 16 Nov 2024 14:33:29 +0000 Subject: [PATCH 2/2] support SEMI join without LEFT/RIGHT --- src/ast/query.rs | 9 +++++++++ src/keywords.rs | 1 + src/parser/mod.rs | 5 +++++ tests/sqlparser_common.rs | 8 ++++++++ 4 files changed, 23 insertions(+) diff --git a/src/ast/query.rs b/src/ast/query.rs index 7956a783e..2160da0d0 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -1694,6 +1694,13 @@ impl fmt::Display for Join { suffix(constraint) ), JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation), + JoinOperator::Semi(constraint) => write!( + f, + " {}SEMI JOIN {}{}", + prefix(constraint), + self.relation, + suffix(constraint) + ), JoinOperator::LeftSemi(constraint) => write!( f, " {}LEFT SEMI JOIN {}{}", @@ -1753,6 +1760,8 @@ pub enum JoinOperator { RightOuter(JoinConstraint), FullOuter(JoinConstraint), CrossJoin, + /// SEMI (non-standard) + Semi(JoinConstraint), /// LEFT SEMI (non-standard) LeftSemi(JoinConstraint), /// RIGHT SEMI (non-standard) diff --git a/src/keywords.rs b/src/keywords.rs index f1391a921..fdf2bf35c 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -893,6 +893,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[ Keyword::DISTRIBUTE, Keyword::GLOBAL, Keyword::ANTI, + Keyword::SEMI, // for MSSQL-specific OUTER APPLY (seems reserved in most dialects) Keyword::OUTER, Keyword::SET, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d6333bf9e..1a094c147 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -10030,6 +10030,11 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::JOIN)?; JoinOperator::Anti } + Keyword::SEMI => { + let _ = self.next_token(); // consume SEMI + self.expect_keyword(Keyword::JOIN)?; + JoinOperator::Semi + } Keyword::FULL => { let _ = self.next_token(); // consume FULL let _ = self.parse_keyword(Keyword::OUTER); // [ OUTER ] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 3368028bc..77496781c 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -6013,6 +6013,10 @@ fn parse_joins_on() { JoinOperator::RightOuter )] ); + assert_eq!( + only(&verified_only_select("SELECT * FROM t1 SEMI JOIN t2 ON c1 = c2").from).joins, + vec![join_with_constraint("t2", None, false, JoinOperator::Semi)] + ); assert_eq!( only(&verified_only_select("SELECT * FROM t1 LEFT SEMI JOIN t2 ON c1 = c2").from).joins, vec![join_with_constraint( @@ -6121,6 +6125,10 @@ 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 SEMI JOIN t2 USING(c1)").from).joins, + vec![join_with_constraint("t2", None, JoinOperator::Semi)] + ); 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)]