apache/shardingsphere · error · StatementClassNotSupportedException
Statement class is not supported.
Error message
Statement class is not supported.
What it means
The default arm of the statement-class switch in MCPJdbcStatementExecutor: after execution, a classification whose StatementClass is not QUERY, EXPLAIN, DML, DDL, or DCL (e.g. TCL, SAVEPOINT, TRANSACTION_CONTROL, or an unknown class) reaches StatementClassNotSupportedException. QUERY/EXPLAIN require a result set, DML returns rows or an update count, DDL/DCL return an ack; anything else means the classifier and executor disagree about supported classes.
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPJdbcStatementExecutor.java:251
boolean hasResultSet = statement.execute(classificationResult.getNormalizedSql());
switch (classificationResult.getStatementClass()) {
case QUERY:
case EXPLAIN:
if (!hasResultSet) {
throw new QueryDidNotReturnResultSetException();
}
return createResultSetResult(statement.getResultSet(), executionRequest, classificationResult);
case DML:
return hasResultSet
? createResultSetResult(statement.getResultSet(), executionRequest, classificationResult)
: SQLExecutionResult.updateCount(classificationResult.getStatementClass(), classificationResult.getStatementType(), statement.getUpdateCount(),
executionRequest.getMaxRows(), executionRequest.getTimeoutMs(), classificationResult.getNormalizedSql());
case DDL:
case DCL:
return SQLExecutionResult.statementAck(classificationResult.getStatementClass(), classificationResult.getStatementType(),
executionRequest.getMaxRows(), executionRequest.getTimeoutMs(), classificationResult.getNormalizedSql());
default:
throw new StatementClassNotSupportedException();
}
}
private SQLExecutionResult createResultSetResult(final ResultSet resultSet, final SQLExecutionRequest executionRequest, final ClassificationResult classificationResult) throws SQLException {
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
LinkedList<SQLExecutionColumnDefinition> columns = new LinkedList<>();
for (int index = 1; index <= resultSetMetaData.getColumnCount(); index++) {
columns.add(new SQLExecutionColumnDefinition(resultSetMetaData.getColumnLabel(index), resultSetMetaData.getColumnTypeName(index),
resultSetMetaData.getColumnTypeName(index), ResultSetMetaData.columnNoNulls != resultSetMetaData.isNullable(index)));
}
LinkedList<List<Object>> rows = new LinkedList<>();
boolean truncated = false;
int effectiveMaxRows = 0 >= executionRequest.getMaxRows() ? Integer.MAX_VALUE : executionRequest.getMaxRows();
while (resultSet.next()) {
if (rows.size() >= effectiveMaxRows) {
truncated = true;
break;
}View on GitHub (pinned to e952770a21)
Solutions
- Send transaction-control SQL (BEGIN/COMMIT/ROLLBACK/SAVEPOINT) through the dedicated transaction statement path/tool, not execute_query/execute_update.
- Align versions of the mcp core modules so StatementClass enum values agree between analyzer and executor.
- Capture the failing SQL and its reported statement class and report it if it is a plain QUERY/DML/DDL.
Example fix
// before
await tools.call('database_gateway_execute_query', { sql: 'COMMIT' }); // StatementClassNotSupportedException
// after: use the transaction control tool/path
await tools.call('database_gateway_transaction', { operation: 'commit' }); Defensive patterns
Strategy: validation
Validate before calling
// Keep transaction SQL out of the generic execution tools
const TCL_RE = /^\s*(BEGIN|START\s+TRANSACTION|COMMIT|ROLLBACK|SAVEPOINT|RELEASE\s+SAVEPOINT|SET\s+TRANSACTION)\b/i;
if (TCL_RE.test(sql)) {
throw new Error('Use the transaction control tool for transaction SQL: ' + sql);
}
return tools.call(tool, args); Type guard
function isTransactionControlSql(sql) {
return /^\s*(BEGIN|START\s+TRANSACTION|COMMIT|ROLLBACK|SAVEPOINT|RELEASE\s+SAVEPOINT)\b/i.test(sql);
} Try / catch
try {
return await tools.call('database_gateway_execute_query', { sql });
} catch (e) {
if (/Statement class is not supported/.test(e.message)) {
// log SQL + reported statement class; route transaction SQL to the transaction tool, others report upstream
}
throw e;
} Prevention
- Send BEGIN/COMMIT/ROLLBACK/SAVEPOINT only through the transaction tooling.
- Keep mcp core module versions consistent so StatementClass values match.
- Strip transaction wrappers from SQL before submitting to execution tools.
When it happens
Trigger: A statement classified as a class the JDBC executor does not handle reaching the generic statement executor — normally transaction-control statements routed to the wrong executor path, or a future/unknown StatementClass enum value after a version skew between analyzer and executor.
Common situations: BEGIN/COMMIT/ROLLBACK/SAVEPOINT SQL sent through execute_query or execute_update instead of the transaction tools; mixed version jars on the classpath where a newer analyzer produces classes an older executor cannot switch on.
Related errors
- Statement is not supported by the MCP contract.
- database_gateway_execute_query only supports parser-approved
- database_gateway_execute_update does not accept read-only SQ
- Query did not return a result set.
- Generated explain_sql is not valid for the target database.
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/bdd954f356451575.
Report an issue: GitHub.