Write update kind for compiled update and delete queries

This commit is contained in:
Simon Binder 2020-03-04 21:28:08 +01:00
parent b0b9a0ed47
commit b823f534c8
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
13 changed files with 60 additions and 21 deletions

View File

@ -176,16 +176,23 @@ mixin QueryEngine on DatabaseConnectionUser {
/// rows that have been changed.
/// You can use the [updates] parameter so that moor knows which tables are
/// affected by your query. All select streams that depend on a table
/// specified there will then issue another query.
/// specified there will then update their data. For more accurate results,
/// you can also set the [updateKind] parameter to [UpdateKind.delete] or
/// [UpdateKind.update]. This is optional, but can improve the accuracy of
/// query updates, especially when using triggers.
@protected
@visibleForTesting
Future<int> customUpdate(String query,
{List<Variable> variables = const [], Set<TableInfo> updates}) async {
Future<int> customUpdate(
String query, {
List<Variable> variables = const [],
Set<TableInfo> updates,
UpdateKind updateKind,
}) async {
return _customWrite(
query,
variables,
updates,
null, // could be delete or update, so don't specify kind
updateKind,
(executor, sql, vars) {
return executor.runUpdate(sql, vars);
},

View File

@ -1279,7 +1279,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
on: TableUpdateQuery.onTable('config',
limitUpdateKind: UpdateKind.insert),
result: [
TableUpdate('with_defaults', kind: null),
TableUpdate('with_defaults', kind: UpdateKind.insert),
],
),
],

View File

@ -1343,6 +1343,7 @@ abstract class _$TodoDb extends GeneratedDatabase {
'DELETE FROM todos WHERE id = ?',
variables: [Variable.withInt(var1)],
updates: {todosTable},
updateKind: UpdateKind.delete,
);
}

View File

@ -73,10 +73,10 @@ class EntityHandler extends BaseAnalyzer {
return tablesFinder.foundTables.map(mapper.tableToMoor);
}
Iterable<MoorTable> _findUpdatedTables(AstNode node) {
Iterable<WrittenMoorTable> _findUpdatedTables(AstNode node) {
final finder = UpdatedTablesVisitor();
node.acceptWithoutArg(finder);
return finder.writtenTables.map(mapper.tableToMoor);
return finder.writtenTables.map(mapper.writtenToMoor);
}
AstNode _handleMoorDeclaration<T extends MoorDeclaration>(

View File

@ -1,3 +1,4 @@
import 'package:moor/moor.dart' show UpdateKind;
import 'package:sqlparser/sqlparser.dart';
/// An AST-visitor that walks sql statements and finds all tables referenced in
@ -39,6 +40,13 @@ class ReferencedTablesVisitor extends RecursiveVisitor<void, void> {
}
}
class WrittenTable {
final Table table;
final UpdateKind kind;
WrittenTable(this.table, this.kind);
}
/// Finds all tables that could be affected when executing a query. In
/// contrast to [ReferencedTablesVisitor], which finds all references, this
/// visitor only collects tables a query writes to.
@ -48,30 +56,30 @@ class UpdatedTablesVisitor extends ReferencedTablesVisitor {
/// Note that this is a subset of [foundTables], since an updating tables
/// could reference tables it's not updating (e.g. with `INSERT INTO foo
/// SELECT * FROM bar`).
final Set<Table> writtenTables = {};
final Set<WrittenTable> writtenTables = {};
void _addIfResolved(ResolvesToResultSet r) {
void _addIfResolved(ResolvesToResultSet r, UpdateKind kind) {
final resolved = _toTableOrNull(r);
if (resolved != null) {
writtenTables.add(resolved);
writtenTables.add(WrittenTable(resolved, kind));
}
}
@override
void visitDeleteStatement(DeleteStatement e, void arg) {
_addIfResolved(e.from);
_addIfResolved(e.from, UpdateKind.delete);
visitChildren(e, arg);
}
@override
void visitUpdateStatement(UpdateStatement e, void arg) {
_addIfResolved(e.table);
_addIfResolved(e.table, UpdateKind.update);
visitChildren(e, arg);
}
@override
void visitInsertStatement(InsertStatement e, void arg) {
_addIfResolved(e.table);
_addIfResolved(e.table, UpdateKind.insert);
visitChildren(e, arg);
}
}

View File

@ -54,7 +54,7 @@ class QueryHandler {
UpdatingQuery _handleUpdate() {
final updatedFinder = UpdatedTablesVisitor();
context.root.acceptWithoutArg(updatedFinder);
_foundTables = updatedFinder.writtenTables;
_foundTables = updatedFinder.writtenTables.map((w) => w.table).toSet();
final isInsert = context.root is InsertStatement;
@ -62,7 +62,7 @@ class QueryHandler {
name,
context,
_foundElements,
_foundTables.map(mapper.tableToMoor).toList(),
updatedFinder.writtenTables.map(mapper.writtenToMoor).toList(),
isInsert: isInsert,
hasMultipleTables: updatedFinder.foundTables.length > 1,
);

View File

@ -1,4 +1,5 @@
import 'package:moor_generator/moor_generator.dart';
import 'package:moor_generator/src/analyzer/sql_queries/affected_tables_visitor.dart';
import 'package:moor_generator/src/model/sql_query.dart';
import 'package:moor_generator/src/utils/type_converter_hint.dart';
import 'package:sqlparser/sqlparser.dart';
@ -222,4 +223,8 @@ class TypeMapper {
MoorTable tableToMoor(Table table) {
return _engineTablesToSpecified[table];
}
WrittenMoorTable writtenToMoor(WrittenTable table) {
return WrittenMoorTable(tableToMoor(table.table), table.kind);
}
}

View File

@ -1,3 +1,4 @@
import 'package:moor/moor.dart' show UpdateKind;
import 'package:moor_generator/src/analyzer/runner/results.dart';
import 'package:moor_generator/src/utils/hash.dart';
import 'package:recase/recase.dart';
@ -129,9 +130,12 @@ class SqlSelectQuery extends SqlQuery {
}
class UpdatingQuery extends SqlQuery {
final List<MoorTable> updates;
final List<WrittenMoorTable> updates;
final bool isInsert;
bool get isOnlyDelete => updates.every((w) => w.kind == UpdateKind.delete);
bool get isOnlyUpdate => updates.every((w) => w.kind == UpdateKind.update);
UpdatingQuery(String name, AnalysisContext fromContext,
List<FoundElement> elements, this.updates,
{this.isInsert = false, bool hasMultipleTables})

View File

@ -1,4 +1,5 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:moor/moor.dart' show UpdateKind;
import 'package:moor_generator/src/analyzer/options.dart';
import 'package:moor_generator/src/model/used_type_converter.dart';
import 'package:recase/recase.dart';
@ -152,6 +153,13 @@ class MoorTable implements MoorSchemaEntity {
}
}
class WrittenMoorTable {
final MoorTable table;
final UpdateKind kind;
WrittenMoorTable(this.table, this.kind);
}
String dbFieldName(String className) => ReCase(className).camelCase;
String tableInfoNameForTableClass(String className) => '\$${className}Table';

View File

@ -14,7 +14,7 @@ class MoorTrigger implements MoorSchemaEntity {
///
/// This field can be null in case the table wasn't resolved.
MoorTable on;
List<MoorTable> bodyUpdates = [];
List<WrittenMoorTable> bodyUpdates = [];
List<MoorTable> bodyReferences = [];
String _create;
@ -42,7 +42,7 @@ class MoorTrigger implements MoorSchemaEntity {
String get create {
if (_create != null) return _create;
final node = (declaration as MoorTriggerDeclaration).node;
final node = declaration.node;
return node.span.text;
}

View File

@ -29,7 +29,7 @@ class FindStreamUpdateRules {
),
result: [
for (final update in trigger.bodyUpdates)
TableUpdate(update.sqlName)
TableUpdate(update.table.sqlName, kind: update.kind)
],
),
);

View File

@ -205,6 +205,12 @@ class QueryWriter {
_buffer.write(',');
_writeUpdates();
if (_update.isOnlyDelete) {
_buffer.write(', updateKind: UpdateKind.delete');
} else if (_update.isOnlyUpdate) {
_buffer.write(', updateKind: UpdateKind.update');
}
_buffer.write(',);\n}\n');
}
@ -399,7 +405,7 @@ class QueryWriter {
}
void _writeUpdates() {
final from = _update.updates.map((t) => t.dbGetterName).join(', ');
final from = _update.updates.map((t) => t.table.dbGetterName).join(', ');
_buffer..write('updates: {')..write(from)..write('}');
}
}

View File

@ -80,7 +80,7 @@ END;
expect(trigger.bodyReferences.map((t) => t.sqlName),
{'users', 'friendships'});
expect(trigger.bodyUpdates.map((t) => t.sqlName), {'friendships'});
expect(trigger.bodyUpdates.map((t) => t.table.sqlName), {'friendships'});
});
test('in an index', () async {