{"record":{"id":"d62fe7b8ce4e058f","repo":"tursodatabase/turso","slug":"turso-does-not-support-stored-procedures","errorCode":null,"errorMessage":"turso does not support stored procedures","messagePattern":"turso does not support stored procedures","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/jdbc4/JDBC4Connection.java","lineNumber":225,"sourceCode":"  public CallableStatement prepareCall(String sql) throws SQLException {\n    return prepareCall(\n        sql,\n        ResultSet.TYPE_FORWARD_ONLY,\n        ResultSet.CONCUR_READ_ONLY,\n        ResultSet.CLOSE_CURSORS_AT_COMMIT);\n  }\n\n  @Override\n  public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)\n      throws SQLException {\n    return prepareCall(sql, resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);\n  }\n\n  @Override\n  public CallableStatement prepareCall(\n      String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)\n      throws SQLException {\n    throw new SQLException(\"turso does not support stored procedures\");\n  }\n\n  @Override\n  public PreparedStatement prepareStatement(String sql) throws SQLException {\n    return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);\n  }\n\n  @Override\n  public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)\n      throws SQLException {\n    return prepareStatement(\n        sql, resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);\n  }\n\n  @Override\n  public PreparedStatement prepareStatement(\n      String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)\n      throws SQLException {","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/java/src/main/java/tech/turso/jdbc4/JDBC4Connection.java#L207-L243","documentation":"prepareCall (all overloads) throws because CallableStatement, the JDBC interface for stored procedures, is unsupported: a SQLite-compatible engine has no stored procedures to call. The exception is thrown for any SQL string passed, including plain SELECTs, since CallableStatement itself is not implemented by this driver.","triggerScenarios":"conn.prepareCall(\"{call some_proc(?)}\"); JPA/Hibernate StoredProcedureQuery usage; MyBatis mappers with CallableStatement statementType; iBATIS/legacy DAO code; any {call ...} escape-syntax SQL.","commonSituations":"Porting an application from Oracle/SQL Server/PostgreSQL that uses stored procedures; JPA repositories declaring @NamedStoredProcedureQuery; reporting tools that fetch data via procedures; generated data-access code that defaults to prepareCall for all reads.","solutions":["Replace CallableStatement with PreparedStatement executing the equivalent inline SQL (SELECT for queries, INSERT/UPDATE/DELETE for mutations).","Move stored-procedure logic into application code (a service method wrapping plain statements in one transaction).","For small pieces of server-side logic, consider SQL triggers or views where they genuinely substitute.","Remove @NamedStoredProcedureQuery / statementType=\"CALLABLE\" mappings when migrating to Turso."],"exampleFix":"// before\ntry (CallableStatement cs = conn.prepareCall(\"{call get_customer(?)}\")) {\n    cs.setLong(1, customerId);\n    ResultSet rs = cs.executeQuery();\n}\n\n// after\ntry (PreparedStatement ps = conn.prepareStatement(\n        \"SELECT id, name FROM customers WHERE id = ?\")) {\n    ps.setLong(1, customerId);\n    ResultSet rs = ps.executeQuery();\n}","handlingStrategy":"validation","validationCode":"private static boolean isStoredProcedureCall(String sql) {\n    String s = sql.trim().toLowerCase(Locale.ROOT);\n    return s.startsWith(\"{call\") || s.startsWith(\"{\") || s.contains(\"call \");\n}\nif (isStoredProcedureCall(sql)) {\n    throw new UnsupportedOperationException(\n        \"stored procedures are not supported by Turso; inline the SQL instead\");\n}","typeGuard":"private static boolean requiresCallableStatement(String sql) {\n    return isStoredProcedureCall(sql);\n}","tryCatchPattern":"try {\n    stmt = conn.prepareCall(sql);\n} catch (SQLException e) {\n    if (sql.trim().startsWith(\"{\")) {\n        throw new UnsupportedOperationException(\n            \"rewrite stored procedure call as inline SQL: \" + sql, e);\n    }\n    throw e;\n}","preventionTips":["Ban CallableStatement in codebases targeting SQLite-compatible engines (enforce with ArchUnit or lint rules).","Inline stored-procedure bodies as SQL statements coordinated in a service method.","Remove @NamedStoredProcedureQuery and statementType=\"CALLABLE\" mappings during migration.","Add a migration checklist item: search for '{call' and prepareCall."],"tags":["java","jdbc","stored-procedures","callablestatement","unsupported-feature"],"backgroundTag":"stored-procedure-not-supported","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}