tursodatabase/turso · error · SQLException

addBatch(String) cannot be called on a PreparedStatement

Error message

addBatch(String) cannot be called on a PreparedStatement

What it means

JDBC4PreparedStatement overrides addBatch(String sql) to always throw, matching the JDBC contract: a PreparedStatement is compiled once from a fixed SQL string, so adding raw SQL batches to it is invalid. The supported path is the no-arg addBatch(), which queues the currently bound parameter row (currentBatchParams) for executeBatch().

Source

Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4PreparedStatement.java:396

  }

  /** Takes the given set of parameters and binds it to the underlying statement. */
  private void bindParams(Object[] params) throws SQLException {
    requireNonNull(statement);
    for (int paramIndex = 1; paramIndex <= params.length; paramIndex++) {
      statement.bindObject(paramIndex, params[paramIndex - 1]);
    }
  }

  @Override
  public void addBatch() {
    batchQueryParams.add(currentBatchParams);
    currentBatchParams = new Object[paramCount];
  }

  @Override
  public void addBatch(String sql) throws SQLException {
    throw new SQLException("addBatch(String) cannot be called on a PreparedStatement");
  }

  @Override
  public void setCharacterStream(int parameterIndex, @Nullable Reader reader, int length)
      throws SQLException {
    requireNonNull(this.statement);
    if (reader == null) {
      setParam(parameterIndex, null);
      return;
    }
    if (length < 0) {
      throw new SQLException("setCharacterStream length must be non-negative");
    }
    if (length == 0) {
      setParam(parameterIndex, "");
      return;
    }
    try {

View on GitHub (pinned to bad083fafb)

Solutions

  1. On a PreparedStatement, set the parameters and call the no-arg addBatch(); the SQL is fixed at prepare time.
  2. Use conn.createStatement().addBatch(sql) when you genuinely need to batch heterogeneous SQL strings.
  3. In shared helpers, branch on instanceof PreparedStatement so each type uses its own addBatch form.

Example fix

// before
PreparedStatement ps = conn.prepareStatement("INSERT INTO t VALUES (?)");
ps.setInt(1, 42);
ps.addBatch("INSERT INTO t VALUES (42)"); // throws

// after
ps.setInt(1, 42);
ps.addBatch(); // queues the current parameter row
Defensive patterns

Strategy: validation

Validate before calling

if (stmt instanceof PreparedStatement) {
    ((PreparedStatement) stmt).addBatch(); // parameter batch
} else {
    stmt.addBatch(sql); // SQL batch
}

Prevention

When it happens

Trigger: PreparedStatement ps = conn.prepareStatement(sql); ... ps.addBatch(sql); — typically a batch helper originally written against Statement and later handed a PreparedStatement, or ORM code that mixes the two APIs.

Common situations: Utility methods like runBatch(Statement, List<String>) that receive a PreparedStatement; code migrated from createStatement() usage; generic batch runners keyed by SQL string.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/558933a3695a935b. Report an issue: GitHub.