Skip to content

Commit 7650661

Browse files
authored
Use androidx sqlite bindings internally (#230)
1 parent d8b06fa commit 7650661

File tree

66 files changed

+1436
-2276
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1436
-2276
lines changed

CHANGELOG.md

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

3+
## 1.6.0 (unreleased)
4+
5+
* Remove internal SQLDelight and SQLiter dependencies.
6+
* Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from
7+
`androidx.sqlite` that can be used to step through statements in a custom way.
8+
39
## 1.5.1
410

511
* Fix issue in legacy sync client where local writes made offline could have their upload delayed

build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ plugins {
1313
alias(libs.plugins.skie) apply false
1414
alias(libs.plugins.kotlin.jvm) apply false
1515
alias(libs.plugins.kotlin.android) apply false
16-
alias(libs.plugins.sqldelight) apply false
17-
alias(libs.plugins.grammarKitComposer) apply false
1816
alias(libs.plugins.mavenPublishPlugin) apply false
1917
alias(libs.plugins.downloadPlugin) apply false
2018
alias(libs.plugins.kotlinter) apply false

compose/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ kotlin {
2424
sourceSets {
2525
commonMain.dependencies {
2626
api(project(":core"))
27-
implementation(project(":persistence"))
2827
implementation(compose.runtime)
2928
}
3029
androidMain.dependencies {

core/build.gradle.kts

Lines changed: 29 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -74,60 +74,6 @@ val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) {
7474
onlyIfModified(true)
7575
}
7676

77-
val sqliteJDBCFolder =
78-
project.layout.buildDirectory
79-
.dir("jdbc")
80-
.get()
81-
82-
val jniLibsFolder = layout.projectDirectory.dir("src/androidMain/jni")
83-
84-
val downloadJDBCJar by tasks.registering(Download::class) {
85-
val version =
86-
libs.versions.sqlite.jdbc
87-
.get()
88-
val jar =
89-
"https://github.com/xerial/sqlite-jdbc/releases/download/$version/sqlite-jdbc-$version.jar"
90-
91-
src(jar)
92-
dest(sqliteJDBCFolder.file("jdbc.jar"))
93-
onlyIfModified(true)
94-
}
95-
96-
val extractJDBCJNI by tasks.registering(Copy::class) {
97-
dependsOn(downloadJDBCJar)
98-
99-
from(
100-
zipTree(downloadJDBCJar.get().dest).matching {
101-
include("org/sqlite/native/Linux-Android/**")
102-
},
103-
)
104-
105-
into(sqliteJDBCFolder.dir("jni"))
106-
}
107-
108-
val moveJDBCJNIFiles by tasks.registering(Copy::class) {
109-
dependsOn(extractJDBCJNI)
110-
111-
val abiMapping =
112-
mapOf(
113-
"aarch64" to "arm64-v8a",
114-
"arm" to "armeabi-v7a",
115-
"x86_64" to "x86_64",
116-
"x86" to "x86",
117-
)
118-
119-
abiMapping.forEach { (sourceABI, androidABI) ->
120-
from(sqliteJDBCFolder.dir("jni/org/sqlite/native/Linux-Android/$sourceABI")) {
121-
include("*.so")
122-
eachFile {
123-
path = "$androidABI/$name"
124-
}
125-
}
126-
}
127-
128-
into(jniLibsFolder) // Move everything into the base jniLibs folder
129-
}
130-
13177
val generateVersionConstant by tasks.registering {
13278
val target = project.layout.buildDirectory.dir("generated/constants")
13379
val packageName = "com.powersync.build"
@@ -172,6 +118,12 @@ kotlin {
172118
headers(file("src/watchosMain/powersync_static.h"))
173119
}
174120
}
121+
122+
cinterops.create("sqlite3") {
123+
packageName("com.powersync.internal.sqlite3")
124+
includeDirs.allHeaders("src/nativeMain/interop/")
125+
definitionFile.set(project.file("src/nativeMain/interop/sqlite3.def"))
126+
}
175127
}
176128
}
177129

@@ -183,6 +135,7 @@ kotlin {
183135
languageSettings {
184136
optIn("kotlinx.cinterop.ExperimentalForeignApi")
185137
optIn("kotlin.time.ExperimentalTime")
138+
optIn("kotlin.experimental.ExperimentalObjCRefinement")
186139
}
187140
}
188141

@@ -192,9 +145,6 @@ kotlin {
192145

193146
val commonJava by creating {
194147
dependsOn(commonMain.get())
195-
dependencies {
196-
implementation(libs.sqlite.jdbc)
197-
}
198148
}
199149

200150
commonMain.configure {
@@ -203,6 +153,8 @@ kotlin {
203153
}
204154

205155
dependencies {
156+
api(libs.androidx.sqlite.sqlite)
157+
206158
implementation(libs.uuid)
207159
implementation(libs.kotlin.stdlib)
208160
implementation(libs.ktor.client.contentnegotiation)
@@ -212,29 +164,47 @@ kotlin {
212164
implementation(libs.kotlinx.datetime)
213165
implementation(libs.stately.concurrency)
214166
implementation(libs.configuration.annotations)
215-
api(projects.persistence)
216167
api(libs.ktor.client.core)
217168
api(libs.kermit)
218169
}
219170
}
220171

221172
androidMain {
222173
dependsOn(commonJava)
223-
dependencies.implementation(libs.ktor.client.okhttp)
174+
dependencies {
175+
implementation(libs.ktor.client.okhttp)
176+
implementation(libs.androidx.sqlite.bundled)
177+
}
224178
}
225179

226180
jvmMain {
227181
dependsOn(commonJava)
228182

229183
dependencies {
230184
implementation(libs.ktor.client.okhttp)
185+
implementation(libs.androidx.sqlite.bundled)
231186
}
232187
}
233188

234189
appleMain.dependencies {
235190
implementation(libs.ktor.client.darwin)
191+
192+
// We're not using the bundled SQLite library for Apple platforms. Instead, we depend on
193+
// static-sqlite-driver to link SQLite and have our own bindings implementing the
194+
// driver. The reason for this is that androidx.sqlite-bundled causes linker errors for
195+
// our Swift SDK.
196+
implementation(projects.staticSqliteDriver)
197+
}
198+
199+
// Common apple targets where we link the core extension dynamically
200+
val appleNonWatchOsMain by creating {
201+
dependsOn(appleMain.get())
236202
}
237203

204+
macosMain.orNull?.dependsOn(appleNonWatchOsMain)
205+
iosMain.orNull?.dependsOn(appleNonWatchOsMain)
206+
tvosMain.orNull?.dependsOn(appleNonWatchOsMain)
207+
238208
commonTest.dependencies {
239209
implementation(libs.kotlin.test)
240210
implementation(libs.test.coroutines)
@@ -294,12 +264,6 @@ android {
294264
ndkVersion = "27.1.12297006"
295265
}
296266

297-
androidComponents.onVariants {
298-
tasks.named("preBuild") {
299-
dependsOn(moveJDBCJNIFiles)
300-
}
301-
}
302-
303267
tasks.named<ProcessResources>(kotlin.jvm().compilations["main"].processResourcesTaskName) {
304268
from(downloadPowersyncDesktopBinaries)
305269
}

core/proguard-rules.pro

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,23 @@
11
package com.powersync
22

33
import android.content.Context
4-
import com.powersync.db.JdbcSqliteDriver
5-
import com.powersync.db.buildDefaultWalProperties
6-
import com.powersync.db.internal.InternalSchema
7-
import com.powersync.db.migrateDriver
8-
import kotlinx.coroutines.CoroutineScope
9-
import org.sqlite.SQLiteCommitListener
10-
import java.util.concurrent.atomic.AtomicBoolean
4+
import androidx.sqlite.SQLiteConnection
5+
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
116

127
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
138
public actual class DatabaseDriverFactory(
149
private val context: Context,
1510
) {
16-
internal actual fun createDriver(
17-
scope: CoroutineScope,
18-
dbFilename: String,
19-
dbDirectory: String?,
20-
readOnly: Boolean,
21-
): PsSqlDriver {
22-
val schema = InternalSchema
11+
private val driver = BundledSQLiteDriver().also { it.addPowerSyncExtension() }
2312

24-
val dbPath =
25-
if (dbDirectory != null) {
26-
"$dbDirectory/$dbFilename"
27-
} else {
28-
context.getDatabasePath(dbFilename)
29-
}
13+
internal actual fun resolveDefaultDatabasePath(dbFilename: String): String = context.getDatabasePath(dbFilename).path
3014

31-
val properties = buildDefaultWalProperties(readOnly = readOnly)
32-
val isFirst = IS_FIRST_CONNECTION.getAndSet(false)
33-
if (isFirst) {
34-
// Make sure the temp_store_directory points towards a temporary directory we actually
35-
// have access to. Due to sandboxing, the default /tmp/ is inaccessible.
36-
// The temp_store_directory pragma is deprecated and not thread-safe, so we only set it
37-
// on the first connection (it sets a global field and will affect every connection
38-
// opened).
39-
val escapedPath = context.cacheDir.absolutePath.replace("\"", "\"\"")
40-
properties.setProperty("temp_store_directory", "\"$escapedPath\"")
41-
}
42-
43-
val driver =
44-
JdbcSqliteDriver(
45-
url = "jdbc:sqlite:$dbPath",
46-
properties = properties,
47-
)
48-
49-
migrateDriver(driver, schema)
50-
51-
driver.loadExtensions(
52-
"libpowersync.so" to "sqlite3_powersync_init",
53-
)
54-
55-
val mappedDriver = PsSqlDriver(driver = driver)
56-
57-
driver.connection.database.addUpdateListener { _, _, table, _ ->
58-
mappedDriver.updateTable(table)
59-
}
60-
61-
driver.connection.database.addCommitListener(
62-
object : SQLiteCommitListener {
63-
override fun onCommit() {
64-
// We track transactions manually
65-
}
66-
67-
override fun onRollback() {
68-
mappedDriver.clearTableUpdates()
69-
}
70-
},
71-
)
72-
73-
return mappedDriver
74-
}
15+
internal actual fun openConnection(
16+
path: String,
17+
openFlags: Int,
18+
): SQLiteConnection = driver.open(path, openFlags)
19+
}
7520

76-
private companion object {
77-
val IS_FIRST_CONNECTION = AtomicBoolean(true)
78-
}
21+
public fun BundledSQLiteDriver.addPowerSyncExtension() {
22+
addExtension("libpowersync.so", "sqlite3_powersync_init")
7923
}

0 commit comments

Comments
 (0)