mirror of https://github.com/AMT-Cheif/drift.git
More docs, integration tests
This commit is contained in:
parent
5e5628d1a8
commit
efc5a6c985
|
@ -179,6 +179,8 @@ We currently support the following extensions:
|
||||||
- `rtree`: Static analysis support for the [R*Tree](https://www.sqlite.org/rtree.html) extension.
|
- `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.
|
||||||
- `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).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -8,9 +8,13 @@ import 'dart:typed_data';
|
||||||
import '../src/runtime/query_builder/query_builder.dart';
|
import '../src/runtime/query_builder/query_builder.dart';
|
||||||
import '../src/runtime/types/mapping.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> {
|
final class GeopolyPolygonType implements CustomSqlType<GeopolyPolygon> {
|
||||||
///
|
/// Default constant constructor for the geopoly type.
|
||||||
const GeopolyPolygonType();
|
const GeopolyPolygonType();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -43,29 +47,33 @@ final class GeopolyPolygonType implements CustomSqlType<GeopolyPolygon> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In Geopoly, a polygon can be text or a blob
|
/// In Geopoly, a polygon can be text or a blob.
|
||||||
sealed class GeopolyPolygon {
|
sealed class GeopolyPolygon {
|
||||||
const 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;
|
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;
|
const factory GeopolyPolygon.blob(Uint8List value) = GeopolyPolygonBlob;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// A [GeopolyPolygon] being described as text.
|
||||||
final class GeopolyPolygonString extends GeopolyPolygon {
|
final class GeopolyPolygonString extends GeopolyPolygon {
|
||||||
///
|
/// The textual description of the polygon.
|
||||||
final String value;
|
final String value;
|
||||||
|
|
||||||
///
|
/// Creates a polygon from the underlying textual [value].
|
||||||
const GeopolyPolygonString(this.value) : super._();
|
const GeopolyPolygonString(this.value) : super._();
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// A [GeopolyPolygon] being described as binary data.
|
||||||
final class GeopolyPolygonBlob extends GeopolyPolygon {
|
final class GeopolyPolygonBlob extends GeopolyPolygon {
|
||||||
///
|
/// The binary description of the polygon.
|
||||||
final Uint8List value;
|
final Uint8List value;
|
||||||
|
|
||||||
///
|
/// Creates a polygon from the underlying binary [value].
|
||||||
const GeopolyPolygonBlob(this.value) : super._();
|
const GeopolyPolygonBlob(this.value) : super._();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Mocks generated by Mockito 5.4.3 from annotations
|
// Mocks generated by Mockito 5.4.4 from annotations
|
||||||
// in drift/test/test_utils/test_utils.dart.
|
// in drift/test/test_utils/test_utils.dart.
|
||||||
// Do not manually edit this file.
|
// Do not manually edit this file.
|
||||||
|
|
||||||
|
|
|
@ -583,6 +583,8 @@ class MyDatabase {
|
||||||
return everyElement(
|
return everyElement(
|
||||||
anyOf(
|
anyOf(
|
||||||
isA<AssetId>().having((e) => e.extension, 'extension', '.json'),
|
isA<AssetId>().having((e) => e.extension, 'extension', '.json'),
|
||||||
|
// Allow reading SDK or other package assets to set up the analyzer.
|
||||||
|
isA<AssetId>().having((e) => e.package, 'package', isNot('a')),
|
||||||
other,
|
other,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue