Move ffi bindings into new moor_ffi package

This commit is contained in:
Simon Binder 2019-09-14 20:54:13 +02:00
parent 9e498fb575
commit 928c9832b5
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
33 changed files with 275 additions and 128 deletions

View File

@ -9,6 +9,10 @@ TODO: Properly describe these additions when they're finalized:
- Analyzer plugin for Dart Code
- `ffi` libraries
### Minor changes
- a `Constant<String>` can now be written to SQL, it used to throw before. This is useful
if you need default values for strings columns.
### Breaking changes
- __THIS LIKELY AFFECTS YOUR APP:__ Removed the `transaction` parameter for callbacks
in transactions and `beforeOpen` callbacks. So, instead of writing
@ -28,7 +32,9 @@ TODO: Properly describe these additions when they're finalized:
your database instead of a transaction objects. They will be delegated automatically.
On a similar note, we also removed the `operateOn` parameter from compiled queries.
- Compiled queries that return only a single column (e.g. `SELECT COUNT(*) FROM users`)
will just return their value (in this case, an `int`) now. Moor no longer generates a
new class in that case.
- Removed `MigrationStrategy.onFinished`. Use `beforeOpen` instead.
- Compiled sql queries starting with an underscore will now generate private match queries.
Previously, the query `_allUsers` would generate a `watchAllUsers` method, that has been

View File

@ -1,15 +0,0 @@
/// A version of moor that runs on the Dart VM by integrating sqlite3 with
/// ffi.
@experimental
library moor_vm;
import 'dart:async';
import 'dart:io';
import 'package:meta/meta.dart';
import 'backends.dart';
import 'moor.dart';
import 'src/vm/api/database.dart';
part 'src/vm/vm_database.dart';

View File

@ -1,105 +0,0 @@
import 'dart:async';
import 'package:moor/moor.dart';
import 'package:pedantic/pedantic.dart';
import 'package:test_api/test_api.dart';
import 'package:moor/moor_vm.dart';
import '../data/tables/todos.dart';
TodoDb db;
void main() {
test('CRUD integration test', () async {
db = TodoDb(VMDatabase.memory(logStatements: false));
// write some dummy data
await insertCategory();
await insertUser();
await insertTodos();
await db.into(db.sharedTodos).insert(SharedTodo(todo: 2, user: 1));
// test select statements
final forUser = (await db.someDao.todosForUser(1)).single;
expect(forUser.title, 'Another entry');
// test delete statements
await db.deleteTodoById(2);
final queryAgain = await db.someDao.todosForUser(1);
expect(queryAgain, isEmpty);
// test update statements
await (db.update(db.todosTable)..where((t) => t.id.equals(1)))
.write(const TodosTableCompanion(content: Value('Updated content')));
final readUpdated = await db.select(db.todosTable).getSingle();
expect(readUpdated.content, 'Updated content');
});
test('Transactions test', () async {
db = TodoDb(VMDatabase.memory(logStatements: false));
final completedOperations = StreamController<String>();
unawaited(db.transaction((_) async {
await insertCategory();
completedOperations.add('transaction');
await pumpEventQueue();
}));
unawaited(insertUser().then((_) {
completedOperations.add('regular');
}));
await expectLater(
completedOperations.stream, emitsInOrder(['transaction', 'regular']));
// call .getSingle to verify both rows have been written
await db.select(db.users).getSingle();
await db.select(db.categories).getSingle();
});
}
Future insertCategory() async {
final forInsert = const CategoriesCompanion(description: Value('Work'));
final row = Category(id: 1, description: 'Work');
final id = await db.into(db.categories).insert(forInsert);
expect(id, equals(1));
final loaded = await db.select(db.categories).getSingle();
expect(loaded, equals(row));
}
Future insertUser() async {
final profilePic = Uint8List.fromList([1, 2, 3, 4, 5, 6]);
final forInsert = UsersCompanion(
name: const Value('Dashy McDashface'),
isAwesome: const Value(true),
profilePicture: Value(profilePic),
);
final id = await db.into(db.users).insert(forInsert);
expect(id, equals(1));
final user = await db.select(db.users).getSingle();
expect(user.id, equals(1));
expect(user.name, equals('Dashy McDashface'));
expect(user.isAwesome, isTrue);
expect(user.profilePicture, profilePic);
}
Future insertTodos() async {
await db.into(db.todosTable).insertAll([
TodosTableCompanion(
title: const Value('A first entry'),
content: const Value('Some content I guess'),
targetDate: Value(DateTime(2019)),
),
const TodosTableCompanion(
title: Value('Another entry'),
content: Value('this is a really creative test case'),
category: Value(1), // "Work"
),
]);
}

12
moor_ffi/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.DS_Store
.dart_tool/
.packages
.pub/
build/
pubspec.lock
# todo determine whether metadata should be added to gitignore (the file says it shouldn't, but does this break)?
.metadata

3
moor_ffi/CHANGELOG.md Normal file
View File

@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

24
moor_ffi/LICENSE Normal file
View File

@ -0,0 +1,24 @@
MIT License
Copyright (c) 2019 Simon Binder
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This project also bundles sqlite, which is in the Public Domain.
See https://www.sqlite.org/copyright.html

