This is an automated email from the ASF dual-hosted git repository.

menghaoranss 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 eaea2a3d5c2 Add storage-aware DataNode conversion utility (#39684)
eaea2a3d5c2 is described below

commit eaea2a3d5c20969deb49d928734a896d19566136
Author: Haoran Meng <[email protected]>
AuthorDate: Wed Sep 2 14:15:29 2026 +0800

    Add storage-aware DataNode conversion utility (#39684)
    
    * Add storage-aware DataNode conversion utility
    
    * Use per-storage schemas in single rule decoration
    
    * Preserve fallback for unresolved single table data sources
    
    * Preserve original storage type fallback after data source aggregation
    
    * Preserve single table schema behavior with resolved default schema
---
 .../SingleRuleConfigurationDecorator.java          |  6 ++-
 .../single/util/SingleTableLoadUtils.java          | 12 +++++
 .../SingleRuleConfigurationDecoratorTest.java      | 63 +++++++++++++++++++++-
 .../single/util/SingleTableLoadUtilsTest.java      |  7 +++
 4 files changed, 85 insertions(+), 3 deletions(-)

diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecorator.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecorator.java
index c1924895a75..2631598c799 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecorator.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecorator.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.single.decorator;
 
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
+import 
org.apache.shardingsphere.database.connector.core.metadata.identifier.DefaultSchemaNameResolver;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
 import 
org.apache.shardingsphere.infra.config.rule.decorator.RuleConfigurationDecorator;
@@ -75,7 +76,10 @@ public final class SingleRuleConfigurationDecorator 
implements RuleConfiguration
         if (splitTables.contains(SingleTableConstants.ALL_TABLES) || 
splitTables.contains(SingleTableConstants.ALL_SCHEMA_TABLES)) {
             return loadAllTables(isSchemaAvailable, actualDataNodes);
         }
-        Collection<DataNode> configuredDataNodes = 
SingleTableLoadUtils.convertToDataNodes(databaseName, databaseType, 
splitTables);
+        String defaultSchemaName = dataSources.isEmpty()
+                ? new 
DatabaseTypeRegistry(databaseType).getDefaultSchemaName(databaseName)
+                : DefaultSchemaNameResolver.resolveStorage(databaseType, 
dataSources.values().iterator().next(), databaseName);
+        Collection<DataNode> configuredDataNodes = 
SingleTableLoadUtils.convertToDataNodesWithDefaultSchemaName(defaultSchemaName, 
databaseType, splitTables);
         return loadSpecifiedTables(isSchemaAvailable, actualDataNodes, 
builtRules, configuredDataNodes);
     }
     
diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/util/SingleTableLoadUtils.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/util/SingleTableLoadUtils.java
index 5c932cf3088..4fcd5df6cf8 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/util/SingleTableLoadUtils.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/util/SingleTableLoadUtils.java
@@ -111,6 +111,18 @@ public final class SingleTableLoadUtils {
         return dataNodes.stream().map(each -> new DataNode(databaseName, 
databaseType, each)).collect(Collectors.toCollection(LinkedList::new));
     }
     
+    /**
+     * Convert tables to data nodes with resolved default schema name.
+     *
+     * @param defaultSchemaName resolved default schema name
+     * @param databaseType database type
+     * @param dataNodes data nodes
+     * @return data nodes
+     */
+    public static Collection<DataNode> 
convertToDataNodesWithDefaultSchemaName(final String defaultSchemaName, final 
DatabaseType databaseType, final Collection<String> dataNodes) {
+        return dataNodes.stream().map(each -> 
DataNode.createWithDefaultSchemaName(defaultSchemaName, databaseType, 
each)).collect(Collectors.toCollection(LinkedList::new));
+    }
+    
     /**
      * Get all tables node string.
      *
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecoratorTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecoratorTest.java
index 5e3e52615c2..b4a6990c8ca 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecoratorTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/decorator/SingleRuleConfigurationDecoratorTest.java
@@ -18,12 +18,15 @@
 package org.apache.shardingsphere.single.decorator;
 
 import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
+import 
org.apache.shardingsphere.database.connector.core.metadata.identifier.DefaultSchemaNameResolver;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
 import 
org.apache.shardingsphere.infra.config.rule.decorator.RuleConfigurationDecorator;
 import org.apache.shardingsphere.infra.database.DatabaseTypeEngine;
 import org.apache.shardingsphere.infra.datanode.DataNode;
 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.single.config.SingleRuleConfiguration;
 import org.apache.shardingsphere.single.constant.SingleTableConstants;
@@ -64,7 +67,7 @@ import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(AutoMockExtension.class)
-@StaticMockSettings({DatabaseTypeEngine.class, 
SingleTableDataNodeLoader.class, SingleTableLoadUtils.class})
+@StaticMockSettings({DatabaseTypeEngine.class, 
DefaultSchemaNameResolver.class, SingleTableDataNodeLoader.class, 
SingleTableLoadUtils.class})
 class SingleRuleConfigurationDecoratorTest {
     
     private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
@@ -74,7 +77,9 @@ class SingleRuleConfigurationDecoratorTest {
     
     @BeforeEach
     void setUp() {
+        
when(DatabaseTypeEngine.getDefaultStorageType()).thenReturn(databaseType);
         
when(DatabaseTypeEngine.getStorageType(any(DataSource.class))).thenReturn(databaseType);
+        when(DefaultSchemaNameResolver.resolveStorage(any(DatabaseType.class), 
any(DataSource.class), anyString())).thenReturn("foo_db");
     }
     
     @ParameterizedTest(name = "{0}")
@@ -116,6 +121,35 @@ class SingleRuleConfigurationDecoratorTest {
         }
     }
     
+    @Test
+    void assertDecorateUsesFirstDataSourceDefaultSchema() {
+        DataSource firstDataSource = mock(DataSource.class);
+        DatabaseType firstDatabaseType = mock(DatabaseType.class);
+        
when(DatabaseTypeEngine.getStorageType(firstDataSource)).thenReturn(firstDatabaseType);
+        DataSource secondDataSource = mock(DataSource.class);
+        
when(DatabaseTypeEngine.getStorageType(secondDataSource)).thenReturn(mock(DatabaseType.class));
+        Map<String, DataSource> dataSources = new LinkedHashMap<>(2, 1F);
+        dataSources.put("foo_ds", firstDataSource);
+        dataSources.put("bar_ds", secondDataSource);
+        when(DefaultSchemaNameResolver.resolveStorage(firstDatabaseType, 
firstDataSource, "foo_db")).thenReturn("foo_schema");
+        Map<String, Collection<DataNode>> actualDataNodes = 
Collections.singletonMap("foo_tbl", Collections.singleton(new 
DataNode("foo_ds", "foo_schema", "foo_tbl")));
+        when(SingleTableDataNodeLoader.load(anyString(), anyMap(), 
anyCollection(), anyCollection(), anyMap())).thenReturn(actualDataNodes);
+        Collection<String> splitTables = Collections.singleton("foo_ds.*");
+        
when(SingleTableLoadUtils.splitTableLines(anyCollection())).thenReturn(splitTables);
+        
when(SingleTableLoadUtils.getExcludedTables(anyCollection())).thenReturn(Collections.emptyList());
+        
when(SingleTableLoadUtils.getFeatureRequiredSingleTables(anyCollection())).thenReturn(Collections.emptyList());
+        
when(SingleTableLoadUtils.convertToDataNodesWithDefaultSchemaName(anyString(), 
any(DatabaseType.class), anyCollection())).thenAnswer(invocation -> {
+            assertThat(invocation.<String>getArgument(0), is("foo_schema"));
+            assertThat(invocation.<DatabaseType>getArgument(1), 
is(firstDatabaseType));
+            assertThat(invocation.<Collection<String>>getArgument(2), 
contains("foo_ds.*"));
+            return Collections.singleton(new DataNode("foo_ds", "foo_schema", 
"*"));
+        });
+        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(splitTables, null);
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(true)) {
+            assertThat(decorator.decorate("foo_db", dataSources, 
Collections.emptyList(), ruleConfig).getTables(), 
contains("foo_ds.foo_schema.foo_tbl"));
+        }
+    }
+    
     @Test
     void assertDecorateUsesDefaultStorageTypeWhenNoDataSourceConfigured() {
         DataNode dataNode = new DataNode("foo_ds", "foo_schema", "t_order");
@@ -131,6 +165,27 @@ class SingleRuleConfigurationDecoratorTest {
         }
     }
     
+    @Test
+    void assertDecorateWithAggregatedDataSource() {
+        DataSource dataSource = mock(DataSource.class);
+        DatabaseType storageType = 
TypedSPILoader.getService(DatabaseType.class, "H2");
+        
when(DatabaseTypeEngine.getStorageType(dataSource)).thenReturn(storageType);
+        DataSourceMapperRuleAttribute ruleAttribute = 
mock(DataSourceMapperRuleAttribute.class);
+        
when(ruleAttribute.getDataSourceMapper()).thenReturn(Collections.singletonMap("ds",
 Collections.singleton("foo_ds")));
+        ShardingSphereRule rule = mock(ShardingSphereRule.class);
+        when(rule.getAttributes()).thenReturn(new 
RuleAttributes(ruleAttribute));
+        Collection<String> splitTables = Collections.singleton("missing_ds.*");
+        
when(SingleTableLoadUtils.splitTableLines(anyCollection())).thenReturn(splitTables);
+        
when(SingleTableLoadUtils.getExcludedTables(anyCollection())).thenReturn(Collections.emptyList());
+        
when(SingleTableLoadUtils.getFeatureRequiredSingleTables(anyCollection())).thenReturn(Collections.emptyList());
+        when(DefaultSchemaNameResolver.resolveStorage(storageType, dataSource, 
"foo_db")).thenReturn("PUBLIC");
+        
when(SingleTableLoadUtils.convertToDataNodesWithDefaultSchemaName(anyString(), 
any(DatabaseType.class), anyCollection())).thenCallRealMethod();
+        when(SingleTableDataNodeLoader.load(anyString(), anyMap(), 
anyCollection(), anyCollection(), anyMap())).thenReturn(Collections.emptyMap());
+        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(splitTables, null);
+        SingleRuleConfiguration actual = decorator.decorate("foo_db", 
Collections.singletonMap("foo_ds", dataSource), Collections.singleton(rule), 
ruleConfig);
+        assertTrue(actual.getTables().isEmpty());
+    }
+    
     @Test
     void assertDecorateThrowsWhenSpecifiedTableMismatch() {
         Map<String, Collection<DataNode>> actualDataNodes = new 
LinkedHashMap<>(2, 1F);
@@ -250,7 +305,10 @@ class SingleRuleConfigurationDecoratorTest {
     private MockedConstruction<DatabaseTypeRegistry> mockSchemaRegistry(final 
boolean schemaAvailable) {
         DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
         
when(dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()).thenReturn(schemaAvailable);
-        return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData));
+        return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
{
+            when(mock.getDefaultSchemaName(anyString())).thenAnswer(invocation 
-> invocation.getArgument(0));
+            
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
+        });
     }
     
     private void mockSplitAndConvert(final Collection<String> splitTables, 
final Collection<DataNode> configuredDataNodes,
@@ -258,6 +316,7 @@ class SingleRuleConfigurationDecoratorTest {
         
when(SingleTableLoadUtils.splitTableLines(anyCollection())).thenReturn(splitTables);
         
when(SingleTableLoadUtils.getExcludedTables(anyCollection())).thenReturn(excludedTables);
         when(SingleTableLoadUtils.convertToDataNodes(anyString(), 
any(DatabaseType.class), anyCollection())).thenReturn(configuredDataNodes);
+        
when(SingleTableLoadUtils.convertToDataNodesWithDefaultSchemaName(anyString(), 
any(DatabaseType.class), anyCollection())).thenReturn(configuredDataNodes);
         
when(SingleTableLoadUtils.getFeatureRequiredSingleTables(anyCollection())).thenReturn(featureRequiredTables);
     }
     
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/util/SingleTableLoadUtilsTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/util/SingleTableLoadUtilsTest.java
index 40c83f37fe6..e0e1f420c50 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/util/SingleTableLoadUtilsTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/util/SingleTableLoadUtilsTest.java
@@ -93,6 +93,13 @@ class SingleTableLoadUtilsTest {
                 is(new LinkedList<>(Arrays.asList(expectedDataNode1, 
expectedDataNode2))));
     }
     
+    @Test
+    void assertConvertToDataNodesWithDefaultSchemaName() {
+        Collection<DataNode> expected = Arrays.asList(new DataNode("foo_ds", 
"foo_schema", "foo_tbl"), new DataNode("bar_ds", "foo_schema", 
"schema.bar_tbl.part"));
+        
assertThat(SingleTableLoadUtils.convertToDataNodesWithDefaultSchemaName("foo_schema",
 databaseType, Arrays.asList("foo_ds.foo_tbl", "bar_ds.schema.bar_tbl.part")),
+                is(new LinkedList<>(expected)));
+    }
+    
     @Test
     void assertGetAllTablesNodeStr() {
         assertThat(SingleTableLoadUtils.getAllTablesNodeStr(databaseType), 
is("*.*"));

Reply via email to