Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ jobs:

- name: Web tests
run: |
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.6.0/sqlite3.wasm -o example/web/sqlite3.wasm
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.6.0/sqlite3mc.wasm -o example/web/sqlite3mc.wasm
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.7.0/sqlite3.wasm -o example/web/sqlite3.wasm
curl https://simon-public.fsn1.your-objectstorage.com/assets/sqlite3/2.7.0/sqlite3mc.wasm -o example/web/sqlite3mc.wasm
dart test -P web -r expanded
# If browsers behave differently on different platforms, surely that's not our fault...
# So, only run browser tests on Linux to be faster.
Expand Down
5 changes: 5 additions & 0 deletions sqlite3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.7.0-dev

- Add support for commit and rollback hooks as well as a predicate that can
revert transactions.

## 2.6.1

- Fix out-of-bound reads in the `xWrite` implementation of the OPFS-locks based
Expand Down
2 changes: 2 additions & 0 deletions sqlite3/assets/sqlite3.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ void *sqlite3_update_hook(sqlite3 *,
void (*)(void *, int, sqlite3_char const *,
sqlite3_char const *, int64_t),
void *);
void *sqlite3_commit_hook(sqlite3 *, int (*)(void *), void *);
void *sqlite3_rollback_hook(sqlite3 *, void (*)(void *), void *);
int sqlite3_get_autocommit(sqlite3 *db);