14
moor_ffi/README.md Normal file
View File

@ -0,0 +1,14 @@
# moor_ffi
Moor backend that uses the new `dart:ffi` apis.
## Supported platforms
At the moment, this plugin supports Android natively. However, it's also going to run on all
platforms that expose `sqlite3` as a shared native library (macOS and virtually all Linux
distros).
## Notes
Using `flutter run` or `flutter build` when this library is imported is going to take very long for
the first time. The reason is that we compile sqlite. Subsequent builds should take an acceptable
time execute.

12
moor_ffi/android/.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild/
cpp/sqlite*
sqlite_*.zip

View File

@ -0,0 +1,67 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
}
}
plugins {
id "de.undercouch.download" version "4.0.0"
}
group 'com.example.moor_ffi'
version '1.0'
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path "cpp/CMakeLists.txt"
}
}
lintOptions {
disable 'InvalidPackage'
}
}
task downloadSqlite(type: Download) {
src 'https://sqlite.org/2019/sqlite-amalgamation-3290000.zip'
dest 'sqlite_3290000.zip'
overwrite false
}
task extractSqlite(dependsOn: downloadSqlite, type: Copy) {
from zipTree(downloadSqlite.dest).matching {
include '*/sqlite3.c'
eachFile { it.setPath(it.getName()) } // Don't use top-level folder in zip
}
into 'cpp'
}
preBuild.dependsOn extractSqlite

View File

@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.4.1)
project(sqlite3)
add_library(sqlite3 SHARED sqlite3.c)

View File

@ -0,0 +1,2 @@
org.gradle.jvmargs=-Xmx1536M

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip

View File

@ -0,0 +1 @@
rootProject.name = 'moor_ffi'

37
moor_ffi/ios/.gitignore vendored Normal file
View File

@ -0,0 +1,37 @@
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
/Flutter/flutter_export_environment.sh

View File

View File

@ -0,0 +1,4 @@
#import <Flutter/Flutter.h>
@interface MoorFfiPlugin : NSObject<FlutterPlugin>
@end

View File

@ -0,0 +1,8 @@
#import "MoorFfiPlugin.h"
#import <moor_ffi/moor_ffi-Swift.h>
@implementation MoorFfiPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
[SwiftMoorFfiPlugin registerWithRegistrar:registrar];
}
@end

View File

@ -0,0 +1,14 @@
import Flutter
import UIKit
public class SwiftMoorFfiPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "moor_ffi", binaryMessenger: registrar.messenger())
let instance = SwiftMoorFfiPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}

View File

@ -0,0 +1,21 @@
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'moor_ffi'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
s.ios.deployment_target = '8.0'
end

View File

@ -0,0 +1,7 @@
import 'dart:io';
import 'package:moor/backends.dart';
import 'package:moor/moor.dart';
import 'package:moor_ffi/src/api/database.dart';
part 'src/vm_database.dart';

View File

@ -4,11 +4,11 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:moor/src/vm/bindings/constants.dart';
import 'package:moor/src/vm/bindings/types.dart' as types;
import 'package:moor/src/vm/bindings/bindings.dart';
import 'package:moor/src/vm/ffi/blob.dart';
import 'package:moor/src/vm/ffi/utils.dart';
import 'package:moor_ffi/src/bindings/constants.dart';
import 'package:moor_ffi/src/bindings/types.dart' as types;
import 'package:moor_ffi/src/bindings/bindings.dart';
import 'package:moor_ffi/src/ffi/blob.dart';
import 'package:moor_ffi/src/ffi/utils.dart';
part 'errors.dart';
part 'prepared_statement.dart';

View File

@ -3,7 +3,7 @@ import 'dart:ffi';
import 'dart:typed_data';
import 'package:moor/src/vm/ffi/utils.dart';
import 'package:moor_ffi/src/ffi/utils.dart';
/// Pointer to arbitrary blobs in C.
class CBlob extends Struct<CBlob> {
@ -33,6 +33,7 @@ class CBlob extends Struct<CBlob> {
final str = addressOf;
if (isNullPointer(str)) return null;
// todo can we user Pointer.asExternalTypedData here?
final blob = Uint8List(bytesToRead);
for (var i = 0; i < bytesToRead; ++i) {
blob[i] = str.elementAt(i).load<CBlob>().data;

View File

@ -1,4 +1,4 @@
part of 'package:moor/moor_vm.dart';
part of 'package:moor_ffi/moor_ffi.dart';
/// A moor database that runs on the Dart VM.
class VMDatabase extends DelegatedDatabase {

25
moor_ffi/pubspec.yaml Normal file
View File

@ -0,0 +1,25 @@
name: moor_ffi
description: "Experimental moor implementation that uses dart:ffi"
version: 2.0.0-dev
author:
homepage:
environment:
sdk: ">=2.5.0-dev <2.6.0"
dependencies:
moor: ^2.0.0
# flutter:
# sdk: flutter
dev_dependencies:
test: ^1.6.0
#flutter:
# plugin:
# androidPackage: com.example.moor_ffi
# pluginClass: MoorFfiPlugin
dependency_overrides:
moor:
path: ../moor