From 73b74afb3e34ebe0fb4efb7f39c8e17cc534a70d Mon Sep 17 00:00:00 2001 From: Jorge Sardina <jorge@skilldevs.com> Date: Tue, 22 Apr 2025 12:16:55 +0200 Subject: [PATCH 1/2] Fix write detection when using UPDATE/INSERT/DELETE with RETURNING in raw queries. --- packages/drift_sqlite_async/CHANGELOG.md | 4 ++++ packages/drift_sqlite_async/lib/src/executor.dart | 3 ++- packages/drift_sqlite_async/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/drift_sqlite_async/CHANGELOG.md b/packages/drift_sqlite_async/CHANGELOG.md index d39db6f..b101ac4 100644 --- a/packages/drift_sqlite_async/CHANGELOG.md +++ b/packages/drift_sqlite_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +- Fix write detection when using UPDATE/INSERT/DELETE with RETURNING in raw queries. + ## 0.2.1 - Fix lints. diff --git a/packages/drift_sqlite_async/lib/src/executor.dart b/packages/drift_sqlite_async/lib/src/executor.dart index dbd4c96..41886f3 100644 --- a/packages/drift_sqlite_async/lib/src/executor.dart +++ b/packages/drift_sqlite_async/lib/src/executor.dart @@ -8,7 +8,8 @@ import 'package:sqlite_async/sqlite_async.dart'; // Ends with " RETURNING *", or starts with insert/update/delete. // Drift-generated queries will always have the RETURNING *. // The INSERT/UPDATE/DELETE check is for custom queries, and is not exhaustive. -final _returningCheck = RegExp(r'( RETURNING \*;?$)|(^(INSERT|UPDATE|DELETE))', +final _returningCheck = RegExp( + r'( RETURNING \*;?\s*$)|(^\s*(INSERT|UPDATE|DELETE))', caseSensitive: false); class _SqliteAsyncDelegate extends _SqliteAsyncQueryDelegate diff --git a/packages/drift_sqlite_async/pubspec.yaml b/packages/drift_sqlite_async/pubspec.yaml index 98eb766..62a64da 100644 --- a/packages/drift_sqlite_async/pubspec.yaml +++ b/packages/drift_sqlite_async/pubspec.yaml @@ -1,5 +1,5 @@ name: drift_sqlite_async -version: 0.2.1 +version: 0.2.2 homepage: https://github.com/powersync-ja/sqlite_async.dart repository: https://github.com/powersync-ja/sqlite_async.dart description: Use Drift with a sqlite_async database, allowing both to be used in the same application. From 9f2a984657837d18665677cf07fe515558fcd76b Mon Sep 17 00:00:00 2001 From: Jorge Sardina <jorge@skilldevs.com> Date: Thu, 24 Apr 2025 12:41:38 +0200 Subject: [PATCH 2/2] add tests --- .../drift_sqlite_async/test/basic_test.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/drift_sqlite_async/test/basic_test.dart b/packages/drift_sqlite_async/test/basic_test.dart index 371c149..339cc04 100644 --- a/packages/drift_sqlite_async/test/basic_test.dart +++ b/packages/drift_sqlite_async/test/basic_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:drift/drift.dart'; import 'package:drift_sqlite_async/drift_sqlite_async.dart'; +import 'package:sqlite3/common.dart'; import 'package:sqlite_async/sqlite_async.dart'; import 'package:test/test.dart'; @@ -219,5 +220,29 @@ void main() { .data, equals({'count': 1})); }); + + test('cannot update database with read', () async { + await expectLater(() => dbu.customSelect(''' +-- trick to circumvent regex detecting writes +INSERT INTO test_data(description) VALUES('test data'); +''').get(), throwsA(isA<SqliteException>())); + }); + + test('allows spaces after returning', () async { + // This tests that the statement is forwarded to the write connection + // despite using customSelect(). If it wasn't, we'd get an error about + // the database being read-only. + final row = await dbu.customSelect( + 'INSERT INTO test_data(description) VALUES(?) RETURNING * ', + variables: [Variable('Test Data')]).getSingle(); + expect(row.data['description'], equals('Test Data')); + }); + + test('allows spaces before insert', () async { + final row = await dbu.customSelect( + ' INSERT INTO test_data(description) VALUES(?) ', + variables: [Variable('Test Data')]).get(); + expect(row, isEmpty); + }); }); }