apache/shardingsphere · error · SQLToolMismatchException
database_gateway_execute_query only supports parser-approved
Error message
database_gateway_execute_query only supports parser-approved QUERY statements. Use database_gateway_execute_explain_query for EXPLAIN diagnostics or database_gateway_execute_update for side-effecting SQL.
What it means
database_gateway_execute_query only accepts statements the analyzer classifies as QUERY; checkReadOnlyQuery analyzes the SQL and throws SQLToolMismatchException when the classification is not a query statement. The message directs the caller to database_gateway_execute_explain_query for EXPLAIN or database_gateway_execute_update for side-effecting SQL, and the exception carries suggested arguments (normalized SQL, database, schema) so a client can automatically reroute to the right tool.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/ExecuteQueryToolHandler.java:60
public String getToolName() {
return CoreToolNames.EXECUTE_QUERY;
}
@Override
public MCPSuccessPayload handle(final MCPFeatureRequestContext requestContext, final Map<String, Object> arguments) {
MCPToolArguments toolArguments = new MCPToolArguments(arguments);
String sql = toolArguments.getStringArgument("sql");
checkReadOnlyQuery(requestContext, toolArguments, sql);
SQLExecutionToolHandlerSupport.checkExecutionArguments(toolArguments, CoreToolNames.EXECUTE_QUERY);
return SQLExecutionPayload.query(requestContext.getExecutionFacade().execute(SQLExecutionToolHandlerSupport.createReadOnlyExecutionRequest(
requestContext.getSessionIdentity().getSessionId(), toolArguments,
SQLExecutionToolHandlerSupport.resolveSchema(requestContext, toolArguments), sql, CoreToolNames.EXECUTE_QUERY)));
}
private void checkReadOnlyQuery(final MCPFeatureRequestContext requestContext, final MCPToolArguments toolArguments, final String sql) {
ClassificationResult classificationResult = SQLExecutionToolHandlerSupport.analyze(requestContext, toolArguments, sql);
if (!SQLExecutionToolHandlerSupport.isQueryStatement(classificationResult)) {
throw new SQLToolMismatchException(
"database_gateway_execute_query only supports parser-approved QUERY statements. "
+ "Use database_gateway_execute_explain_query for EXPLAIN diagnostics or database_gateway_execute_update for side-effecting SQL.",
CoreToolNames.EXECUTE_QUERY, CoreToolNames.EXECUTE_UPDATE, classificationResult,
createSuggestedArguments(toolArguments, classificationResult));
}
}
private Map<String, Object> createSuggestedArguments(final MCPToolArguments toolArguments, final ClassificationResult classificationResult) {
Map<String, Object> result = new LinkedHashMap<>(4, 1F);
SQLExecutionToolHandlerSupport.putIfNotEmpty(result, "database", toolArguments.getStringArgument("database"));
SQLExecutionToolHandlerSupport.putIfNotEmpty(result, "schema", toolArguments.getStringArgument("schema"));
result.put("sql", classificationResult.getNormalizedSql());
result.put(MCPPayloadFieldNames.EXECUTION_MODE, "preview");
return result;
}
}
View on GitHub (pinned to e952770a21)
Solutions
- Route DML/DDL/DCL to database_gateway_execute_update with an explicit execution_mode.
- Route EXPLAIN statements to database_gateway_execute_explain_query.
- Use the exception's suggested-arguments payload to reroute programmatically to the recommended tool.
- Client-side, pre-classify by leading keyword (SELECT/WITH-only reads -> execute_query) before choosing the tool.
Example fix
// before
await tools.call('database_gateway_execute_query', { sql: "UPDATE t SET a=1" }); // SQLToolMismatchException
// after
const leading = sql.trimStart().toUpperCase();
const tool = leading.startsWith('EXPLAIN') ? 'database_gateway_execute_explain_query'
: /^(SELECT|WITH)\b/.test(leading) ? 'database_gateway_execute_query'
: 'database_gateway_execute_update';
await tools.call(tool, leading.startsWith('EXPLAIN') ? { sql } : { sql, execution_mode: 'execute' }); Defensive patterns
Strategy: validation
Validate before calling
// Route by statement shape before choosing the tool
function pickSqlTool(sql) {
const s = sql.trim().replace(/\/\*.*?\*\//g, '').trim();
const upper = s.toUpperCase();
if (/^(EXPLAIN|DESC|DESCRIBE)\b/.test(upper)) return ['database_gateway_execute_explain_query', {}];
if (/^WITH\b/.test(upper)) return null; // CTE: could be read or write - try query, reroute on mismatch
if (/^SELECT\b/.test(upper)) return ['database_gateway_execute_query', {}];
return ['database_gateway_execute_update', { execution_mode: 'execute' }];
}
const [tool, extra] = pickSqlTool(sql); Type guard
function looksLikeQuery(sql) {
const upper = sql.trim().replace(/^[;(\s]+/, '').toUpperCase();
return /^SELECT\b/.test(upper) || (/^WITH\b/.test(upper) && !/\b(INERT|INSERT|UPDATE|DELETE|MERGE)\b/.test(upper));
} Try / catch
try {
return await tools.call('database_gateway_execute_query', { sql });
} catch (e) {
if (e.name === 'SQLToolMismatchException' && e.suggestedArguments) {
return tools.call(e.suggestedTool, e.suggestedArguments); // reroute via exception payload
}
throw e;
} Prevention
- Classify SQL by leading keyword before selecting execute_query vs execute_update vs explain.
- Remember WITH-led statements may be writes; let the mismatch exception's suggested arguments reroute.
- Never send EXPLAIN to execute_query.
- Keep one SQL statement per call; strip leading transactions/semicolons.
When it happens
Trigger: Calling database_gateway_execute_query with INSERT/UPDATE/DELETE/DDL, EXPLAIN ..., or any statement whose leading keyword/parse tree is not a SELECT-class query (including CTE-led writes, which parse as DML despite starting with WITH).
Common situations: LLM agent choosing the query tool for a mutation; EXPLAIN ANALYZE sent to the read-only tool; copying SQL between execute_update and execute_query handlers; WITH ... DELETE misclassified by the caller as a read.
Related errors
- database_gateway_execute_update does not accept read-only SQ
- Cross-schema SQL is not supported for database `%s`: `%s`.
- Statement is banned by the MCP contract.
- Completion argument `%s` is not declared for %s `%s`.
- %s execution_mode is required.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/904fef520abe9156.
Report an issue: GitHub.