123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- 'use strict';
- const { cppdb } = require('../util');
- const controllers = new WeakMap();
- module.exports = function transaction(fn) {
- if (typeof fn !== 'function') throw new TypeError('Expected first argument to be a function');
- const db = this[cppdb];
- const controller = getController(db, this);
- const { apply } = Function.prototype;
- // Each version of the transaction function has these same properties
- const properties = {
- default: { value: wrapTransaction(apply, fn, db, controller.default) },
- deferred: { value: wrapTransaction(apply, fn, db, controller.deferred) },
- immediate: { value: wrapTransaction(apply, fn, db, controller.immediate) },
- exclusive: { value: wrapTransaction(apply, fn, db, controller.exclusive) },
- database: { value: this, enumerable: true },
- };
- Object.defineProperties(properties.default.value, properties);
- Object.defineProperties(properties.deferred.value, properties);
- Object.defineProperties(properties.immediate.value, properties);
- Object.defineProperties(properties.exclusive.value, properties);
- // Return the default version of the transaction function
- return properties.default.value;
- };
- // Return the database's cached transaction controller, or create a new one
- const getController = (db, self) => {
- let controller = controllers.get(db);
- if (!controller) {
- const shared = {
- commit: db.prepare('COMMIT', self, false),
- rollback: db.prepare('ROLLBACK', self, false),
- savepoint: db.prepare('SAVEPOINT `\t_bs3.\t`', self, false),
- release: db.prepare('RELEASE `\t_bs3.\t`', self, false),
- rollbackTo: db.prepare('ROLLBACK TO `\t_bs3.\t`', self, false),
- };
- controllers.set(db, controller = {
- default: Object.assign({ begin: db.prepare('BEGIN', self, false) }, shared),
- deferred: Object.assign({ begin: db.prepare('BEGIN DEFERRED', self, false) }, shared),
- immediate: Object.assign({ begin: db.prepare('BEGIN IMMEDIATE', self, false) }, shared),
- exclusive: Object.assign({ begin: db.prepare('BEGIN EXCLUSIVE', self, false) }, shared),
- });
- }
- return controller;
- };
- // Return a new transaction function by wrapping the given function
- const wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
- let before, after, undo;
- if (db.inTransaction) {
- before = savepoint;
- after = release;
- undo = rollbackTo;
- } else {
- before = begin;
- after = commit;
- undo = rollback;
- }
- before.run();
- try {
- const result = apply.call(fn, this, arguments);
- after.run();
- return result;
- } catch (ex) {
- if (db.inTransaction) {
- undo.run();
- if (undo !== rollback) after.run();
- }
- throw ex;
- }
- };
|