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.
|
||||
Enabling this option is safe when using a `NativeDatabase` with `sqlite3_flutter_libs`,
|
||||
which compiles sqlite3 with the R*Tree extension enabled.
|
||||
- [geopoly](https://www.sqlite.org/geopoly.html), a generalization of the R*Tree module supporting more complex
|
||||
polygons.
|
||||
- `moor_ffi`: Enables support for functions that are only available when using a `NativeDatabase`. This contains `pow`, `sqrt` and a variety
|
||||
of trigonometric functions. Details on those functions are available [here]({{ "../Platforms/vm.md#moor-only-functions" | pageUrl }}).
|
||||
- `math`: Assumes that sqlite3 was compiled with [math functions](https://www.sqlite.org/lang_mathfunc.html).
|
||||
|
|
|
@ -29,6 +29,7 @@ targets:
|
|||
modules:
|
||||
- json1
|
||||
- fts5
|
||||
- geopoly
|
||||
build_web_compilers:entrypoint:
|
||||
generate_for:
|
||||
- "web/drift_worker.dart"
|
||||
|
@ -59,3 +60,4 @@ targets:
|
|||
modules:
|
||||
- json1
|
||||
- fts5
|
||||
- geopoly
|
||||
|
|
|
@ -8,9 +8,13 @@ import 'dart:typed_data';
|
|||
import '../src/runtime/query_builder/query_builder.dart';
|
||||
import '../src/runtime/types/mapping.dart';
|
||||
|
||||
/// The type used for the `_shape` column in virtual `GEOPOLY` tables.
|
||||
///
|
||||
/// This type is responsible for representing shape values in Dart. It is
|
||||
/// created by drift when the `geopoly` extension is enabled and a `CREATE
|
||||
/// VIRTUAL TABLE USING geopoly` table is declared in a `.drift` file.
|
||||
final class GeopolyPolygonType implements CustomSqlType<GeopolyPolygon> {
|
||||
///
|
||||
/// Default constant constructor for the geopoly type.
|
||||
const GeopolyPolygonType();
|
||||
|
||||
@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 {
|
||||
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._();
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
// Do not manually edit this file.
|
||||
|
||||
|
|
|
@ -583,6 +583,8 @@ class MyDatabase {
|
|||
return everyElement(
|
||||
anyOf(
|
||||
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,
|
||||
),
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue