mirror of https://github.com/AMT-Cheif/drift.git
commit
18206c8510
|
@ -167,3 +167,66 @@ Applying a `customConstraint` will override all other constraints that would be
|
||||||
particular, that means that we need to also include the `NOT NULL` constraint again.
|
particular, that means that we need to also include the `NOT NULL` constraint again.
|
||||||
|
|
||||||
You can also add table-wide constraints by overriding the `customConstraints` getter in your table class.
|
You can also add table-wide constraints by overriding the `customConstraints` getter in your table class.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
[Foreign key references](https://www.sqlite.org/foreignkeys.html) can be expressed
|
||||||
|
in Dart tables with the `references()` method when building a column:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class Todos extends Table {
|
||||||
|
// ...
|
||||||
|
IntColumn get category => integer().nullable().references(Categories, #id)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataClassName("Category")
|
||||||
|
class Categories extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
// and more columns...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The first parameter to `references` points to the table on which a reference should be created.
|
||||||
|
The second parameter is a [symbol](https://dart.dev/guides/language/language-tour#symbols) of the column to use for the reference.
|
||||||
|
|
||||||
|
Optionally, the `onUpdate` and `onDelete` parameters can be used to describe what
|
||||||
|
should happen when the target row gets updated or deleted.
|
||||||
|
|
||||||
|
Be aware that, in sqlite3, foreign key references aren't enabled by default.
|
||||||
|
They need to be enabled with `PRAGMA foreign_keys = ON`.
|
||||||
|
A suitable place to issue that pragma with drift is in a [post-migration callback]({{ '../Advanced Features/migrations.md#post-migration-callbacks' | pageUrl }}).
|
||||||
|
|
||||||
|
## Views
|
||||||
|
|
||||||
|
It is also possible to define [SQL views](https://www.sqlite.org/lang_createview.html)
|
||||||
|
as Dart classes.
|
||||||
|
To do so, write an abstract class extending `View`. This example declares a view reading
|
||||||
|
the amount of todo-items added to a category in the schema from [the example]({{ 'index.md' | pageUrl }}):
|
||||||
|
|
||||||
|
```dart
|
||||||
|
abstract class CategoryTodoCount extends View {
|
||||||
|
TodosTable get todos;
|
||||||
|
Categories get categories;
|
||||||
|
|
||||||
|
Expression<int> get itemCount => todos.id.count();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([categories.description, itemCount])
|
||||||
|
.from(categories)
|
||||||
|
.join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside a Dart view, use
|
||||||
|
|
||||||
|
- abstract getters to declare tables that you'll read from (e.g. `TodosTable get todos`)
|
||||||
|
- `Expression` getters to add columns: (e.g. `itemCount => todos.id.count()`);
|
||||||
|
- the overridden `as` method to define the select statement backing the view
|
||||||
|
|
||||||
|
Finally, a view needs to be added to a database or accessor by including it in the
|
||||||
|
`views` parameter:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
@DriftDatabase(tables: [Todos, Categories], views: [CategoryTodoCount])
|
||||||
|
class MyDatabase extends _$MyDatabase {
|
||||||
|
```
|
||||||
|
|
|
@ -5,18 +5,67 @@ import 'package:drift/native.dart';
|
||||||
|
|
||||||
part 'main.g.dart';
|
part 'main.g.dart';
|
||||||
|
|
||||||
|
@DataClassName('TodoCategory')
|
||||||
|
class TodoCategories extends Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
TextColumn get name => text()();
|
||||||
|
}
|
||||||
|
|
||||||
class TodoItems extends Table {
|
class TodoItems extends Table {
|
||||||
IntColumn get id => integer().autoIncrement()();
|
IntColumn get id => integer().autoIncrement()();
|
||||||
TextColumn get title => text()();
|
TextColumn get title => text()();
|
||||||
TextColumn get content => text().nullable()();
|
TextColumn get content => text().nullable()();
|
||||||
|
IntColumn get categoryId => integer().references(TodoCategories, #id)();
|
||||||
|
|
||||||
|
TextColumn get generatedText => text().nullable().generatedAs(
|
||||||
|
title + const Constant(' (') + content + const Constant(')'))();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DriftDatabase(tables: [TodoItems])
|
abstract class TodoCategoryItemCount extends View {
|
||||||
|
TodoItems get todoItems;
|
||||||
|
TodoCategories get todoCategories;
|
||||||
|
|
||||||
|
Expression<int> get itemCount => todoItems.id.count();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([
|
||||||
|
todoCategories.name,
|
||||||
|
itemCount,
|
||||||
|
]).from(todoCategories).join([
|
||||||
|
innerJoin(todoItems, todoItems.categoryId.equalsExp(todoCategories.id))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DriftView(name: 'customViewName')
|
||||||
|
abstract class TodoItemWithCategoryNameView extends View {
|
||||||
|
TodoItems get todoItems;
|
||||||
|
TodoCategories get todoCategories;
|
||||||
|
|
||||||
|
Expression<String> get title =>
|
||||||
|
todoItems.title +
|
||||||
|
const Constant('(') +
|
||||||
|
todoCategories.name +
|
||||||
|
const Constant(')');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([todoItems.id, title]).from(todoItems).join([
|
||||||
|
innerJoin(
|
||||||
|
todoCategories, todoCategories.id.equalsExp(todoItems.categoryId))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DriftDatabase(tables: [
|
||||||
|
TodoItems,
|
||||||
|
TodoCategories,
|
||||||
|
], views: [
|
||||||
|
TodoCategoryItemCount,
|
||||||
|
TodoItemWithCategoryNameView,
|
||||||
|
])
|
||||||
class Database extends _$Database {
|
class Database extends _$Database {
|
||||||
Database(QueryExecutor e) : super(e);
|
Database(QueryExecutor e) : super(e);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 2;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration {
|
MigrationStrategy get migration {
|
||||||
|
@ -27,11 +76,12 @@ class Database extends _$Database {
|
||||||
// Add a bunch of default items in a batch
|
// Add a bunch of default items in a batch
|
||||||
await batch((b) {
|
await batch((b) {
|
||||||
b.insertAll(todoItems, [
|
b.insertAll(todoItems, [
|
||||||
TodoItemsCompanion.insert(title: 'A first entry'),
|
TodoItemsCompanion.insert(title: 'A first entry', categoryId: 0),
|
||||||
TodoItemsCompanion.insert(
|
TodoItemsCompanion.insert(
|
||||||
title: 'Todo: Checkout drift',
|
title: 'Todo: Checkout drift',
|
||||||
content: const Value('Drift is a persistence library for Dart '
|
content: const Value('Drift is a persistence library for Dart '
|
||||||
'and Flutter applications.'),
|
'and Flutter applications.'),
|
||||||
|
categoryId: 0,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -55,10 +105,17 @@ Future<void> main() async {
|
||||||
print('Todo-item in database: $event');
|
print('Todo-item in database: $event');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add category
|
||||||
|
final categoryId = await db
|
||||||
|
.into(db.todoCategories)
|
||||||
|
.insert(TodoCategoriesCompanion.insert(name: 'Category'));
|
||||||
|
|
||||||
// Add another entry
|
// Add another entry
|
||||||
await db
|
await db.into(db.todoItems).insert(TodoItemsCompanion.insert(
|
||||||
.into(db.todoItems)
|
title: 'Another entry added later', categoryId: categoryId));
|
||||||
.insert(TodoItemsCompanion.insert(title: 'Another entry added later'));
|
|
||||||
|
(await db.select(db.customViewName).get()).forEach(print);
|
||||||
|
(await db.select(db.todoCategoryItemCount).get()).forEach(print);
|
||||||
|
|
||||||
// Delete all todo items
|
// Delete all todo items
|
||||||
await db.delete(db.todoItems).go();
|
await db.delete(db.todoItems).go();
|
||||||
|
|
|
@ -7,11 +7,193 @@ part of 'main.dart';
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
|
// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this
|
||||||
|
class TodoCategory extends DataClass implements Insertable<TodoCategory> {
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
TodoCategory({required this.id, required this.name});
|
||||||
|
factory TodoCategory.fromData(Map<String, dynamic> data, {String? prefix}) {
|
||||||
|
final effectivePrefix = prefix ?? '';
|
||||||
|
return TodoCategory(
|
||||||
|
id: const IntType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}id'])!,
|
||||||
|
name: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}name'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
map['id'] = Variable<int>(id);
|
||||||
|
map['name'] = Variable<String>(name);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoCategoriesCompanion toCompanion(bool nullToAbsent) {
|
||||||
|
return TodoCategoriesCompanion(
|
||||||
|
id: Value(id),
|
||||||
|
name: Value(name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory TodoCategory.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return TodoCategory(
|
||||||
|
id: serializer.fromJson<int>(json['id']),
|
||||||
|
name: serializer.fromJson<String>(json['name']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoCategory.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
TodoCategory.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': serializer.toJson<int>(id),
|
||||||
|
'name': serializer.toJson<String>(name),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoCategory copyWith({int? id, String? name}) => TodoCategory(
|
||||||
|
id: id ?? this.id,
|
||||||
|
name: name ?? this.name,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('TodoCategory(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('name: $name')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(id, name);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is TodoCategory && other.id == this.id && other.name == this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TodoCategoriesCompanion extends UpdateCompanion<TodoCategory> {
|
||||||
|
final Value<int> id;
|
||||||
|
final Value<String> name;
|
||||||
|
const TodoCategoriesCompanion({
|
||||||
|
this.id = const Value.absent(),
|
||||||
|
this.name = const Value.absent(),
|
||||||
|
});
|
||||||
|
TodoCategoriesCompanion.insert({
|
||||||
|
this.id = const Value.absent(),
|
||||||
|
required String name,
|
||||||
|
}) : name = Value(name);
|
||||||
|
static Insertable<TodoCategory> custom({
|
||||||
|
Expression<int>? id,
|
||||||
|
Expression<String>? name,
|
||||||
|
}) {
|
||||||
|
return RawValuesInsertable({
|
||||||
|
if (id != null) 'id': id,
|
||||||
|
if (name != null) 'name': name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoCategoriesCompanion copyWith({Value<int>? id, Value<String>? name}) {
|
||||||
|
return TodoCategoriesCompanion(
|
||||||
|
id: id ?? this.id,
|
||||||
|
name: name ?? this.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (id.present) {
|
||||||
|
map['id'] = Variable<int>(id.value);
|
||||||
|
}
|
||||||
|
if (name.present) {
|
||||||
|
map['name'] = Variable<String>(name.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('TodoCategoriesCompanion(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('name: $name')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $TodoCategoriesTable extends TodoCategories
|
||||||
|
with TableInfo<$TodoCategoriesTable, TodoCategory> {
|
||||||
|
final GeneratedDatabase _db;
|
||||||
|
final String? _alias;
|
||||||
|
$TodoCategoriesTable(this._db, [this._alias]);
|
||||||
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
|
'id', aliasedName, false,
|
||||||
|
type: const IntType(),
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
|
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
||||||
|
'name', aliasedName, false,
|
||||||
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [id, name];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? 'todo_categories';
|
||||||
|
@override
|
||||||
|
String get actualTableName => 'todo_categories';
|
||||||
|
@override
|
||||||
|
VerificationContext validateIntegrity(Insertable<TodoCategory> instance,
|
||||||
|
{bool isInserting = false}) {
|
||||||
|
final context = VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('id')) {
|
||||||
|
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||||
|
}
|
||||||
|
if (data.containsKey('name')) {
|
||||||
|
context.handle(
|
||||||
|
_nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_nameMeta);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<GeneratedColumn> get $primaryKey => {id};
|
||||||
|
@override
|
||||||
|
TodoCategory map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
return TodoCategory.fromData(data,
|
||||||
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
$TodoCategoriesTable createAlias(String alias) {
|
||||||
|
return $TodoCategoriesTable(_db, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TodoItem extends DataClass implements Insertable<TodoItem> {
|
class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
final int id;
|
final int id;
|
||||||
final String title;
|
final String title;
|
||||||
final String? content;
|
final String? content;
|
||||||
TodoItem({required this.id, required this.title, this.content});
|
final int categoryId;
|
||||||
|
final String? generatedText;
|
||||||
|
TodoItem(
|
||||||
|
{required this.id,
|
||||||
|
required this.title,
|
||||||
|
this.content,
|
||||||
|
required this.categoryId,
|
||||||
|
this.generatedText});
|
||||||
factory TodoItem.fromData(Map<String, dynamic> data, {String? prefix}) {
|
factory TodoItem.fromData(Map<String, dynamic> data, {String? prefix}) {
|
||||||
final effectivePrefix = prefix ?? '';
|
final effectivePrefix = prefix ?? '';
|
||||||
return TodoItem(
|
return TodoItem(
|
||||||
|
@ -21,6 +203,10 @@ class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
.mapFromDatabaseResponse(data['${effectivePrefix}title'])!,
|
.mapFromDatabaseResponse(data['${effectivePrefix}title'])!,
|
||||||
content: const StringType()
|
content: const StringType()
|
||||||
.mapFromDatabaseResponse(data['${effectivePrefix}content']),
|
.mapFromDatabaseResponse(data['${effectivePrefix}content']),
|
||||||
|
categoryId: const IntType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}category_id'])!,
|
||||||
|
generatedText: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}generated_text']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
|
@ -31,6 +217,7 @@ class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
if (!nullToAbsent || content != null) {
|
if (!nullToAbsent || content != null) {
|
||||||
map['content'] = Variable<String?>(content);
|
map['content'] = Variable<String?>(content);
|
||||||
}
|
}
|
||||||
|
map['category_id'] = Variable<int>(categoryId);
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +228,7 @@ class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
content: content == null && nullToAbsent
|
content: content == null && nullToAbsent
|
||||||
? const Value.absent()
|
? const Value.absent()
|
||||||
: Value(content),
|
: Value(content),
|
||||||
|
categoryId: Value(categoryId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +239,8 @@ class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
id: serializer.fromJson<int>(json['id']),
|
id: serializer.fromJson<int>(json['id']),
|
||||||
title: serializer.fromJson<String>(json['title']),
|
title: serializer.fromJson<String>(json['title']),
|
||||||
content: serializer.fromJson<String?>(json['content']),
|
content: serializer.fromJson<String?>(json['content']),
|
||||||
|
categoryId: serializer.fromJson<int>(json['categoryId']),
|
||||||
|
generatedText: serializer.fromJson<String?>(json['generatedText']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
factory TodoItem.fromJsonString(String encodedJson,
|
factory TodoItem.fromJsonString(String encodedJson,
|
||||||
|
@ -65,71 +255,93 @@ class TodoItem extends DataClass implements Insertable<TodoItem> {
|
||||||
'id': serializer.toJson<int>(id),
|
'id': serializer.toJson<int>(id),
|
||||||
'title': serializer.toJson<String>(title),
|
'title': serializer.toJson<String>(title),
|
||||||
'content': serializer.toJson<String?>(content),
|
'content': serializer.toJson<String?>(content),
|
||||||
|
'categoryId': serializer.toJson<int>(categoryId),
|
||||||
|
'generatedText': serializer.toJson<String?>(generatedText),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
TodoItem copyWith(
|
TodoItem copyWith(
|
||||||
{int? id,
|
{int? id,
|
||||||
String? title,
|
String? title,
|
||||||
Value<String?> content = const Value.absent()}) =>
|
Value<String?> content = const Value.absent(),
|
||||||
|
int? categoryId,
|
||||||
|
Value<String?> generatedText = const Value.absent()}) =>
|
||||||
TodoItem(
|
TodoItem(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
content: content.present ? content.value : this.content,
|
content: content.present ? content.value : this.content,
|
||||||
|
categoryId: categoryId ?? this.categoryId,
|
||||||
|
generatedText:
|
||||||
|
generatedText.present ? generatedText.value : this.generatedText,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return (StringBuffer('TodoItem(')
|
return (StringBuffer('TodoItem(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('title: $title, ')
|
..write('title: $title, ')
|
||||||
..write('content: $content')
|
..write('content: $content, ')
|
||||||
|
..write('categoryId: $categoryId, ')
|
||||||
|
..write('generatedText: $generatedText')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(id, title, content);
|
int get hashCode =>
|
||||||
|
Object.hash(id, title, content, categoryId, generatedText);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
(other is TodoItem &&
|
(other is TodoItem &&
|
||||||
other.id == this.id &&
|
other.id == this.id &&
|
||||||
other.title == this.title &&
|
other.title == this.title &&
|
||||||
other.content == this.content);
|
other.content == this.content &&
|
||||||
|
other.categoryId == this.categoryId &&
|
||||||
|
other.generatedText == this.generatedText);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TodoItemsCompanion extends UpdateCompanion<TodoItem> {
|
class TodoItemsCompanion extends UpdateCompanion<TodoItem> {
|
||||||
final Value<int> id;
|
final Value<int> id;
|
||||||
final Value<String> title;
|
final Value<String> title;
|
||||||
final Value<String?> content;
|
final Value<String?> content;
|
||||||
|
final Value<int> categoryId;
|
||||||
const TodoItemsCompanion({
|
const TodoItemsCompanion({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
this.title = const Value.absent(),
|
this.title = const Value.absent(),
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
|
this.categoryId = const Value.absent(),
|
||||||
});
|
});
|
||||||
TodoItemsCompanion.insert({
|
TodoItemsCompanion.insert({
|
||||||
this.id = const Value.absent(),
|
this.id = const Value.absent(),
|
||||||
required String title,
|
required String title,
|
||||||
this.content = const Value.absent(),
|
this.content = const Value.absent(),
|
||||||
}) : title = Value(title);
|
required int categoryId,
|
||||||
|
}) : title = Value(title),
|
||||||
|
categoryId = Value(categoryId);
|
||||||
static Insertable<TodoItem> custom({
|
static Insertable<TodoItem> custom({
|
||||||
Expression<int>? id,
|
Expression<int>? id,
|
||||||
Expression<String>? title,
|
Expression<String>? title,
|
||||||
Expression<String?>? content,
|
Expression<String?>? content,
|
||||||
|
Expression<int>? categoryId,
|
||||||
}) {
|
}) {
|
||||||
return RawValuesInsertable({
|
return RawValuesInsertable({
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
if (title != null) 'title': title,
|
if (title != null) 'title': title,
|
||||||
if (content != null) 'content': content,
|
if (content != null) 'content': content,
|
||||||
|
if (categoryId != null) 'category_id': categoryId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TodoItemsCompanion copyWith(
|
TodoItemsCompanion copyWith(
|
||||||
{Value<int>? id, Value<String>? title, Value<String?>? content}) {
|
{Value<int>? id,
|
||||||
|
Value<String>? title,
|
||||||
|
Value<String?>? content,
|
||||||
|
Value<int>? categoryId}) {
|
||||||
return TodoItemsCompanion(
|
return TodoItemsCompanion(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
title: title ?? this.title,
|
title: title ?? this.title,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
|
categoryId: categoryId ?? this.categoryId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +357,9 @@ class TodoItemsCompanion extends UpdateCompanion<TodoItem> {
|
||||||
if (content.present) {
|
if (content.present) {
|
||||||
map['content'] = Variable<String?>(content.value);
|
map['content'] = Variable<String?>(content.value);
|
||||||
}
|
}
|
||||||
|
if (categoryId.present) {
|
||||||
|
map['category_id'] = Variable<int>(categoryId.value);
|
||||||
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +368,8 @@ class TodoItemsCompanion extends UpdateCompanion<TodoItem> {
|
||||||
return (StringBuffer('TodoItemsCompanion(')
|
return (StringBuffer('TodoItemsCompanion(')
|
||||||
..write('id: $id, ')
|
..write('id: $id, ')
|
||||||
..write('title: $title, ')
|
..write('title: $title, ')
|
||||||
..write('content: $content')
|
..write('content: $content, ')
|
||||||
|
..write('categoryId: $categoryId')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
@ -165,21 +381,41 @@ class $TodoItemsTable extends TodoItems
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$TodoItemsTable(this._db, [this._alias]);
|
$TodoItemsTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _titleMeta = const VerificationMeta('title');
|
final VerificationMeta _titleMeta = const VerificationMeta('title');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
||||||
'title', aliasedName, false,
|
'title', aliasedName, false,
|
||||||
type: const StringType(), requiredDuringInsert: true);
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> content = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> content = GeneratedColumn<String?>(
|
||||||
'content', aliasedName, true,
|
'content', aliasedName, true,
|
||||||
type: const StringType(), requiredDuringInsert: false);
|
type: const StringType(), requiredDuringInsert: false);
|
||||||
|
final VerificationMeta _categoryIdMeta = const VerificationMeta('categoryId');
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns => [id, title, content];
|
late final GeneratedColumn<int?> categoryId = GeneratedColumn<int?>(
|
||||||
|
'category_id', aliasedName, false,
|
||||||
|
type: const IntType(),
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
defaultConstraints: 'REFERENCES todo_categories (id)');
|
||||||
|
final VerificationMeta _generatedTextMeta =
|
||||||
|
const VerificationMeta('generatedText');
|
||||||
|
@override
|
||||||
|
late final GeneratedColumn<String?> generatedText = GeneratedColumn<String?>(
|
||||||
|
'generated_text', aliasedName, true,
|
||||||
|
type: const StringType(),
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
generatedAs: GeneratedAs(
|
||||||
|
title + const Constant(' (') + content + const Constant(')'), false));
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns =>
|
||||||
|
[id, title, content, categoryId, generatedText];
|
||||||
@override
|
@override
|
||||||
String get aliasedName => _alias ?? 'todo_items';
|
String get aliasedName => _alias ?? 'todo_items';
|
||||||
@override
|
@override
|
||||||
|
@ -202,6 +438,20 @@ class $TodoItemsTable extends TodoItems
|
||||||
context.handle(_contentMeta,
|
context.handle(_contentMeta,
|
||||||
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
|
content.isAcceptableOrUnknown(data['content']!, _contentMeta));
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('category_id')) {
|
||||||
|
context.handle(
|
||||||
|
_categoryIdMeta,
|
||||||
|
categoryId.isAcceptableOrUnknown(
|
||||||
|
data['category_id']!, _categoryIdMeta));
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_categoryIdMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('generated_text')) {
|
||||||
|
context.handle(
|
||||||
|
_generatedTextMeta,
|
||||||
|
generatedText.isAcceptableOrUnknown(
|
||||||
|
data['generated_text']!, _generatedTextMeta));
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,12 +469,236 @@ class $TodoItemsTable extends TodoItems
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TodoCategoryItemCountData extends DataClass {
|
||||||
|
final String name;
|
||||||
|
final int itemCount;
|
||||||
|
TodoCategoryItemCountData({required this.name, required this.itemCount});
|
||||||
|
factory TodoCategoryItemCountData.fromData(Map<String, dynamic> data,
|
||||||
|
{String? prefix}) {
|
||||||
|
final effectivePrefix = prefix ?? '';
|
||||||
|
return TodoCategoryItemCountData(
|
||||||
|
name: const StringType().mapFromDatabaseResponse(
|
||||||
|
data['${effectivePrefix}todo_categories.name'])!,
|
||||||
|
itemCount: const IntType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}item_count'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoCategoryItemCountData.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return TodoCategoryItemCountData(
|
||||||
|
name: serializer.fromJson<String>(json['name']),
|
||||||
|
itemCount: serializer.fromJson<int>(json['itemCount']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoCategoryItemCountData.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
TodoCategoryItemCountData.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'name': serializer.toJson<String>(name),
|
||||||
|
'itemCount': serializer.toJson<int>(itemCount),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoCategoryItemCountData copyWith({String? name, int? itemCount}) =>
|
||||||
|
TodoCategoryItemCountData(
|
||||||
|
name: name ?? this.name,
|
||||||
|
itemCount: itemCount ?? this.itemCount,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('TodoCategoryItemCountData(')
|
||||||
|
..write('name: $name, ')
|
||||||
|
..write('itemCount: $itemCount')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(name, itemCount);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is TodoCategoryItemCountData &&
|
||||||
|
other.name == this.name &&
|
||||||
|
other.itemCount == this.itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $TodoCategoryItemCountView
|
||||||
|
extends ViewInfo<$TodoCategoryItemCountView, TodoCategoryItemCountData>
|
||||||
|
implements HasResultSet {
|
||||||
|
final _$Database _db;
|
||||||
|
final String? _alias;
|
||||||
|
$TodoCategoryItemCountView(this._db, [this._alias]);
|
||||||
|
$TodoItemsTable get todoItems => _db.todoItems;
|
||||||
|
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [todoCategories.name, itemCount];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
@override
|
||||||
|
String get entityName => 'todo_category_item_count';
|
||||||
|
@override
|
||||||
|
String? get createViewStmt => null;
|
||||||
|
@override
|
||||||
|
$TodoCategoryItemCountView get asDslTable => this;
|
||||||
|
@override
|
||||||
|
TodoCategoryItemCountData map(Map<String, dynamic> data,
|
||||||
|
{String? tablePrefix}) {
|
||||||
|
return TodoCategoryItemCountData.fromData(data,
|
||||||
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
||||||
|
'name', aliasedName, false,
|
||||||
|
type: const StringType());
|
||||||
|
late final GeneratedColumn<int?> itemCount = GeneratedColumn<int?>(
|
||||||
|
'item_count', aliasedName, false,
|
||||||
|
type: const IntType(),
|
||||||
|
generatedAs: GeneratedAs(todoItems.id.count(), false));
|
||||||
|
@override
|
||||||
|
$TodoCategoryItemCountView createAlias(String alias) {
|
||||||
|
return $TodoCategoryItemCountView(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query? get query =>
|
||||||
|
(_db.selectOnly(todoCategories, includeJoinedTableColumns: false)
|
||||||
|
..addColumns($columns))
|
||||||
|
.join([
|
||||||
|
innerJoin(todoItems, todoItems.categoryId.equalsExp(todoCategories.id))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TodoItemWithCategoryNameViewData extends DataClass {
|
||||||
|
final int id;
|
||||||
|
final String title;
|
||||||
|
TodoItemWithCategoryNameViewData({required this.id, required this.title});
|
||||||
|
factory TodoItemWithCategoryNameViewData.fromData(Map<String, dynamic> data,
|
||||||
|
{String? prefix}) {
|
||||||
|
final effectivePrefix = prefix ?? '';
|
||||||
|
return TodoItemWithCategoryNameViewData(
|
||||||
|
id: const IntType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}todo_items.id'])!,
|
||||||
|
title: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}title'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoItemWithCategoryNameViewData.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return TodoItemWithCategoryNameViewData(
|
||||||
|
id: serializer.fromJson<int>(json['id']),
|
||||||
|
title: serializer.fromJson<String>(json['title']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoItemWithCategoryNameViewData.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
TodoItemWithCategoryNameViewData.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'id': serializer.toJson<int>(id),
|
||||||
|
'title': serializer.toJson<String>(title),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoItemWithCategoryNameViewData copyWith({int? id, String? title}) =>
|
||||||
|
TodoItemWithCategoryNameViewData(
|
||||||
|
id: id ?? this.id,
|
||||||
|
title: title ?? this.title,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('TodoItemWithCategoryNameViewData(')
|
||||||
|
..write('id: $id, ')
|
||||||
|
..write('title: $title')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(id, title);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is TodoItemWithCategoryNameViewData &&
|
||||||
|
other.id == this.id &&
|
||||||
|
other.title == this.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $TodoItemWithCategoryNameViewView extends ViewInfo<
|
||||||
|
$TodoItemWithCategoryNameViewView,
|
||||||
|
TodoItemWithCategoryNameViewData> implements HasResultSet {
|
||||||
|
final _$Database _db;
|
||||||
|
final String? _alias;
|
||||||
|
$TodoItemWithCategoryNameViewView(this._db, [this._alias]);
|
||||||
|
$TodoItemsTable get todoItems => _db.todoItems;
|
||||||
|
$TodoCategoriesTable get todoCategories => _db.todoCategories;
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [todoItems.id, title];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
@override
|
||||||
|
String get entityName => 'customViewName';
|
||||||
|
@override
|
||||||
|
String? get createViewStmt => null;
|
||||||
|
@override
|
||||||
|
$TodoItemWithCategoryNameViewView get asDslTable => this;
|
||||||
|
@override
|
||||||
|
TodoItemWithCategoryNameViewData map(Map<String, dynamic> data,
|
||||||
|
{String? tablePrefix}) {
|
||||||
|
return TodoItemWithCategoryNameViewData.fromData(data,
|
||||||
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
|
'id', aliasedName, false,
|
||||||
|
type: const IntType(), defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
|
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
||||||
|
'title', aliasedName, false,
|
||||||
|
type: const StringType(),
|
||||||
|
generatedAs: GeneratedAs(
|
||||||
|
todoItems.title +
|
||||||
|
const Constant('(') +
|
||||||
|
todoCategories.name +
|
||||||
|
const Constant(')'),
|
||||||
|
false));
|
||||||
|
@override
|
||||||
|
$TodoItemWithCategoryNameViewView createAlias(String alias) {
|
||||||
|
return $TodoItemWithCategoryNameViewView(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query? get query =>
|
||||||
|
(_db.selectOnly(todoItems, includeJoinedTableColumns: false)
|
||||||
|
..addColumns($columns))
|
||||||
|
.join([
|
||||||
|
innerJoin(
|
||||||
|
todoCategories, todoCategories.id.equalsExp(todoItems.categoryId))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
abstract class _$Database extends GeneratedDatabase {
|
abstract class _$Database extends GeneratedDatabase {
|
||||||
_$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e);
|
_$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e);
|
||||||
_$Database.connect(DatabaseConnection c) : super.connect(c);
|
_$Database.connect(DatabaseConnection c) : super.connect(c);
|
||||||
|
late final $TodoCategoriesTable todoCategories = $TodoCategoriesTable(this);
|
||||||
late final $TodoItemsTable todoItems = $TodoItemsTable(this);
|
late final $TodoItemsTable todoItems = $TodoItemsTable(this);
|
||||||
|
late final $TodoCategoryItemCountView todoCategoryItemCount =
|
||||||
|
$TodoCategoryItemCountView(this);
|
||||||
|
late final $TodoItemWithCategoryNameViewView customViewName =
|
||||||
|
$TodoItemWithCategoryNameViewView(this);
|
||||||
@override
|
@override
|
||||||
Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
|
Iterable<TableInfo> get allTables => allSchemaEntities.whereType<TableInfo>();
|
||||||
@override
|
@override
|
||||||
List<DatabaseSchemaEntity> get allSchemaEntities => [todoItems];
|
List<DatabaseSchemaEntity> get allSchemaEntities =>
|
||||||
|
[todoCategories, todoItems, todoCategoryItemCount, customViewName];
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ class DriftDatabase {
|
||||||
/// The tables to include in the database
|
/// The tables to include in the database
|
||||||
final List<Type> tables;
|
final List<Type> tables;
|
||||||
|
|
||||||
|
/// The views to include in the database
|
||||||
|
final List<Type> views;
|
||||||
|
|
||||||
/// Optionally, the list of daos to use. A dao can also make queries like a
|
/// Optionally, the list of daos to use. A dao can also make queries like a
|
||||||
/// regular database class, making is suitable to extract parts of your
|
/// regular database class, making is suitable to extract parts of your
|
||||||
/// database logic into smaller components.
|
/// database logic into smaller components.
|
||||||
|
@ -58,6 +61,7 @@ class DriftDatabase {
|
||||||
/// class should be generated using the specified [DriftDatabase.tables].
|
/// class should be generated using the specified [DriftDatabase.tables].
|
||||||
const DriftDatabase({
|
const DriftDatabase({
|
||||||
this.tables = const [],
|
this.tables = const [],
|
||||||
|
this.views = const [],
|
||||||
this.daos = const [],
|
this.daos = const [],
|
||||||
this.queries = const {},
|
this.queries = const {},
|
||||||
this.include = const {},
|
this.include = const {},
|
||||||
|
@ -91,6 +95,9 @@ class DriftAccessor {
|
||||||
/// The tables accessed by this DAO.
|
/// The tables accessed by this DAO.
|
||||||
final List<Type> tables;
|
final List<Type> tables;
|
||||||
|
|
||||||
|
/// The views to make accessible in this DAO.
|
||||||
|
final List<Type> views;
|
||||||
|
|
||||||
/// {@macro drift_compile_queries_param}
|
/// {@macro drift_compile_queries_param}
|
||||||
final Map<String, String> queries;
|
final Map<String, String> queries;
|
||||||
|
|
||||||
|
@ -101,6 +108,7 @@ class DriftAccessor {
|
||||||
/// the referenced documentation on how to use daos with drift.
|
/// the referenced documentation on how to use daos with drift.
|
||||||
const DriftAccessor({
|
const DriftAccessor({
|
||||||
this.tables = const [],
|
this.tables = const [],
|
||||||
|
this.views = const [],
|
||||||
this.queries = const {},
|
this.queries = const {},
|
||||||
this.include = const {},
|
this.include = const {},
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,8 @@ abstract class HasResultSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subclasses represent a table in a database generated by drift.
|
/// Subclasses represent a table in a database generated by drift.
|
||||||
|
///
|
||||||
|
/// For more information on how to write tables, see [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/)
|
||||||
abstract class Table extends HasResultSet {
|
abstract class Table extends HasResultSet {
|
||||||
/// Defines a table to be used with drift.
|
/// Defines a table to be used with drift.
|
||||||
const Table();
|
const Table();
|
||||||
|
@ -118,6 +120,46 @@ abstract class Table extends HasResultSet {
|
||||||
ColumnBuilder<double> real() => _isGenerated();
|
ColumnBuilder<double> real() => _isGenerated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Subclasses represent a view in a database generated by drift.
|
||||||
|
///
|
||||||
|
/// For more information on how to define views in Dart, see
|
||||||
|
/// [the documentation](https://drift.simonbinder.eu/docs/getting-started/advanced_dart_tables/#views)
|
||||||
|
abstract class View extends HasResultSet {
|
||||||
|
/// Defines a view to be used with drift.
|
||||||
|
const View();
|
||||||
|
|
||||||
|
/// The select method can be used in [as] to define the select query backing
|
||||||
|
/// this view.
|
||||||
|
///
|
||||||
|
/// The select statement should select all columns defined on this view.
|
||||||
|
@protected
|
||||||
|
View select(List<Expression> columns) => _isGenerated();
|
||||||
|
|
||||||
|
/// This method should be called on [select] to define the main table of this
|
||||||
|
/// view:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// abstract class CategoryTodoCount extends View {
|
||||||
|
/// TodosTable get todos;
|
||||||
|
/// Categories get categories;
|
||||||
|
///
|
||||||
|
/// Expression<int> get itemCount => todos.id.count();
|
||||||
|
///
|
||||||
|
/// @override
|
||||||
|
/// Query as() => select([categories.description, itemCount])
|
||||||
|
/// .from(categories)
|
||||||
|
/// .join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
@protected
|
||||||
|
SimpleSelectStatement from(Table table) => _isGenerated();
|
||||||
|
|
||||||
|
/// This method is overridden by Dart-defined views to declare the right
|
||||||
|
/// query to run.
|
||||||
|
@visibleForOverriding
|
||||||
|
Query as();
|
||||||
|
}
|
||||||
|
|
||||||
/// A class to be used as an annotation on [Table] classes to customize the
|
/// A class to be used as an annotation on [Table] classes to customize the
|
||||||
/// name for the data class that will be generated for the table class. The data
|
/// name for the data class that will be generated for the table class. The data
|
||||||
/// class is a dart object that will be used to represent a row in the table.
|
/// class is a dart object that will be used to represent a row in the table.
|
||||||
|
@ -169,3 +211,26 @@ class UseRowClass {
|
||||||
const UseRowClass(this.type,
|
const UseRowClass(this.type,
|
||||||
{this.constructor = '', this.generateInsertable = false});
|
{this.constructor = '', this.generateInsertable = false});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An annotation specifying view properties
|
||||||
|
@Target({TargetKind.classType})
|
||||||
|
class DriftView {
|
||||||
|
/// The sql view name to be used. By default, drift will use the snake_case
|
||||||
|
/// representation of your class name as the sql view name. For instance, a
|
||||||
|
/// [View] class named `UserView` will be called `user_view` by
|
||||||
|
/// default.
|
||||||
|
final String? name;
|
||||||
|
|
||||||
|
/// The name for the data class that will be generated for the view class.
|
||||||
|
/// The data class is a dart object that will be used to represent a result of
|
||||||
|
/// the view.
|
||||||
|
/// {@template drift_custom_data_class}
|
||||||
|
/// By default, drift will attempt to use the view name followed by "Data"
|
||||||
|
/// when naming data classes (e.g. a view named "UserView" will generate a
|
||||||
|
/// data class called "UserViewData").
|
||||||
|
/// {@macro drift_custom_data_class}
|
||||||
|
final String? dataClassName;
|
||||||
|
|
||||||
|
/// Customize view name and data class name
|
||||||
|
const DriftView({this.name, this.dataClassName});
|
||||||
|
}
|
||||||
|
|
|
@ -223,17 +223,24 @@ abstract class DatabaseConnectionUser {
|
||||||
/// The [distinct] parameter (defaults to false) can be used to remove
|
/// The [distinct] parameter (defaults to false) can be used to remove
|
||||||
/// duplicate rows from the result set.
|
/// duplicate rows from the result set.
|
||||||
///
|
///
|
||||||
|
/// The [includeJoinedTableColumns] parameter (defaults to true) can be used
|
||||||
|
/// to determinate join statement's `useColumns` parameter default value. Set
|
||||||
|
/// it to false if you don't want to include joined table columns by default.
|
||||||
|
/// If you leave it on true and don't set `useColumns` parameter to false in
|
||||||
|
/// join declarations, all columns of joined table will be included in query
|
||||||
|
/// by default.
|
||||||
|
///
|
||||||
/// For simple queries, use [select].
|
/// For simple queries, use [select].
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - the documentation on [aggregate expressions](https://drift.simonbinder.eu/docs/getting-started/expressions/#aggregate)
|
/// - the documentation on [aggregate expressions](https://drift.simonbinder.eu/docs/getting-started/expressions/#aggregate)
|
||||||
/// - the documentation on [group by](https://drift.simonbinder.eu/docs/advanced-features/joins/#group-by)
|
/// - the documentation on [group by](https://drift.simonbinder.eu/docs/advanced-features/joins/#group-by)
|
||||||
JoinedSelectStatement<T, R> selectOnly<T extends HasResultSet, R>(
|
JoinedSelectStatement<T, R> selectOnly<T extends HasResultSet, R>(
|
||||||
ResultSetImplementation<T, R> table, {
|
ResultSetImplementation<T, R> table,
|
||||||
bool distinct = false,
|
{bool distinct = false,
|
||||||
}) {
|
bool includeJoinedTableColumns = true}) {
|
||||||
return JoinedSelectStatement<T, R>(
|
return JoinedSelectStatement<T, R>(
|
||||||
resolvedEngine, table, [], distinct, false);
|
resolvedEngine, table, [], distinct, false, includeJoinedTableColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts a [DeleteStatement] that can be used to delete rows from a table.
|
/// Starts a [DeleteStatement] that can be used to delete rows from a table.
|
||||||
|
|
|
@ -27,30 +27,39 @@ class Join<T extends HasResultSet, D> extends Component {
|
||||||
final _JoinType type;
|
final _JoinType type;
|
||||||
|
|
||||||
/// The [TableInfo] that will be added to the query
|
/// The [TableInfo] that will be added to the query
|
||||||
final ResultSetImplementation<T, D> table;
|
final Table table;
|
||||||
|
|
||||||
/// For joins that aren't [_JoinType.cross], contains an additional predicate
|
/// For joins that aren't [_JoinType.cross], contains an additional predicate
|
||||||
/// that must be matched for the join.
|
/// that must be matched for the join.
|
||||||
final Expression<bool?>? on;
|
final Expression<bool?>? on;
|
||||||
|
|
||||||
/// Whether [table] should appear in the result set (defaults to true).
|
/// Whether [table] should appear in the result set (defaults to true).
|
||||||
|
/// Default value can be changed by `includeJoinedTableColumns` in
|
||||||
|
/// `selectOnly` statements.
|
||||||
///
|
///
|
||||||
/// It can be useful to exclude some tables. Sometimes, tables are used in a
|
/// It can be useful to exclude some tables. Sometimes, tables are used in a
|
||||||
/// join only to run aggregate functions on them.
|
/// join only to run aggregate functions on them.
|
||||||
final bool includeInResult;
|
final bool? includeInResult;
|
||||||
|
|
||||||
/// Constructs a [Join] by providing the relevant fields. [on] is optional for
|
/// Constructs a [Join] by providing the relevant fields. [on] is optional for
|
||||||
/// [_JoinType.cross].
|
/// [_JoinType.cross].
|
||||||
Join._(this.type, this.table, this.on, {bool? includeInResult})
|
Join._(this.type, this.table, this.on, {this.includeInResult}) {
|
||||||
: includeInResult = includeInResult ?? true;
|
if (table is! ResultSetImplementation<T, D>) {
|
||||||
|
throw ArgumentError(
|
||||||
|
'Invalid table parameter. You must provide the table reference from '
|
||||||
|
'generated database object.',
|
||||||
|
'table');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void writeInto(GenerationContext context) {
|
void writeInto(GenerationContext context) {
|
||||||
context.buffer.write(_joinKeywords[type]);
|
context.buffer.write(_joinKeywords[type]);
|
||||||
context.buffer.write(' JOIN ');
|
context.buffer.write(' JOIN ');
|
||||||
|
|
||||||
context.buffer.write(table.tableWithAlias);
|
final resultSet = table as ResultSetImplementation<T, D>;
|
||||||
context.watchedTables.add(table);
|
context.buffer.write(resultSet.tableWithAlias);
|
||||||
|
context.watchedTables.add(resultSet);
|
||||||
|
|
||||||
if (type != _JoinType.cross) {
|
if (type != _JoinType.cross) {
|
||||||
context.buffer.write(' ON ');
|
context.buffer.write(' ON ');
|
||||||
|
@ -70,9 +79,7 @@ class Join<T extends HasResultSet, D> extends Component {
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
||||||
/// - http://www.sqlitetutorial.net/sqlite-inner-join/
|
/// - http://www.sqlitetutorial.net/sqlite-inner-join/
|
||||||
Join innerJoin<T extends HasResultSet, D>(
|
Join innerJoin(Table other, Expression<bool?> on, {bool? useColumns}) {
|
||||||
ResultSetImplementation<T, D> other, Expression<bool?> on,
|
|
||||||
{bool? useColumns}) {
|
|
||||||
return Join._(_JoinType.inner, other, on, includeInResult: useColumns);
|
return Join._(_JoinType.inner, other, on, includeInResult: useColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +91,7 @@ Join innerJoin<T extends HasResultSet, D>(
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
||||||
/// - http://www.sqlitetutorial.net/sqlite-left-join/
|
/// - http://www.sqlitetutorial.net/sqlite-left-join/
|
||||||
Join leftOuterJoin<T extends HasResultSet, D>(
|
Join leftOuterJoin(Table other, Expression<bool?> on, {bool? useColumns}) {
|
||||||
ResultSetImplementation<T, D> other, Expression<bool?> on,
|
|
||||||
{bool? useColumns}) {
|
|
||||||
return Join._(_JoinType.leftOuter, other, on, includeInResult: useColumns);
|
return Join._(_JoinType.leftOuter, other, on, includeInResult: useColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +103,6 @@ Join leftOuterJoin<T extends HasResultSet, D>(
|
||||||
/// See also:
|
/// See also:
|
||||||
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
/// - https://drift.simonbinder.eu/docs/advanced-features/joins/#joins
|
||||||
/// - http://www.sqlitetutorial.net/sqlite-cross-join/
|
/// - http://www.sqlitetutorial.net/sqlite-cross-join/
|
||||||
Join crossJoin<T extends HasResultSet, D>(ResultSetImplementation<T, D> other,
|
Join crossJoin(Table other, {bool? useColumns}) {
|
||||||
{bool? useColumns}) {
|
|
||||||
return Join._(_JoinType.cross, other, null, includeInResult: useColumns);
|
return Join._(_JoinType.cross, other, null, includeInResult: useColumns);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,10 @@ class GenerationContext {
|
||||||
/// explicit indices starting at [explicitVariableIndex].
|
/// explicit indices starting at [explicitVariableIndex].
|
||||||
int? explicitVariableIndex;
|
int? explicitVariableIndex;
|
||||||
|
|
||||||
|
/// When set to an entity name (view or table), generated column in that
|
||||||
|
/// entity definition will written into query as expression
|
||||||
|
String? generatingForView;
|
||||||
|
|
||||||
/// All tables that the generated query reads from.
|
/// All tables that the generated query reads from.
|
||||||
final List<ResultSetImplementation> watchedTables = [];
|
final List<ResultSetImplementation> watchedTables = [];
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Migrator {
|
||||||
await createIndex(entity);
|
await createIndex(entity);
|
||||||
} else if (entity is OnCreateQuery) {
|
} else if (entity is OnCreateQuery) {
|
||||||
await _issueCustomQuery(entity.sql, const []);
|
await _issueCustomQuery(entity.sql, const []);
|
||||||
} else if (entity is View) {
|
} else if (entity is ViewInfo) {
|
||||||
await createView(entity);
|
await createView(entity);
|
||||||
} else {
|
} else {
|
||||||
throw AssertionError('Unknown entity: $entity');
|
throw AssertionError('Unknown entity: $entity');
|
||||||
|
@ -81,6 +81,17 @@ class Migrator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Drop and recreate all views. You should call it on every upgrade
|
||||||
|
Future<void> recreateAllViews() async {
|
||||||
|
for (final entity in _db.allSchemaEntities) {
|
||||||
|
if (entity is ViewInfo) {
|
||||||
|
await _issueCustomQuery(
|
||||||
|
'DROP VIEW IF EXISTS ${entity.entityName}', const []);
|
||||||
|
await createView(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GenerationContext _createContext() {
|
GenerationContext _createContext() {
|
||||||
return GenerationContext.fromDb(_db);
|
return GenerationContext.fromDb(_db);
|
||||||
}
|
}
|
||||||
|
@ -305,8 +316,17 @@ class Migrator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a `CREATE VIEW` statement to create the [view].
|
/// Executes a `CREATE VIEW` statement to create the [view].
|
||||||
Future<void> createView(View view) {
|
Future<void> createView(ViewInfo view) async {
|
||||||
return _issueCustomQuery(view.createViewStmt, const []);
|
final stmt = view.createViewStmt;
|
||||||
|
if (stmt != null) {
|
||||||
|
await _issueCustomQuery(stmt, const []);
|
||||||
|
} else if (view.query != null) {
|
||||||
|
final context = GenerationContext.fromDb(_db);
|
||||||
|
context.generatingForView = view.entityName;
|
||||||
|
context.buffer.write('CREATE VIEW ${view.entityName} AS ');
|
||||||
|
view.query!.writeInto(context);
|
||||||
|
await _issueCustomQuery(context.sql, const []);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drops a table, trigger or index.
|
/// Drops a table, trigger or index.
|
||||||
|
|
|
@ -33,6 +33,7 @@ part 'expressions/variables.dart';
|
||||||
part 'schema/column_impl.dart';
|
part 'schema/column_impl.dart';
|
||||||
part 'schema/entities.dart';
|
part 'schema/entities.dart';
|
||||||
part 'schema/table_info.dart';
|
part 'schema/table_info.dart';
|
||||||
|
part 'schema/view_info.dart';
|
||||||
|
|
||||||
part 'statements/select/custom_select.dart';
|
part 'statements/select/custom_select.dart';
|
||||||
part 'statements/select/select.dart';
|
part 'statements/select/select.dart';
|
||||||
|
|
|
@ -153,6 +153,9 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void writeInto(GenerationContext context, {bool ignoreEscape = false}) {
|
void writeInto(GenerationContext context, {bool ignoreEscape = false}) {
|
||||||
|
if (generatedAs != null && context.generatingForView == tableName) {
|
||||||
|
generatedAs!.generatedAs.writeInto(context);
|
||||||
|
} else {
|
||||||
if (context.hasMultipleTables) {
|
if (context.hasMultipleTables) {
|
||||||
context.buffer
|
context.buffer
|
||||||
..write(tableName)
|
..write(tableName)
|
||||||
|
@ -160,6 +163,7 @@ class GeneratedColumn<T> extends Column<T> {
|
||||||
}
|
}
|
||||||
context.buffer.write(ignoreEscape ? $name : escapedName);
|
context.buffer.write(ignoreEscape ? $name : escapedName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks whether the given value fits into this column. The default
|
/// Checks whether the given value fits into this column. The default
|
||||||
/// implementation only checks for nullability, but subclasses might enforce
|
/// implementation only checks for nullability, but subclasses might enforce
|
||||||
|
|
|
@ -47,28 +47,6 @@ class Index extends DatabaseSchemaEntity {
|
||||||
Index(this.entityName, this.createIndexStmt);
|
Index(this.entityName, this.createIndexStmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sqlite view.
|
|
||||||
///
|
|
||||||
/// In drift, views can only be declared in `.drift` files.
|
|
||||||
///
|
|
||||||
/// For more information on views, see the [CREATE VIEW][sqlite-docs]
|
|
||||||
/// documentation from sqlite, or the [entry on sqlitetutorial.net][sql-tut].
|
|
||||||
///
|
|
||||||
/// [sqlite-docs]: https://www.sqlite.org/lang_createview.html
|
|
||||||
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
|
||||||
abstract class View<Self, Row> extends ResultSetImplementation<Self, Row>
|
|
||||||
implements HasResultSet {
|
|
||||||
@override
|
|
||||||
final String entityName;
|
|
||||||
|
|
||||||
/// The `CREATE VIEW` sql statement that can be used to create this view.
|
|
||||||
final String createViewStmt;
|
|
||||||
|
|
||||||
/// Creates an view model by the [createViewStmt] and its [entityName].
|
|
||||||
/// Mainly used by generated code.
|
|
||||||
View(this.entityName, this.createViewStmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An internal schema entity to run an sql statement when the database is
|
/// An internal schema entity to run an sql statement when the database is
|
||||||
/// created.
|
/// created.
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
part of '../query_builder.dart';
|
||||||
|
|
||||||
|
/// A sqlite view.
|
||||||
|
///
|
||||||
|
/// In drift, views can only be declared in `.drift` files.
|
||||||
|
///
|
||||||
|
/// For more information on views, see the [CREATE VIEW][sqlite-docs]
|
||||||
|
/// documentation from sqlite, or the [entry on sqlitetutorial.net][sql-tut].
|
||||||
|
///
|
||||||
|
/// [sqlite-docs]: https://www.sqlite.org/lang_createview.html
|
||||||
|
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
||||||
|
abstract class ViewInfo<Self extends HasResultSet, Row>
|
||||||
|
implements ResultSetImplementation<Self, Row> {
|
||||||
|
@override
|
||||||
|
String get entityName;
|
||||||
|
|
||||||
|
/// The `CREATE VIEW` sql statement that can be used to create this view.
|
||||||
|
String? get createViewStmt;
|
||||||
|
|
||||||
|
/// Predefined query from `View.as()`
|
||||||
|
Query? get query;
|
||||||
|
}
|
|
@ -12,13 +12,16 @@ class JoinedSelectStatement<FirstT extends HasResultSet, FirstD>
|
||||||
/// instead.
|
/// instead.
|
||||||
JoinedSelectStatement(DatabaseConnectionUser database,
|
JoinedSelectStatement(DatabaseConnectionUser database,
|
||||||
ResultSetImplementation<FirstT, FirstD> table, this._joins,
|
ResultSetImplementation<FirstT, FirstD> table, this._joins,
|
||||||
[this.distinct = false, this._includeMainTableInResult = true])
|
[this.distinct = false,
|
||||||
|
this._includeMainTableInResult = true,
|
||||||
|
this._includeJoinedTablesInResult = true])
|
||||||
: super(database, table);
|
: super(database, table);
|
||||||
|
|
||||||
/// Whether to generate a `SELECT DISTINCT` query that will remove duplicate
|
/// Whether to generate a `SELECT DISTINCT` query that will remove duplicate
|
||||||
/// rows from the result set.
|
/// rows from the result set.
|
||||||
final bool distinct;
|
final bool distinct;
|
||||||
final bool _includeMainTableInResult;
|
final bool _includeMainTableInResult;
|
||||||
|
final bool _includeJoinedTablesInResult;
|
||||||
final List<Join> _joins;
|
final List<Join> _joins;
|
||||||
|
|
||||||
/// All columns that we're selecting from.
|
/// All columns that we're selecting from.
|
||||||
|
@ -43,8 +46,8 @@ class JoinedSelectStatement<FirstT extends HasResultSet, FirstD>
|
||||||
@override
|
@override
|
||||||
int get _returnedColumnCount {
|
int get _returnedColumnCount {
|
||||||
return _joins.fold(_selectedColumns.length, (prev, join) {
|
return _joins.fold(_selectedColumns.length, (prev, join) {
|
||||||
if (join.includeInResult) {
|
if (join.includeInResult ?? _includeJoinedTablesInResult) {
|
||||||
return prev + join.table.$columns.length;
|
return prev + (join.table as ResultSetImplementation).$columns.length;
|
||||||
}
|
}
|
||||||
return prev;
|
return prev;
|
||||||
});
|
});
|
||||||
|
@ -61,9 +64,10 @@ class JoinedSelectStatement<FirstT extends HasResultSet, FirstD>
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final join in _joins) {
|
for (final join in _joins) {
|
||||||
if (onlyResults && !join.includeInResult) continue;
|
if (onlyResults &&
|
||||||
|
!(join.includeInResult ?? _includeJoinedTablesInResult)) continue;
|
||||||
|
|
||||||
yield join.table;
|
yield join.table as ResultSetImplementation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +90,11 @@ class JoinedSelectStatement<FirstT extends HasResultSet, FirstD>
|
||||||
final column = _selectedColumns[i];
|
final column = _selectedColumns[i];
|
||||||
String chosenAlias;
|
String chosenAlias;
|
||||||
if (column is GeneratedColumn) {
|
if (column is GeneratedColumn) {
|
||||||
|
if (ctx.generatingForView == column.tableName) {
|
||||||
|
chosenAlias = '${column.$name}';
|
||||||
|
} else {
|
||||||
chosenAlias = '${column.tableName}.${column.$name}';
|
chosenAlias = '${column.tableName}.${column.$name}';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
chosenAlias = 'c$i';
|
chosenAlias = 'c$i';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1536,14 +1536,21 @@ class MyViewData extends DataClass {
|
||||||
other.syncStateImplicit == this.syncStateImplicit);
|
other.syncStateImplicit == this.syncStateImplicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyView extends View<MyView, MyViewData> {
|
class MyView extends ViewInfo<MyView, MyViewData> implements HasResultSet {
|
||||||
MyView()
|
final _$CustomTablesDb _db;
|
||||||
: super('my_view',
|
final String? _alias;
|
||||||
'CREATE VIEW my_view AS SELECT * FROM config WHERE sync_state = 2');
|
MyView(this._db, [this._alias]);
|
||||||
@override
|
@override
|
||||||
List<GeneratedColumn> get $columns =>
|
List<GeneratedColumn> get $columns =>
|
||||||
[configKey, configValue, syncState, syncStateImplicit];
|
[configKey, configValue, syncState, syncStateImplicit];
|
||||||
@override
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
@override
|
||||||
|
String get entityName => 'my_view';
|
||||||
|
@override
|
||||||
|
String get createViewStmt =>
|
||||||
|
'CREATE VIEW my_view AS SELECT * FROM config WHERE sync_state = 2';
|
||||||
|
@override
|
||||||
MyView get asDslTable => this;
|
MyView get asDslTable => this;
|
||||||
@override
|
@override
|
||||||
MyViewData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
MyViewData map(Map<String, dynamic> data, {String? tablePrefix}) {
|
||||||
|
@ -1566,6 +1573,13 @@ class MyView extends View<MyView, MyViewData> {
|
||||||
'sync_state_implicit', aliasedName, true,
|
'sync_state_implicit', aliasedName, true,
|
||||||
type: const IntType())
|
type: const IntType())
|
||||||
.withConverter<SyncType?>(ConfigTable.$converter1);
|
.withConverter<SyncType?>(ConfigTable.$converter1);
|
||||||
|
@override
|
||||||
|
MyView createAlias(String alias) {
|
||||||
|
return MyView(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query? get query => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _$CustomTablesDb extends GeneratedDatabase {
|
abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
|
@ -1578,7 +1592,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
|
||||||
late final Trigger myTrigger = Trigger(
|
late final Trigger myTrigger = Trigger(
|
||||||
'CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN INSERT INTO with_defaults VALUES (new.config_key, LENGTH(new.config_value));END',
|
'CREATE TRIGGER my_trigger AFTER INSERT ON config BEGIN INSERT INTO with_defaults VALUES (new.config_key, LENGTH(new.config_value));END',
|
||||||
'my_trigger');
|
'my_trigger');
|
||||||
late final MyView myView = MyView();
|
late final MyView myView = MyView(this);
|
||||||
late final NoIds noIds = NoIds(this);
|
late final NoIds noIds = NoIds(this);
|
||||||
late final WithConstraints withConstraints = WithConstraints(this);
|
late final WithConstraints withConstraints = WithConstraints(this);
|
||||||
late final Mytable mytable = Mytable(this);
|
late final Mytable mytable = Mytable(this);
|
||||||
|
|
|
@ -117,6 +117,28 @@ class CustomConverter extends TypeConverter<MyCustomObject, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class CategoryTodoCountView extends View {
|
||||||
|
TodosTable get todos;
|
||||||
|
Categories get categories;
|
||||||
|
|
||||||
|
Expression<int> get itemCount => todos.id.count();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([categories.description, itemCount])
|
||||||
|
.from(categories)
|
||||||
|
.join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class TodoWithCategoryView extends View {
|
||||||
|
TodosTable get todos;
|
||||||
|
Categories get categories;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([todos.title, categories.description])
|
||||||
|
.from(todos)
|
||||||
|
.join([innerJoin(categories, categories.id.equalsExp(todos.category))]);
|
||||||
|
}
|
||||||
|
|
||||||
@DriftDatabase(
|
@DriftDatabase(
|
||||||
tables: [
|
tables: [
|
||||||
TodosTable,
|
TodosTable,
|
||||||
|
@ -126,6 +148,10 @@ class CustomConverter extends TypeConverter<MyCustomObject, String> {
|
||||||
TableWithoutPK,
|
TableWithoutPK,
|
||||||
PureDefaults,
|
PureDefaults,
|
||||||
],
|
],
|
||||||
|
views: [
|
||||||
|
CategoryTodoCountView,
|
||||||
|
TodoWithCategoryView,
|
||||||
|
],
|
||||||
daos: [SomeDao],
|
daos: [SomeDao],
|
||||||
queries: {
|
queries: {
|
||||||
'allTodosWithCategory': 'SELECT t.*, c.id as catId, c."desc" as catDesc '
|
'allTodosWithCategory': 'SELECT t.*, c.id as catId, c."desc" as catDesc '
|
||||||
|
|
|
@ -184,6 +184,7 @@ class $CategoriesTable extends Categories
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$CategoriesTable(this._db, [this._alias]);
|
$CategoriesTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
|
@ -191,12 +192,14 @@ class $CategoriesTable extends Categories
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _descriptionMeta =
|
final VerificationMeta _descriptionMeta =
|
||||||
const VerificationMeta('description');
|
const VerificationMeta('description');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> description = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> description = GeneratedColumn<String?>(
|
||||||
'desc', aliasedName, false,
|
'desc', aliasedName, false,
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
requiredDuringInsert: true,
|
requiredDuringInsert: true,
|
||||||
$customConstraints: 'NOT NULL UNIQUE');
|
$customConstraints: 'NOT NULL UNIQUE');
|
||||||
final VerificationMeta _priorityMeta = const VerificationMeta('priority');
|
final VerificationMeta _priorityMeta = const VerificationMeta('priority');
|
||||||
|
@override
|
||||||
late final GeneratedColumnWithTypeConverter<CategoryPriority, int?> priority =
|
late final GeneratedColumnWithTypeConverter<CategoryPriority, int?> priority =
|
||||||
GeneratedColumn<int?>('priority', aliasedName, false,
|
GeneratedColumn<int?>('priority', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
|
@ -205,6 +208,7 @@ class $CategoriesTable extends Categories
|
||||||
.withConverter<CategoryPriority>($CategoriesTable.$converter0);
|
.withConverter<CategoryPriority>($CategoriesTable.$converter0);
|
||||||
final VerificationMeta _descriptionInUpperCaseMeta =
|
final VerificationMeta _descriptionInUpperCaseMeta =
|
||||||
const VerificationMeta('descriptionInUpperCase');
|
const VerificationMeta('descriptionInUpperCase');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> descriptionInUpperCase =
|
late final GeneratedColumn<String?> descriptionInUpperCase =
|
||||||
GeneratedColumn<String?>('description_in_upper_case', aliasedName, false,
|
GeneratedColumn<String?>('description_in_upper_case', aliasedName, false,
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
|
@ -474,12 +478,14 @@ class $TodosTableTable extends TodosTable
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$TodosTableTable(this._db, [this._alias]);
|
$TodosTableTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _titleMeta = const VerificationMeta('title');
|
final VerificationMeta _titleMeta = const VerificationMeta('title');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
||||||
'title', aliasedName, true,
|
'title', aliasedName, true,
|
||||||
additionalChecks:
|
additionalChecks:
|
||||||
|
@ -487,14 +493,17 @@ class $TodosTableTable extends TodosTable
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
requiredDuringInsert: false);
|
requiredDuringInsert: false);
|
||||||
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
final VerificationMeta _contentMeta = const VerificationMeta('content');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> content = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> content = GeneratedColumn<String?>(
|
||||||
'content', aliasedName, false,
|
'content', aliasedName, false,
|
||||||
type: const StringType(), requiredDuringInsert: true);
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _targetDateMeta = const VerificationMeta('targetDate');
|
final VerificationMeta _targetDateMeta = const VerificationMeta('targetDate');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<DateTime?> targetDate = GeneratedColumn<DateTime?>(
|
late final GeneratedColumn<DateTime?> targetDate = GeneratedColumn<DateTime?>(
|
||||||
'target_date', aliasedName, true,
|
'target_date', aliasedName, true,
|
||||||
type: const IntType(), requiredDuringInsert: false);
|
type: const IntType(), requiredDuringInsert: false);
|
||||||
final VerificationMeta _categoryMeta = const VerificationMeta('category');
|
final VerificationMeta _categoryMeta = const VerificationMeta('category');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> category = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> category = GeneratedColumn<int?>(
|
||||||
'category', aliasedName, true,
|
'category', aliasedName, true,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
|
@ -757,12 +766,14 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$UsersTable(this._db, [this._alias]);
|
$UsersTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
||||||
'name', aliasedName, false,
|
'name', aliasedName, false,
|
||||||
additionalChecks:
|
additionalChecks:
|
||||||
|
@ -770,6 +781,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
requiredDuringInsert: true);
|
requiredDuringInsert: true);
|
||||||
final VerificationMeta _isAwesomeMeta = const VerificationMeta('isAwesome');
|
final VerificationMeta _isAwesomeMeta = const VerificationMeta('isAwesome');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<bool?> isAwesome = GeneratedColumn<bool?>(
|
late final GeneratedColumn<bool?> isAwesome = GeneratedColumn<bool?>(
|
||||||
'is_awesome', aliasedName, false,
|
'is_awesome', aliasedName, false,
|
||||||
type: const BoolType(),
|
type: const BoolType(),
|
||||||
|
@ -778,11 +790,13 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
defaultValue: const Constant(true));
|
defaultValue: const Constant(true));
|
||||||
final VerificationMeta _profilePictureMeta =
|
final VerificationMeta _profilePictureMeta =
|
||||||
const VerificationMeta('profilePicture');
|
const VerificationMeta('profilePicture');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<Uint8List?> profilePicture =
|
late final GeneratedColumn<Uint8List?> profilePicture =
|
||||||
GeneratedColumn<Uint8List?>('profile_picture', aliasedName, false,
|
GeneratedColumn<Uint8List?>('profile_picture', aliasedName, false,
|
||||||
type: const BlobType(), requiredDuringInsert: true);
|
type: const BlobType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _creationTimeMeta =
|
final VerificationMeta _creationTimeMeta =
|
||||||
const VerificationMeta('creationTime');
|
const VerificationMeta('creationTime');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<DateTime?> creationTime =
|
late final GeneratedColumn<DateTime?> creationTime =
|
||||||
GeneratedColumn<DateTime?>('creation_time', aliasedName, false,
|
GeneratedColumn<DateTime?>('creation_time', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
|
@ -974,10 +988,12 @@ class $SharedTodosTable extends SharedTodos
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$SharedTodosTable(this._db, [this._alias]);
|
$SharedTodosTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _todoMeta = const VerificationMeta('todo');
|
final VerificationMeta _todoMeta = const VerificationMeta('todo');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> todo = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> todo = GeneratedColumn<int?>(
|
||||||
'todo', aliasedName, false,
|
'todo', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _userMeta = const VerificationMeta('user');
|
final VerificationMeta _userMeta = const VerificationMeta('user');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> user = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> user = GeneratedColumn<int?>(
|
||||||
'user', aliasedName, false,
|
'user', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
|
@ -1114,14 +1130,17 @@ class $TableWithoutPKTable extends TableWithoutPK
|
||||||
$TableWithoutPKTable(this._db, [this._alias]);
|
$TableWithoutPKTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _notReallyAnIdMeta =
|
final VerificationMeta _notReallyAnIdMeta =
|
||||||
const VerificationMeta('notReallyAnId');
|
const VerificationMeta('notReallyAnId');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> notReallyAnId = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> notReallyAnId = GeneratedColumn<int?>(
|
||||||
'not_really_an_id', aliasedName, false,
|
'not_really_an_id', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _someFloatMeta = const VerificationMeta('someFloat');
|
final VerificationMeta _someFloatMeta = const VerificationMeta('someFloat');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<double?> someFloat = GeneratedColumn<double?>(
|
late final GeneratedColumn<double?> someFloat = GeneratedColumn<double?>(
|
||||||
'some_float', aliasedName, false,
|
'some_float', aliasedName, false,
|
||||||
type: const RealType(), requiredDuringInsert: true);
|
type: const RealType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _customMeta = const VerificationMeta('custom');
|
final VerificationMeta _customMeta = const VerificationMeta('custom');
|
||||||
|
@override
|
||||||
late final GeneratedColumnWithTypeConverter<MyCustomObject, String?> custom =
|
late final GeneratedColumnWithTypeConverter<MyCustomObject, String?> custom =
|
||||||
GeneratedColumn<String?>('custom', aliasedName, false,
|
GeneratedColumn<String?>('custom', aliasedName, false,
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
|
@ -1291,6 +1310,7 @@ class $PureDefaultsTable extends PureDefaults
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$PureDefaultsTable(this._db, [this._alias]);
|
$PureDefaultsTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _txtMeta = const VerificationMeta('txt');
|
final VerificationMeta _txtMeta = const VerificationMeta('txt');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> txt = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> txt = GeneratedColumn<String?>(
|
||||||
'insert', aliasedName, true,
|
'insert', aliasedName, true,
|
||||||
type: const StringType(), requiredDuringInsert: false);
|
type: const StringType(), requiredDuringInsert: false);
|
||||||
|
@ -1326,6 +1346,215 @@ class $PureDefaultsTable extends PureDefaults
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CategoryTodoCountViewData extends DataClass {
|
||||||
|
final String description;
|
||||||
|
final int itemCount;
|
||||||
|
CategoryTodoCountViewData(
|
||||||
|
{required this.description, required this.itemCount});
|
||||||
|
factory CategoryTodoCountViewData.fromData(Map<String, dynamic> data,
|
||||||
|
{String? prefix}) {
|
||||||
|
final effectivePrefix = prefix ?? '';
|
||||||
|
return CategoryTodoCountViewData(
|
||||||
|
description: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}categories.desc'])!,
|
||||||
|
itemCount: const IntType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}item_count'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory CategoryTodoCountViewData.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return CategoryTodoCountViewData(
|
||||||
|
description: serializer.fromJson<String>(json['description']),
|
||||||
|
itemCount: serializer.fromJson<int>(json['itemCount']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory CategoryTodoCountViewData.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
CategoryTodoCountViewData.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'description': serializer.toJson<String>(description),
|
||||||
|
'itemCount': serializer.toJson<int>(itemCount),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CategoryTodoCountViewData copyWith({String? description, int? itemCount}) =>
|
||||||
|
CategoryTodoCountViewData(
|
||||||
|
description: description ?? this.description,
|
||||||
|
itemCount: itemCount ?? this.itemCount,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('CategoryTodoCountViewData(')
|
||||||
|
..write('description: $description, ')
|
||||||
|
..write('itemCount: $itemCount')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(description, itemCount);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is CategoryTodoCountViewData &&
|
||||||
|
other.description == this.description &&
|
||||||
|
other.itemCount == this.itemCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $CategoryTodoCountViewView
|
||||||
|
extends ViewInfo<$CategoryTodoCountViewView, CategoryTodoCountViewData>
|
||||||
|
implements HasResultSet {
|
||||||
|
final _$TodoDb _db;
|
||||||
|
final String? _alias;
|
||||||
|
$CategoryTodoCountViewView(this._db, [this._alias]);
|
||||||
|
$TodosTableTable get todos => _db.todosTable;
|
||||||
|
$CategoriesTable get categories => _db.categories;
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [categories.description, itemCount];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
@override
|
||||||
|
String get entityName => 'category_todo_count_view';
|
||||||
|
@override
|
||||||
|
String? get createViewStmt => null;
|
||||||
|
@override
|
||||||
|
$CategoryTodoCountViewView get asDslTable => this;
|
||||||
|
@override
|
||||||
|
CategoryTodoCountViewData map(Map<String, dynamic> data,
|
||||||
|
{String? tablePrefix}) {
|
||||||
|
return CategoryTodoCountViewData.fromData(data,
|
||||||
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final GeneratedColumn<String?> description = GeneratedColumn<String?>(
|
||||||
|
'desc', aliasedName, false,
|
||||||
|
type: const StringType(), $customConstraints: 'NOT NULL UNIQUE');
|
||||||
|
late final GeneratedColumn<int?> itemCount = GeneratedColumn<int?>(
|
||||||
|
'item_count', aliasedName, false,
|
||||||
|
type: const IntType(), generatedAs: GeneratedAs(todos.id.count(), false));
|
||||||
|
@override
|
||||||
|
$CategoryTodoCountViewView createAlias(String alias) {
|
||||||
|
return $CategoryTodoCountViewView(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query? get query =>
|
||||||
|
(_db.selectOnly(categories, includeJoinedTableColumns: false)
|
||||||
|
..addColumns($columns))
|
||||||
|
.join([innerJoin(todos, todos.category.equalsExp(categories.id))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TodoWithCategoryViewData extends DataClass {
|
||||||
|
final String? title;
|
||||||
|
final String description;
|
||||||
|
TodoWithCategoryViewData({this.title, required this.description});
|
||||||
|
factory TodoWithCategoryViewData.fromData(Map<String, dynamic> data,
|
||||||
|
{String? prefix}) {
|
||||||
|
final effectivePrefix = prefix ?? '';
|
||||||
|
return TodoWithCategoryViewData(
|
||||||
|
title: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}todos.title']),
|
||||||
|
description: const StringType()
|
||||||
|
.mapFromDatabaseResponse(data['${effectivePrefix}categories.desc'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoWithCategoryViewData.fromJson(Map<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return TodoWithCategoryViewData(
|
||||||
|
title: serializer.fromJson<String?>(json['title']),
|
||||||
|
description: serializer.fromJson<String>(json['description']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory TodoWithCategoryViewData.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
TodoWithCategoryViewData.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'title': serializer.toJson<String?>(title),
|
||||||
|
'description': serializer.toJson<String>(description),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TodoWithCategoryViewData copyWith(
|
||||||
|
{Value<String?> title = const Value.absent(), String? description}) =>
|
||||||
|
TodoWithCategoryViewData(
|
||||||
|
title: title.present ? title.value : this.title,
|
||||||
|
description: description ?? this.description,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('TodoWithCategoryViewData(')
|
||||||
|
..write('title: $title, ')
|
||||||
|
..write('description: $description')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(title, description);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is TodoWithCategoryViewData &&
|
||||||
|
other.title == this.title &&
|
||||||
|
other.description == this.description);
|
||||||
|
}
|
||||||
|
|
||||||
|
class $TodoWithCategoryViewView
|
||||||
|
extends ViewInfo<$TodoWithCategoryViewView, TodoWithCategoryViewData>
|
||||||
|
implements HasResultSet {
|
||||||
|
final _$TodoDb _db;
|
||||||
|
final String? _alias;
|
||||||
|
$TodoWithCategoryViewView(this._db, [this._alias]);
|
||||||
|
$TodosTableTable get todos => _db.todosTable;
|
||||||
|
$CategoriesTable get categories => _db.categories;
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> get $columns => [todos.title, categories.description];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? entityName;
|
||||||
|
@override
|
||||||
|
String get entityName => 'todo_with_category_view';
|
||||||
|
@override
|
||||||
|
String? get createViewStmt => null;
|
||||||
|
@override
|
||||||
|
$TodoWithCategoryViewView get asDslTable => this;
|
||||||
|
@override
|
||||||
|
TodoWithCategoryViewData map(Map<String, dynamic> data,
|
||||||
|
{String? tablePrefix}) {
|
||||||
|
return TodoWithCategoryViewData.fromData(data,
|
||||||
|
prefix: tablePrefix != null ? '$tablePrefix.' : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final GeneratedColumn<String?> title = GeneratedColumn<String?>(
|
||||||
|
'title', aliasedName, true,
|
||||||
|
additionalChecks:
|
||||||
|
GeneratedColumn.checkTextLength(minTextLength: 4, maxTextLength: 16),
|
||||||
|
type: const StringType());
|
||||||
|
late final GeneratedColumn<String?> description = GeneratedColumn<String?>(
|
||||||
|
'desc', aliasedName, false,
|
||||||
|
type: const StringType(), $customConstraints: 'NOT NULL UNIQUE');
|
||||||
|
@override
|
||||||
|
$TodoWithCategoryViewView createAlias(String alias) {
|
||||||
|
return $TodoWithCategoryViewView(_db, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query? get query => (_db.selectOnly(todos, includeJoinedTableColumns: false)
|
||||||
|
..addColumns($columns))
|
||||||
|
.join([innerJoin(categories, categories.id.equalsExp(todos.category))]);
|
||||||
|
}
|
||||||
|
|
||||||
abstract class _$TodoDb extends GeneratedDatabase {
|
abstract class _$TodoDb extends GeneratedDatabase {
|
||||||
_$TodoDb(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e);
|
_$TodoDb(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e);
|
||||||
_$TodoDb.connect(DatabaseConnection c) : super.connect(c);
|
_$TodoDb.connect(DatabaseConnection c) : super.connect(c);
|
||||||
|
@ -1335,6 +1564,10 @@ abstract class _$TodoDb extends GeneratedDatabase {
|
||||||
late final $SharedTodosTable sharedTodos = $SharedTodosTable(this);
|
late final $SharedTodosTable sharedTodos = $SharedTodosTable(this);
|
||||||
late final $TableWithoutPKTable tableWithoutPK = $TableWithoutPKTable(this);
|
late final $TableWithoutPKTable tableWithoutPK = $TableWithoutPKTable(this);
|
||||||
late final $PureDefaultsTable pureDefaults = $PureDefaultsTable(this);
|
late final $PureDefaultsTable pureDefaults = $PureDefaultsTable(this);
|
||||||
|
late final $CategoryTodoCountViewView categoryTodoCountView =
|
||||||
|
$CategoryTodoCountViewView(this);
|
||||||
|
late final $TodoWithCategoryViewView todoWithCategoryView =
|
||||||
|
$TodoWithCategoryViewView(this);
|
||||||
late final SomeDao someDao = SomeDao(this as TodoDb);
|
late final SomeDao someDao = SomeDao(this as TodoDb);
|
||||||
Selectable<AllTodosWithCategoryResult> allTodosWithCategory() {
|
Selectable<AllTodosWithCategoryResult> allTodosWithCategory() {
|
||||||
return customSelect(
|
return customSelect(
|
||||||
|
@ -1412,7 +1645,9 @@ abstract class _$TodoDb extends GeneratedDatabase {
|
||||||
users,
|
users,
|
||||||
sharedTodos,
|
sharedTodos,
|
||||||
tableWithoutPK,
|
tableWithoutPK,
|
||||||
pureDefaults
|
pureDefaults,
|
||||||
|
categoryTodoCountView,
|
||||||
|
todoWithCategoryView
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ void main() {
|
||||||
|
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
filter.add(i);
|
filter.add(i);
|
||||||
await pumpEventQueue(times: 10);
|
await pumpEventQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
final values = await db
|
final values = await db
|
||||||
|
@ -113,6 +113,6 @@ void main() {
|
||||||
.map((row) => row.read<String>('r'))
|
.map((row) => row.read<String>('r'))
|
||||||
.getSingle();
|
.getSingle();
|
||||||
|
|
||||||
expect(values, anyOf('0,3', '3'));
|
expect(values, anyOf('0,3', '3', '1,3'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -401,6 +401,43 @@ void main() {
|
||||||
expect(result.read(todos.id.count()), equals(10));
|
expect(result.read(todos.id.count()), equals(10));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('use selectOnly(includeJoinedTableColumns) instead of useColumns',
|
||||||
|
() async {
|
||||||
|
final categories = db.categories;
|
||||||
|
final todos = db.todosTable;
|
||||||
|
|
||||||
|
final query =
|
||||||
|
db.selectOnly(categories, includeJoinedTableColumns: false).join([
|
||||||
|
innerJoin(
|
||||||
|
todos,
|
||||||
|
todos.category.equalsExp(categories.id),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
query
|
||||||
|
..addColumns([categories.id, todos.id.count()])
|
||||||
|
..groupBy([categories.id]);
|
||||||
|
|
||||||
|
when(executor.runSelect(any, any)).thenAnswer((_) async {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'categories.id': 2,
|
||||||
|
'c1': 10,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
final result = await query.getSingle();
|
||||||
|
|
||||||
|
verify(executor.runSelect(
|
||||||
|
'SELECT categories.id AS "categories.id", COUNT(todos.id) AS "c1" '
|
||||||
|
'FROM categories INNER JOIN todos ON todos.category = categories.id '
|
||||||
|
'GROUP BY categories.id;',
|
||||||
|
[]));
|
||||||
|
|
||||||
|
expect(result.read(categories.id), equals(2));
|
||||||
|
expect(result.read(todos.id.count()), equals(10));
|
||||||
|
});
|
||||||
|
|
||||||
test('injects custom error message when a table is used multiple times',
|
test('injects custom error message when a table is used multiple times',
|
||||||
() async {
|
() async {
|
||||||
when(executor.runSelect(any, any)).thenAnswer((_) => Future.error('nah'));
|
when(executor.runSelect(any, any)).thenAnswer((_) => Future.error('nah'));
|
||||||
|
|
|
@ -64,6 +64,24 @@ void main() {
|
||||||
'custom TEXT NOT NULL'
|
'custom TEXT NOT NULL'
|
||||||
');',
|
');',
|
||||||
[]));
|
[]));
|
||||||
|
|
||||||
|
verify(mockExecutor.runCustom(
|
||||||
|
'CREATE VIEW category_todo_count_view AS SELECT '
|
||||||
|
'categories."desc" AS "categories.desc", '
|
||||||
|
'COUNT(todos.id) AS "item_count" '
|
||||||
|
'FROM categories '
|
||||||
|
'INNER JOIN todos '
|
||||||
|
'ON todos.category = categories.id',
|
||||||
|
[]));
|
||||||
|
|
||||||
|
verify(mockExecutor.runCustom(
|
||||||
|
'CREATE VIEW todo_with_category_view AS SELECT '
|
||||||
|
'todos.title AS "todos.title", '
|
||||||
|
'categories."desc" AS "categories.desc" '
|
||||||
|
'FROM todos '
|
||||||
|
'INNER JOIN categories '
|
||||||
|
'ON categories.id = todos.category',
|
||||||
|
[]));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('creates individual tables', () async {
|
test('creates individual tables', () async {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:analyzer/dart/ast/ast.dart';
|
import 'package:analyzer/dart/ast/ast.dart';
|
||||||
import 'package:analyzer/dart/constant/value.dart';
|
import 'package:analyzer/dart/constant/value.dart';
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
|
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
||||||
import 'package:analyzer/dart/element/type.dart';
|
import 'package:analyzer/dart/element/type.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/sqlite_keywords.dart';
|
import 'package:drift/sqlite_keywords.dart';
|
||||||
|
@ -9,6 +10,7 @@ import 'package:drift_dev/src/analyzer/errors.dart';
|
||||||
import 'package:drift_dev/src/analyzer/runner/steps.dart';
|
import 'package:drift_dev/src/analyzer/runner/steps.dart';
|
||||||
import 'package:drift_dev/src/model/declarations/declaration.dart';
|
import 'package:drift_dev/src/model/declarations/declaration.dart';
|
||||||
import 'package:drift_dev/src/model/used_type_converter.dart';
|
import 'package:drift_dev/src/model/used_type_converter.dart';
|
||||||
|
import 'package:drift_dev/src/utils/exception.dart';
|
||||||
import 'package:drift_dev/src/utils/names.dart';
|
import 'package:drift_dev/src/utils/names.dart';
|
||||||
import 'package:drift_dev/src/utils/type_utils.dart';
|
import 'package:drift_dev/src/utils/type_utils.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
@ -20,6 +22,7 @@ import '../custom_row_class.dart';
|
||||||
|
|
||||||
part 'column_parser.dart';
|
part 'column_parser.dart';
|
||||||
part 'table_parser.dart';
|
part 'table_parser.dart';
|
||||||
|
part 'view_parser.dart';
|
||||||
part 'use_dao_parser.dart';
|
part 'use_dao_parser.dart';
|
||||||
part 'use_moor_parser.dart';
|
part 'use_moor_parser.dart';
|
||||||
|
|
||||||
|
@ -28,16 +31,23 @@ class MoorDartParser {
|
||||||
|
|
||||||
late ColumnParser _columnParser;
|
late ColumnParser _columnParser;
|
||||||
late TableParser _tableParser;
|
late TableParser _tableParser;
|
||||||
|
late ViewParser _viewParser;
|
||||||
|
|
||||||
MoorDartParser(this.step) {
|
MoorDartParser(this.step) {
|
||||||
_columnParser = ColumnParser(this);
|
_columnParser = ColumnParser(this);
|
||||||
_tableParser = TableParser(this);
|
_tableParser = TableParser(this);
|
||||||
|
_viewParser = ViewParser(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoorTable?> parseTable(ClassElement classElement) {
|
Future<MoorTable?> parseTable(ClassElement classElement) {
|
||||||
return _tableParser.parseTable(classElement);
|
return _tableParser.parseTable(classElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<MoorView?> parseView(
|
||||||
|
ClassElement classElement, List<MoorTable> tables) {
|
||||||
|
return _viewParser.parseView(classElement, tables);
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to parse the column created from the Dart getter.
|
/// Attempts to parse the column created from the Dart getter.
|
||||||
///
|
///
|
||||||
/// When the column is invalid, an error will be logged and `null` is
|
/// When the column is invalid, an error will be logged and `null` is
|
||||||
|
|
|
@ -40,6 +40,13 @@ class UseDaoParser {
|
||||||
const [];
|
const [];
|
||||||
final queryStrings = annotation.peek('queries')?.mapValue ?? {};
|
final queryStrings = annotation.peek('queries')?.mapValue ?? {};
|
||||||
|
|
||||||
|
final viewTypes = annotation
|
||||||
|
.peek('views')
|
||||||
|
?.listValue
|
||||||
|
.map((obj) => obj.toTypeValue())
|
||||||
|
.whereType<DartType>() ??
|
||||||
|
const [];
|
||||||
|
|
||||||
final includes = annotation
|
final includes = annotation
|
||||||
.read('include')
|
.read('include')
|
||||||
.objectValue
|
.objectValue
|
||||||
|
@ -50,12 +57,14 @@ class UseDaoParser {
|
||||||
[];
|
[];
|
||||||
|
|
||||||
final parsedTables = await step.parseTables(tableTypes, element);
|
final parsedTables = await step.parseTables(tableTypes, element);
|
||||||
|
final parsedViews = await step.parseViews(viewTypes, element, parsedTables);
|
||||||
final parsedQueries = step.readDeclaredQueries(queryStrings.cast());
|
final parsedQueries = step.readDeclaredQueries(queryStrings.cast());
|
||||||
|
|
||||||
return Dao(
|
return Dao(
|
||||||
declaration: DatabaseOrDaoDeclaration(element, step.file),
|
declaration: DatabaseOrDaoDeclaration(element, step.file),
|
||||||
dbClass: dbImpl,
|
dbClass: dbImpl,
|
||||||
declaredTables: parsedTables,
|
declaredTables: parsedTables,
|
||||||
|
declaredViews: parsedViews,
|
||||||
declaredIncludes: includes,
|
declaredIncludes: includes,
|
||||||
declaredQueries: parsedQueries,
|
declaredQueries: parsedQueries,
|
||||||
);
|
);
|
||||||
|
|
|
@ -23,6 +23,13 @@ class UseMoorParser {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final viewTypes = annotation
|
||||||
|
.peek('views')
|
||||||
|
?.listValue
|
||||||
|
.map((obj) => obj.toTypeValue())
|
||||||
|
.whereType<DartType>() ??
|
||||||
|
const [];
|
||||||
|
|
||||||
final tableTypes = tablesOrNull ?? [];
|
final tableTypes = tablesOrNull ?? [];
|
||||||
final queryStrings = annotation.peek('queries')?.mapValue ?? {};
|
final queryStrings = annotation.peek('queries')?.mapValue ?? {};
|
||||||
final includes = annotation
|
final includes = annotation
|
||||||
|
@ -34,13 +41,14 @@ class UseMoorParser {
|
||||||
[];
|
[];
|
||||||
|
|
||||||
final parsedTables = await step.parseTables(tableTypes, element);
|
final parsedTables = await step.parseTables(tableTypes, element);
|
||||||
|
final parsedViews = await step.parseViews(viewTypes, element, parsedTables);
|
||||||
final parsedQueries = step.readDeclaredQueries(queryStrings.cast());
|
final parsedQueries = step.readDeclaredQueries(queryStrings.cast());
|
||||||
final daoTypes = _readDaoTypes(annotation);
|
final daoTypes = _readDaoTypes(annotation);
|
||||||
|
|
||||||
return Database(
|
return Database(
|
||||||
declaration: DatabaseOrDaoDeclaration(element, step.file),
|
declaration: DatabaseOrDaoDeclaration(element, step.file),
|
||||||
declaredTables: parsedTables,
|
declaredTables: parsedTables,
|
||||||
|
declaredViews: parsedViews,
|
||||||
daos: daoTypes,
|
daos: daoTypes,
|
||||||
declaredIncludes: includes,
|
declaredIncludes: includes,
|
||||||
declaredQueries: parsedQueries,
|
declaredQueries: parsedQueries,
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
part of 'parser.dart';
|
||||||
|
|
||||||
|
/// Parses a [MoorView] from a Dart class.
|
||||||
|
class ViewParser {
|
||||||
|
final MoorDartParser base;
|
||||||
|
|
||||||
|
ViewParser(this.base);
|
||||||
|
|
||||||
|
Future<MoorView?> parseView(
|
||||||
|
ClassElement element, List<MoorTable> tables) async {
|
||||||
|
final name = await _parseViewName(element);
|
||||||
|
final columns = (await _parseColumns(element)).toList();
|
||||||
|
final staticReferences =
|
||||||
|
(await _parseStaticReferences(element, tables)).toList();
|
||||||
|
final dataClassInfo = _readDataClassInformation(columns, element);
|
||||||
|
final query = await _parseQuery(element, staticReferences, columns);
|
||||||
|
|
||||||
|
final view = MoorView(
|
||||||
|
declaration: DartViewDeclaration(element, base.step.file),
|
||||||
|
name: name,
|
||||||
|
dartTypeName: dataClassInfo.enforcedName,
|
||||||
|
existingRowClass: dataClassInfo.existingClass,
|
||||||
|
entityInfoName: '\$${element.name}View',
|
||||||
|
staticReferences: staticReferences.map((ref) => ref.declaration).toList(),
|
||||||
|
viewQuery: query,
|
||||||
|
);
|
||||||
|
|
||||||
|
view.columns = columns;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
_DataClassInformation _readDataClassInformation(
|
||||||
|
List<MoorColumn> columns, ClassElement element) {
|
||||||
|
DartObject? useRowClass;
|
||||||
|
String? dataClassName;
|
||||||
|
|
||||||
|
for (final annotation in element.metadata) {
|
||||||
|
final computed = annotation.computeConstantValue();
|
||||||
|
final annotationClass = computed!.type!.element!.name;
|
||||||
|
|
||||||
|
if (annotationClass == 'DriftView') {
|
||||||
|
dataClassName = computed.getField('dataClassName')?.toStringValue();
|
||||||
|
} else if (annotationClass == 'UseRowClass') {
|
||||||
|
useRowClass = computed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataClassName != null && useRowClass != null) {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
message: "A table can't be annotated with both @DataClassName and "
|
||||||
|
'@UseRowClass',
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
FoundDartClass? existingClass;
|
||||||
|
String? constructorInExistingClass;
|
||||||
|
bool? generateInsertable;
|
||||||
|
|
||||||
|
var name = dataClassName ?? dataClassNameForClassName(element.name);
|
||||||
|
|
||||||
|
if (useRowClass != null) {
|
||||||
|
final type = useRowClass.getField('type')!.toTypeValue();
|
||||||
|
constructorInExistingClass =
|
||||||
|
useRowClass.getField('constructor')!.toStringValue()!;
|
||||||
|
generateInsertable =
|
||||||
|
useRowClass.getField('generateInsertable')!.toBoolValue()!;
|
||||||
|
|
||||||
|
if (type is InterfaceType) {
|
||||||
|
existingClass = FoundDartClass(type.element, type.typeArguments);
|
||||||
|
name = type.element.name;
|
||||||
|
} else {
|
||||||
|
base.step.reportError(ErrorInDartCode(
|
||||||
|
message: 'The @UseRowClass annotation must be used with a class',
|
||||||
|
affectedElement: element,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final verified = existingClass == null
|
||||||
|
? null
|
||||||
|
: validateExistingClass(columns, existingClass,
|
||||||
|
constructorInExistingClass!, generateInsertable!, base.step);
|
||||||
|
return _DataClassInformation(name, verified);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> _parseViewName(ClassElement element) async {
|
||||||
|
for (final annotation in element.metadata) {
|
||||||
|
final computed = annotation.computeConstantValue();
|
||||||
|
final annotationClass = computed!.type!.element!.name;
|
||||||
|
|
||||||
|
if (annotationClass == 'DriftView') {
|
||||||
|
final name = computed.getField('name')?.toStringValue();
|
||||||
|
if (name != null) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element.name.snakeCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Iterable<MoorColumn>> _parseColumns(ClassElement element) async {
|
||||||
|
final columnNames = element.allSupertypes
|
||||||
|
.map((t) => t.element)
|
||||||
|
.followedBy([element])
|
||||||
|
.expand((e) => e.fields)
|
||||||
|
.where((field) =>
|
||||||
|
isExpression(field.type) &&
|
||||||
|
field.getter != null &&
|
||||||
|
!field.getter!.isSynthetic)
|
||||||
|
.map((field) => field.name)
|
||||||
|
.toSet();
|
||||||
|
|
||||||
|
final fields = columnNames.map((name) {
|
||||||
|
final getter = element.getGetter(name) ??
|
||||||
|
element.lookUpInheritedConcreteGetter(name, element.library);
|
||||||
|
return getter!.variable;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
final results = await Future.wait(fields.map((field) async {
|
||||||
|
final dartType = (field.type as InterfaceType).typeArguments[0];
|
||||||
|
final typeName = dartType.element!.name!;
|
||||||
|
final sqlType = _dartTypeToColumnType(typeName);
|
||||||
|
|
||||||
|
if (sqlType == null) {
|
||||||
|
final String errorMessage;
|
||||||
|
if (typeName == 'dynamic') {
|
||||||
|
errorMessage = 'You must specify Expression<?> type argument';
|
||||||
|
} else {
|
||||||
|
errorMessage =
|
||||||
|
'Invalid Expression<?> type argument `$typeName` found. '
|
||||||
|
'Must be one of: '
|
||||||
|
'bool, String, int, DateTime, Uint8List, double';
|
||||||
|
}
|
||||||
|
throw analysisError(base.step, field, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
final node =
|
||||||
|
await base.loadElementDeclaration(field.getter!) as MethodDeclaration;
|
||||||
|
final expression = (node.body as ExpressionFunctionBody).expression;
|
||||||
|
|
||||||
|
return MoorColumn(
|
||||||
|
type: sqlType,
|
||||||
|
dartGetterName: field.name,
|
||||||
|
name: ColumnName.implicitly(ReCase(field.name).snakeCase),
|
||||||
|
nullable: dartType.nullabilitySuffix == NullabilitySuffix.question,
|
||||||
|
generatedAs: ColumnGeneratedAs(expression.toString(), false));
|
||||||
|
}).toList());
|
||||||
|
|
||||||
|
return results.whereType();
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnType? _dartTypeToColumnType(String name) {
|
||||||
|
return const {
|
||||||
|
'bool': ColumnType.boolean,
|
||||||
|
'String': ColumnType.text,
|
||||||
|
'int': ColumnType.integer,
|
||||||
|
'DateTime': ColumnType.datetime,
|
||||||
|
'Uint8List': ColumnType.blob,
|
||||||
|
'double': ColumnType.real,
|
||||||
|
}[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<_TableReference>> _parseStaticReferences(
|
||||||
|
ClassElement element, List<MoorTable> tables) async {
|
||||||
|
return await Stream.fromIterable(element.allSupertypes
|
||||||
|
.map((t) => t.element)
|
||||||
|
.followedBy([element]).expand((e) => e.fields))
|
||||||
|
.asyncMap((field) => _getStaticReference(field, tables))
|
||||||
|
.where((ref) => ref != null)
|
||||||
|
.cast<_TableReference>()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<_TableReference?> _getStaticReference(
|
||||||
|
FieldElement field, List<MoorTable> tables) async {
|
||||||
|
if (field.getter != null) {
|
||||||
|
try {
|
||||||
|
final node = await base.loadElementDeclaration(field.getter!);
|
||||||
|
if (node is MethodDeclaration && node.body is EmptyFunctionBody) {
|
||||||
|
final type = tables.firstWhereOrNull(
|
||||||
|
(tbl) => tbl.fromClass!.name == node.returnType.toString());
|
||||||
|
if (type != null) {
|
||||||
|
final name = node.name.toString();
|
||||||
|
final declaration = '${type.entityInfoName} get $name => '
|
||||||
|
'_db.${type.dbGetterName};';
|
||||||
|
return _TableReference(type, name, declaration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ViewQueryInformation> _parseQuery(ClassElement element,
|
||||||
|
List<_TableReference> references, List<MoorColumn> columns) async {
|
||||||
|
final as =
|
||||||
|
element.methods.where((method) => method.name == 'as').firstOrNull;
|
||||||
|
|
||||||
|
if (as != null) {
|
||||||
|
try {
|
||||||
|
final node = await base.loadElementDeclaration(as);
|
||||||
|
|
||||||
|
var target =
|
||||||
|
((node as MethodDeclaration).body as ExpressionFunctionBody)
|
||||||
|
.expression as MethodInvocation;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (target.target == null) break;
|
||||||
|
target = target.target as MethodInvocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.methodName.toString() != 'select') {
|
||||||
|
throw analysisError(
|
||||||
|
base.step,
|
||||||
|
element,
|
||||||
|
'The `as()` query declaration must be started '
|
||||||
|
'with `select(columns).from(table)');
|
||||||
|
}
|
||||||
|
|
||||||
|
final columnListLiteral =
|
||||||
|
target.argumentList.arguments[0] as ListLiteral;
|
||||||
|
final columnList =
|
||||||
|
columnListLiteral.elements.map((col) => col.toString()).map((col) {
|
||||||
|
final parts = col.split('.');
|
||||||
|
if (parts.length > 1) {
|
||||||
|
final reference =
|
||||||
|
references.firstWhereOrNull((ref) => ref.name == parts[0]);
|
||||||
|
if (reference == null) {
|
||||||
|
throw analysisError(
|
||||||
|
base.step,
|
||||||
|
element,
|
||||||
|
'Table named `${parts[0]}` not found! Maybe not included in '
|
||||||
|
'@DriftDatabase or not belongs to this database');
|
||||||
|
}
|
||||||
|
final column = reference.table.columns
|
||||||
|
.firstWhere((col) => col.dartGetterName == parts[1]);
|
||||||
|
column.table = reference.table;
|
||||||
|
return MapEntry(
|
||||||
|
'${reference.name}.${column.dartGetterName}', column);
|
||||||
|
}
|
||||||
|
final column =
|
||||||
|
columns.firstWhere((col) => col.dartGetterName == parts[0]);
|
||||||
|
return MapEntry('${column.dartGetterName}', column);
|
||||||
|
});
|
||||||
|
final columnMap = Map.fromEntries(columnList);
|
||||||
|
|
||||||
|
target = target.parent as MethodInvocation;
|
||||||
|
if (target.methodName.toString() != 'from') {
|
||||||
|
throw analysisError(
|
||||||
|
base.step,
|
||||||
|
element,
|
||||||
|
'The `as()` query declaration must be started '
|
||||||
|
'with `select(columns).from(table)');
|
||||||
|
}
|
||||||
|
|
||||||
|
final from = target.argumentList.arguments[0].toString();
|
||||||
|
var query = '';
|
||||||
|
|
||||||
|
if (target.parent is MethodInvocation) {
|
||||||
|
target = target.parent as MethodInvocation;
|
||||||
|
query = target.toString().substring(target.target!.toString().length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ViewQueryInformation(columnMap, from, query);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
throw analysisError(
|
||||||
|
base.step, element, 'Failed to parse view `as()` query');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw analysisError(base.step, element, 'Missing `as()` query declaration');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TableReference {
|
||||||
|
MoorTable table;
|
||||||
|
String name;
|
||||||
|
String declaration;
|
||||||
|
|
||||||
|
_TableReference(this.table, this.name, this.declaration);
|
||||||
|
}
|
|
@ -18,6 +18,12 @@ class AnalyzeDartStep extends AnalyzingStep {
|
||||||
(entry.declaration as DartTableDeclaration).element: entry
|
(entry.declaration as DartTableDeclaration).element: entry
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final viewDartClasses = {
|
||||||
|
for (final entry in unsortedEntities)
|
||||||
|
if (entry.declaration is DartViewDeclaration)
|
||||||
|
(entry.declaration as DartViewDeclaration).element: entry
|
||||||
|
};
|
||||||
|
|
||||||
for (final declaredHere in accessor.declaredTables) {
|
for (final declaredHere in accessor.declaredTables) {
|
||||||
// See issue #447: The table added to an accessor might already be
|
// See issue #447: The table added to an accessor might already be
|
||||||
// included through a transitive moor file. In that case, we just ignore
|
// included through a transitive moor file. In that case, we just ignore
|
||||||
|
@ -39,6 +45,23 @@ class AnalyzeDartStep extends AnalyzingStep {
|
||||||
_resolveDartColumnReferences(tableDartClasses);
|
_resolveDartColumnReferences(tableDartClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final declaredHere in accessor.declaredViews) {
|
||||||
|
// See issue #447: The view added to an accessor might already be
|
||||||
|
// included through a transitive moor file. In that case, we just ignore
|
||||||
|
// it to avoid duplicates.
|
||||||
|
final declaration = declaredHere.declaration;
|
||||||
|
if (declaration is DartViewDeclaration &&
|
||||||
|
viewDartClasses.containsKey(declaration.element)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a Dart view that we already included - add it now
|
||||||
|
unsortedEntities.add(declaredHere);
|
||||||
|
if (declaration is DartViewDeclaration) {
|
||||||
|
viewDartClasses[declaration.element] = declaredHere;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<MoorSchemaEntity>? availableEntities;
|
List<MoorSchemaEntity>? availableEntities;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -8,6 +8,7 @@ part of '../steps.dart';
|
||||||
/// Notably, this step does not analyze defined queries.
|
/// Notably, this step does not analyze defined queries.
|
||||||
class ParseDartStep extends Step {
|
class ParseDartStep extends Step {
|
||||||
static const _tableTypeChecker = TypeChecker.fromRuntime(Table);
|
static const _tableTypeChecker = TypeChecker.fromRuntime(Table);
|
||||||
|
static const _viewTypeChecker = TypeChecker.fromRuntime(View);
|
||||||
static const _generatedInfoChecker = TypeChecker.fromRuntime(TableInfo);
|
static const _generatedInfoChecker = TypeChecker.fromRuntime(TableInfo);
|
||||||
static const _useMoorChecker = TypeChecker.fromRuntime(DriftDatabase);
|
static const _useMoorChecker = TypeChecker.fromRuntime(DriftDatabase);
|
||||||
static const _useDaoChecker = TypeChecker.fromRuntime(DriftAccessor);
|
static const _useDaoChecker = TypeChecker.fromRuntime(DriftAccessor);
|
||||||
|
@ -18,6 +19,7 @@ class ParseDartStep extends Step {
|
||||||
MoorDartParser get parser => _parser;
|
MoorDartParser get parser => _parser;
|
||||||
|
|
||||||
final Map<ClassElement, MoorTable> _tables = {};
|
final Map<ClassElement, MoorTable> _tables = {};
|
||||||
|
final Map<ClassElement, MoorView> _views = {};
|
||||||
|
|
||||||
ParseDartStep(Task task, FoundFile file, this.library) : super(task, file) {
|
ParseDartStep(Task task, FoundFile file, this.library) : super(task, file) {
|
||||||
_parser = MoorDartParser(this);
|
_parser = MoorDartParser(this);
|
||||||
|
@ -66,6 +68,18 @@ class ParseDartStep extends Step {
|
||||||
return _tables[element];
|
return _tables[element];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<MoorView?> _parseView(
|
||||||
|
ClassElement element, List<MoorTable> tables) async {
|
||||||
|
if (!_views.containsKey(element)) {
|
||||||
|
final view = await parser.parseView(element, tables);
|
||||||
|
|
||||||
|
if (view != null) {
|
||||||
|
_views[element] = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _views[element];
|
||||||
|
}
|
||||||
|
|
||||||
void _lintDartTable(MoorTable table, ClassElement from) {
|
void _lintDartTable(MoorTable table, ClassElement from) {
|
||||||
if (table.primaryKey != null) {
|
if (table.primaryKey != null) {
|
||||||
final hasAdditional = table.columns.any((c) {
|
final hasAdditional = table.columns.any((c) {
|
||||||
|
@ -101,8 +115,8 @@ class ParseDartStep extends Step {
|
||||||
|
|
||||||
/// Resolves a [MoorTable] for the class of each [DartType] in [types].
|
/// Resolves a [MoorTable] for the class of each [DartType] in [types].
|
||||||
/// The [initializedBy] element should be the piece of code that caused the
|
/// The [initializedBy] element should be the piece of code that caused the
|
||||||
/// parsing (e.g. the database class that is annotated with `@UseMoor`). This
|
/// parsing (e.g. the database class that is annotated with `@DriftDatabase`).
|
||||||
/// will allow for more descriptive error messages.
|
/// This will allow for more descriptive error messages.
|
||||||
Future<List<MoorTable>> parseTables(
|
Future<List<MoorTable>> parseTables(
|
||||||
Iterable<DartType> types, Element initializedBy) {
|
Iterable<DartType> types, Element initializedBy) {
|
||||||
return Future.wait(types.map((type) {
|
return Future.wait(types.map((type) {
|
||||||
|
@ -122,6 +136,29 @@ class ParseDartStep extends Step {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves a [MoorView] for the class of each [DartType] in [types].
|
||||||
|
/// The [initializedBy] element should be the piece of code that caused the
|
||||||
|
/// parsing (e.g. the database class that is annotated with `@DriftDatabase`).
|
||||||
|
/// This will allow for more descriptive error messages.
|
||||||
|
Future<List<MoorView>> parseViews(
|
||||||
|
Iterable<DartType> types, Element initializedBy, List<MoorTable> tables) {
|
||||||
|
return Future.wait(types.map((type) {
|
||||||
|
if (!_viewTypeChecker.isAssignableFrom(type.element!)) {
|
||||||
|
reportError(ErrorInDartCode(
|
||||||
|
severity: Severity.criticalError,
|
||||||
|
message: 'The type $type is not a drift view',
|
||||||
|
affectedElement: initializedBy,
|
||||||
|
));
|
||||||
|
return Future.value(null);
|
||||||
|
} else {
|
||||||
|
return _parseView(type.element as ClassElement, tables);
|
||||||
|
}
|
||||||
|
})).then((list) {
|
||||||
|
// only keep tables that were resolved successfully
|
||||||
|
return List.from(list.where((t) => t != null));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
List<DeclaredQuery> readDeclaredQueries(Map<DartObject, DartObject> obj) {
|
List<DeclaredQuery> readDeclaredQueries(Map<DartObject, DartObject> obj) {
|
||||||
return obj.entries.map((entry) {
|
return obj.entries.map((entry) {
|
||||||
final key = entry.key.toStringValue()!;
|
final key = entry.key.toStringValue()!;
|
||||||
|
|
|
@ -22,10 +22,13 @@ class ViewAnalyzer extends BaseAnalyzer {
|
||||||
Future<void> resolve(Iterable<MoorView> viewsToAnalyze) async {
|
Future<void> resolve(Iterable<MoorView> viewsToAnalyze) async {
|
||||||
// Going through the topologically sorted list and analyzing each view.
|
// Going through the topologically sorted list and analyzing each view.
|
||||||
for (final view in viewsToAnalyze) {
|
for (final view in viewsToAnalyze) {
|
||||||
final ctx = engine.analyzeNode(
|
if (view.declaration is! MoorViewDeclaration) continue;
|
||||||
view.declaration!.node, view.file!.parseResult.sql);
|
final viewDeclaration = view.declaration as MoorViewDeclaration;
|
||||||
|
|
||||||
|
final ctx =
|
||||||
|
engine.analyzeNode(viewDeclaration.node, view.file!.parseResult.sql);
|
||||||
lintContext(ctx, view.name);
|
lintContext(ctx, view.name);
|
||||||
final declaration = view.declaration!.creatingStatement;
|
final declaration = viewDeclaration.creatingStatement;
|
||||||
|
|
||||||
final parserView = view.parserView =
|
final parserView = view.parserView =
|
||||||
const SchemaFromCreateTable(moorExtensions: true)
|
const SchemaFromCreateTable(moorExtensions: true)
|
||||||
|
@ -76,7 +79,7 @@ class ViewAnalyzer extends BaseAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
engine.registerView(mapper.extractView(view));
|
engine.registerView(mapper.extractView(view));
|
||||||
view.references = findReferences(view.declaration!.node).toList();
|
view.references = findReferences(viewDeclaration.node).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,9 @@ class MoorColumn implements HasDeclaration, HasType {
|
||||||
|
|
||||||
bool get isGenerated => generatedAs != null;
|
bool get isGenerated => generatedAs != null;
|
||||||
|
|
||||||
|
/// Parent table
|
||||||
|
MoorTable? table;
|
||||||
|
|
||||||
/// The column type from the dsl library. For instance, if a table has
|
/// The column type from the dsl library. For instance, if a table has
|
||||||
/// declared an `IntColumn`, the matching dsl column name would also be an
|
/// declared an `IntColumn`, the matching dsl column name would also be an
|
||||||
/// `IntColumn`.
|
/// `IntColumn`.
|
||||||
|
|
|
@ -22,6 +22,13 @@ abstract class BaseMoorAccessor implements HasDeclaration {
|
||||||
/// that.
|
/// that.
|
||||||
final List<MoorTable> declaredTables;
|
final List<MoorTable> declaredTables;
|
||||||
|
|
||||||
|
/// All views that have been declared on this accessor directly.
|
||||||
|
///
|
||||||
|
/// This contains the `views` field from a `DriftDatabase` or `UseDao`
|
||||||
|
/// annotation, but not tables that are declared in imported moor files.
|
||||||
|
/// Use [views] for that.
|
||||||
|
final List<MoorView> declaredViews;
|
||||||
|
|
||||||
/// The `includes` field from the `UseMoor` or `UseDao` annotation.
|
/// The `includes` field from the `UseMoor` or `UseDao` annotation.
|
||||||
final List<String> declaredIncludes;
|
final List<String> declaredIncludes;
|
||||||
|
|
||||||
|
@ -48,7 +55,7 @@ abstract class BaseMoorAccessor implements HasDeclaration {
|
||||||
/// Resolved imports from this file.
|
/// Resolved imports from this file.
|
||||||
List<FoundFile>? imports = [];
|
List<FoundFile>? imports = [];
|
||||||
|
|
||||||
BaseMoorAccessor._(this.declaration, this.declaredTables,
|
BaseMoorAccessor._(this.declaration, this.declaredTables, this.declaredViews,
|
||||||
this.declaredIncludes, this.declaredQueries);
|
this.declaredIncludes, this.declaredQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,9 +67,11 @@ class Database extends BaseMoorAccessor {
|
||||||
this.daos = const [],
|
this.daos = const [],
|
||||||
DatabaseOrDaoDeclaration? declaration,
|
DatabaseOrDaoDeclaration? declaration,
|
||||||
List<MoorTable> declaredTables = const [],
|
List<MoorTable> declaredTables = const [],
|
||||||
|
List<MoorView> declaredViews = const [],
|
||||||
List<String> declaredIncludes = const [],
|
List<String> declaredIncludes = const [],
|
||||||
List<DeclaredQuery> declaredQueries = const [],
|
List<DeclaredQuery> declaredQueries = const [],
|
||||||
}) : super._(declaration, declaredTables, declaredIncludes, declaredQueries);
|
}) : super._(declaration, declaredTables, declaredViews, declaredIncludes,
|
||||||
|
declaredQueries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A dao, declared via an `UseDao` annotation on a Dart class.
|
/// A dao, declared via an `UseDao` annotation on a Dart class.
|
||||||
|
@ -74,7 +83,9 @@ class Dao extends BaseMoorAccessor {
|
||||||
required this.dbClass,
|
required this.dbClass,
|
||||||
DatabaseOrDaoDeclaration? declaration,
|
DatabaseOrDaoDeclaration? declaration,
|
||||||
required List<MoorTable> declaredTables,
|
required List<MoorTable> declaredTables,
|
||||||
|
List<MoorView> declaredViews = const [],
|
||||||
required List<String> declaredIncludes,
|
required List<String> declaredIncludes,
|
||||||
required List<DeclaredQuery> declaredQueries,
|
required List<DeclaredQuery> declaredQueries,
|
||||||
}) : super._(declaration, declaredTables, declaredIncludes, declaredQueries);
|
}) : super._(declaration, declaredTables, declaredViews, declaredIncludes,
|
||||||
|
declaredQueries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,23 @@ abstract class ViewDeclarationWithSql implements ViewDeclaration {
|
||||||
CreateViewStatement get creatingStatement;
|
CreateViewStatement get creatingStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DartViewDeclaration implements ViewDeclaration, DartDeclaration {
|
||||||
|
@override
|
||||||
|
final SourceRange declaration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ClassElement element;
|
||||||
|
|
||||||
|
DartViewDeclaration._(this.declaration, this.element);
|
||||||
|
|
||||||
|
factory DartViewDeclaration(ClassElement element, FoundFile file) {
|
||||||
|
return DartViewDeclaration._(
|
||||||
|
SourceRange.fromElementAndFile(element, file),
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MoorViewDeclaration
|
class MoorViewDeclaration
|
||||||
implements ViewDeclaration, MoorDeclaration, ViewDeclarationWithSql {
|
implements ViewDeclaration, MoorDeclaration, ViewDeclarationWithSql {
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'model.dart';
|
||||||
/// A parsed view
|
/// A parsed view
|
||||||
class MoorView extends MoorEntityWithResultSet {
|
class MoorView extends MoorEntityWithResultSet {
|
||||||
@override
|
@override
|
||||||
final MoorViewDeclaration? declaration;
|
final ViewDeclaration? declaration;
|
||||||
|
|
||||||
/// The associated view to use for the sqlparser package when analyzing
|
/// The associated view to use for the sqlparser package when analyzing
|
||||||
/// sql queries. Note that this field is set lazily.
|
/// sql queries. Note that this field is set lazily.
|
||||||
|
@ -38,12 +38,18 @@ class MoorView extends MoorEntityWithResultSet {
|
||||||
@override
|
@override
|
||||||
ExistingRowClass? existingRowClass;
|
ExistingRowClass? existingRowClass;
|
||||||
|
|
||||||
|
final List<String> staticReferences;
|
||||||
|
|
||||||
|
final ViewQueryInformation? viewQuery;
|
||||||
|
|
||||||
MoorView({
|
MoorView({
|
||||||
this.declaration,
|
this.declaration,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.dartTypeName,
|
required this.dartTypeName,
|
||||||
required this.entityInfoName,
|
required this.entityInfoName,
|
||||||
this.existingRowClass,
|
this.existingRowClass,
|
||||||
|
this.staticReferences = const [],
|
||||||
|
this.viewQuery,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -80,7 +86,7 @@ class MoorView extends MoorEntityWithResultSet {
|
||||||
|
|
||||||
/// The `CREATE VIEW` statement that can be used to create this view.
|
/// The `CREATE VIEW` statement that can be used to create this view.
|
||||||
String createSql(MoorOptions options) {
|
String createSql(MoorOptions options) {
|
||||||
final decl = declaration;
|
final decl = declaration as MoorViewDeclaration?;
|
||||||
if (decl == null) {
|
if (decl == null) {
|
||||||
throw StateError('Cannot show SQL for views without a declaration');
|
throw StateError('Cannot show SQL for views without a declaration');
|
||||||
}
|
}
|
||||||
|
@ -94,3 +100,11 @@ class MoorView extends MoorEntityWithResultSet {
|
||||||
@override
|
@override
|
||||||
String get displayName => name;
|
String get displayName => name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ViewQueryInformation {
|
||||||
|
final Map<String, MoorColumn> columns;
|
||||||
|
final String from;
|
||||||
|
final String query;
|
||||||
|
|
||||||
|
ViewQueryInformation(this.columns, this.from, this.query);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
|
import 'package:analyzer/exception/exception.dart';
|
||||||
|
import 'package:drift_dev/src/analyzer/errors.dart';
|
||||||
|
import 'package:drift_dev/src/analyzer/runner/steps.dart';
|
||||||
|
|
||||||
|
Exception analysisError(Step step, Element element, String message) {
|
||||||
|
final error = ErrorInDartCode(
|
||||||
|
message: message,
|
||||||
|
severity: Severity.criticalError,
|
||||||
|
affectedElement: element,
|
||||||
|
);
|
||||||
|
step.reportError(error);
|
||||||
|
return AnalysisException(error.toString());
|
||||||
|
}
|
|
@ -18,6 +18,12 @@ bool isColumn(DartType type) {
|
||||||
!name.contains('Builder');
|
!name.contains('Builder');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isExpression(DartType type) {
|
||||||
|
final name = type.element?.name ?? '';
|
||||||
|
|
||||||
|
return isFromMoor(type) && name.startsWith('Expression');
|
||||||
|
}
|
||||||
|
|
||||||
extension TypeUtils on DartType {
|
extension TypeUtils on DartType {
|
||||||
String get userVisibleName => getDisplayString(withNullability: true);
|
String get userVisibleName => getDisplayString(withNullability: true);
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class DatabaseWriter {
|
||||||
|
|
||||||
DatabaseWriter(this.db, this.scope);
|
DatabaseWriter(this.db, this.scope);
|
||||||
|
|
||||||
String get _dbClassName {
|
String get dbClassName {
|
||||||
if (scope.generationOptions.isGeneratingForSchema) {
|
if (scope.generationOptions.isGeneratingForSchema) {
|
||||||
return 'DatabaseAtV${scope.generationOptions.forSchema}';
|
return 'DatabaseAtV${scope.generationOptions.forSchema}';
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,13 @@ class DatabaseWriter {
|
||||||
TableWriter(table, scope.child()).writeInto();
|
TableWriter(table, scope.child()).writeInto();
|
||||||
}
|
}
|
||||||
for (final view in db.views) {
|
for (final view in db.views) {
|
||||||
ViewWriter(view, scope.child()).write();
|
ViewWriter(view, scope.child(), this).write();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the database class
|
// Write the database class
|
||||||
final dbScope = scope.child();
|
final dbScope = scope.child();
|
||||||
|
|
||||||
final className = _dbClassName;
|
final className = dbClassName;
|
||||||
final firstLeaf = dbScope.leaf();
|
final firstLeaf = dbScope.leaf();
|
||||||
final isAbstract = !scope.generationOptions.isGeneratingForSchema;
|
final isAbstract = !scope.generationOptions.isGeneratingForSchema;
|
||||||
if (isAbstract) {
|
if (isAbstract) {
|
||||||
|
@ -95,7 +95,7 @@ class DatabaseWriter {
|
||||||
buffer: dbScope.leaf(),
|
buffer: dbScope.leaf(),
|
||||||
getterName: entity.dbGetterName,
|
getterName: entity.dbGetterName,
|
||||||
returnType: entity.entityInfoName,
|
returnType: entity.entityInfoName,
|
||||||
code: '${entity.entityInfoName}()',
|
code: '${entity.entityInfoName}(this)',
|
||||||
options: scope.generationOptions,
|
options: scope.generationOptions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:drift_dev/writer.dart';
|
||||||
class DataClassWriter {
|
class DataClassWriter {
|
||||||
final MoorEntityWithResultSet table;
|
final MoorEntityWithResultSet table;
|
||||||
final Scope scope;
|
final Scope scope;
|
||||||
|
final columns = <MoorColumn>[];
|
||||||
|
|
||||||
bool get isInsertable => table is MoorTable;
|
bool get isInsertable => table is MoorTable;
|
||||||
|
|
||||||
|
@ -32,8 +33,16 @@ class DataClassWriter {
|
||||||
_buffer.writeln('{');
|
_buffer.writeln('{');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write view columns
|
||||||
|
final view = table;
|
||||||
|
if (view is MoorView && view.viewQuery != null) {
|
||||||
|
columns.addAll(view.viewQuery!.columns.values);
|
||||||
|
} else {
|
||||||
|
columns.addAll(table.columns);
|
||||||
|
}
|
||||||
|
|
||||||
// write individual fields
|
// write individual fields
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
if (column.documentationComment != null) {
|
if (column.documentationComment != null) {
|
||||||
_buffer.write('${column.documentationComment}\n');
|
_buffer.write('${column.documentationComment}\n');
|
||||||
}
|
}
|
||||||
|
@ -46,7 +55,7 @@ class DataClassWriter {
|
||||||
_buffer
|
_buffer
|
||||||
..write(table.dartTypeName)
|
..write(table.dartTypeName)
|
||||||
..write('({')
|
..write('({')
|
||||||
..write(table.columns.map((column) {
|
..write(columns.map((column) {
|
||||||
final nullableDartType = column.typeConverter != null &&
|
final nullableDartType = column.typeConverter != null &&
|
||||||
scope.options.nullAwareTypeConverters
|
scope.options.nullAwareTypeConverters
|
||||||
? column.typeConverter!.hasNullableDartType
|
? column.typeConverter!.hasNullableDartType
|
||||||
|
@ -80,8 +89,8 @@ class DataClassWriter {
|
||||||
_writeToString();
|
_writeToString();
|
||||||
_writeHashCode();
|
_writeHashCode();
|
||||||
|
|
||||||
overrideEquals(table.columns.map((c) => c.dartGetterName),
|
overrideEquals(
|
||||||
table.dartTypeName, _buffer);
|
columns.map((c) => c.dartGetterName), table.dartTypeName, _buffer);
|
||||||
|
|
||||||
// finish class declaration
|
// finish class declaration
|
||||||
_buffer.write('}');
|
_buffer.write('}');
|
||||||
|
@ -103,7 +112,7 @@ class DataClassWriter {
|
||||||
|
|
||||||
final writer = RowMappingWriter(
|
final writer = RowMappingWriter(
|
||||||
const [],
|
const [],
|
||||||
{for (final column in table.columns) column: column.dartGetterName},
|
{for (final column in columns) column: column.dartGetterName},
|
||||||
table,
|
table,
|
||||||
scope.generationOptions,
|
scope.generationOptions,
|
||||||
scope.options,
|
scope.options,
|
||||||
|
@ -124,7 +133,7 @@ class DataClassWriter {
|
||||||
..write('serializer ??= $_runtimeOptions.defaultSerializer;\n')
|
..write('serializer ??= $_runtimeOptions.defaultSerializer;\n')
|
||||||
..write('return $dataClassName(');
|
..write('return $dataClassName(');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final getter = column.dartGetterName;
|
final getter = column.dartGetterName;
|
||||||
final jsonKey = column.getJsonKey(scope.options);
|
final jsonKey = column.getJsonKey(scope.options);
|
||||||
final type = column.dartTypeCode(scope.generationOptions);
|
final type = column.dartTypeCode(scope.generationOptions);
|
||||||
|
@ -150,7 +159,7 @@ class DataClassWriter {
|
||||||
'serializer ??= $_runtimeOptions.defaultSerializer;\n'
|
'serializer ??= $_runtimeOptions.defaultSerializer;\n'
|
||||||
'return <String, dynamic>{\n');
|
'return <String, dynamic>{\n');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
final name = column.getJsonKey(scope.options);
|
final name = column.getJsonKey(scope.options);
|
||||||
final getter = column.dartGetterName;
|
final getter = column.dartGetterName;
|
||||||
final needsThis = getter == 'serializer';
|
final needsThis = getter == 'serializer';
|
||||||
|
@ -168,9 +177,9 @@ class DataClassWriter {
|
||||||
final wrapNullableInValue = scope.options.generateValuesInCopyWith;
|
final wrapNullableInValue = scope.options.generateValuesInCopyWith;
|
||||||
|
|
||||||
_buffer.write('$dataClassName copyWith({');
|
_buffer.write('$dataClassName copyWith({');
|
||||||
for (var i = 0; i < table.columns.length; i++) {
|
for (var i = 0; i < columns.length; i++) {
|
||||||
final column = table.columns[i];
|
final column = columns[i];
|
||||||
final last = i == table.columns.length - 1;
|
final last = i == columns.length - 1;
|
||||||
final isNullable = column.nullableInDart;
|
final isNullable = column.nullableInDart;
|
||||||
|
|
||||||
final typeName = column.dartTypeCode(scope.generationOptions);
|
final typeName = column.dartTypeCode(scope.generationOptions);
|
||||||
|
@ -194,7 +203,7 @@ class DataClassWriter {
|
||||||
|
|
||||||
_buffer.write('}) => $dataClassName(');
|
_buffer.write('}) => $dataClassName(');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
// We also have a method parameter called like the getter, so we can use
|
// We also have a method parameter called like the getter, so we can use
|
||||||
// field: field ?? this.field. If we wrapped the parameter in a `Value`,
|
// field: field ?? this.field. If we wrapped the parameter in a `Value`,
|
||||||
// we can use field.present ? field.value : this.field
|
// we can use field.present ? field.value : this.field
|
||||||
|
@ -217,7 +226,7 @@ class DataClassWriter {
|
||||||
'(bool nullToAbsent) {\n')
|
'(bool nullToAbsent) {\n')
|
||||||
..write('final map = <String, Expression> {};');
|
..write('final map = <String, Expression> {};');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
// Generated column - cannot be used for inserts or updates
|
// Generated column - cannot be used for inserts or updates
|
||||||
if (column.isGenerated) continue;
|
if (column.isGenerated) continue;
|
||||||
|
|
||||||
|
@ -275,7 +284,7 @@ class DataClassWriter {
|
||||||
..write(asTable.getNameForCompanionClass(scope.options))
|
..write(asTable.getNameForCompanionClass(scope.options))
|
||||||
..write('(');
|
..write('(');
|
||||||
|
|
||||||
for (final column in table.columns) {
|
for (final column in columns) {
|
||||||
// Generated columns are not parts of companions.
|
// Generated columns are not parts of companions.
|
||||||
if (column.isGenerated) continue;
|
if (column.isGenerated) continue;
|
||||||
|
|
||||||
|
@ -304,7 +313,7 @@ class DataClassWriter {
|
||||||
void _writeToString() {
|
void _writeToString() {
|
||||||
overrideToString(
|
overrideToString(
|
||||||
table.dartTypeName,
|
table.dartTypeName,
|
||||||
[for (final column in table.columns) column.dartGetterName],
|
[for (final column in columns) column.dartGetterName],
|
||||||
_buffer,
|
_buffer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -312,7 +321,7 @@ class DataClassWriter {
|
||||||
void _writeHashCode() {
|
void _writeHashCode() {
|
||||||
_buffer.write('@override\n int get hashCode => ');
|
_buffer.write('@override\n int get hashCode => ');
|
||||||
|
|
||||||
final fields = table.columns.map((c) => c.dartGetterName).toList();
|
final fields = columns.map((c) => c.dartGetterName).toList();
|
||||||
const HashCodeWriter().writeHashCode(fields, _buffer);
|
const HashCodeWriter().writeHashCode(fields, _buffer);
|
||||||
_buffer.write(';');
|
_buffer.write(';');
|
||||||
}
|
}
|
||||||
|
@ -332,7 +341,11 @@ class RowMappingWriter {
|
||||||
|
|
||||||
void writeArguments(StringBuffer buffer) {
|
void writeArguments(StringBuffer buffer) {
|
||||||
String readAndMap(MoorColumn column) {
|
String readAndMap(MoorColumn column) {
|
||||||
final rawData = "data['\${effectivePrefix}${column.name.name}']";
|
var columnName = column.name.name;
|
||||||
|
if (column.table != null && column.table != table) {
|
||||||
|
columnName = '${column.table!.sqlName}.${column.name.name}';
|
||||||
|
}
|
||||||
|
final rawData = "data['\${effectivePrefix}$columnName']";
|
||||||
final sqlType = 'const ${sqlTypes[column.type]}()';
|
final sqlType = 'const ${sqlTypes[column.type]}()';
|
||||||
var loadType = '$sqlType.mapFromDatabaseResponse($rawData)';
|
var loadType = '$sqlType.mapFromDatabaseResponse($rawData)';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:drift_dev/moor_generator.dart';
|
import 'package:drift_dev/moor_generator.dart';
|
||||||
import 'package:drift_dev/src/utils/string_escaper.dart';
|
import 'package:drift_dev/src/utils/string_escaper.dart';
|
||||||
|
|
||||||
|
import '../database_writer.dart';
|
||||||
import '../writer.dart';
|
import '../writer.dart';
|
||||||
import 'data_class_writer.dart';
|
import 'data_class_writer.dart';
|
||||||
import 'table_writer.dart';
|
import 'table_writer.dart';
|
||||||
|
@ -8,6 +9,7 @@ import 'table_writer.dart';
|
||||||
class ViewWriter extends TableOrViewWriter {
|
class ViewWriter extends TableOrViewWriter {
|
||||||
final MoorView view;
|
final MoorView view;
|
||||||
final Scope scope;
|
final Scope scope;
|
||||||
|
final DatabaseWriter databaseWriter;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late StringBuffer buffer;
|
late StringBuffer buffer;
|
||||||
|
@ -15,7 +17,7 @@ class ViewWriter extends TableOrViewWriter {
|
||||||
@override
|
@override
|
||||||
MoorView get tableOrView => view;
|
MoorView get tableOrView => view;
|
||||||
|
|
||||||
ViewWriter(this.view, this.scope);
|
ViewWriter(this.view, this.scope, this.databaseWriter);
|
||||||
|
|
||||||
void write() {
|
void write() {
|
||||||
if (scope.generationOptions.writeDataClasses &&
|
if (scope.generationOptions.writeDataClasses &&
|
||||||
|
@ -29,29 +31,79 @@ class ViewWriter extends TableOrViewWriter {
|
||||||
void _writeViewInfoClass() {
|
void _writeViewInfoClass() {
|
||||||
buffer = scope.leaf();
|
buffer = scope.leaf();
|
||||||
|
|
||||||
buffer.write('class ${view.entityInfoName} extends View');
|
buffer.write('class ${view.entityInfoName} extends ViewInfo');
|
||||||
if (scope.generationOptions.writeDataClasses) {
|
if (scope.generationOptions.writeDataClasses) {
|
||||||
buffer.write('<${view.entityInfoName}, '
|
buffer.write('<${view.entityInfoName}, '
|
||||||
'${view.dartTypeCode(scope.generationOptions)}>');
|
'${view.dartTypeCode(scope.generationOptions)}>');
|
||||||
} else {
|
} else {
|
||||||
buffer.write('<${view.entityInfoName}, Never>');
|
buffer.write('<${view.entityInfoName}, Never>');
|
||||||
}
|
}
|
||||||
|
buffer.write(' implements HasResultSet');
|
||||||
|
|
||||||
buffer
|
buffer
|
||||||
..write('{\n')
|
..write('{\n')
|
||||||
..write('${view.entityInfoName}(): super(')
|
// write the generated database reference that is set in the constructor
|
||||||
..write(asDartLiteral(view.name))
|
..write('final ${databaseWriter.dbClassName} _db;\n')
|
||||||
..write(',')
|
..write('final ${scope.nullableType('String')} _alias;\n')
|
||||||
..write(asDartLiteral(view.createSql(scope.options)))
|
..write('${view.entityInfoName}(this._db, [this._alias]);\n');
|
||||||
..write(');');
|
|
||||||
|
|
||||||
|
for (final ref in view.staticReferences) {
|
||||||
|
buffer.write('$ref\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view.viewQuery == null) {
|
||||||
writeGetColumnsOverride();
|
writeGetColumnsOverride();
|
||||||
|
} else {
|
||||||
|
final columns = view.viewQuery!.columns.keys.join(', ');
|
||||||
|
buffer.write('@override\nList<GeneratedColumn> get \$columns => '
|
||||||
|
'[$columns];\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer
|
||||||
|
..write('@override\nString get aliasedName => '
|
||||||
|
'_alias ?? entityName;\n')
|
||||||
|
..write('@override\n String get entityName=>'
|
||||||
|
' ${asDartLiteral(view.name)};\n');
|
||||||
|
|
||||||
|
if (view.declaration is MoorViewDeclaration) {
|
||||||
|
buffer.write('@override\n String get createViewStmt =>'
|
||||||
|
' ${asDartLiteral(view.createSql(scope.options))};\n');
|
||||||
|
} else {
|
||||||
|
buffer.write('@override\n String? get createViewStmt => null;\n');
|
||||||
|
}
|
||||||
|
|
||||||
writeAsDslTable();
|
writeAsDslTable();
|
||||||
writeMappingMethod(scope);
|
writeMappingMethod(scope);
|
||||||
|
|
||||||
for (final column in view.columns) {
|
for (final column in view.viewQuery?.columns.values ?? view.columns) {
|
||||||
writeColumnGetter(column, scope.generationOptions, false);
|
writeColumnGetter(column, scope.generationOptions, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_writeAliasGenerator();
|
||||||
|
_writeQuery();
|
||||||
|
|
||||||
buffer.writeln('}');
|
buffer.writeln('}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _writeAliasGenerator() {
|
||||||
|
final typeName = view.entityInfoName;
|
||||||
|
|
||||||
|
buffer
|
||||||
|
..write('@override\n')
|
||||||
|
..write('$typeName createAlias(String alias) {\n')
|
||||||
|
..write('return $typeName(_db, alias);')
|
||||||
|
..write('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _writeQuery() {
|
||||||
|
buffer.write('@override\nQuery? get query => ');
|
||||||
|
final query = view.viewQuery;
|
||||||
|
if (query != null) {
|
||||||
|
buffer.write('(_db.selectOnly(${query.from}, '
|
||||||
|
'includeJoinedTableColumns: false)..addColumns(\$columns))'
|
||||||
|
'${query.query};');
|
||||||
|
} else {
|
||||||
|
buffer.write('null;\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
||||||
io: ^1.0.3
|
io: ^1.0.3
|
||||||
|
|
||||||
# Moor-specific analysis and apis
|
# Moor-specific analysis and apis
|
||||||
drift: ^1.1.0-dev
|
drift: '>=1.1.0-dev <1.2.0'
|
||||||
sqlite3: '>=0.1.6 <2.0.0'
|
sqlite3: '>=0.1.6 <2.0.0'
|
||||||
sqlparser: ^0.18.0
|
sqlparser: ^0.18.0
|
||||||
|
|
||||||
|
|
|
@ -130,10 +130,12 @@ class $KeyValuesTable extends KeyValues
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$KeyValuesTable(this._db, [this._alias]);
|
$KeyValuesTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _keyMeta = const VerificationMeta('key');
|
final VerificationMeta _keyMeta = const VerificationMeta('key');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> key = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> key = GeneratedColumn<String?>(
|
||||||
'key', aliasedName, false,
|
'key', aliasedName, false,
|
||||||
type: const StringType(), requiredDuringInsert: true);
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _valueMeta = const VerificationMeta('value');
|
final VerificationMeta _valueMeta = const VerificationMeta('value');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> value = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> value = GeneratedColumn<String?>(
|
||||||
'value', aliasedName, false,
|
'value', aliasedName, false,
|
||||||
type: const StringType(), requiredDuringInsert: true);
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
|
|
|
@ -237,26 +237,31 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$UsersTable(this._db, [this._alias]);
|
$UsersTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
||||||
'name', aliasedName, false,
|
'name', aliasedName, false,
|
||||||
type: const StringType(), requiredDuringInsert: true);
|
type: const StringType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _birthDateMeta = const VerificationMeta('birthDate');
|
final VerificationMeta _birthDateMeta = const VerificationMeta('birthDate');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<DateTime?> birthDate = GeneratedColumn<DateTime?>(
|
late final GeneratedColumn<DateTime?> birthDate = GeneratedColumn<DateTime?>(
|
||||||
'birth_date', aliasedName, false,
|
'birth_date', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _profilePictureMeta =
|
final VerificationMeta _profilePictureMeta =
|
||||||
const VerificationMeta('profilePicture');
|
const VerificationMeta('profilePicture');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<Uint8List?> profilePicture =
|
late final GeneratedColumn<Uint8List?> profilePicture =
|
||||||
GeneratedColumn<Uint8List?>('profile_picture', aliasedName, true,
|
GeneratedColumn<Uint8List?>('profile_picture', aliasedName, true,
|
||||||
type: const BlobType(), requiredDuringInsert: false);
|
type: const BlobType(), requiredDuringInsert: false);
|
||||||
final VerificationMeta _preferencesMeta =
|
final VerificationMeta _preferencesMeta =
|
||||||
const VerificationMeta('preferences');
|
const VerificationMeta('preferences');
|
||||||
|
@override
|
||||||
late final GeneratedColumnWithTypeConverter<Preferences, String?>
|
late final GeneratedColumnWithTypeConverter<Preferences, String?>
|
||||||
preferences = GeneratedColumn<String?>('preferences', aliasedName, true,
|
preferences = GeneratedColumn<String?>('preferences', aliasedName, true,
|
||||||
type: const StringType(), requiredDuringInsert: false)
|
type: const StringType(), requiredDuringInsert: false)
|
||||||
|
@ -468,15 +473,18 @@ class $FriendshipsTable extends Friendships
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$FriendshipsTable(this._db, [this._alias]);
|
$FriendshipsTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _firstUserMeta = const VerificationMeta('firstUser');
|
final VerificationMeta _firstUserMeta = const VerificationMeta('firstUser');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> firstUser = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> firstUser = GeneratedColumn<int?>(
|
||||||
'first_user', aliasedName, false,
|
'first_user', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _secondUserMeta = const VerificationMeta('secondUser');
|
final VerificationMeta _secondUserMeta = const VerificationMeta('secondUser');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> secondUser = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> secondUser = GeneratedColumn<int?>(
|
||||||
'second_user', aliasedName, false,
|
'second_user', aliasedName, false,
|
||||||
type: const IntType(), requiredDuringInsert: true);
|
type: const IntType(), requiredDuringInsert: true);
|
||||||
final VerificationMeta _reallyGoodFriendsMeta =
|
final VerificationMeta _reallyGoodFriendsMeta =
|
||||||
const VerificationMeta('reallyGoodFriends');
|
const VerificationMeta('reallyGoodFriends');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<bool?> reallyGoodFriends = GeneratedColumn<bool?>(
|
late final GeneratedColumn<bool?> reallyGoodFriends = GeneratedColumn<bool?>(
|
||||||
'really_good_friends', aliasedName, false,
|
'really_good_friends', aliasedName, false,
|
||||||
type: const BoolType(),
|
type: const BoolType(),
|
||||||
|
|
|
@ -128,12 +128,14 @@ class $UsersTable extends Users with TableInfo<$UsersTable, User> {
|
||||||
final String? _alias;
|
final String? _alias;
|
||||||
$UsersTable(this._db, [this._alias]);
|
$UsersTable(this._db, [this._alias]);
|
||||||
final VerificationMeta _idMeta = const VerificationMeta('id');
|
final VerificationMeta _idMeta = const VerificationMeta('id');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
late final GeneratedColumn<int?> id = GeneratedColumn<int?>(
|
||||||
'id', aliasedName, false,
|
'id', aliasedName, false,
|
||||||
type: const IntType(),
|
type: const IntType(),
|
||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
defaultConstraints: 'PRIMARY KEY AUTOINCREMENT');
|
||||||
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
final VerificationMeta _nameMeta = const VerificationMeta('name');
|
||||||
|
@override
|
||||||
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
late final GeneratedColumn<String?> name = GeneratedColumn<String?>(
|
||||||
'name', aliasedName, false,
|
'name', aliasedName, false,
|
||||||
type: const StringType(),
|
type: const StringType(),
|
||||||
|
|
Loading…
Reference in New Issue