apache/shardingsphere · error · SQLFeatureNotSupportedException
beforeFirst
Error message
beforeFirst
What it means
beforeFirst() would reposition the cursor before the first row for a second pass, which ShardingSphere's forward-only merge result cannot do — the underlying streams from multiple shards are consumed once. The driver implements it as a final method throwing SQLFeatureNotSupportedException.
Source
Thrown at jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/unsupported/AbstractUnsupportedOperationResultSet.java:60
@Override
public final boolean isAfterLast() throws SQLException {
throw new SQLFeatureNotSupportedException("isAfterLast");
}
@Override
public final boolean isFirst() throws SQLException {
throw new SQLFeatureNotSupportedException("isFirst");
}
@Override
public final boolean isLast() throws SQLException {
throw new SQLFeatureNotSupportedException("isLast");
}
@Override
public final void beforeFirst() throws SQLException {
throw new SQLFeatureNotSupportedException("beforeFirst");
}
@Override
public final void afterLast() throws SQLException {
throw new SQLFeatureNotSupportedException("afterLast");
}
@Override
public final boolean first() throws SQLException {
throw new SQLFeatureNotSupportedException("first");
}
@Override
public final boolean last() throws SQLException {
throw new SQLFeatureNotSupportedException("last");
}
@OverrideView on GitHub (pinned to e952770a21)
Solutions
- Collect rows into a List during the first pass and iterate the List again.
- If two passes over server data are truly needed, execute the query twice (each execution re-routes to shards).
- Use a CachedRowSet (RowSetProvider.newFactory().createCachedRowSet()) populated from the result for repeatable navigation.
- Restructure to a single pass that carries computed state forward.
Example fix
// before
while (rs.next()) { total += rs.getDouble(2); }
rs.beforeFirst(); // throws
while (rs.next()) { write(rs, total); }
// after
List<Object[]> rows = new ArrayList<>();
while (rs.next()) { total += rs.getDouble(2); rows.add(new Object[]{rs.getObject(1), rs.getDouble(2)}); }
for (Object[] r : rows) { write(r, total); } Defensive patterns
Strategy: fallback
Validate before calling
if (rs.getType() == ResultSet.TYPE_FORWARD_ONLY) {
List<Object[]> rows = new ArrayList<>();
while (rs.next()) { rows.add(mapRow(rs)); }
// iterate 'rows' as many passes as needed — never call beforeFirst()
} Try / catch
try {
rs.beforeFirst();
} catch (SQLFeatureNotSupportedException e) {
// second pass impossible: re-execute the query or use the buffered copy
} Prevention
- Assume single-pass semantics for every sharded query result; buffer on first read.
- Use CachedRowSet when handing results to code you don't control.
- Design two-phase algorithms around a pre-fetched list, not cursor rewinds.
When it happens
Trigger: Calling rs.beforeFirst() after an iteration to loop over the same ResultSet again — the classic 'second pass' pattern — on any ShardingSphere-driver query result.
Common situations: Two-phase algorithms (compute totals, then details); serializers that validate first and write second; JDBC tutorial examples; code that worked on MySQL Connector/J scrollable result sets before introducing sharding.
Related errors
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/2106c1b7703c7856.
Report an issue: GitHub.