tursodatabase/turso · error · SQLException

turso does not support stored procedures

Error message

turso does not support stored procedures

What it means

Connection.prepareCall(...) - the entry point for CallableStatement used to invoke stored procedures - throws SQLException (not SQLFeatureNotSupportedException) at JDBC4Connection.java:225, because the SQLite engine turso wraps has no stored procedures. All four prepareCall overloads funnel into this one rejection. Note the exception type: catching only SQLFeatureNotSupportedException will miss it.

Solutions

  1. Replace the stored-procedure call with plain SQL: move the logic into the application, or into SQL statements executed via prepareStatement().
  2. For MyBatis, change statementType="CALLABLE" to statementType="PREPARED" with an equivalent SELECT/INSERT.
  3. Catch SQLException (the driver throws SQLException here, not SQLFeatureNotSupportedException) if a shared layer must tolerate it.
  4. Grep the migrated codebase for prepareCall and '{call' / '{?=' patterns and rewrite every hit.

Example fix

// before
try (CallableStatement cs = conn.prepareCall("{ call get_user(?, ?) }")) {
    cs.setLong(1, id); cs.execute();
}

// after
try (PreparedStatement ps = conn.prepareStatement("select name from users where id = ?")) {
    ps.setLong(1, id);
    try (ResultSet rs = ps.executeQuery()) { ... }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// detect up front and avoid CallableStatement entirely
// turso/SQLite has no stored procedures: route procedure-style calls to
// plain SQL executed via prepareStatement

Try / catch

try {
    stmt = conn.prepareCall(sql);
} catch (SQLException e) { // note: SQLException, NOT SQLFeatureNotSupportedException
    if (e.getMessage().contains("stored procedures")) {
        // rewrite as plain SQL via prepareStatement
    } else { throw e; }
}

Prevention

When it happens

Trigger: conn.prepareCall("{ call compute_totals(?) }"); MyBatis mappers with statementType="CALLABLE"; JPA/Hibernate native queries wrapped in {call ...} syntax; legacy DAO code written against Oracle/SQL Server/MySQL migrated to turso; ORM tooling that probes callable support.

Common situations: Porting an enterprise codebase that relied on stored procedures; Spring StoredProcedure or SimpleJdbcCall helpers; reporting tools that default to CallableStatement for anything resembling a procedure call; schema migrations that left procedure-call statements behind after the procedures themselves were dropped.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/d62fe7b8ce4e058f. Report an issue: GitHub.

Appendix: source

Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4Connection.java:225

  public CallableStatement prepareCall(String sql) throws SQLException {
    return prepareCall(
        sql,
        ResultSet.TYPE_FORWARD_ONLY,
        ResultSet.CONCUR_READ_ONLY,
        ResultSet.CLOSE_CURSORS_AT_COMMIT);
  }

  @Override
  public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
      throws SQLException {
    return prepareCall(sql, resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);
  }

  @Override
  public CallableStatement prepareCall(
      String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
      throws SQLException {
    throw new SQLException("turso does not support stored procedures");
  }

  @Override
  public PreparedStatement prepareStatement(String sql) throws SQLException {
    return prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
  }

  @Override
  public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
      throws SQLException {
    return prepareStatement(
        sql, resultSetType, resultSetConcurrency, ResultSet.CLOSE_CURSORS_AT_COMMIT);
  }

  @Override
  public PreparedStatement prepareStatement(
      String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
      throws SQLException {

View on GitHub (pinned to 244cde92a7)