Honour operator precedence in MySQL and Spark DIV - #2471
Open
zvonimir-dd wants to merge 1 commit into
Open
Conversation
`get_next_precedence` announces `DIV` at `Precedence::MulDivModOp`, but both dialects parsed its right operand with `parse_expr()` (= `parse_subexpr(0)`), so the operand absorbed every following operator: `7 DIV 2 + 1` grouped as `7 DIV (2 + 1)` = 2 where MySQL and Spark both give `(7 DIV 2) + 1` = 4. Thread the caller's precedence through instead, as SqliteDialect already does for REGEXP / MATCH / GLOB. `Display` emits no parentheses, so a mis-grouped tree round-trips to the original SQL and the round-trip helpers could not catch this; the new tests assert on the tree. Fixes apache#2460 Environment: Datadog workspace Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LucaCappelletti94
approved these changes
Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2460.
Dialect::get_next_precedenceannouncesDIVatPrecedence::MulDivModOp, butMySqlDialect::parse_infixand
SparkSqlDialect::parse_infixboth ignored theirprecedenceargument and parsed the rightoperand with
parse_expr()(=parse_subexpr(0)), so the operand absorbed every following operator:7 DIV 2 + 17 DIV (2 + 1)= 2(7 DIV 2) + 1= 49 DIV 3 * 39 DIV (3 * 3)= 1(9 DIV 3) * 3= 9a DIV 2 = 1a DIV (2 = 1)(a DIV 2) = 1Both engines place
DIVwith*and/: MySQL'soperator precedence table lists
*, /, DIV, %, MODon one row, and Spark'sSqlBaseParser.g4hasoperator=(ASTERISK | SLASH | PERCENT | DIV)in a single left-recursive rule.The fix threads the caller's precedence through to
parse_subexpr, exactly asSqliteDialect::parse_infixalready does forREGEXP/MATCH/GLOB(#2419). Sinceparse_subexprpasses the upcoming operator's precedence into the dialect hook, the right operandis parsed at
MulDivModOp, which stops it before+and makesDIVleft-associative against*,/and itself.DisplayforExpr::BinaryOpemits no parentheses, so a mis-grouped tree round-trips back to theoriginal SQL —
verified_stmt/verified_expralone cannot catch this. The new tests thereforeassert on the tree:
tests/sqlparser_mysql.rs::parse_div_precedencecovers+,*,DIVagainstitself,
=, and explicit parentheses;tests/sqlparser_spark.rs::test_div_precedencemirrors thefirst two for Spark. I confirmed both fail without the source change.
GenericDialectis deliberately not included — it has noDIVoperator support, so it neverproduces
MyIntegerDivide.Full suite green (1605 tests, up 2 from 1603),
cargo fmt --checkandcargo clippy --all-targets --all-features -- -D warningsclean.Thanks to @LucaCappelletti94, who spotted this while reviewing #2436 and wrote both the patch and the
headline test.