diff --git a/packages/console_test_app/bin/sqflite_ffi_simple_bin.dart b/packages/console_test_app/bin/sqflite_ffi_simple_bin.dart index fbec4a4c..611db3b5 100644 --- a/packages/console_test_app/bin/sqflite_ffi_simple_bin.dart +++ b/packages/console_test_app/bin/sqflite_ffi_simple_bin.dart @@ -3,6 +3,9 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart'; Future main(List arguments) async { sqfliteFfiInit(); var db = await databaseFactoryFfi.openDatabase(inMemoryDatabasePath); + var sqliteVersion = + (await db.rawQuery('select sqlite_version()')).first.values.first; + print('sqlite version: $sqliteVersion'); await db.setVersion(1); await db.close(); } diff --git a/packages/console_test_app/lib/console_test_app.dart b/packages/console_test_app/lib/console_test_app.dart deleted file mode 100644 index f64ad726..00000000 --- a/packages/console_test_app/lib/console_test_app.dart +++ /dev/null @@ -1,3 +0,0 @@ -int calculate() { - return 6 * 7; -} diff --git a/packages_web/sqflite_common_ffi_web_test/.gitignore b/packages_web/sqflite_common_ffi_web_test/.gitignore index 2006521e..8737eac9 100644 --- a/packages_web/sqflite_common_ffi_web_test/.gitignore +++ b/packages_web/sqflite_common_ffi_web_test/.gitignore @@ -9,3 +9,4 @@ build/ /web/sqlite3.wasm /test/sqflite_sw.js /test/sqlite3.wasm +/test/sqflite_sw_v1.js diff --git a/packages_web/sqflite_common_ffi_web_test/test/sqflite_ffi_web_test.dart b/packages_web/sqflite_common_ffi_web_test/test/sqflite_ffi_web_test.dart index 23c22444..a8c16e16 100644 --- a/packages_web/sqflite_common_ffi_web_test/test/sqflite_ffi_web_test.dart +++ b/packages_web/sqflite_common_ffi_web_test/test/sqflite_ffi_web_test.dart @@ -5,7 +5,9 @@ import 'package:sqflite_common_test/all_test.dart' as all; import 'package:sqflite_common_test/sqflite_test.dart'; import 'package:test/test.dart'; -var _factory = databaseFactoryFfiWeb; +var _factory = createDatabaseFactoryFfiWeb( + options: + SqfliteFfiWebOptions(sharedWorkerUri: Uri.parse('sqflite_sw_v1.js'))); class SqfliteFfiWebTestContext extends SqfliteLocalTestContext { SqfliteFfiWebTestContext() : super(databaseFactory: _factory); diff --git a/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests.dart b/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests.dart new file mode 100644 index 00000000..7899b8d1 --- /dev/null +++ b/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests.dart @@ -0,0 +1,5 @@ +import 'package:sqflite_common_ffi_web/src/setup/setup.dart'; + +Future main() async { + await setupBinaries(options: SetupOptions(verbose: true, dir: 'test')); +} diff --git a/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests_force.dart b/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests_force.dart new file mode 100644 index 00000000..8fb39f01 --- /dev/null +++ b/packages_web/sqflite_common_ffi_web_test/tool/setup_web_tests_force.dart @@ -0,0 +1,6 @@ +import 'package:sqflite_common_ffi_web/src/setup/setup.dart'; + +Future main() async { + await setupBinaries( + options: SetupOptions(verbose: true, dir: 'test', force: true)); +} diff --git a/sqflite/doc/opening_asset_db.md b/sqflite/doc/opening_asset_db.md index 7166de9d..2f8cad6f 100644 --- a/sqflite/doc/opening_asset_db.md +++ b/sqflite/doc/opening_asset_db.md @@ -51,7 +51,7 @@ if (!exists) { } catch (_) {} // Copy from asset - ByteData data = await rootBundle.load(join("assets", "example.db")); + ByteData data = await rootBundle.load(url.join("assets", "example.db")); List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); @@ -87,7 +87,7 @@ try { } catch (_) {} // Copy from asset -ByteData data = await rootBundle.load(join("assets", "example.db")); +ByteData data = await rootBundle.load(url.join("assets", "example.db")); List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); await new File(path).writeAsBytes(bytes, flush: true); diff --git a/sqflite/example/lib/exp_test_page.dart b/sqflite/example/lib/exp_test_page.dart index d67ce664..ca5087e7 100644 --- a/sqflite/example/lib/exp_test_page.dart +++ b/sqflite/example/lib/exp_test_page.dart @@ -345,10 +345,10 @@ class ExpTestPage extends TestPage { await deleteDatabase(path); // Copy from asset - final data = await rootBundle.load(join('assets', 'issue_64.db')); + final data = await rootBundle.load(url.join('assets', 'issue_64.db')); final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await writeFileAsBytes(path, bytes); + await databaseFactory.writeDatabaseBytes(path, bytes); // open the database final db = await openDatabase(path); diff --git a/sqflite/example/lib/open_test_page.dart b/sqflite/example/lib/open_test_page.dart index 8c0c57f6..89592fbc 100644 --- a/sqflite/example/lib/open_test_page.dart +++ b/sqflite/example/lib/open_test_page.dart @@ -345,12 +345,11 @@ class OpenTestPage extends TestPage { await createDirectory(path); } catch (_) {} - // Copy from asset - final data = await rootBundle.load(join('assets', 'example.db')); - final List bytes = +// Copy from asset to a database file. + final data = await rootBundle.load(url.join('assets', 'example.db')); + final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - // Write and flush the bytes written - await writeFileAsBytes(path, bytes, flush: true); + await databaseFactory.writeDatabaseBytes(path, bytes); // open the database final db = await openDatabase(path); @@ -643,7 +642,7 @@ class OpenTestPage extends TestPage { print('Creating new copy from asset'); // Copy from asset - final data = await rootBundle.load(join('assets', 'example.db')); + final data = await rootBundle.load(url.join('assets', 'example.db')); final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); await writeFileAsBytes(path, bytes); diff --git a/sqflite_common_ffi/CHANGELOG.md b/sqflite_common_ffi/CHANGELOG.md index e8a973a5..14aca600 100644 --- a/sqflite_common_ffi/CHANGELOG.md +++ b/sqflite_common_ffi/CHANGELOG.md @@ -1,6 +1,7 @@ -## 2.3.0 +## 2.3.0+2 * Dart 3 only. +* Bundle Windows sqlite3.dll 3.42.0 ## 2.2.5 diff --git a/sqflite_common_ffi/doc/updating_sqlite_dll.md b/sqflite_common_ffi/doc/updating_sqlite_dll.md new file mode 100644 index 00000000..f53fbdee --- /dev/null +++ b/sqflite_common_ffi/doc/updating_sqlite_dll.md @@ -0,0 +1,10 @@ +# Updating SQLite DLL + +This projects ships with a precompiled version of the SQLite DLL for Windows that allows running dart and flutter app +without additional setup. This version might not be the latest available at https://www.sqlite.org/download.html. + +You should grab and copy a fresh version of sqlite3.dll for your released app to bundle with your app. + +## Upgrading SQLite dll + +`sqlite3_info.dart` can be modified to download a new version of the SQLite DLL by running `tool/windows_setup.dart`. diff --git a/sqflite_common_ffi/lib/src/windows/setup.dart b/sqflite_common_ffi/lib/src/windows/setup.dart index 2a5ea399..1d2681c2 100644 --- a/sqflite_common_ffi/lib/src/windows/setup.dart +++ b/sqflite_common_ffi/lib/src/windows/setup.dart @@ -6,6 +6,9 @@ import 'package:sqflite_common_ffi/src/windows/setup_impl.dart'; import 'package:sqlite3/open.dart'; import 'package:sqlite3/sqlite3.dart'; +/// Local info file name. +const sqflite3InfoJsonFileName = 'sqflite3_info.json'; + /// Get the dll path from our package path. String packageGetSqlite3DllPath(String packagePath) { var path = join(packagePath, 'src', 'windows', 'sqlite3.dll'); diff --git a/sqflite_common_ffi/lib/src/windows/sqlite3.dll b/sqflite_common_ffi/lib/src/windows/sqlite3.dll index b9fce0d3..1544c2db 100644 Binary files a/sqflite_common_ffi/lib/src/windows/sqlite3.dll and b/sqflite_common_ffi/lib/src/windows/sqlite3.dll differ diff --git a/sqflite_common_ffi/lib/src/windows/sqlite3_info.dart b/sqflite_common_ffi/lib/src/windows/sqlite3_info.dart new file mode 100644 index 00000000..fa3fd7df --- /dev/null +++ b/sqflite_common_ffi/lib/src/windows/sqlite3_info.dart @@ -0,0 +1,48 @@ +/// Information about the bundled sqlite3.dll +/// https://www.sqlite.org/download.html +class Sqlite3DllInfo { + /// Version string. + final String version; + + /// Src zip. + final String srcZip; + + /// Sha3. + final String sha3; + + /// Sqflite3DllInfo. + Sqlite3DllInfo(this.version, this.srcZip, this.sha3); + + /// Sqflite3DllInfo from map. + factory Sqlite3DllInfo.fromMap(Map map) => Sqlite3DllInfo( + map['version']!.toString(), + map['srcZip']!.toString(), + map['sha3']!.toString()); + + /// To map. + Map toMap() => { + 'version': version, + 'srcZip': srcZip, + 'sha3': sha3, + }; + @override + String toString() => '$version $srcZip $sha3'; +} + +/// 3.38.2 info +var sqlite3_38_2Info = Sqlite3DllInfo( + '3.38.2', + 'https://www.sqlite.org/2022/sqlite-dll-win64-x64-3380200.zip', + '9f71eec9a2c7f12602eaa2af76bd7c052e540502ae7a89dac540e10962e2fa35'); + +// sqlite-dll-win64-x64-3420000.zip +// (1.16 MiB) 64-bit DLL (x64) for SQLite version 3.42.0. +// (SHA3-256: 2425efa95556793a20761dfdab0d3b56a52e61716e8bb65e6a0a3590d41c97c0) +/// 3.42.0 info +var sqlite3_42_0Info = Sqlite3DllInfo( + '3.42.0', + 'https://www.sqlite.org/2023/sqlite-dll-win64-x64-3420000.zip', + '2425efa95556793a20761dfdab0d3b56a52e61716e8bb65e6a0a3590d41c97c0'); + +/// Current info +var sqlite3Info = sqlite3_42_0Info; diff --git a/sqflite_common_ffi/lib/src/windows/sqlite3_info.json b/sqflite_common_ffi/lib/src/windows/sqlite3_info.json new file mode 100644 index 00000000..24a09eed --- /dev/null +++ b/sqflite_common_ffi/lib/src/windows/sqlite3_info.json @@ -0,0 +1 @@ +{"version":"3.42.0","srcZip":"https://www.sqlite.org/2023/sqlite-dll-win64-x64-3420000.zip","sha3":"2425efa95556793a20761dfdab0d3b56a52e61716e8bb65e6a0a3590d41c97c0"} \ No newline at end of file diff --git a/sqflite_common_ffi/lib/src/windows/sqlite3_version.md b/sqflite_common_ffi/lib/src/windows/sqlite3_version.md deleted file mode 100644 index 6594c7e5..00000000 --- a/sqflite_common_ffi/lib/src/windows/sqlite3_version.md +++ /dev/null @@ -1,3 +0,0 @@ -* v3.38.2 -* https://www.sqlite.org/2022/sqlite-dll-win64-x64-3380200.zip -* sha3: `9f71eec9a2c7f12602eaa2af76bd7c052e540502ae7a89dac540e10962e2fa35` \ No newline at end of file diff --git a/sqflite_common_ffi/pubspec.yaml b/sqflite_common_ffi/pubspec.yaml index 812d2cbc..4de6ffe4 100644 --- a/sqflite_common_ffi/pubspec.yaml +++ b/sqflite_common_ffi/pubspec.yaml @@ -1,7 +1,7 @@ name: sqflite_common_ffi homepage: https://github.com/tekartik/sqflite/tree/master/sqflite_common_ffi description: sqflite ffi based implementation, for desktop and units tests. -version: 2.3.0 +version: 2.3.0+2 funding: - https://github.com/sponsors/alextekartik diff --git a/sqflite_common_ffi/test/sqflite_ffi_windows_setup_test.dart b/sqflite_common_ffi/test/sqflite_ffi_windows_setup_test.dart index aacbeab3..a5984ac6 100644 --- a/sqflite_common_ffi/test/sqflite_ffi_windows_setup_test.dart +++ b/sqflite_common_ffi/test/sqflite_ffi_windows_setup_test.dart @@ -2,17 +2,13 @@ import 'dart:io'; import 'package:archive/archive_io.dart'; -import 'package:http/http.dart'; import 'package:path/path.dart'; import 'package:process_run/shell.dart'; import 'package:sqflite_common_ffi/src/windows/setup.dart'; +import 'package:sqflite_common_ffi/src/windows/sqlite3_info.dart'; import 'package:test/test.dart'; -var windowsSqliteVersion = '3.38.2'; -var windowsZipSrc = - 'https://www.sqlite.org/2022/sqlite-dll-win64-x64-3380200.zip'; -var windowsZipSha3 = - '9f71eec9a2c7f12602eaa2af76bd7c052e540502ae7a89dac540e10962e2fa35'; +import '../tool/windows_setup.dart'; Future computeSha3(String file, {String openssl = 'openssl'}) async { var line = (await run( @@ -45,21 +41,9 @@ Future windowsFindOpenssl() async { } void main() { - var localZip = join('.local', basename(windowsZipSrc)); - Future getZip() async { - if (!File(localZip).existsSync()) { - await Directory(dirname(localZip)).create(recursive: true); - try { - await File(localZip) - .writeAsBytes(await readBytes(Uri.parse(windowsZipSrc))); - } catch (e) { - stderr.writeln( - 'Fail to fetch sqlite.zip version $windowsSqliteVersion at $windowsZipSrc'); - return false; - } - } - return true; - } + var helper = Sqlite3DllSetupHelper(sqlite3Info); + var srcZip = sqlite3Info.srcZip; + var localZip = join('.local', basename(srcZip)); group('sqlite3.dll', () { test('sha3', () async { @@ -69,16 +53,16 @@ void main() { openssl = await windowsFindOpenssl(); } if (openssl != null) { - if (await getZip()) { + if (await helper.getZip()) { var computed = await computeSha3(localZip, openssl: openssl); - expect(computed, windowsZipSha3); + expect(computed, sqlite3Info.sha3); } } }); test('checkDll', () async { var dllPath = findWindowsDllPath()!; - if (await getZip()) { + if (await helper.getZip()) { final inputStream = InputFileStream(localZip); final archive = ZipDecoder().decodeBuffer(inputStream); extractArchiveToDisk(archive, dirname(localZip)); diff --git a/sqflite_common_ffi/test/sqflite_ffi_windows_test.dart b/sqflite_common_ffi/test/sqflite_ffi_windows_test.dart index e9b35980..d494050c 100644 --- a/sqflite_common_ffi/test/sqflite_ffi_windows_test.dart +++ b/sqflite_common_ffi/test/sqflite_ffi_windows_test.dart @@ -2,10 +2,9 @@ import 'dart:io'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; +import 'package:sqflite_common_ffi/src/windows/sqlite3_info.dart'; import 'package:test/test.dart'; -import 'sqflite_ffi_windows_setup_test.dart'; - void main() { if (Platform.isWindows) { sqfliteFfiInit(); @@ -15,7 +14,7 @@ void main() { final results = await db.rawQuery('select sqlite_version()'); var version = results.first.values.first; - expect(version, windowsSqliteVersion); + expect(version, sqlite3Info.version); }); }); } diff --git a/sqflite_common_ffi/tool/windows_setup.dart b/sqflite_common_ffi/tool/windows_setup.dart new file mode 100644 index 00000000..5685bafd --- /dev/null +++ b/sqflite_common_ffi/tool/windows_setup.dart @@ -0,0 +1,97 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:archive/archive_io.dart'; +import 'package:http/http.dart'; +import 'package:path/path.dart'; +import 'package:sqflite_common_ffi/src/windows/setup.dart'; +import 'package:sqflite_common_ffi/src/windows/setup_impl.dart'; +import 'package:sqflite_common_ffi/src/windows/sqlite3_info.dart'; + +class Sqlite3DllSetupHelper { + final Sqlite3DllInfo sqlite3Info; + // This tests does actually the actual install of the dll + + late var srcZip = sqlite3Info.srcZip; + late var localZip = join('.local', basename(srcZip)); + late var localExtractedZipDir = + join('.local', basenameWithoutExtension(srcZip)); + late var localExtractedJsonInfoFile = + join(localExtractedZipDir, sqflite3InfoJsonFileName); + var bundledDir = join('lib', dirname(packageGetSqlite3DllPath('.'))); + late var bundledJsonInfoFilePath = join(bundledDir, 'sqlite3_info.json'); + late var bundledSqlite3DllFilePath = join(bundledDir, 'sqlite3.dll'); + late var localInfoZip = join('.local', basename(srcZip)); + + Sqlite3DllSetupHelper(this.sqlite3Info); + + /// For tools and test only. not exported. + /// Returns null on failure, don't care about the failure... + Future readBundleInfo() async { + try { + var map = pathGetJson(bundledJsonInfoFilePath); + var sqlite3Info = Sqlite3DllInfo.fromMap(map); + print('sqlite3Info $sqlite3Info'); + } catch (_) {} + return null; + } + + Future getZip() async { + if (!File(localZip).existsSync()) { + print('Downloading sqlite3 $sqlite3Info'); + await Directory(dirname(localZip)).create(recursive: true); + try { + await File(localZip).writeAsBytes(await readBytes(Uri.parse(srcZip))); + } catch (e) { + stderr.writeln( + 'Fail to fetch sqlite.zip version $sqlite3_38_2Info at $srcZip'); + return false; + } + } + return true; + } + + Future extractZip() async { + var jsonInfo = File(localExtractedJsonInfoFile); + if (!jsonInfo.existsSync()) { + // Extract the zip + print('Extracting $localZip to $localExtractedZipDir'); + final inputStream = InputFileStream(localZip); + final archive = ZipDecoder().decodeBuffer(inputStream); + extractArchiveToDisk(archive, localExtractedZipDir); + await jsonInfo.writeAsString(jsonEncode(sqlite3Info.toMap())); + } + } + + Future copyToBundle() async { + var srcFile = join(localExtractedZipDir, 'sqlite3.dll'); + var dstFile = bundledSqlite3DllFilePath; + print('Copying $srcZip to $dstFile'); + //await File(dstFile).delete(recursive: true); + await File(srcFile).copy(dstFile); + await File(bundledJsonInfoFilePath) + .writeAsString(jsonEncode(sqlite3Info.toMap())); + //await File() + } +} + +/// This tool is actually ran on linux to download install the updated dll +Future main() async { + await setupSqliteDll(); +} + +Future setupSqliteDll() async { + // Tested only on linux for now. + if (Platform.isLinux) { + var helper = Sqlite3DllSetupHelper(sqlite3Info); + var info = await helper.readBundleInfo(); + if (info?.version != sqlite3Info.version) { + await helper.getZip(); + await helper.extractZip(); + await helper.copyToBundle(); + } else { + print('sqlite3 $sqlite3Info already up to date'); + } + } else { + stderr.writeln('To run on linux!'); + } +} diff --git a/sqflite_common_test/lib/exp_test.dart b/sqflite_common_test/lib/exp_test.dart index 780e2263..ab997681 100644 --- a/sqflite_common_test/lib/exp_test.dart +++ b/sqflite_common_test/lib/exp_test.dart @@ -331,52 +331,6 @@ void run(SqfliteTestContext context) { await db.close(); }); - /* - no bundle support - - test('Issue#64', () async { - // await Sqflite.devSetDebugModeOn(true); - var path =await context.initDeleteDb('issue_64.db'); - - // delete existing if any - await deleteDatabase(path); - - // Copy from asset - var data = await rootBundle.load(join('assets', 'issue_64.db')); - var bytes = - data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - await new File(path).writeAsBytes(bytes); - - // open the database - var db =await factory.openDatabase(path); - - var result = await db.query('recordings', - columns: ['id', 'content', 'file', 'speaker', 'reference']); - print('result1: $result'); - expect(result.length, 2); - - // This one does not work - // to investigate - result = await db.query('recordings', - columns: ['id', 'content', 'file', 'speaker', 'reference'], - where: 'speaker = ?', - whereArgs: [1]); - - print('result2: $result'); - expect(result.length, 2); - - result = await db.query( - 'recordings', - columns: ['id', 'content', 'file', 'speaker', 'reference'], - where: 'speaker = 1', - ); - print('result3: $result'); - expect(result.length, 2); - - await db.close(); - }); - */ - test('sql dump file', () async { // await Sqflite.devSetDebugModeOn(true); diff --git a/sqflite_common_test/lib/sqflite_protocol_test.dart b/sqflite_common_test/lib/sqflite_protocol_test.dart index f8dfb936..94dddd2c 100644 --- a/sqflite_common_test/lib/sqflite_protocol_test.dart +++ b/sqflite_common_test/lib/sqflite_protocol_test.dart @@ -56,17 +56,20 @@ void run(SqfliteTestContext? context) { await scenario.factory.databaseExists(inMemoryDatabasePath); scenario.end(); }); - test('delete', () async { - final scenario = wrapStartScenario(factory, [ - [ - 'deleteDatabase', - {'path': ':memory:'}, - null - ] - ]); - await scenario.factory.deleteDatabase(inMemoryDatabasePath); - scenario.end(); - }); + test( + 'delete', + () async { + final scenario = wrapStartScenario(factory, [ + [ + 'deleteDatabase', + {'path': ':memory:'}, + null + ] + ]); + await scenario.factory.deleteDatabase(inMemoryDatabasePath); + scenario.end(); + }, + ); test('execute', () async { final scenario = wrapStartScenario(factory, [ protocolOpenStep,