mirror of https://github.com/AMT-Cheif/drift.git
Fix null analysis for Dart views (#2031)
This commit is contained in:
parent
16f586f444
commit
1681c83bea
|
@ -465,15 +465,14 @@ class $TodoItemsTable extends TodoItems
|
|||
|
||||
class TodoCategoryItemCountData extends DataClass {
|
||||
final String name;
|
||||
final int itemCount;
|
||||
const TodoCategoryItemCountData(
|
||||
{required this.name, required this.itemCount});
|
||||
final int? itemCount;
|
||||
const TodoCategoryItemCountData({required this.name, this.itemCount});
|
||||
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']),
|
||||
itemCount: serializer.fromJson<int?>(json['itemCount']),
|
||||
);
|
||||
}
|
||||
factory TodoCategoryItemCountData.fromJsonString(String encodedJson,
|
||||
|
@ -486,14 +485,15 @@ class TodoCategoryItemCountData extends DataClass {
|
|||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'name': serializer.toJson<String>(name),
|
||||
'itemCount': serializer.toJson<int>(itemCount),
|
||||
'itemCount': serializer.toJson<int?>(itemCount),
|
||||
};
|
||||
}
|
||||
|
||||
TodoCategoryItemCountData copyWith({String? name, int? itemCount}) =>
|
||||
TodoCategoryItemCountData copyWith(
|
||||
{String? name, Value<int?> itemCount = const Value.absent()}) =>
|
||||
TodoCategoryItemCountData(
|
||||
name: name ?? this.name,
|
||||
itemCount: itemCount ?? this.itemCount,
|
||||
itemCount: itemCount.present ? itemCount.value : this.itemCount,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
|
@ -525,7 +525,7 @@ class $TodoCategoryItemCountView
|
|||
$TodoCategoriesTable get todoCategories =>
|
||||
attachedDatabase.todoCategories.createAlias('t1');
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todoCategories.name, itemCount];
|
||||
List<GeneratedColumn> get $columns => [name, itemCount];
|
||||
@override
|
||||
String get aliasedName => _alias ?? entityName;
|
||||
@override
|
||||
|
@ -542,15 +542,16 @@ class $TodoCategoryItemCountView
|
|||
name: attachedDatabase.options.types
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}name'])!,
|
||||
itemCount: attachedDatabase.options.types
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}item_count'])!,
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}item_count']),
|
||||
);
|
||||
}
|
||||
|
||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
||||
'name', aliasedName, false,
|
||||
type: DriftSqlType.string);
|
||||
type: DriftSqlType.string,
|
||||
generatedAs: GeneratedAs(todoCategories.name, false));
|
||||
late final GeneratedColumn<int> itemCount = GeneratedColumn<int>(
|
||||
'item_count', aliasedName, false,
|
||||
'item_count', aliasedName, true,
|
||||
type: DriftSqlType.int,
|
||||
generatedAs: GeneratedAs(todoItems.id.count(), false));
|
||||
@override
|
||||
|
@ -569,15 +570,14 @@ class $TodoCategoryItemCountView
|
|||
|
||||
class TodoItemWithCategoryNameViewData extends DataClass {
|
||||
final int id;
|
||||
final String title;
|
||||
const TodoItemWithCategoryNameViewData(
|
||||
{required this.id, required this.title});
|
||||
final String? title;
|
||||
const TodoItemWithCategoryNameViewData({required this.id, this.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']),
|
||||
title: serializer.fromJson<String?>(json['title']),
|
||||
);
|
||||
}
|
||||
factory TodoItemWithCategoryNameViewData.fromJsonString(String encodedJson,
|
||||
|
@ -590,14 +590,15 @@ class TodoItemWithCategoryNameViewData extends DataClass {
|
|||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<int>(id),
|
||||
'title': serializer.toJson<String>(title),
|
||||
'title': serializer.toJson<String?>(title),
|
||||
};
|
||||
}
|
||||
|
||||
TodoItemWithCategoryNameViewData copyWith({int? id, String? title}) =>
|
||||
TodoItemWithCategoryNameViewData copyWith(
|
||||
{int? id, Value<String?> title = const Value.absent()}) =>
|
||||
TodoItemWithCategoryNameViewData(
|
||||
id: id ?? this.id,
|
||||
title: title ?? this.title,
|
||||
title: title.present ? title.value : this.title,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
|
@ -629,7 +630,7 @@ class $TodoItemWithCategoryNameViewView extends ViewInfo<
|
|||
$TodoCategoriesTable get todoCategories =>
|
||||
attachedDatabase.todoCategories.createAlias('t1');
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todoItems.id, title];
|
||||
List<GeneratedColumn> get $columns => [id, title];
|
||||
@override
|
||||
String get aliasedName => _alias ?? entityName;
|
||||
@override
|
||||
|
@ -646,14 +647,15 @@ class $TodoItemWithCategoryNameViewView extends ViewInfo<
|
|||
id: attachedDatabase.options.types
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}id'])!,
|
||||
title: attachedDatabase.options.types
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}title'])!,
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}title']),
|
||||
);
|
||||
}
|
||||
|
||||
late final GeneratedColumn<int> id =
|
||||
GeneratedColumn<int>('id', aliasedName, false, type: DriftSqlType.int);
|
||||
late final GeneratedColumn<int> id = GeneratedColumn<int>(
|
||||
'id', aliasedName, false,
|
||||
type: DriftSqlType.int, generatedAs: GeneratedAs(todoItems.id, false));
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title', aliasedName, false,
|
||||
'title', aliasedName, true,
|
||||
type: DriftSqlType.string,
|
||||
generatedAs: GeneratedAs(
|
||||
todoItems.title +
|
||||
|
|
|
@ -83,8 +83,8 @@ void main() {
|
|||
verify(mockExecutor.runCustom(
|
||||
'CREATE VIEW IF NOT EXISTS todo_with_category_view '
|
||||
'(title, "desc") AS SELECT '
|
||||
't0.title AS "t0.title", '
|
||||
't1."desc" AS "t1.desc" '
|
||||
't0.title AS "title", '
|
||||
't1."desc" AS "desc" '
|
||||
'FROM todos t0 '
|
||||
'INNER JOIN categories t1 '
|
||||
'ON t1.id = t0.category',
|
||||
|
|
|
@ -1380,16 +1380,15 @@ class $PureDefaultsTable extends PureDefaults
|
|||
}
|
||||
|
||||
class CategoryTodoCountViewData extends DataClass {
|
||||
final String description;
|
||||
final int itemCount;
|
||||
const CategoryTodoCountViewData(
|
||||
{required this.description, required this.itemCount});
|
||||
final String? description;
|
||||
final int? itemCount;
|
||||
const CategoryTodoCountViewData({this.description, this.itemCount});
|
||||
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']),
|
||||
description: serializer.fromJson<String?>(json['description']),
|
||||
itemCount: serializer.fromJson<int?>(json['itemCount']),
|
||||
);
|
||||
}
|
||||
factory CategoryTodoCountViewData.fromJsonString(String encodedJson,
|
||||
|
@ -1401,15 +1400,17 @@ class CategoryTodoCountViewData extends DataClass {
|
|||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'description': serializer.toJson<String>(description),
|
||||
'itemCount': serializer.toJson<int>(itemCount),
|
||||
'description': serializer.toJson<String?>(description),
|
||||
'itemCount': serializer.toJson<int?>(itemCount),
|
||||
};
|
||||
}
|
||||
|
||||
CategoryTodoCountViewData copyWith({String? description, int? itemCount}) =>
|
||||
CategoryTodoCountViewData copyWith(
|
||||
{Value<String?> description = const Value.absent(),
|
||||
Value<int?> itemCount = const Value.absent()}) =>
|
||||
CategoryTodoCountViewData(
|
||||
description: description ?? this.description,
|
||||
itemCount: itemCount ?? this.itemCount,
|
||||
description: description.present ? description.value : this.description,
|
||||
itemCount: itemCount.present ? itemCount.value : this.itemCount,
|
||||
);
|
||||
@override
|
||||
String toString() {
|
||||
|
@ -1456,19 +1457,19 @@ class $CategoryTodoCountViewView
|
|||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return CategoryTodoCountViewData(
|
||||
description: attachedDatabase.options.types
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}description'])!,
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}description']),
|
||||
itemCount: attachedDatabase.options.types
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}item_count'])!,
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}item_count']),
|
||||
);
|
||||
}
|
||||
|
||||
late final GeneratedColumn<String> description = GeneratedColumn<String>(
|
||||
'description', aliasedName, false,
|
||||
'description', aliasedName, true,
|
||||
type: DriftSqlType.string,
|
||||
generatedAs:
|
||||
GeneratedAs(categories.description + const Variable('!'), false));
|
||||
late final GeneratedColumn<int> itemCount = GeneratedColumn<int>(
|
||||
'item_count', aliasedName, false,
|
||||
'item_count', aliasedName, true,
|
||||
type: DriftSqlType.int,
|
||||
generatedAs: GeneratedAs(todos.id.count(), false));
|
||||
@override
|
||||
|
@ -1547,7 +1548,7 @@ class $TodoWithCategoryViewView
|
|||
$CategoriesTable get categories =>
|
||||
attachedDatabase.categories.createAlias('t1');
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [todos.title, categories.description];
|
||||
List<GeneratedColumn> get $columns => [title, description];
|
||||
@override
|
||||
String get aliasedName => _alias ?? entityName;
|
||||
@override
|
||||
|
@ -1569,11 +1570,12 @@ class $TodoWithCategoryViewView
|
|||
}
|
||||
|
||||
late final GeneratedColumn<String> title = GeneratedColumn<String>(
|
||||
'title', aliasedName, false,
|
||||
type: DriftSqlType.string);
|
||||
'title', aliasedName, true,
|
||||
type: DriftSqlType.string, generatedAs: GeneratedAs(todos.title, false));
|
||||
late final GeneratedColumn<String> description = GeneratedColumn<String>(
|
||||
'desc', aliasedName, false,
|
||||
type: DriftSqlType.string);
|
||||
type: DriftSqlType.string,
|
||||
generatedAs: GeneratedAs(categories.description, false));
|
||||
@override
|
||||
$TodoWithCategoryViewView createAlias(String alias) {
|
||||
return $TodoWithCategoryViewView(attachedDatabase, alias);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
## 2.2.0-dev
|
||||
|
||||
- Fix the nullability of columns generated for Dart-defined views.
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Analysis support `fts5` tables with external content tables.
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'package:analyzer/dart/ast/ast.dart';
|
|||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/dart/constant/value.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:collection/collection.dart';
|
||||
import 'package:drift_dev/moor_generator.dart';
|
||||
|
|
|
@ -9,28 +9,32 @@ class ViewParser {
|
|||
Future<MoorView?> parseView(
|
||||
ClassElement element, List<DriftTable> tables) async {
|
||||
final name = await _parseViewName(element);
|
||||
final columns = (await _parseColumns(element)).toList();
|
||||
final staticReferences = await _parseStaticReferences(element, tables);
|
||||
final dataClassInfo = _readDataClassInformation(columns, element);
|
||||
final query = await _parseQuery(element, staticReferences, columns);
|
||||
|
||||
final view = MoorView(
|
||||
declaration:
|
||||
DartViewDeclaration(element, base.step.file, staticReferences),
|
||||
final staticReferences = await _parseStaticReferences(element, tables);
|
||||
final structure = await _parseSelectStructure(element, staticReferences);
|
||||
final columns =
|
||||
(await _parseColumns(element, structure, staticReferences)).toList();
|
||||
|
||||
final dataClassInfo = _readDataClassInformation(columns, element);
|
||||
|
||||
return MoorView(
|
||||
declaration: DartViewDeclaration(
|
||||
element,
|
||||
base.step.file,
|
||||
structure.primarySource,
|
||||
staticReferences,
|
||||
structure.dartQuerySource,
|
||||
),
|
||||
name: name,
|
||||
dartTypeName: dataClassInfo.enforcedName,
|
||||
existingRowClass: dataClassInfo.existingClass,
|
||||
customParentClass: dataClassInfo.extending,
|
||||
entityInfoName: '\$${element.name}View',
|
||||
viewQuery: query,
|
||||
);
|
||||
|
||||
view.references = [
|
||||
)
|
||||
..columns = columns
|
||||
..references = [
|
||||
for (final staticRef in staticReferences) staticRef.table,
|
||||
];
|
||||
|
||||
view.columns = columns;
|
||||
return view;
|
||||
}
|
||||
|
||||
_DataClassInformation _readDataClassInformation(
|
||||
|
@ -114,26 +118,58 @@ class ViewParser {
|
|||
return ReCase(element.name).snakeCase;
|
||||
}
|
||||
|
||||
Future<Iterable<DriftColumn>> _parseColumns(ClassElement element) async {
|
||||
final columnNames = element.allSupertypes
|
||||
.map((t) => t.element2)
|
||||
.followedBy([element])
|
||||
.expand((e) => e.fields)
|
||||
.where((field) =>
|
||||
(isExpression(field.type) || isColumn(field.type)) &&
|
||||
field.getter != null &&
|
||||
!field.getter!.isSynthetic)
|
||||
.map((field) => field.name)
|
||||
.toSet();
|
||||
Future<List<DriftColumn>> _parseColumns(
|
||||
ClassElement element,
|
||||
_ParsedDartViewSelect structure,
|
||||
List<TableReferenceInDartView> references,
|
||||
) async {
|
||||
final columns = <DriftColumn>[];
|
||||
|
||||
final fields = columnNames.map((name) {
|
||||
final getter = element.getGetter(name) ??
|
||||
element.lookUpInheritedConcreteGetter(name, element.library);
|
||||
return getter!.variable;
|
||||
}).toList();
|
||||
for (final columnReference in structure.selectedColumns) {
|
||||
final parts = columnReference.toSource().split('.');
|
||||
|
||||
final results = await Future.wait(fields.map((field) async {
|
||||
final dartType = (field.type as InterfaceType).typeArguments[0];
|
||||
// Column reference like `foo.bar`, where `foo` is a table that has been
|
||||
// referenced in this view.
|
||||
if (parts.length > 1) {
|
||||
final reference =
|
||||
references.firstWhereOrNull((ref) => ref.name == parts[0]);
|
||||
if (reference == null) {
|
||||
base.step.reportError(ErrorInDartCode(
|
||||
message: 'Table named `${parts[0]}` not found! Maybe not '
|
||||
'included in @DriftDatabase or not belongs to this database',
|
||||
affectedElement: element,
|
||||
affectedNode: columnReference,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
final column = reference.table.columns
|
||||
.firstWhere((col) => col.dartGetterName == parts[1]);
|
||||
column.table = reference.table;
|
||||
|
||||
columns.add(DriftColumn(
|
||||
type: column.type,
|
||||
nullable: column.nullable || structure.referenceIsNullable(reference),
|
||||
dartGetterName: column.dartGetterName,
|
||||
name: column.name,
|
||||
generatedAs: ColumnGeneratedAs(
|
||||
'${reference.name}.${column.dartGetterName}', false),
|
||||
typeConverter: column.typeConverter,
|
||||
));
|
||||
} else {
|
||||
// Locally-defined column, defined as a getter on this view class.
|
||||
final getter = element.thisType.getGetter(parts[0]);
|
||||
|
||||
if (getter == null) {
|
||||
base.step.reportError(ErrorInDartCode(
|
||||
message: 'This column could not be found in the local view.',
|
||||
affectedElement: element,
|
||||
affectedNode: columnReference,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
final dartType = (getter.returnType as InterfaceType).typeArguments[0];
|
||||
final typeName = dartType.nameIfInterfaceType!;
|
||||
final sqlType = _dartTypeToColumnType(typeName);
|
||||
|
||||
|
@ -147,23 +183,24 @@ class ViewParser {
|
|||
'Must be one of: '
|
||||
'bool, String, int, DateTime, Uint8List, double';
|
||||
}
|
||||
throw analysisError(base.step, field, errorMessage);
|
||||
throw analysisError(base.step, getter, errorMessage);
|
||||
}
|
||||
|
||||
final node =
|
||||
await base.loadElementDeclaration(field.getter!) as MethodDeclaration;
|
||||
await base.loadElementDeclaration(getter) as MethodDeclaration;
|
||||
final expression = (node.body as ExpressionFunctionBody).expression;
|
||||
|
||||
return DriftColumn(
|
||||
columns.add(DriftColumn(
|
||||
type: sqlType,
|
||||
dartGetterName: field.name,
|
||||
name: ColumnName.implicitly(ReCase(field.name).snakeCase),
|
||||
nullable: dartType.nullabilitySuffix == NullabilitySuffix.question,
|
||||
dartGetterName: getter.name,
|
||||
name: ColumnName.implicitly(ReCase(getter.name).snakeCase),
|
||||
nullable: true,
|
||||
generatedAs: ColumnGeneratedAs(expression.toString(), false),
|
||||
);
|
||||
}).toList());
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return results.whereType();
|
||||
return columns;
|
||||
}
|
||||
|
||||
DriftSqlType? _dartTypeToColumnType(String name) {
|
||||
|
@ -207,17 +244,19 @@ class ViewParser {
|
|||
return null;
|
||||
}
|
||||
|
||||
Future<ViewQueryInformation> _parseQuery(
|
||||
Future<_ParsedDartViewSelect> _parseSelectStructure(
|
||||
ClassElement element,
|
||||
List<TableReferenceInDartView> references,
|
||||
List<DriftColumn> columns) async {
|
||||
) async {
|
||||
final as =
|
||||
element.methods.where((method) => method.name == 'as').firstOrNull;
|
||||
|
||||
if (as != null) {
|
||||
try {
|
||||
final node = await base.loadElementDeclaration(as);
|
||||
if (as == null) {
|
||||
throw analysisError(
|
||||
base.step, element, 'Missing `as()` query declaration');
|
||||
}
|
||||
|
||||
final node = await base.loadElementDeclaration(as);
|
||||
final body = (node as MethodDeclaration).body;
|
||||
if (body is! ExpressionFunctionBody) {
|
||||
throw analysisError(
|
||||
|
@ -228,10 +267,34 @@ class ViewParser {
|
|||
);
|
||||
}
|
||||
|
||||
final innerJoins = <TableReferenceInDartView>[];
|
||||
final outerJoins = <TableReferenceInDartView>[];
|
||||
|
||||
// We have something like Query as() => select([...]).from(foo).join(...).
|
||||
// First, crawl up so get the `select`:
|
||||
Expression? target = body.expression;
|
||||
for (;;) {
|
||||
if (target is MethodInvocation) {
|
||||
if (target.target == null) break;
|
||||
|
||||
final name = target.methodName.toSource();
|
||||
if (name == 'join') {
|
||||
final joinList = target.argumentList.arguments[0] as ListLiteral;
|
||||
for (final entry in joinList.elements) {
|
||||
// Do we have something like innerJoin(foo, bar)?
|
||||
if (entry is MethodInvocation) {
|
||||
final isInnerJoin = entry.methodName.toSource() == 'innerJoin';
|
||||
final table = references.firstWhereOrNull((element) =>
|
||||
element.name == entry.argumentList.arguments[0].toSource());
|
||||
|
||||
if (table != null) {
|
||||
final list = isInnerJoin ? innerJoins : outerJoins;
|
||||
list.add(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target = target.target;
|
||||
} else if (target is CascadeExpression) {
|
||||
target = target.target;
|
||||
|
@ -252,31 +315,9 @@ class ViewParser {
|
|||
'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);
|
||||
}).toList();
|
||||
final columnListLiteral = target.argumentList.arguments[0] as ListLiteral;
|
||||
final columnExpressions =
|
||||
columnListLiteral.elements.whereType<Expression>().toList();
|
||||
|
||||
target = target.parent as MethodInvocation;
|
||||
if (target.methodName.toString() != 'from') {
|
||||
|
@ -287,18 +328,40 @@ class ViewParser {
|
|||
'with `select(columns).from(table)');
|
||||
}
|
||||
|
||||
final from = target.argumentList.arguments[0].toString();
|
||||
final from = target.argumentList.arguments[0].toSource();
|
||||
final resolvedFrom =
|
||||
references.firstWhereOrNull((element) => element.name == from);
|
||||
if (resolvedFrom == null) {
|
||||
base.step.reportError(
|
||||
ErrorInDartCode(
|
||||
message: 'Table reference `$from` not found, is it added to this '
|
||||
'view as a getter?',
|
||||
affectedElement: as,
|
||||
affectedNode: target.argumentList,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final query =
|
||||
body.expression.toString().substring(target.toString().length);
|
||||
|
||||
return ViewQueryInformation(columnList, 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');
|
||||
return _ParsedDartViewSelect(
|
||||
resolvedFrom, innerJoins, outerJoins, columnExpressions, query);
|
||||
}
|
||||
}
|
||||
|
||||
class _ParsedDartViewSelect {
|
||||
final TableReferenceInDartView? primarySource;
|
||||
final List<TableReferenceInDartView> innerJoins;
|
||||
final List<TableReferenceInDartView> outerJoins;
|
||||
|
||||
final List<Expression> selectedColumns;
|
||||
final String dartQuerySource;
|
||||
|
||||
_ParsedDartViewSelect(this.primarySource, this.innerJoins, this.outerJoins,
|
||||
this.selectedColumns, this.dartQuerySource);
|
||||
|
||||
bool referenceIsNullable(TableReferenceInDartView ref) {
|
||||
return ref != primarySource && !innerJoins.contains(ref);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,18 +17,17 @@ class DartViewDeclaration implements ViewDeclaration, DartDeclaration {
|
|||
@override
|
||||
final ClassElement element;
|
||||
|
||||
final String dartQuerySource;
|
||||
final TableReferenceInDartView? primaryFrom;
|
||||
final List<TableReferenceInDartView> staticReferences;
|
||||
|
||||
DartViewDeclaration._(this.declaration, this.element, this.staticReferences);
|
||||
|
||||
factory DartViewDeclaration(ClassElement element, FoundFile file,
|
||||
List<TableReferenceInDartView> staticReferences) {
|
||||
return DartViewDeclaration._(
|
||||
SourceRange.fromElementAndFile(element, file),
|
||||
element,
|
||||
staticReferences,
|
||||
);
|
||||
}
|
||||
DartViewDeclaration(
|
||||
this.element,
|
||||
FoundFile file,
|
||||
this.primaryFrom,
|
||||
this.staticReferences,
|
||||
this.dartQuerySource,
|
||||
) : declaration = SourceRange.fromElementAndFile(element, file);
|
||||
}
|
||||
|
||||
class TableReferenceInDartView {
|
||||
|
|
|
@ -42,8 +42,6 @@ class MoorView extends DriftEntityWithResultSet {
|
|||
@override
|
||||
final String? customParentClass;
|
||||
|
||||
final ViewQueryInformation? viewQuery;
|
||||
|
||||
MoorView({
|
||||
this.declaration,
|
||||
required this.name,
|
||||
|
@ -51,7 +49,6 @@ class MoorView extends DriftEntityWithResultSet {
|
|||
required this.entityInfoName,
|
||||
this.existingRowClass,
|
||||
this.customParentClass,
|
||||
this.viewQuery,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -102,13 +99,3 @@ class MoorView extends DriftEntityWithResultSet {
|
|||
@override
|
||||
String get displayName => name;
|
||||
}
|
||||
|
||||
class ViewQueryInformation {
|
||||
/// All columns from this Dart-defined view, in the order in which they were
|
||||
/// added to the `query` getter.
|
||||
final List<MapEntry<String, DriftColumn>> columns;
|
||||
final String from;
|
||||
final String query;
|
||||
|
||||
ViewQueryInformation(this.columns, this.from, this.query);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ import 'package:drift_dev/writer.dart';
|
|||
class DataClassWriter {
|
||||
final DriftEntityWithResultSet table;
|
||||
final Scope scope;
|
||||
final columns = <DriftColumn>[];
|
||||
|
||||
List<DriftColumn> get columns => table.columns;
|
||||
|
||||
bool get isInsertable => table is DriftTable;
|
||||
|
||||
|
@ -30,14 +31,6 @@ class DataClassWriter {
|
|||
_buffer.writeln('{');
|
||||
}
|
||||
|
||||
// write view columns
|
||||
final view = table;
|
||||
if (view is MoorView && view.viewQuery != null) {
|
||||
columns.addAll(view.viewQuery!.columns.map((e) => e.value));
|
||||
} else {
|
||||
columns.addAll(table.columns);
|
||||
}
|
||||
|
||||
// write individual fields
|
||||
for (final column in columns) {
|
||||
if (column.documentationComment != null) {
|
||||
|
|
|
@ -189,14 +189,7 @@ abstract class TableOrViewWriter {
|
|||
writer.writeArguments(buffer);
|
||||
buffer.write(';\n');
|
||||
} else {
|
||||
List<DriftColumn> columns;
|
||||
|
||||
final view = tableOrView;
|
||||
if (view is MoorView && view.viewQuery != null) {
|
||||
columns = view.viewQuery!.columns.map((e) => e.value).toList();
|
||||
} else {
|
||||
columns = tableOrView.columns;
|
||||
}
|
||||
final columns = tableOrView.columns;
|
||||
|
||||
final writer = RowMappingWriter(
|
||||
positional: const [],
|
||||
|
|
|
@ -63,13 +63,7 @@ class ViewWriter extends TableOrViewWriter {
|
|||
}
|
||||
}
|
||||
|
||||
if (view.viewQuery == null) {
|
||||
writeGetColumnsOverride();
|
||||
} else {
|
||||
final columns = view.viewQuery!.columns.map((e) => e.key).join(', ');
|
||||
buffer.write('@override\nList<GeneratedColumn> get \$columns => '
|
||||
'[$columns];\n');
|
||||
}
|
||||
|
||||
buffer
|
||||
..write('@override\nString get aliasedName => '
|
||||
|
@ -87,21 +81,8 @@ class ViewWriter extends TableOrViewWriter {
|
|||
writeAsDslTable();
|
||||
writeMappingMethod(scope);
|
||||
|
||||
final columns = view.viewQuery?.columns.map((e) => e.value) ?? view.columns;
|
||||
for (final column in columns) {
|
||||
if (view.columns.contains(column)) {
|
||||
for (final column in view.columns) {
|
||||
writeColumnGetter(column, scope.generationOptions, false);
|
||||
} else {
|
||||
// This column only exists as a getter so that it can be referenced in
|
||||
// Dart, but it wasn't defined by the user. Instead, the column is
|
||||
// implicitly generated from a entry in the `select()` query clause.
|
||||
// We can drop all information from it since only the name is relevant.
|
||||
final shortColumn = DriftColumn(
|
||||
type: column.type,
|
||||
dartGetterName: column.dartGetterName,
|
||||
name: column.name);
|
||||
writeColumnGetter(shortColumn, scope.generationOptions, false);
|
||||
}
|
||||
}
|
||||
|
||||
_writeAliasGenerator();
|
||||
|
@ -130,13 +111,16 @@ class ViewWriter extends TableOrViewWriter {
|
|||
|
||||
void _writeQuery() {
|
||||
buffer.write('@override\nQuery? get query => ');
|
||||
final query = view.viewQuery;
|
||||
if (query != null) {
|
||||
buffer.write('(attachedDatabase.selectOnly(${query.from})'
|
||||
'..addColumns(\$columns))'
|
||||
'${query.query};');
|
||||
|
||||
if (view.isDeclaredInDart) {
|
||||
final definition = view.declaration as DartViewDeclaration;
|
||||
|
||||
buffer
|
||||
..write('(attachedDatabase.selectOnly(${definition.primaryFrom?.name})'
|
||||
'..addColumns(\$columns))')
|
||||
..writeln('${definition.dartQuerySource};');
|
||||
} else {
|
||||
buffer.write('null;\n');
|
||||
buffer.writeln('null;');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue