prestodb/presto · error · SQLFeatureNotSupportedException

deleteRow

Error message

deleteRow

What it means

deleteRow() is part of the updatable result set API; Presto result sets are read-only and cannot delete rows through a cursor. The driver therefore throws SQLFeatureNotSupportedException("deleteRow") unconditionally.

Source

Thrown at presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoResultSet.java:1054

    @Override
    public void insertRow()
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("insertRow");
    }

    @Override
    public void updateRow()
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("updateRow");
    }

    @Override
    public void deleteRow()
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("deleteRow");
    }

    @Override
    public void refreshRow()
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("refreshRow");
    }

    @Override
    public void cancelRowUpdates()
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("cancelRowUpdates");
    }

    @Override
    public void moveToInsertRow()

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Issue a PreparedStatement DELETE FROM ... WHERE ... statement instead of cursor deletion
  2. Split read and write phases: collect keys while reading, then delete with a batched SQL DELETE
  3. Check resultSet.getConcurrency() before attempting cursor deletes and branch to SQL when read-only

Example fix

// before
while (rs.next()) {
    if (isStale(rs)) { rs.deleteRow(); }
}
// after
List<Long> stale = new ArrayList<>();
while (rs.next()) { if (isStale(rs)) stale.add(rs.getLong("id")); }
try (PreparedStatement ps = conn.prepareStatement(
        "DELETE FROM orders WHERE id = ?")) {
    for (Long id : stale) { ps.setLong(1, id); ps.addBatch(); }
    ps.executeBatch();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (rs.getConcurrency() == ResultSet.CONCUR_READ_ONLY) {
    // use SQL DELETE instead of rs.deleteRow()
}

Type guard

static boolean supportsDeleteRow(ResultSet rs) {
    try {
        return rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE;
    } catch (SQLException e) {
        return false;
    }
}

Try / catch

try {
    rs.deleteRow();
} catch (SQLFeatureNotSupportedException e) {
    // issue DELETE ... WHERE via PreparedStatement
}

Prevention

When it happens

Trigger: Calling deleteRow() on a ResultSet created from a PrestoStatement while iterating results, intending to remove the current row from the table.

Common situations: Legacy CRUD code ported from OLTP drivers to Presto; data-cleanup scripts that mix reading and deleting in one pass; generic JDBC tooling assuming CONCUR_UPDATABLE.

Related errors


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