Skip to content

Commit 0e8a578

Browse files
committed
Allow resuming transactions after caught errors.
1 parent 08495cf commit 0e8a578

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

lib/src/sqlite_connection_impl.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,8 @@ Future<void> _sqliteConnectionIsolateInner(_SqliteConnectionParams params,
269269
server.open((data) async {
270270
if (data is _SqliteIsolateClose) {
271271
if (txId != null) {
272-
try {
272+
if (!db.autocommit) {
273273
db.execute('ROLLBACK');
274-
} catch (e) {
275-
// Ignore
276274
}
277275
txId = null;
278276
txError = null;
@@ -290,7 +288,8 @@ Future<void> _sqliteConnectionIsolateInner(_SqliteConnectionParams params,
290288
txId = data.ctxId;
291289
} else if (txId != null && txId != data.ctxId) {
292290
// Locks should prevent this from happening
293-
throw AssertionError('Mixed transactions: $txId and ${data.ctxId}');
291+
throw sqlite.SqliteException(
292+
0, 'Mixed transactions: $txId and ${data.ctxId}');
294293
} else if (data.sql == 'ROLLBACK') {
295294
// This is the only valid way to clear an error
296295
txError = null;
@@ -307,7 +306,13 @@ Future<void> _sqliteConnectionIsolateInner(_SqliteConnectionParams params,
307306
return result;
308307
} catch (err) {
309308
if (txId != null) {
310-
txError = err;
309+
if (db.autocommit) {
310+
// Transaction rolled back
311+
txError = sqlite.SqliteException(0,
312+
'Transaction rolled back by earlier statement: ${err.toString()}');
313+
} else {
314+
// Recoverable error
315+
}
311316
}
312317
rethrow;
313318
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ environment:
66
sdk: '>=2.19.1 <4.0.0'
77

88
dependencies:
9-
sqlite3: '>=1.10.1 <3.0.0'
9+
sqlite3: '^2.3.0'
1010
async: ^2.10.0
1111
collection: ^1.17.0
1212

test/basic_test.dart

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,30 @@ void main() {
222222
await createTables(db);
223223

224224
var tp = db.writeTransaction((tx) async {
225-
tx.execute('INSERT INTO test_data(description) VALUES(?)', ['test1']);
226-
tx.execute('INSERT INTO test_data(description) VALUES(?)', ['test2']);
227-
ignore(tx.execute('INSERT INTO test_data(description) VALUES(json(?))',
228-
['test3'])); // Errors
229-
// Will not be executed because of the above error
225+
tx.execute(
226+
'INSERT OR ROLLBACK INTO test_data(id, description) VALUES(?, ?)',
227+
[1, 'test1']);
228+
tx.execute(
229+
'INSERT OR ROLLBACK INTO test_data(id, description) VALUES(?, ?)',
230+
[2, 'test2']);
230231
ignore(tx.execute(
231-
'INSERT INTO test_data(description) VALUES(?) RETURNING *',
232-
['test4']));
232+
'INSERT OR ROLLBACK INTO test_data(id, description) VALUES(?, ?)',
233+
[2, 'test3'])); // Errors
234+
235+
// Will not be executed because of the above rollback
236+
ignore(tx.execute(
237+
'INSERT OR ROLLBACK INTO test_data(id, description) VALUES(?, ?)',
238+
[4, 'test4']));
233239
});
234240

235241
// The error propagates up to the transaction
236-
await expectLater(tp, throwsA((e) => e is sqlite.SqliteException));
242+
await expectLater(
243+
tp,
244+
throwsA((e) =>
245+
e is sqlite.SqliteException &&
246+
e.message
247+
.contains('Transaction rolled back by earlier statement') &&
248+
e.message.contains('UNIQUE constraint failed')));
237249

238250
expect(await db.get('SELECT count() count FROM test_data'),
239251
equals({'count': 0}));
@@ -321,6 +333,27 @@ void main() {
321333
});
322334
expect(computed, equals(5));
323335
});
336+
337+
test('should allow resuming transaction after errors', () async {
338+
final db = await setupDatabase(path: path);
339+
await createTables(db);
340+
await db.writeTransaction((tx) async {
341+
var caught = false;
342+
try {
343+
// This error does not rollback the transaction
344+
await tx.execute('NOT A VALID STATEMENT');
345+
} catch (e) {
346+
// Ignore
347+
caught = true;
348+
}
349+
expect(caught, equals(true));
350+
351+
final rs = await tx.execute(
352+
'INSERT INTO test_data(description) VALUES(?) RETURNING description',
353+
['Test Data']);
354+
expect(rs.rows[0], equals(['Test Data']));
355+
});
356+
});
324357
});
325358
}
326359

0 commit comments

Comments
 (0)