Add support for the Snowflake MINUS set operator (#1652)

This commit is contained in:
Yoav Cohen 2025-01-10 00:52:09 +01:00 committed by GitHub
parent 4fdf5e1b30
commit 5a761dd6db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 19 additions and 3 deletions

View file

@ -208,6 +208,7 @@ pub enum SetOperator {
Union, Union,
Except, Except,
Intersect, Intersect,
Minus,
} }
impl fmt::Display for SetOperator { impl fmt::Display for SetOperator {
@ -216,6 +217,7 @@ impl fmt::Display for SetOperator {
SetOperator::Union => "UNION", SetOperator::Union => "UNION",
SetOperator::Except => "EXCEPT", SetOperator::Except => "EXCEPT",
SetOperator::Intersect => "INTERSECT", SetOperator::Intersect => "INTERSECT",
SetOperator::Minus => "MINUS",
}) })
} }
} }

View file

@ -503,6 +503,7 @@ define_keywords!(
MILLISECOND, MILLISECOND,
MILLISECONDS, MILLISECONDS,
MIN, MIN,
MINUS,
MINUTE, MINUTE,
MINUTES, MINUTES,
MINVALUE, MINVALUE,
@ -921,6 +922,7 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
Keyword::UNION, Keyword::UNION,
Keyword::EXCEPT, Keyword::EXCEPT,
Keyword::INTERSECT, Keyword::INTERSECT,
Keyword::MINUS,
// Reserved only as a table alias in the `FROM`/`JOIN` clauses: // Reserved only as a table alias in the `FROM`/`JOIN` clauses:
Keyword::ON, Keyword::ON,
Keyword::JOIN, Keyword::JOIN,
@ -984,6 +986,7 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
Keyword::UNION, Keyword::UNION,
Keyword::EXCEPT, Keyword::EXCEPT,
Keyword::INTERSECT, Keyword::INTERSECT,
Keyword::MINUS,
Keyword::CLUSTER, Keyword::CLUSTER,
Keyword::DISTRIBUTE, Keyword::DISTRIBUTE,
Keyword::RETURNING, Keyword::RETURNING,

View file

@ -9942,7 +9942,9 @@ impl<'a> Parser<'a> {
let op = self.parse_set_operator(&self.peek_token().token); let op = self.parse_set_operator(&self.peek_token().token);
let next_precedence = match op { let next_precedence = match op {
// UNION and EXCEPT have the same binding power and evaluate left-to-right // UNION and EXCEPT have the same binding power and evaluate left-to-right
Some(SetOperator::Union) | Some(SetOperator::Except) => 10, Some(SetOperator::Union) | Some(SetOperator::Except) | Some(SetOperator::Minus) => {
10
}
// INTERSECT has higher precedence than UNION/EXCEPT // INTERSECT has higher precedence than UNION/EXCEPT
Some(SetOperator::Intersect) => 20, Some(SetOperator::Intersect) => 20,
// Unexpected token or EOF => stop parsing the query body // Unexpected token or EOF => stop parsing the query body
@ -9969,13 +9971,19 @@ impl<'a> Parser<'a> {
Token::Word(w) if w.keyword == Keyword::UNION => Some(SetOperator::Union), Token::Word(w) if w.keyword == Keyword::UNION => Some(SetOperator::Union),
Token::Word(w) if w.keyword == Keyword::EXCEPT => Some(SetOperator::Except), Token::Word(w) if w.keyword == Keyword::EXCEPT => Some(SetOperator::Except),
Token::Word(w) if w.keyword == Keyword::INTERSECT => Some(SetOperator::Intersect), Token::Word(w) if w.keyword == Keyword::INTERSECT => Some(SetOperator::Intersect),
Token::Word(w) if w.keyword == Keyword::MINUS => Some(SetOperator::Minus),
_ => None, _ => None,
} }
} }
pub fn parse_set_quantifier(&mut self, op: &Option<SetOperator>) -> SetQuantifier { pub fn parse_set_quantifier(&mut self, op: &Option<SetOperator>) -> SetQuantifier {
match op { match op {
Some(SetOperator::Except | SetOperator::Intersect | SetOperator::Union) => { Some(
SetOperator::Except
| SetOperator::Intersect
| SetOperator::Union
| SetOperator::Minus,
) => {
if self.parse_keywords(&[Keyword::DISTINCT, Keyword::BY, Keyword::NAME]) { if self.parse_keywords(&[Keyword::DISTINCT, Keyword::BY, Keyword::NAME]) {
SetQuantifier::DistinctByName SetQuantifier::DistinctByName
} else if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { } else if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) {

View file

@ -6860,7 +6860,7 @@ fn parse_derived_tables() {
} }
#[test] #[test]
fn parse_union_except_intersect() { fn parse_union_except_intersect_minus() {
// TODO: add assertions // TODO: add assertions
verified_stmt("SELECT 1 UNION SELECT 2"); verified_stmt("SELECT 1 UNION SELECT 2");
verified_stmt("SELECT 1 UNION ALL SELECT 2"); verified_stmt("SELECT 1 UNION ALL SELECT 2");
@ -6868,6 +6868,9 @@ fn parse_union_except_intersect() {
verified_stmt("SELECT 1 EXCEPT SELECT 2"); verified_stmt("SELECT 1 EXCEPT SELECT 2");
verified_stmt("SELECT 1 EXCEPT ALL SELECT 2"); verified_stmt("SELECT 1 EXCEPT ALL SELECT 2");
verified_stmt("SELECT 1 EXCEPT DISTINCT SELECT 1"); verified_stmt("SELECT 1 EXCEPT DISTINCT SELECT 1");
verified_stmt("SELECT 1 MINUS SELECT 2");
verified_stmt("SELECT 1 MINUS ALL SELECT 2");
verified_stmt("SELECT 1 MINUS DISTINCT SELECT 1");
verified_stmt("SELECT 1 INTERSECT SELECT 2"); verified_stmt("SELECT 1 INTERSECT SELECT 2");
verified_stmt("SELECT 1 INTERSECT ALL SELECT 2"); verified_stmt("SELECT 1 INTERSECT ALL SELECT 2");
verified_stmt("SELECT 1 INTERSECT DISTINCT SELECT 1"); verified_stmt("SELECT 1 INTERSECT DISTINCT SELECT 1");