diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index db3f5c0661..5cfff36965 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -151,7 +151,9 @@ pub use self::teradata::TeradataDialect; #[cfg(feature = "derive-dialect")] pub use sqlparser_derive::derive_dialect; -use crate::ast::{ColumnOption, DataType, Expr, GranteesType, Ident, ObjectNamePart, Statement}; +use crate::ast::{ + BinaryOperator, ColumnOption, DataType, Expr, GranteesType, Ident, ObjectNamePart, Statement, +}; pub use crate::keywords; use crate::keywords::Keyword; use crate::parser::{Parser, ParserError}; @@ -2295,6 +2297,26 @@ pub fn dialect_from_str(dialect_name: impl AsRef) -> Option Option> { + if !parser.parse_keyword(Keyword::DIV) { + return None; + } + Some( + parser + .parse_subexpr(precedence) + .map(|right| Expr::BinaryOp { + left: Box::new(expr.clone()), + op: BinaryOperator::MyIntegerDivide, + right: Box::new(right), + }), + ) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index 2fc135f84b..95bd27168e 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -15,11 +15,8 @@ // specific language governing permissions and limitations // under the License. -#[cfg(not(feature = "std"))] -use alloc::boxed::Box; - use crate::{ - ast::{BinaryOperator, Expr, LockTable, LockTableType, Statement}, + ast::{Expr, LockTable, LockTableType, Statement}, dialect::{BindPlaceholderStyle, Dialect, Precedence}, keywords::Keyword, parser::{Parser, ParserError}, @@ -115,25 +112,11 @@ impl Dialect for MySqlDialect { fn parse_infix( &self, - parser: &mut crate::parser::Parser, - expr: &crate::ast::Expr, + parser: &mut Parser, + expr: &Expr, 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_subexpr(precedence) { - Ok(expr) => expr, - Err(e) => return Some(Err(e)), - }); - Some(Ok(Expr::BinaryOp { - left, - op: BinaryOperator::MyIntegerDivide, - right, - })) - } else { - None - } + ) -> Option> { + super::parse_div_infix(parser, expr, precedence) } fn parse_statement(&self, parser: &mut Parser) -> Option> { diff --git a/src/dialect/spark.rs b/src/dialect/spark.rs index 19eb6ed184..c3f369cfbf 100644 --- a/src/dialect/spark.rs +++ b/src/dialect/spark.rs @@ -15,12 +15,8 @@ // specific language governing permissions and limitations // under the License. -#[cfg(not(feature = "std"))] -use alloc::boxed::Box; - -use crate::ast::{BinaryOperator, Expr}; +use crate::ast::Expr; use crate::dialect::Dialect; -use crate::keywords::Keyword; use crate::parser::{Parser, ParserError}; /// A [`Dialect`] for [Apache Spark SQL](https://spark.apache.org/docs/latest/sql-ref.html). @@ -155,19 +151,6 @@ impl Dialect for SparkSqlDialect { expr: &Expr, precedence: u8, ) -> Option> { - if parser.parse_keyword(Keyword::DIV) { - let left = Box::new(expr.clone()); - let right = Box::new(match parser.parse_subexpr(precedence) { - Ok(expr) => expr, - Err(e) => return Some(Err(e)), - }); - Some(Ok(Expr::BinaryOp { - left, - op: BinaryOperator::MyIntegerDivide, - right, - })) - } else { - None - } + super::parse_div_infix(parser, expr, precedence) } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a95105eba5..dbf09bb763 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -22210,3 +22210,23 @@ fn parse_reference_clauses_leave_duplicates_unconsumed() { } } } + +#[test] +fn parse_div_with_nested_divisor() { + let dialects = TestedDialects::new(vec![ + Box::new(MySqlDialect {}), + Box::new(sqlparser::dialect::SparkSqlDialect), + ]); + assert_eq!( + dialects.verified_expr("10 DIV (5 DIV 2)"), + Expr::BinaryOp { + left: Box::new(Expr::value(number("10"))), + op: BinaryOperator::MyIntegerDivide, + right: Box::new(Expr::Nested(Box::new(Expr::BinaryOp { + left: Box::new(Expr::value(number("5"))), + op: BinaryOperator::MyIntegerDivide, + right: Box::new(Expr::value(number("2"))), + }))), + } + ); +}