{"record":{"id":"12093592de906e40","repo":"tursodatabase/turso","slug":"sqlite-only-supports-closing-cursors-at-commit","errorCode":null,"errorMessage":"SQLite only supports closing cursors at commit","messagePattern":"SQLite only supports closing cursors at commit","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/core/TursoConnection.java","lineNumber":166,"sourceCode":"   *   <li>type: {@link ResultSet#TYPE_FORWARD_ONLY}\n   *   <li>concurrency: {@link ResultSet#CONCUR_READ_ONLY})\n   *   <li>holdability: {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}\n   * </ul>\n   *\n   * @param resultSetType the type setting.\n   * @param resultSetConcurrency the concurrency setting.\n   * @param resultSetHoldability the holdability setting.\n   */\n  public void checkCursor(int resultSetType, int resultSetConcurrency, int resultSetHoldability)\n      throws SQLException {\n    if (resultSetType != ResultSet.TYPE_FORWARD_ONLY) {\n      throw new SQLException(\"SQLite only supports TYPE_FORWARD_ONLY cursors\");\n    }\n    if (resultSetConcurrency != ResultSet.CONCUR_READ_ONLY) {\n      throw new SQLException(\"SQLite only supports CONCUR_READ_ONLY cursors\");\n    }\n    if (resultSetHoldability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {\n      throw new SQLException(\"SQLite only supports closing cursors at commit\");\n    }\n  }\n\n  /**\n   * Sets the auto-commit mode for this connection.\n   *\n   * <p>When auto-commit is enabled (the default), each SQL statement is committed automatically\n   * upon completion. When auto-commit is disabled, statements are grouped into transactions that\n   * must be explicitly committed or rolled back.\n   *\n   * <p>If this method is called to enable auto-commit while a transaction is active, the current\n   * transaction is committed first.\n   *\n   * @param autoCommit true to enable auto-commit mode; false to disable it\n   * @throws SQLException if a database access error occurs or the connection is closed\n   */\n  public void setAutoCommit(boolean autoCommit) throws SQLException {\n    synchronized (transactionLock) {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/java/src/main/java/tech/turso/core/TursoConnection.java#L148-L184","documentation":"The third guard in TursoConnection.checkCursor(): cursors only exist while a statement steps rows, so ResultSet.HOLD_CURSORS_OVER_COMMIT is meaningless. Only ResultSet.CLOSE_CURSORS_AT_COMMIT is accepted when a holdability argument is supplied.","triggerScenarios":"Calling createStatement(type, concurrency, holdability) or prepareStatement(sql, type, concurrency, holdability) with holdability = ResultSet.HOLD_CURSORS_OVER_COMMIT (1) instead of CLOSE_CURSORS_AT_COMMIT (2).","commonSituations":"Frameworks or boilerplate copied from J2EE code that explicitly holds cursors across commits; porting from DB2/Oracle where holdability matters; code that passes all three constants defensively without knowing the driver's defaults.","solutions":["Pass ResultSet.CLOSE_CURSORS_AT_COMMIT as the holdability argument","Drop the 3-arg overload and use createStatement(type, concurrency) or the no-arg createStatement() — driver defaults never trip this check","Consume result sets fully before committing rather than holding them open"],"exampleFix":"// before\nStatement st = conn.createStatement(\n    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,\n    ResultSet.HOLD_CURSORS_OVER_COMMIT);\n\n// after\nStatement st = conn.createStatement(\n    ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,\n    ResultSet.CLOSE_CURSORS_AT_COMMIT);","handlingStrategy":"validation","validationCode":"int holdability = ResultSet.CLOSE_CURSORS_AT_COMMIT;\nif (holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {\n  holdability = ResultSet.CLOSE_CURSORS_AT_COMMIT;\n}\nStatement st = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, holdability);","typeGuard":"static boolean isSupportedHoldability(int resultSetHoldability) {\n  return resultSetHoldability == ResultSet.CLOSE_CURSORS_AT_COMMIT;\n}","tryCatchPattern":"try {\n  st = conn.createStatement(type, concurrency, reqHoldability);\n} catch (SQLException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"closing cursors at commit\")) {\n    st = conn.createStatement(type, concurrency);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Use the shorter createStatement/prepareStatement overloads unless holdability is genuinely required","Drain result sets before committing instead of holding cursors across commits","Strip HOLD_CURSORS_OVER_COMMIT from code ported from J2EE-era drivers"],"tags":["java","jdbc","resultset","holdability","bindings","sqlite"],"backgroundTag":"jdbc-cursor-holdability-unsupported","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}