apache/shardingsphere · error · QueryDidNotReturnResultSetException
Query did not return a result set.
Error message
Query did not return a result set.
What it means
In MCPJdbcStatementExecutor.executeStatement, a statement classified as QUERY or EXPLAIN must return a ResultSet; if Statement.execute() reports no result set (hasResultSet == false) the executor throws QueryDidNotReturnResultSetException. It catches driver/classifier divergence — e.g. the analyzer called it a query but the target database treated the statement as an update — instead of NPE-ing on getResultSet().
Source
Thrown at mcp/core/src/main/java/org/apache/shardingsphere/mcp/core/tool/handler/execute/MCPJdbcStatementExecutor.java:238
}
private void configureStatement(final Statement statement, final SQLExecutionRequest executionRequest) throws SQLException {
if (0 < executionRequest.getMaxRows()) {
statement.setMaxRows(resolveStatementMaxRows(executionRequest.getMaxRows()));
}
if (0 < executionRequest.getTimeoutMs()) {
statement.setQueryTimeout((executionRequest.getTimeoutMs() + 999) / 1000);
}
}
private SQLExecutionResult executeStatement(final Statement statement, final SQLExecutionRequest executionRequest,
final ClassificationResult classificationResult) throws SQLException {
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();View on GitHub (pinned to e952770a21)
Solutions
- Rewrite the statement into a form your database returns a result set for (e.g. SELECT INTO -> separate CREATE + INSERT/SELECT).
- For EXPLAIN variants that return no rows, use database_gateway_execute_explain_query so the facade's explain handling applies.
- Reproduce with a plain JDBC client to confirm the driver indeed returns no ResultSet; if it does, report a classifier bug with the exact SQL and database type.
- Check for driver/proxy version mismatches if the same SQL used to work.
Example fix
// before (SQL Server dialect)
await tools.call('database_gateway_execute_query', { sql: 'SELECT a, b INTO new_t FROM t' }); // no ResultSet -> error
// after
await tools.call('database_gateway_execute_update', { sql: 'CREATE TABLE new_t (a INT, b INT)', execution_mode: 'execute' });
await tools.call('database_gateway_execute_update', { sql: 'INSERT INTO new_t SELECT a, b FROM t', execution_mode: 'execute' }); Defensive patterns
Strategy: validation
Validate before calling
// Screen out statement forms known to return no result set on your dialect
function returnsResultSet(sql, dialect) {
const upper = sql.trim().toUpperCase();
if (dialect === 'SQLServer' && /\bINTO\b/.test(upper) && upper.startsWith('SELECT')) return false;
return true;
}
if (!returnsResultSet(sql, dialect)) {
return tools.call('database_gateway_execute_update', { sql, execution_mode: 'execute' });
}
return tools.call('database_gateway_execute_query', { sql }); Type guard
function isPlainResultSetQuery(sql) {
const upper = sql.trim().toUpperCase();
return /^SELECT\b/.test(upper) && !/\bINTO\b/.test(upper);
} Try / catch
try {
return await tools.call('database_gateway_execute_query', { sql });
} catch (e) {
if (/did not return a result set/i.test(e.message)) {
// dialect divergence: reroute to update path or rewrite statement (e.g. SELECT INTO -> CREATE+INSERT)
return rewriteWithoutSelectInto(sql);
}
throw e;
} Prevention
- Avoid dialect-specific non-result-set SELECT forms (SELECT ... INTO) via MCP tools.
- Prefer the explain tool for EXPLAIN statements instead of raw execute_query.
- Verify new SQL shapes with one dry call before wiring them into automated flows.
- Keep driver and analyzer versions aligned with the target database version.
When it happens
Trigger: A SQL statement the analyzer classifies as QUERY/EXPLAIN but the JDBC driver executes without a result set: dialect-specific SELECT-like statements (SELECT ... INTO, some WITH ... DML on certain engines), database-specific EXPLAIN variants that return only update counts, or a driver version whose behavior for a statement differs from the parser's assumption.
Common situations: Porting SQL between dialects (e.g. SQL Server SELECT INTO, MySQL SELECT ... FOR UPDATE via odd drivers); non-standard EXPLAIN syntax; proxy/driver combinations that swallow result sets; statement text mutated by normalization before execution.
Related errors
- Generated explain_sql is not valid for the target database.
- 0
- Column index out of range: %s
- database_gateway_execute_query only supports parser-approved
- database_gateway_execute_update does not accept read-only SQ
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/691fe2f6b26004d2.
Report an issue: GitHub.