// Statements
Expand Down
2 changes: 1 addition & 1 deletion sqlite3/assets/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ add_custom_command(
OUTPUT required_symbols.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../
COMMAND dart run tool/wasm_symbols.dart ${CMAKE_CURRENT_BINARY_DIR}/required_symbols.txt
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../tool/wasm_symbols.dart
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../tool/wasm_symbols.dart ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/src/wasm/wasm_interop.dart
VERBATIM
)
add_custom_target(required_symbols DEPENDS required_symbols.txt)
Expand Down
2 changes: 2 additions & 0 deletions sqlite3/assets/wasm/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import_dart("function_hook") extern void dartUpdateHook(void *id, int kind,
const char *db,
const char *table,
sqlite3_int64 rowid);
import_dart("function_commit_hook") extern int dartCommitHook(void *id);
import_dart("function_rollback_hook") extern void dartRollbackHook(void *id);
import_dart("function_compare") extern int dartXCompare(void *id, int lengthA,
const void *a,
int lengthB,
Expand Down
8 changes: 8 additions & 0 deletions sqlite3/assets/wasm/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@ SQLITE_API void dart_sqlite3_updates(sqlite3 *db, int id) {
sqlite3_update_hook(db, id >= 0 ? &dartUpdateHook : NULL, (void *)id);
}

SQLITE_API void dart_sqlite3_commits(sqlite3 *db, int id) {
sqlite3_commit_hook(db, id >= 0 ? &dartCommitHook : NULL, (void *)id);
}

SQLITE_API void dart_sqlite3_rollbacks(sqlite3 *db, int id) {
sqlite3_rollback_hook(db, id >= 0 ? &dartRollbackHook : NULL, (void *)id);
}

SQLITE_API int dart_sqlite3_create_collation(sqlite3 *db, const char *zName,
int eTextRep, int id) {
return sqlite3_create_collation_v2(db, zName, eTextRep, (void *)id,
Expand Down
46 changes: 46 additions & 0 deletions sqlite3/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,52 @@ abstract class CommonDatabase {
/// - [Data Change Notification Callbacks](https://www.sqlite.org/c3ref/update_hook.html)
Stream<SqliteUpdate> get updates;

/// The [VoidPredicate] that is used to filter out transactions before commiting.
///
/// This is run before every commit, i.e. before the end of an explicit
/// transaction and before the end of an implicit transactions created by
/// an insert / update / delete operation.
///
/// If the filter returns `false`, the commit is converted into a rollback.
///
/// The function should not do anything that modifies the database connection,
/// e.g. run SQL statements, prepare statements or step.
///
/// See also:
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
VoidPredicate? get commitFilter;
set commitFilter(VoidPredicate? commitFilter);

/// An async stream that fires after each commit.
///
/// Listening to this stream will register a "commit hook" on the native
/// database. Each commit that sqlite3 reports through that hook will then
/// be added to the stream.
///
/// Note that the stream reports updates _asynchronously_, e.g. one event
/// loop iteration after sqlite reports them.
///
/// Also note this works in conjunction with `commitFilter`. If the filter
/// function is not null and returns `false`, the commit will not occur and
/// this stream will not fire.
///
/// See also:
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
Stream<void> get commits;

/// An async stream that fires after each rollback.
///
/// Listening to this stream will register a "rollback hook" on the native
/// database. Each rollback that sqlite3 reports through that hook will then
/// be added to the stream.
///
/// Note that the stream reports updates _asynchronously_, e.g. one event
/// loop iteration after sqlite reports them.
///
/// See also:
/// - [Commit Hooks](https://www.sqlite.org/c3ref/commit_hook.html)
Stream<void> get rollbacks;

/// Executes the [sql] statement with the provided [parameters], ignoring any
/// rows returned by the statement.
///
Expand Down
56 changes: 56 additions & 0 deletions sqlite3/lib/src/ffi/bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,8 @@ final class FfiDatabase extends RawSqliteDatabase {
final BindingsWithLibrary bindings;
final Pointer<sqlite3> db;
NativeCallable<_UpdateHook>? _installedUpdateHook;
NativeCallable<_CommitHook>? _installedCommitHook;
NativeCallable<_RollbackHook>? _installedRollbackHook;

FfiDatabase(this.bindings, this.db);

Expand Down Expand Up @@ -566,6 +568,37 @@ final class FfiDatabase extends RawSqliteDatabase {
previous?.close();
}

@override
void sqlite3_commit_hook(RawCommitHook? hook) {
final previous = _installedCommitHook;

if (hook == null) {
_installedCommitHook = null;
bindings.bindings.sqlite3_commit_hook(db, nullPtr(), nullPtr());
} else {
final native = _installedCommitHook = hook.toNative();
bindings.bindings
.sqlite3_commit_hook(db, native.nativeFunction, nullPtr());
}

previous?.close();
}

@override
void sqlite3_rollback_hook(RawRollbackHook? hook) {
final previous = _installedRollbackHook;

if (hook == null) {
bindings.bindings.sqlite3_rollback_hook(db, nullPtr(), nullPtr());
} else {
final native = _installedRollbackHook = hook.toNative();
bindings.bindings
.sqlite3_rollback_hook(db, native.nativeFunction, nullPtr());
}

previous?.close();
}

@override
int sqlite3_db_config(int op, int value) {
final result = bindings.bindings.sqlite3_db_config(
Expand Down Expand Up @@ -976,6 +1009,8 @@ typedef _XCompare = Int Function(
Pointer<Void>, Int, Pointer<Void>, Int, Pointer<Void>);
typedef _UpdateHook = Void Function(
Pointer<Void>, Int, Pointer<sqlite3_char>, Pointer<sqlite3_char>, Int64);
typedef _CommitHook = Int Function(Pointer<Void>);
typedef _RollbackHook = Void Function(Pointer<Void>);

extension on RawXFunc {
NativeCallable<_XFunc> toNative(Bindings bindings) {
Expand Down Expand Up @@ -1029,3 +1064,24 @@ extension on RawUpdateHook {
)..keepIsolateAlive = false;
}
}

extension on RawCommitHook {
NativeCallable<_CommitHook> toNative() {
return NativeCallable.isolateLocal(
(Pointer<Void> _) {
return this();
},
exceptionalReturn: 1,
)..keepIsolateAlive = false;
}
}

extension on RawRollbackHook {
NativeCallable<_RollbackHook> toNative() {
return NativeCallable.isolateLocal(
(Pointer<Void> _) {
this();
},
)..keepIsolateAlive = false;
}
}
54 changes: 54 additions & 0 deletions sqlite3/lib/src/ffi/sqlite3.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,60 @@ class Bindings {
ffi.Int64)>>,
ffi.Pointer<ffi.Void>)>();

ffi.Pointer<ffi.Void> sqlite3_commit_hook(
ffi.Pointer<sqlite3> arg0,
ffi.Pointer<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>
arg1,
ffi.Pointer<ffi.Void> arg2,
) {
return _sqlite3_commit_hook(
arg0,
arg1,
arg2,
);
}

late final _sqlite3_commit_hookPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<sqlite3>,
ffi.Pointer<
ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>,
ffi.Pointer<ffi.Void>)>>('sqlite3_commit_hook');
late final _sqlite3_commit_hook = _sqlite3_commit_hookPtr.asFunction<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<sqlite3>,
ffi.Pointer<
ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Void>)>>,
ffi.Pointer<ffi.Void>)>();

ffi.Pointer<ffi.Void> sqlite3_rollback_hook(
ffi.Pointer<sqlite3> arg0,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
arg1,
ffi.Pointer<ffi.Void> arg2,
) {
return _sqlite3_rollback_hook(
arg0,
arg1,
arg2,
);
}

late final _sqlite3_rollback_hookPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<sqlite3>,
ffi.Pointer<
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>,
ffi.Pointer<ffi.Void>)>>('sqlite3_rollback_hook');
late final _sqlite3_rollback_hook = _sqlite3_rollback_hookPtr.asFunction<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<sqlite3>,
ffi.Pointer<
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>,
ffi.Pointer<ffi.Void>)>();

int sqlite3_get_autocommit(
ffi.Pointer<sqlite3> db,
) {
Expand Down
3 changes: 3 additions & 0 deletions sqlite3/lib/src/functions.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import 'package:meta/meta.dart';

/// A filter function without any arguments.
typedef VoidPredicate = bool Function();

/// A collating function provided to a sql collation.
///
/// The function must return a `int`.
Expand Down
6 changes: 6 additions & 0 deletions sqlite3/lib/src/implementation/bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ typedef RawXFunc = void Function(RawSqliteContext, List<RawSqliteValue>);
typedef RawXStep = void Function(RawSqliteContext, List<RawSqliteValue>);
typedef RawXFinal = void Function(RawSqliteContext);
typedef RawUpdateHook = void Function(int kind, String tableName, int rowId);
typedef RawCommitHook = int Function();
typedef RawRollbackHook = void Function();
typedef RawCollation = int Function(String? a, String? b);

abstract base class RawSqliteDatabase {
Expand All @@ -81,6 +83,10 @@ abstract base class RawSqliteDatabase {

void sqlite3_update_hook(RawUpdateHook? hook);

void sqlite3_commit_hook(RawCommitHook? hook);

void sqlite3_rollback_hook(RawRollbackHook? hook);

/// Returns a compiler able to create prepared statements from the utf8-
/// encoded SQL string passed as its argument.
RawStatementCompiler newCompiler(List<int> utf8EncodedSql);
Expand Down
Loading
Loading