prestodb/presto · error · SQLFeatureNotSupportedException

Result set holdability must be HOLD_CURSORS_OVER_COMMIT

Error message

Result set holdability must be HOLD_CURSORS_OVER_COMMIT

What it means

PrestoConnection.checkHoldability() throws SQLFeatureNotSupportedException when the requested resultSetHoldability is not ResultSet.HOLD_CURSORS_OVER_COMMIT. Presto has no cross-transaction cursor semantics; the driver only supports holdable cursors, rejecting CLOSE_CURSORS_AT_COMMIT in createStatement/prepareStatement/createStatement overload with holdability.

Source

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

        }
    }

    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) {
            case TRANSACTION_READ_UNCOMMITTED:
                return "READ UNCOMMITTED";
            case TRANSACTION_READ_COMMITTED:
                return "READ COMMITTED";
            case TRANSACTION_REPEATABLE_READ:
                return "REPEATABLE READ";
            case TRANSACTION_SERIALIZABLE:
                return "SERIALIZABLE";
        }
        throw new SQLException("Invalid transaction isolation level: " + level);
    }

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Pass ResultSet.HOLD_CURSORS_OVER_COMMIT as the holdability argument
  2. Do not call setHoldability with CLOSE_CURSORS_AT_COMMIT; omit it and use driver defaults
  3. Restructure commit/rollback logic so it does not depend on cursors closing at commit

Example fix

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

Strategy: validation

Validate before calling

if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
    holdability = ResultSet.HOLD_CURSORS_OVER_COMMIT; // only supported value
}

Try / catch

try {
    stmt = conn.createStatement(type, concurrency, holdability);
} catch (SQLFeatureNotSupportedException e) {
    stmt = conn.createStatement();
}

Prevention

When it happens

Trigger: createStatement(type, concurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT); prepareStatement(sql, type, concurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT); setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT) on the connection.

Common situations: Transaction-management code ported from DB2/Oracle that pins cursor behavior to commit; frameworks that set holdability explicitly for rollback safety.

Related errors


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