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 c8667e5f003 Add identifier matching to support schema case policies 
(#39170)
c8667e5f003 is described below

commit c8667e5f003f13ef9e9775aad6346dbcc896e6ec
Author: Haoran Meng <[email protected]>
AuthorDate: Sat Jul 18 21:29:24 2026 +0800

    Add identifier matching to support schema case policies (#39170)
---
 .../identifier/IdentifierNormalizeEngine.java      | 19 +++++++++++---
 .../identifier/IdentifierNormalizeEngineTest.java  | 22 +++++++++++++---
 .../handler/update/LoadSingleTableExecutor.java    | 25 ++++++++++++++----
 .../update/LoadSingleTableExecutorTest.java        | 30 ++++++++++++++--------
 4 files changed, 73 insertions(+), 23 deletions(-)

diff --git 
a/database/connector/core/src/main/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngine.java
 
b/database/connector/core/src/main/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngine.java
index c14c3ab2c78..89d7178e010 100644
--- 
a/database/connector/core/src/main/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngine.java
+++ 
b/database/connector/core/src/main/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngine.java
@@ -24,6 +24,8 @@ import 
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoa
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 
 import javax.sql.DataSource;
+import java.util.Collection;
+import java.util.Optional;
 
 /**
  * Identifier normalize engine.
@@ -54,9 +56,6 @@ public final class IdentifierNormalizeEngine {
      * @return normalized identifier
      */
     public static String normalize(final IdentifierCasePolicy policy, final 
String identifier) {
-        if (null == identifier) {
-            return null;
-        }
         QuoteCharacter quoteCharacter = 
QuoteCharacter.getQuoteCharacter(identifier);
         String unwrappedIdentifier = quoteCharacter.unwrap(identifier);
         if (QuoteCharacter.NONE != quoteCharacter) {
@@ -64,4 +63,18 @@ public final class IdentifierNormalizeEngine {
         }
         return LookupMode.NORMALIZED == 
policy.getLookupMode(QuoteCharacter.NONE) ? 
policy.normalize(unwrappedIdentifier) : unwrappedIdentifier;
     }
+    
+    /**
+     * Find matched stored identifier.
+     *
+     * @param storedNames stored identifier names
+     * @param policy identifier case policy
+     * @param identifier identifier
+     * @return matched stored identifier
+     */
+    public static Optional<String> findMatchedIdentifier(final 
Collection<String> storedNames, final IdentifierCasePolicy policy, final String 
identifier) {
+        QuoteCharacter quoteCharacter = 
QuoteCharacter.getQuoteCharacter(identifier);
+        String unwrappedIdentifier = quoteCharacter.unwrap(identifier);
+        return storedNames.stream().filter(each -> policy.matches(each, 
unwrappedIdentifier, quoteCharacter)).findFirst();
+    }
 }
diff --git 
a/database/connector/core/src/test/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngineTest.java
 
b/database/connector/core/src/test/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngineTest.java
index a3f50b9a7ea..8b2bd6f5dbc 100644
--- 
a/database/connector/core/src/test/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngineTest.java
+++ 
b/database/connector/core/src/test/java/org/apache/shardingsphere/database/connector/core/metadata/identifier/IdentifierNormalizeEngineTest.java
@@ -21,9 +21,11 @@ import 
org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.junit.jupiter.api.Test;
 
+import java.util.Collections;
+import java.util.Optional;
+
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertNull;
 
 class IdentifierNormalizeEngineTest {
     
@@ -48,8 +50,20 @@ class IdentifierNormalizeEngineTest {
     }
     
     @Test
-    void assertNormalizeNullIdentifier() {
-        IdentifierCasePolicy policy = 
IdentifierNormalizeEngine.resolvePolicy(databaseType, null, 
IdentifierScope.TABLE);
-        assertNull(IdentifierNormalizeEngine.normalize(policy, null));
+    void assertFindMatchedIdentifierWithUnquotedIdentifier() {
+        IdentifierCasePolicy policy = 
IdentifierCasePolicyFactory.newInsensitivePolicySet().getPolicy(IdentifierScope.TABLE);
+        
assertThat(IdentifierNormalizeEngine.findMatchedIdentifier(Collections.singletonList("Foo_Tbl"),
 policy, "foo_tbl"), is(Optional.of("Foo_Tbl")));
+    }
+    
+    @Test
+    void assertFindMatchedIdentifierWithQuotedIdentifier() {
+        IdentifierCasePolicy policy = 
IdentifierCasePolicyFactory.newInsensitivePolicySet().getPolicy(IdentifierScope.TABLE);
+        
assertThat(IdentifierNormalizeEngine.findMatchedIdentifier(Collections.singletonList("Foo_Tbl"),
 policy, "\"Foo_Tbl\""), is(Optional.of("Foo_Tbl")));
+    }
+    
+    @Test
+    void assertFindMatchedIdentifierWhenNotMatched() {
+        IdentifierCasePolicy policy = 
IdentifierCasePolicyFactory.newSensitivePolicySet().getPolicy(IdentifierScope.TABLE);
+        
assertThat(IdentifierNormalizeEngine.findMatchedIdentifier(Collections.singletonList("Foo_Tbl"),
 policy, "foo_tbl"), is(Optional.empty()));
     }
 }
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 0a627bdefc4..f9fc406687b 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
@@ -19,6 +19,9 @@ package 
org.apache.shardingsphere.single.distsql.handler.update;
 
 import lombok.Setter;
 import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
+import 
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicy;
+import 
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierNormalizeEngine;
+import 
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
 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.TableExistsException;
@@ -110,6 +113,7 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
                 String.join(",", invalidDataSources), String.join(",", 
aggregatedDataSourceMap.keySet()))));
         Map<String, DatabaseType> storageUnitDatabaseTypes = 
