diff --git a/bindings/javascript/packages/common/promise.ts b/bindings/javascript/packages/common/promise.ts index 26edc537c..068b36f7f 100644 --- a/bindings/javascript/packages/common/promise.ts +++ b/bindings/javascript/packages/common/promise.ts @@ -38,6 +38,7 @@ class Database { private db: NativeDatabase; private execLock: AsyncLock; + private connected: boolean = false; private _inTransaction: boolean = false; constructor(db: NativeDatabase) { @@ -56,7 +57,9 @@ class Database { * connect database */ async connect() { + if (this.connected) { return; } await this.db.connectAsync(); + this.connected = true; } /** @@ -70,7 +73,11 @@ class Database { } try { - return new Statement(this.db.prepare(sql), this.db, this.execLock); + if (this.connected) { + return new Statement(maybeValue(this.db.prepare(sql)), this.db, this.execLock); + } else { + return new Statement(maybePromise(this.connect().then(() => this.db.prepare(sql))), this.db, this.execLock) + } } catch (err) { throw convertError(err); } @@ -218,15 +225,43 @@ class Database { } } +interface MaybeLazy { + apply(fn: (value: T) => void); + resolve(): Promise, + must(): T; +} + +function maybePromise(lazy: Promise): MaybeLazy { + let value = null; + return { + apply(fn) { lazy.then(x => { fn(x); return x; }); }, + resolve() { return lazy.then(x => value = x); }, + must() { + if (value == null) { + throw new Error(`database must be connected before execution the function`) + } + return value; + }, + } +} + +function maybeValue(value: T): MaybeLazy { + return { + apply(fn) { fn(value); }, + resolve() { return Promise.resolve(value); }, + must() { return value; }, + } +} + /** * Statement represents a prepared SQL statement that can be executed. */ class Statement { - private stmt: NativeStatement; + private stmt: MaybeLazy; private db: NativeDatabase; private execLock: AsyncLock; - constructor(stmt: NativeStatement, db: NativeDatabase, execLock: AsyncLock) { + constructor(stmt: MaybeLazy, db: NativeDatabase, execLock: AsyncLock) { this.stmt = stmt; this.db = db; this.execLock = execLock; @@ -238,7 +273,7 @@ class Statement { * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled. */ raw(raw) { - this.stmt.raw(raw); + this.stmt.apply(s => s.raw(raw)); return this; } @@ -248,7 +283,7 @@ class Statement { * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled. */ pluck(pluckMode) { - this.stmt.pluck(pluckMode); + this.stmt.apply(s => s.pluck(pluckMode)); return this; } @@ -258,7 +293,7 @@ class Statement { * @param {boolean} [toggle] - Whether to use safe integers. */ safeIntegers(toggle) { - this.stmt.safeIntegers(toggle); + this.stmt.apply(s => s.safeIntegers(toggle)); return this; } @@ -268,7 +303,7 @@ class Statement { * @returns {Array} An array of column objects with name, column, table, database, and type properties. */ columns() { - return this.stmt.columns(); + return this.stmt.must().columns(); } get source() { @@ -289,13 +324,14 @@ class Statement { async run(...bindParameters) { const totalChangesBefore = this.db.totalChanges(); - this.stmt.reset(); - bindParams(this.stmt, bindParameters); + let stmt = await this.stmt.resolve(); + stmt.reset(); + bindParams(stmt, bindParameters); await this.execLock.acquire(); try { while (true) { - const stepResult = await this.stmt.stepSync(); + const stepResult = await stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.ioLoopAsync(); continue; @@ -324,13 +360,15 @@ class Statement { * @param bindParameters - The bind parameters for executing the statement. */ async get(...bindParameters) { - this.stmt.reset(); - bindParams(this.stmt, bindParameters); + let stmt = await this.stmt.resolve(); + + stmt.reset(); + bindParams(stmt, bindParameters); await this.execLock.acquire(); try { while (true) { - const stepResult = await this.stmt.stepSync(); + const stepResult = await stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.ioLoopAsync(); continue; @@ -339,7 +377,7 @@ class Statement { return undefined; } if (stepResult === STEP_ROW) { - return this.stmt.row(); + return stmt.row(); } } } finally { @@ -353,13 +391,15 @@ class Statement { * @param bindParameters - The bind parameters for executing the statement. */ async *iterate(...bindParameters) { - this.stmt.reset(); - bindParams(this.stmt, bindParameters); + let stmt = await this.stmt.resolve(); + + stmt.reset(); + bindParams(stmt, bindParameters); await this.execLock.acquire(); try { while (true) { - const stepResult = await this.stmt.stepSync(); + const stepResult = await stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.ioLoopAsync(); continue; @@ -368,7 +408,7 @@ class Statement { break; } if (stepResult === STEP_ROW) { - yield this.stmt.row(); + yield stmt.row(); } } } finally { @@ -382,14 +422,16 @@ class Statement { * @param bindParameters - The bind parameters for executing the statement. */ async all(...bindParameters) { - this.stmt.reset(); - bindParams(this.stmt, bindParameters); + let stmt = await this.stmt.resolve(); + + stmt.reset(); + bindParams(stmt, bindParameters); const rows: any[] = []; await this.execLock.acquire(); try { while (true) { - const stepResult = await this.stmt.stepSync(); + const stepResult = await stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.ioLoopAsync(); continue; @@ -398,7 +440,7 @@ class Statement { break; } if (stepResult === STEP_ROW) { - rows.push(this.stmt.row()); + rows.push(stmt.row()); } } return rows; @@ -432,7 +474,14 @@ class Statement { } close() { - this.stmt.finalize(); + let stmt; + try { + stmt = this.stmt.must(); + } catch (e) { + // ignore error - if stmt wasn't initialized it's fine + return; + } + stmt.finalize(); } } diff --git a/bindings/javascript/packages/native/index.js b/bindings/javascript/packages/native/index.js index f9adceea6..8e63811ea 100644 --- a/bindings/javascript/packages/native/index.js +++ b/bindings/javascript/packages/native/index.js @@ -67,7 +67,7 @@ const isMuslFromChildProcess = () => { function requireNative() { if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) { try { - return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH); + nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH); } catch (err) { loadErrors.push(err) } @@ -81,8 +81,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-android-arm64') const bindingPackageVersion = require('@tursodatabase/database-android-arm64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -97,8 +97,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-android-arm-eabi') const bindingPackageVersion = require('@tursodatabase/database-android-arm-eabi/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -117,8 +117,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-win32-x64-msvc') const bindingPackageVersion = require('@tursodatabase/database-win32-x64-msvc/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -133,8 +133,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-win32-ia32-msvc') const bindingPackageVersion = require('@tursodatabase/database-win32-ia32-msvc/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -149,8 +149,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-win32-arm64-msvc') const bindingPackageVersion = require('@tursodatabase/database-win32-arm64-msvc/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -168,8 +168,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-darwin-universal') const bindingPackageVersion = require('@tursodatabase/database-darwin-universal/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -184,8 +184,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-darwin-x64') const bindingPackageVersion = require('@tursodatabase/database-darwin-x64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -200,8 +200,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-darwin-arm64') const bindingPackageVersion = require('@tursodatabase/database-darwin-arm64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -220,8 +220,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-freebsd-x64') const bindingPackageVersion = require('@tursodatabase/database-freebsd-x64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -236,8 +236,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-freebsd-arm64') const bindingPackageVersion = require('@tursodatabase/database-freebsd-arm64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -257,8 +257,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-x64-musl') const bindingPackageVersion = require('@tursodatabase/database-linux-x64-musl/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -273,8 +273,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-x64-gnu') const bindingPackageVersion = require('@tursodatabase/database-linux-x64-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -291,8 +291,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-arm64-musl') const bindingPackageVersion = require('@tursodatabase/database-linux-arm64-musl/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -307,8 +307,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-arm64-gnu') const bindingPackageVersion = require('@tursodatabase/database-linux-arm64-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -325,8 +325,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-arm-musleabihf') const bindingPackageVersion = require('@tursodatabase/database-linux-arm-musleabihf/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -341,42 +341,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-arm-gnueabihf') const bindingPackageVersion = require('@tursodatabase/database-linux-arm-gnueabihf/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) - } - return binding - } catch (e) { - loadErrors.push(e) - } - } - } else if (process.arch === 'loong64') { - if (isMusl()) { - try { - return require('./turso.linux-loong64-musl.node') - } catch (e) { - loadErrors.push(e) - } - try { - const binding = require('@tursodatabase/database-linux-loong64-musl') - const bindingPackageVersion = require('@tursodatabase/database-linux-loong64-musl/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) - } - return binding - } catch (e) { - loadErrors.push(e) - } - } else { - try { - return require('./turso.linux-loong64-gnu.node') - } catch (e) { - loadErrors.push(e) - } - try { - const binding = require('@tursodatabase/database-linux-loong64-gnu') - const bindingPackageVersion = require('@tursodatabase/database-linux-loong64-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -393,8 +359,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-riscv64-musl') const bindingPackageVersion = require('@tursodatabase/database-linux-riscv64-musl/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -409,8 +375,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-riscv64-gnu') const bindingPackageVersion = require('@tursodatabase/database-linux-riscv64-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -426,8 +392,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-ppc64-gnu') const bindingPackageVersion = require('@tursodatabase/database-linux-ppc64-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -442,8 +408,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-linux-s390x-gnu') const bindingPackageVersion = require('@tursodatabase/database-linux-s390x-gnu/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -462,8 +428,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-openharmony-arm64') const bindingPackageVersion = require('@tursodatabase/database-openharmony-arm64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -478,8 +444,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-openharmony-x64') const bindingPackageVersion = require('@tursodatabase/database-openharmony-x64/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -494,8 +460,8 @@ function requireNative() { try { const binding = require('@tursodatabase/database-openharmony-arm') const bindingPackageVersion = require('@tursodatabase/database-openharmony-arm/package.json').version - if (bindingPackageVersion !== '0.2.0-pre.10' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { - throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.10 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) + if (bindingPackageVersion !== '0.2.0-pre.11' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') { + throw new Error(`Native binding package version mismatch, expected 0.2.0-pre.11 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`) } return binding } catch (e) { @@ -512,32 +478,22 @@ function requireNative() { nativeBinding = requireNative() if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) { - let wasiBinding = null - let wasiBindingError = null try { - wasiBinding = require('./turso.wasi.cjs') - nativeBinding = wasiBinding + nativeBinding = require('./turso.wasi.cjs') } catch (err) { if (process.env.NAPI_RS_FORCE_WASI) { - wasiBindingError = err + loadErrors.push(err) } } if (!nativeBinding) { try { - wasiBinding = require('@tursodatabase/database-wasm32-wasi') - nativeBinding = wasiBinding + nativeBinding = require('@tursodatabase/database-wasm32-wasi') } catch (err) { if (process.env.NAPI_RS_FORCE_WASI) { - wasiBindingError.cause = err loadErrors.push(err) } } } - if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) { - const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error') - error.cause = wasiBindingError - throw error - } } if (!nativeBinding) { @@ -546,12 +502,7 @@ if (!nativeBinding) { `Cannot find native binding. ` + `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` + 'Please try `npm i` again after removing both package-lock.json and node_modules directory.', - { - cause: loadErrors.reduce((err, cur) => { - cur.cause = err - return cur - }), - }, + { cause: loadErrors } ) } throw new Error(`Failed to load native binding`) diff --git a/bindings/javascript/packages/native/promise.test.ts b/bindings/javascript/packages/native/promise.test.ts index 2dc5be8fb..19f3c2b86 100644 --- a/bindings/javascript/packages/native/promise.test.ts +++ b/bindings/javascript/packages/native/promise.test.ts @@ -65,11 +65,11 @@ test('file-must-exist', async () => { await expect(async () => await connect(path, { fileMustExist: true })).rejects.toThrowError(/failed to open file/); }) -test('explicit connect', async () => { +test('implicit connect', async () => { const db = new Database(':memory:'); - expect(() => db.prepare("SELECT 1")).toThrowError(/database must be connected/g); - await db.connect(); - expect(await db.prepare("SELECT 1 as x").all()).toEqual([{ x: 1 }]); + const defer = db.prepare("SELECT * FROM t"); + await expect(async () => await defer.all()).rejects.toThrowError(/no such table: t/); + expect(() => db.prepare("SELECT * FROM t")).toThrowError(/no such table: t/); }) test('zero-limit-bug', async () => { diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 5788c4044..4a8b5c3ab 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -52,12 +52,17 @@ pub struct DatabaseInner { path: String, opts: Option, io: Arc, - db: OnceLock>>, - conn: OnceLock>>, - is_connected: OnceLock, + connect: OnceLock, default_safe_integers: Mutex, } +pub struct DatabaseConnect { + // hold db reference in order to keep it alive + // _db can be None if DB is controlled externally (for example, by sync-engine) + _db: Option>, + conn: Arc, +} + pub(crate) fn is_memory(path: &str) -> bool { path == ":memory:" } @@ -152,7 +157,7 @@ fn create_error(status: napi::Status, message: &str) -> napi::Error { } fn connect_sync(db: &DatabaseInner) -> napi::Result<()> { - if db.is_connected.get() == Some(&true) { + if db.connect.get().is_some() { return Ok(()); } @@ -170,7 +175,6 @@ fn connect_sync(db: &DatabaseInner) -> napi::Result<()> { busy_timeout = Some(std::time::Duration::from_millis(timeout as u64)); } } - tracing::info!("flags: {:?}", flags); let io = &db.io; let file = io .open_file(&db.path, flags, false) @@ -197,15 +201,12 @@ fn connect_sync(db: &DatabaseInner) -> napi::Result<()> { conn.set_busy_timeout(busy_timeout); } - db.is_connected - .set(true) - .map_err(|_| create_generic_error("db already connected, API misuse"))?; - db.db - .set(Some(db_core)) - .map_err(|_| create_generic_error("db already connected, API misuse"))?; - db.conn - .set(Some(conn)) - .map_err(|_| create_generic_error("db already connected, API misuse"))?; + let connect = DatabaseConnect { + _db: Some(db_core), + conn, + }; + // there can be races between concurrent connect - so let's ignore error in case of + let _ = db.connect.set(connect); Ok(()) } @@ -249,9 +250,7 @@ impl Database { path, opts, io, - db: OnceLock::new(), - conn: OnceLock::new(), - is_connected: OnceLock::new(), + connect: OnceLock::new(), default_safe_integers: Mutex::new(false), })), }) @@ -260,18 +259,8 @@ impl Database { pub fn set_connected(&self, conn: Arc) -> napi::Result<()> { let inner = self.inner()?; inner - .db - .set(None) - .map_err(|_| create_generic_error("database was already connected"))?; - - inner - .conn - .set(Some(conn)) - .map_err(|_| create_generic_error("database was already connected"))?; - - inner - .is_connected - .set(true) + .connect + .set(DatabaseConnect { _db: None, conn }) .map_err(|_| create_generic_error("database was already connected"))?; Ok(()) @@ -301,7 +290,7 @@ impl Database { } fn conn(&self) -> Result> { - let Some(Some(conn)) = self.inner()?.conn.get() else { + let Some(DatabaseConnect { conn, .. }) = self.inner()?.connect.get() else { return Err(create_generic_error("database must be connected")); }; Ok(conn.clone()) @@ -331,7 +320,7 @@ impl Database { if self.inner.is_none() { return Ok(false); } - Ok(self.inner()?.is_connected.get() == Some(&true)) + Ok(self.inner()?.connect.get().is_some()) } /// Prepares a statement for execution.