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("*.*"));