@electric-sql/drivers
Version:
ElectricSQL database drivers.
122 lines • 3.38 kB
JavaScript
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