From fb7dd7797d7e4df72ae05b841e20bdaddc227e9a Mon Sep 17 00:00:00 2001 From: Jamie Brandon Date: Thu, 12 Sep 2019 18:17:28 +0100 Subject: [PATCH] Parse ALL/SOME/ANY --- src/ast/mod.rs | 15 ++++++++++++ src/ast/visit_macro.rs | 18 ++++++++++++++ src/parser.rs | 37 +++++++++++++++++++++++++---- tests/sqlparser_common.rs | 49 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 5 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 79cc82e..356e0f4 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -192,6 +192,19 @@ pub enum Expr { /// A parenthesized subquery `(SELECT ...)`, used in expression like /// `SELECT (subquery) AS x` or `WHERE (subquery) = x` Subquery(Box), + /// ` ANY/SOME ()` + Any { + left: Box, + op: BinaryOperator, + right: Box, + some: bool, // just tracks which syntax was used + }, + /// ` ALL ()` + All { + left: Box, + op: BinaryOperator, + right: Box, + } } impl fmt::Display for Expr { @@ -267,6 +280,8 @@ impl fmt::Display for Expr { } Expr::Exists(s) => write!(f, "EXISTS ({})", s), Expr::Subquery(s) => write!(f, "({})", s), + Expr::Any{left, op, right, some} => write!(f, "{} {} {} ({})", left, op, if *some { "SOME" } else { "ANY" }, right), + Expr::All{left, op, right} => write!(f, "{} {} ALL ({})", left, op, right), } } } diff --git a/src/ast/visit_macro.rs b/src/ast/visit_macro.rs index d103f3e..bc70bb1 100644 --- a/src/ast/visit_macro.rs +++ b/src/ast/visit_macro.rs @@ -278,6 +278,14 @@ macro_rules! make_visitor { visit_subquery(self, subquery) } + fn visit_any(&mut self, left: &'ast $($mut)* Expr, op: &'ast $($mut)* BinaryOperator, right: &'ast $($mut)* Query) { + visit_any(self, left, op, right) + } + + fn visit_all(&mut self, left: &'ast $($mut)* Expr, op: &'ast $($mut)* BinaryOperator, right: &'ast $($mut)* Query) { + visit_all(self, left, op, right) + } + fn visit_insert( &mut self, table_name: &'ast $($mut)* ObjectName, @@ -888,6 +896,8 @@ macro_rules! make_visitor { ), Expr::Exists(query) => visitor.visit_exists(query), Expr::Subquery(query) => visitor.visit_subquery(query), + Expr::Any{left, op, right, some: _} => visitor.visit_any(left, op, right), + Expr::All{left, op, right} => visitor.visit_all(left, op, right), } } @@ -1089,6 +1099,14 @@ macro_rules! make_visitor { visitor.visit_query(subquery) } + pub fn visit_any<'ast, V: $name<'ast> + ?Sized>(visitor: &mut V, left: &'ast $($mut)* Expr, op: &'ast $($mut)* BinaryOperator, right: &'ast $($mut)* Query) { + visitor.visit_any(left, op, right) + } + + pub fn visit_all<'ast, V: $name<'ast> + ?Sized>(visitor: &mut V, left: &'ast $($mut)* Expr, op: &'ast $($mut)* BinaryOperator, right: &'ast $($mut)* Query) { + visitor.visit_all(left, op, right) + } + pub fn visit_insert<'ast, V: $name<'ast> + ?Sized>( visitor: &mut V, table_name: &'ast $($mut)* ObjectName, diff --git a/src/parser.rs b/src/parser.rs index 050de40..aed0c06 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -670,11 +670,38 @@ impl Parser { }; if let Some(op) = regular_binary_operator { - Ok(Expr::BinaryOp { - left: Box::new(expr), - op, - right: Box::new(self.parse_subexpr(precedence)?), - }) + let any = self.parse_keyword("ANY"); + let some = !any && self.parse_keyword("SOME"); + let all = !any && !some && self.parse_keyword("ALL"); + if any || some || all { + use BinaryOperator::*; + match op { + Eq | NotEq | Gt | GtEq | Lt | LtEq => (), + _ => self.expected("comparison operator", Some(tok))?, + } + self.expect_token(&Token::LParen)?; + let query = self.parse_query()?; + self.expect_token(&Token::RParen)?; + if any || some { + Ok(Expr::Any{ + left: Box::new(expr), + op, + right: Box::new(query), + some, + }) + } else { + Ok(Expr::All{ + left: Box::new(expr), + op, + right: Box::new(query), + })} + } else { + Ok(Expr::BinaryOp { + left: Box::new(expr), + op, + right: Box::new(self.parse_subexpr(precedence)?), + }) + } } else if let Token::Word(ref k) = tok { match k.keyword.as_ref() { "IS" => { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index bb31774..115e34d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -2415,6 +2415,55 @@ fn parse_scalar_subqueries() { }); } +#[test] +fn parse_any_some_all() { + let sql = "1 < ANY (SELECT 2)"; + assert_matches!(verified_expr(sql), Expr::Any { + op: BinaryOperator::Lt, .. + //left: box Expr, + //right: box Query { .. }, + }); + + let sql = "1 < SOME (SELECT 2)"; + assert_matches!(verified_expr(sql), Expr::Any { + op: BinaryOperator::Lt, .. + //left: box Expr, + //right: box Query { .. }, + }); + + let sql = "1 < ALL (SELECT 2)"; + assert_matches!(verified_expr(sql), Expr::All { + op: BinaryOperator::Lt, .. + //left: box Expr, + //right: box Query { .. }, + }); + + let res = parse_sql_statements("SELECT 1 WHERE 1 < ANY SELECT 2"); + assert_eq!( + ParserError::ParserError("Expected (, found: SELECT".to_string()), + res.unwrap_err() + ); + + let res = parse_sql_statements("SELECT 1 WHERE 1 < NONE (SELECT 2)"); + assert_eq!( + // TODO this is a pretty unhelpful error - it started parsing "NONE (SELECT" as applying the function NONE to the argument SELECT + ParserError::ParserError("Expected ), found: 2".to_string()), + res.unwrap_err() + ); + + let res = parse_sql_statements("SELECT 1 WHERE 1 < ANY (SELECT 2"); + assert_eq!( + ParserError::ParserError("Expected ), found: EOF".to_string()), + res.unwrap_err() + ); + + let res = parse_sql_statements("SELECT 1 WHERE 1 + ANY (SELECT 2)"); + assert_eq!( + ParserError::ParserError("Expected comparison operator, found: +".to_string()), + res.unwrap_err() + ); +} + #[test] fn parse_exists_subquery() { let expected_inner = verified_query("SELECT 1");