Skip to content

Commit dc6e759

Browse files
committed
Cleanup.
1 parent 9a5a5b9 commit dc6e759

File tree

9 files changed

+88
-18
lines changed

9 files changed

+88
-18
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@
77
pubspec.lock
88

99
.idea
10+
*.db
11+
*.db-*
12+
test-db
13+
sqlite-autoconf-*

README.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ great as an in-app database. However, SQLite is designed for many different use
77
some configuration for optimal performance as an in-app database.
88

99
The [sqlite3](https://pub.dev/packages/sqlite3) Dart bindings are great for direct synchronous access
10-
to a SQLite database, but leaves the configuration the developer.
10+
to a SQLite database, but leaves the configuration up to the developer.
1111

1212
This library wraps the bindings and configures the database with a good set of defaults, with
1313
all database calls being asynchronous to avoid blocking the UI, while still providing direct SQL
@@ -16,6 +16,59 @@ query access.
1616
## Features
1717

1818
* All operations are asynchronous by default - does not block the main isolate.
19+
* Watch a query to automatically re-run on changes to the underlying data.
1920
* Concurrent transactions supported by default - one write transaction and many multiple read transactions.
20-
* Uses WAL mode with minimal locking.
21+
* Uses WAL mode for fast writes and running read transactions concurrently with a write transaction.
2122
* Direct synchronous access in an isolate is supported for performance-sensitive use cases.
23+
* Automatically convert query args to JSON where applicable, making JSON1 operations simple.
24+
* Direct SQL queries - no wrapper classes or code generation required.
25+
26+
## Installation
27+
28+
```sh
29+
dart pub add sqlite_async
30+
```
31+
32+
For flutter applications, additionally add `sqlite3_flutter_libs` to include the native SQLite
33+
library.
34+
35+
For other platforms, see the [sqlite3 package docs](https://pub.dev/packages/sqlite3#supported-platforms).
36+
37+
Web is currently not supported.
38+
39+
## Getting Started
40+
41+
```dart
42+
import 'package:sqlite_async/sqlite_async.dart';
43+
44+
final migrations = SqliteMigrations()
45+
..add(SqliteMigration(1, (tx) async {
46+
await tx.execute(
47+
'CREATE TABLE test_data(id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT)');
48+
}));
49+
50+
void main() async {
51+
final db = SqliteDatabase(path: 'test.db');
52+
await migrations.migrate(db);
53+
54+
// Use execute() or executeBatch() for INSERT/UPDATE/DELETE statements
55+
await db.executeBatch('INSERT INTO test_data(data) values(?)', [
56+
['Test1'],
57+
['Test2']
58+
]);
59+
60+
// Use getAll(), get() or getOptional() for SELECT statements
61+
var results = await db.getAll('SELECT * FROM test_data');
62+
print('Results: $results');
63+
64+
// Combine multiple statements into a single write transaction for:
65+
// 1. Atomic persistence (all updates are either applied or rolled back).
66+
// 2. Improved throughput.
67+
await db.writeTransaction((tx) async {
68+
await db.execute('INSERT INTO test_data(data) values(?)', ['Test3']);
69+
await db.execute('INSERT INTO test_data(data) values(?)', ['Test4']);
70+
});
71+
72+
await db.close();
73+
}
74+
```

example/json_example.dart

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:sqlite_async/sqlite_async.dart';
22

33
// This example shows using a custom class and SQLite's JSON1 functionality,
44
// to efficiently map between custom data objects and SQLite.
5+
// This is especially useful for bulk INSERT, UPDATE or DELETE statements,
56

67
class User {
78
int? id;
@@ -51,13 +52,13 @@ SELECT name, email FROM (${User.selectJsonData('?')})
5152
RETURNING id''', [users]);
5253
var ids = idRows.map((row) => row['id']).toList();
5354

54-
// Alternative using json1 functions directly
55+
// Alternative, using json1 functions directly
5556
await db.execute('''
5657
INSERT INTO users(name, email)
5758
SELECT e.value ->> 'name', e.value ->> 'email' FROM json_each(?) e
5859
RETURNING id''', [users]);
5960

60-
// Select by id
61+
// Select using "WHERE id IN ..."
6162
var queriedUsers = (await db.getAll(
6263
"SELECT id, name, email FROM users WHERE id IN (SELECT json_each.value FROM json_each(?)) ORDER BY name",
6364
[ids]))
@@ -66,12 +67,17 @@ RETURNING id''', [users]);
6667

6768
print(queriedUsers);
6869

69-
// Bulk update
70+
// Bulk update using UPDATE FROM
7071
await db.execute('''
7172
UPDATE users
7273
SET name = args.name, email = args.email
7374
FROM (${User.selectJsonData('?')}) as args
7475
WHERE users.id = args.id''', [queriedUsers]);
7576

77+
// Bulk delete using "WHERE id IN ..."
78+
await db.execute('''
79+
DELETE FROM users WHERE id IN (SELECT json_each.value FROM json_each(?))''',
80+
[queriedUsers.map((u) => u.id).toList()]);
81+
7682
await db.close();
7783
}

lib/src/mutex.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,17 @@ class SimpleMutex implements Mutex {
9797
await lock(() async {});
9898
}
9999

100+
/// Get a serialized instance that can be passed over to a different isolate.
100101
SerializedMutex get shared {
101102
_shared ??= SharedMutexServer._withMutex(this);
102103
return _shared!.serialized;
103104
}
104105
}
105106

