prestodb/presto · error · SQLException

SQL statement is not a query:

Error message

SQL statement is not a query: 

What it means

Statement.executeQuery() calls execute(sql) and, if it returns false (meaning the statement produced no ResultSet — i.e. it was an update/DDL statement), throws this SQLException including the offending SQL text. executeQuery is only valid for statements that return rows.

Source

Thrown at presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java:80

        this.connection = new AtomicReference<>(requireNonNull(connection, "connection is null"));
    }

    public void setProgressMonitor(Consumer<QueryStats> progressMonitor)
    {
        progressCallback.set(Optional.of(requireNonNull(progressMonitor, "progressMonitor is null")));
    }

    public void clearProgressMonitor()
    {
        progressCallback.set(Optional.empty());
    }

    @Override
    public ResultSet executeQuery(String sql)
            throws SQLException
    {
        if (!execute(sql)) {
            throw new SQLException("SQL statement is not a query: " + sql);
        }
        return currentResult.get();
    }

    @Override
    public void close()
            throws SQLException
    {
        connection.set(null);
        closeResultSet();
    }

    @Override
    public int getMaxFieldSize()
            throws SQLException
    {
        checkOpen();
        return 0;

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Use executeUpdate() for DML, or execute()/executeLargeUpdate() for DDL and utility statements
  2. Check execute()'s boolean return and only call getResultSet() when true
  3. CAST the statement or add a trivial SELECT if you truly need rows, though the correct API is preferred

Example fix

// before
ResultSet rs = stmt.executeQuery("DROP TABLE t");
// after
boolean hasRs = stmt.execute("DROP TABLE t");
// hasRs == false: statement is not a query
Defensive patterns

Strategy: validation

Validate before calling

String s = sql.trim().toLowerCase();
boolean nonQuery = s.startsWith("insert") || s.startsWith("update") || s.startsWith("delete") || s.startsWith("create") || s.startsWith("drop") || s.startsWith("alter") || s.startsWith("set ") || s.startsWith("call") || s.startsWith("grant") || s.startsWith("revoke");
if (nonQuery) throw new IllegalArgumentException("Use execute/executeUpdate, not executeQuery, for: " + s);

Try / catch

try (ResultSet rs = stmt.executeQuery(sql)) {
    // consume rows
} catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("SQL statement is not a query")) {
        stmt.execute(sql); // re-run as non-query
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling PrestoStatement.executeQuery with INSERT/UPDATE/DELETE/CREATE/DROP/ALTER/SET SESSION/CALL — any statement where execute() returns false because there is no result set.

Common situations: Running DDL or DML through executeQuery out of habit; framework code that always calls executeQuery; migration scripts executed via a query-only API; syntax confusion where a Presto utility statement has no result columns.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/7a708593ef6509ed. Report an issue: GitHub.