Repository navigation
fix: bind ^ tighter than *, / and % - #2831
Merged
manticore-projects merged 1 commit intoOct 18, 2026
Merged
Conversation
Since the Pratt rewrite, ^ shares the precedence of the multiplicative operators, so 2 * 3 ^ 2 parses as (2 * 3) ^ 2. PostgreSQL (power) and MySQL (XOR) both evaluate it as 2 * (3 ^ 2), as did JSqlParser 5.3, where BitwiseXor was an operand of MultiplicativeExpression. Move ^ to the level of **, above * / % DIV and still left-associative.
Contributor
|
Thank you for fixing this. |
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.
Since the Pratt rewrite (5.4),
^has the same precedence as*,/,%andDIV, so2 * 3 ^ 2parses as(2 * 3) ^ 2. Both readings of^disagree with that:2 * 3 ^ 218=2 * (3 ^ 2)2=2 * (3 ^ 2)(2 * 3) ^ 22 * (3 ^ 2)10 / 2 ^ 22.5=10 / (2 ^ 2)NULL=10 / (2 ^ 2)(10 / 2) ^ 210 / (2 ^ 2)2 ^ 3 * 2162(2 ^ 3) * 22 ^ 3 ^ 264, left-associative(2 ^ 3) ^ 25.3 got this right:
BitwiseXorwas the operand ofMultiplicativeExpression. A PostgreSQL query likeSELECT pi() * r ^ 2now gives visitors aBitwiseXorat the top of the tree with aMultiplicationinside it. The SQL text still round-trips, because the deparser prints the tree back in the same token order.The change moves
OP_CARETinprattArithRestto the level of**(prec 7). That is above* / % DIVand still left-associative.**already sits at that level, so in DuckDB^and**(both power) now share a level, as they do in DuckDB itself. #2795 had pointed out that the two differed. No productions or lookaheads change. Only one precedence constant moves.One thing I'm unsure about: in T-SQL,
^sits at the+ -level, below*. So withDialect.SQLSERVER,2 * 3 ^ 2was right before this change and is wrong after it.1 + 2 ^ 3was already wrong there and stays wrong. Release 5.3 behaved like this PR. If you'd like, I can add aDialect.SQLSERVERbranch that keeps^at prec 5.Tests: two new cases in
ExpressionPrecedenceTest, covering*,/and%against^,^on the left, left associativity, and a parse/deparse round trip.What was run:
./gradlew test --tests ExpressionPrecedenceTestwithout the grammar change:testCaretBindsTighterThanMultiplicationfails (expected: <Multiplication> but was: <BitwiseXor>). With the change, it passes../gradlew check(JDK 21, with a JDK 17 toolchain): BUILD SUCCESSFUL. That covers 10,747 tests with 0 failures, plus checkstyle, PMD, spotless, spotbugs and jacoco coverage verification.+ - * / % ^ & | # << >>, comparisons,BETWEEN,IN,IS NULL,NOT,CASEand::intwere parsed with JSqlParser. Each tree was re-printed fully parenthesised, and PostgreSQL evaluated both forms. With this change, every remaining mismatch involves& | # << >>against+ -, which is a separate issue. None is caused by^.Not run:
gradle jmh. The change only renumbers a precedence constant, so I didn't benchmark it.checkGrammarAmbiguity: no productions changed.