Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions lib/base/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class Transaction extends EventEmitter {
}

this._aborted = false
this._abortReason = null
this._rollbackRequested = false
if (isolationLevel) {
if (Object.keys(ISOLATION_LEVEL).some(key => {
Expand Down Expand Up @@ -199,6 +200,22 @@ class Transaction extends EventEmitter {
})
}

/**
* Creates a TransactionError for an aborted transaction, preserving the
* original abort reason (if any) as `originalError`.
*
* @private
* @return {TransactionError}
*/

_createAbortError () {
const err = new TransactionError('Transaction has been aborted.', 'EABORT')
if (this._abortReason) {
Object.defineProperty(err, 'originalError', { enumerable: true, value: this._abortReason })
}
return err
}

/**
* @private
* @param {basicCallback} [callback]
Expand All @@ -207,7 +224,7 @@ class Transaction extends EventEmitter {

_commit (callback) {
if (this._aborted) {
return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
return setImmediate(callback, this._createAbortError())
}

if (!this._acquiredConnection) {
Expand Down Expand Up @@ -279,7 +296,7 @@ class Transaction extends EventEmitter {

_rollback (callback) {
if (this._aborted) {
return setImmediate(callback, new TransactionError('Transaction has been aborted.', 'EABORT'))
return setImmediate(callback, this._createAbortError())
}

if (!this._acquiredConnection) {
Expand Down
9 changes: 9 additions & 0 deletions lib/tedious/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ class Request extends BaseRequest {
} catch (e) {
// noop
}
if (err && this.parent._aborted) {
this.parent._abortReason = err
}
callback(err, ...args)
}
if (err) return callbackWithRelease(err)
Expand Down Expand Up @@ -501,6 +504,9 @@ class Request extends BaseRequest {
}

this.parent.release(connection)
if (error && this.parent._aborted) {
this.parent._abortReason = error
}
hasReturned = true

if (error) {
Expand Down Expand Up @@ -865,6 +871,9 @@ class Request extends BaseRequest {
}

this.parent.release(connection)
if (error && this.parent._aborted) {
this.parent._abortReason = error
}
hasReturned = true

if (error) {
Expand Down
1 change: 1 addition & 0 deletions lib/tedious/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Transaction extends BaseTransaction {
this._acquiredConnection = null
this._acquiredConfig = null
this._aborted = true
this._abortReason = this._abortReason || new Error('Transaction was rolled back by the server')

publish(CHANNELS.TRANSACTION_ROLLBACK, () => ({
transactionId: IDS.get(this),
Expand Down
59 changes: 59 additions & 0 deletions test/common/unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1402,4 +1402,63 @@ describe('connection string auth - tedious', () => {
})
})
})

describe('Transaction EABORT originalError', () => {
const BaseTransaction = require('../../lib/base/transaction')

it('commit on aborted transaction returns EABORT with originalError', (done) => {
const tx = new BaseTransaction(null)
tx._aborted = true
const reason = new Error('deadlock victim')
reason.code = 'EREQUEST'
tx._abortReason = reason

tx.commit((err) => {
assert.ok(err)
assert.strictEqual(err.code, 'EABORT')
assert.strictEqual(err.message, 'Transaction has been aborted.')
assert.strictEqual(err.originalError, reason)
assert.strictEqual(err.originalError.message, 'deadlock victim')
done()
})
})

it('rollback on aborted transaction returns EABORT with originalError', (done) => {
const tx = new BaseTransaction(null)
tx._aborted = true
const reason = new Error('constraint violation')
tx._abortReason = reason

tx.rollback((err) => {
assert.ok(err)
assert.strictEqual(err.code, 'EABORT')
assert.strictEqual(err.message, 'Transaction has been aborted.')
assert.strictEqual(err.originalError, reason)
done()
})
})

it('commit on aborted transaction without _abortReason omits originalError', (done) => {
const tx = new BaseTransaction(null)
tx._aborted = true

tx.commit((err) => {
assert.ok(err)
assert.strictEqual(err.code, 'EABORT')
assert.strictEqual(err.originalError, undefined)
done()
})
})

it('_abortReason is reset on begin', (done) => {
const tx = new BaseTransaction(null)
tx._abortReason = new Error('old reason')
tx._begin(undefined, (err) => {
assert.ifError(err)
assert.strictEqual(tx._abortReason, null)
assert.strictEqual(tx._aborted, false)
done()
})
})
})
})