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 <[email protected]>
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) {