mirror of https://github.com/AMT-Cheif/drift.git
Add common methods as extension on table (#1613)
This commit is contained in:
parent
7bfa00e8b4
commit
672bb930c8
|
@ -1,3 +1,11 @@
|
|||
## 1.4.0
|
||||
|
||||
- Most methods to compose statements are now available as an extension on
|
||||
tables. As an alternative to `update(todos).replace(newEntry)`, you can
|
||||
now write `todos.replaceOne(newEntry)`.
|
||||
- Deprecate the `from(table)` API introduced in 1.3.0. Having the methods on
|
||||
the table instances turned out to be even easier!
|
||||
|
||||
## 1.3.0
|
||||
|
||||
- Add the `from(table)` method to generated databases. It can be used to write
|
||||
|
|
|
@ -535,10 +535,11 @@ class $TodoCategoryItemCountView
|
|||
extends ViewInfo<$TodoCategoryItemCountView, TodoCategoryItemCountData>
|
||||
implements HasResultSet {
|
||||
final String? _alias;
|
||||
$TodoCategoryItemCountView(DatabaseConnectionUser db, [this._alias])
|
||||
: super(db);
|
||||
$TodoItemsTable get todoItems => _db.todoItems;
|
||||
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
||||
@override
|
||||
final _$Database attachedDatabase;
|
||||
$TodoCategoryItemCountView(this.attachedDatabase, [this._alias]);
|
||||
$TodoItemsTable get todoItems => attachedDatabase.todoItems;
|
||||
$TodoCategoriesTable get todoCategories => attachedDatabase.todoCategories;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todoCategories.name, itemCount];
|
||||
@override
|
||||
|
@ -643,10 +644,11 @@ class $TodoItemWithCategoryNameViewView extends ViewInfo<
|
|||
$TodoItemWithCategoryNameViewView,
|
||||
TodoItemWithCategoryNameViewData> implements HasResultSet {
|
||||
final String? _alias;
|
||||
$TodoItemWithCategoryNameViewView(DatabaseConnectionUser db, [this._alias])
|
||||
: super(db);
|
||||
$TodoItemsTable get todoItems => _db.todoItems;
|
||||
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
||||
@override
|
||||
final _$Database attachedDatabase;
|
||||
$TodoItemWithCategoryNameViewView(this.attachedDatabase, [this._alias]);
|
||||
$TodoItemsTable get todoItems => attachedDatabase.todoItems;
|
||||
$TodoCategoriesTable get todoCategories => attachedDatabase.todoCategories;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todoItems.id, title];
|
||||
@override
|
||||
|
|
|
@ -159,7 +159,11 @@ abstract class DatabaseConnectionUser {
|
|||
/// The [TableOrViewOperations] class (or the [TableOperations] extension
|
||||
/// for tables) provides convenience methods that make common operations
|
||||
/// easier to write than using the methods from this class directly.
|
||||
@experimental
|
||||
///
|
||||
/// This method is deprecated. For an even easier access, the methods that
|
||||
/// were made available here are now available on the [table] or view instance
|
||||
/// directly.
|
||||
@Deprecated('Experiment ended - use the methods on the [table] direclty')
|
||||
TableOrViewOperations<T, D> from<T extends HasResultSet, D>(
|
||||
ResultSetImplementation<T, D> table) {
|
||||
return TableOrViewOperations._(this, table);
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
import 'package:drift/drift.dart';
|
||||
|
||||
/// Easily-accessible methods to compose common operations or statements on
|
||||
/// tables or views.
|
||||
extension TableOrViewStatements<Tbl extends HasResultSet, Row>
|
||||
on ResultSetImplementation<Tbl, Row> {
|
||||
/// Composes a `SELECT` statement on the captured table or view.
|
||||
///
|
||||
/// This is equivalent to calling [DatabaseConnectionUser.select].
|
||||
SimpleSelectStatement<Tbl, Row> select({bool distinct = false}) {
|
||||
return attachedDatabase.select(this, distinct: distinct);
|
||||
}
|
||||
|
||||
/// Composes a `SELECT` statement only selecting a subset of columns.
|
||||
///
|
||||
/// This is equivalent to calling [DatabaseConnectionUser.selectOnly].
|
||||
JoinedSelectStatement<Tbl, Row> selectOnly(
|
||||
{bool distinct = false, bool includeJoinedTableColumns = true}) {
|
||||
return attachedDatabase.selectOnly(this,
|
||||
distinct: distinct,
|
||||
includeJoinedTableColumns: includeJoinedTableColumns);
|
||||
}
|
||||
}
|
||||
|
||||
/// Easily-accessible methods to compose common operations or statements on
|
||||
/// tables.
|
||||
extension TableStatements<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
|
||||
/// Creates an insert statment to be used to compose an insert on the table.
|
||||
///
|
||||
/// This is equivalent to calling [DatabaseConnectionUser.into] on the
|
||||
/// captured table. See that method for more information.
|
||||
InsertStatement<Tbl, Row> insert() => attachedDatabase.into(this);
|
||||
|
||||
/// Inserts one row into this table.
|
||||
///
|
||||
/// This is equivalent to calling [InsertStatement.insert] - see that method
|
||||
/// for more information.
|
||||
Future<int> insertOne(
|
||||
Insertable<Row> row, {
|
||||
InsertMode? mode,
|
||||
UpsertClause<Tbl, Row>? onConflict,
|
||||
}) {
|
||||
return insert().insert(row, mode: mode, onConflict: onConflict);
|
||||
}
|
||||
|
||||
/// Inserts one row into this table table, replacing an existing row if it
|
||||
/// exists already.
|
||||
///
|
||||
/// Please note that this method is only available on recent sqlite3 versions.
|
||||
/// See also [InsertStatement.insertOnConflictUpdate].
|
||||
Future<int> insertOnConflictUpdate(Insertable<Row> row) {
|
||||
return insert().insertOnConflictUpdate(row);
|
||||
}
|
||||
|
||||
/// Inserts one row into this table and returns it, along with auto-
|
||||
/// generated fields.
|
||||
///
|
||||
/// Please note that this method is only available on recent sqlite3 versions.
|
||||
/// See also [InsertStatement.insertReturning].
|
||||
Future<Row> insertReturning(
|
||||
Insertable<Row> row, {
|
||||
InsertMode? mode,
|
||||
UpsertClause<Tbl, Row>? onConflict,
|
||||
}) {
|
||||
return insert().insertReturning(
|
||||
row,
|
||||
mode: mode,
|
||||
onConflict: onConflict,
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a statement to compose an `UPDATE` into the database.
|
||||
///
|
||||
/// This is equivalent to calling [DatabaseConnectionUser.update] with the
|
||||
/// captured table.
|
||||
UpdateStatement<Tbl, Row> update() => attachedDatabase.update(this);
|
||||
|
||||
/// Replaces a single row with an update statement.
|
||||
///
|
||||
/// See also [UpdateStatement.replace].
|
||||
Future<void> replaceOne(Insertable<Row> row) {
|
||||
return update().replace(row);
|
||||
}
|
||||
|
||||
/// Creates a statement to compose a `DELETE` from the database.
|
||||
///
|
||||
/// This is equivalent to calling [DatabaseConnectionUser.delete] with the
|
||||
/// captured table.
|
||||
DeleteStatement<Tbl, Row> delete() => attachedDatabase.delete(this);
|
||||
|
||||
/// Deletes the [row] from the captured table.
|
||||
Future<bool> deleteOne(Insertable<Row> row) async {
|
||||
return await (delete()..whereSamePrimaryKey(row)).go() != 0;
|
||||
}
|
||||
|
||||
/// Deletes all rows matching the [filter] from the table.
|
||||
///
|
||||
/// See also [SingleTableQueryMixin.where].
|
||||
Future<int> deleteWhere(Expression<bool?> Function(Tbl tbl) filter) {
|
||||
return (delete()..where(filter)).go();
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ import 'package:meta/meta.dart';
|
|||
// split up.
|
||||
import 'expressions/case_when.dart';
|
||||
|
||||
export 'on_table.dart';
|
||||
|
||||
part 'components/group_by.dart';
|
||||
part 'components/join.dart';
|
||||
part 'components/limit.dart';
|
||||
|
|
|
@ -11,13 +11,6 @@ part of '../query_builder.dart';
|
|||
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
||||
abstract class ViewInfo<Self extends HasResultSet, Row>
|
||||
implements ResultSetImplementation<Self, Row> {
|
||||
@override
|
||||
final DatabaseConnectionUser attachedDatabase;
|
||||
|
||||
/// Constructor for a view implementation, used by drift-generated code.
|
||||
@internal
|
||||
ViewInfo(this.attachedDatabase);
|
||||
|
||||
@override
|
||||
String get entityName;
|
||||
|
||||
|
|
|
@ -1547,7 +1547,9 @@ class MyViewData extends DataClass {
|
|||
|
||||
class MyView extends ViewInfo<MyView, MyViewData> implements HasResultSet {
|
||||
final String? _alias;
|
||||
MyView(DatabaseConnectionUser db, [this._alias]) : super(db);
|
||||
@override
|
||||
final _$CustomTablesDb attachedDatabase;
|
||||
MyView(this.attachedDatabase, [this._alias]);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[configKey, configValue, syncState, syncStateImplicit];
|
||||
|
|
|
@ -1417,10 +1417,11 @@ class $CategoryTodoCountViewView
|
|||
extends ViewInfo<$CategoryTodoCountViewView, CategoryTodoCountViewData>
|
||||
implements HasResultSet {
|
||||
final String? _alias;
|
||||
$CategoryTodoCountViewView(DatabaseConnectionUser db, [this._alias])
|
||||
: super(db);
|
||||
$TodosTableTable get todos => _db.todosTable;
|
||||
$CategoriesTable get categories => _db.categories;
|
||||
@override
|
||||
final _$TodoDb attachedDatabase;
|
||||
$CategoryTodoCountViewView(this.attachedDatabase, [this._alias]);
|
||||
$TodosTableTable get todos => attachedDatabase.todosTable;
|
||||
$CategoriesTable get categories => attachedDatabase.categories;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [categories.description, itemCount];
|
||||
@override
|
||||
|
@ -1523,10 +1524,11 @@ class $TodoWithCategoryViewView
|
|||
extends ViewInfo<$TodoWithCategoryViewView, TodoWithCategoryViewData>
|
||||
implements HasResultSet {
|
||||
final String? _alias;
|
||||
$TodoWithCategoryViewView(DatabaseConnectionUser db, [this._alias])
|
||||
: super(db);
|
||||
$TodosTableTable get todos => _db.todosTable;
|
||||
$CategoriesTable get categories => _db.categories;
|
||||
@override
|
||||
final _$TodoDb attachedDatabase;
|
||||
$TodoWithCategoryViewView(this.attachedDatabase, [this._alias]);
|
||||
$TodosTableTable get todos => attachedDatabase.todosTable;
|
||||
$CategoriesTable get categories => attachedDatabase.categories;
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todos.title, categories.description];
|
||||
@override
|
||||
|
|
|
@ -71,23 +71,21 @@ void main() {
|
|||
});
|
||||
});
|
||||
|
||||
group('delete with from()', () {
|
||||
group('delete on table instances', () {
|
||||
test('delete()', () async {
|
||||
await db.from(db.users).delete().go();
|
||||
await db.users.delete().go();
|
||||
|
||||
verify(executor.runDelete('DELETE FROM users;', []));
|
||||
});
|
||||
|
||||
test('deleteOne()', () async {
|
||||
await db.from(db.users).deleteOne(const UsersCompanion(id: Value(3)));
|
||||
await db.users.deleteOne(const UsersCompanion(id: Value(3)));
|
||||
|
||||
verify(executor.runDelete('DELETE FROM users WHERE id = ?;', [3]));
|
||||
});
|
||||
|
||||
test('deleteWhere', () async {
|
||||
await db
|
||||
.from(db.users)
|
||||
.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3));
|
||||
await db.users.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3));
|
||||
|
||||
verify(executor.runDelete('DELETE FROM users WHERE id < ?;', [3]));
|
||||
});
|
||||
|
|
|
@ -454,4 +454,55 @@ void main() {
|
|||
));
|
||||
});
|
||||
});
|
||||
|
||||
group('on table instances', () {
|
||||
test('insert', () async {
|
||||
await db.categories
|
||||
.insert()
|
||||
.insert(CategoriesCompanion.insert(description: 'description'));
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT INTO categories ("desc") VALUES (?)', ['description']));
|
||||
});
|
||||
|
||||
test('insertOne', () async {
|
||||
await db.categories.insertOne(
|
||||
CategoriesCompanion.insert(description: 'description'),
|
||||
mode: InsertMode.insertOrReplace);
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT OR REPLACE INTO categories ("desc") VALUES (?)',
|
||||
['description']));
|
||||
});
|
||||
|
||||
test('insertOnConflictUpdate', () async {
|
||||
when(executor.runSelect(any, any)).thenAnswer(
|
||||
(_) => Future.value([
|
||||
{
|
||||
'id': 1,
|
||||
'desc': 'description',
|
||||
'description_in_upper_case': 'DESCRIPTION',
|
||||
'priority': 1,
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
final row = await db.categories.insertReturning(
|
||||
CategoriesCompanion.insert(description: 'description'));
|
||||
expect(
|
||||
row,
|
||||
Category(
|
||||
id: 1,
|
||||
description: 'description',
|
||||
descriptionInUpperCase: 'DESCRIPTION',
|
||||
priority: CategoryPriority.medium,
|
||||
),
|
||||
);
|
||||
|
||||
verify(executor.runSelect(
|
||||
'INSERT INTO categories ("desc") VALUES (?) RETURNING *',
|
||||
['description'],
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -180,4 +180,21 @@ void main() {
|
|||
['new name', 3]));
|
||||
});
|
||||
});
|
||||
|
||||
group('update on table instances', () {
|
||||
test('update()', () async {
|
||||
await db.users.update().write(const UsersCompanion(id: Value(3)));
|
||||
|
||||
verify(executor.runUpdate('UPDATE users SET id = ?;', [3]));
|
||||
});
|
||||
|
||||
test('replace', () async {
|
||||
await db.categories.replace(const CategoriesCompanion(
|
||||
id: Value(3), description: Value('new name')));
|
||||
|
||||
verify(executor.runUpdate(
|
||||
'UPDATE categories SET "desc" = ?, priority = 0 WHERE id = ?;',
|
||||
['new name', 3]));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -187,9 +187,7 @@ class ViewParser {
|
|||
(tbl) => tbl.fromClass!.name == node.returnType.toString());
|
||||
if (type != null) {
|
||||
final name = node.name.toString();
|
||||
final declaration = '${type.entityInfoName} get $name => '
|
||||
'_db.${type.dbGetterName};';
|
||||
return TableReferenceInDartView(type, name, declaration);
|
||||
return TableReferenceInDartView(type, name);
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
|
|
|
@ -34,9 +34,8 @@ class DartViewDeclaration implements ViewDeclaration, DartDeclaration {
|
|||
class TableReferenceInDartView {
|
||||
final MoorTable table;
|
||||
final String name;
|
||||
final String declaration;
|
||||
|
||||
TableReferenceInDartView(this.table, this.name, this.declaration);
|
||||
TableReferenceInDartView(this.table, this.name);
|
||||
}
|
||||
|
||||
class MoorViewDeclaration
|
||||
|
|
|
@ -38,19 +38,21 @@ class ViewWriter extends TableOrViewWriter {
|
|||
} else {
|
||||
buffer.write('<${view.entityInfoName}, Never>');
|
||||
}
|
||||
buffer.write(' implements HasResultSet');
|
||||
buffer.writeln(' implements HasResultSet {');
|
||||
|
||||
buffer
|
||||
..write('{\n')
|
||||
// write the generated database reference that is set in the constructor
|
||||
..write('final ${scope.nullableType('String')} _alias;\n')
|
||||
..write('${view.entityInfoName}(DatabaseConnectionUser db, '
|
||||
'[this._alias]): super(db);\n');
|
||||
..writeln('final ${scope.nullableType('String')} _alias;')
|
||||
..writeln(
|
||||
'@override final ${databaseWriter.dbClassName} attachedDatabase;')
|
||||
..writeln('${view.entityInfoName}(this.attachedDatabase, '
|
||||
'[this._alias]);');
|
||||
|
||||
final declaration = view.declaration;
|
||||
if (declaration is DartViewDeclaration) {
|
||||
for (final ref in declaration.staticReferences) {
|
||||
buffer.write('${ref.declaration}\n');
|
||||
final declaration = '${ref.table.entityInfoName} get ${ref.name} => '
|
||||
'attachedDatabase.${ref.table.dbGetterName};';
|
||||
buffer.writeln(declaration);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue