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 ea9c85c26a3 Refine MCP architecture boundary and sharding request
merge (#39036)
ea9c85c26a3 is described below
commit ea9c85c26a3b6773b574f6f5e8ac8a44dceae0ce
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Jul 7 20:05:10 2026 +0800
Refine MCP architecture boundary and sharding request merge (#39036)
- cover all MCP feature packages in the generic module boundary test
- preserve generic workflow request fields when merging sharding requests
- reuse descriptor resource swap construction for fixed and template
resources
---
.../core/workflow/WorkflowProxyQueryService.java | 4 +-
.../capability/MCPDatabaseCapabilityOption.java | 61 ++++++++
.../database/capability/MCPDatabaseDialect.java | 160 +++++++++++++++++++++
.../ClickHouseMCPDatabaseCapabilityOption.java | 8 ++
.../FirebirdMCPDatabaseCapabilityOption.java | 17 +++
.../dialect/HiveMCPDatabaseCapabilityOption.java | 14 ++
.../MariaDBMCPDatabaseCapabilityOption.java | 35 +++++
.../dialect/MySQLMCPDatabaseCapabilityOption.java | 26 ++++
.../OpenGaussMCPDatabaseCapabilityOption.java | 34 +++++
.../dialect/OracleMCPDatabaseCapabilityOption.java | 16 +++
.../PostgreSQLMCPDatabaseCapabilityOption.java | 34 +++++
.../dialect/PrestoMCPDatabaseCapabilityOption.java | 8 ++
.../SQLServerMCPDatabaseCapabilityOption.java | 23 +++
.../metadata/jdbc/MCPJdbcMetadataLoader.java | 78 ++--------
.../support/workflow/service/WorkflowSQLUtils.java | 70 +++------
.../service/WorkflowValidationSupport.java | 10 --
.../capability/MCPDatabaseDialectTest.java | 147 +++++++++++++++++++
.../jdbc/AbstractMCPJdbcMetadataLoaderTest.java | 17 +--
.../workflow/service/WorkflowSQLUtilsTest.java | 17 ++-
.../service/WorkflowValidationSupportTest.java | 4 +-
20 files changed, 641 insertions(+), 142 deletions(-)
diff --git
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowProxyQueryService.java
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowProxyQueryService.java
index a7b42b97770..47ca1992957 100644
---
a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowProxyQueryService.java
+++
b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/workflow/WorkflowProxyQueryService.java
@@ -23,6 +23,7 @@ import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPUnavailableExcept
import org.apache.shardingsphere.mcp.core.session.MCPSessionManager;
import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseCapability;
import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseCapabilityProvider;
+import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseDialect;
import
org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
import
org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureQueryFacade;
import org.apache.shardingsphere.mcp.support.workflow.service.WorkflowSQLUtils;
@@ -147,8 +148,7 @@ public final class WorkflowProxyQueryService implements
MCPFeatureQueryFacade {
return false;
}
String databaseType =
databaseCapabilityProvider.provide(databaseName).map(MCPDatabaseCapability::getDatabaseType).orElse("");
- return "MySQL".equalsIgnoreCase(databaseType) ||
"MariaDB".equalsIgnoreCase(databaseType)
- || "PostgreSQL".equalsIgnoreCase(databaseType) ||
"openGauss".equalsIgnoreCase(databaseType);
+ return
MCPDatabaseDialect.of(databaseType).isInformationSchemaColumnSchemaFilterRequired();
}
private Connection openConnection(final String databaseName) throws
SQLException {
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityOption.java
index f51155d9d8b..df833532134 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityOption.java
@@ -17,9 +17,16 @@
package org.apache.shardingsphere.mcp.support.database.capability;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import org.apache.shardingsphere.infra.spi.annotation.SingletonSPI;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPI;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option.
*/
@@ -76,6 +83,60 @@ public interface MCPDatabaseCapabilityOption extends
TypedSPI {
*/
boolean isSequenceSupported();
+ /**
+ * Get identifier quote character.
+ *
+ * @return identifier quote character
+ */
+ default QuoteCharacter getIdentifierQuoteCharacter() {
+ return QuoteCharacter.QUOTE;
+ }
+
+ /**
+ * Get identifier case policy set.
+ *
+ * @return identifier case policy set
+ */
+ default IdentifierCasePolicySet getIdentifierCasePolicySet() {
+ return IdentifierCasePolicyFactory.newSensitivePolicySet();
+ }
+
+ /**
+ * Judge whether unquoted identifiers are folded by database metadata
lookup.
+ *
+ * @return whether unquoted identifiers are folded
+ */
+ default boolean isUnquotedIdentifierCaseFolded() {
+ return false;
+ }
+
+ /**
+ * Get system schemas.
+ *
+ * @return system schemas
+ */
+ default Collection<String> getSystemSchemas() {
+ return List.of();
+ }
+
+ /**
+ * Get sequence metadata query.
+ *
+ * @return sequence metadata query
+ */
+ default Optional<String> getSequenceQuery() {
+ return Optional.empty();
+ }
+
+ /**
+ * Judge whether information_schema column lookup should filter
table_schema.
+ *
+ * @return whether information_schema column lookup should filter
table_schema
+ */
+ default boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return false;
+ }
+
@Override
String getType();
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialect.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialect.java
new file mode 100644
index 00000000000..be92283c0d8
--- /dev/null
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialect.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.mcp.support.database.capability;
+
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicy;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * MCP database dialect capabilities.
+ */
+public final class MCPDatabaseDialect {
+
+ private static final Collection<String> LEGACY_SYSTEM_SCHEMAS = List.of(
+ "information_schema", "mysql", "performance_schema", "pg_catalog",
"shardingsphere", "sys", "system_lobs");
+
+ private final String databaseType;
+
+ private final Optional<MCPDatabaseCapabilityOption> option;
+
+ private MCPDatabaseDialect(final String databaseType, final
Optional<MCPDatabaseCapabilityOption> option) {
+ this.databaseType = databaseType;
+ this.option = option;
+ }
+
+ /**
+ * Create MCP database dialect capabilities.
+ *
+ * @param databaseType database type
+ * @return MCP database dialect capabilities
+ */
+ public static MCPDatabaseDialect of(final String databaseType) {
+ String actualDatabaseType = trimToEmpty(databaseType);
+ Optional<MCPDatabaseCapabilityOption> option =
actualDatabaseType.isEmpty()
+ ? Optional.empty()
+ :
TypedSPILoader.findService(MCPDatabaseCapabilityOption.class,
actualDatabaseType);
+ return new MCPDatabaseDialect(actualDatabaseType, option);
+ }
+
+ /**
+ * Get identifier quote character.
+ *
+ * @return identifier quote character
+ */
+ public QuoteCharacter getIdentifierQuoteCharacter() {
+ return
option.map(MCPDatabaseCapabilityOption::getIdentifierQuoteCharacter).orElseGet(this::getFallbackIdentifierQuoteCharacter);
+ }
+
+ /**
+ * Get identifier case policy.
+ *
+ * @param identifierScope identifier scope
+ * @return identifier case policy
+ */
+ public IdentifierCasePolicy getIdentifierCasePolicy(final IdentifierScope
identifierScope) {
+ return
option.map(MCPDatabaseCapabilityOption::getIdentifierCasePolicySet)
+
.orElseGet(IdentifierCasePolicyFactory::newSensitivePolicySet).getPolicy(identifierScope);
+ }
+
+ /**
+ * Get default schema semantics.
+ *
+ * @return default schema semantics
+ */
+ public SchemaSemantics getDefaultSchemaSemantics() {
+ return
option.map(MCPDatabaseCapabilityOption::getDefaultSchemaSemantics).orElse(SchemaSemantics.NATIVE_SCHEMA);
+ }
+
+ /**
+ * Judge whether unquoted identifiers are folded by database metadata
lookup.
+ *
+ * @return whether unquoted identifiers are folded
+ */
+ public boolean isUnquotedIdentifierCaseFolded() {
+ return
option.map(MCPDatabaseCapabilityOption::isUnquotedIdentifierCaseFolded).orElse(false);
+ }
+
+ /**
+ * Get sequence metadata query.
+ *
+ * @return sequence metadata query
+ */
+ public Optional<String> getSequenceQuery() {
+ return option.flatMap(MCPDatabaseCapabilityOption::getSequenceQuery);
+ }
+
+ /**
+ * Judge whether information_schema column lookup should filter
table_schema.
+ *
+ * @return whether information_schema column lookup should filter
table_schema
+ */
+ public boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return
option.map(MCPDatabaseCapabilityOption::isInformationSchemaColumnSchemaFilterRequired).orElse(false);
+ }
+
+ /**
+ * Judge whether schema is a system schema.
+ *
+ * @param schemaName schema name
+ * @return whether schema is a system schema
+ */
+ public boolean isSystemSchema(final String schemaName) {
+ String actualSchemaName = trimToEmpty(schemaName);
+ if (actualSchemaName.isEmpty()) {
+ return false;
+ }
+ Collection<String> systemSchemas =
option.map(MCPDatabaseCapabilityOption::getSystemSchemas).orElseGet(List::of);
+ return containsSystemSchema(systemSchemas, actualSchemaName) ||
containsSystemSchema(LEGACY_SYSTEM_SCHEMAS, actualSchemaName);
+ }
+
+ /**
+ * Judge whether schema or catalog is a system schema.
+ *
+ * @param schemaName schema name
+ * @param catalogName catalog name
+ * @param defaultSchemaSemantics default schema semantics
+ * @return whether schema or catalog is a system schema
+ */
+ public boolean isSystemSchema(final String schemaName, final String
catalogName, final SchemaSemantics defaultSchemaSemantics) {
+ return isSystemSchema(schemaName) ||
SchemaSemantics.DATABASE_AS_SCHEMA == defaultSchemaSemantics &&
isSystemSchema(catalogName);
+ }
+
+ private QuoteCharacter getFallbackIdentifierQuoteCharacter() {
+ return databaseType.isEmpty() ? QuoteCharacter.BACK_QUOTE :
QuoteCharacter.QUOTE;
+ }
+
+ private static boolean containsSystemSchema(final Collection<String>
systemSchemas, final String schemaName) {
+ for (String each : systemSchemas) {
+ if (schemaName.equalsIgnoreCase(each)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static String trimToEmpty(final String value) {
+ return null == value ? "" : value.trim();
+ }
+}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/ClickHouseMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/ClickHouseMCPDatabaseCapabilityOption.java
index 5a7f9c01ad6..42e58afaac6 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/ClickHouseMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/ClickHouseMCPDatabaseCapabilityOption.java
@@ -21,6 +21,9 @@ import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecution
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+
/**
* MCP database capability option for ClickHouse.
*/
@@ -29,4 +32,9 @@ public final class ClickHouseMCPDatabaseCapabilityOption
extends AbstractMCPData
public ClickHouseMCPDatabaseCapabilityOption() {
super("ClickHouse", TransactionCapability.NONE, false,
SchemaSemantics.DATABASE_AS_SCHEMA, SchemaExecutionSemantics.FIXED_TO_DATABASE,
false, false);
}
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema");
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/FirebirdMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/FirebirdMCPDatabaseCapabilityOption.java
index 1a91af22408..6b3de8ff60f 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/FirebirdMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/FirebirdMCPDatabaseCapabilityOption.java
@@ -21,13 +21,30 @@ import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecution
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for Firebird.
*/
public final class FirebirdMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY =
+ "SELECT '' AS SEQUENCE_SCHEMA, TRIM(RDB$GENERATOR_NAME) AS
SEQUENCE_NAME FROM RDB$GENERATORS WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0";
+
public FirebirdMCPDatabaseCapabilityOption() {
super("Firebird", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.NATIVE_SCHEMA,
SchemaExecutionSemantics.BEST_EFFORT, true, true);
}
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("system_lobs", "system_tables");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/HiveMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/HiveMCPDatabaseCapabilityOption.java
index 16e4a979a1e..3a1d6667d1b 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/HiveMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/HiveMCPDatabaseCapabilityOption.java
@@ -17,10 +17,14 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+
/**
* MCP database capability option for Hive.
*/
@@ -29,4 +33,14 @@ public final class HiveMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCa
public HiveMCPDatabaseCapabilityOption() {
super("Hive", TransactionCapability.NONE, false,
SchemaSemantics.DATABASE_AS_SCHEMA, SchemaExecutionSemantics.FIXED_TO_DATABASE,
false, false);
}
+
+ @Override
+ public QuoteCharacter getIdentifierQuoteCharacter() {
+ return QuoteCharacter.BACK_QUOTE;
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "sys");
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MariaDBMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MariaDBMCPDatabaseCapabilityOption.java
index 39cd4ce4d88..f7c73d5b3e3 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MariaDBMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MariaDBMCPDatabaseCapabilityOption.java
@@ -17,17 +17,52 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for MariaDB.
*/
public final class MariaDBMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY =
+ "SELECT TABLE_SCHEMA AS SEQUENCE_SCHEMA, TABLE_NAME AS
SEQUENCE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'SEQUENCE'";
+
public MariaDBMCPDatabaseCapabilityOption() {
super("MariaDB", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.DATABASE_AS_SCHEMA,
SchemaExecutionSemantics.FIXED_TO_DATABASE, false, true);
}
+
+ @Override
+ public QuoteCharacter getIdentifierQuoteCharacter() {
+ return QuoteCharacter.BACK_QUOTE;
+ }
+
+ @Override
+ public IdentifierCasePolicySet getIdentifierCasePolicySet() {
+ return IdentifierCasePolicyFactory.newMySQLInsensitivePolicySet();
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "mysql", "performance_schema",
"shardingsphere", "sys");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
+
+ @Override
+ public boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return true;
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MySQLMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MySQLMCPDatabaseCapabilityOption.java
index 40e5d27aaff..e49bcbc7b2c 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MySQLMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/MySQLMCPDatabaseCapabilityOption.java
@@ -17,11 +17,17 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import
org.apache.shardingsphere.mcp.support.database.capability.DatabaseVersionUtil;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+
/**
* MCP database capability option for MySQL.
*/
@@ -32,6 +38,26 @@ public final class MySQLMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseC
SchemaSemantics.DATABASE_AS_SCHEMA,
SchemaExecutionSemantics.FIXED_TO_DATABASE, false, false);
}
+ @Override
+ public QuoteCharacter getIdentifierQuoteCharacter() {
+ return QuoteCharacter.BACK_QUOTE;
+ }
+
+ @Override
+ public IdentifierCasePolicySet getIdentifierCasePolicySet() {
+ return IdentifierCasePolicyFactory.newMySQLInsensitivePolicySet();
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "mysql", "performance_schema",
"shardingsphere", "sys");
+ }
+
+ @Override
+ public boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return true;
+ }
+
@Override
public boolean isExplainAnalyzeSupported(final String databaseVersion) {
return DatabaseVersionUtil.isVersionAtLeast(databaseVersion, 8, 0, 18);
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OpenGaussMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OpenGaussMCPDatabaseCapabilityOption.java
index b3954a5c988..51acb294d60 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OpenGaussMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OpenGaussMCPDatabaseCapabilityOption.java
@@ -17,20 +17,54 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for openGauss.
*/
public final class OpenGaussMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY =
+ "SELECT sequence_schema AS SEQUENCE_SCHEMA, sequence_name AS
SEQUENCE_NAME FROM information_schema.sequences";
+
public OpenGaussMCPDatabaseCapabilityOption() {
super("openGauss", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.NATIVE_SCHEMA,
SchemaExecutionSemantics.BEST_EFFORT, true, true);
}
+ @Override
+ public IdentifierCasePolicySet getIdentifierCasePolicySet() {
+ return IdentifierCasePolicyFactory.newLowerCasePolicySet();
+ }
+
+ @Override
+ public boolean isUnquotedIdentifierCaseFolded() {
+ return true;
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "pg_catalog", "shardingsphere");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
+
+ @Override
+ public boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return true;
+ }
+
@Override
public boolean isExplainAnalyzeSupported(final String databaseVersion) {
return true;
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OracleMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OracleMCPDatabaseCapabilityOption.java
index 7a8b0addb9d..86376c36937 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OracleMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/OracleMCPDatabaseCapabilityOption.java
@@ -21,13 +21,29 @@ import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecution
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for Oracle.
*/
public final class OracleMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY = "SELECT USER AS
SEQUENCE_SCHEMA, sequence_name AS SEQUENCE_NAME FROM USER_SEQUENCES";
+
public OracleMCPDatabaseCapabilityOption() {
super("Oracle", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.NATIVE_SCHEMA,
SchemaExecutionSemantics.BEST_EFFORT, true, true);
}
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("sys", "system_lobs");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PostgreSQLMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PostgreSQLMCPDatabaseCapabilityOption.java
index 8c40f55cc14..f4944824123 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PostgreSQLMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PostgreSQLMCPDatabaseCapabilityOption.java
@@ -17,20 +17,54 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for PostgreSQL.
*/
public final class PostgreSQLMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY =
+ "SELECT sequence_schema AS SEQUENCE_SCHEMA, sequence_name AS
SEQUENCE_NAME FROM information_schema.sequences";
+
public PostgreSQLMCPDatabaseCapabilityOption() {
super("PostgreSQL", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.NATIVE_SCHEMA,
SchemaExecutionSemantics.BEST_EFFORT, true, true);
}
+ @Override
+ public IdentifierCasePolicySet getIdentifierCasePolicySet() {
+ return IdentifierCasePolicyFactory.newLowerCasePolicySet();
+ }
+
+ @Override
+ public boolean isUnquotedIdentifierCaseFolded() {
+ return true;
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "pg_catalog", "shardingsphere");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
+
+ @Override
+ public boolean isInformationSchemaColumnSchemaFilterRequired() {
+ return true;
+ }
+
@Override
public boolean isExplainAnalyzeSupported(final String databaseVersion) {
return true;
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PrestoMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PrestoMCPDatabaseCapabilityOption.java
index 5ec547121df..621d2aff804 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PrestoMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/PrestoMCPDatabaseCapabilityOption.java
@@ -21,6 +21,9 @@ import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecution
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+
/**
* MCP database capability option for Presto.
*/
@@ -30,6 +33,11 @@ public final class PrestoMCPDatabaseCapabilityOption extends
AbstractMCPDatabase
super("Presto", TransactionCapability.LOCAL, false,
SchemaSemantics.NATIVE_SCHEMA, SchemaExecutionSemantics.BEST_EFFORT, true,
false);
}
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema");
+ }
+
@Override
public boolean isExplainAnalyzeSupported(final String databaseVersion) {
return true;
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/SQLServerMCPDatabaseCapabilityOption.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/SQLServerMCPDatabaseCapabilityOption.java
index bb4d3afa089..acae8ac004c 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/SQLServerMCPDatabaseCapabilityOption.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/dialect/SQLServerMCPDatabaseCapabilityOption.java
@@ -17,17 +17,40 @@
package org.apache.shardingsphere.mcp.support.database.capability.dialect;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaExecutionSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.capability.TransactionCapability;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
/**
* MCP database capability option for SQLServer.
*/
public final class SQLServerMCPDatabaseCapabilityOption extends
AbstractMCPDatabaseCapabilityOption {
+ private static final String SEQUENCE_QUERY =
+ "SELECT schemas.name AS SEQUENCE_SCHEMA, seq.name AS SEQUENCE_NAME
FROM sys.sequences seq INNER JOIN sys.schemas schemas ON seq.schema_id =
schemas.schema_id";
+
public SQLServerMCPDatabaseCapabilityOption() {
super("SQLServer", TransactionCapability.LOCAL_WITH_SAVEPOINT, true,
SchemaSemantics.NATIVE_SCHEMA,
SchemaExecutionSemantics.BEST_EFFORT, true, true);
}
+
+ @Override
+ public QuoteCharacter getIdentifierQuoteCharacter() {
+ return QuoteCharacter.BRACKETS;
+ }
+
+ @Override
+ public Collection<String> getSystemSchemas() {
+ return List.of("information_schema", "sys");
+ }
+
+ @Override
+ public Optional<String> getSequenceQuery() {
+ return Optional.of(SEQUENCE_QUERY);
+ }
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
index 41a5f5f5a8e..1c3b2e62d67 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoader.java
@@ -19,8 +19,7 @@ package
org.apache.shardingsphere.mcp.support.database.metadata.jdbc;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseCapabilityOption;
+import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseDialect;
import
org.apache.shardingsphere.mcp.support.database.capability.SchemaSemantics;
import
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPColumnMetadata;
import
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPDatabaseMetadata;
@@ -39,7 +38,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -50,22 +48,6 @@ import java.util.Set;
*/
public final class MCPJdbcMetadataLoader {
- private static final Set<String> SYSTEM_SCHEMAS =
Set.of("INFORMATION_SCHEMA", "MYSQL", "PERFORMANCE_SCHEMA", "PG_CATALOG",
"SHARDINGSPHERE", "SYS", "SYSTEM_LOBS");
-
- private static final String INFORMATION_SCHEMA_SEQUENCE_QUERY =
- "SELECT sequence_schema AS SEQUENCE_SCHEMA, sequence_name AS
SEQUENCE_NAME FROM information_schema.sequences";
-
- private static final String SQL_SERVER_SEQUENCE_QUERY =
- "SELECT schemas.name AS SEQUENCE_SCHEMA, seq.name AS SEQUENCE_NAME
FROM sys.sequences seq INNER JOIN sys.schemas schemas ON seq.schema_id =
schemas.schema_id";
-
- private static final String ORACLE_SEQUENCE_QUERY = "SELECT USER AS
SEQUENCE_SCHEMA, sequence_name AS SEQUENCE_NAME FROM USER_SEQUENCES";
-
- private static final String MARIADB_SEQUENCE_QUERY =
- "SELECT TABLE_SCHEMA AS SEQUENCE_SCHEMA, TABLE_NAME AS
SEQUENCE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'SEQUENCE'";
-
- private static final String FIREBIRD_SEQUENCE_QUERY =
- "SELECT '' AS SEQUENCE_SCHEMA, TRIM(RDB$GENERATOR_NAME) AS
SEQUENCE_NAME FROM RDB$GENERATORS WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0";
-
/**
* Load database metadata.
*
@@ -85,21 +67,22 @@ public final class MCPJdbcMetadataLoader {
private MCPDatabaseMetadata loadDatabaseMetadata(final String
databaseName, final RuntimeDatabaseProfile databaseProfile,
final Connection
connection, final DatabaseMetaData databaseMetaData) throws SQLException {
- SchemaSemantics defaultSchemaSemantics =
getDefaultSchemaSemantics(databaseProfile.getDatabaseType());
+ MCPDatabaseDialect databaseDialect =
MCPDatabaseDialect.of(databaseProfile.getDatabaseType());
+ SchemaSemantics defaultSchemaSemantics =
databaseDialect.getDefaultSchemaSemantics();
DatabaseMetadataAccumulator accumulator = new
DatabaseMetadataAccumulator(databaseName, databaseProfile.getDatabaseType(),
databaseProfile.getDatabaseVersion());
- loadTables(databaseName, defaultSchemaSemantics, accumulator,
databaseMetaData);
- loadViews(databaseName, defaultSchemaSemantics, accumulator,
databaseMetaData);
- loadSequences(databaseName, defaultSchemaSemantics,
databaseProfile.getDatabaseType(), accumulator, connection);
+ loadTables(databaseName, defaultSchemaSemantics, databaseDialect,
accumulator, databaseMetaData);
+ loadViews(databaseName, defaultSchemaSemantics, databaseDialect,
accumulator, databaseMetaData);
+ loadSequences(databaseName, defaultSchemaSemantics, databaseDialect,
accumulator, connection);
return accumulator.build();
}
- private void loadTables(final String databaseName, final SchemaSemantics
defaultSchemaSemantics,
+ private void loadTables(final String databaseName, final SchemaSemantics
defaultSchemaSemantics, final MCPDatabaseDialect databaseDialect,
final DatabaseMetadataAccumulator accumulator,
final DatabaseMetaData databaseMetaData) throws SQLException {
try (ResultSet tables = databaseMetaData.getTables(null, null, "%",
new String[]{"TABLE"})) {
while (tables.next()) {
String schemaName =
Objects.toString(tables.getString("TABLE_SCHEM"), "").trim();
String catalogName =
Objects.toString(tables.getString("TABLE_CAT"), "").trim();
- if (isSystemSchema(defaultSchemaSemantics, schemaName,
catalogName)) {
+ if (databaseDialect.isSystemSchema(schemaName, catalogName,
defaultSchemaSemantics)) {
continue;
}
String normalizedSchemaName =
normalizeSchemaName(databaseName, defaultSchemaSemantics, schemaName);
@@ -118,13 +101,13 @@ public final class MCPJdbcMetadataLoader {
}
}
- private void loadViews(final String databaseName, final SchemaSemantics
defaultSchemaSemantics,
+ private void loadViews(final String databaseName, final SchemaSemantics
defaultSchemaSemantics, final MCPDatabaseDialect databaseDialect,
final DatabaseMetadataAccumulator accumulator,
final DatabaseMetaData databaseMetaData) throws SQLException {
try (ResultSet views = databaseMetaData.getTables(null, null, "%", new
String[]{"VIEW"})) {
while (views.next()) {
String schemaName =
Objects.toString(views.getString("TABLE_SCHEM"), "").trim();
String catalogName =
Objects.toString(views.getString("TABLE_CAT"), "").trim();
- if (isSystemSchema(defaultSchemaSemantics, schemaName,
catalogName)) {
+ if (databaseDialect.isSystemSchema(schemaName, catalogName,
defaultSchemaSemantics)) {
continue;
}
String normalizedSchemaName =
normalizeSchemaName(databaseName, defaultSchemaSemantics, schemaName);
@@ -140,16 +123,16 @@ public final class MCPJdbcMetadataLoader {
}
}
- private void loadSequences(final String databaseName, final
SchemaSemantics defaultSchemaSemantics, final String databaseType,
+ private void loadSequences(final String databaseName, final
SchemaSemantics defaultSchemaSemantics, final MCPDatabaseDialect
databaseDialect,
final DatabaseMetadataAccumulator accumulator,
final Connection connection) throws SQLException {
- Optional<String> sequenceQuery = getSequenceQuery(databaseType);
+ Optional<String> sequenceQuery = databaseDialect.getSequenceQuery();
if (sequenceQuery.isEmpty()) {
return;
}
try (Statement statement = connection.createStatement(); ResultSet
sequences = statement.executeQuery(sequenceQuery.get())) {
while (sequences.next()) {
String schemaName =
Objects.toString(sequences.getString("SEQUENCE_SCHEMA"), "").trim();
- if (isSystemSchema(schemaName)) {
+ if (databaseDialect.isSystemSchema(schemaName)) {
continue;
}
String normalizedSchemaName =
normalizeSchemaName(databaseName, defaultSchemaSemantics, schemaName);
@@ -161,35 +144,6 @@ public final class MCPJdbcMetadataLoader {
}
}
- private Optional<String> getSequenceQuery(final String databaseType) {
- if (null == databaseType || databaseType.isBlank()) {
- return Optional.empty();
- }
- switch (databaseType.toUpperCase(Locale.ENGLISH)) {
- case "POSTGRESQL":
- case "OPENGAUSS":
- return Optional.of(INFORMATION_SCHEMA_SEQUENCE_QUERY);
- case "SQLSERVER":
- return Optional.of(SQL_SERVER_SEQUENCE_QUERY);
- case "ORACLE":
- return Optional.of(ORACLE_SEQUENCE_QUERY);
- case "MARIADB":
- return Optional.of(MARIADB_SEQUENCE_QUERY);
- case "FIREBIRD":
- return Optional.of(FIREBIRD_SEQUENCE_QUERY);
- default:
- return Optional.empty();
- }
- }
-
- private boolean isSystemSchema(final String schemaName) {
- return SYSTEM_SCHEMAS.contains(schemaName.toUpperCase(Locale.ENGLISH));
- }
-
- private boolean isSystemSchema(final SchemaSemantics
defaultSchemaSemantics, final String schemaName, final String catalogName) {
- return isSystemSchema(schemaName) ||
SchemaSemantics.DATABASE_AS_SCHEMA == defaultSchemaSemantics &&
isSystemSchema(catalogName);
- }
-
private List<String> loadColumns(final DatabaseMetaData databaseMetaData,
final String catalogName, final String schemaName, final String objectName)
throws SQLException {
List<String> result = new LinkedList<>();
try (ResultSet columns =
databaseMetaData.getColumns(getPattern(catalogName), getPattern(schemaName),
objectName, "%")) {
@@ -222,12 +176,6 @@ public final class MCPJdbcMetadataLoader {
return result.isEmpty() ? null : result;
}
- private SchemaSemantics getDefaultSchemaSemantics(final String
databaseType) {
- return TypedSPILoader.findService(MCPDatabaseCapabilityOption.class,
databaseType)
- .map(MCPDatabaseCapabilityOption::getDefaultSchemaSemantics)
- .orElse(SchemaSemantics.NATIVE_SCHEMA);
- }
-
private String normalizeSchemaName(final String databaseName, final
SchemaSemantics defaultSchemaSemantics, final String schemaName) {
String result = Objects.toString(schemaName, "").trim();
if (!result.isEmpty()) {
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
index 159369bb4aa..dce598d0e92 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtils.java
@@ -19,8 +19,12 @@ package
org.apache.shardingsphere.mcp.support.workflow.service;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicy;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
import
org.apache.shardingsphere.mcp.api.protocol.exception.MCPInvalidRequestException;
+import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseDialect;
import java.util.LinkedHashMap;
import java.util.List;
@@ -81,8 +85,12 @@ public final class WorkflowSQLUtils {
public static String canonicalizeIdentifier(final String databaseType,
final String identifier) {
String rawIdentifier = trimToEmpty(identifier);
String result = normalizeIdentifier(rawIdentifier);
- return !isDelimitedIdentifier(rawIdentifier) &&
isLowerCaseFoldedIdentifierDatabase(databaseType) &&
!isSpecialSQLIdentifier(result)
- ? result.toLowerCase(Locale.ENGLISH)
+ if (isDelimitedIdentifier(rawIdentifier) ||
isSpecialSQLIdentifier(result)) {
+ return result;
+ }
+ MCPDatabaseDialect databaseDialect =
MCPDatabaseDialect.of(databaseType);
+ return databaseDialect.isUnquotedIdentifierCaseFolded()
+ ?
databaseDialect.getIdentifierCasePolicy(IdentifierScope.TABLE).normalize(result)
: result;
}
@@ -122,7 +130,7 @@ public final class WorkflowSQLUtils {
checkSupportedIdentifier("identifier", actualIdentifier);
return actualIdentifier.isEmpty() ||
!isSpecialDistSQLIdentifier(actualIdentifier) &&
!isDelimitedIdentifier(rawIdentifier)
? actualIdentifier
- : IdentifierQuoteStyle.BACK_QUOTE.wrap(actualIdentifier);
+ : wrapIdentifier(QuoteCharacter.BACK_QUOTE, actualIdentifier);
}
/**
@@ -134,7 +142,7 @@ public final class WorkflowSQLUtils {
public static String formatGeneratedRuleDistSQLIdentifier(final String
identifier) {
String actualIdentifier = normalizeIdentifier(trimToEmpty(identifier));
checkSupportedIdentifier("identifier", actualIdentifier);
- return actualIdentifier.isEmpty() ? actualIdentifier :
IdentifierQuoteStyle.BACK_QUOTE.wrap(actualIdentifier);
+ return actualIdentifier.isEmpty() ? actualIdentifier :
wrapIdentifier(QuoteCharacter.BACK_QUOTE, actualIdentifier);
}
/**
@@ -150,7 +158,7 @@ public final class WorkflowSQLUtils {
checkSupportedIdentifier("identifier", actualIdentifier);
return actualIdentifier.isEmpty() ||
!isSpecialSQLIdentifier(actualIdentifier) &&
!isDelimitedIdentifier(rawIdentifier)
? actualIdentifier
- :
getSQLIdentifierQuoteStyle(databaseType).wrap(actualIdentifier);
+ :
wrapIdentifier(MCPDatabaseDialect.of(databaseType).getIdentifierQuoteCharacter(),
actualIdentifier);
}
/**
@@ -164,12 +172,8 @@ public final class WorkflowSQLUtils {
public static boolean isSameIdentifier(final String databaseType, final
String identifier, final String existingIdentifier) {
String actualIdentifier = normalizeIdentifier(identifier);
String actualExistingIdentifier =
normalizeIdentifier(existingIdentifier);
- if (isCaseInsensitiveIdentifierDatabase(databaseType)) {
- return actualIdentifier.equalsIgnoreCase(actualExistingIdentifier);
- }
- return isLowerCaseFoldedIdentifierDatabase(databaseType)
- ? canonicalizeIdentifier(databaseType,
identifier).equals(actualExistingIdentifier)
- : actualIdentifier.equals(actualExistingIdentifier);
+ IdentifierCasePolicy identifierCasePolicy =
MCPDatabaseDialect.of(databaseType).getIdentifierCasePolicy(IdentifierScope.TABLE);
+ return identifierCasePolicy.matches(actualExistingIdentifier,
actualIdentifier, getQuoteCharacter(identifier));
}
/**
@@ -294,44 +298,16 @@ public final class WorkflowSQLUtils {
return !identifier.matches(UNQUOTED_IDENTIFIER_PATTERN);
}
- private static boolean isCaseInsensitiveIdentifierDatabase(final String
databaseType) {
- String actualDatabaseType =
trimToEmpty(databaseType).toLowerCase(Locale.ENGLISH);
- return "mysql".equals(actualDatabaseType) ||
"mariadb".equals(actualDatabaseType);
+ private static QuoteCharacter getQuoteCharacter(final String identifier) {
+ String actualIdentifier = trimToEmpty(identifier);
+ return isDelimitedIdentifier(actualIdentifier) ?
QuoteCharacter.getQuoteCharacter(actualIdentifier) : QuoteCharacter.NONE;
}
- private static boolean isLowerCaseFoldedIdentifierDatabase(final String
databaseType) {
- String actualDatabaseType =
trimToEmpty(databaseType).toLowerCase(Locale.ENGLISH);
- return "postgresql".equals(actualDatabaseType) ||
"opengauss".equals(actualDatabaseType);
- }
-
- private static IdentifierQuoteStyle getSQLIdentifierQuoteStyle(final
String databaseType) {
- String actualDatabaseType =
trimToEmpty(databaseType).toLowerCase(Locale.ENGLISH);
- if (actualDatabaseType.isEmpty() || "mysql".equals(actualDatabaseType)
|| "mariadb".equals(actualDatabaseType) || "hive".equals(actualDatabaseType)) {
- return IdentifierQuoteStyle.BACK_QUOTE;
- }
- return "sqlserver".equals(actualDatabaseType) ?
IdentifierQuoteStyle.BRACKETS : IdentifierQuoteStyle.DOUBLE_QUOTE;
- }
-
- private enum IdentifierQuoteStyle {
-
- BACK_QUOTE("`", "`"),
-
- DOUBLE_QUOTE("\"", "\""),
-
- BRACKETS("[", "]");
-
- private final String startDelimiter;
-
- private final String endDelimiter;
-
- IdentifierQuoteStyle(final String startDelimiter, final String
endDelimiter) {
- this.startDelimiter = startDelimiter;
- this.endDelimiter = endDelimiter;
- }
-
- private String wrap(final String value) {
- return startDelimiter + value.replace(endDelimiter, endDelimiter +
endDelimiter) + endDelimiter;
- }
+ private static String wrapIdentifier(final QuoteCharacter quoteCharacter,
final String value) {
+ return QuoteCharacter.NONE == quoteCharacter
+ ? value
+ : quoteCharacter.getStartDelimiter() +
value.replace(quoteCharacter.getEndDelimiter(),
quoteCharacter.getEndDelimiter() + quoteCharacter.getEndDelimiter())
+ + quoteCharacter.getEndDelimiter();
}
private static Map<String, String> parsePropertyString(final String value)
{
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
index 93130497533..b412bdd8782 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupport.java
@@ -95,16 +95,6 @@ public final class WorkflowValidationSupport {
return WorkflowLifecycle.STATUS_PASSED;
}
- /**
- * Create the baseline projection SQL used by workflow validation.
- *
- * @param snapshot workflow snapshot
- * @return projection validation SQL
- */
- public String createProjectionValidationSql(final WorkflowContextSnapshot
snapshot) {
- return createProjectionValidationSql(snapshot, "MySQL");
- }
-
/**
* Create the baseline projection SQL used by workflow validation.
*
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialectTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialectTest.java
new file mode 100644
index 00000000000..dc1a75b0923
--- /dev/null
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseDialectTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.mcp.support.database.capability;
+
+import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicy;
+import
org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class MCPDatabaseDialectTest {
+
+ @Test
+ void assertOf() {
+ MCPDatabaseDialect actual = MCPDatabaseDialect.of("");
+ assertThat(actual.getIdentifierQuoteCharacter(),
is(QuoteCharacter.BACK_QUOTE));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getIdentifierQuoteCharacterArguments")
+ void assertGetIdentifierQuoteCharacter(final String name, final String
databaseType, final QuoteCharacter expected) {
+ QuoteCharacter actual =
MCPDatabaseDialect.of(databaseType).getIdentifierQuoteCharacter();
+ assertThat(actual, is(expected));
+ }
+
+ @Test
+ void assertGetIdentifierCasePolicyWithCaseInsensitiveIdentifier() {
+ IdentifierCasePolicy actual =
MCPDatabaseDialect.of("MySQL").getIdentifierCasePolicy(IdentifierScope.TABLE);
+ assertTrue(actual.matches("phone", "Phone", QuoteCharacter.QUOTE));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getDefaultSchemaSemanticsArguments")
+ void assertGetDefaultSchemaSemantics(final String name, final String
databaseType, final SchemaSemantics expected) {
+ SchemaSemantics actual =
MCPDatabaseDialect.of(databaseType).getDefaultSchemaSemantics();
+ assertThat(actual, is(expected));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("isUnquotedIdentifierCaseFoldedArguments")
+ void assertIsUnquotedIdentifierCaseFolded(final String name, final String
databaseType, final boolean expected) {
+ boolean actual =
MCPDatabaseDialect.of(databaseType).isUnquotedIdentifierCaseFolded();
+ assertThat(actual, is(expected));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("getSequenceQueryArguments")
+ void assertGetSequenceQuery(final String name, final String databaseType,
final String expected) {
+ String actual =
MCPDatabaseDialect.of(databaseType).getSequenceQuery().orElse("");
+ assertThat(actual, is(expected));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("isInformationSchemaColumnSchemaFilterRequiredArguments")
+ void assertIsInformationSchemaColumnSchemaFilterRequired(final String
name, final String databaseType, final boolean expected) {
+ boolean actual =
MCPDatabaseDialect.of(databaseType).isInformationSchemaColumnSchemaFilterRequired();
+ assertThat(actual, is(expected));
+ }
+
+ @Test
+ void assertIsSystemSchema() {
+ boolean actual =
MCPDatabaseDialect.of("PostgreSQL").isSystemSchema("PG_CATALOG");
+ assertTrue(actual);
+ }
+
+ @Test
+ void assertIsSystemSchemaWithCatalog() {
+ boolean actual = MCPDatabaseDialect.of("MySQL").isSystemSchema("",
"information_schema", SchemaSemantics.DATABASE_AS_SCHEMA);
+ assertTrue(actual);
+ }
+
+ @Test
+ void assertIsSystemSchemaWithUserSchema() {
+ boolean actual =
MCPDatabaseDialect.of("MySQL").isSystemSchema("orders");
+ assertFalse(actual);
+ }
+
+ @Test
+ void assertIsSystemSchemaWithUnknownDatabaseType() {
+ boolean actual =
MCPDatabaseDialect.of("FixtureDB").isSystemSchema("INFORMATION_SCHEMA");
+ assertTrue(actual);
+ }
+
+ private static Stream<Arguments> getIdentifierQuoteCharacterArguments() {
+ return Stream.of(
+ Arguments.of("mysql", "MySQL", QuoteCharacter.BACK_QUOTE),
+ Arguments.of("hive", "Hive", QuoteCharacter.BACK_QUOTE),
+ Arguments.of("sql server", "SQLServer",
QuoteCharacter.BRACKETS),
+ Arguments.of("postgresql", "PostgreSQL", QuoteCharacter.QUOTE),
+ Arguments.of("unknown", "FixtureDB", QuoteCharacter.QUOTE));
+ }
+
+ private static Stream<Arguments> isUnquotedIdentifierCaseFoldedArguments()
{
+ return Stream.of(
+ Arguments.of("postgresql", "PostgreSQL", true),
+ Arguments.of("open gauss", "openGauss", true),
+ Arguments.of("mysql", "MySQL", false),
+ Arguments.of("unknown", "FixtureDB", false));
+ }
+
+ private static Stream<Arguments> getDefaultSchemaSemanticsArguments() {
+ return Stream.of(
+ Arguments.of("mysql", "MySQL",
SchemaSemantics.DATABASE_AS_SCHEMA),
+ Arguments.of("postgresql", "PostgreSQL",
SchemaSemantics.NATIVE_SCHEMA),
+ Arguments.of("unknown", "FixtureDB",
SchemaSemantics.NATIVE_SCHEMA));
+ }
+
+ private static Stream<Arguments> getSequenceQueryArguments() {
+ return Stream.of(
+ Arguments.of("postgresql", "PostgreSQL", "SELECT
sequence_schema AS SEQUENCE_SCHEMA, sequence_name AS SEQUENCE_NAME FROM
information_schema.sequences"),
+ Arguments.of("sql server", "SQLServer",
+ "SELECT schemas.name AS SEQUENCE_SCHEMA, seq.name AS
SEQUENCE_NAME FROM sys.sequences seq INNER JOIN sys.schemas schemas ON
seq.schema_id = schemas.schema_id"),
+ Arguments.of("mysql", "MySQL", ""));
+ }
+
+ private static Stream<Arguments>
isInformationSchemaColumnSchemaFilterRequiredArguments() {
+ return Stream.of(
+ Arguments.of("mysql", "MySQL", true),
+ Arguments.of("postgresql", "PostgreSQL", true),
+ Arguments.of("sql server", "SQLServer", false),
+ Arguments.of("unknown", "FixtureDB", false));
+ }
+}
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/AbstractMCPJdbcMetadataLoaderTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/AbstractMCPJdbcMetadataLoaderTest.java
index 0c025bd7af2..51d49d4f9b3 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/AbstractMCPJdbcMetadataLoaderTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/AbstractMCPJdbcMetadataLoaderTest.java
@@ -22,6 +22,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeFactory;
+import
org.apache.shardingsphere.mcp.support.database.capability.MCPDatabaseDialect;
import
org.apache.shardingsphere.mcp.support.database.capability.SupportedMCPMetadataObjectType;
import
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPColumnMetadata;
import
org.apache.shardingsphere.mcp.support.database.metadata.model.MCPDatabaseMetadata;
@@ -251,21 +252,7 @@ abstract class AbstractMCPJdbcMetadataLoaderTest {
}
protected String getSequenceQuery(final String databaseType) {
- switch (databaseType) {
- case "PostgreSQL":
- case "openGauss":
- return "SELECT sequence_schema AS SEQUENCE_SCHEMA,
sequence_name AS SEQUENCE_NAME FROM information_schema.sequences";
- case "SQLServer":
- return "SELECT schemas.name AS SEQUENCE_SCHEMA, seq.name AS
SEQUENCE_NAME FROM sys.sequences seq INNER JOIN sys.schemas schemas ON
seq.schema_id = schemas.schema_id";
- case "Oracle":
- return "SELECT USER AS SEQUENCE_SCHEMA, sequence_name AS
SEQUENCE_NAME FROM USER_SEQUENCES";
- case "MariaDB":
- return "SELECT TABLE_SCHEMA AS SEQUENCE_SCHEMA, TABLE_NAME AS
SEQUENCE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'SEQUENCE'";
- case "Firebird":
- return "SELECT '' AS SEQUENCE_SCHEMA, TRIM(RDB$GENERATOR_NAME)
AS SEQUENCE_NAME FROM RDB$GENERATORS WHERE COALESCE(RDB$SYSTEM_FLAG, 0) = 0";
- default:
- return "";
- }
+ return
MCPDatabaseDialect.of(databaseType).getSequenceQuery().orElse("");
}
protected ResultSet mockResultSet(final String columnName, final String...
values) throws SQLException {
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtilsTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtilsTest.java
index 4ad58fde79c..eae4d10de2e 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtilsTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowSQLUtilsTest.java
@@ -70,6 +70,11 @@ class WorkflowSQLUtilsTest {
assertThat(WorkflowSQLUtils.canonicalizeIdentifier("openGauss",
"Phone"), is("phone"));
}
+ @Test
+ void assertCanonicalizeIdentifierKeepsMySQLUnquotedIdentifier() {
+ assertThat(WorkflowSQLUtils.canonicalizeIdentifier("MySQL", "Phone"),
is("Phone"));
+ }
+
@Test
void assertCanonicalizeIdentifierPreservesSpecialIdentifier() {
assertThat(WorkflowSQLUtils.canonicalizeIdentifier("PostgreSQL",
"Phone Number"), is("Phone Number"));
@@ -184,6 +189,12 @@ class WorkflowSQLUtilsTest {
assertThat(actualValue, is("\"order detail\""));
}
+ @Test
+ void assertFormatSQLIdentifierUsesSQLServerQuoteStyle() {
+ String actualValue = WorkflowSQLUtils.formatSQLIdentifier("SQLServer",
"order detail");
+ assertThat(actualValue, is("[order detail]"));
+ }
+
@Test
void assertFormatSQLIdentifierPreservesDelimitedSafeIdentifier() {
String actualValue =
WorkflowSQLUtils.formatSQLIdentifier("PostgreSQL", "\"orders\"");
@@ -206,8 +217,12 @@ class WorkflowSQLUtilsTest {
}
@Test
- void
assertIsSameIdentifierKeepsPostgreSQLExistingQuotedIdentifierDistinct() {
+ void assertIsSameIdentifierRejectsUnquotedPostgreSQLQuotedName() {
assertFalse(WorkflowSQLUtils.isSameIdentifier("PostgreSQL", "Phone",
"Phone"));
+ }
+
+ @Test
+ void assertIsSameIdentifierMatchesQuotedPostgreSQLName() {
assertTrue(WorkflowSQLUtils.isSameIdentifier("PostgreSQL",
"\"Phone\"", "Phone"));
}
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupportTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupportTest.java
index 341fcfc7737..9069ae36af6 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupportTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowValidationSupportTest.java
@@ -123,7 +123,7 @@ class WorkflowValidationSupportTest {
@Test
void assertCreateProjectionValidationSql() {
-
assertThat(validationSupport.createProjectionValidationSql(createSnapshot()),
is("SELECT phone FROM orders"));
+
assertThat(validationSupport.createProjectionValidationSql(createSnapshot(),
"MySQL"), is("SELECT phone FROM orders"));
}
@Test
@@ -131,7 +131,7 @@ class WorkflowValidationSupportTest {
WorkflowContextSnapshot snapshot = createSnapshot();
snapshot.getRequest().setTable("order detail");
snapshot.getRequest().setColumn("Phone Number");
- assertThat(validationSupport.createProjectionValidationSql(snapshot),
is("SELECT `Phone Number` FROM `order detail`"));
+ assertThat(validationSupport.createProjectionValidationSql(snapshot,
"MySQL"), is("SELECT `Phone Number` FROM `order detail`"));
}
@Test