You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/07/13 07:15:18 UTC
(shardingsphere) branch master updated: Refine MCP identifier policies and EXPLAIN support (#39083)
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 e638c97826a Refine MCP identifier policies and EXPLAIN support (#39083)
e638c97826a is described below
commit e638c97826ae600f08886da12356b33a97bf2568
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Mon Jul 13 15:15:11 2026 +0800
Refine MCP identifier policies and EXPLAIN support (#39083)
---
.../MCPCompletionSpecificationFactoryTest.java | 5 +-
.../tool/handler/execute/ClassificationResult.java | 29 +++-
.../execute/ExplainSQLCandidateValidator.java | 3 +-
.../handler/execute/MCPSQLExecutionFacade.java | 24 ++--
.../handler/execute/SQLStatementObjectName.java | 27 ++++
.../execute/SQLStatementObjectNameReader.java | 15 ++-
.../execute/SQLStatementTargetResolver.java | 62 +++++----
.../tool/handler/execute/StatementClassifier.java | 3 +-
.../core/completion/MCPCompletionServiceTest.java | 5 +-
.../provider/MetadataCompletionProviderTest.java | 5 +-
.../metadata/MetadataResourceHandlerTest.java | 10 +-
.../MetadataResourcePayloadMapperTest.java | 4 +-
.../execute/ExplainSQLCandidateValidatorTest.java | 6 +
.../handler/execute/MCPSQLExecutionFacadeTest.java | 54 ++++++--
.../metadata/SearchMetadataPayloadBuilderTest.java | 4 +-
.../EncryptWorkflowPlanningServiceTest.java | 3 +-
.../service/MaskWorkflowPlanningServiceTest.java | 3 +-
.../database/capability/MCPDatabaseCapability.java | 10 +-
.../capability/MCPDatabaseCapabilityProvider.java | 90 +------------
.../ClickHouseMCPDatabaseCapabilityOption.java | 2 +-
.../dialect/HiveMCPDatabaseCapabilityOption.java | 2 +-
.../jdbc/MCPJdbcDatabaseProfileLoader.java | 92 ++++++++++++-
.../metadata/jdbc/RuntimeDatabaseProfile.java | 3 +
.../service/RuntimeDatabaseValidationService.java | 42 +++---
.../MCPDatabaseCapabilityProviderTest.java | 148 ++++-----------------
.../jdbc/MCPJdbcDatabaseProfileLoaderTest.java | 83 +++++++++++-
.../jdbc/MCPJdbcMetadataLoaderFailureTest.java | 4 +-
.../RuntimeDatabaseValidationServiceTest.java | 36 ++++-
.../WorkflowPlanningContextValidatorTest.java | 3 +-
.../service/WorkflowPlanningSupportTest.java | 3 +-
30 files changed, 458 insertions(+), 322 deletions(-)
diff --git a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
index f19438b2cd1..93239f3ff38 100644
--- a/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
+++ b/mcp/bootstrap/src/test/java/org/apache/shardingsphere/mcp/bootstrap/transport/capability/completion/MCPCompletionSpecificationFactoryTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.bootstrap.transport.capability.completion;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import io.modelcontextprotocol.server.McpServerFeatures.SyncCompletionSpecification;
import io.modelcontextprotocol.server.McpSyncServerExchange;
import io.modelcontextprotocol.spec.McpError;
@@ -198,8 +199,8 @@ class MCPCompletionSpecificationFactoryTest {
private MCPRuntimeContext createRuntimeContext(final WorkflowSessionContext workflowSessionContext) {
MCPDatabaseCapabilityProvider databaseCapabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
when(databaseCapabilityProvider.getDatabaseProfiles()).thenReturn(List.of(
- new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true),
- new RuntimeDatabaseProfile("warehouse", "FixtureWarehouseDB", "2.0", true, true)));
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet()),
+ new RuntimeDatabaseProfile("warehouse", "FixtureWarehouseDB", "2.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet())));
MCPRuntimeContext result = mock(MCPRuntimeContext.class, RETURNS_DEEP_STUBS);
when(result.getDatabaseCapabilityProvider()).thenReturn(databaseCapabilityProvider);
when(result.getWorkflowSessionContext()).thenReturn(workflowSessionContext);
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ClassificationResult.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ClassificationResult.java
index 20a0d471842..f1cf58c8498 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ClassificationResult.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ClassificationResult.java
@@ -17,10 +17,12 @@
package org.apache.shardingsphere.mcp.core.tool.handler.execute;
+import lombok.AccessLevel;
import lombok.Getter;
import org.apache.shardingsphere.mcp.support.database.capability.SupportedMCPStatement;
import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
@@ -51,22 +53,39 @@ public final class ClassificationResult {
private final Collection<String> referencedObjectNames;
+ @Getter(AccessLevel.NONE)
+ private final Collection<SQLStatementObjectName> referencedObjects;
+
private final String savepointName;
public ClassificationResult(final SupportedMCPStatement statementClass, final String statementType, final String normalizedSql, final String targetObjectName, final String savepointName) {
- this(statementClass, statementType, normalizedSql, targetObjectName, savepointName, targetObjectName.isEmpty() ? List.of() : List.of(targetObjectName));
+ this(statementClass, statementType, normalizedSql, savepointName,
+ targetObjectName.isEmpty() ? List.of() : List.of(SQLStatementObjectName.fromNormalizedName(targetObjectName)));
}
- ClassificationResult(final SupportedMCPStatement statementClass, final String statementType, final String normalizedSql, final String targetObjectName, final String savepointName,
- final Collection<String> referencedObjectNames) {
+ ClassificationResult(final SupportedMCPStatement statementClass, final String statementType, final String normalizedSql, final String savepointName,
+ final Collection<SQLStatementObjectName> referencedObjects) {
this.statementClass = statementClass;
this.statementType = statementType;
this.normalizedSql = normalizedSql;
- this.targetObjectName = targetObjectName;
- this.referencedObjectNames = referencedObjectNames;
+ targetObjectName = referencedObjects.isEmpty() ? "" : referencedObjects.iterator().next().objectName();
+ referencedObjectNames = createReferencedObjectNames(referencedObjects);
+ this.referencedObjects = referencedObjects;
this.savepointName = savepointName;
}
+ private Collection<String> createReferencedObjectNames(final Collection<SQLStatementObjectName> referencedObjects) {
+ Collection<String> result = new LinkedHashSet<>(referencedObjects.size(), 1F);
+ for (SQLStatementObjectName each : referencedObjects) {
+ result.add(each.objectName());
+ }
+ return result;
+ }
+
+ Collection<SQLStatementObjectName> getReferencedObjects() {
+ return referencedObjects;
+ }
+
/**
* Get the target object name when one exists.
*
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidator.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidator.java
index 1e624dc04e8..faf6024e166 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidator.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidator.java
@@ -46,8 +46,7 @@ final class ExplainSQLCandidateValidator {
() -> new MCPInvalidRequestException("Executable comments are not supported by the MCP explain query tool."));
List<SQLStatementToken> tokens = scanner.tokenize(actualExplainSql);
checkExplainCandidate(tokens, explainedStatement.getNormalizedSql(), actualExplainSql);
- return new ClassificationResult(SupportedMCPStatement.EXPLAIN, "EXPLAIN", actualExplainSql, explainedStatement.getTargetObjectName().orElse(""), "",
- explainedStatement.getReferencedObjectNames());
+ return new ClassificationResult(SupportedMCPStatement.EXPLAIN, "EXPLAIN", actualExplainSql, "", explainedStatement.getReferencedObjects());
}
private void checkExplainCandidate(final List<SQLStatementToken> tokens, final String sql, final String explainSql) {
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java
index a0216a082ee..1b8d03dd74a 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java
@@ -20,6 +20,8 @@ package org.apache.shardingsphere.mcp.core.tool.handler.execute;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicy;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.mcp.api.protocol.exception.MCPInvalidRequestException;
import org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import org.apache.shardingsphere.mcp.api.protocol.exception.MCPUnsupportedException;
@@ -161,24 +163,26 @@ public final class MCPSQLExecutionFacade implements MCPFeatureExecutionFacade {
if (SchemaExecutionSemantics.BEST_EFFORT == databaseCapability.getSchemaExecutionSemantics()) {
return;
}
- for (String each : classificationResult.getReferencedObjectNames()) {
- if (isCrossSchemaReference(each, executionRequest.getDatabase(), classificationResult)) {
+ IdentifierCasePolicy identifierCasePolicy = databaseCapability.getIdentifierCasePolicySet().getPolicy(IdentifierScope.SCHEMA);
+ for (SQLStatementObjectName each : classificationResult.getReferencedObjects()) {
+ if (isCrossSchemaReference(each, executionRequest.getDatabase(), classificationResult, identifierCasePolicy)) {
throw recordFailure(executionRequest, classificationResult.getTraceStatementMarker(), new MCPInvalidRequestException(
- String.format("Cross-schema SQL is not supported for database `%s`: `%s`.", executionRequest.getDatabase(), each)));
+ String.format("Cross-schema SQL is not supported for database `%s`: `%s`.", executionRequest.getDatabase(), each.objectName())));
}
}
}
- private boolean isCrossSchemaReference(final String objectName, final String databaseName, final ClassificationResult classificationResult) {
- int qualifierSeparatorIndex = objectName.indexOf('.');
- if (-1 != qualifierSeparatorIndex) {
- return !objectName.substring(0, qualifierSeparatorIndex).equalsIgnoreCase(databaseName);
+ private boolean isCrossSchemaReference(final SQLStatementObjectName objectName, final String databaseName, final ClassificationResult classificationResult,
+ final IdentifierCasePolicy identifierCasePolicy) {
+ if (objectName.qualified()) {
+ return !identifierCasePolicy.matches(databaseName, objectName.firstIdentifier(), objectName.firstIdentifierQuoteCharacter());
}
- return isDatabaseOrSchemaBoundaryReference(objectName, databaseName, classificationResult);
+ return isDatabaseOrSchemaBoundaryReference(objectName, databaseName, classificationResult, identifierCasePolicy);
}
- private boolean isDatabaseOrSchemaBoundaryReference(final String objectName, final String databaseName, final ClassificationResult classificationResult) {
- if (objectName.equalsIgnoreCase(databaseName)) {
+ private boolean isDatabaseOrSchemaBoundaryReference(final SQLStatementObjectName objectName, final String databaseName, final ClassificationResult classificationResult,
+ final IdentifierCasePolicy identifierCasePolicy) {
+ if (identifierCasePolicy.matches(databaseName, objectName.firstIdentifier(), objectName.firstIdentifierQuoteCharacter())) {
return false;
}
String actualSql = classificationResult.getNormalizedSql();
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectName.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectName.java
index 3f586cc779e..0f8f97f4bc7 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectName.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectName.java
@@ -19,18 +19,45 @@ package org.apache.shardingsphere.mcp.core.tool.handler.execute;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
@AllArgsConstructor(access = AccessLevel.PACKAGE)
+@EqualsAndHashCode(exclude = "nextIndex")
final class SQLStatementObjectName {
private final String objectName;
+ private final String firstIdentifier;
+
+ private final QuoteCharacter firstIdentifierQuoteCharacter;
+
+ private final boolean qualified;
+
private final int nextIndex;
+ static SQLStatementObjectName fromNormalizedName(final String objectName) {
+ int qualifierSeparatorIndex = objectName.indexOf('.');
+ return new SQLStatementObjectName(objectName, -1 == qualifierSeparatorIndex ? objectName : objectName.substring(0, qualifierSeparatorIndex),
+ QuoteCharacter.NONE, -1 != qualifierSeparatorIndex, 0);
+ }
+
String objectName() {
return objectName;
}
+ String firstIdentifier() {
+ return firstIdentifier;
+ }
+
+ QuoteCharacter firstIdentifierQuoteCharacter() {
+ return firstIdentifierQuoteCharacter;
+ }
+
+ boolean qualified() {
+ return qualified;
+ }
+
int nextIndex() {
return nextIndex;
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectNameReader.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectNameReader.java
index 80c7c43b9c3..0a8b64938c1 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectNameReader.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementObjectNameReader.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.mcp.core.tool.handler.execute;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
import java.util.List;
@@ -39,24 +40,28 @@ final class SQLStatementObjectNameReader {
continue;
}
if ("(".equals(token.text())) {
- return new SQLStatementObjectName("", currentIndex);
+ return new SQLStatementObjectName("", "", QuoteCharacter.NONE, false, currentIndex);
}
return readQualifiedName(tokens, currentIndex);
}
- return new SQLStatementObjectName("", currentIndex);
+ return new SQLStatementObjectName("", "", QuoteCharacter.NONE, false, currentIndex);
}
SQLStatementObjectName readQualifiedName(final List<SQLStatementToken> tokens, final int startIndex) {
if (startIndex >= tokens.size() || !isObjectNameSegment(tokens.get(startIndex))) {
- return new SQLStatementObjectName("", startIndex);
+ return new SQLStatementObjectName("", "", QuoteCharacter.NONE, false, startIndex);
}
- StringBuilder result = new StringBuilder(scanner.normalizeIdentifier(tokens.get(startIndex).text()));
+ SQLStatementToken firstToken = tokens.get(startIndex);
+ String firstIdentifier = scanner.normalizeIdentifier(firstToken.text());
+ StringBuilder result = new StringBuilder(firstIdentifier);
int currentIndex = startIndex + 1;
+ boolean qualified = false;
while (currentIndex + 1 < tokens.size() && ".".equals(tokens.get(currentIndex).text()) && isObjectNameSegment(tokens.get(currentIndex + 1))) {
result.append('.').append(scanner.normalizeIdentifier(tokens.get(currentIndex + 1).text()));
currentIndex += 2;
+ qualified = true;
}
- return new SQLStatementObjectName(result.toString(), currentIndex);
+ return new SQLStatementObjectName(result.toString(), firstIdentifier, QuoteCharacter.getQuoteCharacter(firstToken.text()), qualified, currentIndex);
}
int skipObjectTail(final List<SQLStatementToken> tokens, final int startIndex) {
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
index 95fd5652350..5a34c7895b0 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/SQLStatementTargetResolver.java
@@ -43,18 +43,13 @@ final class SQLStatementTargetResolver {
objectNameReader = new SQLStatementObjectNameReader(scanner);
}
- String resolve(final SQLStatementStructure statementStructure) {
- Collection<String> objectNames = resolveAll(statementStructure);
- return objectNames.isEmpty() ? "" : objectNames.iterator().next();
- }
-
- Collection<String> resolveAll(final SQLStatementStructure statementStructure) {
- Set<String> result = new LinkedHashSet<>(16, 1F);
+ Collection<SQLStatementObjectName> resolveAll(final SQLStatementStructure statementStructure) {
+ Set<SQLStatementObjectName> result = new LinkedHashSet<>(16, 1F);
collect(statementStructure, new LinkedList<>(), result);
return result;
}
- private void collect(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collect(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<SQLStatementObjectName> objectNames) {
if ("SELECT".equals(statementStructure.statementType())) {
collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, objectNames);
collectSelectTargetObjectNames(statementStructure, visitedAliases, objectNames);
@@ -65,7 +60,8 @@ final class SQLStatementTargetResolver {
collectCommonTableExpressionObjectNames(statementStructure, visitedAliases, objectNames);
}
- private void collectCommonTableExpressionObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collectCommonTableExpressionObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames) {
for (SQLCommonTableExpression each : statementStructure.commonTableExpressions()) {
String normalizedAliasName = scanner.normalizeIdentifierForComparison(each.aliasName());
if (visitedAliases.contains(normalizedAliasName)) {
@@ -75,11 +71,13 @@ final class SQLStatementTargetResolver {
}
}
- private void collectSelectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collectSelectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames) {
collectClauseObjectNames(statementStructure, visitedAliases, objectNames, "FROM", "JOIN");
}
- private void collectClauseObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames, final String... keywords) {
+ private void collectClauseObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames, final String... keywords) {
List<SQLStatementToken> tokens = scanner.tokenize(statementStructure.mainSql());
for (int each : findKeywordIndexes(tokens, keywords)) {
collectObjectNamesFromClause(statementStructure, tokens, each, visitedAliases, objectNames);
@@ -89,7 +87,7 @@ final class SQLStatementTargetResolver {
}
private void collectObjectNamesFromClause(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final int keywordIndex,
- final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ final Collection<String> visitedAliases, final Collection<SQLStatementObjectName> objectNames) {
int objectStartIndex = keywordIndex + 1;
if (scanner.isKeyword(tokens.get(keywordIndex), "INHERITS") && objectStartIndex < tokens.size() && "(".equals(tokens.get(objectStartIndex).text())) {
objectStartIndex++;
@@ -97,7 +95,8 @@ final class SQLStatementTargetResolver {
collectObjectNamesFromList(statementStructure, tokens, objectStartIndex, visitedAliases, objectNames, "ONLY", "LATERAL");
}
- private void collectNestedQueryObjectNames(final List<SQLStatementToken> tokens, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collectNestedQueryObjectNames(final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames) {
int index = 0;
while (index < tokens.size()) {
if (!"(".equals(tokens.get(index).text())) {
@@ -115,7 +114,8 @@ final class SQLStatementTargetResolver {
}
}
- private void collectObjectName(final SQLStatementStructure statementStructure, final String objectName, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collectObjectName(final SQLStatementStructure statementStructure, final SQLStatementObjectName objectName, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames) {
Optional<SQLCommonTableExpression> commonTableExpression = findCommonTableExpression(statementStructure, objectName);
if (commonTableExpression.isEmpty()) {
addObjectName(objectNames, objectName);
@@ -128,7 +128,8 @@ final class SQLStatementTargetResolver {
}
}
- private void collectDirectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases, final Collection<String> objectNames) {
+ private void collectDirectTargetObjectNames(final SQLStatementStructure statementStructure, final Collection<String> visitedAliases,
+ final Collection<SQLStatementObjectName> objectNames) {
List<SQLStatementToken> tokens = scanner.tokenize(statementStructure.mainSql());
String statementType = statementStructure.statementType();
if ("INSERT".equals(statementType) || "MERGE".equals(statementType)) {
@@ -170,7 +171,7 @@ final class SQLStatementTargetResolver {
}
private void collectDeleteTargetObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> objectNames) {
+ final Collection<SQLStatementObjectName> objectNames) {
int deleteTargetStartIndex = skipDeleteModifierKeywords(tokens, 1);
if (deleteTargetStartIndex < tokens.size() && !scanner.isKeyword(tokens.get(deleteTargetStartIndex), "FROM", "USING")) {
collectObjectNamesFromList(statementStructure, tokens, deleteTargetStartIndex, visitedAliases, objectNames);
@@ -197,7 +198,7 @@ final class SQLStatementTargetResolver {
}
private void collectCreateSourceObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> objectNames) {
+ final Collection<SQLStatementObjectName> objectNames) {
if (isStatementObjectType(tokens, "INDEX", "TRIGGER", "POLICY")) {
collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
@@ -207,14 +208,14 @@ final class SQLStatementTargetResolver {
}
private void collectDropSourceObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> objectNames) {
+ final Collection<SQLStatementObjectName> objectNames) {
if (isStatementObjectType(tokens, "INDEX")) {
collectObjectNamesAfterKeyword(statementStructure, tokens, "ON", visitedAliases, objectNames);
}
}
private void collectAlterDestinationObjectNames(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> objectNames) {
+ final Collection<SQLStatementObjectName> objectNames) {
for (int index = 0; index < tokens.size(); index++) {
if (isRenameToDestination(tokens, index)) {
collectObjectNamesFromList(statementStructure, tokens, index + 2, visitedAliases, objectNames);
@@ -251,8 +252,11 @@ final class SQLStatementTargetResolver {
return result;
}
- private Optional<SQLCommonTableExpression> findCommonTableExpression(final SQLStatementStructure statementStructure, final String aliasName) {
- String normalizedAliasName = scanner.normalizeIdentifierForComparison(aliasName);
+ private Optional<SQLCommonTableExpression> findCommonTableExpression(final SQLStatementStructure statementStructure, final SQLStatementObjectName objectName) {
+ if (objectName.qualified()) {
+ return Optional.empty();
+ }
+ String normalizedAliasName = scanner.normalizeIdentifierForComparison(objectName.objectName());
for (SQLCommonTableExpression each : statementStructure.commonTableExpressions()) {
if (scanner.normalizeIdentifierForComparison(each.aliasName()).equals(normalizedAliasName)) {
return Optional.of(each);
@@ -262,7 +266,7 @@ final class SQLStatementTargetResolver {
}
private void collectObjectNamesAfterTypeKeyword(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final Collection<String> visitedAliases,
- final Collection<String> objectNames, final String... optionalKeywords) {
+ final Collection<SQLStatementObjectName> objectNames, final String... optionalKeywords) {
for (int index = 0; index < tokens.size(); index++) {
if (SQL_OBJECT_TYPE_KEYWORDS.contains(tokens.get(index).upperText())) {
collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, objectNames, optionalKeywords);
@@ -272,7 +276,7 @@ final class SQLStatementTargetResolver {
}
private boolean collectObjectNamesAfterKeyword(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final String keyword,
- final Collection<String> visitedAliases, final Collection<String> objectNames, final String... optionalKeywords) {
+ final Collection<String> visitedAliases, final Collection<SQLStatementObjectName> objectNames, final String... optionalKeywords) {
for (int index = 0; index < tokens.size(); index++) {
if (scanner.isKeyword(tokens.get(index), keyword)) {
collectObjectNamesFromList(statementStructure, tokens, index + 1, visitedAliases, objectNames, optionalKeywords);
@@ -283,14 +287,14 @@ final class SQLStatementTargetResolver {
}
private void collectObjectNamesFromList(final SQLStatementStructure statementStructure, final List<SQLStatementToken> tokens, final int startIndex,
- final Collection<String> visitedAliases, final Collection<String> objectNames, final String... optionalKeywords) {
+ final Collection<String> visitedAliases, final Collection<SQLStatementObjectName> objectNames, final String... optionalKeywords) {
int currentIndex = startIndex;
while (currentIndex < tokens.size()) {
SQLStatementObjectName objectName = objectNameReader.readObjectName(tokens, currentIndex, optionalKeywords);
if (objectName.objectName().isEmpty()) {
return;
}
- collectObjectName(statementStructure, objectName.objectName(), visitedAliases, objectNames);
+ collectObjectName(statementStructure, objectName, visitedAliases, objectNames);
currentIndex = objectNameReader.skipObjectTail(tokens, objectName.nextIndex());
if (currentIndex >= tokens.size() || !",".equals(tokens.get(currentIndex).text())) {
return;
@@ -299,12 +303,12 @@ final class SQLStatementTargetResolver {
}
}
- private void collectQualifiedFunctionNames(final List<SQLStatementToken> tokens, final Collection<String> objectNames) {
+ private void collectQualifiedFunctionNames(final List<SQLStatementToken> tokens, final Collection<SQLStatementObjectName> objectNames) {
int index = 0;
while (index < tokens.size()) {
SQLStatementObjectName objectName = objectNameReader.readQualifiedName(tokens, index);
if (isQualifiedFunctionName(tokens, objectName)) {
- addObjectName(objectNames, objectName.objectName());
+ addObjectName(objectNames, objectName);
index = objectName.nextIndex() - 1;
}
index++;
@@ -316,8 +320,8 @@ final class SQLStatementTargetResolver {
&& "(".equals(tokens.get(objectName.nextIndex()).text());
}
- private void addObjectName(final Collection<String> objectNames, final String objectName) {
- if (!objectName.isEmpty()) {
+ private void addObjectName(final Collection<SQLStatementObjectName> objectNames, final SQLStatementObjectName objectName) {
+ if (!objectName.objectName().isEmpty()) {
objectNames.add(objectName);
}
}
diff --git a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/StatementClassifier.java b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/StatementClassifier.java
index db4a778ac8e..94dbec4bb2b 100644
--- a/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/StatementClassifier.java
+++ b/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/StatementClassifier.java
@@ -60,8 +60,7 @@ public final class StatementClassifier {
SQLStatementStructure statementStructure = structureResolver.resolve(actualSql);
SupportedMCPStatement statementClass = statementClassResolver.resolve(statementStructure);
safetyValidator.checkStructuredStatement(statementClass, statementStructure);
- return new ClassificationResult(statementClass, statementStructure.statementType(), actualSql, targetResolver.resolve(statementStructure), "",
- targetResolver.resolveAll(statementStructure));
+ return new ClassificationResult(statementClass, statementStructure.statementType(), actualSql, "", targetResolver.resolveAll(statementStructure));
}
private boolean isTransactionControlStatement(final String upperSql) {
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/MCPCompletionServiceTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/MCPCompletionServiceTest.java
index a92d116858c..b87e625318c 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/MCPCompletionServiceTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/MCPCompletionServiceTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.core.completion;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.infra.spi.ShardingSphereServiceLoader;
import org.apache.shardingsphere.mcp.api.protocol.exception.MCPInvalidRequestException;
import org.apache.shardingsphere.mcp.core.completion.provider.MetadataCompletionProvider;
@@ -144,8 +145,8 @@ class MCPCompletionServiceTest {
private MCPRuntimeContext createRuntimeContext(final WorkflowSessionContext workflowSessionContext) {
MCPDatabaseCapabilityProvider databaseCapabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
when(databaseCapabilityProvider.getDatabaseProfiles()).thenReturn(List.of(
- new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true),
- new RuntimeDatabaseProfile("warehouse", "FixtureWarehouseDB", "2.0", true, true)));
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet()),
+ new RuntimeDatabaseProfile("warehouse", "FixtureWarehouseDB", "2.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet())));
MCPRuntimeContext result = mock(MCPRuntimeContext.class, RETURNS_DEEP_STUBS);
when(result.getDatabaseCapabilityProvider()).thenReturn(databaseCapabilityProvider);
when(result.getWorkflowSessionContext()).thenReturn(workflowSessionContext);
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
index 94de6294c26..48a44a21857 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/completion/provider/MetadataCompletionProviderTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.core.completion.provider;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
@@ -260,11 +261,11 @@ class MetadataCompletionProviderTest {
}
private RuntimeDatabaseProfile createDatabaseProfile(final String database) {
- return new RuntimeDatabaseProfile(database, "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile(database, "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private RuntimeDatabaseProfile createDatabaseMetadata() {
- return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private ShardingSphereSchema createSchemaMetadata() {
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
index df4ff8adb29..aa169fdbcd1 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourceHandlerTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.core.resource.handler.metadata;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.mcp.api.protocol.response.MCPResponse;
import org.apache.shardingsphere.mcp.api.resource.MCPUriVariables;
import org.apache.shardingsphere.mcp.api.resource.descriptor.MCPResourceDescriptor;
@@ -113,7 +114,8 @@ class MetadataResourceHandlerTest {
@Test
void assertHandleListResourceWithEmptyScope() {
MetadataResourceHandler handler = new MetadataResourceHandler("shardingsphere://databases/{database}/schemas", (requestContext, uriVariables) -> List.of());
- MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true))),
+ MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet()))),
new MCPUriVariables(Map.of("database", "logic_db")));
Map<?, ?> actualEmptyState = (Map<?, ?>) actual.toPayload().get("empty_state");
assertThat(actualEmptyState.get("category"), is("empty_scope"));
@@ -124,7 +126,8 @@ class MetadataResourceHandlerTest {
@Test
void assertHandleSchemaDetailResourceNotVisible() {
MetadataResourceHandler handler = new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}", (requestContext, uriVariables) -> List.of());
- MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true))),
+ MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet()))),
new MCPUriVariables(Map.of("database", "logic_db", "schema", "missing_schema")));
Map<?, ?> actualEmptyState = (Map<?, ?>) actual.toPayload().get("empty_state");
assertThat(actualEmptyState.get("category"), is("schema_not_visible"));
@@ -136,7 +139,8 @@ class MetadataResourceHandlerTest {
@Test
void assertHandleObjectDetailResourceNotVisible() {
MetadataResourceHandler handler = new MetadataResourceHandler("shardingsphere://databases/{database}/schemas/{schema}/tables/{table}", (requestContext, uriVariables) -> List.of());
- MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true))),
+ MCPResponse actual = handler.handle(createDatabaseContext(Optional.of(
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet()))),
new MCPUriVariables(Map.of("database", "logic_db", "schema", "public", "table", "missing_table")));
Map<?, ?> actualEmptyState = (Map<?, ?>) actual.toPayload().get("empty_state");
assertThat(actualEmptyState.get("category"), is("object_not_visible"));
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourcePayloadMapperTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourcePayloadMapperTest.java
index 0c2ee4ba367..fdf2505fe88 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourcePayloadMapperTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/resource/handler/metadata/MetadataResourcePayloadMapperTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.core.resource.handler.metadata;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereColumn;
@@ -46,7 +47,8 @@ class MetadataResourcePayloadMapperTest {
MCPMetadataQueryFacade metadataQueryFacade = mock(MCPMetadataQueryFacade.class);
when(metadataQueryFacade.querySchemas("logic_db")).thenReturn(List.of(createSchemaMetadata()));
List<?> actual = new MetadataResourcePayloadMapper(metadataQueryFacade, new MCPUriVariables(Map.of()), true)
- .map(createMetadata("logical-database"), List.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true)));
+ .map(createMetadata("logical-database"), List.of(
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet())));
Map<?, ?> actualDatabase = (Map<?, ?>) actual.getFirst();
assertThat(actualDatabase.get("database"), is("logic_db"));
assertThat(actualDatabase.get("databaseType"), is("FixtureDB"));
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidatorTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidatorTest.java
index cec31a884ce..e7c217ab0e6 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidatorTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExplainSQLCandidateValidatorTest.java
@@ -51,6 +51,12 @@ class ExplainSQLCandidateValidatorTest {
assertThat(actual.getNormalizedSql(), is("EXPLAIN QUERY TREE SELECT * FROM foo_orders WHERE status = 'READY TO SHIP'"));
}
+ @Test
+ void assertValidateHiveCBO() {
+ ClassificationResult actual = validator.validate("SELECT * FROM foo_orders", "EXPLAIN CBO SELECT * FROM foo_orders");
+ assertThat(actual.getNormalizedSql(), is("EXPLAIN CBO SELECT * FROM foo_orders"));
+ }
+
@Test
void assertValidateWithNonExecutableComments() {
ClassificationResult actual = validator.validate("SELECT '/*!80018 ANALYZE */' FROM foo_orders",
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
index 27c2dc8c221..10f9c2ef0e9 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacadeTest.java
@@ -17,6 +17,8 @@
package org.apache.shardingsphere.mcp.core.tool.handler.execute;
+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.api.protocol.exception.MCPInvalidRequestException;
import org.apache.shardingsphere.mcp.api.protocol.exception.MCPQueryFailedException;
import org.apache.shardingsphere.mcp.core.protocol.exception.MCPBannedSQLStatementException;
@@ -40,7 +42,6 @@ import org.mockito.ArgumentCaptor;
import java.sql.SQLException;
import java.sql.SQLSyntaxErrorException;
import java.util.Collections;
-import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
@@ -214,8 +215,7 @@ class MCPSQLExecutionFacadeTest {
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY));
SQLExecutionRequest request = createExecutionRequest("SELECT * FROM other_db.orders");
- ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "",
- List.of("other_db.orders"));
+ ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "");
MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
@@ -271,8 +271,7 @@ class MCPSQLExecutionFacadeTest {
SQLExecutionTraceFactory traceFactory = mock(SQLExecutionTraceFactory.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.DML));
SQLExecutionRequest request = createExecutionRequest("UPDATE other_db.orders SET status = 'DONE'");
- ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.DML, "UPDATE", "UPDATE other_db.orders SET status = 'DONE'", "other_db.orders", "",
- List.of("other_db.orders"));
+ ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.DML, "UPDATE", "UPDATE other_db.orders SET status = 'DONE'", "other_db.orders", "");
MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, transactionExecutor, statementExecutor, traceFactory, createStatementClassifier(classification));
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
@@ -288,8 +287,7 @@ class MCPSQLExecutionFacadeTest {
MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY));
- ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM logic_db.orders", "logic_db.orders", "",
- List.of("logic_db.orders"));
+ ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM logic_db.orders", "logic_db.orders", "");
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
@@ -305,14 +303,44 @@ class MCPSQLExecutionFacadeTest {
verifyNoInteractions(transactionExecutor);
}
+ @Test
+ void assertExecuteWithCaseInsensitiveQualifiedCurrentDatabase() {
+ MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
+ MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
+ MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
+ MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY));
+ SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
+ mockSessionLock(coordinator);
+ when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
+ SQLExecutionRequest request = createExecutionRequest("SELECT * FROM Logic_DB.orders");
+ when(statementExecutor.execute(eq(request), any(), eq(capability))).thenReturn(response);
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, mock(MCPJdbcTransactionStatementExecutor.class), statementExecutor,
+ mock(SQLExecutionTraceFactory.class));
+ assertThat(facade.execute(request), is(response));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("assertExecuteWithNonMatchingCurrentDatabaseIdentifierCases")
+ void assertExecuteWithNonMatchingCurrentDatabaseIdentifier(final String name, final String sql, final IdentifierCasePolicySet identifierCasePolicySet) {
+ MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
+ MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
+ MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY));
+ when(capability.getIdentifierCasePolicySet()).thenReturn(identifierCasePolicySet);
+ mockSessionLock(coordinator);
+ when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
+ MCPSQLExecutionFacade facade = createFacade(capabilityProvider, coordinator, mock(MCPJdbcTransactionStatementExecutor.class), mock(MCPJdbcStatementExecutor.class),
+ mock(SQLExecutionTraceFactory.class));
+ MCPInvalidRequestException actual = assertThrows(MCPInvalidRequestException.class, () -> facade.execute(createExecutionRequest(sql)));
+ assertThat(actual.getMessage(), is("Cross-schema SQL is not supported for database `logic_db`: `Logic_DB.orders`."));
+ }
+
@Test
void assertExecuteWithCrossSchemaSqlEnabled() {
MCPDatabaseCapabilityProvider capabilityProvider = mock(MCPDatabaseCapabilityProvider.class);
MCPSessionExecutionCoordinator coordinator = mock(MCPSessionExecutionCoordinator.class);
MCPJdbcStatementExecutor statementExecutor = mock(MCPJdbcStatementExecutor.class);
MCPDatabaseCapability capability = createCapability(Set.of(SupportedMCPStatement.QUERY), SchemaExecutionSemantics.BEST_EFFORT);
- ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "",
- List.of("other_db.orders"));
+ ClassificationResult classification = new ClassificationResult(SupportedMCPStatement.QUERY, "SELECT", "SELECT * FROM other_db.orders", "other_db.orders", "");
SQLExecutionResponse response = SQLExecutionResponse.resultSet(SupportedMCPStatement.QUERY, "SELECT", Collections.emptyList(), Collections.emptyList(), false);
mockSessionLock(coordinator);
when(capabilityProvider.provide("logic_db")).thenReturn(Optional.of(capability));
@@ -434,6 +462,7 @@ class MCPSQLExecutionFacadeTest {
MCPDatabaseCapability result = mock(MCPDatabaseCapability.class);
when(result.getSupportedStatementClasses()).thenReturn(supportedStatementClasses);
when(result.getSchemaExecutionSemantics()).thenReturn(schemaExecutionSemantics);
+ when(result.getIdentifierCasePolicySet()).thenReturn(IdentifierCasePolicyFactory.newInsensitivePolicySet());
return result;
}
@@ -449,6 +478,7 @@ class MCPSQLExecutionFacadeTest {
Arguments.of("query aliased object list", "SELECT * FROM logic_db.orders o, other_db.items i", "other_db.items", "QUERY"),
Arguments.of("query partitioned object list", "SELECT * FROM logic_db.orders PARTITION (p0) o, other_db.items i", "other_db.items", "QUERY"),
Arguments.of("cte object list", "WITH query_result AS (SELECT * FROM logic_db.orders, other_db.items) SELECT * FROM query_result", "other_db.items", "QUERY"),
+ Arguments.of("qualified object distinct from quoted cte alias", "WITH \"other_db.items\" AS (SELECT 1) SELECT * FROM other_db.items", "other_db.items", "QUERY"),
Arguments.of("unused cte reference", "WITH unused_result AS (SELECT * FROM other_db.items) SELECT * FROM logic_db.orders", "other_db.items", "QUERY"),
Arguments.of("insert select object list", "INSERT INTO logic_db.orders_archive SELECT * FROM logic_db.orders, other_db.items", "other_db.items", "DML"),
Arguments.of("update target object list", "UPDATE logic_db.orders o, other_db.items i SET o.status = 'DONE'", "other_db.items", "DML"),
@@ -479,6 +509,12 @@ class MCPSQLExecutionFacadeTest {
Arguments.of("qualified function", "SELECT other_db.refresh_orders()", "other_db.refresh_orders", "QUERY"));
}
+ private static Stream<Arguments> assertExecuteWithNonMatchingCurrentDatabaseIdentifierCases() {
+ return Stream.of(
+ Arguments.of("case-sensitive unquoted identifier", "SELECT * FROM Logic_DB.orders", IdentifierCasePolicyFactory.newSensitivePolicySet()),
+ Arguments.of("quoted identifier exact match", "SELECT * FROM \"Logic_DB\".orders", IdentifierCasePolicyFactory.newInsensitivePolicySet()));
+ }
+
private static Stream<Arguments> assertExecuteExplainWithSyntaxFailureCases() {
return Stream.of(
Arguments.of("JDBC syntax exception", new MCPInvalidRequestException("bad explain", new SQLSyntaxErrorException("bad explain"))),
diff --git a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataPayloadBuilderTest.java b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataPayloadBuilderTest.java
index 3e2c8b01ea9..e1c659c32aa 100644
--- a/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataPayloadBuilderTest.java
+++ b/mcp/core/src/test/java/org/apache/shardingsphere/mcp/core/tool/handler/metadata/SearchMetadataPayloadBuilderTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.core.tool.handler.metadata;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import org.apache.shardingsphere.mcp.core.tool.request.MetadataSearchRequest;
import org.apache.shardingsphere.mcp.core.tool.response.MetadataSearchHit;
@@ -101,7 +102,8 @@ class SearchMetadataPayloadBuilderTest {
MCPFeatureCapabilityFacade capabilityFacade = mock(MCPFeatureCapabilityFacade.class);
when(result.getMetadataQueryFacade()).thenReturn(metadataQueryFacade);
when(result.getCapabilityFacade()).thenReturn(capabilityFacade);
- when(metadataQueryFacade.queryDatabases()).thenReturn(List.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true)));
+ when(metadataQueryFacade.queryDatabases()).thenReturn(
+ List.of(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet())));
when(metadataQueryFacade.querySchema("logic_db", "public")).thenReturn(Optional.of(mock(ShardingSphereSchema.class)));
when(capabilityFacade.findDatabaseProfile("logic_db")).thenReturn(Optional.of(mock(RuntimeDatabaseProfile.class)));
return result;
diff --git a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningServiceTest.java b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningServiceTest.java
index c291187da7e..b80d9c1ddb0 100644
--- a/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningServiceTest.java
+++ b/mcp/features/encrypt/src/test/java/org/apache/shardingsphere/mcp/feature/encrypt/tool/service/EncryptWorkflowPlanningServiceTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.feature.encrypt.tool.service;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
@@ -373,7 +374,7 @@ class EncryptWorkflowPlanningServiceTest {
}
private RuntimeDatabaseProfile createDatabaseMetadata() {
- return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private ShardingSphereSchema createSchemaMetadata() {
diff --git a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowPlanningServiceTest.java b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowPlanningServiceTest.java
index 4d838b885fa..0d6f99cf9e7 100644
--- a/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowPlanningServiceTest.java
+++ b/mcp/features/mask/src/test/java/org/apache/shardingsphere/mcp/feature/mask/tool/service/MaskWorkflowPlanningServiceTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.feature.mask.tool.service;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
@@ -302,7 +303,7 @@ class MaskWorkflowPlanningServiceTest {
}
private RuntimeDatabaseProfile createDatabaseMetadata() {
- return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private ShardingSphereSchema createSchemaMetadata() {
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapability.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapability.java
index 3723706f94e..fa22b11ba3a 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapability.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapability.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.mcp.support.database.capability;
import lombok.Getter;
import org.apache.shardingsphere.database.connector.core.metadata.database.metadata.option.schema.DialectSchemaSemantics;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
+import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseProfile;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -46,17 +47,16 @@ public final class MCPDatabaseCapability {
private final IdentifierCasePolicySet identifierCasePolicySet;
- public MCPDatabaseCapability(final String databaseName, final boolean supportsTransaction, final boolean supportsSavepoint,
- final IdentifierCasePolicySet identifierCasePolicySet, final MCPDatabaseCapabilityOption option) {
- this.databaseName = databaseName;
+ MCPDatabaseCapability(final RuntimeDatabaseProfile databaseProfile, final MCPDatabaseCapabilityOption option) {
+ databaseName = databaseProfile.getDatabase();
databaseType = option.getType();
MCPDatabaseDialect databaseDialect = MCPDatabaseDialect.of(option.getType());
supportedMetadataObjectTypes = createSupportedMetadataObjectTypes(databaseDialect);
- transactionCapability = databaseDialect.getTransactionCapability(supportsTransaction, supportsSavepoint);
+ transactionCapability = databaseDialect.getTransactionCapability(databaseProfile.isSupportsTransaction(), databaseProfile.isSupportsSavepoint());
supportedStatementClasses = createSupportedStatementClasses(transactionCapability, option.isExplainSupported());
defaultSchemaSemantics = databaseDialect.getDefaultSchemaSemantics();
schemaExecutionSemantics = createSchemaExecutionSemantics(defaultSchemaSemantics);
- this.identifierCasePolicySet = identifierCasePolicySet;
+ identifierCasePolicySet = databaseProfile.getIdentifierCasePolicySet();
}
private static SchemaExecutionSemantics createSchemaExecutionSemantics(final DialectSchemaSemantics defaultSchemaSemantics) {
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProvider.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProvider.java
index 8e7079dcc37..d4f832bc55d 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProvider.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProvider.java
@@ -17,30 +17,17 @@
package org.apache.shardingsphere.mcp.support.database.capability;
-import lombok.RequiredArgsConstructor;
-import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
-import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
-import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
-import org.apache.shardingsphere.infra.metadata.identifier.IdentifierCasePolicyResolver;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.MCPJdbcDatabaseProfileLoader;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
-import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConnectionException;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseProfile;
import org.apache.shardingsphere.mcp.support.database.spi.MCPFeatureCapabilityFacade;
-import javax.sql.DataSource;
-import java.io.PrintWriter;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Properties;
-import java.util.logging.Logger;
/**
* MCP database capability provider.
@@ -53,7 +40,7 @@ public final class MCPDatabaseCapabilityProvider implements MCPFeatureCapability
public MCPDatabaseCapabilityProvider(final Map<String, RuntimeDatabaseConfiguration> runtimeDatabases) {
databaseProfiles = new MCPJdbcDatabaseProfileLoader().load(runtimeDatabases);
- databaseCapabilities = createDatabaseCapabilities(databaseProfiles, runtimeDatabases);
+ databaseCapabilities = createDatabaseCapabilities(databaseProfiles);
}
@Override
@@ -71,83 +58,12 @@ public final class MCPDatabaseCapabilityProvider implements MCPFeatureCapability
return new LinkedList<>(databaseProfiles.values());
}
- private Map<String, MCPDatabaseCapability> createDatabaseCapabilities(final Map<String, RuntimeDatabaseProfile> databaseProfiles,
- final Map<String, RuntimeDatabaseConfiguration> runtimeDatabases) {
+ private Map<String, MCPDatabaseCapability> createDatabaseCapabilities(final Map<String, RuntimeDatabaseProfile> databaseProfiles) {
Map<String, MCPDatabaseCapability> result = new LinkedHashMap<>(databaseProfiles.size(), 1F);
for (RuntimeDatabaseProfile each : databaseProfiles.values()) {
TypedSPILoader.findService(MCPDatabaseCapabilityOption.class, each.getDatabaseType())
- .ifPresent(option -> result.put(each.getDatabase(), new MCPDatabaseCapability(each.getDatabase(), each.isSupportsTransaction(), each.isSupportsSavepoint(),
- resolveIdentifierCasePolicySet(each, runtimeDatabases.get(each.getDatabase())), option)));
+ .ifPresent(option -> result.put(each.getDatabase(), new MCPDatabaseCapability(each, option)));
}
return result;
}
-
- private IdentifierCasePolicySet resolveIdentifierCasePolicySet(final RuntimeDatabaseProfile databaseProfile, final RuntimeDatabaseConfiguration runtimeDatabaseConfig) {
- DatabaseType databaseType = TypedSPILoader.getService(DatabaseType.class, databaseProfile.getDatabaseType());
- return new IdentifierCasePolicyResolver().resolve(databaseType, new ConfigurationProperties(new Properties()),
- new RuntimeDatabaseDataSource(databaseProfile.getDatabase(), runtimeDatabaseConfig));
- }
-
- @RequiredArgsConstructor
- private static final class RuntimeDatabaseDataSource implements DataSource {
-
- private final String databaseName;
-
- private final RuntimeDatabaseConfiguration runtimeDatabaseConfig;
-
- @Override
- public Connection getConnection() throws SQLException {
- try {
- return runtimeDatabaseConfig.openConnection(databaseName);
- } catch (final RuntimeDatabaseConnectionException ex) {
- if (ex.getCause() instanceof SQLException cause) {
- throw cause;
- }
- throw new SQLException(ex);
- }
- }
-
- @Override
- public Connection getConnection(final String username, final String password) throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public PrintWriter getLogWriter() throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public void setLogWriter(final PrintWriter out) throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public void setLoginTimeout(final int seconds) throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public int getLoginTimeout() throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public Logger getParentLogger() throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException();
- }
-
- @Override
- public <T> T unwrap(final Class<T> iface) throws SQLException {
- if (iface.isInstance(this)) {
- return iface.cast(this);
- }
- throw new SQLException(String.format("Unable to unwrap runtime database data source to `%s`.", iface.getName()));
- }
-
- @Override
- public boolean isWrapperFor(final Class<?> iface) {
- return iface.isInstance(this);
- }
- }
}
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 11619244bf5..e86713afa7f 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
@@ -23,6 +23,6 @@ package org.apache.shardingsphere.mcp.support.database.capability.dialect;
public final class ClickHouseMCPDatabaseCapabilityOption extends AbstractMCPDatabaseCapabilityOption {
public ClickHouseMCPDatabaseCapabilityOption() {
- super("ClickHouse", false);
+ super("ClickHouse", true);
}
}
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 2665add48fe..216b534ef24 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
@@ -23,6 +23,6 @@ package org.apache.shardingsphere.mcp.support.database.capability.dialect;
public final class HiveMCPDatabaseCapabilityOption extends AbstractMCPDatabaseCapabilityOption {
public HiveMCPDatabaseCapabilityOption() {
- super("Hive", false);
+ super("Hive", true);
}
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
index df1476994a0..2d4d73a44ce 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoader.java
@@ -17,17 +17,26 @@
package org.apache.shardingsphere.mcp.support.database.metadata.jdbc;
+import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeFactory;
+import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import org.apache.shardingsphere.infra.exception.external.ShardingSphereExternalException;
+import org.apache.shardingsphere.infra.metadata.identifier.IdentifierCasePolicyResolver;
+import javax.sql.DataSource;
+import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Properties;
+import java.util.logging.Logger;
/**
* MCP JDBC database profile loader.
@@ -58,15 +67,27 @@ public final class MCPJdbcDatabaseProfileLoader {
* @throws RuntimeDatabaseConnectionException when profile metadata loading fails
*/
public RuntimeDatabaseProfile load(final String databaseName, final RuntimeDatabaseConfiguration runtimeDatabaseConfig) {
+ DatabaseType databaseType;
+ String databaseVersion;
+ boolean supportsTransaction;
+ boolean supportsSavepoint;
try (Connection connection = runtimeDatabaseConfig.openConnection(databaseName)) {
DatabaseMetaData databaseMetaData = connection.getMetaData();
- DatabaseType databaseType = loadDatabaseType(databaseName, databaseMetaData);
- String databaseVersion = Objects.toString(databaseMetaData.getDatabaseProductVersion(), "").trim();
- boolean supportsTransaction = databaseMetaData.supportsTransactions();
- return new RuntimeDatabaseProfile(databaseName, databaseType.getType(), databaseVersion, supportsTransaction, supportsTransaction && databaseMetaData.supportsSavepoints());
+ databaseType = loadDatabaseType(databaseName, databaseMetaData);
+ databaseVersion = Objects.toString(databaseMetaData.getDatabaseProductVersion(), "").trim();
+ supportsTransaction = databaseMetaData.supportsTransactions();
+ supportsSavepoint = supportsTransaction && databaseMetaData.supportsSavepoints();
} catch (final SQLException ex) {
throw RuntimeDatabaseConnectionException.connectionFailed(databaseName, ex);
}
+ return new RuntimeDatabaseProfile(databaseName, databaseType.getType(), databaseVersion, supportsTransaction, supportsSavepoint,
+ resolveIdentifierCasePolicySet(databaseName, databaseType, runtimeDatabaseConfig));
+ }
+
+ private IdentifierCasePolicySet resolveIdentifierCasePolicySet(final String databaseName, final DatabaseType databaseType,
+ final RuntimeDatabaseConfiguration runtimeDatabaseConfig) {
+ return new IdentifierCasePolicyResolver().resolve(databaseType, new ConfigurationProperties(new Properties()),
+ new RuntimeDatabaseDataSource(databaseName, runtimeDatabaseConfig));
}
private DatabaseType loadDatabaseType(final String databaseName, final DatabaseMetaData databaseMetaData) throws SQLException {
@@ -76,4 +97,67 @@ public final class MCPJdbcDatabaseProfileLoader {
throw RuntimeDatabaseConnectionException.invalidConfiguration(databaseName, ex);
}
}
+
+ @RequiredArgsConstructor
+ private static final class RuntimeDatabaseDataSource implements DataSource {
+
+ private final String databaseName;
+
+ private final RuntimeDatabaseConfiguration runtimeDatabaseConfig;
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ try {
+ return runtimeDatabaseConfig.openConnection(databaseName);
+ } catch (final RuntimeDatabaseConnectionException ex) {
+ if (ex.getCause() instanceof SQLException cause) {
+ throw cause;
+ }
+ throw new SQLException(ex);
+ }
+ }
+
+ @Override
+ public Connection getConnection(final String username, final String password) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public void setLogWriter(final PrintWriter out) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public void setLoginTimeout(final int seconds) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public <T> T unwrap(final Class<T> iface) throws SQLException {
+ if (iface.isInstance(this)) {
+ return iface.cast(this);
+ }
+ throw new SQLException(String.format("Unable to unwrap runtime database data source to `%s`.", iface.getName()));
+ }
+
+ @Override
+ public boolean isWrapperFor(final Class<?> iface) {
+ return iface.isInstance(this);
+ }
+ }
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseProfile.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseProfile.java
index c7d041c2544..7033518c4fc 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseProfile.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/RuntimeDatabaseProfile.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.mcp.support.database.metadata.jdbc;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
/**
* Runtime database profile.
@@ -36,4 +37,6 @@ public final class RuntimeDatabaseProfile {
private final boolean supportsTransaction;
private final boolean supportsSavepoint;
+
+ private final IdentifierCasePolicySet identifierCasePolicySet;
}
diff --git a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationService.java b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationService.java
index b6592b28126..454c9f3fb9c 100644
--- a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationService.java
+++ b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationService.java
@@ -17,6 +17,10 @@
package org.apache.shardingsphere.mcp.support.database.tool.service;
+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.IdentifierCasePolicySet;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.MCPJdbcDatabaseProfileLoader;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.MCPJdbcMetadataLoader;
@@ -95,7 +99,7 @@ public final class RuntimeDatabaseValidationService {
return createFailureResult(database, checks, ex, recoveryFactory);
}
try {
- validateDatabaseVisibility(database, runtimeDatabaseConfig.get(), schemas);
+ validateDatabaseVisibility(database, runtimeDatabaseConfig.get(), schemas, databaseProfile.getIdentifierCasePolicySet());
checks.add(RuntimeDatabaseValidationCheckResult.passed("database_visibility", "Validated the requested database name against visible JDBC metadata and connection context."));
} catch (final RuntimeDatabaseConnectionException ex) {
checks.add(RuntimeDatabaseValidationCheckResult.failed("database_visibility", ex.getCategory(), "The requested database name is not visible to the configured JDBC connection."));
@@ -133,12 +137,13 @@ public final class RuntimeDatabaseValidationService {
return RuntimeDatabaseValidationResult.failed(database, checks, cause.getCategory(), recoveryFactory.apply(cause));
}
- private void validateDatabaseVisibility(final String database, final RuntimeDatabaseConfiguration runtimeDatabaseConfig, final Collection<ShardingSphereSchema> schemas) {
- if (containsVisibleSchema(schemas, database)) {
+ private void validateDatabaseVisibility(final String database, final RuntimeDatabaseConfiguration runtimeDatabaseConfig, final Collection<ShardingSphereSchema> schemas,
+ final IdentifierCasePolicySet identifierCasePolicySet) {
+ if (containsVisibleSchema(schemas, database, identifierCasePolicySet.getPolicy(IdentifierScope.SCHEMA))) {
return;
}
try (Connection connection = runtimeDatabaseConfig.openConnection(resolveExceptionDatabaseName(database))) {
- if (isVisibleDatabase(connection, database)) {
+ if (isVisibleDatabase(connection, database, identifierCasePolicySet)) {
return;
}
} catch (final SQLException ex) {
@@ -148,26 +153,28 @@ public final class RuntimeDatabaseValidationService {
new IllegalStateException(String.format("Requested database `%s` is not visible to the configured JDBC connection.", database)));
}
- private boolean containsVisibleSchema(final Collection<ShardingSphereSchema> schemas, final String database) {
+ private boolean containsVisibleSchema(final Collection<ShardingSphereSchema> schemas, final String database, final IdentifierCasePolicy identifierCasePolicy) {
for (ShardingSphereSchema each : schemas) {
- if (database.equalsIgnoreCase(each.getName())) {
+ if (matches(each.getName(), database, identifierCasePolicy)) {
return true;
}
}
return false;
}
- private boolean isVisibleDatabase(final Connection connection, final String database) throws SQLException {
- return matches(connection.getCatalog(), database)
- || matches(connection.getSchema(), database)
- || containsCatalog(connection.getMetaData(), database)
- || containsSchema(connection.getMetaData(), database);
+ private boolean isVisibleDatabase(final Connection connection, final String database, final IdentifierCasePolicySet identifierCasePolicySet) throws SQLException {
+ IdentifierCasePolicy databasePolicy = identifierCasePolicySet.getPolicy(IdentifierScope.DATABASE);
+ IdentifierCasePolicy schemaPolicy = identifierCasePolicySet.getPolicy(IdentifierScope.SCHEMA);
+ return matches(connection.getCatalog(), database, databasePolicy)
+ || matches(connection.getSchema(), database, schemaPolicy)
+ || containsCatalog(connection.getMetaData(), database, databasePolicy)
+ || containsSchema(connection.getMetaData(), database, schemaPolicy);
}
- private boolean containsCatalog(final DatabaseMetaData databaseMetaData, final String database) throws SQLException {
+ private boolean containsCatalog(final DatabaseMetaData databaseMetaData, final String database, final IdentifierCasePolicy identifierCasePolicy) throws SQLException {
try (ResultSet resultSet = databaseMetaData.getCatalogs()) {
while (resultSet.next()) {
- if (matches(resultSet.getString(1), database)) {
+ if (matches(resultSet.getString(1), database, identifierCasePolicy)) {
return true;
}
}
@@ -175,10 +182,10 @@ public final class RuntimeDatabaseValidationService {
return false;
}
- private boolean containsSchema(final DatabaseMetaData databaseMetaData, final String database) throws SQLException {
+ private boolean containsSchema(final DatabaseMetaData databaseMetaData, final String database, final IdentifierCasePolicy identifierCasePolicy) throws SQLException {
try (ResultSet resultSet = databaseMetaData.getSchemas()) {
while (resultSet.next()) {
- if (matches(resultSet.getString("TABLE_SCHEM"), database)) {
+ if (matches(resultSet.getString("TABLE_SCHEM"), database, identifierCasePolicy)) {
return true;
}
}
@@ -186,8 +193,9 @@ public final class RuntimeDatabaseValidationService {
return false;
}
- private boolean matches(final String actualValue, final String expectedValue) {
- return !Objects.toString(actualValue, "").trim().isEmpty() && actualValue.trim().equalsIgnoreCase(expectedValue);
+ private boolean matches(final String storedName, final String identifier, final IdentifierCasePolicy identifierCasePolicy) {
+ String actualStoredName = Objects.toString(storedName, "").trim();
+ return !actualStoredName.isEmpty() && identifierCasePolicy.matches(actualStoredName, identifier, QuoteCharacter.NONE);
}
private String resolveExceptionDatabaseName(final String database) {
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProviderTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProviderTest.java
index 0ae8ace8025..54aaa387715 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProviderTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/capability/MCPDatabaseCapabilityProviderTest.java
@@ -27,26 +27,17 @@ import org.apache.shardingsphere.database.connector.core.metadata.identifier.Ide
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
-import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeFactory;
-import org.apache.shardingsphere.infra.metadata.identifier.IdentifierCasePolicyResolver;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import org.apache.shardingsphere.mcp.support.fixture.SupportDatabaseTypeFactoryMocker;
+import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.MCPJdbcDatabaseProfileLoader;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConfiguration;
-import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseConnectionException;
+import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.RuntimeDatabaseProfile;
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 org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
-import org.mockito.stubbing.Answer;
-import javax.sql.DataSource;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -59,14 +50,10 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class MCPDatabaseCapabilityProviderTest {
@@ -105,37 +92,11 @@ class MCPDatabaseCapabilityProviderTest {
@Test
void assertProvideWithoutCapabilityOption() {
- MCPDatabaseCapabilityProvider provider = createCapabilityProvider("FixtureDB", "",
- new CapabilityFixture(false, false, false, DialectSchemaSemantics.NATIVE_SCHEMA));
+ MCPDatabaseCapabilityProvider provider = createCapabilityProvider("FixtureDB", new CapabilityFixture(false, false, false, DialectSchemaSemantics.NATIVE_SCHEMA));
assertThat(provider.findDatabaseProfile("logic_db").orElseThrow().getDatabaseType(), is("FixtureDB"));
assertFalse(provider.provide("logic_db").isPresent());
}
- @Test
- void assertResolveIdentifierCasePolicyPerDatabase() throws SQLException {
- Connection firstConnection = mock(Connection.class);
- Connection secondConnection = mock(Connection.class);
- RuntimeDatabaseConfiguration firstRuntimeDatabase = createRuntimeDatabaseConfiguration("first_db", "MySQL", "", true, true, firstConnection);
- RuntimeDatabaseConfiguration secondRuntimeDatabase = createRuntimeDatabaseConfiguration("second_db", "MySQL", "", true, true, secondConnection);
- Map<String, RuntimeDatabaseConfiguration> runtimeDatabases = new LinkedHashMap<>(2, 1F);
- runtimeDatabases.put("first_db", firstRuntimeDatabase);
- runtimeDatabases.put("second_db", secondRuntimeDatabase);
- Map<Connection, IdentifierCasePolicySet> policies = Map.of(
- firstConnection, IdentifierCasePolicyFactory.newSensitivePolicySet(), secondConnection, IdentifierCasePolicyFactory.newInsensitivePolicySet());
- MCPDatabaseCapabilityProvider provider = createCapabilityProvider(runtimeDatabases,
- Map.of("MySQL", new CapabilityFixture(true, true, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA)), invocation -> {
- try (Connection connection = invocation.getArgument(2, DataSource.class).getConnection()) {
- return policies.get(connection);
- }
- });
- assertFalse(provider.provide("first_db").orElseThrow().getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
- assertTrue(provider.provide("second_db").orElseThrow().getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
- verify(firstRuntimeDatabase, times(2)).openConnection("first_db");
- verify(firstRuntimeDatabase, never()).openConnection("second_db");
- verify(secondRuntimeDatabase, times(2)).openConnection("second_db");
- verify(secondRuntimeDatabase, never()).openConnection("first_db");
- }
-
@Test
void assertPreserveScopedIdentifierCasePolicies() {
IdentifierCasePolicySet insensitivePolicySet = IdentifierCasePolicyFactory.newInsensitivePolicySet();
@@ -143,34 +104,14 @@ class MCPDatabaseCapabilityProviderTest {
insensitivePolicySet.getPolicy(IdentifierScope.TABLE),
Map.of(IdentifierScope.TABLE, IdentifierCasePolicyFactory.newSensitivePolicySet().getPolicy(IdentifierScope.TABLE),
IdentifierScope.COLUMN, insensitivePolicySet.getPolicy(IdentifierScope.COLUMN)));
+ CapabilityFixture capabilityFixture = new CapabilityFixture(true, true, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA);
MCPDatabaseCapabilityProvider provider = createCapabilityProvider(
- Map.of("logic_db", createRuntimeDatabaseConfiguration("logic_db", "MySQL", "", true, true)),
- Map.of("MySQL", new CapabilityFixture(true, true, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA)), invocation -> scopedPolicySet);
+ Map.of("logic_db", createDatabaseProfile("logic_db", "MySQL", capabilityFixture, scopedPolicySet)), Map.of("MySQL", capabilityFixture));
IdentifierCasePolicySet actual = provider.provide("logic_db").orElseThrow().getIdentifierCasePolicySet();
assertFalse(actual.getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
assertTrue(actual.getPolicy(IdentifierScope.COLUMN).matches("phone", "Phone", QuoteCharacter.NONE));
}
- @Test
- void assertResolveIdentifierCasePolicyWhenRuntimeConnectionFails() throws SQLException {
- SQLException connectionFailure = new SQLException("connection unavailable");
- Connection profileConnection = mock(Connection.class);
- RuntimeDatabaseConfiguration runtimeDatabase = createRuntimeDatabaseConfiguration("logic_db", "MySQL", "", true, true, profileConnection);
- when(runtimeDatabase.openConnection("logic_db")).thenReturn(profileConnection)
- .thenThrow(RuntimeDatabaseConnectionException.connectionFailed("logic_db", connectionFailure));
- MCPDatabaseCapabilityProvider provider = createCapabilityProvider(Map.of("logic_db", runtimeDatabase),
- Map.of("MySQL", new CapabilityFixture(true, true, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA)), invocation -> {
- try (Connection ignored = invocation.getArgument(2, DataSource.class).getConnection()) {
- return IdentifierCasePolicyFactory.newSensitivePolicySet();
- } catch (final SQLException ex) {
- assertThat(ex, is(connectionFailure));
- return IdentifierCasePolicyFactory.newInsensitivePolicySet();
- }
- });
- assertTrue(provider.provide("logic_db").orElseThrow().getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
- verify(runtimeDatabase, times(2)).openConnection("logic_db");
- }
-
@ParameterizedTest(name = "{0}")
@MethodSource("provideCapabilityMatrixArguments")
void assertProvideWithCapabilityMatrix(final String name, final String databaseType, final boolean expectedTransactionControl,
@@ -178,7 +119,7 @@ class MCPDatabaseCapabilityProviderTest {
final SchemaExecutionSemantics expectedSchemaExecutionSemantics, final boolean expectedExplainSupport) {
CapabilityFixture capabilityFixture = new CapabilityFixture(expectedTransactionControl, expectedSavepoint, expectedSequenceSupport,
SchemaExecutionSemantics.FIXED_TO_DATABASE == expectedSchemaExecutionSemantics ? DialectSchemaSemantics.DATABASE_AS_SCHEMA : DialectSchemaSemantics.NATIVE_SCHEMA);
- Optional<MCPDatabaseCapability> actual = createCapabilityProvider(databaseType, "", capabilityFixture).provide("logic_db");
+ Optional<MCPDatabaseCapability> actual = createCapabilityProvider(databaseType, capabilityFixture).provide("logic_db");
assertTrue(actual.isPresent());
assertThat(actual.get().isSupportsTransactionControl(), is(expectedTransactionControl));
assertThat(actual.get().isSupportsSavepoint(), is(expectedSavepoint));
@@ -193,34 +134,29 @@ class MCPDatabaseCapabilityProviderTest {
Map<String, CapabilityFixture> capabilityFixtures = Map.of(
"MySQL", new CapabilityFixture(true, true, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA),
"Hive", new CapabilityFixture(false, false, false, DialectSchemaSemantics.DATABASE_AS_SCHEMA));
- return createCapabilityProvider(createRuntimeDatabases(Map.of("logic_db", "MySQL", "warehouse", "Hive"), capabilityFixtures), capabilityFixtures);
+ Map<String, RuntimeDatabaseProfile> databaseProfiles = new LinkedHashMap<>(2, 1F);
+ databaseProfiles.put("logic_db", createDatabaseProfile("logic_db", "MySQL", capabilityFixtures.get("MySQL"), IdentifierCasePolicyFactory.newSensitivePolicySet()));
+ databaseProfiles.put("warehouse", createDatabaseProfile("warehouse", "Hive", capabilityFixtures.get("Hive"), IdentifierCasePolicyFactory.newSensitivePolicySet()));
+ return createCapabilityProvider(databaseProfiles, capabilityFixtures);
}
- private MCPDatabaseCapabilityProvider createCapabilityProvider(final String databaseType, final String databaseVersion, final CapabilityFixture capabilityFixture) {
- return createCapabilityProvider(Map.of("logic_db", createRuntimeDatabaseConfiguration("logic_db", databaseType, databaseVersion,
- capabilityFixture.transactionSupported, capabilityFixture.savepointSupported)),
+ private MCPDatabaseCapabilityProvider createCapabilityProvider(final String databaseType, final CapabilityFixture capabilityFixture) {
+ return createCapabilityProvider(Map.of("logic_db", createDatabaseProfile(
+ "logic_db", databaseType, capabilityFixture, IdentifierCasePolicyFactory.newSensitivePolicySet())),
Map.of(databaseType, capabilityFixture));
}
- private MCPDatabaseCapabilityProvider createCapabilityProvider(final Map<String, RuntimeDatabaseConfiguration> runtimeDatabases,
+ private MCPDatabaseCapabilityProvider createCapabilityProvider(final Map<String, RuntimeDatabaseProfile> databaseProfiles,
final Map<String, CapabilityFixture> capabilityFixtures) {
- return createCapabilityProvider(runtimeDatabases, capabilityFixtures, invocation -> {
- try (Connection ignored = invocation.getArgument(2, DataSource.class).getConnection()) {
- return IdentifierCasePolicyFactory.newSensitivePolicySet();
- }
- });
- }
-
- private MCPDatabaseCapabilityProvider createCapabilityProvider(final Map<String, RuntimeDatabaseConfiguration> runtimeDatabases,
- final Map<String, CapabilityFixture> capabilityFixtures,
- final Answer<IdentifierCasePolicySet> identifierCasePolicyResolverAnswer) {
+ Map<String, RuntimeDatabaseConfiguration> runtimeDatabases = new LinkedHashMap<>(databaseProfiles.size(), 1F);
+ for (String each : databaseProfiles.keySet()) {
+ runtimeDatabases.put(each, mock(RuntimeDatabaseConfiguration.class));
+ }
try (
- MockedStatic<DatabaseTypeFactory> ignored = SupportDatabaseTypeFactoryMocker.mockByConnectionMetadata();
+ MockedConstruction<MCPJdbcDatabaseProfileLoader> ignored = mockConstruction(MCPJdbcDatabaseProfileLoader.class,
+ (mock, context) -> when(mock.load(any())).thenReturn(databaseProfiles));
MockedStatic<TypedSPILoader> typedSPILoader = mockStatic(TypedSPILoader.class, CALLS_REAL_METHODS);
- MockedStatic<DatabaseTypedSPILoader> databaseTypedSPILoader = mockStatic(DatabaseTypedSPILoader.class);
- MockedConstruction<IdentifierCasePolicyResolver> ignoredResolver =
- mockConstruction(IdentifierCasePolicyResolver.class,
- (mock, context) -> when(mock.resolve(any(), any(), any())).thenAnswer(identifierCasePolicyResolverAnswer))) {
+ MockedStatic<DatabaseTypedSPILoader> databaseTypedSPILoader = mockStatic(DatabaseTypedSPILoader.class)) {
for (Entry<String, CapabilityFixture> entry : capabilityFixtures.entrySet()) {
mockDatabaseType(entry.getKey(), entry.getValue(), typedSPILoader, databaseTypedSPILoader);
}
@@ -242,41 +178,9 @@ class MCPDatabaseCapabilityProviderTest {
databaseTypedSPILoader.when(() -> DatabaseTypedSPILoader.findService(DialectDatabaseMetaData.class, databaseTypeFromSPI)).thenReturn(Optional.of(dialectDatabaseMetaData));
}
- private Map<String, RuntimeDatabaseConfiguration> createRuntimeDatabases(final Map<String, String> databaseTypes, final Map<String, CapabilityFixture> capabilityFixtures) {
- Map<String, RuntimeDatabaseConfiguration> result = new LinkedHashMap<>(databaseTypes.size(), 1F);
- for (Entry<String, String> entry : databaseTypes.entrySet()) {
- CapabilityFixture capabilityFixture = capabilityFixtures.get(entry.getValue());
- result.put(entry.getKey(), createRuntimeDatabaseConfiguration(entry.getKey(), entry.getValue(), "",
- capabilityFixture.transactionSupported, capabilityFixture.savepointSupported));
- }
- return result;
- }
-
- private RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final String databaseName, final String databaseType, final String databaseVersion,
- final boolean transactionSupported, final boolean savepointSupported) {
- return createRuntimeDatabaseConfiguration(databaseName, databaseType, databaseVersion, transactionSupported, savepointSupported, mock(Connection.class));
- }
-
- private RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final String databaseName, final String databaseType, final String databaseVersion,
- final boolean transactionSupported, final boolean savepointSupported, final Connection connection) {
- RuntimeDatabaseConfiguration result = mock(RuntimeDatabaseConfiguration.class);
- try {
- DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
- Statement statement = mock(Statement.class);
- ResultSet scalarResultSet = mock(ResultSet.class);
- when(result.openConnection(databaseName)).thenReturn(connection);
- when(connection.getMetaData()).thenReturn(databaseMetaData);
- when(connection.createStatement()).thenReturn(statement);
- when(statement.executeQuery(anyString())).thenReturn(scalarResultSet);
- when(scalarResultSet.next()).thenReturn(false);
- when(databaseMetaData.getDatabaseProductVersion()).thenReturn(databaseVersion);
- when(databaseMetaData.supportsTransactions()).thenReturn(transactionSupported);
- when(databaseMetaData.supportsSavepoints()).thenReturn(savepointSupported);
- when(databaseMetaData.getURL()).thenReturn(SupportDatabaseTypeFactoryMocker.createJdbcUrl(databaseType));
- } catch (final SQLException ex) {
- throw new IllegalStateException(ex);
- }
- return result;
+ private RuntimeDatabaseProfile createDatabaseProfile(final String databaseName, final String databaseType, final CapabilityFixture capabilityFixture,
+ final IdentifierCasePolicySet identifierCasePolicySet) {
+ return new RuntimeDatabaseProfile(databaseName, databaseType, "", capabilityFixture.transactionSupported, capabilityFixture.savepointSupported, identifierCasePolicySet);
}
private static Stream<Arguments> provideCapabilityMatrixArguments() {
@@ -287,8 +191,8 @@ class MCPDatabaseCapabilityProviderTest {
Arguments.of("sql server", "SQLServer", true, true, true, SchemaExecutionSemantics.BEST_EFFORT, false),
Arguments.of("mariadb", "MariaDB", true, true, true, SchemaExecutionSemantics.FIXED_TO_DATABASE, true),
Arguments.of("oracle", "Oracle", true, true, true, SchemaExecutionSemantics.BEST_EFFORT, false),
- Arguments.of("clickhouse", "ClickHouse", false, false, false, SchemaExecutionSemantics.FIXED_TO_DATABASE, false),
- Arguments.of("hive", "Hive", false, false, false, SchemaExecutionSemantics.FIXED_TO_DATABASE, false),
+ Arguments.of("clickhouse", "ClickHouse", false, false, false, SchemaExecutionSemantics.FIXED_TO_DATABASE, true),
+ Arguments.of("hive", "Hive", false, false, false, SchemaExecutionSemantics.FIXED_TO_DATABASE, true),
Arguments.of("presto", "Presto", true, false, false, SchemaExecutionSemantics.BEST_EFFORT, true),
Arguments.of("firebird", "Firebird", true, true, true, SchemaExecutionSemantics.BEST_EFFORT, false));
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoaderTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoaderTest.java
index 169fda8e66a..2d6b539f87c 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoaderTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcDatabaseProfileLoaderTest.java
@@ -17,15 +17,24 @@
package org.apache.shardingsphere.mcp.support.database.metadata.jdbc;
+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.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseTypeFactory;
import org.apache.shardingsphere.infra.exception.external.ShardingSphereExternalException;
+import org.apache.shardingsphere.infra.metadata.identifier.IdentifierCasePolicyResolver;
import org.apache.shardingsphere.mcp.support.fixture.SupportDatabaseTypeFactoryMocker;
import org.junit.jupiter.api.Test;
+import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
+import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
+import java.util.LinkedHashMap;
+import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@@ -35,14 +44,22 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class MCPJdbcDatabaseProfileLoaderTest {
@Test
void assertLoad() throws SQLException {
- try (MockedStatic<DatabaseTypeFactory> ignored = SupportDatabaseTypeFactoryMocker.mockByConnectionMetadata()) {
+ IdentifierCasePolicySet expectedIdentifierCasePolicySet = IdentifierCasePolicyFactory.newSensitivePolicySet();
+ try (
+ MockedStatic<DatabaseTypeFactory> ignored = SupportDatabaseTypeFactoryMocker.mockByConnectionMetadata();
+ MockedConstruction<IdentifierCasePolicyResolver> ignoredResolver = mockConstruction(IdentifierCasePolicyResolver.class,
+ (mock, context) -> when(mock.resolve(any(), any(), any())).thenReturn(expectedIdentifierCasePolicySet))) {
RuntimeDatabaseProfile actual =
new MCPJdbcDatabaseProfileLoader().load("logic_db", createRuntimeDatabaseConfiguration(SupportDatabaseTypeFactoryMocker.createJdbcUrl("FixtureDB"), "1.0", true, true));
assertThat(actual.getDatabase(), is("logic_db"));
@@ -50,6 +67,7 @@ class MCPJdbcDatabaseProfileLoaderTest {
assertThat(actual.getDatabaseVersion(), is("1.0"));
assertTrue(actual.isSupportsTransaction());
assertTrue(actual.isSupportsSavepoint());
+ assertThat(actual.getIdentifierCasePolicySet(), is(expectedIdentifierCasePolicySet));
}
}
@@ -63,6 +81,62 @@ class MCPJdbcDatabaseProfileLoaderTest {
}
}
+ @Test
+ void assertLoadIdentifierCasePolicyPerDatabase() throws SQLException {
+ Connection firstConnection = mock(Connection.class);
+ Connection secondConnection = mock(Connection.class);
+ RuntimeDatabaseConfiguration firstRuntimeDatabase = createRuntimeDatabaseConfiguration(
+ SupportDatabaseTypeFactoryMocker.createJdbcUrl("FixtureDB"), "1.0", true, true, firstConnection);
+ RuntimeDatabaseConfiguration secondRuntimeDatabase = createRuntimeDatabaseConfiguration(
+ SupportDatabaseTypeFactoryMocker.createJdbcUrl("FixtureDB"), "1.0", true, true, secondConnection);
+ Map<String, RuntimeDatabaseConfiguration> runtimeDatabases = new LinkedHashMap<>(2, 1F);
+ runtimeDatabases.put("first_db", firstRuntimeDatabase);
+ runtimeDatabases.put("second_db", secondRuntimeDatabase);
+ Map<Connection, IdentifierCasePolicySet> policies = Map.of(
+ firstConnection, IdentifierCasePolicyFactory.newSensitivePolicySet(), secondConnection, IdentifierCasePolicyFactory.newInsensitivePolicySet());
+ try (
+ MockedStatic<DatabaseTypeFactory> ignored = SupportDatabaseTypeFactoryMocker.mockByConnectionMetadata();
+ MockedConstruction<IdentifierCasePolicyResolver> ignoredResolver = mockConstruction(IdentifierCasePolicyResolver.class,
+ (mock, context) -> when(mock.resolve(any(), any(), any())).thenAnswer(invocation -> {
+ try (Connection connection = invocation.getArgument(2, DataSource.class).getConnection()) {
+ return policies.get(connection);
+ }
+ }))) {
+ Map<String, RuntimeDatabaseProfile> actual = new MCPJdbcDatabaseProfileLoader().load(runtimeDatabases);
+ assertFalse(actual.get("first_db").getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
+ assertTrue(actual.get("second_db").getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
+ verify(firstRuntimeDatabase, times(2)).openConnection("first_db");
+ verify(firstRuntimeDatabase, never()).openConnection("second_db");
+ verify(secondRuntimeDatabase, times(2)).openConnection("second_db");
+ verify(secondRuntimeDatabase, never()).openConnection("first_db");
+ }
+ }
+
+ @Test
+ void assertLoadWhenIdentifierCasePolicyConnectionFails() throws SQLException {
+ SQLException connectionFailure = new SQLException("connection unavailable");
+ Connection profileConnection = mock(Connection.class);
+ RuntimeDatabaseConfiguration runtimeDatabaseConfig = createRuntimeDatabaseConfiguration(
+ SupportDatabaseTypeFactoryMocker.createJdbcUrl("FixtureDB"), "1.0", true, true, profileConnection);
+ when(runtimeDatabaseConfig.openConnection("logic_db")).thenReturn(profileConnection)
+ .thenThrow(RuntimeDatabaseConnectionException.connectionFailed("logic_db", connectionFailure));
+ try (
+ MockedStatic<DatabaseTypeFactory> ignored = SupportDatabaseTypeFactoryMocker.mockByConnectionMetadata();
+ MockedConstruction<IdentifierCasePolicyResolver> ignoredResolver = mockConstruction(IdentifierCasePolicyResolver.class,
+ (mock, context) -> when(mock.resolve(any(), any(), any())).thenAnswer(invocation -> {
+ try (Connection ignoredConnection = invocation.getArgument(2, DataSource.class).getConnection()) {
+ return IdentifierCasePolicyFactory.newSensitivePolicySet();
+ } catch (final SQLException ex) {
+ assertThat(ex, is(connectionFailure));
+ return IdentifierCasePolicyFactory.newInsensitivePolicySet();
+ }
+ }))) {
+ RuntimeDatabaseProfile actual = new MCPJdbcDatabaseProfileLoader().load("logic_db", runtimeDatabaseConfig);
+ assertTrue(actual.getIdentifierCasePolicySet().getPolicy(IdentifierScope.TABLE).matches("phone", "Phone", QuoteCharacter.NONE));
+ verify(runtimeDatabaseConfig, times(2)).openConnection("logic_db");
+ }
+ }
+
@Test
void assertLoadWithInvalidJdbcUrl() {
try (MockedStatic<DatabaseTypeFactory> mocked = mockStatic(DatabaseTypeFactory.class)) {
@@ -77,8 +151,13 @@ class MCPJdbcDatabaseProfileLoaderTest {
private RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final String jdbcUrl, final String databaseVersion,
final boolean supportsTransaction, final boolean supportsSavepoint) throws SQLException {
+ return createRuntimeDatabaseConfiguration(jdbcUrl, databaseVersion, supportsTransaction, supportsSavepoint, mock(Connection.class));
+ }
+
+ private RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration(final String jdbcUrl, final String databaseVersion,
+ final boolean supportsTransaction, final boolean supportsSavepoint,
+ final Connection connection) throws SQLException {
RuntimeDatabaseConfiguration result = mock(RuntimeDatabaseConfiguration.class);
- Connection connection = mock(Connection.class);
DatabaseMetaData databaseMetaData = mock(DatabaseMetaData.class);
when(result.openConnection(anyString())).thenReturn(connection);
when(connection.getMetaData()).thenReturn(databaseMetaData);
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderFailureTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderFailureTest.java
index 6b0cd6b7f6c..d4e4334ea55 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderFailureTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/metadata/jdbc/MCPJdbcMetadataLoaderFailureTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.support.database.metadata.jdbc;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.junit.jupiter.api.Test;
import java.sql.SQLException;
@@ -36,7 +37,8 @@ class MCPJdbcMetadataLoaderFailureTest {
when(runtimeDatabaseConfiguration.openConnection("logic_db")).thenThrow(expected);
MCPJdbcMetadataLoader metadataLoader = new MCPJdbcMetadataLoader();
RuntimeDatabaseConnectionException actual = assertThrows(RuntimeDatabaseConnectionException.class,
- () -> metadataLoader.load("logic_db", runtimeDatabaseConfiguration, new RuntimeDatabaseProfile("logic_db", "FixtureDB", "", true, true)));
+ () -> metadataLoader.load("logic_db", runtimeDatabaseConfiguration,
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet())));
assertThat(actual.getMessage(), is("Runtime database `logic_db` connection failed: connection_failed."));
assertThat(actual.getCause(), is(expected));
}
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationServiceTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationServiceTest.java
index 54c895dd707..2a45094769e 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationServiceTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/database/tool/service/RuntimeDatabaseValidationServiceTest.java
@@ -17,6 +17,9 @@
package org.apache.shardingsphere.mcp.support.database.tool.service;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicySet;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.metadata.database.schema.model.ShardingSphereSchema;
import org.apache.shardingsphere.mcp.support.database.metadata.jdbc.MCPJdbcDatabaseProfileLoader;
@@ -127,8 +130,12 @@ class RuntimeDatabaseValidationServiceTest {
MCPJdbcDatabaseProfileLoader profileLoader = getProfileLoader();
MCPJdbcMetadataLoader metadataLoader = getMetadataLoader();
RuntimeDatabaseConfiguration runtimeDatabaseConfig = createRuntimeDatabaseConfiguration();
- when(profileLoader.load(any(), any(RuntimeDatabaseConfiguration.class))).thenReturn(createProfile());
- when(metadataLoader.load(any(), any(RuntimeDatabaseConfiguration.class), any(RuntimeDatabaseProfile.class))).thenReturn(createMetadata("logic_db"));
+ IdentifierCasePolicySet identifierCasePolicySet = new IdentifierCasePolicySet(
+ IdentifierCasePolicyFactory.newSensitivePolicySet().getPolicy(IdentifierScope.TABLE),
+ Map.of(IdentifierScope.SCHEMA, IdentifierCasePolicyFactory.newInsensitivePolicySet().getPolicy(IdentifierScope.SCHEMA)));
+ when(profileLoader.load(any(), any(RuntimeDatabaseConfiguration.class)))
+ .thenReturn(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, identifierCasePolicySet));
+ when(metadataLoader.load(any(), any(RuntimeDatabaseConfiguration.class), any(RuntimeDatabaseProfile.class))).thenReturn(createMetadata("Logic_DB"));
RuntimeDatabaseValidationResult actual = service.validate(new RuntimeDatabaseValidationRequest("logic_db"),
ignored -> Optional.of(runtimeDatabaseConfig), RuntimeDatabaseValidationServiceTest::createRecoveryPayload);
Map<String, Object> actualPayload = actual.toPayload();
@@ -142,8 +149,9 @@ class RuntimeDatabaseValidationServiceTest {
MCPJdbcDatabaseProfileLoader profileLoader = getProfileLoader();
MCPJdbcMetadataLoader metadataLoader = getMetadataLoader();
RuntimeDatabaseConfiguration runtimeDatabaseConfig = new RuntimeDatabaseConfiguration(InvisibleDatabaseDriver.JDBC_URL, "demo", "", InvisibleDatabaseDriver.class.getName());
- when(profileLoader.load(any(), any(RuntimeDatabaseConfiguration.class))).thenReturn(createProfile());
- when(metadataLoader.load(any(), any(RuntimeDatabaseConfiguration.class), any(RuntimeDatabaseProfile.class))).thenReturn(createMetadata("public"));
+ when(profileLoader.load(any(), any(RuntimeDatabaseConfiguration.class))).thenReturn(
+ new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newSensitivePolicySet()));
+ when(metadataLoader.load(any(), any(RuntimeDatabaseConfiguration.class), any(RuntimeDatabaseProfile.class))).thenReturn(createMetadata("Logic_DB"));
RuntimeDatabaseValidationResult actual = service.validate(new RuntimeDatabaseValidationRequest("logic_db"),
ignored -> Optional.of(runtimeDatabaseConfig), RuntimeDatabaseValidationServiceTest::createRecoveryPayload);
Map<String, Object> actualPayload = actual.toPayload();
@@ -153,6 +161,24 @@ class RuntimeDatabaseValidationServiceTest {
assertThat(((Map<?, ?>) ((List<?>) actualPayload.get("checks")).get(4)).get("status"), is("failed"));
}
+ @Test
+ void assertValidateWithVisibleCatalog() throws SQLException {
+ RuntimeDatabaseValidationService service = new RuntimeDatabaseValidationService();
+ RuntimeDatabaseConfiguration runtimeDatabaseConfig = mock(RuntimeDatabaseConfiguration.class);
+ Connection connection = mock(Connection.class);
+ when(runtimeDatabaseConfig.openConnection("logic_db")).thenReturn(connection);
+ when(connection.getCatalog()).thenReturn("Logic_DB");
+ IdentifierCasePolicySet identifierCasePolicySet = new IdentifierCasePolicySet(
+ IdentifierCasePolicyFactory.newInsensitivePolicySet().getPolicy(IdentifierScope.TABLE),
+ Map.of(IdentifierScope.SCHEMA, IdentifierCasePolicyFactory.newSensitivePolicySet().getPolicy(IdentifierScope.SCHEMA)));
+ when(getProfileLoader().load(any(), any(RuntimeDatabaseConfiguration.class)))
+ .thenReturn(new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, identifierCasePolicySet));
+ when(getMetadataLoader().load(any(), any(RuntimeDatabaseConfiguration.class), any(RuntimeDatabaseProfile.class))).thenReturn(createMetadata("public"));
+ RuntimeDatabaseValidationResult actual = service.validate(new RuntimeDatabaseValidationRequest("logic_db"),
+ ignored -> Optional.of(runtimeDatabaseConfig), RuntimeDatabaseValidationServiceTest::createRecoveryPayload);
+ assertThat(actual.toPayload().get("status"), is("ready"));
+ }
+
@Test
void assertValidateWithMetadataReadFailure() {
RuntimeDatabaseValidationService service = new RuntimeDatabaseValidationService();
@@ -215,7 +241,7 @@ class RuntimeDatabaseValidationServiceTest {
}
private static RuntimeDatabaseProfile createProfile() {
- return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private static RuntimeDatabaseConfiguration createRuntimeDatabaseConfiguration() {
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningContextValidatorTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningContextValidatorTest.java
index 90ee55038f1..c8388c9de89 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningContextValidatorTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningContextValidatorTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.support.workflow.service;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
@@ -186,7 +187,7 @@ class WorkflowPlanningContextValidatorTest {
}
private RuntimeDatabaseProfile createDatabaseMetadata() {
- return new RuntimeDatabaseProfile("logic_db", "Fixture", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "Fixture", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private ShardingSphereSchema createSchemaMetadata() {
diff --git a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
index f90c078b005..2cd816f21a4 100644
--- a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
+++ b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/workflow/service/WorkflowPlanningSupportTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.mcp.support.workflow.service;
+import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierCasePolicyFactory;
import org.apache.shardingsphere.database.connector.core.metadata.database.enums.TableType;
import org.apache.shardingsphere.database.connector.core.metadata.identifier.IdentifierScope;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
@@ -354,7 +355,7 @@ class WorkflowPlanningSupportTest {
}
private RuntimeDatabaseProfile createDatabaseMetadata() {
- return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true);
+ return new RuntimeDatabaseProfile("logic_db", "FixtureDB", "1.0", true, true, IdentifierCasePolicyFactory.newInsensitivePolicySet());
}
private ShardingSphereSchema createSchemaMetadata(final String schemaName, final String tableName, final String columnName) {