From b0b9a0ed474ddeaac33bd4c372b1c3fffba98ab7 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 4 Mar 2020 20:59:03 +0100 Subject: [PATCH] Generate more accurate update rules for triggers --- moor/CHANGELOG.md | 2 ++ moor/lib/src/runtime/api/stream_updates.dart | 18 +++++++++-- .../src/runtime/executor/stream_queries.dart | 12 ++++++- .../query_builder/statements/delete.dart | 3 +- .../query_builder/statements/insert.dart | 3 +- moor/test/data/tables/custom_tables.g.dart | 16 +++++++--- moor/test/streams_test.dart | 13 +++++++- moor_generator/lib/src/model/trigger.dart | 2 +- .../services/find_stream_update_rules.dart | 20 ++++++++++-- .../lib/src/writer/database_writer.dart | 32 ++++++------------- .../find_stream_update_rules_test.dart | 6 +++- 11 files changed, 87 insertions(+), 40 deletions(-) diff --git a/moor/CHANGELOG.md b/moor/CHANGELOG.md index bd3bab01..d4d16106 100644 --- a/moor/CHANGELOG.md +++ b/moor/CHANGELOG.md @@ -17,6 +17,8 @@ - Experimentally support IndexedDB to store sqlite data on the web - Moor will no longer wait for query stream listeners to receive a done event when closing a database or transaction. +- Updated stream queries: They now take triggers into account and more accurately detect when an update + is necessary. ## 2.4.1 diff --git a/moor/lib/src/runtime/api/stream_updates.dart b/moor/lib/src/runtime/api/stream_updates.dart index 83fdb1c8..ad2c1497 100644 --- a/moor/lib/src/runtime/api/stream_updates.dart +++ b/moor/lib/src/runtime/api/stream_updates.dart @@ -57,10 +57,11 @@ class WritePropagation extends UpdateRule { final TableUpdateQuery on; /// All updates that will be performed by the trigger listening on [on]. - final Set result; + final List result; /// Default constructor. See [WritePropagation] for details. - const WritePropagation(this.on, this.result) : super._(); + const WritePropagation({@required this.on, @required this.result}) + : super._(); } /// Classifies a [TableUpdate] by what kind of write happened - an insert, an @@ -90,6 +91,12 @@ class TableUpdate { /// Default constant constructor. const TableUpdate(this.table, {this.kind}); + /// Creates a [TableUpdate] instance based on a [TableInfo] instead of the raw + /// name. + factory TableUpdate.fromTable(TableInfo table, {UpdateKind kind}) { + return TableUpdate(table.actualTableName, kind: kind); + } + @override int get hashCode => $mrjf($mrjc(kind.hashCode, table.hashCode)); @@ -97,6 +104,11 @@ class TableUpdate { bool operator ==(dynamic other) { return other is TableUpdate && other.kind == kind && other.table == table; } + + @override + String toString() { + return 'TableUpdate($table, kind: $kind)'; + } } /// A table update query describes information to listen for [TableUpdate]s. @@ -111,7 +123,7 @@ abstract class TableUpdateQuery { const factory TableUpdateQuery.any() = AnyUpdateQuery; /// A query that listens for all updates that match any query in [queries]. - const factory TableUpdateQuery.allOf(Set queries) = + const factory TableUpdateQuery.allOf(List queries) = MultipleUpdateQuery; /// A query that listens for all updates on a specific [table]. diff --git a/moor/lib/src/runtime/executor/stream_queries.dart b/moor/lib/src/runtime/executor/stream_queries.dart index 449ca915..7f8dc642 100644 --- a/moor/lib/src/runtime/executor/stream_queries.dart +++ b/moor/lib/src/runtime/executor/stream_queries.dart @@ -275,7 +275,7 @@ class AnyUpdateQuery extends TableUpdateQuery { } class MultipleUpdateQuery extends TableUpdateQuery { - final Set queries; + final List queries; const MultipleUpdateQuery(this.queries); @@ -297,4 +297,14 @@ class SpecificUpdateQuery extends TableUpdateQuery { limitUpdateKind == null || update.kind == limitUpdateKind; } + + @override + int get hashCode => $mrjf($mrjc(limitUpdateKind.hashCode, table.hashCode)); + + @override + bool operator ==(dynamic other) { + return other is SpecificUpdateQuery && + other.limitUpdateKind == limitUpdateKind && + other.table == table; + } } diff --git a/moor/lib/src/runtime/query_builder/statements/delete.dart b/moor/lib/src/runtime/query_builder/statements/delete.dart index f3d2e035..88232684 100644 --- a/moor/lib/src/runtime/query_builder/statements/delete.dart +++ b/moor/lib/src/runtime/query_builder/statements/delete.dart @@ -32,7 +32,8 @@ class DeleteStatement extends Query final rows = await ctx.executor.runDelete(ctx.sql, ctx.boundVariables); if (rows > 0) { - database.markTablesUpdated({table}); + database.notifyUpdates( + {TableUpdate.fromTable(table, kind: UpdateKind.delete)}); } return rows; }); diff --git a/moor/lib/src/runtime/query_builder/statements/insert.dart b/moor/lib/src/runtime/query_builder/statements/insert.dart index a59d69aa..dfcab195 100644 --- a/moor/lib/src/runtime/query_builder/statements/insert.dart +++ b/moor/lib/src/runtime/query_builder/statements/insert.dart @@ -36,7 +36,8 @@ class InsertStatement { return await database.executor.doWhenOpened((e) async { final id = await database.executor.runInsert(ctx.sql, ctx.boundVariables); - database.markTablesUpdated({table}); + database.notifyUpdates( + {TableUpdate.fromTable(table, kind: UpdateKind.insert)}); return id; }); } diff --git a/moor/test/data/tables/custom_tables.g.dart b/moor/test/data/tables/custom_tables.g.dart index 8eed5d2f..d6e7b06b 100644 --- a/moor/test/data/tables/custom_tables.g.dart +++ b/moor/test/data/tables/custom_tables.g.dart @@ -1273,11 +1273,17 @@ abstract class _$CustomTablesDb extends GeneratedDatabase { email ]; @override - StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ - WritePropagation( - TableUpdateQuery.onTable('config', limitUpdateKind: null), - {TableUpdate('with_defaults', kind: null)}) - ]); + StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules( + [ + WritePropagation( + on: TableUpdateQuery.onTable('config', + limitUpdateKind: UpdateKind.insert), + result: [ + TableUpdate('with_defaults', kind: null), + ], + ), + ], + ); } class TableValuedResult { diff --git a/moor/test/streams_test.dart b/moor/test/streams_test.dart index f177c24a..552f1a58 100644 --- a/moor/test/streams_test.dart +++ b/moor/test/streams_test.dart @@ -230,13 +230,24 @@ void main() { }); }); + // note: There's a trigger on config inserts that updates with_defaults test('updates streams for updates caused by triggers', () async { final db = CustomTablesDb(executor); db.select(db.withDefaults).watch().listen((_) {}); - db.markTablesUpdated({db.config}); + db.notifyUpdates({const TableUpdate('config', kind: UpdateKind.insert)}); await pumpEventQueue(times: 1); verify(executor.runSelect(any, any)).called(2); }); + + test('limits trigger propagation to the target type of trigger', () async { + final db = CustomTablesDb(executor); + db.select(db.withDefaults).watch().listen((_) {}); + + db.notifyUpdates({const TableUpdate('config', kind: UpdateKind.delete)}); + await pumpEventQueue(times: 1); + + verify(executor.runSelect(any, any)).called(1); + }); } diff --git a/moor_generator/lib/src/model/trigger.dart b/moor_generator/lib/src/model/trigger.dart index 28b37c53..70fa5ebe 100644 --- a/moor_generator/lib/src/model/trigger.dart +++ b/moor_generator/lib/src/model/trigger.dart @@ -8,7 +8,7 @@ class MoorTrigger implements MoorSchemaEntity { final String displayName; @override - final TriggerDeclaration declaration; + final MoorTriggerDeclaration declaration; /// The table on which this trigger operates. /// diff --git a/moor_generator/lib/src/services/find_stream_update_rules.dart b/moor_generator/lib/src/services/find_stream_update_rules.dart index 70676e4a..5c4e49f0 100644 --- a/moor_generator/lib/src/services/find_stream_update_rules.dart +++ b/moor_generator/lib/src/services/find_stream_update_rules.dart @@ -1,5 +1,6 @@ import 'package:moor/moor.dart'; import 'package:moor_generator/moor_generator.dart'; +import 'package:sqlparser/sqlparser.dart'; class FindStreamUpdateRules { final Database db; @@ -10,13 +11,26 @@ class FindStreamUpdateRules { final rules = []; for (final trigger in db.entities.whereType()) { + final target = trigger.declaration.node.target; + UpdateKind targetKind; + if (target is DeleteTarget) { + targetKind = UpdateKind.delete; + } else if (target is InsertTarget) { + targetKind = UpdateKind.insert; + } else { + targetKind = UpdateKind.update; + } + rules.add( WritePropagation( - TableUpdateQuery.onTable(trigger.on.sqlName), - { + on: TableUpdateQuery.onTable( + trigger.on.sqlName, + limitUpdateKind: targetKind, + ), + result: [ for (final update in trigger.bodyUpdates) TableUpdate(update.sqlName) - }, + ], ), ); } diff --git a/moor_generator/lib/src/writer/database_writer.dart b/moor_generator/lib/src/writer/database_writer.dart index ef7c6a01..6081d928 100644 --- a/moor_generator/lib/src/writer/database_writer.dart +++ b/moor_generator/lib/src/writer/database_writer.dart @@ -116,16 +116,12 @@ class DatabaseWriter { ..write('@override\nStreamQueryUpdateRules get streamUpdateRules => ') ..write('const StreamQueryUpdateRules(['); - var isFirst = true; for (final rule in updateRules.rules) { - if (!isFirst) { - schemaScope.write(', '); - } - isFirst = false; rule.writeConstructor(schemaScope); + schemaScope.write(', '); } - schemaScope.write(']);\n'); + schemaScope.write('],);\n'); } // close the class @@ -145,21 +141,16 @@ extension on UpdateRule { if (this is WritePropagation) { final write = this as WritePropagation; - buffer.write('WritePropagation('); + buffer.write('WritePropagation(on: '); write.on.writeConstructor(buffer); - buffer.write(', {'); + buffer.write(', result: ['); - var isFirst = true; for (final update in write.result) { - if (!isFirst) { - buffer.write(', '); - } - isFirst = false; - update.writeConstructor(buffer); + buffer.write(', '); } - buffer.write('})'); + buffer.write('],)'); } } } @@ -181,18 +172,13 @@ extension on TableUpdateQuery { 'limitUpdateKind: ${_kindToDartExpr[query.limitUpdateKind]})'); } else if (this is MultipleUpdateQuery) { final queries = (this as MultipleUpdateQuery).queries; - var isFirst = true; - buffer.write('TableUpdateQuery.allOf({'); + buffer.write('TableUpdateQuery.allOf(['); for (final query in queries) { - if (!isFirst) { - buffer.write(', '); - } - isFirst = false; - query.writeConstructor(buffer); + buffer.write(', '); } - buffer.write('})'); + buffer.write('])'); } } } diff --git a/moor_generator/test/services/find_stream_update_rules_test.dart b/moor_generator/test/services/find_stream_update_rules_test.dart index ec72ce6c..abc4c62c 100644 --- a/moor_generator/test/services/find_stream_update_rules_test.dart +++ b/moor_generator/test/services/find_stream_update_rules_test.dart @@ -39,7 +39,11 @@ class MyDatabase {} expect( rules.rules.single, isA() - .having((e) => e.on, 'on', const TableUpdateQuery.onTable('users')) + .having( + (e) => e.on, + 'on', + const TableUpdateQuery.onTable('users', + limitUpdateKind: UpdateKind.insert)) .having((e) => e.result, 'result', {const TableUpdate('users')}), ); });