From db289e0e78fd630f7af665759fb54e8eb649c75b Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 10 Nov 2022 12:28:44 -0800 Subject: [PATCH 1/6] initial fix --- src/parser.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 0753d263e..4becf9ecb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -363,6 +363,36 @@ impl<'a> Parser<'a> { Ok(expr) } + pub fn parse_interval_expr(&mut self) -> Result { + let precedence = 0; + let mut expr = self.parse_prefix()?; + + loop { + let next_precedence = self.get_next_interval_precedence()?; + + if precedence >= next_precedence { + break; + } + + expr = self.parse_infix(expr, next_precedence)?; + } + + Ok(expr) + } + + /// Get the precedence of the next token + /// With AND, OR, and XOR + pub fn get_next_interval_precedence(&self) -> Result { + let token = self.peek_token(); + + match token { + Token::Word(w) if w.keyword == Keyword::AND => Ok(0), + Token::Word(w) if w.keyword == Keyword::OR => Ok(0), + Token::Word(w) if w.keyword == Keyword::XOR => Ok(0), + _ => self.get_next_precedence() + } + } + pub fn parse_assert(&mut self) -> Result { let condition = self.parse_expr()?; let message = if self.parse_keyword(Keyword::AS) { @@ -1151,7 +1181,7 @@ impl<'a> Parser<'a> { // The first token in an interval is a string literal which specifies // the duration of the interval. - let value = self.parse_expr()?; + let value = self.parse_interval_expr()?; // Following the string literal is a qualifier which indicates the units // of the duration specified in the string literal. From 4ff1019f2b96269b234fd8bca8eb129c6eb08279 Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 10 Nov 2022 12:37:40 -0800 Subject: [PATCH 2/6] add comma --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 4becf9ecb..9024b2c10 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -389,7 +389,7 @@ impl<'a> Parser<'a> { Token::Word(w) if w.keyword == Keyword::AND => Ok(0), Token::Word(w) if w.keyword == Keyword::OR => Ok(0), Token::Word(w) if w.keyword == Keyword::XOR => Ok(0), - _ => self.get_next_precedence() + _ => self.get_next_precedence(), } } From 4e6242f786f510649055135d06eff6a9aaadf832 Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 10 Nov 2022 16:15:41 -0800 Subject: [PATCH 3/6] add test --- tests/sqlparser_common.rs | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 4efb8cc7c..e1e2efbe7 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3120,6 +3120,70 @@ fn parse_interval() { ); } +#[test] +fn parse_interval_and_or_xor() { + let sql = "SELECT col FROM test \ + WHERE d3_date > d1_date + INTERVAL '5 days' \ + AND d2_date > d1_date + INTERVAL '3 days'"; + + let actual_ast = Parser::parse_sql(&GenericDialect {}, sql).unwrap(); + + let expected_ast = vec![Statement::Query(Box::new(Query { + with: None, + body: Box::new(SetExpr::Select(Box::new(Select { + distinct: false, + top: None, + projection: vec![UnnamedExpr(Expr::Identifier(Ident { value: "col".to_string(), quote_style: None }))], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident { value: "test".to_string(), quote_style: None }]), + alias: None, + args: None, + with_hints: vec![] }, + joins: vec![] }], + lateral_views: vec![], + selection: Some(Expr::BinaryOp { + left: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident { value: "d3_date".to_string(), quote_style: None })), + op: BinaryOperator::Gt, + right: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None })), + op: BinaryOperator::Plus, + right: Box::new(Expr::Interval { + value: Box::new(Expr::Value(Value::SingleQuotedString("5 days".to_string()))), // Token ? + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None }) }) }), + op: BinaryOperator::And, + right: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident { value: "d2_date".to_string(), quote_style: None })), + op: BinaryOperator::Gt, + right: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None })), + op: BinaryOperator::Plus, + right: Box::new(Expr::Interval { + value: Box::new(Expr::Value(Value::SingleQuotedString("3 days".to_string()))), // Token ? + leading_field: None, + leading_precision: None, + last_field: None, + fractional_seconds_precision: None }) }) }) }), + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + qualify: None }))), + order_by: vec![], + limit: None, + offset: None, + fetch: None, + lock: None }))]; + + assert_eq!(actual_ast, expected_ast); +} + #[test] fn parse_at_timezone() { let zero = Expr::Value(number("0")); From 53696dab94b00d3a2b518aae863e9ced80482f43 Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 10 Nov 2022 16:22:23 -0800 Subject: [PATCH 4/6] style --- tests/sqlparser_common.rs | 61 ++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e1e2efbe7..dec951740 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3133,53 +3133,86 @@ fn parse_interval_and_or_xor() { body: Box::new(SetExpr::Select(Box::new(Select { distinct: false, top: None, - projection: vec![UnnamedExpr(Expr::Identifier(Ident { value: "col".to_string(), quote_style: None }))], + projection: vec![UnnamedExpr(Expr::Identifier(Ident { + value: "col".to_string(), + quote_style: None, + }))], into: None, from: vec![TableWithJoins { relation: TableFactor::Table { - name: ObjectName(vec![Ident { value: "test".to_string(), quote_style: None }]), + name: ObjectName(vec![Ident { + value: "test".to_string(), + quote_style: None, + }]), alias: None, args: None, - with_hints: vec![] }, - joins: vec![] }], + with_hints: vec![], + }, + joins: vec![], + }], lateral_views: vec![], selection: Some(Expr::BinaryOp { left: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident { value: "d3_date".to_string(), quote_style: None })), + left: Box::new(Expr::Identifier(Ident { + value: "d3_date".to_string(), + quote_style: None, + })), op: BinaryOperator::Gt, right: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None })), + left: Box::new(Expr::Identifier(Ident { + value: "d1_date".to_string(), + quote_style: None, + })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval { - value: Box::new(Expr::Value(Value::SingleQuotedString("5 days".to_string()))), // Token ? + value: Box::new(Expr::Value(Value::SingleQuotedString( + "5 days".to_string(), + ))), leading_field: None, leading_precision: None, last_field: None, - fractional_seconds_precision: None }) }) }), + fractional_seconds_precision: None, + }), + }), + }), op: BinaryOperator::And, right: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident { value: "d2_date".to_string(), quote_style: None })), + left: Box::new(Expr::Identifier(Ident { + value: "d2_date".to_string(), + quote_style: None, + })), op: BinaryOperator::Gt, right: Box::new(Expr::BinaryOp { - left: Box::new(Expr::Identifier(Ident { value: "d1_date".to_string(), quote_style: None })), + left: Box::new(Expr::Identifier(Ident { + value: "d1_date".to_string(), + quote_style: None, + })), op: BinaryOperator::Plus, right: Box::new(Expr::Interval { - value: Box::new(Expr::Value(Value::SingleQuotedString("3 days".to_string()))), // Token ? + value: Box::new(Expr::Value(Value::SingleQuotedString( + "3 days".to_string(), + ))), leading_field: None, leading_precision: None, last_field: None, - fractional_seconds_precision: None }) }) }) }), + fractional_seconds_precision: None, + }), + }), + }), + }), group_by: vec![], cluster_by: vec![], distribute_by: vec![], sort_by: vec![], having: None, - qualify: None }))), + qualify: None, + }))), order_by: vec![], limit: None, offset: None, fetch: None, - lock: None }))]; + lock: None, + }))]; assert_eq!(actual_ast, expected_ast); } From 0e7223d616e152ccfd92bb4565e8617f96be3ad1 Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 17 Nov 2022 12:07:27 -0800 Subject: [PATCH 5/6] add more tests --- tests/sqlparser_common.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index dec951740..b9c5177d1 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3215,6 +3215,21 @@ fn parse_interval_and_or_xor() { }))]; assert_eq!(actual_ast, expected_ast); + + verified_stmt("SELECT col FROM test \ + WHERE d3_date > d1_date + INTERVAL '5 days' \ + AND d2_date > d1_date + INTERVAL '3 days'" + ); + + verified_stmt("SELECT col FROM test \ + WHERE d3_date > d1_date + INTERVAL '5 days' \ + OR d2_date > d1_date + INTERVAL '3 days'" + ); + + verified_stmt("SELECT col FROM test \ + WHERE d3_date > d1_date + INTERVAL '5 days' \ + XOR d2_date > d1_date + INTERVAL '3 days'" + ); } #[test] From 27e21d6d2250070062deb20ac9bb85d100d6ddfa Mon Sep 17 00:00:00 2001 From: Sarah Yurick Date: Thu, 17 Nov 2022 12:10:17 -0800 Subject: [PATCH 6/6] codestyle fix --- tests/sqlparser_common.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index b9c5177d1..c5e260e37 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3216,19 +3216,22 @@ fn parse_interval_and_or_xor() { assert_eq!(actual_ast, expected_ast); - verified_stmt("SELECT col FROM test \ + verified_stmt( + "SELECT col FROM test \ WHERE d3_date > d1_date + INTERVAL '5 days' \ - AND d2_date > d1_date + INTERVAL '3 days'" + AND d2_date > d1_date + INTERVAL '3 days'", ); - verified_stmt("SELECT col FROM test \ + verified_stmt( + "SELECT col FROM test \ WHERE d3_date > d1_date + INTERVAL '5 days' \ - OR d2_date > d1_date + INTERVAL '3 days'" + OR d2_date > d1_date + INTERVAL '3 days'", ); - verified_stmt("SELECT col FROM test \ + verified_stmt( + "SELECT col FROM test \ WHERE d3_date > d1_date + INTERVAL '5 days' \ - XOR d2_date > d1_date + INTERVAL '3 days'" + XOR d2_date > d1_date + INTERVAL '3 days'", ); }