From 8a3fa556d7e551104310a76b734cbf6cbea6829d Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 26 Apr 2026 16:50:09 +0700 Subject: [PATCH] fix(folio-core): probe schema before create on wasmJs Wasm SqlDriver doesn't auto-track user_version like the Android driver, so awaitCreate() ran on every page load and tripped over already-created tables. Read PRAGMA user_version, run awaitCreate/awaitMigrate based on it, and self-heal pre-existing tables with version 0 by stamping the current schema version. --- .../folio/core/data/DriverFactory.wasmJs.kt | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/examples/folio/core/src/wasmJsMain/kotlin/app/folio/core/data/DriverFactory.wasmJs.kt b/examples/folio/core/src/wasmJsMain/kotlin/app/folio/core/data/DriverFactory.wasmJs.kt index 117340a..3679cbc 100644 --- a/examples/folio/core/src/wasmJsMain/kotlin/app/folio/core/data/DriverFactory.wasmJs.kt +++ b/examples/folio/core/src/wasmJsMain/kotlin/app/folio/core/data/DriverFactory.wasmJs.kt @@ -3,6 +3,7 @@ package app.folio.core.data import app.cash.sqldelight.async.coroutines.awaitCreate +import app.cash.sqldelight.async.coroutines.awaitMigrate import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.worker.WebWorkerDriver import app.folio.db.LedgerDatabase @@ -11,10 +12,59 @@ import org.w3c.dom.Worker actual class DriverFactory { actual suspend fun create(): SqlDriver { val driver = WebWorkerDriver(createSqliteWorker()) - LedgerDatabase.Schema.awaitCreate(driver) + val schema = LedgerDatabase.Schema + val current = readUserVersion(driver) + val target = schema.version + when { + current == 0L && !schemaExists(driver) -> { + schema.awaitCreate(driver) + writeUserVersion(driver, target) + } + current == 0L -> writeUserVersion(driver, target) + current < target -> { + schema.awaitMigrate(driver, current, target) + writeUserVersion(driver, target) + } + } return driver } } +private suspend fun readUserVersion(driver: SqlDriver): Long { + var version = 0L + driver.executeQuery( + identifier = null, + sql = "PRAGMA user_version", + mapper = { cursor -> + app.cash.sqldelight.db.QueryResult.AsyncValue { + if (cursor.next().await()) version = cursor.getLong(0) ?: 0L + Unit + } + }, + parameters = 0, + ).await() + return version +} + +private suspend fun writeUserVersion(driver: SqlDriver, version: Long) { + driver.execute(null, "PRAGMA user_version = $version", 0).await() +} + +private suspend fun schemaExists(driver: SqlDriver): Boolean { + var exists = false + driver.executeQuery( + identifier = null, + sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' LIMIT 1", + mapper = { cursor -> + app.cash.sqldelight.db.QueryResult.AsyncValue { + exists = cursor.next().await() + Unit + } + }, + parameters = 0, + ).await() + return exists +} + private fun createSqliteWorker(): Worker = js("""new Worker(new URL("./sqlite.worker.js", import.meta.url), { type: "module" })""")