Skip to content

Commit 37ddc67

Browse files
committed
Review feedback
1 parent 351638e commit 37ddc67

File tree

8 files changed

+64
-14
lines changed

8 files changed

+64
-14
lines changed

core/src/androidMain/kotlin/com/powersync/DatabaseDriverFactory.android.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.powersync
33
import android.content.Context
44
import androidx.sqlite.SQLiteConnection
55
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
6+
import kotlin.Throws
67

78
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
89
public actual class DatabaseDriverFactory(
@@ -23,4 +24,5 @@ public fun BundledSQLiteDriver.addPowerSyncExtension() {
2324
}
2425

2526
@ExperimentalPowerSyncAPI
27+
@Throws(PowerSyncException::class)
2628
public actual fun resolvePowerSyncLoadableExtensionPath(): String? = "libpowersync.so"

core/src/appleNonWatchOsMain/kotlin/com/powersync/DatabaseDriverFactory.appleNonWatchOs.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ public actual class DatabaseDriverFactory {
2323
}
2424

2525
@ExperimentalPowerSyncAPI
26+
@Throws(PowerSyncException::class)
2627
public actual fun resolvePowerSyncLoadableExtensionPath(): String? = powerSyncExtensionPath

core/src/commonMain/kotlin/com/powersync/DatabaseDriverFactory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public expect class DatabaseDriverFactory {
2828
* SDK.
2929
*/
3030
@ExperimentalPowerSyncAPI
31+
@Throws(PowerSyncException::class)
3132
public expect fun resolvePowerSyncLoadableExtensionPath(): String?
3233

3334
@OptIn(ExperimentalPowerSyncAPI::class)

core/src/watchosMain/kotlin/com/powersync/DatabaseDriverFactory.watchos.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ private val didLoadExtension by lazy {
3333
}
3434

3535
@ExperimentalPowerSyncAPI
36+
@Throws(PowerSyncException::class)
3637
public actual fun resolvePowerSyncLoadableExtensionPath(): String? {
3738
didLoadExtension
3839
return null

integrations/room/README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,21 @@ been loaded. To do that:
2828
3. Configure raw tables for your Room databases.
2929

3030
After these steps, you can open your Room database like you normally would. Then, you can use the
31-
following method to obtain a `PowerSyncDatabase` instance that is backed by Room:
31+
following method to obtain a `PowerSyncDatabase` instance which is backed by Room:
3232

3333
```Kotlin
34-
val pool = RoomConnectionPool(yourRoomDatabase)
34+
// With Room, you need to use raw tables (https://docs.powersync.com/usage/use-case-examples/raw-tables).
35+
// This is because Room verifies your schema at runtime, and PowerSync-managed views will not
36+
// pass those checks.
37+
val schema = Schema(...)
38+
val pool = RoomConnectionPool(yourRoomDatabase, schema)
3539
val powersync = PowerSyncDatabase.opened(
3640
pool = pool,
3741
scope = this,
38-
schema = Schema(...), // With Room, you need to use raw tables
42+
schema = schema,
3943
identifier = "databaseName", // Prefer to use the same path/name as your Room database
4044
logger = Logger,
4145
)
42-
4346
powersync.connect(...)
4447
```
4548

integrations/room/src/commonIntegrationTest/kotlin/com/powersync/integrations/room/PowerSyncRoomTest.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class PowerSyncRoomTest {
3939

4040
val powersync =
4141
PowerSyncDatabase.opened(
42-
pool = RoomConnectionPool(database),
42+
pool = RoomConnectionPool(database, TestDatabase.schema),
4343
scope = this,
4444
schema = TestDatabase.schema,
4545
identifier = "test",
@@ -62,7 +62,7 @@ class PowerSyncRoomTest {
6262
fun roomWritePowerSyncWatch() =
6363
runTest {
6464
val logger = Logger(loggerConfigInit())
65-
val pool = RoomConnectionPool(database)
65+
val pool = RoomConnectionPool(database, TestDatabase.schema)
6666

6767
val powersync =
6868
PowerSyncDatabase.opened(
@@ -85,7 +85,6 @@ class PowerSyncRoomTest {
8585

8686
turbine.awaitItem() shouldHaveSize 0
8787
database.userDao().create(User("id", "name"))
88-
pool.transferRoomUpdatesToPowerSync() // TODO: Would be cool if this wasn't necessary
8988
turbine.awaitItem() shouldHaveSize 1
9089
turbine.cancel()
9190
}
@@ -95,7 +94,7 @@ class PowerSyncRoomTest {
9594
fun powersyncWriteRoomRead() =
9695
runTest {
9796
val logger = Logger(loggerConfigInit())
98-
val pool = RoomConnectionPool(database)
97+
val pool = RoomConnectionPool(database, TestDatabase.schema)
9998

10099
val powersync =
101100
PowerSyncDatabase.opened(
@@ -115,7 +114,7 @@ class PowerSyncRoomTest {
115114
fun powersyncWriteRoomWatch() =
116115
runTest {
117116
val logger = Logger(loggerConfigInit())
118-
val pool = RoomConnectionPool(database)
117+
val pool = RoomConnectionPool(database, TestDatabase.schema)
119118

120119
val powersync =
121120
PowerSyncDatabase.opened(

integrations/room/src/commonIntegrationTest/kotlin/com/powersync/integrations/room/TestDatabase.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import androidx.room.PrimaryKey
1010
import androidx.room.Query
1111
import androidx.room.RoomDatabase
1212
import androidx.room.RoomDatabaseConstructor
13+
import com.powersync.db.schema.PendingStatement
14+
import com.powersync.db.schema.PendingStatementParameter
15+
import com.powersync.db.schema.RawTable
1316
import com.powersync.db.schema.Schema
1417
import kotlinx.coroutines.flow.Flow
1518

@@ -40,7 +43,25 @@ abstract class TestDatabase : RoomDatabase() {
4043
abstract fun userDao(): UserDao
4144

4245
companion object {
43-
val schema = Schema()
46+
val schema =
47+
Schema(
48+
RawTable(
49+
name = "user",
50+
put =
51+
PendingStatement(
52+
"INSERT INTO user (id, name) VALUES (?, ?)",
53+
listOf(
54+
PendingStatementParameter.Id,
55+
PendingStatementParameter.Column("name"),
56+
),
57+
),
58+
delete =
59+
PendingStatement(
60+
"DELETE FROM user WHERE id = ?",
61+
listOf(PendingStatementParameter.Id),
62+
),
63+
),
64+
)
4465
}
4566
}
4667

integrations/room/src/commonMain/kotlin/com/powersync/integrations/room/RoomConnectionPool.kt

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import androidx.room.useWriterConnection
88
import androidx.sqlite.SQLiteStatement
99
import com.powersync.db.driver.SQLiteConnectionLease
1010
import com.powersync.db.driver.SQLiteConnectionPool
11+
import com.powersync.db.schema.Schema
1112
import kotlinx.coroutines.currentCoroutineContext
1213
import kotlinx.coroutines.flow.MutableSharedFlow
1314
import kotlinx.coroutines.flow.SharedFlow
15+
import kotlinx.coroutines.launch
1416
import kotlinx.coroutines.runBlocking
1517
import kotlinx.serialization.json.Json
1618
import kotlin.coroutines.CoroutineContext
@@ -23,16 +25,23 @@ import kotlin.coroutines.CoroutineContext
2325
*
2426
* Writes made from the wrapped PowerSync database, including writes made for the sync process, are
2527
* forwarded to Room and will update your flows automatically.
26-
* On the other hand, the PowerSync SDK needs to be notified about updates in Room. For that, use
27-
* the [transferRoomUpdatesToPowerSync] method as a collector of a Room flow listening on all your
28-
* tables.
28+
*
29+
* On the other hand, the PowerSync SDK needs to be notified about updates in Room. For that, a
30+
* schema parameter can be used in the constructor. It will call [syncRoomUpdatesToPowerSync] to
31+
* collect a Room flow on all tables. Alternatively, [transferPendingRoomUpdatesToPowerSync] can be
32+
* called after issuing writes in Room to transfer them to PowerSync.
2933
*/
3034
public class RoomConnectionPool(
3135
private val db: RoomDatabase,
36+
schema: Schema? = null,
3237
) : SQLiteConnectionPool {
3338
private val _updates = MutableSharedFlow<Set<String>>()
3439
private var hasInstalledUpdateHook = false
3540

41+
init {
42+
schema?.let { syncRoomUpdatesToPowerSync(it) }
43+
}
44+
3645
override suspend fun <R> withAllConnections(action: suspend (SQLiteConnectionLease, List<SQLiteConnectionLease>) -> R) {
3746
// We can't obtain a list of all connections on Room. That's fine though, we expect this to
3847
// be used with raw tables, and withAllConnections is only used to apply a PowerSync schema.
@@ -50,12 +59,25 @@ public class RoomConnectionPool(
5059
* Makes pending updates tracked by Room's invalidation tracker available to the PowerSync
5160
* database, updating flows and triggering CRUD uploads.
5261
*/
53-
public suspend fun transferRoomUpdatesToPowerSync() {
62+
public suspend fun transferPendingRoomUpdatesToPowerSync() {
5463
write {
5564
// The end of the write callback invokes powersync_update_hooks('get') for this
5665
}
5766
}
5867

68+
/**
69+
* Registers a Room listener on all tables mentioned in the [schema] and invokes
70+
* [transferPendingRoomUpdatesToPowerSync] when they change.
71+
*/
72+
public fun syncRoomUpdatesToPowerSync(schema: Schema) {
73+
db.getCoroutineScope().launch {
74+
val tables = schema.rawTables.map { it.name }.toTypedArray()
75+
db.invalidationTracker.createFlow(*tables, emitInitialState = false).collect {
76+
transferPendingRoomUpdatesToPowerSync()
77+
}
78+
}
79+
}
80+
5981
override suspend fun <T> write(callback: suspend (SQLiteConnectionLease) -> T): T =
6082
db.useWriterConnection {
6183
if (!hasInstalledUpdateHook) {

0 commit comments

Comments
 (0)