apache/shardingsphere · error · SQLFeatureNotSupportedException
previous
Error message
previous
What it means
ShardingSphere driver result sets are forward-only (TYPE_FORWARD_ONLY) merge results, so ResultSet.previous() — moving the cursor backward — is final and throws SQLFeatureNotSupportedException. Streaming merged results from multiple shards cannot be rewound without re-execution, hence the hard restriction.
Source
Thrown at jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/unsupported/AbstractUnsupportedOperationResultSet.java:35
package org.apache.shardingsphere.driver.jdbc.unsupported;
import java.io.Reader;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Map;
/**
* Unsupported {@code ResultSet} methods.
*/
public abstract class AbstractUnsupportedOperationResultSet extends AbstractUnsupportedUpdateOperationResultSet {
@Override
public final boolean previous() throws SQLException {
throw new SQLFeatureNotSupportedException("previous");
}
@Override
public final boolean isBeforeFirst() throws SQLException {
throw new SQLFeatureNotSupportedException("isBeforeFirst");
}
@Override
public final boolean isAfterLast() throws SQLException {
throw new SQLFeatureNotSupportedException("isAfterLast");
}
@Override
public final boolean isFirst() throws SQLException {
throw new SQLFeatureNotSupportedException("isFirst");
}
@OverrideView on GitHub (pinned to e952770a21)
Solutions
- Restructure to a single forward pass: read everything you need from each row as rs.next() advances.
- Buffer rows into a List<Row> (or Apache Commons DbUtils RowSetDynaClass) and navigate the in-memory copy.
- If a scrollable cursor is genuinely required, execute the query on a direct (non-sharded) DataSource or via the ShardingSphere Proxy with a client/driver that materializes rows.
- Re-execute the query with different OFFSET/FETCH (LIMIT) instead of scrolling backward for pagination.
Example fix
// before
while (rs.next()) { process(rs); }
if (rs.previous()) { reprocess(rs); } // throws
// after
List<OrderRow> rows = new ArrayList<>();
while (rs.next()) { rows.add(mapRow(rs)); }
// navigate 'rows' backward freely Defensive patterns
Strategy: validation
Validate before calling
int type = rs.getType();
if (type == ResultSet.TYPE_FORWARD_ONLY) {
// do not call previous(): stream the rows forward or buffer them
} Try / catch
try {
rs.previous();
} catch (SQLFeatureNotSupportedException e) {
// re-fetch or use buffered rows instead of scrolling back
} Prevention
- Check rs.getType() == ResultSet.TYPE_FORWARD_ONLY before any cursor API beyond next().
- Default to buffering rows when two-directional navigation is needed.
- Test cursor-heavy code against the actual sharded datasource, not a passthrough driver.
When it happens
Trigger: Calling rs.previous() on a ResultSet returned by a query executed through ShardingSphereDataSource, e.g. implementing a two-pass iteration or a bidirectional paginator directly on the ResultSet.
Common situations: Report/paging code that iterates once forward then steps back; generic ResultSet wrappers (exporters, CSV writers) that call previous() to peek; frameworks assuming scroll-insensitive result sets because the underlying MySQL/PostgreSQL driver supports them.
Related errors
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/2a85c7018b7d8536.
Report an issue: GitHub.