mirror of https://github.com/AMT-Cheif/drift.git
assumeCorrectReference resovels #2715
assumeCorrectReference option added assume the correct reference of the dart code in views, when resolver fails.
This commit is contained in:
parent
39018d381c
commit
7c2c17d5e4
|
@ -102,6 +102,9 @@ class DriftOptions {
|
||||||
@JsonKey(name: 'write_to_columns_mixins', defaultValue: false)
|
@JsonKey(name: 'write_to_columns_mixins', defaultValue: false)
|
||||||
final bool writeToColumnsMixins;
|
final bool writeToColumnsMixins;
|
||||||
|
|
||||||
|
@JsonKey(name: 'assume_correct_reference', defaultValue: false)
|
||||||
|
final bool assumeCorrectReference;
|
||||||
|
|
||||||
@JsonKey(name: 'has_separate_analyzer', defaultValue: false)
|
@JsonKey(name: 'has_separate_analyzer', defaultValue: false)
|
||||||
final bool hasDriftAnalyzer;
|
final bool hasDriftAnalyzer;
|
||||||
|
|
||||||
|
@ -135,6 +138,7 @@ class DriftOptions {
|
||||||
this.writeToColumnsMixins = false,
|
this.writeToColumnsMixins = false,
|
||||||
this.fatalWarnings = false,
|
this.fatalWarnings = false,
|
||||||
this.hasDriftAnalyzer = false,
|
this.hasDriftAnalyzer = false,
|
||||||
|
this.assumeCorrectReference = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
DriftOptions({
|
DriftOptions({
|
||||||
|
@ -160,6 +164,7 @@ class DriftOptions {
|
||||||
required this.fatalWarnings,
|
required this.fatalWarnings,
|
||||||
required this.preamble,
|
required this.preamble,
|
||||||
required this.hasDriftAnalyzer,
|
required this.hasDriftAnalyzer,
|
||||||
|
required this.assumeCorrectReference,
|
||||||
this.dialect,
|
this.dialect,
|
||||||
}) {
|
}) {
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
|
|
@ -34,6 +34,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
structure.dartQuerySource,
|
structure.dartQuerySource,
|
||||||
structure.primarySource,
|
structure.primarySource,
|
||||||
staticReferences,
|
staticReferences,
|
||||||
|
structure.staticSource
|
||||||
),
|
),
|
||||||
references: [
|
references: [
|
||||||
for (final reference in staticReferences) reference.table,
|
for (final reference in staticReferences) reference.table,
|
||||||
|
@ -178,7 +179,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
final from = target.argumentList.arguments[0].toSource();
|
final from = target.argumentList.arguments[0].toSource();
|
||||||
final resolvedFrom =
|
final resolvedFrom =
|
||||||
references.firstWhereOrNull((element) => element.name == from);
|
references.firstWhereOrNull((element) => element.name == from);
|
||||||
if (resolvedFrom == null) {
|
if (resolvedFrom == null && !resolver.driver.options.assumeCorrectReference) {
|
||||||
reportError(
|
reportError(
|
||||||
DriftAnalysisError.inDartAst(
|
DriftAnalysisError.inDartAst(
|
||||||
as,
|
as,
|
||||||
|
@ -188,11 +189,28 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
AnnotatedDartCode query;
|
||||||
|
if (resolvedFrom == null &&
|
||||||
|
resolver.driver.options.assumeCorrectReference) {
|
||||||
|
query = AnnotatedDartCode.build(
|
||||||
|
(builder) => builder.addText(body.expression.toSource().replaceAll(target!.toSource(), '')));
|
||||||
|
} else {
|
||||||
|
query = AnnotatedDartCode.build(
|
||||||
|
(builder) => builder.addAstNode(body.expression, exclude: {target!}));
|
||||||
|
}
|
||||||
|
|
||||||
final query = AnnotatedDartCode.build(
|
// if(resolver.driver.options.assumeCorrectReference){
|
||||||
(builder) => builder.addAstNode(body.expression, exclude: {target!}));
|
// bool separate = false;
|
||||||
|
// for (int i = 0; i < query.elements.length; i++) {
|
||||||
|
// if (separate && query.elements[i] is String && i != query.elements.length-1) {
|
||||||
|
// query.elements[i]+=',';
|
||||||
|
// separate = false;
|
||||||
|
// }
|
||||||
|
// separate = query.elements[i] is DartTopLevelSymbol;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
return _ParsedDartViewSelect(
|
return _ParsedDartViewSelect(
|
||||||
resolvedFrom, innerJoins, outerJoins, columnExpressions, query);
|
resolvedFrom, innerJoins, outerJoins, columnExpressions, query, from);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<DriftColumn>> _parseColumns(
|
Future<List<DriftColumn>> _parseColumns(
|
||||||
|
@ -209,7 +227,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
final reference =
|
final reference =
|
||||||
references.firstWhereOrNull((ref) => ref.name == parts[0]);
|
references.firstWhereOrNull((ref) => ref.name == parts[0]);
|
||||||
if (reference == null) {
|
if (reference == null || reference.table == null) {
|
||||||
reportError(DriftAnalysisError.inDartAst(
|
reportError(DriftAnalysisError.inDartAst(
|
||||||
discovered.dartElement,
|
discovered.dartElement,
|
||||||
columnReference,
|
columnReference,
|
||||||
|
@ -280,7 +298,11 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
nameInSql: ReCase(getter.name).snakeCase,
|
nameInSql: ReCase(getter.name).snakeCase,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
constraints: [
|
constraints: [
|
||||||
ColumnGeneratedAs(AnnotatedDartCode.ast(expression), false)
|
resolver.driver.options.assumeCorrectReference
|
||||||
|
? ColumnGeneratedAs(AnnotatedDartCode.build((builder) {
|
||||||
|
builder.addText(expression.toSource());
|
||||||
|
}), false)
|
||||||
|
: ColumnGeneratedAs(AnnotatedDartCode.ast(expression), false),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -310,8 +332,9 @@ class _ParsedDartViewSelect {
|
||||||
final List<Expression> selectedColumns;
|
final List<Expression> selectedColumns;
|
||||||
final AnnotatedDartCode dartQuerySource;
|
final AnnotatedDartCode dartQuerySource;
|
||||||
|
|
||||||
|
final String? staticSource;
|
||||||
_ParsedDartViewSelect(this.primarySource, this.innerJoins, this.outerJoins,
|
_ParsedDartViewSelect(this.primarySource, this.innerJoins, this.outerJoins,
|
||||||
this.selectedColumns, this.dartQuerySource);
|
this.selectedColumns, this.dartQuerySource, [this.staticSource]);
|
||||||
|
|
||||||
bool referenceIsNullable(TableReferenceInDartView ref) {
|
bool referenceIsNullable(TableReferenceInDartView ref) {
|
||||||
return ref != primarySource && !innerJoins.contains(ref);
|
return ref != primarySource && !innerJoins.contains(ref);
|
||||||
|
|
|
@ -96,6 +96,6 @@ class DartViewSource extends DriftViewSource {
|
||||||
final AnnotatedDartCode dartQuerySource;
|
final AnnotatedDartCode dartQuerySource;
|
||||||
final TableReferenceInDartView? primaryFrom;
|
final TableReferenceInDartView? primaryFrom;
|
||||||
final List<TableReferenceInDartView> staticReferences;
|
final List<TableReferenceInDartView> staticReferences;
|
||||||
|
final String? staticSource;
|
||||||
DartViewSource(this.dartQuerySource, this.primaryFrom, this.staticReferences);
|
DartViewSource(this.dartQuerySource, this.primaryFrom, this.staticReferences, [this.staticSource]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,8 @@ class ElementSerializer {
|
||||||
'staticReferences': [
|
'staticReferences': [
|
||||||
for (final reference in source.staticReferences)
|
for (final reference in source.staticReferences)
|
||||||
_serializeTableReferenceInDartView(reference),
|
_serializeTableReferenceInDartView(reference),
|
||||||
]
|
],
|
||||||
|
'staticSource':source.staticSource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,6 +658,9 @@ class ElementDeserializer {
|
||||||
for (final element in serializedSource.list('staticReferences'))
|
for (final element in serializedSource.list('staticReferences'))
|
||||||
readReference(element as Map)
|
readReference(element as Map)
|
||||||
],
|
],
|
||||||
|
serializedSource['staticSource'] != null
|
||||||
|
? serializedSource['staticSource'] as String
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw UnsupportedError('Unknown view source $serializedSource');
|
throw UnsupportedError('Unknown view source $serializedSource');
|
||||||
|
|
|
@ -33,6 +33,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
|
||||||
'store_date_time_values_as_text',
|
'store_date_time_values_as_text',
|
||||||
'case_from_dart_to_sql',
|
'case_from_dart_to_sql',
|
||||||
'write_to_columns_mixins',
|
'write_to_columns_mixins',
|
||||||
|
'assume_correct_reference',
|
||||||
'has_separate_analyzer',
|
'has_separate_analyzer',
|
||||||
'preamble',
|
'preamble',
|
||||||
'fatal_warnings'
|
'fatal_warnings'
|
||||||
|
@ -94,6 +95,8 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
|
||||||
preamble: $checkedConvert('preamble', (v) => v as String?),
|
preamble: $checkedConvert('preamble', (v) => v as String?),
|
||||||
hasDriftAnalyzer: $checkedConvert(
|
hasDriftAnalyzer: $checkedConvert(
|
||||||
'has_separate_analyzer', (v) => v as bool? ?? false),
|
'has_separate_analyzer', (v) => v as bool? ?? false),
|
||||||
|
assumeCorrectReference: $checkedConvert(
|
||||||
|
'assume_correct_reference', (v) => v as bool? ?? false),
|
||||||
dialect: $checkedConvert('sql',
|
dialect: $checkedConvert('sql',
|
||||||
(v) => v == null ? null : DialectOptions.fromJson(v as Map)),
|
(v) => v == null ? null : DialectOptions.fromJson(v as Map)),
|
||||||
);
|
);
|
||||||
|
@ -124,6 +127,7 @@ DriftOptions _$DriftOptionsFromJson(Map json) => $checkedCreate(
|
||||||
'writeToColumnsMixins': 'write_to_columns_mixins',
|
'writeToColumnsMixins': 'write_to_columns_mixins',
|
||||||
'fatalWarnings': 'fatal_warnings',
|
'fatalWarnings': 'fatal_warnings',
|
||||||
'hasDriftAnalyzer': 'has_separate_analyzer',
|
'hasDriftAnalyzer': 'has_separate_analyzer',
|
||||||
|
'assumeCorrectReference': 'assume_correct_reference',
|
||||||
'dialect': 'sql'
|
'dialect': 'sql'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -157,6 +161,7 @@ Map<String, dynamic> _$DriftOptionsToJson(DriftOptions instance) =>
|
||||||
'case_from_dart_to_sql':
|
'case_from_dart_to_sql':
|
||||||
_$CaseFromDartToSqlEnumMap[instance.caseFromDartToSql]!,
|
_$CaseFromDartToSqlEnumMap[instance.caseFromDartToSql]!,
|
||||||
'write_to_columns_mixins': instance.writeToColumnsMixins,
|
'write_to_columns_mixins': instance.writeToColumnsMixins,
|
||||||
|
'assume_correct_reference': instance.assumeCorrectReference,
|
||||||
'has_separate_analyzer': instance.hasDriftAnalyzer,
|
'has_separate_analyzer': instance.hasDriftAnalyzer,
|
||||||
'preamble': instance.preamble,
|
'preamble': instance.preamble,
|
||||||
'fatal_warnings': instance.fatalWarnings,
|
'fatal_warnings': instance.fatalWarnings,
|
||||||
|
|
|
@ -138,7 +138,7 @@ class ViewWriter extends TableOrViewWriter {
|
||||||
final source = view.source;
|
final source = view.source;
|
||||||
if (source is DartViewSource) {
|
if (source is DartViewSource) {
|
||||||
emitter
|
emitter
|
||||||
..write('(attachedDatabase.selectOnly(${source.primaryFrom?.name})'
|
..write('(attachedDatabase.selectOnly(${scope.options.assumeCorrectReference? source.primaryFrom?.name ?? source.staticSource:source.primaryFrom?.name})'
|
||||||
'..addColumns(\$columns))')
|
'..addColumns(\$columns))')
|
||||||
..writeDart(source.dartQuerySource)
|
..writeDart(source.dartQuerySource)
|
||||||
..writeln(';');
|
..writeln(';');
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:build_test/build_test.dart';
|
import 'package:build_test/build_test.dart';
|
||||||
import 'package:drift_dev/src/backends/build/analyzer.dart';
|
import 'package:drift_dev/src/backends/build/analyzer.dart';
|
||||||
|
@ -614,6 +616,153 @@ class MyDatabase {
|
||||||
expect(readAssets, isEmpty);
|
expect(readAssets, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('generates views from drift tables', () async {
|
||||||
|
final debugLogger = Logger('driftBuild');
|
||||||
|
debugLogger.onRecord.listen((e) => print(e.message));
|
||||||
|
|
||||||
|
final result = await emulateDriftBuild(
|
||||||
|
inputs: {
|
||||||
|
'a|lib/drift/datastore_db.dart': '''
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
part 'datastore_db.g.dart';
|
||||||
|
mixin AutoIncrement on Table {
|
||||||
|
IntColumn get id => integer().autoIncrement()();
|
||||||
|
}
|
||||||
|
@DataClassName('TodoEntry')
|
||||||
|
class TodosTable extends Table with AutoIncrement {
|
||||||
|
@override
|
||||||
|
String get tableName => 'todos';
|
||||||
|
TextColumn get title => text().withLength(min: 4, max: 16).nullable()();
|
||||||
|
TextColumn get content => text()();
|
||||||
|
@JsonKey('target_date')
|
||||||
|
DateTimeColumn get targetDate => dateTime().nullable().unique()();
|
||||||
|
IntColumn get category => integer().references(Categories, #id).nullable()();
|
||||||
|
TextColumn get status => textEnum<TodoStatus>().nullable()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Set<Column>>? get uniqueKeys => [
|
||||||
|
{title, category},
|
||||||
|
{title, targetDate},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TodoStatus { open, workInProgress, done }
|
||||||
|
|
||||||
|
class Users extends Table with AutoIncrement {
|
||||||
|
TextColumn get name => text().withLength(min: 6, max: 32).unique()();
|
||||||
|
BoolColumn get isAwesome => boolean().withDefault(const Constant(true))();
|
||||||
|
|
||||||
|
BlobColumn get profilePicture => blob()();
|
||||||
|
DateTimeColumn get creationTime => dateTime()
|
||||||
|
// ignore: recursive_getters
|
||||||
|
.check(creationTime.isBiggerThan(Constant(DateTime.utc(1950))))
|
||||||
|
.withDefault(currentDateAndTime)();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DataClassName('Category')
|
||||||
|
class Categories extends Table with AutoIncrement {
|
||||||
|
TextColumn get description =>
|
||||||
|
text().named('desc').customConstraint('NOT NULL UNIQUE')();
|
||||||
|
IntColumn get priority =>
|
||||||
|
intEnum<CategoryPriority>().withDefault(const Constant(0))();
|
||||||
|
|
||||||
|
TextColumn get descriptionInUpperCase =>
|
||||||
|
text().generatedAs(description.upper())();
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CategoryPriority { low, medium, high }
|
||||||
|
abstract class CategoryTodoCountView extends View {
|
||||||
|
TodosTable get todos;
|
||||||
|
Categories get categories;
|
||||||
|
|
||||||
|
Expression<int> get categoryId => categories.id;
|
||||||
|
Expression<String> get description =>
|
||||||
|
categories.description + const Variable('!');
|
||||||
|
Expression<int> get itemCount => todos.id.count();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Query as() => select([categoryId, description, itemCount])
|
||||||
|
.from(categories)
|
||||||
|
.join([innerJoin(todos, todos.category.equalsExp(categories.id))])
|
||||||
|
..groupBy([categories.id]);
|
||||||
|
}
|
||||||
|
abstract class ComboGroupView extends View {
|
||||||
|
late final DatastoreDb attachedDatabase;
|
||||||
|
IntColumn get comboGroupID => attachedDatabase.comboGroup.comboGroupID;
|
||||||
|
IntColumn get objectNumber => attachedDatabase.comboGroup.objectNumber;
|
||||||
|
TextColumn get stringText => attachedDatabase.stringTable.stringText;
|
||||||
|
// ComboGroup get comboGroup => attachedDatabase.comboGroup;
|
||||||
|
// late final ComboGroup comboGroup;
|
||||||
|
@override
|
||||||
|
Query as() => select([
|
||||||
|
comboGroupID,
|
||||||
|
objectNumber,
|
||||||
|
stringText,
|
||||||
|
]).from(attachedDatabase.comboGroup).join([
|
||||||
|
innerJoin(
|
||||||
|
attachedDatabase.stringTable,
|
||||||
|
attachedDatabase.stringTable.stringNumberID
|
||||||
|
.equalsExp(attachedDatabase.comboGroup.nameID)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DriftDatabase(
|
||||||
|
tables: [TodosTable, Categories],
|
||||||
|
include: {'combo_group.drift','string_table.drift'},
|
||||||
|
views: [CategoryTodoCountView,ComboGroupView],
|
||||||
|
)
|
||||||
|
class DatastoreDb extends _\$DatastoreDb {
|
||||||
|
DatastoreDb(super.e);
|
||||||
|
@override
|
||||||
|
int get schemaVersion => 1;
|
||||||
|
}
|
||||||
|
''',
|
||||||
|
'a|lib/drift/combo_group.drift': '''
|
||||||
|
CREATE TABLE [COMBO_GROUP](
|
||||||
|
[ComboGroupID] [int] NOT NULL PRIMARY KEY,
|
||||||
|
[HierStrucID] [bigint] NULL,
|
||||||
|
[ObjectNumber] [int] NULL,
|
||||||
|
[NameID] [bigint] NULL,
|
||||||
|
[OptionBits] [nvarchar](8) NULL,
|
||||||
|
[SluIndex] [int] NULL,
|
||||||
|
[HhtSluIndex] [int] NULL);
|
||||||
|
''',
|
||||||
|
'a|lib/drift/string_table.drift': '''
|
||||||
|
CREATE TABLE [STRING_TABLE](
|
||||||
|
[StringID] [bigint] NOT NULL PRIMARY KEY,
|
||||||
|
[StringNumberID] [bigint] NULL,
|
||||||
|
[LangID] [int] NULL,
|
||||||
|
[IsVisible] [bit] NOT NULL DEFAULT ((1)),
|
||||||
|
[IsDeleted] [bit] NOT NULL DEFAULT ((0)),
|
||||||
|
[HierStrucID] [bigint] NULL,
|
||||||
|
[PosRef] [bigint] NULL,
|
||||||
|
[StringText] [nvarchar](128) NULL
|
||||||
|
);
|
||||||
|
''',
|
||||||
|
},
|
||||||
|
modularBuild: true,
|
||||||
|
options: BuilderOptions({'assume_correct_reference': true}),
|
||||||
|
logger: debugLogger
|
||||||
|
);
|
||||||
|
|
||||||
|
// var actual = utf8.decode(result.writer.assets[(result.writer.assets.keys
|
||||||
|
// .firstWhere((key) => key.path == ('lib/drift/datastore_db.drift.dart')))]!);
|
||||||
|
|
||||||
|
checkOutputs(
|
||||||
|
{
|
||||||
|
'a|lib/drift/datastore_db.drift.dart': decodedMatches(
|
||||||
|
allOf(
|
||||||
|
contains(r'attachedDatabase.selectOnly(attachedDatabase.comboGroup)'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'a|lib/drift/combo_group.drift.dart' : anything,
|
||||||
|
'a|lib/drift/string_table.drift.dart' : anything,
|
||||||
|
},
|
||||||
|
result.dartOutputs,
|
||||||
|
result.writer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
group('reports issues', () {
|
group('reports issues', () {
|
||||||
for (final fatalWarnings in [false, true]) {
|
for (final fatalWarnings in [false, true]) {
|
||||||
group('fatalWarnings: $fatalWarnings', () {
|
group('fatalWarnings: $fatalWarnings', () {
|
||||||
|
|
Loading…
Reference in New Issue