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
|
## 1.3.0
|
||||||
|
|
||||||
- Add the `from(table)` method to generated databases. It can be used to write
|
- Add the `from(table)` method to generated databases. It can be used to write
|
||||||
|
|
|
@ -535,10 +535,11 @@ class $TodoCategoryItemCountView
|
||||||
extends ViewInfo<$TodoCategoryItemCountView, TodoCategoryItemCountData>
|
extends ViewInfo<$TodoCategoryItemCountView, TodoCategoryItemCountData>
|
||||||
implements HasResultSet {
|
implements HasResultSet {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$TodoCategoryItemCountView(DatabaseConnectionUser db, [this._alias])
|
@override
|
||||||
: super(db);
|
final _$Database attachedDatabase;
|
||||||
$TodoItemsTable get todoItems => _db.todoItems;
|
$TodoCategoryItemCountView(this.attachedDatabase, [this._alias]);
|
||||||
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
$TodoItemsTable get todoItems => attachedDatabase.todoItems;
|
||||||
|
$TodoCategoriesTable get todoCategories => attachedDatabase.todoCategories;
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [todoCategories.name, itemCount];
|
List<GeneratedColumn> get $columns => [todoCategories.name, itemCount];
|
||||||
@override
|
@override
|
||||||
|
@ -643,10 +644,11 @@ class $TodoItemWithCategoryNameViewView extends ViewInfo<
|
||||||
$TodoItemWithCategoryNameViewView,
|
$TodoItemWithCategoryNameViewView,
|
||||||
TodoItemWithCategoryNameViewData> implements HasResultSet {
|
TodoItemWithCategoryNameViewData> implements HasResultSet {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$TodoItemWithCategoryNameViewView(DatabaseConnectionUser db, [this._alias])
|
@override
|
||||||
: super(db);
|
final _$Database attachedDatabase;
|
||||||
$TodoItemsTable get todoItems => _db.todoItems;
|
$TodoItemWithCategoryNameViewView(this.attachedDatabase, [this._alias]);
|
||||||
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
$TodoItemsTable get todoItems => attachedDatabase.todoItems;
|
||||||
|
$TodoCategoriesTable get todoCategories => attachedDatabase.todoCategories;
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [todoItems.id, title];
|
List<GeneratedColumn> get $columns => [todoItems.id, title];
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -159,7 +159,11 @@ abstract class DatabaseConnectionUser {
|
||||||
/// The [TableOrViewOperations] class (or the [TableOperations] extension
|
/// The [TableOrViewOperations] class (or the [TableOperations] extension
|
||||||
/// for tables) provides convenience methods that make common operations
|
/// for tables) provides convenience methods that make common operations
|
||||||
/// easier to write than using the methods from this class directly.
|
/// 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>(
|
TableOrViewOperations<T, D> from<T extends HasResultSet, D>(
|
||||||
ResultSetImplementation<T, D> table) {
|
ResultSetImplementation<T, D> table) {
|
||||||
return TableOrViewOperations._(this, 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.
|
// split up.
|
||||||
import 'expressions/case_when.dart';
|
import 'expressions/case_when.dart';
|
||||||
|
|
||||||
|
export 'on_table.dart';
|
||||||
|
|
||||||
part 'components/group_by.dart';
|
part 'components/group_by.dart';
|
||||||
part 'components/join.dart';
|
part 'components/join.dart';
|
||||||
part 'components/limit.dart';
|
part 'components/limit.dart';
|
||||||
|
|
|
@ -11,13 +11,6 @@ part of '../query_builder.dart';
|
||||||
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
||||||
abstract class ViewInfo<Self extends HasResultSet, Row>
|
abstract class ViewInfo<Self extends HasResultSet, Row>
|
||||||
implements ResultSetImplementation<Self, Row> {
|
implements ResultSetImplementation<Self, Row> {
|
||||||
@override
|
|
||||||
final DatabaseConnectionUser attachedDatabase;
|
|
||||||
|
|
||||||
/// Constructor for a view implementation, used by drift-generated code.
|
|
||||||
@internal
|
|
||||||
ViewInfo(this.attachedDatabase);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get entityName;
|
String get entityName;
|
||||||
|
|
||||||
|
|
|
@ -1547,7 +1547,9 @@ class MyViewData extends DataClass {
|
||||||
|
|
||||||
class MyView extends ViewInfo<MyView, MyViewData> implements HasResultSet {
|
class MyView extends ViewInfo<MyView, MyViewData> implements HasResultSet {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
MyView(DatabaseConnectionUser db, [this._alias]) : super(db);
|
@override
|
||||||
|
final _$CustomTablesDb attachedDatabase;
|
||||||
|
MyView(this.attachedDatabase, [this._alias]);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns =>
|
List<GeneratedColumn> get $columns =>
|
||||||
[configKey, configValue, syncState, syncStateImplicit];
|
[configKey, configValue, syncState, syncStateImplicit];
|
||||||
|
|
|
@ -1417,10 +1417,11 @@ class $CategoryTodoCountViewView
|
||||||
extends ViewInfo<$CategoryTodoCountViewView, CategoryTodoCountViewData>
|
extends ViewInfo<$CategoryTodoCountViewView, CategoryTodoCountViewData>
|
||||||
implements HasResultSet {
|
implements HasResultSet {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$CategoryTodoCountViewView(DatabaseConnectionUser db, [this._alias])
|
@override
|
||||||
: super(db);
|
final _$TodoDb attachedDatabase;
|
||||||
$TodosTableTable get todos => _db.todosTable;
|
$CategoryTodoCountViewView(this.attachedDatabase, [this._alias]);
|
||||||
$CategoriesTable get categories => _db.categories;
|
$TodosTableTable get todos => attachedDatabase.todosTable;
|
||||||
|
$CategoriesTable get categories => attachedDatabase.categories;
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [categories.description, itemCount];
|
List<GeneratedColumn> get $columns => [categories.description, itemCount];
|
||||||
@override
|
@override
|
||||||
|
@ -1523,10 +1524,11 @@ class $TodoWithCategoryViewView
|
||||||
extends ViewInfo<$TodoWithCategoryViewView, TodoWithCategoryViewData>
|
extends ViewInfo<$TodoWithCategoryViewView, TodoWithCategoryViewData>
|
||||||
implements HasResultSet {
|
implements HasResultSet {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$TodoWithCategoryViewView(DatabaseConnectionUser db, [this._alias])
|
@override
|
||||||
: super(db);
|
final _$TodoDb attachedDatabase;
|
||||||
$TodosTableTable get todos => _db.todosTable;
|
$TodoWithCategoryViewView(this.attachedDatabase, [this._alias]);
|
||||||
$CategoriesTable get categories => _db.categories;
|
$TodosTableTable get todos => attachedDatabase.todosTable;
|
||||||
|
$CategoriesTable get categories => attachedDatabase.categories;
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [todos.title, categories.description];
|
List<GeneratedColumn> get $columns => [todos.title, categories.description];
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -71,23 +71,21 @@ void main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('delete with from()', () {
|
group('delete on table instances', () {
|
||||||
test('delete()', () async {
|
test('delete()', () async {
|
||||||
await db.from(db.users).delete().go();
|
await db.users.delete().go();
|
||||||
|
|
||||||
verify(executor.runDelete('DELETE FROM users;', []));
|
verify(executor.runDelete('DELETE FROM users;', []));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('deleteOne()', () async {
|
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]));
|
verify(executor.runDelete('DELETE FROM users WHERE id = ?;', [3]));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('deleteWhere', () async {
|
test('deleteWhere', () async {
|
||||||
await db
|
await db.users.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3));
|
||||||
.from(db.users)
|
|
||||||
.deleteWhere((tbl) => tbl.id.isSmallerThanValue(3));
|
|
||||||
|
|
||||||
verify(executor.runDelete('DELETE FROM users WHERE id < ?;', [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]));
|
['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());
|
(tbl) => tbl.fromClass!.name == node.returnType.toString());
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
final name = node.name.toString();
|
final name = node.name.toString();
|
||||||
final declaration = '${type.entityInfoName} get $name => '
|
return TableReferenceInDartView(type, name);
|
||||||
'_db.${type.dbGetterName};';
|
|
||||||
return TableReferenceInDartView(type, name, declaration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
|
@ -34,9 +34,8 @@ class DartViewDeclaration implements ViewDeclaration, DartDeclaration {
|
||||||
class TableReferenceInDartView {
|
class TableReferenceInDartView {
|
||||||
final MoorTable table;
|
final MoorTable table;
|
||||||
final String name;
|
final String name;
|
||||||
final String declaration;
|
|
||||||
|
|
||||||
TableReferenceInDartView(this.table, this.name, this.declaration);
|
TableReferenceInDartView(this.table, this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoorViewDeclaration
|
class MoorViewDeclaration
|
||||||
|
|
|
@ -38,19 +38,21 @@ class ViewWriter extends TableOrViewWriter {
|
||||||
} else {
|
} else {
|
||||||
buffer.write('<${view.entityInfoName}, Never>');
|
buffer.write('<${view.entityInfoName}, Never>');
|
||||||
}
|
}
|
||||||
buffer.write(' implements HasResultSet');
|
buffer.writeln(' implements HasResultSet {');
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
..write('{\n')
|
..writeln('final ${scope.nullableType('String')} _alias;')
|
||||||
// write the generated database reference that is set in the constructor
|
..writeln(
|
||||||
..write('final ${scope.nullableType('String')} _alias;\n')
|
'@override final ${databaseWriter.dbClassName} attachedDatabase;')
|
||||||
..write('${view.entityInfoName}(DatabaseConnectionUser db, '
|
..writeln('${view.entityInfoName}(this.attachedDatabase, '
|
||||||
'[this._alias]): super(db);\n');
|
'[this._alias]);');
|
||||||
|
|
||||||
final declaration = view.declaration;
|
final declaration = view.declaration;
|
||||||
if (declaration is DartViewDeclaration) {
|
if (declaration is DartViewDeclaration) {
|
||||||
for (final ref in declaration.staticReferences) {
|
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