107+
/// Serialized version of a Mutex, can be passed over to different isolates.
108+
/// Use [open] to get a Mutex instance.
109+
///
110+
/// Uses [SendPort] to communicate with the source mutex.
106111
class SerializedMutex {
107112
final SerializedPortClient client;
108113

@@ -113,6 +118,9 @@ class SerializedMutex {
113118
}
114119
}
115120

121+
/// Mutex instantiated from a source mutex, potentially in a different isolate.
122+
///
123+
/// Uses a [SendPort] to communicate with the source mutex.
116124
class SharedMutex implements Mutex {
117125
final ChildPortClient client;
118126

@@ -176,11 +184,6 @@ class SharedMutexServer {
176184

177185
late final PortServer server;
178186

179-
factory SharedMutexServer._() {
180-
final Mutex mutex = Mutex();
181-
return SharedMutexServer._withMutex(mutex);
182-
}
183-
184187
SharedMutexServer._withMutex(this.mutex) {
185188
server = PortServer((Object? arg) async {
186189
return await _handle(arg);

lib/src/sqlite_connection_impl.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ void _sqliteConnectionIsolate(_SqliteConnectionParams params) async {
238238
throw sqlite.SqliteException(
239239
0, 'Transaction must be closed within the read or write lock');
240240
}
241+
return null;
241242
} else if (data is _SqliteIsolateStatement) {
242243
if (data.sql == 'BEGIN' || data.sql == 'BEGIN IMMEDIATE') {
243244
if (txId != null) {
@@ -282,6 +283,9 @@ void _sqliteConnectionIsolate(_SqliteConnectionParams params) async {
282283
}
283284
} else if (data is _SqliteIsolateConnectionClose) {
284285
db.dispose();
286+
return null;
287+
} else {
288+
throw ArgumentError('Unknown data type $data');
285289
}
286290
});
287291

lib/src/sqlite_database.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ class SqliteDatabase with SqliteQueries implements SqliteConnection {
121121
} else {
122122
updates!.tables.addAll(message.tables);
123123
}
124+
return null;
124125
} else if (message is InitDb) {
125126
await _initialized;
127+
return null;
126128
} else if (message is SubscribeToUpdates) {
127129
if (subscriptions.containsKey(message.port)) {
128130
return;
@@ -131,9 +133,13 @@ class SqliteDatabase with SqliteQueries implements SqliteConnection {
131133
message.port.send(event);
132134
});
133135
subscriptions[message.port] = subscription;
136+
return null;
134137
} else if (message is UnsubscribeToUpdates) {
135138
final subscription = subscriptions.remove(message.port);
136139
subscription?.cancel();
140+
return null;
141+
} else {
142+
throw ArgumentError('Unknown message type: $message');
137143
}
138144
});
139145
}

test/isolate_test.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import 'dart:isolate';
2-
import 'dart:math';
32

4-
import 'package:sqlite_async/sqlite_async.dart';
53
import 'package:test/test.dart';
6-
import 'package:sqlite3/sqlite3.dart' as sqlite;
4+
75
import 'util.dart';
86

97
void main() {

test/migration_test.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import 'dart:math';
2-
31
import 'package:sqlite_async/sqlite_async.dart';
42
import 'package:test/test.dart';
5-
import 'package:sqlite3/sqlite3.dart' as sqlite;
3+
64
import 'util.dart';
75

86
void main() {

test/watch_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import 'dart:async';
22
import 'dart:isolate';
33
import 'dart:math';
44

5-
import 'package:async/async.dart';
65
import 'package:sqlite3/sqlite3.dart';
76
import 'package:sqlite_async/sqlite_async.dart';
87
import 'package:sqlite_async/src/database_utils.dart';
9-
import 'package:sqlite_async/src/isolate_connection_factory.dart';
108
import 'package:test/test.dart';
119

1210
import 'util.dart';

0 commit comments

Comments
 (0)