mirror of https://github.com/AMT-Cheif/drift.git
Merge pull request #2946 from nikitadol/develop
Add support for geopoly Interface
This commit is contained in:
commit
8865e9360f
|
@ -180,7 +180,10 @@ We currently support the following extensions:
|
||||||
Functions like `highlight` or `bm25` are available as well.
|
Functions like `highlight` or `bm25` are available as well.
|
||||||
- `rtree`: Static analysis support for the [R\*Tree](https://www.sqlite.org/rtree.html) extension.
|
- `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`,
|
Enabling this option is safe when using a `NativeDatabase` with `sqlite3_flutter_libs`,
|
||||||
which compiles sqlite3 with the R\*Tree extension enabled.
|
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. 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
|
- `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 }}).
|
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).
|
- `math`: Assumes that sqlite3 was compiled with [math functions](https://www.sqlite.org/lang_mathfunc.html).
|
||||||
|
|
|
@ -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.
|
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).
|
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 `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.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
|
@ -29,6 +29,7 @@ targets:
|
||||||
modules:
|
modules:
|
||||||
- json1
|
- json1
|
||||||
- fts5
|
- fts5
|
||||||
|
- geopoly
|
||||||
build_web_compilers:entrypoint:
|
build_web_compilers:entrypoint:
|
||||||
generate_for:
|
generate_for:
|
||||||
- "web/drift_worker.dart"
|
- "web/drift_worker.dart"
|
||||||
|
@ -59,3 +60,4 @@ targets:
|
||||||
modules:
|
modules:
|
||||||
- json1
|
- json1
|
||||||
- fts5
|
- fts5
|
||||||
|
- geopoly
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/// 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';
|
||||||
|
|
||||||
|
/// 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<GeopolyPolygon> {
|
||||||
|
/// Default constant constructor for the geopoly type.
|
||||||
|
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._();
|
||||||
|
|
||||||
|
/// 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._();
|
||||||
|
}
|
|
@ -7,3 +7,5 @@ export 'runtime/query_builder/query_builder.dart' show TableInfo;
|
||||||
|
|
||||||
export 'dsl/dsl.dart'
|
export 'dsl/dsl.dart'
|
||||||
show Table, TableIndex, View, DriftDatabase, DriftAccessor;
|
show Table, TableIndex, View, DriftDatabase, DriftAccessor;
|
||||||
|
|
||||||
|
export '../extensions/geopoly.dart';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'dart:core';
|
|
||||||
import 'dart:core' as core;
|
import 'dart:core' as core;
|
||||||
|
import 'dart:core';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE VIRTUAL TABLE geopoly_test USING geopoly(a);
|
||||||
|
|
||||||
|
area: SELECT geopoly_area(_shape) FROM geopoly_test WHERE rowid = ?;
|
|
@ -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<GeopolyPolygonType>());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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<GeopolyTest, GeopolyTestData>,
|
||||||
|
VirtualTableInfo<GeopolyTest, GeopolyTestData> {
|
||||||
|
@override
|
||||||
|
final GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
GeopolyTest(this.attachedDatabase, [this._alias]);
|
||||||
|
static const VerificationMeta _shapeMeta = const VerificationMeta('shape');
|
||||||
|
late final GeneratedColumn<GeopolyPolygon> shape =
|
||||||
|
GeneratedColumn<GeopolyPolygon>('_shape', aliasedName, true,
|
||||||
|
type: const GeopolyPolygonType(),
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
$customConstraints: '');
|
||||||
|
static const VerificationMeta _aMeta = const VerificationMeta('a');
|
||||||
|
late final GeneratedColumn<DriftAny> a = GeneratedColumn<DriftAny>(
|
||||||
|
'a', aliasedName, true,
|
||||||
|
type: DriftSqlType.any,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
$customConstraints: '');
|
||||||
|
@override
|
||||||
|
List<GeneratedColumn> 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<GeopolyTestData> 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<GeneratedColumn> get $primaryKey => const {};
|
||||||
|
@override
|
||||||
|
GeopolyTestData map(Map<String, dynamic> 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<GeopolyTestData> {
|
||||||
|
final GeopolyPolygon? shape;
|
||||||
|
final DriftAny? a;
|
||||||
|
const GeopolyTestData({this.shape, this.a});
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (!nullToAbsent || shape != null) {
|
||||||
|
map['_shape'] =
|
||||||
|
Variable<GeopolyPolygon>(shape, const GeopolyPolygonType());
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || a != null) {
|
||||||
|
map['a'] = Variable<DriftAny>(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<String, dynamic> json,
|
||||||
|
{ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return GeopolyTestData(
|
||||||
|
shape: serializer.fromJson<GeopolyPolygon?>(json['_shape']),
|
||||||
|
a: serializer.fromJson<DriftAny?>(json['a']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
factory GeopolyTestData.fromJsonString(String encodedJson,
|
||||||
|
{ValueSerializer? serializer}) =>
|
||||||
|
GeopolyTestData.fromJson(
|
||||||
|
DataClass.parseJson(encodedJson) as Map<String, dynamic>,
|
||||||
|
serializer: serializer);
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||||
|
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'_shape': serializer.toJson<GeopolyPolygon?>(shape),
|
||||||
|
'a': serializer.toJson<DriftAny?>(a),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
GeopolyTestData copyWith(
|
||||||
|
{Value<GeopolyPolygon?> shape = const Value.absent(),
|
||||||
|
Value<DriftAny?> 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<GeopolyTestData> {
|
||||||
|
final Value<GeopolyPolygon?> shape;
|
||||||
|
final Value<DriftAny?> a;
|
||||||
|
final Value<int> 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<GeopolyTestData> custom({
|
||||||
|
Expression<GeopolyPolygon>? shape,
|
||||||
|
Expression<DriftAny>? a,
|
||||||
|
Expression<int>? rowid,
|
||||||
|
}) {
|
||||||
|
return RawValuesInsertable({
|
||||||
|
if (shape != null) '_shape': shape,
|
||||||
|
if (a != null) 'a': a,
|
||||||
|
if (rowid != null) 'rowid': rowid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
GeopolyTestCompanion copyWith(
|
||||||
|
{Value<GeopolyPolygon?>? shape, Value<DriftAny?>? a, Value<int>? rowid}) {
|
||||||
|
return GeopolyTestCompanion(
|
||||||
|
shape: shape ?? this.shape,
|
||||||
|
a: a ?? this.a,
|
||||||
|
rowid: rowid ?? this.rowid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, Expression>{};
|
||||||
|
if (shape.present) {
|
||||||
|
map['_shape'] =
|
||||||
|
Variable<GeopolyPolygon>(shape.value, const GeopolyPolygonType());
|
||||||
|
}
|
||||||
|
if (a.present) {
|
||||||
|
map['a'] = Variable<DriftAny>(a.value);
|
||||||
|
}
|
||||||
|
if (rowid.present) {
|
||||||
|
map['rowid'] = Variable<int>(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<double?> area(int var1) {
|
||||||
|
return customSelect(
|
||||||
|
'SELECT geopoly_area(_shape) AS _c0 FROM geopoly_test WHERE "rowid" = ?1',
|
||||||
|
variables: [
|
||||||
|
Variable<int>(var1)
|
||||||
|
],
|
||||||
|
readsFrom: {
|
||||||
|
geopolyTest,
|
||||||
|
}).map((QueryRow row) => row.readNullable<double>('_c0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||||
|
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||||
|
@override
|
||||||
|
List<DatabaseSchemaEntity> get allSchemaEntities => [geopolyTest];
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ abstract class DriftBackend {
|
||||||
return element.source!.uri;
|
return element.source!.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get canReadDart;
|
||||||
|
|
||||||
/// Resolves a Dart library by its uri.
|
/// Resolves a Dart library by its uri.
|
||||||
///
|
///
|
||||||
/// This should also be able to resolve SDK libraries.
|
/// This should also be able to resolve SDK libraries.
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:analyzer/dart/element/element.dart';
|
import 'package:analyzer/dart/element/element.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart';
|
import 'package:sqlparser/sqlparser.dart';
|
||||||
|
|
||||||
import '../options.dart';
|
|
||||||
import '../backend.dart';
|
import '../backend.dart';
|
||||||
import '../drift_native_functions.dart';
|
import '../drift_native_functions.dart';
|
||||||
|
import '../options.dart';
|
||||||
import '../resolver/dart/helper.dart';
|
import '../resolver/dart/helper.dart';
|
||||||
import '../resolver/discover.dart';
|
import '../resolver/discover.dart';
|
||||||
import '../resolver/drift/sqlparser/mapping.dart';
|
import '../resolver/drift/sqlparser/mapping.dart';
|
||||||
|
@ -62,10 +63,32 @@ class DriftAnalysisDriver {
|
||||||
|
|
||||||
AnalysisResultCacheReader? cacheReader;
|
AnalysisResultCacheReader? cacheReader;
|
||||||
|
|
||||||
KnownDriftTypes? _knownTypes;
|
final KnownDriftTypes? _knownTypes;
|
||||||
|
|
||||||
DriftAnalysisDriver(this.backend, this.options, {bool isTesting = false})
|
KnownDriftTypes get knownTypes => _knownTypes!;
|
||||||
: _isTesting = isTesting;
|
|
||||||
|
@visibleForTesting
|
||||||
|
DriftAnalysisDriver(
|
||||||
|
this.backend,
|
||||||
|
this.options,
|
||||||
|
this._knownTypes, {
|
||||||
|
bool isTesting = false,
|
||||||
|
}) : _isTesting = isTesting;
|
||||||
|
|
||||||
|
static Future<DriftAnalysisDriver> init(
|
||||||
|
DriftBackend backend,
|
||||||
|
DriftOptions options, {
|
||||||
|
bool isTesting = false,
|
||||||
|
}) async {
|
||||||
|
final driver = DriftAnalysisDriver(
|
||||||
|
backend,
|
||||||
|
options,
|
||||||
|
await KnownDriftTypes.resolve(backend),
|
||||||
|
isTesting: isTesting,
|
||||||
|
);
|
||||||
|
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
|
|
||||||
SqlEngine newSqlEngine() {
|
SqlEngine newSqlEngine() {
|
||||||
return SqlEngine(
|
return SqlEngine(
|
||||||
|
@ -83,17 +106,13 @@ class DriftAnalysisDriver {
|
||||||
if (options.hasModule(SqlModule.rtree)) const RTreeExtension(),
|
if (options.hasModule(SqlModule.rtree)) const RTreeExtension(),
|
||||||
if (options.hasModule(SqlModule.spellfix1))
|
if (options.hasModule(SqlModule.spellfix1))
|
||||||
const Spellfix1Extension(),
|
const Spellfix1Extension(),
|
||||||
|
if (options.hasModule(SqlModule.geopoly)) const GeopolyExtension(),
|
||||||
],
|
],
|
||||||
version: options.sqliteVersion,
|
version: options.sqliteVersion,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads types important for Drift analysis.
|
|
||||||
Future<KnownDriftTypes> loadKnownTypes() async {
|
|
||||||
return _knownTypes ??= await KnownDriftTypes.resolve(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For a given file under [uri], attempts to restore serialized analysis
|
/// For a given file under [uri], attempts to restore serialized analysis
|
||||||
/// results that have been stored before.
|
/// results that have been stored before.
|
||||||
///
|
///
|
||||||
|
@ -358,6 +377,7 @@ abstract class AnalysisResultCacheReader {
|
||||||
Future<CachedDiscoveryResults?> readDiscovery(Uri uri);
|
Future<CachedDiscoveryResults?> readDiscovery(Uri uri);
|
||||||
|
|
||||||
Future<LibraryElement?> readTypeHelperFor(Uri uri);
|
Future<LibraryElement?> readTypeHelperFor(Uri uri);
|
||||||
|
|
||||||
Future<String?> readElementCacheFor(Uri uri);
|
Future<String?> readElementCacheFor(Uri uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -414,6 +414,19 @@ enum SqlModule {
|
||||||
rtree,
|
rtree,
|
||||||
|
|
||||||
spellfix1,
|
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.
|
/// The possible values for the case of the table and column names.
|
||||||
|
|
|
@ -344,7 +344,7 @@ class ColumnParser {
|
||||||
.apply(getter.name.lexeme);
|
.apply(getter.name.lexeme);
|
||||||
ColumnType columnType;
|
ColumnType columnType;
|
||||||
|
|
||||||
final helper = await _resolver.resolver.driver.loadKnownTypes();
|
final helper = _resolver.resolver.driver.knownTypes;
|
||||||
|
|
||||||
if (foundStartMethod == _startCustom) {
|
if (foundStartMethod == _startCustom) {
|
||||||
final expression = remainingExpr.argumentList.arguments.single;
|
final expression = remainingExpr.argumentList.arguments.single;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:analyzer/dart/element/type_provider.dart';
|
||||||
import 'package:analyzer/dart/element/type_system.dart';
|
import 'package:analyzer/dart/element/type_system.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
import '../../driver/driver.dart';
|
import '../../backend.dart';
|
||||||
import '../../driver/error.dart';
|
import '../../driver/error.dart';
|
||||||
import '../../results/results.dart';
|
import '../../results/results.dart';
|
||||||
import '../resolver.dart';
|
import '../resolver.dart';
|
||||||
|
@ -32,6 +32,7 @@ class KnownDriftTypes {
|
||||||
final InterfaceElement jsonTypeConverter;
|
final InterfaceElement jsonTypeConverter;
|
||||||
final InterfaceType driftAny;
|
final InterfaceType driftAny;
|
||||||
final InterfaceType uint8List;
|
final InterfaceType uint8List;
|
||||||
|
final InterfaceType geopolyPolygon;
|
||||||
|
|
||||||
KnownDriftTypes._(
|
KnownDriftTypes._(
|
||||||
this.helperLibrary,
|
this.helperLibrary,
|
||||||
|
@ -47,6 +48,7 @@ class KnownDriftTypes {
|
||||||
this.driftAccessor,
|
this.driftAccessor,
|
||||||
this.driftAny,
|
this.driftAny,
|
||||||
this.uint8List,
|
this.uint8List,
|
||||||
|
this.geopolyPolygon,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Constructs the set of known drift types from a helper library, which is
|
/// Constructs the set of known drift types from a helper library, which is
|
||||||
|
@ -73,6 +75,8 @@ class KnownDriftTypes {
|
||||||
.defaultInstantiation,
|
.defaultInstantiation,
|
||||||
(exportNamespace.get('Uint8List') as InterfaceElement)
|
(exportNamespace.get('Uint8List') as InterfaceElement)
|
||||||
.defaultInstantiation,
|
.defaultInstantiation,
|
||||||
|
(exportNamespace.get('GeopolyPolygon') as InterfaceElement)
|
||||||
|
.defaultInstantiation,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +102,14 @@ class KnownDriftTypes {
|
||||||
return type?.asInstanceOf(converter);
|
return type?.asInstanceOf(converter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<KnownDriftTypes> resolve(DriftAnalysisDriver driver) async {
|
static Future<KnownDriftTypes?> resolve(DriftBackend backend) async {
|
||||||
final library = await driver.backend.readDart(uri);
|
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');
|
static final Uri uri = Uri.parse('package:drift/src/drift_dev_helper.dart');
|
||||||
|
@ -256,7 +264,7 @@ class DataClassInformation {
|
||||||
useRowClass.getField('constructor')!.toStringValue()!;
|
useRowClass.getField('constructor')!.toStringValue()!;
|
||||||
final generateInsertable =
|
final generateInsertable =
|
||||||
useRowClass.getField('generateInsertable')!.toBoolValue()!;
|
useRowClass.getField('generateInsertable')!.toBoolValue()!;
|
||||||
final helper = await resolver.resolver.driver.loadKnownTypes();
|
final helper = resolver.resolver.driver.knownTypes;
|
||||||
|
|
||||||
if (type is InterfaceType) {
|
if (type is InterfaceType) {
|
||||||
final found = FoundDartClass(type.element, type.typeArguments);
|
final found = FoundDartClass(type.element, type.typeArguments);
|
||||||
|
|
|
@ -53,7 +53,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
|
||||||
Future<TableReferenceInDartView?> _getStaticReference(
|
Future<TableReferenceInDartView?> _getStaticReference(
|
||||||
FieldElement field) async {
|
FieldElement field) async {
|
||||||
final type = field.type;
|
final type = field.type;
|
||||||
final knownTypes = await resolver.driver.loadKnownTypes();
|
final knownTypes = resolver.driver.knownTypes;
|
||||||
final typeSystem = field.library.typeSystem;
|
final typeSystem = field.library.typeSystem;
|
||||||
|
|
||||||
if (type is! InterfaceType ||
|
if (type is! InterfaceType ||
|
||||||
|
|
|
@ -73,8 +73,7 @@ class DiscoverStep {
|
||||||
_file.discovery = NotADartLibrary();
|
_file.discovery = NotADartLibrary();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
final finder =
|
final finder = _FindDartElements(this, library, _driver.knownTypes);
|
||||||
_FindDartElements(this, library, await _driver.loadKnownTypes());
|
|
||||||
await finder.find();
|
await finder.find();
|
||||||
|
|
||||||
_file.errorsDuringDiscovery.addAll(finder.errors);
|
_file.errorsDuringDiscovery.addAll(finder.errors);
|
||||||
|
|
|
@ -37,7 +37,7 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final knownTypes = await resolver.driver.loadKnownTypes();
|
final knownTypes = resolver.driver.knownTypes;
|
||||||
return readCustomType(
|
return readCustomType(
|
||||||
knownTypes.helperLibrary,
|
knownTypes.helperLibrary,
|
||||||
expression,
|
expression,
|
||||||
|
@ -64,7 +64,7 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final knownTypes = await resolver.driver.loadKnownTypes();
|
final knownTypes = resolver.driver.knownTypes;
|
||||||
return readTypeConverter(
|
return readTypeConverter(
|
||||||
knownTypes.helperLibrary,
|
knownTypes.helperLibrary,
|
||||||
expression,
|
expression,
|
||||||
|
@ -153,7 +153,7 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
||||||
innerType,
|
innerType,
|
||||||
false,
|
false,
|
||||||
this,
|
this,
|
||||||
await resolver.driver.loadKnownTypes(),
|
resolver.driver.knownTypes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,7 +166,7 @@ abstract class DriftElementResolver<T extends DiscoveredElement>
|
||||||
));
|
));
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
final knownTypes = await resolver.driver.loadKnownTypes();
|
final knownTypes = resolver.driver.knownTypes;
|
||||||
return validateExistingClass(columns, foundDartClass,
|
return validateExistingClass(columns, foundDartClass,
|
||||||
source.constructorName ?? '', false, this, knownTypes);
|
source.constructorName ?? '', false, this, knownTypes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,9 +76,11 @@ class TypeMapping {
|
||||||
var type = _driftTypeToParser(column.sqlType.builtin)
|
var type = _driftTypeToParser(column.sqlType.builtin)
|
||||||
.withNullable(column.nullable);
|
.withNullable(column.nullable);
|
||||||
|
|
||||||
if (column.sqlType.isCustom) {
|
type = switch (column.sqlType) {
|
||||||
type = type.addHint(CustomTypeHint(column.sqlType.custom!));
|
ColumnDriftType() => type,
|
||||||
}
|
ColumnCustomType(:final custom) => type.addHint(CustomTypeHint(custom)),
|
||||||
|
};
|
||||||
|
|
||||||
if (column.typeConverter case AppliedTypeConverter c) {
|
if (column.typeConverter case AppliedTypeConverter c) {
|
||||||
type = type.addHint(TypeConverterHint(c));
|
type = type.addHint(TypeConverterHint(c));
|
||||||
}
|
}
|
||||||
|
@ -147,6 +149,20 @@ class TypeMapping {
|
||||||
return ColumnType.custom(customHint.type);
|
return ColumnType.custom(customHint.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type.hint<IsGeopolyPolygon>() != null) {
|
||||||
|
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));
|
return ColumnType.drift(_toDefaultType(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ class DriftTableResolver extends DriftElementResolver<DiscoveredDriftTable> {
|
||||||
type.builtin == DriftSqlType.int
|
type.builtin == DriftSqlType.int
|
||||||
? EnumType.intEnum
|
? EnumType.intEnum
|
||||||
: EnumType.textEnum,
|
: EnumType.textEnum,
|
||||||
await resolver.driver.loadKnownTypes(),
|
resolver.driver.knownTypes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ class DriftViewResolver extends DriftElementResolver<DiscoveredDriftView> {
|
||||||
? null
|
? null
|
||||||
: await createTypeResolver(
|
: await createTypeResolver(
|
||||||
allReferences,
|
allReferences,
|
||||||
await resolver.driver.loadKnownTypes(),
|
resolver.driver.knownTypes,
|
||||||
);
|
);
|
||||||
|
|
||||||
final context = engine.analyzeNode(
|
final context = engine.analyzeNode(
|
||||||
|
|
|
@ -19,7 +19,7 @@ class FileAnalyzer {
|
||||||
|
|
||||||
Future<FileAnalysisResult> runAnalysisOn(FileState state) async {
|
Future<FileAnalysisResult> runAnalysisOn(FileState state) async {
|
||||||
final result = FileAnalysisResult();
|
final result = FileAnalysisResult();
|
||||||
final knownTypes = await driver.loadKnownTypes();
|
final knownTypes = driver.knownTypes;
|
||||||
|
|
||||||
if (state.extension == '.dart') {
|
if (state.extension == '.dart') {
|
||||||
for (final elementAnalysis in state.analysis.values) {
|
for (final elementAnalysis in state.analysis.values) {
|
||||||
|
|
|
@ -488,7 +488,7 @@ DartType regularColumnType(
|
||||||
|
|
||||||
extension on TypeProvider {
|
extension on TypeProvider {
|
||||||
DartType typeFor(ColumnType type, KnownDriftTypes knownTypes) {
|
DartType typeFor(ColumnType type, KnownDriftTypes knownTypes) {
|
||||||
if (type.custom case CustomColumnType custom) {
|
if (type case ColumnCustomType(:final custom)) {
|
||||||
return custom.dartType;
|
return custom.dartType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,18 +131,19 @@ class AnnotatedDartCodeBuilder {
|
||||||
void addDriftType(HasType hasType) {
|
void addDriftType(HasType hasType) {
|
||||||
void addNonListType() {
|
void addNonListType() {
|
||||||
final converter = hasType.typeConverter;
|
final converter = hasType.typeConverter;
|
||||||
final customType = hasType.sqlType.custom;
|
|
||||||
|
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
final nullable = converter.canBeSkippedForNulls && hasType.nullable;
|
final nullable = converter.canBeSkippedForNulls && hasType.nullable;
|
||||||
|
|
||||||
addDartType(converter.dartType);
|
addDartType(converter.dartType);
|
||||||
if (nullable) addText('?');
|
if (nullable) addText('?');
|
||||||
} else if (customType != null) {
|
|
||||||
addDartType(customType.dartType);
|
|
||||||
if (hasType.nullable) addText('?');
|
|
||||||
} else {
|
} else {
|
||||||
addTopLevel(dartTypeNames[hasType.sqlType.builtin]!);
|
switch (hasType.sqlType) {
|
||||||
|
case ColumnDriftType():
|
||||||
|
addTopLevel(dartTypeNames[hasType.sqlType.builtin]!);
|
||||||
|
case ColumnCustomType(:final custom):
|
||||||
|
addDartType(custom.dartType);
|
||||||
|
}
|
||||||
if (hasType.nullable) addText('?');
|
if (hasType.nullable) addText('?');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ abstract class SqlQuery {
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
AnalysisContext? get fromContext;
|
AnalysisContext? get fromContext;
|
||||||
|
|
||||||
AstNode? get root;
|
AstNode? get root;
|
||||||
|
|
||||||
/// Whether this query was declared in a `.drift` file.
|
/// Whether this query was declared in a `.drift` file.
|
||||||
|
@ -474,6 +475,7 @@ class InferredResultSet {
|
||||||
});
|
});
|
||||||
|
|
||||||
Iterable<ScalarResultColumn> get scalarColumns => columns.whereType();
|
Iterable<ScalarResultColumn> get scalarColumns => columns.whereType();
|
||||||
|
|
||||||
Iterable<NestedResult> get nestedResults => columns.whereType();
|
Iterable<NestedResult> get nestedResults => columns.whereType();
|
||||||
|
|
||||||
/// Whether a new class needs to be written to store the result of this query.
|
/// 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 {
|
int get _columnTypeCompatibilityHash {
|
||||||
return Object.hash(sqlType.builtin, sqlType.custom?.dartType);
|
final custom = switch (sqlType) {
|
||||||
|
ColumnDriftType() => null,
|
||||||
|
ColumnCustomType(:final custom) => custom,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Object.hash(sqlType.builtin, custom?.dartType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -758,12 +765,29 @@ final class ScalarResultColumn extends ResultColumn
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isCompatibleTo(ResultColumn other) {
|
bool isCompatibleTo(ResultColumn other) {
|
||||||
return other is ScalarResultColumn &&
|
if (other is ScalarResultColumn &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.sqlType.builtin == sqlType.builtin &&
|
other.sqlType.builtin == sqlType.builtin &&
|
||||||
other.sqlType.custom?.dartType == sqlType.custom?.dartType &&
|
|
||||||
other.nullable == nullable &&
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ abstract class HasType {
|
||||||
/// appears where an array is expected or has a type converter applied to it.
|
/// 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
|
/// [HasType] is the interface for sql-typed elements and is implemented by
|
||||||
/// columns.
|
/// columns.
|
||||||
class ColumnType {
|
sealed class ColumnType {
|
||||||
/// The builtin drift type used by this column.
|
/// The builtin drift type used by this column.
|
||||||
///
|
///
|
||||||
/// Even though it's unused there, custom types also have this field set -
|
/// Even though it's unused there, custom types also have this field set -
|
||||||
|
@ -44,14 +44,21 @@ class ColumnType {
|
||||||
/// all.
|
/// all.
|
||||||
final DriftSqlType builtin;
|
final DriftSqlType builtin;
|
||||||
|
|
||||||
/// Details about the custom type, if one is present.
|
const ColumnType._(this.builtin);
|
||||||
final CustomColumnType? custom;
|
|
||||||
|
|
||||||
bool get isCustom => custom != null;
|
const factory ColumnType.drift(DriftSqlType builtin) = ColumnDriftType;
|
||||||
|
|
||||||
const ColumnType.drift(this.builtin) : custom = null;
|
const factory ColumnType.custom(CustomColumnType custom) = ColumnCustomType;
|
||||||
|
}
|
||||||
|
|
||||||
ColumnType.custom(CustomColumnType this.custom) : builtin = DriftSqlType.any;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OperationOnTypes on HasType {
|
extension OperationOnTypes on HasType {
|
||||||
|
|
|
@ -200,16 +200,16 @@ class ElementSerializer {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object?> _serializeColumnType(ColumnType type) {
|
Map<String, Object?> _serializeColumnType(ColumnType type) {
|
||||||
final custom = type.custom;
|
return switch (type) {
|
||||||
|
ColumnDriftType() => {
|
||||||
return {
|
'builtin': type.builtin.name,
|
||||||
if (custom != null)
|
},
|
||||||
'custom': {
|
ColumnCustomType(:final custom) => {
|
||||||
'dart': _serializeType(custom.dartType),
|
'custom': {
|
||||||
'expression': custom.expression.toJson(),
|
'dart': _serializeType(custom.dartType),
|
||||||
}
|
'expression': custom.expression.toJson(),
|
||||||
else
|
}
|
||||||
'builtin': type.builtin.name,
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,11 +46,11 @@ class AnalysisContextBackend extends DriftBackend {
|
||||||
|
|
||||||
AnalysisContextBackend(this.context, this.provider);
|
AnalysisContextBackend(this.context, this.provider);
|
||||||
|
|
||||||
static PhysicalDriftDriver createDriver({
|
static Future<PhysicalDriftDriver> createDriver({
|
||||||
DriftOptions options = const DriftOptions.defaults(),
|
DriftOptions options = const DriftOptions.defaults(),
|
||||||
ResourceProvider? resourceProvider,
|
ResourceProvider? resourceProvider,
|
||||||
required String projectDirectory,
|
required String projectDirectory,
|
||||||
}) {
|
}) async {
|
||||||
final underlyingProvider =
|
final underlyingProvider =
|
||||||
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
|
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
|
||||||
final provider = OverlayResourceProvider(underlyingProvider);
|
final provider = OverlayResourceProvider(underlyingProvider);
|
||||||
|
@ -62,7 +62,7 @@ class AnalysisContextBackend extends DriftBackend {
|
||||||
final context = contextCollection.contextFor(projectDirectory);
|
final context = contextCollection.contextFor(projectDirectory);
|
||||||
|
|
||||||
final backend = AnalysisContextBackend(context, provider);
|
final backend = AnalysisContextBackend(context, provider);
|
||||||
final driver = DriftAnalysisDriver(backend, options);
|
final driver = await DriftAnalysisDriver.init(backend, options);
|
||||||
return PhysicalDriftDriver(driver, backend);
|
return PhysicalDriftDriver(driver, backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,9 @@ class AnalysisContextBackend extends DriftBackend {
|
||||||
return Future.value(resourceProvider.getFile(path).readAsStringSync());
|
return Future.value(resourceProvider.getFile(path).readAsStringSync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get canReadDart => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LibraryElement> readDart(Uri uri) async {
|
Future<LibraryElement> readDart(Uri uri) async {
|
||||||
final result = await context.currentSession.getLibraryByUri(uri.toString());
|
final result = await context.currentSession.getLibraryByUri(uri.toString());
|
||||||
|
|
|
@ -31,7 +31,7 @@ class DriftDiscover extends Builder {
|
||||||
@override
|
@override
|
||||||
Future<void> build(BuildStep buildStep) async {
|
Future<void> build(BuildStep buildStep) async {
|
||||||
final backend = DriftBuildBackend(buildStep);
|
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 prepared = await driver.findLocalElements(buildStep.inputId.uri);
|
||||||
final discovery = prepared.discovery;
|
final discovery = prepared.discovery;
|
||||||
|
@ -84,7 +84,7 @@ class DriftAnalyzer extends Builder {
|
||||||
@override
|
@override
|
||||||
Future<void> build(BuildStep buildStep) async {
|
Future<void> build(BuildStep buildStep) async {
|
||||||
final backend = DriftBuildBackend(buildStep);
|
final backend = DriftBuildBackend(buildStep);
|
||||||
final driver = DriftAnalysisDriver(backend, options)
|
final driver = await DriftAnalysisDriver.init(backend, options)
|
||||||
..cacheReader =
|
..cacheReader =
|
||||||
BuildCacheReader(buildStep, findsLocalElementsReliably: true);
|
BuildCacheReader(buildStep, findsLocalElementsReliably: true);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ class DriftBuildBackend extends DriftBackend {
|
||||||
return id.uri;
|
return id.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get canReadDart => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LibraryElement> readDart(Uri uri) async {
|
Future<LibraryElement> readDart(Uri uri) async {
|
||||||
if (uri.scheme == 'dart') {
|
if (uri.scheme == 'dart') {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:build/build.dart';
|
import 'package:build/build.dart';
|
||||||
import 'package:dart_style/dart_style.dart';
|
import 'package:dart_style/dart_style.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
|
|
||||||
import '../../analysis/custom_result_class.dart';
|
import '../../analysis/custom_result_class.dart';
|
||||||
|
@ -102,7 +103,7 @@ class DriftBuilder extends Builder {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> build(BuildStep buildStep) async {
|
Future<void> build(BuildStep buildStep) async {
|
||||||
final run = _DriftBuildRun(options, generationMode, buildStep);
|
final run = await _DriftBuildRun.init(options, generationMode, buildStep);
|
||||||
await run.run();
|
await run.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,18 +134,31 @@ class _DriftBuildRun {
|
||||||
Set<Uri> analyzedUris = {};
|
Set<Uri> analyzedUris = {};
|
||||||
bool _didPrintWarning = false;
|
bool _didPrintWarning = false;
|
||||||
|
|
||||||
_DriftBuildRun(this.options, this.mode, this.buildStep)
|
@visibleForTesting
|
||||||
: driver = DriftAnalysisDriver(DriftBuildBackend(buildStep), options)
|
_DriftBuildRun(this.options, this.mode, this.buildStep, this.driver);
|
||||||
..cacheReader = BuildCacheReader(
|
|
||||||
buildStep,
|
static Future<_DriftBuildRun> init(
|
||||||
// The discovery and analyzer builders will have emitted IR for
|
DriftOptions options,
|
||||||
// every relevant file in a previous build step that this builder
|
DriftGenerationMode mode,
|
||||||
// has a dependency on.
|
BuildStep buildStep,
|
||||||
findsResolvedElementsReliably:
|
) async {
|
||||||
!mode.embeddedAnalyzer || options.hasDriftAnalyzer,
|
return _DriftBuildRun(
|
||||||
findsLocalElementsReliably:
|
options,
|
||||||
!mode.embeddedAnalyzer || options.hasDriftAnalyzer,
|
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<void> run() async {
|
Future<void> run() async {
|
||||||
await _warnAboutDeprecatedOptions();
|
await _warnAboutDeprecatedOptions();
|
||||||
|
|
|
@ -184,6 +184,7 @@ const _$SqlModuleEnumMap = {
|
||||||
SqlModule.math: 'math',
|
SqlModule.math: 'math',
|
||||||
SqlModule.rtree: 'rtree',
|
SqlModule.rtree: 'rtree',
|
||||||
SqlModule.spellfix1: 'spellfix1',
|
SqlModule.spellfix1: 'spellfix1',
|
||||||
|
SqlModule.geopoly: 'geopoly',
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$CaseFromDartToSqlEnumMap = {
|
const _$CaseFromDartToSqlEnumMap = {
|
||||||
|
|
|
@ -21,7 +21,7 @@ Future<List<DriftElement>> extractDriftElementsFromDatabase(
|
||||||
final logger = Logger('extractDriftElementsFromDatabase');
|
final logger = Logger('extractDriftElementsFromDatabase');
|
||||||
final uri = Uri.parse('db.drift');
|
final uri = Uri.parse('db.drift');
|
||||||
final backend = _SingleFileNoAnalyzerBackend(logger, uri);
|
final backend = _SingleFileNoAnalyzerBackend(logger, uri);
|
||||||
final driver = DriftAnalysisDriver(
|
final driver = await DriftAnalysisDriver.init(
|
||||||
backend,
|
backend,
|
||||||
DriftOptions.defaults(
|
DriftOptions.defaults(
|
||||||
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
||||||
|
@ -91,6 +91,9 @@ class _SingleFileNoAnalyzerBackend extends DriftBackend {
|
||||||
return Future.value(contents);
|
return Future.value(contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get canReadDart => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LibraryElement> readDart(Uri uri) async {
|
Future<LibraryElement> readDart(Uri uri) async {
|
||||||
_noAnalyzer();
|
_noAnalyzer();
|
||||||
|
|
|
@ -2,10 +2,10 @@ import 'package:drift/drift.dart';
|
||||||
import 'package:recase/recase.dart';
|
import 'package:recase/recase.dart';
|
||||||
import 'package:sqlparser/sqlparser.dart' hide ResultColumn;
|
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/options.dart';
|
||||||
import '../../analysis/resolver/queries/explicit_alias_transformer.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 '../../utils/string_escaper.dart';
|
||||||
import '../writer.dart';
|
import '../writer.dart';
|
||||||
import 'result_set_writer.dart';
|
import 'result_set_writer.dart';
|
||||||
|
@ -31,6 +31,7 @@ class QueryWriter {
|
||||||
|
|
||||||
late final ExplicitAliasTransformer _transformer;
|
late final ExplicitAliasTransformer _transformer;
|
||||||
final TextEmitter _emitter;
|
final TextEmitter _emitter;
|
||||||
|
|
||||||
StringBuffer get _buffer => _emitter.buffer;
|
StringBuffer get _buffer => _emitter.buffer;
|
||||||
|
|
||||||
DriftOptions get options => scope.writer.options;
|
DriftOptions get options => scope.writer.options;
|
||||||
|
@ -207,13 +208,14 @@ class QueryWriter {
|
||||||
_emitter.dartCode(_emitter.innerColumnType(column.sqlType));
|
_emitter.dartCode(_emitter.innerColumnType(column.sqlType));
|
||||||
String code;
|
String code;
|
||||||
|
|
||||||
if (column.sqlType.isCustom) {
|
switch (column.sqlType) {
|
||||||
final method = isNullable ? 'readNullableWithType' : 'readWithType';
|
case ColumnDriftType():
|
||||||
final typeImpl = _emitter.dartCode(column.sqlType.custom!.expression);
|
final method = isNullable ? 'readNullable' : 'read';
|
||||||
code = 'row.$method<$rawDartType>($typeImpl, $dartLiteral)';
|
code = 'row.$method<$rawDartType>($dartLiteral)';
|
||||||
} else {
|
case ColumnCustomType(:final custom):
|
||||||
final method = isNullable ? 'readNullable' : 'read';
|
final method = isNullable ? 'readNullableWithType' : 'readWithType';
|
||||||
code = 'row.$method<$rawDartType>($dartLiteral)';
|
final typeImpl = _emitter.dartCode(custom.expression);
|
||||||
|
code = 'row.$method<$rawDartType>($typeImpl, $dartLiteral)';
|
||||||
}
|
}
|
||||||
|
|
||||||
final converter = column.typeConverter;
|
final converter = column.typeConverter;
|
||||||
|
@ -759,6 +761,7 @@ class _ExpandedDeclarationWriter {
|
||||||
class _ExpandedVariableWriter {
|
class _ExpandedVariableWriter {
|
||||||
final SqlQuery query;
|
final SqlQuery query;
|
||||||
final TextEmitter _emitter;
|
final TextEmitter _emitter;
|
||||||
|
|
||||||
StringBuffer get _buffer => _emitter.buffer;
|
StringBuffer get _buffer => _emitter.buffer;
|
||||||
|
|
||||||
_ExpandedVariableWriter(this.query, this._emitter);
|
_ExpandedVariableWriter(this.query, this._emitter);
|
||||||
|
|
|
@ -13,6 +13,7 @@ class DataClassWriter {
|
||||||
bool get isInsertable => table is DriftTable;
|
bool get isInsertable => table is DriftTable;
|
||||||
|
|
||||||
final TextEmitter _emitter;
|
final TextEmitter _emitter;
|
||||||
|
|
||||||
StringBuffer get _buffer => _emitter.buffer;
|
StringBuffer get _buffer => _emitter.buffer;
|
||||||
|
|
||||||
DataClassWriter(this.table, this.scope) : _emitter = scope.leaf();
|
DataClassWriter(this.table, this.scope) : _emitter = scope.leaf();
|
||||||
|
@ -341,11 +342,12 @@ class RowMappingWriter {
|
||||||
final columnName = column.nameInSql;
|
final columnName = column.nameInSql;
|
||||||
final rawData = "data['\${effectivePrefix}$columnName']";
|
final rawData = "data['\${effectivePrefix}$columnName']";
|
||||||
|
|
||||||
String sqlType;
|
final String sqlType;
|
||||||
if (column.sqlType.custom case CustomColumnType custom) {
|
switch (column.sqlType) {
|
||||||
sqlType = writer.dartCode(custom.expression);
|
case ColumnDriftType():
|
||||||
} else {
|
sqlType = writer.drift(column.sqlType.builtin.toString());
|
||||||
sqlType = writer.drift(column.sqlType.builtin.toString());
|
case ColumnCustomType(:final custom):
|
||||||
|
sqlType = writer.dartCode(custom.expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)';
|
var loadType = '$databaseGetter.typeMapping.read($sqlType, $rawData)';
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'update_companion_writer.dart';
|
||||||
/// Both classes need to generate column getters and a mapping function.
|
/// Both classes need to generate column getters and a mapping function.
|
||||||
abstract class TableOrViewWriter {
|
abstract class TableOrViewWriter {
|
||||||
DriftElementWithResultSet get tableOrView;
|
DriftElementWithResultSet get tableOrView;
|
||||||
|
|
||||||
TextEmitter get emitter;
|
TextEmitter get emitter;
|
||||||
|
|
||||||
StringBuffer get buffer => emitter.buffer;
|
StringBuffer get buffer => emitter.buffer;
|
||||||
|
@ -209,12 +210,12 @@ abstract class TableOrViewWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.sqlType.isCustom) {
|
switch (column.sqlType) {
|
||||||
additionalParams['type'] =
|
case ColumnDriftType():
|
||||||
emitter.dartCode(column.sqlType.custom!.expression);
|
additionalParams['type'] =
|
||||||
} else {
|
emitter.drift(column.sqlType.builtin.toString());
|
||||||
additionalParams['type'] =
|
case ColumnCustomType(:final custom):
|
||||||
emitter.drift(column.sqlType.builtin.toString());
|
additionalParams['type'] = emitter.dartCode(custom.expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isRequiredForInsert != null) {
|
if (isRequiredForInsert != null) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Writer extends _NodeOrWriter {
|
||||||
final GenerationOptions generationOptions;
|
final GenerationOptions generationOptions;
|
||||||
|
|
||||||
TextEmitter get header => _header;
|
TextEmitter get header => _header;
|
||||||
|
|
||||||
TextEmitter get imports => _imports;
|
TextEmitter get imports => _imports;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -51,6 +52,7 @@ class Writer extends _NodeOrWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope child() => _root.child();
|
Scope child() => _root.child();
|
||||||
|
|
||||||
TextEmitter leaf() => _root.leaf();
|
TextEmitter leaf() => _root.leaf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,12 +145,12 @@ abstract class _NodeOrWriter {
|
||||||
return AnnotatedDartCode.build((b) {
|
return AnnotatedDartCode.build((b) {
|
||||||
AnnotatedDartCode sqlDartType;
|
AnnotatedDartCode sqlDartType;
|
||||||
|
|
||||||
if (converter.sqlType.isCustom) {
|
switch (converter.sqlType) {
|
||||||
sqlDartType =
|
case ColumnDriftType():
|
||||||
AnnotatedDartCode.type(converter.sqlType.custom!.dartType);
|
sqlDartType =
|
||||||
} else {
|
AnnotatedDartCode([dartTypeNames[converter.sqlType.builtin]!]);
|
||||||
sqlDartType =
|
case ColumnCustomType(:final custom):
|
||||||
AnnotatedDartCode([dartTypeNames[converter.sqlType.builtin]!]);
|
sqlDartType = AnnotatedDartCode.type(custom.dartType);
|
||||||
}
|
}
|
||||||
|
|
||||||
final className = converter.alsoAppliesToJsonConversion
|
final className = converter.alsoAppliesToJsonConversion
|
||||||
|
@ -205,12 +207,11 @@ abstract class _NodeOrWriter {
|
||||||
/// This type does not respect type converters or arrays.
|
/// This type does not respect type converters or arrays.
|
||||||
AnnotatedDartCode innerColumnType(ColumnType type, {bool nullable = false}) {
|
AnnotatedDartCode innerColumnType(ColumnType type, {bool nullable = false}) {
|
||||||
return AnnotatedDartCode.build((b) {
|
return AnnotatedDartCode.build((b) {
|
||||||
final custom = type.custom;
|
switch (type) {
|
||||||
|
case ColumnDriftType():
|
||||||
if (custom != null) {
|
b.addTopLevel(dartTypeNames[type.builtin]!);
|
||||||
b.addDartType(custom.dartType);
|
case ColumnCustomType(:final custom):
|
||||||
} else {
|
b.addDartType(custom.dartType);
|
||||||
b.addTopLevel(dartTypeNames[type.builtin]!);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nullable) {
|
if (nullable) {
|
||||||
|
@ -241,12 +242,15 @@ abstract class _NodeOrWriter {
|
||||||
b.addCode(expression);
|
b.addCode(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (column.sqlType.isCustom) {
|
switch (column.sqlType) {
|
||||||
// Also specify the custom type since it can't be inferred from the
|
case ColumnDriftType():
|
||||||
// value passed to the variable.
|
break;
|
||||||
b
|
case ColumnCustomType(:final custom):
|
||||||
..addText(', ')
|
// Also specify the custom type since it can't be inferred from the
|
||||||
..addCode(column.sqlType.custom!.expression);
|
// value passed to the variable.
|
||||||
|
b
|
||||||
|
..addText(', ')
|
||||||
|
..addCode(custom.expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
b.addText(')');
|
b.addText(')');
|
||||||
|
@ -422,6 +426,7 @@ class TextEmitter extends _Node {
|
||||||
TextEmitter(Scope super.parent) : writer = parent.writer;
|
TextEmitter(Scope super.parent) : writer = parent.writer;
|
||||||
|
|
||||||
void write(Object? object) => buffer.write(object);
|
void write(Object? object) => buffer.write(object);
|
||||||
|
|
||||||
void writeln(Object? object) => buffer.writeln(object);
|
void writeln(Object? object) => buffer.writeln(object);
|
||||||
|
|
||||||
void writeUriRef(Uri definition, String element) {
|
void writeUriRef(Uri definition, String element) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('handles cyclic imports', () async {
|
test('handles cyclic imports', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/entry.dart': '''
|
'a|lib/entry.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ CREATE TABLE bars (
|
||||||
group("reports error when an import can't be found", () {
|
group("reports error when an import can't be found", () {
|
||||||
for (final extension in const ['drift', 'moor']) {
|
for (final extension in const ['drift', 'moor']) {
|
||||||
test('in $extension files', () async {
|
test('in $extension files', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.$extension': '''
|
'a|lib/a.$extension': '''
|
||||||
import 'b.$extension';
|
import 'b.$extension';
|
||||||
''',
|
''',
|
||||||
|
@ -56,7 +56,7 @@ import 'b.$extension';
|
||||||
}
|
}
|
||||||
|
|
||||||
test('in a dart file', () async {
|
test('in a dart file', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class Database {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves tables and queries', () async {
|
test('resolves tables and queries', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/database.dart': r'''
|
'a|lib/database.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ class ProgrammingLanguages extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('still supports .moor files', () async {
|
test('still supports .moor files', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ CREATE TABLE users (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports multiple references between same entities', () async {
|
test('supports multiple references between same entities', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ class ThisTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports references across files', () async {
|
test('supports references across files', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/this_table.dart': '''
|
'a|lib/this_table.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ class OtherTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reports sensible error for missing table', () async {
|
test('reports sensible error for missing table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
getCompanyCustomersCount:
|
getCompanyCustomersCount:
|
||||||
SELECT COUNT(*) AS "count"
|
SELECT COUNT(*) AS "count"
|
||||||
|
|
|
@ -75,7 +75,7 @@ sqlite:
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reports error about table when module is not imported', () async {
|
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;',
|
'a|lib/a.drift': 'CREATE VIRTUAL TABLE place_spellfix USING spellfix1;',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('finds dart expressions', () async {
|
test('finds dart expressions', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
import 'foo.dart';
|
import 'foo.dart';
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ var expr_0 = const MyConverter();
|
||||||
|
|
||||||
test('only includes direct imports if no Dart expressions are used',
|
test('only includes direct imports if no Dart expressions are used',
|
||||||
() async {
|
() async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
import 'foo.dart';
|
import 'foo.dart';
|
||||||
import '2.drift';
|
import '2.drift';
|
||||||
|
@ -48,7 +48,7 @@ import 'bar.dart';
|
||||||
});
|
});
|
||||||
|
|
||||||
test('finds nested dart imports', () async {
|
test('finds nested dart imports', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'b.drift';
|
import 'b.drift';
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ import 'import.dart';
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not throw for invalid import', () async {
|
test('does not throw for invalid import', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'b.drift';
|
import 'b.drift';
|
||||||
import 'does_not_exist.drift';
|
import 'does_not_exist.drift';
|
||||||
|
@ -96,8 +96,8 @@ import 'c.drift';
|
||||||
expect(result.temporaryDartFile, isNot(contains('import')));
|
expect(result.temporaryDartFile, isNot(contains('import')));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws if entrypoint does not exist', () {
|
test('throws if entrypoint does not exist', () async {
|
||||||
final backend = TestBackend.inTest({});
|
final backend = await TestBackend.inTest({});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
() =>
|
() =>
|
||||||
|
@ -106,8 +106,8 @@ import 'c.drift';
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws if entrypoint is invalid', () {
|
test('throws if entrypoint is invalid', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '! this not a valid drift file !',
|
'a|lib/main.drift': '! this not a valid drift file !',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('analyzes views referencing Dart tables', () async {
|
test('analyzes views referencing Dart tables', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/db.dart': '''
|
'a|lib/db.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('gracefully handles daos with invalid types', () async {
|
test('gracefully handles daos with invalid types', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/bar.dart': '''
|
'a|lib/bar.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('reports a warning', () {
|
group('reports a warning', () {
|
||||||
test('when the table is not a class type', () async {
|
test('when the table is not a class type', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -26,7 +26,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the column is not a symbol literal', () async {
|
test('when the column is not a symbol literal', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -48,7 +48,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('includes referenced table in database', () async {
|
test('includes referenced table in database', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -78,7 +78,7 @@ class Database {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the referenced column does not exist', () async {
|
test('when the referenced column does not exist', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -106,7 +106,7 @@ class Database {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves reference', () async {
|
test('resolves reference', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -149,7 +149,7 @@ class Database {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves self-references', () async {
|
test('resolves self-references', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
|
@ -9,7 +9,7 @@ void main() {
|
||||||
test(
|
test(
|
||||||
'It should rename the table and column name to its snake case version by default',
|
'It should rename the table and column name to its snake case version by default',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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',
|
test('It should rename the table and column name to its snake case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -64,7 +64,7 @@ class Database {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('It should not rename the table and column name', () async {
|
test('It should not rename the table and column name', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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',
|
test('It should rename the table and column name to its camel case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -122,7 +122,7 @@ class Database {}
|
||||||
test(
|
test(
|
||||||
'It should rename the table and column name to its constant case version',
|
'It should rename the table and column name to its constant case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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',
|
test('It should rename the table and column name to its pascal case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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',
|
test('It should rename the table and column name to its lower case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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',
|
test('It should rename the table and column name to its upper case version',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -235,7 +235,7 @@ class Database {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('recognizes custom column types', () async {
|
test('recognizes custom column types', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -254,13 +254,18 @@ class TestTable extends Table {
|
||||||
final column = table.columns.single;
|
final column = table.columns.single;
|
||||||
|
|
||||||
expect(column.sqlType.builtin, DriftSqlType.any);
|
expect(column.sqlType.builtin, DriftSqlType.any);
|
||||||
expect(column.sqlType.custom?.dartType.toString(), 'List<String>');
|
switch (column.sqlType) {
|
||||||
expect(column.sqlType.custom?.expression.toString(), 'StringArrayType()');
|
case ColumnDriftType():
|
||||||
|
break;
|
||||||
|
case ColumnCustomType(:final custom):
|
||||||
|
expect(custom.dartType.toString(), 'List<String>');
|
||||||
|
expect(custom.expression.toString(), 'StringArrayType()');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
group('customConstraint analysis', () {
|
group('customConstraint analysis', () {
|
||||||
test('reports errors', () async {
|
test('reports errors', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -279,7 +284,7 @@ class TestTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves foreign key references', () async {
|
test('resolves foreign key references', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -320,7 +325,7 @@ class TestTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about missing `NOT NULL`', () async {
|
test('warns about missing `NOT NULL`', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -339,7 +344,7 @@ class TestTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('applies constraints', () async {
|
test('applies constraints', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
late TestBackend state;
|
late TestBackend state;
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() async {
|
||||||
state = TestBackend(const {
|
state = await TestBackend.init(const {
|
||||||
'a|lib/invalid_no_unnamed_constructor.dart': '''
|
'a|lib/invalid_no_unnamed_constructor.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ class Companies extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles `ANY` columns', () async {
|
test('handles `ANY` columns', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'row.dart';
|
import 'row.dart';
|
||||||
|
|
||||||
|
@ -528,7 +528,7 @@ class FooData {
|
||||||
|
|
||||||
group('records as row types', () {
|
group('records as row types', () {
|
||||||
test('supported with explicit record', () async {
|
test('supported with explicit record', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
@ -562,7 +562,7 @@ class Users extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supported with implicit record', () async {
|
test('supported with implicit record', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
|
@ -7,7 +7,7 @@ void main() {
|
||||||
final mainUri = Uri.parse('package:a/main.dart');
|
final mainUri = Uri.parse('package:a/main.dart');
|
||||||
|
|
||||||
test('parses schema version getter', () async {
|
test('parses schema version getter', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': r'''
|
'a|lib/main.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class MyDatabase extends _$MyDatabase {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parses schema version field', () async {
|
test('parses schema version field', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': r'''
|
'a|lib/main.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class MyDatabase extends _$MyDatabase {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not warn about missing tables parameter', () async {
|
test('does not warn about missing tables parameter', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': r'''
|
'a|lib/main.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class MyDatabase2 extends _$MyDatabase {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports inheritance for daos', () async {
|
test('supports inheritance for daos', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/database.dart': r'''
|
'a|lib/database.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class ProductsDao extends BaseProductsDao with _$ProductDaoMixin {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('only includes duplicate elements once', () async {
|
test('only includes duplicate elements once', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ void main() {
|
||||||
late TestBackend backend;
|
late TestBackend backend;
|
||||||
|
|
||||||
setUpAll(() async {
|
setUpAll(() async {
|
||||||
backend = TestBackend({
|
backend = await TestBackend.init({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('reports a warning', () {
|
group('reports a warning', () {
|
||||||
test('when the table is not a class type', () async {
|
test('when the table is not a class type', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the table is not a symbol literal', () async {
|
test('when the table is not a symbol literal', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the referenced table does not exist', () async {
|
test('when the referenced table does not exist', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves reference', () async {
|
test('resolves reference', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class Foo extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves self-references', () async {
|
test('resolves self-references', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('resolves index', () async {
|
test('resolves index', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class MyTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about missing columns', () async {
|
test('warns about missing columns', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('warns about invalid column', () async {
|
test('warns about invalid column', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('can define abstract tables', () async {
|
test('can define abstract tables', () async {
|
||||||
final test = TestBackend.inTest({
|
final test = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ void main() {
|
||||||
late TestBackend backend;
|
late TestBackend backend;
|
||||||
late FileState state;
|
late FileState state;
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() async {
|
||||||
backend = TestBackend({
|
backend = await TestBackend.init({
|
||||||
'a|lib/main.dart': r'''
|
'a|lib/main.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
late TestBackend backend;
|
late TestBackend backend;
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() async {
|
||||||
backend = TestBackend({
|
backend = await TestBackend.init({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ class Pianos extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reads custom constraints from table', () async {
|
test('reads custom constraints from table', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ class WithConstraints extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about foreign key references from customConstraints', () async {
|
test('warns about foreign key references from customConstraints', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ class WithConstraints extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can resolve references from import', () async {
|
test('can resolve references from import', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/topic.dart': '''
|
'a|lib/topic.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ class Videos extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports autoIncrement on int64 columns', () async {
|
test('supports autoIncrement on int64 columns', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
late TestBackend state;
|
late TestBackend state;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() async {
|
||||||
state = TestBackend({
|
state = await TestBackend.init({
|
||||||
'a|lib/json.dart': '''
|
'a|lib/json.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ void main() {
|
||||||
final mainUri = Uri.parse('package:a/main.dart');
|
final mainUri = Uri.parse('package:a/main.dart');
|
||||||
|
|
||||||
test('does not allow autoIncrement() to have a unique constraint', () async {
|
test('does not allow autoIncrement() to have a unique constraint', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.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 {
|
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': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class Test extends Table {
|
||||||
test(
|
test(
|
||||||
'does not allow primary key to have a unique constraint through override',
|
'does not allow primary key to have a unique constraint through override',
|
||||||
() async {
|
() async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class Test extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about duplicate unique declarations', () async {
|
test('warns about duplicate unique declarations', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class Test extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parses unique key definitions', () async {
|
test('parses unique key definitions', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('can analyze Dart view', () async {
|
test('can analyze Dart view', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.dart': '''
|
'a|lib/main.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('drift files', () {
|
group('drift files', () {
|
||||||
test('finds local elements', () async {
|
test('finds local elements', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE foo (bar INTEGER);
|
CREATE TABLE foo (bar INTEGER);
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ CREATE VIEW my_view AS SELECT whatever FROM unknown_table;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reports syntax errors', () async {
|
test('reports syntax errors', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE valid_1 (bar INTEGER);
|
CREATE TABLE valid_1 (bar INTEGER);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ CREATE TABLE valid_2 (bar INTEGER);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about duplicate elements', () async {
|
test('warns about duplicate elements', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE a (id INTEGER);
|
CREATE TABLE a (id INTEGER);
|
||||||
CREATE VIEW a AS VALUES(1,2,3);
|
CREATE VIEW a AS VALUES(1,2,3);
|
||||||
|
@ -82,7 +82,7 @@ CREATE VIEW a AS VALUES(1,2,3);
|
||||||
|
|
||||||
group('imports', () {
|
group('imports', () {
|
||||||
test('are resolved', () async {
|
test('are resolved', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': "import 'b.drift';",
|
'a|lib/a.drift': "import 'b.drift';",
|
||||||
'a|lib/b.drift': "CREATE TABLE foo (bar INTEGER);",
|
'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 {
|
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/a.drift': "import 'a.drift'; import 'b.drift';",
|
||||||
'a|lib/b.drift': "import 'a.drift';",
|
'a|lib/b.drift': "import 'a.drift';",
|
||||||
});
|
});
|
||||||
|
@ -119,7 +119,7 @@ CREATE VIEW a AS VALUES(1,2,3);
|
||||||
|
|
||||||
group('dart files', () {
|
group('dart files', () {
|
||||||
test('fails for part files', () async {
|
test('fails for part files', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
part of 'b.dart';
|
part of 'b.dart';
|
||||||
''',
|
''',
|
||||||
|
@ -136,7 +136,7 @@ part 'a.dart';
|
||||||
});
|
});
|
||||||
|
|
||||||
test('finds tables', () async {
|
test('finds tables', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -172,7 +172,7 @@ class Groups extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('ignores abstract tables', () async {
|
test('ignores abstract tables', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ abstract class BaseRelationTable extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('table name errors', () async {
|
test('table name errors', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/expr.dart': '''
|
'a|lib/expr.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ class InvalidGetter extends Table {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about duplicate elements', () async {
|
test('warns about duplicate elements', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.dart': '''
|
'a|lib/a.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('view created', () async {
|
test('view created', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/table.drift': '''
|
'foo|lib/table.drift': '''
|
||||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
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 {
|
test('view created from another view', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/table.drift': '''
|
'foo|lib/table.drift': '''
|
||||||
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
CREATE TABLE t (id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL);
|
||||||
''',
|
''',
|
||||||
|
@ -68,7 +68,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('view without table', () async {
|
test('view without table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE VIEW random_view AS
|
CREATE VIEW random_view AS
|
||||||
SELECT name FROM t WHERE id % 2 = 0;
|
SELECT name FROM t WHERE id % 2 = 0;
|
||||||
|
@ -82,7 +82,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not allow nested columns', () async {
|
test('does not allow nested columns', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (bar INTEGER NOT NULL PRIMARY KEY);
|
CREATE TABLE foo (bar INTEGER NOT NULL PRIMARY KEY);
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ void main() {
|
||||||
test('imported views are analyzed', () async {
|
test('imported views are analyzed', () async {
|
||||||
// Regression test for https://github.com/simolus3/drift/issues/1639
|
// Regression test for https://github.com/simolus3/drift/issues/1639
|
||||||
|
|
||||||
final testState = TestBackend.inTest({
|
final testState = await TestBackend.inTest({
|
||||||
'a|lib/imported.drift': '''
|
'a|lib/imported.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
b TEXT NOT NULL
|
b TEXT NOT NULL
|
||||||
|
@ -124,7 +124,7 @@ query: SELECT * FROM my_view;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('picks valid Dart names for columns', () async {
|
test('picks valid Dart names for columns', () async {
|
||||||
final testState = TestBackend.inTest({
|
final testState = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE VIEW IF NOT EXISTS repro AS
|
CREATE VIEW IF NOT EXISTS repro AS
|
||||||
SELECT 1,
|
SELECT 1,
|
||||||
|
@ -148,7 +148,7 @@ CREATE VIEW IF NOT EXISTS repro AS
|
||||||
});
|
});
|
||||||
|
|
||||||
test('copies type converter from table', () async {
|
test('copies type converter from table', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'converter.dart';
|
import 'converter.dart';
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ TypeConverter<Object, int> createConverter() => throw UnimplementedError();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can declare type converter on view column', () async {
|
test('can declare type converter on view column', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'converter.dart';
|
import 'converter.dart';
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ TypeConverter<Object, int> createConverter() => throw UnimplementedError();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports enum columns', () async {
|
test('supports enum columns', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enums.dart';
|
import 'enums.dart';
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ enum MyEnum {
|
||||||
String expectedSql,
|
String expectedSql,
|
||||||
DriftOptions options,
|
DriftOptions options,
|
||||||
) async {
|
) async {
|
||||||
final backend = TestBackend.inTest(
|
final backend = await TestBackend.inTest(
|
||||||
{'a|lib/a.drift': definition},
|
{'a|lib/a.drift': definition},
|
||||||
options: options,
|
options: options,
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('parse nested CTE', () async {
|
test('parse nested CTE', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/test.drift': '''
|
'a|lib/test.drift': '''
|
||||||
test:
|
test:
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -38,7 +38,7 @@ SELECT
|
||||||
});
|
});
|
||||||
|
|
||||||
test('recognizes CTE clause', () async {
|
test('recognizes CTE clause', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/test.drift': '''
|
'a|lib/test.drift': '''
|
||||||
test:
|
test:
|
||||||
WITH RECURSIVE
|
WITH RECURSIVE
|
||||||
|
@ -70,7 +70,7 @@ WITH RECURSIVE
|
||||||
});
|
});
|
||||||
|
|
||||||
test('finds the underlying table when aliased through CTE', () async {
|
test('finds the underlying table when aliased through CTE', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/test.drift': '''
|
'a|lib/test.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('can use existing row classes in drift files', () async {
|
test('can use existing row classes in drift files', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/db.drift': '''
|
'a|lib/db.drift': '''
|
||||||
import 'rows.dart';
|
import 'rows.dart';
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class ExistingForView {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can use generic row classes', () async {
|
test('can use generic row classes', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/generic.dart': '''
|
'a|lib/generic.dart': '''
|
||||||
//@dart=2.13
|
//@dart=2.13
|
||||||
typedef StringRow = GenericRow<String>;
|
typedef StringRow = GenericRow<String>;
|
||||||
|
@ -105,7 +105,7 @@ CREATE TABLE drift_ints (
|
||||||
|
|
||||||
group('can use records', () {
|
group('can use records', () {
|
||||||
test('with explicit structure', () async {
|
test('with explicit structure', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'helper.dart';
|
import 'helper.dart';
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ typedef MyRecord = ({String foo, int? bar});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('implicitly', () async {
|
test('implicitly', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
foo TEXT NOT NULL,
|
foo TEXT NOT NULL,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('reports an error when importing a part file into .drift', () async {
|
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': '''
|
'a|lib/base.dart': '''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ void main() {
|
||||||
|
|
||||||
test('integration tests with drift files and experimental inference',
|
test('integration tests with drift files and experimental inference',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
const {
|
const {
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE numbers (foo REAL NOT NULL);
|
CREATE TABLE numbers (foo REAL NOT NULL);
|
||||||
|
|
|
@ -8,7 +8,7 @@ const _options = DriftOptions.defaults(modules: [SqlModule.fts5]);
|
||||||
void main() {
|
void main() {
|
||||||
group('reports error', () {
|
group('reports error', () {
|
||||||
test('for missing content table', () async {
|
test('for missing content table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE VIRTUAL TABLE fts USING fts5(a, c, content=tbl);
|
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 {
|
test('for invalid rowid of content table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE tbl (a, b, c, my_pk INTEGER PRIMARY KEY);
|
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 {
|
test('when referencing an unknown column', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY);
|
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 {
|
test('finds referenced table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY);
|
CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('drift files can import original dart source', () async {
|
test('drift files can import original dart source', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/base.dart': r'''
|
'a|lib/base.dart': r'''
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
// https://github.com/simolus3/drift/issues/2097#issuecomment-1273008383
|
// https://github.com/simolus3/drift/issues/2097#issuecomment-1273008383
|
||||||
test('virtual columns are not required for inserts', () async {
|
test('virtual columns are not required for inserts', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'foo|lib/a.drift': r'''
|
'foo|lib/a.drift': r'''
|
||||||
CREATE TABLE IF NOT EXISTS nodes (
|
CREATE TABLE IF NOT EXISTS nodes (
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
// Regression test for https://github.com/simolus3/drift/issues/754
|
// Regression test for https://github.com/simolus3/drift/issues/754
|
||||||
test('supports fts5 tables with external content', () async {
|
test('supports fts5 tables with external content', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b TEXT, c TEXT);
|
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');
|
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, content='tbl', content_rowid='a');
|
||||||
|
|
|
@ -20,7 +20,7 @@ query: INSERT INTO foo VALUES (?, ?, ?)
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('does not support newer sqlite features by default', () async {
|
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');
|
final file = await state.analyze('package:a/main.drift');
|
||||||
expect(
|
expect(
|
||||||
|
@ -38,7 +38,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports newer sqlite features', () async {
|
test('supports newer sqlite features', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
_content,
|
_content,
|
||||||
options: const DriftOptions.defaults(
|
options: const DriftOptions.defaults(
|
||||||
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
sqliteAnalysisOptions: SqliteAnalysisOptions(
|
||||||
|
|
|
@ -14,7 +14,7 @@ enum Fruit {
|
||||||
void main() {
|
void main() {
|
||||||
group('warns about invalid type converter value', () {
|
group('warns about invalid type converter value', () {
|
||||||
test('in table definition', () async {
|
test('in table definition', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ CREATE TABLE a (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('for query', () async {
|
test('for query', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import 'package:drift/drift.dart' show DriftSqlType;
|
import 'package:drift/drift.dart' show DriftSqlType;
|
||||||
import 'package:drift_dev/src/analysis/results/column.dart';
|
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/table.dart';
|
||||||
|
import 'package:drift_dev/src/analysis/results/types.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
import '../../test_utils.dart';
|
import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('reports foreign keys in drift model', () async {
|
test('reports foreign keys in drift model', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
foo INTEGER PRIMARY KEY,
|
foo INTEGER PRIMARY KEY,
|
||||||
|
@ -55,7 +56,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('recognizes aliases to rowid', () async {
|
test('recognizes aliases to rowid', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
|
@ -83,7 +84,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('parses enum columns', () async {
|
test('parses enum columns', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not allow converters for enum columns', () async {
|
test('does not allow converters for enum columns', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
@ -199,7 +200,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not allow enum types for non-enums', () async {
|
test('does not allow enum types for non-enums', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
@ -222,7 +223,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports JSON KEY annotation', () async {
|
test('supports JSON KEY annotation', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE waybills (
|
CREATE TABLE waybills (
|
||||||
parent INT JSON KEY parentDoc NULL,
|
parent INT JSON KEY parentDoc NULL,
|
||||||
|
@ -243,7 +244,7 @@ CREATE TABLE waybills (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('recognizes documentation comments', () async {
|
test('recognizes documentation comments', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE IF NOT EXISTS currencies (
|
CREATE TABLE IF NOT EXISTS currencies (
|
||||||
-- The name of this currency
|
-- The name of this currency
|
||||||
|
@ -265,7 +266,7 @@ CREATE TABLE IF NOT EXISTS currencies (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can use custom types', () async {
|
test('can use custom types', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'b.dart';
|
import 'b.dart';
|
||||||
|
|
||||||
|
@ -286,13 +287,17 @@ class MyType implements CustomSqlType<String> {}
|
||||||
final table = file.analyzedElements.single as DriftTable;
|
final table = file.analyzedElements.single as DriftTable;
|
||||||
final column = table.columns.single;
|
final column = table.columns.single;
|
||||||
|
|
||||||
expect(column.sqlType.isCustom, isTrue);
|
switch (column.sqlType) {
|
||||||
expect(column.sqlType.custom?.dartType.toString(), 'String');
|
case ColumnDriftType():
|
||||||
expect(column.sqlType.custom?.expression.toString(), 'MyType()');
|
fail('expect custom type');
|
||||||
|
case ColumnCustomType(:final custom):
|
||||||
|
expect(custom.dartType.toString(), 'String');
|
||||||
|
expect(custom.expression.toString(), 'MyType()');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('recognizes bigint columns', () async {
|
test('recognizes bigint columns', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
bar INT64 NOT NULL
|
bar INT64 NOT NULL
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('supports virtual tables across drift files', () async {
|
test('supports virtual tables across drift files', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/table.drift': '''
|
'a|lib/table.drift': '''
|
||||||
CREATE TABLE example_table (
|
CREATE TABLE example_table (
|
||||||
|
@ -43,7 +43,7 @@ exampleSearch: SELECT example_table.**, s.* FROM example_table
|
||||||
});
|
});
|
||||||
|
|
||||||
test('query virtual tables with unknown function', () async {
|
test('query virtual tables with unknown function', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/table.drift': '''
|
'a|lib/table.drift': '''
|
||||||
CREATE TABLE example_table (
|
CREATE TABLE example_table (
|
||||||
|
@ -72,7 +72,7 @@ SELECT rowid, highlight(example_table_search, 0, '[match]', '[match]') name,
|
||||||
});
|
});
|
||||||
|
|
||||||
test('supports spellfix1 tables', () async {
|
test('supports spellfix1 tables', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{'a|lib/a.drift': 'CREATE VIRTUAL TABLE demo USING spellfix1;'},
|
{'a|lib/a.drift': 'CREATE VIRTUAL TABLE demo USING spellfix1;'},
|
||||||
options: DriftOptions.defaults(
|
options: DriftOptions.defaults(
|
||||||
dialect: DialectOptions(
|
dialect: DialectOptions(
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Future<Iterable<SqlQuery>> analyzeQueries(String driftFile) async {
|
Future<Iterable<SqlQuery>> 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');
|
final result = await state.analyze('package:a/a.drift');
|
||||||
return result.fileAnalysis!.resolvedQueries.values;
|
return result.fileAnalysis!.resolvedQueries.values;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import 'utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('recognizes existing row classes', () async {
|
test('recognizes existing row classes', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can use named constructors', () async {
|
test('can use named constructors', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("warns if existing row classes don't exist", () async {
|
test("warns if existing row classes don't exist", () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ foo WITH MyRow: SELECT 'hello world', 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves existing row class', () async {
|
test('resolves existing row class', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ class MyRow {
|
||||||
|
|
||||||
group('matches', () {
|
group('matches', () {
|
||||||
test('single column type', () async {
|
test('single column type', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
foo WITH int: SELECT 1 AS r;
|
foo WITH int: SELECT 1 AS r;
|
||||||
''',
|
''',
|
||||||
|
@ -139,7 +139,7 @@ foo WITH int: SELECT 1 AS r;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('single table', () async {
|
test('single table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ typedef MyRow = TblData;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('single table with custom row class', () async {
|
test('single table with custom row class', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ class MyTableRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('alternative to table class', () async {
|
test('alternative to table class', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ class MyQueryRow {
|
||||||
|
|
||||||
group('nested column', () {
|
group('nested column', () {
|
||||||
test('single column into field', () async {
|
test('single column into field', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ class MyQueryRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('single column into single-element record', () async {
|
test('single column into single-element record', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ class MyQueryRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('custom result set', () async {
|
test('custom result set', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -344,7 +344,7 @@ class JsonStructure {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('table', () async {
|
test('table', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -382,7 +382,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('table as alternative to row class', () async {
|
test('table as alternative to row class', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -425,7 +425,7 @@ class MyRow {
|
||||||
|
|
||||||
group('nested LIST query', () {
|
group('nested LIST query', () {
|
||||||
test('single column type', () async {
|
test('single column type', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -461,7 +461,7 @@ class MyQueryRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('custom result set with class', () async {
|
test('custom result set with class', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -503,7 +503,7 @@ class MyNestedTable {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('custom result set with record', () async {
|
test('custom result set with record', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -545,7 +545,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('into record', () async {
|
test('into record', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -586,7 +586,7 @@ typedef MyRow = (int, List<TblData>);
|
||||||
test(
|
test(
|
||||||
'default record',
|
'default record',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
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 {
|
test('mix', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -685,7 +685,7 @@ class MyRow {
|
||||||
|
|
||||||
group('error', () {
|
group('error', () {
|
||||||
test('when the specified class has no default constructor', () async {
|
test('when the specified class has no default constructor', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -705,7 +705,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when the desired constructor does not exist', () async {
|
test('when the desired constructor does not exist', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -725,7 +725,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there is a parameter with no matching column', () async {
|
test('when there is a parameter with no matching column', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -745,7 +745,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a record has too many positional fields', () async {
|
test('when a record has too many positional fields', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -767,7 +767,7 @@ typedef MyRow = (int, String, DateTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a record has an unmatched named field', () async {
|
test('when a record has an unmatched named field', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
@ -788,7 +788,7 @@ typedef MyRow = (int, {String d});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when there is a type mismatch on a scalar column', () async {
|
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': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -808,7 +808,7 @@ class MyRow {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('when a list column is not a list', () async {
|
test('when a list column is not a list', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
@ -832,7 +832,7 @@ class MyRow {
|
||||||
test(
|
test(
|
||||||
'when there is a type mismatch on a nested scalar column',
|
'when there is a type mismatch on a nested scalar column',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'a.dart';
|
import 'a.dart';
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('experimental inference - integration test', () async {
|
test('experimental inference - integration test', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE artists (
|
CREATE TABLE artists (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -76,7 +76,7 @@ q: SELECT * FROM t WHERE i IN ?1;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('warns about default values outside of expressions', () async {
|
test('warns about default values outside of expressions', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': r'''
|
'foo|lib/a.drift': r'''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY,
|
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 {
|
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'''
|
'foo|lib/a.drift': r'''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY,
|
id INT NOT NULL PRIMARY KEY,
|
||||||
|
@ -118,7 +118,7 @@ in: INSERT INTO foo (id) $placeholder;
|
||||||
test(
|
test(
|
||||||
'warns when nested results appear in compound statements',
|
'warns when nested results appear in compound statements',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY,
|
id INT NOT NULL PRIMARY KEY,
|
||||||
|
@ -142,7 +142,7 @@ all: SELECT foo.** FROM foo UNION ALL SELECT foo.** FROM foo;
|
||||||
test(
|
test(
|
||||||
'warns when nested query appear in nested query',
|
'warns when nested query appear in nested query',
|
||||||
() async {
|
() async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY,
|
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 {
|
test('in top-level queries', () async {
|
||||||
state = TestBackend.inTest({
|
state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
@ -189,7 +189,7 @@ test: INSERT INTO foo VALUES (?)
|
||||||
});
|
});
|
||||||
|
|
||||||
test('in CREATE TRIGGER statements', () async {
|
test('in CREATE TRIGGER statements', () async {
|
||||||
state = TestBackend.inTest({
|
state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
@ -205,7 +205,7 @@ END;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('in @create statements', () async {
|
test('in @create statements', () async {
|
||||||
state = TestBackend.inTest({
|
state = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INT NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -5,7 +5,7 @@ import '../../test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('select from view', () async {
|
test('select from view', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TABLE artists (
|
CREATE TABLE artists (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('respects explicit type arguments', () async {
|
test('respects explicit type arguments', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': '''
|
'foo|lib/main.drift': '''
|
||||||
bar(?1 AS TEXT, :foo AS BOOLEAN): SELECT ?, :foo;
|
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 {
|
test('can read from builtin tables', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/main.drift': '''
|
'a|lib/main.drift': '''
|
||||||
testQuery: SELECT * FROM sqlite_schema;
|
testQuery: SELECT * FROM sqlite_schema;
|
||||||
''',
|
''',
|
||||||
|
@ -43,7 +43,7 @@ testQuery: SELECT * FROM sqlite_schema;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('reads REQUIRED syntax', () async {
|
test('reads REQUIRED syntax', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': '''
|
'foo|lib/main.drift': '''
|
||||||
bar(REQUIRED ?1 AS TEXT OR NULL, REQUIRED :foo AS BOOLEAN): SELECT ?, :foo;
|
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 {
|
test('infers result set for views', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': r'''
|
'foo|lib/main.drift': r'''
|
||||||
CREATE VIEW my_view AS SELECT 'foo', 2;
|
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 {
|
test('infers nested result set for views', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': r'''
|
'foo|lib/main.drift': r'''
|
||||||
CREATE VIEW my_view AS SELECT 'foo', 2;
|
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 {
|
test('infers nested result sets for custom result sets', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': r'''
|
'foo|lib/main.drift': r'''
|
||||||
query: SELECT 1 AS a, b.** FROM (SELECT 2 AS b, 3 AS c) AS b;
|
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]) {
|
for (final dateTimeAsText in [false, true]) {
|
||||||
test('analyzing date times (stored as text: $dateTimeAsText)', () async {
|
test('analyzing date times (stored as text: $dateTimeAsText)', () async {
|
||||||
final state = TestBackend.inTest(
|
final state = await TestBackend.inTest(
|
||||||
{
|
{
|
||||||
'foo|lib/foo.drift': r'''
|
'foo|lib/foo.drift': r'''
|
||||||
CREATE TABLE foo (
|
CREATE TABLE foo (
|
||||||
|
@ -202,7 +202,7 @@ q3: SELECT datetime('now');
|
||||||
}
|
}
|
||||||
|
|
||||||
test('resolves nested result sets', () async {
|
test('resolves nested result sets', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': r'''
|
'foo|lib/main.drift': r'''
|
||||||
CREATE TABLE points (
|
CREATE TABLE points (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
@ -243,7 +243,7 @@ FROM routes
|
||||||
});
|
});
|
||||||
|
|
||||||
test('resolves nullability of aliases in nested result sets', () async {
|
test('resolves nullability of aliases in nested result sets', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'foo|lib/main.drift': r'''
|
'foo|lib/main.drift': r'''
|
||||||
CREATE TABLE tableA1 (id INTEGER);
|
CREATE TABLE tableA1 (id INTEGER);
|
||||||
CREATE TABLE tableB1 (id INTEGER);
|
CREATE TABLE tableB1 (id INTEGER);
|
||||||
|
@ -285,7 +285,7 @@ LEFT JOIN tableB1 AS tableB2 -- nullable
|
||||||
|
|
||||||
test('supports custom functions', () async {
|
test('supports custom functions', () async {
|
||||||
final withoutOptions =
|
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');
|
var result = await withoutOptions.analyze('package:a/a.drift');
|
||||||
expect(result.allErrors, [
|
expect(result.allErrors, [
|
||||||
isDriftError('Function my_function could not be found')
|
isDriftError('Function my_function could not be found')
|
||||||
|
@ -294,14 +294,13 @@ LEFT JOIN tableB1 AS tableB2 -- nullable
|
||||||
.withSpan('my_function()'),
|
.withSpan('my_function()'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final withOptions =
|
final withOptions = await TestBackend.inTest(
|
||||||
TestBackend.inTest({'a|lib/a.drift': 'a: SELECT my_function(?, ?);'},
|
{'a|lib/a.drift': 'a: SELECT my_function(?, ?);'},
|
||||||
options: DriftOptions.defaults(
|
options: DriftOptions.defaults(
|
||||||
sqliteAnalysisOptions: SqliteAnalysisOptions(knownFunctions: {
|
sqliteAnalysisOptions: SqliteAnalysisOptions(knownFunctions: {
|
||||||
'my_function':
|
'my_function': KnownSqliteFunction.fromJson('boolean (int, text)')
|
||||||
KnownSqliteFunction.fromJson('boolean (int, text)')
|
}),
|
||||||
}),
|
));
|
||||||
));
|
|
||||||
result = await withOptions.analyze('package:a/a.drift');
|
result = await withOptions.analyze('package:a/a.drift');
|
||||||
|
|
||||||
withOptions.expectNoErrors();
|
withOptions.expectNoErrors();
|
||||||
|
@ -318,7 +317,7 @@ LEFT JOIN tableB1 AS tableB2 -- nullable
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can cast to DATETIME and BOOLEAN', () async {
|
test('can cast to DATETIME and BOOLEAN', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
a: SELECT CAST(1 AS BOOLEAN) AS a, CAST(2 AS DATETIME) as b;
|
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 {
|
test('can cast to enum type', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'enum.dart';
|
import 'enum.dart';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
group('from clean state', () {
|
group('from clean state', () {
|
||||||
test('resolves simple tables', () async {
|
test('resolves simple tables', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
foo INTEGER PRIMARY KEY,
|
foo INTEGER PRIMARY KEY,
|
||||||
|
@ -42,7 +42,7 @@ CREATE TABLE b (
|
||||||
|
|
||||||
group('references', () {
|
group('references', () {
|
||||||
test('self', () async {
|
test('self', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
foo INTEGER PRIMARY KEY,
|
foo INTEGER PRIMARY KEY,
|
||||||
|
@ -61,7 +61,7 @@ CREATE TABLE a (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('across files', () async {
|
test('across files', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'b.drift';
|
import 'b.drift';
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ CREATE TABLE b (
|
||||||
});
|
});
|
||||||
|
|
||||||
test('for triggers', () async {
|
test('for triggers', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'b.drift';
|
import 'b.drift';
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ CREATE TABLE deleted_b (
|
||||||
|
|
||||||
group('non-existing', () {
|
group('non-existing', () {
|
||||||
test('from table', () async {
|
test('from table', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
foo INTEGER PRIMARY KEY,
|
foo INTEGER PRIMARY KEY,
|
||||||
|
@ -151,7 +151,7 @@ CREATE TABLE a (
|
||||||
[isDriftError('`b` could not be found in any import.')]);
|
[isDriftError('`b` could not be found in any import.')]);
|
||||||
});
|
});
|
||||||
test('in a trigger', () async {
|
test('in a trigger', () async {
|
||||||
final backend = TestBackend.inTest(const {
|
final backend = await TestBackend.inTest(const {
|
||||||
'foo|lib/a.drift': '''
|
'foo|lib/a.drift': '''
|
||||||
CREATE TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar BEGIN
|
CREATE TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar BEGIN
|
||||||
END;
|
END;
|
||||||
|
@ -172,7 +172,7 @@ END;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('emits warning on invalid import', () async {
|
test('emits warning on invalid import', () async {
|
||||||
final backend = TestBackend.inTest({
|
final backend = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': "import 'b.drift';",
|
'a|lib/a.drift': "import 'b.drift';",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import '../test_utils.dart';
|
||||||
void main() {
|
void main() {
|
||||||
late TestBackend tester;
|
late TestBackend tester;
|
||||||
|
|
||||||
setUpAll(() => tester = TestBackend({}));
|
setUpAll(() async => tester = await TestBackend.init({}));
|
||||||
tearDownAll(() => tester.dispose());
|
tearDownAll(() => tester.dispose());
|
||||||
|
|
||||||
group('from AST', () {
|
group('from AST', () {
|
||||||
|
|
|
@ -37,24 +37,43 @@ class TestBackend extends DriftBackend {
|
||||||
AnalysisContext? _dartContext;
|
AnalysisContext? _dartContext;
|
||||||
OverlayResourceProvider? _resourceProvider;
|
OverlayResourceProvider? _resourceProvider;
|
||||||
|
|
||||||
TestBackend(
|
TestBackend._(
|
||||||
Map<String, String> sourceContents, {
|
Map<String, String> sourceContents, {
|
||||||
DriftOptions options = const DriftOptions.defaults(),
|
DriftOptions options = const DriftOptions.defaults(),
|
||||||
this.analyzerExperiments = const Iterable.empty(),
|
this.analyzerExperiments = const Iterable.empty(),
|
||||||
}) : sourceContents = {
|
}) : sourceContents = {
|
||||||
for (final entry in sourceContents.entries)
|
for (final entry in sourceContents.entries)
|
||||||
AssetId.parse(entry.key).uri.toString(): entry.value,
|
AssetId.parse(entry.key).uri.toString(): entry.value,
|
||||||
} {
|
};
|
||||||
driver = DriftAnalysisDriver(this, options, isTesting: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
factory TestBackend.inTest(
|
static Future<TestBackend> init(
|
||||||
Map<String, String> sourceContents, {
|
Map<String, String> sourceContents, {
|
||||||
DriftOptions options = const DriftOptions.defaults(),
|
DriftOptions options = const DriftOptions.defaults(),
|
||||||
Iterable<String> analyzerExperiments = const Iterable.empty(),
|
Iterable<String> analyzerExperiments = const Iterable.empty(),
|
||||||
}) {
|
}) async {
|
||||||
final backend = TestBackend(sourceContents,
|
final backend = TestBackend._(
|
||||||
options: options, analyzerExperiments: analyzerExperiments);
|
sourceContents,
|
||||||
|
options: options,
|
||||||
|
analyzerExperiments: analyzerExperiments,
|
||||||
|
);
|
||||||
|
|
||||||
|
backend.driver =
|
||||||
|
await DriftAnalysisDriver.init(backend, options, isTesting: true);
|
||||||
|
|
||||||
|
return backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<TestBackend> inTest(
|
||||||
|
Map<String, String> sourceContents, {
|
||||||
|
DriftOptions options = const DriftOptions.defaults(),
|
||||||
|
Iterable<String> analyzerExperiments = const Iterable.empty(),
|
||||||
|
}) async {
|
||||||
|
final backend = await TestBackend.init(
|
||||||
|
sourceContents,
|
||||||
|
options: options,
|
||||||
|
analyzerExperiments: analyzerExperiments,
|
||||||
|
);
|
||||||
|
|
||||||
addTearDown(backend.dispose);
|
addTearDown(backend.dispose);
|
||||||
|
|
||||||
return backend;
|
return backend;
|
||||||
|
@ -62,9 +81,10 @@ class TestBackend extends DriftBackend {
|
||||||
|
|
||||||
static Future<FileState> analyzeSingle(String content,
|
static Future<FileState> analyzeSingle(String content,
|
||||||
{String asset = 'a|lib/a.drift',
|
{String asset = 'a|lib/a.drift',
|
||||||
DriftOptions options = const DriftOptions.defaults()}) {
|
DriftOptions options = const DriftOptions.defaults()}) async {
|
||||||
final assetId = AssetId.parse(asset);
|
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);
|
return backend.driver.fullyAnalyze(assetId.uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +237,9 @@ class TestBackend extends DriftBackend {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get canReadDart => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<LibraryElement> readDart(Uri uri) async {
|
Future<LibraryElement> readDart(Uri uri) async {
|
||||||
await ensureHasDartAnalyzer();
|
await ensureHasDartAnalyzer();
|
||||||
|
|
|
@ -578,11 +578,15 @@ class MyDatabase {
|
||||||
};
|
};
|
||||||
final outputs = await emulateDriftBuild(inputs: inputs);
|
final outputs = await emulateDriftBuild(inputs: inputs);
|
||||||
final readAssets = outputs.readAssetsByBuilder;
|
final readAssets = outputs.readAssetsByBuilder;
|
||||||
|
// Allow reading SDK or other package assets to set up the analyzer.
|
||||||
|
final isFromExternalPackage =
|
||||||
|
isA<AssetId>().having((e) => e.package, 'package', isNot('a'));
|
||||||
|
|
||||||
Matcher onlyReadsJsonsAnd(dynamic other) {
|
Matcher onlyReadsJsonsAnd(dynamic other) {
|
||||||
return everyElement(
|
return everyElement(
|
||||||
anyOf(
|
anyOf(
|
||||||
isA<AssetId>().having((e) => e.extension, 'extension', '.json'),
|
isA<AssetId>().having((e) => e.extension, 'extension', '.json'),
|
||||||
|
isFromExternalPackage,
|
||||||
other,
|
other,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -606,7 +610,8 @@ class MyDatabase {
|
||||||
// However, the discover builder should not read other drift files.
|
// However, the discover builder should not read other drift files.
|
||||||
for (final input in inputs.keys) {
|
for (final input in inputs.keys) {
|
||||||
if (input.endsWith('.drift')) {
|
if (input.endsWith('.drift')) {
|
||||||
expectReadsForBuilder(input, DriftDiscover, [makeAssetId(input)]);
|
expectReadsForBuilder(input, DriftDiscover,
|
||||||
|
everyElement(anyOf(makeAssetId(input), isFromExternalPackage)));
|
||||||
} else {
|
} else {
|
||||||
expectReadsForBuilder(
|
expectReadsForBuilder(
|
||||||
input,
|
input,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import '../analysis/test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('finds update rules for triggers', () async {
|
test('finds update rules for triggers', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
@ -51,7 +51,7 @@ class MyDatabase {}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('finds update rules for foreign key constraint', () async {
|
test('finds update rules for foreign key constraint', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
CREATE TABLE a (
|
CREATE TABLE a (
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -95,7 +95,7 @@ CREATE VIEW user_ids AS SELECT id FROM users;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<DriftElement>> _analyzeAndSerialize(String source) async {
|
Future<List<DriftElement>> _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 file = await state.analyze('package:a/a.drift');
|
||||||
|
|
||||||
final writer = SchemaWriter(file.analyzedElements.toList());
|
final writer = SchemaWriter(file.analyzedElements.toList());
|
||||||
|
|
|
@ -14,7 +14,7 @@ import '../../analysis/test_utils.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('writer integration test', () async {
|
test('writer integration test', () async {
|
||||||
final state = TestBackend.inTest({
|
final state = await TestBackend.inTest({
|
||||||
'a|lib/a.drift': '''
|
'a|lib/a.drift': '''
|
||||||
import 'main.dart';
|
import 'main.dart';
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ void main() {
|
||||||
{DriftOptions options = const DriftOptions.defaults(
|
{DriftOptions options = const DriftOptions.defaults(
|
||||||
generateNamedParameters: true,
|
generateNamedParameters: true,
|
||||||
)}) async {
|
)}) async {
|
||||||
final state =
|
final state = await TestBackend.inTest({'a|lib/main.drift': driftFile},
|
||||||
TestBackend.inTest({'a|lib/main.drift': driftFile}, options: options);
|
options: options);
|
||||||
final file = await state.analyze('package:a/main.drift');
|
final file = await state.analyze('package:a/main.drift');
|
||||||
state.expectNoErrors();
|
state.expectNoErrors();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ export 'src/analysis/analysis.dart';
|
||||||
export 'src/analysis/types/join_analysis.dart';
|
export 'src/analysis/types/join_analysis.dart';
|
||||||
export 'src/ast/ast.dart';
|
export 'src/ast/ast.dart';
|
||||||
export 'src/engine/module/fts5.dart' show Fts5Extension, Fts5Table;
|
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/json1.dart' show Json1Extension;
|
||||||
export 'src/engine/module/math.dart' show BuiltInMathExtension;
|
export 'src/engine/module/math.dart' show BuiltInMathExtension;
|
||||||
export 'src/engine/module/rtree.dart' show RTreeExtension;
|
export 'src/engine/module/rtree.dart' show RTreeExtension;
|
||||||
|
|
|
@ -41,6 +41,7 @@ class ResolvedType {
|
||||||
this.hints = const [],
|
this.hints = const [],
|
||||||
this.nullable = false,
|
this.nullable = false,
|
||||||
this.isArray = false});
|
this.isArray = false});
|
||||||
|
|
||||||
const ResolvedType.bool({bool? nullable = false})
|
const ResolvedType.bool({bool? nullable = false})
|
||||||
: this(
|
: this(
|
||||||
type: BasicType.int,
|
type: BasicType.int,
|
||||||
|
@ -111,6 +112,7 @@ abstract class TypeHint {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => runtimeType.hashCode;
|
int get hashCode => runtimeType.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => other.runtimeType == runtimeType;
|
bool operator ==(Object other) => other.runtimeType == runtimeType;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +133,12 @@ class IsBigInt extends TypeHint {
|
||||||
const IsBigInt();
|
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,
|
/// 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
|
/// 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.
|
/// properly. Failure to resolve the type will have the [unknown] flag set.
|
||||||
|
@ -152,10 +160,12 @@ class ResolveResult {
|
||||||
const ResolveResult(this.type)
|
const ResolveResult(this.type)
|
||||||
: needsContext = false,
|
: needsContext = false,
|
||||||
unknown = false;
|
unknown = false;
|
||||||
|
|
||||||
const ResolveResult.needsContext()
|
const ResolveResult.needsContext()
|
||||||
: type = null,
|
: type = null,
|
||||||
needsContext = true,
|
needsContext = true,
|
||||||
unknown = false;
|
unknown = false;
|
||||||
|
|
||||||
const ResolveResult.unknown()
|
const ResolveResult.unknown()
|
||||||
: type = null,
|
: type = null,
|
||||||
needsContext = false,
|
needsContext = false,
|
||||||
|
|
|
@ -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 = <TableColumn>[
|
||||||
|
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<String> 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<Typeable> 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<ResolvedType> 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'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue