This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 1a40be57d95 Fix MySQL OR and XOR operator precedence (#40329)
1a40be57d95 is described below
commit 1a40be57d95c63383a7cce0a557951e522b8d07b
Author: Rain. <[email protected]>
AuthorDate: Thu Oct 22 21:08:03 2026 +0800
Fix MySQL OR and XOR operator precedence (#40329)
* Fix MySQL OR and XOR operator precedence
MySQL's operator precedence table ranks XOR above OR (AND > XOR > OR), but
the
MySQL grammar listed the `expr orOperator expr` alternative ahead of
`expr XOR expr`. ANTLR4 resolves the alternatives of a directly
left-recursive
rule by their order, with the earlier alternative binding tighter, so OR
outranked XOR:
1 OR 1 XOR 1 -> (1 OR 1) XOR 1 (wrong)
1 XOR 1 OR 1 -> 1 XOR (1 OR 1) (wrong)
Swapping the two alternatives restores the MySQL precedence:
1 OR 1 XOR 1 -> 1 OR (1 XOR 1)
1 XOR 1 OR 1 -> (1 XOR 1) OR 1
AND stays above both alternatives, so the AND/OR groupings are unchanged.
No visitor change is needed: MySQLStatementVisitor#visitExpr discriminates
on
ctx.XOR() / ctx.andOperator() / ctx.orOperator(), which are mutually
exclusive.
Covered by the Parser IT case select_where_with_expr_with_xor_or.
* Fix MySQL OR and XOR operator precedence
MySQL evaluates `AND` tighter than `XOR`, and `XOR` tighter than `OR`.
In ANTLR4 a directly left-recursive rule assigns precedence by the order
of its alternatives, so `expr` had to list `XOR` before `orOperator`.
It listed them the other way around, grouping `1 OR 1 XOR 1` as
`(1 OR 1) XOR 1`.
Apply the same reordering to the Doris grammar, which keeps its own copy
of this shared rule. Doris documents the same precedence (`AND, &&` = 9,
`XOR` = 10, `OR` = 11), so both dialects now match their vendors.
Cover both dialects with one Parser IT case; its expected AST carries no
`db-types`, so MySQL and Doris assert the same tree from the same SQL.
Reported in #40295. Reverting either grammar alone reproduces the
failures in the corresponding `Internal*ParserIT`.
---
.../src/main/antlr4/imports/doris/BaseRule.g4 | 2 +-
.../src/main/antlr4/imports/mysql/BaseRule.g4 | 2 +-
.../main/resources/case/dml/select-expression.xml | 62 ++++++++++++++++++++++
.../sql/supported/dml/select-expression.xml | 1 +
4 files changed, 65 insertions(+), 2 deletions(-)
diff --git
a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
index 43b4f83b849..7b6313ac1fb 100644
--- a/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
+++ b/parser/sql/engine/dialect/doris/src/main/antlr4/imports/doris/BaseRule.g4
@@ -970,8 +970,8 @@ triggerOrder
expr
: booleanPrimary
| expr andOperator expr
- | expr orOperator expr
| expr XOR expr
+ | expr orOperator expr
| notOperator expr
;
diff --git
a/parser/sql/engine/dialect/mysql/src/main/antlr4/imports/mysql/BaseRule.g4
b/parser/sql/engine/dialect/mysql/src/main/antlr4/imports/mysql/BaseRule.g4
index 1cf48417601..324023a12ac 100644
--- a/parser/sql/engine/dialect/mysql/src/main/antlr4/imports/mysql/BaseRule.g4
+++ b/parser/sql/engine/dialect/mysql/src/main/antlr4/imports/mysql/BaseRule.g4
@@ -392,8 +392,8 @@ triggerOrder
expr
: booleanPrimary
| expr andOperator expr
- | expr orOperator expr
| expr XOR expr
+ | expr orOperator expr
| notOperator expr
;
diff --git a/test/it/parser/src/main/resources/case/dml/select-expression.xml
b/test/it/parser/src/main/resources/case/dml/select-expression.xml
index 465f75de876..de4cb82a28d 100644
--- a/test/it/parser/src/main/resources/case/dml/select-expression.xml
+++ b/test/it/parser/src/main/resources/case/dml/select-expression.xml
@@ -1097,6 +1097,68 @@
</expr>
</where>
</select>
+ <select sql-case-id="select_where_with_expr_with_xor_or"
parameters="1,2,3">
+ <from start-index="14" stop-index="20">
+ <simple-table name="t_order" start-index="14" stop-index="20" />
+ </from>
+ <projections distinct-row="false" start-index="7" stop-index="7">
+ <shorthand-projection start-index="7" stop-index="7" />
+ </projections>
+ <where start-index="22" stop-index="96">
+ <expr>
+ <binary-operation-expression start-index="28" stop-index="96">
+ <left>
+ <binary-operation-expression start-index="28"
stop-index="72">
+ <left>
+ <binary-operation-expression start-index="28"
stop-index="47">
+ <left>
+ <column name="order_id"
start-index="28" stop-index="43">
+ <owner name="t_order"
start-index="28" stop-index="34" />
+ </column>
+ </left>
+ <operator>=</operator>
+ <right>
+ <literal-expression value="1"
start-index="47" stop-index="47" />
+ <parameter-marker-expression
parameter-index="0" start-index="47" stop-index="47" />
+ </right>
+ </binary-operation-expression>
+ </left>
+ <operator>XOR</operator>
+ <right>
+ <binary-operation-expression start-index="53"
stop-index="72">
+ <left>
+ <literal-expression value="2"
start-index="53" stop-index="53" />
+ <parameter-marker-expression
parameter-index="1" start-index="53" stop-index="53" />
+ </left>
+ <operator>=</operator>
+ <right>
+ <column name="order_id"
start-index="57" stop-index="72">
+ <owner name="t_order"
start-index="57" stop-index="63" />
+ </column>
+ </right>
+ </binary-operation-expression>
+ </right>
+ </binary-operation-expression>
+ </left>
+ <operator>OR</operator>
+ <right>
+ <binary-operation-expression start-index="77"
stop-index="96">
+ <left>
+ <literal-expression value="3" start-index="77"
stop-index="77" />
+ <parameter-marker-expression
parameter-index="2" start-index="77" stop-index="77" />
+ </left>
+ <operator>=</operator>
+ <right>
+ <column name="order_id" start-index="81"
stop-index="96">
+ <owner name="t_order" start-index="81"
stop-index="87" />
+ </column>
+ </right>
+ </binary-operation-expression>
+ </right>
+ </binary-operation-expression>
+ </expr>
+ </where>
+ </select>
<select sql-case-id="select_where_with_expr_with_and" parameters="1,2">
<from start-index="14" stop-index="20">
diff --git
a/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
b/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
index de991a052a0..69a49cf033a 100644
--- a/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
+++ b/test/it/parser/src/main/resources/sql/supported/dml/select-expression.xml
@@ -58,6 +58,7 @@
<sql-case id="select_where_with_expr_with_or_sign" value="SELECT * FROM
t_order WHERE t_order.order_id = ? || ? = t_order.order_id" db-types="MySQL" />
<sql-case id="select_concat_pipe_with_table" value="SELECT 'a' || 'b' FROM
t_order" db-types="MySQL" />
<sql-case id="select_where_with_expr_with_xor" value="SELECT * FROM
t_order WHERE t_order.order_id = ? XOR ? = t_order.order_id"
db-types="MySQL,Presto,Doris" />
+ <sql-case id="select_where_with_expr_with_xor_or" value="SELECT * FROM
t_order WHERE t_order.order_id = ? XOR ? = t_order.order_id OR ? =
t_order.order_id" db-types="MySQL,Doris" />
<sql-case id="select_where_with_expr_with_and" value="SELECT * FROM
t_order WHERE t_order.order_id = ? AND ? = t_order.order_id" db-types="MySQL" />
<sql-case id="select_where_with_expr_with_and_or" value="SELECT * FROM
t_order WHERE t_order.order_id = ? AND ? = t_order.order_id OR t_order.status =
'failed' AND ? = t_order.order_id" />
<sql-case id="select_where_with_expr_with_and_sign" value="SELECT * FROM
t_order WHERE t_order.order_id = ? && ? = t_order.order_id"
db-types="MySQL" />