diff --git a/.changeset/angry-foxes-hammer.md b/.changeset/angry-foxes-hammer.md new file mode 100644 index 000000000..f8e85589c --- /dev/null +++ b/.changeset/angry-foxes-hammer.md @@ -0,0 +1,5 @@ +--- +'@journeyapps/powersync-sdk-react-native': patch +--- + +Fixed watched queries from updating before writes have been commited on the write connection. diff --git a/.changeset/fast-chicken-vanish.md b/.changeset/fast-chicken-vanish.md new file mode 100644 index 000000000..7db781341 --- /dev/null +++ b/.changeset/fast-chicken-vanish.md @@ -0,0 +1,6 @@ +--- +'@journeyapps/powersync-sdk-react-native': minor +'@journeyapps/powersync-sdk-common': minor +--- + +Added the ability to receive batched table updates from DB adapters. diff --git a/.gitmodules b/.gitmodules index ad7f8869a..1b6f520e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "apps/supabase-todolist"] path = apps/supabase-todolist - url = git@github.com:journeyapps/powersync-supabase-react-native-todolist-demo.git + url = git@github.com:powersync-ja/powersync-supabase-react-native-todolist-demo.git diff --git a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts index e46e0232d..3257e9b98 100644 --- a/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts +++ b/packages/powersync-sdk-common/src/client/AbstractPowerSyncDatabase.ts @@ -1,7 +1,7 @@ import _ from 'lodash'; import { Mutex } from 'async-mutex'; import Logger, { ILogger } from 'js-logger'; -import { DBAdapter, QueryResult, Transaction } from '../db/DBAdapter'; +import { DBAdapter, QueryResult, Transaction, isBatchedUpdateNotification } from '../db/DBAdapter'; import { Schema } from '../db/schema/Schema'; import { SyncStatus } from '../db/crud/SyncStatus'; import { UploadQueueStats } from '../db/crud/UploadQueueStatus'; @@ -511,15 +511,21 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver { - const { table } = update; const { rawTableNames } = options; - if (!rawTableNames && !table.match(POWERSYNC_TABLE_MATCH)) { + const tables = isBatchedUpdateNotification(update) ? update.tables : [update.table]; + + const filteredTables = rawTableNames ? tables : tables.filter((t) => !!t.match(POWERSYNC_TABLE_MATCH)); + if (!filteredTables.length) { return; } - const tableName = rawTableNames ? table : table.replace(POWERSYNC_TABLE_MATCH, ''); - throttledTableUpdates.push(tableName); + // Remove any PowerSync table prefixes if necessary + const mappedTableNames = rawTableNames + ? filteredTables + : filteredTables.map((t) => t.replace(POWERSYNC_TABLE_MATCH, '')); + + throttledTableUpdates.push(...mappedTableNames); flushTableUpdates(); } diff --git a/packages/powersync-sdk-common/src/db/DBAdapter.ts b/packages/powersync-sdk-common/src/db/DBAdapter.ts index 0be4e5d3f..26aba1980 100644 --- a/packages/powersync-sdk-common/src/db/DBAdapter.ts +++ b/packages/powersync-sdk-common/src/db/DBAdapter.ts @@ -5,6 +5,11 @@ import { BaseListener, BaseObserverInterface } from '../utils/BaseObserver'; +/** + * TODO most of these types could be exported to a common `types` package + * which is used by the DB adapter libraries as well. + */ + /** * Object returned by SQL Query executions { * insertId: Represent the auto-generated row id if applicable @@ -54,14 +59,28 @@ export enum RowUpdateType { SQLITE_DELETE = 9, SQLITE_UPDATE = 23 } -export interface UpdateNotification { +export interface TableUpdateOperation { opType: RowUpdateType; - table: string; rowId: number; } +export interface UpdateNotification extends TableUpdateOperation { + table: string; +} + +export interface BatchedUpdateNotification { + rawUpdates: UpdateNotification[]; + tables: string[]; + groupedUpdates: Record; +} export interface DBAdapterListener extends BaseListener { - tablesUpdated: (updateNotification: UpdateNotification) => void; + /** + * Listener for table updates. + * Allows for single table updates in order to maintain API compatibility + * without the need for a major version bump + * The DB adapter can also batch update notifications if supported. + */ + tablesUpdated: (updateNotification: BatchedUpdateNotification | UpdateNotification) => void; } export interface DBLockOptions { @@ -77,3 +96,9 @@ export interface DBAdapter extends BaseObserverInterface, DBG writeLock: (fn: (tx: LockContext) => Promise, options?: DBLockOptions) => Promise; writeTransaction: (fn: (tx: Transaction) => Promise, options?: DBLockOptions) => Promise; } + +export function isBatchedUpdateNotification( + update: BatchedUpdateNotification | UpdateNotification +): update is BatchedUpdateNotification { + return 'tables' in update; +} diff --git a/packages/powersync-sdk-react-native/package.json b/packages/powersync-sdk-react-native/package.json index 257d15bcb..24d9b0327 100644 --- a/packages/powersync-sdk-react-native/package.json +++ b/packages/powersync-sdk-react-native/package.json @@ -27,7 +27,7 @@ }, "homepage": "https://docs.powersync.co/", "peerDependencies": { - "@journeyapps/react-native-quick-sqlite": "^1.0.0", + "@journeyapps/react-native-quick-sqlite": "^1.1.0", "base-64": "^1.0.0", "react": "*", "react-native": "*", @@ -44,7 +44,7 @@ "async-lock": "^1.4.0" }, "devDependencies": { - "@journeyapps/react-native-quick-sqlite": "^1.0.0", + "@journeyapps/react-native-quick-sqlite": "^1.1.0", "@types/async-lock": "^1.4.0", "react-native": "0.72.4", "react": "18.2.0", diff --git a/packages/powersync-sdk-react-native/src/db/adapters/react-native-quick-sqlite/RNQSDBAdapter.ts b/packages/powersync-sdk-react-native/src/db/adapters/react-native-quick-sqlite/RNQSDBAdapter.ts index 5cae2294a..f2fbbe924 100644 --- a/packages/powersync-sdk-react-native/src/db/adapters/react-native-quick-sqlite/RNQSDBAdapter.ts +++ b/packages/powersync-sdk-react-native/src/db/adapters/react-native-quick-sqlite/RNQSDBAdapter.ts @@ -21,7 +21,7 @@ export class RNQSDBAdapter extends BaseObserver implements DB constructor(protected baseDB: QuickSQLiteConnection, public name: string) { super(); // link table update commands - baseDB.registerUpdateHook((update) => { + baseDB.registerTablesChangedHook((update) => { this.iterateListeners((cb) => cb.tablesUpdated?.(update)); }); diff --git a/yarn.lock b/yarn.lock index 22008a359..a63f460fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2220,10 +2220,10 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@journeyapps/react-native-quick-sqlite@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@journeyapps/react-native-quick-sqlite/-/react-native-quick-sqlite-1.0.0.tgz#bb836a82a64705a2be6de27560b1e8816bba19d0" - integrity sha512-rQPE5OoMfXCyBBnCNMhkd4pES8zt0CbxiWb6GfZ04ik/cKji14GWBkvw9YZdyutc3zb3CNiexHYP1xZzlQYTQg== +"@journeyapps/react-native-quick-sqlite@^1.0.0", "@journeyapps/react-native-quick-sqlite@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@journeyapps/react-native-quick-sqlite/-/react-native-quick-sqlite-1.1.0.tgz#cf4aa6694b7232d0f86e565fdba4e41ef15d80cc" + integrity sha512-Pg6VA6ABC7N5FrNB5eqTgNsKdzzmDSp5aBtnQh1BlcZu7ISPZdCcKo+ZJtKyzTAWpc17LIttvJwxez6zBxUdOw== dependencies: lodash "^4.17.21" uuid "3.4.0"