diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index f73219e8b..f5c50d0d8 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -98,12 +98,12 @@ impl Dialect for MySqlDialect { &self, parser: &mut crate::parser::Parser, expr: &crate::ast::Expr, - _precedence: u8, + precedence: u8, ) -> Option> { // Parse DIV as an operator if parser.parse_keyword(Keyword::DIV) { let left = Box::new(expr.clone()); - let right = Box::new(match parser.parse_expr() { + let right = Box::new(match parser.parse_subexpr(precedence) { Ok(expr) => expr, Err(e) => return Some(Err(e)), }); diff --git a/src/dialect/spark.rs b/src/dialect/spark.rs index 2b32c6c23..024aec715 100644 --- a/src/dialect/spark.rs +++ b/src/dialect/spark.rs @@ -137,11 +137,11 @@ impl Dialect for SparkSqlDialect { &self, parser: &mut Parser, expr: &Expr, - _precedence: u8, + precedence: u8, ) -> Option> { if parser.parse_keyword(Keyword::DIV) { let left = Box::new(expr.clone()); - let right = Box::new(match parser.parse_expr() { + let right = Box::new(match parser.parse_subexpr(precedence) { Ok(expr) => expr, Err(e) => return Some(Err(e)), }); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index a8d7f6676..a083b5507 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -3759,6 +3759,62 @@ fn parse_div_infix_propagates_parse_error() { assert_matches!(err, ParserError::ParserError(_)); } +#[test] +fn parse_div_precedence() { + let div = |left: Expr, right: Expr| Expr::BinaryOp { + left: Box::new(left), + op: BinaryOperator::MyIntegerDivide, + right: Box::new(right), + }; + let num = |n: &str| Expr::value(number(n)); + + // `DIV` shares the precedence of `*` and `/`, so `+` must end up at the root. + assert_eq!( + Expr::BinaryOp { + left: Box::new(div(num("7"), num("2"))), + op: BinaryOperator::Plus, + right: Box::new(num("1")), + }, + mysql().verified_expr("7 DIV 2 + 1") + ); + + // Equal precedence resolves left-associatively, both against `*` and against itself. + assert_eq!( + Expr::BinaryOp { + left: Box::new(div(num("9"), num("3"))), + op: BinaryOperator::Multiply, + right: Box::new(num("3")), + }, + mysql().verified_expr("9 DIV 3 * 3") + ); + assert_eq!( + div(div(num("10"), num("5")), num("2")), + mysql().verified_expr("10 DIV 5 DIV 2") + ); + + assert_eq!( + Expr::BinaryOp { + left: Box::new(div(Expr::Identifier(Ident::new("a")), num("2"))), + op: BinaryOperator::Eq, + right: Box::new(num("1")), + }, + mysql().verified_expr("a DIV 2 = 1") + ); + + // Explicit parentheses still push the whole expression into the right operand. + assert_eq!( + div( + num("7"), + Expr::Nested(Box::new(Expr::BinaryOp { + left: Box::new(num("2")), + op: BinaryOperator::Plus, + right: Box::new(num("1")), + })) + ), + mysql().verified_expr("7 DIV (2 + 1)") + ); +} + #[test] fn parse_drop_temporary_table() { let sql = "DROP TEMPORARY TABLE foo"; diff --git a/tests/sqlparser_spark.rs b/tests/sqlparser_spark.rs index 3ec46c107..0f292f0e9 100644 --- a/tests/sqlparser_spark.rs +++ b/tests/sqlparser_spark.rs @@ -171,6 +171,36 @@ fn test_div_literal() { spark().one_statement_parses_to("SELECT 10 div 3", "SELECT 10 DIV 3"); } +#[test] +fn test_div_precedence() { + let div = |left: Expr, right: Expr| Expr::BinaryOp { + left: Box::new(left), + op: BinaryOperator::MyIntegerDivide, + right: Box::new(right), + }; + let num = |n: &str| Expr::value(number(n)); + + // `DIV` shares the precedence of `*` and `/`, so `+` must end up at the root. + assert_eq!( + Expr::BinaryOp { + left: Box::new(div(num("7"), num("2"))), + op: BinaryOperator::Plus, + right: Box::new(num("1")), + }, + spark().verified_expr("7 DIV 2 + 1") + ); + + // Equal precedence resolves left-associatively. + assert_eq!( + Expr::BinaryOp { + left: Box::new(div(num("9"), num("3"))), + op: BinaryOperator::Multiply, + right: Box::new(num("3")), + }, + spark().verified_expr("9 DIV 3 * 3") + ); +} + // -------------------------------- // Struct support // --------------------------------