diff --git a/lib/sqlite3_async.dart b/lib/sqlite3_async.dart index ccf4093..7b1388c 100644 --- a/lib/sqlite3_async.dart +++ b/lib/sqlite3_async.dart @@ -8,6 +8,8 @@ import 'package:logging/logging.dart'; import 'package:logging_appenders/logging_appenders.dart'; import 'package:sqlite3/sqlite3.dart'; +export 'package:sqlite3/sqlite3.dart' show AllowedArgumentCount; + /// An opened sqlite3 database with async methods. class AsyncDatabase { static final Logger _log = Logger((AsyncDatabase).toString()); @@ -80,6 +82,24 @@ class AsyncDatabase { body: _StatementParams(sql, parameters)); } + // Register a custom function we can invoke from sql + Future createFunction({ + required String functionName, + required ScalarFunction function, + AllowedArgumentCount argumentCount = const AllowedArgumentCount.any(), + bool deterministic = false, + bool directOnly = true, + }) async { + return await _sendCommand("createFunction", + body: _CreateFunctionParams( + functionName: functionName, + function: function, + argumentCount: argumentCount, + deterministic: deterministic, + directOnly: directOnly, + )); + } + /// Closes this database and releases associated resources. Future dispose() async { await _sendCommand("dispose"); @@ -115,6 +135,9 @@ class AsyncDatabase { case "select": _selectSync(db!, cmd, ourReceivePort); break; + case "createFunction": + _createFunctionSync(db!, cmd, ourReceivePort); + break; case "dispose": _disposeSync(db!, cmd, ourReceivePort); break; @@ -172,6 +195,19 @@ class AsyncDatabase { body: version)); } + static void _createFunctionSync( + Database db, _AsyncDatabaseCommand cmd, ReceivePort ourReceivePort) { + _CreateFunctionParams params = cmd.body; + db.createFunction( + functionName: params.functionName, + function: params.function, + argumentCount: params.argumentCount, + deterministic: params.deterministic, + directOnly: params.directOnly, + ); + cmd.sendPort.send(_AsyncDatabaseCommand(cmd.type, ourReceivePort.sendPort)); + } + static Database _openSync( _AsyncDatabaseCommand cmd, ReceivePort ourReceivePort) { _OpenDatabaseParams params = cmd.body; @@ -226,3 +262,20 @@ class _StatementParams { const _StatementParams(this.sql, this.parameters); } + +@immutable +class _CreateFunctionParams { + final String functionName; + final ScalarFunction function; + final AllowedArgumentCount argumentCount; + final bool deterministic; + final bool directOnly; + + const _CreateFunctionParams({ + required this.functionName, + required this.function, + this.argumentCount = const AllowedArgumentCount.any(), + this.deterministic = false, + this.directOnly = true, + }); +} diff --git a/pubspec.yaml b/pubspec.yaml index e759e79..c5a68aa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,10 +12,10 @@ dependencies: sdk: flutter logging: ^1.2.0 logging_appenders: ^1.3.1 - sqlite3: ^2.4.4 + sqlite3: ^2.4.7 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^4.0.0 - sqlite3_flutter_libs: ^0.5.24 + sqlite3_flutter_libs: ^0.5.26 diff --git a/test/sqlite3_async_test.dart b/test/sqlite3_async_test.dart index f3fc413..c848168 100644 --- a/test/sqlite3_async_test.dart +++ b/test/sqlite3_async_test.dart @@ -82,6 +82,32 @@ void main() { expect(fullInsertionsTime, greaterThan(triggeredInsertionsTime)); await db.dispose(); }); + + test('create custom function', () async { + var db = await AsyncDatabase.open(testDbPath); + await _createTable(db); + + await db.createFunction( + functionName: 'get_int_from_row_name', + argumentCount: const AllowedArgumentCount(1), + function: (args) { + final [name as String] = args; + return int.parse(name.split('_')[1]); + }, + ); + + for (int i = 0; i < 100; i++) { + await _insertItem("a_$i", db); + } + + var rows = await db.select( + "SELECT name, get_int_from_row_name(name) as name_int FROM items"); + + expect( + rows.every((element) => element['name'] == 'a_${element['name_int']}'), + true); + await db.dispose(); + }); } Future _insertItem(String itemName, AsyncDatabase db) {