diff --git a/Package.swift b/Package.swift index f67490a2..a6c2ad52 100644 --- a/Package.swift +++ b/Package.swift @@ -21,4 +21,4 @@ let package = Package( ) , ] -) +) \ No newline at end of file diff --git a/connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt b/connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt index 1ada35eb..577c1757 100644 --- a/connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt +++ b/connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt @@ -2,10 +2,12 @@ package com.powersync.connector.supabase import co.touchlab.kermit.Logger import com.powersync.PowerSyncDatabase +import com.powersync.PowerSyncException import com.powersync.connectors.PowerSyncBackendConnector import com.powersync.connectors.PowerSyncCredentials import com.powersync.db.crud.CrudEntry import com.powersync.db.crud.UpdateType +import com.powersync.db.runWrappedSuspending import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.annotations.SupabaseInternal import io.github.jan.supabase.auth.Auth @@ -90,55 +92,70 @@ public class SupabaseConnector( } } + @Throws(PowerSyncException::class) public suspend fun login( email: String, password: String, ) { - supabaseClient.auth.signInWith(Email) { - this.email = email - this.password = password + runWrappedSuspending { + supabaseClient.auth.signInWith(Email) { + this.email = email + this.password = password + } } } + @Throws(PowerSyncException::class) public suspend fun signUp( email: String, password: String, ) { - supabaseClient.auth.signUpWith(Email) { - this.email = email - this.password = password + runWrappedSuspending { + supabaseClient.auth.signUpWith(Email) { + this.email = email + this.password = password + } } } + @Throws(PowerSyncException::class) public suspend fun signOut() { - supabaseClient.auth.signOut() + runWrappedSuspending { + supabaseClient.auth.signOut() + } } public fun session(): UserSession? = supabaseClient.auth.currentSessionOrNull() public val sessionStatus: StateFlow = supabaseClient.auth.sessionStatus + @Throws(PowerSyncException::class) public suspend fun loginAnonymously() { - supabaseClient.auth.signInAnonymously() + runWrappedSuspending { + supabaseClient.auth.signInAnonymously() + } } /** * Get credentials for PowerSync. */ + @Throws(PowerSyncException::class) override suspend fun fetchCredentials(): PowerSyncCredentials { - check(supabaseClient.auth.sessionStatus.value is SessionStatus.Authenticated) { "Supabase client is not authenticated" } + return runWrappedSuspending { + check(supabaseClient.auth.sessionStatus.value is SessionStatus.Authenticated) { "Supabase client is not authenticated" } - // Use Supabase token for PowerSync - val session = supabaseClient.auth.currentSessionOrNull() ?: error("Could not fetch Supabase credentials") + // Use Supabase token for PowerSync + val session = supabaseClient.auth.currentSessionOrNull() ?: error("Could not fetch Supabase credentials") - check(session.user != null) { "No user data" } + check(session.user != null) { "No user data" } - // userId is for debugging purposes only - return PowerSyncCredentials( - endpoint = powerSyncEndpoint, - token = session.accessToken, // Use the access token to authenticate against PowerSync - userId = session.user!!.id, - ) + // userId is for debugging purposes only + PowerSyncCredentials( + endpoint = powerSyncEndpoint, + token = session.accessToken, // Use the access token to authenticate against PowerSync + userId = session.user!!.id, + ) + } } /** @@ -147,60 +164,63 @@ public class SupabaseConnector( * This function is called whenever there is data to upload, whether the device is online or offline. * If this call throws an error, it is retried periodically. */ + @Throws(PowerSyncException::class) override suspend fun uploadData(database: PowerSyncDatabase) { - val transaction = database.getNextCrudTransaction() ?: return + return runWrappedSuspending { + val transaction = database.getNextCrudTransaction() ?: return@runWrappedSuspending - var lastEntry: CrudEntry? = null - try { - for (entry in transaction.crud) { - lastEntry = entry + var lastEntry: CrudEntry? = null + try { + for (entry in transaction.crud) { + lastEntry = entry - val table = supabaseClient.from(entry.table) + val table = supabaseClient.from(entry.table) - when (entry.op) { - UpdateType.PUT -> { - val data = entry.opData?.toMutableMap() ?: mutableMapOf() - data["id"] = entry.id - table.upsert(data) - } + when (entry.op) { + UpdateType.PUT -> { + val data = entry.opData?.toMutableMap() ?: mutableMapOf() + data["id"] = entry.id + table.upsert(data) + } - UpdateType.PATCH -> { - table.update(entry.opData!!) { - filter { - eq("id", entry.id) + UpdateType.PATCH -> { + table.update(entry.opData!!) { + filter { + eq("id", entry.id) + } } } - } - UpdateType.DELETE -> { - table.delete { - filter { - eq("id", entry.id) + UpdateType.DELETE -> { + table.delete { + filter { + eq("id", entry.id) + } } } } } - } - transaction.complete(null) - } catch (e: Exception) { - if (errorCode != null && PostgresFatalCodes.isFatalError(errorCode.toString())) { - /** - * Instead of blocking the queue with these errors, - * discard the (rest of the) transaction. - * - * Note that these errors typically indicate a bug in the application. - * If protecting against data loss is important, save the failing records - * elsewhere instead of discarding, and/or notify the user. - */ - Logger.e("Data upload error: ${e.message}") - Logger.e("Discarding entry: $lastEntry") transaction.complete(null) - return - } + } catch (e: Exception) { + if (errorCode != null && PostgresFatalCodes.isFatalError(errorCode.toString())) { + /** + * Instead of blocking the queue with these errors, + * discard the (rest of the) transaction. + * + * Note that these errors typically indicate a bug in the application. + * If protecting against data loss is important, save the failing records + * elsewhere instead of discarding, and/or notify the user. + */ + Logger.e("Data upload error: ${e.message}") + Logger.e("Discarding entry: $lastEntry") + transaction.complete(null) + return@runWrappedSuspending + } - Logger.e("Data upload error - retrying last entry: $lastEntry, $e") - throw e + Logger.e("Data upload error - retrying last entry: $lastEntry, $e") + throw e + } } } } diff --git a/core/src/commonMain/kotlin/com/powersync/Exceptions.kt b/core/src/commonMain/kotlin/com/powersync/Exceptions.kt new file mode 100644 index 00000000..1c9388a4 --- /dev/null +++ b/core/src/commonMain/kotlin/com/powersync/Exceptions.kt @@ -0,0 +1,3 @@ +package com.powersync + +public class PowerSyncException(message: String, cause: Throwable): Exception(message, cause) \ No newline at end of file diff --git a/core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt b/core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt index 888751e2..60fa5575 100644 --- a/core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt +++ b/core/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt @@ -6,6 +6,7 @@ import com.powersync.db.crud.CrudBatch import com.powersync.db.crud.CrudTransaction import com.powersync.sync.SyncStatus import com.powersync.utils.JsonParam +import kotlin.coroutines.cancellation.CancellationException /** * A PowerSync managed database. @@ -25,6 +26,7 @@ public interface PowerSyncDatabase : Queries { /** * Suspend function that resolves when the first sync has occurred */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun waitForFirstSync() /** @@ -56,7 +58,7 @@ public interface PowerSyncDatabase : Queries { * ``` * TODO: Internal Team - Status changes are reported on [statusStream]. */ - + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun connect( connector: PowerSyncBackendConnector, crudThrottleMs: Long = 1000L, @@ -81,6 +83,7 @@ public interface PowerSyncDatabase : Queries { * data by transaction. One batch may contain data from multiple transactions, * and a single transaction may be split over multiple batches. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun getCrudBatch(limit: Int = 100): CrudBatch? /** @@ -96,12 +99,13 @@ public interface PowerSyncDatabase : Queries { * Unlike [getCrudBatch], this only returns data from a single transaction at a time. * All data for the transaction is loaded into memory. */ - + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun getNextCrudTransaction(): CrudTransaction? /** * Convenience method to get the current version of PowerSync. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun getPowerSyncVersion(): String /** @@ -109,6 +113,7 @@ public interface PowerSyncDatabase : Queries { * * Use [connect] to connect again. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun disconnect() /** @@ -119,6 +124,7 @@ public interface PowerSyncDatabase : Queries { * * To preserve data in local-only tables, set clearLocal to false. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun disconnectAndClear(clearLocal: Boolean = true) /** @@ -127,5 +133,6 @@ public interface PowerSyncDatabase : Queries { * * Once close is called, this database cannot be used again - a new one must be constructed. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun close() } diff --git a/core/src/commonMain/kotlin/com/powersync/connectors/PowerSyncBackendConnector.kt b/core/src/commonMain/kotlin/com/powersync/connectors/PowerSyncBackendConnector.kt index c20488a2..e1ce40af 100644 --- a/core/src/commonMain/kotlin/com/powersync/connectors/PowerSyncBackendConnector.kt +++ b/core/src/commonMain/kotlin/com/powersync/connectors/PowerSyncBackendConnector.kt @@ -1,11 +1,14 @@ package com.powersync.connectors import com.powersync.PowerSyncDatabase +import com.powersync.PowerSyncException +import com.powersync.db.runWrappedSuspending import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch +import kotlin.coroutines.cancellation.CancellationException /** * Implement this to connect an app backend. @@ -26,10 +29,13 @@ public abstract class PowerSyncBackendConnector { * * These credentials may have expired already. */ + @Throws(PowerSyncException::class, CancellationException::class) public open suspend fun getCredentialsCached(): PowerSyncCredentials? { - cachedCredentials?.let { return it } - prefetchCredentials()?.join() - return cachedCredentials + return runWrappedSuspending { + cachedCredentials?.let { return@runWrappedSuspending it } + prefetchCredentials()?.join() + cachedCredentials + } } /** @@ -49,6 +55,7 @@ public abstract class PowerSyncBackendConnector { * * This may be called before the current credentials have expired. */ + @Throws(PowerSyncException::class, CancellationException::class) public open suspend fun prefetchCredentials(): Job? { fetchRequest?.takeIf { it.isActive }?.let { return it } @@ -74,6 +81,7 @@ public abstract class PowerSyncBackendConnector { * * This token is kept for the duration of a sync connection. */ + @Throws(PowerSyncException::class, CancellationException::class) public abstract suspend fun fetchCredentials(): PowerSyncCredentials? /** @@ -83,5 +91,6 @@ public abstract class PowerSyncBackendConnector { * * Any thrown errors will result in a retry after the configured wait period (default: 5 seconds). */ + @Throws(PowerSyncException::class, CancellationException::class) public abstract suspend fun uploadData(database: PowerSyncDatabase) } diff --git a/core/src/commonMain/kotlin/com/powersync/db/Functions.kt b/core/src/commonMain/kotlin/com/powersync/db/Functions.kt new file mode 100644 index 00000000..6dc47b5c --- /dev/null +++ b/core/src/commonMain/kotlin/com/powersync/db/Functions.kt @@ -0,0 +1,23 @@ +package com.powersync.db + +import com.powersync.PowerSyncException + +public fun runWrapped(block: () -> R): R = try { + block() +} catch (t: Throwable) { + if (t is PowerSyncException) { + throw t + } else { + throw PowerSyncException(t.message ?: "Unknown internal exception", t) + } +} + +public suspend fun runWrappedSuspending(block: suspend () -> R): R = try { + block() +} catch (t: Throwable) { + if (t is PowerSyncException) { + throw t + } else { + throw PowerSyncException(t.message ?: "Unknown internal exception", t) + } +} \ No newline at end of file diff --git a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index cefd591f..f3c3149e 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -290,7 +290,7 @@ internal class PowerSyncDatabaseImpl( val hasSynced = timestamp != null if (hasSynced != currentStatus.hasSynced) { - val formattedDateTime = "${timestamp!!.replace(" ","T").toLocalDateTime()}Z" + val formattedDateTime = "${timestamp!!.replace(" ", "T").toLocalDateTime()}Z" val lastSyncedAt = Instant.parse(formattedDateTime) currentStatus.update(hasSynced = hasSynced, lastSyncedAt = lastSyncedAt) } diff --git a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt index 90c72e19..b4e8a806 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt @@ -1,12 +1,15 @@ package com.powersync.db +import com.powersync.PowerSyncException import com.powersync.db.internal.PowerSyncTransaction import kotlinx.coroutines.flow.Flow +import kotlin.coroutines.cancellation.CancellationException public interface Queries { /** * Execute a write query (INSERT, UPDATE, DELETE) */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun execute( sql: String, parameters: List? = listOf(), @@ -17,6 +20,7 @@ public interface Queries { * If there is no result, throws an [IllegalArgumentException]. * See [getOptional] for queries where the result might be empty. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun get( sql: String, parameters: List? = listOf(), @@ -26,6 +30,7 @@ public interface Queries { /** * Execute a read-only (SELECT) query and return the results. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun getAll( sql: String, parameters: List? = listOf(), @@ -35,6 +40,7 @@ public interface Queries { /** * Execute a read-only (SELECT) query and return a single optional result. */ + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun getOptional( sql: String, parameters: List? = listOf(), @@ -44,13 +50,16 @@ public interface Queries { /** * Execute a read-only (SELECT) query every time the source tables are modified and return the results as a List in [Flow]. */ + @Throws(PowerSyncException::class, CancellationException::class) public fun watch( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): Flow> + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun writeTransaction(callback: (PowerSyncTransaction) -> R): R + @Throws(PowerSyncException::class, CancellationException::class) public suspend fun readTransaction(callback: (PowerSyncTransaction) -> R): R } diff --git a/core/src/commonMain/kotlin/com/powersync/db/SqlCursor.kt b/core/src/commonMain/kotlin/com/powersync/db/SqlCursor.kt index d1a7f1bb..a579d2ac 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/SqlCursor.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/SqlCursor.kt @@ -1,23 +1,59 @@ package com.powersync.db -import co.touchlab.skie.configuration.annotations.FunctionInterop +import com.powersync.PowerSyncException public interface SqlCursor { + @Throws(PowerSyncException::class) public fun getBoolean(index: Int): Boolean? + @Throws(PowerSyncException::class) public fun getBytes(index: Int): ByteArray? + @Throws(PowerSyncException::class) public fun getDouble(index: Int): Double? + @Throws(PowerSyncException::class) public fun getLong(index: Int): Long? + @Throws(PowerSyncException::class) public fun getString(index: Int): String? + @Throws(PowerSyncException::class) public fun columnName(index: Int): String? public val columnCount: Int public val columnNames: Map + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getBoolean(name: String): Boolean = getColumnValue(name) { getBoolean(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getBytes(name: String): ByteArray = getColumnValue(name) { getBytes(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getDouble(name: String): Double = getColumnValue(name) { getDouble(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getLong(name: String): Long = getColumnValue(name) { getLong(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getString(name: String): String = getColumnValue(name) { getString(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getBooleanOptional(name: String): Boolean? = getColumnValueOptional(name) { getBoolean(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getBytesOptional(name: String): ByteArray? = getColumnValueOptional(name) { getBytes(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getDoubleOptional(name: String): Double? = getColumnValueOptional(name) { getDouble(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getLongOptional(name: String): Long? = getColumnValueOptional(name) { getLong(it) } + + @Throws(PowerSyncException::class, IllegalArgumentException::class) + public fun getStringOptional(name: String): String? = getColumnValueOptional(name) { getString(it) } } private inline fun SqlCursor.getColumnValue( @@ -32,30 +68,3 @@ private inline fun SqlCursor.getColumnValueOptional( name: String, getValue: (Int) -> T?, ): T? = columnNames[name]?.let { getValue(it) } - -// This causes a collision the functions created in Swift and there we need to disable this conversion -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getBoolean(name: String): Boolean = getColumnValue(name) { getBoolean(it) } - -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getBytes(name: String): ByteArray = getColumnValue(name) { getBytes(it) } - -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getDouble(name: String): Double = getColumnValue(name) { getDouble(it) } - -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getLong(name: String): Long = getColumnValue(name) { getLong(it) } - -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getString(name: String): String = getColumnValue(name) { getString(it) } - -public fun SqlCursor.getBooleanOptional(name: String): Boolean? = getColumnValueOptional(name) { getBoolean(it) } - -public fun SqlCursor.getBytesOptional(name: String): ByteArray? = getColumnValueOptional(name) { getBytes(it) } - -public fun SqlCursor.getDoubleOptional(name: String): Double? = getColumnValueOptional(name) { getDouble(it) } - -public fun SqlCursor.getLongOptional(name: String): Long? = getColumnValueOptional(name) { getLong(it) } - -@FunctionInterop.FileScopeConversion.Disabled -public fun SqlCursor.getStringOptional(name: String): String? = getColumnValueOptional(name) { getString(it) } 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 59ae6d57..966edf36 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -9,6 +9,7 @@ import app.cash.sqldelight.db.SqlPreparedStatement import com.persistence.PowersyncQueries 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.CoroutineScope @@ -82,7 +83,7 @@ internal class InternalDatabaseImpl( override suspend fun execute( sql: String, parameters: List?, - ): Long = withContext(dbContext) { executeSync(sql, parameters) } + ): Long = withContext(dbContext) { runWrapped { executeSync(sql, parameters) } } private fun executeSync( sql: String, @@ -103,7 +104,7 @@ internal class InternalDatabaseImpl( sql: String, parameters: List?, mapper: (SqlCursor) -> RowType, - ): RowType = withContext(dbContext) { getSync(sql, parameters, mapper) } + ): RowType = withContext(dbContext) { runWrapped { getSync(sql, parameters, mapper) } } private fun getSync( sql: String, @@ -125,7 +126,7 @@ internal class InternalDatabaseImpl( sql: String, parameters: List?, mapper: (SqlCursor) -> RowType, - ): List = withContext(dbContext) { getAllSync(sql, parameters, mapper) } + ): List = withContext(dbContext) { runWrapped { getAllSync(sql, parameters, mapper) } } private fun getAllSync( sql: String, @@ -144,7 +145,7 @@ internal class InternalDatabaseImpl( sql: String, parameters: List?, mapper: (SqlCursor) -> RowType, - ): RowType? = withContext(dbContext) { getOptionalSync(sql, parameters, mapper) } + ): RowType? = withContext(dbContext) { runWrapped { getOptionalSync(sql, parameters, mapper) } } private fun getOptionalSync( sql: String, @@ -211,15 +212,19 @@ internal class InternalDatabaseImpl( override suspend fun readTransaction(callback: PowerSyncTransaction.() -> R): R = withContext(dbContext) { - transactor.transactionWithResult(noEnclosing = true) { - callback(transaction) + runWrapped { + transactor.transactionWithResult(noEnclosing = true) { + callback(transaction) + } } } override suspend fun writeTransaction(callback: PowerSyncTransaction.() -> R): R = withContext(dbContext) { - transactor.transactionWithResult(noEnclosing = true) { - callback(transaction) + runWrapped { + transactor.transactionWithResult(noEnclosing = true) { + callback(transaction) + } } } @@ -279,21 +284,23 @@ internal class InternalDatabaseImpl( override fun getExistingTableNames(tableGlob: String): List { val existingTableNames = - createQuery( - "SELECT name FROM sqlite_master WHERE type='table' AND name GLOB ?", - parameters = 1, - binders = { - bindString(0, tableGlob) - }, - mapper = { cursor -> - cursor.getString(0)!! - }, - ).executeAsList() + runWrapped { + createQuery( + "SELECT name FROM sqlite_master WHERE type='table' AND name GLOB ?", + parameters = 1, + binders = { + bindString(0, tableGlob) + }, + mapper = { cursor -> + cursor.getString(0)!! + }, + ).executeAsList() + } return existingTableNames } override fun close() { - this.driver.close() + runWrapped { this.driver.close() } } internal data class ExplainQueryResult( diff --git a/core/src/iosMain/kotlin/com/powersync/DatabaseDriverFactory.ios.kt b/core/src/iosMain/kotlin/com/powersync/DatabaseDriverFactory.ios.kt index cec18926..4ad8a6bb 100644 --- a/core/src/iosMain/kotlin/com/powersync/DatabaseDriverFactory.ios.kt +++ b/core/src/iosMain/kotlin/com/powersync/DatabaseDriverFactory.ios.kt @@ -1,7 +1,9 @@ package com.powersync import co.touchlab.sqliter.DatabaseConfiguration +import co.touchlab.sqliter.DatabaseConfiguration.Logging import co.touchlab.sqliter.DatabaseConnection +import co.touchlab.sqliter.interop.Logger import com.powersync.db.internal.InternalSchema import com.powersync.persistence.driver.NativeSqliteDriver import com.powersync.persistence.driver.wrapConnection @@ -51,6 +53,17 @@ public actual class DatabaseDriverFactory { dbFilename: String, ): PsSqlDriver { val schema = InternalSchema + val sqlLogger = object : Logger { + override val eActive: Boolean + get() = false + override val vActive: Boolean + get() = false + + override fun eWrite(message: String, exception: Throwable?) {} + override fun trace(message: String) {} + override fun vWrite(message: String) {} + } + this.driver = PsSqlDriver( scope = scope, @@ -61,6 +74,7 @@ public actual class DatabaseDriverFactory { name = dbFilename, version = schema.version.toInt(), create = { connection -> wrapConnection(connection) { schema.create(it) } }, + loggingConfig = Logging(logger = sqlLogger), lifecycleConfig = DatabaseConfiguration.Lifecycle( onCreateConnection = { connection ->