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 4cb41851215 Fix CREATE READWRITE_SPLITTING RULE IF NOT EXISTS for 
existing rule (#39371)
4cb41851215 is described below

commit 4cb41851215ec88693f1e104a453aef6621803cd
Author: Eunbin Son <[email protected]>
AuthorDate: Tue Sep 8 19:19:28 2026 +0900

    Fix CREATE READWRITE_SPLITTING RULE IF NOT EXISTS for existing rule (#39371)
    
    * Fix CREATE READWRITE_SPLITTING RULE IF NOT EXISTS for existing rule
    
    * Skip existing rules in readwrite-splitting IF NOT EXISTS checks
    
    checkCreation skipped only the duplicate-name check for IF NOT EXISTS and
    continued into actual data source validation with every submitted
    segment. Since that validation seeds the duplicate sets from the whole
    current configuration, repeating an unchanged rule threw
    DuplicateReadwriteSplittingActualDataSourceException instead of becoming
    a no-op, because the executor removes existing-name segments later.
    
    Exclude segments whose rule name already exists from the remaining
    creation validations when IF NOT EXISTS is specified, and keep validating
    genuinely new segments.
    
    Add a case reusing the same rule name and the same write and read storage
    units, and a case where a new segment reuses an existing write data
    source and must still fail.
    
    * Validate new readwrite-splitting names against expanded names
    
    Excluding every ReadwriteSplittingRule mapper key from the logic data
    source set also removed collision protection: with an existing group
    rw_${0..1}, creating literal rw_0 passed because the remaining duplicate
    checks compare raw configuration names, and rebuilding the rule then
    merged both definitions under expanded key rw_0.
    
    Restore the full logic data source set and instead exempt only the exact
    existing configurations that IF NOT EXISTS discards, so genuinely new
    segments are still validated against all expanded logical names.
    
    ---------
    
    Co-authored-by: Liang Zhang <[email protected]>
---
 RELEASE-NOTES.md                                   |  1 +
 .../ReadwriteSplittingRuleStatementChecker.java    | 20 ++++--
 ...ReadwriteSplittingRuleStatementCheckerTest.java | 74 +++++++++++++++++++++-
 3 files changed, 87 insertions(+), 8 deletions(-)

diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index 7b429a9443d..628090686a6 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -42,6 +42,7 @@
 1. Metadata: Fix wrong logic table metadata when config same actual table name 
in different storage unit - 
[#39157](https://github.com/apache/shardingsphere/pull/39157)
 1. DistSQL: Fix case-sensitive storage unit matching in `SHOW RULES USED 
STORAGE UNIT` - [#38848](https://github.com/apache/shardingsphere/pull/38848)
 1. DistSQL: Use case-insensitive table name matching in broadcast create and 
drop executors - [#39200](https://github.com/apache/shardingsphere/pull/39200)
+1. DistSQL: Fix `CREATE READWRITE_SPLITTING RULE IF NOT EXISTS` failing for an 
existing rule name - 
[#39371](https://github.com/apache/shardingsphere/pull/39371)
 1. DistSQL: Match mask table names case-insensitively in create and alter 
executors - [#39361](https://github.com/apache/shardingsphere/pull/39361)
 1. JDBC: Fix stale generated values leaking into prepared statement 
executeBatch calls without pending batches - 
[#38160](https://github.com/apache/shardingsphere/pull/38160)
 1. JDBC: Fix MySQL-compatible typed string conversion for 
`ResultSet#getObject(index, Class<T>)` - 
[#38444](https://github.com/apache/shardingsphere/pull/38444)
diff --git 
a/features/readwrite-splitting/distsql/handler/src/main/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementChecker.java
 
b/features/readwrite-splitting/distsql/handler/src/main/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementChecker.java
index 414b1eed691..0e77b52a023 100644
--- 
a/features/readwrite-splitting/distsql/handler/src/main/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementChecker.java
+++ 
b/features/readwrite-splitting/distsql/handler/src/main/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementChecker.java
@@ -67,10 +67,20 @@ public final class ReadwriteSplittingRuleStatementChecker {
                                      final ReadwriteSplittingRuleConfiguration 
currentRuleConfig, final boolean ifNotExists) {
         checkDuplicateRuleNames(database, segments, currentRuleConfig, 
ifNotExists);
         String databaseName = database.getName();
-        checkDataSourcesExist(databaseName, segments, database);
-        checkDuplicatedDataSourceNames(databaseName, segments, 
currentRuleConfig, true);
-        checkTransactionalReadQueryStrategy(segments);
-        checkLoadBalancers(segments);
+        Collection<ReadwriteSplittingRuleSegment> toBeCreatedSegments = 
ifNotExists ? getNotExistedSegments(segments, currentRuleConfig) : segments;
+        checkDataSourcesExist(databaseName, toBeCreatedSegments, database);
+        checkDuplicatedDataSourceNames(databaseName, toBeCreatedSegments, 
currentRuleConfig, true);
+        checkTransactionalReadQueryStrategy(toBeCreatedSegments);
+        checkLoadBalancers(toBeCreatedSegments);
+    }
+    
+    private static Collection<ReadwriteSplittingRuleSegment> 
getNotExistedSegments(final Collection<ReadwriteSplittingRuleSegment> segments,
+                                                                               
    final ReadwriteSplittingRuleConfiguration currentRuleConfig) {
+        if (null == currentRuleConfig) {
+            return segments;
+        }
+        Collection<String> currentRuleNames = 
currentRuleConfig.getDataSourceGroups().stream().map(ReadwriteSplittingDataSourceGroupRuleConfiguration::getName).collect(Collectors.toSet());
+        return segments.stream().filter(each -> 
!currentRuleNames.contains(each.getName())).collect(Collectors.toList());
     }
     
     /**
@@ -100,7 +110,7 @@ public final class ReadwriteSplittingRuleStatementChecker {
     private static void checkDuplicateRuleNames(final ShardingSphereDatabase 
database,
                                                 final 
Collection<ReadwriteSplittingRuleSegment> segments, final 
ReadwriteSplittingRuleConfiguration currentRuleConfig, final boolean 
ifNotExists) {
         checkDuplicateRuleNamesWithSelf(database.getName(), segments);
-        checkDuplicateRuleNamesWithExistsDataSources(database, segments);
+        checkDuplicateRuleNamesWithExistsDataSources(database, ifNotExists ? 
getNotExistedSegments(segments, currentRuleConfig) : segments);
         if (!ifNotExists) {
             checkDuplicateRuleNamesWithRuleConfiguration(database.getName(), 
currentRuleConfig, segments);
         }
diff --git 
a/features/readwrite-splitting/distsql/handler/src/test/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementCheckerTest.java
 
b/features/readwrite-splitting/distsql/handler/src/test/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementCheckerTest.java
index d148530299b..5f7d9bedd3c 100644
--- 
a/features/readwrite-splitting/distsql/handler/src/test/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementCheckerTest.java
+++ 
b/features/readwrite-splitting/distsql/handler/src/test/java/org/apache/shardingsphere/readwritesplitting/distsql/handler/checker/ReadwriteSplittingRuleStatementCheckerTest.java
@@ -26,6 +26,9 @@ import 
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.MissingReq
 import 
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.MissingRequiredStrategyException;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import 
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
+import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
+import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
+import org.apache.shardingsphere.infra.rule.attribute.RuleAttributes;
 import 
org.apache.shardingsphere.infra.rule.attribute.datasource.DataSourceMapperRuleAttribute;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.infra.util.props.PropertiesBuilder;
@@ -34,6 +37,7 @@ import 
org.apache.shardingsphere.readwritesplitting.config.ReadwriteSplittingRul
 import 
org.apache.shardingsphere.readwritesplitting.config.rule.ReadwriteSplittingDataSourceGroupRuleConfiguration;
 import 
org.apache.shardingsphere.readwritesplitting.distsql.segment.ReadwriteSplittingRuleSegment;
 import 
org.apache.shardingsphere.readwritesplitting.exception.actual.DuplicateReadwriteSplittingActualDataSourceException;
+import 
org.apache.shardingsphere.readwritesplitting.rule.ReadwriteSplittingRule;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
 import org.junit.jupiter.api.BeforeEach;
@@ -48,6 +52,8 @@ import org.mockito.Mock;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Properties;
 import java.util.stream.Stream;
 
@@ -55,6 +61,7 @@ import static 
org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(AutoMockExtension.class)
@@ -70,7 +77,7 @@ class ReadwriteSplittingRuleStatementCheckerTest {
     @BeforeEach
     void setUp() {
         when(database.getName()).thenReturn("foo_db");
-        
when(database.getRuleMetaData().getAttributes(DataSourceMapperRuleAttribute.class)).thenReturn(Collections.emptyList());
+        when(database.getRuleMetaData()).thenReturn(new 
RuleMetaData(Collections.emptyList()));
         
lenient().when(resourceMetaData.getStorageUnits()).thenReturn(Collections.emptyMap());
         
lenient().when(resourceMetaData.getNotExistedDataSources(any())).thenReturn(Collections.emptySet());
         when(database.getResourceMetaData()).thenReturn(resourceMetaData);
@@ -130,10 +137,53 @@ class ReadwriteSplittingRuleStatementCheckerTest {
         assertDoesNotThrow(() -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Collections.singleton(segment), currentRuleConfig, true));
     }
     
+    @Test
+    void assertCheckCreationWithIfNotExistsWhenActualDataSourcesUnchanged() {
+        ReadwriteSplittingRuleSegment segment = new 
ReadwriteSplittingRuleSegment("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), null, null);
+        Collection<ReadwriteSplittingDataSourceGroupRuleConfiguration> 
dataSourceGroups = Collections.singleton(
+                new 
ReadwriteSplittingDataSourceGroupRuleConfiguration("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), "RANDOM"));
+        ReadwriteSplittingRuleConfiguration currentRuleConfig = new 
ReadwriteSplittingRuleConfiguration(dataSourceGroups, Collections.emptyMap());
+        assertDoesNotThrow(() -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Collections.singleton(segment), currentRuleConfig, true));
+    }
+    
+    @Test
+    void 
assertCheckCreationWithIfNotExistsWhenNewSegmentDuplicatesExistedDataSource() {
+        ReadwriteSplittingRuleSegment existedSegment = new 
ReadwriteSplittingRuleSegment("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), null, null);
+        ReadwriteSplittingRuleSegment newSegment = new 
ReadwriteSplittingRuleSegment("foo_rule_1", "write_ds_0", 
Collections.singletonList("read_ds_2"), null, null);
+        Collection<ReadwriteSplittingDataSourceGroupRuleConfiguration> 
dataSourceGroups = Collections.singleton(
+                new 
ReadwriteSplittingDataSourceGroupRuleConfiguration("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), "RANDOM"));
+        ReadwriteSplittingRuleConfiguration currentRuleConfig = new 
ReadwriteSplittingRuleConfiguration(dataSourceGroups, Collections.emptyMap());
+        
assertThrows(DuplicateReadwriteSplittingActualDataSourceException.class,
+                () -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Arrays.asList(existedSegment, newSegment), currentRuleConfig, true));
+    }
+    
+    @Test
+    void 
assertCheckCreationWithIfNotExistsWhenRuleNameExistsInExpandedLogicDataSources()
 {
+        RuleMetaData ruleMetaData = 
mockRuleMetaDataWithLogicDataSources(ReadwriteSplittingRule.class, 
Arrays.asList("rw_0", "rw_1"));
+        when(database.getRuleMetaData()).thenReturn(ruleMetaData);
+        ReadwriteSplittingRuleSegment segment = new 
ReadwriteSplittingRuleSegment("rw_0", "write_ds_0", 
Collections.singletonList("read_ds_0"), null, null);
+        Collection<ReadwriteSplittingDataSourceGroupRuleConfiguration> 
dataSourceGroups = Collections.singleton(
+                new 
ReadwriteSplittingDataSourceGroupRuleConfiguration("rw_${0..1}", "write_ds_9", 
Collections.singletonList("read_ds_9"), "RANDOM"));
+        ReadwriteSplittingRuleConfiguration currentRuleConfig = new 
ReadwriteSplittingRuleConfiguration(dataSourceGroups, Collections.emptyMap());
+        assertThrows(InvalidRuleConfigurationException.class,
+                () -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Collections.singleton(segment), currentRuleConfig, true));
+    }
+    
+    @Test
+    void 
assertCheckCreationWithIfNotExistsWhenRuleNameExistsInOwnLogicDataSources() {
+        RuleMetaData ruleMetaData = 
mockRuleMetaDataWithLogicDataSource(ReadwriteSplittingRule.class, "foo_rule_0");
+        when(database.getRuleMetaData()).thenReturn(ruleMetaData);
+        ReadwriteSplittingRuleSegment segment = new 
ReadwriteSplittingRuleSegment("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), null, null);
+        Collection<ReadwriteSplittingDataSourceGroupRuleConfiguration> 
dataSourceGroups = Collections.singleton(
+                new 
ReadwriteSplittingDataSourceGroupRuleConfiguration("foo_rule_0", "write_ds_9", 
Collections.singletonList("read_ds_9"), "RANDOM"));
+        ReadwriteSplittingRuleConfiguration currentRuleConfig = new 
ReadwriteSplittingRuleConfiguration(dataSourceGroups, Collections.emptyMap());
+        assertDoesNotThrow(() -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Collections.singleton(segment), currentRuleConfig, true));
+    }
+    
     @Test
     void assertCheckCreationWithDuplicateRuleNameInLogicDataSources() {
-        DataSourceMapperRuleAttribute ruleAttribute = () -> 
Collections.singletonMap("foo_rule_0", Collections.singleton("actual_ds_0"));
-        
when(database.getRuleMetaData().getAttributes(DataSourceMapperRuleAttribute.class)).thenReturn(Collections.singleton(ruleAttribute));
+        RuleMetaData ruleMetaData = 
mockRuleMetaDataWithLogicDataSource(ShardingSphereRule.class, "foo_rule_0");
+        when(database.getRuleMetaData()).thenReturn(ruleMetaData);
         ReadwriteSplittingRuleSegment segment = new 
ReadwriteSplittingRuleSegment("foo_rule_0", "write_ds_0", 
Arrays.asList("read_ds_0", "read_ds_1"), null, null);
         assertThrows(InvalidRuleConfigurationException.class, () -> 
ReadwriteSplittingRuleStatementChecker.checkCreation(database, 
Collections.singleton(segment), null, false));
     }
@@ -186,6 +236,24 @@ class ReadwriteSplittingRuleStatementCheckerTest {
         assertThrows(MissingRequiredRuleException.class, () -> 
ReadwriteSplittingRuleStatementChecker.checkAlteration(database, 
Collections.singleton(segment), currentRuleConfig));
     }
     
+    private RuleMetaData mockRuleMetaDataWithLogicDataSources(final Class<? 
extends ShardingSphereRule> ruleClass, final Collection<String> 
logicDataSourceNames) {
+        ShardingSphereRule rule = mock(ruleClass);
+        Map<String, Collection<String>> dataSourceMapper = new 
LinkedHashMap<>(logicDataSourceNames.size(), 1F);
+        for (String each : logicDataSourceNames) {
+            dataSourceMapper.put(each, Collections.singleton("actual_ds_0"));
+        }
+        DataSourceMapperRuleAttribute ruleAttribute = () -> dataSourceMapper;
+        lenient().when(rule.getAttributes()).thenReturn(new 
RuleAttributes(ruleAttribute));
+        return new RuleMetaData(Collections.singleton(rule));
+    }
+    
+    private RuleMetaData mockRuleMetaDataWithLogicDataSource(final Class<? 
extends ShardingSphereRule> ruleClass, final String logicDataSourceName) {
+        ShardingSphereRule rule = mock(ruleClass);
+        DataSourceMapperRuleAttribute ruleAttribute = () -> 
Collections.singletonMap(logicDataSourceName, 
Collections.singleton("actual_ds_0"));
+        lenient().when(rule.getAttributes()).thenReturn(new 
RuleAttributes(ruleAttribute));
+        return new RuleMetaData(Collections.singleton(rule));
+    }
+    
     private static Stream<Arguments> invalidWeightConfigurationArguments() {
         return Stream.of(
                 Arguments.of("empty weight properties",

Reply via email to