Skip to content

Commit a290e87

Browse files
authored
Merge pull request simolus3#268 from jackd/transaction-hooks
Added commit / rollback hooks
2 parents c4f1db9 + 940880a commit a290e87

25 files changed

+767
-114
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ jobs:
209209

210210
- name: Web tests
211211
run: |
212-
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.6.0/sqlite3.wasm -o example/web/sqlite3.wasm
213-
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.6.0/sqlite3mc.wasm -o example/web/sqlite3mc.wasm
212+
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.7.0/sqlite3.wasm -o example/web/sqlite3.wasm
213+
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.7.0/sqlite3mc.wasm -o example/web/sqlite3mc.wasm
214214
dart test -P web -r expanded
215215
# If browsers behave differently on different platforms, surely that's not our fault...
216216
# So, only run browser tests on Linux to be faster.

sqlite3/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.7.0-dev
2+
3+
- Add support for commit and rollback hooks as well as a predicate that can
4+
revert transactions.
5+
16
## 2.6.1
27

38
- Fix out-of-bound reads in the `xWrite` implementation of the OPFS-locks based

sqlite3/assets/sqlite3.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ void *sqlite3_update_hook(sqlite3 *,
4040
void (*)(void *, int, sqlite3_char const *,
4141
sqlite3_char const *, int64_t),
4242
void *);
43+
void *sqlite3_commit_hook(sqlite3 *, int (*)(void *), void *);
44+
void *sqlite3_rollback_hook(sqlite3 *, void (*)(void *), void *);
4345
int sqlite3_get_autocommit(sqlite3 *db);
4446

4547
// Statements

sqlite3/assets/wasm/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ add_custom_command(
3232
OUTPUT required_symbols.txt
3333
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../
3434
COMMAND dart run tool/wasm_symbols.dart ${CMAKE_CURRENT_BINARY_DIR}/required_symbols.txt
35-
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../tool/wasm_symbols.dart
35+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../tool/wasm_symbols.dart ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/src/wasm/wasm_interop.dart
3636
VERBATIM
3737
)
3838
add_custom_target(required_symbols DEPENDS required_symbols.txt)

sqlite3/assets/wasm/bridge.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import_dart("function_hook") extern void dartUpdateHook(void *id, int kind,
5454
const char *db,
5555
const char *table,
5656
sqlite3_int64 rowid);
57+
import_dart("function_commit_hook") extern int dartCommitHook(void *id);
58+
import_dart("function_rollback_hook") extern void dartRollbackHook(void *id);
5759
import_dart("function_compare") extern int dartXCompare(void *id, int lengthA,
5860
const void *a,
5961
int lengthB,

sqlite3/assets/wasm/helpers.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ SQLITE_API void dart_sqlite3_updates(sqlite3 *db, int id) {
227227
sqlite3_update_hook(db, id >= 0 ? &dartUpdateHook : NULL, (void *)id);
228228
}
229229

230+
SQLITE_API void dart_sqlite3_commits(sqlite3 *db, int id) {
231+
sqlite3_commit_hook(db, id >= 0 ? &dartCommitHook : NULL, (void *)id);
232+
}
233+
234+
SQLITE_API void dart_sqlite3_rollbacks(sqlite3 *db, int id) {
235+
sqlite3_rollback_hook(db, id >= 0 ? &dartRollbackHook : NULL, (void *)id);
236+
}
237+
230238
SQLITE_API int dart_sqlite3_create_collation(sqlite3 *db, const char *zName,
231239
int eTextRep, int id) {
232240
return sqlite3_create_collation_v2(db, zName, eTextRep, (void *)id,

sqlite3/lib/src/database.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,52 @@ abstract class CommonDatabase {
5151
/// - [Data Change Notification Callbacks](https://www.sqlite.org/c3ref/update_hook.html)
5252
Stream<SqliteUpdate> get updates;
5353

54+
/// The [VoidPredicate] that is used to filter out transactions before commiting.
55+
///
56+
/// This is run before every commit, i.e. before the end of an explicit
57+
/// transaction and before the end of an implicit transactions created by
58+
/// an insert / update / delete operation.
59+
///
60+
/// If the filter returns `false`, the commit is converted into a rollback.
61+
///
62+
/// The function should not do anything that modifies the database connection,
63+
/// e.g. run SQL statements, prepare statements or step.
64+
///
65+
/// See also:
66+
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
67+
VoidPredicate? get commitFilter;
68+
set commitFilter(VoidPredicate? commitFilter);
69+
70+
/// An async stream that fires after each commit.
71+
///
72+
/// Listening to this stream will register a "commit hook" on the native
73+
/// database. Each commit that sqlite3 reports through that hook will then
74+
/// be added to the stream.
75+
///
76+
/// Note that the stream reports updates _asynchronously_, e.g. one event
77+
/// loop iteration after sqlite reports them.
78+
///
79+
/// Also note this works in conjunction with `commitFilter`. If the filter
80+
/// function is not null and returns `false`, the commit will not occur and
81+
/// this stream will not fire.
82+
///
83+
/// See also:
84+
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
85+
Stream<void> get commits;
86+
87+
/// An async stream that fires after each rollback.
88+
///
89+
/// Listening to this stream will register a "rollback hook" on the native
90+
/// database. Each rollback that sqlite3 reports through that hook will then
91+
/// be added to the stream.
92+
///
93+
/// Note that the stream reports updates _asynchronously_, e.g. one event
94+
/// loop iteration after sqlite reports them.
95+
///
96+
/// See also:
97+
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
98+
Stream<void> get rollbacks;
99+
54100
/// Executes the [sql] statement with the provided [parameters], ignoring any
55101
/// rows returned by the statement.
56102
///

sqlite3/lib/src/ffi/bindings.dart

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,8 @@ final class FfiDatabase extends RawSqliteDatabase {
414414
final BindingsWithLibrary bindings;
415415
final Pointer<sqlite3> db;
416416
NativeCallable<_UpdateHook>? _installedUpdateHook;
417+
NativeCallable<_CommitHook>? _installedCommitHook;
418+
NativeCallable<_RollbackHook>? _installedRollbackHook;
417419

418420
FfiDatabase(this.bindings, this.db);
419421

@@ -566,6 +568,37 @@ final class FfiDatabase extends RawSqliteDatabase {
566568
previous?.close();
567569
}
568570

571+
@override
572+
void sqlite3_commit_hook(RawCommitHook? hook) {
573+
final previous = _installedCommitHook;
574+
575+
if (hook == null) {
576+
_installedCommitHook = null;
577+
bindings.bindings.sqlite3_commit_hook(db, nullPtr(), nullPtr());
578+
} else {
579+
final native = _installedCommitHook = hook.toNative();
580+
bindings.bindings
581+
.sqlite3_commit_hook(db, native.nativeFunction, nullPtr());
582+
}
583+
584+
previous?.close();
585+
}
586+
587+
@override
588+
void sqlite3_rollback_hook(RawRollbackHook? hook) {
589+
final previous = _installedRollbackHook;
590+
591+
if (hook == null) {
592+
bindings.bindings.sqlite3_rollback_hook(db, nullPtr(), nullPtr());
593+
} else {
594+
final native = _installedRollbackHook = hook.toNative();
595+
bindings.bindings
596+
.sqlite3_rollback_hook(db, native.nativeFunction, nullPtr());
597+
}
598+
599+
previous?.close();
600+
}
601+
569602
@override
570603
int sqlite3_db_config(int op, int value) {
571604
final result = bindings.bindings.sqlite3_db_config(
@@ -976,6 +1009,8 @@ typedef _XCompare = Int Function(
9761009
Pointer<Void>, Int, Pointer<Void>, Int, Pointer<Void>);
9771010
typedef _UpdateHook = Void Function(
9781011
Pointer<Void>, Int, Pointer<sqlite3_char>, Pointer<sqlite3_char>, Int64);
1012+
typedef _CommitHook = Int Function(Pointer<Void>);
1013+
typedef _RollbackHook = Void Function(Pointer<Void>);
9791014

9801015
extension on RawXFunc {
9811016
NativeCallable<_XFunc> toNative(Bindings bindings) {
@@ -1029,3 +1064,24 @@ extension on RawUpdateHook {
10291064
)..keepIsolateAlive = false;
10301065
}
10311066
}
1067+
1068+
extension on RawCommitHook {
1069+
NativeCallable<_CommitHook> toNative() {
1070+
return NativeCallable.isolateLocal(
1071+
(Pointer<Void> _) {
1072+
return this();
1073+
},
1074+
exceptionalReturn: 1,
1075+
)..keepIsolateAlive = false;
1076+
}
1077+
}
1078+
1079+
extension on RawRollbackHook {
1080+
NativeCallable<_RollbackHook> toNative() {
1081+
return NativeCallable.isolateLocal(
1082+
(Pointer<Void> _) {
1083+
this();
1084+
},
1085+
)..keepIsolateAlive = false;
1086+
}
1087+
}

sqlite3/lib/src/ffi/sqlite3.g.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,60 @@ class Bindings {
317317
ffi.Int64)>>,
318318
ffi.Pointer<ffi.Void>)>();
319319

320+
ffi.Pointer<ffi.Void> sqlite3_commit_hook(
321+
ffi.Pointer<sqlite3> arg0,
322+
ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>
323+
arg1,
324+
ffi.Pointer<ffi.Void> arg2,
325+
) {
326+
return _sqlite3_commit_hook(
327+
arg0,
328+
arg1,
329+
arg2,
330+
);
331+
}
332+
333+
late final _sqlite3_commit_hookPtr = _lookup<
334+
ffi.NativeFunction<
335+
ffi.Pointer<ffi.Void> Function(
336+
ffi.Pointer<sqlite3>,
337+
ffi.Pointer<
338+
ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>,
339+
ffi.Pointer<ffi.Void>)>>('sqlite3_commit_hook');
340+
late final _sqlite3_commit_hook = _sqlite3_commit_hookPtr.asFunction<
341+
ffi.Pointer<ffi.Void> Function(
342+
ffi.Pointer<sqlite3>,
343+
ffi.Pointer<
344+
ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>,
345+
ffi.Pointer<ffi.Void>)>();
346+
347+
ffi.Pointer<ffi.Void> sqlite3_rollback_hook(
348+
ffi.Pointer<sqlite3> arg0,
349+
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
350+
arg1,
351+
ffi.Pointer<ffi.Void> arg2,
352+
) {
353+
return _sqlite3_rollback_hook(
354+
arg0,
355+
arg1,
356+
arg2,
357+
);
358+
}
359+
360+
late final _sqlite3_rollback_hookPtr = _lookup<
361+
ffi.NativeFunction<
362+
ffi.Pointer<ffi.Void> Function(
363+
ffi.Pointer<sqlite3>,
364+
ffi.Pointer<
365+
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>,
366+
ffi.Pointer<ffi.Void>)>>('sqlite3_rollback_hook');
367+
late final _sqlite3_rollback_hook = _sqlite3_rollback_hookPtr.asFunction<
368+
ffi.Pointer<ffi.Void> Function(
369+
ffi.Pointer<sqlite3>,
370+
ffi.Pointer<
371+
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>,
372+
ffi.Pointer<ffi.Void>)>();
373+
320374
int sqlite3_get_autocommit(
321375
ffi.Pointer<sqlite3> db,
322376
) {

sqlite3/lib/src/functions.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import 'package:meta/meta.dart';
22

3+
/// A filter function without any arguments.
4+
typedef VoidPredicate = bool Function();
5+
36
/// A collating function provided to a sql collation.
47
///
58
/// The function must return a `int`.

sqlite3/lib/src/implementation/bindings.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ typedef RawXFunc = void Function(RawSqliteContext, List<RawSqliteValue>);
5959
typedef RawXStep = void Function(RawSqliteContext, List<RawSqliteValue>);
6060
typedef RawXFinal = void Function(RawSqliteContext);
6161
typedef RawUpdateHook = void Function(int kind, String tableName, int rowId);
62+
typedef RawCommitHook = int Function();
63+
typedef RawRollbackHook = void Function();
6264
typedef RawCollation = int Function(String? a, String? b);
6365

6466
abstract base class RawSqliteDatabase {
@@ -81,6 +83,10 @@ abstract base class RawSqliteDatabase {
8183

8284
void sqlite3_update_hook(RawUpdateHook? hook);
8385

86+
void sqlite3_commit_hook(RawCommitHook? hook);
87+
88+
void sqlite3_rollback_hook(RawRollbackHook? hook);
89+
8490
/// Returns a compiler able to create prepared statements from the utf8-
8591
/// encoded SQL string passed as its argument.
8692
RawStatementCompiler newCompiler(List<int> utf8EncodedSql);

0 commit comments

Comments
 (0)