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 08369e3cc2d Fix Single table schema availability handling (#39621)
08369e3cc2d is described below

commit 08369e3cc2d27b5ae167f302f624d63f35923590
Author: Liang Zhang <[email protected]>
AuthorDate: Fri Aug 28 12:33:19 2026 +0800

    Fix Single table schema availability handling (#39621)
    
    * Fix Single table schema availability handling
    
    * Fix Single table schema availability handling
---
 .../single/datanode/SingleTableDataNodeLoader.java |  8 ++--
 .../SingleRuleConfigurationDecorator.java          | 11 +++++-
 .../SingleMutableDataNodeRuleAttribute.java        | 17 +++++++-
 .../single/util/SingleTableLoadUtils.java          |  4 +-
 .../datanode/SingleTableDataNodeLoaderTest.java    | 32 +++++++++++++++
 .../SingleRuleConfigurationDecoratorTest.java      | 46 +++++++++++++---------
 .../SingleMutableDataNodeRuleAttributeTest.java    |  5 ++-
 .../single/util/SingleTableLoadUtilsTest.java      | 18 +++++++--
 .../handler/update/LoadSingleTableExecutor.java    | 30 ++++++++------
 .../handler/update/UnloadSingleTableExecutor.java  |  4 +-
 .../update/LoadSingleTableExecutorTest.java        | 36 ++++++++++++-----
 .../update/UnloadSingleTableExecutorTest.java      | 28 ++++++++++---
 12 files changed, 177 insertions(+), 62 deletions(-)

diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
index ae251323eb7..e7afaa1b412 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoader.java
@@ -198,7 +198,9 @@ public final class SingleTableDataNodeLoader {
             if (null == configuredTablesForDataSource) {
                 continue;
             }
-            if 
(configuredTablesForDataSource.containsKey(SingleTableConstants.ASTERISK)) {
+            Collection<String> configuredTablesForAllSchemas = 
configuredTablesForDataSource.get(SingleTableConstants.ASTERISK);
+            if (null != configuredTablesForAllSchemas
+                    && 
(configuredTablesForAllSchemas.contains(SingleTableConstants.ASTERISK) || 
configuredTablesForAllSchemas.contains(each.getTableName()))) {
                 result.add(each);
                 continue;
             }
@@ -224,9 +226,9 @@ public final class SingleTableDataNodeLoader {
             DatabaseType databaseType = 
validStorageTypes.getOrDefault(parsedDataNode.getDataSourceName(), 
protocolType);
             DataNode dataNode = new DataNode(databaseName, databaseType, each);
             Map<String, Collection<String>> schemaTables = 
result.getOrDefault(dataNode.getDataSourceName(), new LinkedHashMap<>());
-            Collection<String> tables = 
schemaTables.getOrDefault(dataNode.getSchemaName(), new LinkedList<>());
+            Collection<String> tables = 
schemaTables.computeIfAbsent(dataNode.getSchemaName(),
+                    ignored -> 
SingleTableConstants.ASTERISK.equals(dataNode.getSchemaName()) ? new 
CaseInsensitiveSet<>() : new LinkedHashSet<>());
             tables.add(dataNode.getTableName());
-            schemaTables.putIfAbsent(dataNode.getSchemaName(), tables);
             result.putIfAbsent(dataNode.getDataSourceName(), schemaTables);
         }
         return result;
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 10923e140e1..c1924895a75 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
@@ -159,7 +159,7 @@ public final class SingleRuleConfigurationDecorator 
implements RuleConfiguration
             if (expectedDataNodes.containsKey(entry.getKey())) {
                 DataNode dataNode = expectedDataNodes.get(entry.getKey());
                 String tableNodeStr = getTableNodeString(isSchemaAvailable, 
physicalDataNode);
-                
ShardingSpherePreconditions.checkState(physicalDataNode.equals(dataNode),
+                
ShardingSpherePreconditions.checkState(isMatchedDataNode(isSchemaAvailable, 
physicalDataNode, dataNode),
                         () -> new 
InvalidSingleRuleConfigurationException(String.format("Single table `%s` is 
found that does not match %s", tableNodeStr,
                                 getTableNodeString(isSchemaAvailable, 
dataNode))));
                 result.add(tableNodeStr);
@@ -175,13 +175,20 @@ public final class SingleRuleConfigurationDecorator 
implements RuleConfiguration
             ShardingSpherePreconditions.checkContainsKey(actualDataNodes, 
each.getTableName(), () -> new 
SingleTableNotFoundException(getTableNodeString(isSchemaAvailable, each)));
             DataNode actualDataNode = 
actualDataNodes.get(each.getTableName()).iterator().next();
             String tableNodeStr = getTableNodeString(isSchemaAvailable, 
actualDataNode);
-            
ShardingSpherePreconditions.checkState(actualDataNode.equals(each), () -> new 
InvalidSingleRuleConfigurationException(
+            
ShardingSpherePreconditions.checkState(isMatchedDataNode(isSchemaAvailable, 
actualDataNode, each), () -> new InvalidSingleRuleConfigurationException(
                     String.format("Single table '%s' is found that does not 
match %s", tableNodeStr, getTableNodeString(isSchemaAvailable, each))));
             result.add(tableNodeStr);
         }
         return result;
     }
     
+    private boolean isMatchedDataNode(final boolean isSchemaAvailable, final 
DataNode actualDataNode, final DataNode configuredDataNode) {
+        if (isSchemaAvailable || 
!SingleTableConstants.ASTERISK.equals(configuredDataNode.getSchemaName())) {
+            return actualDataNode.equals(configuredDataNode);
+        }
+        return 
actualDataNode.getDataSourceName().equals(configuredDataNode.getDataSourceName())
 && actualDataNode.getTableName().equals(configuredDataNode.getTableName());
+    }
+    
     @Override
     public Class<SingleRuleConfiguration> getType() {
         return SingleRuleConfiguration.class;
diff --git 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
index 191283b35d5..8825c562242 100644
--- 
a/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
+++ 
b/kernel/single/core/src/main/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttribute.java
@@ -100,12 +100,23 @@ public final class SingleMutableDataNodeRuleAttribute 
implements MutableDataNode
             if (null == dataNodes) {
                 continue;
             }
+            Collection<String> removedDataNodeStrings = new LinkedList<>();
             Iterator<DataNode> iterator = dataNodes.iterator();
             while (iterator.hasNext()) {
                 DataNode dataNode = iterator.next();
                 if (schemaNames.contains(dataNode.getSchemaName())) {
                     iterator.remove();
-                    
configuration.getTables().remove(SingleTableLoadUtils.getDataNodeString(protocolType,
 dataNode.getDataSourceName(), dataNode.getSchemaName(), each));
+                    removedDataNodeStrings.add(getDataNodeString(dataNode, 
each));
+                }
+            }
+            if (!removedDataNodeStrings.isEmpty()) {
+                Collection<String> retainedDataNodeStrings = new 
LinkedHashSet<>(dataNodes.size(), 1F);
+                for (DataNode dataNode : dataNodes) {
+                    retainedDataNodeStrings.add(getDataNodeString(dataNode, 
each));
+                }
+                removedDataNodeStrings.removeAll(retainedDataNodeStrings);
+                for (String removedDataNodeString : removedDataNodeStrings) {
+                    configuration.getTables().remove(removedDataNodeString);
                 }
             }
             if (dataNodes.isEmpty()) {
@@ -115,6 +126,10 @@ public final class SingleMutableDataNodeRuleAttribute 
implements MutableDataNode
         }
     }
     
+    private String getDataNodeString(final DataNode dataNode, final String 
tableName) {
+        return SingleTableLoadUtils.getDataNodeString(protocolType, 
dataNode.getDataSourceName(), dataNode.getSchemaName(), tableName);
+    }
+    
     @SuppressWarnings("CollectionWithoutInitialCapacity")
     @Override
     public Optional<DataNode> findTableDataNode(final String schemaName, final 
String tableName) {
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 c9782810ec7..a66121a4f57 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
@@ -132,7 +132,7 @@ public final class SingleTableLoadUtils {
      */
     public static String getAllTablesNodeStrFromDataSource(final DatabaseType 
databaseType, final String dataSourceName, final String schemaName) {
         DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData();
-        return 
dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().isPresent()
+        return dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()
                 ? formatDataNode(dataSourceName, schemaName, 
SingleTableConstants.ASTERISK)
                 : formatDataNode(dataSourceName, 
SingleTableConstants.ASTERISK);
     }
@@ -148,7 +148,7 @@ public final class SingleTableLoadUtils {
      */
     public static String getDataNodeString(final DatabaseType databaseType, 
final String dataSourceName, final String schemaName, final String tableName) {
         DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData();
-        return 
dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().isPresent() ? 
formatDataNode(dataSourceName, schemaName, tableName) : 
formatDataNode(dataSourceName, tableName);
+        return dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable() ? 
formatDataNode(dataSourceName, schemaName, tableName) : 
formatDataNode(dataSourceName, tableName);
     }
     
     private static String formatDataNode(final String dataSourceName, final 
String tableName) {
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
index cad0aad5812..48a71137868 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/datanode/SingleTableDataNodeLoaderTest.java
@@ -19,7 +19,9 @@ package org.apache.shardingsphere.single.datanode;
 
 import ch.qos.logback.classic.Level;
 import 
org.apache.shardingsphere.database.connector.core.metadata.data.loader.type.SchemaMetaDataLoader;
+import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
 import org.apache.shardingsphere.infra.database.DatabaseTypeEngine;
 import org.apache.shardingsphere.infra.datanode.DataNode;
 import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
@@ -51,6 +53,7 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
@@ -60,6 +63,9 @@ import static org.hamcrest.Matchers.is;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyCollection;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -171,6 +177,32 @@ class SingleTableDataNodeLoaderTest {
         
assertThat(actual.get("foo_tbl2").iterator().next().getDataSourceName(), 
is("foo_ds"));
     }
     
+    @Test
+    void assertLoadExactTablesWhenSchemaIsUnavailable() throws SQLException {
+        Map<String, Collection<String>> fooSchemaTableNames = new 
LinkedHashMap<>(2, 1F);
+        fooSchemaTableNames.put("bar_schema", 
Collections.singleton("FOO_TBL1"));
+        fooSchemaTableNames.put("dbo", Collections.singleton("foo_tbl2"));
+        Map<String, Collection<String>> barSchemaTableNames = 
Collections.singletonMap("dbo", Arrays.asList("bar_tbl1", "bar_tbl2"));
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("dbo"));
+        
when(dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()).thenReturn(false);
+        try (
+                MockedStatic<DatabaseTypeEngine> databaseTypeEngine = 
mockStatic(DatabaseTypeEngine.class);
+                MockedConstruction<DatabaseTypeRegistry> ignoredRegistry = 
mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> {
+                    
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
+                });
+                MockedConstruction<SchemaMetaDataLoader> ignored = 
mockConstruction(SchemaMetaDataLoader.class, (mock, context) -> {
+                    when(mock.loadSchemaTableNames(anyString(), 
any(DataSource.class), anyCollection(), anyCollection()))
+                            .thenAnswer(invocation -> 
dataSourceMap.get("foo_ds") == invocation.getArgument(1) ? fooSchemaTableNames 
: barSchemaTableNames);
+                })) {
+            databaseTypeEngine.when(() -> 
DatabaseTypeEngine.getStorageType(dataSourceMap.get("foo_ds"))).thenReturn(databaseType);
+            databaseTypeEngine.when(() -> 
DatabaseTypeEngine.getStorageType(dataSourceMap.get("bar_ds"))).thenReturn(databaseType);
+            Map<String, Collection<DataNode>> actual = 
SingleTableDataNodeLoader.load(
+                    "foo_db", databaseType, dataSourceMap, 
Collections.emptyList(), Arrays.asList("foo_ds.foo_tbl1", "bar_ds.*"));
+            assertThat(new TreeSet<>(actual.keySet()), is(new 
TreeSet<>(Arrays.asList("FOO_TBL1", "bar_tbl1", "bar_tbl2"))));
+        }
+    }
+    
     @Test
     void assertLoadWithSameTableInDifferentSchemas() {
         Collection<String> configuredTables = 
Collections.singleton("foo_ds.target_schema.same_tbl");
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 ab85ebfd9cb..5e3e52615c2 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
@@ -133,16 +133,20 @@ class SingleRuleConfigurationDecoratorTest {
     
     @Test
     void assertDecorateThrowsWhenSpecifiedTableMismatch() {
-        DataNode dataNode = new DataNode("bar_ds", "foo_schema", "t_order");
-        Map<String, Collection<DataNode>> actualDataNodes = 
Collections.singletonMap(dataNode.getTableName(), 
Collections.singleton(dataNode));
-        Collection<String> splitTables = 
Collections.singletonList("*.foo_schema.t_order");
-        Collection<DataNode> configuredDataNodes = Collections.singleton(new 
DataNode("foo_ds", "foo_schema", "t_order"));
+        Map<String, Collection<DataNode>> actualDataNodes = new 
LinkedHashMap<>(2, 1F);
+        actualDataNodes.put("expanded_tbl", Collections.singleton(new 
DataNode("expand_ds", "foo_schema", "expanded_tbl")));
+        actualDataNodes.put("t_order", Collections.singleton(new 
DataNode("foo_ds", "bar_schema", "t_order")));
+        Collection<String> splitTables = 
Arrays.asList("expand_ds.foo_schema.*", "foo_ds.foo_schema.t_order");
+        Collection<DataNode> configuredDataNodes = Arrays.asList(
+                new DataNode("expand_ds", "foo_schema", 
SingleTableConstants.ASTERISK), new DataNode("foo_ds", "foo_schema", 
"t_order"));
         when(SingleTableDataNodeLoader.load(anyString(), anyMap(), 
anyCollection(), anyCollection(), anyMap())).thenReturn(actualDataNodes);
         mockSplitAndConvert(splitTables, configuredDataNodes, 
Collections.emptyList(), Collections.emptyList());
         Map<String, DataSource> dataSources = 
Collections.singletonMap("foo_ds", mock(DataSource.class));
-        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(Collections.singletonList("*.foo_schema.t_order"), 
null);
-        assertThrows(InvalidSingleRuleConfigurationException.class,
-                () -> decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig));
+        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(splitTables, null);
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(true)) {
+            assertThrows(InvalidSingleRuleConfigurationException.class,
+                    () -> decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig));
+        }
     }
     
     @Test
@@ -150,17 +154,19 @@ class SingleRuleConfigurationDecoratorTest {
         Map<String, Collection<DataNode>> actualDataNodes = new 
LinkedHashMap<>(3, 1F);
         actualDataNodes.put("feature_tbl", Collections.singleton(new 
DataNode("skip_ds", "foo_schema", "feature_tbl")));
         actualDataNodes.put("expanded_tbl", Collections.singleton(new 
DataNode("expand_ds", "foo_schema", "expanded_tbl")));
-        actualDataNodes.put("matched_tbl", Collections.singleton(new 
DataNode("bar_ds", "foo_schema", "matched_tbl")));
+        actualDataNodes.put("matched_tbl", Collections.singleton(new 
DataNode("bar_ds", "dbo", "matched_tbl")));
         when(SingleTableDataNodeLoader.load(anyString(), anyMap(), 
anyCollection(), anyCollection(), anyMap())).thenReturn(actualDataNodes);
-        Collection<String> splitTables = Arrays.asList("expand_ds.*", 
"bar_ds.bar_tbl");
+        Collection<String> splitTables = Arrays.asList("expand_ds.*", 
"bar_ds.matched_tbl");
         Collection<DataNode> configuredDataNodes = Arrays.asList(
-                new DataNode("expand_ds", "foo_schema", 
SingleTableConstants.ASTERISK),
-                new DataNode("bar_ds", "foo_schema", "matched_tbl"));
+                new DataNode("expand_ds", SingleTableConstants.ASTERISK, 
SingleTableConstants.ASTERISK),
+                new DataNode("bar_ds", SingleTableConstants.ASTERISK, 
"matched_tbl"));
         mockSplitAndConvert(splitTables, configuredDataNodes, 
Collections.emptyList(), Collections.singleton("feature_tbl"));
-        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(Arrays.asList("expand_ds.*", "bar_ds.bar_tbl"), null);
+        SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(splitTables, null);
         Map<String, DataSource> dataSources = 
Collections.singletonMap("foo_ds", mock(DataSource.class));
-        assertThat(decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig).getTables(),
-                contains("expand_ds.expanded_tbl", "bar_ds.matched_tbl"));
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(false)) {
+            assertThat(decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig).getTables(),
+                    contains("expand_ds.expanded_tbl", "bar_ds.matched_tbl"));
+        }
     }
     
     @Test
@@ -184,17 +190,19 @@ class SingleRuleConfigurationDecoratorTest {
     @Test
     void assertDecorateThrowsWhenExpandedNodeMismatch() {
         Collection<String> emptyTables = Collections.emptySet();
-        Map<String, Collection<DataNode>> actualDataNodes = 
Collections.singletonMap("t_order", Collections.singleton(new 
DataNode("other_ds", "foo_schema", "t_order")));
+        Map<String, Collection<DataNode>> actualDataNodes = 
Collections.singletonMap("t_order", Collections.singleton(new 
DataNode("other_ds", "dbo", "t_order")));
         when(SingleTableDataNodeLoader.load(anyString(), anyMap(), 
anyCollection(), anyCollection(), anyMap())).thenReturn(actualDataNodes);
         Collection<String> splitTables = Arrays.asList("expand_ds.*", 
"expand_ds.t_order");
         Collection<DataNode> configuredDataNodes = Arrays.asList(
-                new DataNode("expand_ds", "foo_schema", 
SingleTableConstants.ASTERISK),
-                new DataNode("expand_ds", "foo_schema", "t_order"));
+                new DataNode("expand_ds", SingleTableConstants.ASTERISK, 
SingleTableConstants.ASTERISK),
+                new DataNode("expand_ds", SingleTableConstants.ASTERISK, 
"t_order"));
         mockSplitAndConvert(splitTables, configuredDataNodes, emptyTables, 
emptyTables);
         SingleRuleConfiguration ruleConfig = new 
SingleRuleConfiguration(Arrays.asList("expand_ds.*", "expand_ds.t_order"), 
null);
         Map<String, DataSource> dataSources = 
Collections.singletonMap("foo_ds", mock(DataSource.class));
-        assertThrows(InvalidSingleRuleConfigurationException.class,
-                () -> decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig));
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(false)) {
+            assertThrows(InvalidSingleRuleConfigurationException.class,
+                    () -> decorator.decorate("foo_db", dataSources, 
Collections.singleton(mock(ShardingSphereRule.class, RETURNS_DEEP_STUBS)), 
ruleConfig));
+        }
     }
     
     @Test
diff --git 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
index f1a05e91523..27128f3ab46 100644
--- 
a/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
+++ 
b/kernel/single/core/src/test/java/org/apache/shardingsphere/single/rule/attribute/SingleMutableDataNodeRuleAttributeTest.java
@@ -105,7 +105,7 @@ class SingleMutableDataNodeRuleAttributeTest {
     @MethodSource("removeWithSchemaNamesArguments")
     void assertRemoveWithSchemaNames(final String name, final 
Collection<DataNode> initialDataNodes, final Collection<String> schemaNames,
                                      final int expectedDataNodeCount, final 
Collection<String> expectedTables, final boolean expectedTableInMap, final 
boolean expectedLogicTableExists) {
-        SingleRuleConfiguration configuration = new 
SingleRuleConfiguration(new 
LinkedList<>(createDataNodeStrings(initialDataNodes)), null);
+        SingleRuleConfiguration configuration = new 
SingleRuleConfiguration(new LinkedList<>(new 
LinkedHashSet<>(createDataNodeStrings(initialDataNodes))), null);
         Map<String, Collection<DataNode>> singleTableDataNodes = 
createSingleTableDataNodes(initialDataNodes);
         SingleTableMapperRuleAttribute tableMapperRuleAttribute = new 
SingleTableMapperRuleAttribute(singleTableDataNodes.values());
         SingleMutableDataNodeRuleAttribute ruleAttribute = 
createRuleAttribute(configuration, Collections.singleton("foo_ds"), 
singleTableDataNodes, tableMapperRuleAttribute);
@@ -194,6 +194,7 @@ class SingleMutableDataNodeRuleAttributeTest {
     private static Stream<Arguments> removeWithSchemaNamesArguments() {
         DataNode fooDataNode = new DataNode("foo_ds", "foo_schema", "foo_tbl");
         DataNode barDataNode = new DataNode("foo_ds", "bar_schema", "foo_tbl");
+        DataNode otherDataSourceNode = new DataNode("bar_ds", "bar_schema", 
"foo_tbl");
         return Stream.of(
                 Arguments.of("keep data node when schema not matched", 
Collections.singleton(fooDataNode), Collections.singleton("baz_schema"), 1,
                         
Collections.singleton(SingleTableLoadUtils.getDataNodeString(DATABASE_TYPE, 
"foo_ds", "foo_schema", "foo_tbl")), true, true),
@@ -201,6 +202,8 @@ class SingleMutableDataNodeRuleAttributeTest {
                         
Collections.singleton(SingleTableLoadUtils.getDataNodeString(DATABASE_TYPE, 
"foo_ds", "foo_schema", "foo_tbl")), true, true),
                 Arguments.of("remove matched schema and keep others", 
Arrays.asList(fooDataNode, barDataNode), Collections.singleton("foo_schema"), 1,
                         
Collections.singleton(SingleTableLoadUtils.getDataNodeString(DATABASE_TYPE, 
"foo_ds", "bar_schema", "foo_tbl")), true, true),
+                Arguments.of("remove matched schema and keep another data 
source", Arrays.asList(fooDataNode, otherDataSourceNode), 
Collections.singleton("foo_schema"), 1,
+                        
Collections.singleton(SingleTableLoadUtils.getDataNodeString(DATABASE_TYPE, 
"bar_ds", "bar_schema", "foo_tbl")), true, true),
                 Arguments.of("remove all matched schemas", 
Collections.singleton(fooDataNode), Collections.singleton("foo_schema"), 0, 
Collections.emptyList(), false, false));
     }
     
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 f646959551b..873c6ec4b28 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
@@ -100,11 +100,20 @@ class SingleTableLoadUtilsTest {
     
     @Test
     void assertGetAllTablesNodeStrWithSchema() {
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(true)) {
             assertThat(SingleTableLoadUtils.getAllTablesNodeStr(databaseType), 
is("*.*.*"));
         }
     }
     
+    @Test
+    void assertFormatBySchemaAvailabilityWhenDefaultSchemaExists() {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(false)) {
+            assertThat(SingleTableLoadUtils.getAllTablesNodeStr(databaseType), 
is("*.*.*"));
+            
assertThat(SingleTableLoadUtils.getAllTablesNodeStrFromDataSource(databaseType, 
"foo_ds", "foo_schema"), is("foo_ds.*"));
+            assertThat(SingleTableLoadUtils.getDataNodeString(databaseType, 
"foo_ds", "foo_schema", "foo_tbl"), is("foo_ds.foo_tbl"));
+        }
+    }
+    
     @Test
     void assertGetAllTablesNodeStrFromDataSource() {
         
assertThat(SingleTableLoadUtils.getAllTablesNodeStrFromDataSource(databaseType, 
"foo_ds", "foo_schema"), is("foo_ds.*"));
@@ -112,7 +121,7 @@ class SingleTableLoadUtilsTest {
     
     @Test
     void assertGetAllTablesNodeStrFromDataSourceWithSchema() {
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(true)) {
             
assertThat(SingleTableLoadUtils.getAllTablesNodeStrFromDataSource(databaseType, 
"foo_ds", "foo_schema"), is("foo_ds.foo_schema.*"));
         }
     }
@@ -124,14 +133,15 @@ class SingleTableLoadUtilsTest {
     
     @Test
     void assertGetDataNodeStringWithSchema() {
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaRegistry(true)) {
             assertThat(SingleTableLoadUtils.getDataNodeString(databaseType, 
"foo_ds", "foo_schema", "foo_tbl"), is("foo_ds.foo_schema.foo_tbl"));
         }
     }
     
-    private MockedConstruction<DatabaseTypeRegistry> mockSchemaRegistry() {
+    private MockedConstruction<DatabaseTypeRegistry> mockSchemaRegistry(final 
boolean schemaAvailable) {
         DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
         
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("foo_schema"));
+        
when(dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()).thenReturn(schemaAvailable);
         return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData));
     }
     
diff --git 
a/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutor.java
 
b/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutor.java
index f9fc406687b..75768397294 100644
--- 
a/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutor.java
+++ 
b/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutor.java
@@ -68,9 +68,12 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
             database.checkStorageUnitsExisted(storageUnitNames);
         }
         String defaultSchemaName = database.getDefaultSchemaName();
-        checkShouldNotExistLogicTables(sqlStatement, defaultSchemaName);
+        DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(database.getProtocolType()).getDialectDatabaseMetaData();
+        boolean isSchemaAvailable = 
dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable();
+        checkShouldNotExistLogicTables(sqlStatement, defaultSchemaName, 
isSchemaAvailable);
         if (!storageUnitNames.isEmpty()) {
-            checkShouldExistActualTables(sqlStatement, storageUnitNames, 
defaultSchemaName);
+            boolean isSchemaWildcard = !isSchemaAvailable && 
dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().isPresent();
+            checkShouldExistActualTables(sqlStatement, storageUnitNames, 
defaultSchemaName, isSchemaWildcard);
         }
     }
     
@@ -78,24 +81,22 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
         return 
sqlStatement.getTables().stream().map(SingleTableSegment::getStorageUnitName).filter(each
 -> !SingleTableConstants.ASTERISK.equals(each)).collect(Collectors.toSet());
     }
     
-    private void checkShouldNotExistLogicTables(final LoadSingleTableStatement 
sqlStatement, final String defaultSchemaName) {
+    private void checkShouldNotExistLogicTables(final LoadSingleTableStatement 
sqlStatement, final String defaultSchemaName, final boolean isSchemaAvailable) {
         Collection<SingleTableSegment> tableSegments = 
sqlStatement.getTables();
-        DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(database.getProtocolType()).getDialectDatabaseMetaData();
-        boolean isSchemaSupportedDatabaseType = 
dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().isPresent();
         ShardingSphereSchema schema = database.getSchema(defaultSchemaName);
         for (SingleTableSegment each : tableSegments) {
-            checkTableNodeFormat(isSchemaSupportedDatabaseType, each);
+            checkTableNodeFormat(isSchemaAvailable, each);
             if (!SingleTableConstants.ASTERISK.equals(each.getTableName())) {
                 
ShardingSpherePreconditions.checkState(!schema.containsTable(each.getTableName()),
 () -> new TableExistsException(each.getTableName()));
             }
         }
     }
     
-    private void checkTableNodeFormat(final boolean 
isSchemaSupportedDatabaseType, final SingleTableSegment singleTableSegment) {
+    private void checkTableNodeFormat(final boolean isSchemaAvailable, final 
SingleTableSegment singleTableSegment) {
         if 
(SingleTableConstants.ALL_TABLES.equals(singleTableSegment.toString()) || 
SingleTableConstants.ALL_SCHEMA_TABLES.equals(singleTableSegment.toString())) {
             return;
         }
-        if (isSchemaSupportedDatabaseType) {
+        if (isSchemaAvailable) {
             
ShardingSpherePreconditions.checkState(singleTableSegment.getSchemaName().isPresent(),
                     () -> new 
InvalidDataNodeFormatException(singleTableSegment.toString(), "Current database 
is schema required, please use format `db.schema.table`"));
         } else {
@@ -104,7 +105,8 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
         }
     }
     
-    private void checkShouldExistActualTables(final LoadSingleTableStatement 
sqlStatement, final Collection<String> storageUnitNames, final String 
defaultSchemaName) {
+    private void checkShouldExistActualTables(final LoadSingleTableStatement 
sqlStatement, final Collection<String> storageUnitNames, final String 
defaultSchemaName,
+                                              final boolean isSchemaWildcard) {
         Map<String, DataSource> dataSourceMap = 
database.getResourceMetaData().getStorageUnits().entrySet()
                 .stream().collect(Collectors.toMap(Entry::getKey, entry -> 
entry.getValue().getDataSource()));
         Map<String, DataSource> aggregatedDataSourceMap = 
PhysicalDataSourceAggregator.getAggregatedDataSources(dataSourceMap, 
database.getRuleMetaData().getRules());
@@ -122,10 +124,12 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
                         () -> new TableNotFoundException(tableName, 
storageUnitName));
                 DatabaseType storageUnitDatabaseType = 
storageUnitDatabaseTypes.get(storageUnitName);
                 Map<String, Collection<String>> schemaTableNames = 
actualTableNodes.get(storageUnitName);
-                Collection<String> actualTableNames = 
each.getSchemaName().map(schemaName -> 
schemaTableNames.getOrDefault(schemaName, Collections.emptySet()))
-                        .orElseGet(() -> 
getDefaultSchemaTableNames(schemaTableNames, defaultSchemaName, 
storageUnitName, storageUnitDatabaseType,
-                                aggregatedDataSourceMap.get(storageUnitName), 
schemaPolicies));
-                
ShardingSpherePreconditions.checkState(actualTableNames.contains(tableName), () 
-> new TableNotFoundException(tableName, storageUnitName));
+                boolean tableExists = each.getSchemaName().map(schemaName -> 
schemaTableNames.getOrDefault(schemaName, 
Collections.emptySet()).contains(tableName))
+                        .orElseGet(() -> isSchemaWildcard
+                                ? 
schemaTableNames.values().stream().anyMatch(tableNames -> 
tableNames.contains(tableName))
+                                : getDefaultSchemaTableNames(schemaTableNames, 
defaultSchemaName, storageUnitName, storageUnitDatabaseType,
+                                        
aggregatedDataSourceMap.get(storageUnitName), 
schemaPolicies).contains(tableName));
+                ShardingSpherePreconditions.checkState(tableExists, () -> new 
TableNotFoundException(tableName, storageUnitName));
             }
         }
     }
diff --git 
a/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutor.java
 
b/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutor.java
index a788dc92b2b..2134cc036d4 100644
--- 
a/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutor.java
+++ 
b/kernel/single/distsql/handler/src/main/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutor.java
@@ -34,6 +34,7 @@ import 
org.apache.shardingsphere.single.config.SingleRuleConfiguration;
 import 
org.apache.shardingsphere.single.distsql.statement.rdl.UnloadSingleTableStatement;
 import org.apache.shardingsphere.single.exception.SingleTableNotFoundException;
 import org.apache.shardingsphere.single.rule.SingleRule;
+import org.apache.shardingsphere.single.util.SingleTableLoadUtils;
 
 import java.util.Collection;
 import java.util.Collections;
@@ -86,7 +87,8 @@ public final class UnloadSingleTableExecutor implements 
DatabaseRuleAlterExecuto
     private void checkTableRuleExist(final String databaseName, final 
DatabaseType databaseType, final Collection<DataNode> dataNodes, final String 
tableName) {
         ShardingSpherePreconditions.checkNotEmpty(dataNodes, () -> new 
MissingRequiredRuleException("Single", databaseName, tableName));
         DataNode dataNode = dataNodes.iterator().next();
-        
ShardingSpherePreconditions.checkContains(rule.getConfiguration().getTables(), 
dataNode.format(databaseType), () -> new MissingRequiredRuleException("Single", 
databaseName, tableName));
+        String dataNodeString = 
SingleTableLoadUtils.getDataNodeString(databaseType, 
dataNode.getDataSourceName(), dataNode.getSchemaName(), 
dataNode.getTableName());
+        
ShardingSpherePreconditions.checkContains(rule.getConfiguration().getTables(), 
dataNodeString, () -> new MissingRequiredRuleException("Single", databaseName, 
tableName));
     }
     
     @Override
diff --git 
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
 
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
index 610ed7eb3c3..e1017b4788c 100644
--- 
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
+++ 
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/LoadSingleTableExecutorTest.java
@@ -96,16 +96,16 @@ class LoadSingleTableExecutorTest {
     
     @ParameterizedTest(name = "{0}")
     @MethodSource("assertCheckBeforeUpdateWithPreValidationFailureArguments")
-    void assertCheckBeforeUpdateWithPreValidationFailure(final String name, 
final boolean schemaSupported, final SingleTableSegment tableSegment,
+    void assertCheckBeforeUpdateWithPreValidationFailure(final String name, 
final boolean schemaAvailable, final SingleTableSegment tableSegment,
                                                          final boolean 
tableExists, final Class<? extends RuntimeException> expectedException) {
         prepareStorageUnits();
-        prepareSchema(tableExists, schemaSupported ? "foo_schema" : "foo_db");
-        if (schemaSupported) {
+        prepareSchema(tableExists, schemaAvailable ? "foo_schema" : "foo_db");
+        if (schemaAvailable) {
             when(database.getDefaultSchemaName()).thenReturn("foo_schema");
         }
         LoadSingleTableStatement sqlStatement = new 
LoadSingleTableStatement(Collections.singleton(tableSegment));
-        if (schemaSupported) {
-            try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaSupportedDatabaseTypeRegistry()) {
+        if (schemaAvailable) {
+            try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaDatabaseTypeRegistry(true)) {
                 assertThrows(expectedException, () -> 
executor.checkBeforeUpdate(sqlStatement));
             }
         } else {
@@ -132,7 +132,7 @@ class LoadSingleTableExecutorTest {
     void assertCheckBeforeUpdateWithSchemaSupportedDatabaseType() {
         
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("foo_schema", 
Collections.singleton("foo_tbl")));
         LoadSingleTableStatement sqlStatement = new 
LoadSingleTableStatement(Arrays.asList(new SingleTableSegment("foo_ds", 
"foo_schema", "foo_tbl"), new SingleTableSegment("*", "*")));
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaSupportedDatabaseTypeRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaDatabaseTypeRegistry(true)) {
             when(database.getDefaultSchemaName()).thenReturn("foo_schema");
             prepareSchema(false, "foo_schema");
             assertDoesNotThrow(() -> executor.checkBeforeUpdate(sqlStatement));
@@ -146,11 +146,27 @@ class LoadSingleTableExecutorTest {
         assertDoesNotThrow(() -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_tbl")))));
     }
     
+    @Test
+    void assertCheckBeforeUpdateRejectsNonDefaultSchemaWhenSchemaUnavailable() 
{
+        
when(DatabaseTypeEngine.getStorageType(any(DataSource.class))).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
+        
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("bar_schema", 
Collections.singleton("foo_tbl")));
+        assertThrows(TableNotFoundException.class,
+                () -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_tbl")))));
+    }
+    
+    @Test
+    void assertCheckBeforeUpdateWithDefaultSchemaWhenSchemaUnavailable() {
+        
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("bar_schema", 
Collections.singleton("foo_tbl")));
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaDatabaseTypeRegistry(false)) {
+            assertDoesNotThrow(() -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_tbl")))));
+        }
+    }
+    
     @Test
     void 
assertCheckBeforeUpdateWithExplicitSchemaAndDifferentProtocolAndStorageTypes() {
         
when(DatabaseTypeEngine.getStorageType(any(DataSource.class))).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
         
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("foo_schema", 
Collections.singleton("foo_tbl")));
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaSupportedDatabaseTypeRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaDatabaseTypeRegistry(true)) {
             assertDoesNotThrow(() -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_schema", "foo_tbl")))));
         }
     }
@@ -159,7 +175,7 @@ class LoadSingleTableExecutorTest {
     void 
assertCheckBeforeUpdateWithMismatchedExplicitSchemaAndDifferentProtocolAndStorageTypes()
 {
         
when(DatabaseTypeEngine.getStorageType(any(DataSource.class))).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
         
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("FOO_SCHEMA", 
Collections.singleton("foo_tbl")));
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaSupportedDatabaseTypeRegistry()) {
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockSchemaDatabaseTypeRegistry(true)) {
             assertThrows(TableNotFoundException.class,
                     () -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_schema", "foo_tbl")))));
         }
@@ -214,11 +230,11 @@ class LoadSingleTableExecutorTest {
         }
     }
     
-    private MockedConstruction<DatabaseTypeRegistry> 
mockSchemaSupportedDatabaseTypeRegistry() {
+    private MockedConstruction<DatabaseTypeRegistry> 
mockSchemaDatabaseTypeRegistry(final boolean schemaAvailable) {
         DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
         
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("foo_schema"));
+        
when(dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()).thenReturn(schemaAvailable);
         return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
{
-            when(mock.getDefaultSchemaName("foo_db")).thenReturn("foo_schema");
             
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
         });
     }
diff --git 
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutorTest.java
 
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutorTest.java
index 6545e5e7817..66b594faf8c 100644
--- 
a/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutorTest.java
+++ 
b/kernel/single/distsql/handler/src/test/java/org/apache/shardingsphere/single/distsql/handler/update/UnloadSingleTableExecutorTest.java
@@ -17,7 +17,9 @@
 
 package org.apache.shardingsphere.single.distsql.handler.update;
 
+import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
 import 
org.apache.shardingsphere.database.exception.core.exception.syntax.table.NoSuchTableException;
 import 
org.apache.shardingsphere.distsql.handler.engine.update.rdl.rule.spi.database.DatabaseRuleDefinitionExecutor;
 import org.apache.shardingsphere.infra.datanode.DataNode;
@@ -41,6 +43,7 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Mock;
+import org.mockito.MockedConstruction;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.mockito.junit.jupiter.MockitoSettings;
 import org.mockito.quality.Strictness;
@@ -63,7 +66,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.when;
 
 @ExtendWith(MockitoExtension.class)
@@ -108,10 +113,17 @@ class UnloadSingleTableExecutorTest {
     
     @ParameterizedTest(name = "{0}")
     @MethodSource("assertCheckBeforeUpdateWithSuccessArguments")
-    void assertCheckBeforeUpdate(final String name, final 
UnloadSingleTableStatement sqlStatement, final Collection<String> allTables,
+    void assertCheckBeforeUpdate(final String name, final boolean 
schemaAvailable, final UnloadSingleTableStatement sqlStatement, final 
Collection<String> allTables,
                                  final Collection<String> singleTables, final 
Map<String, Collection<DataNode>> tableDataNodes, final Collection<String> 
configuredTables) {
         prepareCheckBeforeUpdateContext(allTables, singleTables, 
tableDataNodes, configuredTables);
-        assertDoesNotThrow(() -> executor.checkBeforeUpdate(sqlStatement));
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("foo_schema"));
+        
when(dialectDatabaseMetaData.getSchemaOption().isSchemaAvailable()).thenReturn(schemaAvailable);
+        try (
+                MockedConstruction<DatabaseTypeRegistry> ignored = 
mockConstruction(
+                        DatabaseTypeRegistry.class, (mock, context) -> 
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData))) {
+            assertDoesNotThrow(() -> executor.checkBeforeUpdate(sqlStatement));
+        }
     }
     
     @Test
@@ -179,13 +191,17 @@ class UnloadSingleTableExecutorTest {
         Map<String, Collection<DataNode>> multipleTableNodes = new 
HashMap<>(2, 1F);
         multipleTableNodes.put("foo_tbl", Collections.singleton(new 
DataNode("foo_ds.foo_tbl")));
         multipleTableNodes.put("bar_tbl", Collections.singleton(new 
DataNode("foo_ds.foo_schema.bar_tbl")));
+        Map<String, Collection<DataNode>> schemaTableNodes = new HashMap<>(1, 
1F);
+        schemaTableNodes.put("foo_tbl", Collections.singleton(new 
DataNode("foo_ds.foo_schema.foo_tbl")));
         return Stream.of(
-                Arguments.of("unload all tables bypasses table checks", new 
UnloadSingleTableStatement(true, Collections.emptyList()),
+                Arguments.of("unload all tables bypasses table checks", false, 
new UnloadSingleTableStatement(true, Collections.emptyList()),
                         Collections.emptyList(), Collections.emptyList(), 
Collections.emptyMap(), Collections.emptyList()),
-                Arguments.of("valid single table passes checks", new 
UnloadSingleTableStatement(false, Collections.singletonList("foo_tbl")),
+                Arguments.of("valid single table passes checks", false, new 
UnloadSingleTableStatement(false, Collections.singletonList("foo_tbl")),
                         Collections.singleton("foo_tbl"), 
Collections.singleton("foo_tbl"), singleTableNodes, 
Collections.singleton("foo_ds.foo_tbl")),
-                Arguments.of("valid multiple tables pass checks", new 
UnloadSingleTableStatement(false, Arrays.asList("foo_tbl", "bar_tbl")),
-                        Arrays.asList("foo_tbl", "bar_tbl"), 
Arrays.asList("foo_tbl", "bar_tbl"), multipleTableNodes, 
Arrays.asList("foo_ds.foo_tbl", "foo_ds.bar_tbl")));
+                Arguments.of("valid multiple tables pass checks", false, new 
UnloadSingleTableStatement(false, Arrays.asList("foo_tbl", "bar_tbl")),
+                        Arrays.asList("foo_tbl", "bar_tbl"), 
Arrays.asList("foo_tbl", "bar_tbl"), multipleTableNodes, 
Arrays.asList("foo_ds.foo_tbl", "foo_ds.bar_tbl")),
+                Arguments.of("valid schema table passes checks", true, new 
UnloadSingleTableStatement(false, Collections.singletonList("foo_tbl")),
+                        Collections.singleton("foo_tbl"), 
Collections.singleton("foo_tbl"), schemaTableNodes, 
Collections.singleton("foo_ds.foo_schema.foo_tbl")));
     }
     
     private static Stream<Arguments> 
assertBuildToBeAlteredRuleConfigurationArguments() {

Reply via email to