Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ jobs:
matrix:
include:
- os: macos-latest
targets: iosSimulatorArm64Test macosArm64Test watchosSimulatorArm64Test tvosSimulatorArm64Test jvmTest
name: macos-watchos-tvos
targets: watchosSimulatorArm64Test tvosSimulatorArm64Test
- os: macos-latest
name: mac-ios-macos
targets: iosSimulatorArm64Test macosArm64Test
- os: macos-latest
name: mac-jvm
targets: jvmTest
- os: ubuntu-latest
name: ubuntu
targets: testDebugUnitTest testReleaseUnitTest jvmTest lintKotlin
- os: windows-latest
name: windows
targets: jvmTest
runs-on: ${{ matrix.os }}
timeout-minutes: 30
name: Test ${{ matrix.name }}
timeout-minutes: 20

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -60,7 +70,7 @@ jobs:
if: failure()
uses: actions/upload-artifact@v4
with:
name: report-for-${{ matrix.os }}
name: report-for-${{ matrix.name }}
path: |
**/build/reports/
**/build/test-results/
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
`androidx.sqlite` that can be used to step through statements in a custom way.
* Fix an issue where `watch()` would run queries more often than intended.
* Add an integration for the Room database library ([readme](integrations/room/README.md)).
* Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open
PowerSync instances. See [the readme](integrations/sqldelight/README.md) for details.

## 1.5.1

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


## Demo Apps / Example Projects
Expand Down
1 change: 1 addition & 0 deletions compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
alias(libs.plugins.compose.compiler)
alias(libs.plugins.kotlinter)
id("com.powersync.plugins.sonatype")
id("com.powersync.plugins.sharedbuild")
id("dokka-convention")
}

Expand Down
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ compose-lifecycle = "2.9.2"
androidxSqlite = "2.6.0-rc02"
androidxSplashscreen = "1.0.1"
room = "2.8.0-rc02"
sqldelight = "2.1.0"

# plugins
android-gradle-plugin = "8.12.1"
Expand Down Expand Up @@ -103,6 +104,9 @@ androidx-sqlite-sqlite = { module = "androidx.sqlite:sqlite", version.ref = "and
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "androidxSqlite" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
sqldelight-dialect-sqlite38 = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" }

# Sample - Android
androidx-core = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
Expand Down Expand Up @@ -151,3 +155,4 @@ kotlin-atomicfu = { id = "org.jetbrains.kotlinx.atomicfu", version.ref = "atomic
buildKonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildKonfig" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
androidx-room = { id = "androidx.room", version.ref = "room" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
46 changes: 46 additions & 0 deletions integrations/sqldelight-test-database/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import com.powersync.plugins.utils.powersyncTargets
import org.jmailen.gradle.kotlinter.tasks.FormatTask
import org.jmailen.gradle.kotlinter.tasks.LintTask

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.kotlinter)
alias(libs.plugins.sqldelight)
id("com.powersync.plugins.sharedbuild")
}

kotlin {
// We don't test on Android devices, JVM tests are enough for the SQLDelight test package since
// it doesn't contain Android-specific code.
powersyncTargets(android = false)

explicitApi()
applyDefaultHierarchyTemplate()

sourceSets {
commonMain.dependencies {
api(libs.sqldelight.runtime)
}
}
}

sqldelight {
databases {
linkSqlite.set(false)

create("TestDatabase") {
packageName.set("com.powersync.integrations.sqldelight")
generateAsync.set(true)
deriveSchemaFromMigrations.set(false)
dialect(libs.sqldelight.dialect.sqlite38)
}
}
}

tasks.withType<LintTask> {
exclude { it.file.path.contains("build/generated") }
}

tasks.withType<FormatTask> {
exclude { it.file.path.contains("build/generated") }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CREATE TABLE todos (
id TEXT NOT NULL DEFAULT '',
title TEXT,
content TEXT
);

all:
SELECT * FROM todos;

create:
INSERT INTO todos (id, title, content) VALUES (uuid(), ?, ?);

update:
UPDATE todos SET content = content || title RETURNING *;
62 changes: 62 additions & 0 deletions integrations/sqldelight/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## PowerSync SQLDelight driver

This library provides the `PowerSyncDriver` class, which implements an `SqlDriver` for `SQLDelight`
backed by PowerSync.

## Setup

Add a dependency on `com.powersync:integration-sqldelight`, using the same version you use for the
PowerSync SDK.

## Usage

To get started, ensure that SQLDelight is not linking sqlite3 (the PowerSync SDK takes care of that,
and you don't want to link it twice). Also, ensure the async generator is active because the
PowerSync driver does not support synchronous reads:

```kotlin
sqldelight {
databases {
linkSqlite.set(false)

create("MyAppDatabase") {
generateAsync.set(true)
deriveSchemaFromMigrations.set(false)

dialect("app.cash.sqldelight:sqlite-3-38-dialect")
}
}
}
```

Next, define your tables in `.sq` files (but note that the `CREATE TABLE` statement won't be used,
PowerSync creates JSON-backed views for tables instead).
Open a PowerSync database [in the usual way](https://docs.powersync.com/client-sdk-references/kotlin-multiplatform#getting-started)
and finally pass it to the constructor of your generated SQLDelight database:

```kotlin
val db: PowerSyncDatabase = openPowerSyncDatabase()
val yourSqlDelightDatabase = YourDatabase(PowerSyncDriver(db))
```

Afterwards, writes on both databases (the original `PowerSyncDatabase` instance and the SQLDelight
database) will be visible to each other, update each other's query flows and will get synced
properly.

## Limitations

Please note that this library is currently in alpha. It is tested, but API changes are still
possible.

There are also some limitations to be aware of:

1. Due to historical reasons, the PowerSync SDK migrates all databases to `user_version` 1 when
created (but it will never downgrade a database).
So if you want to use SQLDelight's schema tools, the first version would have to be `2`.
2. The `CREATE TABLE` statements in your `.sq` files are only used at build time to verify your
queries. At runtime, PowerSync will create tables from your schema as views, the defined
statements are ignored.
If you want to use the schema managed by SQLDelight, configure PowerSync to use
[raw tables](https://docs.powersync.com/usage/use-case-examples/raw-tables).
3. Functions and tables contributed by the PowerSync core extension are not visible to `.sq` files
at the moment. We might revisit this with a custom dialect in the future.
70 changes: 70 additions & 0 deletions integrations/sqldelight/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import com.powersync.plugins.utils.powersyncTargets

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.android.library)
alias(libs.plugins.kotlinter)
alias(libs.plugins.kotlin.atomicfu)
id("com.powersync.plugins.sonatype")
id("com.powersync.plugins.sharedbuild")
id("dokka-convention")
}

kotlin {
powersyncTargets()
explicitApi()
applyDefaultHierarchyTemplate()

sourceSets {
commonMain.dependencies {
api(projects.core)
api(libs.sqldelight.runtime)
implementation(libs.kotlinx.coroutines.core)
}

commonTest.dependencies {
// Separate project because SQLDelight can't generate code in test source sets.
implementation(projects.integrations.sqldelightTestDatabase)

implementation(libs.kotlin.test)
implementation(libs.kotlinx.io)
implementation(libs.test.turbine)
implementation(libs.test.coroutines)
implementation(libs.test.kotest.assertions)

implementation(libs.sqldelight.coroutines)
}

val commonIntegrationTest by creating {
dependsOn(commonTest.get())
}

// The PowerSync SDK links the core extension, so we can just run tests as-is.
jvmTest.get().dependsOn(commonIntegrationTest)

// We have special setup in this build configuration to make these tests link the PowerSync extension, so they
// can run integration tests along with the executable for unit testing.
nativeTest.orNull?.dependsOn(commonIntegrationTest)
}
}

android {
namespace = "com.powersync.drivers.common"
compileSdk =
libs.versions.android.compileSdk
.get()
.toInt()
defaultConfig {
minSdk =
libs.versions.android.minSdk
.get()
.toInt()
}
kotlin {
jvmToolchain(17)
}
}

dokka {
moduleName.set("PowerSync for SQLDelight")
}
3 changes: 3 additions & 0 deletions integrations/sqldelight/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=integration-sqldelight
POM_NAME=PowerSync SQLDelight driver
POM_DESCRIPTION=Use a PowerSync database for your SQLDelight database.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.powersync.integrations.sqldelight

import com.powersync.DatabaseDriverFactory

actual fun databaseDriverFactory(): DatabaseDriverFactory = DatabaseDriverFactory()
Loading
Loading