|
|
|
import { Result } from "./result.mjs";
|
|
|
|
import { PawSQLiteError } from "./pawsqlite_error.mjs";
|
|
|
|
import { query } from "./query.mjs";
|
|
|
|
|
|
|
|
|
|
|
|
export class Transaction {
|
|
|
|
constructor(dbName, adapter, enqueue, rollbackOnError=false) {
|
|
|
|
this.dbName = dbName;
|
|
|
|
this.adapter = adapter;
|
|
|
|
this._rollbackOnError = rollbackOnError;
|
|
|
|
|
|
|
|
this._enqueue = enqueue;
|
|
|
|
this._completeCb = null;
|
|
|
|
|
|
|
|
this._readyWait = null;
|
|
|
|
this._ready = false;
|
|
|
|
this._finalized = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
async sql(sql, ...args) {
|
|
|
|
if (this._finalized) {
|
|
|
|
throw new PawSQLiteError("Transaction has already completed");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this._ready) {
|
|
|
|
await this._waitUntilReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
let result;
|
|
|
|
try {
|
|
|
|
result = await this.adapter.sql(this.dbName, ...query(sql, ...args));
|
|
|
|
} catch (e) {
|
|
|
|
if (this._rollbackOnError) {
|
|
|
|
await this.rollback();
|
|
|
|
}
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
return new Result(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
commit() {
|
|
|
|
return this._complete("COMMIT");
|
|
|
|
}
|
|
|
|
|
|
|
|
rollback() {
|
|
|
|
return this._complete("ROLLBACK");
|
|
|
|
}
|
|
|
|
|
|
|
|
async _waitUntilReady() {
|
|
|
|
if (!this._readyWait) {
|
|
|
|
this._readyWait = (async () => {
|
|
|
|
this._completeCb = await this._enqueue();
|
|
|
|
await this._begin();
|
|
|
|
this._ready = true;
|
|
|
|
})();
|
|
|
|
}
|
|
|
|
|
|
|
|
await this._readyWait;
|
|
|
|
}
|
|
|
|
|
|
|
|
async _begin() {
|
|
|
|
const result = await this.adapter.sql(this.dbName, "BEGIN");
|
|
|
|
}
|
|
|
|
|
|
|
|
async _complete(sql) {
|
|
|
|
if (this._finalized) {
|
|
|
|
throw new PawSQLiteError("Transaction has already completed");
|
|
|
|
}
|
|
|
|
this._finalized = true;
|
|
|
|
|
|
|
|
if (!this._readyWait) {
|
|
|
|
// Transaction was unused
|
|
|
|
return;
|
|
|
|
} else if (!this._ready) {
|
|
|
|
await this._waitUntilReady();
|
|
|
|
}
|
|
|
|
|
|
|
|
let result;
|
|
|
|
let error;
|
|
|
|
try {
|
|
|
|
result = await this.adapter.sql(this.dbName, sql);
|
|
|
|
} catch (e) {
|
|
|
|
error = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._completeCb();
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Result(result);
|
|
|
|
}
|
|
|
|
}
|