mirror of https://github.com/AMT-Cheif/drift.git
Delete deprecated moor_ffi package
This commit is contained in:
parent
603c9a20cc
commit
ae7aa51ee3
|
@ -1,13 +0,0 @@
|
|||
.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
|
||||
coverage/**
|
|
@ -1,58 +0,0 @@
|
|||
## 0.8.0
|
||||
|
||||
This package is now deprecated, but will continue to work for Flutter users.
|
||||
|
||||
Moor users should use the new `package:moor/ffi.dart` library.
|
||||
To migrate,
|
||||
- replace imports
|
||||
- of `package:moor_ffi/moor_ffi.dart` with `package:moor/ffi.dart`
|
||||
- of `package:moor_ffi/open_helper.dart` with `package:sqlite3/open.dart`
|
||||
- when using Flutter, add a dependency on `sqlite3_flutter_libs`
|
||||
|
||||
Users of this package that don't use moor should use the new [sqlite3](https://pub.dev/packages/sqlite3)
|
||||
package instead.
|
||||
|
||||
## 0.7.0
|
||||
|
||||
- Throw an error when using an unsupported datatype as argument
|
||||
- Return null from `REGEXP` when either argument is null (used to report an error)
|
||||
|
||||
## 0.6.0
|
||||
|
||||
- Added `moor_contains` sql function to support case-sensitive contains
|
||||
- Workaround for `dlopen` issues on some Android devices.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
- Provide mathematical functions in sql (`pow`, `power`, `sin`, `cos`, `tan`, `asin`, `atan`, `acos`, `sqrt`)
|
||||
- On Android, use sqlite 3.31.1
|
||||
- added an `extendedResultCode` to `SqliteException`
|
||||
|
||||
## 0.4.0
|
||||
|
||||
- Use precompiled libraries for faster build times
|
||||
|
||||
## 0.3.2
|
||||
|
||||
- Fix a bug where empty blobs would read as `null` instead of an empty list
|
||||
|
||||
## 0.3.1
|
||||
|
||||
- Implement `overrideForAll` and `overrideFor` - thanks, [@negator](https://github.com/negator)
|
||||
|
||||
## 0.3.0
|
||||
|
||||
- Better setup for compiling sqlite3 on Android
|
||||
- Compilation options to increase runtime performance, enable `fts5` and `json1`
|
||||
- We no longer download sqlite sources on the first run, they now ship with the plugin
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Remove the `background` flag from the moor apis provided by this package. Use the moor isolate api
|
||||
instead.
|
||||
- Remove builtin support for background execution from the low-level `Database` api
|
||||
- Support Dart 2.6, drop support for older versions
|
||||
|
||||
## 0.0.1
|
||||
|
||||
- Initial release. Contains standalone bindings and a moor implementation.
|
|
@ -1,24 +0,0 @@
|
|||
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
|
|
@ -1,111 +0,0 @@
|
|||
This package has been deprecated! Consider migrating to new sqlite packages depending on your setup:
|
||||
|
||||
- You use moor to build Flutter apps: Remove `moor_ffi` from your dependencies and add `sqlite3_flutter_libs`
|
||||
instead. Also, replace the following imports:
|
||||
- change `package:moor_ffi/moor_ffi.dart` to `package:moor/ffi.dart`
|
||||
- change `package:moor_ffi/open_helper.dart` to `package:sqlite3/open.dart`
|
||||
- You use moor, but without Flutter: Just drop the `moor_ffi` dependency and use the new
|
||||
`package:moor/ffi.dart` library. You need to make sure that a dynamic sqlite3 library is
|
||||
available at runtime.
|
||||
- You don't use moor, but you do use Flutter: Depend on `sqlite3` and `sqlite3_flutter_libs`. The
|
||||
`sqlite3` package contains the new Dart apis to use sqlite3.
|
||||
- You don't use the main moor package or Flutter: Just depend on the `sqlite3` package and make sure that a
|
||||
dynamic sqlite3 library is available at runtime
|
||||
|
||||
# moor_ffi
|
||||
|
||||
Dart bindings to sqlite by using `dart:ffi`. This library contains utils to make
|
||||
integration with [moor](https://pub.dev/packages/moor) easier, but it can also be used
|
||||
as a standalone package. It also doesn't depend on Flutter, so it can be used on Dart VM
|
||||
applications as well.
|
||||
|
||||
## Supported platforms
|
||||
You can make this library work on any platform that lets you obtain a `DynamicLibrary`
|
||||
in which sqlite's symbols are available (see below).
|
||||
|
||||
Out of the box, this library supports all platforms where `sqlite3` is installed:
|
||||
- iOS: Yes
|
||||
- macOS: Yes
|
||||
- Linux: Available on most distros
|
||||
- Windows: Additional setup is required
|
||||
- Android: Yes when used with Flutter, this library includes the necessary native libraries on Android
|
||||
|
||||
### On other platforms
|
||||
Using this library on platforms that are not supported out of the box is fairly
|
||||
straightforward. For instance, if you release your own `sqlite3.so` next to your application,
|
||||
you could use
|
||||
```dart
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:moor_ffi/open_helper.dart';
|
||||
|
||||
void main() {
|
||||
open.overrideFor(OperatingSystem.linux, _openOnLinux);
|
||||
|
||||
final db = Database.memory();
|
||||
db.close();
|
||||
}
|
||||
|
||||
DynamicLibrary _openOnLinux() {
|
||||
final script = File(Platform.script.toFilePath());
|
||||
final libraryNextToScript = File('${script.path}/sqlite3.so');
|
||||
return DynamicLibrary.open(libraryNextToScript.path);
|
||||
}
|
||||
```
|
||||
Just be sure to first override the behavior and then open the database. Further,
|
||||
if you want to use the isolate api, you can only use a static method or a top-level
|
||||
function to open the library. For Windows, a similar setup with a `sqlite3.dll` library
|
||||
should work.
|
||||
|
||||
### Supported datatypes
|
||||
This library supports `null`, `int`, `double`, `String` and `Uint8List` to bind args.
|
||||
Returned columns from select statements will have the same types.
|
||||
|
||||
## Using without moor
|
||||
```dart
|
||||
import 'package:moor_ffi/database.dart';
|
||||
|
||||
void main() {
|
||||
final database = Database.memory();
|
||||
// run some database operations. See the example for details
|
||||
database.close();
|
||||
}
|
||||
```
|
||||
|
||||
Be sure to __always__ call `Database.close` to avoid memory leaks!
|
||||
|
||||
## Using with moor
|
||||
If you're migrating an existing project using `moor_flutter`, see the
|
||||
[documentation](https://moor.simonbinder.eu/docs/other-engines/vm/#migrating-from-moor-flutter-to-moor-ffi).
|
||||
|
||||
Add both `moor` and `moor_ffi` to your pubspec:
|
||||
```yaml
|
||||
dependencies:
|
||||
moor: ^2.0.0
|
||||
moor_ffi: ^0.2.0
|
||||
dev_dependencies:
|
||||
moor_generator: ^2.0.0
|
||||
```
|
||||
|
||||
You can then use a `VmDatabase` as an executor:
|
||||
```dart
|
||||
@UseMoor(...)
|
||||
class MyDatabase extends _$MyDatabase {
|
||||
|
||||
MyDatabase(): super(VmDatabase(File('app.db')));
|
||||
}
|
||||
```
|
||||
If you need to find an appropriate directory for the database file, you can use the `LazyDatabase` wrapper
|
||||
from moor. It can be used to create the inner `VmDatabase` asynchronously:
|
||||
```dart
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
// use this instead of VmDatabase(...)
|
||||
LazyDatabase(() async {
|
||||
final dbFolder = await getApplicationDocumentsDirectory();
|
||||
final file = File(p.join(dbFolder.path, 'app.db'));
|
||||
return VmDatabase(file);
|
||||
});
|
||||
```
|
|
@ -1 +0,0 @@
|
|||
../analysis_options.yaml
|
|
@ -1,11 +0,0 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
|
||||
.externalNativeBuild/
|
||||
cpp/sqlite*
|
|
@ -1,42 +0,0 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
}
|
||||
}
|
||||
|
||||
group 'eu.simonbinder.moor_ffi'
|
||||
version '1.0'
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
|
||||
maven {
|
||||
url 'https://dl.bintray.com/sbinder/sqlite3-native-library/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'eu.simonbinder:sqlite3-native-library:3.32.3'
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
org.gradle.jvmargs=-Xmx1536M
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
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
|
|
@ -1 +0,0 @@
|
|||
rootProject.name = 'moor_ffi'
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="eu.simonbinder.moor_ffi"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
</manifest>
|
|
@ -1,17 +0,0 @@
|
|||
package eu.simonbinder.moor_ffi;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
|
||||
public class MoorFfiPlugin implements FlutterPlugin {
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(FlutterPluginBinding binding) {
|
||||
// Do nothing, we only need the native libraries.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(FlutterPluginBinding binding) {
|
||||
// Again, nothing to do here.
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import 'package:moor_ffi/database.dart';
|
||||
|
||||
const _createTable = r'''
|
||||
CREATE TABLE frameworks (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR NOT NULL
|
||||
);
|
||||
''';
|
||||
|
||||
void main() {
|
||||
final db = Database.memory();
|
||||
db.execute(_createTable);
|
||||
|
||||
final insertStmt = db.prepare('INSERT INTO frameworks(name) VALUES (?)');
|
||||
insertStmt.execute(['Flutter']);
|
||||
insertStmt.execute(['AngularDart']);
|
||||
insertStmt.close();
|
||||
|
||||
final selectStmt = db.prepare('SELECT * FROM frameworks ORDER BY name');
|
||||
final result = selectStmt.select();
|
||||
for (final row in result) {
|
||||
print('${row['id']}: ${row['name']}');
|
||||
}
|
||||
|
||||
selectStmt.close();
|
||||
db.close();
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
.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
|
|
@ -1,4 +0,0 @@
|
|||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface MoorFfiPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
|
@ -1,8 +0,0 @@
|
|||
#import "MoorFfiPlugin.h"
|
||||
#import <moor_ffi/moor_ffi-Swift.h>
|
||||
|
||||
@implementation MoorFfiPlugin
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
[SwiftMoorFfiPlugin registerWithRegistrar:registrar];
|
||||
}
|
||||
@end
|
|
@ -1,14 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#
|
||||
# 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'
|
||||
|
||||
# when we run on Dart 2.6, we should use this library and also use DynamicLibrary.executable()
|
||||
# s.dependency 'sqlite3'
|
||||
|
||||
s.ios.deployment_target = '8.0'
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
/// Exports the low-level [Database] class to run operations on a sqlite
|
||||
/// database via `dart:ffi`.
|
||||
@Deprecated('Consider migrating to package:sqlite3/sqlite3.dart')
|
||||
library database;
|
||||
|
||||
import 'package:moor_ffi/src/bindings/types.dart';
|
||||
|
||||
export 'src/api/result.dart';
|
||||
export 'src/bindings/types.dart' hide Database, Statement;
|
||||
export 'src/impl/database.dart'
|
||||
show SqliteException, Database, PreparedStatement;
|
|
@ -1,8 +0,0 @@
|
|||
@Deprecated('Use package:moor/ffi.dart instead')
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:moor/backends.dart';
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor_ffi/database.dart';
|
||||
|
||||
part 'src/vm_database.dart';
|
|
@ -1,8 +0,0 @@
|
|||
/// Utils to open a [DynamicLibrary] on platforms that aren't supported by
|
||||
/// `moor_ffi` by default.
|
||||
@Deprecated('Consider migrating to package:sqlite3/open.dart')
|
||||
library open_helper;
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
export 'src/load_library.dart';
|
|
@ -1,64 +0,0 @@
|
|||
import 'dart:collection';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
/// Stores the result of a select statement.
|
||||
class Result extends Iterable<Row> {
|
||||
final List<String> columnNames;
|
||||
// a result set can have multiple columns with the same name, but that's rare
|
||||
// and users usually use a name as index. So we cache that for O(1) lookups
|
||||
Map<String, int> _calculatedIndexes;
|
||||
final List<List<dynamic>> rows;
|
||||
|
||||
Result(this.columnNames, this.rows) {
|
||||
_calculatedIndexes = {
|
||||
for (var column in columnNames) column: columnNames.lastIndexOf(column),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Iterator<Row> get iterator => _ResultIterator(this);
|
||||
}
|
||||
|
||||
/// Stores a single row in the result of a select statement.
|
||||
class Row extends MapMixin<String, dynamic>
|
||||
with UnmodifiableMapMixin<String, dynamic> {
|
||||
final Result _result;
|
||||
final int _rowIndex;
|
||||
|
||||
Row._(this._result, this._rowIndex);
|
||||
|
||||
/// Returns the value stored in the [i]-th column in this row (zero-indexed).
|
||||
dynamic columnAt(int i) {
|
||||
return _result.rows[_rowIndex][i];
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic operator [](Object key) {
|
||||
if (key is! String) return null;
|
||||
|
||||
final index = _result._calculatedIndexes[key];
|
||||
if (index == null) return null;
|
||||
|
||||
return columnAt(index);
|
||||
}
|
||||
|
||||
@override
|
||||
Iterable<String> get keys => _result.columnNames;
|
||||
}
|
||||
|
||||
class _ResultIterator extends Iterator<Row> {
|
||||
final Result result;
|
||||
int index = -1;
|
||||
|
||||
_ResultIterator(this.result);
|
||||
|
||||
@override
|
||||
Row get current => Row._(result, index);
|
||||
|
||||
@override
|
||||
bool moveNext() {
|
||||
index++;
|
||||
return index < result.rows.length;
|
||||
}
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:moor_ffi/open_helper.dart';
|
||||
|
||||
import '../ffi/blob.dart';
|
||||
|
||||
import 'signatures.dart';
|
||||
import 'types.dart';
|
||||
|
||||
// ignore_for_file: comment_references, non_constant_identifier_names
|
||||
|
||||
class _SQLiteBindings {
|
||||
DynamicLibrary sqlite;
|
||||
|
||||
int Function(Pointer<CBlob> filename, Pointer<Pointer<Database>> databaseOut,
|
||||
int flags, Pointer<CBlob> vfs) sqlite3_open_v2;
|
||||
|
||||
int Function(Pointer<Database> database) sqlite3_close_v2;
|
||||
void Function(Pointer<Void> ptr) sqlite3_free;
|
||||
|
||||
int Function(
|
||||
Pointer<Database> database,
|
||||
Pointer<CBlob> query,
|
||||
int nbytes,
|
||||
Pointer<Pointer<Statement>> statementOut,
|
||||
Pointer<Pointer<CBlob>> tail) sqlite3_prepare_v2;
|
||||
|
||||
int Function(
|
||||
Pointer<Database> database,
|
||||
Pointer<CBlob> query,
|
||||
Pointer<Void> callback,
|
||||
Pointer<Void> cbFirstArg,
|
||||
Pointer<Pointer<CBlob>> errorMsgOut,
|
||||
) sqlite3_exec;
|
||||
|
||||
int Function(Pointer<Statement> statement) sqlite3_step;
|
||||
|
||||
int Function(Pointer<Statement> statement) sqlite3_reset;
|
||||
|
||||
int Function(Pointer<Statement> statement) sqlite3_finalize;
|
||||
|
||||
int Function(Pointer<Statement> statement) sqlite3_column_count;
|
||||
|
||||
Pointer<CBlob> Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_name;
|
||||
|
||||
int Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_type;
|
||||
|
||||
double Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_double;
|
||||
int Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_int64;
|
||||
Pointer<CBlob> Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_text;
|
||||
Pointer<CBlob> Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_blob;
|
||||
|
||||
/// Returns the amount of bytes to read when using [sqlite3_column_blob].
|
||||
int Function(Pointer<Statement> statement, int columnIndex)
|
||||
sqlite3_column_bytes;
|
||||
|
||||
int Function(Pointer<Database> db) sqlite3_changes;
|
||||
int Function(Pointer<Database> db) sqlite3_last_insert_rowid;
|
||||
|
||||
int Function(Pointer<Database> db) sqlite3_extended_errcode;
|
||||
Pointer<CBlob> Function(int code) sqlite3_errstr;
|
||||
Pointer<CBlob> Function(Pointer<Database> database) sqlite3_errmsg;
|
||||
int Function(Pointer<Database> database, int onOff)
|
||||
sqlite3_extended_result_codes;
|
||||
|
||||
int Function(Pointer<Statement> statement, int columnIndex, double value)
|
||||
sqlite3_bind_double;
|
||||
int Function(Pointer<Statement> statement, int columnIndex, int value)
|
||||
sqlite3_bind_int64;
|
||||
int Function(
|
||||
Pointer<Statement> statement,
|
||||
int columnIndex,
|
||||
Pointer<CBlob> value,
|
||||
int minusOne,
|
||||
Pointer<Void> disposeCb) sqlite3_bind_text;
|
||||
int Function(
|
||||
Pointer<Statement> statement,
|
||||
int columnIndex,
|
||||
Pointer<CBlob> value,
|
||||
int length,
|
||||
Pointer<Void> disposeCb) sqlite3_bind_blob;
|
||||
int Function(Pointer<Statement> statement, int columnIndex) sqlite3_bind_null;
|
||||
sqlite3_bind_parameter_count_dart sqlite3_bind_parameter_count;
|
||||
|
||||
int Function(
|
||||
Pointer<Database> db,
|
||||
Pointer<Uint8> zFunctionName,
|
||||
int argCount,
|
||||
int eTextRep,
|
||||
Pointer<Void> arg,
|
||||
Pointer<NativeFunction<sqlite3_function_handler>> handler,
|
||||
Pointer<NativeFunction<sqlite3_function_handler>> step,
|
||||
Pointer<NativeFunction<sqlite3_function_finalizer>> finalizer,
|
||||
Pointer<NativeFunction<sqlite3_finalizer>> destroyArg,
|
||||
) sqlite3_create_function_v2;
|
||||
|
||||
Pointer<CBlob> Function(Pointer<SqliteValue> value) sqlite3_value_blob;
|
||||
Pointer<CBlob> Function(Pointer<SqliteValue> value) sqlite3_value_text;
|
||||
double Function(Pointer<SqliteValue> value) sqlite3_value_double;
|
||||
int Function(Pointer<SqliteValue> value) sqlite3_value_int64;
|
||||
int Function(Pointer<SqliteValue> value) sqlite3_value_bytes;
|
||||
int Function(Pointer<SqliteValue> value) sqlite3_value_type;
|
||||
|
||||
void Function(Pointer<FunctionContext> ctx) sqlite3_result_null;
|
||||
void Function(Pointer<FunctionContext> ctx, int value) sqlite3_result_int64;
|
||||
void Function(Pointer<FunctionContext> ctx, double value)
|
||||
sqlite3_result_double;
|
||||
void Function(Pointer<FunctionContext> ctx, Pointer<CBlob> msg, int length)
|
||||
sqlite3_result_error;
|
||||
|
||||
_SQLiteBindings() {
|
||||
sqlite = open.openSqlite();
|
||||
|
||||
sqlite3_bind_double = sqlite
|
||||
.lookup<NativeFunction<sqlite3_bind_double_native>>(
|
||||
'sqlite3_bind_double')
|
||||
.asFunction();
|
||||
sqlite3_bind_int64 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_bind_int64_native>>('sqlite3_bind_int64')
|
||||
.asFunction();
|
||||
sqlite3_bind_text = sqlite
|
||||
.lookup<NativeFunction<sqlite3_bind_text_native>>('sqlite3_bind_text')
|
||||
.asFunction();
|
||||
sqlite3_bind_blob = sqlite
|
||||
.lookup<NativeFunction<sqlite3_bind_blob_native>>('sqlite3_bind_blob')
|
||||
.asFunction();
|
||||
sqlite3_bind_null = sqlite
|
||||
.lookup<NativeFunction<sqlite3_bind_null_native>>('sqlite3_bind_null')
|
||||
.asFunction();
|
||||
sqlite3_bind_parameter_count = sqlite.lookupFunction<
|
||||
sqlite3_bind_parameter_count_native,
|
||||
sqlite3_bind_parameter_count_dart>('sqlite3_bind_parameter_count');
|
||||
sqlite3_open_v2 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_open_v2_native_t>>('sqlite3_open_v2')
|
||||
.asFunction();
|
||||
sqlite3_close_v2 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_close_v2_native_t>>('sqlite3_close_v2')
|
||||
.asFunction();
|
||||
sqlite3_free = sqlite
|
||||
.lookup<NativeFunction<sqlite3_free_native>>('sqlite3_free')
|
||||
.asFunction();
|
||||
sqlite3_prepare_v2 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_prepare_v2_native_t>>(
|
||||
'sqlite3_prepare_v2')
|
||||
.asFunction();
|
||||
sqlite3_exec = sqlite
|
||||
.lookup<NativeFunction<sqlite3_exec_native>>('sqlite3_exec')
|
||||
.asFunction();
|
||||
sqlite3_step = sqlite
|
||||
.lookup<NativeFunction<sqlite3_step_native_t>>('sqlite3_step')
|
||||
.asFunction();
|
||||
sqlite3_reset = sqlite
|
||||
.lookup<NativeFunction<sqlite3_reset_native_t>>('sqlite3_reset')
|
||||
.asFunction();
|
||||
sqlite3_changes = sqlite
|
||||
.lookup<NativeFunction<sqlite3_changes_native>>('sqlite3_changes')
|
||||
.asFunction();
|
||||
sqlite3_last_insert_rowid = sqlite
|
||||
.lookup<NativeFunction<sqlite3_last_insert_rowid_native>>(
|
||||
'sqlite3_last_insert_rowid')
|
||||
.asFunction();
|
||||
sqlite3_finalize = sqlite
|
||||
.lookup<NativeFunction<sqlite3_finalize_native_t>>('sqlite3_finalize')
|
||||
.asFunction();
|
||||
sqlite3_extended_errcode = sqlite
|
||||
.lookup<NativeFunction<sqlite3_extended_errcode_native_t>>(
|
||||
'sqlite3_extended_errcode')
|
||||
.asFunction();
|
||||
sqlite3_errstr = sqlite
|
||||
.lookup<NativeFunction<sqlite3_errstr_native_t>>('sqlite3_errstr')
|
||||
.asFunction();
|
||||
sqlite3_errmsg = sqlite
|
||||
.lookup<NativeFunction<sqlite3_errmsg_native_t>>('sqlite3_errmsg')
|
||||
.asFunction();
|
||||
sqlite3_extended_result_codes = sqlite
|
||||
.lookup<NativeFunction<sqlite3_extended_result_codes_t>>(
|
||||
'sqlite3_extended_result_codes')
|
||||
.asFunction();
|
||||
sqlite3_column_count = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_count_native_t>>(
|
||||
'sqlite3_column_count')
|
||||
.asFunction();
|
||||
sqlite3_column_name = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_name_native_t>>(
|
||||
'sqlite3_column_name')
|
||||
.asFunction();
|
||||
sqlite3_column_type = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_type_native_t>>(
|
||||
'sqlite3_column_type')
|
||||
.asFunction();
|
||||
sqlite3_column_double = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_double_native_t>>(
|
||||
'sqlite3_column_double')
|
||||
.asFunction();
|
||||
sqlite3_column_int64 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_int64_native_t>>(
|
||||
'sqlite3_column_int64')
|
||||
.asFunction();
|
||||
sqlite3_column_text = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_text_native_t>>(
|
||||
'sqlite3_column_text')
|
||||
.asFunction();
|
||||
sqlite3_column_blob = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_blob_native_t>>(
|
||||
'sqlite3_column_blob')
|
||||
.asFunction();
|
||||
sqlite3_column_bytes = sqlite
|
||||
.lookup<NativeFunction<sqlite3_column_bytes_native_t>>(
|
||||
'sqlite3_column_bytes')
|
||||
.asFunction();
|
||||
sqlite3_create_function_v2 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_create_function_v2_native>>(
|
||||
'sqlite3_create_function_v2')
|
||||
.asFunction();
|
||||
sqlite3_value_blob = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_blob_native>>('sqlite3_value_blob')
|
||||
.asFunction();
|
||||
sqlite3_value_text = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_text_native>>('sqlite3_value_text')
|
||||
.asFunction();
|
||||
sqlite3_value_double = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_double_native>>(
|
||||
'sqlite3_value_double')
|
||||
.asFunction();
|
||||
sqlite3_value_int64 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_int64_native>>(
|
||||
'sqlite3_value_int64')
|
||||
.asFunction();
|
||||
sqlite3_value_bytes = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_bytes_native>>(
|
||||
'sqlite3_value_bytes')
|
||||
.asFunction();
|
||||
sqlite3_value_type = sqlite
|
||||
.lookup<NativeFunction<sqlite3_value_type_native>>('sqlite3_value_type')
|
||||
.asFunction();
|
||||
sqlite3_result_null = sqlite
|
||||
.lookup<NativeFunction<sqlite3_result_null_native>>(
|
||||
'sqlite3_result_null')
|
||||
.asFunction();
|
||||
sqlite3_result_int64 = sqlite
|
||||
.lookup<NativeFunction<sqlite3_result_int64_native>>(
|
||||
'sqlite3_result_int64')
|
||||
.asFunction();
|
||||
sqlite3_result_double = sqlite
|
||||
.lookup<NativeFunction<sqlite3_result_double_native>>(
|
||||
'sqlite3_result_double')
|
||||
.asFunction();
|
||||
sqlite3_result_error = sqlite
|
||||
.lookup<NativeFunction<sqlite3_result_error_native>>(
|
||||
'sqlite3_result_error')
|
||||
.asFunction();
|
||||
}
|
||||
}
|
||||
|
||||
_SQLiteBindings _cachedBindings;
|
||||
_SQLiteBindings get bindings => _cachedBindings ??= _SQLiteBindings();
|
|
@ -1,193 +0,0 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
/// Result Codes
|
||||
///
|
||||
/// Many SQLite functions return an integer result code from the set shown
|
||||
/// here in order to indicates success or failure.
|
||||
///
|
||||
/// New error codes may be added in future versions of SQLite.
|
||||
///
|
||||
/// See also: SQLITE_IOERR_READ | extended result codes,
|
||||
/// sqlite3_vtab_on_conflict() SQLITE_ROLLBACK | result codes.
|
||||
class Errors {
|
||||
/// Successful result
|
||||
static const int SQLITE_OK = 0;
|
||||
|
||||
/// Generic error
|
||||
static const int SQLITE_ERROR = 1;
|
||||
|
||||
/// Internal logic error in SQLite
|
||||
static const int SQLITE_INTERNAL = 2;
|
||||
|
||||
/// Access permission denied
|
||||
static const int SQLITE_PERM = 3;
|
||||
|
||||
/// Callback routine requested an abort
|
||||
static const int SQLITE_ABORT = 4;
|
||||
|
||||
/// The database file is locked
|
||||
static const int SQLITE_BUSY = 5;
|
||||
|
||||
/// A table in the database is locked
|
||||
static const int SQLITE_LOCKED = 6;
|
||||
|
||||
/// A malloc() failed
|
||||
static const int SQLITE_NOMEM = 7;
|
||||
|
||||
/// Attempt to write a readonly database
|
||||
static const int SQLITE_READONLY = 8;
|
||||
|
||||
/// Operation terminated by sqlite3_interrupt()
|
||||
static const int SQLITE_INTERRUPT = 9;
|
||||
|
||||
/// Some kind of disk I/O error occurred
|
||||
static const int SQLITE_IOERR = 10;
|
||||
|
||||
/// The database disk image is malformed
|
||||
static const int SQLITE_CORRUPT = 11;
|
||||
|
||||
/// Unknown opcode in sqlite3_file_control()
|
||||
static const int SQLITE_NOTFOUND = 12;
|
||||
|
||||
/// Insertion failed because database is full
|
||||
static const int SQLITE_FULL = 13;
|
||||
|
||||
/// Unable to open the database file
|
||||
static const int SQLITE_CANTOPEN = 14;
|
||||
|
||||
/// Database lock protocol error
|
||||
static const int SQLITE_PROTOCOL = 15;
|
||||
|
||||
/// Internal use only
|
||||
static const int SQLITE_EMPTY = 16;
|
||||
|
||||
/// The database schema changed
|
||||
static const int SQLITE_SCHEMA = 17;
|
||||
|
||||
/// String or BLOB exceeds size limit
|
||||
static const int SQLITE_TOOBIG = 18;
|
||||
|
||||
/// Abort due to constraint violation
|
||||
static const int SQLITE_CONSTRAINT = 19;
|
||||
|
||||
/// Data type mismatch
|
||||
static const int SQLITE_MISMATCH = 20;
|
||||
|
||||
/// Library used incorrectly
|
||||
static const int SQLITE_MISUSE = 21;
|
||||
|
||||
/// Uses OS features not supported on host
|
||||
static const int SQLITE_NOLFS = 22;
|
||||
|
||||
/// Authorization denied
|
||||
static const int SQLITE_AUTH = 23;
|
||||
|
||||
/// Not used
|
||||
static const int SQLITE_FORMAT = 24;
|
||||
|
||||
/// 2nd parameter to sqlite3_bind out of range
|
||||
static const int SQLITE_RANGE = 25;
|
||||
|
||||
/// File opened that is not a database file
|
||||
static const int SQLITE_NOTADB = 26;
|
||||
|
||||
/// Notifications from sqlite3_log()
|
||||
static const int SQLITE_NOTICE = 27;
|
||||
|
||||
/// Warnings from sqlite3_log()
|
||||
static const int SQLITE_WARNING = 28;
|
||||
|
||||
/// sqlite3_step() has another row ready
|
||||
static const int SQLITE_ROW = 100;
|
||||
|
||||
/// sqlite3_step() has finished executing
|
||||
static const int SQLITE_DONE = 101;
|
||||
}
|
||||
|
||||
/// Flags For File Open Operations
|
||||
///
|
||||
/// These bit values are intended for use in the
|
||||
/// 3rd parameter to the [sqlite3_open_v2()] interface and
|
||||
/// in the 4th parameter to the `sqlite3_vfs.xOpen` method.
|
||||
class Flags {
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_READONLY = 0x00000001;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_READWRITE = 0x00000002;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_CREATE = 0x00000004;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_DELETEONCLOSE = 0x00000008;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_EXCLUSIVE = 0x00000010;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_AUTOPROXY = 0x00000020;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_URI = 0x00000040;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_MEMORY = 0x00000080;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_MAIN_DB = 0x00000100;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_TEMP_DB = 0x00000200;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_TRANSIENT_DB = 0x00000400;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_MAIN_JOURNAL = 0x00000800;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_TEMP_JOURNAL = 0x00001000;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_SUBJOURNAL = 0x00002000;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_MASTER_JOURNAL = 0x00004000;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_NOMUTEX = 0x00008000;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_FULLMUTEX = 0x00010000;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_SHAREDCACHE = 0x00020000;
|
||||
|
||||
/// Ok for sqlite3_open_v2()
|
||||
static const int SQLITE_OPEN_PRIVATECACHE = 0x00040000;
|
||||
|
||||
/// VFS only
|
||||
static const int SQLITE_OPEN_WAL = 0x00080000;
|
||||
}
|
||||
|
||||
class TextEncodings {
|
||||
static const int SQLITE_UTF8 = 1;
|
||||
}
|
||||
|
||||
class FunctionFlags {
|
||||
static const int SQLITE_DETERMINISTIC = 0x000000800;
|
||||
static const int SQLITE_DIRECTONLY = 0x000080000;
|
||||
}
|
||||
|
||||
class Types {
|
||||
static const int SQLITE_INTEGER = 1;
|
||||
static const int SQLITE_FLOAT = 2;
|
||||
static const int SQLITE_TEXT = 3;
|
||||
static const int SQLITE_BLOB = 4;
|
||||
static const int SQLITE_NULL = 5;
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import '../ffi/blob.dart';
|
||||
|
||||
import 'types.dart';
|
||||
|
||||
typedef sqlite3_open_v2_native_t = Int32 Function(Pointer<CBlob> filename,
|
||||
Pointer<Pointer<Database>> ppDb, Int32 flags, Pointer<CBlob> vfs);
|
||||
|
||||
typedef sqlite3_close_v2_native_t = Int32 Function(Pointer<Database> database);
|
||||
|
||||
typedef sqlite3_free_native = Void Function(Pointer<Void> pointer);
|
||||
|
||||
typedef sqlite3_prepare_v2_native_t = Int32 Function(
|
||||
Pointer<Database> database,
|
||||
Pointer<CBlob> query,
|
||||
Int32 nbytes,
|
||||
Pointer<Pointer<Statement>> statementOut,
|
||||
Pointer<Pointer<CBlob>> tail);
|
||||
|
||||
typedef sqlite3_exec_native = Int32 Function(
|
||||
Pointer<Database> database,
|
||||
Pointer<CBlob> query,
|
||||
Pointer<Void> callback,
|
||||
Pointer<Void> firstCbArg,
|
||||
Pointer<Pointer<CBlob>> errorOut);
|
||||
|
||||
typedef sqlite3_step_native_t = Int32 Function(Pointer<Statement> statement);
|
||||
|
||||
typedef sqlite3_reset_native_t = Int32 Function(Pointer<Statement> statement);
|
||||
|
||||
typedef sqlite3_finalize_native_t = Int32 Function(
|
||||
Pointer<Statement> statement);
|
||||
|
||||
typedef sqlite3_extended_errcode_native_t = Int32 Function(
|
||||
Pointer<Database> database);
|
||||
|
||||
typedef sqlite3_errstr_native_t = Pointer<CBlob> Function(Int32 error);
|
||||
|
||||
typedef sqlite3_errmsg_native_t = Pointer<CBlob> Function(
|
||||
Pointer<Database> database);
|
||||
|
||||
typedef sqlite3_extended_result_codes_t = Int32 Function(
|
||||
Pointer<Database> database, Int32 onOff);
|
||||
|
||||
typedef sqlite3_column_count_native_t = Int32 Function(
|
||||
Pointer<Statement> statement);
|
||||
|
||||
typedef sqlite3_column_name_native_t = Pointer<CBlob> Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_type_native_t = Int32 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_double_native_t = Double Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_int64_native_t = Int64 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_text_native_t = Pointer<CBlob> Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_blob_native_t = Pointer<CBlob> Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_column_bytes_native_t = Int32 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
|
||||
typedef sqlite3_changes_native = Int32 Function(Pointer<Database> database);
|
||||
typedef sqlite3_last_insert_rowid_native = Int64 Function(
|
||||
Pointer<Database> database);
|
||||
|
||||
typedef sqlite3_bind_double_native = Int32 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex, Double value);
|
||||
typedef sqlite3_bind_int64_native = Int32 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex, Int64 value);
|
||||
typedef sqlite3_bind_text_native = Int32 Function(
|
||||
Pointer<Statement> statement,
|
||||
Int32 columnIndex,
|
||||
Pointer<CBlob> value,
|
||||
Int32 length,
|
||||
Pointer<Void> callback);
|
||||
typedef sqlite3_bind_blob_native = Int32 Function(
|
||||
Pointer<Statement> statement,
|
||||
Int32 columnIndex,
|
||||
Pointer<CBlob> value,
|
||||
Int32 length,
|
||||
Pointer<Void> callback);
|
||||
typedef sqlite3_bind_null_native = Int32 Function(
|
||||
Pointer<Statement> statement, Int32 columnIndex);
|
||||
typedef sqlite3_bind_parameter_count_dart = int Function(
|
||||
Pointer<Statement> stmt);
|
||||
typedef sqlite3_bind_parameter_count_native = Int32 Function(
|
||||
Pointer<Statement> statement);
|
||||
|
||||
typedef sqlite3_function_handler = Void Function(
|
||||
Pointer<FunctionContext> context,
|
||||
Int32 argCount,
|
||||
Pointer<Pointer<SqliteValue>> args);
|
||||
|
||||
typedef sqlite3_function_finalizer = Void Function(
|
||||
Pointer<FunctionContext> context);
|
||||
|
||||
typedef sqlite3_finalizer = Void Function(Pointer<Void> ptr);
|
||||
|
||||
typedef sqlite3_create_function_v2_native = Int32 Function(
|
||||
Pointer<Database> db,
|
||||
Pointer<Uint8> zFunctionName,
|
||||
Int32 nArg,
|
||||
Int32 eTextRep,
|
||||
Pointer<Void> pApp,
|
||||
Pointer<NativeFunction<sqlite3_function_handler>> xFunc,
|
||||
Pointer<NativeFunction<sqlite3_function_handler>> xStep,
|
||||
Pointer<NativeFunction<sqlite3_function_finalizer>> xDestroy,
|
||||
Pointer<NativeFunction<sqlite3_finalizer>> finalizePApp,
|
||||
);
|
||||
|
||||
typedef sqlite3_value_blob_native = Pointer<CBlob> Function(
|
||||
Pointer<SqliteValue> value);
|
||||
typedef sqlite3_value_double_native = Double Function(
|
||||
Pointer<SqliteValue> value);
|
||||
typedef sqlite3_value_int64_native = Int64 Function(Pointer<SqliteValue> value);
|
||||
typedef sqlite3_value_text_native = Pointer<CBlob> Function(
|
||||
Pointer<SqliteValue> value);
|
||||
typedef sqlite3_value_bytes_native = Int32 Function(Pointer<SqliteValue> value);
|
||||
typedef sqlite3_value_type_native = Int32 Function(Pointer<SqliteValue> value);
|
||||
|
||||
typedef sqlite3_result_null_native = Void Function(
|
||||
Pointer<FunctionContext> context);
|
||||
typedef sqlite3_result_double_native = Void Function(
|
||||
Pointer<FunctionContext> context, Double value);
|
||||
typedef sqlite3_result_int64_native = Void Function(
|
||||
Pointer<FunctionContext> context, Int64 value);
|
||||
typedef sqlite3_result_error_native = Void Function(
|
||||
Pointer<FunctionContext> context, Pointer<CBlob> char, Int32 len);
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
|
||||
import '../ffi/blob.dart';
|
||||
import '../ffi/utils.dart';
|
||||
import 'bindings.dart';
|
||||
import 'constants.dart';
|
||||
|
||||
// ignore_for_file: comment_references
|
||||
|
||||
/// Database Connection Handle
|
||||
///
|
||||
/// Each open SQLite database is represented by a pointer to an instance of
|
||||
/// the opaque structure named "sqlite3". It is useful to think of an sqlite3
|
||||
/// pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
|
||||
/// [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
|
||||
/// is its destructor. There are many other interfaces (such as
|
||||
/// [sqlite3_prepare_v2()], [sqlite3_create_function()], and
|
||||
/// [sqlite3_busy_timeout()] to name but three) that are methods on an
|
||||
class Database extends Struct {}
|
||||
|
||||
/// SQL Statement Object
|
||||
///
|
||||
/// An instance of this object represents a single SQL statement.
|
||||
/// This object is variously known as a "prepared statement" or a
|
||||
/// "compiled SQL statement" or simply as a "statement".
|
||||
///
|
||||
/// The life of a statement object goes something like this:
|
||||
///
|
||||
/// <ol>
|
||||
/// <li> Create the object using [sqlite3_prepare_v2()] or a related
|
||||
/// function.
|
||||
/// <li> Bind values to [host parameters] using the sqlite3_bind_*()
|
||||
/// interfaces.
|
||||
/// <li> Run the SQL by calling [sqlite3_step()] one or more times.
|
||||
/// <li> Reset the statement using [sqlite3_reset()] then go back
|
||||
/// to step 2. Do this zero or more times.
|
||||
/// <li> Destroy the object using [sqlite3_finalize()].
|
||||
/// </ol>
|
||||
///
|
||||
/// Refer to documentation on individual methods above for additional
|
||||
/// information.
|
||||
class Statement extends Struct {}
|
||||
|
||||
/// The context in which an SQL function executes is stored in this object.
|
||||
/// A pointer to this object is always the first paramater to
|
||||
/// application-defined SQL functions.
|
||||
///
|
||||
/// See also:
|
||||
/// - https://www.sqlite.org/c3ref/context.html
|
||||
class FunctionContext extends Struct {}
|
||||
|
||||
/// A value object in sqlite, which can represent all values that can be stored
|
||||
/// in a database table.
|
||||
class SqliteValue extends Struct {}
|
||||
|
||||
/// Extension to extract value from a [SqliteValue].
|
||||
extension SqliteValuePointer on Pointer<SqliteValue> {
|
||||
/// Extracts the raw value from the object.
|
||||
///
|
||||
/// Depending on the type of this value as set in sqlite, [value] returns
|
||||
/// - a [String]
|
||||
/// - a [Uint8List]
|
||||
/// - a [int]
|
||||
/// - a [double]
|
||||
/// - `null`
|
||||
///
|
||||
/// For texts and bytes, the value will be copied.
|
||||
dynamic get value {
|
||||
final api = bindings;
|
||||
|
||||
final type = api.sqlite3_value_type(this);
|
||||
switch (type) {
|
||||
case Types.SQLITE_INTEGER:
|
||||
return api.sqlite3_value_int64(this);
|
||||
case Types.SQLITE_FLOAT:
|
||||
return api.sqlite3_value_double(this);
|
||||
case Types.SQLITE_TEXT:
|
||||
final length = api.sqlite3_value_bytes(this);
|
||||
return api.sqlite3_value_text(this).readAsStringWithLength(length);
|
||||
case Types.SQLITE_BLOB:
|
||||
final length = api.sqlite3_value_bytes(this);
|
||||
if (length == 0) {
|
||||
// sqlite3_value_bytes returns a null pointer for non-null blobs with
|
||||
// a length of 0. Note that we can distinguish this from a proper null
|
||||
// by checking the type (which isn't SQLITE_NULL)
|
||||
return Uint8List(0);
|
||||
}
|
||||
return api.sqlite3_value_blob(this).readBytes(length);
|
||||
case Types.SQLITE_NULL:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SqliteFunctionContextPointer on Pointer<FunctionContext> {
|
||||
void resultNull() {
|
||||
bindings.sqlite3_result_null(this);
|
||||
}
|
||||
|
||||
void resultInt(int value) {
|
||||
bindings.sqlite3_result_int64(this, value);
|
||||
}
|
||||
|
||||
void resultDouble(double value) {
|
||||
bindings.sqlite3_result_double(this, value);
|
||||
}
|
||||
|
||||
void resultNum(num value) {
|
||||
if (value is int) {
|
||||
resultInt(value);
|
||||
return;
|
||||
} else if (value is double) {
|
||||
resultDouble(value);
|
||||
return;
|
||||
}
|
||||
|
||||
throw AssertionError();
|
||||
}
|
||||
|
||||
void resultBool(bool value) {
|
||||
resultInt(value ? 1 : 0);
|
||||
}
|
||||
|
||||
void resultError(String message) {
|
||||
final encoded = Uint8List.fromList(utf8.encode(message));
|
||||
final ptr = CBlob.allocate(encoded);
|
||||
|
||||
bindings.sqlite3_result_error(this, ptr, encoded.length);
|
||||
|
||||
// Note that sqlite3_result_error makes a private copy of error message
|
||||
// before returning. Hence, we can deallocate the message here.
|
||||
ptr.free();
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:moor_ffi/src/ffi/utils.dart';
|
||||
import 'package:ffi/ffi.dart' as ffi;
|
||||
|
||||
/// Pointer to arbitrary blobs in C.
|
||||
class CBlob extends Struct {
|
||||
static Pointer<CBlob> allocate(Uint8List blob, {int paddingAtEnd = 0}) {
|
||||
final str = ffi.allocate<Uint8>(count: blob.length + paddingAtEnd);
|
||||
|
||||
final asList = str.asTypedList(blob.length + paddingAtEnd);
|
||||
asList.setAll(0, blob);
|
||||
|
||||
if (paddingAtEnd != 0) {
|
||||
asList.fillRange(blob.length, blob.length + paddingAtEnd, 0);
|
||||
}
|
||||
|
||||
return str.cast();
|
||||
}
|
||||
|
||||
/// Allocates a 0-terminated string, encoded as utf8 and read from the
|
||||
/// [string].
|
||||
static Pointer<CBlob> allocateString(String string) {
|
||||
final encoded = utf8.encode(string);
|
||||
final data = Uint8List(encoded.length + 1) // already filled with zeroes
|
||||
..setAll(0, encoded);
|
||||
return CBlob.allocate(data);
|
||||
}
|
||||
}
|
||||
|
||||
extension CBlobPointer on Pointer<CBlob> {
|
||||
/// Reads a 0-terminated string, decoded with utf8.
|
||||
///
|
||||
/// Warning: This method is very, very slow. If there is any way to know the
|
||||
/// length of the string to read, [readAsStringWithLength] will be orders of
|
||||
/// magnitude faster.
|
||||
String readString() {
|
||||
if (isNullPointer) return null;
|
||||
|
||||
var len = 0;
|
||||
final asUintPointer = cast<Uint8>();
|
||||
while (asUintPointer[++len] != 0) {}
|
||||
|
||||
final units = readBytes(len);
|
||||
return utf8.decode(units);
|
||||
}
|
||||
|
||||
/// More efficient version of [readString] that doesn't have to find a nil-
|
||||
/// terminator. [length] is the amount of bytes to read. The string will be
|
||||
/// decoded via [utf8].
|
||||
String readAsStringWithLength(int length) {
|
||||
return utf8.decode(readBytes(length));
|
||||
}
|
||||
|
||||
/// Reads [length] bytes from this address.
|
||||
Uint8List readBytes(int length) {
|
||||
assert(length >= 0);
|
||||
if (isNullPointer) return null;
|
||||
|
||||
final data = cast<Uint8>().asTypedList(length);
|
||||
return Uint8List.fromList(data);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart' as ffi;
|
||||
|
||||
extension FreePointerExtension on Pointer {
|
||||
bool get isNullPointer => this == nullptr;
|
||||
|
||||
void free() {
|
||||
ffi.free(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a null-pointer with a specified type.
|
||||
///
|
||||
/// The [nullptr] getter from `dart:ffi` can be slow due to being a
|
||||
/// `Pointer<Null>` on which the VM has to perform runtime type checks. See also
|
||||
/// https://github.com/dart-lang/sdk/issues/39488
|
||||
@pragma('vm:prefer-inline')
|
||||
Pointer<T> nullPtr<T extends NativeType>() => nullptr.cast<T>();
|
|
@ -1,270 +0,0 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:moor_ffi/src/api/result.dart';
|
||||
import 'package:moor_ffi/src/bindings/constants.dart';
|
||||
import 'package:moor_ffi/src/bindings/signatures.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 'moor_functions.dart';
|
||||
part 'prepared_statement.dart';
|
||||
|
||||
const _openingFlags = Flags.SQLITE_OPEN_READWRITE | Flags.SQLITE_OPEN_CREATE;
|
||||
const _readOnlyOpeningFlags = Flags.SQLITE_OPEN_READONLY;
|
||||
|
||||
/// A opened sqlite database.
|
||||
class Database {
|
||||
final Pointer<types.Database> _db;
|
||||
final List<PreparedStatement> _preparedStmt = [];
|
||||
final List<Pointer<Void>> _furtherAllocations = [];
|
||||
|
||||
bool _isClosed = false;
|
||||
|
||||
Database._(this._db);
|
||||
|
||||
/// Opens the [file] as a sqlite3 database. The file will be created if it
|
||||
/// doesn't exist.
|
||||
factory Database.openFile(File file) => Database.open(file.absolute.path);
|
||||
|
||||
/// Opens an in-memory sqlite3 database.
|
||||
factory Database.memory() => Database.open(':memory:');
|
||||
|
||||
/// Opens an sqlite3 database from a filename.
|
||||
///
|
||||
/// Unless [readOnly] is set to true, database is opened in read/write mode.
|
||||
factory Database.open(String fileName, {bool readOnly = false}) {
|
||||
final dbOut = allocate<Pointer<types.Database>>();
|
||||
final pathC = CBlob.allocateString(fileName);
|
||||
final openingFlags =
|
||||
(readOnly ?? false) ? _readOnlyOpeningFlags : _openingFlags;
|
||||
|
||||
final resultCode =
|
||||
bindings.sqlite3_open_v2(pathC, dbOut, openingFlags, nullPtr());
|
||||
final dbPointer = dbOut.value;
|
||||
|
||||
dbOut.free();
|
||||
pathC.free();
|
||||
|
||||
if (resultCode == Errors.SQLITE_OK) {
|
||||
// Turn extended result code to on.
|
||||
bindings.sqlite3_extended_result_codes(dbPointer, 1);
|
||||
|
||||
return Database._(dbPointer);
|
||||
} else {
|
||||
bindings.sqlite3_close_v2(dbPointer);
|
||||
throw SqliteException._fromErrorCode(dbPointer, resultCode);
|
||||
}
|
||||
}
|
||||
|
||||
void _ensureOpen() {
|
||||
if (_isClosed) {
|
||||
throw Exception('This database has already been closed');
|
||||
}
|
||||
}
|
||||
|
||||
/// Closes this database connection and releases the resources it uses. If
|
||||
/// an error occurs while closing the database, an exception will be thrown.
|
||||
/// The allocated memory will be freed either way.
|
||||
void close() {
|
||||
if (_isClosed) return;
|
||||
|
||||
// close all prepared statements first
|
||||
_isClosed = true;
|
||||
for (final stmt in _preparedStmt) {
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
final code = bindings.sqlite3_close_v2(_db);
|
||||
SqliteException exception;
|
||||
if (code != Errors.SQLITE_OK) {
|
||||
exception = SqliteException._fromErrorCode(_db, code);
|
||||
}
|
||||
_isClosed = true;
|
||||
|
||||
for (final additional in _furtherAllocations) {
|
||||
additional.free();
|
||||
}
|
||||
|
||||
// we don't need to deallocate the _db pointer, sqlite takes care of that
|
||||
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleStmtFinalized(PreparedStatement stmt) {
|
||||
if (!_isClosed) {
|
||||
_preparedStmt.remove(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes the [sql] statement and ignores the result. Will throw if an
|
||||
/// error occurs while executing.
|
||||
void execute(String sql) {
|
||||
_ensureOpen();
|
||||
|
||||
final sqlPtr = CBlob.allocateString(sql);
|
||||
final errorOut = allocate<Pointer<CBlob>>();
|
||||
|
||||
final result =
|
||||
bindings.sqlite3_exec(_db, sqlPtr, nullPtr(), nullPtr(), errorOut);
|
||||
sqlPtr.free();
|
||||
|
||||
final errorPtr = errorOut.value;
|
||||
errorOut.free();
|
||||
|
||||
String errorMsg;
|
||||
if (!errorPtr.isNullPointer) {
|
||||
errorMsg = errorPtr.readString();
|
||||
// the message was allocated from sqlite, we need to free it
|
||||
bindings.sqlite3_free(errorPtr.cast());
|
||||
}
|
||||
|
||||
if (result != Errors.SQLITE_OK) {
|
||||
throw SqliteException(result, errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepares the [sql] statement.
|
||||
PreparedStatement prepare(String sql) {
|
||||
_ensureOpen();
|
||||
|
||||
final stmtOut = allocate<Pointer<types.Statement>>();
|
||||
final sqlPtr = CBlob.allocateString(sql);
|
||||
|
||||
final resultCode =
|
||||
bindings.sqlite3_prepare_v2(_db, sqlPtr, -1, stmtOut, nullPtr());
|
||||
sqlPtr.free();
|
||||
|
||||
final stmt = stmtOut.value;
|
||||
stmtOut.free();
|
||||
|
||||
if (resultCode != Errors.SQLITE_OK) {
|
||||
// we don't need to worry about freeing the statement. If preparing the
|
||||
// statement was unsuccessful, stmtOut.load() will be null
|
||||
throw SqliteException._fromErrorCode(_db, resultCode);
|
||||
}
|
||||
|
||||
final prepared = PreparedStatement._(stmt, this);
|
||||
_preparedStmt.add(prepared);
|
||||
|
||||
return prepared;
|
||||
}
|
||||
|
||||
/// Registers a custom sqlite function by its [name].
|
||||
///
|
||||
/// The function must take [argumentCount] arguments, and it may not take more
|
||||
/// than 127 arguments. If it can take a variable amount of arguments,
|
||||
/// [argumentCount] should be set to `-1`.
|
||||
///
|
||||
/// When the output of the function depends solely on its input,
|
||||
/// [isDeterministic] should be set. This allows sqlite's query planer to make
|
||||
/// further optimizations.
|
||||
/// When [directOnly] is set (defaults to true), the function can't be used
|
||||
/// outside a query (e.g. in triggers, views, check constraints, index
|
||||
/// expressions, etc.). Unless necessary, this should be enabled for security
|
||||
/// purposes. See the discussion at the link for more details
|
||||
/// The length of the utf8 encoding of [name] must not exceed 255 bytes.
|
||||
///
|
||||
/// See also:
|
||||
/// - https://sqlite.org/c3ref/create_function.html
|
||||
@visibleForTesting
|
||||
void createFunction(
|
||||
String name,
|
||||
int argumentCount,
|
||||
Pointer<NativeFunction<sqlite3_function_handler>> implementation, {
|
||||
bool isDeterministic = false,
|
||||
bool directOnly = true,
|
||||
}) {
|
||||
_ensureOpen();
|
||||
final encodedName = Uint8List.fromList(utf8.encode(name));
|
||||
// length of encoded name is limited to 255 bytes in utf8, excluding the 0
|
||||
// terminator
|
||||
if (encodedName.length > 255) {
|
||||
throw ArgumentError.value(
|
||||
name, 'name', 'Must be at most 255 bytes when encoded as utf8');
|
||||
}
|
||||
|
||||
// argument length should be between -1 and 127
|
||||
if (argumentCount < -1 || argumentCount > 127) {
|
||||
throw ArgumentError.value(
|
||||
argumentCount, 'argumentCount', 'Should be between -1 and 127');
|
||||
}
|
||||
|
||||
final namePtr = CBlob.allocate(encodedName, paddingAtEnd: 1);
|
||||
_furtherAllocations.add(namePtr.cast());
|
||||
|
||||
var textFlag = TextEncodings.SQLITE_UTF8;
|
||||
|
||||
if (isDeterministic) textFlag |= FunctionFlags.SQLITE_DETERMINISTIC;
|
||||
if (directOnly) textFlag |= FunctionFlags.SQLITE_DIRECTONLY;
|
||||
|
||||
final result = bindings.sqlite3_create_function_v2(
|
||||
_db,
|
||||
namePtr.cast(),
|
||||
argumentCount,
|
||||
textFlag,
|
||||
nullPtr(), // *pApp, we don't use that
|
||||
implementation,
|
||||
nullPtr(), // *xStep, null for regular functions
|
||||
nullPtr(), // *xFinal, null for regular functions
|
||||
nullPtr(), // finalizer for *pApp,
|
||||
);
|
||||
|
||||
if (result != Errors.SQLITE_OK) {
|
||||
throw SqliteException._fromErrorCode(_db, result);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables non-standard functions that ship with `moor_ffi`.
|
||||
///
|
||||
/// After calling [enableMoorFfiFunctions], the following functions can
|
||||
/// be used in sql: `power`, `pow`, `sqrt`, `sin`, `cos`, `tan`, `asin`,
|
||||
/// `acos`, `atan` and `regexp`.
|
||||
///
|
||||
/// At the moment, these functions are only available in statements. In
|
||||
/// particular, they're not available in triggers, check constraints, index
|
||||
/// expressions.
|
||||
///
|
||||
/// This should only be called once per database.
|
||||
void enableMoorFfiFunctions() {
|
||||
_registerOn(this);
|
||||
}
|
||||
|
||||
/// Get the application defined version of this database.
|
||||
int userVersion() {
|
||||
final stmt = prepare('PRAGMA user_version');
|
||||
final result = stmt.select();
|
||||
stmt.close();
|
||||
|
||||
return result.first.columnAt(0) as int;
|
||||
}
|
||||
|
||||
/// Update the application defined version of this database.
|
||||
void setUserVersion(int version) {
|
||||
execute('PRAGMA user_version = $version');
|
||||
}
|
||||
|
||||
/// Returns the amount of rows affected by the last INSERT, UPDATE or DELETE
|
||||
/// statement.
|
||||
int getUpdatedRows() {
|
||||
_ensureOpen();
|
||||
return bindings.sqlite3_changes(_db);
|
||||
}
|
||||
|
||||
/// Returns the row-id of the last inserted row.
|
||||
int getLastInsertId() {
|
||||
_ensureOpen();
|
||||
return bindings.sqlite3_last_insert_rowid(_db);
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
part of 'database.dart';
|
||||
|
||||
class SqliteException implements Exception {
|
||||
final String message;
|
||||
final String explanation;
|
||||
|
||||
/// SQLite extended result code.
|
||||
///
|
||||
/// As defined in https://sqlite.org/rescode.html, it represent an error code,
|
||||
/// providing some idea of the cause of the failure.
|
||||
final int extendedResultCode;
|
||||
|
||||
/// SQLite primary result code.
|
||||
///
|
||||
/// As defined in https://sqlite.org/rescode.html, it represent an error code,
|
||||
/// providing some idea of the cause of the failure.
|
||||
int get resultCode => extendedResultCode & 0xFF;
|
||||
|
||||
SqliteException(this.extendedResultCode, this.message, [this.explanation]);
|
||||
|
||||
factory SqliteException._fromErrorCode(Pointer<types.Database> db, int code) {
|
||||
// We don't need to free the pointer returned by sqlite3_errmsg: "Memory to
|
||||
// hold the error message string is managed internally. The application does
|
||||
// not need to worry about freeing the result."
|
||||
// https://www.sqlite.org/c3ref/errcode.html
|
||||
final dbMessage = bindings.sqlite3_errmsg(db).readString();
|
||||
|
||||
String explanation;
|
||||
if (code != null) {
|
||||
// Getting hold of more explanatory error code as SQLITE_IOERR error group
|
||||
// has an extensive list of extended error codes
|
||||
final extendedCode = bindings.sqlite3_extended_errcode(db);
|
||||
final errStr = bindings.sqlite3_errstr(extendedCode).readString();
|
||||
|
||||
explanation = '$errStr (code $extendedCode)';
|
||||
}
|
||||
|
||||
return SqliteException(code, dbMessage, explanation);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (explanation == null) {
|
||||
return 'SqliteException($extendedResultCode): $message';
|
||||
} else {
|
||||
return 'SqliteException($extendedResultCode): $message, $explanation';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
part of 'database.dart';
|
||||
|
||||
void _powImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
// sqlite will ensure that this is only called with 2 arguments
|
||||
final first = args[0].value;
|
||||
final second = args[1].value;
|
||||
|
||||
if (first == null || second == null || first is! num || second is! num) {
|
||||
ctx.resultNull();
|
||||
return;
|
||||
}
|
||||
|
||||
final result = pow(first as num, second as num);
|
||||
ctx.resultNum(result);
|
||||
}
|
||||
|
||||
/// Base implementation for a sqlite function that takes one numerical argument
|
||||
/// and returns one numerical argument.
|
||||
///
|
||||
/// If [argCount] is not `1` or the single argument is not of a numerical type,
|
||||
/// [ctx] will complete to null. Otherwise, it will complete to the result of
|
||||
/// [calculation] with the casted argument.
|
||||
void _unaryNumFunction(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args, num Function(num) calculation) {
|
||||
// sqlite will ensure that this is only called with one argument
|
||||
final value = args[0].value;
|
||||
if (value is num) {
|
||||
ctx.resultNum(calculation(value));
|
||||
} else {
|
||||
ctx.resultNull();
|
||||
}
|
||||
}
|
||||
|
||||
void _sinImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, sin);
|
||||
}
|
||||
|
||||
void _cosImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, cos);
|
||||
}
|
||||
|
||||
void _tanImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, tan);
|
||||
}
|
||||
|
||||
void _sqrtImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, sqrt);
|
||||
}
|
||||
|
||||
void _asinImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, asin);
|
||||
}
|
||||
|
||||
void _acosImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, acos);
|
||||
}
|
||||
|
||||
void _atanImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_unaryNumFunction(ctx, argCount, args, atan);
|
||||
}
|
||||
|
||||
void _regexpImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
var multiLine = false;
|
||||
var caseSensitive = true;
|
||||
var unicode = false;
|
||||
var dotAll = false;
|
||||
|
||||
if (argCount < 2 || argCount > 3) {
|
||||
ctx.resultError('Expected two or three arguments to regexp');
|
||||
return;
|
||||
}
|
||||
|
||||
final firstParam = args[0].value;
|
||||
final secondParam = args[1].value;
|
||||
|
||||
if (firstParam == null || secondParam == null) {
|
||||
ctx.resultNull();
|
||||
return;
|
||||
}
|
||||
if (firstParam is! String || secondParam is! String) {
|
||||
ctx.resultError('Expected two strings as parameters to regexp');
|
||||
return;
|
||||
}
|
||||
|
||||
if (argCount == 3) {
|
||||
// In the variant with three arguments, the last (int) arg can be used to
|
||||
// enable regex flags. See the regexp() extension in moor for details.
|
||||
final value = args[2].value;
|
||||
if (value is int) {
|
||||
multiLine = (value & 1) == 1;
|
||||
caseSensitive = (value & 2) != 2;
|
||||
unicode = (value & 4) == 4;
|
||||
dotAll = (value & 8) == 8;
|
||||
}
|
||||
}
|
||||
|
||||
RegExp regex;
|
||||
try {
|
||||
regex = RegExp(
|
||||
firstParam as String,
|
||||
multiLine: multiLine,
|
||||
caseSensitive: caseSensitive,
|
||||
unicode: unicode,
|
||||
dotAll: dotAll,
|
||||
);
|
||||
} on FormatException catch (e) {
|
||||
ctx.resultError('Invalid regex: $e');
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.resultBool(regex.hasMatch(secondParam as String));
|
||||
}
|
||||
|
||||
void _containsImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
if (argCount < 2 || argCount > 3) {
|
||||
ctx.resultError('Expected 2 or 3 arguments to moor_contains');
|
||||
return;
|
||||
}
|
||||
|
||||
final first = args[0].value;
|
||||
final second = args[1].value;
|
||||
|
||||
if (first is! String || second is! String) {
|
||||
ctx.resultError('First two args must be strings');
|
||||
return;
|
||||
}
|
||||
|
||||
final caseSensitive = argCount == 3 && args[2].value == 1;
|
||||
|
||||
final firstAsString = first as String;
|
||||
final secondAsString = second as String;
|
||||
|
||||
final result = caseSensitive
|
||||
? firstAsString.contains(secondAsString)
|
||||
: firstAsString.toLowerCase().contains(secondAsString.toLowerCase());
|
||||
|
||||
ctx.resultInt(result ? 1 : 0);
|
||||
}
|
||||
|
||||
void _registerOn(Database db) {
|
||||
final powImplPointer =
|
||||
Pointer.fromFunction<sqlite3_function_handler>(_powImpl);
|
||||
db.createFunction('power', 2, powImplPointer, isDeterministic: true);
|
||||
db.createFunction('pow', 2, powImplPointer, isDeterministic: true);
|
||||
|
||||
db.createFunction('sqrt', 1, Pointer.fromFunction(_sqrtImpl),
|
||||
isDeterministic: true);
|
||||
|
||||
db.createFunction('sin', 1, Pointer.fromFunction(_sinImpl),
|
||||
isDeterministic: true);
|
||||
db.createFunction('cos', 1, Pointer.fromFunction(_cosImpl),
|
||||
isDeterministic: true);
|
||||
db.createFunction('tan', 1, Pointer.fromFunction(_tanImpl),
|
||||
isDeterministic: true);
|
||||
db.createFunction('asin', 1, Pointer.fromFunction(_asinImpl),
|
||||
isDeterministic: true);
|
||||
db.createFunction('acos', 1, Pointer.fromFunction(_acosImpl),
|
||||
isDeterministic: true);
|
||||
db.createFunction('atan', 1, Pointer.fromFunction(_atanImpl),
|
||||
isDeterministic: true);
|
||||
|
||||
db.createFunction('regexp', 2, Pointer.fromFunction(_regexpImpl),
|
||||
isDeterministic: true);
|
||||
// Third argument can be used to set flags (like multiline, case sensitivity,
|
||||
// etc.)
|
||||
db.createFunction('regexp_moor_ffi', 3, Pointer.fromFunction(_regexpImpl));
|
||||
|
||||
final containsImplPointer =
|
||||
Pointer.fromFunction<sqlite3_function_handler>(_containsImpl);
|
||||
db.createFunction('moor_contains', 2, containsImplPointer,
|
||||
isDeterministic: true);
|
||||
db.createFunction('moor_contains', 3, containsImplPointer,
|
||||
isDeterministic: true);
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
part of 'database.dart';
|
||||
|
||||
/// A prepared statement that can be executed multiple times.
|
||||
class PreparedStatement {
|
||||
final Pointer<types.Statement> _stmt;
|
||||
final Database _db;
|
||||
bool _closed = false;
|
||||
|
||||
bool _bound = false;
|
||||
final List<Pointer> _allocatedWhileBinding = [];
|
||||
|
||||
PreparedStatement._(this._stmt, this._db);
|
||||
|
||||
/// Closes this prepared statement and releases its resources.
|
||||
void close() {
|
||||
if (!_closed) {
|
||||
_reset();
|
||||
bindings.sqlite3_finalize(_stmt);
|
||||
_db._handleStmtFinalized(this);
|
||||
}
|
||||
_closed = true;
|
||||
}
|
||||
|
||||
/// Returns the amount of parameters in this prepared statement.
|
||||
///
|
||||
/// See also:
|
||||
/// - `sqlite3_bind_parameter_count`: https://www.sqlite.org/c3ref/bind_parameter_count.html
|
||||
int get parameterCount => bindings.sqlite3_bind_parameter_count(_stmt);
|
||||
|
||||
void _ensureNotFinalized() {
|
||||
if (_closed) {
|
||||
throw StateError('Tried to operate on a released prepared statement');
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes this prepared statement as a select statement. The returned rows
|
||||
/// will be returned.
|
||||
Result select([List<dynamic> params]) {
|
||||
assert(
|
||||
(params?.length ?? 0) == parameterCount,
|
||||
'Expected $parameterCount params, but got ${params?.length ?? 0}.',
|
||||
);
|
||||
|
||||
_ensureNotFinalized();
|
||||
_reset();
|
||||
_bindParams(params);
|
||||
|
||||
final columnCount = bindings.sqlite3_column_count(_stmt);
|
||||
// not using a Map<String, int> for indexed because column names are not
|
||||
// guaranteed to be unique
|
||||
final names = List<String>(columnCount);
|
||||
final rows = <List<dynamic>>[];
|
||||
|
||||
for (var i = 0; i < columnCount; i++) {
|
||||
// name pointer doesn't need to be disposed, that happens when we finalize
|
||||
names[i] = bindings.sqlite3_column_name(_stmt, i).readString();
|
||||
}
|
||||
|
||||
int resultCode;
|
||||
while ((resultCode = _step()) == Errors.SQLITE_ROW) {
|
||||
rows.add([for (var i = 0; i < columnCount; i++) _readValue(i)]);
|
||||
}
|
||||
|
||||
if (resultCode != Errors.SQLITE_OK && resultCode != Errors.SQLITE_DONE) {
|
||||
throw SqliteException._fromErrorCode(_db._db, resultCode);
|
||||
}
|
||||
|
||||
return Result(names, rows);
|
||||
}
|
||||
|
||||
dynamic _readValue(int index) {
|
||||
final type = bindings.sqlite3_column_type(_stmt, index);
|
||||
switch (type) {
|
||||
case Types.SQLITE_INTEGER:
|
||||
return bindings.sqlite3_column_int64(_stmt, index);
|
||||
case Types.SQLITE_FLOAT:
|
||||
return bindings.sqlite3_column_double(_stmt, index);
|
||||
case Types.SQLITE_TEXT:
|
||||
final length = bindings.sqlite3_column_bytes(_stmt, index);
|
||||
return bindings
|
||||
.sqlite3_column_text(_stmt, index)
|
||||
.readAsStringWithLength(length);
|
||||
case Types.SQLITE_BLOB:
|
||||
final length = bindings.sqlite3_column_bytes(_stmt, index);
|
||||
if (length == 0) {
|
||||
// sqlite3_column_blob returns a null pointer for non-null blobs with
|
||||
// a length of 0. Note that we can distinguish this from a proper null
|
||||
// by checking the type (which isn't SQLITE_NULL)
|
||||
return Uint8List(0);
|
||||
}
|
||||
return bindings.sqlite3_column_blob(_stmt, index).readBytes(length);
|
||||
case Types.SQLITE_NULL:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes this prepared statement.
|
||||
void execute([List<dynamic> params]) {
|
||||
_ensureNotFinalized();
|
||||
_reset();
|
||||
_bindParams(params);
|
||||
|
||||
final result = _step();
|
||||
|
||||
if (result != Errors.SQLITE_OK && result != Errors.SQLITE_DONE) {
|
||||
throw SqliteException._fromErrorCode(_db._db, result);
|
||||
}
|
||||
}
|
||||
|
||||
void _reset() {
|
||||
if (_bound) {
|
||||
bindings.sqlite3_reset(_stmt);
|
||||
_bound = false;
|
||||
}
|
||||
for (final pointer in _allocatedWhileBinding) {
|
||||
pointer.free();
|
||||
}
|
||||
_allocatedWhileBinding.clear();
|
||||
}
|
||||
|
||||
void _bindParams(List<dynamic> params) {
|
||||
if (params != null && params.isNotEmpty) {
|
||||
// variables in sqlite are 1-indexed
|
||||
for (var i = 1; i <= params.length; i++) {
|
||||
final param = params[i - 1];
|
||||
|
||||
if (param == null) {
|
||||
bindings.sqlite3_bind_null(_stmt, i);
|
||||
} else if (param is int) {
|
||||
bindings.sqlite3_bind_int64(_stmt, i, param);
|
||||
} else if (param is num) {
|
||||
bindings.sqlite3_bind_double(_stmt, i, param.toDouble());
|
||||
} else if (param is String) {
|
||||
final ptr = CBlob.allocateString(param);
|
||||
_allocatedWhileBinding.add(ptr);
|
||||
|
||||
bindings.sqlite3_bind_text(_stmt, i, ptr, -1, nullPtr());
|
||||
} else if (param is Uint8List) {
|
||||
if (param.isEmpty) {
|
||||
// malloc(0) is implementation-defined and might return a null
|
||||
// pointer, which is not what we want: Passing a null-pointer to
|
||||
// sqlite3_bind_blob will always bind NULL. So, we just pass 0x1 and
|
||||
// set a length of 0
|
||||
bindings.sqlite3_bind_blob(
|
||||
_stmt, i, Pointer.fromAddress(1), param.length, nullPtr());
|
||||
} else {
|
||||
final ptr = CBlob.allocate(param);
|
||||
|
||||
bindings.sqlite3_bind_blob(_stmt, i, ptr, param.length, nullPtr());
|
||||
_allocatedWhileBinding.add(ptr);
|
||||
}
|
||||
} else {
|
||||
throw ArgumentError.value(
|
||||
param,
|
||||
'params[$i]',
|
||||
'Allowed parameters must either be null or an int, num, String or '
|
||||
'Uint8List.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_bound = true;
|
||||
}
|
||||
|
||||
int _step() => bindings.sqlite3_step(_stmt);
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Signature responsible for loading the dynamic sqlite3 library that moor will
|
||||
/// use.
|
||||
typedef OpenLibrary = DynamicLibrary Function();
|
||||
|
||||
enum OperatingSystem {
|
||||
android,
|
||||
linux,
|
||||
iOS,
|
||||
macOS,
|
||||
windows,
|
||||
fuchsia,
|
||||
}
|
||||
|
||||
/// The instance managing different approaches to load the [DynamicLibrary] for
|
||||
/// sqlite when needed. See the documentation for [OpenDynamicLibrary] to learn
|
||||
/// how the default opening behavior can be overridden.
|
||||
final OpenDynamicLibrary open = OpenDynamicLibrary._();
|
||||
|
||||
DynamicLibrary _defaultOpen() {
|
||||
if (Platform.isLinux || Platform.isAndroid) {
|
||||
try {
|
||||
return DynamicLibrary.open('libsqlite3.so');
|
||||
} catch (_) {
|
||||
if (Platform.isAndroid) {
|
||||
// On some (especially old) Android devices, we somehow can't dlopen
|
||||
// libraries shipped with the apk. We need to find the full path of the
|
||||
// library (/data/data/<id>/lib/libsqlite3.so) and open that one.
|
||||
// For details, see https://github.com/simolus3/moor/issues/420
|
||||
final appIdAsBytes = File('/proc/self/cmdline').readAsBytesSync();
|
||||
|
||||
// app id ends with the first \0 character in here.
|
||||
final endOfAppId = max(appIdAsBytes.indexOf(0), 0);
|
||||
final appId = String.fromCharCodes(appIdAsBytes.sublist(0, endOfAppId));
|
||||
|
||||
return DynamicLibrary.open('/data/data/$appId/lib/libsqlite3.so');
|
||||
}
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
if (Platform.isMacOS || Platform.isIOS) {
|
||||
// todo: Consider including sqlite3 in the build and use DynamicLibrary.
|
||||
// executable()
|
||||
return DynamicLibrary.open('/usr/lib/libsqlite3.dylib');
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
return DynamicLibrary.open('sqlite3.dll');
|
||||
}
|
||||
|
||||
throw UnsupportedError(
|
||||
'moor_ffi does not support ${Platform.operatingSystem} yet');
|
||||
}
|
||||
|
||||
/// Manages functions that define how to load the [DynamicLibrary] for sqlite.
|
||||
///
|
||||
/// The default behavior will use `DynamicLibrary.open('libsqlite3.so')` on
|
||||
/// Linux and Android, `DynamicLibrary.open('libsqlite3.dylib')` on iOS and
|
||||
/// macOS and `DynamicLibrary.open('sqlite3.dll')` on Windows.
|
||||
///
|
||||
/// The default behavior can be overridden for a specific OS by using
|
||||
/// [overrideFor]. To override the behavior on all platforms, use
|
||||
/// [overrideForAll].
|
||||
class OpenDynamicLibrary {
|
||||
final Map<OperatingSystem, OpenLibrary> _overriddenPlatforms = {};
|
||||
OpenLibrary _overriddenForAll;
|
||||
|
||||
OpenDynamicLibrary._();
|
||||
|
||||
/// Returns the current [OperatingSystem] as read from the [Platform] getters.
|
||||
OperatingSystem get os {
|
||||
if (Platform.isAndroid) return OperatingSystem.android;
|
||||
if (Platform.isLinux) return OperatingSystem.linux;
|
||||
if (Platform.isIOS) return OperatingSystem.iOS;
|
||||
if (Platform.isMacOS) return OperatingSystem.macOS;
|
||||
if (Platform.isWindows) return OperatingSystem.windows;
|
||||
if (Platform.isFuchsia) return OperatingSystem.fuchsia;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Opens the [DynamicLibrary] from which `moor_ffi` is going to
|
||||
/// [DynamicLibrary.lookup] sqlite's methods that will be used. This method is
|
||||
/// meant to be called by `moor_ffi` only.
|
||||
DynamicLibrary openSqlite() {
|
||||
if (_overriddenForAll != null) {
|
||||
return _overriddenForAll();
|
||||
}
|
||||
|
||||
final forPlatform = _overriddenPlatforms[os];
|
||||
if (forPlatform != null) {
|
||||
return forPlatform();
|
||||
}
|
||||
|
||||
return _defaultOpen();
|
||||
}
|
||||
|
||||
/// Makes `moor_ffi` use the [open] function when running on the specified
|
||||
/// [os]. This can be used to override the loading behavior on some platforms.
|
||||
/// To override that behavior on all platforms, consider using
|
||||
/// [overrideForAll].
|
||||
/// This method must be called before opening any database.
|
||||
///
|
||||
/// When using the asynchronous API over isolates, [open] __must be__ a top-
|
||||
/// level function or a static method.
|
||||
void overrideFor(OperatingSystem os, OpenLibrary open) {
|
||||
_overriddenPlatforms[os] = open;
|
||||
}
|
||||
|
||||
// ignore: use_setters_to_change_properties
|
||||
/// Makes `moor_ffi` use the [OpenLibrary] function for all Dart platforms.
|
||||
/// If this method has been called, it takes precedence over [overrideFor].
|
||||
/// This method must be called before opening any database.
|
||||
///
|
||||
/// When using the asynchronous API over isolates, [open] __must be__ a top-
|
||||
/// level function or a static method.
|
||||
void overrideForAll(OpenLibrary open) {
|
||||
_overriddenForAll = open;
|
||||
}
|
||||
|
||||
/// Clears all associated open helpers for all platforms.
|
||||
@visibleForTesting
|
||||
void reset() {
|
||||
_overriddenForAll = null;
|
||||
_overriddenPlatforms.clear();
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
part of 'package:moor_ffi/moor_ffi.dart';
|
||||
|
||||
/// A moor database that runs on the Dart VM.
|
||||
class VmDatabase extends DelegatedDatabase {
|
||||
VmDatabase._(DatabaseDelegate delegate, bool logStatements)
|
||||
: super(delegate, isSequential: true, logStatements: logStatements);
|
||||
|
||||
/// Creates a database that will store its result in the [file], creating it
|
||||
/// if it doesn't exist.
|
||||
factory VmDatabase(File file, {bool logStatements = false}) {
|
||||
return VmDatabase._(_VmDelegate(file), logStatements);
|
||||
}
|
||||
|
||||
/// Creates an in-memory database won't persist its changes on disk.
|
||||
factory VmDatabase.memory({bool logStatements = false}) {
|
||||
return VmDatabase._(_VmDelegate(null), logStatements);
|
||||
}
|
||||
}
|
||||
|
||||
class _VmDelegate extends DatabaseDelegate {
|
||||
Database _db;
|
||||
|
||||
final File file;
|
||||
|
||||
_VmDelegate(this.file);
|
||||
|
||||
@override
|
||||
final TransactionDelegate transactionDelegate = const NoTransactionDelegate();
|
||||
|
||||
@override
|
||||
DbVersionDelegate versionDelegate;
|
||||
|
||||
@override
|
||||
Future<bool> get isOpen => Future.value(_db != null);
|
||||
|
||||
@override
|
||||
Future<void> open(QueryExecutorUser user) async {
|
||||
if (file != null) {
|
||||
_db = Database.openFile(file);
|
||||
} else {
|
||||
_db = Database.memory();
|
||||
}
|
||||
_db.enableMoorFfiFunctions();
|
||||
versionDelegate = _VmVersionDelegate(_db);
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runBatched(BatchedStatements statements) async {
|
||||
final prepared = [
|
||||
for (final stmt in statements.statements) _db.prepare(stmt),
|
||||
];
|
||||
|
||||
for (final application in statements.arguments) {
|
||||
final stmt = prepared[application.statementIndex];
|
||||
|
||||
stmt.execute(application.arguments);
|
||||
}
|
||||
|
||||
for (final stmt in prepared) {
|
||||
stmt.close();
|
||||
}
|
||||
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
Future _runWithArgs(String statement, List<dynamic> args) async {
|
||||
if (args.isEmpty) {
|
||||
_db.execute(statement);
|
||||
} else {
|
||||
final stmt = _db.prepare(statement);
|
||||
stmt.execute(args);
|
||||
stmt.close();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> runCustom(String statement, List args) async {
|
||||
await _runWithArgs(statement, args);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInsert(String statement, List args) async {
|
||||
await _runWithArgs(statement, args);
|
||||
return _db.getLastInsertId();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runUpdate(String statement, List args) async {
|
||||
await _runWithArgs(statement, args);
|
||||
return _db.getUpdatedRows();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<QueryResult> runSelect(String statement, List args) async {
|
||||
final stmt = _db.prepare(statement);
|
||||
final result = stmt.select(args);
|
||||
stmt.close();
|
||||
|
||||
return Future.value(QueryResult(result.columnNames, result.rows));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
_db.close();
|
||||
}
|
||||
}
|
||||
|
||||
class _VmVersionDelegate extends DynamicVersionDelegate {
|
||||
final Database database;
|
||||
|
||||
_VmVersionDelegate(this.database);
|
||||
|
||||
@override
|
||||
Future<int> get schemaVersion => Future.value(database.userVersion());
|
||||
|
||||
@override
|
||||
Future<void> setSchemaVersion(int version) {
|
||||
database.setUserVersion(version);
|
||||
return Future.value();
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
name: moor_ffi
|
||||
description: "Provides sqlite bindings using dart:ffi, including a moor executor"
|
||||
version: 0.8.0
|
||||
homepage: https://github.com/simolus3/moor/tree/develop/moor_ffi
|
||||
issue_tracker: https://github.com/simolus3/moor/issues
|
||||
|
||||
environment:
|
||||
sdk: ">=2.6.0 <3.0.0"
|
||||
flutter: any
|
||||
|
||||
dependencies:
|
||||
moor: ^3.0.0
|
||||
ffi: ^0.1.3
|
||||
collection: ^1.0.0
|
||||
meta: ^1.0.2
|
||||
# moor_ffi used to include sqlite on Flutter builds, so we depend on sqlite3_flutter_libs to continue this behavior.
|
||||
# This makes moor_ffi a Flutter-only package - not what we want, but now it's deprecated anyways. Non-Flutter users
|
||||
# should use the `sqlite3` package instead.
|
||||
sqlite3_flutter_libs: ^0.2.0
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.6.0
|
||||
path: ^1.6.0
|
|
@ -1,95 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test("database can't be used after close", () {
|
||||
final db = Database.memory();
|
||||
db.execute('SELECT 1');
|
||||
|
||||
db.close();
|
||||
|
||||
expect(() => db.execute('SELECT 1'), throwsA(anything));
|
||||
});
|
||||
|
||||
test('getUpdatedRows', () {
|
||||
final db = Database.memory();
|
||||
|
||||
db
|
||||
..execute('CREATE TABLE foo (bar INT);')
|
||||
..execute('INSERT INTO foo VALUES (3);');
|
||||
|
||||
expect(db.getUpdatedRows(), 1);
|
||||
});
|
||||
|
||||
test('closing multiple times works', () {
|
||||
final db = Database.memory();
|
||||
db.execute('SELECT 1');
|
||||
|
||||
db.close();
|
||||
db.close(); // shouldn't throw or crash
|
||||
});
|
||||
|
||||
test('throws exception on an invalid statement', () {
|
||||
final db = Database.memory();
|
||||
db.execute('CREATE TABLE foo (bar INTEGER CHECK (bar > 10));');
|
||||
|
||||
expect(
|
||||
() => db.execute('INSERT INTO foo VALUES (3);'),
|
||||
throwsA(const TypeMatcher<SqliteException>().having(
|
||||
(e) => e.message, 'message', contains('CHECK constraint failed'))),
|
||||
);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('throws when preparing an invalid statement', () {
|
||||
final db = Database.memory();
|
||||
|
||||
expect(
|
||||
() => db.prepare('INSERT INTO foo VALUES (3);'),
|
||||
throwsA(const TypeMatcher<SqliteException>()
|
||||
.having((e) => e.message, 'message', contains('no such table'))),
|
||||
);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('open read-only', () async {
|
||||
final path = join('.dart_tool', 'moor_ffi', 'test', 'read_only.db');
|
||||
// Make sure the path exists
|
||||
try {
|
||||
await Directory(dirname(path)).create(recursive: true);
|
||||
} catch (_) {}
|
||||
// but not the db
|
||||
try {
|
||||
await File(path).delete();
|
||||
} catch (_) {}
|
||||
|
||||
// Opening a non-existent database should fail
|
||||
try {
|
||||
Database.open(path, readOnly: true);
|
||||
fail('should fail');
|
||||
} on SqliteException catch (_) {}
|
||||
|
||||
// Open in read-write mode to create the database
|
||||
var db = Database.open(path);
|
||||
// Change the user version to test read-write access
|
||||
db.setUserVersion(1);
|
||||
db.close();
|
||||
|
||||
// Open in read-only
|
||||
db = Database.open(path, readOnly: true);
|
||||
// Change the user version to test read-only mode
|
||||
try {
|
||||
db.setUserVersion(2);
|
||||
fail('should fail');
|
||||
} on SqliteException catch (_) {}
|
||||
// Check that it has not changed
|
||||
expect(db.userVersion(), 1);
|
||||
|
||||
db.close();
|
||||
});
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('insert statements report their id', () {
|
||||
final opened = Database.memory();
|
||||
opened.execute('CREATE TABLE tbl(a INTEGER PRIMARY KEY AUTOINCREMENT)');
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
opened.execute('INSERT INTO tbl DEFAULT VALUES');
|
||||
expect(opened.getLastInsertId(), i + 1);
|
||||
}
|
||||
|
||||
opened.close();
|
||||
});
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('can bind and retreive 64 bit ints', () {
|
||||
const value = 1 << 62;
|
||||
|
||||
final opened = Database.memory();
|
||||
final stmt = opened.prepare('SELECT ?');
|
||||
|
||||
final result = stmt.select([value]);
|
||||
expect(result, [
|
||||
{'?': value}
|
||||
]);
|
||||
|
||||
opened.close();
|
||||
});
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Database db;
|
||||
|
||||
setUp(() => db = Database.memory()..enableMoorFfiFunctions());
|
||||
tearDown(() => db.close());
|
||||
|
||||
dynamic selectSingle(String expression) {
|
||||
final stmt = db.prepare('SELECT $expression AS r;');
|
||||
final rows = stmt.select();
|
||||
stmt.close();
|
||||
|
||||
return rows.single['r'];
|
||||
}
|
||||
|
||||
group('pow', () {
|
||||
dynamic _resultOfPow(String a, String b) {
|
||||
return selectSingle('pow($a, $b)');
|
||||
}
|
||||
|
||||
test('returns null when any argument is null', () {
|
||||
expect(_resultOfPow('null', 'null'), isNull);
|
||||
expect(_resultOfPow('3', 'null'), isNull);
|
||||
expect(_resultOfPow('null', '3'), isNull);
|
||||
});
|
||||
|
||||
test('returns correct results', () {
|
||||
expect(_resultOfPow('10', '0'), 1);
|
||||
expect(_resultOfPow('0', '10'), 0);
|
||||
expect(_resultOfPow('0', '0'), 1);
|
||||
expect(_resultOfPow('2', '5'), 32);
|
||||
expect(_resultOfPow('3.5', '2'), 12.25);
|
||||
expect(_resultOfPow('10', '-1'), 0.1);
|
||||
});
|
||||
});
|
||||
|
||||
for (final scenario in _testCases) {
|
||||
final function = scenario.sqlFunction;
|
||||
|
||||
test(function, () {
|
||||
final stmt = db.prepare('SELECT $function(?) AS r');
|
||||
|
||||
for (final input in scenario.inputs) {
|
||||
final sqlResult = stmt.select([input]).single['r'];
|
||||
final dartResult = scenario.dartEquivalent(input);
|
||||
|
||||
// NaN in sqlite is null, account for that
|
||||
if (dartResult.isNaN) {
|
||||
expect(
|
||||
sqlResult,
|
||||
null,
|
||||
reason: '$function($input) = $dartResult',
|
||||
);
|
||||
} else {
|
||||
expect(
|
||||
sqlResult,
|
||||
equals(dartResult),
|
||||
reason: '$function($input) = $dartResult',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final resultWithNull = stmt.select([null]);
|
||||
expect(resultWithNull.single['r'], isNull);
|
||||
});
|
||||
}
|
||||
|
||||
group('regexp', () {
|
||||
test('cannot be called with more or fewer than 2 parameters', () {
|
||||
expect(() => db.execute("SELECT regexp('foo')"),
|
||||
throwsA(isA<SqliteException>()));
|
||||
|
||||
expect(() => db.execute("SELECT regexp('foo', 'bar', 'baz')"),
|
||||
throwsA(isA<SqliteException>()));
|
||||
});
|
||||
|
||||
test('results in error when not passing a string', () {
|
||||
final complainsAboutTypes = throwsA(isA<SqliteException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
contains('Expected two strings as parameters to regexp'),
|
||||
));
|
||||
|
||||
expect(() => db.execute("SELECT 'foo' REGEXP 3"), complainsAboutTypes);
|
||||
expect(() => db.execute("SELECT 3 REGEXP 'foo'"), complainsAboutTypes);
|
||||
});
|
||||
|
||||
test('fails on invalid regex', () {
|
||||
expect(
|
||||
() => db.execute("SELECT 'foo' REGEXP '('"),
|
||||
throwsA(isA<SqliteException>()
|
||||
.having((e) => e.message, 'message', contains('Invalid regex'))),
|
||||
);
|
||||
});
|
||||
|
||||
test('returns true on a match', () {
|
||||
final stmt = db.prepare("SELECT 'foo' REGEXP 'fo+' AS r");
|
||||
final result = stmt.select();
|
||||
expect(result.single['r'], 1);
|
||||
});
|
||||
|
||||
test("returns false when the regex doesn't match", () {
|
||||
final stmt = db.prepare("SELECT 'bar' REGEXP 'fo+' AS r");
|
||||
final result = stmt.select();
|
||||
expect(result.single['r'], 0);
|
||||
});
|
||||
|
||||
test('supports flags', () {
|
||||
final stmt =
|
||||
db.prepare(r"SELECT regexp_moor_ffi('^bar', 'foo\nbar', 8) AS r;");
|
||||
final result = stmt.select();
|
||||
expect(result.single['r'], 0);
|
||||
});
|
||||
|
||||
test('returns null when either argument is null', () {
|
||||
final stmt = db.prepare('SELECT ? REGEXP ?');
|
||||
|
||||
expect(stmt.select(['foo', null]).single.columnAt(0), isNull);
|
||||
expect(stmt.select([null, 'foo']).single.columnAt(0), isNull);
|
||||
|
||||
stmt.close();
|
||||
});
|
||||
});
|
||||
|
||||
group('moor_contains', () {
|
||||
test('checks for type errors', () {
|
||||
expect(() => db.execute('SELECT moor_contains(12, 1);'),
|
||||
throwsA(isA<SqliteException>()));
|
||||
});
|
||||
|
||||
test('case insensitive without parameter', () {
|
||||
expect(selectSingle("moor_contains('foo', 'O')"), 1);
|
||||
});
|
||||
|
||||
test('case insensitive with parameter', () {
|
||||
expect(selectSingle("moor_contains('foo', 'O', 0)"), 1);
|
||||
});
|
||||
|
||||
test('case sensitive', () {
|
||||
expect(selectSingle("moor_contains('Hello', 'hell', 1)"), 0);
|
||||
expect(selectSingle("moor_contains('hi', 'i', 1)"), 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// utils to verify the sql functions behave exactly like the ones from the VM
|
||||
|
||||
class _UnaryFunctionTestCase {
|
||||
final String sqlFunction;
|
||||
final num Function(num) dartEquivalent;
|
||||
final List<num> inputs;
|
||||
|
||||
const _UnaryFunctionTestCase(
|
||||
this.sqlFunction, this.dartEquivalent, this.inputs);
|
||||
}
|
||||
|
||||
const _unaryInputs = [
|
||||
pi,
|
||||
0,
|
||||
pi / 2,
|
||||
e,
|
||||
123,
|
||||
];
|
||||
|
||||
const _testCases = <_UnaryFunctionTestCase>[
|
||||
_UnaryFunctionTestCase('sin', sin, _unaryInputs),
|
||||
_UnaryFunctionTestCase('cos', cos, _unaryInputs),
|
||||
_UnaryFunctionTestCase('tan', tan, _unaryInputs),
|
||||
_UnaryFunctionTestCase('sqrt', sqrt, _unaryInputs),
|
||||
_UnaryFunctionTestCase('asin', asin, _unaryInputs),
|
||||
_UnaryFunctionTestCase('acos', acos, _unaryInputs),
|
||||
_UnaryFunctionTestCase('atan', atan, _unaryInputs),
|
||||
];
|
|
@ -1,147 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('prepared statements can be used multiple times', () {
|
||||
final opened = Database.memory();
|
||||
opened.execute('CREATE TABLE tbl (a TEXT);');
|
||||
|
||||
final stmt = opened.prepare('INSERT INTO tbl(a) VALUES(?)');
|
||||
stmt.execute(['a']);
|
||||
stmt.execute(['b']);
|
||||
stmt.close();
|
||||
|
||||
final select = opened.prepare('SELECT * FROM tbl ORDER BY a');
|
||||
final result = select.select();
|
||||
|
||||
expect(result, hasLength(2));
|
||||
expect(result.map((row) => row['a']), ['a', 'b']);
|
||||
|
||||
select.close();
|
||||
|
||||
opened.close();
|
||||
});
|
||||
|
||||
test('prepared statements cannot be used after close', () {
|
||||
final opened = Database.memory();
|
||||
|
||||
final stmt = opened.prepare('SELECT ?');
|
||||
stmt.close();
|
||||
|
||||
expect(stmt.select, throwsA(anything));
|
||||
|
||||
opened.close();
|
||||
});
|
||||
|
||||
test('prepared statements cannot be used after db is closed', () {
|
||||
final opened = Database.memory();
|
||||
final stmt = opened.prepare('SELECT 1');
|
||||
opened.close();
|
||||
|
||||
expect(stmt.select, throwsA(anything));
|
||||
});
|
||||
|
||||
Uint8List _insertBlob(Uint8List value) {
|
||||
final opened = Database.memory();
|
||||
opened.execute('CREATE TABLE tbl (x BLOB);');
|
||||
|
||||
final insert = opened.prepare('INSERT INTO tbl VALUES (?)');
|
||||
insert.execute([value]);
|
||||
insert.close();
|
||||
|
||||
final select = opened.prepare('SELECT * FROM tbl');
|
||||
final result = select.select().single;
|
||||
|
||||
opened.close();
|
||||
return result['x'] as Uint8List;
|
||||
}
|
||||
|
||||
test('can bind empty blob in prepared statements', () {
|
||||
expect(_insertBlob(Uint8List(0)), isEmpty);
|
||||
});
|
||||
|
||||
test('can bind null blob in prepared statements', () {
|
||||
expect(_insertBlob(null), isNull);
|
||||
});
|
||||
|
||||
test('can bind and read non-empty blob', () {
|
||||
const bytes = [1, 2, 3];
|
||||
expect(_insertBlob(Uint8List.fromList(bytes)), bytes);
|
||||
});
|
||||
|
||||
test('throws when sql statement has an error', () {
|
||||
final db = Database.memory();
|
||||
db.execute('CREATE TABLE foo (id INTEGER CHECK (id > 10));');
|
||||
|
||||
final stmt = db.prepare('INSERT INTO foo VALUES (9)');
|
||||
|
||||
expect(
|
||||
stmt.execute,
|
||||
throwsA(const TypeMatcher<SqliteException>()
|
||||
.having((e) => e.message, 'message', contains('foo'))),
|
||||
);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('throws an exception when iterating over result rows', () {
|
||||
final db = Database.memory()
|
||||
..createFunction(
|
||||
'raise_if_two',
|
||||
1,
|
||||
Pointer.fromFunction(_raiseIfTwo),
|
||||
);
|
||||
|
||||
db.execute(
|
||||
'CREATE TABLE tbl (a INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT)');
|
||||
// insert with a = 1..3
|
||||
for (var i = 0; i < 3; i++) {
|
||||
db.execute('INSERT INTO tbl DEFAULT VALUES');
|
||||
}
|
||||
|
||||
final statement = db.prepare('SELECT raise_if_two(a) FROM tbl ORDER BY a');
|
||||
|
||||
expect(
|
||||
statement.select,
|
||||
throwsA(isA<SqliteException>()
|
||||
.having((e) => e.message, 'message', contains('was two'))),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws an exception when passing an invalid type as argument', () {
|
||||
final db = Database.memory();
|
||||
final stmt = db.prepare('SELECT ?');
|
||||
|
||||
expect(() => stmt.execute([false]), throwsArgumentError);
|
||||
db.close();
|
||||
});
|
||||
|
||||
group('asserts that the amount of parameters are correct', () {
|
||||
final db = Database.memory();
|
||||
|
||||
test('when no parameters are set', () {
|
||||
final stmt = db.prepare('SELECT ?');
|
||||
expect(stmt.select, throwsA(isA<AssertionError>()));
|
||||
});
|
||||
|
||||
test('when the wrong amount of parameters are set', () {
|
||||
final stmt = db.prepare('SELECT ?, ?');
|
||||
expect(() => stmt.select([1]), throwsA(isA<AssertionError>()));
|
||||
});
|
||||
|
||||
tearDownAll(db.close);
|
||||
});
|
||||
}
|
||||
|
||||
void _raiseIfTwo(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
final value = args[0].value;
|
||||
if (value == 2) {
|
||||
ctx.resultError('parameter was two');
|
||||
} else {
|
||||
ctx.resultNull();
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('select statements return expected value', () {
|
||||
final opened = Database.memory();
|
||||
|
||||
final prepared = opened.prepare('SELECT ?');
|
||||
|
||||
final result1 = prepared.select([1]);
|
||||
expect(result1.columnNames, ['?']);
|
||||
expect(result1.single.columnAt(0), 1);
|
||||
|
||||
final result2 = prepared.select([2]);
|
||||
expect(result2.columnNames, ['?']);
|
||||
expect(result2.single.columnAt(0), 2);
|
||||
|
||||
final result3 = prepared.select(['']);
|
||||
expect(result3.columnNames, ['?']);
|
||||
expect(result3.single.columnAt(0), '');
|
||||
|
||||
opened.close();
|
||||
});
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:moor_ffi/src/bindings/constants.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('open read-only exception', () async {
|
||||
final path =
|
||||
join('.dart_tool', 'moor_ffi', 'test', 'read_only_exception.db');
|
||||
// Make sure the path exists
|
||||
try {
|
||||
await Directory(dirname(path)).create(recursive: true);
|
||||
} catch (_) {}
|
||||
// but not the db
|
||||
try {
|
||||
await File(path).delete();
|
||||
} catch (_) {}
|
||||
|
||||
// Opening a non-existent database should fail
|
||||
try {
|
||||
Database.open(path, readOnly: true);
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
expect(e.extendedResultCode, Errors.SQLITE_CANTOPEN);
|
||||
expect(e.toString(), startsWith('SqliteException(14): '));
|
||||
}
|
||||
});
|
||||
|
||||
test('statement exception', () async {
|
||||
// Only testing some common errors...
|
||||
final db = Database.memory();
|
||||
|
||||
// Basic syntax error
|
||||
try {
|
||||
db.execute('DUMMY');
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
expect(e.extendedResultCode, Errors.SQLITE_ERROR);
|
||||
expect(e.resultCode, Errors.SQLITE_ERROR);
|
||||
expect(e.toString(), startsWith('SqliteException(1): '));
|
||||
}
|
||||
|
||||
// No table
|
||||
try {
|
||||
db.execute('SELECT * FROM missing_table');
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
expect(e.extendedResultCode, Errors.SQLITE_ERROR);
|
||||
expect(e.resultCode, Errors.SQLITE_ERROR);
|
||||
}
|
||||
|
||||
// Constraint primary key
|
||||
db.execute('CREATE TABLE Test (name TEXT PRIMARY KEY)');
|
||||
db.execute("INSERT INTO Test(name) VALUES('test1')");
|
||||
try {
|
||||
db.execute("INSERT INTO Test(name) VALUES('test1')");
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
// SQLITE_CONSTRAINT_PRIMARYKEY (1555)
|
||||
expect(e.extendedResultCode, 1555);
|
||||
expect(e.resultCode, Errors.SQLITE_CONSTRAINT);
|
||||
expect(e.toString(), startsWith('SqliteException(1555): '));
|
||||
}
|
||||
|
||||
// Constraint using prepared statement
|
||||
db.execute('CREATE TABLE Test2 (id PRIMARY KEY, name TEXT UNIQUE)');
|
||||
final prepared = db.prepare('INSERT INTO Test2(name) VALUES(?)');
|
||||
prepared.execute(['test2']);
|
||||
try {
|
||||
prepared.execute(['test2']);
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
// SQLITE_CONSTRAINT_UNIQUE (2067)
|
||||
expect(e.extendedResultCode, 2067);
|
||||
expect(e.resultCode, Errors.SQLITE_CONSTRAINT);
|
||||
}
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('busy exception', () async {
|
||||
final path = join('.dart_tool', 'moor_ffi', 'test', 'busy.db');
|
||||
// Make sure the path exists
|
||||
try {
|
||||
await Directory(dirname(path)).create(recursive: true);
|
||||
} catch (_) {}
|
||||
// but not the db
|
||||
try {
|
||||
await File(path).delete();
|
||||
} catch (_) {}
|
||||
|
||||
final db1 = Database.open(path);
|
||||
final db2 = Database.open(path);
|
||||
db1.execute('BEGIN EXCLUSIVE TRANSACTION');
|
||||
try {
|
||||
db2.execute('BEGIN EXCLUSIVE TRANSACTION');
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
expect(e.extendedResultCode, Errors.SQLITE_BUSY);
|
||||
expect(e.resultCode, Errors.SQLITE_BUSY);
|
||||
}
|
||||
db1.close();
|
||||
db2.close();
|
||||
});
|
||||
|
||||
test('invalid format', () async {
|
||||
final path = join('.dart_tool', 'moor_ffi', 'test', 'invalid_format.db');
|
||||
// Make sure the path exists
|
||||
try {
|
||||
await Directory(dirname(path)).create(recursive: true);
|
||||
} catch (_) {}
|
||||
await File(path).writeAsString('not a database file');
|
||||
|
||||
final db = Database.open(path);
|
||||
try {
|
||||
db.setUserVersion(1);
|
||||
fail('should fail');
|
||||
} on SqliteException catch (e) {
|
||||
expect(e.extendedResultCode, Errors.SQLITE_NOTADB);
|
||||
expect(e.resultCode, Errors.SQLITE_NOTADB);
|
||||
}
|
||||
db.close();
|
||||
});
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
import 'dart:ffi';
|
||||
|
||||
import 'package:moor/moor.dart';
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final _params = <dynamic>[];
|
||||
|
||||
void _testFunImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
_params.clear();
|
||||
for (var i = 0; i < argCount; i++) {
|
||||
_params.add(args[i].value);
|
||||
}
|
||||
|
||||
ctx.resultNull();
|
||||
}
|
||||
|
||||
void _testNullImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
ctx.resultNull();
|
||||
}
|
||||
|
||||
void _testIntImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
ctx.resultInt(420);
|
||||
}
|
||||
|
||||
void _testDoubleImpl(Pointer<FunctionContext> ctx, int argCount,
|
||||
Pointer<Pointer<SqliteValue>> args) {
|
||||
ctx.resultDouble(133.7);
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('can read arguments of user defined functions', () {
|
||||
final db = Database.memory();
|
||||
db.createFunction('test_fun', 6, Pointer.fromFunction(_testFunImpl));
|
||||
|
||||
db.execute(
|
||||
r'''SELECT test_fun(1, 2.5, 'hello world', X'ff00ff', X'', NULL)''');
|
||||
db.close();
|
||||
|
||||
expect(_params, [
|
||||
1,
|
||||
2.5,
|
||||
'hello world',
|
||||
Uint8List.fromList([255, 0, 255]),
|
||||
Uint8List(0),
|
||||
null,
|
||||
]);
|
||||
});
|
||||
|
||||
group('can return', () {
|
||||
Database db;
|
||||
|
||||
setUp(() => db = Database.memory());
|
||||
tearDown(() => db.close());
|
||||
|
||||
test('null', () {
|
||||
db.createFunction('test_null', 0, Pointer.fromFunction(_testNullImpl));
|
||||
final stmt = db.prepare('SELECT test_null() AS result');
|
||||
|
||||
expect(stmt.select(), [
|
||||
{'result': null}
|
||||
]);
|
||||
});
|
||||
|
||||
test('integers', () {
|
||||
db.createFunction('test_int', 0, Pointer.fromFunction(_testIntImpl));
|
||||
final stmt = db.prepare('SELECT test_int() AS result');
|
||||
|
||||
expect(stmt.select(), [
|
||||
{'result': 420}
|
||||
]);
|
||||
});
|
||||
|
||||
test('doubles', () {
|
||||
db.createFunction(
|
||||
'test_double', 0, Pointer.fromFunction(_testDoubleImpl));
|
||||
final stmt = db.prepare('SELECT test_double() AS result');
|
||||
|
||||
expect(stmt.select(), [
|
||||
{'result': 133.7}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('throws when using a long function name', () {
|
||||
final db = Database.memory();
|
||||
|
||||
expect(
|
||||
() => db.createFunction('foo' * 100, 10, nullptr), throwsArgumentError);
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('throws when using an invalid argument count', () {
|
||||
final db = Database.memory();
|
||||
|
||||
expect(() => db.createFunction('foo', -2, nullptr), throwsArgumentError);
|
||||
expect(() => db.createFunction('foo', 128, nullptr), throwsArgumentError);
|
||||
|
||||
db.close();
|
||||
});
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
void main() {
|
||||
test('can set the user version on a database', () {
|
||||
final file = File(p.join(
|
||||
Directory.systemTemp.absolute.path, 'moor_ffi_test_user_version.db'));
|
||||
final opened = Database.openFile(file);
|
||||
|
||||
var version = opened.userVersion();
|
||||
expect(version, 0);
|
||||
|
||||
opened.setUserVersion(3);
|
||||
version = opened.userVersion();
|
||||
expect(version, 3);
|
||||
|
||||
// ensure that the version is stored on file
|
||||
opened.close();
|
||||
|
||||
final another = Database.openFile(file);
|
||||
expect(another.userVersion(), 3);
|
||||
another.close();
|
||||
|
||||
file.deleteSync();
|
||||
});
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
import 'package:moor/moor.dart';
|
||||
import 'package:moor_ffi/src/ffi/blob.dart';
|
||||
import 'package:moor_ffi/src/ffi/utils.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('utf8 store and load test', () {
|
||||
const content = 'Hasta Mañana';
|
||||
final blob = CBlob.allocateString(content);
|
||||
|
||||
expect(blob.readString(), content);
|
||||
blob.free();
|
||||
});
|
||||
|
||||
test('blob load and store test', () {
|
||||
final data = List.generate(256, (x) => x);
|
||||
final blob = CBlob.allocate(Uint8List.fromList(data));
|
||||
|
||||
expect(blob.readBytes(256), data);
|
||||
blob.free();
|
||||
});
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:moor_ffi/database.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Database database;
|
||||
|
||||
setUp(() => database = Database.memory());
|
||||
|
||||
tearDown(() => database.close());
|
||||
|
||||
test('violating constraint throws exception with extended error code', () {
|
||||
database.execute('CREATE TABLE tbl(a INTEGER NOT NULL)');
|
||||
|
||||
final statement = database.prepare('INSERT INTO tbl DEFAULT VALUES');
|
||||
|
||||
expect(
|
||||
statement.execute,
|
||||
throwsA(
|
||||
isA<SqliteException>().having(
|
||||
(e) => e.explanation, 'explanation', endsWith(' (code 1299)')),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:moor_ffi/open_helper.dart';
|
||||
|
||||
void main() {
|
||||
tearDown(open.reset);
|
||||
|
||||
test('opening behavior can be overridden', () {
|
||||
var called = false;
|
||||
open.overrideFor(open.os, () {
|
||||
called = true;
|
||||
return null;
|
||||
});
|
||||
|
||||
expect(open.openSqlite(), isNull);
|
||||
expect(called, isTrue);
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue