mirror of https://github.com/AMT-Cheif/drift.git
New tableUpdates stream method on QueryEngine (#394)
This commit is contained in:
parent
2811d91fa1
commit
3002d87bcb
|
@ -19,6 +19,8 @@
|
||||||
or transaction.
|
or transaction.
|
||||||
- Updated stream queries: They now take triggers into account and more accurately detect when an update
|
- Updated stream queries: They now take triggers into account and more accurately detect when an update
|
||||||
is necessary.
|
is necessary.
|
||||||
|
- New `tableUpdates` method that can be used to listen for a subset of table updates outside of
|
||||||
|
a query.
|
||||||
|
|
||||||
## 2.4.1
|
## 2.4.1
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,23 @@ mixin QueryEngine on DatabaseConnectionUser {
|
||||||
_resolvedEngine.streamQueries.handleTableUpdates(withRulesApplied);
|
_resolvedEngine.streamQueries.handleTableUpdates(withRulesApplied);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a stream that emits `null` each time a table that would affect
|
||||||
|
/// [query] is changed.
|
||||||
|
///
|
||||||
|
/// When called inside a transaction, the stream will close when the
|
||||||
|
/// transaction completes or is rolled back. Otherwise, the stream will
|
||||||
|
/// complete as the database is closed.
|
||||||
|
Stream<Null> tableUpdates(
|
||||||
|
[TableUpdateQuery query = const TableUpdateQuery.any()]) {
|
||||||
|
return _resolvedEngine.streamQueries
|
||||||
|
.updatesForSync(query)
|
||||||
|
.asyncMap((event) async {
|
||||||
|
// streamQueries.updatesForSync is a synchronous stream - make it
|
||||||
|
// asynchronous by awaiting null for each event.
|
||||||
|
return await null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts an [InsertStatement] for a given table. You can use that statement
|
/// Starts an [InsertStatement] for a given table. You can use that statement
|
||||||
/// to write data into the [table] by using [InsertStatement.insert].
|
/// to write data into the [table] by using [InsertStatement.insert].
|
||||||
@protected
|
@protected
|
||||||
|
|
|
@ -93,7 +93,7 @@ class TableUpdate {
|
||||||
|
|
||||||
/// Creates a [TableUpdate] instance based on a [TableInfo] instead of the raw
|
/// Creates a [TableUpdate] instance based on a [TableInfo] instead of the raw
|
||||||
/// name.
|
/// name.
|
||||||
factory TableUpdate.fromTable(TableInfo table, {UpdateKind kind}) {
|
factory TableUpdate.onTable(TableInfo table, {UpdateKind kind}) {
|
||||||
return TableUpdate(table.actualTableName, kind: kind);
|
return TableUpdate(table.actualTableName, kind: kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,21 +126,32 @@ abstract class TableUpdateQuery {
|
||||||
const factory TableUpdateQuery.allOf(List<TableUpdateQuery> queries) =
|
const factory TableUpdateQuery.allOf(List<TableUpdateQuery> queries) =
|
||||||
MultipleUpdateQuery;
|
MultipleUpdateQuery;
|
||||||
|
|
||||||
|
/// A query that listens for all updates on a specific [table] by its name.
|
||||||
|
///
|
||||||
|
/// The optional [limitUpdateKind] parameter can be used to limit the updates
|
||||||
|
/// to a certain kind.
|
||||||
|
const factory TableUpdateQuery.onTableName(String table,
|
||||||
|
{UpdateKind limitUpdateKind}) = SpecificUpdateQuery;
|
||||||
|
|
||||||
/// A query that listens for all updates on a specific [table].
|
/// A query that listens for all updates on a specific [table].
|
||||||
///
|
///
|
||||||
/// The optional [limitUpdateKind] parameter can be used to limit the updates
|
/// The optional [limitUpdateKind] parameter can be used to limit the updates
|
||||||
/// to a certain kind.
|
/// to a certain kind.
|
||||||
const factory TableUpdateQuery.onTable(String table,
|
factory TableUpdateQuery.onTable(TableInfo table,
|
||||||
{UpdateKind limitUpdateKind}) = SpecificUpdateQuery;
|
{UpdateKind limitUpdateKind}) {
|
||||||
|
return TableUpdateQuery.onTableName(
|
||||||
|
table.actualTableName,
|
||||||
|
limitUpdateKind: limitUpdateKind,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// A query that listens for any change on any table in [tables].
|
/// A query that listens for any change on any table in [tables].
|
||||||
factory TableUpdateQuery.onAllTables(Iterable<TableInfo> tables) {
|
factory TableUpdateQuery.onAllTables(Iterable<TableInfo> tables) {
|
||||||
// analyzer bug, remove when Dart 2.8 is stable
|
// analyzer bug, remove when Dart 2.8 is stable
|
||||||
// ignore: prefer_const_constructors
|
// ignore: prefer_const_constructors
|
||||||
return TableUpdateQuery.allOf([
|
return TableUpdateQuery.allOf(
|
||||||
for (final table in tables)
|
[for (final table in tables) TableUpdateQuery.onTable(table)],
|
||||||
TableUpdateQuery.onTable(table.actualTableName)
|
);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines whether the [update] would be picked up by this query.
|
/// Determines whether the [update] would be picked up by this query.
|
||||||
|
|
|
@ -107,7 +107,7 @@ class StreamQueryStore {
|
||||||
return stream.stream;
|
return stream.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Null> _updatesFor(TableUpdateQuery query) {
|
Stream<Null> updatesForSync(TableUpdateQuery query) {
|
||||||
return _tableUpdates.stream
|
return _tableUpdates.stream
|
||||||
.where((e) => e.any(query.matches))
|
.where((e) => e.any(query.matches))
|
||||||
.map((_) => null);
|
.map((_) => null);
|
||||||
|
@ -219,7 +219,7 @@ class QueryStream<T> {
|
||||||
fetchAndEmitData();
|
fetchAndEmitData();
|
||||||
|
|
||||||
_tablesChangedSubscription =
|
_tablesChangedSubscription =
|
||||||
_store._updatesFor(_fetcher.readsFrom).listen((_) {
|
_store.updatesForSync(_fetcher.readsFrom).listen((_) {
|
||||||
// table has changed, invalidate cache
|
// table has changed, invalidate cache
|
||||||
_lastData = null;
|
_lastData = null;
|
||||||
fetchAndEmitData();
|
fetchAndEmitData();
|
||||||
|
|
|
@ -33,7 +33,7 @@ class DeleteStatement<T extends Table, D extends DataClass> extends Query<T, D>
|
||||||
|
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
database.notifyUpdates(
|
database.notifyUpdates(
|
||||||
{TableUpdate.fromTable(table, kind: UpdateKind.delete)});
|
{TableUpdate.onTable(table, kind: UpdateKind.delete)});
|
||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,8 +36,8 @@ class InsertStatement<D extends DataClass> {
|
||||||
|
|
||||||
return await database.executor.doWhenOpened((e) async {
|
return await database.executor.doWhenOpened((e) async {
|
||||||
final id = await database.executor.runInsert(ctx.sql, ctx.boundVariables);
|
final id = await database.executor.runInsert(ctx.sql, ctx.boundVariables);
|
||||||
database.notifyUpdates(
|
database
|
||||||
{TableUpdate.fromTable(table, kind: UpdateKind.insert)});
|
.notifyUpdates({TableUpdate.onTable(table, kind: UpdateKind.insert)});
|
||||||
return id;
|
return id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ class UpdateStatement<T extends Table, D extends DataClass> extends Query<T, D>
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
database.notifyUpdates(
|
database
|
||||||
{TableUpdate.fromTable(table, kind: UpdateKind.update)});
|
.notifyUpdates({TableUpdate.onTable(table, kind: UpdateKind.update)});
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows;
|
return rows;
|
||||||
|
|
|
@ -1276,7 +1276,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules(
|
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules(
|
||||||
[
|
[
|
||||||
WritePropagation(
|
WritePropagation(
|
||||||
on: TableUpdateQuery.onTable('config',
|
on: TableUpdateQuery.onTableName('config',
|
||||||
limitUpdateKind: UpdateKind.insert),
|
limitUpdateKind: UpdateKind.insert),
|
||||||
result: [
|
result: [
|
||||||
TableUpdate('with_defaults', kind: UpdateKind.insert),
|
TableUpdate('with_defaults', kind: UpdateKind.insert),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:moor/moor.dart';
|
import 'package:moor/moor.dart';
|
||||||
|
import 'package:moor/src/runtime/api/runtime_api.dart';
|
||||||
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
import 'package:moor/src/runtime/executor/stream_queries.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
@ -250,4 +251,69 @@ void main() {
|
||||||
|
|
||||||
verify(executor.runSelect(any, any)).called(1);
|
verify(executor.runSelect(any, any)).called(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('listen for table updates', () {
|
||||||
|
test('any', () async {
|
||||||
|
var counter = 0;
|
||||||
|
db.tableUpdates().listen((event) => counter++);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.todosTable});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 1);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.users});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stream is async', () {
|
||||||
|
var counter = 0;
|
||||||
|
db.tableUpdates().listen((event) => counter++);
|
||||||
|
|
||||||
|
db.markTablesUpdated({});
|
||||||
|
// no wait here, the counter should not be updated yet.
|
||||||
|
expect(counter, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('specific table', () async {
|
||||||
|
var counter = 0;
|
||||||
|
db
|
||||||
|
.tableUpdates(TableUpdateQuery.onTable(db.users))
|
||||||
|
.listen((event) => counter++);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.todosTable});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 0);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.users});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 1);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.categories});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('specific table and update kind', () async {
|
||||||
|
var counter = 0;
|
||||||
|
db
|
||||||
|
.tableUpdates(TableUpdateQuery.onTable(db.users,
|
||||||
|
limitUpdateKind: UpdateKind.update))
|
||||||
|
.listen((event) => counter++);
|
||||||
|
|
||||||
|
db.markTablesUpdated({db.todosTable});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 0);
|
||||||
|
|
||||||
|
db.notifyUpdates(
|
||||||
|
{TableUpdate.onTable(db.users, kind: UpdateKind.update)});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 1);
|
||||||
|
|
||||||
|
db.notifyUpdates(
|
||||||
|
{TableUpdate.onTable(db.users, kind: UpdateKind.delete)});
|
||||||
|
await pumpEventQueue(times: 1);
|
||||||
|
expect(counter, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ void main() {
|
||||||
// After the transaction completes, the queries should be updated
|
// After the transaction completes, the queries should be updated
|
||||||
verify(
|
verify(
|
||||||
streamQueries.handleTableUpdates(
|
streamQueries.handleTableUpdates(
|
||||||
{TableUpdate.fromTable(db.users, kind: UpdateKind.update)}),
|
{TableUpdate.onTable(db.users, kind: UpdateKind.update)}),
|
||||||
).called(1);
|
).called(1);
|
||||||
verify(executor.transactions.send());
|
verify(executor.transactions.send());
|
||||||
});
|
});
|
||||||
|
|
|
@ -106,7 +106,7 @@ void main() {
|
||||||
));
|
));
|
||||||
|
|
||||||
verify(streamQueries.handleTableUpdates(
|
verify(streamQueries.handleTableUpdates(
|
||||||
{TableUpdate.fromTable(db.todosTable, kind: UpdateKind.update)}));
|
{TableUpdate.onTable(db.todosTable, kind: UpdateKind.update)}));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('are not issued when no data was changed', () async {
|
test('are not issued when no data was changed', () async {
|
||||||
|
|
|
@ -23,7 +23,7 @@ class FindStreamUpdateRules {
|
||||||
|
|
||||||
rules.add(
|
rules.add(
|
||||||
WritePropagation(
|
WritePropagation(
|
||||||
on: TableUpdateQuery.onTable(
|
on: TableUpdateQuery.onTableName(
|
||||||
trigger.on.sqlName,
|
trigger.on.sqlName,
|
||||||
limitUpdateKind: targetKind,
|
limitUpdateKind: targetKind,
|
||||||
),
|
),
|
||||||
|
|
|
@ -168,7 +168,8 @@ extension on TableUpdateQuery {
|
||||||
buffer.write('TableUpdateQuery.any()');
|
buffer.write('TableUpdateQuery.any()');
|
||||||
} else if (this is SpecificUpdateQuery) {
|
} else if (this is SpecificUpdateQuery) {
|
||||||
final query = this as SpecificUpdateQuery;
|
final query = this as SpecificUpdateQuery;
|
||||||
buffer.write('TableUpdateQuery.onTable(${asDartLiteral(query.table)}, '
|
buffer
|
||||||
|
.write('TableUpdateQuery.onTableName(${asDartLiteral(query.table)}, '
|
||||||
'limitUpdateKind: ${_kindToDartExpr[query.limitUpdateKind]})');
|
'limitUpdateKind: ${_kindToDartExpr[query.limitUpdateKind]})');
|
||||||
} else if (this is MultipleUpdateQuery) {
|
} else if (this is MultipleUpdateQuery) {
|
||||||
final queries = (this as MultipleUpdateQuery).queries;
|
final queries = (this as MultipleUpdateQuery).queries;
|
||||||
|
|
|
@ -42,7 +42,7 @@ class MyDatabase {}
|
||||||
.having(
|
.having(
|
||||||
(e) => e.on,
|
(e) => e.on,
|
||||||
'on',
|
'on',
|
||||||
const TableUpdateQuery.onTable('users',
|
const TableUpdateQuery.onTableName('users',
|
||||||
limitUpdateKind: UpdateKind.insert))
|
limitUpdateKind: UpdateKind.insert))
|
||||||
.having((e) => e.result, 'result', {const TableUpdate('users')}),
|
.having((e) => e.result, 'result', {const TableUpdate('users')}),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue