From 15001364c2ef052897d10b1ec3c26b656ed62484 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Wed, 10 Apr 2024 18:31:44 +0200 Subject: [PATCH 1/9] add geopoly support --- drift/lib/src/runtime/types/mapping.dart | 31 ++ drift_dev/lib/src/analysis/driver/driver.dart | 2 + drift_dev/lib/src/analysis/options.dart | 13 + .../resolver/drift/sqlparser/mapping.dart | 13 +- .../analysis/resolver/shared/dart_types.dart | 2 +- drift_dev/lib/src/analysis/results/dart.dart | 13 +- drift_dev/lib/src/analysis/results/query.dart | 32 +- drift_dev/lib/src/analysis/results/types.dart | 32 +- drift_dev/lib/src/analysis/serializer.dart | 25 +- .../lib/src/generated/analysis/options.g.dart | 1 + .../lib/src/writer/queries/query_writer.dart | 22 +- .../src/writer/tables/data_class_writer.dart | 13 +- .../lib/src/writer/tables/table_writer.dart | 14 +- drift_dev/lib/src/writer/writer.dart | 45 +-- .../analysis/resolver/dart/column_test.dart | 10 +- .../analysis/resolver/drift/table_test.dart | 12 +- .../migrations_example/lib/database.g.dart | 2 +- sqlparser/lib/sqlparser.dart | 1 + sqlparser/lib/src/analysis/types/data.dart | 10 + sqlparser/lib/src/engine/module/geopoly.dart | 292 ++++++++++++++++++ .../test/engine/module/geopoly_test.dart | 30 ++ 21 files changed, 542 insertions(+), 73 deletions(-) create mode 100644 sqlparser/lib/src/engine/module/geopoly.dart create mode 100644 sqlparser/test/engine/module/geopoly_test.dart diff --git a/drift/lib/src/runtime/types/mapping.dart b/drift/lib/src/runtime/types/mapping.dart index 26b1211d..0fec915e 100644 --- a/drift/lib/src/runtime/types/mapping.dart +++ b/drift/lib/src/runtime/types/mapping.dart @@ -81,6 +81,15 @@ final class SqlTypes { return dartValue.rawSqlValue; } + if (dartValue is GeopolyPolygon) { + switch (dartValue) { + case _StringGeopolyPolygon(:final value): + return value; + case _BlobGeopolyPolygon(:final value): + return value; + } + } + return dartValue; } @@ -534,3 +543,25 @@ final class _ByDialectType implements DialectAwareSqlType { return _selectType(context.typeMapping).sqlTypeName(context); } } + +/// https://www.sqlite.org/geopoly.html +/// In Geopoly, a polygon can be text or a blob +sealed class GeopolyPolygon { + const GeopolyPolygon._(); + + const factory GeopolyPolygon.text(String value) = _StringGeopolyPolygon; + + const factory GeopolyPolygon.blob(Uint8List value) = _BlobGeopolyPolygon; +} + +final class _StringGeopolyPolygon extends GeopolyPolygon { + final String value; + + const _StringGeopolyPolygon(this.value) : super._(); +} + +final class _BlobGeopolyPolygon extends GeopolyPolygon { + final Uint8List value; + + const _BlobGeopolyPolygon(this.value) : super._(); +} diff --git a/drift_dev/lib/src/analysis/driver/driver.dart b/drift_dev/lib/src/analysis/driver/driver.dart index 0be3d1ba..c9b0fdd8 100644 --- a/drift_dev/lib/src/analysis/driver/driver.dart +++ b/drift_dev/lib/src/analysis/driver/driver.dart @@ -83,6 +83,8 @@ class DriftAnalysisDriver { if (options.hasModule(SqlModule.rtree)) const RTreeExtension(), if (options.hasModule(SqlModule.spellfix1)) const Spellfix1Extension(), + if (options.hasModule(SqlModule.geopoly)) + const GeopolyExtension(), ], version: options.sqliteVersion, ), diff --git a/drift_dev/lib/src/analysis/options.dart b/drift_dev/lib/src/analysis/options.dart index fe298795..0c484920 100644 --- a/drift_dev/lib/src/analysis/options.dart +++ b/drift_dev/lib/src/analysis/options.dart @@ -400,6 +400,19 @@ enum SqlModule { rtree, spellfix1, + + /// The Geopoly module is an alternative interface to the R-Tree extension + /// that uses the GeoJSON notation (RFC-7946) + /// to describe two-dimensional polygons. + /// + /// Geopoly includes functions for detecting + /// when one polygon is contained within or overlaps with another, + /// for computing the area enclosed by a polygon + /// for doing linear transformations of polygons, + /// for rendering polygons as SVG, and other similar operations. + /// + /// See more: https://www.sqlite.org/geopoly.html + geopoly, } /// The possible values for the case of the table and column names. diff --git a/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart b/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart index 50a1d651..3b0e8bb8 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart @@ -76,9 +76,12 @@ class TypeMapping { var type = _driftTypeToParser(column.sqlType.builtin) .withNullable(column.nullable); - if (column.sqlType.isCustom) { - type = type.addHint(CustomTypeHint(column.sqlType.custom!)); - } + type = switch (column.sqlType) { + ColumnDriftType() => type, + ColumnCustomType(:final custom) => type.addHint(CustomTypeHint(custom)), + ColumnGeopolyPolygonType() => type.addHint(const IsGeopolyPolygon()), + }; + if (column.typeConverter case AppliedTypeConverter c) { type = type.addHint(TypeConverterHint(c)); } @@ -147,6 +150,10 @@ class TypeMapping { return ColumnType.custom(customHint.type); } + if (type.hint() != null) { + return const ColumnType.geopolyPolygon(); + } + return ColumnType.drift(_toDefaultType(type)); } } diff --git a/drift_dev/lib/src/analysis/resolver/shared/dart_types.dart b/drift_dev/lib/src/analysis/resolver/shared/dart_types.dart index 0a64ab0f..8d2f4821 100644 --- a/drift_dev/lib/src/analysis/resolver/shared/dart_types.dart +++ b/drift_dev/lib/src/analysis/resolver/shared/dart_types.dart @@ -488,7 +488,7 @@ DartType regularColumnType( extension on TypeProvider { DartType typeFor(ColumnType type, KnownDriftTypes knownTypes) { - if (type.custom case CustomColumnType custom) { + if (type case ColumnCustomType(:final custom)) { return custom.dartType; } diff --git a/drift_dev/lib/src/analysis/results/dart.dart b/drift_dev/lib/src/analysis/results/dart.dart index 5171595d..641d70f2 100644 --- a/drift_dev/lib/src/analysis/results/dart.dart +++ b/drift_dev/lib/src/analysis/results/dart.dart @@ -131,18 +131,21 @@ class AnnotatedDartCodeBuilder { void addDriftType(HasType hasType) { void addNonListType() { final converter = hasType.typeConverter; - final customType = hasType.sqlType.custom; if (converter != null) { final nullable = converter.canBeSkippedForNulls && hasType.nullable; addDartType(converter.dartType); if (nullable) addText('?'); - } else if (customType != null) { - addDartType(customType.dartType); - if (hasType.nullable) addText('?'); } else { - addTopLevel(dartTypeNames[hasType.sqlType.builtin]!); + switch (hasType.sqlType) { + case ColumnDriftType(): + addTopLevel(dartTypeNames[hasType.sqlType.builtin]!); + case ColumnCustomType(:final custom): + addDartType(custom.dartType); + case ColumnGeopolyPolygonType(:final dartType): + addTopLevel(dartType); + } if (hasType.nullable) addText('?'); } } diff --git a/drift_dev/lib/src/analysis/results/query.dart b/drift_dev/lib/src/analysis/results/query.dart index 014c18ba..db28749d 100644 --- a/drift_dev/lib/src/analysis/results/query.dart +++ b/drift_dev/lib/src/analysis/results/query.dart @@ -118,6 +118,7 @@ abstract class SqlQuery { final String name; AnalysisContext? get fromContext; + AstNode? get root; /// Whether this query was declared in a `.drift` file. @@ -474,6 +475,7 @@ class InferredResultSet { }); Iterable get scalarColumns => columns.whereType(); + Iterable get nestedResults => columns.whereType(); /// Whether a new class needs to be written to store the result of this query. @@ -747,7 +749,12 @@ final class ScalarResultColumn extends ResultColumn } int get _columnTypeCompatibilityHash { - return Object.hash(sqlType.builtin, sqlType.custom?.dartType); + final custom = switch (sqlType) { + ColumnDriftType() || ColumnGeopolyPolygonType() => null, + ColumnCustomType(:final custom) => custom, + }; + + return Object.hash(sqlType.builtin, custom?.dartType); } @override @@ -758,12 +765,29 @@ final class ScalarResultColumn extends ResultColumn @override bool isCompatibleTo(ResultColumn other) { - return other is ScalarResultColumn && + if (other is ScalarResultColumn && other.name == name && other.sqlType.builtin == sqlType.builtin && - other.sqlType.custom?.dartType == sqlType.custom?.dartType && other.nullable == nullable && - other.typeConverter == typeConverter; + other.typeConverter == typeConverter) { + // ok + } else { + return false; + } + + switch ((sqlType, other.sqlType)) { + case ( + ColumnCustomType(:final custom), + ColumnCustomType(custom: final otherCustom) + ): + if (custom.dartType != otherCustom.dartType) { + return false; + } + case _: + break; + } + + return true; } } diff --git a/drift_dev/lib/src/analysis/results/types.dart b/drift_dev/lib/src/analysis/results/types.dart index 54dd42c8..5333221c 100644 --- a/drift_dev/lib/src/analysis/results/types.dart +++ b/drift_dev/lib/src/analysis/results/types.dart @@ -36,7 +36,7 @@ abstract class HasType { /// appears where an array is expected or has a type converter applied to it. /// [HasType] is the interface for sql-typed elements and is implemented by /// columns. -class ColumnType { +sealed class ColumnType { /// The builtin drift type used by this column. /// /// Even though it's unused there, custom types also have this field set - @@ -45,13 +45,35 @@ class ColumnType { final DriftSqlType builtin; /// Details about the custom type, if one is present. - final CustomColumnType? custom; + // CustomColumnType? get custom => switch (this) { + // ColumnDriftType() || ColumnGeopolyPolygonType() => null, + // ColumnCustomType(:final custom) => custom, + // }; - bool get isCustom => custom != null; + const ColumnType._(this.builtin); - const ColumnType.drift(this.builtin) : custom = null; + const factory ColumnType.drift(DriftSqlType builtin) = ColumnDriftType; - ColumnType.custom(CustomColumnType this.custom) : builtin = DriftSqlType.any; + const factory ColumnType.custom(CustomColumnType custom) = ColumnCustomType; + + const factory ColumnType.geopolyPolygon() = ColumnGeopolyPolygonType; +} + +final class ColumnDriftType extends ColumnType { + const ColumnDriftType(super.builtin) : super._(); +} + +final class ColumnCustomType extends ColumnType { + final CustomColumnType custom; + + const ColumnCustomType(this.custom) : super._(DriftSqlType.any); +} + +final class ColumnGeopolyPolygonType extends ColumnType { + const ColumnGeopolyPolygonType() : super._(DriftSqlType.any); + + DartTopLevelSymbol get dartType => + DartTopLevelSymbol('GeopolyPolygon', AnnotatedDartCode.drift); } extension OperationOnTypes on HasType { diff --git a/drift_dev/lib/src/analysis/serializer.dart b/drift_dev/lib/src/analysis/serializer.dart index b27ff839..746ac661 100644 --- a/drift_dev/lib/src/analysis/serializer.dart +++ b/drift_dev/lib/src/analysis/serializer.dart @@ -198,17 +198,20 @@ class ElementSerializer { } Map _serializeColumnType(ColumnType type) { - final custom = type.custom; - - return { - if (custom != null) - 'custom': { - 'dart': _serializeType(custom.dartType), - 'expression': custom.expression.toJson(), - } - else - 'builtin': type.builtin.name, - }; + switch (type) { + case ColumnGeopolyPolygonType(): + case ColumnDriftType(): + return { + 'builtin': type.builtin.name, + }; + case ColumnCustomType(:final custom): + return { + 'custom': { + 'dart': _serializeType(custom.dartType), + 'expression': custom.expression.toJson(), + } + }; + } } Map _serializeColumn(DriftColumn column) { diff --git a/drift_dev/lib/src/generated/analysis/options.g.dart b/drift_dev/lib/src/generated/analysis/options.g.dart index 4e86b7c9..efea74e7 100644 --- a/drift_dev/lib/src/generated/analysis/options.g.dart +++ b/drift_dev/lib/src/generated/analysis/options.g.dart @@ -174,6 +174,7 @@ const _$SqlModuleEnumMap = { SqlModule.math: 'math', SqlModule.rtree: 'rtree', SqlModule.spellfix1: 'spellfix1', + SqlModule.geopoly: 'geopoly', }; const _$CaseFromDartToSqlEnumMap = { diff --git a/drift_dev/lib/src/writer/queries/query_writer.dart b/drift_dev/lib/src/writer/queries/query_writer.dart index fb5be6a3..ddc9da12 100644 --- a/drift_dev/lib/src/writer/queries/query_writer.dart +++ b/drift_dev/lib/src/writer/queries/query_writer.dart @@ -2,10 +2,10 @@ import 'package:drift/drift.dart'; import 'package:recase/recase.dart'; import 'package:sqlparser/sqlparser.dart' hide ResultColumn; -import '../../analysis/resolver/queries/nested_queries.dart'; -import '../../analysis/results/results.dart'; import '../../analysis/options.dart'; import '../../analysis/resolver/queries/explicit_alias_transformer.dart'; +import '../../analysis/resolver/queries/nested_queries.dart'; +import '../../analysis/results/results.dart'; import '../../utils/string_escaper.dart'; import '../writer.dart'; import 'result_set_writer.dart'; @@ -31,6 +31,7 @@ class QueryWriter { late final ExplicitAliasTransformer _transformer; final TextEmitter _emitter; + StringBuffer get _buffer => _emitter.buffer; DriftOptions get options => scope.writer.options; @@ -207,13 +208,15 @@ class QueryWriter { _emitter.dartCode(_emitter.innerColumnType(column.sqlType)); String code; - if (column.sqlType.isCustom) { - final method = isNullable ? 'readNullableWithType' : 'readWithType'; - final typeImpl = _emitter.dartCode(column.sqlType.custom!.expression); - code = 'row.$method<$rawDartType>($typeImpl, $dartLiteral)'; - } else { - final method = isNullable ? 'readNullable' : 'read'; - code = 'row.$method<$rawDartType>($dartLiteral)'; + switch (column.sqlType) { + case ColumnGeopolyPolygonType(): + case ColumnDriftType(): + final method = isNullable ? 'readNullable' : 'read'; + code = 'row.$method<$rawDartType>($dartLiteral)'; + case ColumnCustomType(:final custom): + final method = isNullable ? 'readNullableWithType' : 'readWithType'; + final typeImpl = _emitter.dartCode(custom.expression); + code = 'row.$method<$rawDartType>($typeImpl, $dartLiteral)'; } final converter = column.typeConverter; @@ -759,6 +762,7 @@ class _ExpandedDeclarationWriter { class _ExpandedVariableWriter { final SqlQuery query; final TextEmitter _emitter; + StringBuffer get _buffer => _emitter.buffer; _ExpandedVariableWriter(this.query, this._emitter); 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 289a8f06..25642540 100644 --- a/drift_dev/lib/src/writer/tables/data_class_writer.dart +++ b/drift_dev/lib/src/writer/tables/data_class_writer.dart @@ -13,6 +13,7 @@ class DataClassWriter { bool get isInsertable => table is DriftTable; final TextEmitter _emitter; + StringBuffer get _buffer => _emitter.buffer; DataClassWriter(this.table, this.scope) : _emitter = scope.leaf(); @@ -341,11 +342,13 @@ class RowMappingWriter { final columnName = column.nameInSql; final rawData = "data['\${effectivePrefix}$columnName']"; - String sqlType; - if (column.sqlType.custom case CustomColumnType custom) { - sqlType = writer.dartCode(custom.expression); - } else { - sqlType = writer.drift(column.sqlType.builtin.toString()); + final String sqlType; + switch (column.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + sqlType = writer.drift(column.sqlType.builtin.toString()); + case ColumnCustomType(:final custom): + sqlType = writer.dartCode(custom.expression); } var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)'; diff --git a/drift_dev/lib/src/writer/tables/table_writer.dart b/drift_dev/lib/src/writer/tables/table_writer.dart index 2ea4b6c4..f4c9baab 100644 --- a/drift_dev/lib/src/writer/tables/table_writer.dart +++ b/drift_dev/lib/src/writer/tables/table_writer.dart @@ -11,6 +11,7 @@ import 'update_companion_writer.dart'; /// Both classes need to generate column getters and a mapping function. abstract class TableOrViewWriter { DriftElementWithResultSet get tableOrView; + TextEmitter get emitter; StringBuffer get buffer => emitter.buffer; @@ -210,12 +211,13 @@ abstract class TableOrViewWriter { } } - if (column.sqlType.isCustom) { - additionalParams['type'] = - emitter.dartCode(column.sqlType.custom!.expression); - } else { - additionalParams['type'] = - emitter.drift(column.sqlType.builtin.toString()); + switch (column.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + additionalParams['type'] = + emitter.drift(column.sqlType.builtin.toString()); + case ColumnCustomType(:final custom): + additionalParams['type'] = emitter.dartCode(custom.expression); } if (isRequiredForInsert != null) { diff --git a/drift_dev/lib/src/writer/writer.dart b/drift_dev/lib/src/writer/writer.dart index a6b36dfc..6b9c1ba0 100644 --- a/drift_dev/lib/src/writer/writer.dart +++ b/drift_dev/lib/src/writer/writer.dart @@ -26,6 +26,7 @@ class Writer extends _NodeOrWriter { final GenerationOptions generationOptions; TextEmitter get header => _header; + TextEmitter get imports => _imports; @override @@ -51,6 +52,7 @@ class Writer extends _NodeOrWriter { } Scope child() => _root.child(); + TextEmitter leaf() => _root.leaf(); } @@ -142,12 +144,13 @@ abstract class _NodeOrWriter { return AnnotatedDartCode.build((b) { AnnotatedDartCode sqlDartType; - if (converter.sqlType.isCustom) { - sqlDartType = - AnnotatedDartCode.type(converter.sqlType.custom!.dartType); - } else { - sqlDartType = - AnnotatedDartCode([dartTypeNames[converter.sqlType.builtin]!]); + switch (converter.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + sqlDartType = + AnnotatedDartCode([dartTypeNames[converter.sqlType.builtin]!]); + case ColumnCustomType(:final custom): + sqlDartType = AnnotatedDartCode.type(custom.dartType); } final className = converter.alsoAppliesToJsonConversion @@ -204,12 +207,13 @@ abstract class _NodeOrWriter { /// This type does not respect type converters or arrays. AnnotatedDartCode innerColumnType(ColumnType type, {bool nullable = false}) { return AnnotatedDartCode.build((b) { - final custom = type.custom; - - if (custom != null) { - b.addDartType(custom.dartType); - } else { - b.addTopLevel(dartTypeNames[type.builtin]!); + switch (type) { + case ColumnGeopolyPolygonType(:final dartType): + b.addTopLevel(dartType); + case ColumnDriftType(): + b.addTopLevel(dartTypeNames[type.builtin]!); + case ColumnCustomType(:final custom): + b.addDartType(custom.dartType); } if (nullable) { @@ -240,12 +244,16 @@ abstract class _NodeOrWriter { b.addCode(expression); } - if (column.sqlType.isCustom) { - // Also specify the custom type since it can't be inferred from the - // value passed to the variable. - b - ..addText(', ') - ..addCode(column.sqlType.custom!.expression); + switch (column.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + break; + case ColumnCustomType(:final custom): + // Also specify the custom type since it can't be inferred from the + // value passed to the variable. + b + ..addText(', ') + ..addCode(custom.expression); } b.addText(')'); @@ -421,6 +429,7 @@ class TextEmitter extends _Node { TextEmitter(Scope super.parent) : writer = parent.writer; void write(Object? object) => buffer.write(object); + void writeln(Object? object) => buffer.writeln(object); void writeUriRef(Uri definition, String element) { diff --git a/drift_dev/test/analysis/resolver/dart/column_test.dart b/drift_dev/test/analysis/resolver/dart/column_test.dart index 2e45c5ce..d21370c5 100644 --- a/drift_dev/test/analysis/resolver/dart/column_test.dart +++ b/drift_dev/test/analysis/resolver/dart/column_test.dart @@ -254,8 +254,14 @@ class TestTable extends Table { final column = table.columns.single; expect(column.sqlType.builtin, DriftSqlType.any); - expect(column.sqlType.custom?.dartType.toString(), 'List'); - expect(column.sqlType.custom?.expression.toString(), 'StringArrayType()'); + switch (column.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + break; + case ColumnCustomType(:final custom): + expect(custom.dartType.toString(), 'List'); + expect(custom.expression.toString(), 'StringArrayType()'); + } }); group('customConstraint analysis', () { diff --git a/drift_dev/test/analysis/resolver/drift/table_test.dart b/drift_dev/test/analysis/resolver/drift/table_test.dart index cf81d1ae..09d251d8 100644 --- a/drift_dev/test/analysis/resolver/drift/table_test.dart +++ b/drift_dev/test/analysis/resolver/drift/table_test.dart @@ -1,6 +1,7 @@ import 'package:drift/drift.dart' show DriftSqlType; import 'package:drift_dev/src/analysis/results/column.dart'; import 'package:drift_dev/src/analysis/results/table.dart'; +import 'package:drift_dev/src/analysis/results/types.dart'; import 'package:test/test.dart'; import '../../test_utils.dart'; @@ -286,8 +287,13 @@ class MyType implements CustomSqlType {} final table = file.analyzedElements.single as DriftTable; final column = table.columns.single; - expect(column.sqlType.isCustom, isTrue); - expect(column.sqlType.custom?.dartType.toString(), 'String'); - expect(column.sqlType.custom?.expression.toString(), 'MyType()'); + switch (column.sqlType) { + case ColumnDriftType(): + case ColumnGeopolyPolygonType(): + fail('expect custom type'); + case ColumnCustomType(:final custom): + expect(custom.dartType.toString(), 'String'); + expect(custom.expression.toString(), 'MyType()'); + } }); } diff --git a/examples/migrations_example/lib/database.g.dart b/examples/migrations_example/lib/database.g.dart index f0656955..6e810776 100644 --- a/examples/migrations_example/lib/database.g.dart +++ b/examples/migrations_example/lib/database.g.dart @@ -282,7 +282,7 @@ class Groups extends Table with TableInfo { 'deleted', aliasedName, true, type: DriftSqlType.bool, requiredDuringInsert: false, - $customConstraints: 'DEFAULT FALSE', + $customConstraints: 'NULL DEFAULT FALSE', defaultValue: const CustomExpression('FALSE')); static const VerificationMeta _ownerMeta = const VerificationMeta('owner'); late final GeneratedColumn owner = GeneratedColumn( diff --git a/sqlparser/lib/sqlparser.dart b/sqlparser/lib/sqlparser.dart index 6fb44d39..b0fc9f44 100644 --- a/sqlparser/lib/sqlparser.dart +++ b/sqlparser/lib/sqlparser.dart @@ -5,6 +5,7 @@ export 'src/analysis/analysis.dart'; export 'src/analysis/types/join_analysis.dart'; export 'src/ast/ast.dart'; export 'src/engine/module/fts5.dart' show Fts5Extension, Fts5Table; +export 'src/engine/module/geopoly.dart' show GeopolyExtension; export 'src/engine/module/json1.dart' show Json1Extension; export 'src/engine/module/math.dart' show BuiltInMathExtension; export 'src/engine/module/rtree.dart' show RTreeExtension; diff --git a/sqlparser/lib/src/analysis/types/data.dart b/sqlparser/lib/src/analysis/types/data.dart index 33afe39c..a028f4ee 100644 --- a/sqlparser/lib/src/analysis/types/data.dart +++ b/sqlparser/lib/src/analysis/types/data.dart @@ -41,6 +41,7 @@ class ResolvedType { this.hints = const [], this.nullable = false, this.isArray = false}); + const ResolvedType.bool({bool? nullable = false}) : this( type: BasicType.int, @@ -111,6 +112,7 @@ abstract class TypeHint { @override int get hashCode => runtimeType.hashCode; + @override bool operator ==(dynamic other) => other.runtimeType == runtimeType; } @@ -131,6 +133,12 @@ class IsBigInt extends TypeHint { const IsBigInt(); } +/// This could be a `blob` or `text` depending on the context +/// https://www.sqlite.org/geopoly.html +class IsGeopolyPolygon extends TypeHint { + const IsGeopolyPolygon(); +} + /// Result of resolving a type. This can either have the resolved [type] set, /// or it can inform the called that it [needsContext] to resolve the type /// properly. Failure to resolve the type will have the [unknown] flag set. @@ -152,10 +160,12 @@ class ResolveResult { const ResolveResult(this.type) : needsContext = false, unknown = false; + const ResolveResult.needsContext() : type = null, needsContext = true, unknown = false; + const ResolveResult.unknown() : type = null, needsContext = false, diff --git a/sqlparser/lib/src/engine/module/geopoly.dart b/sqlparser/lib/src/engine/module/geopoly.dart new file mode 100644 index 00000000..7bb6c80b --- /dev/null +++ b/sqlparser/lib/src/engine/module/geopoly.dart @@ -0,0 +1,292 @@ +import 'package:sqlparser/src/analysis/analysis.dart'; +import 'package:sqlparser/src/ast/ast.dart'; +import 'package:sqlparser/src/engine/module/module.dart'; +import 'package:sqlparser/src/engine/sql_engine.dart'; +import 'package:sqlparser/src/reader/tokenizer/token.dart'; + +final class GeopolyExtension implements Extension { + const GeopolyExtension(); + + @override + void register(SqlEngine engine) { + engine + ..registerModule(_GeopolyModule(engine)) + ..registerFunctionHandler(_GeopolyFunctionHandler()); + } +} + +const String _shapeKeyword = '_shape'; +const ResolvedType _typePolygon = ResolvedType( + type: BasicType.blob, + nullable: true, + hints: [ + IsGeopolyPolygon(), + ], +); + +final class _GeopolyModule extends Module { + _GeopolyModule(this.engine) : super('geopoly'); + + final SqlEngine engine; + + @override + Table parseTable(CreateVirtualTableStatement stmt) { + final resolvedColumns = [ + RowId(), + TableColumn( + _shapeKeyword, + _typePolygon, + ), + ]; + + for (final column in stmt.argumentContent) { + final tokens = engine.tokenize(column); + + final String resolvedName; + final ResolvedType resolvedType; + switch (tokens) { + // geoID INTEGER not null + case [final name, final type, final not, final $null, final eof] + when name.type == TokenType.identifier && + type.type == TokenType.identifier && + not.type == TokenType.not && + $null.type == TokenType.$null && + eof.type == TokenType.eof: + resolvedName = name.lexeme; + resolvedType = engine.schemaReader + .resolveColumnType(type.lexeme) + .withNullable(false); + // a INTEGER + case [final name, final type, final eof] + when name.type == TokenType.identifier && + type.type == TokenType.identifier && + eof.type == TokenType.eof: + resolvedName = name.lexeme; + resolvedType = engine.schemaReader + .resolveColumnType(type.lexeme) + .withNullable(true); + // b + case [final name, final eof] + when name.type == TokenType.identifier && eof.type == TokenType.eof: + resolvedName = name.lexeme; + resolvedType = const ResolvedType( + type: BasicType.any, + nullable: true, + ); + // ? + default: + throw ArgumentError('Can\'t be parsed', column); + } + + resolvedColumns.add( + TableColumn( + resolvedName, + resolvedType, + ), + ); + } + + return Table( + name: stmt.tableName, + resolvedColumns: resolvedColumns, + definition: stmt, + isVirtual: true, + ); + } +} + +final class _GeopolyFunctionHandler extends FunctionHandler { + @override + Set get functionNames => { + for (final value in _GeopolyFunctions.values) value.sqlName, + }; + + @override + ResolveResult inferArgumentType( + AnalysisContext context, + SqlInvocation call, + Expression argument, + ) { + // TODO(nikitadol): Copy from `_Fts5Functions`. Must be removed when argument index appears + int? argumentIndex; + if (call.parameters is ExprFunctionParameters) { + argumentIndex = (call.parameters as ExprFunctionParameters) + .parameters + .indexOf(argument); + } + if (argumentIndex == null || argumentIndex < 0) { + // couldn't find expression in arguments, so we don't know the type + return const ResolveResult.unknown(); + } + // + + final func = _GeopolyFunctions.bySqlName(call.name); + + if (argumentIndex < func.args.length) { + return ResolveResult(func.args[argumentIndex]); + } else if (func.otherArgs != null) { + return ResolveResult(func.otherArgs); + } else { + return ResolveResult.unknown(); + } + } + + @override + ResolveResult inferReturnType( + AnalysisContext context, + SqlInvocation call, + List expandedArgs, + ) { + final func = _GeopolyFunctions.bySqlName(call.name); + + if (expandedArgs.length == func.args.length) { + // ok + } else if (expandedArgs.length > func.args.length && + func.otherArgs != null) { + // ok + } else { + final buffer = StringBuffer( + 'The function `${func.sqlName}` takes ', + ); + + buffer.write('${func.args.length} '); + + switch (func.args.length) { + case 1: + buffer.write('argument'); + case > 1: + buffer.write('arguments'); + } + + if (func.otherArgs != null) { + buffer.write(' (or more)'); + } + + buffer.write(' but ${expandedArgs.length} '); + + switch (expandedArgs.length) { + case 1: + buffer.write('argument is'); + case > 1: + buffer.write('arguments are'); + } + buffer.write('passed'); + + throw ArgumentError(buffer); + } + + return ResolveResult( + func.returnType, + ); + } +} + +const _typeInt = ResolvedType( + type: BasicType.int, + nullable: true, +); + +const _typeReal = ResolvedType( + type: BasicType.real, + nullable: true, +); + +const _typeBlob = ResolvedType( + type: BasicType.blob, + nullable: true, +); + +const _typeText = ResolvedType( + type: BasicType.text, + nullable: true, +); + +enum _GeopolyFunctions { + overlap( + 'geopoly_overlap', + _typeInt, + [_typePolygon, _typePolygon], + ), + within( + 'geopoly_within', + _typeInt, + [_typePolygon, _typePolygon], + ), + area( + 'geopoly_area', + _typeReal, + [_typePolygon], + ), + blob( + 'geopoly_blob', + _typeBlob, + [_typePolygon], + ), + json( + 'geopoly_json', + _typeText, + [_typePolygon], + ), + svg( + 'geopoly_svg', + _typeText, + [_typePolygon], + _typeText, + ), + bbox( + 'geopoly_bbox', + _typeBlob, + [_typePolygon], + ), + groupBbox( + 'geopoly_group_bbox', + _typeBlob, + [_typePolygon], + ), + containsPoint( + 'geopoly_contains_point', + _typeInt, + [_typePolygon, _typeInt, _typeInt], + ), + xform( + 'geopoly_xform', + _typeBlob, + [ + _typePolygon, + _typeReal, + _typeReal, + _typeReal, + _typeReal, + _typeReal, + _typeReal + ], + ), + regular( + 'geopoly_regular', + _typeBlob, + [_typeReal, _typeReal, _typeReal, _typeInt], + ), + ccw( + 'geopoly_ccw', + _typeBlob, + [_typePolygon], + ); + + final String sqlName; + final ResolvedType returnType; + final List args; + final ResolvedType? otherArgs; + + const _GeopolyFunctions( + this.sqlName, + this.returnType, + this.args, [ + this.otherArgs, + ]); + + factory _GeopolyFunctions.bySqlName(String sqlName) { + return _GeopolyFunctions.values.firstWhere( + (element) => element.sqlName == sqlName, + orElse: () => throw ArgumentError('$sqlName not exists')); + } +} diff --git a/sqlparser/test/engine/module/geopoly_test.dart b/sqlparser/test/engine/module/geopoly_test.dart new file mode 100644 index 00000000..5eaa14de --- /dev/null +++ b/sqlparser/test/engine/module/geopoly_test.dart @@ -0,0 +1,30 @@ +import 'package:sqlparser/sqlparser.dart'; +import 'package:test/test.dart'; + +final _geopolyOptions = EngineOptions( + enabledExtensions: const [ + GeopolyExtension(), + ], +); + +void main() { + group('creating geopoly tables', () { + final engine = SqlEngine(_geopolyOptions); + + test('can create geopoly table', () { + final result = engine.analyze( + '''CREATE VIRTUAL TABLE geo USING geopoly(a integer not null, b integer, c);'''); + + final table = const SchemaFromCreateTable() + .read(result.root as TableInducingStatement); + + expect(table.name, 'geo'); + final columns = table.resultColumns; + expect(columns, hasLength(4)); + expect(columns[0].type.type, equals(BasicType.blob)); + expect(columns[1].type.type, equals(BasicType.int)); + expect(columns[2].type.type, equals(BasicType.int)); + expect(columns[3].type.type, equals(BasicType.any)); + }); + }); +} From bf8da493bf805800f0a7950987cc4c5231d21776 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Wed, 10 Apr 2024 18:49:22 +0200 Subject: [PATCH 2/9] `dart format .` --- drift_dev/lib/src/analysis/driver/driver.dart | 3 +-- extras/benchmarks/bin/benchmarks.dart | 2 +- extras/benchmarks/lib/src/moor/cache_prepared_statements.dart | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drift_dev/lib/src/analysis/driver/driver.dart b/drift_dev/lib/src/analysis/driver/driver.dart index c9b0fdd8..72cf604c 100644 --- a/drift_dev/lib/src/analysis/driver/driver.dart +++ b/drift_dev/lib/src/analysis/driver/driver.dart @@ -83,8 +83,7 @@ class DriftAnalysisDriver { if (options.hasModule(SqlModule.rtree)) const RTreeExtension(), if (options.hasModule(SqlModule.spellfix1)) const Spellfix1Extension(), - if (options.hasModule(SqlModule.geopoly)) - const GeopolyExtension(), + if (options.hasModule(SqlModule.geopoly)) const GeopolyExtension(), ], version: options.sqliteVersion, ), diff --git a/extras/benchmarks/bin/benchmarks.dart b/extras/benchmarks/bin/benchmarks.dart index b8a25bcb..e8c4fb6a 100644 --- a/extras/benchmarks/bin/benchmarks.dart +++ b/extras/benchmarks/bin/benchmarks.dart @@ -25,7 +25,7 @@ Future main() async { output.writeAsStringSync(json.encode(tracker.timings)); - // Make sure the process exits. Otherwise, unclosed resources from the + // Make sure the process exits. Otherwise, unclosed resources from the // benchmarks will keep the process alive. exit(0); } diff --git a/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart b/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart index 24edca0d..ddc25e14 100644 --- a/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart +++ b/extras/benchmarks/lib/src/moor/cache_prepared_statements.dart @@ -18,7 +18,7 @@ SELECT * FROM key_values WHERE value IN (SELECT value FROM key_values WHERE valu '''; final fs = []; - + for (var i = 0; i < _numQueries; i++) { fs.add( _db.customSelect(queryToBench, variables: [Variable(uuid.v4())]).get(), From 349787ad4b69f267262f38869b1b650f8ede81d7 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Wed, 10 Apr 2024 19:09:38 +0200 Subject: [PATCH 3/9] add geopoly to docs --- docs/pages/docs/SQL API/extensions.md | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/pages/docs/SQL API/extensions.md b/docs/pages/docs/SQL API/extensions.md index d23c8bf9..a91188cb 100644 --- a/docs/pages/docs/SQL API/extensions.md +++ b/docs/pages/docs/SQL API/extensions.md @@ -78,3 +78,35 @@ The `bm25`, `highlight` and `snippet` functions from fts5 can also be used in cu It's not possible to declare fts5 tables, or queries on fts5 tables, in Dart. You can learn more about the fts5 extension on [sqlite.org](https://www.sqlite.org/fts5.html). + +## geopoly +The Geopoly module is an alternative interface to the [R-Tree](https://www.sqlite.org/rtree.html) extension +that uses the [GeoJSON](https://geojson.org/) notation ([RFC-7946](https://datatracker.ietf.org/doc/html/rfc7946)) +to describe two-dimensional polygons. +Geopoly includes functions for detecting when one polygon is contained within or overlaps with another, +for computing the area enclosed by a polygon, +for doing linear transformations of polygons, +for rendering polygons as [SVG](https://en.wikipedia.org/wiki/SVG), +and other similar operations. + +To enable the fts5 extension in drift files and compiled queries, modify the +[build options]({{ "../Generation options/index.md" | pageUrl }}) to include +`geopoly` in the `sqlite_module` section. + +An example of creating a virtual table using this extension: +```sql +create virtual table geo using geopoly(geoID, a, b); +``` +Sqlite will accept any types in additional columns (`geoID`, `a`, `b` from the example above), +so `drift` will generate a `DriftAny` type for these columns, which is not always convenient. +To avoid this, you can add types as in this example: +```sql +create virtual table geo using geopoly ( + geoID INTEGER not null, + a INTEGER, + b +); +``` +This will add hints to column types and then the Dart code will be more convenient to use + +You can learn more about the geopoly extension on [sqlite.org](https://www.sqlite.org/geopoly.html). From 5e5628d1a81882b0d2345fdf32b11f4efbba13ae Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Sun, 14 Apr 2024 23:51:29 +0200 Subject: [PATCH 4/9] move geopoly to extensions --- drift/lib/extensions/geopoly.dart | 71 +++++++++++++++++++ drift/lib/src/drift_dev_helper.dart | 2 + drift/lib/src/runtime/types/mapping.dart | 33 +-------- drift_dev/lib/src/analysis/driver/driver.dart | 35 ++++++--- .../src/analysis/resolver/dart/column.dart | 2 +- .../src/analysis/resolver/dart/helper.dart | 12 ++-- .../lib/src/analysis/resolver/dart/view.dart | 2 +- .../lib/src/analysis/resolver/discover.dart | 3 +- .../resolver/drift/element_resolver.dart | 8 +-- .../resolver/drift/sqlparser/mapping.dart | 13 +++- .../src/analysis/resolver/drift/table.dart | 2 +- .../lib/src/analysis/resolver/drift/view.dart | 2 +- .../src/analysis/resolver/file_analysis.dart | 2 +- drift_dev/lib/src/analysis/results/dart.dart | 2 - drift_dev/lib/src/analysis/results/query.dart | 2 +- drift_dev/lib/src/analysis/results/types.dart | 15 ---- drift_dev/lib/src/analysis/serializer.dart | 15 ++-- .../backends/analyzer_context_backend.dart | 6 +- .../lib/src/backends/build/analyzer.dart | 4 +- .../lib/src/backends/build/drift_builder.dart | 40 +++++++---- .../src/services/schema/sqlite_to_drift.dart | 2 +- .../lib/src/writer/queries/query_writer.dart | 1 - .../src/writer/tables/data_class_writer.dart | 1 - .../lib/src/writer/tables/table_writer.dart | 1 - drift_dev/lib/src/writer/writer.dart | 4 -- drift_dev/test/analysis/generic_test.dart | 16 ++--- drift_dev/test/analysis/options_test.dart | 2 +- .../test/analysis/preprocess_drift_test.dart | 16 ++--- drift_dev/test/analysis/repro_1280_test.dart | 2 +- drift_dev/test/analysis/repro_967_test.dart | 2 +- .../resolver/dart/column_references_test.dart | 12 ++-- .../analysis/resolver/dart/column_test.dart | 27 ++++--- .../dart/custom_row_classes_test.dart | 10 +-- .../resolver/dart/database_parser_test.dart | 10 +-- .../resolver/dart/enum_columns_test.dart | 2 +- .../resolver/dart/foreign_key_test.dart | 10 +-- .../analysis/resolver/dart/index_test.dart | 4 +- .../resolver/dart/invalid_column_test.dart | 2 +- .../resolver/dart/regression_1172_test.dart | 2 +- .../resolver/dart/table_parser_test.dart | 4 +- .../analysis/resolver/dart/table_test.dart | 12 ++-- .../resolver/dart/type_converter_test.dart | 4 +- .../resolver/dart/unique_columns_test.dart | 10 +-- .../analysis/resolver/dart/view_test.dart | 2 +- .../test/analysis/resolver/discover_test.dart | 20 +++--- .../resolver/drift/create_view_test.dart | 20 +++--- .../analysis/resolver/drift/cte_test.dart | 6 +- .../drift/custom_row_classes_test.dart | 8 +-- ...errors_when_importing_part_files_test.dart | 2 +- .../resolver/drift/ffi_extension_test.dart | 2 +- .../analysis/resolver/drift/fts5_test.dart | 8 +-- .../resolver/drift/regression_1138_test.dart | 2 +- .../resolver/drift/regression_2097_test.dart | 2 +- .../resolver/drift/regression_754_test.dart | 2 +- .../resolver/drift/sqlite_version_test.dart | 4 +- .../sqlparser/type_converter_lints_test.dart | 4 +- .../analysis/resolver/drift/table_test.dart | 17 +++-- .../resolver/drift/virtual_table_test.dart | 6 +- .../queries/custom_class_name_test.dart | 2 +- .../queries/existing_row_classes_test.dart | 54 +++++++------- .../resolver/queries/inference_test.dart | 2 +- .../resolver/queries/linter_test.dart | 14 ++-- .../queries/queries_from_views_test.dart | 2 +- .../resolver/queries/query_analyzer_test.dart | 39 +++++----- .../test/analysis/resolver/resolver_test.dart | 14 ++-- .../test/analysis/results/dart_test.dart | 2 +- drift_dev/test/analysis/test_utils.dart | 40 ++++++++--- .../find_stream_update_rules_test.dart | 4 +- .../test/services/schema/reader_test.dart | 2 +- .../test/services/schema/writer_test.dart | 2 +- .../writer/queries/query_writer_test.dart | 4 +- 71 files changed, 394 insertions(+), 319 deletions(-) create mode 100644 drift/lib/extensions/geopoly.dart diff --git a/drift/lib/extensions/geopoly.dart b/drift/lib/extensions/geopoly.dart new file mode 100644 index 00000000..fbc401aa --- /dev/null +++ b/drift/lib/extensions/geopoly.dart @@ -0,0 +1,71 @@ +/// https://www.sqlite.org/geopoly.html +/// The Geopoly Interface To The SQLite R*Tree Module + +library geopoly; + +import 'dart:typed_data'; + +import '../src/runtime/query_builder/query_builder.dart'; +import '../src/runtime/types/mapping.dart'; + +/// +final class GeopolyPolygonType implements CustomSqlType { + /// + const GeopolyPolygonType(); + + @override + String mapToSqlLiteral(GeopolyPolygon dartValue) { + throw UnimplementedError(); + } + + @override + Object mapToSqlParameter(GeopolyPolygon dartValue) { + switch (dartValue) { + case GeopolyPolygonString(:final value): + return value; + case GeopolyPolygonBlob(:final value): + return value; + } + } + + @override + GeopolyPolygon read(Object fromSql) { + return switch (fromSql) { + Uint8List() => GeopolyPolygon.blob(fromSql), + String() => GeopolyPolygon.text(fromSql), + _ => throw UnimplementedError(), + }; + } + + @override + String sqlTypeName(GenerationContext context) { + throw UnimplementedError(); + } +} + +/// In Geopoly, a polygon can be text or a blob +sealed class GeopolyPolygon { + const GeopolyPolygon._(); + + const factory GeopolyPolygon.text(String value) = GeopolyPolygonString; + + const factory GeopolyPolygon.blob(Uint8List value) = GeopolyPolygonBlob; +} + +/// +final class GeopolyPolygonString extends GeopolyPolygon { + /// + final String value; + + /// + const GeopolyPolygonString(this.value) : super._(); +} + +/// +final class GeopolyPolygonBlob extends GeopolyPolygon { + /// + final Uint8List value; + + /// + const GeopolyPolygonBlob(this.value) : super._(); +} diff --git a/drift/lib/src/drift_dev_helper.dart b/drift/lib/src/drift_dev_helper.dart index 5a374855..9f043ac2 100644 --- a/drift/lib/src/drift_dev_helper.dart +++ b/drift/lib/src/drift_dev_helper.dart @@ -7,3 +7,5 @@ export 'runtime/query_builder/query_builder.dart' show TableInfo; export 'dsl/dsl.dart' show Table, TableIndex, View, DriftDatabase, DriftAccessor; + +export '../extensions/geopoly.dart'; diff --git a/drift/lib/src/runtime/types/mapping.dart b/drift/lib/src/runtime/types/mapping.dart index e9c83c4a..20018220 100644 --- a/drift/lib/src/runtime/types/mapping.dart +++ b/drift/lib/src/runtime/types/mapping.dart @@ -1,5 +1,5 @@ -import 'dart:core'; import 'dart:core' as core; +import 'dart:core'; import 'dart:typed_data'; import 'package:collection/collection.dart'; @@ -81,15 +81,6 @@ final class SqlTypes { return dartValue.rawSqlValue; } - if (dartValue is GeopolyPolygon) { - switch (dartValue) { - case _StringGeopolyPolygon(:final value): - return value; - case _BlobGeopolyPolygon(:final value): - return value; - } - } - return dartValue; } @@ -551,25 +542,3 @@ final class _ByDialectType implements DialectAwareSqlType { return _selectType(context.typeMapping).sqlTypeName(context); } } - -/// https://www.sqlite.org/geopoly.html -/// In Geopoly, a polygon can be text or a blob -sealed class GeopolyPolygon { - const GeopolyPolygon._(); - - const factory GeopolyPolygon.text(String value) = _StringGeopolyPolygon; - - const factory GeopolyPolygon.blob(Uint8List value) = _BlobGeopolyPolygon; -} - -final class _StringGeopolyPolygon extends GeopolyPolygon { - final String value; - - const _StringGeopolyPolygon(this.value) : super._(); -} - -final class _BlobGeopolyPolygon extends GeopolyPolygon { - final Uint8List value; - - const _BlobGeopolyPolygon(this.value) : super._(); -} diff --git a/drift_dev/lib/src/analysis/driver/driver.dart b/drift_dev/lib/src/analysis/driver/driver.dart index 72cf604c..4992a3b9 100644 --- a/drift_dev/lib/src/analysis/driver/driver.dart +++ b/drift_dev/lib/src/analysis/driver/driver.dart @@ -1,11 +1,12 @@ import 'dart:convert'; import 'package:analyzer/dart/element/element.dart'; +import 'package:meta/meta.dart'; import 'package:sqlparser/sqlparser.dart'; -import '../options.dart'; import '../backend.dart'; import '../drift_native_functions.dart'; +import '../options.dart'; import '../resolver/dart/helper.dart'; import '../resolver/discover.dart'; import '../resolver/drift/sqlparser/mapping.dart'; @@ -62,10 +63,30 @@ class DriftAnalysisDriver { AnalysisResultCacheReader? cacheReader; - KnownDriftTypes? _knownTypes; + final KnownDriftTypes knownTypes; - DriftAnalysisDriver(this.backend, this.options, {bool isTesting = false}) - : _isTesting = isTesting; + @visibleForTesting + DriftAnalysisDriver( + this.backend, + this.options, + this.knownTypes, { + bool isTesting = false, + }) : _isTesting = isTesting; + + static Future init( + DriftBackend backend, + DriftOptions options, { + bool isTesting = false, + }) async { + final driver = DriftAnalysisDriver( + backend, + options, + await KnownDriftTypes.resolve(backend), + isTesting: isTesting, + ); + + return driver; + } SqlEngine newSqlEngine() { return SqlEngine( @@ -90,11 +111,6 @@ class DriftAnalysisDriver { ); } - /// Loads types important for Drift analysis. - Future loadKnownTypes() async { - return _knownTypes ??= await KnownDriftTypes.resolve(this); - } - /// For a given file under [uri], attempts to restore serialized analysis /// results that have been stored before. /// @@ -359,6 +375,7 @@ abstract class AnalysisResultCacheReader { Future readDiscovery(Uri uri); Future readTypeHelperFor(Uri uri); + Future readElementCacheFor(Uri uri); } diff --git a/drift_dev/lib/src/analysis/resolver/dart/column.dart b/drift_dev/lib/src/analysis/resolver/dart/column.dart index ef9b9e4a..32a9adbc 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/column.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/column.dart @@ -344,7 +344,7 @@ class ColumnParser { .apply(getter.name.lexeme); ColumnType columnType; - final helper = await _resolver.resolver.driver.loadKnownTypes(); + final helper = _resolver.resolver.driver.knownTypes; if (foundStartMethod == _startCustom) { final expression = remainingExpr.argumentList.arguments.single; diff --git a/drift_dev/lib/src/analysis/resolver/dart/helper.dart b/drift_dev/lib/src/analysis/resolver/dart/helper.dart index 88dfe6c8..f73659ad 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/helper.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/helper.dart @@ -7,7 +7,7 @@ import 'package:analyzer/dart/element/type_provider.dart'; import 'package:analyzer/dart/element/type_system.dart'; import 'package:collection/collection.dart'; -import '../../driver/driver.dart'; +import '../../backend.dart'; import '../../driver/error.dart'; import '../../results/results.dart'; import '../resolver.dart'; @@ -32,6 +32,7 @@ class KnownDriftTypes { final InterfaceElement jsonTypeConverter; final InterfaceType driftAny; final InterfaceType uint8List; + final InterfaceType geopolyPolygon; KnownDriftTypes._( this.helperLibrary, @@ -47,6 +48,7 @@ class KnownDriftTypes { this.driftAccessor, this.driftAny, this.uint8List, + this.geopolyPolygon, ); /// Constructs the set of known drift types from a helper library, which is @@ -73,6 +75,8 @@ class KnownDriftTypes { .defaultInstantiation, (exportNamespace.get('Uint8List') as InterfaceElement) .defaultInstantiation, + (exportNamespace.get('GeopolyPolygon') as InterfaceElement) + .defaultInstantiation, ); } @@ -98,8 +102,8 @@ class KnownDriftTypes { return type?.asInstanceOf(converter); } - static Future resolve(DriftAnalysisDriver driver) async { - final library = await driver.backend.readDart(uri); + static Future resolve(DriftBackend backend) async { + final library = await backend.readDart(uri); return KnownDriftTypes._fromLibrary(library); } @@ -256,7 +260,7 @@ class DataClassInformation { useRowClass.getField('constructor')!.toStringValue()!; final generateInsertable = useRowClass.getField('generateInsertable')!.toBoolValue()!; - final helper = await resolver.resolver.driver.loadKnownTypes(); + final helper = resolver.resolver.driver.knownTypes; if (type is InterfaceType) { final found = FoundDartClass(type.element, type.typeArguments); diff --git a/drift_dev/lib/src/analysis/resolver/dart/view.dart b/drift_dev/lib/src/analysis/resolver/dart/view.dart index 9a8f3b2e..7a16d508 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/view.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/view.dart @@ -53,7 +53,7 @@ class DartViewResolver extends LocalElementResolver { Future _getStaticReference( FieldElement field) async { final type = field.type; - final knownTypes = await resolver.driver.loadKnownTypes(); + final knownTypes = resolver.driver.knownTypes; final typeSystem = field.library.typeSystem; if (type is! InterfaceType || diff --git a/drift_dev/lib/src/analysis/resolver/discover.dart b/drift_dev/lib/src/analysis/resolver/discover.dart index 1d942380..0e672889 100644 --- a/drift_dev/lib/src/analysis/resolver/discover.dart +++ b/drift_dev/lib/src/analysis/resolver/discover.dart @@ -73,8 +73,7 @@ class DiscoverStep { _file.discovery = NotADartLibrary(); break; } - final finder = - _FindDartElements(this, library, await _driver.loadKnownTypes()); + final finder = _FindDartElements(this, library, _driver.knownTypes); await finder.find(); _file.errorsDuringDiscovery.addAll(finder.errors); diff --git a/drift_dev/lib/src/analysis/resolver/drift/element_resolver.dart b/drift_dev/lib/src/analysis/resolver/drift/element_resolver.dart index 010356b3..478ec46c 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/element_resolver.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/element_resolver.dart @@ -37,7 +37,7 @@ abstract class DriftElementResolver return null; } - final knownTypes = await resolver.driver.loadKnownTypes(); + final knownTypes = resolver.driver.knownTypes; return readCustomType( knownTypes.helperLibrary, expression, @@ -64,7 +64,7 @@ abstract class DriftElementResolver return null; } - final knownTypes = await resolver.driver.loadKnownTypes(); + final knownTypes = resolver.driver.knownTypes; return readTypeConverter( knownTypes.helperLibrary, expression, @@ -153,7 +153,7 @@ abstract class DriftElementResolver innerType, false, this, - await resolver.driver.loadKnownTypes(), + resolver.driver.knownTypes, ); } } @@ -166,7 +166,7 @@ abstract class DriftElementResolver )); return null; } else { - final knownTypes = await resolver.driver.loadKnownTypes(); + final knownTypes = resolver.driver.knownTypes; return validateExistingClass(columns, foundDartClass, source.constructorName ?? '', false, this, knownTypes); } diff --git a/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart b/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart index 3b0e8bb8..513a7cdd 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/sqlparser/mapping.dart @@ -79,7 +79,6 @@ class TypeMapping { type = switch (column.sqlType) { ColumnDriftType() => type, ColumnCustomType(:final custom) => type.addHint(CustomTypeHint(custom)), - ColumnGeopolyPolygonType() => type.addHint(const IsGeopolyPolygon()), }; if (column.typeConverter case AppliedTypeConverter c) { @@ -151,7 +150,17 @@ class TypeMapping { } if (type.hint() != null) { - return const ColumnType.geopolyPolygon(); + final knownTypes = driver.knownTypes; + + return ColumnType.custom( + CustomColumnType( + AnnotatedDartCode.importedSymbol( + Uri.parse('package:drift/extensions/geopoly.dart'), + 'const GeopolyPolygonType()', + ), + knownTypes.geopolyPolygon, + ), + ); } return ColumnType.drift(_toDefaultType(type)); diff --git a/drift_dev/lib/src/analysis/resolver/drift/table.dart b/drift_dev/lib/src/analysis/resolver/drift/table.dart index e6b7c16a..1919dd2d 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/table.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/table.dart @@ -77,7 +77,7 @@ class DriftTableResolver extends DriftElementResolver { type.builtin == DriftSqlType.int ? EnumType.intEnum : EnumType.textEnum, - await resolver.driver.loadKnownTypes(), + resolver.driver.knownTypes, ); } } diff --git a/drift_dev/lib/src/analysis/resolver/drift/view.dart b/drift_dev/lib/src/analysis/resolver/drift/view.dart index b2f74149..ebadef85 100644 --- a/drift_dev/lib/src/analysis/resolver/drift/view.dart +++ b/drift_dev/lib/src/analysis/resolver/drift/view.dart @@ -27,7 +27,7 @@ class DriftViewResolver extends DriftElementResolver { ? null : await createTypeResolver( allReferences, - await resolver.driver.loadKnownTypes(), + resolver.driver.knownTypes, ); final context = engine.analyzeNode( diff --git a/drift_dev/lib/src/analysis/resolver/file_analysis.dart b/drift_dev/lib/src/analysis/resolver/file_analysis.dart index 2802c065..d6ac734d 100644 --- a/drift_dev/lib/src/analysis/resolver/file_analysis.dart +++ b/drift_dev/lib/src/analysis/resolver/file_analysis.dart @@ -19,7 +19,7 @@ class FileAnalyzer { Future runAnalysisOn(FileState state) async { final result = FileAnalysisResult(); - final knownTypes = await driver.loadKnownTypes(); + final knownTypes = driver.knownTypes; if (state.extension == '.dart') { for (final elementAnalysis in state.analysis.values) { diff --git a/drift_dev/lib/src/analysis/results/dart.dart b/drift_dev/lib/src/analysis/results/dart.dart index eb3ce737..fc99729f 100644 --- a/drift_dev/lib/src/analysis/results/dart.dart +++ b/drift_dev/lib/src/analysis/results/dart.dart @@ -143,8 +143,6 @@ class AnnotatedDartCodeBuilder { addTopLevel(dartTypeNames[hasType.sqlType.builtin]!); case ColumnCustomType(:final custom): addDartType(custom.dartType); - case ColumnGeopolyPolygonType(:final dartType): - addTopLevel(dartType); } if (hasType.nullable) addText('?'); } diff --git a/drift_dev/lib/src/analysis/results/query.dart b/drift_dev/lib/src/analysis/results/query.dart index db28749d..5566ed8d 100644 --- a/drift_dev/lib/src/analysis/results/query.dart +++ b/drift_dev/lib/src/analysis/results/query.dart @@ -750,7 +750,7 @@ final class ScalarResultColumn extends ResultColumn int get _columnTypeCompatibilityHash { final custom = switch (sqlType) { - ColumnDriftType() || ColumnGeopolyPolygonType() => null, + ColumnDriftType() => null, ColumnCustomType(:final custom) => custom, }; diff --git a/drift_dev/lib/src/analysis/results/types.dart b/drift_dev/lib/src/analysis/results/types.dart index 5333221c..be43846c 100644 --- a/drift_dev/lib/src/analysis/results/types.dart +++ b/drift_dev/lib/src/analysis/results/types.dart @@ -44,19 +44,11 @@ sealed class ColumnType { /// all. final DriftSqlType builtin; - /// Details about the custom type, if one is present. - // CustomColumnType? get custom => switch (this) { - // ColumnDriftType() || ColumnGeopolyPolygonType() => null, - // ColumnCustomType(:final custom) => custom, - // }; - const ColumnType._(this.builtin); const factory ColumnType.drift(DriftSqlType builtin) = ColumnDriftType; const factory ColumnType.custom(CustomColumnType custom) = ColumnCustomType; - - const factory ColumnType.geopolyPolygon() = ColumnGeopolyPolygonType; } final class ColumnDriftType extends ColumnType { @@ -69,13 +61,6 @@ final class ColumnCustomType extends ColumnType { const ColumnCustomType(this.custom) : super._(DriftSqlType.any); } -final class ColumnGeopolyPolygonType extends ColumnType { - const ColumnGeopolyPolygonType() : super._(DriftSqlType.any); - - DartTopLevelSymbol get dartType => - DartTopLevelSymbol('GeopolyPolygon', AnnotatedDartCode.drift); -} - extension OperationOnTypes on HasType { bool get isUint8ListInDart { return sqlType.builtin == DriftSqlType.blob && typeConverter == null; diff --git a/drift_dev/lib/src/analysis/serializer.dart b/drift_dev/lib/src/analysis/serializer.dart index a496d5f2..2b30dc32 100644 --- a/drift_dev/lib/src/analysis/serializer.dart +++ b/drift_dev/lib/src/analysis/serializer.dart @@ -200,20 +200,17 @@ class ElementSerializer { } Map _serializeColumnType(ColumnType type) { - switch (type) { - case ColumnGeopolyPolygonType(): - case ColumnDriftType(): - return { + return switch (type) { + ColumnDriftType() => { 'builtin': type.builtin.name, - }; - case ColumnCustomType(:final custom): - return { + }, + ColumnCustomType(:final custom) => { 'custom': { 'dart': _serializeType(custom.dartType), 'expression': custom.expression.toJson(), } - }; - } + }, + }; } Map _serializeColumn(DriftColumn column) { diff --git a/drift_dev/lib/src/backends/analyzer_context_backend.dart b/drift_dev/lib/src/backends/analyzer_context_backend.dart index 6a5f81cd..6a8c93b5 100644 --- a/drift_dev/lib/src/backends/analyzer_context_backend.dart +++ b/drift_dev/lib/src/backends/analyzer_context_backend.dart @@ -46,11 +46,11 @@ class AnalysisContextBackend extends DriftBackend { AnalysisContextBackend(this.context, this.provider); - static PhysicalDriftDriver createDriver({ + static Future createDriver({ DriftOptions options = const DriftOptions.defaults(), ResourceProvider? resourceProvider, required String projectDirectory, - }) { + }) async { final underlyingProvider = resourceProvider ?? PhysicalResourceProvider.INSTANCE; final provider = OverlayResourceProvider(underlyingProvider); @@ -62,7 +62,7 @@ class AnalysisContextBackend extends DriftBackend { final context = contextCollection.contextFor(projectDirectory); final backend = AnalysisContextBackend(context, provider); - final driver = DriftAnalysisDriver(backend, options); + final driver = await DriftAnalysisDriver.init(backend, options); return PhysicalDriftDriver(driver, backend); } diff --git a/drift_dev/lib/src/backends/build/analyzer.dart b/drift_dev/lib/src/backends/build/analyzer.dart index 70ac0890..a4209a02 100644 --- a/drift_dev/lib/src/backends/build/analyzer.dart +++ b/drift_dev/lib/src/backends/build/analyzer.dart @@ -31,7 +31,7 @@ class DriftDiscover extends Builder { @override Future build(BuildStep buildStep) async { final backend = DriftBuildBackend(buildStep); - final driver = DriftAnalysisDriver(backend, options); + final driver = await DriftAnalysisDriver.init(backend, options); final prepared = await driver.findLocalElements(buildStep.inputId.uri); final discovery = prepared.discovery; @@ -84,7 +84,7 @@ class DriftAnalyzer extends Builder { @override Future build(BuildStep buildStep) async { final backend = DriftBuildBackend(buildStep); - final driver = DriftAnalysisDriver(backend, options) + final driver = await DriftAnalysisDriver.init(backend, options) ..cacheReader = BuildCacheReader(buildStep, findsLocalElementsReliably: true); diff --git a/drift_dev/lib/src/backends/build/drift_builder.dart b/drift_dev/lib/src/backends/build/drift_builder.dart index 9f257516..987c4c55 100644 --- a/drift_dev/lib/src/backends/build/drift_builder.dart +++ b/drift_dev/lib/src/backends/build/drift_builder.dart @@ -1,5 +1,6 @@ import 'package:build/build.dart'; import 'package:dart_style/dart_style.dart'; +import 'package:meta/meta.dart'; import 'package:pub_semver/pub_semver.dart'; import '../../analysis/custom_result_class.dart'; @@ -102,7 +103,7 @@ class DriftBuilder extends Builder { @override Future build(BuildStep buildStep) async { - final run = _DriftBuildRun(options, generationMode, buildStep); + final run = await _DriftBuildRun.init(options, generationMode, buildStep); await run.run(); } } @@ -133,18 +134,31 @@ class _DriftBuildRun { Set analyzedUris = {}; bool _didPrintWarning = false; - _DriftBuildRun(this.options, this.mode, this.buildStep) - : driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options) - ..cacheReader = BuildCacheReader( - buildStep, - // The discovery and analyzer builders will have emitted IR for - // every relevant file in a previous build step that this builder - // has a dependency on. - findsResolvedElementsReliably: - !mode.embeddedAnalyzer || options.hasDriftAnalyzer, - findsLocalElementsReliably: - !mode.embeddedAnalyzer || options.hasDriftAnalyzer, - ); + @visibleForTesting + _DriftBuildRun(this.options, this.mode, this.buildStep, this.driver); + + static Future<_DriftBuildRun> init( + DriftOptions options, + DriftGenerationMode mode, + BuildStep buildStep, + ) async { + return _DriftBuildRun( + options, + mode, + buildStep, + await DriftAnalysisDriver.init(DriftBuildBackend(buildStep), options) + ..cacheReader = BuildCacheReader( + buildStep, + // The discovery and analyzer builders will have emitted IR for + // every relevant file in a previous build step that this builder + // has a dependency on. + findsResolvedElementsReliably: + !mode.embeddedAnalyzer || options.hasDriftAnalyzer, + findsLocalElementsReliably: + !mode.embeddedAnalyzer || options.hasDriftAnalyzer, + ), + ); + } Future run() async { await _warnAboutDeprecatedOptions(); diff --git a/drift_dev/lib/src/services/schema/sqlite_to_drift.dart b/drift_dev/lib/src/services/schema/sqlite_to_drift.dart index b635e82f..49bd9151 100644 --- a/drift_dev/lib/src/services/schema/sqlite_to_drift.dart +++ b/drift_dev/lib/src/services/schema/sqlite_to_drift.dart @@ -21,7 +21,7 @@ Future> extractDriftElementsFromDatabase( final logger = Logger('extractDriftElementsFromDatabase'); final uri = Uri.parse('db.drift'); final backend = _SingleFileNoAnalyzerBackend(logger, uri); - final driver = DriftAnalysisDriver( + final driver = await DriftAnalysisDriver.init( backend, DriftOptions.defaults( sqliteAnalysisOptions: SqliteAnalysisOptions( diff --git a/drift_dev/lib/src/writer/queries/query_writer.dart b/drift_dev/lib/src/writer/queries/query_writer.dart index ddc9da12..892be308 100644 --- a/drift_dev/lib/src/writer/queries/query_writer.dart +++ b/drift_dev/lib/src/writer/queries/query_writer.dart @@ -209,7 +209,6 @@ class QueryWriter { String code; switch (column.sqlType) { - case ColumnGeopolyPolygonType(): case ColumnDriftType(): final method = isNullable ? 'readNullable' : 'read'; code = 'row.$method<$rawDartType>($dartLiteral)'; 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 25642540..f3995d96 100644 --- a/drift_dev/lib/src/writer/tables/data_class_writer.dart +++ b/drift_dev/lib/src/writer/tables/data_class_writer.dart @@ -345,7 +345,6 @@ class RowMappingWriter { final String sqlType; switch (column.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): sqlType = writer.drift(column.sqlType.builtin.toString()); case ColumnCustomType(:final custom): sqlType = writer.dartCode(custom.expression); diff --git a/drift_dev/lib/src/writer/tables/table_writer.dart b/drift_dev/lib/src/writer/tables/table_writer.dart index f4c9baab..a2fa749a 100644 --- a/drift_dev/lib/src/writer/tables/table_writer.dart +++ b/drift_dev/lib/src/writer/tables/table_writer.dart @@ -213,7 +213,6 @@ abstract class TableOrViewWriter { switch (column.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): additionalParams['type'] = emitter.drift(column.sqlType.builtin.toString()); case ColumnCustomType(:final custom): diff --git a/drift_dev/lib/src/writer/writer.dart b/drift_dev/lib/src/writer/writer.dart index 9199aafb..37c84496 100644 --- a/drift_dev/lib/src/writer/writer.dart +++ b/drift_dev/lib/src/writer/writer.dart @@ -147,7 +147,6 @@ abstract class _NodeOrWriter { switch (converter.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): sqlDartType = AnnotatedDartCode([dartTypeNames[converter.sqlType.builtin]!]); case ColumnCustomType(:final custom): @@ -209,8 +208,6 @@ abstract class _NodeOrWriter { AnnotatedDartCode innerColumnType(ColumnType type, {bool nullable = false}) { return AnnotatedDartCode.build((b) { switch (type) { - case ColumnGeopolyPolygonType(:final dartType): - b.addTopLevel(dartType); case ColumnDriftType(): b.addTopLevel(dartTypeNames[type.builtin]!); case ColumnCustomType(:final custom): @@ -247,7 +244,6 @@ abstract class _NodeOrWriter { switch (column.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): break; case ColumnCustomType(:final custom): // Also specify the custom type since it can't be inferred from the diff --git a/drift_dev/test/analysis/generic_test.dart b/drift_dev/test/analysis/generic_test.dart index 8b19e74e..72853f2b 100644 --- a/drift_dev/test/analysis/generic_test.dart +++ b/drift_dev/test/analysis/generic_test.dart @@ -8,7 +8,7 @@ import 'test_utils.dart'; void main() { test('handles cyclic imports', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/entry.dart': ''' import 'package:drift/drift.dart'; @@ -42,7 +42,7 @@ CREATE TABLE bars ( group("reports error when an import can't be found", () { for (final extension in const ['drift', 'moor']) { test('in $extension files', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.$extension': ''' import 'b.$extension'; ''', @@ -56,7 +56,7 @@ import 'b.$extension'; } test('in a dart file', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -74,7 +74,7 @@ class Database { }); test('resolves tables and queries', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/database.dart': r''' import 'package:drift/drift.dart'; @@ -178,7 +178,7 @@ class ProgrammingLanguages extends Table { }); test('still supports .moor files', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -203,7 +203,7 @@ CREATE TABLE users ( }); test('supports multiple references between same entities', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -231,7 +231,7 @@ class ThisTable extends Table { }); test('supports references across files', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/this_table.dart': ''' import 'package:drift/drift.dart'; @@ -272,7 +272,7 @@ class OtherTable extends Table { }); test('reports sensible error for missing table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' getCompanyCustomersCount: SELECT COUNT(*) AS "count" diff --git a/drift_dev/test/analysis/options_test.dart b/drift_dev/test/analysis/options_test.dart index 37f5035b..be4ba9e9 100644 --- a/drift_dev/test/analysis/options_test.dart +++ b/drift_dev/test/analysis/options_test.dart @@ -75,7 +75,7 @@ sqlite: }); test('reports error about table when module is not imported', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': 'CREATE VIRTUAL TABLE place_spellfix USING spellfix1;', }); diff --git a/drift_dev/test/analysis/preprocess_drift_test.dart b/drift_dev/test/analysis/preprocess_drift_test.dart index 7a4103ef..086bcb51 100644 --- a/drift_dev/test/analysis/preprocess_drift_test.dart +++ b/drift_dev/test/analysis/preprocess_drift_test.dart @@ -5,7 +5,7 @@ import 'test_utils.dart'; void main() { test('finds dart expressions', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' import 'foo.dart'; @@ -26,7 +26,7 @@ var expr_0 = const MyConverter(); test('only includes direct imports if no Dart expressions are used', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' import 'foo.dart'; import '2.drift'; @@ -48,7 +48,7 @@ import 'bar.dart'; }); test('finds nested dart imports', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'b.drift'; @@ -72,7 +72,7 @@ import 'import.dart'; }); test('does not throw for invalid import', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'b.drift'; import 'does_not_exist.drift'; @@ -96,8 +96,8 @@ import 'c.drift'; expect(result.temporaryDartFile, isNot(contains('import'))); }); - test('throws if entrypoint does not exist', () { - final backend = TestBackend.inTest({}); + test('throws if entrypoint does not exist', () async { + final backend = await TestBackend.inTest({}); expect( () => @@ -106,8 +106,8 @@ import 'c.drift'; ); }); - test('throws if entrypoint is invalid', () { - final backend = TestBackend.inTest({ + test('throws if entrypoint is invalid', () async { + final backend = await TestBackend.inTest({ 'a|lib/main.drift': '! this not a valid drift file !', }); diff --git a/drift_dev/test/analysis/repro_1280_test.dart b/drift_dev/test/analysis/repro_1280_test.dart index 89bb5935..f852b6ff 100644 --- a/drift_dev/test/analysis/repro_1280_test.dart +++ b/drift_dev/test/analysis/repro_1280_test.dart @@ -6,7 +6,7 @@ import 'test_utils.dart'; void main() { test('analyzes views referencing Dart tables', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/db.dart': ''' import 'package:drift/drift.dart'; import 'dart:io'; diff --git a/drift_dev/test/analysis/repro_967_test.dart b/drift_dev/test/analysis/repro_967_test.dart index 24fa14b9..63d8745c 100644 --- a/drift_dev/test/analysis/repro_967_test.dart +++ b/drift_dev/test/analysis/repro_967_test.dart @@ -5,7 +5,7 @@ import 'test_utils.dart'; void main() { test('gracefully handles daos with invalid types', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/bar.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/column_references_test.dart b/drift_dev/test/analysis/resolver/dart/column_references_test.dart index d1f698b2..5bd1710c 100644 --- a/drift_dev/test/analysis/resolver/dart/column_references_test.dart +++ b/drift_dev/test/analysis/resolver/dart/column_references_test.dart @@ -8,7 +8,7 @@ import '../../test_utils.dart'; void main() { group('reports a warning', () { test('when the table is not a class type', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -26,7 +26,7 @@ class Foo extends Table { }); test('when the column is not a symbol literal', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -48,7 +48,7 @@ class Foo extends Table { }); test('includes referenced table in database', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -78,7 +78,7 @@ class Database {} }); test('when the referenced column does not exist', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -106,7 +106,7 @@ class Database {} }); test('resolves reference', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -149,7 +149,7 @@ class Database {} }); test('resolves self-references', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/column_test.dart b/drift_dev/test/analysis/resolver/dart/column_test.dart index d21370c5..3cf9de0a 100644 --- a/drift_dev/test/analysis/resolver/dart/column_test.dart +++ b/drift_dev/test/analysis/resolver/dart/column_test.dart @@ -9,7 +9,7 @@ void main() { test( 'It should rename the table and column name to its snake case version by default', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -36,7 +36,7 @@ class Database {} test('It should rename the table and column name to its snake case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -64,7 +64,7 @@ class Database {} }); test('It should not rename the table and column name', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -93,7 +93,7 @@ class Database {} }); test('It should rename the table and column name to its camel case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -122,7 +122,7 @@ class Database {} test( 'It should rename the table and column name to its constant case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -150,7 +150,7 @@ class Database {} }); test('It should rename the table and column name to its pascal case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -178,7 +178,7 @@ class Database {} }); test('It should rename the table and column name to its lower case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -207,7 +207,7 @@ class Database {} }); test('It should rename the table and column name to its upper case version', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -235,7 +235,7 @@ class Database {} }); test('recognizes custom column types', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -256,7 +256,6 @@ class TestTable extends Table { expect(column.sqlType.builtin, DriftSqlType.any); switch (column.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): break; case ColumnCustomType(:final custom): expect(custom.dartType.toString(), 'List'); @@ -266,7 +265,7 @@ class TestTable extends Table { group('customConstraint analysis', () { test('reports errors', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -285,7 +284,7 @@ class TestTable extends Table { }); test('resolves foreign key references', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -326,7 +325,7 @@ class TestTable extends Table { }); test('warns about missing `NOT NULL`', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -345,7 +344,7 @@ class TestTable extends Table { }); test('applies constraints', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/custom_row_classes_test.dart b/drift_dev/test/analysis/resolver/dart/custom_row_classes_test.dart index b648d9dd..c7a4a4fc 100644 --- a/drift_dev/test/analysis/resolver/dart/custom_row_classes_test.dart +++ b/drift_dev/test/analysis/resolver/dart/custom_row_classes_test.dart @@ -7,8 +7,8 @@ import '../../test_utils.dart'; void main() { late TestBackend state; - setUpAll(() { - state = TestBackend(const { + setUpAll(() async { + state = await TestBackend.init(const { 'a|lib/invalid_no_unnamed_constructor.dart': ''' import 'package:drift/drift.dart'; @@ -408,7 +408,7 @@ class Companies extends Table { }); test('handles `ANY` columns', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'row.dart'; @@ -528,7 +528,7 @@ class FooData { group('records as row types', () { test('supported with explicit record', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -562,7 +562,7 @@ class Users extends Table { }); test('supported with implicit record', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/database_parser_test.dart b/drift_dev/test/analysis/resolver/dart/database_parser_test.dart index 3369bedb..bce5fa49 100644 --- a/drift_dev/test/analysis/resolver/dart/database_parser_test.dart +++ b/drift_dev/test/analysis/resolver/dart/database_parser_test.dart @@ -7,7 +7,7 @@ void main() { final mainUri = Uri.parse('package:a/main.dart'); test('parses schema version getter', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': r''' import 'package:drift/drift.dart'; @@ -27,7 +27,7 @@ class MyDatabase extends _$MyDatabase { }); test('parses schema version field', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': r''' import 'package:drift/drift.dart'; @@ -47,7 +47,7 @@ class MyDatabase extends _$MyDatabase { }); test('does not warn about missing tables parameter', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': r''' import 'package:drift/drift.dart'; @@ -69,7 +69,7 @@ class MyDatabase2 extends _$MyDatabase { }); test('supports inheritance for daos', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/database.dart': r''' import 'package:drift/drift.dart'; @@ -115,7 +115,7 @@ class ProductsDao extends BaseProductsDao with _$ProductDaoMixin { }); test('only includes duplicate elements once', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/enum_columns_test.dart b/drift_dev/test/analysis/resolver/dart/enum_columns_test.dart index e7cc4dad..25003332 100644 --- a/drift_dev/test/analysis/resolver/dart/enum_columns_test.dart +++ b/drift_dev/test/analysis/resolver/dart/enum_columns_test.dart @@ -9,7 +9,7 @@ void main() { late TestBackend backend; setUpAll(() async { - backend = TestBackend({ + backend = await TestBackend.init({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/foreign_key_test.dart b/drift_dev/test/analysis/resolver/dart/foreign_key_test.dart index 250fb64b..bffe940b 100644 --- a/drift_dev/test/analysis/resolver/dart/foreign_key_test.dart +++ b/drift_dev/test/analysis/resolver/dart/foreign_key_test.dart @@ -7,7 +7,7 @@ import '../../test_utils.dart'; void main() { group('reports a warning', () { test('when the table is not a class type', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -29,7 +29,7 @@ class Foo extends Table { }); test('when the table is not a symbol literal', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -54,7 +54,7 @@ class Foo extends Table { }); test('when the referenced table does not exist', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -83,7 +83,7 @@ class Foo extends Table { }); test('resolves reference', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -116,7 +116,7 @@ class Foo extends Table { }); test('resolves self-references', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/index_test.dart b/drift_dev/test/analysis/resolver/dart/index_test.dart index 7196e13a..18afc8d3 100644 --- a/drift_dev/test/analysis/resolver/dart/index_test.dart +++ b/drift_dev/test/analysis/resolver/dart/index_test.dart @@ -5,7 +5,7 @@ import '../../test_utils.dart'; void main() { test('resolves index', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -41,7 +41,7 @@ class MyTable extends Table { }); test('warns about missing columns', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/invalid_column_test.dart b/drift_dev/test/analysis/resolver/dart/invalid_column_test.dart index f93ecbe3..0c301b30 100644 --- a/drift_dev/test/analysis/resolver/dart/invalid_column_test.dart +++ b/drift_dev/test/analysis/resolver/dart/invalid_column_test.dart @@ -7,7 +7,7 @@ import '../../test_utils.dart'; void main() { test('warns about invalid column', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/regression_1172_test.dart b/drift_dev/test/analysis/resolver/dart/regression_1172_test.dart index 147ff722..24624138 100644 --- a/drift_dev/test/analysis/resolver/dart/regression_1172_test.dart +++ b/drift_dev/test/analysis/resolver/dart/regression_1172_test.dart @@ -4,7 +4,7 @@ import '../../test_utils.dart'; void main() { test('can define abstract tables', () async { - final test = TestBackend.inTest({ + final test = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/table_parser_test.dart b/drift_dev/test/analysis/resolver/dart/table_parser_test.dart index 23d73280..55b7c48e 100644 --- a/drift_dev/test/analysis/resolver/dart/table_parser_test.dart +++ b/drift_dev/test/analysis/resolver/dart/table_parser_test.dart @@ -11,8 +11,8 @@ void main() { late TestBackend backend; late FileState state; - setUpAll(() { - backend = TestBackend({ + setUpAll(() async { + backend = await TestBackend.init({ 'a|lib/main.dart': r''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/table_test.dart b/drift_dev/test/analysis/resolver/dart/table_test.dart index f827853b..91e152bc 100644 --- a/drift_dev/test/analysis/resolver/dart/table_test.dart +++ b/drift_dev/test/analysis/resolver/dart/table_test.dart @@ -8,8 +8,8 @@ import '../../test_utils.dart'; void main() { late TestBackend backend; - setUpAll(() { - backend = TestBackend({ + setUpAll(() async { + backend = await TestBackend.init({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -335,7 +335,7 @@ class Pianos extends Table { }); test('reads custom constraints from table', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -377,7 +377,7 @@ class WithConstraints extends Table { }); test('warns about foreign key references from customConstraints', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -410,7 +410,7 @@ class WithConstraints extends Table { }); test('can resolve references from import', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/topic.dart': ''' import 'package:drift/drift.dart'; @@ -452,7 +452,7 @@ class Videos extends Table { }); test('supports autoIncrement on int64 columns', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/type_converter_test.dart b/drift_dev/test/analysis/resolver/dart/type_converter_test.dart index ccf5d152..a3a6da4c 100644 --- a/drift_dev/test/analysis/resolver/dart/type_converter_test.dart +++ b/drift_dev/test/analysis/resolver/dart/type_converter_test.dart @@ -6,8 +6,8 @@ import '../../test_utils.dart'; void main() { late TestBackend state; - setUp(() { - state = TestBackend({ + setUp(() async { + state = await TestBackend.init({ 'a|lib/json.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/unique_columns_test.dart b/drift_dev/test/analysis/resolver/dart/unique_columns_test.dart index 603b7e4e..144af507 100644 --- a/drift_dev/test/analysis/resolver/dart/unique_columns_test.dart +++ b/drift_dev/test/analysis/resolver/dart/unique_columns_test.dart @@ -7,7 +7,7 @@ void main() { final mainUri = Uri.parse('package:a/main.dart'); test('does not allow autoIncrement() to have a unique constraint', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -29,7 +29,7 @@ class Test extends Table { }); test('does not allow primary key to have a unique constraint', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -55,7 +55,7 @@ class Test extends Table { test( 'does not allow primary key to have a unique constraint through override', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -84,7 +84,7 @@ class Test extends Table { }); test('warns about duplicate unique declarations', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; @@ -108,7 +108,7 @@ class Test extends Table { }); test('parses unique key definitions', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/dart/view_test.dart b/drift_dev/test/analysis/resolver/dart/view_test.dart index 3a72831b..41dccb34 100644 --- a/drift_dev/test/analysis/resolver/dart/view_test.dart +++ b/drift_dev/test/analysis/resolver/dart/view_test.dart @@ -6,7 +6,7 @@ import '../../test_utils.dart'; void main() { test('can analyze Dart view', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/discover_test.dart b/drift_dev/test/analysis/resolver/discover_test.dart index c3ed3d5a..9d752de2 100644 --- a/drift_dev/test/analysis/resolver/discover_test.dart +++ b/drift_dev/test/analysis/resolver/discover_test.dart @@ -8,7 +8,7 @@ import '../test_utils.dart'; void main() { group('drift files', () { test('finds local elements', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE foo (bar INTEGER); @@ -41,7 +41,7 @@ CREATE VIEW my_view AS SELECT whatever FROM unknown_table; }); test('reports syntax errors', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE valid_1 (bar INTEGER); @@ -64,7 +64,7 @@ CREATE TABLE valid_2 (bar INTEGER); }); test('warns about duplicate elements', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE a (id INTEGER); CREATE VIEW a AS VALUES(1,2,3); @@ -82,7 +82,7 @@ CREATE VIEW a AS VALUES(1,2,3); group('imports', () { test('are resolved', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': "import 'b.drift';", 'a|lib/b.drift': "CREATE TABLE foo (bar INTEGER);", }); @@ -106,7 +106,7 @@ CREATE VIEW a AS VALUES(1,2,3); }); test('can handle circular imports', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': "import 'a.drift'; import 'b.drift';", 'a|lib/b.drift': "import 'a.drift';", }); @@ -119,7 +119,7 @@ CREATE VIEW a AS VALUES(1,2,3); group('dart files', () { test('fails for part files', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' part of 'b.dart'; ''', @@ -136,7 +136,7 @@ part 'a.dart'; }); test('finds tables', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -172,7 +172,7 @@ class Groups extends Table { }); test('ignores abstract tables', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; @@ -208,7 +208,7 @@ abstract class BaseRelationTable extends Table { }); test('table name errors', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/expr.dart': ''' import 'package:drift/drift.dart'; @@ -239,7 +239,7 @@ class InvalidGetter extends Table { }); test('warns about duplicate elements', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/drift/create_view_test.dart b/drift_dev/test/analysis/resolver/drift/create_view_test.dart index 9fe69b93..3d0cffb6 100644 --- a/drift_dev/test/analysis/resolver/drift/create_view_test.dart +++ b/drift_dev/test/analysis/resolver/drift/create_view_test.dart @@ -8,7 +8,7 @@ import '../../test_utils.dart'; void main() { test('view created', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/table.drift': ''' CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL); ''', @@ -34,7 +34,7 @@ void main() { }); test('view created from another view', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/table.drift': ''' CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL); ''', @@ -68,7 +68,7 @@ void main() { }); test('view without table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE VIEW random_view AS SELECT name FROM t WHERE id % 2 = 0; @@ -82,7 +82,7 @@ void main() { }); test('does not allow nested columns', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo (bar INTEGER NOT NULL PRIMARY KEY); @@ -102,7 +102,7 @@ void main() { test('imported views are analyzed', () async { // Regression test for https://github.com/simolus3/drift/issues/1639 - final testState = TestBackend.inTest({ + final testState = await TestBackend.inTest({ 'a|lib/imported.drift': ''' CREATE TABLE a ( b TEXT NOT NULL @@ -124,7 +124,7 @@ query: SELECT * FROM my_view; }); test('picks valid Dart names for columns', () async { - final testState = TestBackend.inTest({ + final testState = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE VIEW IF NOT EXISTS repro AS SELECT 1, @@ -148,7 +148,7 @@ CREATE VIEW IF NOT EXISTS repro AS }); test('copies type converter from table', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'converter.dart'; @@ -185,7 +185,7 @@ TypeConverter createConverter() => throw UnimplementedError(); }); test('can declare type converter on view column', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'converter.dart'; @@ -223,7 +223,7 @@ TypeConverter createConverter() => throw UnimplementedError(); }); test('supports enum columns', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enums.dart'; @@ -280,7 +280,7 @@ enum MyEnum { String expectedSql, DriftOptions options, ) async { - final backend = TestBackend.inTest( + final backend = await TestBackend.inTest( {'a|lib/a.drift': definition}, options: options, ); diff --git a/drift_dev/test/analysis/resolver/drift/cte_test.dart b/drift_dev/test/analysis/resolver/drift/cte_test.dart index f9583be1..7bbc57d0 100644 --- a/drift_dev/test/analysis/resolver/drift/cte_test.dart +++ b/drift_dev/test/analysis/resolver/drift/cte_test.dart @@ -6,7 +6,7 @@ import '../../test_utils.dart'; void main() { test('parse nested CTE', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/test.drift': ''' test: SELECT @@ -38,7 +38,7 @@ SELECT }); test('recognizes CTE clause', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/test.drift': ''' test: WITH RECURSIVE @@ -70,7 +70,7 @@ WITH RECURSIVE }); test('finds the underlying table when aliased through CTE', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/test.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/drift_dev/test/analysis/resolver/drift/custom_row_classes_test.dart b/drift_dev/test/analysis/resolver/drift/custom_row_classes_test.dart index c01e134e..1727b59b 100644 --- a/drift_dev/test/analysis/resolver/drift/custom_row_classes_test.dart +++ b/drift_dev/test/analysis/resolver/drift/custom_row_classes_test.dart @@ -5,7 +5,7 @@ import '../../test_utils.dart'; void main() { test('can use existing row classes in drift files', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/db.drift': ''' import 'rows.dart'; @@ -61,7 +61,7 @@ class ExistingForView { }); test('can use generic row classes', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/generic.dart': ''' //@dart=2.13 typedef StringRow = GenericRow; @@ -105,7 +105,7 @@ CREATE TABLE drift_ints ( group('can use records', () { test('with explicit structure', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'helper.dart'; @@ -135,7 +135,7 @@ typedef MyRecord = ({String foo, int? bar}); }); test('implicitly', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE foo ( foo TEXT NOT NULL, diff --git a/drift_dev/test/analysis/resolver/drift/errors_when_importing_part_files_test.dart b/drift_dev/test/analysis/resolver/drift/errors_when_importing_part_files_test.dart index 2ab6ce30..823d13a2 100644 --- a/drift_dev/test/analysis/resolver/drift/errors_when_importing_part_files_test.dart +++ b/drift_dev/test/analysis/resolver/drift/errors_when_importing_part_files_test.dart @@ -5,7 +5,7 @@ import '../../test_utils.dart'; void main() { test('reports an error when importing a part file into .drift', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/base.dart': ''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/drift/ffi_extension_test.dart b/drift_dev/test/analysis/resolver/drift/ffi_extension_test.dart index b3b5cbed..0b2d47c6 100644 --- a/drift_dev/test/analysis/resolver/drift/ffi_extension_test.dart +++ b/drift_dev/test/analysis/resolver/drift/ffi_extension_test.dart @@ -84,7 +84,7 @@ void main() { test('integration tests with drift files and experimental inference', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( const { 'foo|lib/a.drift': ''' CREATE TABLE numbers (foo REAL NOT NULL); diff --git a/drift_dev/test/analysis/resolver/drift/fts5_test.dart b/drift_dev/test/analysis/resolver/drift/fts5_test.dart index cfe205ec..3906d60a 100644 --- a/drift_dev/test/analysis/resolver/drift/fts5_test.dart +++ b/drift_dev/test/analysis/resolver/drift/fts5_test.dart @@ -8,7 +8,7 @@ const _options = DriftOptions.defaults(modules: [SqlModule.fts5]); void main() { group('reports error', () { test('for missing content table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE VIRTUAL TABLE fts USING fts5(a, c, content=tbl); ''', @@ -23,7 +23,7 @@ CREATE VIRTUAL TABLE fts USING fts5(a, c, content=tbl); }); test('for invalid rowid of content table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE tbl (a, b, c, my_pk INTEGER PRIMARY KEY); @@ -39,7 +39,7 @@ CREATE VIRTUAL TABLE fts USING fts5(a, c, content=tbl, content_rowid=d); }); test('when referencing an unknown column', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY); @@ -54,7 +54,7 @@ CREATE VIRTUAL TABLE fts USING fts5(e, c, content=tbl, content_rowid=d); }); test('finds referenced table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.drift': ''' CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY); diff --git a/drift_dev/test/analysis/resolver/drift/regression_1138_test.dart b/drift_dev/test/analysis/resolver/drift/regression_1138_test.dart index f56bb112..4e4b3744 100644 --- a/drift_dev/test/analysis/resolver/drift/regression_1138_test.dart +++ b/drift_dev/test/analysis/resolver/drift/regression_1138_test.dart @@ -5,7 +5,7 @@ import '../../test_utils.dart'; void main() { test('drift files can import original dart source', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/base.dart': r''' import 'package:drift/drift.dart'; diff --git a/drift_dev/test/analysis/resolver/drift/regression_2097_test.dart b/drift_dev/test/analysis/resolver/drift/regression_2097_test.dart index c4dd0b9a..793f916b 100644 --- a/drift_dev/test/analysis/resolver/drift/regression_2097_test.dart +++ b/drift_dev/test/analysis/resolver/drift/regression_2097_test.dart @@ -8,7 +8,7 @@ import '../../test_utils.dart'; void main() { // https://github.com/simolus3/drift/issues/2097#issuecomment-1273008383 test('virtual columns are not required for inserts', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'foo|lib/a.drift': r''' CREATE TABLE IF NOT EXISTS nodes ( diff --git a/drift_dev/test/analysis/resolver/drift/regression_754_test.dart b/drift_dev/test/analysis/resolver/drift/regression_754_test.dart index 8d4594f8..4a5fee2f 100644 --- a/drift_dev/test/analysis/resolver/drift/regression_754_test.dart +++ b/drift_dev/test/analysis/resolver/drift/regression_754_test.dart @@ -6,7 +6,7 @@ import '../../test_utils.dart'; void main() { // Regression test for https://github.com/simolus3/drift/issues/754 test('supports fts5 tables with external content', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE tbl(a INTEGER PRIMARY KEY, b TEXT, c TEXT); CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a'); diff --git a/drift_dev/test/analysis/resolver/drift/sqlite_version_test.dart b/drift_dev/test/analysis/resolver/drift/sqlite_version_test.dart index dbe4d196..50d059c8 100644 --- a/drift_dev/test/analysis/resolver/drift/sqlite_version_test.dart +++ b/drift_dev/test/analysis/resolver/drift/sqlite_version_test.dart @@ -20,7 +20,7 @@ query: INSERT INTO foo VALUES (?, ?, ?) void main() { test('does not support newer sqlite features by default', () async { - final state = TestBackend.inTest(_content); + final state = await TestBackend.inTest(_content); final file = await state.analyze('package:a/main.drift'); expect( @@ -38,7 +38,7 @@ void main() { }); test('supports newer sqlite features', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( _content, options: const DriftOptions.defaults( sqliteAnalysisOptions: SqliteAnalysisOptions( diff --git a/drift_dev/test/analysis/resolver/drift/sqlparser/type_converter_lints_test.dart b/drift_dev/test/analysis/resolver/drift/sqlparser/type_converter_lints_test.dart index 726db570..906b07c0 100644 --- a/drift_dev/test/analysis/resolver/drift/sqlparser/type_converter_lints_test.dart +++ b/drift_dev/test/analysis/resolver/drift/sqlparser/type_converter_lints_test.dart @@ -14,7 +14,7 @@ enum Fruit { void main() { group('warns about invalid type converter value', () { test('in table definition', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; @@ -38,7 +38,7 @@ CREATE TABLE a ( }); test('for query', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; diff --git a/drift_dev/test/analysis/resolver/drift/table_test.dart b/drift_dev/test/analysis/resolver/drift/table_test.dart index 09d251d8..f9aaaa9c 100644 --- a/drift_dev/test/analysis/resolver/drift/table_test.dart +++ b/drift_dev/test/analysis/resolver/drift/table_test.dart @@ -8,7 +8,7 @@ import '../../test_utils.dart'; void main() { test('reports foreign keys in drift model', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE a ( foo INTEGER PRIMARY KEY, @@ -56,7 +56,7 @@ CREATE TABLE b ( }); test('recognizes aliases to rowid', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE users ( id INTEGER PRIMARY KEY, @@ -84,7 +84,7 @@ CREATE TABLE b ( }); test('parses enum columns', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; @@ -168,7 +168,7 @@ CREATE TABLE b ( }); test('does not allow converters for enum columns', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; @@ -200,7 +200,7 @@ CREATE TABLE b ( }); test('does not allow enum types for non-enums', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; @@ -223,7 +223,7 @@ CREATE TABLE b ( }); test('supports JSON KEY annotation', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE waybills ( parent INT JSON KEY parentDoc NULL, @@ -244,7 +244,7 @@ CREATE TABLE waybills ( }); test('recognizes documentation comments', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE IF NOT EXISTS currencies ( -- The name of this currency @@ -266,7 +266,7 @@ CREATE TABLE IF NOT EXISTS currencies ( }); test('can use custom types', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'b.dart'; @@ -289,7 +289,6 @@ class MyType implements CustomSqlType {} switch (column.sqlType) { case ColumnDriftType(): - case ColumnGeopolyPolygonType(): fail('expect custom type'); case ColumnCustomType(:final custom): expect(custom.dartType.toString(), 'String'); diff --git a/drift_dev/test/analysis/resolver/drift/virtual_table_test.dart b/drift_dev/test/analysis/resolver/drift/virtual_table_test.dart index 3322b976..49f13984 100644 --- a/drift_dev/test/analysis/resolver/drift/virtual_table_test.dart +++ b/drift_dev/test/analysis/resolver/drift/virtual_table_test.dart @@ -7,7 +7,7 @@ import '../../test_utils.dart'; void main() { test('supports virtual tables across drift files', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/table.drift': ''' CREATE TABLE example_table ( @@ -43,7 +43,7 @@ exampleSearch: SELECT example_table.**, s.* FROM example_table }); test('query virtual tables with unknown function', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/table.drift': ''' CREATE TABLE example_table ( @@ -72,7 +72,7 @@ SELECT rowid, highlight(example_table_search, 0, '[match]', '[match]') name, }); test('supports spellfix1 tables', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( {'a|lib/a.drift': 'CREATE VIRTUAL TABLE demo USING spellfix1;'}, options: DriftOptions.defaults( dialect: DialectOptions( diff --git a/drift_dev/test/analysis/resolver/queries/custom_class_name_test.dart b/drift_dev/test/analysis/resolver/queries/custom_class_name_test.dart index fd405922..9103ebbf 100644 --- a/drift_dev/test/analysis/resolver/queries/custom_class_name_test.dart +++ b/drift_dev/test/analysis/resolver/queries/custom_class_name_test.dart @@ -8,7 +8,7 @@ import '../../test_utils.dart'; void main() { Future> analyzeQueries(String driftFile) async { - final state = TestBackend.inTest({'a|lib/a.drift': driftFile}); + final state = await TestBackend.inTest({'a|lib/a.drift': driftFile}); final result = await state.analyze('package:a/a.drift'); return result.fileAnalysis!.resolvedQueries.values; diff --git a/drift_dev/test/analysis/resolver/queries/existing_row_classes_test.dart b/drift_dev/test/analysis/resolver/queries/existing_row_classes_test.dart index e6a84c9a..c5f59293 100644 --- a/drift_dev/test/analysis/resolver/queries/existing_row_classes_test.dart +++ b/drift_dev/test/analysis/resolver/queries/existing_row_classes_test.dart @@ -8,7 +8,7 @@ import 'utils.dart'; void main() { test('recognizes existing row classes', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -35,7 +35,7 @@ class MyRow { }); test('can use named constructors', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -75,7 +75,7 @@ class MyRow { }); test("warns if existing row classes don't exist", () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -91,7 +91,7 @@ foo WITH MyRow: SELECT 'hello world', 2; }); test('resolves existing row class', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -122,7 +122,7 @@ class MyRow { group('matches', () { test('single column type', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' foo WITH int: SELECT 1 AS r; ''', @@ -139,7 +139,7 @@ foo WITH int: SELECT 1 AS r; }); test('single table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -166,7 +166,7 @@ typedef MyRow = TblData; }); test('single table with custom row class', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -195,7 +195,7 @@ class MyTableRow { }); test('alternative to table class', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -229,7 +229,7 @@ class MyQueryRow { group('nested column', () { test('single column into field', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -263,7 +263,7 @@ class MyQueryRow { }); test('single column into single-element record', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -298,7 +298,7 @@ class MyQueryRow { }); test('custom result set', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -344,7 +344,7 @@ class JsonStructure { }); test('table', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -382,7 +382,7 @@ class MyRow { }); test('table as alternative to row class', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -425,7 +425,7 @@ class MyRow { group('nested LIST query', () { test('single column type', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -461,7 +461,7 @@ class MyQueryRow { }); test('custom result set with class', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -503,7 +503,7 @@ class MyNestedTable { }); test('custom result set with record', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -545,7 +545,7 @@ class MyRow { }); test('into record', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -586,7 +586,7 @@ typedef MyRow = (int, List); test( 'default record', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -625,7 +625,7 @@ foo WITH Record: SELECT 1 AS a, LIST(SELECT * FROM tbl) AS b FROM tbl; ); test('mix', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -685,7 +685,7 @@ class MyRow { group('error', () { test('when the specified class has no default constructor', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -705,7 +705,7 @@ class MyRow { }); test('when the desired constructor does not exist', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -725,7 +725,7 @@ class MyRow { }); test('when there is a parameter with no matching column', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -745,7 +745,7 @@ class MyRow { }); test('when a record has too many positional fields', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -767,7 +767,7 @@ typedef MyRow = (int, String, DateTime); }); test('when a record has an unmatched named field', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'a|lib/a.drift': ''' import 'a.dart'; @@ -788,7 +788,7 @@ typedef MyRow = (int, {String d}); }); test('when there is a type mismatch on a scalar column', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -808,7 +808,7 @@ class MyRow { }); test('when a list column is not a list', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; @@ -832,7 +832,7 @@ class MyRow { test( 'when there is a type mismatch on a nested scalar column', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'a.dart'; diff --git a/drift_dev/test/analysis/resolver/queries/inference_test.dart b/drift_dev/test/analysis/resolver/queries/inference_test.dart index 4aec05e0..e00d46c9 100644 --- a/drift_dev/test/analysis/resolver/queries/inference_test.dart +++ b/drift_dev/test/analysis/resolver/queries/inference_test.dart @@ -6,7 +6,7 @@ import '../../test_utils.dart'; void main() { test('experimental inference - integration test', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE artists ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/drift_dev/test/analysis/resolver/queries/linter_test.dart b/drift_dev/test/analysis/resolver/queries/linter_test.dart index a4997ef6..052ac4d2 100644 --- a/drift_dev/test/analysis/resolver/queries/linter_test.dart +++ b/drift_dev/test/analysis/resolver/queries/linter_test.dart @@ -76,7 +76,7 @@ q: SELECT * FROM t WHERE i IN ?1; }); test('warns about default values outside of expressions', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': r''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY, @@ -96,7 +96,7 @@ all ($limit = 3): SELECT * FROM foo LIMIT $limit; }); test('warns when placeholder are used in insert with columns', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': r''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY, @@ -118,7 +118,7 @@ in: INSERT INTO foo (id) $placeholder; test( 'warns when nested results appear in compound statements', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY, @@ -142,7 +142,7 @@ all: SELECT foo.** FROM foo UNION ALL SELECT foo.** FROM foo; test( 'warns when nested query appear in nested query', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY, @@ -175,7 +175,7 @@ all: SELECT foo.**, LIST(SELECT *, LIST(SELECT * FROM foo) FROM foo) FROM foo; } test('in top-level queries', () async { - state = TestBackend.inTest({ + state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY AUTOINCREMENT, @@ -189,7 +189,7 @@ test: INSERT INTO foo VALUES (?) }); test('in CREATE TRIGGER statements', () async { - state = TestBackend.inTest({ + state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY AUTOINCREMENT, @@ -205,7 +205,7 @@ END; }); test('in @create statements', () async { - state = TestBackend.inTest({ + state = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE foo ( id INT NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart b/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart index cd03377e..995f6a5f 100644 --- a/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart +++ b/drift_dev/test/analysis/resolver/queries/queries_from_views_test.dart @@ -5,7 +5,7 @@ import '../../test_utils.dart'; void main() { test('select from view', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'foo|lib/a.drift': ''' CREATE TABLE artists ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/drift_dev/test/analysis/resolver/queries/query_analyzer_test.dart b/drift_dev/test/analysis/resolver/queries/query_analyzer_test.dart index 6bab2e91..76c9728a 100644 --- a/drift_dev/test/analysis/resolver/queries/query_analyzer_test.dart +++ b/drift_dev/test/analysis/resolver/queries/query_analyzer_test.dart @@ -9,7 +9,7 @@ import 'utils.dart'; void main() { test('respects explicit type arguments', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': ''' bar(?1 AS TEXT, :foo AS BOOLEAN): SELECT ?, :foo; ''', @@ -29,7 +29,7 @@ bar(?1 AS TEXT, :foo AS BOOLEAN): SELECT ?, :foo; }); test('can read from builtin tables', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/main.drift': ''' testQuery: SELECT * FROM sqlite_schema; ''', @@ -43,7 +43,7 @@ testQuery: SELECT * FROM sqlite_schema; }); test('reads REQUIRED syntax', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': ''' bar(REQUIRED ?1 AS TEXT OR NULL, REQUIRED :foo AS BOOLEAN): SELECT ?, :foo; ''', @@ -64,7 +64,7 @@ bar(REQUIRED ?1 AS TEXT OR NULL, REQUIRED :foo AS BOOLEAN): SELECT ?, :foo; }); test('infers result set for views', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': r''' CREATE VIEW my_view AS SELECT 'foo', 2; @@ -90,7 +90,7 @@ query: SELECT * FROM my_view; }); test('infers nested result set for views', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': r''' CREATE VIEW my_view AS SELECT 'foo', 2; @@ -123,7 +123,7 @@ query: SELECT foo.**, bar.** FROM my_view foo, my_view bar; }); test('infers nested result sets for custom result sets', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': r''' query: SELECT 1 AS a, b.** FROM (SELECT 2 AS b, 3 AS c) AS b; ''', @@ -154,7 +154,7 @@ query: SELECT 1 AS a, b.** FROM (SELECT 2 AS b, 3 AS c) AS b; for (final dateTimeAsText in [false, true]) { test('analyzing date times (stored as text: $dateTimeAsText)', () async { - final state = TestBackend.inTest( + final state = await TestBackend.inTest( { 'foo|lib/foo.drift': r''' CREATE TABLE foo ( @@ -202,7 +202,7 @@ q3: SELECT datetime('now'); } test('resolves nested result sets', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': r''' CREATE TABLE points ( id INTEGER NOT NULL PRIMARY KEY, @@ -243,7 +243,7 @@ FROM routes }); test('resolves nullability of aliases in nested result sets', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'foo|lib/main.drift': r''' CREATE TABLE tableA1 (id INTEGER); CREATE TABLE tableB1 (id INTEGER); @@ -285,7 +285,7 @@ LEFT JOIN tableB1 AS tableB2 -- nullable test('supports custom functions', () async { final withoutOptions = - TestBackend.inTest({'a|lib/a.drift': 'a: SELECT my_function();'}); + await TestBackend.inTest({'a|lib/a.drift': 'a: SELECT my_function();'}); var result = await withoutOptions.analyze('package:a/a.drift'); expect(result.allErrors, [ isDriftError('Function my_function could not be found') @@ -294,14 +294,13 @@ LEFT JOIN tableB1 AS tableB2 -- nullable .withSpan('my_function()'), ]); - final withOptions = - TestBackend.inTest({'a|lib/a.drift': 'a: SELECT my_function(?, ?);'}, - options: DriftOptions.defaults( - sqliteAnalysisOptions: SqliteAnalysisOptions(knownFunctions: { - 'my_function': - KnownSqliteFunction.fromJson('boolean (int, text)') - }), - )); + final withOptions = await TestBackend.inTest( + {'a|lib/a.drift': 'a: SELECT my_function(?, ?);'}, + options: DriftOptions.defaults( + sqliteAnalysisOptions: SqliteAnalysisOptions(knownFunctions: { + 'my_function': KnownSqliteFunction.fromJson('boolean (int, text)') + }), + )); result = await withOptions.analyze('package:a/a.drift'); withOptions.expectNoErrors(); @@ -318,7 +317,7 @@ LEFT JOIN tableB1 AS tableB2 -- nullable }); test('can cast to DATETIME and BOOLEAN', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' a: SELECT CAST(1 AS BOOLEAN) AS a, CAST(2 AS DATETIME) as b; ''', @@ -337,7 +336,7 @@ a: SELECT CAST(1 AS BOOLEAN) AS a, CAST(2 AS DATETIME) as b; }); test('can cast to enum type', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'enum.dart'; diff --git a/drift_dev/test/analysis/resolver/resolver_test.dart b/drift_dev/test/analysis/resolver/resolver_test.dart index 29f389c8..5e7e3ec8 100644 --- a/drift_dev/test/analysis/resolver/resolver_test.dart +++ b/drift_dev/test/analysis/resolver/resolver_test.dart @@ -7,7 +7,7 @@ import '../test_utils.dart'; void main() { group('from clean state', () { test('resolves simple tables', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE a ( foo INTEGER PRIMARY KEY, @@ -42,7 +42,7 @@ CREATE TABLE b ( group('references', () { test('self', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE a ( foo INTEGER PRIMARY KEY, @@ -61,7 +61,7 @@ CREATE TABLE a ( }); test('across files', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'b.drift'; @@ -93,7 +93,7 @@ CREATE TABLE b ( }); test('for triggers', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'b.drift'; @@ -133,7 +133,7 @@ CREATE TABLE deleted_b ( group('non-existing', () { test('from table', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE a ( foo INTEGER PRIMARY KEY, @@ -151,7 +151,7 @@ CREATE TABLE a ( [isDriftError('`b` could not be found in any import.')]); }); test('in a trigger', () async { - final backend = TestBackend.inTest(const { + final backend = await TestBackend.inTest(const { 'foo|lib/a.drift': ''' CREATE TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar BEGIN END; @@ -172,7 +172,7 @@ END; }); test('emits warning on invalid import', () async { - final backend = TestBackend.inTest({ + final backend = await TestBackend.inTest({ 'a|lib/a.drift': "import 'b.drift';", }); diff --git a/drift_dev/test/analysis/results/dart_test.dart b/drift_dev/test/analysis/results/dart_test.dart index e4d0537c..04bb1cc6 100644 --- a/drift_dev/test/analysis/results/dart_test.dart +++ b/drift_dev/test/analysis/results/dart_test.dart @@ -8,7 +8,7 @@ import '../test_utils.dart'; void main() { late TestBackend tester; - setUpAll(() => tester = TestBackend({})); + setUpAll(() async => tester = await TestBackend.init({})); tearDownAll(() => tester.dispose()); group('from AST', () { diff --git a/drift_dev/test/analysis/test_utils.dart b/drift_dev/test/analysis/test_utils.dart index df38ca6c..a570eeee 100644 --- a/drift_dev/test/analysis/test_utils.dart +++ b/drift_dev/test/analysis/test_utils.dart @@ -37,24 +37,43 @@ class TestBackend extends DriftBackend { AnalysisContext? _dartContext; OverlayResourceProvider? _resourceProvider; - TestBackend( + TestBackend._( Map sourceContents, { DriftOptions options = const DriftOptions.defaults(), this.analyzerExperiments = const Iterable.empty(), }) : sourceContents = { for (final entry in sourceContents.entries) AssetId.parse(entry.key).uri.toString(): entry.value, - } { - driver = DriftAnalysisDriver(this, options, isTesting: true); - } + }; - factory TestBackend.inTest( + static Future init( Map sourceContents, { DriftOptions options = const DriftOptions.defaults(), Iterable analyzerExperiments = const Iterable.empty(), - }) { - final backend = TestBackend(sourceContents, - options: options, analyzerExperiments: analyzerExperiments); + }) async { + final backend = TestBackend._( + sourceContents, + options: options, + analyzerExperiments: analyzerExperiments, + ); + + backend.driver = + await DriftAnalysisDriver.init(backend, options, isTesting: true); + + return backend; + } + + static Future inTest( + Map sourceContents, { + DriftOptions options = const DriftOptions.defaults(), + Iterable analyzerExperiments = const Iterable.empty(), + }) async { + final backend = await TestBackend.init( + sourceContents, + options: options, + analyzerExperiments: analyzerExperiments, + ); + addTearDown(backend.dispose); return backend; @@ -62,9 +81,10 @@ class TestBackend extends DriftBackend { static Future analyzeSingle(String content, {String asset = 'a|lib/a.drift', - DriftOptions options = const DriftOptions.defaults()}) { + DriftOptions options = const DriftOptions.defaults()}) async { final assetId = AssetId.parse(asset); - final backend = TestBackend.inTest({asset: content}, options: options); + final backend = + await TestBackend.inTest({asset: content}, options: options); return backend.driver.fullyAnalyze(assetId.uri); } diff --git a/drift_dev/test/services/find_stream_update_rules_test.dart b/drift_dev/test/services/find_stream_update_rules_test.dart index 75671a92..3df8e921 100644 --- a/drift_dev/test/services/find_stream_update_rules_test.dart +++ b/drift_dev/test/services/find_stream_update_rules_test.dart @@ -7,7 +7,7 @@ import '../analysis/test_utils.dart'; void main() { test('finds update rules for triggers', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE users ( id INTEGER NOT NULL PRIMARY KEY, @@ -51,7 +51,7 @@ class MyDatabase {} }); test('finds update rules for foreign key constraint', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' CREATE TABLE a ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, diff --git a/drift_dev/test/services/schema/reader_test.dart b/drift_dev/test/services/schema/reader_test.dart index 42ed761f..6ef9a06f 100644 --- a/drift_dev/test/services/schema/reader_test.dart +++ b/drift_dev/test/services/schema/reader_test.dart @@ -95,7 +95,7 @@ CREATE VIEW user_ids AS SELECT id FROM users; } Future> _analyzeAndSerialize(String source) async { - final state = TestBackend.inTest({'a|lib/a.drift': source}); + final state = await TestBackend.inTest({'a|lib/a.drift': source}); final file = await state.analyze('package:a/a.drift'); final writer = SchemaWriter(file.analyzedElements.toList()); diff --git a/drift_dev/test/services/schema/writer_test.dart b/drift_dev/test/services/schema/writer_test.dart index 3a357b37..e530bb71 100644 --- a/drift_dev/test/services/schema/writer_test.dart +++ b/drift_dev/test/services/schema/writer_test.dart @@ -14,7 +14,7 @@ import '../../analysis/test_utils.dart'; void main() { test('writer integration test', () async { - final state = TestBackend.inTest({ + final state = await TestBackend.inTest({ 'a|lib/a.drift': ''' import 'main.dart'; diff --git a/drift_dev/test/writer/queries/query_writer_test.dart b/drift_dev/test/writer/queries/query_writer_test.dart index de92f6a6..bc7d10dc 100644 --- a/drift_dev/test/writer/queries/query_writer_test.dart +++ b/drift_dev/test/writer/queries/query_writer_test.dart @@ -15,8 +15,8 @@ void main() { {DriftOptions options = const DriftOptions.defaults( generateNamedParameters: true, )}) async { - final state = - TestBackend.inTest({'a|lib/main.drift': driftFile}, options: options); + final state = await TestBackend.inTest({'a|lib/main.drift': driftFile}, + options: options); final file = await state.analyze('package:a/main.drift'); state.expectNoErrors(); From 9a1dc2b0e3c644192293253b94e3fe1bf8ad55a4 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Sat, 20 Apr 2024 21:01:32 +0200 Subject: [PATCH 5/9] update docs --- docs/pages/docs/Generation options/index.md | 3 +++ docs/pages/docs/SQL API/extensions.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pages/docs/Generation options/index.md b/docs/pages/docs/Generation options/index.md index 0246caa1..d3bab9fe 100644 --- a/docs/pages/docs/Generation options/index.md +++ b/docs/pages/docs/Generation options/index.md @@ -186,6 +186,9 @@ We currently support the following extensions: - `spellfix1`: Assumes that the [spellfix1](https://www.sqlite.org/spellfix1.html) module is available. Note that this is not the case for most sqlite3 builds, including the ones shipping with `sqlite3_flutter_libs`. +- `geopoly`: Assumes that the [geopoly](https://www.sqlite.org/geopoly.html) + module is available. Note that this is not the case for most sqlite3 builds, + including the ones shipping with `sqlite3_flutter_libs`. ### Known custom functions diff --git a/docs/pages/docs/SQL API/extensions.md b/docs/pages/docs/SQL API/extensions.md index a91188cb..cfd9ef43 100644 --- a/docs/pages/docs/SQL API/extensions.md +++ b/docs/pages/docs/SQL API/extensions.md @@ -89,7 +89,7 @@ for doing linear transformations of polygons, for rendering polygons as [SVG](https://en.wikipedia.org/wiki/SVG), and other similar operations. -To enable the fts5 extension in drift files and compiled queries, modify the +To enable the `geopoly` extension in drift files and compiled queries, modify the [build options]({{ "../Generation options/index.md" | pageUrl }}) to include `geopoly` in the `sqlite_module` section. From 7c6e36e4f4e7fca56d950467c63cade4e7604f82 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Sun, 21 Apr 2024 13:00:57 +0200 Subject: [PATCH 6/9] fix no backend error --- drift_dev/lib/src/analysis/backend.dart | 2 ++ drift_dev/lib/src/analysis/driver/driver.dart | 6 ++++-- drift_dev/lib/src/analysis/resolver/dart/helper.dart | 10 +++++++--- .../lib/src/backends/analyzer_context_backend.dart | 3 +++ drift_dev/lib/src/backends/build/backend.dart | 3 +++ drift_dev/lib/src/services/schema/sqlite_to_drift.dart | 3 +++ drift_dev/test/analysis/test_utils.dart | 3 +++ 7 files changed, 25 insertions(+), 5 deletions(-) diff --git a/drift_dev/lib/src/analysis/backend.dart b/drift_dev/lib/src/analysis/backend.dart index 72c9729c..ac37545b 100644 --- a/drift_dev/lib/src/analysis/backend.dart +++ b/drift_dev/lib/src/analysis/backend.dart @@ -19,6 +19,8 @@ abstract class DriftBackend { return element.source!.uri; } + bool get canReadDart; + /// Resolves a Dart library by its uri. /// /// This should also be able to resolve SDK libraries. diff --git a/drift_dev/lib/src/analysis/driver/driver.dart b/drift_dev/lib/src/analysis/driver/driver.dart index 4992a3b9..a52aaab4 100644 --- a/drift_dev/lib/src/analysis/driver/driver.dart +++ b/drift_dev/lib/src/analysis/driver/driver.dart @@ -63,13 +63,15 @@ class DriftAnalysisDriver { AnalysisResultCacheReader? cacheReader; - final KnownDriftTypes knownTypes; + final KnownDriftTypes? _knownTypes; + + KnownDriftTypes get knownTypes => _knownTypes!; @visibleForTesting DriftAnalysisDriver( this.backend, this.options, - this.knownTypes, { + this._knownTypes, { bool isTesting = false, }) : _isTesting = isTesting; diff --git a/drift_dev/lib/src/analysis/resolver/dart/helper.dart b/drift_dev/lib/src/analysis/resolver/dart/helper.dart index f73659ad..d329924b 100644 --- a/drift_dev/lib/src/analysis/resolver/dart/helper.dart +++ b/drift_dev/lib/src/analysis/resolver/dart/helper.dart @@ -102,10 +102,14 @@ class KnownDriftTypes { return type?.asInstanceOf(converter); } - static Future resolve(DriftBackend backend) async { - final library = await backend.readDart(uri); + static Future resolve(DriftBackend backend) async { + if (backend.canReadDart) { + final library = await backend.readDart(uri); - return KnownDriftTypes._fromLibrary(library); + return KnownDriftTypes._fromLibrary(library); + } + + return null; } static final Uri uri = Uri.parse('package:drift/src/drift_dev_helper.dart'); diff --git a/drift_dev/lib/src/backends/analyzer_context_backend.dart b/drift_dev/lib/src/backends/analyzer_context_backend.dart index 6a8c93b5..5369c04b 100644 --- a/drift_dev/lib/src/backends/analyzer_context_backend.dart +++ b/drift_dev/lib/src/backends/analyzer_context_backend.dart @@ -96,6 +96,9 @@ class AnalysisContextBackend extends DriftBackend { return Future.value(resourceProvider.getFile(path).readAsStringSync()); } + @override + bool get canReadDart => true; + @override Future readDart(Uri uri) async { final result = await context.currentSession.getLibraryByUri(uri.toString()); diff --git a/drift_dev/lib/src/backends/build/backend.dart b/drift_dev/lib/src/backends/build/backend.dart index bbcf802d..81c27730 100644 --- a/drift_dev/lib/src/backends/build/backend.dart +++ b/drift_dev/lib/src/backends/build/backend.dart @@ -38,6 +38,9 @@ class DriftBuildBackend extends DriftBackend { return id.uri; } + @override + bool get canReadDart => true; + @override Future readDart(Uri uri) async { if (uri.scheme == 'dart') { diff --git a/drift_dev/lib/src/services/schema/sqlite_to_drift.dart b/drift_dev/lib/src/services/schema/sqlite_to_drift.dart index 49bd9151..372b2261 100644 --- a/drift_dev/lib/src/services/schema/sqlite_to_drift.dart +++ b/drift_dev/lib/src/services/schema/sqlite_to_drift.dart @@ -91,6 +91,9 @@ class _SingleFileNoAnalyzerBackend extends DriftBackend { return Future.value(contents); } + @override + bool get canReadDart => false; + @override Future readDart(Uri uri) async { _noAnalyzer(); diff --git a/drift_dev/test/analysis/test_utils.dart b/drift_dev/test/analysis/test_utils.dart index a570eeee..04cfb78d 100644 --- a/drift_dev/test/analysis/test_utils.dart +++ b/drift_dev/test/analysis/test_utils.dart @@ -237,6 +237,9 @@ class TestBackend extends DriftBackend { return null; } + @override + bool get canReadDart => true; + @override Future readDart(Uri uri) async { await ensureHasDartAnalyzer(); From efc5a6c985bd43ea2baaa51f93ac7d6fde82d1c0 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 22 Apr 2024 22:51:07 +0200 Subject: [PATCH 7/9] More docs, integration tests --- docs/pages/docs/Generation options/index.md | 2 + drift/build.yaml | 2 + drift/lib/extensions/geopoly.dart | 24 +- drift/test/extensions/geopoly.drift | 3 + .../extensions/geopoly_integration_test.dart | 49 ++++ .../geopoly_integration_test.g.dart | 226 ++++++++++++++++++ drift/test/test_utils/test_utils.mocks.dart | 2 +- .../build/build_integration_test.dart | 2 + 8 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 drift/test/extensions/geopoly.drift create mode 100644 drift/test/extensions/geopoly_integration_test.dart create mode 100644 drift/test/extensions/geopoly_integration_test.g.dart diff --git a/docs/pages/docs/Generation options/index.md b/docs/pages/docs/Generation options/index.md index 0246caa1..50d836ec 100644 --- a/docs/pages/docs/Generation options/index.md +++ b/docs/pages/docs/Generation options/index.md @@ -179,6 +179,8 @@ We currently support the following extensions: - `rtree`: Static analysis support for the [R*Tree](https://www.sqlite.org/rtree.html) extension. Enabling this option is safe when using a `NativeDatabase` with `sqlite3_flutter_libs`, which compiles sqlite3 with the R*Tree extension enabled. +- [geopoly](https://www.sqlite.org/geopoly.html), a generalization of the R*Tree module supporting more complex + polygons. - `moor_ffi`: Enables support for functions that are only available when using a `NativeDatabase`. This contains `pow`, `sqrt` and a variety of trigonometric functions. Details on those functions are available [here]({{ "../Platforms/vm.md#moor-only-functions" | pageUrl }}). - `math`: Assumes that sqlite3 was compiled with [math functions](https://www.sqlite.org/lang_mathfunc.html). diff --git a/drift/build.yaml b/drift/build.yaml index 52b9a734..1aa5be5e 100644 --- a/drift/build.yaml +++ b/drift/build.yaml @@ -29,6 +29,7 @@ targets: modules: - json1 - fts5 + - geopoly build_web_compilers:entrypoint: generate_for: - "web/drift_worker.dart" @@ -59,3 +60,4 @@ targets: modules: - json1 - fts5 + - geopoly diff --git a/drift/lib/extensions/geopoly.dart b/drift/lib/extensions/geopoly.dart index fbc401aa..4bd104a3 100644 --- a/drift/lib/extensions/geopoly.dart +++ b/drift/lib/extensions/geopoly.dart @@ -8,9 +8,13 @@ import 'dart:typed_data'; import '../src/runtime/query_builder/query_builder.dart'; import '../src/runtime/types/mapping.dart'; +/// The type used for the `_shape` column in virtual `GEOPOLY` tables. /// +/// This type is responsible for representing shape values in Dart. It is +/// created by drift when the `geopoly` extension is enabled and a `CREATE +/// VIRTUAL TABLE USING geopoly` table is declared in a `.drift` file. final class GeopolyPolygonType implements CustomSqlType { - /// + /// Default constant constructor for the geopoly type. const GeopolyPolygonType(); @override @@ -43,29 +47,33 @@ final class GeopolyPolygonType implements CustomSqlType { } } -/// In Geopoly, a polygon can be text or a blob +/// In Geopoly, a polygon can be text or a blob. sealed class GeopolyPolygon { const GeopolyPolygon._(); + /// Creates a geopoly shape from a textual representation listing its points. + /// + /// For details on the syntax for [value], see https://www.sqlite.org/geopoly.html. const factory GeopolyPolygon.text(String value) = GeopolyPolygonString; + /// Creates a geopoly shape from the binary representation used by sqlite3. const factory GeopolyPolygon.blob(Uint8List value) = GeopolyPolygonBlob; } -/// +/// A [GeopolyPolygon] being described as text. final class GeopolyPolygonString extends GeopolyPolygon { - /// + /// The textual description of the polygon. final String value; - /// + /// Creates a polygon from the underlying textual [value]. const GeopolyPolygonString(this.value) : super._(); } -/// +/// A [GeopolyPolygon] being described as binary data. final class GeopolyPolygonBlob extends GeopolyPolygon { - /// + /// The binary description of the polygon. final Uint8List value; - /// + /// Creates a polygon from the underlying binary [value]. const GeopolyPolygonBlob(this.value) : super._(); } diff --git a/drift/test/extensions/geopoly.drift b/drift/test/extensions/geopoly.drift new file mode 100644 index 00000000..de46f95d --- /dev/null +++ b/drift/test/extensions/geopoly.drift @@ -0,0 +1,3 @@ +CREATE VIRTUAL TABLE geopoly_test USING geopoly(a); + +area: SELECT geopoly_area(_shape) FROM geopoly_test WHERE rowid = ?; diff --git a/drift/test/extensions/geopoly_integration_test.dart b/drift/test/extensions/geopoly_integration_test.dart new file mode 100644 index 00000000..1a029029 --- /dev/null +++ b/drift/test/extensions/geopoly_integration_test.dart @@ -0,0 +1,49 @@ +@TestOn('vm') +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:drift/extensions/geopoly.dart'; +import 'package:sqlite3/sqlite3.dart'; +import 'package:test/test.dart'; + +import '../test_utils/database_vm.dart'; + +part 'geopoly_integration_test.g.dart'; + +void main() { + preferLocalSqlite3(); + + test( + 'can access geopoly types', + () async { + final database = _GeopolyTestDatabase(NativeDatabase.memory()); + expect(database.geopolyTest.shape.type, isA()); + + final id = + await database.geopolyTest.insertOne(GeopolyTestCompanion.insert( + shape: Value(GeopolyPolygon.text('[[0,0],[1,0],[0.5,1],[0,0]]')), + )); + + final area = await database.area(id).getSingle(); + expect(area, 0.5); + }, + skip: _canUseGeopoly() + ? null + : 'Cannot test, your sqlite3 does not support geopoly.', + ); +} + +bool _canUseGeopoly() { + final db = sqlite3.openInMemory(); + final result = db + .select('SELECT sqlite_compileoption_used(?)', ['ENABLE_GEOPOLY']).single; + db.dispose(); + return result.values[0] == 1; +} + +@DriftDatabase(include: {'geopoly.drift'}) +class _GeopolyTestDatabase extends _$_GeopolyTestDatabase { + _GeopolyTestDatabase(super.e); + + @override + int get schemaVersion => 1; +} diff --git a/drift/test/extensions/geopoly_integration_test.g.dart b/drift/test/extensions/geopoly_integration_test.g.dart new file mode 100644 index 00000000..a307c839 --- /dev/null +++ b/drift/test/extensions/geopoly_integration_test.g.dart @@ -0,0 +1,226 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'geopoly_integration_test.dart'; + +// ignore_for_file: type=lint +class GeopolyTest extends Table + with + TableInfo, + VirtualTableInfo { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + GeopolyTest(this.attachedDatabase, [this._alias]); + static const VerificationMeta _shapeMeta = const VerificationMeta('shape'); + late final GeneratedColumn shape = + GeneratedColumn('_shape', aliasedName, true, + type: const GeopolyPolygonType(), + requiredDuringInsert: false, + $customConstraints: ''); + static const VerificationMeta _aMeta = const VerificationMeta('a'); + late final GeneratedColumn a = GeneratedColumn( + 'a', aliasedName, true, + type: DriftSqlType.any, + requiredDuringInsert: false, + $customConstraints: ''); + @override + List get $columns => [shape, a]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'geopoly_test'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('_shape')) { + context.handle( + _shapeMeta, shape.isAcceptableOrUnknown(data['_shape']!, _shapeMeta)); + } + if (data.containsKey('a')) { + context.handle(_aMeta, a.isAcceptableOrUnknown(data['a']!, _aMeta)); + } + return context; + } + + @override + Set get $primaryKey => const {}; + @override + GeopolyTestData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return GeopolyTestData( + shape: attachedDatabase.typeMapping + .read(const GeopolyPolygonType(), data['${effectivePrefix}_shape']), + a: attachedDatabase.typeMapping + .read(DriftSqlType.any, data['${effectivePrefix}a']), + ); + } + + @override + GeopolyTest createAlias(String alias) { + return GeopolyTest(attachedDatabase, alias); + } + + @override + bool get dontWriteConstraints => true; + @override + String get moduleAndArgs => 'geopoly(a)'; +} + +class GeopolyTestData extends DataClass implements Insertable { + final GeopolyPolygon? shape; + final DriftAny? a; + const GeopolyTestData({this.shape, this.a}); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || shape != null) { + map['_shape'] = + Variable(shape, const GeopolyPolygonType()); + } + if (!nullToAbsent || a != null) { + map['a'] = Variable(a); + } + return map; + } + + GeopolyTestCompanion toCompanion(bool nullToAbsent) { + return GeopolyTestCompanion( + shape: + shape == null && nullToAbsent ? const Value.absent() : Value(shape), + a: a == null && nullToAbsent ? const Value.absent() : Value(a), + ); + } + + factory GeopolyTestData.fromJson(Map json, + {ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return GeopolyTestData( + shape: serializer.fromJson(json['_shape']), + a: serializer.fromJson(json['a']), + ); + } + factory GeopolyTestData.fromJsonString(String encodedJson, + {ValueSerializer? serializer}) => + GeopolyTestData.fromJson( + DataClass.parseJson(encodedJson) as Map, + serializer: serializer); + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + '_shape': serializer.toJson(shape), + 'a': serializer.toJson(a), + }; + } + + GeopolyTestData copyWith( + {Value shape = const Value.absent(), + Value a = const Value.absent()}) => + GeopolyTestData( + shape: shape.present ? shape.value : this.shape, + a: a.present ? a.value : this.a, + ); + @override + String toString() { + return (StringBuffer('GeopolyTestData(') + ..write('shape: $shape, ') + ..write('a: $a') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(shape, a); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is GeopolyTestData && + other.shape == this.shape && + other.a == this.a); +} + +class GeopolyTestCompanion extends UpdateCompanion { + final Value shape; + final Value a; + final Value rowid; + const GeopolyTestCompanion({ + this.shape = const Value.absent(), + this.a = const Value.absent(), + this.rowid = const Value.absent(), + }); + GeopolyTestCompanion.insert({ + this.shape = const Value.absent(), + this.a = const Value.absent(), + this.rowid = const Value.absent(), + }); + static Insertable custom({ + Expression? shape, + Expression? a, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (shape != null) '_shape': shape, + if (a != null) 'a': a, + if (rowid != null) 'rowid': rowid, + }); + } + + GeopolyTestCompanion copyWith( + {Value? shape, Value? a, Value? rowid}) { + return GeopolyTestCompanion( + shape: shape ?? this.shape, + a: a ?? this.a, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (shape.present) { + map['_shape'] = + Variable(shape.value, const GeopolyPolygonType()); + } + if (a.present) { + map['a'] = Variable(a.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('GeopolyTestCompanion(') + ..write('shape: $shape, ') + ..write('a: $a, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +abstract class _$_GeopolyTestDatabase extends GeneratedDatabase { + _$_GeopolyTestDatabase(QueryExecutor e) : super(e); + late final GeopolyTest geopolyTest = GeopolyTest(this); + Selectable area(int var1) { + return customSelect( + 'SELECT geopoly_area(_shape) AS _c0 FROM geopoly_test WHERE "rowid" = ?1', + variables: [ + Variable(var1) + ], + readsFrom: { + geopolyTest, + }).map((QueryRow row) => row.readNullable('_c0')); + } + + @override + Iterable> get allTables => + allSchemaEntities.whereType>(); + @override + List get allSchemaEntities => [geopolyTest]; +} diff --git a/drift/test/test_utils/test_utils.mocks.dart b/drift/test/test_utils/test_utils.mocks.dart index 03d207d3..0d052c05 100644 --- a/drift/test/test_utils/test_utils.mocks.dart +++ b/drift/test/test_utils/test_utils.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.4.3 from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in drift/test/test_utils/test_utils.dart. // Do not manually edit this file. diff --git a/drift_dev/test/backends/build/build_integration_test.dart b/drift_dev/test/backends/build/build_integration_test.dart index d81f397f..e1828132 100644 --- a/drift_dev/test/backends/build/build_integration_test.dart +++ b/drift_dev/test/backends/build/build_integration_test.dart @@ -583,6 +583,8 @@ class MyDatabase { return everyElement( anyOf( isA().having((e) => e.extension, 'extension', '.json'), + // Allow reading SDK or other package assets to set up the analyzer. + isA().having((e) => e.package, 'package', isNot('a')), other, ), ); From c5959cc446f9d1febe3b9c737a2248608ec756c8 Mon Sep 17 00:00:00 2001 From: Nikita Dauhashei Date: Mon, 22 Apr 2024 23:22:18 +0200 Subject: [PATCH 8/9] fix docs --- docs/pages/docs/Generation options/index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/pages/docs/Generation options/index.md b/docs/pages/docs/Generation options/index.md index c41198b0..bc6b843a 100644 --- a/docs/pages/docs/Generation options/index.md +++ b/docs/pages/docs/Generation options/index.md @@ -180,7 +180,8 @@ We currently support the following extensions: Enabling this option is safe when using a `NativeDatabase` with `sqlite3_flutter_libs`, which compiles sqlite3 with the R*Tree extension enabled. - [geopoly](https://www.sqlite.org/geopoly.html), a generalization of the R*Tree module supporting more complex - polygons. + polygons. Note that this is not the case for most sqlite3 builds, + including the ones shipping with `sqlite3_flutter_libs`. - `moor_ffi`: Enables support for functions that are only available when using a `NativeDatabase`. This contains `pow`, `sqrt` and a variety of trigonometric functions. Details on those functions are available [here]({{ "../Platforms/vm.md#moor-only-functions" | pageUrl }}). - `math`: Assumes that sqlite3 was compiled with [math functions](https://www.sqlite.org/lang_mathfunc.html). @@ -188,9 +189,6 @@ We currently support the following extensions: - `spellfix1`: Assumes that the [spellfix1](https://www.sqlite.org/spellfix1.html) module is available. Note that this is not the case for most sqlite3 builds, including the ones shipping with `sqlite3_flutter_libs`. -- `geopoly`: Assumes that the [geopoly](https://www.sqlite.org/geopoly.html) - module is available. Note that this is not the case for most sqlite3 builds, - including the ones shipping with `sqlite3_flutter_libs`. ### Known custom functions From ccae3b4ee64af36bbe8b350b05adbd7650219888 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Tue, 23 Apr 2024 15:04:22 +0200 Subject: [PATCH 9/9] Expand allowlist for assets in test --- .../test/backends/build/build_integration_test.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drift_dev/test/backends/build/build_integration_test.dart b/drift_dev/test/backends/build/build_integration_test.dart index e1828132..9027541f 100644 --- a/drift_dev/test/backends/build/build_integration_test.dart +++ b/drift_dev/test/backends/build/build_integration_test.dart @@ -578,13 +578,15 @@ class MyDatabase { }; final outputs = await emulateDriftBuild(inputs: inputs); final readAssets = outputs.readAssetsByBuilder; + // Allow reading SDK or other package assets to set up the analyzer. + final isFromExternalPackage = + isA().having((e) => e.package, 'package', isNot('a')); Matcher onlyReadsJsonsAnd(dynamic other) { return everyElement( anyOf( isA().having((e) => e.extension, 'extension', '.json'), - // Allow reading SDK or other package assets to set up the analyzer. - isA().having((e) => e.package, 'package', isNot('a')), + isFromExternalPackage, other, ), ); @@ -608,7 +610,8 @@ class MyDatabase { // However, the discover builder should not read other drift files. for (final input in inputs.keys) { if (input.endsWith('.drift')) { - expectReadsForBuilder(input, DriftDiscover, [makeAssetId(input)]); + expectReadsForBuilder(input, DriftDiscover, + everyElement(anyOf(makeAssetId(input), isFromExternalPackage))); } else { expectReadsForBuilder( input,