mirror of https://github.com/AMT-Cheif/drift.git
Generate more accurate update rules for triggers
This commit is contained in:
parent
0b0d5792fd
commit
b0b9a0ed47
|
@ -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
|
||||
|
||||
|
|
|
@ -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<TableUpdate> result;
|
||||
final List<TableUpdate> 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<TableUpdateQuery> queries) =
|
||||
const factory TableUpdateQuery.allOf(List<TableUpdateQuery> queries) =
|
||||
MultipleUpdateQuery;
|
||||
|
||||
/// A query that listens for all updates on a specific [table].
|
||||
|
|
|
@ -275,7 +275,7 @@ class AnyUpdateQuery extends TableUpdateQuery {
|
|||
}
|
||||
|
||||
class MultipleUpdateQuery extends TableUpdateQuery {
|
||||
final Set<TableUpdateQuery> queries;
|
||||
final List<TableUpdateQuery> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ class DeleteStatement<T extends Table, D extends DataClass> extends Query<T, D>
|
|||
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;
|
||||
});
|
||||
|
|
|
@ -36,7 +36,8 @@ class InsertStatement<D extends DataClass> {
|
|||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1273,11 +1273,17 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
|||
email
|
||||
];
|
||||
@override
|
||||
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([
|
||||
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules(
|
||||
[
|
||||
WritePropagation(
|
||||
TableUpdateQuery.onTable('config', limitUpdateKind: null),
|
||||
{TableUpdate('with_defaults', kind: null)})
|
||||
]);
|
||||
on: TableUpdateQuery.onTable('config',
|
||||
limitUpdateKind: UpdateKind.insert),
|
||||
result: [
|
||||
TableUpdate('with_defaults', kind: null),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class TableValuedResult {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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 = <UpdateRule>[];
|
||||
|
||||
for (final trigger in db.entities.whereType<MoorTrigger>()) {
|
||||
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)
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
rule.writeConstructor(schemaScope);
|
||||
schemaScope.write(', ');
|
||||
}
|
||||
isFirst = false;
|
||||
rule.writeConstructor(schemaScope);
|
||||
}
|
||||
|
||||
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) {
|
||||
update.writeConstructor(buffer);
|
||||
buffer.write(', ');
|
||||
}
|
||||
isFirst = false;
|
||||
|
||||
update.writeConstructor(buffer);
|
||||
}
|
||||
|
||||
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) {
|
||||
query.writeConstructor(buffer);
|
||||
buffer.write(', ');
|
||||
}
|
||||
isFirst = false;
|
||||
|
||||
query.writeConstructor(buffer);
|
||||
}
|
||||
buffer.write('})');
|
||||
buffer.write('])');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@ class MyDatabase {}
|
|||
expect(
|
||||
rules.rules.single,
|
||||
isA<WritePropagation>()
|
||||
.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')}),
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue