Added the ability to execute sql outside of transactions with the database.sql method
This commit is contained in:
parent
da2a186c95
commit
7387e20b09
@ -61,7 +61,7 @@ const db = await PawSQLite.open("test", {
|
||||
|
||||
### Querying the database:
|
||||
|
||||
You can query the database by using the `sql` method on a transaction object. For convenience, database objects also contain an `sql` method which is shorthand for `db.autoTransaction((tx) => tx.sql(sql, ...args));`. The `sql` method also allows you to bind parameters to the satatement. Bound parameters are escaped by whichever native SQLite implementation your chosen adapter uses.
|
||||
You can query the database by using the `sql` method on a database or transaction object. The `sql` method also allows you to bind parameters to the satatement. Bound parameters are escaped by whichever native SQLite implementation your chosen adapter uses.
|
||||
|
||||
```javascript
|
||||
await db.sql("SELECT * FROM contacts WHERE name=?", "Paul");
|
||||
|
278
cjs/pawsqlite.js
278
cjs/pawsqlite.js
@ -65,7 +65,11 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "Database": () => (/* binding */ Database)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _transaction_manager_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transaction_manager.mjs */ "./src/transaction_manager.mjs");
|
||||
/* harmony import */ var _task_manager_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./task_manager.mjs */ "./src/task_manager.mjs");
|
||||
/* harmony import */ var _transaction_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transaction.mjs */ "./src/transaction.mjs");
|
||||
/* harmony import */ var _query_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query.mjs */ "./src/query.mjs");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -76,7 +80,7 @@ class Database {
|
||||
this.version = null;
|
||||
this.path = null;
|
||||
|
||||
this.transactionManager = new _transaction_manager_mjs__WEBPACK_IMPORTED_MODULE_0__.TransactionManager(dbName, this.adapter);
|
||||
this.taskManager = new _task_manager_mjs__WEBPACK_IMPORTED_MODULE_0__.TaskManager();
|
||||
}
|
||||
|
||||
async open() {
|
||||
@ -100,10 +104,13 @@ class Database {
|
||||
}
|
||||
|
||||
transaction() {
|
||||
return this.transactionManager.transaction();
|
||||
return new _transaction_mjs__WEBPACK_IMPORTED_MODULE_1__.Transaction(
|
||||
this.dbName,
|
||||
this.adapter,
|
||||
this.taskManager.enqueue.bind(this.taskManager)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async autoTransaction(cb, inheritTx) {
|
||||
let tx = inheritTx || this.transaction();
|
||||
let result;
|
||||
@ -125,9 +132,23 @@ class Database {
|
||||
}
|
||||
|
||||
|
||||
// Helper method to start a transaction and execute a single SQL statement
|
||||
sql(sql, ...args) {
|
||||
return this.autoTransaction((tx) => tx.sql(sql, ...args));
|
||||
// Execute a single SQL statement
|
||||
async sql(sql, ...args) {
|
||||
const completeCb = await this.taskManager.enqueue();
|
||||
|
||||
let result;
|
||||
let error;
|
||||
try {
|
||||
result = await this.adapter.sql(this.dbName, ...(0,_query_mjs__WEBPACK_IMPORTED_MODULE_2__.query)(sql, ...args));
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
completeCb();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return new Result(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,6 +279,68 @@ class PawSQLiteError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/query.mjs":
|
||||
/*!***********************!*\
|
||||
!*** ./src/query.mjs ***!
|
||||
\***********************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "query": () => (/* binding */ query),
|
||||
/* harmony export */ "buildQuery": () => (/* binding */ buildQuery),
|
||||
/* harmony export */ "validateQuery": () => (/* binding */ validateQuery)
|
||||
/* harmony export */ });
|
||||
function query(sql, ...args) {
|
||||
validateQuery(sql, ...args);
|
||||
return buildQuery(sql, ...args);
|
||||
}
|
||||
|
||||
// Allow for slightly more complex parameter substitution.
|
||||
// Instances of "???" will be replaced by the same number of comma-separated
|
||||
// question marks as items in the corresponding nested parateter array
|
||||
// eg. buildQuery("SELECT (???) FROM ?", [["col1", "col2"], "table1"])
|
||||
// would output: ["SELECT (?, ?) FROM ?", ["col1", "col2", "table1"]]
|
||||
function buildQuery(sql, ...args) {
|
||||
const parts = sql.split("???");
|
||||
const subParamLengths = args
|
||||
.filter(Array.isArray)
|
||||
.map((a) => a.length);
|
||||
if (parts.length !== subParamLengths.length + 1) {
|
||||
throw new PawSQLiteError("Unable to build query: sub-" +
|
||||
"paramters do not match sub-paramters in query");
|
||||
}
|
||||
|
||||
const newQuery = parts.reduce((p1, p2, i) => {
|
||||
const length = subParamLengths[i - 1];
|
||||
return p1 + new Array(length).fill("?").join(", ") + p2;
|
||||
});
|
||||
|
||||
const flatParams = args.reduce((acc, v) => {
|
||||
if (Array.isArray(v)) {
|
||||
Array.prototype.push.apply(acc, v);
|
||||
} else {
|
||||
acc.push(v);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return [newQuery, ...flatParams];
|
||||
}
|
||||
|
||||
function validateQuery(sql, ...args) {
|
||||
const reg = /^\s*(BEGIN|END|COMMIT|ROLLBACK)(?:[^A-Z]|$)/i;
|
||||
const match = reg.exec(sql);
|
||||
if (match) {
|
||||
const statement = match[1].toUpperCase();
|
||||
throw new PawSQLiteError("Manually managing transactions is " +
|
||||
"forbidden. Found: \"" + statement + "\" statement.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/result.mjs":
|
||||
@ -293,6 +376,65 @@ class Result extends Array {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/task_manager.mjs":
|
||||
/*!******************************!*\
|
||||
!*** ./src/task_manager.mjs ***!
|
||||
\******************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "TaskManager": () => (/* binding */ TaskManager)
|
||||
/* harmony export */ });
|
||||
class TaskManager {
|
||||
constructor() {
|
||||
this._queue = [];
|
||||
this._inTask = false;
|
||||
}
|
||||
|
||||
enqueue() {
|
||||
let completeSignal;
|
||||
let taskComplete = new Promise((r, _) => {
|
||||
completeSignal = r;
|
||||
});
|
||||
|
||||
let readySignal;
|
||||
let dbReady = new Promise((r, _) => {
|
||||
readySignal = () => {
|
||||
r(completeSignal);
|
||||
};
|
||||
});
|
||||
|
||||
this._queue.push({readySignal, taskComplete});
|
||||
|
||||
this._processQueue();
|
||||
return dbReady;
|
||||
}
|
||||
|
||||
async _processQueue() {
|
||||
// We're already processing the queue
|
||||
if (this._inTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let item = this._queue.shift();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._inTask = true;
|
||||
|
||||
item.readySignal();
|
||||
await item.taskComplete;
|
||||
|
||||
this._inTask = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/transaction.mjs":
|
||||
/*!*****************************!*\
|
||||
!*** ./src/transaction.mjs ***!
|
||||
@ -305,6 +447,8 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _result_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./result.mjs */ "./src/result.mjs");
|
||||
/* harmony import */ var _pawsqlite_error_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./pawsqlite_error.mjs */ "./src/pawsqlite_error.mjs");
|
||||
/* harmony import */ var _query_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query.mjs */ "./src/query.mjs");
|
||||
|
||||
|
||||
|
||||
|
||||
@ -332,46 +476,16 @@ class Transaction {
|
||||
await this._waitUntilReady();
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
return await this._executeSQL(sql, ...args);
|
||||
result = await this.adapter.sql(this.dbName, ...(0,_query_mjs__WEBPACK_IMPORTED_MODULE_2__.query)(sql, ...args));
|
||||
} catch (e) {
|
||||
if (this._rollbackOnError) {
|
||||
await this.rollback();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for slightly more complex parameter substitution.
|
||||
// Instances of "???" will be replaced by the same number of comma-separated
|
||||
// question marks as items in the corresponding nested parateter array
|
||||
// eg. buildQuery("SELECT (???) FROM ?", [["col1", "col2"], "table1"])
|
||||
// would output: ["SELECT (?, ?) FROM ?", ["col1", "col2", "table1"]]
|
||||
buildQuery(sql, ...args) {
|
||||
const parts = sql.split("???");
|
||||
const subParamLengths = args
|
||||
.filter(Array.isArray)
|
||||
.map((a) => a.length);
|
||||
if (parts.length !== subParamLengths.length + 1) {
|
||||
throw new _pawsqlite_error_mjs__WEBPACK_IMPORTED_MODULE_1__.PawSQLiteError("Unable to build query: sub-" +
|
||||
"paramters do not match sub-paramters in query");
|
||||
}
|
||||
|
||||
const newQuery = parts.reduce((p1, p2, i) => {
|
||||
const length = subParamLengths[i - 1];
|
||||
return p1 + new Array(length).fill("?").join(", ") + p2;
|
||||
});
|
||||
|
||||
const flatParams = args.reduce((acc, v) => {
|
||||
if (Array.isArray(v)) {
|
||||
Array.prototype.push.apply(acc, v);
|
||||
} else {
|
||||
acc.push(v);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return [newQuery, ...flatParams];
|
||||
return new _result_mjs__WEBPACK_IMPORTED_MODULE_0__.Result(result);
|
||||
}
|
||||
|
||||
commit() {
|
||||
@ -394,21 +508,6 @@ class Transaction {
|
||||
await this._readyWait;
|
||||
}
|
||||
|
||||
async _executeSQL(sql, ...args) {
|
||||
const reg = /^\s*(BEGIN|END|COMMIT|ROLLBACK)(?:[^A-Z]|$)/i;
|
||||
const match = reg.exec(sql);
|
||||
if (match) {
|
||||
const statement = match[1].toUpperCase();
|
||||
throw new _pawsqlite_error_mjs__WEBPACK_IMPORTED_MODULE_1__.PawSQLiteError("Manually managing transactions is " +
|
||||
"forbidden. Found: \"" + statement + "\" statement.");
|
||||
}
|
||||
|
||||
const result = await this.adapter.sql(this.dbName,
|
||||
...this.buildQuery(sql, ...args));
|
||||
|
||||
return new _result_mjs__WEBPACK_IMPORTED_MODULE_0__.Result(result);
|
||||
}
|
||||
|
||||
async _begin() {
|
||||
const result = await this.adapter.sql(this.dbName, "BEGIN");
|
||||
}
|
||||
@ -444,75 +543,6 @@ class Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./src/transaction_manager.mjs":
|
||||
/*!*************************************!*\
|
||||
!*** ./src/transaction_manager.mjs ***!
|
||||
\*************************************/
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ "TransactionManager": () => (/* binding */ TransactionManager)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _transaction_mjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transaction.mjs */ "./src/transaction.mjs");
|
||||
|
||||
|
||||
class TransactionManager {
|
||||
constructor(dbName, adapter) {
|
||||
this.dbName = dbName;
|
||||
this.adapter = adapter;
|
||||
this._queue = [];
|
||||
this._inTransaction = false;
|
||||
}
|
||||
|
||||
transaction() {
|
||||
return new _transaction_mjs__WEBPACK_IMPORTED_MODULE_0__.Transaction(this.dbName, this.adapter,
|
||||
this.enqueue.bind(this));
|
||||
}
|
||||
|
||||
enqueue() {
|
||||
let completeSignal;
|
||||
let transactionComplete = new Promise((r, _) => {
|
||||
completeSignal = r;
|
||||
});
|
||||
|
||||
let readySignal;
|
||||
let dbReady = new Promise((r, _) => {
|
||||
readySignal = () => {
|
||||
r(completeSignal);
|
||||
};
|
||||
});
|
||||
|
||||
this._queue.push({readySignal, transactionComplete});
|
||||
|
||||
this._processQueue();
|
||||
return dbReady;
|
||||
}
|
||||
|
||||
async _processQueue() {
|
||||
// We're already processing the queue
|
||||
if (this._inTransaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let item = this._queue.shift();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._inTransaction = true;
|
||||
|
||||
item.readySignal();
|
||||
await item.transactionComplete;
|
||||
|
||||
this._inTransaction = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,4 +1,6 @@
|
||||
import { TransactionManager } from "./transaction_manager.mjs";
|
||||
import { TaskManager } from "./task_manager.mjs";
|
||||
import { Transaction } from "./transaction.mjs";
|
||||
import { query } from "./query.mjs";
|
||||
|
||||
|
||||
export class Database {
|
||||
@ -8,7 +10,7 @@ export class Database {
|
||||
this.version = null;
|
||||
this.path = null;
|
||||
|
||||
this.transactionManager = new TransactionManager(dbName, this.adapter);
|
||||
this.taskManager = new TaskManager();
|
||||
}
|
||||
|
||||
async open() {
|
||||
@ -32,10 +34,13 @@ export class Database {
|
||||
}
|
||||
|
||||
transaction() {
|
||||
return this.transactionManager.transaction();
|
||||
return new Transaction(
|
||||
this.dbName,
|
||||
this.adapter,
|
||||
this.taskManager.enqueue.bind(this.taskManager)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async autoTransaction(cb, inheritTx) {
|
||||
let tx = inheritTx || this.transaction();
|
||||
let result;
|
||||
@ -57,8 +62,22 @@ export class Database {
|
||||
}
|
||||
|
||||
|
||||
// Helper method to start a transaction and execute a single SQL statement
|
||||
sql(sql, ...args) {
|
||||
return this.autoTransaction((tx) => tx.sql(sql, ...args));
|
||||
// Execute a single SQL statement
|
||||
async sql(sql, ...args) {
|
||||
const completeCb = await this.taskManager.enqueue();
|
||||
|
||||
let result;
|
||||
let error;
|
||||
try {
|
||||
result = await this.adapter.sql(this.dbName, ...query(sql, ...args));
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
completeCb();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
return new Result(result);
|
||||
}
|
||||
}
|
46
src/query.mjs
Normal file
46
src/query.mjs
Normal file
@ -0,0 +1,46 @@
|
||||
export function query(sql, ...args) {
|
||||
validateQuery(sql, ...args);
|
||||
return buildQuery(sql, ...args);
|
||||
}
|
||||
|
||||
// Allow for slightly more complex parameter substitution.
|
||||
// Instances of "???" will be replaced by the same number of comma-separated
|
||||
// question marks as items in the corresponding nested parateter array
|
||||
// eg. buildQuery("SELECT (???) FROM ?", [["col1", "col2"], "table1"])
|
||||
// would output: ["SELECT (?, ?) FROM ?", ["col1", "col2", "table1"]]
|
||||
export function buildQuery(sql, ...args) {
|
||||
const parts = sql.split("???");
|
||||
const subParamLengths = args
|
||||
.filter(Array.isArray)
|
||||
.map((a) => a.length);
|
||||
if (parts.length !== subParamLengths.length + 1) {
|
||||
throw new PawSQLiteError("Unable to build query: sub-" +
|
||||
"paramters do not match sub-paramters in query");
|
||||
}
|
||||
|
||||
const newQuery = parts.reduce((p1, p2, i) => {
|
||||
const length = subParamLengths[i - 1];
|
||||
return p1 + new Array(length).fill("?").join(", ") + p2;
|
||||
});
|
||||
|
||||
const flatParams = args.reduce((acc, v) => {
|
||||
if (Array.isArray(v)) {
|
||||
Array.prototype.push.apply(acc, v);
|
||||
} else {
|
||||
acc.push(v);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return [newQuery, ...flatParams];
|
||||
}
|
||||
|
||||
export function validateQuery(sql, ...args) {
|
||||
const reg = /^\s*(BEGIN|END|COMMIT|ROLLBACK)(?:[^A-Z]|$)/i;
|
||||
const match = reg.exec(sql);
|
||||
if (match) {
|
||||
const statement = match[1].toUpperCase();
|
||||
throw new PawSQLiteError("Manually managing transactions is " +
|
||||
"forbidden. Found: \"" + statement + "\" statement.");
|
||||
}
|
||||
}
|
46
src/task_manager.mjs
Normal file
46
src/task_manager.mjs
Normal file
@ -0,0 +1,46 @@
|
||||
export class TaskManager {
|
||||
constructor() {
|
||||
this._queue = [];
|
||||
this._inTask = false;
|
||||
}
|
||||
|
||||
enqueue() {
|
||||
let completeSignal;
|
||||
let taskComplete = new Promise((r, _) => {
|
||||
completeSignal = r;
|
||||
});
|
||||
|
||||
let readySignal;
|
||||
let dbReady = new Promise((r, _) => {
|
||||
readySignal = () => {
|
||||
r(completeSignal);
|
||||
};
|
||||
});
|
||||
|
||||
this._queue.push({readySignal, taskComplete});
|
||||
|
||||
this._processQueue();
|
||||
return dbReady;
|
||||
}
|
||||
|
||||
async _processQueue() {
|
||||
// We're already processing the queue
|
||||
if (this._inTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let item = this._queue.shift();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._inTask = true;
|
||||
|
||||
item.readySignal();
|
||||
await item.taskComplete;
|
||||
|
||||
this._inTask = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import { Result } from "./result.mjs";
|
||||
import { PawSQLiteError } from "./pawsqlite_error.mjs";
|
||||
import { query } from "./query.mjs";
|
||||
|
||||
|
||||
export class Transaction {
|
||||
@ -25,46 +26,16 @@ export class Transaction {
|
||||
await this._waitUntilReady();
|
||||
}
|
||||
|
||||
let result;
|
||||
try {
|
||||
return await this._executeSQL(sql, ...args);
|
||||
result = await this.adapter.sql(this.dbName, ...query(sql, ...args));
|
||||
} catch (e) {
|
||||
if (this._rollbackOnError) {
|
||||
await this.rollback();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for slightly more complex parameter substitution.
|
||||
// Instances of "???" will be replaced by the same number of comma-separated
|
||||
// question marks as items in the corresponding nested parateter array
|
||||
// eg. buildQuery("SELECT (???) FROM ?", [["col1", "col2"], "table1"])
|
||||
// would output: ["SELECT (?, ?) FROM ?", ["col1", "col2", "table1"]]
|
||||
buildQuery(sql, ...args) {
|
||||
const parts = sql.split("???");
|
||||
const subParamLengths = args
|
||||
.filter(Array.isArray)
|
||||
.map((a) => a.length);
|
||||
if (parts.length !== subParamLengths.length + 1) {
|
||||
throw new PawSQLiteError("Unable to build query: sub-" +
|
||||
"paramters do not match sub-paramters in query");
|
||||
}
|
||||
|
||||
const newQuery = parts.reduce((p1, p2, i) => {
|
||||
const length = subParamLengths[i - 1];
|
||||
return p1 + new Array(length).fill("?").join(", ") + p2;
|
||||
});
|
||||
|
||||
const flatParams = args.reduce((acc, v) => {
|
||||
if (Array.isArray(v)) {
|
||||
Array.prototype.push.apply(acc, v);
|
||||
} else {
|
||||
acc.push(v);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return [newQuery, ...flatParams];
|
||||
return new Result(result);
|
||||
}
|
||||
|
||||
commit() {
|
||||
@ -87,21 +58,6 @@ export class Transaction {
|
||||
await this._readyWait;
|
||||
}
|
||||
|
||||
async _executeSQL(sql, ...args) {
|
||||
const reg = /^\s*(BEGIN|END|COMMIT|ROLLBACK)(?:[^A-Z]|$)/i;
|
||||
const match = reg.exec(sql);
|
||||
if (match) {
|
||||
const statement = match[1].toUpperCase();
|
||||
throw new PawSQLiteError("Manually managing transactions is " +
|
||||
"forbidden. Found: \"" + statement + "\" statement.");
|
||||
}
|
||||
|
||||
const result = await this.adapter.sql(this.dbName,
|
||||
...this.buildQuery(sql, ...args));
|
||||
|
||||
return new Result(result);
|
||||
}
|
||||
|
||||
async _begin() {
|
||||
const result = await this.adapter.sql(this.dbName, "BEGIN");
|
||||
}
|
||||
|
@ -1,55 +0,0 @@
|
||||
import { Transaction } from "./transaction.mjs";
|
||||
|
||||
export class TransactionManager {
|
||||
constructor(dbName, adapter) {
|
||||
this.dbName = dbName;
|
||||
this.adapter = adapter;
|
||||
this._queue = [];
|
||||
this._inTransaction = false;
|
||||
}
|
||||
|
||||
transaction() {
|
||||
return new Transaction(this.dbName, this.adapter,
|
||||
this.enqueue.bind(this));
|
||||
}
|
||||
|
||||
enqueue() {
|
||||
let completeSignal;
|
||||
let transactionComplete = new Promise((r, _) => {
|
||||
completeSignal = r;
|
||||
});
|
||||
|
||||
let readySignal;
|
||||
let dbReady = new Promise((r, _) => {
|
||||
readySignal = () => {
|
||||
r(completeSignal);
|
||||
};
|
||||
});
|
||||
|
||||
this._queue.push({readySignal, transactionComplete});
|
||||
|
||||
this._processQueue();
|
||||
return dbReady;
|
||||
}
|
||||
|
||||
async _processQueue() {
|
||||
// We're already processing the queue
|
||||
if (this._inTransaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
let item = this._queue.shift();
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._inTransaction = true;
|
||||
|
||||
item.readySignal();
|
||||
await item.transactionComplete;
|
||||
|
||||
this._inTransaction = false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user