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);
+    });
   });
 }