diff --git a/drift/example/main.g.dart b/drift/example/main.g.dart index 09d983b1..7854df26 100644 --- a/drift/example/main.g.dart +++ b/drift/example/main.g.dart @@ -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 json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return TodoCategoryItemCountData( name: serializer.fromJson(json['name']), - itemCount: serializer.fromJson(json['itemCount']), + itemCount: serializer.fromJson(json['itemCount']), ); } factory TodoCategoryItemCountData.fromJsonString(String encodedJson, @@ -486,14 +485,15 @@ class TodoCategoryItemCountData extends DataClass { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'name': serializer.toJson(name), - 'itemCount': serializer.toJson(itemCount), + 'itemCount': serializer.toJson(itemCount), }; } - TodoCategoryItemCountData copyWith({String? name, int? itemCount}) => + TodoCategoryItemCountData copyWith( + {String? name, Value 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 get $columns => [todoCategories.name, itemCount]; + List 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 name = GeneratedColumn( 'name', aliasedName, false, - type: DriftSqlType.string); + type: DriftSqlType.string, + generatedAs: GeneratedAs(todoCategories.name, false)); late final GeneratedColumn itemCount = GeneratedColumn( - '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 json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return TodoItemWithCategoryNameViewData( id: serializer.fromJson(json['id']), - title: serializer.fromJson(json['title']), + title: serializer.fromJson(json['title']), ); } factory TodoItemWithCategoryNameViewData.fromJsonString(String encodedJson, @@ -590,14 +590,15 @@ class TodoItemWithCategoryNameViewData extends DataClass { serializer ??= driftRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'title': serializer.toJson(title), + 'title': serializer.toJson(title), }; } - TodoItemWithCategoryNameViewData copyWith({int? id, String? title}) => + TodoItemWithCategoryNameViewData copyWith( + {int? id, Value 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 get $columns => [todoItems.id, title]; + List 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 id = - GeneratedColumn('id', aliasedName, false, type: DriftSqlType.int); + late final GeneratedColumn id = GeneratedColumn( + 'id', aliasedName, false, + type: DriftSqlType.int, generatedAs: GeneratedAs(todoItems.id, false)); late final GeneratedColumn title = GeneratedColumn( - 'title', aliasedName, false, + 'title', aliasedName, true, type: DriftSqlType.string, generatedAs: GeneratedAs( todoItems.title + diff --git a/drift/test/database/statements/schema_test.dart b/drift/test/database/statements/schema_test.dart index b5143847..cfc8606f 100644 --- a/drift/test/database/statements/schema_test.dart +++ b/drift/test/database/statements/schema_test.dart @@ -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', diff --git a/drift/test/generated/todos.g.dart b/drift/test/generated/todos.g.dart index 05b88841..51f9d054 100644 --- a/drift/test/generated/todos.g.dart +++ b/drift/test/generated/todos.g.dart @@ -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 json, {ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return CategoryTodoCountViewData( - description: serializer.fromJson(json['description']), - itemCount: serializer.fromJson(json['itemCount']), + description: serializer.fromJson(json['description']), + itemCount: serializer.fromJson(json['itemCount']), ); } factory CategoryTodoCountViewData.fromJsonString(String encodedJson, @@ -1401,15 +1400,17 @@ class CategoryTodoCountViewData extends DataClass { Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'description': serializer.toJson(description), - 'itemCount': serializer.toJson(itemCount), + 'description': serializer.toJson(description), + 'itemCount': serializer.toJson(itemCount), }; } - CategoryTodoCountViewData copyWith({String? description, int? itemCount}) => + CategoryTodoCountViewData copyWith( + {Value description = const Value.absent(), + Value 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 description = GeneratedColumn( - 'description', aliasedName, false, + 'description', aliasedName, true, type: DriftSqlType.string, generatedAs: GeneratedAs(categories.description + const Variable('!'), false)); late final GeneratedColumn itemCount = GeneratedColumn( - '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 get $columns => [todos.title, categories.description]; + List get $columns => [title, description]; @override String get aliasedName => _alias ?? entityName; @override @@ -1569,11 +1570,12 @@ class $TodoWithCategoryViewView } late final GeneratedColumn title = GeneratedColumn( - 'title', aliasedName, false, - type: DriftSqlType.string); + 'title', aliasedName, true, + type: DriftSqlType.string, generatedAs: GeneratedAs(todos.title, false)); late final GeneratedColumn description = GeneratedColumn( 'desc', aliasedName, false, - type: DriftSqlType.string); + type: DriftSqlType.string, + generatedAs: GeneratedAs(categories.description, false)); @override $TodoWithCategoryViewView createAlias(String alias) { return $TodoWithCategoryViewView(attachedDatabase, alias); diff --git a/drift_dev/CHANGELOG.md b/drift_dev/CHANGELOG.md index d0a617fb..a4860184 100644 --- a/drift_dev/CHANGELOG.md +++ b/drift_dev/CHANGELOG.md @@ -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. diff --git a/drift_dev/lib/src/analyzer/dart/parser.dart b/drift_dev/lib/src/analyzer/dart/parser.dart index 48d55e45..fb6f125f 100644 --- a/drift_dev/lib/src/analyzer/dart/parser.dart +++ b/drift_dev/lib/src/analyzer/dart/parser.dart @@ -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'; diff --git a/drift_dev/lib/src/analyzer/dart/view_parser.dart b/drift_dev/lib/src/analyzer/dart/view_parser.dart index 2ea3560f..a31dc062 100644 --- a/drift_dev/lib/src/analyzer/dart/view_parser.dart +++ b/drift_dev/lib/src/analyzer/dart/view_parser.dart @@ -9,28 +9,32 @@ class ViewParser { Future parseView( ClassElement element, List 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> _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> _parseColumns( + ClassElement element, + _ParsedDartViewSelect structure, + List references, + ) async { + final columns = []; - 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 _parseQuery( - ClassElement element, - List references, - List columns) async { + Future<_ParsedDartViewSelect> _parseSelectStructure( + ClassElement element, + List 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 = []; + final outerJoins = []; + + // 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().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 innerJoins; + final List outerJoins; + + final List 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); } } diff --git a/drift_dev/lib/src/model/declarations/views.dart b/drift_dev/lib/src/model/declarations/views.dart index df509fae..6ae29f5e 100644 --- a/drift_dev/lib/src/model/declarations/views.dart +++ b/drift_dev/lib/src/model/declarations/views.dart @@ -17,18 +17,17 @@ class DartViewDeclaration implements ViewDeclaration, DartDeclaration { @override final ClassElement element; + final String dartQuerySource; + final TableReferenceInDartView? primaryFrom; final List staticReferences; - DartViewDeclaration._(this.declaration, this.element, this.staticReferences); - - factory DartViewDeclaration(ClassElement element, FoundFile file, - List 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 { diff --git a/drift_dev/lib/src/model/view.dart b/drift_dev/lib/src/model/view.dart index e268aceb..f9900063 100644 --- a/drift_dev/lib/src/model/view.dart +++ b/drift_dev/lib/src/model/view.dart @@ -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> columns; - final String from; - final String query; - - ViewQueryInformation(this.columns, this.from, this.query); -} diff --git a/drift_dev/lib/src/writer/tables/data_class_writer.dart b/drift_dev/lib/src/writer/tables/data_class_writer.dart index c0e40cfd..b4183229 100644 --- a/drift_dev/lib/src/writer/tables/data_class_writer.dart +++ b/drift_dev/lib/src/writer/tables/data_class_writer.dart @@ -6,7 +6,8 @@ import 'package:drift_dev/writer.dart'; class DataClassWriter { final DriftEntityWithResultSet table; final Scope scope; - final columns = []; + + List 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) { diff --git a/drift_dev/lib/src/writer/tables/table_writer.dart b/drift_dev/lib/src/writer/tables/table_writer.dart index 9e4863c7..db5550f6 100644 --- a/drift_dev/lib/src/writer/tables/table_writer.dart +++ b/drift_dev/lib/src/writer/tables/table_writer.dart @@ -189,14 +189,7 @@ abstract class TableOrViewWriter { writer.writeArguments(buffer); buffer.write(';\n'); } else { - List 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 [], diff --git a/drift_dev/lib/src/writer/tables/view_writer.dart b/drift_dev/lib/src/writer/tables/view_writer.dart index 6398aee8..c321786d 100644 --- a/drift_dev/lib/src/writer/tables/view_writer.dart +++ b/drift_dev/lib/src/writer/tables/view_writer.dart @@ -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 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;'); } } }