Fix null analysis for Dart views (#2031)

This commit is contained in:
Simon Binder 2022-09-08 22:54:43 +02:00
parent 16f586f444
commit 1681c83bea
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
11 changed files with 284 additions and 258 deletions

View File

@ -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 +

View File

@ -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',

View File

@ -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);

View File

@ -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.

View File

@ -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';

View File

@ -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 = [
for (final staticRef in staticReferences) staticRef.table,
];
view.columns = columns;
return view;
)
..columns = columns
..references = [
for (final staticRef in staticReferences) staticRef.table,
];
}
_DataClassInformation _readDataClassInformation(
@ -114,56 +118,89 @@ 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];
final typeName = dartType.nameIfInterfaceType!;
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';
// 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;
}
throw analysisError(base.step, field, errorMessage);
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);
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, getter, errorMessage);
}
final node =
await base.loadElementDeclaration(getter) as MethodDeclaration;
final expression = (node.body as ExpressionFunctionBody).expression;
columns.add(DriftColumn(
type: sqlType,
dartGetterName: getter.name,
name: ColumnName.implicitly(ReCase(getter.name).snakeCase),
nullable: true,
generatedAs: ColumnGeneratedAs(expression.toString(), false),
));
}
}
final node =
await base.loadElementDeclaration(field.getter!) as MethodDeclaration;
final expression = (node.body as ExpressionFunctionBody).expression;
return DriftColumn(
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();
return columns;
}
DriftSqlType? _dartTypeToColumnType(String name) {
@ -207,98 +244,124 @@ class ViewParser {
return null;
}
Future<ViewQueryInformation> _parseQuery(
ClassElement element,
List<TableReferenceInDartView> references,
List<DriftColumn> columns) async {
Future<_ParsedDartViewSelect> _parseSelectStructure(
ClassElement element,
List<TableReferenceInDartView> references,
) 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 body = (node as MethodDeclaration).body;
if (body is! ExpressionFunctionBody) {
throw analysisError(
final node = await base.loadElementDeclaration(as);
final body = (node as MethodDeclaration).body;
if (body is! ExpressionFunctionBody) {
throw analysisError(
base.step,
element,
'The `as()` query declaration must be an expression (=>). '
'Block function body `{ return x; }` not acceptable.',
);
}
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;
} else {
throw analysisError(
base.step,
element,
'The `as()` query declaration must be an expression (=>). '
'Block function body `{ return x; }` not acceptable.',
);
}
Expression? target = body.expression;
for (;;) {
if (target is MethodInvocation) {
if (target.target == null) break;
target = target.target;
} else if (target is CascadeExpression) {
target = target.target;
} else {
throw analysisError(
base.step,
element,
'The `as()` query declaration contains invalid expression type '
'${target.runtimeType}');
}
}
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);
}).toList();
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();
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');
'The `as()` query declaration contains invalid expression type '
'${target.runtimeType}');
}
}
throw analysisError(base.step, element, 'Missing `as()` query declaration');
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 columnExpressions =
columnListLiteral.elements.whereType<Expression>().toList();
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].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 _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);
}
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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) {

View File

@ -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 [],

View File

@ -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');
}
writeGetColumnsOverride();
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)) {
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);
}
for (final column in view.columns) {
writeColumnGetter(column, 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;');
}
}
}