Skip to content

Commit 8cbf758

Browse files
authored
Upgrade to Room 3.0 (#360)
1 parent a31cf94 commit 8cbf758

11 files changed

Lines changed: 108 additions & 69 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.14.0 (unreleased)
4+
5+
- __Breaking change__ (for Room integration): The Room integration uses Room 3.0 now.
6+
37
## 1.13.0
48

59
- __Breaking change__: Aligning with Kotlin and Androidx multiplatform libraries, this

gradle/libs.versions.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ compose-preview = "1.11.2"
3232
compose-lifecycle = "2.10.0"
3333
androidxSqlite = "2.7.0"
3434
androidxSplashscreen = "1.2.0"
35-
room = "2.8.4"
35+
room = "3.0.0"
3636
sqldelight = "2.3.2"
3737

3838
# plugins
@@ -102,8 +102,8 @@ supabase-storage = { module = "io.github.jan-tennert.supabase:storage-kt", versi
102102
androidx-sqlite-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "androidxSqlite" }
103103
androidx-sqlite-async = { module = "androidx.sqlite:sqlite-async", version.ref = "androidxSqlite" }
104104
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidxSqlite" }
105-
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
106-
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
105+
androidx-room-runtime = { module = "androidx.room3:room3-runtime", version.ref = "room" }
106+
androidx-room-compiler = { module = "androidx.room3:room3-compiler", version.ref = "room" }
107107
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
108108
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
109109
sqldelight-dialect-sqlite38 = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
@@ -152,5 +152,5 @@ kotlinter = { id = "org.jmailen.kotlinter", version.ref = "kotlinter" }
152152
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
153153
buildKonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" }
154154
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
155-
androidx-room = { id = "androidx.room", version.ref = "room" }
155+
androidx-room = { id = "androidx.room3", version.ref = "room" }
156156
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }

integrations/room/build.gradle.kts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ kotlin {
1717
powersyncTargets(
1818
android = {
1919
namespace = "com.powersync.integrations.room"
20-
}
20+
},
21+
legacyJavaSupport = false,
22+
web = true
2123
)
2224

2325
explicitApi()
@@ -33,23 +35,43 @@ kotlin {
3335
commonMain.dependencies {
3436
api(projects.common)
3537
api(libs.androidx.room.runtime)
36-
api(libs.androidx.sqlite.bundled)
3738

39+
implementation(libs.androidx.sqlite.async)
3840
implementation(libs.kotlinx.serialization.json)
3941
}
4042

43+
val commonNonWeb by creating {
44+
dependsOn(commonMain.get())
45+
46+
dependencies {
47+
api(libs.androidx.sqlite.bundled)
48+
}
49+
}
50+
51+
jvmMain {
52+
dependsOn(commonNonWeb)
53+
}
54+
nativeMain {
55+
dependsOn(commonNonWeb)
56+
}
57+
androidMain {
58+
dependsOn(commonNonWeb)
59+
}
60+
4161
commonTest.dependencies {
4262
implementation(libs.kotlin.test)
4363
implementation(libs.kotlinx.io)
4464
implementation(libs.test.kotest.assertions)
4565
implementation(libs.test.coroutines)
4666
implementation(libs.test.turbine)
47-
48-
implementation(libs.androidx.sqlite.bundled)
4967
}
5068

5169
val commonIntegrationTest by creating {
5270
dependsOn(commonTest.get())
71+
72+
dependencies {
73+
implementation(libs.androidx.sqlite.bundled)
74+
}
5375
}
5476

5577
// We're putting the native libraries into our JAR, so integration tests for the JVM can run as part of the unit

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package com.powersync.integrations.room
22

3-
import androidx.room.ConstructedBy
4-
import androidx.room.Dao
5-
import androidx.room.Database
6-
import androidx.room.Delete
7-
import androidx.room.Entity
8-
import androidx.room.Insert
9-
import androidx.room.PrimaryKey
10-
import androidx.room.Query
11-
import androidx.room.RoomDatabase
12-
import androidx.room.RoomDatabaseConstructor
3+
import androidx.room3.ConstructedBy
4+
import androidx.room3.Dao
5+
import androidx.room3.Database
6+
import androidx.room3.Delete
7+
import androidx.room3.Entity
8+
import androidx.room3.Insert
9+
import androidx.room3.PrimaryKey
10+
import androidx.room3.Query
11+
import androidx.room3.RoomDatabase
12+
import androidx.room3.RoomDatabaseConstructor
1313
import androidx.sqlite.SQLiteConnection
1414
import androidx.sqlite.execSQL
1515
import com.powersync.db.schema.RawTable
@@ -52,7 +52,7 @@ abstract class TestDatabase : RoomDatabase() {
5252
),
5353
)
5454

55-
override fun onOpen(connection: SQLiteConnection) {
55+
override suspend fun onOpen(connection: SQLiteConnection) {
5656
connection.execSQL("CREATE VIRTUAL TABLE users_fts USING fts5(id UNINDEXED, name)")
5757
}
5858
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package com.powersync.integrations.room
22

3-
import androidx.room.RoomDatabase
3+
import androidx.room3.RoomDatabase
44

55
expect fun createDatabaseBuilder(): RoomDatabase.Builder<TestDatabase>

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

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
package com.powersync.integrations.room
22

3-
import androidx.room.RoomDatabase
4-
import androidx.room.Transactor
5-
import androidx.room.execSQL
6-
import androidx.room.useReaderConnection
7-
import androidx.room.useWriterConnection
3+
import androidx.room3.RoomDatabase
4+
import androidx.room3.Transactor
5+
import androidx.room3.executeSQL
6+
import androidx.room3.useReaderConnection
7+
import androidx.room3.useWriterConnection
88
import androidx.sqlite.SQLiteException
99
import androidx.sqlite.SQLiteStatement
10+
import androidx.sqlite.async.step
1011
import com.powersync.db.driver.SQLiteConnectionLease
1112
import com.powersync.db.driver.SQLiteConnectionPool
1213
import com.powersync.db.schema.Schema
13-
import kotlinx.coroutines.currentCoroutineContext
1414
import kotlinx.coroutines.flow.MutableSharedFlow
1515
import kotlinx.coroutines.flow.SharedFlow
1616
import kotlinx.coroutines.launch
17-
import kotlinx.coroutines.runBlocking
18-
import kotlinx.serialization.json.Json
19-
import kotlin.coroutines.CoroutineContext
2017

2118
/**
2219
* A [SQLiteConnectionPool] implementation for the PowerSync SDK that is backed by a [RoomDatabase].
@@ -53,7 +50,7 @@ public class RoomConnectionPool(
5350

5451
override suspend fun <T> read(callback: suspend (SQLiteConnectionLease) -> T): T =
5552
db.useReaderConnection {
56-
callback(RoomTransactionLease(it, currentCoroutineContext()))
53+
callback(it.asConnectionLease())
5754
}
5855

5956
/**
@@ -94,11 +91,11 @@ public class RoomConnectionPool(
9491
db.useWriterConnection {
9592
if (!hasInstalledUpdateHook) {
9693
hasInstalledUpdateHook = true
97-
it.execSQL("SELECT powersync_update_hooks('install')")
94+
it.executeSQL("SELECT powersync_update_hooks('install')")
9895
}
9996

10097
try {
101-
callback(RoomTransactionLease(it, currentCoroutineContext()))
98+
callback(it.asConnectionLease())
10299
} finally {
103100
// List changed tables, excluding virtual and shadow tables for e.g. fts5
104101
val statement =
@@ -139,43 +136,15 @@ public class RoomConnectionPool(
139136
}
140137
}
141138

142-
private class RoomTransactionLease(
143-
private val transactor: Transactor,
144-
/**
145-
* The context to use for [runBlocking] calls to avoid the "Attempted to use connection on a
146-
* different coroutine" error.
147-
*/
148-
private val context: CoroutineContext,
139+
internal expect suspend fun Transactor.asConnectionLease(): SQLiteConnectionLease
140+
141+
internal abstract class RoomTransactionLease(
142+
protected val transactor: Transactor,
149143
) : SQLiteConnectionLease {
150144
override suspend fun isInTransaction(): Boolean = transactor.inTransaction()
151145

152-
override suspend fun <R> usePrepared(
153-
sql: String,
154-
block: (SQLiteStatement) -> R,
155-
): R = transactor.usePrepared(sql, block)
156-
157146
override suspend fun <R> usePreparedAsync(
158147
sql: String,
159148
block: suspend (SQLiteStatement) -> R,
160-
): R =
161-
transactor.usePrepared(sql) {
162-
// TODO: This is suspending in Room3, where we can avoid the runBlocking here.
163-
stmt ->
164-
// Don't use the context here, Room2 does that for us. We're not allowed to use the
165-
// connection from different threads.
166-
runBlocking { block(stmt) }
167-
}
168-
169-
override fun isInTransactionSync(): Boolean =
170-
runBlocking(context) {
171-
isInTransaction()
172-
}
173-
174-
override fun <R> usePreparedSync(
175-
sql: String,
176-
block: (SQLiteStatement) -> R,
177-
): R =
178-
runBlocking(context) {
179-
usePrepared(sql, block)
180-
}
149+
): R = transactor.usePrepared(sql, block)
181150
}

