{"record":{"id":"b7d920b28e9f6dd7","repo":"pentaho/pentaho-kettle","slug":"unable-to-move-resultset-to-after-the-last-position","errorCode":null,"errorMessage":"Unable to move resultset to after the last position","messagePattern":"Unable to move resultset to after the last position","errorType":"exception","errorClass":"KettleDatabaseException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/database/Database.java","lineNumber":2961,"sourceCode":"      return rs.absolute( position );\n    } catch ( SQLException e ) {\n      throw new KettleDatabaseException( \"Unable to move resultset to position \" + position, e );\n    }\n  }\n\n  public boolean relative( ResultSet rs, int rows ) throws KettleDatabaseException {\n    try {\n      return rs.relative( rows );\n    } catch ( SQLException e ) {\n      throw new KettleDatabaseException( \"Unable to move the resultset forward \" + rows + \" rows\", e );\n    }\n  }\n\n  public void afterLast( ResultSet rs ) throws KettleDatabaseException {\n    try {\n      rs.afterLast();\n    } catch ( SQLException e ) {\n      throw new KettleDatabaseException( \"Unable to move resultset to after the last position\", e );\n    }\n  }\n\n  public void first( ResultSet rs ) throws KettleDatabaseException {\n    try {\n      rs.first();\n    } catch ( SQLException e ) {\n      throw new KettleDatabaseException( \"Unable to move resultset to the first position\", e );\n    }\n  }\n\n  /**\n   * Get a row from the resultset. Do not use lazy conversion\n   *\n   * @param rs The resultset to get the row from\n   * @return one row or null if no row was found on the resultset or if an error occurred.\n   */\n  public Object[] getRow( ResultSet rs ) throws KettleDatabaseException {","sourceCodeStart":2943,"sourceCodeEnd":2979,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/database/Database.java#L2943-L2979","documentation":"KettleDatabaseException wrapper for ResultSet.afterLast() failures. afterLast() repositions the cursor after the final row, which requires a scrollable result set; the underlying SQLException is attached as cause.","triggerScenarios":"Calling Database.afterLast(rs) on a TYPE_FORWARD_ONLY result set or one whose driver doesn't support afterLast(); result set already closed.","commonSituations":"Trying to count rows by jumping to the end on a default statement; iterating backwards after afterLast() on drivers with limited scroll support.","solutions":["Create the Statement with a scrollable result set type","Use a COUNT(*) query instead of seeking afterLast to size the result set","Check rs.getType() before cursor navigation calls","Keep the result set/connection open for the duration of navigation"],"exampleFix":"// before\nStatement st = conn.createStatement();\ndb.afterLast(rs);\n// after\nStatement st = conn.createStatement(\n  ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\nrs = st.executeQuery(sql);","handlingStrategy":"validation","validationCode":"if (rs.getType() == ResultSet.TYPE_FORWARD_ONLY) {\n  throw new IllegalStateException(\"afterLast() needs a scrollable result set\");\n}","typeGuard":"null","tryCatchPattern":"try {\n  db.afterLast(rs);\n} catch (KettleDatabaseException e) {\n  // fallback: use COUNT(*) query to size result instead\n}","preventionTips":["Use COUNT(*) rather than afterLast() for row counts","Create scrollable statements when backward navigation is needed","Keep result sets open during navigation","Verify driver support for afterLast"],"tags":["jdbc","resultset","cursor"],"backgroundTag":"unsupported-operation","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}