Skip to content

Commit f41b0a4

Browse files
committed
Fix Swift tests
1 parent 8f5f8cd commit f41b0a4

File tree

12 files changed

+87
-70
lines changed

12 files changed

+87
-70
lines changed

core/src/appleMain/kotlin/com/powersync/DatabaseDriverFactory.apple.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.powersync
22

33
import kotlinx.cinterop.UnsafeNumber
4+
import kotlinx.io.files.FileSystem
45
import platform.Foundation.NSApplicationSupportDirectory
56
import platform.Foundation.NSBundle
67
import platform.Foundation.NSFileManager
@@ -10,7 +11,7 @@ import kotlin.getValue
1011

1112
@OptIn(UnsafeNumber::class)
1213
internal fun appleDefaultDatabasePath(dbFilename: String): String {
13-
// This needs to be compatible with https://github.com/touchlab/SQLiter/blob/a37bbe7e9c65e6a5a94c5bfcaccdaae55ad2bac9/sqliter-driver/src/appleMain/kotlin/co/touchlab/sqliter/DatabaseFileContext.kt#L36-L51
14+
// This needs to be compatible with https://github.com/touchlab/SQLiter/blob/a37bbe7e9c65e6a5a94c5bfcaccdaae55ad2bac9/sqliter-driver/src/appleMain/kotlin/co/touchlab/sqliter/DatabaseFileContext.kt#L36-L51
1415
val paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true)
1516
val documentsDirectory = paths[0] as String
1617

@@ -22,7 +23,7 @@ internal fun appleDefaultDatabasePath(dbFilename: String): String {
2223
fileManager.createDirectoryAtPath(databaseDirectory, true, null, null)
2324
}; // Create folder
2425

25-
return databaseDirectory
26+
return "$databaseDirectory/$dbFilename"
2627
}
2728

