Skip to content

Commit 0d56088

Browse files
authored
Add SQLDelight driver (#231)
1 parent 828e1cc commit 0d56088

File tree

16 files changed

+711
-8
lines changed

16 files changed

+711
-8
lines changed

.github/workflows/test.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,23 @@ jobs:
1616
matrix:
1717
include:
1818
- os: macos-latest
19-
targets: iosSimulatorArm64Test macosArm64Test watchosSimulatorArm64Test tvosSimulatorArm64Test jvmTest
19+
name: macos-watchos-tvos
20+
targets: watchosSimulatorArm64Test tvosSimulatorArm64Test
21+
- os: macos-latest
22+
name: mac-ios-macos
23+
targets: iosSimulatorArm64Test macosArm64Test
24+
- os: macos-latest
25+
name: mac-jvm
26+
targets: jvmTest
2027
- os: ubuntu-latest
28+
name: ubuntu
2129
targets: testDebugUnitTest testReleaseUnitTest jvmTest lintKotlin
2230
- os: windows-latest
31+
name: windows
2332
targets: jvmTest
2433
runs-on: ${{ matrix.os }}
25-
timeout-minutes: 30
34+
name: Test ${{ matrix.name }}
35+
timeout-minutes: 20
2636

2737
steps:
2838
- uses: actions/checkout@v4
@@ -60,7 +70,7 @@ jobs:
6070
if: failure()
6171
uses: actions/upload-artifact@v4
6272
with:
63-
name: report-for-${{ matrix.os }}
73+
name: report-for-${{ matrix.name }}
6474
path: |
6575
**/build/reports/
6676
**/build/test-results/

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
`androidx.sqlite` that can be used to step through statements in a custom way.
88
* Fix an issue where `watch()` would run queries more often than intended.
99
* Add an integration for the Room database library ([readme](integrations/room/README.md)).
10+
* Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open
11+
PowerSync instances. See [the readme](integrations/sqldelight/README.md) for details.
1012

1113
## 1.5.1
1214

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ and API documentation [here](https://powersync-ja.github.io/powersync-kotlin/).
3636
- [integrations](./integrations/)
3737
- [room](./integrations/room/README.md): Allows using the [Room database library](https://developer.android.com/jetpack/androidx/releases/room)
3838
with PowerSync, making it easier to run typed queries on the database.
39+
- [sqldelight](./integrations/sqldelight/README.md): Allows using [SQLDelight](https://sqldelight.github.io/sqldelight)
40+
with PowerSync, also enabling typed statements on the database.
3941

4042

4143
## Demo Apps / Example Projects

compose/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ plugins {
88
alias(libs.plugins.compose.compiler)
99
alias(libs.plugins.kotlinter)
1010
id("com.powersync.plugins.sonatype")
11+
id("com.powersync.plugins.sharedbuild")
1112
id("dokka-convention")
1213
}
1314

gradle/libs.versions.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ compose-lifecycle = "2.9.2"
3333
androidxSqlite = "2.6.0-rc02"
3434
androidxSplashscreen = "1.0.1"
3535
room = "2.8.0-rc02"
36+
sqldelight = "2.1.0"
3637

3738
# plugins
3839
android-gradle-plugin = "8.12.1"
@@ -103,6 +104,9 @@ androidx-sqlite-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "and
103104
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidxSqlite" }
104105
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
105106
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
107+
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
108+
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
109+
sqldelight-dialect-sqlite38 = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }
106110

107111
# Sample - Android
108112
androidx-core = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
@@ -151,3 +155,4 @@ kotlin-atomicfu = { id = "org.jetbrains.kotlinx.atomicfu", version.ref = "atomic
151155
buildKonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" }
152156
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
153157
androidx-room = { id = "androidx.room", version.ref = "room" }
158+
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import com.powersync.plugins.utils.powersyncTargets
2+
import org.jmailen.gradle.kotlinter.tasks.FormatTask
3+
import org.jmailen.gradle.kotlinter.tasks.LintTask
4+
5+
plugins {
6+
alias(libs.plugins.kotlinMultiplatform)
7+
alias(libs.plugins.kotlinter)
8+
alias(libs.plugins.sqldelight)
9+
id("com.powersync.plugins.sharedbuild")
10+
}
11+
12+
kotlin {
13+
// We don't test on Android devices, JVM tests are enough for the SQLDelight test package since
14+
// it doesn't contain Android-specific code.
15+
powersyncTargets(android = false)
16+
17+
explicitApi()
18+
applyDefaultHierarchyTemplate()
19+
20+
sourceSets {
21+
commonMain.dependencies {
22+
api(libs.sqldelight.runtime)
23+
}
24+
}
25+
}
26+
27+
sqldelight {
28+
databases {
29+
linkSqlite.set(false)
30+
31+
create("TestDatabase") {
32+
packageName.set("com.powersync.integrations.sqldelight")
33+
generateAsync.set(true)
34+
deriveSchemaFromMigrations.set(false)
35+
dialect(libs.sqldelight.dialect.sqlite38)
36+
}
37+
}
38+
}
39+
40+
tasks.withType<LintTask> {
41+
exclude { it.file.path.contains("build/generated") }
42+
}
43+
44+
tasks.withType<FormatTask> {
45+
exclude { it.file.path.contains("build/generated") }
46+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CREATE TABLE todos (
2+
id TEXT NOT NULL DEFAULT '',
3+
title TEXT,
4+
content TEXT
5+
);
6+
7+
all:
8+
SELECT * FROM todos;
9+
10+
create:
11+
INSERT INTO todos (id, title, content) VALUES (uuid(), ?, ?);
12+
13+
update:
14+
UPDATE todos SET content = content || title RETURNING *;

integrations/sqldelight/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
## PowerSync SQLDelight driver
2+
3+
This library provides the `PowerSyncDriver` class, which implements an `SqlDriver` for `SQLDelight`
4+
backed by PowerSync.
5+
6+
## Setup
7+
8+
Add a dependency on `com.powersync:integration-sqldelight`, using the same version you use for the
9+
PowerSync SDK.
10+
11+
## Usage
12+
13+
To get started, ensure that SQLDelight is not linking sqlite3 (the PowerSync SDK takes care of that,
14+
and you don't want to link it twice). Also, ensure the async generator is active because the
15+
PowerSync driver does not support synchronous reads:
16+
17+
```kotlin
18+
sqldelight {
19+
databases {
20+
linkSqlite.set(false)
21+
22+
create("MyAppDatabase") {
23+
generateAsync.set(true)
24+
deriveSchemaFromMigrations.set(false)
25+
26+
dialect("app.cash.sqldelight:sqlite-3-38-dialect")
27+
}
28+
}
29+
}
30+
```
31+
32+
Next, define your tables in `.sq` files (but note that the `CREATE TABLE` statement won't be used,
33+
PowerSync creates JSON-backed views for tables instead).
34+
Open a PowerSync database [in the usual way](https://docs.powersync.com/client-sdk-references/kotlin-multiplatform#getting-started)
35+
and finally pass it to the constructor of your generated SQLDelight database:
36+
37+
```kotlin
38+
val db: PowerSyncDatabase = openPowerSyncDatabase()
39+
val yourSqlDelightDatabase = YourDatabase(PowerSyncDriver(db))
40+
```
41+
42+
Afterwards, writes on both databases (the original `PowerSyncDatabase` instance and the SQLDelight
43+
database) will be visible to each other, update each other's query flows and will get synced
44+
properly.
45+
46+
## Limitations
47+
48+
Please note that this library is currently in alpha. It is tested, but API changes are still
49+
possible.
50+
51+
There are also some limitations to be aware of:
52+
53+
1. Due to historical reasons, the PowerSync SDK migrates all databases to `user_version` 1 when
54+
created (but it will never downgrade a database).
55+
So if you want to use SQLDelight's schema tools, the first version would have to be `2`.
56+
2. The `CREATE TABLE` statements in your `.sq` files are only used at build time to verify your
57+
queries. At runtime, PowerSync will create tables from your schema as views, the defined
58+
statements are ignored.
59+
If you want to use the schema managed by SQLDelight, configure PowerSync to use
60+
[raw tables](https://docs.powersync.com/usage/use-case-examples/raw-tables).
61+
3. Functions and tables contributed by the PowerSync core extension are not visible to `.sq` files
62+
at the moment. We might revisit this with a custom dialect in the future.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import com.powersync.plugins.utils.powersyncTargets
2+
3+
plugins {
4+
alias(libs.plugins.kotlinMultiplatform)
5+
alias(libs.plugins.android.library)
6+
alias(libs.plugins.kotlinter)
7+
alias(libs.plugins.kotlin.atomicfu)
8+
id("com.powersync.plugins.sonatype")
9+
id("com.powersync.plugins.sharedbuild")
10+
id("dokka-convention")
11+
}
12+
13+
kotlin {
14+
powersyncTargets()
15+
explicitApi()
16+
applyDefaultHierarchyTemplate()
17+
18+
sourceSets {
19+
commonMain.dependencies {
20+
api(projects.core)
21+
api(libs.sqldelight.runtime)
22+
implementation(libs.kotlinx.coroutines.core)
23+
}
24+
25+
commonTest.dependencies {
26+
// Separate project because SQLDelight can't generate code in test source sets.
27+
implementation(projects.integrations.sqldelightTestDatabase)
28+
29+
implementation(libs.kotlin.test)
30+
implementation(libs.kotlinx.io)
31+
implementation(libs.test.turbine)
32+
implementation(libs.test.coroutines)
33+
implementation(libs.test.kotest.assertions)
34+
35+
implementation(libs.sqldelight.coroutines)
36+
}
37+
38+
val commonIntegrationTest by creating {
39+
dependsOn(commonTest.get())
40+
}
41+
42+
// The PowerSync SDK links the core extension, so we can just run tests as-is.
43+
jvmTest.get().dependsOn(commonIntegrationTest)
44+
45+
// We have special setup in this build configuration to make these tests link the PowerSync extension, so they
46+
// can run integration tests along with the executable for unit testing.
47+
nativeTest.orNull?.dependsOn(commonIntegrationTest)
48+
}
49+
}
50+
51+
android {
52+
namespace = "com.powersync.drivers.common"
53+
compileSdk =
54+
libs.versions.android.compileSdk
55+
.get()
56+
.toInt()
57+
defaultConfig {
58+
minSdk =
59+
libs.versions.android.minSdk
60+
.get()
61+
.toInt()
62+
}
63+
kotlin {
64+
jvmToolchain(17)
65+
}
66+
}
67+
68+
dokka {
69+
moduleName.set("PowerSync for SQLDelight")
70+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
POM_ARTIFACT_ID=integration-sqldelight
2+
POM_NAME=PowerSync SQLDelight driver
3+
POM_DESCRIPTION=Use a PowerSync database for your SQLDelight database.

0 commit comments

Comments
 (0)