mirror of
https://github.com/immich-app/drift.git
synced 2026-09-30 13:22:57 +08:00
Expose similar interface to computeWithDatabase on web (#3672)
This commit is contained in:
@@ -266,75 +266,6 @@ extension ComputeWithDriftIsolate<DB extends DatabaseConnectionUser> on DB {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns a short-lived isolate to run the [computation] with a drift
|
||||
/// database.
|
||||
///
|
||||
/// Essentially, this is a variant of [Isolate.run] for computations that also
|
||||
/// need to share a drift database between them. As drift databases are
|
||||
/// stateful objects, they can't be send across isolates (and thus used in
|
||||
/// [Isolate.run] or Flutter's `compute`) without special setup.
|
||||
///
|
||||
/// This method will extract the underlying database connection of `this`
|
||||
/// database into a form that can be serialized across isolates. Then,
|
||||
/// [Isolate.run] will be called to invoke [computation]. The [connect]
|
||||
/// function is responsible for creating an instance of your database class
|
||||
/// from the low-level connection.
|
||||
///
|
||||
/// As an example, consider a database class:
|
||||
///
|
||||
/// ```dart
|
||||
/// class MyDatabase extends $MyDatabase {
|
||||
/// MyDatabase(QueryExecutor executor): super(executor);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [computeWithDatabase] can then be used to access an instance of
|
||||
/// `MyDatabase` in a new isolate, even though `MyDatabase` is not generally
|
||||
/// sharable between isolates:
|
||||
///
|
||||
/// ```dart
|
||||
/// Future<void> loadBulkData(MyDatabase db) async {
|
||||
/// await db.computeWithDatabase(
|
||||
/// connect: MyDatabase.new,
|
||||
/// computation: (db) async {
|
||||
/// // This computation has access to a second `db` that is internally
|
||||
/// // linked to the original database.
|
||||
/// final data = await fetchRowsFromNetwork();
|
||||
/// await db.batch((batch) {
|
||||
/// // More expensive work like inserting data
|
||||
/// });
|
||||
/// },
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that with the recommended setup of `NativeDatabase.createInBackground`,
|
||||
/// drift will already use an isolate to run your SQL statements. Using
|
||||
/// [computeWithDatabase] is beneficial when an an expensive work unit needs
|
||||
/// to use the database, or when creating the SQL statements itself is
|
||||
/// expensive.
|
||||
/// In particular, note that [computeWithDatabase] does not create a second
|
||||
/// database connection to sqlite3 - the current one is re-used. So if you're
|
||||
/// using a synchronous database connection, using this method is unlikely to
|
||||
/// take significant loads off the main isolate. For that reason, the use of
|
||||
/// `NativeDatabase.createInBackground` is encouraged.
|
||||
@experimental
|
||||
Future<Ret> computeWithDatabase<Ret>({
|
||||
required FutureOr<Ret> Function(DB) computation,
|
||||
required DB Function(DatabaseConnection) connect,
|
||||
}) async {
|
||||
final connection = await serializableConnection();
|
||||
|
||||
return await Isolate.run(() async {
|
||||
final database = connect(await connection.connect());
|
||||
try {
|
||||
return await computation(database);
|
||||
} finally {
|
||||
await database.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [RunningDriftServer] and sends a [SendPort] that can be used to
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:drift/isolate.dart';
|
||||
import 'package:drift/src/runtime/api/runtime_api.dart';
|
||||
|
||||
/// Spawns a short-lived isolate to run the [computation] with a drift
|
||||
/// database.
|
||||
Future<Ret>
|
||||
computeWithDatabaseImplementation<Ret, DB extends GeneratedDatabase>({
|
||||
required FutureOr<Ret> Function(DB) computation,
|
||||
required DB Function(DatabaseConnection) connect,
|
||||
required DB database,
|
||||
}) async {
|
||||
final connection = await database.serializableConnection();
|
||||
|
||||
return await Isolate.run(() async {
|
||||
final database = connect(await connection.connect());
|
||||
try {
|
||||
return await computation(database);
|
||||
} finally {
|
||||
await database.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/src/runtime/api/runtime_api.dart';
|
||||
|
||||
/// This will directly execute the passed computation as a future with the
|
||||
/// current Database.
|
||||
Future<Ret>
|
||||
computeWithDatabaseImplementation<Ret, DB extends GeneratedDatabase>({
|
||||
required FutureOr<Ret> Function(DB) computation,
|
||||
required DB Function(DatabaseConnection) connect,
|
||||
required DB database,
|
||||
}) async =>
|
||||
await computation(database);
|
||||
@@ -151,4 +151,64 @@ abstract class GeneratedDatabase extends DatabaseConnectionUser
|
||||
return true;
|
||||
}());
|
||||
}
|
||||
|
||||
/// On native platforms this spawns a short-lived isolate to run the [computation] with a drift
|
||||
/// database.
|
||||
/// On web platforms, this will run the [computation] on the current JavaScript context.
|
||||
///
|
||||
/// Essentially, this is a variant of [Isolate.run] for computations that also
|
||||
/// need to share a drift database between them. As drift databases are
|
||||
/// stateful objects, they can't be send across isolates (and thus used in
|
||||
/// [Isolate.run] or Flutter's `compute`) without special setup.
|
||||
///
|
||||
/// This method will extract the underlying database connection of `this`
|
||||
/// database into a form that can be serialized across isolates. Then,
|
||||
/// [Isolate.run] will be called to invoke [computation]. The [connect]
|
||||
/// function is responsible for creating an instance of your database class
|
||||
/// from the low-level connection.
|
||||
///
|
||||
/// As an example, consider a database class:
|
||||
///
|
||||
/// ```dart
|
||||
/// class MyDatabase extends $MyDatabase {
|
||||
/// MyDatabase(QueryExecutor executor): super(executor);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [computeWithDatabase] can then be used to access an instance of
|
||||
/// `MyDatabase` in a new isolate, even though `MyDatabase` is not generally
|
||||
/// sharable between isolates:
|
||||
///
|
||||
/// ```dart
|
||||
/// Future<void> loadBulkData(MyDatabase db) async {
|
||||
/// await db.computeWithDatabase(
|
||||
/// connect: MyDatabase.new,
|
||||
/// computation: (db) async {
|
||||
/// // This computation has access to a second `db` that is internally
|
||||
/// // linked to the original database.
|
||||
/// final data = await fetchRowsFromNetwork();
|
||||
/// await db.batch((batch) {
|
||||
/// // More expensive work like inserting data
|
||||
/// });
|
||||
/// },
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that with the recommended setup of `NativeDatabase.createInBackground`,
|
||||
/// drift will already use an isolate to run your SQL statements. Using
|
||||
/// [computeWithDatabase] is beneficial when an an expensive work unit needs
|
||||
/// to use the database, or when creating the SQL statements itself is
|
||||
/// expensive.
|
||||
/// In particular, note that [computeWithDatabase] does not create a second
|
||||
/// database connection to sqlite3 - the current one is re-used. So if you're
|
||||
/// using a synchronous database connection, using this method is unlikely to
|
||||
/// take significant loads off the main isolate. For that reason, the use of
|
||||
/// `NativeDatabase.createInBackground` is encouraged.
|
||||
Future<Ret> computeWithDatabase<Ret, DB extends GeneratedDatabase>({
|
||||
required FutureOr<Ret> Function(DB) computation,
|
||||
required DB Function(DatabaseConnection) connect,
|
||||
}) =>
|
||||
computeWithDatabaseImplementation(
|
||||
computation: computation, connect: connect, database: this as DB);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import 'package:meta/meta.dart';
|
||||
|
||||
import '../devtools/devtools.dart' as devtools;
|
||||
import '../query_builder/query_builder.dart';
|
||||
import 'compute_with_database_implementation/compute_with_database_native.dart'
|
||||
if (dart.library.js_interop) 'compute_with_database_implementation/compute_with_database_unsupported.dart';
|
||||
|
||||
part 'batch.dart';
|
||||
part 'connection.dart';
|
||||
|
||||
@@ -315,7 +315,7 @@ class _FakeMigrator_26 extends _i1.SmartFake implements _i2.Migrator {
|
||||
);
|
||||
}
|
||||
|
||||
class _FakeFuture_27<T1> extends _i1.SmartFake implements _i5.Future<T1> {
|
||||
class _FakeFuture_27<T> extends _i1.SmartFake implements _i5.Future<T> {
|
||||
_FakeFuture_27(
|
||||
Object parent,
|
||||
Invocation parentInvocation,
|
||||
@@ -965,6 +965,72 @@ class MockTodoDb extends _i1.Mock implements _i3.TodoDb {
|
||||
returnValueForMissingStub: _i5.Future<void>.value(),
|
||||
) as _i5.Future<void>);
|
||||
|
||||
@override
|
||||
_i5.Future<Ret> computeWithDatabase<Ret, DB extends _i2.GeneratedDatabase>({
|
||||
required _i5.FutureOr<Ret> Function(DB)? computation,
|
||||
required DB Function(_i2.DatabaseConnection)? connect,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#computeWithDatabase,
|
||||
[],
|
||||
{
|
||||
#computation: computation,
|
||||
#connect: connect,
|
||||
},
|
||||
),
|
||||
returnValue: _i6.ifNotNull(
|
||||
_i6.dummyValueOrNull<Ret>(
|
||||
this,
|
||||
Invocation.method(
|
||||
#computeWithDatabase,
|
||||
[],
|
||||
{
|
||||
#computation: computation,
|
||||
#connect: connect,
|
||||
},
|
||||
),
|
||||
),
|
||||
(Ret v) => _i5.Future<Ret>.value(v),
|
||||
) ??
|
||||
_FakeFuture_27<Ret>(
|
||||
this,
|
||||
Invocation.method(
|
||||
#computeWithDatabase,
|
||||
[],
|
||||
{
|
||||
#computation: computation,
|
||||
#connect: connect,
|
||||
},
|
||||
),
|
||||
),
|
||||
returnValueForMissingStub: _i6.ifNotNull(
|
||||
_i6.dummyValueOrNull<Ret>(
|
||||
this,
|
||||
Invocation.method(
|
||||
#computeWithDatabase,
|
||||
[],
|
||||
{
|
||||
#computation: computation,
|
||||
#connect: connect,
|
||||
},
|
||||
),
|
||||
),
|
||||
(Ret v) => _i5.Future<Ret>.value(v),
|
||||
) ??
|
||||
_FakeFuture_27<Ret>(
|
||||
this,
|
||||
Invocation.method(
|
||||
#computeWithDatabase,
|
||||
[],
|
||||
{
|
||||
#computation: computation,
|
||||
#connect: connect,
|
||||
},
|
||||
),
|
||||
),
|
||||
) as _i5.Future<Ret>);
|
||||
|
||||
@override
|
||||
_i5.Stream<T> createStream<T extends Object>(
|
||||
_i4.QueryStreamFetcher<T>? stmt) =>
|
||||
|
||||
Reference in New Issue
Block a user