| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- 'use strict'
- const debug = require('debug')('mssql:base')
- const { EventEmitter } = require('events')
- const { IDS } = require('../utils')
- const globalConnection = require('../global-connection')
- const { TransactionError } = require('../error')
- const shared = require('../shared')
- const ISOLATION_LEVEL = require('../isolationlevel')
- /**
- * Class Transaction.
- *
- * @property {Number} isolationLevel Controls the locking and row versioning behavior of TSQL statements issued by a connection. READ_COMMITTED by default.
- * @property {String} name Transaction name. Empty string by default.
- *
- * @fires Transaction#begin
- * @fires Transaction#commit
- * @fires Transaction#rollback
- */
- class Transaction extends EventEmitter {
- /**
- * Create new Transaction.
- *
- * @param {Connection} [parent] If ommited, global connection is used instead.
- */
- constructor (parent) {
- super()
- IDS.add(this, 'Transaction')
- debug('transaction(%d): created', IDS.get(this))
- this.parent = parent || globalConnection.pool
- this.isolationLevel = ISOLATION_LEVEL.READ_COMMITTED
- this.name = ''
- }
- get connected () {
- return this.parent.connected
- }
- /**
- * Acquire connection from connection pool.
- *
- * @param {Request} request Request.
- * @param {ConnectionPool~acquireCallback} [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise.
- * @return {Transaction|Promise}
- */
- acquire (request, callback) {
- if (!this._acquiredConnection) {
- setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
- return this
- }
- if (this._activeRequest) {
- setImmediate(callback, new TransactionError("Can't acquire connection for the request. There is another request in progress.", 'EREQINPROG'))
- return this
- }
- this._activeRequest = request
- setImmediate(callback, null, this._acquiredConnection, this._acquiredConfig)
- return this
- }
- /**
- * Release connection back to the pool.
- *
- * @param {Connection} connection Previously acquired connection.
- * @return {Transaction}
- */
- release (connection) {
- if (connection === this._acquiredConnection) {
- this._activeRequest = null
- }
- return this
- }
- /**
- * Begin a transaction.
- *
- * @param {Number} [isolationLevel] Controls the locking and row versioning behavior of TSQL statements issued by a connection.
- * @param {basicCallback} [callback] A callback which is called after transaction has began, or an error has occurred. If omited, method returns Promise.
- * @return {Transaction|Promise}
- */
- begin (isolationLevel, callback) {
- if (isolationLevel instanceof Function) {
- callback = isolationLevel
- isolationLevel = undefined
- }
- if (typeof callback === 'function') {
- this._begin(isolationLevel, err => {
- if (!err) {
- this.emit('begin')
- }
- callback(err)
- })
- return this
- }
- return new shared.Promise((resolve, reject) => {
- this._begin(isolationLevel, err => {
- if (err) return reject(err)
- this.emit('begin')
- resolve(this)
- })
- })
- }
- /**
- * @private
- * @param {Number} [isolationLevel]
- * @param {basicCallback} [callback]
- * @return {Transaction}
- */
- _begin (isolationLevel, callback) {
- if (this._acquiredConnection) {
- return setImmediate(callback, new TransactionError('Transaction has already begun.', 'EALREADYBEGUN'))
- }
- this._aborted = false
- this._rollbackRequested = false
- if (isolationLevel) {
- if (Object.keys(ISOLATION_LEVEL).some(key => {
- return ISOLATION_LEVEL[key] === isolationLevel
- })) {
- this.isolationLevel = isolationLevel
- } else {
- throw new TransactionError('Invalid isolation level.')
- }
- }
- setImmediate(callback)
- }
- /**
- * Commit a transaction.
- *
- * @param {basicCallback} [callback] A callback which is called after transaction has commited, or an error has occurred. If omited, method returns Promise.
- * @return {Transaction|Promise}
- */
- commit (callback) {
- if (typeof callback === 'function') {
- this._commit(err => {
- if (!err) {
- this.emit('commit')
- }
- callback(err)
- })
- return this
- }
- return new shared.Promise((resolve, reject) => {
- this._commit(err => {
- if (err) return reject(err)
- this.emit('commit')
- resolve()
- })
- })
- }
- /**
- * @private
- * @param {basicCallback} [callback]
- * @return {Transaction}
- */
- _commit (callback) {
- if (this._aborted) {
- return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
- }
- if (!this._acquiredConnection) {
- return setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
- }
- if (this._activeRequest) {
- return setImmediate(callback, new TransactionError("Can't commit transaction. There is a request in progress.", 'EREQINPROG'))
- }
- setImmediate(callback)
- }
- /**
- * Returns new request using this transaction.
- *
- * @return {Request}
- */
- request () {
- return new shared.driver.Request(this)
- }
- /**
- * Rollback a transaction.
- *
- * @param {basicCallback} [callback] A callback which is called after transaction has rolled back, or an error has occurred. If omited, method returns Promise.
- * @return {Transaction|Promise}
- */
- rollback (callback) {
- if (typeof callback === 'function') {
- this._rollback(err => {
- if (!err) {
- this.emit('rollback', this._aborted)
- }
- callback(err)
- })
- return this
- }
- return new shared.Promise((resolve, reject) => {
- return this._rollback(err => {
- if (err) return reject(err)
- this.emit('rollback', this._aborted)
- resolve()
- })
- })
- }
- /**
- * @private
- * @param {basicCallback} [callback]
- * @return {Transaction}
- */
- _rollback (callback) {
- if (this._aborted) {
- return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
- }
- if (!this._acquiredConnection) {
- return setImmediate(callback, new TransactionError('Transaction has not begun. Call begin() first.', 'ENOTBEGUN'))
- }
- if (this._activeRequest) {
- return setImmediate(callback, new TransactionError("Can't rollback transaction. There is a request in progress.", 'EREQINPROG'))
- }
- this._rollbackRequested = true
- setImmediate(callback)
- }
- }
- module.exports = Transaction
|