/
wasm.dart
148 lines (129 loc) · 4.32 KB
/
wasm.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/// An experimental web-compatible version of drift that doesn't depend
/// on external JavaScript sources.
///
/// While the implementation is tested and no API breaking changes are expected
/// to the public interface, it is still fairly new and may have remaining bugs
/// or issues.
///
/// A generally less efficient, but currently more stable backend is available
/// through the `package:drift/web.dart` library described in the
/// [documentation][https://drift.simonbinder.eu/web/].
@experimental
library drift.wasm;
import 'package:meta/meta.dart';
import 'package:sqlite3/common.dart';
import 'package:sqlite3/wasm.dart';
import 'backends.dart';
import 'src/sqlite3/database.dart';
/// Signature of a function that can perform setup work on a [database] before
/// drift is fully ready.
///
/// This could be used to, for instance, set encryption keys for SQLCipher
/// implementations.
typedef WasmDatabaseSetup = void Function(CommonDatabase database);
/// An experimental, WebAssembly based implementation of a drift sqlite3
/// database.
///
/// Using this database requires adding a WebAssembly file for sqlite3 to your
/// app.
/// The [documentation](https://drift.simonbinder.eu/web/#drift-wasm) describes
/// how to obtain this file. A [working example](https://github.com/simolus3/drift/blob/04539882330d80519128fec1ceb120fb1623a831/examples/app/lib/database/connection/web.dart#L27-L36)
/// is also available in the drift repository.
class WasmDatabase extends DelegatedDatabase {
WasmDatabase._(DatabaseDelegate delegate, bool logStatements)
: super(delegate, isSequential: true, logStatements: logStatements);
/// Creates a wasm database at [path] in the virtual file system of the
/// [sqlite3] module.
/// If [fileSystem] provided, the data is guaranteed to be
/// stored in the IndexedDB when the request is complete. Attention!
/// Insert/update queries may be slower when this option enabled. If you want
/// to insert more than one rows, be sure you run in a transaction if
/// possible.
factory WasmDatabase({
required CommmonSqlite3 sqlite3,
required String path,
WasmDatabaseSetup? setup,
IndexedDbFileSystem? fileSystem,
bool logStatements = false,
bool cachePreparedStatements = true,
}) {
return WasmDatabase._(
_WasmDelegate(sqlite3, path, setup, fileSystem, cachePreparedStatements),
logStatements,
);
}
/// Creates an in-memory database in the loaded [sqlite3] database.
factory WasmDatabase.inMemory(
CommmonSqlite3 sqlite3, {
WasmDatabaseSetup? setup,
bool logStatements = false,
bool cachePreparedStatements = true,
}) {
return WasmDatabase._(
_WasmDelegate(sqlite3, null, setup, null, cachePreparedStatements),
logStatements,
);
}
}
class _WasmDelegate extends Sqlite3Delegate<CommonDatabase> {
final CommmonSqlite3 _sqlite3;
final String? _path;
final IndexedDbFileSystem? _fileSystem;
_WasmDelegate(
this._sqlite3,
this._path,
WasmDatabaseSetup? setup,
this._fileSystem,
bool cachePreparedStatements,
) : super(
setup,
cachePreparedStatements: cachePreparedStatements,
);
@override
CommonDatabase openDatabase() {
final path = _path;
if (path == null) {
return _sqlite3.openInMemory();
} else {
return _sqlite3.open(path);
}
}
Future<void> _flush() async {
await _fileSystem?.flush();
}
Future _runWithArgs(String statement, List<Object?> args) async {
runWithArgsSync(statement, args);
if (!isInTransaction) {
await _flush();
}
}
@override
Future<void> runCustom(String statement, List<Object?> args) async {
await _runWithArgs(statement, args);
}
@override
Future<int> runInsert(String statement, List<Object?> args) async {
await _runWithArgs(statement, args);
return database.lastInsertRowId;
}
@override
Future<int> runUpdate(String statement, List<Object?> args) async {
await _runWithArgs(statement, args);
return database.getUpdatedRows();
}
@override
Future<void> runBatched(BatchedStatements statements) async {
runBatchSync(statements);
if (!isInTransaction) {
await _flush();
}
}
@override
Future<void> close() async {
await super.close();
if (closeUnderlyingWhenClosed) {
database.dispose();
await _flush();
}
}
}