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

  1. Send transaction-control SQL (BEGIN/COMMIT/ROLLBACK/SAVEPOINT) through the dedicated transaction statement path/tool, not execute_query/execute_update.
  2. Align versions of the mcp core modules so StatementClass enum values agree between analyzer and executor.
  3. 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

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


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/bdd954f356451575. Report an issue: GitHub.