Skip to content

Commit 66d5e47

Browse files
committed
Start transaction before prepare call (1.4)
This is a backport of the PR #467 to `v1.4-andium` stable branch. Previously, when auto-commit if off, the implicit transaction was started automatically when `PreparedStatement#execute(void)` is called. This was found to cause the problems with `Statement#executeQuery(String)`, that calls `prepare(String)` and `execute()` internally. As transaction was starting only after `prepare` call - the `prepare` itself was running with effective auto-commit mode (in a separate implicit transaction). It is harmless in most cases, but was found to break transaction-wide caching in `duckdb-iceberg`. The problem was only happening when the `execute(String)` is running outside of active transaction, for example as a first call in freshly opened connection. Still this was causing non-negligible overhead. This change makes the implicit transaction to be started before `prepare` call, so the same transaction is active during `execute` call. There are no changes in logic when auto-commit is on (default per JDBC spec), so the Iceberg caching is still broken in this mode. This is intended to be fixed in future, for now to have effective caching with `duckdb-iceberg` it is necessary to disable auto-commit, it can be done by one of the following methods: In connection config: ```java Properties config = new Properties(); config.put(DuckDBDriver.JDBC_AUTO_COMMIT, false); Connection conn = DriverManager.getConnection(url, config); ``` In connection URL: ```java Connection conn = DriverManager.getConnection("jdbc:duckdb:path/to/my.db;jdbc_auto_commit=false;"); ``` On a connection manually: ```java conn.setAutoCommit(false); ``` Testing: there are no easily observable effects from this change, the transactional logic is covered by existing tests, Iceberg caching is tested manually.
1 parent ea919f8 commit 66d5e47

File tree

1 file changed

+9
-8
lines changed

1 file changed

+9
-8
lines changed

src/main/java/org/duckdb/DuckDBPreparedStatement.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private boolean startTransaction() throws SQLException {
101101
this.conn.transactionRunning = true;
102102
// Start transaction via Statement
103103
try (Statement s = conn.createStatement()) {
104-
s.execute("BEGIN TRANSACTION;");
104+
s.execute("BEGIN TRANSACTION");
105105
return true;
106106
}
107107
} catch (NullPointerException e) {
@@ -138,6 +138,11 @@ private void prepare(String sql) throws SQLException {
138138
conn.connRefLock.lock();
139139
try {
140140
conn.checkOpen();
141+
142+
if (!isConnAutoCommit()) {
143+
startTransaction();
144+
}
145+
141146
stmtRef = DuckDBNative.duckdb_jdbc_prepare(conn.connRef, sql.getBytes(UTF_8));
142147
// Track prepared statement inside the parent connection
143148
conn.preparedStatements.add(this);
@@ -156,10 +161,6 @@ private void prepare(String sql) throws SQLException {
156161

157162
@Override
158163
public boolean execute() throws SQLException {
159-
return execute(true);
160-
}
161-
162-
private boolean execute(boolean startTransaction) throws SQLException {
163164
checkOpen();
164165
checkPrepared();
165166

@@ -180,7 +181,7 @@ private boolean execute(boolean startTransaction) throws SQLException {
180181
}
181182
selectResult = null;
182183

183-
if (startTransaction && !isConnAutoCommit()) {
184+
if (!isConnAutoCommit()) {
184185
startTransaction();
185186
}
186187

@@ -657,7 +658,7 @@ private long[] executeBatchedPreparedStatement() throws SQLException {
657658
long[] updateCounts = new long[this.batchedParams.size()];
658659
for (int i = 0; i < this.batchedParams.size(); i++) {
659660
params = this.batchedParams.get(i);
660-
execute(false);
661+
execute();
661662
updateCounts[i] = getUpdateCountInternal();
662663
}
663664
clearBatch();
@@ -690,7 +691,7 @@ private long[] executeBatchedStatements() throws SQLException {
690691
long[] updateCounts = new long[this.batchedStatements.size()];
691692
for (int i = 0; i < this.batchedStatements.size(); i++) {
692693
prepare(this.batchedStatements.get(i));
693-
execute(false);
694+
execute();
694695
updateCounts[i] = getUpdateCountInternal();
695696
}
696697
clearBatch();

0 commit comments

Comments
 (0)