Skip to content

Commit 60f7b52

Browse files
committed
Merge remote-tracking branch 'origin/prepare-release' into get_all_connections
2 parents c9f8fa2 + 085caad commit 60f7b52

23 files changed

+616
-205
lines changed

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## 2025-05-22
7+
8+
---
9+
10+
Packages with breaking changes:
11+
12+
- There are no breaking changes in this release.
13+
14+
Packages with other changes:
15+
16+
- [`sqlite_async` - `v0.11.5`](#sqlite_async---v0115)
17+
18+
---
19+
20+
#### `sqlite_async` - `v0.11.5`
21+
22+
- Allow profiling queries. Queries are profiled by default in debug and profile builds, the runtime
23+
for queries is added to profiling timelines under the `sqlite_async` tag.
24+
- Fix cancelling `watch()` queries sometimes taking longer than necessary.
25+
- Fix web databases not respecting lock timeouts.
26+
627
## 2024-11-06
728

829
### Changes

packages/drift_sqlite_async/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.2.2
2+
3+
- Fix write detection when using UPDATE/INSERT/DELETE with RETURNING in raw queries.
4+
15
## 0.2.1
26

37
- Fix lints.

packages/drift_sqlite_async/lib/src/executor.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import 'package:sqlite_async/sqlite_async.dart';
88
// Ends with " RETURNING *", or starts with insert/update/delete.
99
// Drift-generated queries will always have the RETURNING *.
1010
// The INSERT/UPDATE/DELETE check is for custom queries, and is not exhaustive.
11-
final _returningCheck = RegExp(r'( RETURNING \*;?$)|(^(INSERT|UPDATE|DELETE))',
11+
final _returningCheck = RegExp(
12+
r'( RETURNING \*;?\s*$)|(^\s*(INSERT|UPDATE|DELETE))',
1213
caseSensitive: false);
1314

1415
class _SqliteAsyncDelegate extends _SqliteAsyncQueryDelegate

packages/drift_sqlite_async/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: drift_sqlite_async
2-
version: 0.2.1
2+
version: 0.2.2
33
homepage: https://github.com/powersync-ja/sqlite_async.dart
44
repository: https://github.com/powersync-ja/sqlite_async.dart
55
description: Use Drift with a sqlite_async database, allowing both to be used in the same application.

packages/drift_sqlite_async/test/basic_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66

77
import 'package:drift/drift.dart';
88
import 'package:drift_sqlite_async/drift_sqlite_async.dart';
9+
import 'package:sqlite3/common.dart';
910
import 'package:sqlite_async/sqlite_async.dart';
1011
import 'package:test/test.dart';
1112

@@ -219,5 +220,29 @@ void main() {
219220
.data,
220221
equals({'count': 1}));
221222
});
223+
224+
test('cannot update database with read', () async {
225+
await expectLater(() => dbu.customSelect('''
226+
-- trick to circumvent regex detecting writes
227+
INSERT INTO test_data(description) VALUES('test data');
228+
''').get(), throwsA(isA<SqliteException>()));
229+
});
230+
231+
test('allows spaces after returning', () async {
232+
// This tests that the statement is forwarded to the write connection
233+
// despite using customSelect(). If it wasn't, we'd get an error about
234+
// the database being read-only.
235+
final row = await dbu.customSelect(
236+
'INSERT INTO test_data(description) VALUES(?) RETURNING * ',
237+
variables: [Variable('Test Data')]).getSingle();
238+
expect(row.data['description'], equals('Test Data'));
239+
});
240+
241+
test('allows spaces before insert', () async {
242+
final row = await dbu.customSelect(
243+
' INSERT INTO test_data(description) VALUES(?) ',
244+
variables: [Variable('Test Data')]).get();
245+
expect(row, isEmpty);
246+
});
222247
});
223248
}

packages/drift_sqlite_async/test/db_test.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,16 @@ void main() {
106106
'[]'
107107
]));
108108
});
109+
110+
test('delete returns affected rows', () async {
111+
for (var i = 0; i < 10; i++) {
112+
await dbu
113+
.into(dbu.todoItems)
114+
.insert(TodoItemsCompanion.insert(description: 'desc $i'));
115+
}
116+
117+
final deleted = await dbu.delete(dbu.todoItems).go();
118+
expect(deleted, 10);
119+
});
109120
});
110121
}

packages/sqlite_async/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.11.5
2+
3+
- Allow profiling queries. Queries are profiled by default in debug and profile builds, the runtime
4+
for queries is added to profiling timelines under the `sqlite_async` tag.
5+
- Fix cancelling `watch()` queries sometimes taking longer than necessary.
6+
- Fix web databases not respecting lock timeouts.
7+
18
## 0.11.4
29

310
- Add `SqliteConnection.synchronousWrapper` and `SqliteDatabase.singleConnection`.

packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
import 'dart:developer';
2+
13
import 'package:sqlite3/common.dart';
24
import 'package:sqlite_async/src/common/mutex.dart';
35
import 'package:sqlite_async/src/sqlite_connection.dart';
6+
import 'package:sqlite_async/src/sqlite_options.dart';
47
import 'package:sqlite_async/src/sqlite_queries.dart';
58
import 'package:sqlite_async/src/update_notification.dart';
9+
import 'package:sqlite_async/src/utils/profiler.dart';
610

711
/// A simple "synchronous" connection which provides the async SqliteConnection
812
/// implementation using a synchronous SQLite connection
@@ -14,7 +18,15 @@ class SyncSqliteConnection extends SqliteConnection with SqliteQueries {
1418

1519
bool _closed = false;
1620

17-
SyncSqliteConnection(this.db, Mutex m) {
21+
/// Whether queries should be added to the `dart:developer` timeline.
22+
///
23+
/// This is enabled by default outside of release builds, see
24+
/// [SqliteOptions.profileQueries] for details.
25+
final bool profileQueries;
26+
27+
SyncSqliteConnection(this.db, Mutex m, {bool? profileQueries})
28+
: profileQueries =
29+
profileQueries ?? const SqliteOptions().profileQueries {
1830
mutex = m.open();
1931
updates = db.updates.map(
2032
(event) {
@@ -26,15 +38,31 @@ class SyncSqliteConnection extends SqliteConnection with SqliteQueries {
2638
@override
2739
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
2840
{Duration? lockTimeout, String? debugContext}) {
29-
return mutex.lock(() => callback(SyncReadContext(db)),
30-
timeout: lockTimeout);
41+
final task = profileQueries ? TimelineTask() : null;
42+
task?.start('${profilerPrefix}mutex_lock');
43+
44+
return mutex.lock(
45+
() {
46+
task?.finish();
47+
return callback(SyncReadContext(db, parent: task));
48+
},
49+
timeout: lockTimeout,
50+
);
3151
}
3252

3353
@override
3454
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
3555
{Duration? lockTimeout, String? debugContext}) {
36-
return mutex.lock(() => callback(SyncWriteContext(db)),
37-
timeout: lockTimeout);
56+
final task = profileQueries ? TimelineTask() : null;
57+
task?.start('${profilerPrefix}mutex_lock');
58+
59+
return mutex.lock(
60+
() {
61+
task?.finish();
62+
return callback(SyncWriteContext(db, parent: task));
63+
},
64+
timeout: lockTimeout,
65+
);
3866
}
3967

4068
@override
@@ -53,9 +81,12 @@ class SyncSqliteConnection extends SqliteConnection with SqliteQueries {
5381
}
5482

5583
class SyncReadContext implements SqliteReadContext {
84+
final TimelineTask? task;
85+
5686
CommonDatabase db;
5787

58-
SyncReadContext(this.db);
88+
SyncReadContext(this.db, {TimelineTask? parent})
89+
: task = TimelineTask(parent: parent);
5990

6091
@override
6192
Future<T> computeWithDatabase<T>(
@@ -65,13 +96,23 @@ class SyncReadContext implements SqliteReadContext {
6596

6697
@override
6798
Future<Row> get(String sql, [List<Object?> parameters = const []]) async {
68-
return db.select(sql, parameters).first;
99+
return task.timeSync(
100+
'get',
101+
() => db.select(sql, parameters).first,
102+
sql: sql,
103+
parameters: parameters,
104+
);
69105
}
70106

71107
@override
72108
Future<ResultSet> getAll(String sql,
73109
[List<Object?> parameters = const []]) async {
74-
return db.select(sql, parameters);
110+
return task.timeSync(
111+
'getAll',
112+
() => db.select(sql, parameters),
113+
sql: sql,
114+
parameters: parameters,
115+
);
75116
}
76117

77118
@override
@@ -91,26 +132,32 @@ class SyncReadContext implements SqliteReadContext {
91132
}
92133

93134
class SyncWriteContext extends SyncReadContext implements SqliteWriteContext {
94-
SyncWriteContext(super.db);
135+
SyncWriteContext(super.db, {super.parent});
95136

96137
@override
97138
Future<ResultSet> execute(String sql,
98139
[List<Object?> parameters = const []]) async {
99-
return db.select(sql, parameters);
140+
return task.timeSync(
141+
'execute',
142+
() => db.select(sql, parameters),
143+
sql: sql,
144+
parameters: parameters,
145+
);
100146
}
101147

102148
@override
103149
Future<void> executeBatch(
104150
String sql, List<List<Object?>> parameterSets) async {
105-
return computeWithDatabase((db) async {
151+
task.timeSync('executeBatch', () {
106152
final statement = db.prepare(sql, checkNoTail: true);
107153
try {
108154
for (var parameters in parameterSets) {
109-
statement.execute(parameters);
155+
task.timeSync('iteration', () => statement.execute(parameters),
156+
parameters: parameters);
110157
}
111158
} finally {
112159
statement.dispose();
113160
}
114-
});
161+
}, sql: sql);
115162
}
116163
}

0 commit comments

Comments
 (0)