{"record":{"id":"f76326b4c3374669","repo":"apache/shardingsphere","slug":"generated-explain-sql-is-not-valid-for-the-target","errorCode":null,"errorMessage":"Generated explain_sql is not valid for the target database.","messagePattern":"Generated explain_sql is not valid for the target database\\.","errorType":"validation","errorClass":"ExplainSQLSyntaxException","httpStatus":null,"severity":"error","filePath":"mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java","lineNumber":86,"sourceCode":"        return sessionExecutionCoordinator.executeWithSessionLock(executionRequest.getSessionId(), () -> {\n            MCPDatabaseCapability databaseCapability = getDatabaseCapability(executionRequest);\n            return executeInternal(executionRequest, statementAnalyzer.analyze(executionRequest.getSql(), databaseCapability), databaseCapability);\n        });\n    }\n    \n    private SQLExecutionResult execute(final SQLExecutionRequest executionRequest, final ClassificationResult classificationResult, final MCPDatabaseCapability databaseCapability) {\n        return sessionExecutionCoordinator.executeWithSessionLock(executionRequest.getSessionId(), () -> executeInternal(executionRequest, classificationResult, databaseCapability));\n    }\n    \n    @Override\n    public SQLExecutionResult executeExplain(final SQLExecutionRequest executionRequest, final String sql) {\n        MCPDatabaseCapability databaseCapability = getDatabaseCapability(executionRequest);\n        ClassificationResult classificationResult = new ExplainSQLCandidateValidator(statementAnalyzer).validate(sql, executionRequest.getSql(), databaseCapability);\n        try {\n            return execute(executionRequest, classificationResult, databaseCapability);\n        } catch (final MCPInvalidRequestException | MCPQueryFailedException ex) {\n            if (MCPJDBCErrorCategory.SYNTAX == MCPJDBCExceptionClassifier.classify(databaseCapability.getDatabaseType(), ex)) {\n                throw new ExplainSQLSyntaxException(executionRequest.getDatabase(), executionRequest.getSchema(), sql, executionRequest.getSql(), ex);\n            }\n            throw ex;\n        }\n    }\n    \n    private MCPDatabaseCapability getDatabaseCapability(final SQLExecutionRequest executionRequest) {\n        Optional<MCPDatabaseCapability> databaseCapability = databaseCapabilityProvider.provide(executionRequest.getDatabase());\n        ShardingSpherePreconditions.checkState(databaseCapability.isPresent(), DatabaseCapabilityNotFoundException::new);\n        return databaseCapability.orElseThrow();\n    }\n    \n    private SQLExecutionResult executeInternal(final SQLExecutionRequest executionRequest, final ClassificationResult classificationResult,\n                                               final MCPDatabaseCapability databaseCapability) {\n        ShardingSpherePreconditions.checkContains(databaseCapability.getSupportedStatementClasses(), classificationResult.getStatementClass(),\n                StatementClassNotSupportedException::new);\n        checkCrossSchemaSql(executionRequest, databaseCapability, classificationResult);\n        return switch (classificationResult.getStatementClass()) {\n            case TRANSACTION_CONTROL, SAVEPOINT -> transactionStatementExecutor.execute(","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPSQLExecutionFacade.java#L68-L104","documentation":"executeExplain wraps execution of a generated EXPLAIN statement; when the underlying execution fails with an MCPInvalidRequestException or MCPQueryFailedException whose JDBC error category is SYNTAX, the facade rethrows it as ExplainSQLSyntaxException — meaning the EXPLAIN statement synthesized for the user's SQL is not valid for the target database. Non-syntax failures are rethrown unchanged, so this error specifically means the dialect's EXPLAIN syntax/format is the problem, not the user's data or connection.","triggerScenarios":"Calling database_gateway_execute_explain_query where the validator accepted the candidate EXPLAIN form but the target database rejects it: unsupported EXPLAIN options for the dialect (e.g. ANALYZE/FORMAT on engines without them), EXPLAIN of statement types the engine cannot explain, or a dialect capability mismatch between the validator and the actual server version.","commonSituations":"Running against an older/newer database version than the capability set assumes; exotic statements (some DDL, stored routine bodies) that engines refuse to EXPLAIN; proxy layers that rewrite EXPLAIN into unsupported forms.","solutions":["Simplify the SQL being explained (remove dialect-specific options like ANALYZE or FORMAT from the original statement).","Verify the target database version supports EXPLAIN for that statement type by running the generated EXPLAIN directly in a SQL client.","Retry with a plain SELECT/UPDATE form of the statement to get the execution plan differently.","If the generated SQL looks valid for your engine, report a dialect capability bug including database type, version, and original SQL."],"exampleFix":"// before\nawait tools.call('database_gateway_execute_explain_query', { sql: 'EXPLAIN ANALYZE SELECT * FROM t' }); // engine rejects -> ExplainSQLSyntaxException\n\n// after (engine without EXPLAIN ANALYZE)\nawait tools.call('database_gateway_execute_explain_query', { sql: 'SELECT * FROM t' }); // let the tool synthesize a supported EXPLAIN","handlingStrategy":"fallback","validationCode":"// Pre-check EXPLAIN feature usage against a dialect allowlist\nconst EXPLAIN_FEATURES = { PostgreSQL: ['ANALYZE', 'FORMAT', 'VERBOSE'], MySQL: ['FORMAT', 'EXTENDED'], OpenGauss: ['ANALYZE', 'FORMAT'] };\nfunction explainSupported(sql, dialect) {\n  const upper = sql.toUpperCase();\n  const allowed = EXPLAIN_FEATURES[dialect] ?? [];\n  const used = (upper.match(/\\bANALYZE\\b|\\bVERBOSE\\b|\\bFORMAT\\s+\\w+/g) ?? []).map(s => s.trim());\n  return used.every(f => allowed.some(a => f.startsWith(a)));\n}\nif (!explainSupported(sql, dialect)) sql = sql.replace(/^\\s*EXPLAIN\\s+[^ ]*\\s*/i, 'EXPLAIN ');","typeGuard":null,"tryCatchPattern":"try {\n  return await tools.call('database_gateway_execute_explain_query', { sql });\n} catch (e) {\n  if (e.name === 'ExplainSQLSyntaxException') {\n    // fall back: strip EXPLAIN options or explain the bare statement\n    return tools.call('database_gateway_execute_explain_query', { sql: stripExplainOptions(sql) });\n  }\n  throw e;\n}","preventionTips":["Send the bare statement and let the tool synthesize the dialect-correct EXPLAIN.","Check the target engine's EXPLAIN option support before embedding options.","Confirm generated EXPLAIN works by running it in a native client when adding new dialects.","Pin the correct database argument so the right dialect capability set is used."],"tags":["mcp","sql","explain","dialect","syntax"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}