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
- On a PreparedStatement, set the parameters and call the no-arg addBatch(); the SQL is fixed at prepare time.
- Use conn.createStatement().addBatch(sql) when you genuinely need to batch heterogeneous SQL strings.
- 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
- Type batch helpers as PreparedStatement or Statement explicitly — never the base type when both may flow through.
- Remember: Statement batches SQL strings; PreparedStatement batches parameter rows.
- Code-review for addBatch(<string>) on any prepared object.
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
- Expected first argument to be an array of statements
- Expected first argument to be an array of statements
- Exception while binding NULL value at position " + position
- Expected first argument to be an array of statements
- sync is disabled as database was opened without sync support
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/558933a3695a935b.
Report an issue: GitHub.