diff --git a/Cargo.lock b/Cargo.lock index 4820f68..1eee862 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,7 +205,7 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "powersync_core" -version = "0.3.11" +version = "0.3.12" dependencies = [ "bytes", "num-derive", @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "powersync_loadable" -version = "0.3.11" +version = "0.3.12" dependencies = [ "powersync_core", "sqlite_nostd", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "powersync_sqlite" -version = "0.3.11" +version = "0.3.12" dependencies = [ "cc", "powersync_core", @@ -331,7 +331,7 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "sqlite3" -version = "0.3.11" +version = "0.3.12" dependencies = [ "cc", ] diff --git a/Cargo.toml b/Cargo.toml index 310a704..e866fed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ inherits = "release" inherits = "wasm" [workspace.package] -version = "0.3.11" +version = "0.3.12" edition = "2021" authors = ["JourneyApps"] keywords = ["sqlite", "powersync"] diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 6877e75..2f45226 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -6,7 +6,7 @@ plugins { } group = "co.powersync" -version = "0.3.11" +version = "0.3.12" description = "PowerSync Core SQLite Extension" repositories { diff --git a/android/src/prefab/prefab.json b/android/src/prefab/prefab.json index 47d5ba6..9406220 100644 --- a/android/src/prefab/prefab.json +++ b/android/src/prefab/prefab.json @@ -2,5 +2,5 @@ "name": "powersync_sqlite_core", "schema_version": 2, "dependencies": [], - "version": "0.3.11" + "version": "0.3.12" } diff --git a/crates/core/src/migrations.rs b/crates/core/src/migrations.rs index 0bfbb03..32b8eef 100644 --- a/crates/core/src/migrations.rs +++ b/crates/core/src/migrations.rs @@ -333,5 +333,26 @@ json_object('sql', 'DELETE FROM ps_migration WHERE id >= 7') local_db.exec_safe(&stmt).into_db_result(local_db)?; } + if current_version < 8 && target_version >= 8 { + let stmt = "\ +ALTER TABLE ps_sync_state RENAME TO ps_sync_state_old; +CREATE TABLE ps_sync_state ( + priority INTEGER NOT NULL PRIMARY KEY, + last_synced_at TEXT NOT NULL +) STRICT; +INSERT INTO ps_sync_state (priority, last_synced_at) + SELECT priority, MAX(last_synced_at) FROM ps_sync_state_old GROUP BY priority; +DROP TABLE ps_sync_state_old; +INSERT INTO ps_migration(id, down_migrations) VALUES(8, json_array( +json_object('sql', 'ALTER TABLE ps_sync_state RENAME TO ps_sync_state_new'), +json_object('sql', 'CREATE TABLE ps_sync_state (\n priority INTEGER NOT NULL,\n last_synced_at TEXT NOT NULL\n) STRICT'), +json_object('sql', 'INSERT INTO ps_sync_state SELECT * FROM ps_sync_state_new'), +json_object('sql', 'DROP TABLE ps_sync_state_new'), +json_object('sql', 'DELETE FROM ps_migration WHERE id >= 8') +)); +"; + local_db.exec_safe(&stmt).into_db_result(local_db)?; + } + Ok(()) } diff --git a/crates/core/src/view_admin.rs b/crates/core/src/view_admin.rs index a476154..20db27d 100644 --- a/crates/core/src/view_admin.rs +++ b/crates/core/src/view_admin.rs @@ -120,7 +120,7 @@ fn powersync_init_impl( setup_internal_views(local_db)?; - powersync_migrate(ctx, 7)?; + powersync_migrate(ctx, 8)?; Ok(String::from("")) } diff --git a/dart/pubspec.lock b/dart/pubspec.lock index bdc6d4a..67324a5 100644 --- a/dart/pubspec.lock +++ b/dart/pubspec.lock @@ -5,23 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "03f6da266a27a4538a69295ec142cb5717d7d4e5727b84658b63e1e1509bac9c" + sha256: dc27559385e905ad30838356c5f5d574014ba39872d732111cd07ac0beff4c57 url: "https://pub.dev" source: hosted - version: "79.0.0" - _macros: - dependency: transitive - description: dart - source: sdk - version: "0.3.3" + version: "80.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: c9040fc56483c22a5e04a9f6a251313118b1a3c42423770623128fa484115643 + sha256: "192d1c5b944e7e53b24b5586db760db934b177d4147c42fbca8c8c5f1eb8d11e" url: "https://pub.dev" source: hosted - version: "7.2.0" + version: "7.3.0" args: dependency: transitive description: @@ -34,10 +29,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" boolean_selector: dependency: transitive description: @@ -46,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" collection: dependency: transitive description: @@ -78,16 +81,24 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + fake_async: + dependency: "direct dev" + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" ffi: dependency: transitive description: name: ffi - sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.4" file: - dependency: transitive + dependency: "direct dev" description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 @@ -138,10 +149,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.2" logging: dependency: transitive description: @@ -150,14 +161,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - macros: - dependency: transitive - description: - name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" - url: "https://pub.dev" - source: hosted - version: "0.1.3-main.0" matcher: dependency: transitive description: @@ -250,10 +253,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.0" source_map_stack_trace: dependency: transitive description: @@ -282,10 +285,18 @@ packages: dependency: "direct main" description: name: sqlite3 - sha256: "35d3726fe18ab1463403a5cc8d97dbc81f2a0b08082e8173851363fcc97b6627" + sha256: "32b632dda27d664f85520093ed6f735ae5c49b5b75345afb8b19411bc59bb53d" url: "https://pub.dev" source: hosted - version: "2.7.2" + version: "2.7.4" + sqlite3_test: + dependency: "direct dev" + description: + name: sqlite3_test + sha256: "0b6f76541385cbe0cebf9454854f78dc9aa18b8cb512d8e597e63385e61d4f45" + url: "https://pub.dev" + source: hosted + version: "0.1.1" stack_trace: dependency: transitive description: @@ -322,10 +333,10 @@ packages: dependency: "direct dev" description: name: test - sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d" + sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e" url: "https://pub.dev" source: hosted - version: "1.25.14" + version: "1.25.15" test_api: dependency: transitive description: @@ -370,10 +381,10 @@ packages: dependency: transitive description: name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" web_socket: dependency: transitive description: @@ -407,4 +418,4 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.7.0 <4.0.0" diff --git a/dart/pubspec.yaml b/dart/pubspec.yaml index f2cd1a3..1867319 100644 --- a/dart/pubspec.yaml +++ b/dart/pubspec.yaml @@ -8,3 +8,6 @@ dependencies: sqlite3: ^2.4.5 dev_dependencies: test: ^1.25.0 + file: ^7.0.1 + sqlite3_test: ^0.1.1 + fake_async: ^1.3.3 diff --git a/dart/test/migration_test.dart b/dart/test/migration_test.dart index b50af97..a149ada 100644 --- a/dart/test/migration_test.dart +++ b/dart/test/migration_test.dart @@ -246,5 +246,34 @@ ${fixtures.schema5.trim()} end'''; expect(schema, equals(expected)); }); + + test('schema 7 -> 8 migrates last_synced_at data', () { + db.execute(fixtures.expectedState[7]!); + + for (var i = 0; i < 10; i++) { + db.execute( + 'INSERT OR REPLACE INTO ps_sync_state (priority, last_synced_at) VALUES (?, ?);', + [2147483647, '2025-03-05 14:58:${i.toString().padLeft(2, '0')}'], + ); + + db.execute( + 'INSERT OR REPLACE INTO ps_sync_state (priority, last_synced_at) VALUES (?, ?);', + [3, '2025-03-05 13:58:${i.toString().padLeft(2, '0')}'], + ); + } + + db.execute('SELECT powersync_test_migration(8);'); + + expect(db.select('SELECT * FROM ps_sync_state ORDER BY priority'), [ + { + 'priority': 3, + 'last_synced_at': '2025-03-05 13:58:09', + }, + { + 'priority': 2147483647, + 'last_synced_at': '2025-03-05 14:58:09', + } + ]); + }); }); } diff --git a/dart/test/sync_test.dart b/dart/test/sync_test.dart index d8015ad..0e1bd17 100644 --- a/dart/test/sync_test.dart +++ b/dart/test/sync_test.dart @@ -1,16 +1,28 @@ import 'dart:convert'; +import 'package:fake_async/fake_async.dart'; +import 'package:file/local.dart'; import 'package:sqlite3/common.dart'; +import 'package:sqlite3/sqlite3.dart'; +import 'package:sqlite3_test/sqlite3_test.dart'; import 'package:test/test.dart'; import 'utils/native_test_utils.dart'; void main() { + final vfs = TestSqliteFileSystem(fs: const LocalFileSystem()); + + setUpAll(() { + loadExtension(); + sqlite3.registerVirtualFileSystem(vfs, makeDefault: false); + }); + tearDownAll(() => sqlite3.unregisterVirtualFileSystem(vfs)); + group('sync tests', () { late CommonDatabase db; setUp(() async { - db = openTestDatabase() + db = openTestDatabase(vfs) ..select('select powersync_init();') ..select('select powersync_replace_schema(?)', [json.encode(_schema)]); }); @@ -187,6 +199,38 @@ void main() { } }); + test('can sync multiple times', () { + fakeAsync((controller) { + for (var i = 0; i < 10; i++) { + for (var prio in const [1, 2, 3, null]) { + pushCheckpointComplete('1', null, [], priority: prio); + + // Make sure there's only a single row in last_synced_at + expect( + db.select( + "SELECT datetime(last_synced_at, 'localtime') AS last_synced_at FROM ps_sync_state WHERE priority = ?", + [prio ?? 2147483647]), + [ + {'last_synced_at': '2025-03-01 ${10 + i}:00:00'} + ], + ); + + if (prio == null) { + expect( + db.select( + "SELECT datetime(powersync_last_synced_at(), 'localtime') AS last_synced_at"), + [ + {'last_synced_at': '2025-03-01 ${10 + i}:00:00'} + ], + ); + } + } + + controller.elapse(const Duration(hours: 1)); + } + }, initialTime: DateTime(2025, 3, 1, 10)); + }); + test('clearing database clears sync status', () { pushSyncData('prio1', '1', 'row-0', 'PUT', {'col': 'hi'}); diff --git a/dart/test/utils/migration_fixtures.dart b/dart/test/utils/migration_fixtures.dart index 3cc0e2a..a76be78 100644 --- a/dart/test/utils/migration_fixtures.dart +++ b/dart/test/utils/migration_fixtures.dart @@ -1,5 +1,5 @@ /// The current database version -const databaseVersion = 7; +const databaseVersion = 8; /// This is the base database state that we expect at various schema versions. /// Generated by loading the specific library version, and exporting the schema. @@ -216,6 +216,51 @@ const expectedState = { ;INSERT INTO ps_migration(id, down_migrations) VALUES(5, '[{"sql":"SELECT powersync_drop_view(view.name)\n FROM sqlite_master view\n WHERE view.type = ''view''\n AND view.sql GLOB ''*-- powersync-auto-generated''"},{"sql":"ALTER TABLE ps_buckets RENAME TO ps_buckets_5"},{"sql":"ALTER TABLE ps_oplog RENAME TO ps_oplog_5"},{"sql":"CREATE TABLE ps_buckets(\n name TEXT PRIMARY KEY,\n last_applied_op INTEGER NOT NULL DEFAULT 0,\n last_op INTEGER NOT NULL DEFAULT 0,\n target_op INTEGER NOT NULL DEFAULT 0,\n add_checksum INTEGER NOT NULL DEFAULT 0,\n pending_delete INTEGER NOT NULL DEFAULT 0\n, op_checksum INTEGER NOT NULL DEFAULT 0, remove_operations INTEGER NOT NULL DEFAULT 0)"},{"sql":"INSERT INTO ps_buckets(name, last_applied_op, last_op, target_op, add_checksum, op_checksum, pending_delete)\n SELECT name, last_applied_op, last_op, target_op, add_checksum, op_checksum, pending_delete FROM ps_buckets_5"},{"sql":"CREATE TABLE ps_oplog(\n bucket TEXT NOT NULL,\n op_id INTEGER NOT NULL,\n op INTEGER NOT NULL,\n row_type TEXT,\n row_id TEXT,\n key TEXT,\n data TEXT,\n hash INTEGER NOT NULL,\n superseded INTEGER NOT NULL)"},{"sql":"CREATE INDEX ps_oplog_by_row ON ps_oplog (row_type, row_id) WHERE superseded = 0"},{"sql":"CREATE INDEX ps_oplog_by_opid ON ps_oplog (bucket, op_id)"},{"sql":"CREATE INDEX ps_oplog_by_key ON ps_oplog (bucket, key) WHERE superseded = 0"},{"sql":"INSERT INTO ps_oplog(bucket, op_id, op, row_type, row_id, key, data, hash, superseded)\n SELECT ps_buckets_5.name, oplog.op_id, 3, oplog.row_type, oplog.row_id, oplog.key, oplog.data, oplog.hash, 0\n FROM ps_oplog_5 oplog\n JOIN ps_buckets_5\n ON ps_buckets_5.id = oplog.bucket"},{"sql":"DROP TABLE ps_oplog_5"},{"sql":"DROP TABLE ps_buckets_5"},{"sql":"INSERT INTO ps_oplog(bucket, op_id, op, row_type, row_id, hash, superseded)\n SELECT ''$local'', 1, 4, r.row_type, r.row_id, 0, 0\n FROM ps_updated_rows r"},{"sql":"INSERT OR REPLACE INTO ps_buckets(name, pending_delete, last_op, target_op) VALUES(''$local'', 1, 0, 9223372036854775807)"},{"sql":"DROP TABLE ps_updated_rows"},{"sql":"DELETE FROM ps_migration WHERE id >= 5"}]') ;INSERT INTO ps_migration(id, down_migrations) VALUES(6, '[{"sql":"DELETE FROM ps_migration WHERE id >= 6"}]') ;INSERT INTO ps_migration(id, down_migrations) VALUES(7, '[{"sql":"INSERT OR REPLACE INTO ps_kv(key, value) SELECT ''last_synced_at'', last_synced_at FROM ps_sync_state WHERE priority = 2147483647"},{"sql":"DROP TABLE ps_sync_state"},{"sql":"DELETE FROM ps_migration WHERE id >= 7"}]') +''', + 8: r''' +;CREATE TABLE ps_buckets( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + last_applied_op INTEGER NOT NULL DEFAULT 0, + last_op INTEGER NOT NULL DEFAULT 0, + target_op INTEGER NOT NULL DEFAULT 0, + add_checksum INTEGER NOT NULL DEFAULT 0, + op_checksum INTEGER NOT NULL DEFAULT 0, + pending_delete INTEGER NOT NULL DEFAULT 0 +) STRICT +;CREATE TABLE ps_crud (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT, tx_id INTEGER) +;CREATE TABLE ps_kv(key TEXT PRIMARY KEY NOT NULL, value BLOB) +;CREATE TABLE ps_migration(id INTEGER PRIMARY KEY, down_migrations TEXT) +;CREATE TABLE ps_oplog( + bucket INTEGER NOT NULL, + op_id INTEGER NOT NULL, + row_type TEXT, + row_id TEXT, + key TEXT, + data TEXT, + hash INTEGER NOT NULL) STRICT +;CREATE TABLE ps_sync_state ( + priority INTEGER NOT NULL PRIMARY KEY, + last_synced_at TEXT NOT NULL +) STRICT +;CREATE TABLE ps_tx(id INTEGER PRIMARY KEY NOT NULL, current_tx INTEGER, next_tx INTEGER) +;CREATE TABLE ps_untyped(type TEXT NOT NULL, id TEXT NOT NULL, data TEXT, PRIMARY KEY (type, id)) +;CREATE TABLE ps_updated_rows( + row_type TEXT, + row_id TEXT, + PRIMARY KEY(row_type, row_id)) STRICT, WITHOUT ROWID +;CREATE UNIQUE INDEX ps_buckets_name ON ps_buckets (name) +;CREATE INDEX ps_oplog_key ON ps_oplog (bucket, key) +;CREATE INDEX ps_oplog_opid ON ps_oplog (bucket, op_id) +;CREATE INDEX ps_oplog_row ON ps_oplog (row_type, row_id) +;INSERT INTO ps_migration(id, down_migrations) VALUES(1, null) +;INSERT INTO ps_migration(id, down_migrations) VALUES(2, '[{"sql":"DELETE FROM ps_migration WHERE id >= 2","params":[]},{"sql":"DROP TABLE ps_tx","params":[]},{"sql":"ALTER TABLE ps_crud DROP COLUMN tx_id","params":[]}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(3, '[{"sql":"DELETE FROM ps_migration WHERE id >= 3"},{"sql":"DROP TABLE ps_kv"}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(4, '[{"sql":"DELETE FROM ps_migration WHERE id >= 4"},{"sql":"ALTER TABLE ps_buckets DROP COLUMN op_checksum"},{"sql":"ALTER TABLE ps_buckets DROP COLUMN remove_operations"}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(5, '[{"sql":"SELECT powersync_drop_view(view.name)\n FROM sqlite_master view\n WHERE view.type = ''view''\n AND view.sql GLOB ''*-- powersync-auto-generated''"},{"sql":"ALTER TABLE ps_buckets RENAME TO ps_buckets_5"},{"sql":"ALTER TABLE ps_oplog RENAME TO ps_oplog_5"},{"sql":"CREATE TABLE ps_buckets(\n name TEXT PRIMARY KEY,\n last_applied_op INTEGER NOT NULL DEFAULT 0,\n last_op INTEGER NOT NULL DEFAULT 0,\n target_op INTEGER NOT NULL DEFAULT 0,\n add_checksum INTEGER NOT NULL DEFAULT 0,\n pending_delete INTEGER NOT NULL DEFAULT 0\n, op_checksum INTEGER NOT NULL DEFAULT 0, remove_operations INTEGER NOT NULL DEFAULT 0)"},{"sql":"INSERT INTO ps_buckets(name, last_applied_op, last_op, target_op, add_checksum, op_checksum, pending_delete)\n SELECT name, last_applied_op, last_op, target_op, add_checksum, op_checksum, pending_delete FROM ps_buckets_5"},{"sql":"CREATE TABLE ps_oplog(\n bucket TEXT NOT NULL,\n op_id INTEGER NOT NULL,\n op INTEGER NOT NULL,\n row_type TEXT,\n row_id TEXT,\n key TEXT,\n data TEXT,\n hash INTEGER NOT NULL,\n superseded INTEGER NOT NULL)"},{"sql":"CREATE INDEX ps_oplog_by_row ON ps_oplog (row_type, row_id) WHERE superseded = 0"},{"sql":"CREATE INDEX ps_oplog_by_opid ON ps_oplog (bucket, op_id)"},{"sql":"CREATE INDEX ps_oplog_by_key ON ps_oplog (bucket, key) WHERE superseded = 0"},{"sql":"INSERT INTO ps_oplog(bucket, op_id, op, row_type, row_id, key, data, hash, superseded)\n SELECT ps_buckets_5.name, oplog.op_id, 3, oplog.row_type, oplog.row_id, oplog.key, oplog.data, oplog.hash, 0\n FROM ps_oplog_5 oplog\n JOIN ps_buckets_5\n ON ps_buckets_5.id = oplog.bucket"},{"sql":"DROP TABLE ps_oplog_5"},{"sql":"DROP TABLE ps_buckets_5"},{"sql":"INSERT INTO ps_oplog(bucket, op_id, op, row_type, row_id, hash, superseded)\n SELECT ''$local'', 1, 4, r.row_type, r.row_id, 0, 0\n FROM ps_updated_rows r"},{"sql":"INSERT OR REPLACE INTO ps_buckets(name, pending_delete, last_op, target_op) VALUES(''$local'', 1, 0, 9223372036854775807)"},{"sql":"DROP TABLE ps_updated_rows"},{"sql":"DELETE FROM ps_migration WHERE id >= 5"}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(6, '[{"sql":"DELETE FROM ps_migration WHERE id >= 6"}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(7, '[{"sql":"INSERT OR REPLACE INTO ps_kv(key, value) SELECT ''last_synced_at'', last_synced_at FROM ps_sync_state WHERE priority = 2147483647"},{"sql":"DROP TABLE ps_sync_state"},{"sql":"DELETE FROM ps_migration WHERE id >= 7"}]') +;INSERT INTO ps_migration(id, down_migrations) VALUES(8, '[{"sql":"ALTER TABLE ps_sync_state RENAME TO ps_sync_state_new"},{"sql":"CREATE TABLE ps_sync_state (\n priority INTEGER NOT NULL,\n last_synced_at TEXT NOT NULL\n) STRICT"},{"sql":"INSERT INTO ps_sync_state SELECT * FROM ps_sync_state_new"},{"sql":"DROP TABLE ps_sync_state_new"},{"sql":"DELETE FROM ps_migration WHERE id >= 8"}]') ''', }; @@ -285,6 +330,17 @@ const data1 = { (2, 3, 'lists', 'l1', '', '{}', 3) ;INSERT INTO ps_updated_rows(row_type, row_id) VALUES ('lists', 'l2') +''', + 8: r''' +;INSERT INTO ps_buckets(id, name, last_applied_op, last_op, target_op, add_checksum, op_checksum, pending_delete) VALUES + (1, 'b1', 0, 0, 0, 0, 120, 0), + (2, 'b2', 0, 0, 0, 1005, 3, 0) +;INSERT INTO ps_oplog(bucket, op_id, row_type, row_id, key, data, hash) VALUES + (1, 1, 'todos', 't1', '', '{}', 100), + (1, 2, 'todos', 't2', '', '{}', 20), + (2, 3, 'lists', 'l1', '', '{}', 3) +;INSERT INTO ps_updated_rows(row_type, row_id) VALUES + ('lists', 'l2') ''' }; @@ -326,7 +382,8 @@ final dataDown1 = { ('b2', 3, 3, 'lists', 'l1', '', '{}', 3, 0) ''', 5: data1[5]!, - 6: data1[5]! + 6: data1[5]!, + 7: data1[5]!, }; final finalData1 = data1[databaseVersion]!; diff --git a/dart/test/utils/native_test_utils.dart b/dart/test/utils/native_test_utils.dart index bc8b637..49733c7 100644 --- a/dart/test/utils/native_test_utils.dart +++ b/dart/test/utils/native_test_utils.dart @@ -8,18 +8,33 @@ import 'package:path/path.dart' as p; const defaultSqlitePath = 'libsqlite3.so.0'; const libPath = '../target/debug'; +var didLoadExtension = false; -CommonDatabase openTestDatabase() { +void applyOpenOverride() { sqlite_open.open.overrideFor(sqlite_open.OperatingSystem.linux, () { return DynamicLibrary.open('libsqlite3.so.0'); }); sqlite_open.open.overrideFor(sqlite_open.OperatingSystem.macOS, () { return DynamicLibrary.open('libsqlite3.dylib'); }); +} + +CommonDatabase openTestDatabase([VirtualFileSystem? vfs]) { + applyOpenOverride(); + if (!didLoadExtension) { + loadExtension(); + } + + return sqlite3.open(':memory:', vfs: vfs?.name); +} + +void loadExtension() { + applyOpenOverride(); + var lib = DynamicLibrary.open(getLibraryForPlatform(path: libPath)); var extension = SqliteExtension.inLibrary(lib, 'sqlite3_powersync_init'); sqlite3.ensureExtensionLoaded(extension); - return sqlite3.open(':memory:'); + didLoadExtension = true; } String getLibraryForPlatform({String? path = "."}) { diff --git a/powersync-sqlite-core.podspec b/powersync-sqlite-core.podspec index 5ffbdab..85ce01a 100644 --- a/powersync-sqlite-core.podspec +++ b/powersync-sqlite-core.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'powersync-sqlite-core' - s.version = '0.3.11' + s.version = '0.3.12' s.summary = 'PowerSync SQLite Extension' s.description = <<-DESC PowerSync extension for SQLite. diff --git a/tool/build_xcframework.sh b/tool/build_xcframework.sh index 5ca6ca5..68d3173 100755 --- a/tool/build_xcframework.sh +++ b/tool/build_xcframework.sh @@ -28,9 +28,9 @@ function createXcframework() { MinimumOSVersion 11.0 CFBundleVersion - 0.3.11 + 0.3.12 CFBundleShortVersionString - 0.3.11 + 0.3.12 EOF