mirror of https://github.com/AMT-Cheif/drift.git
added support for CREATE VIEW in .moor files
This commit is contained in:
parent
7a2c4e7f75
commit
c9269e13be
|
@ -47,6 +47,27 @@ class Index extends DatabaseSchemaEntity {
|
|||
Index(this.entityName, this.createIndexStmt);
|
||||
}
|
||||
|
||||
/// A sqlite view.
|
||||
///
|
||||
/// In moor, views can only be declared in `.moor` files.
|
||||
///
|
||||
/// For more information on views, see the [CREATE VIEW][sqlite-docs]
|
||||
/// documentation from sqlite, or the [entry on sqlitetutorial.net][sql-tut].
|
||||
///
|
||||
/// [sqlite-docs]: https://www.sqlite.org/lang_createview.html
|
||||
/// [sql-tut]: https://www.sqlitetutorial.net/sqlite-create-view/
|
||||
class View extends DatabaseSchemaEntity {
|
||||
@override
|
||||
final String entityName;
|
||||
|
||||
/// The `CREATE VIEW` sql statement that can be used to create this view.
|
||||
final String createViewStmt;
|
||||
|
||||
/// Creates an view model by the [createViewStmt] and its [entityName].
|
||||
/// Mainly used by generated code.
|
||||
View(this.entityName, this.createViewStmt);
|
||||
}
|
||||
|
||||
/// An internal schema entity to run an sql statement when the database is
|
||||
/// created.
|
||||
///
|
||||
|
|
|
@ -491,7 +491,7 @@ abstract class _$Database extends GeneratedDatabase {
|
|||
Future<int> _resetCategory(int var1) {
|
||||
return customUpdate(
|
||||
'UPDATE todos SET category = NULL WHERE category = ?',
|
||||
variables: [Variable.withInt(var1)],
|
||||
variables: [Variable<int>(var1)],
|
||||
updates: {todos},
|
||||
updateKind: UpdateKind.update,
|
||||
);
|
||||
|
@ -521,8 +521,8 @@ class CategoriesWithCountResult {
|
|||
final String desc;
|
||||
final int amount;
|
||||
CategoriesWithCountResult({
|
||||
this.id,
|
||||
this.desc,
|
||||
this.amount,
|
||||
@required this.id,
|
||||
@required this.desc,
|
||||
@required this.amount,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:sqlparser/sqlparser.dart';
|
|||
typedef LogFunction = void Function(dynamic message,
|
||||
[Object error, StackTrace stackTrace]);
|
||||
|
||||
/// Base class for errors that can be presented to an user.
|
||||
/// Base class for errors that can be presented to a user.
|
||||
class MoorError {
|
||||
final Severity severity;
|
||||
final String message;
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:moor_generator/src/analyzer/runner/results.dart';
|
|||
import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/lints/linter.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/query_analyzer.dart';
|
||||
import 'package:moor_generator/src/model/view.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:sqlparser/utils/find_referenced_tables.dart';
|
||||
|
||||
|
@ -16,14 +17,18 @@ class EntityHandler extends BaseAnalyzer {
|
|||
AnalyzeMoorStep get moorStep => step as AnalyzeMoorStep;
|
||||
|
||||
EntityHandler(
|
||||
AnalyzeMoorStep step, this.file, List<MoorTable> availableTables)
|
||||
: super(availableTables, step) {
|
||||
AnalyzeMoorStep step,
|
||||
this.file,
|
||||
List<MoorTable> availableTables,
|
||||
List<MoorView> availableViews,
|
||||
) : super(availableTables, availableViews, step) {
|
||||
_referenceResolver = _ReferenceResolvingVisitor(this);
|
||||
}
|
||||
|
||||
final Map<CreateTriggerStatement, MoorTrigger> _triggers = {};
|
||||
final Map<TableInducingStatement, MoorTable> _tables = {};
|
||||
final Map<CreateIndexStatement, MoorIndex> _indexes = {};
|
||||
final Map<CreateViewStatement, MoorView> _views = {};
|
||||
|
||||
_ReferenceResolvingVisitor _referenceResolver;
|
||||
|
||||
|
@ -56,6 +61,12 @@ class EntityHandler extends BaseAnalyzer {
|
|||
|
||||
_lint(node, 'special @create table');
|
||||
entity.references.addAll(_findTables(node.statement));
|
||||
} else if (entity is MoorView) {
|
||||
final node =
|
||||
_handleMoorDeclaration(entity, _views) as CreateViewStatement;
|
||||
_lint(node, node.viewName);
|
||||
entity.references.addAll(_findTables(node.query));
|
||||
entity.references.addAll(_findViews(node.query));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +86,12 @@ class EntityHandler extends BaseAnalyzer {
|
|||
return tablesFinder.foundTables.map(mapper.tableToMoor);
|
||||
}
|
||||
|
||||
Iterable<MoorView> _findViews(AstNode node) {
|
||||
final tablesFinder = ReferencedTablesVisitor();
|
||||
node.acceptWithoutArg(tablesFinder);
|
||||
return tablesFinder.foundViews.map(mapper.viewToMoor);
|
||||
}
|
||||
|
||||
Iterable<WrittenMoorTable> _findUpdatedTables(AstNode node) {
|
||||
final finder = UpdatedTablesVisitor();
|
||||
node.acceptWithoutArg(finder);
|
||||
|
|
|
@ -32,6 +32,8 @@ class MoorParser {
|
|||
} else if (parsedStmt is CreateTriggerStatement) {
|
||||
// the table will be resolved in the analysis step
|
||||
createdEntities.add(MoorTrigger.fromMoor(parsedStmt, step.file));
|
||||
} else if (parsedStmt is CreateViewStatement) {
|
||||
createdEntities.add(MoorView.fromMoor(parsedStmt, step.file));
|
||||
} else if (parsedStmt is CreateIndexStatement) {
|
||||
createdEntities.add(MoorIndex.fromMoor(parsedStmt, step.file));
|
||||
} else if (parsedStmt is DeclaredStatement) {
|
||||
|
|
|
@ -3,12 +3,16 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:moor_generator/src/model/sql_query.dart';
|
||||
import 'package:moor_generator/src/model/view.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
abstract class FileResult {
|
||||
final List<MoorSchemaEntity> declaredEntities;
|
||||
|
||||
Iterable<MoorTable> get declaredTables => declaredEntities.whereType();
|
||||
Iterable<MoorTable> get declaredTables =>
|
||||
declaredEntities.whereType<MoorTable>();
|
||||
Iterable<MoorView> get declaredViews =>
|
||||
declaredEntities.whereType<MoorView>();
|
||||
|
||||
FileResult(this.declaredEntities);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'package:moor_generator/src/analyzer/sql_queries/query_analyzer.dart';
|
|||
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/task.dart';
|
||||
import 'package:moor_generator/src/model/sql_query.dart';
|
||||
import 'package:moor_generator/src/model/view.dart';
|
||||
import 'package:moor_generator/src/utils/entity_reference_sorter.dart';
|
||||
|
||||
import 'package:source_gen/source_gen.dart';
|
||||
|
@ -60,6 +61,10 @@ abstract class AnalyzingStep extends Step {
|
|||
}
|
||||
|
||||
Iterable<MoorTable> _availableTables(List<FoundFile> imports) {
|
||||
return _availableEntities(imports).whereType();
|
||||
return _availableEntities(imports).whereType<MoorTable>();
|
||||
}
|
||||
|
||||
Iterable<MoorView> _availableViews(List<FoundFile> imports) {
|
||||
return _availableEntities(imports).whereType<MoorView>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,8 +74,9 @@ class AnalyzeDartStep extends AnalyzingStep {
|
|||
.expand((f) => f.resolvedQueries);
|
||||
|
||||
final availableTables = availableEntities.whereType<MoorTable>().toList();
|
||||
final parser =
|
||||
SqlAnalyzer(this, availableTables, accessor.declaredQueries);
|
||||
final availableViews = availableEntities.whereType<MoorView>().toList();
|
||||
final parser = SqlAnalyzer(
|
||||
this, availableTables, availableViews, accessor.declaredQueries);
|
||||
parser.parse();
|
||||
|
||||
accessor
|
||||
|
|
|
@ -18,10 +18,15 @@ class AnalyzeMoorStep extends AnalyzingStep {
|
|||
.followedBy(parseResult.declaredTables)
|
||||
.toList();
|
||||
|
||||
final parser = SqlAnalyzer(this, availableTables, parseResult.queries)
|
||||
..parse();
|
||||
final availableViews = _availableViews(transitiveImports)
|
||||
.followedBy(parseResult.declaredViews)
|
||||
.toList();
|
||||
|
||||
EntityHandler(this, parseResult, availableTables).handle();
|
||||
final parser =
|
||||
SqlAnalyzer(this, availableTables, availableViews, parseResult.queries)
|
||||
..parse();
|
||||
|
||||
EntityHandler(this, parseResult, availableTables, availableViews).handle();
|
||||
|
||||
parseResult.resolvedQueries = parser.foundQueries;
|
||||
}
|
||||
|
|
|
@ -7,17 +7,19 @@ import 'package:moor_generator/src/analyzer/runner/steps.dart';
|
|||
import 'package:moor_generator/src/model/sql_query.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/query_handler.dart';
|
||||
import 'package:moor_generator/src/analyzer/sql_queries/type_mapping.dart';
|
||||
import 'package:moor_generator/src/model/view.dart';
|
||||
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
||||
|
||||
abstract class BaseAnalyzer {
|
||||
final List<MoorTable> tables;
|
||||
final List<MoorView> views;
|
||||
final Step step;
|
||||
|
||||
@protected
|
||||
final TypeMapper mapper;
|
||||
SqlEngine _engine;
|
||||
|
||||
BaseAnalyzer(this.tables, this.step)
|
||||
BaseAnalyzer(this.tables, this.views, this.step)
|
||||
: mapper = TypeMapper(
|
||||
applyTypeConvertersToVariables:
|
||||
step.task.session.options.applyConvertersOnVariables,
|
||||
|
@ -28,10 +30,24 @@ abstract class BaseAnalyzer {
|
|||
if (_engine == null) {
|
||||
_engine = step.task.session.spawnEngine();
|
||||
tables.map(mapper.extractStructure).forEach(_engine.registerTable);
|
||||
resolveViews();
|
||||
views.map(mapper.extractView).forEach(_engine.registerView);
|
||||
}
|
||||
return _engine;
|
||||
}
|
||||
|
||||
/// Parses the view and adds columns to its resolved columns.
|
||||
@protected
|
||||
void resolveViews() {
|
||||
for (final view in views) {
|
||||
final ctx = _engine.analyzeNode(
|
||||
view.declaration.node, view.declaration.createSql);
|
||||
view.parserView = const SchemaFromCreateTable(moorExtensions: true)
|
||||
.readView(ctx, view.declaration.creatingStatement);
|
||||
view.columns = view.parserView.resolvedColumns;
|
||||
}
|
||||
}
|
||||
|
||||
@protected
|
||||
void report(AnalysisError error, {String Function() msg, Severity severity}) {
|
||||
if (step.file.type == FileType.moor) {
|
||||
|
@ -62,8 +78,9 @@ class SqlAnalyzer extends BaseAnalyzer {
|
|||
|
||||
final List<SqlQuery> foundQueries = [];
|
||||
|
||||
SqlAnalyzer(Step step, List<MoorTable> tables, this.definedQueries)
|
||||
: super(tables, step);
|
||||
SqlAnalyzer(Step step, List<MoorTable> tables, List<MoorView> views,
|
||||
this.definedQueries)
|
||||
: super(tables, views, step);
|
||||
|
||||
void parse() {
|
||||
for (final query in definedQueries) {
|
||||
|
|
|
@ -17,6 +17,7 @@ class QueryHandler {
|
|||
final TypeMapper mapper;
|
||||
|
||||
Set<Table> _foundTables;
|
||||
Set<View> _foundViews;
|
||||
List<FoundElement> _foundElements;
|
||||
Iterable<FoundVariable> get _foundVariables =>
|
||||
_foundElements.whereType<FoundVariable>();
|
||||
|
@ -75,8 +76,13 @@ class QueryHandler {
|
|||
final tableFinder = ReferencedTablesVisitor();
|
||||
_select.acceptWithoutArg(tableFinder);
|
||||
_foundTables = tableFinder.foundTables;
|
||||
_foundViews = tableFinder.foundViews;
|
||||
final moorTables =
|
||||
_foundTables.map(mapper.tableToMoor).where((s) => s != null).toList();
|
||||
final moorViews =
|
||||
_foundViews.map(mapper.viewToMoor).where((s) => s != null).toList();
|
||||
|
||||
final moorEntities = [...moorTables, ...moorViews];
|
||||
|
||||
String requestedName;
|
||||
if (source is DeclaredMoorQuery) {
|
||||
|
@ -87,7 +93,7 @@ class QueryHandler {
|
|||
name,
|
||||
context,
|
||||
_foundElements,
|
||||
moorTables,
|
||||
moorEntities,
|
||||
_inferResultSet(),
|
||||
requestedName,
|
||||
);
|
||||
|
@ -121,6 +127,12 @@ class QueryHandler {
|
|||
candidatesForSingleTable.clear();
|
||||
}
|
||||
|
||||
if (_foundViews.isNotEmpty) {
|
||||
// For now we're not using the single table optimization when selecting
|
||||
// from views.
|
||||
candidatesForSingleTable.clear();
|
||||
}
|
||||
|
||||
// if all columns read from the same table, and all columns in that table
|
||||
// are present in the result set, we can use the data class we generate for
|
||||
// that table instead of generating another class just for this result set.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:moor/moor.dart' as m;
|
||||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/model/sql_query.dart';
|
||||
import 'package:moor_generator/src/model/view.dart';
|
||||
import 'package:moor_generator/src/utils/type_converter_hint.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:sqlparser/utils/find_referenced_tables.dart' as s;
|
||||
|
@ -9,6 +10,7 @@ import 'package:sqlparser/utils/find_referenced_tables.dart' as s;
|
|||
/// library.
|
||||
class TypeMapper {
|
||||
final Map<Table, MoorTable> _engineTablesToSpecified = {};
|
||||
final Map<View, MoorView> _engineViewsToSpecified = {};
|
||||
final bool applyTypeConvertersToVariables;
|
||||
|
||||
TypeMapper({this.applyTypeConvertersToVariables = false});
|
||||
|
@ -87,6 +89,20 @@ class TypeMapper {
|
|||
throw StateError('Unexpected type: $type');
|
||||
}
|
||||
|
||||
/// Converts a [MoorView] into something that can be understood
|
||||
/// by the sqlparser library.
|
||||
View extractView(MoorView view) {
|
||||
if (view.parserView != null) {
|
||||
final parserView = view.parserView;
|
||||
_engineViewsToSpecified[parserView] = view;
|
||||
return parserView;
|
||||
}
|
||||
final engineView = View(name: view.name, resolvedColumns: view.columns);
|
||||
engineView.setMeta<MoorView>(view);
|
||||
_engineViewsToSpecified[engineView] = view;
|
||||
return engineView;
|
||||
}
|
||||
|
||||
/// Extracts variables and Dart templates from the [ctx]. Variables are
|
||||
/// sorted by their ascending index. Placeholders are sorted by the position
|
||||
/// they have in the query. When comparing variables and placeholders, the
|
||||
|
@ -249,6 +265,10 @@ class TypeMapper {
|
|||
return _engineTablesToSpecified[table];
|
||||
}
|
||||
|
||||
MoorView viewToMoor(View view) {
|
||||
return _engineViewsToSpecified[view];
|
||||
}
|
||||
|
||||
WrittenMoorTable writtenToMoor(s.TableWrite table) {
|
||||
final moorKind = const {
|
||||
s.UpdateKind.insert: m.UpdateKind.insert,
|
||||
|
|
|
@ -11,6 +11,7 @@ part 'index.dart';
|
|||
part 'special_queries.dart';
|
||||
part 'tables.dart';
|
||||
part 'trigger.dart';
|
||||
part 'views.dart';
|
||||
|
||||
/// Interface for model elements that are declared somewhere.
|
||||
abstract class HasDeclaration {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
part of 'declaration.dart';
|
||||
|
||||
abstract class ViewDeclaration extends Declaration {}
|
||||
|
||||
abstract class ViewDeclarationWithSql implements ViewDeclaration {
|
||||
/// The `CREATE VIEW` statement used to create this view.
|
||||
String get createSql;
|
||||
|
||||
/// The parsed statement creating this view.
|
||||
CreateViewStatement get creatingStatement;
|
||||
}
|
||||
|
||||
class MoorViewDeclaration
|
||||
implements ViewDeclaration, MoorDeclaration, ViewDeclarationWithSql {
|
||||
@override
|
||||
final SourceRange declaration;
|
||||
|
||||
@override
|
||||
final CreateViewStatement node;
|
||||
|
||||
MoorViewDeclaration._(this.declaration, this.node);
|
||||
|
||||
factory MoorViewDeclaration(CreateViewStatement node, FoundFile file) {
|
||||
return MoorViewDeclaration._(
|
||||
SourceRange.fromNodeAndFile(node, file),
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get createSql => node.span.text;
|
||||
|
||||
@override
|
||||
CreateViewStatement get creatingStatement => node;
|
||||
}
|
|
@ -10,3 +10,4 @@ export 'table.dart';
|
|||
export 'trigger.dart';
|
||||
export 'types.dart';
|
||||
export 'used_type_converter.dart';
|
||||
export 'view.dart';
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:moor/moor.dart' show $mrjf, $mrjc, UpdateKind;
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:moor_generator/src/model/base_entity.dart';
|
||||
import 'package:moor_generator/src/utils/hash.dart';
|
||||
import 'package:moor_generator/src/writer/writer.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
|
@ -108,7 +109,7 @@ abstract class SqlQuery {
|
|||
}
|
||||
|
||||
class SqlSelectQuery extends SqlQuery {
|
||||
final List<MoorTable> readsFrom;
|
||||
final List<MoorSchemaEntity> readsFrom;
|
||||
final InferredResultSet resultSet;
|
||||
|
||||
/// The name of the result class, as requested by the user.
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import 'package:moor_generator/src/analyzer/options.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/file_graph.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import 'base_entity.dart';
|
||||
import 'declarations/declaration.dart';
|
||||
import 'model.dart';
|
||||
|
||||
/// A parsed view
|
||||
class MoorView extends MoorSchemaEntity {
|
||||
@override
|
||||
final MoorViewDeclaration declaration;
|
||||
|
||||
/// The associated view to use for the sqlparser package when analyzing
|
||||
/// sql queries. Note that this field is set lazily.
|
||||
View parserView;
|
||||
|
||||
final String name;
|
||||
|
||||
@override
|
||||
List<MoorSchemaEntity> references = [];
|
||||
|
||||
List<ViewColumn> columns;
|
||||
|
||||
MoorView({
|
||||
this.declaration,
|
||||
this.name,
|
||||
});
|
||||
|
||||
factory MoorView.fromMoor(CreateViewStatement stmt, FoundFile file) {
|
||||
return MoorView(
|
||||
declaration: MoorViewDeclaration(stmt, file),
|
||||
name: stmt.viewName,
|
||||
);
|
||||
}
|
||||
|
||||
/// The `CREATE VIEW` statement that can be used to create this view.
|
||||
String createSql(MoorOptions options) {
|
||||
return declaration.formatSqlIfAvailable(options) ?? declaration.createSql;
|
||||
}
|
||||
|
||||
@override
|
||||
String get dbGetterName => dbFieldName(name);
|
||||
|
||||
@override
|
||||
String get displayName => name;
|
||||
}
|
|
@ -85,6 +85,15 @@ class DatabaseWriter {
|
|||
'${asDartLiteral(entity.createSql(scope.options))})',
|
||||
options: scope.generationOptions,
|
||||
);
|
||||
} else if (entity is MoorView) {
|
||||
writeMemoizedGetter(
|
||||
buffer: dbScope.leaf(),
|
||||
getterName: entity.dbGetterName,
|
||||
returnType: 'View',
|
||||
code: 'View(${asDartLiteral(entity.displayName)}, '
|
||||
'${asDartLiteral(entity.createSql(scope.options))})',
|
||||
options: scope.generationOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
@Tags(['analyzer'])
|
||||
import 'package:moor_generator/src/analyzer/errors.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('view created', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/table.moor': '''
|
||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||
''',
|
||||
'foo|lib/a.moor': '''
|
||||
import 'table.moor';
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
final view = file.currentResult.declaredViews.single;
|
||||
expect(view.columns.length, equals(1));
|
||||
final column = view.columns.single;
|
||||
|
||||
state.close();
|
||||
|
||||
expect(column.type.type, BasicType.text);
|
||||
|
||||
expect(file.errors.errors, isEmpty);
|
||||
});
|
||||
|
||||
test('view without table', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE VIEW random_view AS
|
||||
SELECT name FROM t WHERE id % 2 = 0;
|
||||
''',
|
||||
});
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
|
||||
state.close();
|
||||
|
||||
expect(
|
||||
file.errors.errors,
|
||||
contains(isA<MoorError>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Could not find t.'),
|
||||
)));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import 'package:moor_generator/moor_generator.dart';
|
||||
import 'package:moor_generator/src/analyzer/options.dart';
|
||||
import 'package:moor_generator/src/analyzer/runner/results.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils.dart';
|
||||
|
||||
void main() {
|
||||
test('select from view test', () async {
|
||||
final state = TestState.withContent({
|
||||
'foo|lib/a.moor': '''
|
||||
CREATE TABLE artists (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE albums (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
artist INTEGER NOT NULL REFERENCES artists (id)
|
||||
);
|
||||
|
||||
CREATE TABLE tracks (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
album INTEGER NOT NULL REFERENCES albums (id),
|
||||
duration_seconds INTEGER NOT NULL,
|
||||
was_single BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
|
||||
CREATE VIEW total_duration_by_artist_view AS
|
||||
SELECT a.*, SUM(tracks.duration_seconds) AS duration
|
||||
FROM artists a
|
||||
INNER JOIN albums ON albums.artist = a.id
|
||||
INNER JOIN tracks ON tracks.album = albums.id
|
||||
GROUP BY artists.id;
|
||||
|
||||
totalDurationByArtist:
|
||||
SELECT * FROM total_duration_by_artist_view;
|
||||
'''
|
||||
}, options: const MoorOptions());
|
||||
|
||||
final file = await state.analyze('package:foo/a.moor');
|
||||
final result = file.currentResult as ParsedMoorFile;
|
||||
final queries = result.resolvedQueries;
|
||||
|
||||
expect(state.session.errorsInFileAndImports(file), isEmpty);
|
||||
state.close();
|
||||
|
||||
final totalDurationByArtist =
|
||||
queries.singleWhere((q) => q.name == 'totalDurationByArtist');
|
||||
expect(
|
||||
totalDurationByArtist,
|
||||
returnsColumns({
|
||||
'id': ColumnType.integer,
|
||||
'name': ColumnType.text,
|
||||
'duration': ColumnType.integer,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -9,7 +9,7 @@ class View extends NamedResultSet with HasMetaMixin implements HumanReadable {
|
|||
@override
|
||||
final List<ViewColumn> resolvedColumns;
|
||||
|
||||
/// The ast node that created this table
|
||||
/// The ast node that created this view
|
||||
final CreateViewStatement? definition;
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in New Issue