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

Commit 3657f32e by Ratanak Lun Committed by Sushant

fix(mssql): improper parameter binding in sql generation (#8782)

1 parent ccee348c
...@@ -144,16 +144,9 @@ class Query extends AbstractQuery { ...@@ -144,16 +144,9 @@ class Query extends AbstractQuery {
static formatBindParameters(sql, values, dialect) { static formatBindParameters(sql, values, dialect) {
const bindParam = {}; const bindParam = {};
let i = 0;
const seen = {};
const replacementFunc = (match, key, values) => { const replacementFunc = (match, key, values) => {
if (seen[key] !== undefined) {
return seen[key];
}
if (values[key] !== undefined) { if (values[key] !== undefined) {
i = i + 1;
bindParam[key] = values[key]; bindParam[key] = values[key];
seen[key] = '$' + i;
return '@' + key; return '@' + key;
} }
return undefined; return undefined;
......
...@@ -3,38 +3,42 @@ ...@@ -3,38 +3,42 @@
const chai = require('chai'), const chai = require('chai'),
expect = chai.expect, expect = chai.expect,
Sequelize = require(__dirname + '/../../../../index'), Sequelize = require(__dirname + '/../../../../index'),
Support = require(__dirname + '/../../support'),
dialect = Support.getTestDialect(),
tedious = require('tedious'), tedious = require('tedious'),
sinon = require('sinon'), sinon = require('sinon'),
connectionStub = sinon.stub(tedious, 'Connection'); connectionStub = sinon.stub(tedious, 'Connection');
connectionStub.returns({on() {}}); connectionStub.returns({on() {}});
describe('[MSSQL] Connection Manager', () => { if (dialect === 'mssql') {
let instance, describe('[MSSQL Specific] Connection Manager', () => {
config; let instance,
beforeEach(() => { config;
config = { beforeEach(() => {
dialect: 'mssql', config = {
database: 'none', dialect: 'mssql',
username: 'none', database: 'none',
password: 'none', username: 'none',
host: 'localhost', password: 'none',
port: 2433, host: 'localhost',
pool: {}, port: 2433,
dialectOptions: { pool: {},
domain: 'TEST.COM' dialectOptions: {
} domain: 'TEST.COM'
}; }
instance = new Sequelize(config.database };
, config.username instance = new Sequelize(config.database
, config.password , config.username
, config); , config.password
}); , config);
it('connectionManager._connect() Does not delete `domain` from config.dialectOptions',
() => {
expect(config.dialectOptions.domain).to.equal('TEST.COM');
instance.dialect.connectionManager._connect(config);
expect(config.dialectOptions.domain).to.equal('TEST.COM');
}); });
});
it('connectionManager._connect() Does not delete `domain` from config.dialectOptions',
() => {
expect(config.dialectOptions.domain).to.equal('TEST.COM');
instance.dialect.connectionManager._connect(config);
expect(config.dialectOptions.domain).to.equal('TEST.COM');
});
});
}
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
const path = require('path'); const path = require('path');
const Query = require(path.resolve('./lib/dialects/mssql/query.js')); const Query = require(path.resolve('./lib/dialects/mssql/query.js'));
const Support = require(path.resolve('./test/support')); const Support = require(__dirname + '/../../support');
const dialect = Support.getTestDialect();
const sequelize = Support.sequelize; const sequelize = Support.sequelize;
const sinon = require('sinon'); const sinon = require('sinon');
const expect = require('chai').expect; const expect = require('chai').expect;
...@@ -12,32 +13,58 @@ const connectionStub = { beginTransaction: () => {}, lib: tedious }; ...@@ -12,32 +13,58 @@ const connectionStub = { beginTransaction: () => {}, lib: tedious };
let sandbox, query; let sandbox, query;
describe('[MSSQL]', () => { if (dialect === 'mssql') {
describe('beginTransaction', () => { describe('[MSSQL Specific] Query', () => {
beforeEach(() => { describe('beginTransaction', () => {
sandbox = sinon.sandbox.create(); beforeEach(() => {
const options = { sandbox = sinon.sandbox.create();
transaction: { name: 'transactionName' }, const options = {
isolationLevel: 'REPEATABLE_READ', transaction: { name: 'transactionName' },
logging: false isolationLevel: 'REPEATABLE_READ',
}; logging: false
sandbox.stub(connectionStub, 'beginTransaction').callsFake(cb => { };
cb(); sandbox.stub(connectionStub, 'beginTransaction').callsFake(cb => {
cb();
});
query = new Query(connectionStub, sequelize, options);
}); });
query = new Query(connectionStub, sequelize, options);
});
it('should call beginTransaction with correct arguments', () => { it('should call beginTransaction with correct arguments', () => {
return query._run(connectionStub, 'BEGIN TRANSACTION') return query._run(connectionStub, 'BEGIN TRANSACTION')
.then(() => { .then(() => {
expect(connectionStub.beginTransaction.called).to.equal(true); expect(connectionStub.beginTransaction.called).to.equal(true);
expect(connectionStub.beginTransaction.args[0][1]).to.equal('transactionName'); expect(connectionStub.beginTransaction.args[0][1]).to.equal('transactionName');
expect(connectionStub.beginTransaction.args[0][2]).to.equal(tediousIsolationLevel.REPEATABLE_READ); expect(connectionStub.beginTransaction.args[0][2]).to.equal(tediousIsolationLevel.REPEATABLE_READ);
}); });
});
afterEach(() => {
sandbox.restore();
});
}); });
afterEach(() => { describe('formatBindParameters', () => {
sandbox.restore(); it('should convert Sequelize named binding format to MSSQL format', () => {
const sql = 'select $one as a, $two as b, $one as c, $three as d, $one as e';
const values = { one: 1, two: 2, three: 3 };
const expected = 'select @one as a, @two as b, @one as c, @three as d, @one as e';
const result = Query.formatBindParameters(sql, values, dialect);
expect(result[0]).to.be.a('string');
expect(result[0]).to.equal(expected);
});
it('should convert Sequelize numbered binding format to MSSQL format', () => {
const sql = 'select $1 as a, $2 as b, $1 as c, $3 as d, $1 as e';
const values = [1, 2, 3];
const expected = 'select @0 as a, @1 as b, @0 as c, @2 as d, @0 as e';
const result = Query.formatBindParameters(sql, values, dialect);
expect(result[0]).to.be.a('string');
expect(result[0]).to.equal(expected);
});
}); });
}); });
}); }
...@@ -2,63 +2,67 @@ ...@@ -2,63 +2,67 @@
const ResourceLock = require('../../../../lib/dialects/mssql/resource-lock'), const ResourceLock = require('../../../../lib/dialects/mssql/resource-lock'),
Promise = require('../../../../lib/promise'), Promise = require('../../../../lib/promise'),
assert = require('assert'); assert = require('assert'),
Support = require(__dirname + '/../../support'),
dialect = Support.getTestDialect();
describe('[MSSQL Specific] ResourceLock', () => { if (dialect === 'mssql') {
it('should process requests serially', () => { describe('[MSSQL Specific] ResourceLock', () => {
const expected = {}; it('should process requests serially', () => {
const lock = new ResourceLock(expected); const expected = {};
let last = 0; const lock = new ResourceLock(expected);
let last = 0;
function validateResource(actual) { function validateResource(actual) {
assert.equal(actual, expected); assert.equal(actual, expected);
} }
return Promise.all([ return Promise.all([
Promise.using(lock.lock(), resource => { Promise.using(lock.lock(), resource => {
validateResource(resource); validateResource(resource);
assert.equal(last, 0); assert.equal(last, 0);
last = 1; last = 1;
return Promise.delay(15); return Promise.delay(15);
}), }),
Promise.using(lock.lock(), resource => { Promise.using(lock.lock(), resource => {
validateResource(resource); validateResource(resource);
assert.equal(last, 1); assert.equal(last, 1);
last = 2; last = 2;
}), }),
Promise.using(lock.lock(), resource => { Promise.using(lock.lock(), resource => {
validateResource(resource); validateResource(resource);
assert.equal(last, 2); assert.equal(last, 2);
last = 3; last = 3;
return Promise.delay(5); return Promise.delay(5);
}) })
]); ]);
}); });
it('should still return resource after failure', () => { it('should still return resource after failure', () => {
const expected = {}; const expected = {};
const lock = new ResourceLock(expected); const lock = new ResourceLock(expected);
function validateResource(actual) { function validateResource(actual) {
assert.equal(actual, expected); assert.equal(actual, expected);
} }
return Promise.all([ return Promise.all([
Promise.using(lock.lock(), resource => { Promise.using(lock.lock(), resource => {
validateResource(resource); validateResource(resource);
throw new Error('unexpected error'); throw new Error('unexpected error');
}).catch(() => {}), }).catch(() => {}),
Promise.using(lock.lock(), validateResource) Promise.using(lock.lock(), validateResource)
]); ]);
}); });
it('should be able to.lock resource without waiting on lock', () => { it('should be able to.lock resource without waiting on lock', () => {
const expected = {}; const expected = {};
const lock = new ResourceLock(expected); const lock = new ResourceLock(expected);
assert.equal(lock.unwrap(), expected); assert.equal(lock.unwrap(), expected);
});
}); });
}); }
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!