2829
internal val powerSyncExtensionPath: String by lazy {

core/src/appleNonWatchOsMain/kotlin/com/powersync/DatabaseDriverFactory.appleNonWatchOs.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.powersync
22

33
import androidx.sqlite.SQLiteConnection
44
import com.powersync.sqlite.Database
5-
import com.powersync.sqlite.SqliteException
65

76
@Suppress(names = ["EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING"])
87
public actual class DatabaseDriverFactory {
@@ -15,7 +14,7 @@ public actual class DatabaseDriverFactory {
1514
val db = Database.open(path, openFlags)
1615
try {
1716
db.loadExtension(powerSyncExtensionPath, "sqlite3_powersync_init")
18-
} catch (e: SqliteException) {
17+
} catch (e: PowerSyncException) {
1918
db.close()
2019
throw e
2120
}

core/src/appleTest/kotlin/com/powersync/sqlite/DatabaseTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.powersync.sqlite
22

33
import androidx.sqlite.SQLiteConnection
44
import androidx.sqlite.execSQL
5+
import com.powersync.PowerSyncException
56
import io.kotest.assertions.throwables.shouldThrow
67
import io.kotest.matchers.shouldBe
78
import kotlin.test.Test
@@ -22,9 +23,9 @@ class DatabaseTest {
2223
@Test
2324
fun syntaxError() =
2425
inMemoryDatabase().use {
25-
val exception = shouldThrow<SqliteException> { it.execSQL("bad syntax") }
26+
val exception = shouldThrow<PowerSyncException> { it.execSQL("bad syntax") }
2627

27-
exception.toString() shouldBe "SqliteException(1): SQL logic error at offset 0, near \"bad\": syntax error for SQL: bad syntax"
28+
exception.message shouldBe "SqliteException(1): SQL logic error at offset 0, near \"bad\": syntax error for SQL: bad syntax"
2829
Unit
2930
}
3031

core/src/appleTest/kotlin/com/powersync/sqlite/StatementTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.powersync.sqlite
22

33
import androidx.sqlite.SQLiteConnection
4+
import com.powersync.PowerSyncException
45
import io.kotest.assertions.throwables.shouldThrow
56
import io.kotest.matchers.shouldBe
67
import kotlin.test.Test
@@ -28,16 +29,16 @@ class StatementTest {
2829
fun testBindOutOfBounds() =
2930
inMemoryDatabase().use { db ->
3031
db.prepare("SELECT ?").use { stmt ->
31-
shouldThrow<SqliteException> {
32+
shouldThrow<PowerSyncException> {
3233
stmt.bindText(-1, "foo")
3334
}
34-
shouldThrow<SqliteException> {
35+
shouldThrow<PowerSyncException> {
3536
stmt.bindText(0, "foo")
3637
}
3738

3839
stmt.bindText(1, "foo")
3940

40-
shouldThrow<SqliteException> {
41+
shouldThrow<PowerSyncException> {
4142
stmt.bindText(2, "foo")
4243
}
4344
}

core/src/commonMain/kotlin/com/powersync/PowerSyncDatabaseFactory.kt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import co.touchlab.skie.configuration.annotations.DefaultArgumentInterop
55
import com.powersync.db.ActiveDatabaseGroup
66
import com.powersync.db.PowerSyncDatabaseImpl
77
import com.powersync.db.driver.InternalConnectionPool
8+
import com.powersync.db.driver.LazyPool
89
import com.powersync.db.schema.Schema
910
import com.powersync.utils.generateLogger
1011
import kotlinx.coroutines.CoroutineScope
@@ -55,13 +56,15 @@ internal fun createPowerSyncDatabaseImpl(
5556
val activeDatabaseGroup = ActiveDatabaseGroup.referenceDatabase(logger, identifier)
5657

5758
val pool =
58-
InternalConnectionPool(
59-
factory,
60-
scope,
61-
dbFilename,
62-
dbDirectory,
63-
activeDatabaseGroup.first.group.writeLockMutex,
64-
)
59+
LazyPool {
60+
InternalConnectionPool(
61+
factory,
62+
scope,
63+
dbFilename,
64+
dbDirectory,
65+
activeDatabaseGroup.first.group.writeLockMutex,
66+
)
67+
}
6568

6669
return PowerSyncDatabase.opened(
6770
pool,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.powersync.db.driver
2+
3+
import com.powersync.ExperimentalPowerSyncAPI
4+
import kotlinx.coroutines.flow.SharedFlow
5+
6+
/**
7+
* A [SQLiteConnectionPool] implemented by constructing an inner pool on first access.
8+
*
9+
* This allows [InternalConnectionPool] to construct connections immediately (which potentially
10+
* throws an exception that we want to report when the SDK is actually used instead of when it's
11+
* first constructed).
12+
*/
13+
@OptIn(ExperimentalPowerSyncAPI::class)
14+
internal class LazyPool(
15+
openInner: () -> SQLiteConnectionPool,
16+
) : SQLiteConnectionPool {
17+
private val pool by lazy(openInner)
18+
19+
override suspend fun <T> read(callback: suspend (SQLiteConnectionLease) -> T): T = pool.read(callback)
20+
21+
override suspend fun <T> write(callback: suspend (SQLiteConnectionLease) -> T): T = pool.write(callback)
22+
23+
override suspend fun <R> withAllConnections(action: suspend (SQLiteConnectionLease, List<SQLiteConnectionLease>) -> R) =
24+
pool.withAllConnections(action)
25+
26+
override val updates: SharedFlow<Set<String>>
27+
get() = pool.updates
28+
29+
override suspend fun close() = pool.close()
30+
}

core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,17 +210,6 @@ internal class InternalDatabaseImpl(
210210
// Register callback for table updates on a specific table
211211
override fun updatesOnTables(): SharedFlow<Set<String>> = pool.updates
212212

213-
// Unfortunately Errors can't be thrown from Swift SDK callbacks.
214-
// These are currently returned and should be thrown here.
215-
private inline fun <R> catchSwiftExceptions(action: () -> R): R {
216-
val result = action()
217-
218-
if (result is PowerSyncException) {
219-
throw result
220-
}
221-
return result
222-
}
223-
224213
private suspend fun getSourceTables(
225214
sql: String,
226215
parameters: List<Any?>?,

core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,14 @@ internal class PowerSyncTransactionImpl(
5959
@ExperimentalPowerSyncAPI
6060
internal suspend fun <T> SQLiteConnectionLease.runTransaction(cb: suspend (PowerSyncTransaction) -> T): T {
6161
execSQL("BEGIN")
62-
var didComplete = false
6362
return try {
6463
val result = cb(PowerSyncTransactionImpl(this))
65-
didComplete = true
6664

6765
check(isInTransaction())
6866
execSQL("COMMIT")
6967
result
7068
} catch (e: Throwable) {
71-
if (!didComplete && isInTransaction()) {
69+
if (isInTransaction()) {
7270
execSQL("ROLLBACK")
7371
}
7472

core/src/nativeMain/interop/sqlite3.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ int sqlite3_load_extension(
3232
const char *zProc, /* Entry point. Derived from zFile if 0 */
3333
char **pzErrMsg /* Put error message here if not 0 */
3434
);
35+
int sqlite3_extended_result_codes(sqlite3*, int onoff);
3536

3637
// Statements
3738
int sqlite3_prepare16_v3(

core/src/nativeMain/kotlin/com/powersync/sqlite/Database.kt

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import androidx.sqlite.SQLiteConnection
44
import androidx.sqlite.SQLiteStatement
55
import cnames.structs.sqlite3
66
import cnames.structs.sqlite3_stmt
7+
import com.powersync.PowerSyncException
78
import com.powersync.internal.sqlite3.sqlite3_close_v2
89
import com.powersync.internal.sqlite3.sqlite3_db_config
10+
import com.powersync.internal.sqlite3.sqlite3_extended_result_codes
911
import com.powersync.internal.sqlite3.sqlite3_free
1012
import com.powersync.internal.sqlite3.sqlite3_get_autocommit
1113
import com.powersync.internal.sqlite3.sqlite3_initialize
@@ -24,6 +26,14 @@ import kotlinx.cinterop.toKStringFromUtf8
2426
import kotlinx.cinterop.utf16
2527
import kotlinx.cinterop.value
2628

29+
/**
30+
* A simple implementation of the [SQLiteConnection] interface backed by a synchronous `sqlite3*`
31+
* database pointer and the SQLite C APIs called via cinterop.
32+
*
33+
* Multiple instances of this class are bundled into an
34+
* [com.powersync.db.driver.InternalConnectionPool] and called from [kotlinx.coroutines.Dispatchers.IO]
35+
* to make these APIs asynchronous.
36+
*/
2737
internal class Database(
2838
private val ptr: CPointer<sqlite3>,
2939
) : SQLiteConnection {
@@ -55,7 +65,7 @@ internal class Database(
5565
sqlite3_free(errorMessagePointer.value)
5666
}
5767

58-
throw SqliteException(resultCode, errorMessage ?: "unknown error")
68+
throw PowerSyncException("Could not load extension ($resultCode): ${errorMessage ?: "unknown error"}", null)
5969
}
6070
}
6171

@@ -65,7 +75,7 @@ internal class Database(
6575

6676
private fun Int.checkResult(stmt: String? = null) {
6777
if (this != 0) {
68-
throw SqliteException.createExceptionInDatabase(this, ptr, stmt)
78+
throw createExceptionInDatabase(ptr, stmt)
6979
}
7080
}
7181

@@ -77,17 +87,21 @@ internal class Database(
7787
memScoped {
7888
var rc = sqlite3_initialize()
7989
if (rc != 0) {
80-
throw SqliteException.createExceptionOutsideOfDatabase(rc)
90+
throw PowerSyncException("sqlite3_initialize() failed", null)
8191
}
8292

8393
val encodedPath = path.cstr.getPointer(this)
8494
val ptr = allocPointerTo<sqlite3>()
8595
rc = sqlite3_open_v2(encodedPath, ptr.ptr, flags, null)
8696
if (rc != 0) {
87-
throw SqliteException.createExceptionOutsideOfDatabase(rc)
97+
throw PowerSyncException("Could not open database $path with $flags", null)
8898
}
8999

90100
val db = ptr.value!!
101+
102+
// Enable extended error codes.
103+
sqlite3_extended_result_codes(db, 1)
104+
91105
// Enable extensions via the C API
92106
sqlite3_db_config(db, DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 0)
93107

0 commit comments

Comments
 (0)