/* * Copyright (c) 2015-present, Vitaly Tomilov * * See the LICENSE file at the top-level directory of this distribution * for licensing information. * * Removal or modification of this copyright notice is prohibited. */ const {InnerState} = require('./inner-state'); const {addInspection} = require('./utils'); const {assert} = require('./assert'); /** * @enum {number} * @alias txMode.isolationLevel * @readonly * @summary Transaction Isolation Level. * @description * The type is available from the {@link txMode} namespace. * * @see $[Transaction Isolation] */ const isolationLevel = { /** Isolation level not specified. */ none: 0, /** ISOLATION LEVEL SERIALIZABLE */ serializable: 1, /** ISOLATION LEVEL REPEATABLE READ */ repeatableRead: 2, /** ISOLATION LEVEL READ COMMITTED */ readCommitted: 3 // From the official documentation: http://www.postgresql.org/docs/9.5/static/sql-set-transaction.html // The SQL standard defines one additional level, READ UNCOMMITTED. In PostgreSQL READ UNCOMMITTED is treated as READ COMMITTED. // => skipping `READ UNCOMMITTED`. }; /** * @class txMode.TransactionMode * @description * Constructs a complete transaction-opening `BEGIN` command, from these options: * - isolation level * - access mode * - deferrable mode * * The type is available from the {@link txMode} namespace. * * @param {} [options] * Transaction Mode options. * * @param {txMode.isolationLevel} [options.tiLevel] * Transaction Isolation Level. * * @param {boolean} [options.readOnly] * Sets transaction access mode based on the read-only flag: * - `undefined` - access mode not specified (default) * - `true` - access mode is set to `READ ONLY` * - `false` - access mode is set to `READ WRITE` * * @param {boolean} [options.deferrable] * Sets transaction deferrable mode based on the boolean value: * - `undefined` - deferrable mode not specified (default) * - `true` - mode is set to `DEFERRABLE` * - `false` - mode is set to `NOT DEFERRABLE` * * It is used only when `tiLevel`=`isolationLevel.serializable` * and `readOnly`=`true`, or else it is ignored. * * @returns {txMode.TransactionMode} * * @see $[BEGIN], {@link txMode.isolationLevel} * * @example * * const {TransactionMode, isolationLevel} = pgp.txMode; * * // Create a reusable transaction mode (serializable + read-only + deferrable): * const mode = new TransactionMode({ * tiLevel: isolationLevel.serializable, * readOnly: true, * deferrable: true * }); * * db.tx({mode}, t => { * return t.any('SELECT * FROM table'); * }) * .then(data => { * // success; * }) * .catch(error => { * // error * }); * * // Instead of the default BEGIN, such transaction will start with: * * // BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE * */ class TransactionMode extends InnerState { constructor(options) { options = assert(options, ['tiLevel', 'deferrable', 'readOnly']); const {readOnly, deferrable} = options; let {tiLevel} = options; let level, accessMode, deferrableMode, begin = 'begin'; tiLevel = (tiLevel > 0) ? parseInt(tiLevel) : 0; if (tiLevel > 0 && tiLevel < 4) { const values = ['serializable', 'repeatable read', 'read committed']; level = 'isolation level ' + values[tiLevel - 1]; } if (readOnly) { accessMode = 'read only'; } else { if (readOnly !== undefined) { accessMode = 'read write'; } } // From the official documentation: http://www.postgresql.org/docs/9.5/static/sql-set-transaction.html // The DEFERRABLE transaction property has no effect unless the transaction is also SERIALIZABLE and READ ONLY if (tiLevel === isolationLevel.serializable && readOnly) { if (deferrable) { deferrableMode = 'deferrable'; } else { if (deferrable !== undefined) { deferrableMode = 'not deferrable'; } } } if (level) { begin += ' ' + level; } if (accessMode) { begin += ' ' + accessMode; } if (deferrableMode) { begin += ' ' + deferrableMode; } super({begin, capBegin: begin.toUpperCase()}); } /** * @method txMode.TransactionMode#begin * @description * Returns a complete BEGIN statement, according to all the parameters passed into the class. * * This method is primarily for internal use by the library. * * @param {boolean} [cap=false] * Indicates whether the returned SQL must be capitalized. * * @returns {string} */ begin(cap) { return cap ? this._inner.capBegin : this._inner.begin; } } addInspection(TransactionMode, function () { return this.begin(true); }); /** * @namespace txMode * @description * Transaction Mode namespace, available as `pgp.txMode`, before and after initializing the library. * * Extends the default `BEGIN` with Transaction Mode parameters: * - isolation level * - access mode * - deferrable mode * * @property {function} TransactionMode * {@link txMode.TransactionMode TransactionMode} class constructor. * * @property {txMode.isolationLevel} isolationLevel * Transaction Isolation Level enumerator * * @see $[BEGIN] */ module.exports = { isolationLevel, TransactionMode };