mirror of https://github.com/AMT-Cheif/drift.git
Initial support for custom data classes for views
This commit is contained in:
parent
dd196df25b
commit
0775c093e3
|
@ -1,5 +1,4 @@
|
|||
//@dart=2.9
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/nullability_suffix.dart';
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
|
@ -17,6 +16,7 @@ import 'package:recase/recase.dart';
|
|||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import '../custom_row_class.dart';
|
||||
import 'find_dart_class.dart';
|
||||
|
||||
class CreateTableReader {
|
||||
/// The AST of this `CREATE TABLE` statement.
|
||||
|
@ -173,7 +173,7 @@ class CreateTableReader {
|
|||
final overriddenNames = moorTableInfo.overriddenDataClassName;
|
||||
|
||||
if (moorTableInfo.useExistingDartClass) {
|
||||
final clazz = await _findDartClass(overriddenNames);
|
||||
final clazz = await findDartClass(step, imports, overriddenNames);
|
||||
if (clazz == null) {
|
||||
step.reportError(ErrorInMoorFile(
|
||||
span: stmt.tableNameToken.span,
|
||||
|
@ -262,34 +262,11 @@ class CreateTableReader {
|
|||
}
|
||||
|
||||
Future<DartType> _readDartType(String typeIdentifier) async {
|
||||
final foundClass = await _findDartClass(typeIdentifier);
|
||||
final foundClass = await findDartClass(step, imports, typeIdentifier);
|
||||
|
||||
return foundClass?.instantiate(
|
||||
typeArguments: const [],
|
||||
nullabilitySuffix: NullabilitySuffix.none,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ClassElement> _findDartClass(String identifier) async {
|
||||
final dartImports = imports
|
||||
.map((import) => import.importedFile)
|
||||
.where((importUri) => importUri.endsWith('.dart'));
|
||||
|
||||
for (final import in dartImports) {
|
||||
final resolved = step.task.session.resolve(step.file, import);
|
||||
LibraryElement library;
|
||||
try {
|
||||
library = await step.task.backend.resolveDart(resolved.uri);
|
||||
} on NotALibraryException {
|
||||
continue;
|
||||
}
|
||||
|
||||
final foundElement = library.exportNamespace.get(identifier);
|
||||
if (foundElement is ClassElement) {
|
||||
return foundElement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// @dart=2.9
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:moor_generator/src/backends/backend.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
Future<ClassElement> findDartClass(
|
||||
Step step, List<ImportStatement> imports, String identifier) async {
|
||||
final dartImports = imports
|
||||
.map((import) => import.importedFile)
|
||||
.where((importUri) => importUri.endsWith('.dart'));
|
||||
|
||||
for (final import in dartImports) {
|
||||
final resolved = step.task.session.resolve(step.file, import);
|
||||
LibraryElement library;
|
||||
try {
|
||||
library = await step.task.backend.resolveDart(resolved.uri);
|
||||
} on NotALibraryException {
|
||||
continue;
|
||||
}
|
||||
|
||||
final foundElement = library.exportNamespace.get(identifier);
|
||||
if (foundElement is ClassElement) {
|
||||
return foundElement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
|
@ -34,6 +34,8 @@ class MoorParser {
|
|||
// the table will be resolved in the analysis step
|
||||
createdEntities.add(MoorTrigger.fromMoor(parsedStmt, step.file));
|
||||
} else if (parsedStmt is CreateViewStatement) {
|
||||
// The view's columns and other data will be analyzed later, in
|
||||
// ViewAnalyzer.
|
||||
createdEntities.add(MoorView.fromMoor(parsedStmt, step.file));
|
||||
} else if (parsedStmt is CreateIndexStatement) {
|
||||
createdEntities.add(MoorIndex.fromMoor(parsedStmt, step.file));
|
||||
|
|
|
@ -4,7 +4,7 @@ part of '../steps.dart';
|
|||
class AnalyzeMoorStep extends AnalyzingStep {
|
||||
AnalyzeMoorStep(Task task, FoundFile file) : super(task, file);
|
||||
|
||||
void analyze() {
|
||||
Future<void> analyze() async {
|
||||
if (file.currentResult == null) {
|
||||
// Error during parsing, ignore.
|
||||
return;
|
||||
|
@ -36,7 +36,9 @@ class AnalyzeMoorStep extends AnalyzingStep {
|
|||
|
||||
EntityHandler(this, parseResult, availableTables).handle();
|
||||
|
||||
ViewAnalyzer(this, availableTables, availableViews).resolve();
|
||||
await ViewAnalyzer(
|
||||
this, availableTables, availableViews, parseResult.imports)
|
||||
.resolve();
|
||||
|
||||
final parser =
|
||||
SqlAnalyzer(this, availableTables, availableViews, parseResult.queries)
|
||||
|
|
|
@ -221,7 +221,8 @@ class Task {
|
|||
step = AnalyzeDartStep(this, file)..analyze();
|
||||
break;
|
||||
case FileType.moor:
|
||||
step = AnalyzeMoorStep(this, file)..analyze();
|
||||
final analyzeMoor = step = AnalyzeMoorStep(this, file);
|
||||
await analyzeMoor.analyze();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//@dart=2.9
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
import 'package:moor_generator/src/analyzer/moor/find_dart_class.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/query_analyzer.dart';
|
||||
import 'package:moor_generator/src/model/table.dart';
|
||||
|
@ -7,25 +9,30 @@ import 'package:moor_generator/src/model/view.dart';
|
|||
import 'package:recase/recase.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import '../custom_row_class.dart';
|
||||
|
||||
class ViewAnalyzer extends BaseAnalyzer {
|
||||
final List<MoorView> viewsToAnalyze;
|
||||
final List<ImportStatement> imports;
|
||||
|
||||
ViewAnalyzer(Step step, List<MoorTable> tables, this.viewsToAnalyze)
|
||||
ViewAnalyzer(
|
||||
Step step, List<MoorTable> tables, this.viewsToAnalyze, this.imports)
|
||||
: // We're about to analyze views and add them to the engine, but don't
|
||||
// add the unfinished views right away
|
||||
super(tables, const [], step);
|
||||
|
||||
/// Resolves all the views in topological order.
|
||||
void resolve() {
|
||||
Future<void> resolve() async {
|
||||
// Going through the topologically sorted list and analyzing each view.
|
||||
for (final view in viewsToAnalyze) {
|
||||
final ctx =
|
||||
engine.analyzeNode(view.declaration.node, view.file.parseResult.sql);
|
||||
lintContext(ctx, view.name);
|
||||
final declaration = view.declaration.creatingStatement;
|
||||
|
||||
final parserView = view.parserView =
|
||||
const SchemaFromCreateTable(moorExtensions: true)
|
||||
.readView(ctx, view.declaration.creatingStatement);
|
||||
.readView(ctx, declaration);
|
||||
|
||||
final columns = [
|
||||
for (final column in parserView.resolvedColumns)
|
||||
|
@ -38,8 +45,31 @@ class ViewAnalyzer extends BaseAnalyzer {
|
|||
];
|
||||
view.columns = columns;
|
||||
|
||||
engine.registerView(mapper.extractView(view));
|
||||
final desiredNames = declaration.moorTableName;
|
||||
if (desiredNames != null) {
|
||||
final dataClassName = desiredNames.overriddenDataClassName;
|
||||
if (desiredNames.useExistingDartClass) {
|
||||
final clazz = await findDartClass(step, imports, dataClassName);
|
||||
if (clazz == null) {
|
||||
step.reportError(ErrorInMoorFile(
|
||||
span: declaration.viewNameToken.span,
|
||||
message: 'Existing Dart class $dataClassName was not found, are '
|
||||
'you missing an import?',
|
||||
));
|
||||
} else {
|
||||
final rowClass = view.existingRowClass =
|
||||
validateExistingClass(columns, clazz, '', step.errors);
|
||||
final newName = rowClass?.targetClass?.name;
|
||||
if (newName != null) {
|
||||
view.dartTypeName = rowClass?.targetClass?.name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view.dartTypeName = dataClassName;
|
||||
}
|
||||
}
|
||||
|
||||
engine.registerView(mapper.extractView(view));
|
||||
view.references = findReferences(view.declaration.node).toList();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
//@dart=2.9
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
|
||||
/// Some schema entity found.
|
||||
|
@ -37,8 +38,25 @@ abstract class MoorEntityWithResultSet extends MoorSchemaEntity {
|
|||
/// converters.
|
||||
String get entityInfoName;
|
||||
|
||||
/// The existing class designed to hold a row, if there is any.
|
||||
ExistingRowClass /*?*/ get existingRowClass;
|
||||
|
||||
/// The name of the Dart class storing the right column getters for this type.
|
||||
///
|
||||
/// This class is equal to, or a superclass of, [entityInfoName].
|
||||
String get dslName => entityInfoName;
|
||||
|
||||
/// Whether this table has an existing row class, meaning that moor doesn't
|
||||
/// have to generate one on its own.
|
||||
bool get hasExistingRowClass => existingRowClass != null;
|
||||
}
|
||||
|
||||
/// Information used by the generator to generate code for a custom data class
|
||||
/// written by users.
|
||||
class ExistingRowClass {
|
||||
final ClassElement targetClass;
|
||||
final ConstructorElement constructor;
|
||||
final Map<MoorColumn, ParameterElement> mapping;
|
||||
|
||||
ExistingRowClass(this.targetClass, this.constructor, this.mapping);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import 'declarations/declaration.dart';
|
|||
|
||||
/// A parsed table, declared in code by extending `Table` and referencing that
|
||||
/// table in `@UseMoor` or `@UseDao`.
|
||||
class MoorTable implements MoorEntityWithResultSet {
|
||||
class MoorTable extends MoorEntityWithResultSet {
|
||||
/// The [ClassElement] for the class that declares this table or null if
|
||||
/// the table was inferred from a `CREATE TABLE` statement.
|
||||
final ClassElement fromClass;
|
||||
|
@ -24,7 +24,7 @@ class MoorTable implements MoorEntityWithResultSet {
|
|||
/// sql queries. Note that this field is set lazily.
|
||||
Table parserTable;
|
||||
|
||||
/// The existing class designed to hold a row, if there is any.
|
||||
@override
|
||||
final ExistingRowClass /*?*/ existingRowClass;
|
||||
|
||||
/// If [fromClass] is null, another source to use when determining the name
|
||||
|
@ -35,10 +35,6 @@ class MoorTable implements MoorEntityWithResultSet {
|
|||
/// a Dart class.
|
||||
bool get isFromSql => _overriddenName != null;
|
||||
|
||||
/// Whether this table has an existing row class, meaning that moor doesn't
|
||||
/// have to generate one on its own.
|
||||
bool get hasExistingRowClass => existingRowClass != null;
|
||||
|
||||
String get _baseName => _overriddenName ?? fromClass.name;
|
||||
|
||||
@override
|
||||
|
@ -207,16 +203,6 @@ class WrittenMoorTable {
|
|||
WrittenMoorTable(this.table, this.kind);
|
||||
}
|
||||
|
||||
/// Information used by the generator to generate code for a custom data class
|
||||
/// written by users.
|
||||
class ExistingRowClass {
|
||||
final ClassElement targetClass;
|
||||
final ConstructorElement constructor;
|
||||
final Map<MoorColumn, ParameterElement> mapping;
|
||||
|
||||
ExistingRowClass(this.targetClass, this.constructor, this.mapping);
|
||||
}
|
||||
|
||||
String dbFieldName(String className) => ReCase(className).camelCase;
|
||||
|
||||
String tableInfoNameForTableClass(String className) => '\$${className}Table';
|
||||
|
|
|
@ -30,16 +30,20 @@ class MoorView extends MoorEntityWithResultSet {
|
|||
List<MoorColumn> columns;
|
||||
|
||||
@override
|
||||
final String dartTypeName;
|
||||
String dartTypeName;
|
||||
|
||||
@override
|
||||
final String entityInfoName;
|
||||
String entityInfoName;
|
||||
|
||||
@override
|
||||
ExistingRowClass /*?*/ existingRowClass;
|
||||
|
||||
MoorView({
|
||||
this.declaration,
|
||||
this.name,
|
||||
this.dartTypeName,
|
||||
this.entityInfoName,
|
||||
this.existingRowClass,
|
||||
});
|
||||
|
||||
/// Obtains all tables transitively referenced by the declaration of this
|
||||
|
|
|
@ -78,6 +78,71 @@ abstract class TableOrViewWriter {
|
|||
);
|
||||
}
|
||||
|
||||
void writeMappingMethod(Scope scope) {
|
||||
if (!scope.generationOptions.writeDataClasses) {
|
||||
final nullableString = scope.nullableType('String');
|
||||
buffer.writeln('''
|
||||
@override
|
||||
Never map(Map<String, dynamic> data, {$nullableString tablePrefix}) {
|
||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||
}
|
||||
''');
|
||||
return;
|
||||
}
|
||||
|
||||
final dataClassName = tableOrView.dartTypeName;
|
||||
|
||||
buffer.write('@override\n$dataClassName map(Map<String, dynamic> data, '
|
||||
'{${scope.nullableType('String')} tablePrefix}) {\n');
|
||||
|
||||
if (tableOrView.hasExistingRowClass) {
|
||||
buffer.write('final effectivePrefix = '
|
||||
"tablePrefix != null ? '\$tablePrefix.' : '';");
|
||||
|
||||
final info = tableOrView.existingRowClass;
|
||||
final positionalToIndex = <MoorColumn, int>{};
|
||||
final named = <MoorColumn, String>{};
|
||||
|
||||
final parameters = info.constructor.parameters;
|
||||
info.mapping.forEach((column, parameter) {
|
||||
if (parameter.isNamed) {
|
||||
named[column] = parameter.name;
|
||||
} else {
|
||||
positionalToIndex[column] = parameters.indexOf(parameter);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort positional columns by the position of their respective parameter
|
||||
// in the constructor.
|
||||
final positional = positionalToIndex.keys.toList()
|
||||
..sort((a, b) => positionalToIndex[a].compareTo(positionalToIndex[b]));
|
||||
|
||||
final writer = RowMappingWriter(
|
||||
positional,
|
||||
named,
|
||||
tableOrView,
|
||||
scope.generationOptions,
|
||||
dbName: '_db',
|
||||
);
|
||||
|
||||
final classElement = info.targetClass;
|
||||
final ctor = info.constructor;
|
||||
buffer..write('return ')..write(classElement.name);
|
||||
if (ctor.name != null && ctor.name.isNotEmpty) {
|
||||
buffer..write('.')..write(ctor.name);
|
||||
}
|
||||
|
||||
writer.writeArguments(buffer);
|
||||
buffer.write(';\n');
|
||||
} else {
|
||||
// Use default .fromData constructor in the moor-generated data class
|
||||
buffer.write('return $dataClassName.fromData(data, _db, '
|
||||
"prefix: tablePrefix != null ? '\$tablePrefix.' : null);\n");
|
||||
}
|
||||
|
||||
buffer.write('}\n');
|
||||
}
|
||||
|
||||
void writeGetColumnsOverride() {
|
||||
final columnsWithGetters =
|
||||
tableOrView.columns.map((c) => c.dartGetterName).join(', ');
|
||||
|
@ -174,7 +239,7 @@ class TableWriter extends TableOrViewWriter {
|
|||
_writeValidityCheckMethod();
|
||||
_writePrimaryKeyOverride();
|
||||
|
||||
_writeMappingMethod();
|
||||
writeMappingMethod(scope);
|
||||
// _writeReverseMappingMethod();
|
||||
|
||||
_writeAliasGenerator();
|
||||
|
@ -194,71 +259,6 @@ class TableWriter extends TableOrViewWriter {
|
|||
}
|
||||
}
|
||||
|
||||
void _writeMappingMethod() {
|
||||
if (!scope.generationOptions.writeDataClasses) {
|
||||
final nullableString = scope.nullableType('String');
|
||||
buffer.writeln('''
|
||||
@override
|
||||
Never map(Map<String, dynamic> data, {$nullableString tablePrefix}) {
|
||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||
}
|
||||
''');
|
||||
return;
|
||||
}
|
||||
|
||||
final dataClassName = table.dartTypeName;
|
||||
|
||||
buffer.write('@override\n$dataClassName map(Map<String, dynamic> data, '
|
||||
'{${scope.nullableType('String')} tablePrefix}) {\n');
|
||||
|
||||
if (table.hasExistingRowClass) {
|
||||
buffer.write('final effectivePrefix = '
|
||||
"tablePrefix != null ? '\$tablePrefix.' : '';");
|
||||
|
||||
final info = table.existingRowClass;
|
||||
final positionalToIndex = <MoorColumn, int>{};
|
||||
final named = <MoorColumn, String>{};
|
||||
|
||||
final parameters = info.constructor.parameters;
|
||||
info.mapping.forEach((column, parameter) {
|
||||
if (parameter.isNamed) {
|
||||
named[column] = parameter.name;
|
||||
} else {
|
||||
positionalToIndex[column] = parameters.indexOf(parameter);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort positional columns by the position of their respective parameter
|
||||
// in the constructor.
|
||||
final positional = positionalToIndex.keys.toList()
|
||||
..sort((a, b) => positionalToIndex[a].compareTo(positionalToIndex[b]));
|
||||
|
||||
final writer = RowMappingWriter(
|
||||
positional,
|
||||
named,
|
||||
table,
|
||||
scope.generationOptions,
|
||||
dbName: '_db',
|
||||
);
|
||||
|
||||
final classElement = info.targetClass;
|
||||
final ctor = info.constructor;
|
||||
buffer..write('return ')..write(classElement.name);
|
||||
if (ctor.name != null && ctor.name.isNotEmpty) {
|
||||
buffer..write('.')..write(ctor.name);
|
||||
}
|
||||
|
||||
writer.writeArguments(buffer);
|
||||
buffer.write(';\n');
|
||||
} else {
|
||||
// Use default .fromData constructor in the moor-generated data class
|
||||
buffer.write('return $dataClassName.fromData(data, _db, '
|
||||
"prefix: tablePrefix != null ? '\$tablePrefix.' : null);\n");
|
||||
}
|
||||
|
||||
buffer.write('}\n');
|
||||
}
|
||||
|
||||
void _writeColumnVerificationMeta(MoorColumn column) {
|
||||
if (!_skipVerification) {
|
||||
buffer
|
||||
|
|
|
@ -19,7 +19,8 @@ class ViewWriter extends TableOrViewWriter {
|
|||
ViewWriter(this.view, this.scope);
|
||||
|
||||
void write() {
|
||||
if (scope.generationOptions.writeDataClasses) {
|
||||
if (scope.generationOptions.writeDataClasses &&
|
||||
tableOrView.hasExistingRowClass) {
|
||||
DataClassWriter(view, scope).write();
|
||||
}
|
||||
|
||||
|
@ -45,7 +46,7 @@ class ViewWriter extends TableOrViewWriter {
|
|||
|
||||
writeGetColumnsOverride();
|
||||
writeAsDslTable();
|
||||
_writeMappingMethod();
|
||||
writeMappingMethod(scope);
|
||||
|
||||
for (final column in view.columns) {
|
||||
writeColumnGetter(column, scope.generationOptions, false);
|
||||
|
@ -53,30 +54,4 @@ class ViewWriter extends TableOrViewWriter {
|
|||
|
||||
buffer.writeln('}');
|
||||
}
|
||||
|
||||
// After we support custom row classes for views, we can move this into the
|
||||
// shared writer
|
||||
void _writeMappingMethod() {
|
||||
if (!scope.generationOptions.writeDataClasses) {
|
||||
final nullableString = scope.nullableType('String');
|
||||
buffer.writeln('''
|
||||
@override
|
||||
Never map(Map<String, dynamic> data, {$nullableString tablePrefix}) {
|
||||
throw UnsupportedError('TableInfo.map in schema verification code');
|
||||
}
|
||||
''');
|
||||
return;
|
||||
}
|
||||
|
||||
final dataClassName = view.dartTypeName;
|
||||
|
||||
buffer.write('@override\n$dataClassName map(Map<String, dynamic> data, '
|
||||
'{${scope.nullableType('String')} tablePrefix}) {\n');
|
||||
|
||||
// Use default .fromData constructor in the moor-generated data class
|
||||
buffer.write('return $dataClassName.fromData(data, '
|
||||
"prefix: tablePrefix != null ? '\$tablePrefix.' : null);\n");
|
||||
|
||||
buffer.write('}\n');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// @dart=2.9
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:test/scaffolding.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('can use existing row classes in moor files', () async {
|
||||
final state = TestState.withContent({
|
||||
'a|lib/db.moor': '''
|
||||
import 'rows.dart';
|
||||
|
||||
CREATE TABLE custom_name (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
foo TEXT
|
||||
) AS MyCustomClass;
|
||||
|
||||
CREATE VIEW custom_name_view AS CustomViewClass (foo, bar)
|
||||
AS SELECT 1, 2;
|
||||
|
||||
CREATE TABLE existing (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
foo TEXT
|
||||
) WITH ExistingRowClass;
|
||||
|
||||
CREATE VIEW existing_view WITH ExistingForView (foo, bar)
|
||||
AS SELECT 1, 2;
|
||||
''',
|
||||
'a|lib/rows.dart': '''
|
||||
class ExistingRowClass {
|
||||
ExistingRowClass(int id, String? foo);
|
||||
}
|
||||
|
||||
class ExistingForView {
|
||||
ExistingForView(int foo, int bar);
|
||||
}
|
||||
''',
|
||||
});
|
||||
addTearDown(state.close);
|
||||
|
||||
final file = await state.analyze('package:a/db.moor');
|
||||
expect(file.errors.errors, isEmpty);
|
||||
|
||||
final result = file.currentResult as ParsedMoorFile;
|
||||
final customName = result.declaredEntities
|
||||
.singleWhere((e) => e.displayName == 'custom_name') as MoorTable;
|
||||
final existing = result.declaredEntities
|
||||
.singleWhere((e) => e.displayName == 'existing') as MoorTable;
|
||||
final customNameView = result.declaredEntities
|
||||
.singleWhere((e) => e.displayName == 'custom_name_view') as MoorView;
|
||||
final existingView = result.declaredEntities
|
||||
.singleWhere((e) => e.displayName == 'existing_view') as MoorView;
|
||||
|
||||
expect(customName.dartTypeName, 'MyCustomClass');
|
||||
expect(customName.existingRowClass, isNull);
|
||||
|
||||
expect(customNameView.dartTypeName, 'CustomViewClass');
|
||||
expect(customNameView.existingRowClass, isNull);
|
||||
|
||||
expect(existing.dartTypeName, 'ExistingRowClass');
|
||||
expect(existing.existingRowClass.targetClass.name, 'ExistingRowClass');
|
||||
|
||||
expect(existingView.dartTypeName, 'ExistingForView');
|
||||
expect(existingView.existingRowClass.targetClass.name, 'ExistingForView');
|
||||
});
|
||||
}
|
|
@ -15,11 +15,19 @@ class CreateViewStatement extends Statement implements CreatingStatement {
|
|||
|
||||
final List<String>? columns;
|
||||
|
||||
CreateViewStatement(
|
||||
{this.ifNotExists = false,
|
||||
required this.viewName,
|
||||
this.columns,
|
||||
required this.query});
|
||||
/// Moor-specific information about the desired name of a Dart class for this
|
||||
/// table.
|
||||
///
|
||||
/// This will always be `null` when moor extensions are not enabled.
|
||||
MoorTableName? moorTableName;
|
||||
|
||||
CreateViewStatement({
|
||||
this.ifNotExists = false,
|
||||
required this.viewName,
|
||||
this.columns,
|
||||
required this.query,
|
||||
this.moorTableName,
|
||||
});
|
||||
|
||||
@override
|
||||
String get createdName => viewName;
|
||||
|
@ -32,8 +40,11 @@ class CreateViewStatement extends Statement implements CreatingStatement {
|
|||
@override
|
||||
void transformChildren<A>(Transformer<A> transformer, A arg) {
|
||||
query = transformer.transformChild(query, this, arg);
|
||||
moorTableName =
|
||||
transformer.transformNullableChild(moorTableName, this, arg);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<AstNode> get childNodes => [query];
|
||||
Iterable<AstNode> get childNodes =>
|
||||
[query, if (moorTableName != null) moorTableName!];
|
||||
}
|
||||
|
|
|
@ -2071,6 +2071,7 @@ class Parser {
|
|||
|
||||
final ifNotExists = _ifNotExists();
|
||||
final name = _consumeIdentifier('Expected a name for this view');
|
||||
final moorTableName = _moorTableName();
|
||||
|
||||
List<String>? columnNames;
|
||||
if (_matchOne(TokenType.leftParen)) {
|
||||
|
@ -2080,13 +2081,17 @@ class Parser {
|
|||
|
||||
_consume(TokenType.as, 'Expected AS SELECT');
|
||||
|
||||
final query = _fullSelect()!;
|
||||
final query = _fullSelect();
|
||||
if (query == null) {
|
||||
_error('Expected a SELECT statement here');
|
||||
}
|
||||
|
||||
return CreateViewStatement(
|
||||
ifNotExists: ifNotExists,
|
||||
viewName: name.identifier,
|
||||
columns: columnNames,
|
||||
query: query,
|
||||
moorTableName: moorTableName,
|
||||
)
|
||||
..viewNameToken = name
|
||||
..setSpan(create, _previous);
|
||||
|
|
|
@ -18,6 +18,24 @@ void main() {
|
|||
);
|
||||
});
|
||||
|
||||
test('parses a CREATE VIEW statement with an existing Dart class', () {
|
||||
testStatement(
|
||||
'CREATE VIEW my_view AS SELECT 1 WITH ExistingDartClass',
|
||||
CreateViewStatement(
|
||||
viewName: 'my_view',
|
||||
query: SelectStatement(
|
||||
columns: [
|
||||
ExpressionResultColumn(
|
||||
expression: NumericLiteral(1, token(TokenType.numberLiteral)),
|
||||
),
|
||||
],
|
||||
),
|
||||
moorTableName: MoorTableName('ExistingDartClass', true),
|
||||
),
|
||||
moorMode: true,
|
||||
);
|
||||
});
|
||||
|
||||
test('parses a complex CREATE View statement', () {
|
||||
testStatement(
|
||||
'CREATE VIEW IF NOT EXISTS my_complex_view (ids, name, count, type) AS '
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
@TestOn('vm')
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
|
||||
|
|
Loading…
Reference in New Issue