177 lines
4.4 KiB
JavaScript
177 lines
4.4 KiB
JavaScript
|
const Transaction = require('../../execution/transaction');
|
||
|
const debug = require('debug')('knex:tx');
|
||
|
|
||
|
class Transaction_MSSQL extends Transaction {
|
||
|
begin(/** @type {import('tedious').Connection} */ conn) {
|
||
|
debug('transaction::begin id=%s', this.txid);
|
||
|
|
||
|
return new Promise((resolve, reject) => {
|
||
|
conn.beginTransaction(
|
||
|
(err) => {
|
||
|
if (err) {
|
||
|
debug(
|
||
|
'transaction::begin error id=%s message=%s',
|
||
|
this.txid,
|
||
|
err.message
|
||
|
);
|
||
|
return reject(err);
|
||
|
}
|
||
|
resolve();
|
||
|
},
|
||
|
this.outerTx ? this.txid : undefined,
|
||
|
nameToIsolationLevelEnum(this.isolationLevel)
|
||
|
);
|
||
|
}).then(this._resolver, this._rejecter);
|
||
|
}
|
||
|
|
||
|
savepoint(conn) {
|
||
|
debug('transaction::savepoint id=%s', this.txid);
|
||
|
|
||
|
return new Promise((resolve, reject) => {
|
||
|
conn.saveTransaction(
|
||
|
(err) => {
|
||
|
if (err) {
|
||
|
debug(
|
||
|
'transaction::savepoint id=%s message=%s',
|
||
|
this.txid,
|
||
|
err.message
|
||
|
);
|
||
|
return reject(err);
|
||
|
}
|
||
|
|
||
|
this.trxClient.emit('query', {
|
||
|
__knexUid: this.trxClient.__knexUid,
|
||
|
__knexTxId: this.trxClient.__knexTxId,
|
||
|
autogenerated: true,
|
||
|
sql: this.outerTx
|
||
|
? `SAVE TRANSACTION [${this.txid}]`
|
||
|
: `SAVE TRANSACTION`,
|
||
|
});
|
||
|
resolve();
|
||
|
},
|
||
|
this.outerTx ? this.txid : undefined
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
commit(conn, value) {
|
||
|
debug('transaction::commit id=%s', this.txid);
|
||
|
|
||
|
return new Promise((resolve, reject) => {
|
||
|
conn.commitTransaction(
|
||
|
(err) => {
|
||
|
if (err) {
|
||
|
debug(
|
||
|
'transaction::commit error id=%s message=%s',
|
||
|
this.txid,
|
||
|
err.message
|
||
|
);
|
||
|
return reject(err);
|
||
|
}
|
||
|
|
||
|
this._completed = true;
|
||
|
resolve(value);
|
||
|
},
|
||
|
this.outerTx ? this.txid : undefined
|
||
|
);
|
||
|
}).then(() => this._resolver(value), this._rejecter);
|
||
|
}
|
||
|
|
||
|
release(conn, value) {
|
||
|
return this._resolver(value);
|
||
|
}
|
||
|
|
||
|
rollback(conn, error) {
|
||
|
this._completed = true;
|
||
|
debug('transaction::rollback id=%s', this.txid);
|
||
|
|
||
|
return new Promise((_resolve, reject) => {
|
||
|
if (!conn.inTransaction) {
|
||
|
return reject(
|
||
|
error || new Error('Transaction rejected with non-error: undefined')
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (conn.state.name !== 'LoggedIn') {
|
||
|
return reject(
|
||
|
new Error(
|
||
|
"Can't rollback transaction. There is a request in progress"
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
conn.rollbackTransaction(
|
||
|
(err) => {
|
||
|
if (err) {
|
||
|
debug(
|
||
|
'transaction::rollback error id=%s message=%s',
|
||
|
this.txid,
|
||
|
err.message
|
||
|
);
|
||
|
}
|
||
|
|
||
|
reject(
|
||
|
err ||
|
||
|
error ||
|
||
|
new Error('Transaction rejected with non-error: undefined')
|
||
|
);
|
||
|
},
|
||
|
this.outerTx ? this.txid : undefined
|
||
|
);
|
||
|
}).catch((err) => {
|
||
|
if (!error && this.doNotRejectOnRollback) {
|
||
|
this._resolver();
|
||
|
return;
|
||
|
}
|
||
|
if (error) {
|
||
|
try {
|
||
|
err.originalError = error;
|
||
|
} catch (_err) {
|
||
|
// This is to handle https://github.com/knex/knex/issues/4128
|
||
|
}
|
||
|
}
|
||
|
this._rejecter(err);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
rollbackTo(conn, error) {
|
||
|
return this.rollback(conn, error).then(
|
||
|
() =>
|
||
|
void this.trxClient.emit('query', {
|
||
|
__knexUid: this.trxClient.__knexUid,
|
||
|
__knexTxId: this.trxClient.__knexTxId,
|
||
|
autogenerated: true,
|
||
|
sql: `ROLLBACK TRANSACTION`,
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = Transaction_MSSQL;
|
||
|
|
||
|
function nameToIsolationLevelEnum(level) {
|
||
|
if (!level) return;
|
||
|
level = level.toUpperCase().replace(' ', '_');
|
||
|
const knownEnum = isolationEnum[level];
|
||
|
if (!knownEnum) {
|
||
|
throw new Error(
|
||
|
`Unknown Isolation level, was expecting one of: ${JSON.stringify(
|
||
|
humanReadableKeys
|
||
|
)}`
|
||
|
);
|
||
|
}
|
||
|
return knownEnum;
|
||
|
}
|
||
|
|
||
|
// Based on: https://github.com/tediousjs/node-mssql/blob/master/lib/isolationlevel.js
|
||
|
const isolationEnum = {
|
||
|
READ_UNCOMMITTED: 0x01,
|
||
|
READ_COMMITTED: 0x02,
|
||
|
REPEATABLE_READ: 0x03,
|
||
|
SERIALIZABLE: 0x04,
|
||
|
SNAPSHOT: 0x05,
|
||
|
};
|
||
|
const humanReadableKeys = Object.keys(isolationEnum).map((key) =>
|
||
|
key.toLowerCase().replace('_', ' ')
|
||
|
);
|