From 38b9c1f49b9eead75784473d97d9c5cc9d2c86ea Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Tue, 11 Feb 2025 11:01:10 +0200 Subject: [PATCH 1/5] chore: make execute throwable in swift --- CHANGELOG.md | 5 +++++ .../db/internal/InternalDatabaseImpl.kt | 16 +++++++++------- gradle.properties | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1904367..0f5317e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog +## 1.0.0-BETA23 + +* Make `execute` throwable for Swift + ## 1.0.0-BETA22 + * Fix `updateHasSynced` internal null pointer exception ## 1.0.0-BETA21 diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index c609010f..6096220a 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -91,13 +91,15 @@ internal class InternalDatabaseImpl( ): Long { val numParams = parameters?.size ?: 0 - return driver - .execute( - identifier = null, - sql = sql, - parameters = numParams, - binders = getBindersFromParams(parameters), - ).value + return runWrapped { + driver + .execute( + identifier = null, + sql = sql, + parameters = numParams, + binders = getBindersFromParams(parameters), + ).value + } } override suspend fun get( diff --git a/gradle.properties b/gradle.properties index 7bb8e53a..a557879d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ development=true RELEASE_SIGNING_ENABLED=true # Library config GROUP=com.powersync -LIBRARY_VERSION=1.0.0-BETA22 +LIBRARY_VERSION=1.0.0-BETA23 GITHUB_REPO=https://github.com/powersync-ja/powersync-kotlin.git # POM POM_URL=https://github.com/powersync-ja/powersync-kotlin/ From 56f44fdfcd7a290087c869b505b4acc13e786e00 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 12 Feb 2025 14:15:18 +0200 Subject: [PATCH 2/5] chore: make transaction functions throwable --- .../com/powersync/db/internal/PowerSyncTransaction.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt index 56d80138..3902b5f8 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt @@ -1,25 +1,31 @@ package com.powersync.db.internal +import com.powersync.PowerSyncException import com.powersync.db.SqlCursor +import kotlin.coroutines.cancellation.CancellationException public interface PowerSyncTransaction { + @Throws(PowerSyncException::class, CancellationException::class) public fun execute( sql: String, parameters: List? = listOf(), ): Long + @Throws(PowerSyncException::class, CancellationException::class) public fun getOptional( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): RowType? + @Throws(PowerSyncException::class, CancellationException::class) public fun getAll( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): List + @Throws(PowerSyncException::class, CancellationException::class) public fun get( sql: String, parameters: List? = listOf(), From d781345dbd3a739736fe1f2c38f08c63927dcfaf Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 12 Feb 2025 15:04:53 +0200 Subject: [PATCH 3/5] chore: add working solution so far --- CHANGELOG.md | 2 +- .../com/powersync/db/PowerSyncDatabaseImpl.kt | 16 ++++++++-------- .../db/internal/InternalDatabaseImpl.kt | 19 +++++++++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f5317e1..8d7822e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.0.0-BETA23 -* Make `execute` throwable for Swift +* Make `execute` and `PowerSyncTransaction` functions throwable for Swift ## 1.0.0-BETA22 diff --git a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index f99340e1..0194d0c4 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -164,9 +164,9 @@ internal class PowerSyncDatabaseImpl( } override suspend fun getNextCrudTransaction(): CrudTransaction? { - return internalDb.readTransaction { + return internalDb.readTransaction { transaction -> val entry = - bucketStorage.nextCrudItem(this) + bucketStorage.nextCrudItem(transaction) ?: return@readTransaction null val txId = entry.transactionId @@ -222,9 +222,9 @@ internal class PowerSyncDatabaseImpl( mapper: (SqlCursor) -> RowType, ): Flow> = internalDb.watch(sql, parameters, mapper) - override suspend fun readTransaction(callback: (tx: PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) + override suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) - override suspend fun writeTransaction(callback: (tx: PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) + override suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) override suspend fun execute( sql: String, @@ -235,16 +235,16 @@ internal class PowerSyncDatabaseImpl( lastTransactionId: Int, writeCheckpoint: String?, ) { - internalDb.writeTransaction { + internalDb.writeTransaction { transaction -> internalDb.queries.deleteEntriesWithIdLessThan(lastTransactionId.toLong()) - if (writeCheckpoint != null && !bucketStorage.hasCrud(this)) { - execute( + if (writeCheckpoint != null && !bucketStorage.hasCrud(transaction)) { + transaction.execute( "UPDATE ps_buckets SET target_op = CAST(? AS INTEGER) WHERE name='\$local'", listOf(writeCheckpoint), ) } else { - execute( + transaction.execute( "UPDATE ps_buckets SET target_op = CAST(? AS INTEGER) WHERE name='\$local'", listOf(bucketStorage.getMaxOpId()), ) diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index 6096220a..37ef964f 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -219,14 +219,24 @@ internal class InternalDatabaseImpl( override suspend fun readTransaction(callback: PowerSyncTransaction.() -> R): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { - callback(transaction) + val res = callback(transaction) + if (res == TransactionResponse.ROLLBACK) { + rollback(null as R) + } else { + res + } } } override suspend fun writeTransaction(callback: PowerSyncTransaction.() -> R): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { - callback(transaction) + val res = callback(transaction) + if (res == TransactionResponse.ROLLBACK) { + rollback(null as R) + } else { + res + } } } @@ -333,3 +343,8 @@ internal fun getBindersFromParams(parameters: List?): (SqlPreparedStatemen } } } + +public enum class TransactionResponse { + ROLLBACK, +} + From 5c3d23341ff0631240e6c49ae451a2fcfa2c7cb6 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 12 Feb 2025 15:20:04 +0200 Subject: [PATCH 4/5] fix: type issue --- .../kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index 37ef964f..e28df1fe 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -216,7 +216,7 @@ internal class InternalDatabaseImpl( } } - override suspend fun readTransaction(callback: PowerSyncTransaction.() -> R): R = + override suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { val res = callback(transaction) @@ -228,7 +228,7 @@ internal class InternalDatabaseImpl( } } - override suspend fun writeTransaction(callback: PowerSyncTransaction.() -> R): R = + override suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { val res = callback(transaction) From 31664745ee8108abd25328088d7c3470990914fd Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Thu, 13 Feb 2025 08:56:57 +0200 Subject: [PATCH 5/5] chore: use throwablecallback --- .../com/powersync/db/PowerSyncDatabaseImpl.kt | 6 +-- .../kotlin/com/powersync/db/Queries.kt | 6 +-- .../db/internal/InternalDatabaseImpl.kt | 37 +++++++++++-------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index 0194d0c4..b7a7d5dc 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -13,7 +13,7 @@ import com.powersync.db.crud.CrudRow import com.powersync.db.crud.CrudTransaction import com.powersync.db.internal.InternalDatabaseImpl import com.powersync.db.internal.InternalTable -import com.powersync.db.internal.PowerSyncTransaction +import com.powersync.db.internal.ThrowableTransactionCallback import com.powersync.db.schema.Schema import com.powersync.sync.SyncStatus import com.powersync.sync.SyncStream @@ -222,9 +222,9 @@ internal class PowerSyncDatabaseImpl( mapper: (SqlCursor) -> RowType, ): Flow> = internalDb.watch(sql, parameters, mapper) - override suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) + override suspend fun readTransaction(callback: ThrowableTransactionCallback): R = internalDb.writeTransaction(callback) - override suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R = internalDb.writeTransaction(callback) + override suspend fun writeTransaction(callback: ThrowableTransactionCallback): R = internalDb.writeTransaction(callback) override suspend fun execute( sql: String, diff --git a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt index b4e8a806..f400297c 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt @@ -1,7 +1,7 @@ package com.powersync.db import com.powersync.PowerSyncException -import com.powersync.db.internal.PowerSyncTransaction +import com.powersync.db.internal.ThrowableTransactionCallback import kotlinx.coroutines.flow.Flow import kotlin.coroutines.cancellation.CancellationException @@ -58,8 +58,8 @@ public interface Queries { ): Flow> @Throws(PowerSyncException::class, CancellationException::class) - public suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R + public suspend fun writeTransaction(callback: ThrowableTransactionCallback): R @Throws(PowerSyncException::class, CancellationException::class) - public suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R + public suspend fun readTransaction(callback: ThrowableTransactionCallback): R } diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index e28df1fe..d8252877 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -7,11 +7,13 @@ import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlPreparedStatement import com.persistence.PowersyncQueries +import com.powersync.PowerSyncException import com.powersync.PsSqlDriver import com.powersync.db.SqlCursor import com.powersync.db.runWrapped import com.powersync.persistence.PsDatabase import com.powersync.utils.JsonUtil +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview @@ -216,26 +218,28 @@ internal class InternalDatabaseImpl( } } - override suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R = + override suspend fun readTransaction(callback: ThrowableTransactionCallback): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { - val res = callback(transaction) - if (res == TransactionResponse.ROLLBACK) { - rollback(null as R) - } else { - res + runWrapped { + val result = callback.execute(transaction) + if (result is PowerSyncException) { + throw result + } + result } } } - override suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R = + override suspend fun writeTransaction(callback: ThrowableTransactionCallback): R = withContext(dbContext) { transactor.transactionWithResult(noEnclosing = true) { - val res = callback(transaction) - if (res == TransactionResponse.ROLLBACK) { - rollback(null as R) - } else { - res + runWrapped { + val result = callback.execute(transaction) + if (result is PowerSyncException) { + throw result + } + result } } } @@ -344,7 +348,10 @@ internal fun getBindersFromParams(parameters: List?): (SqlPreparedStatemen } } -public enum class TransactionResponse { - ROLLBACK, +/** + * Kotlin allows SAM (Single Abstract Method) interfaces to be treated like lambda expressions. + */ +public fun interface ThrowableTransactionCallback { + @Throws(PowerSyncException::class, CancellationException::class) + public fun execute(transaction: PowerSyncTransaction): R } -