{"record":{"id":"ae820c4f07f51c35","repo":"apache/shardingsphere","slug":"last-ae820c","errorCode":null,"errorMessage":"last","messagePattern":"last","errorType":"exception","errorClass":"SQLFeatureNotSupportedException","httpStatus":null,"severity":"error","filePath":"kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/AbstractUnsupportedOperationSQLFederationResultSet.java","lineNumber":75,"sourceCode":"    \n    @Override\n    public void beforeFirst() throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"beforeFirst\");\n    }\n    \n    @Override\n    public void afterLast() throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"afterLast\");\n    }\n    \n    @Override\n    public boolean first() throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"first\");\n    }\n    \n    @Override\n    public boolean last() throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"last\");\n    }\n    \n    @Override\n    public boolean absolute(final int row) throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"absolute\");\n    }\n    \n    @Override\n    public boolean relative(final int rows) throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"relative\");\n    }\n    \n    @Override\n    public int getRow() throws SQLException {\n        throw new SQLFeatureNotSupportedException(\"getRow\");\n    }\n    \n    @Override","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/kernel/sql-federation/core/src/main/java/org/apache/shardingsphere/sqlfederation/resultset/AbstractUnsupportedOperationSQLFederationResultSet.java#L57-L93","documentation":"last() is not supported by the SQL Federation ResultSet. The federation engine returns a forward-only result stream, so the abstract base class overrides last() to throw SQLFeatureNotSupportedException instead of jumping the cursor to the final row. The throw is unconditional; it is not a data- or state-dependent failure.","triggerScenarios":"Calling rs.last() (often followed by getRow()) on a federated query ResultSet to count rows or inspect the final row, e.g. pagination code that uses last()/getRow() to compute total result size.","commonSituations":"Row-counting idioms ported from MySQL Connector/J (where last()+getRow() is a common COUNT emulation); pagination helpers in older Struts/Spring JDBC code; switching a datasource URL from a native driver to ShardingSphere Proxy with sql-federation enabled for the affected statements.","solutions":["Issue a separate SELECT COUNT(*) federated query for totals instead of last()/getRow()","For the final row only, use ORDER BY <pk> DESC LIMIT 1 and read the single forward result","Cache results in a scroll-insensitive client RowSet if last() navigation is unavoidable"],"exampleFix":"// before\nrs.last();\nint total = rs.getRow();\nrs.beforeFirst();\n\n// after\nint total = executeCount(stmt.getConnection(), countSql);","handlingStrategy":"try-catch","validationCode":"if (rs.getType() == ResultSet.TYPE_FORWARD_ONLY) {\n    // last() unsupported: use a COUNT(*) query for totals\n}","typeGuard":"private boolean supportsLast(final ResultSet rs) throws SQLException {\n    return rs.getType() != ResultSet.TYPE_FORWARD_ONLY;\n}","tryCatchPattern":"try {\n    rs.last();\n    total = rs.getRow();\n} catch (final SQLFeatureNotSupportedException e) {\n    total = countViaSql(conn, countSql); // fallback COUNT query\n}","preventionTips":["Ban the last()/getRow() count idiom in shared code","Always use SELECT COUNT(*) for pagination totals","Create statements with TYPE_SCROLL_INSENSITIVE only for non-federation connections"],"tags":["jdbc","resultset","sql-federation","cursor","row-count","shardingsphere"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}