mirror of
https://github.com/immich-app/drift.git
synced 2026-09-30 13:22:57 +08:00
Format
This commit is contained in:
@@ -34,4 +34,5 @@ class TodoItems extends Table {
|
||||
TextColumn get content => text()();
|
||||
IntColumn get author => integer().references(Users, #id)();
|
||||
}
|
||||
|
||||
// #enddocregion record-explicit
|
||||
|
||||
@@ -18,4 +18,5 @@ class User {
|
||||
|
||||
User.fromDb({required this.id, required this.name, required this.birthday});
|
||||
}
|
||||
|
||||
// #enddocregion named
|
||||
|
||||
@@ -61,13 +61,16 @@ void _queryManager(Database db) async {
|
||||
// #enddocregion generated-dataclass
|
||||
|
||||
// #docregion generated-value
|
||||
await db.users.insertOne(UsersCompanion(
|
||||
id: Value.absent(), // (1)!
|
||||
username: Value('user'), // (2)!
|
||||
));
|
||||
await db.users.insertOne(
|
||||
UsersCompanion(
|
||||
id: Value.absent(), // (1)!
|
||||
username: Value('user'), // (2)!
|
||||
),
|
||||
);
|
||||
|
||||
await (db.update(db.users)..where((tbl) => tbl.id.equals(1)))
|
||||
.write(UsersCompanion(username: Value("Updated name")));
|
||||
await (db.update(db.users)..where((tbl) => tbl.id.equals(1))).write(
|
||||
UsersCompanion(username: Value("Updated name")),
|
||||
);
|
||||
// #enddocregion generated-value
|
||||
|
||||
// #docregion companion-custom
|
||||
|
||||
@@ -8,24 +8,30 @@ extension MigrateToTextDateTimes on GeneratedDatabase {
|
||||
// `allTables` getter returns all tables of the current schema, which may
|
||||
// be incorrect.
|
||||
for (final table in allTables) {
|
||||
final dateTimeColumns =
|
||||
table.$columns.where((c) => c.type == DriftSqlType.dateTime);
|
||||
final dateTimeColumns = table.$columns.where(
|
||||
(c) => c.type == DriftSqlType.dateTime,
|
||||
);
|
||||
|
||||
if (dateTimeColumns.isNotEmpty) {
|
||||
// This table has dateTime columns which need to be migrated.
|
||||
await m.alterTable(TableMigration(
|
||||
table,
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// We assume that the column in the database is an int (unix
|
||||
// timestamp), use `fromUnixEpoch` to convert it to a date time.
|
||||
// Note that the resulting value in the database is in UTC.
|
||||
column: DateTimeExpressions.fromUnixEpoch(column.dartCast<int>()),
|
||||
},
|
||||
));
|
||||
await m.alterTable(
|
||||
TableMigration(
|
||||
table,
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// We assume that the column in the database is an int (unix
|
||||
// timestamp), use `fromUnixEpoch` to convert it to a date time.
|
||||
// Note that the resulting value in the database is in UTC.
|
||||
column: DateTimeExpressions.fromUnixEpoch(
|
||||
column.dartCast<int>(),
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #enddocregion unix-to-text
|
||||
}
|
||||
|
||||
@@ -37,45 +43,54 @@ extension MigrateToTimestamps on GeneratedDatabase {
|
||||
// `allTables` getter returns all tables of the current schema, which may
|
||||
// be incorrect.
|
||||
for (final table in allTables) {
|
||||
final dateTimeColumns =
|
||||
table.$columns.where((c) => c.type == DriftSqlType.dateTime);
|
||||
final dateTimeColumns = table.$columns.where(
|
||||
(c) => c.type == DriftSqlType.dateTime,
|
||||
);
|
||||
|
||||
if (dateTimeColumns.isNotEmpty) {
|
||||
// This table has dateTime columns which need to be migrated.
|
||||
await m.alterTable(TableMigration(
|
||||
table,
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// We assume that the column in the database is a string. We want
|
||||
// to parse it to a date in SQL and then get the unix timestamp of
|
||||
// it.
|
||||
// Note that this requires sqlite version 3.38 or above.
|
||||
column: FunctionCallExpression('unixepoch', [column]),
|
||||
},
|
||||
));
|
||||
await m.alterTable(
|
||||
TableMigration(
|
||||
table,
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// We assume that the column in the database is a string. We want
|
||||
// to parse it to a date in SQL and then get the unix timestamp of
|
||||
// it.
|
||||
// Note that this requires sqlite version 3.38 or above.
|
||||
column: FunctionCallExpression('unixepoch', [column]),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #enddocregion text-to-unix
|
||||
|
||||
Future<void> migrateFromTextDateTimesToUnixTimestampsPre338(
|
||||
Migrator m) async {
|
||||
Migrator m,
|
||||
) async {
|
||||
for (final table in allTables) {
|
||||
final dateTimeColumns =
|
||||
table.$columns.where((c) => c.type == DriftSqlType.dateTime);
|
||||
final dateTimeColumns = table.$columns.where(
|
||||
(c) => c.type == DriftSqlType.dateTime,
|
||||
);
|
||||
|
||||
if (dateTimeColumns.isNotEmpty) {
|
||||
await m.alterTable(TableMigration(
|
||||
table,
|
||||
// #docregion text-to-unix-old
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// Use this as an alternative to `unixepoch`:
|
||||
column: FunctionCallExpression(
|
||||
'strftime', [const Constant('%s'), column]).cast<int>(),
|
||||
},
|
||||
// #enddocregion text-to-unix-old
|
||||
));
|
||||
await m.alterTable(
|
||||
TableMigration(
|
||||
table,
|
||||
// #docregion text-to-unix-old
|
||||
columnTransformer: {
|
||||
for (final column in dateTimeColumns)
|
||||
// Use this as an alternative to `unixepoch`:
|
||||
column: FunctionCallExpression('strftime', [
|
||||
const Constant('%s'),
|
||||
column,
|
||||
]).cast<int>(),
|
||||
},
|
||||
// #enddocregion text-to-unix-old
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,9 @@ extension Snippets on CanUseCommonTables {
|
||||
..where((a) => a.averageLivespan.isSmallerThan(a.amountOfLegs)));
|
||||
|
||||
Future<List<Animal>> findAnimalsByLegs(int legCount) {
|
||||
return (select(animals)..where((a) => a.amountOfLegs.equals(legCount)))
|
||||
.get();
|
||||
return (select(
|
||||
animals,
|
||||
)..where((a) => a.amountOfLegs.equals(legCount))).get();
|
||||
}
|
||||
// #enddocregion comparison
|
||||
|
||||
@@ -54,28 +55,25 @@ extension Snippets on CanUseCommonTables {
|
||||
Animals a = animals.asDslTable;
|
||||
|
||||
// #docregion listand
|
||||
Expression.and([
|
||||
a.isMammal,
|
||||
a.amountOfLegs.equals(4),
|
||||
]);
|
||||
Expression.and([a.isMammal, a.amountOfLegs.equals(4)]);
|
||||
// #enddocregion listand
|
||||
}
|
||||
|
||||
// #docregion arithmetic
|
||||
Future<List<Product>> canBeBought(int amount, int budget) {
|
||||
return (select(products)
|
||||
..where((p) {
|
||||
final totalPrice = p.price * Variable(amount);
|
||||
return totalPrice.isSmallerOrEqualValue(budget);
|
||||
}))
|
||||
return (select(products)..where((p) {
|
||||
final totalPrice = p.price * Variable(amount);
|
||||
return totalPrice.isSmallerOrEqualValue(budget);
|
||||
}))
|
||||
.get();
|
||||
}
|
||||
// #enddocregion arithmetic
|
||||
|
||||
// #docregion emptyCategories
|
||||
Future<List<Category>> emptyCategories() {
|
||||
final hasNoTodo = notExistsQuery(select(todoItems)
|
||||
..where((row) => row.category.equalsExp(categories.id)));
|
||||
final hasNoTodo = notExistsQuery(
|
||||
select(todoItems)..where((row) => row.category.equalsExp(categories.id)),
|
||||
);
|
||||
return (select(categories)..where((row) => hasNoTodo)).get();
|
||||
}
|
||||
// #enddocregion emptyCategories
|
||||
@@ -118,7 +116,7 @@ extension Snippets on CanUseCommonTables {
|
||||
todoItems,
|
||||
todoItems.category.equalsExp(categories.id),
|
||||
useColumns: false,
|
||||
)
|
||||
),
|
||||
]);
|
||||
query
|
||||
..addColumns([amountOfTodos])
|
||||
@@ -149,16 +147,18 @@ extension Snippets on CanUseCommonTables {
|
||||
|
||||
void customExpressions() {
|
||||
// #docregion custom
|
||||
const inactive =
|
||||
CustomExpression<bool>("julianday('now') - julianday(last_login) > 60");
|
||||
const inactive = CustomExpression<bool>(
|
||||
"julianday('now') - julianday(last_login) > 60",
|
||||
);
|
||||
select(users)..where((u) => inactive);
|
||||
// #enddocregion custom
|
||||
// #enddocregion custom
|
||||
}
|
||||
|
||||
// #docregion date2
|
||||
Future<void> increaseDueDates() async {
|
||||
final change = TodoItemsCompanion.custom(
|
||||
dueDate: todoItems.dueDate + Duration(days: 1));
|
||||
dueDate: todoItems.dueDate + Duration(days: 1),
|
||||
);
|
||||
await update(todoItems).write(change);
|
||||
}
|
||||
// #enddocregion date2
|
||||
@@ -166,8 +166,10 @@ extension Snippets on CanUseCommonTables {
|
||||
// #docregion date3
|
||||
Future<void> moveDueDateToNextMonday() async {
|
||||
final change = TodoItemsCompanion.custom(
|
||||
dueDate: todoItems.dueDate
|
||||
.modify(DateTimeModifier.weekday(DateTime.monday)));
|
||||
dueDate: todoItems.dueDate.modify(
|
||||
DateTimeModifier.weekday(DateTime.monday),
|
||||
),
|
||||
);
|
||||
await update(todoItems).write(change);
|
||||
}
|
||||
// #enddocregion date3
|
||||
@@ -210,10 +212,13 @@ extension Snippets on CanUseCommonTables {
|
||||
void rowValuesUsage() {
|
||||
select(animals).where((row) {
|
||||
// Generates (amount_of_legs, average_livespan) < (?, ?)
|
||||
return RowValues([row.amountOfLegs, row.averageLivespan])
|
||||
.isSmallerThan(RowValues([Variable(2), Variable(10)]));
|
||||
return RowValues([
|
||||
row.amountOfLegs,
|
||||
row.averageLivespan,
|
||||
]).isSmallerThan(RowValues([Variable(2), Variable(10)]));
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion rowvalue-use
|
||||
}
|
||||
|
||||
@@ -248,4 +253,5 @@ final class RowValues extends Expression<Never> {
|
||||
context.buffer.write(')');
|
||||
}
|
||||
}
|
||||
|
||||
// #enddocregion row-values
|
||||
|
||||
@@ -64,17 +64,21 @@ class MyDatabase extends _$MyDatabase {
|
||||
|
||||
// #docregion calls-with-contacts
|
||||
Future<List<(Call, Contact)>> callsWithContact() async {
|
||||
final phoneNumbersForContact =
|
||||
contacts.data.jsonEach(this, r'$.phoneNumbers');
|
||||
final phoneNumbersForContact = contacts.data.jsonEach(
|
||||
this,
|
||||
r'$.phoneNumbers',
|
||||
);
|
||||
final phoneNumberQuery = selectOnly(phoneNumbersForContact)
|
||||
..addColumns([phoneNumbersForContact.value]);
|
||||
|
||||
final query = select(calls).join(
|
||||
[innerJoin(contacts, calls.phoneNumber.isInQuery(phoneNumberQuery))]);
|
||||
final query = select(calls).join([
|
||||
innerJoin(contacts, calls.phoneNumber.isInQuery(phoneNumberQuery)),
|
||||
]);
|
||||
|
||||
return query
|
||||
.map((row) => (row.readTable(calls), row.readTable(contacts)))
|
||||
.get();
|
||||
}
|
||||
|
||||
// #enddocregion calls-with-contacts
|
||||
}
|
||||
|
||||
@@ -49,14 +49,16 @@ extension ManagerExamples on AppDatabase {
|
||||
// #docregion manager_create
|
||||
Future<void> createTodoItem() async {
|
||||
// Create a new item
|
||||
await managers.todoItems
|
||||
.create((o) => o(title: 'Title', content: 'Content'));
|
||||
await managers.todoItems.create(
|
||||
(o) => o(title: 'Title', content: 'Content'),
|
||||
);
|
||||
|
||||
// We can also use `mode` and `onConflict` parameters, just
|
||||
// like in the `[InsertStatement.insert]` method on the table
|
||||
await managers.todoItems.create(
|
||||
(o) => o(title: 'Title', content: 'New Content'),
|
||||
mode: InsertMode.replace);
|
||||
(o) => o(title: 'Title', content: 'New Content'),
|
||||
mode: InsertMode.replace,
|
||||
);
|
||||
|
||||
// We can also create multiple items at once
|
||||
await managers.todoItems.bulkCreate(
|
||||
@@ -88,8 +90,9 @@ extension ManagerExamples on AppDatabase {
|
||||
await managers.todoItems.replace(obj);
|
||||
|
||||
// Replace multiple items
|
||||
var objs =
|
||||
await managers.todoItems.filter((o) => o.id.isIn([1, 2, 3])).get();
|
||||
var objs = await managers.todoItems
|
||||
.filter((o) => o.id.isIn([1, 2, 3]))
|
||||
.get();
|
||||
objs = objs.map((o) => o.copyWith(content: 'New Content')).toList();
|
||||
await managers.todoItems.bulkReplace(objs);
|
||||
}
|
||||
@@ -135,14 +138,15 @@ extension ManagerExamples on AppDatabase {
|
||||
Future<void> filterWithType() async {
|
||||
// Filter all items created since 7 days ago
|
||||
managers.todoItems.filter(
|
||||
(f) => f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7))));
|
||||
(f) => f.createdAt.isAfter(DateTime.now().subtract(Duration(days: 7))),
|
||||
);
|
||||
|
||||
// Filter all items with a title that starts with "Title"
|
||||
managers.todoItems.filter((f) => f.title.startsWith('Title'));
|
||||
}
|
||||
// #enddocregion manager_type_specific_filter
|
||||
// #enddocregion manager_type_specific_filter
|
||||
|
||||
// #docregion manager_ordering
|
||||
// #docregion manager_ordering
|
||||
Future<void> orderWithType() async {
|
||||
// Order all items by their creation date in ascending order
|
||||
managers.todoItems.orderBy((o) => o.createdAt.asc());
|
||||
@@ -150,9 +154,9 @@ extension ManagerExamples on AppDatabase {
|
||||
// Order all items by their title in ascending order and then by their content in ascending order
|
||||
managers.todoItems.orderBy((o) => o.title.asc() & o.content.asc());
|
||||
}
|
||||
// #enddocregion manager_ordering
|
||||
// #enddocregion manager_ordering
|
||||
|
||||
// #docregion manager_count
|
||||
// #docregion manager_count
|
||||
Future<void> count() async {
|
||||
// Count all items
|
||||
await managers.todoItems.count();
|
||||
@@ -160,9 +164,9 @@ extension ManagerExamples on AppDatabase {
|
||||
// Count all items with a title of "Title"
|
||||
await managers.todoItems.filter((f) => f.title("Title")).count();
|
||||
}
|
||||
// #enddocregion manager_count
|
||||
// #enddocregion manager_count
|
||||
|
||||
// #docregion manager_exists
|
||||
// #docregion manager_exists
|
||||
Future<void> exists() async {
|
||||
// Check if any items exist
|
||||
await managers.todoItems.exists();
|
||||
@@ -170,9 +174,9 @@ extension ManagerExamples on AppDatabase {
|
||||
// Check if any items with a title of "Title" exist
|
||||
await managers.todoItems.filter((f) => f.title("Title")).exists();
|
||||
}
|
||||
// #enddocregion manager_exists
|
||||
// #enddocregion manager_exists
|
||||
|
||||
// #docregion manager_filter_forward_references
|
||||
// #docregion manager_filter_forward_references
|
||||
Future<void> relationalFilter() async {
|
||||
// Get all items with a category description of "School"
|
||||
managers.todoItems.filter((f) => f.category.description("School"));
|
||||
@@ -180,14 +184,12 @@ extension ManagerExamples on AppDatabase {
|
||||
// These can be combined with other filters
|
||||
// For example, get all items with a title of "Title" or a category description of "School"
|
||||
await managers.todoItems
|
||||
.filter(
|
||||
(f) => f.title("Title") | f.category.description("School"),
|
||||
)
|
||||
.filter((f) => f.title("Title") | f.category.description("School"))
|
||||
.exists();
|
||||
}
|
||||
// #enddocregion manager_filter_forward_references
|
||||
// #enddocregion manager_filter_forward_references
|
||||
|
||||
// #docregion manager_filter_back_references
|
||||
// #docregion manager_filter_back_references
|
||||
Future<void> reverseRelationalFilter() async {
|
||||
// Get the category that has a todo item with an id of 1
|
||||
managers.todoCategory.filter((f) => f.todoItemsRefs((f) => f.id(1)));
|
||||
@@ -198,9 +200,9 @@ extension ManagerExamples on AppDatabase {
|
||||
(f) => f.description("School") | f.todoItemsRefs((f) => f.id(1)),
|
||||
);
|
||||
}
|
||||
// #enddocregion manager_filter_back_references
|
||||
// #enddocregion manager_filter_back_references
|
||||
|
||||
// #docregion manager_filter_custom_back_references
|
||||
// #docregion manager_filter_custom_back_references
|
||||
Future<void> reverseNamedRelationalFilter() async {
|
||||
// Get all users who are administrators of a group with a name containing "Business"
|
||||
// or who own a group with an id of 1, 2, 4, or 5
|
||||
@@ -210,9 +212,9 @@ extension ManagerExamples on AppDatabase {
|
||||
f.ownedGroups((f) => f.id.isIn([1, 2, 4, 5])),
|
||||
);
|
||||
}
|
||||
// #enddocregion manager_filter_custom_back_references
|
||||
// #enddocregion manager_filter_custom_back_references
|
||||
|
||||
// #docregion manager_references
|
||||
// #docregion manager_references
|
||||
Future<void> references() async {
|
||||
/// Get each todo, along with a its categories
|
||||
final todosWithRefs = await managers.todoItems.withReferences().get();
|
||||
@@ -221,21 +223,20 @@ extension ManagerExamples on AppDatabase {
|
||||
}
|
||||
|
||||
/// This also works in the reverse
|
||||
final categoriesWithRefs =
|
||||
await managers.todoCategory.withReferences().get();
|
||||
final categoriesWithRefs = await managers.todoCategory
|
||||
.withReferences()
|
||||
.get();
|
||||
for (final (category, refs) in categoriesWithRefs) {
|
||||
final todos = await refs.todoItemsRefs.get();
|
||||
}
|
||||
}
|
||||
|
||||
// #enddocregion manager_references
|
||||
// #docregion manager_prefetch_references
|
||||
// #enddocregion manager_references
|
||||
// #docregion manager_prefetch_references
|
||||
Future<void> referencesPrefetch() async {
|
||||
/// Get each todo, along with a its categories
|
||||
final todosWithRefs = await managers.todoItems
|
||||
.withReferences(
|
||||
(prefetch) => prefetch(category: true),
|
||||
)
|
||||
.withReferences((prefetch) => prefetch(category: true))
|
||||
.get();
|
||||
for (final (todo, refs) in todosWithRefs) {
|
||||
final category = refs.category?.prefetchedData?.firstOrNull;
|
||||
@@ -253,26 +254,24 @@ extension ManagerExamples on AppDatabase {
|
||||
//final todos = await refs.todoItemsRefs.get();
|
||||
}
|
||||
}
|
||||
// #enddocregion manager_prefetch_references
|
||||
// #enddocregion manager_prefetch_references
|
||||
|
||||
Future<void> referencesPrefetchStream() async {
|
||||
// #docregion manager_prefetch_references_stream
|
||||
// #docregion manager_prefetch_references_stream
|
||||
/// Get each todo, along with a its categories
|
||||
managers.todoCategory
|
||||
.withReferences((prefetch) => prefetch(todoItemsRefs: true, user: true))
|
||||
.watch()
|
||||
.listen(
|
||||
(catWithRefs) {
|
||||
for (final (cat, refs) in catWithRefs) {
|
||||
// Updates to the user table will trigger a query
|
||||
final users = refs.user?.prefetchedData;
|
||||
.listen((catWithRefs) {
|
||||
for (final (cat, refs) in catWithRefs) {
|
||||
// Updates to the user table will trigger a query
|
||||
final users = refs.user?.prefetchedData;
|
||||
|
||||
// However, updates to the TodoItems table will not trigger a query
|
||||
final todos = refs.todoItemsRefs.prefetchedData;
|
||||
}
|
||||
},
|
||||
);
|
||||
// #enddocregion manager_prefetch_references_stream
|
||||
// However, updates to the TodoItems table will not trigger a query
|
||||
final todos = refs.todoItemsRefs.prefetchedData;
|
||||
}
|
||||
});
|
||||
// #enddocregion manager_prefetch_references_stream
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,8 +337,9 @@ Future<void> customOrdering(AppDatabase db) async {
|
||||
void _managerAnnotations(AppDatabase db) async {
|
||||
// #docregion manager_annotations
|
||||
// First create an computed field with an expression you want to use
|
||||
final titleLengthField =
|
||||
db.managers.todoItems.computedField((o) => o.title.length);
|
||||
final titleLengthField = db.managers.todoItems.computedField(
|
||||
(o) => o.title.length,
|
||||
);
|
||||
|
||||
/// Create a copy of the manager with the computed fields you want to use
|
||||
final manager = db.managers.todoItems.withFields([titleLengthField]);
|
||||
@@ -357,14 +357,15 @@ void _managerAnnotations(AppDatabase db) async {
|
||||
final titleLength = titleLengthField.read(refs);
|
||||
print('Item ${item.id} has a title length of $titleLength');
|
||||
}
|
||||
// #enddocregion manager_annotations
|
||||
// #enddocregion manager_annotations
|
||||
}
|
||||
|
||||
void _managerReferencedAnnotations(AppDatabase db) async {
|
||||
// #docregion referenced_annotations
|
||||
// This computed field will get the name of the user of this todo
|
||||
final todoUserName =
|
||||
db.managers.todoItems.computedField((o) => o.category.user.name);
|
||||
final todoUserName = db.managers.todoItems.computedField(
|
||||
(o) => o.category.user.name,
|
||||
);
|
||||
|
||||
/// Create a copy of the manager with the computed fields you want to use
|
||||
final manager = db.managers.todoItems.withFields([todoUserName]);
|
||||
@@ -381,8 +382,9 @@ void _managerAggregatedAnnotations(AppDatabase db) async {
|
||||
// #docregion aggregated_annotations
|
||||
// You can aggregate over multiple rows in a related table
|
||||
// to perform calculations on them
|
||||
final todoCountcomputedField = db.managers.todoCategory
|
||||
.computedField((o) => o.todoItemsRefs((o) => o.id).count());
|
||||
final todoCountcomputedField = db.managers.todoCategory.computedField(
|
||||
(o) => o.todoItemsRefs((o) => o.id).count(),
|
||||
);
|
||||
|
||||
/// Create a copy of the manager with the computed fields you want to use
|
||||
final manager = db.managers.todoCategory.withFields([todoCountcomputedField]);
|
||||
|
||||
@@ -26,9 +26,9 @@ extension SelectExamples on CanUseCommonTables {
|
||||
|
||||
// #docregion order-by
|
||||
Future<List<TodoItem>> sortEntriesAlphabetically() {
|
||||
return (select(todoItems)
|
||||
..orderBy([(t) => OrderingTerm(expression: t.title)]))
|
||||
.get();
|
||||
return (select(
|
||||
todoItems,
|
||||
)..orderBy([(t) => OrderingTerm(expression: t.title)])).get();
|
||||
}
|
||||
// #enddocregion order-by
|
||||
|
||||
@@ -82,7 +82,7 @@ extension SelectExamples on CanUseCommonTables {
|
||||
});
|
||||
// #enddocregion results
|
||||
}
|
||||
// #enddocregion joinIntro
|
||||
// #enddocregion joinIntro
|
||||
|
||||
// #docregion otherTodosInSameCategory
|
||||
/// Searches for todo entries in the same category as the ones having
|
||||
@@ -97,12 +97,17 @@ extension SelectExamples on CanUseCommonTables {
|
||||
// In joins, `useColumns: false` tells drift to not add columns of the
|
||||
// joined table to the result set. This is useful here, since we only join
|
||||
// the tables so that we can refer to them in the where clause.
|
||||
innerJoin(categories, categories.id.equalsExp(otherTodos.category),
|
||||
useColumns: false),
|
||||
innerJoin(todoItems, todoItems.category.equalsExp(categories.id),
|
||||
useColumns: false),
|
||||
])
|
||||
..where(todoItems.title.contains(titleQuery));
|
||||
innerJoin(
|
||||
categories,
|
||||
categories.id.equalsExp(otherTodos.category),
|
||||
useColumns: false,
|
||||
),
|
||||
innerJoin(
|
||||
todoItems,
|
||||
todoItems.category.equalsExp(categories.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])..where(todoItems.title.contains(titleQuery));
|
||||
|
||||
return query.map((row) => row.readTable(otherTodos)).get();
|
||||
}
|
||||
@@ -117,7 +122,7 @@ extension SelectExamples on CanUseCommonTables {
|
||||
todoItems,
|
||||
todoItems.category.equalsExp(categories.id),
|
||||
useColumns: false,
|
||||
)
|
||||
),
|
||||
]);
|
||||
query
|
||||
..addColumns([amountOfTodos])
|
||||
@@ -126,8 +131,10 @@ extension SelectExamples on CanUseCommonTables {
|
||||
final result = await query.get();
|
||||
|
||||
for (final row in result) {
|
||||
print('there are ${row.read(amountOfTodos)} entries in'
|
||||
'${row.readTable(categories)}');
|
||||
print(
|
||||
'there are ${row.read(amountOfTodos)} entries in'
|
||||
'${row.readTable(categories)}',
|
||||
);
|
||||
}
|
||||
}
|
||||
// #enddocregion countTodosInCategories
|
||||
@@ -148,9 +155,9 @@ extension SelectExamples on CanUseCommonTables {
|
||||
..where(todoItems.category.isNull())
|
||||
..addColumns([newDescription]);
|
||||
|
||||
await into(categories).insertFromSelect(query, columns: {
|
||||
categories.name: newDescription,
|
||||
});
|
||||
await into(
|
||||
categories,
|
||||
).insertFromSelect(query, columns: {categories.name: newDescription});
|
||||
}
|
||||
// #enddocregion createCategoryForUnassignedTodoEntries
|
||||
|
||||
@@ -168,19 +175,18 @@ extension SelectExamples on CanUseCommonTables {
|
||||
// we're not selecting from `todos`. Instead, we'll use Subquery.ref to read
|
||||
// from a column in a subquery.
|
||||
final itemCount = longestTodos.ref(todoItems.title).count();
|
||||
final query = select(categories).join(
|
||||
[
|
||||
innerJoin(
|
||||
longestTodos,
|
||||
// Again using .ref() here to access the category in the outer select
|
||||
// statement.
|
||||
longestTodos.ref(todoItems.category).equalsExp(categories.id),
|
||||
useColumns: false,
|
||||
)
|
||||
],
|
||||
)
|
||||
..addColumns([itemCount])
|
||||
..groupBy([categories.id]);
|
||||
final query =
|
||||
select(categories).join([
|
||||
innerJoin(
|
||||
longestTodos,
|
||||
// Again using .ref() here to access the category in the outer select
|
||||
// statement.
|
||||
longestTodos.ref(todoItems.category).equalsExp(categories.id),
|
||||
useColumns: false,
|
||||
),
|
||||
])
|
||||
..addColumns([itemCount])
|
||||
..groupBy([categories.id]);
|
||||
|
||||
final rows = await query.get();
|
||||
|
||||
@@ -214,23 +220,32 @@ extension SelectExamples on CanUseCommonTables {
|
||||
|
||||
// #docregion compound
|
||||
Future<List<(String?, int)>> todoItemsInCategory() async {
|
||||
final countWithCategory = subqueryExpression<int>(selectOnly(todoItems)
|
||||
..addColumns([countAll()])
|
||||
..where(todoItems.category.equalsExp(categories.id)));
|
||||
final countWithCategory = subqueryExpression<int>(
|
||||
selectOnly(todoItems)
|
||||
..addColumns([countAll()])
|
||||
..where(todoItems.category.equalsExp(categories.id)),
|
||||
);
|
||||
|
||||
final countWithoutCategory = subqueryExpression<int>(selectOnly(todoItems)
|
||||
..addColumns([countAll()])
|
||||
..where(todoItems.category.isNull()));
|
||||
final countWithoutCategory = subqueryExpression<int>(
|
||||
selectOnly(todoItems)
|
||||
..addColumns([countAll()])
|
||||
..where(todoItems.category.isNull()),
|
||||
);
|
||||
|
||||
final query = db.selectOnly(categories)
|
||||
..addColumns([categories.name, countWithoutCategory])
|
||||
..groupBy([categories.id]);
|
||||
query.unionAll(db.selectExpressions(
|
||||
[const Constant<String>(null), countWithoutCategory]));
|
||||
query.unionAll(
|
||||
db.selectExpressions([
|
||||
const Constant<String>(null),
|
||||
countWithoutCategory,
|
||||
]),
|
||||
);
|
||||
|
||||
return query
|
||||
.map((row) => (row.read(categories.name), row.read(countWithCategory)!))
|
||||
.get();
|
||||
}
|
||||
|
||||
// #enddocregion compound
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ extension CoreApi on AppDatabase {
|
||||
|
||||
// #docregion tableUpdates
|
||||
Future<void> listenForUpdates() async {
|
||||
final stream = tableUpdates(TableUpdateQuery.onTable(
|
||||
todoItems,
|
||||
limitUpdateKind: UpdateKind.update,
|
||||
));
|
||||
final stream = tableUpdates(
|
||||
TableUpdateQuery.onTable(todoItems, limitUpdateKind: UpdateKind.update),
|
||||
);
|
||||
|
||||
await for (final event in stream) {
|
||||
print('Update on todos table: $event');
|
||||
@@ -27,6 +26,7 @@ extension CoreApi on AppDatabase {
|
||||
void markUpdated() {
|
||||
notifyUpdates({TableUpdate.onTable(todoItems, kind: UpdateKind.insert)});
|
||||
}
|
||||
|
||||
// #enddocregion markUpdated
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ extension Manager on AppDatabase {
|
||||
Selectable<TodoItem> allItemsAfter(DateTime min) {
|
||||
return managers.todoItems.filter((c) => c.createdAt.isAfter(min));
|
||||
}
|
||||
|
||||
// #enddocregion manager
|
||||
}
|
||||
|
||||
@@ -47,5 +48,6 @@ extension CustomQueries on AppDatabase {
|
||||
readsFrom: {todoItems}, // (1)!
|
||||
).map((row) => todoItems.map(row.data));
|
||||
}
|
||||
|
||||
// #enddocregion custom
|
||||
}
|
||||
|
||||
@@ -26,8 +26,9 @@ extension Snippets on CanUseCommonTables {
|
||||
// this is a nested transaction:
|
||||
await transaction(() async {
|
||||
// At this point, the first category is visible
|
||||
await into(categories)
|
||||
.insert(CategoriesCompanion.insert(name: 'second'));
|
||||
await into(
|
||||
categories,
|
||||
).insert(CategoriesCompanion.insert(name: 'second'));
|
||||
// Here, the second category is only visible inside this nested
|
||||
// transaction.
|
||||
});
|
||||
@@ -37,8 +38,9 @@ extension Snippets on CanUseCommonTables {
|
||||
try {
|
||||
await transaction(() async {
|
||||
// At this point, both categories are visible
|
||||
await into(categories)
|
||||
.insert(CategoriesCompanion.insert(name: 'third'));
|
||||
await into(
|
||||
categories,
|
||||
).insert(CategoriesCompanion.insert(name: 'third'));
|
||||
// The third category is only visible here.
|
||||
throw Exception('Abort in the second nested transaction');
|
||||
});
|
||||
@@ -54,5 +56,6 @@ extension Snippets on CanUseCommonTables {
|
||||
|
||||
// After the transaction, two categories are visible.
|
||||
}
|
||||
|
||||
// #enddocregion nested
|
||||
}
|
||||
|
||||
@@ -26,8 +26,9 @@ abstract class CategoryTodoCount extends View {
|
||||
Query as() =>
|
||||
// Views can select columns defined as expression getters on the class, or
|
||||
// they can reference columns from other tables.
|
||||
select([categories.description, itemCount])
|
||||
.from(categories)
|
||||
.join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
|
||||
select([categories.description, itemCount]).from(categories).join([
|
||||
innerJoin(todos, todos.category.equalsExp(categories.id)),
|
||||
]);
|
||||
}
|
||||
|
||||
// #enddocregion view
|
||||
|
||||
@@ -18,9 +18,10 @@ class CategoryWithCount {
|
||||
@DriftDatabase(
|
||||
tables: [TodoItems, Categories],
|
||||
queries: {
|
||||
'categoriesWithCount': 'SELECT *, '
|
||||
'categoriesWithCount':
|
||||
'SELECT *, '
|
||||
'(SELECT COUNT(*) FROM todo_items WHERE category = c.id) AS "amount" '
|
||||
'FROM categories c;'
|
||||
'FROM categories c;',
|
||||
},
|
||||
)
|
||||
class MyDatabase extends $MyDatabase {
|
||||
@@ -69,16 +70,19 @@ class MyDatabase extends $MyDatabase {
|
||||
// earlier, drift knows how to parse a category. The only thing left to do
|
||||
// manually is extracting the amount.
|
||||
return rows
|
||||
.map((row) => CategoryWithCount(
|
||||
category: categories.map(row.data),
|
||||
count: row.read<int>('amount'),
|
||||
))
|
||||
.map(
|
||||
(row) => CategoryWithCount(
|
||||
category: categories.map(row.data),
|
||||
count: row.read<int>('amount'),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion manual
|
||||
// #enddocregion manual
|
||||
|
||||
// #docregion setup
|
||||
}
|
||||
|
||||
// #enddocregion setup
|
||||
|
||||
@@ -4,9 +4,7 @@ import 'package:drift/native.dart';
|
||||
|
||||
part 'database.g.dart';
|
||||
|
||||
@DriftDatabase(
|
||||
include: {'tables.drift'},
|
||||
)
|
||||
@DriftDatabase(include: {'tables.drift'})
|
||||
class MyDb extends _$MyDb {
|
||||
// This example creates a simple in-memory database (without actual
|
||||
// persistence).
|
||||
@@ -23,5 +21,6 @@ extension MoreSnippets on MyDb {
|
||||
Future<void> insert(TodosCompanion companion) async {
|
||||
await into(todos).insert(companion);
|
||||
}
|
||||
|
||||
// #enddocregion dart_interop_insert
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ QueryExecutor openConnection() {
|
||||
final blob = await rootBundle.load('assets/my_database.db');
|
||||
final buffer = blob.buffer;
|
||||
await file.writeAsBytes(
|
||||
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes));
|
||||
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
|
||||
);
|
||||
}
|
||||
|
||||
return file.path;
|
||||
|
||||
@@ -20,7 +20,8 @@ QueryExecutor openConnection() {
|
||||
final blob = await rootBundle.load('assets/my_database.db');
|
||||
final buffer = blob.buffer;
|
||||
await file.writeAsBytes(
|
||||
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes));
|
||||
buffer.asUint8List(blob.offsetInBytes, blob.lengthInBytes),
|
||||
);
|
||||
}
|
||||
|
||||
// Also work around limitations on old Android versions
|
||||
|
||||
@@ -113,10 +113,12 @@ void main() async {
|
||||
void connectSynchronously() {
|
||||
// #docregion delayed
|
||||
MyDatabase(
|
||||
DatabaseConnection.delayed(Future.sync(() async {
|
||||
final isolate = await createIsolate();
|
||||
return isolate.connect(singleClientMode: true);
|
||||
})),
|
||||
DatabaseConnection.delayed(
|
||||
Future.sync(() async {
|
||||
final isolate = await createIsolate();
|
||||
return isolate.connect(singleClientMode: true);
|
||||
}),
|
||||
),
|
||||
);
|
||||
// #enddocregion delayed
|
||||
}
|
||||
@@ -167,10 +169,12 @@ class _IsolateStartRequest {
|
||||
|
||||
// #docregion init_connect
|
||||
DatabaseConnection createDriftIsolateAndConnect() {
|
||||
return DatabaseConnection.delayed(Future.sync(() async {
|
||||
final isolate = await _createDriftIsolate();
|
||||
return await isolate.connect(singleClientMode: true);
|
||||
}));
|
||||
return DatabaseConnection.delayed(
|
||||
Future.sync(() async {
|
||||
final isolate = await _createDriftIsolate();
|
||||
return await isolate.connect(singleClientMode: true);
|
||||
}),
|
||||
);
|
||||
}
|
||||
// #enddocregion init_connect
|
||||
|
||||
@@ -233,21 +237,19 @@ Future<void> insertBulkData(MyDatabase database) async {
|
||||
Future<void> customIsolateUsage(MyDatabase database) async {
|
||||
final connection = await database.serializableConnection();
|
||||
|
||||
await Isolate.run(
|
||||
() async {
|
||||
// We can't share the [database] object across isolates, but the connection
|
||||
// is fine!
|
||||
final databaseForIsolate = MyDatabase(await connection.connect());
|
||||
await Isolate.run(() async {
|
||||
// We can't share the [database] object across isolates, but the connection
|
||||
// is fine!
|
||||
final databaseForIsolate = MyDatabase(await connection.connect());
|
||||
|
||||
try {
|
||||
await databaseForIsolate.batch((batch) {
|
||||
// (...)
|
||||
});
|
||||
} finally {
|
||||
databaseForIsolate.close();
|
||||
}
|
||||
},
|
||||
debugName: 'My custom database task',
|
||||
);
|
||||
try {
|
||||
await databaseForIsolate.batch((batch) {
|
||||
// (...)
|
||||
});
|
||||
} finally {
|
||||
databaseForIsolate.close();
|
||||
}
|
||||
}, debugName: 'My custom database task');
|
||||
}
|
||||
|
||||
// #enddocregion custom-compute
|
||||
|
||||
@@ -9,7 +9,9 @@ import 'dart_api/manager.dart';
|
||||
// #docregion class
|
||||
class LogInterceptor extends QueryInterceptor {
|
||||
Future<T> _run<T>(
|
||||
String description, FutureOr<T> Function() operation) async {
|
||||
String description,
|
||||
FutureOr<T> Function() operation,
|
||||
) async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
print('Running $description');
|
||||
|
||||
@@ -41,44 +43,73 @@ class LogInterceptor extends QueryInterceptor {
|
||||
|
||||
@override
|
||||
Future<void> runBatched(
|
||||
QueryExecutor executor, BatchedStatements statements) {
|
||||
QueryExecutor executor,
|
||||
BatchedStatements statements,
|
||||
) {
|
||||
return _run(
|
||||
'batch with $statements', () => executor.runBatched(statements));
|
||||
'batch with $statements',
|
||||
() => executor.runBatched(statements),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(
|
||||
QueryExecutor executor, String statement, List<Object?> args) {
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run(
|
||||
'$statement with $args', () => executor.runInsert(statement, args));
|
||||
'$statement with $args',
|
||||
() => executor.runInsert(statement, args),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(
|
||||
QueryExecutor executor, String statement, List<Object?> args) {
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run(
|
||||
'$statement with $args', () => executor.runUpdate(statement, args));
|
||||
'$statement with $args',
|
||||
() => executor.runUpdate(statement, args),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runDelete(
|
||||
QueryExecutor executor, String statement, List<Object?> args) {
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run(
|
||||
'$statement with $args', () => executor.runDelete(statement, args));
|
||||
'$statement with $args',
|
||||
() => executor.runDelete(statement, args),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runCustom(
|
||||
QueryExecutor executor, String statement, List<Object?> args) {
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run(
|
||||
'$statement with $args', () => executor.runCustom(statement, args));
|
||||
'$statement with $args',
|
||||
() => executor.runCustom(statement, args),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Map<String, Object?>>> runSelect(
|
||||
QueryExecutor executor, String statement, List<Object?> args) {
|
||||
QueryExecutor executor,
|
||||
String statement,
|
||||
List<Object?> args,
|
||||
) {
|
||||
return _run(
|
||||
'$statement with $args', () => executor.runSelect(statement, args));
|
||||
'$statement with $args',
|
||||
() => executor.runSelect(statement, args),
|
||||
);
|
||||
}
|
||||
}
|
||||
// #enddocregion class
|
||||
|
||||
@@ -62,10 +62,13 @@ class MyDatabase extends _$MyDatabase {
|
||||
|
||||
// Assert that the schema is valid after migrations
|
||||
if (kDebugMode) {
|
||||
final wrongForeignKeys =
|
||||
await customSelect('PRAGMA foreign_key_check').get();
|
||||
assert(wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}');
|
||||
final wrongForeignKeys = await customSelect(
|
||||
'PRAGMA foreign_key_check',
|
||||
).get();
|
||||
assert(
|
||||
wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}',
|
||||
);
|
||||
}
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
@@ -81,9 +84,12 @@ class MyDatabase extends _$MyDatabase {
|
||||
// #docregion change_type
|
||||
from1To2: (m, schema) async {
|
||||
await m.alterTable(
|
||||
TableMigration(schema.todos, columnTransformer: {
|
||||
todos.category: schema.todos.category.cast<int>(),
|
||||
}),
|
||||
TableMigration(
|
||||
schema.todos,
|
||||
columnTransformer: {
|
||||
todos.category: schema.todos.category.cast<int>(),
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
// #enddocregion change_type
|
||||
|
||||
@@ -14,7 +14,7 @@ abstract class _$MyDatabase extends GeneratedDatabase {
|
||||
// #docregion native
|
||||
|
||||
class MyDatabase extends _$MyDatabase {
|
||||
// #enddocregion native
|
||||
// #enddocregion native
|
||||
MyDatabase(super.executor);
|
||||
|
||||
@override
|
||||
@@ -27,17 +27,22 @@ class MyDatabase extends _$MyDatabase {
|
||||
// #docregion native
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
onCreate: (m) async {/* ... */},
|
||||
onUpgrade: (m, from, to) async {/* your existing migration logic */},
|
||||
beforeOpen: (details) async {
|
||||
// your existing beforeOpen callback, enable foreign keys, etc.
|
||||
onCreate: (m) async {
|
||||
/* ... */
|
||||
},
|
||||
onUpgrade: (m, from, to) async {
|
||||
/* your existing migration logic */
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
// your existing beforeOpen callback, enable foreign keys, etc.
|
||||
|
||||
if (kDebugMode) {
|
||||
// This check pulls in a fair amount of code that's not needed
|
||||
// anywhere else, so we recommend only doing it in debug builds.
|
||||
await validateDatabaseSchema();
|
||||
}
|
||||
},
|
||||
);
|
||||
if (kDebugMode) {
|
||||
// This check pulls in a fair amount of code that's not needed
|
||||
// anywhere else, so we recommend only doing it in debug builds.
|
||||
await validateDatabaseSchema();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion native
|
||||
|
||||
@@ -15,7 +15,7 @@ abstract class _$MyDatabase extends GeneratedDatabase {
|
||||
// #docregion web
|
||||
|
||||
class MyDatabase extends _$MyDatabase {
|
||||
// #enddocregion web
|
||||
// #enddocregion web
|
||||
MyDatabase(super.executor);
|
||||
|
||||
@override
|
||||
@@ -28,22 +28,29 @@ class MyDatabase extends _$MyDatabase {
|
||||
// #docregion web
|
||||
@override
|
||||
MigrationStrategy get migration => MigrationStrategy(
|
||||
onCreate: (m) async {/* ... */},
|
||||
onUpgrade: (m, from, to) async {/* your existing migration logic */},
|
||||
beforeOpen: (details) async {
|
||||
// your existing beforeOpen callback, enable foreign keys, etc.
|
||||
onCreate: (m) async {
|
||||
/* ... */
|
||||
},
|
||||
onUpgrade: (m, from, to) async {
|
||||
/* your existing migration logic */
|
||||
},
|
||||
beforeOpen: (details) async {
|
||||
// your existing beforeOpen callback, enable foreign keys, etc.
|
||||
|
||||
// This check pulls in a fair amount of code that's not needed
|
||||
// anywhere else, so we recommend only doing it in debug builds.
|
||||
if (kDebugMode) {
|
||||
// The web schema verifier needs a sqlite3 instance to open another
|
||||
// version of your database so that the two can be compared.
|
||||
final sqlite3 = await WasmSqlite3.loadFromUrl(Uri.parse('/'));
|
||||
sqlite3.registerVirtualFileSystem(InMemoryFileSystem(),
|
||||
makeDefault: true);
|
||||
await validateDatabaseSchema(sqlite3: sqlite3);
|
||||
}
|
||||
},
|
||||
);
|
||||
// This check pulls in a fair amount of code that's not needed
|
||||
// anywhere else, so we recommend only doing it in debug builds.
|
||||
if (kDebugMode) {
|
||||
// The web schema verifier needs a sqlite3 instance to open another
|
||||
// version of your database so that the two can be compared.
|
||||
final sqlite3 = await WasmSqlite3.loadFromUrl(Uri.parse('/'));
|
||||
sqlite3.registerVirtualFileSystem(
|
||||
InMemoryFileSystem(),
|
||||
makeDefault: true,
|
||||
);
|
||||
await validateDatabaseSchema(sqlite3: sqlite3);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion web
|
||||
|
||||
@@ -7,25 +7,18 @@ import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
||||
final class Schema2 extends i0.VersionedSchema {
|
||||
Schema2({required super.database}) : super(version: 2);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
todos,
|
||||
];
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [todos];
|
||||
late final Shape0 todos = Shape0(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'todos',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_0,
|
||||
_column_1,
|
||||
_column_2,
|
||||
_column_3,
|
||||
_column_4,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'todos',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [_column_0, _column_1, _column_2, _column_3, _column_4],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
}
|
||||
|
||||
class Shape0 extends i0.VersionedTable {
|
||||
@@ -43,49 +36,71 @@ class Shape0 extends i0.VersionedTable {
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_0(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints:
|
||||
i1.GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
i1.GeneratedColumn<int>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
hasAutoIncrement: true,
|
||||
type: i1.DriftSqlType.int,
|
||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
||||
'PRIMARY KEY AUTOINCREMENT',
|
||||
),
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('title', aliasedName, false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 6, maxTextLength: 10),
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String>(
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
additionalChecks: i1.GeneratedColumn.checkTextLength(
|
||||
minTextLength: 6,
|
||||
maxTextLength: 10,
|
||||
),
|
||||
type: i1.DriftSqlType.string,
|
||||
);
|
||||
i1.GeneratedColumn<String> _column_2(String aliasedName) =>
|
||||
i1.GeneratedColumn<String>('body', aliasedName, false,
|
||||
type: i1.DriftSqlType.string);
|
||||
i1.GeneratedColumn<String>(
|
||||
'body',
|
||||
aliasedName,
|
||||
false,
|
||||
type: i1.DriftSqlType.string,
|
||||
);
|
||||
i1.GeneratedColumn<int> _column_3(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('category', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int>(
|
||||
'category',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
);
|
||||
i1.GeneratedColumn<DateTime> _column_4(String aliasedName) =>
|
||||
i1.GeneratedColumn<DateTime>('due_date', aliasedName, true,
|
||||
type: i1.DriftSqlType.dateTime);
|
||||
i1.GeneratedColumn<DateTime>(
|
||||
'due_date',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.dateTime,
|
||||
);
|
||||
|
||||
final class Schema3 extends i0.VersionedSchema {
|
||||
Schema3({required super.database}) : super(version: 3);
|
||||
@override
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||
todos,
|
||||
];
|
||||
late final List<i1.DatabaseSchemaEntity> entities = [todos];
|
||||
late final Shape1 todos = Shape1(
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'todos',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_0,
|
||||
_column_1,
|
||||
_column_2,
|
||||
_column_3,
|
||||
_column_4,
|
||||
_column_5,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null);
|
||||
source: i0.VersionedTable(
|
||||
entityName: 'todos',
|
||||
withoutRowId: false,
|
||||
isStrict: false,
|
||||
tableConstraints: [],
|
||||
columns: [
|
||||
_column_0,
|
||||
_column_1,
|
||||
_column_2,
|
||||
_column_3,
|
||||
_column_4,
|
||||
_column_5,
|
||||
],
|
||||
attachedDatabase: database,
|
||||
),
|
||||
alias: null,
|
||||
);
|
||||
}
|
||||
|
||||
class Shape1 extends i0.VersionedTable {
|
||||
@@ -105,8 +120,12 @@ class Shape1 extends i0.VersionedTable {
|
||||
}
|
||||
|
||||
i1.GeneratedColumn<int> _column_5(String aliasedName) =>
|
||||
i1.GeneratedColumn<int>('priority', aliasedName, true,
|
||||
type: i1.DriftSqlType.int);
|
||||
i1.GeneratedColumn<int>(
|
||||
'priority',
|
||||
aliasedName,
|
||||
true,
|
||||
type: i1.DriftSqlType.int,
|
||||
);
|
||||
i0.MigrationStepWithVersion migrationSteps({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
@@ -132,9 +151,6 @@ i0.MigrationStepWithVersion migrationSteps({
|
||||
i1.OnUpgrade stepByStep({
|
||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||
}) =>
|
||||
i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(
|
||||
from1To2: from1To2,
|
||||
from2To3: from2To3,
|
||||
));
|
||||
}) => i0.VersionedSchema.stepByStepHelper(
|
||||
step: migrationSteps(from1To2: from1To2, from2To3: from2To3),
|
||||
);
|
||||
|
||||
@@ -31,6 +31,7 @@ class StepByStep {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion stepbystep
|
||||
}
|
||||
|
||||
@@ -65,10 +66,13 @@ extension StepByStep2 on GeneratedDatabase {
|
||||
|
||||
if (kDebugMode) {
|
||||
// Fail if the migration broke foreign keys
|
||||
final wrongForeignKeys =
|
||||
await customSelect('PRAGMA foreign_key_check').get();
|
||||
assert(wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}');
|
||||
final wrongForeignKeys = await customSelect(
|
||||
'PRAGMA foreign_key_check',
|
||||
).get();
|
||||
assert(
|
||||
wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}',
|
||||
);
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
@@ -119,10 +123,13 @@ extension StepByStep3 on MyDatabase {
|
||||
|
||||
if (kDebugMode) {
|
||||
// Fail if the migration broke foreign keys
|
||||
final wrongForeignKeys =
|
||||
await customSelect('PRAGMA foreign_key_check').get();
|
||||
assert(wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}');
|
||||
final wrongForeignKeys = await customSelect(
|
||||
'PRAGMA foreign_key_check',
|
||||
).get();
|
||||
assert(
|
||||
wrongForeignKeys.isEmpty,
|
||||
'${wrongForeignKeys.map((e) => e.data)}',
|
||||
);
|
||||
}
|
||||
|
||||
await customStatement('PRAGMA foreign_keys = ON;');
|
||||
|
||||
@@ -9,24 +9,41 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
final String? _alias;
|
||||
Todos(this.attachedDatabase, [this._alias]);
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'PRIMARY KEY AUTOINCREMENT',
|
||||
),
|
||||
);
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title', aliasedName, false,
|
||||
additionalChecks:
|
||||
GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 10),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true);
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
additionalChecks: GeneratedColumn.checkTextLength(
|
||||
minTextLength: 6,
|
||||
maxTextLength: 10,
|
||||
),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
||||
'body', aliasedName, false,
|
||||
type: DriftSqlType.string, requiredDuringInsert: true);
|
||||
'body',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<int> category = GeneratedColumn<int>(
|
||||
'category', aliasedName, true,
|
||||
type: DriftSqlType.int, requiredDuringInsert: false);
|
||||
'category',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, title, content, category];
|
||||
@override
|
||||
@@ -40,14 +57,22 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
TodosData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return TodosData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||
title: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}title'])!,
|
||||
content: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}body'])!,
|
||||
category: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}category']),
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
title: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}title'],
|
||||
)!,
|
||||
content: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}body'],
|
||||
)!,
|
||||
category: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}category'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,11 +87,12 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
final String title;
|
||||
final String content;
|
||||
final int? category;
|
||||
const TodosData(
|
||||
{required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category});
|
||||
const TodosData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
@@ -90,8 +116,10 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
);
|
||||
}
|
||||
|
||||
factory TodosData.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer? serializer}) {
|
||||
factory TodosData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return TodosData(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
@@ -111,17 +139,17 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
};
|
||||
}
|
||||
|
||||
TodosData copyWith(
|
||||
{int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent()}) =>
|
||||
TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
);
|
||||
TodosData copyWith({
|
||||
int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent(),
|
||||
}) => TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
);
|
||||
TodosData copyWithCompanion(TodosCompanion data) {
|
||||
return TodosData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
@@ -170,8 +198,8 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
required String title,
|
||||
required String content,
|
||||
this.category = const Value.absent(),
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
static Insertable<TodosData> custom({
|
||||
Expression<int>? id,
|
||||
Expression<String>? title,
|
||||
@@ -186,11 +214,12 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
});
|
||||
}
|
||||
|
||||
TodosCompanion copyWith(
|
||||
{Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category}) {
|
||||
TodosCompanion copyWith({
|
||||
Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category,
|
||||
}) {
|
||||
return TodosCompanion(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
|
||||
@@ -9,27 +9,48 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
final String? _alias;
|
||||
Todos(this.attachedDatabase, [this._alias]);
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'PRIMARY KEY AUTOINCREMENT',
|
||||
),
|
||||
);
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title', aliasedName, false,
|
||||
additionalChecks:
|
||||
GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 10),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true);
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
additionalChecks: GeneratedColumn.checkTextLength(
|
||||
minTextLength: 6,
|
||||
maxTextLength: 10,
|
||||
),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
||||
'body', aliasedName, false,
|
||||
type: DriftSqlType.string, requiredDuringInsert: true);
|
||||
'body',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<int> category = GeneratedColumn<int>(
|
||||
'category', aliasedName, true,
|
||||
type: DriftSqlType.int, requiredDuringInsert: false);
|
||||
'category',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
late final GeneratedColumn<DateTime> dueDate = GeneratedColumn<DateTime>(
|
||||
'due_date', aliasedName, true,
|
||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||
'due_date',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, title, content, category, dueDate];
|
||||
@override
|
||||
@@ -43,16 +64,26 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
TodosData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return TodosData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||
title: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}title'])!,
|
||||
content: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}body'])!,
|
||||
category: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}category']),
|
||||
dueDate: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}due_date']),
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
title: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}title'],
|
||||
)!,
|
||||
content: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}body'],
|
||||
)!,
|
||||
category: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}category'],
|
||||
),
|
||||
dueDate: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}due_date'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,12 +99,13 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
final String content;
|
||||
final int? category;
|
||||
final DateTime? dueDate;
|
||||
const TodosData(
|
||||
{required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category,
|
||||
this.dueDate});
|
||||
const TodosData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category,
|
||||
this.dueDate,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
@@ -103,8 +135,10 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
);
|
||||
}
|
||||
|
||||
factory TodosData.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer? serializer}) {
|
||||
factory TodosData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return TodosData(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
@@ -126,19 +160,19 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
};
|
||||
}
|
||||
|
||||
TodosData copyWith(
|
||||
{int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent(),
|
||||
Value<DateTime?> dueDate = const Value.absent()}) =>
|
||||
TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
dueDate: dueDate.present ? dueDate.value : this.dueDate,
|
||||
);
|
||||
TodosData copyWith({
|
||||
int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent(),
|
||||
Value<DateTime?> dueDate = const Value.absent(),
|
||||
}) => TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
dueDate: dueDate.present ? dueDate.value : this.dueDate,
|
||||
);
|
||||
TodosData copyWithCompanion(TodosCompanion data) {
|
||||
return TodosData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
@@ -193,8 +227,8 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
required String content,
|
||||
this.category = const Value.absent(),
|
||||
this.dueDate = const Value.absent(),
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
static Insertable<TodosData> custom({
|
||||
Expression<int>? id,
|
||||
Expression<String>? title,
|
||||
@@ -211,12 +245,13 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
});
|
||||
}
|
||||
|
||||
TodosCompanion copyWith(
|
||||
{Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category,
|
||||
Value<DateTime?>? dueDate}) {
|
||||
TodosCompanion copyWith({
|
||||
Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category,
|
||||
Value<DateTime?>? dueDate,
|
||||
}) {
|
||||
return TodosCompanion(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
|
||||
@@ -9,33 +9,64 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
final String? _alias;
|
||||
Todos(this.attachedDatabase, [this._alias]);
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id', aliasedName, false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints:
|
||||
GeneratedColumn.constraintIsAlways('PRIMARY KEY AUTOINCREMENT'));
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
hasAutoIncrement: true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'PRIMARY KEY AUTOINCREMENT',
|
||||
),
|
||||
);
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title', aliasedName, false,
|
||||
additionalChecks:
|
||||
GeneratedColumn.checkTextLength(minTextLength: 6, maxTextLength: 10),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true);
|
||||
'title',
|
||||
aliasedName,
|
||||
false,
|
||||
additionalChecks: GeneratedColumn.checkTextLength(
|
||||
minTextLength: 6,
|
||||
maxTextLength: 10,
|
||||
),
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<String> content = GeneratedColumn<String>(
|
||||
'body', aliasedName, false,
|
||||
type: DriftSqlType.string, requiredDuringInsert: true);
|
||||
'body',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
late final GeneratedColumn<int> category = GeneratedColumn<int>(
|
||||
'category', aliasedName, true,
|
||||
type: DriftSqlType.int, requiredDuringInsert: false);
|
||||
'category',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
late final GeneratedColumn<DateTime> dueDate = GeneratedColumn<DateTime>(
|
||||
'due_date', aliasedName, true,
|
||||
type: DriftSqlType.dateTime, requiredDuringInsert: false);
|
||||
'due_date',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
late final GeneratedColumn<int> priority = GeneratedColumn<int>(
|
||||
'priority', aliasedName, true,
|
||||
type: DriftSqlType.int, requiredDuringInsert: false);
|
||||
'priority',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.int,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[id, title, content, category, dueDate, priority];
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
title,
|
||||
content,
|
||||
category,
|
||||
dueDate,
|
||||
priority,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
@@ -47,18 +78,30 @@ class Todos extends Table with TableInfo<Todos, TodosData> {
|
||||
TodosData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return TodosData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||
title: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}title'])!,
|
||||
content: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}body'])!,
|
||||
category: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}category']),
|
||||
dueDate: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.dateTime, data['${effectivePrefix}due_date']),
|
||||
priority: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}priority']),
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
title: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}title'],
|
||||
)!,
|
||||
content: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}body'],
|
||||
)!,
|
||||
category: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}category'],
|
||||
),
|
||||
dueDate: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}due_date'],
|
||||
),
|
||||
priority: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.int,
|
||||
data['${effectivePrefix}priority'],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,13 +118,14 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
final int? category;
|
||||
final DateTime? dueDate;
|
||||
final int? priority;
|
||||
const TodosData(
|
||||
{required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category,
|
||||
this.dueDate,
|
||||
this.priority});
|
||||
const TodosData({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.content,
|
||||
this.category,
|
||||
this.dueDate,
|
||||
this.priority,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
@@ -117,8 +161,10 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
);
|
||||
}
|
||||
|
||||
factory TodosData.fromJson(Map<String, dynamic> json,
|
||||
{ValueSerializer? serializer}) {
|
||||
factory TodosData.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return TodosData(
|
||||
id: serializer.fromJson<int>(json['id']),
|
||||
@@ -142,21 +188,21 @@ class TodosData extends DataClass implements Insertable<TodosData> {
|
||||
};
|
||||
}
|
||||
|
||||
TodosData copyWith(
|
||||
{int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent(),
|
||||
Value<DateTime?> dueDate = const Value.absent(),
|
||||
Value<int?> priority = const Value.absent()}) =>
|
||||
TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
dueDate: dueDate.present ? dueDate.value : this.dueDate,
|
||||
priority: priority.present ? priority.value : this.priority,
|
||||
);
|
||||
TodosData copyWith({
|
||||
int? id,
|
||||
String? title,
|
||||
String? content,
|
||||
Value<int?> category = const Value.absent(),
|
||||
Value<DateTime?> dueDate = const Value.absent(),
|
||||
Value<int?> priority = const Value.absent(),
|
||||
}) => TodosData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
category: category.present ? category.value : this.category,
|
||||
dueDate: dueDate.present ? dueDate.value : this.dueDate,
|
||||
priority: priority.present ? priority.value : this.priority,
|
||||
);
|
||||
TodosData copyWithCompanion(TodosCompanion data) {
|
||||
return TodosData(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
@@ -218,8 +264,8 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
this.category = const Value.absent(),
|
||||
this.dueDate = const Value.absent(),
|
||||
this.priority = const Value.absent(),
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
}) : title = Value(title),
|
||||
content = Value(content);
|
||||
static Insertable<TodosData> custom({
|
||||
Expression<int>? id,
|
||||
Expression<String>? title,
|
||||
@@ -238,13 +284,14 @@ class TodosCompanion extends UpdateCompanion<TodosData> {
|
||||
});
|
||||
}
|
||||
|
||||
TodosCompanion copyWith(
|
||||
{Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category,
|
||||
Value<DateTime?>? dueDate,
|
||||
Value<int?>? priority}) {
|
||||
TodosCompanion copyWith({
|
||||
Value<int>? id,
|
||||
Value<String>? title,
|
||||
Value<String>? content,
|
||||
Value<int?>? category,
|
||||
Value<DateTime?>? dueDate,
|
||||
Value<int?>? priority,
|
||||
}) {
|
||||
return TodosCompanion(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
|
||||
@@ -29,4 +29,5 @@ void main() {
|
||||
await verifier.migrateAndValidate(db, 2);
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion setup
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'generated_migrations/schema_v2.dart' as v2;
|
||||
|
||||
// #docregion main
|
||||
void main() {
|
||||
// #enddocregion main
|
||||
// #enddocregion main
|
||||
late SchemaVerifier verifier;
|
||||
|
||||
setUpAll(() {
|
||||
@@ -20,17 +20,21 @@ void main() {
|
||||
verifier = SchemaVerifier(GeneratedHelper());
|
||||
});
|
||||
|
||||
// #docregion main
|
||||
// #docregion main
|
||||
// ...
|
||||
test('upgrade from v1 to v2', () async {
|
||||
final schema = await verifier.schemaAt(1);
|
||||
|
||||
// Add some data to the table being migrated
|
||||
final oldDb = v1.DatabaseAtV1(schema.newConnection());
|
||||
await oldDb.into(oldDb.todos).insert(v1.TodosCompanion.insert(
|
||||
title: 'my first todo entry',
|
||||
content: 'should still be there after the migration',
|
||||
));
|
||||
await oldDb
|
||||
.into(oldDb.todos)
|
||||
.insert(
|
||||
v1.TodosCompanion.insert(
|
||||
title: 'my first todo entry',
|
||||
content: 'should still be there after the migration',
|
||||
),
|
||||
);
|
||||
await oldDb.close();
|
||||
|
||||
// Run the migration and verify that it adds the name column.
|
||||
@@ -46,4 +50,5 @@ void main() {
|
||||
await migratedDb.close();
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion main
|
||||
|
||||
@@ -3,7 +3,8 @@ import 'type.dart';
|
||||
|
||||
class PeriodicReminders extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
Column<Duration> get frequency => customType(const DurationType())
|
||||
.clientDefault(() => Duration(minutes: 15))();
|
||||
Column<Duration> get frequency => customType(
|
||||
const DurationType(),
|
||||
).clientDefault(() => Duration(minutes: 15))();
|
||||
TextColumn get reminder => text()();
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ class _FallbackDurationType implements CustomSqlType<Duration> {
|
||||
|
||||
const durationType = DialectAwareSqlType<Duration>.via(
|
||||
fallback: _FallbackDurationType(),
|
||||
overrides: {
|
||||
SqlDialect.postgres: DurationType(),
|
||||
},
|
||||
overrides: {SqlDialect.postgres: DurationType()},
|
||||
);
|
||||
// #enddocregion fallback
|
||||
|
||||
@@ -7,5 +7,6 @@ class DartExample extends ExampleDrift {
|
||||
Stream<List<Todo>> watchInCategory(int category) {
|
||||
return filterTodos((todos) => todos.category.equals(category)).watch();
|
||||
}
|
||||
|
||||
// #enddocregion watchInCategory
|
||||
}
|
||||
|
||||
@@ -14,4 +14,5 @@ class UserWithFriends {
|
||||
|
||||
UserWithFriends(this.user, {this.friends = const []});
|
||||
}
|
||||
|
||||
// #enddocregion userwithfriends
|
||||
|
||||
@@ -8,10 +8,7 @@ import 'shared.drift.dart';
|
||||
|
||||
part 'json.g.dart';
|
||||
|
||||
typedef ShoppingCartWithItems = ({
|
||||
ShoppingCart cart,
|
||||
List<BuyableItem> items,
|
||||
});
|
||||
typedef ShoppingCartWithItems = ({ShoppingCart cart, List<BuyableItem> items});
|
||||
|
||||
// #docregion tables
|
||||
@DataClassName('ShoppingCart')
|
||||
@@ -54,8 +51,9 @@ class JsonBasedDatabase extends $JsonBasedDatabase {
|
||||
|
||||
// #docregion createEmptyCart
|
||||
Future<ShoppingCartWithItems> createEmptyCart() async {
|
||||
final cart = await into(shoppingCarts)
|
||||
.insertReturning(const ShoppingCartsCompanion());
|
||||
final cart = await into(
|
||||
shoppingCarts,
|
||||
).insertReturning(const ShoppingCartsCompanion());
|
||||
|
||||
// we set the items property to [] because we've just created the cart - it
|
||||
// will be empty
|
||||
@@ -65,10 +63,13 @@ class JsonBasedDatabase extends $JsonBasedDatabase {
|
||||
|
||||
// #docregion updateCart
|
||||
Future<void> updateCart(ShoppingCartWithItems entry) async {
|
||||
await update(shoppingCarts).replace(entry.cart.copyWith(
|
||||
entries: ShoppingCartEntries(items: [
|
||||
for (final item in entry.items) item.id,
|
||||
])));
|
||||
await update(shoppingCarts).replace(
|
||||
entry.cart.copyWith(
|
||||
entries: ShoppingCartEntries(
|
||||
items: [for (final item in entry.items) item.id],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
// #enddocregion updateCart
|
||||
|
||||
@@ -76,17 +77,15 @@ class JsonBasedDatabase extends $JsonBasedDatabase {
|
||||
Stream<ShoppingCartWithItems> watchCart(int id) {
|
||||
final referencedItems = shoppingCarts.entries.jsonEach(this, r'#$.items');
|
||||
|
||||
final cartWithEntries = select(shoppingCarts).join(
|
||||
[
|
||||
// Join every referenced item from the json array
|
||||
innerJoin(referencedItems, const Constant(true), useColumns: false),
|
||||
// And use that to join the items
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(referencedItems.value.cast()),
|
||||
),
|
||||
],
|
||||
)..where(shoppingCarts.id.equals(id));
|
||||
final cartWithEntries = select(shoppingCarts).join([
|
||||
// Join every referenced item from the json array
|
||||
innerJoin(referencedItems, const Constant(true), useColumns: false),
|
||||
// And use that to join the items
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(referencedItems.value.cast()),
|
||||
),
|
||||
])..where(shoppingCarts.id.equals(id));
|
||||
|
||||
return cartWithEntries.watch().map((rows) {
|
||||
late ShoppingCart cart;
|
||||
@@ -106,17 +105,15 @@ class JsonBasedDatabase extends $JsonBasedDatabase {
|
||||
Stream<List<ShoppingCartWithItems>> watchAllCarts() {
|
||||
final referencedItems = shoppingCarts.entries.jsonEach(this, r'#$.items');
|
||||
|
||||
final cartWithEntries = select(shoppingCarts).join(
|
||||
[
|
||||
// Join every referenced item from the json array
|
||||
innerJoin(referencedItems, const Constant(true), useColumns: false),
|
||||
// And use that to join the items
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(referencedItems.value.cast()),
|
||||
),
|
||||
],
|
||||
);
|
||||
final cartWithEntries = select(shoppingCarts).join([
|
||||
// Join every referenced item from the json array
|
||||
innerJoin(referencedItems, const Constant(true), useColumns: false),
|
||||
// And use that to join the items
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(referencedItems.value.cast()),
|
||||
),
|
||||
]);
|
||||
|
||||
return cartWithEntries.watch().map((rows) {
|
||||
final entriesByCart = <ShoppingCart, List<BuyableItem>>{};
|
||||
@@ -130,9 +127,10 @@ class JsonBasedDatabase extends $JsonBasedDatabase {
|
||||
|
||||
return [
|
||||
for (final entry in entriesByCart.entries)
|
||||
(cart: entry.key, items: entry.value)
|
||||
(cart: entry.key, items: entry.value),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion watchAllCarts
|
||||
}
|
||||
|
||||
@@ -6,10 +6,7 @@ import 'shared.drift.dart';
|
||||
|
||||
import 'relational.drift.dart';
|
||||
|
||||
typedef ShoppingCartWithItems = ({
|
||||
ShoppingCart cart,
|
||||
List<BuyableItem> items,
|
||||
});
|
||||
typedef ShoppingCartWithItems = ({ShoppingCart cart, List<BuyableItem> items});
|
||||
|
||||
// #docregion cart_tables
|
||||
class ShoppingCarts extends Table {
|
||||
@@ -45,14 +42,15 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
await update(shoppingCarts).replace(cart);
|
||||
|
||||
// we replace the entries of the cart, so first delete the old ones
|
||||
await (delete(shoppingCartEntries)
|
||||
..where((entry) => entry.shoppingCart.equals(cart.id)))
|
||||
.go();
|
||||
await (delete(
|
||||
shoppingCartEntries,
|
||||
)..where((entry) => entry.shoppingCart.equals(cart.id))).go();
|
||||
|
||||
// And write the new ones
|
||||
for (final item in entry.items) {
|
||||
await into(shoppingCartEntries)
|
||||
.insert(ShoppingCartEntry(shoppingCart: cart.id, item: item.id));
|
||||
await into(
|
||||
shoppingCartEntries,
|
||||
).insert(ShoppingCartEntry(shoppingCart: cart.id, item: item.id));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -60,8 +58,9 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
|
||||
// #docregion createEmptyCart
|
||||
Future<ShoppingCartWithItems> createEmptyCart() async {
|
||||
final cart = await into(shoppingCarts)
|
||||
.insertReturning(const ShoppingCartsCompanion());
|
||||
final cart = await into(
|
||||
shoppingCarts,
|
||||
).insertReturning(const ShoppingCartsCompanion());
|
||||
// we set the items property to [] because we've just created the cart - it
|
||||
// will be empty
|
||||
return (cart: cart, items: <BuyableItem>[]);
|
||||
@@ -75,14 +74,12 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
..where((cart) => cart.id.equals(id));
|
||||
|
||||
// and also load information about the entries in this cart
|
||||
final contentQuery = select(shoppingCartEntries).join(
|
||||
[
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(shoppingCartEntries.item),
|
||||
),
|
||||
],
|
||||
)..where(shoppingCartEntries.shoppingCart.equals(id));
|
||||
final contentQuery = select(shoppingCartEntries).join([
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(shoppingCartEntries.item),
|
||||
),
|
||||
])..where(shoppingCartEntries.shoppingCart.equals(id));
|
||||
|
||||
final cartStream = cartQuery.watchSingle();
|
||||
|
||||
@@ -93,8 +90,10 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
});
|
||||
|
||||
// now, we can merge the two queries together in one stream
|
||||
return Rx.combineLatest2(cartStream, contentStream,
|
||||
(ShoppingCart cart, List<BuyableItem> items) {
|
||||
return Rx.combineLatest2(cartStream, contentStream, (
|
||||
ShoppingCart cart,
|
||||
List<BuyableItem> items,
|
||||
) {
|
||||
return (cart: cart, items: items);
|
||||
});
|
||||
}
|
||||
@@ -113,14 +112,12 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
final ids = idToCart.keys;
|
||||
|
||||
// select all entries that are included in any cart that we found
|
||||
final entryQuery = select(shoppingCartEntries).join(
|
||||
[
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(shoppingCartEntries.item),
|
||||
)
|
||||
],
|
||||
)..where(shoppingCartEntries.shoppingCart.isIn(ids));
|
||||
final entryQuery = select(shoppingCartEntries).join([
|
||||
innerJoin(
|
||||
buyableItems,
|
||||
buyableItems.id.equalsExp(shoppingCartEntries.item),
|
||||
),
|
||||
])..where(shoppingCartEntries.shoppingCart.isIn(ids));
|
||||
|
||||
return entryQuery.watch().map((rows) {
|
||||
// Store the list of entries for each cart, again using maps for faster
|
||||
@@ -144,5 +141,6 @@ class RelationalDatabase extends $RelationalDatabase {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// #enddocregion watchAllCarts
|
||||
}
|
||||
|
||||
@@ -9,10 +9,7 @@ abstract class ShoppingCart {
|
||||
}
|
||||
|
||||
// #docregion interface
|
||||
typedef ShoppingCartWithItems = ({
|
||||
ShoppingCart cart,
|
||||
List<BuyableItem> items,
|
||||
});
|
||||
typedef ShoppingCartWithItems = ({ShoppingCart cart, List<BuyableItem> items});
|
||||
|
||||
abstract class CartRepository {
|
||||
Future<ShoppingCartWithItems> createEmptyCart();
|
||||
@@ -30,4 +27,5 @@ class BuyableItems extends Table {
|
||||
IntColumn get price => integer()();
|
||||
// we could add more columns as we wish.
|
||||
}
|
||||
|
||||
// #enddocregion buyable_items
|
||||
|
||||
@@ -6,21 +6,23 @@ import 'drift/example.drift.dart';
|
||||
extension FindById<Table extends HasResultSet, Row>
|
||||
on ResultSetImplementation<Table, Row> {
|
||||
Selectable<Row> findById(int id) {
|
||||
return select()
|
||||
..where((row) {
|
||||
final idColumn = columnsByName['id'];
|
||||
return select()..where((row) {
|
||||
final idColumn = columnsByName['id'];
|
||||
|
||||
if (idColumn == null) {
|
||||
throw ArgumentError.value(
|
||||
this, 'this', 'Must be a table with an id column');
|
||||
}
|
||||
if (idColumn == null) {
|
||||
throw ArgumentError.value(
|
||||
this,
|
||||
'this',
|
||||
'Must be a table with an id column',
|
||||
);
|
||||
}
|
||||
|
||||
if (idColumn.type != DriftSqlType.int) {
|
||||
throw ArgumentError('Column `id` is not an integer');
|
||||
}
|
||||
if (idColumn.type != DriftSqlType.int) {
|
||||
throw ArgumentError('Column `id` is not an integer');
|
||||
}
|
||||
|
||||
return idColumn.equals(id);
|
||||
});
|
||||
return idColumn.equals(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
// #enddocregion findById
|
||||
@@ -28,7 +30,10 @@ extension FindById<Table extends HasResultSet, Row>
|
||||
// #docregion updateTitle
|
||||
extension UpdateTitle on DatabaseConnectionUser {
|
||||
Future<Row?> updateTitle<T extends TableInfo<Table, Row>, Row>(
|
||||
T table, int id, String newTitle) async {
|
||||
T table,
|
||||
int id,
|
||||
String newTitle,
|
||||
) async {
|
||||
final columnsByName = table.columnsByName;
|
||||
final stmt = update(table)
|
||||
..where((tbl) {
|
||||
@@ -36,7 +41,10 @@ extension UpdateTitle on DatabaseConnectionUser {
|
||||
|
||||
if (idColumn == null) {
|
||||
throw ArgumentError.value(
|
||||
this, 'this', 'Must be a table with an id column');
|
||||
this,
|
||||
'this',
|
||||
'Must be a table with an id column',
|
||||
);
|
||||
}
|
||||
|
||||
if (idColumn.type != DriftSqlType.int) {
|
||||
@@ -46,9 +54,9 @@ extension UpdateTitle on DatabaseConnectionUser {
|
||||
return idColumn.equals(id);
|
||||
});
|
||||
|
||||
final rows = await stmt.writeReturning(RawValuesInsertable({
|
||||
'title': Variable<String>(newTitle),
|
||||
}));
|
||||
final rows = await stmt.writeReturning(
|
||||
RawValuesInsertable({'title': Variable<String>(newTitle)}),
|
||||
);
|
||||
|
||||
return rows.singleOrNull;
|
||||
}
|
||||
@@ -68,5 +76,6 @@ extension FindTodoEntryById on GeneratedDatabase {
|
||||
Future<Todo?> updateTodoTitle(int id, String newTitle) {
|
||||
return updateTitle(todos, id, newTitle);
|
||||
}
|
||||
|
||||
// #enddocregion updateTodo
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ class MatchResults extends Table {
|
||||
|
||||
@override
|
||||
List<Set<Column<Object>>>? get uniqueKeys => [
|
||||
{teamA, teamB}
|
||||
];
|
||||
{teamA, teamB},
|
||||
];
|
||||
}
|
||||
// #enddocregion upsert-target
|
||||
|
||||
@@ -36,7 +36,8 @@ extension DocumentationSnippets on ModularAccessor {
|
||||
return into(words).insert(
|
||||
WordsCompanion.insert(word: word),
|
||||
onConflict: DoUpdate(
|
||||
(old) => WordsCompanion.custom(usages: old.usages + Constant(1))),
|
||||
(old) => WordsCompanion.custom(usages: old.usages + Constant(1)),
|
||||
),
|
||||
);
|
||||
}
|
||||
// #enddocregion track-word
|
||||
@@ -44,11 +45,19 @@ extension DocumentationSnippets on ModularAccessor {
|
||||
// #docregion upsert-target
|
||||
Future<void> insertMatch(String teamA, String teamB, bool teamAWon) {
|
||||
final data = MatchResultsCompanion.insert(
|
||||
teamA: teamA, teamB: teamB, teamAWon: teamAWon);
|
||||
teamA: teamA,
|
||||
teamB: teamB,
|
||||
teamAWon: teamAWon,
|
||||
);
|
||||
|
||||
return into(matches).insert(data,
|
||||
onConflict:
|
||||
DoUpdate((old) => data, target: [matches.teamA, matches.teamB]));
|
||||
return into(matches).insert(
|
||||
data,
|
||||
onConflict: DoUpdate(
|
||||
(old) => data,
|
||||
target: [matches.teamA, matches.teamB],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion upsert-target
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import 'package:drift/web/worker.dart';
|
||||
class Approach1 {
|
||||
// #docregion approach1
|
||||
Future<DatabaseConnection> connectToWorker() async {
|
||||
return await connectToDriftWorker('/database_worker.dart.js',
|
||||
mode: DriftWorkerMode.dedicatedInShared);
|
||||
return await connectToDriftWorker(
|
||||
'/database_worker.dart.js',
|
||||
mode: DriftWorkerMode.dedicatedInShared,
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion approach1
|
||||
}
|
||||
|
||||
@@ -13,4 +13,5 @@ DynamicLibrary _openOnLinux() {
|
||||
final libraryNextToScript = File('${scriptDir.path}/sqlite3.so');
|
||||
return DynamicLibrary.open(libraryNextToScript.path);
|
||||
}
|
||||
|
||||
// _openOnWindows could be implemented similarly by opening `sqlite3.dll`
|
||||
|
||||
@@ -125,4 +125,5 @@ class DialectAwareTime extends Table {
|
||||
// default date type (integer or text) on sqlite databases.
|
||||
Column<PgDateTime> get timeValue => customType(dateTime)();
|
||||
}
|
||||
|
||||
// #enddocregion time-dialectaware
|
||||
|
||||
@@ -50,4 +50,5 @@ QueryExecutor openMultiThreadedDatabase() {
|
||||
readPool: 4,
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion background-pool
|
||||
|
||||
@@ -10,23 +10,27 @@ typedef _$MyWebDatabase = GeneratedDatabase;
|
||||
|
||||
// #docregion connect
|
||||
DatabaseConnection connectOnWeb() {
|
||||
return DatabaseConnection.delayed(Future(() async {
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app_db', // prefer to only use valid identifiers here
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
);
|
||||
return DatabaseConnection.delayed(
|
||||
Future(() async {
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app_db', // prefer to only use valid identifiers here
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
);
|
||||
|
||||
if (result.missingFeatures.isNotEmpty) {
|
||||
// Depending how central local persistence is to your app, you may want
|
||||
// to show a warning to the user if only unrealiable implemetentations
|
||||
// are available.
|
||||
print('Using ${result.chosenImplementation} due to missing browser '
|
||||
'features: ${result.missingFeatures}');
|
||||
}
|
||||
if (result.missingFeatures.isNotEmpty) {
|
||||
// Depending how central local persistence is to your app, you may want
|
||||
// to show a warning to the user if only unrealiable implemetentations
|
||||
// are available.
|
||||
print(
|
||||
'Using ${result.chosenImplementation} due to missing browser '
|
||||
'features: ${result.missingFeatures}',
|
||||
);
|
||||
}
|
||||
|
||||
return result.resolvedExecutor;
|
||||
}));
|
||||
return result.resolvedExecutor;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// You can then use this method to open your database:
|
||||
@@ -43,7 +47,7 @@ class MyWebDatabase extends _$MyWebDatabase {
|
||||
|
||||
@override
|
||||
int get schemaVersion => throw UnimplementedError();
|
||||
// #docregion connect
|
||||
// #docregion connect
|
||||
}
|
||||
// #enddocregion connect
|
||||
|
||||
@@ -61,20 +65,22 @@ QueryExecutor connectWithDriftFlutter() {
|
||||
|
||||
DatabaseConnection connectWithExisting() {
|
||||
// #docregion existing
|
||||
return DatabaseConnection.delayed(Future(() async {
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app_db', // prefer to only use valid identifiers here
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
final data = await rootBundle.load('my_database');
|
||||
return data.buffer.asUint8List();
|
||||
},
|
||||
);
|
||||
return DatabaseConnection.delayed(
|
||||
Future(() async {
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app_db', // prefer to only use valid identifiers here
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
final data = await rootBundle.load('my_database');
|
||||
return data.buffer.asUint8List();
|
||||
},
|
||||
);
|
||||
|
||||
// ...
|
||||
return result.resolvedExecutor;
|
||||
}));
|
||||
// ...
|
||||
return result.resolvedExecutor;
|
||||
}),
|
||||
);
|
||||
// #enddocregion existing
|
||||
}
|
||||
|
||||
@@ -85,72 +91,75 @@ Future<WasmDatabase> customDatabase() async {
|
||||
final fs = await IndexedDbFileSystem.open(dbName: 'my_app');
|
||||
sqlite3.registerVirtualFileSystem(fs, makeDefault: true);
|
||||
|
||||
return WasmDatabase(
|
||||
sqlite3: sqlite3,
|
||||
path: '/app.db',
|
||||
);
|
||||
return WasmDatabase(sqlite3: sqlite3, path: '/app.db');
|
||||
}
|
||||
// #enddocregion migrate-wasm
|
||||
|
||||
DatabaseConnection migrateAndConnect() {
|
||||
return DatabaseConnection.delayed(Future(() async {
|
||||
// #docregion migrate-wasm
|
||||
// Then you can migrate like this
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app',
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
// Manually open the file system previously used
|
||||
final fs = await IndexedDbFileSystem.open(dbName: 'my_app');
|
||||
const oldPath = '/app.db'; // The path passed to WasmDatabase before
|
||||
return DatabaseConnection.delayed(
|
||||
Future(() async {
|
||||
// #docregion migrate-wasm
|
||||
// Then you can migrate like this
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app',
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
// Manually open the file system previously used
|
||||
final fs = await IndexedDbFileSystem.open(dbName: 'my_app');
|
||||
const oldPath = '/app.db'; // The path passed to WasmDatabase before
|
||||
|
||||
Uint8List? oldDatabase;
|
||||
Uint8List? oldDatabase;
|
||||
|
||||
// Check if the old database exists
|
||||
if (fs.xAccess(oldPath, 0) != 0) {
|
||||
// It does, then copy the old file
|
||||
final (file: file, outFlags: _) =
|
||||
fs.xOpen(Sqlite3Filename(oldPath), 0);
|
||||
final blob = Uint8List(file.xFileSize());
|
||||
file.xRead(blob, 0);
|
||||
file.xClose();
|
||||
fs.xDelete(oldPath, 0);
|
||||
// Check if the old database exists
|
||||
if (fs.xAccess(oldPath, 0) != 0) {
|
||||
// It does, then copy the old file
|
||||
final (file: file, outFlags: _) = fs.xOpen(
|
||||
Sqlite3Filename(oldPath),
|
||||
0,
|
||||
);
|
||||
final blob = Uint8List(file.xFileSize());
|
||||
file.xRead(blob, 0);
|
||||
file.xClose();
|
||||
fs.xDelete(oldPath, 0);
|
||||
|
||||
oldDatabase = blob;
|
||||
}
|
||||
oldDatabase = blob;
|
||||
}
|
||||
|
||||
await fs.close();
|
||||
return oldDatabase;
|
||||
},
|
||||
);
|
||||
// #enddocregion migrate-wasm
|
||||
await fs.close();
|
||||
return oldDatabase;
|
||||
},
|
||||
);
|
||||
// #enddocregion migrate-wasm
|
||||
|
||||
return result.resolvedExecutor;
|
||||
}));
|
||||
return result.resolvedExecutor;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
DatabaseConnection migrateFromLegacy() {
|
||||
return DatabaseConnection.delayed(Future(() async {
|
||||
// #docregion migrate-legacy
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app',
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
final storage = await DriftWebStorage.indexedDbIfSupported('old_db');
|
||||
await storage.open();
|
||||
return DatabaseConnection.delayed(
|
||||
Future(() async {
|
||||
// #docregion migrate-legacy
|
||||
final result = await WasmDatabase.open(
|
||||
databaseName: 'my_app',
|
||||
sqlite3Uri: Uri.parse('sqlite3.wasm'),
|
||||
driftWorkerUri: Uri.parse('drift_worker.dart.js'),
|
||||
initializeDatabase: () async {
|
||||
final storage = await DriftWebStorage.indexedDbIfSupported('old_db');
|
||||
await storage.open();
|
||||
|
||||
final blob = await storage.restore();
|
||||
await storage.close();
|
||||
final blob = await storage.restore();
|
||||
await storage.close();
|
||||
|
||||
return blob;
|
||||
},
|
||||
);
|
||||
// #enddocregion migrate-legacy
|
||||
return blob;
|
||||
},
|
||||
);
|
||||
// #enddocregion migrate-legacy
|
||||
|
||||
return result.resolvedExecutor;
|
||||
}));
|
||||
return result.resolvedExecutor;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// #docregion setupAll
|
||||
@@ -162,9 +171,7 @@ void setupDatabase(CommonDatabase database) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
WasmDatabase.workerMainForOpen(
|
||||
setupAllDatabases: setupDatabase,
|
||||
);
|
||||
WasmDatabase.workerMainForOpen(setupAllDatabases: setupDatabase);
|
||||
}
|
||||
// #enddocregion setupAll
|
||||
|
||||
|
||||
@@ -13,18 +13,26 @@ void main() {
|
||||
|
||||
// Call drift function that will set up this worker
|
||||
driftWorkerMain(() {
|
||||
return WebDatabase.withStorage(DriftWebStorage.indexedDb('worker',
|
||||
migrateFromLocalStorage: false, inWebWorker: true));
|
||||
return WebDatabase.withStorage(
|
||||
DriftWebStorage.indexedDb(
|
||||
'worker',
|
||||
migrateFromLocalStorage: false,
|
||||
inWebWorker: true,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
// #enddocregion worker
|
||||
|
||||
// #docregion client
|
||||
DatabaseConnection connectToWorker() {
|
||||
return DatabaseConnection.delayed(connectToDriftWorker(
|
||||
'worker.dart.js',
|
||||
// Note that SharedWorkers may not be available on all browsers and platforms.
|
||||
mode: DriftWorkerMode.shared,
|
||||
));
|
||||
return DatabaseConnection.delayed(
|
||||
connectToDriftWorker(
|
||||
'worker.dart.js',
|
||||
// Note that SharedWorkers may not be available on all browsers and platforms.
|
||||
mode: DriftWorkerMode.shared,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion client
|
||||
|
||||
@@ -31,7 +31,7 @@ class TodoItems extends Table {
|
||||
// #enddocregion table
|
||||
@DriftDatabase(tables: [TodoItems])
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
// #enddocregion before_generation
|
||||
// #enddocregion before_generation
|
||||
// After generating code, this class needs to define a `schemaVersion` getter
|
||||
// and a constructor telling drift where the database should be stored.
|
||||
// These are described in the getting started guide: https://drift.simonbinder.eu/setup/
|
||||
@@ -45,12 +45,12 @@ class AppDatabase extends _$AppDatabase {
|
||||
throw 'should not show as snippet';
|
||||
}
|
||||
|
||||
// #docregion before_generation
|
||||
// #docregion before_generation
|
||||
}
|
||||
// #enddocregion before_generation
|
||||
|
||||
class OpenFlutter {
|
||||
// #docregion flutter
|
||||
// #docregion flutter
|
||||
static QueryExecutor _openConnection() {
|
||||
return driftDatabase(
|
||||
name: 'my_database',
|
||||
@@ -66,7 +66,7 @@ class OpenFlutter {
|
||||
// #enddocregion flutter
|
||||
|
||||
class OpenPostgres {
|
||||
// #docregion postgres
|
||||
// #docregion postgres
|
||||
static QueryExecutor _openConnection() {
|
||||
return PgDatabase(
|
||||
endpoint: pg.Endpoint(
|
||||
@@ -81,7 +81,7 @@ class OpenPostgres {
|
||||
// #enddocregion postgres
|
||||
|
||||
class OpenSqlite3 {
|
||||
// #docregion sqlite3
|
||||
// #docregion sqlite3
|
||||
static QueryExecutor _openConnection() {
|
||||
return NativeDatabase.createInBackground(File('path/to/your/database'));
|
||||
}
|
||||
@@ -98,12 +98,17 @@ void main() async {
|
||||
|
||||
final database = AppDatabase();
|
||||
|
||||
await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
|
||||
title: 'todo: finish drift setup',
|
||||
content: 'We can now write queries and define our own tables.',
|
||||
));
|
||||
await database
|
||||
.into(database.todoItems)
|
||||
.insert(
|
||||
TodoItemsCompanion.insert(
|
||||
title: 'todo: finish drift setup',
|
||||
content: 'We can now write queries and define our own tables.',
|
||||
),
|
||||
);
|
||||
List<TodoItem> allItems = await database.select(database.todoItems).get();
|
||||
|
||||
print('items in database: $allItems');
|
||||
}
|
||||
|
||||
// #enddocregion use
|
||||
|
||||
@@ -31,12 +31,16 @@ final class PackageVersionDataLoader implements DataLoader {
|
||||
return;
|
||||
}
|
||||
|
||||
final pubspec = await File(resolvedPackage.root.resolve('pubspec.yaml').toFilePath()).readAsString();
|
||||
final pubspec = await File(
|
||||
resolvedPackage.root.resolve('pubspec.yaml').toFilePath(),
|
||||
).readAsString();
|
||||
final parsedPubspec = loadYaml(pubspec);
|
||||
resolvedVersions[package] = (parsedPubspec as Map)['version'].toString();
|
||||
}
|
||||
|
||||
await Future.wait([for (final package in packages) resolvePackage(package)]);
|
||||
await Future.wait([
|
||||
for (final package in packages) resolvePackage(package),
|
||||
]);
|
||||
return resolvedVersions;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user