From 2c14f6ff60c0c0f1451fbb2a0d4451f52c3f221b Mon Sep 17 00:00:00 2001 From: frolovdev Date: Mon, 13 Jun 2022 17:52:28 +0400 Subject: [PATCH 1/2] add more consistency in ast --- src/ast/mod.rs | 13 +++++++++---- src/parser.rs | 33 ++++++++++++++++++++++++++------- tests/sqlparser_common.rs | 11 +++++++---- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 35420e895..c316aa6b3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -337,9 +337,9 @@ pub enum Expr { results: Vec, else_result: Option>, }, - /// An exists expression `EXISTS(SELECT ...)`, used in expressions like - /// `WHERE EXISTS (SELECT ...)`. - Exists(Box), + /// An exists expression `[ NOT ] EXISTS(SELECT ...)`, used in expressions like + /// `WHERE [ NOT ] EXISTS (SELECT ...)`. + Exists { subquery: Box, negated: bool }, /// A parenthesized subquery `(SELECT ...)`, used in expression like /// `SELECT (subquery) AS x` or `WHERE (subquery) = x` Subquery(Box), @@ -466,7 +466,12 @@ impl fmt::Display for Expr { } write!(f, " END") } - Expr::Exists(s) => write!(f, "EXISTS ({})", s), + Expr::Exists { subquery, negated } => write!( + f, + "{}EXISTS ({})", + if *negated { "NOT " } else { "" }, + subquery + ), Expr::Subquery(s) => write!(f, "({})", s), Expr::ListAgg(listagg) => write!(f, "{}", listagg), Expr::GroupingSets(sets) => { diff --git a/src/parser.rs b/src/parser.rs index 693998cec..4d3ae3a27 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -426,7 +426,7 @@ impl<'a> Parser<'a> { Keyword::CASE => self.parse_case_expr(), Keyword::CAST => self.parse_cast_expr(), Keyword::TRY_CAST => self.parse_try_cast_expr(), - Keyword::EXISTS => self.parse_exists_expr(), + Keyword::EXISTS => self.parse_exists_expr(false), Keyword::EXTRACT => self.parse_extract_expr(), Keyword::POSITION => self.parse_position_expr(), Keyword::SUBSTRING => self.parse_substring_expr(), @@ -438,10 +438,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LBracket)?; self.parse_array_expr(true) } - Keyword::NOT => Ok(Expr::UnaryOp { - op: UnaryOperator::Not, - expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), - }), + Keyword::NOT => self.parse_not(), // Here `w` is a word, check if it's a part of a multi-part // identifier, a function call, or a simple identifier: _ => match self.peek_token() { @@ -783,9 +780,12 @@ impl<'a> Parser<'a> { } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. - pub fn parse_exists_expr(&mut self) -> Result { + pub fn parse_exists_expr(&mut self, negated: bool) -> Result { self.expect_token(&Token::LParen)?; - let exists_node = Expr::Exists(Box::new(self.parse_query()?)); + let exists_node = Expr::Exists { + negated, + subquery: Box::new(self.parse_query()?), + }; self.expect_token(&Token::RParen)?; Ok(exists_node) } @@ -984,6 +984,25 @@ impl<'a> Parser<'a> { } } + pub fn parse_not(&mut self) -> Result { + match self.peek_token() { + Token::Word(w) => match w.keyword { + Keyword::EXISTS => { + let negated = self.parse_keyword(Keyword::EXISTS); + self.parse_exists_expr(negated) + } + _ => Ok(Expr::UnaryOp { + op: UnaryOperator::Not, + expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), + }), + }, + _ => Ok(Expr::UnaryOp { + op: UnaryOperator::Not, + expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), + }), + } + } + /// Parse an INTERVAL literal. /// /// Some syntactically valid intervals: diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index b598b008e..1eb10babb 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3591,16 +3591,19 @@ fn parse_exists_subquery() { let sql = "SELECT * FROM t WHERE EXISTS (SELECT 1)"; let select = verified_only_select(sql); assert_eq!( - Expr::Exists(Box::new(expected_inner.clone())), + Expr::Exists { + negated: false, + subquery: Box::new(expected_inner.clone()) + }, select.selection.unwrap(), ); let sql = "SELECT * FROM t WHERE NOT EXISTS (SELECT 1)"; let select = verified_only_select(sql); assert_eq!( - Expr::UnaryOp { - op: UnaryOperator::Not, - expr: Box::new(Expr::Exists(Box::new(expected_inner))), + Expr::Exists { + negated: true, + subquery: Box::new(expected_inner) }, select.selection.unwrap(), ); From a2900d2058d4a358f747e5d8625c9ec558b04217 Mon Sep 17 00:00:00 2001 From: frolovdev Date: Tue, 14 Jun 2022 00:50:20 +0400 Subject: [PATCH 2/2] refactor styling --- src/parser.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 4d3ae3a27..df66a8f57 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -988,7 +988,8 @@ impl<'a> Parser<'a> { match self.peek_token() { Token::Word(w) => match w.keyword { Keyword::EXISTS => { - let negated = self.parse_keyword(Keyword::EXISTS); + let negated = true; + let _ = self.parse_keyword(Keyword::EXISTS); self.parse_exists_expr(negated) } _ => Ok(Expr::UnaryOp {