Skip to content

Commit d937c5d

Browse files
authored
Merge pull request #12 from journeyapps/cleanup
v0.5.1: Close write connection after read connections; Fix watch with query parameters
2 parents f4c45fa + fe62eab commit d937c5d

7 files changed

+134
-5
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.5.1
2+
3+
- Fix `watch` when called with query parameters.
4+
- Clean up `-wal` and `-shm` files on close.
5+
16
## 0.5.0
27

38
- No code changes.

lib/src/connection_pool.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,12 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
174174
@override
175175
Future<void> close() async {
176176
closed = true;
177-
await _writeConnection?.close();
178177
for (var connection in _readConnections) {
179178
await connection.close();
180179
}
180+
// Closing the write connection cleans up the journal files (-shm and -wal files).
181+
// It can only do that if there are no other open connections, so we close the
182+
// read-only connections first.
183+
await _writeConnection?.close();
181184
}
182185
}

lib/src/database_utils.dart

+3-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ Future<Set<String>> getSourceTablesText(
5757
}
5858

5959
/// Given a SELECT query, return the tables that the query depends on.
60-
Future<Set<String>> getSourceTables(SqliteReadContext ctx, String sql) async {
61-
final rows = await ctx.getAll('EXPLAIN $sql');
60+
Future<Set<String>> getSourceTables(SqliteReadContext ctx, String sql,
61+
[List<Object?> parameters = const []]) async {
62+
final rows = await ctx.getAll('EXPLAIN $sql', parameters);
6263
List<int> rootpages = [];
6364
for (var row in rows) {
6465
if (row['opcode'] == 'OpenRead' && row['p3'] == 0 && row['p2'] is int) {

lib/src/sqlite_queries.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
5050
Iterable<String>? triggerOnTables}) async* {
5151
assert(updates != null,
5252
'updates stream must be provided to allow query watching');
53-
final tables = triggerOnTables ?? await getSourceTables(this, sql);
53+
final tables =
54+
triggerOnTables ?? await getSourceTables(this, sql, parameters);
5455
final filteredStream =
5556
updates!.transform(UpdateNotification.filterTablesTransformer(tables));
5657
final throttledStream = UpdateNotification.throttleStream(

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: sqlite_async
22
description: High-performance asynchronous interface for SQLite on Dart and Flutter.
3-
version: 0.5.0
3+
version: 0.5.1
44
repository: https://github.com/journeyapps/sqlite_async.dart
55
environment:
66
sdk: '>=2.19.1 <4.0.0'

test/close_test.dart

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'dart:io';
2+
3+
import 'package:sqlite_async/sqlite_async.dart';
4+
import 'package:test/test.dart';
5+
6+
import 'util.dart';
7+
8+
void main() {
9+
group('Close Tests', () {
10+
late String path;
11+
12+
setUp(() async {
13+
path = dbPath();
14+
await cleanDb(path: path);
15+
});
16+
17+
tearDown(() async {
18+
await cleanDb(path: path);
19+
});
20+
21+
createTables(SqliteDatabase db) async {
22+
await db.writeTransaction((tx) async {
23+
await tx.execute(
24+
'CREATE TABLE test_data(id INTEGER PRIMARY KEY AUTOINCREMENT, description TEXT)');
25+
});
26+
}
27+
28+
test('Open and close', () async {
29+
// Test that the journal files are properly deleted after closing.
30+
// If the write connection is closed before the read connections, that is
31+
// not the case.
32+
33+
final db = await setupDatabase(path: path);
34+
await createTables(db);
35+
36+
await db.execute(
37+
'INSERT INTO test_data(description) VALUES(?)', ['Test Data']);
38+
await db.getAll('SELECT * FROM test_data');
39+
40+
expect(await File('$path-wal').exists(), equals(true));
41+
expect(await File('$path-shm').exists(), equals(true));
42+
43+
await db.close();
44+
45+
expect(await File(path).exists(), equals(true));
46+
47+
expect(await File('$path-wal').exists(), equals(false));
48+
expect(await File('$path-shm').exists(), equals(false));
49+
50+
expect(await File('$path-journal').exists(), equals(false));
51+
});
52+
});
53+
}

test/watch_test.dart

+66
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,72 @@ void main() {
245245
lastTime = r;
246246
}
247247
});
248+
249+
test('watch with parameters', () async {
250+
final db = await setupDatabase(path: path);
251+
await createTables(db);
252+
253+
const baseTime = 20;
254+
255+
const throttleDuration = Duration(milliseconds: baseTime);
256+
257+
final rows = await db.execute(
258+
'INSERT INTO customers(name) VALUES (?) RETURNING id',
259+
['a customer']);
260+
final id = rows[0]['id'];
261+
262+
final stream = db.watch(
263+
'SELECT count() AS count FROM assets WHERE customer_id = ?',
264+
parameters: [id],
265+
throttle: throttleDuration);
266+
267+
var done = false;
268+
inserts() async {
269+
while (!done) {
270+
await db.execute(
271+
'INSERT INTO assets(make, customer_id) VALUES (?, ?)',
272+
['test', id]);
273+
await Future.delayed(
274+
Duration(milliseconds: Random().nextInt(baseTime * 2)));
275+
}
276+
}
277+
278+
const numberOfQueries = 10;
279+
280+
inserts();
281+
try {
282+
List<DateTime> times = [];
283+
final results = await stream.take(numberOfQueries).map((e) {
284+
times.add(DateTime.now());
285+
return e;
286+
}).toList();
287+
288+
var lastCount = 0;
289+
for (var r in results) {
290+
final count = r.first['count'];
291+
// This is not strictly incrementing, since we can't guarantee the
292+
// exact order between reads and writes.
293+
// We can guarantee that there will always be a read after the last write,
294+
// but the previous read may have been after the same write in some cases.
295+
expect(count, greaterThanOrEqualTo(lastCount));
296+
lastCount = count;
297+
}
298+
299+
// The number of read queries must not be greater than the number of writes overall.
300+
expect(numberOfQueries, lessThanOrEqualTo(results.last.first['count']));
301+
302+
DateTime? lastTime;
303+
for (var r in times) {
304+
if (lastTime != null) {
305+
var diff = r.difference(lastTime);
306+
expect(diff, greaterThanOrEqualTo(throttleDuration));
307+
}
308+
lastTime = r;
309+
}
310+
} finally {
311+
done = true;
312+
}
313+
});
248314
});
249315
}
250316

0 commit comments

Comments
 (0)