UNPKG

@electric-sql/drivers

Version:

ElectricSQL database drivers.

122 lines 3.38 kB
import { Mutex } from "async-mutex"; class DatabaseAdapter { db; defaultNamespace = "main"; /* * Even though this driver is synchronous we need to coordinate the calls through a mutex * because of the `group` method which takes a function: `f: (adapter: UncoordinatedDatabaseAdapter) => Promise<void> | void` * that function may call `await` which would open the possibility for another query/transaction * to be interleaved with the execution of that function */ txMutex; constructor(db) { this.db = db; this.txMutex = new Mutex(); } async _runInTransaction(...statements) { const txn = this.db.transaction((stmts) => { let rowsAffected = 0; for (const stmt of stmts) { const prep = this.db.prepare(stmt.sql); const res = prep.run(...wrapBindParams(stmt.args)); rowsAffected += res.changes; } return { rowsAffected }; }); return txn(statements); } async _transaction(f) { let result; const txn = this.db.transaction(f); txn(new WrappedTx(this.db), (res) => result = res); return result; } // Promise interface, but impl not actually async async _run({ sql, args }) { const prep = this.db.prepare(sql); const res = prep.run(...wrapBindParams(args)); return { rowsAffected: res.changes }; } // This `query` function does not enforce that the query is read-only async _query({ sql, args }) { const stmt = this.db.prepare(sql); return stmt.all(...wrapBindParams(args)); } async _runExclusively(f) { const adapter = { run: this._run.bind(this), query: this._query.bind(this), transaction: this._transaction.bind(this), runInTransaction: this._runInTransaction.bind(this) }; return f(adapter); } async runInTransaction(...statements) { return this.txMutex.runExclusive(() => { return this._runInTransaction(...statements); }); } async transaction(f) { return this.txMutex.runExclusive(() => { return this._transaction(f); }); } async run(stmt) { return this.txMutex.runExclusive(() => { return this._run(stmt); }); } async query(stmt) { return this.txMutex.runExclusive(() => { return this._query(stmt); }); } async runExclusively(f) { return this.txMutex.runExclusive(() => { return this._runExclusively(f); }); } } function wrapBindParams(x) { if (x && Array.isArray(x)) { return x; } else if (x) { return [x]; } else { return []; } } class WrappedTx { constructor(db) { this.db = db; } run({ sql, args }, successCallback, errorCallback) { try { const prep = this.db.prepare(sql); const res = prep.run(...wrapBindParams(args)); if (typeof successCallback !== "undefined") successCallback(this, { rowsAffected: res.changes }); } catch (err) { if (typeof errorCallback !== "undefined") errorCallback(err); throw err; } } query({ sql, args }, successCallback, errorCallback) { try { const stmt = this.db.prepare(sql); const rows = stmt.all(...wrapBindParams(args)); successCallback(this, rows); } catch (err) { if (typeof errorCallback !== "undefined") errorCallback(err); throw err; } } } export { DatabaseAdapter }; //# sourceMappingURL=adapter.js.map