getStorageUnitDatabaseTypes(storageUnitNames, aggregatedDataSourceMap);
         Map<String, Map<String, Collection<String>>> actualTableNodes = 
getActualTableNodes(storageUnitNames, aggregatedDataSourceMap, 
storageUnitDatabaseTypes);
+        Map<String, IdentifierCasePolicy> schemaPolicies = new 
LinkedHashMap<>(actualTableNodes.size());
         for (SingleTableSegment each : sqlStatement.getTables()) {
             String tableName = each.getTableName();
             if (!SingleTableConstants.ASTERISK.equals(tableName)) {
@@ -117,15 +121,26 @@ public final class LoadSingleTableExecutor implements 
DatabaseRuleCreateExecutor
                 
ShardingSpherePreconditions.checkState(actualTableNodes.containsKey(storageUnitName),
                         () -> new TableNotFoundException(tableName, 
storageUnitName));
                 DatabaseType storageUnitDatabaseType = 
storageUnitDatabaseTypes.get(storageUnitName);
-                String actualSchemaName = 
isSameDatabaseType(database.getProtocolType(), storageUnitDatabaseType) ? 
defaultSchemaName
-                        : new 
DatabaseTypeRegistry(storageUnitDatabaseType).formatIdentifierPattern(defaultSchemaName);
-                String schemaName = each.getSchemaName().isPresent() ? 
each.getSchemaName().get() : actualSchemaName;
-                
ShardingSpherePreconditions.checkState(actualTableNodes.get(storageUnitName).get(schemaName).contains(tableName),
-                        () -> new TableNotFoundException(tableName, 
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));
             }
         }
     }
     
+    private Collection<String> getDefaultSchemaTableNames(final Map<String, 
Collection<String>> schemaTableNames, final String schemaName, final String 
storageUnitName,
+                                                          final DatabaseType 
storageUnitDatabaseType, final DataSource dataSource,
+                                                          final Map<String, 
IdentifierCasePolicy> schemaPolicies) {
+        if (isSameDatabaseType(database.getProtocolType(), 
storageUnitDatabaseType)) {
+            return schemaTableNames.getOrDefault(schemaName, 
Collections.emptySet());
+        }
+        IdentifierCasePolicy schemaPolicy =
+                schemaPolicies.computeIfAbsent(storageUnitName, ignored -> 
IdentifierNormalizeEngine.resolvePolicy(storageUnitDatabaseType, dataSource, 
IdentifierScope.SCHEMA));
+        return 
IdentifierNormalizeEngine.findMatchedIdentifier(schemaTableNames.keySet(), 
schemaPolicy, 
schemaName).map(schemaTableNames::get).orElseGet(Collections::emptySet);
+    }
+    
     private Map<String, Map<String, Collection<String>>> 
getActualTableNodes(final Collection<String> storageUnitNames, final 
Map<String, DataSource> aggregatedDataSourceMap,
                                                                              
final Map<String, DatabaseType> storageUnitDatabaseTypes) {
         Map<String, Map<String, Collection<String>>> result = new 
LinkedHashMap<>(storageUnitNames.size(), 1F);
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 a5bd95a4a35..610ed7eb3c3 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
@@ -143,8 +143,25 @@ class LoadSingleTableExecutorTest {
     void assertCheckBeforeUpdateWithDifferentProtocolAndStorageTypes() {
         
when(DatabaseTypeEngine.getStorageType(any(DataSource.class))).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
         
prepareActualTableValidationScenario(Collections.singletonMap("foo_ds", new 
MockedDataSource()), Collections.singletonMap("FOO_DB", 
Collections.singleton("foo_tbl")));
-        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockStorageDatabaseTypeRegistry()) {
-            assertDoesNotThrow(() -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_tbl")))));
+        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()) {
+            assertDoesNotThrow(() -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_schema", "foo_tbl")))));
+        }
+    }
+    
+    @Test
+    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()) {
+            assertThrows(TableNotFoundException.class,
+                    () -> executor.checkBeforeUpdate(new 
LoadSingleTableStatement(Collections.singletonList(new 
SingleTableSegment("foo_ds", "foo_schema", "foo_tbl")))));
         }
     }
     
@@ -206,15 +223,6 @@ class LoadSingleTableExecutorTest {
         });
     }
     
-    private MockedConstruction<DatabaseTypeRegistry> 
mockStorageDatabaseTypeRegistry() {
-        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
-        return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
{
-            when(mock.getDefaultSchemaName("foo_db")).thenReturn("foo_db");
-            when(mock.formatIdentifierPattern("foo_db")).thenReturn("FOO_DB");
-            
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
-        });
-    }
-    
     private static Stream<Arguments> 
assertCheckBeforeUpdateWithPreValidationFailureArguments() {
         return Stream.of(
                 Arguments.of("schema unsupported rejects schema name", false, 
new SingleTableSegment("foo_ds", "foo_schema", "foo_tbl"), false, 
InvalidDataNodeFormatException.class),

Reply via email to