More docs, integration tests

This commit is contained in:
Simon Binder 2024-04-22 22:51:07 +02:00
parent 5e5628d1a8
commit efc5a6c985
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
8 changed files with 301 additions and 9 deletions

View File

@ -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).

View File

@ -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

View File

@ -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._();
}

View File

@ -0,0 +1,3 @@
CREATE VIRTUAL TABLE geopoly_test USING geopoly(a);
area: SELECT geopoly_area(_shape) FROM geopoly_test WHERE rowid = ?;

View File

@ -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;
}

View File

@ -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];
}

View File

@ -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.

View 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,
),
);