diff --git a/common/src/nativeMain/kotlin/com/powersync/db/NativeConnectionFactory.kt b/common/src/nativeMain/kotlin/com/powersync/db/NativeConnectionFactory.kt new file mode 100644 index 00000000..e92eb10b --- /dev/null +++ b/common/src/nativeMain/kotlin/com/powersync/db/NativeConnectionFactory.kt @@ -0,0 +1,38 @@ +package com.powersync.db + +import androidx.sqlite.SQLiteConnection +import com.powersync.PersistentConnectionFactory +import com.powersync.PowerSyncException +import com.powersync.sqlite.Database + +/** + * A [PersistentConnectionFactory] implementation delegating to static `sqlite3_` invocations through cinterop. + */ +public abstract class NativeConnectionFactory: PersistentConnectionFactory { + override fun openConnection(path: String, openFlags: Int): SQLiteConnection { + val extensionPath = powersyncLoadableExtensionPath() + val db = Database.open(path, openFlags) + + if (extensionPath != null) { + try { + db.loadExtension(extensionPath, "sqlite3_powersync_init") + } catch (e: PowerSyncException) { + db.close() + throw e + } + } + + return db + } + + override fun openInMemoryConnection(): SQLiteConnection { + return openConnection(":memory:", 0x02) + } + + /** + * If the core extension should be loaded as a dynamic library, returns its path. + * + * Otherwise, installs the core extension as a static extension and returns null. + */ + protected abstract fun powersyncLoadableExtensionPath(): String? +} diff --git a/core/src/nativeMain/kotlin/com/powersync/DatabaseDriverFactory.native.kt b/core/src/nativeMain/kotlin/com/powersync/DatabaseDriverFactory.native.kt index 86d6c093..3dd32a8b 100644 --- a/core/src/nativeMain/kotlin/com/powersync/DatabaseDriverFactory.native.kt +++ b/core/src/nativeMain/kotlin/com/powersync/DatabaseDriverFactory.native.kt @@ -1,29 +1,14 @@ package com.powersync -import androidx.sqlite.SQLiteConnection -import com.powersync.sqlite.Database +import com.powersync.db.NativeConnectionFactory @Suppress(names = ["EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING"]) -public actual class DatabaseDriverFactory: PersistentConnectionFactory { +public actual class DatabaseDriverFactory: PersistentConnectionFactory, NativeConnectionFactory() { actual override fun resolveDefaultDatabasePath(dbFilename: String): String = appleDefaultDatabasePath(dbFilename) @OptIn(ExperimentalPowerSyncAPI::class) - actual override fun openConnection( - path: String, - openFlags: Int, - ): SQLiteConnection { - val db = Database.open(path, openFlags) - try { - db.loadExtension(resolvePowerSyncLoadableExtensionPath()!!, "sqlite3_powersync_init") - } catch (e: PowerSyncException) { - db.close() - throw e - } - return db - } - - actual override fun openInMemoryConnection(): SQLiteConnection { - return openConnection(":memory:", 0x02) + override fun powersyncLoadableExtensionPath(): String? { + return resolvePowerSyncLoadableExtensionPath() } } diff --git a/internal/PowerSyncKotlin/build.gradle.kts b/internal/PowerSyncKotlin/build.gradle.kts index 505bf65f..97019ac5 100644 --- a/internal/PowerSyncKotlin/build.gradle.kts +++ b/internal/PowerSyncKotlin/build.gradle.kts @@ -25,7 +25,7 @@ kotlin { baseName = "PowerSyncKotlin" xcf.add(this) - export(project(":core")) + export(projects.common) isStatic = true binaryOption("bundleId", "PowerSyncKotlin") @@ -45,8 +45,9 @@ kotlin { sourceSets { commonMain.dependencies { - api(project(":core")) + api(projects.common) implementation(libs.ktor.client.logging) + implementation(libs.ktor.client.darwin) } } } diff --git a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt index 81524368..c7cfb1f5 100644 --- a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt +++ b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/SDK.kt @@ -2,6 +2,9 @@ package com.powersync +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.execSQL +import com.powersync.db.NativeConnectionFactory import com.powersync.db.crud.CrudTransaction import com.powersync.sync.SyncClientConfiguration import com.powersync.sync.SyncOptions @@ -12,6 +15,33 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map import io.ktor.client.plugins.logging.Logger as KtorLogger +public fun sqlite3DatabaseFactory(initialStatements: List): PersistentConnectionFactory { + @OptIn(ExperimentalPowerSyncAPI::class) + return object : NativeConnectionFactory() { + override fun powersyncLoadableExtensionPath(): String? { + return resolvePowerSyncLoadableExtensionPath() + } + + override fun resolveDefaultDatabasePath(dbFilename: String): String { + return appleDefaultDatabasePath(dbFilename) + } + + override fun openConnection(path: String, openFlags: Int): SQLiteConnection { + val conn = super.openConnection(path, openFlags) + try { + for (statement in initialStatements) { + conn.execSQL(statement) + } + } catch (e: PowerSyncException) { + conn.close() + throw e + } + + return super.openConnection(path, openFlags) + } + } +} + /** * Helper class designed to bridge SKIEE methods and allow them to throw * `PowerSyncException`. This is necessary because these exceptions cannot