integrations/room/src/commonMain/kotlin/com/powersync/integrations/room/PowerSyncExtension.kt renamed to integrations/room/src/commonNonWeb/kotlin/com/powersync/integrations/room/PowerSyncExtension.kt

File renamed without changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.powersync.integrations.room
2+
3+
import androidx.room3.Transactor
4+
import androidx.sqlite.SQLiteStatement
5+
import com.powersync.db.driver.SQLiteConnectionLease
6+
import kotlinx.coroutines.currentCoroutineContext
7+
import kotlinx.coroutines.runBlocking
8+
import kotlin.coroutines.CoroutineContext
9+
10+
internal actual suspend fun Transactor.asConnectionLease(): SQLiteConnectionLease =
11+
SyncRoomTransactionLease(this, currentCoroutineContext())
12+
13+
private class SyncRoomTransactionLease(
14+
transactor: Transactor,
15+
private val context: CoroutineContext,
16+
) : RoomTransactionLease(transactor) {
17+
override suspend fun <R> usePrepared(
18+
sql: String,
19+
block: (SQLiteStatement) -> R,
20+
): R = transactor.usePrepared(sql, block)
21+
22+
override fun isInTransactionSync(): Boolean =
23+
runBlocking(context) {
24+
isInTransaction()
25+
}
26+
27+
override fun <R> usePreparedSync(
28+
sql: String,
29+
block: (SQLiteStatement) -> R,
30+
): R =
31+
runBlocking(context) {
32+
usePrepared(sql, block)
33+
}
34+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.powersync.integrations.room
22

3-
import androidx.room.Room
4-
import androidx.room.RoomDatabase
3+
import androidx.room3.Room
4+
import androidx.room3.RoomDatabase
55

66
actual fun createDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> =
77
Room.inMemoryDatabaseBuilder<TestDatabase>().addCallback(TestDatabase)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.powersync.integrations.room
22

3-
import androidx.room.Room
4-
import androidx.room.RoomDatabase
3+
import androidx.room3.Room
4+
import androidx.room3.RoomDatabase
55

66
actual fun createDatabaseBuilder(): RoomDatabase.Builder<TestDatabase> =
77
Room.inMemoryDatabaseBuilder<TestDatabase>().addCallback(TestDatabase)

0 commit comments

Comments
 (0)