不要怂,就是干,撸起袖子干!

Commit 7801903d by Mick Hansen

fix(transaction): Issue with transaction options leaking and certain queries run…

…ning outside of the transaction connection.
1 parent 7057a7c9
# 3.14.1
- [FIXED] Issue with transaction options leaking and certain queries running outside of the transaction connection.
# 3.14.0 # 3.14.0
- [FIXED] Apply scopes to `aggregate` [#4764](https://github.com/sequelize/sequelize/issues/4764) - [FIXED] Apply scopes to `aggregate` [#4764](https://github.com/sequelize/sequelize/issues/4764)
- [FIXED] Improved postgres enum schema handling [#4796](https://github.com/sequelize/sequelize/issues/4796) - [FIXED] Improved postgres enum schema handling [#4796](https://github.com/sequelize/sequelize/issues/4796)
......
...@@ -822,13 +822,14 @@ QueryInterface.prototype.setAutocommit = function(transaction, value, options) { ...@@ -822,13 +822,14 @@ QueryInterface.prototype.setAutocommit = function(transaction, value, options) {
throw new Error('Unable to set autocommit for a transaction without transaction object!'); throw new Error('Unable to set autocommit for a transaction without transaction object!');
} }
options = Utils._.extend({ options = _.assign({}, options || {}, {
transaction: transaction,
parent: options.transaction parent: options.transaction
}, options || {}); });
var sql = this.QueryGenerator.setAutocommitQuery(value, options); var sql = this.QueryGenerator.setAutocommitQuery(value, options);
if (sql) { if (sql) {
return this.sequelize.query(sql, { transaction: transaction, logging: options.logging }); return this.sequelize.query(sql, options);
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
...@@ -839,14 +840,15 @@ QueryInterface.prototype.setIsolationLevel = function(transaction, value, option ...@@ -839,14 +840,15 @@ QueryInterface.prototype.setIsolationLevel = function(transaction, value, option
throw new Error('Unable to set isolation level for a transaction without transaction object!'); throw new Error('Unable to set isolation level for a transaction without transaction object!');
} }
options = Utils._.extend({ options = _.assign({}, options || {}, {
transaction: transaction,
parent: options.transaction parent: options.transaction
}, options || {}); });
var sql = this.QueryGenerator.setIsolationLevelQuery(value, options); var sql = this.QueryGenerator.setIsolationLevelQuery(value, options);
if (sql) { if (sql) {
return this.sequelize.query(sql, { transaction: transaction, logging: options.logging }); return this.sequelize.query(sql, options);
} else { } else {
return Promise.resolve(); return Promise.resolve();
} }
...@@ -857,10 +859,10 @@ QueryInterface.prototype.startTransaction = function(transaction, options) { ...@@ -857,10 +859,10 @@ QueryInterface.prototype.startTransaction = function(transaction, options) {
throw new Error('Unable to start a transaction without transaction object!'); throw new Error('Unable to start a transaction without transaction object!');
} }
options = Utils._.extend({ options = _.assign({}, options || {}, {
transaction: transaction, transaction: transaction,
parent: options.transaction parent: options.transaction
}, options || {}); });
var sql = this.QueryGenerator.startTransactionQuery(transaction, options); var sql = this.QueryGenerator.startTransactionQuery(transaction, options);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
...@@ -886,11 +888,12 @@ QueryInterface.prototype.commitTransaction = function(transaction, options) { ...@@ -886,11 +888,12 @@ QueryInterface.prototype.commitTransaction = function(transaction, options) {
throw new Error('Unable to commit a transaction without transaction object!'); throw new Error('Unable to commit a transaction without transaction object!');
} }
options = Utils._.extend({
options = _.assign({}, options || {}, {
transaction: transaction, transaction: transaction,
parent: options.transaction, parent: options.transaction,
supportsSearchPath: false supportsSearchPath: false
}, options || {}); });
var sql = this.QueryGenerator.commitTransactionQuery(options); var sql = this.QueryGenerator.commitTransactionQuery(options);
...@@ -906,11 +909,11 @@ QueryInterface.prototype.rollbackTransaction = function(transaction, options) { ...@@ -906,11 +909,11 @@ QueryInterface.prototype.rollbackTransaction = function(transaction, options) {
throw new Error('Unable to rollback a transaction without transaction object!'); throw new Error('Unable to rollback a transaction without transaction object!');
} }
options = Utils._.extend({ options = _.assign({}, options || {}, {
transaction: transaction, transaction: transaction,
parent: options.transaction, parent: options.transaction,
supportsSearchPath: false supportsSearchPath: false
}, options || {}); });
var sql = this.QueryGenerator.rollbackTransactionQuery(transaction, options); var sql = this.QueryGenerator.rollbackTransactionQuery(transaction, options);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
......
...@@ -33,6 +33,8 @@ var Transaction = module.exports = function(sequelize, options) { ...@@ -33,6 +33,8 @@ var Transaction = module.exports = function(sequelize, options) {
} else { } else {
this.id = this.name = uuid.v4(); this.id = this.name = uuid.v4();
} }
delete this.options.transaction;
}; };
/** /**
......
...@@ -13,6 +13,14 @@ var chai = require('chai') ...@@ -13,6 +13,14 @@ var chai = require('chai')
if (current.dialect.supports.transactions) { if (current.dialect.supports.transactions) {
describe(Support.getTestDialectTeaser('Transaction'), function() { describe(Support.getTestDialectTeaser('Transaction'), function() {
beforeEach(function () {
this.sinon = sinon.sandbox.create();
});
afterEach(function () {
this.sinon.restore();
});
this.timeout(5000); this.timeout(5000);
describe('constructor', function() { describe('constructor', function() {
it('stores options', function() { it('stores options', function() {
...@@ -126,7 +134,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() { ...@@ -126,7 +134,7 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
return expect( return expect(
this.sequelize.transaction().then(function(t) { this.sequelize.transaction().then(function(t) {
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true}).then(function() { return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true}).then(function() {
return t.commit(); return t.rollback();
}).then(function() { }).then(function() {
return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true}); return self.sequelize.query('SELECT 1+1', {transaction: t, raw: true});
}); });
...@@ -170,6 +178,38 @@ describe(Support.getTestDialectTeaser('Transaction'), function() { ...@@ -170,6 +178,38 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
})).to.be.rejectedWith('Error: Transaction cannot be rolled back because it has been finished with state: rollback'); })).to.be.rejectedWith('Error: Transaction cannot be rolled back because it has been finished with state: rollback');
}); });
it('works even if a transaction: null option is passed', function () {
this.sinon.spy(this.sequelize, 'query');
return this.sequelize.transaction({
transaction: null
}).bind(this).then(function (t) {
return t.commit().bind(this).then(function () {
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (var i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
}
});
});
});
it('works even if a transaction: undefined option is passed', function () {
this.sinon.spy(this.sequelize, 'query');
return this.sequelize.transaction({
transaction: undefined
}).bind(this).then(function (t) {
return t.commit().bind(this).then(function () {
expect(this.sequelize.query.callCount).to.be.greaterThan(0);
for (var i = 0; i < this.sequelize.query.callCount; i++) {
expect(this.sequelize.query.getCall(i).args[1].transaction).to.equal(t);
}
});
});
});
if (dialect === 'sqlite'){ if (dialect === 'sqlite'){
it('provides persistent transactions', function () { it('provides persistent transactions', function () {
var sequelize = new Support.Sequelize('database', 'username', 'password', {dialect: 'sqlite'}) var sequelize = new Support.Sequelize('database', 'username', 'password', {dialect: 'sqlite'})
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!