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 39db8f78e08 Fix incorrect range routing for right-side sharding 
columns (#39534)
39db8f78e08 is described below

commit 39db8f78e08f15e40e40b1dade1e6e242c4bec88
Author: Gimin Kim <[email protected]>
AuthorDate: Fri Oct 16 15:20:06 2026 +0900

    Fix incorrect range routing for right-side sharding columns (#39534)
    
    * Fix comparison routing with right-side sharding columns
    
    * Add release note for right-side comparison routing fix
    
    * Preserve broadcast routing for right-side binary columns
    
    * Avoid range checks for equality routing
    
    * Preserve broadcast routing for nested expressions
    
    * Address review feedback on comparison routing
    
    * Fix comparison generator blank-line formatting
    
    ---------
    
    Co-authored-by: Gimin Kim <[email protected]>
---
 RELEASE-NOTES.md                                   |  1 +
 .../ConditionValueCompareOperatorGenerator.java    | 39 +++++++++++++++++++---
 .../WhereClauseShardingConditionEngineTest.java    | 23 +++++++++++++
 ...ConditionValueCompareOperatorGeneratorTest.java | 22 ++++++++++++
 4 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 0f60230e23a..7530cf7f48b 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -101,6 +101,7 @@
 1. Sharding: Compute the Snowflake key generator epoch in UTC instead of the 
JVM default timezone - 
[#38932](https://github.com/apache/shardingsphere/pull/38932)
 1. Sharding: Fix order-dependent data source intersection in Cartesian routing 
- [#39407](https://github.com/apache/shardingsphere/pull/39407)
 1. Sharding: Fix incorrect AVG(DISTINCT) merge result across shards - 
[#39429](https://github.com/apache/shardingsphere/pull/39429)
+1. Sharding: Fix incorrect range routing for right-side sharding columns - 
[#39534](https://github.com/apache/shardingsphere/pull/39534)
 1. Sharding: Fix MySQL SHOW INDEX stripping table suffix from index names of 
non-sharding tables - 
[#39853](https://github.com/apache/shardingsphere/pull/39853)
 1. Sharding: Fix NOT BETWEEN on sharding column being routed as BETWEEN - 
[#39855](https://github.com/apache/shardingsphere/pull/39855)
 1. Sharding: Use configured sharding column name when checking UPDATE 
assignments - [#39864](https://github.com/apache/shardingsphere/pull/39864)
diff --git 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGenerator.java
 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGenerator.java
index aedd7f2f223..87686becf8f 100644
--- 
a/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGenerator.java
+++ 
b/features/sharding/core/src/main/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGenerator.java
@@ -72,8 +72,17 @@ public final class ConditionValueCompareOperatorGenerator 
implements ConditionVa
         if (EQUAL.equals(operator)) {
             left = unwrapBinaryOperator(left);
             right = unwrapBinaryOperator(right);
+        } else if (isRangeOperator(operator) && (isBinaryOperator(left) || 
isBinaryOperator(right))) {
+            return Optional.empty();
+        }
+        boolean isLeftColumn = left instanceof ColumnSegment;
+        if (!isLeftColumn) {
+            if (!(right instanceof ColumnSegment)) {
+                return Optional.empty();
+            }
+            operator = reverseOperator(operator);
         }
-        ExpressionSegment valueExpression = left instanceof ColumnSegment ? 
right : left;
+        ExpressionSegment valueExpression = isLeftColumn ? right : left;
         ConditionValue conditionValue = new ConditionValue(valueExpression, 
params);
         if (conditionValue.isNull()) {
             return generate(null, column, operator, 
conditionValue.getParameterMarkerIndex().orElse(-1));
@@ -117,9 +126,29 @@ public final class ConditionValueCompareOperatorGenerator 
implements ConditionVa
     }
     
     private ExpressionSegment unwrapBinaryOperator(final ExpressionSegment 
segment) {
-        return segment instanceof UnaryOperationExpression
-                && "BINARY".equalsIgnoreCase(((UnaryOperationExpression) 
segment).getOperator())
-                        ? ((UnaryOperationExpression) segment).getExpression()
-                        : segment;
+        return isBinaryOperator(segment) ? ((UnaryOperationExpression) 
segment).getExpression() : segment;
+    }
+    
+    private boolean isBinaryOperator(final ExpressionSegment segment) {
+        return segment instanceof UnaryOperationExpression && 
"BINARY".equalsIgnoreCase(((UnaryOperationExpression) segment).getOperator());
+    }
+    
+    private boolean isRangeOperator(final String operator) {
+        return GREATER_THAN.equals(operator) || LESS_THAN.equals(operator) || 
AT_MOST.equals(operator) || AT_LEAST.equals(operator);
+    }
+    
+    private String reverseOperator(final String operator) {
+        switch (operator) {
+            case GREATER_THAN:
+                return LESS_THAN;
+            case LESS_THAN:
+                return GREATER_THAN;
+            case AT_MOST:
+                return AT_LEAST;
+            case AT_LEAST:
+                return AT_MOST;
+            default:
+                return operator;
+        }
     }
 }
diff --git 
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/engine/WhereClauseShardingConditionEngineTest.java
 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/engine/WhereClauseShardingConditionEngineTest.java
index 06d4ea43a11..f3bf02152a8 100644
--- 
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/engine/WhereClauseShardingConditionEngineTest.java
+++ 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/engine/WhereClauseShardingConditionEngineTest.java
@@ -51,6 +51,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
@@ -191,4 +192,26 @@ class WhereClauseShardingConditionEngineTest {
         List<ShardingCondition> actual = 
shardingConditionEngine.createShardingConditions(sqlStatementContext, 
Collections.emptyList());
         assertTrue(actual.isEmpty());
     }
+    
+    @Test
+    void 
assertCreateEmptyShardingConditionsForRightBinaryOperatorRangeStatement() {
+        ColumnSegment right = new ColumnSegment(0, 0, new 
IdentifierValue("foo_sharding_col"));
+        ExpressionSegment binaryColumn = new UnaryOperationExpression(0, 0, 
right, "BINARY", "BINARY foo_sharding_col");
+        BinaryOperationExpression expression = new 
BinaryOperationExpression(0, 0, new LiteralExpressionSegment(0, 0, "100"), 
binaryColumn, "<", null);
+        when(whereSegment.getExpr()).thenReturn(expression);
+        when(rule.findShardingColumn("foo_sharding_col", 
"")).thenReturn(Optional.of("foo_sharding_col"));
+        Collection<ShardingCondition> actual = 
shardingConditionEngine.createShardingConditions(sqlStatementContext, 
Collections.emptyList());
+        assertTrue(actual.isEmpty());
+    }
+    
+    @Test
+    void 
assertCreateEmptyShardingConditionsForRightNestedColumnRangeStatement() {
+        ColumnSegment column = new ColumnSegment(0, 0, new 
IdentifierValue("foo_sharding_col"));
+        ExpressionSegment right = new BinaryOperationExpression(0, 0, column, 
new LiteralExpressionSegment(0, 0, 1), "+", null);
+        BinaryOperationExpression expression = new 
BinaryOperationExpression(0, 0, new LiteralExpressionSegment(0, 0, 100), right, 
"<", null);
+        when(whereSegment.getExpr()).thenReturn(expression);
+        when(rule.findShardingColumn("foo_sharding_col", 
"")).thenReturn(Optional.of("foo_sharding_col"));
+        Collection<ShardingCondition> actual = 
shardingConditionEngine.createShardingConditions(sqlStatementContext, 
Collections.emptyList());
+        assertTrue(actual.isEmpty());
+    }
 }
diff --git 
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGeneratorTest.java
 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGeneratorTest.java
index a9e206e7c72..4610f9d9e9f 100644
--- 
a/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGeneratorTest.java
+++ 
b/features/sharding/core/src/test/java/org/apache/shardingsphere/sharding/route/engine/condition/generator/impl/ConditionValueCompareOperatorGeneratorTest.java
@@ -31,10 +31,14 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simp
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 import org.apache.shardingsphere.timeservice.core.rule.TimestampServiceRule;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.Optional;
+import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
@@ -147,6 +151,16 @@ class ConditionValueCompareOperatorGeneratorTest {
         assertFalse(shardingConditionValue.isPresent());
     }
     
+    @SuppressWarnings("unchecked")
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("rightColumnComparisonArguments")
+    void assertGenerateConditionValueWithRightColumn(final String name, final 
String operator, final Range<Integer> expectedRange) {
+        BinaryOperationExpression predicate = new BinaryOperationExpression(0, 
0, new LiteralExpressionSegment(0, 0, 1), mock(ColumnSegment.class), operator, 
null);
+        Optional<ShardingConditionValue> actual = 
generator.generate(predicate, column, new LinkedList<>(), 
mock(TimestampServiceRule.class));
+        assertTrue(actual.isPresent());
+        assertThat(((RangeShardingConditionValue<Integer>) 
actual.get()).getValueRange(), is(expectedRange));
+    }
+    
     @SuppressWarnings("unchecked")
     @Test
     void assertGenerateConditionValueWithGreaterThanOperator() {
@@ -223,4 +237,12 @@ class ConditionValueCompareOperatorGeneratorTest {
         Optional<ShardingConditionValue> actual = 
generator.generate(predicate, column, new LinkedList<>(), 
mock(TimestampServiceRule.class));
         assertFalse(actual.isPresent());
     }
+    
+    private static Stream<Arguments> rightColumnComparisonArguments() {
+        return Stream.of(
+                Arguments.of("less than", "<", Range.greaterThan(1)),
+                Arguments.of("at most", "<=", Range.atLeast(1)),
+                Arguments.of("greater than", ">", Range.lessThan(1)),
+                Arguments.of("at least", ">=", Range.atMost(1)));
+    }
 }

Reply via email to