prestodb/presto · error · SQLFeatureNotSupportedException

Result set type must be TYPE_FORWARD_ONLY

Error message

Result set type must be TYPE_FORWARD_ONLY

What it means

PrestoConnection.checkResultSet() throws SQLFeatureNotSupportedException when createStatement/prepareStatement is called with a resultSetType other than ResultSet.TYPE_FORWARD_ONLY. Presto query results are streamed over HTTP in one direction, so scrollable (TYPE_SCROLL_INSENSITIVE/TYPE_SCROLL_SENSITIVE) result sets are genuinely unsupported, not just unimplemented. The check runs before any statement is created.

Source

Thrown at presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoConnection.java:894

            throws SQLException
    {
        if (isClosed()) {
            throw new SQLException("Connection is closed");
        }
    }

    private void initializeQueryInterceptors()
    {
        for (QueryInterceptor interceptor : this.queryInterceptorInstances) {
            interceptor.init(this.sessionProperties);
        }
    }

    private static void checkResultSet(int resultSetType, int resultSetConcurrency)
            throws SQLFeatureNotSupportedException
    {
        if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {
            throw new SQLFeatureNotSupportedException("Result set type must be TYPE_FORWARD_ONLY");
        }
        if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {
            throw new SQLFeatureNotSupportedException("Result set concurrency must be CONCUR_READ_ONLY");
        }
    }

    private static void checkHoldability(int resultSetHoldability)
            throws SQLFeatureNotSupportedException
    {
        if (resultSetHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
            throw new SQLFeatureNotSupportedException("Result set holdability must be HOLD_CURSORS_OVER_COMMIT");
        }
    }

    private static String getIsolationLevel(int level)
            throws SQLException
    {
        switch (level) {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Use createStatement() / prepareStatement(sql) with defaults (TYPE_FORWARD_ONLY, CONCUR_READ_ONLY)
  2. Explicitly pass ResultSet.TYPE_FORWARD_ONLY and ResultSet.CONCUR_READ_ONLY
  3. If random access is needed, buffer rows into your own list instead of scrolling the ResultSet

Example fix

// before
Statement stmt = conn.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
// after
Statement stmt = conn.createStatement(
    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
Defensive patterns

Strategy: validation

Validate before calling

int type = ResultSet.TYPE_SCROLL_INSENSITIVE; // value read from config
if (type != ResultSet.TYPE_FORWARD_ONLY) {
    type = ResultSet.TYPE_FORWARD_ONLY; // coerce before calling driver
}

Try / catch

try {
    stmt = conn.createStatement(type, ResultSet.CONCUR_READ_ONLY);
} catch (SQLFeatureNotSupportedException e) {
    stmt = conn.createStatement(); // fallback to defaults
}

Prevention

When it happens

Trigger: createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ...), createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ...), prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ...) or any call passing a non-forward-only type.

Common situations: Shared DAO/utility code written for MySQL/Oracle that requests scrollable cursors by default; ORM or reporting frameworks configured with scrollable result sets; copy-pasted connection code from another driver's samples.

Related errors


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