Skip to content

Fix write detection when using UPDATE/INSERT/DELETE with RETURNING in raw queries #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/drift_sqlite_async/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
3 changes: 2 additions & 1 deletion packages/drift_sqlite_async/lib/src/executor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/drift_sqlite_async/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
25 changes: 25 additions & 0 deletions packages/drift_sqlite_async/test/basic_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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);
});
});
}