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

Commit 05da4a94 by renaudholcombe Committed by Jan Aagaard Meier

feat(mssql): Add parameter binding support (#7577)

1 parent 5dcc6075
...@@ -7,6 +7,7 @@ const AbstractQuery = require('../abstract/query'); ...@@ -7,6 +7,7 @@ const AbstractQuery = require('../abstract/query');
const sequelizeErrors = require('../../errors.js'); const sequelizeErrors = require('../../errors.js');
const parserStore = require('../parserStore')('mssql'); const parserStore = require('../parserStore')('mssql');
const _ = require('lodash'); const _ = require('lodash');
const TYPES = require('tedious').TYPES;
class Query extends AbstractQuery { class Query extends AbstractQuery {
constructor(connection, sequelize, options) { constructor(connection, sequelize, options) {
...@@ -28,7 +29,22 @@ class Query extends AbstractQuery { ...@@ -28,7 +29,22 @@ class Query extends AbstractQuery {
return 'id'; return 'id';
} }
_run(connection, sql) { getSQLTypeFromJsType(value) {
const paramType = {type: TYPES.VarChar, typeOptions: {} };
paramType.type = TYPES.NVarChar;
if (typeof value === 'number') {
if(Number.isInteger(value)){
paramType.type = TYPES.Int;
} else {
paramType.type = TYPES.Numeric;
//Default to a reasonable numeric precision/scale pending more sophisticated logic
paramType.typeOptions = {precision: 30, scale: 15};
}
}
return paramType;
}
_run(connection, sql, parameters) {
this.sql = sql; this.sql = sql;
//do we need benchmark for this query execution //do we need benchmark for this query execution
...@@ -43,7 +59,7 @@ class Query extends AbstractQuery { ...@@ -43,7 +59,7 @@ class Query extends AbstractQuery {
debug(`executing(${this.connection.uuid || 'default'}) : ${this.sql}`); debug(`executing(${this.connection.uuid || 'default'}) : ${this.sql}`);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// TRANSACTION SUPPORT // TRANSACTION SUPPORT
if (_.startsWith(this.sql, 'BEGIN TRANSACTION')) { if (_.startsWith(this.sql, 'BEGIN TRANSACTION')) {
connection.beginTransaction(err => { connection.beginTransaction(err => {
if (err) { if (err) {
...@@ -94,6 +110,13 @@ class Query extends AbstractQuery { ...@@ -94,6 +110,13 @@ class Query extends AbstractQuery {
} }
}); });
if(parameters){
_.forOwn(parameters, (value, key) => {
const paramType = this.getSQLTypeFromJsType(value);
request.addParameter(key, paramType.type, value, paramType.typeOptions);
});
}
request.on('row', columns => { request.on('row', columns => {
const row = {}; const row = {};
for (const column of columns) { for (const column of columns) {
...@@ -119,6 +142,27 @@ class Query extends AbstractQuery { ...@@ -119,6 +142,27 @@ class Query extends AbstractQuery {
return Promise.using(this.connection.lock(), connection => this._run(connection, sql, parameters)); return Promise.using(this.connection.lock(), connection => this._run(connection, sql, parameters));
} }
static formatBindParameters(sql, values, dialect) {
const bindParam = {};
let i = 0;
const seen = {};
const replacementFunc = (match, key, values) => {
if (seen[key] !== undefined) {
return seen[key];
}
if (values[key] !== undefined) {
i = i + 1;
bindParam[key] = values[key];
seen[key] = '$' + i;
return '@' + key;
}
return undefined;
};
sql = AbstractQuery.formatBindParameters(sql, values, dialect, replacementFunc)[0];
return [sql, bindParam];
}
/** /**
* High level function that handles the results of a query execution. * High level function that handles the results of a query execution.
* *
...@@ -186,7 +230,7 @@ class Query extends AbstractQuery { ...@@ -186,7 +230,7 @@ class Query extends AbstractQuery {
result = data[0]; result = data[0];
} else if (this.isBulkUpdateQuery()) { } else if (this.isBulkUpdateQuery()) {
result = data.length; result = data.length;
} else if (this.isBulkDeleteQuery()){ } else if (this.isBulkDeleteQuery()) {
result = data[0] && data[0].AFFECTEDROWS; result = data[0] && data[0].AFFECTEDROWS;
} else if (this.isVersionQuery()) { } else if (this.isVersionQuery()) {
result = data[0].version; result = data[0].version;
...@@ -253,14 +297,14 @@ class Query extends AbstractQuery { ...@@ -253,14 +297,14 @@ class Query extends AbstractQuery {
)); ));
}); });
return new sequelizeErrors.UniqueConstraintError({message, errors, parent: err, fields}); return new sequelizeErrors.UniqueConstraintError({ message, errors, parent: err, fields });
} }
match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./) || match = err.message.match(/Failed on step '(.*)'.Could not create constraint. See previous errors./) ||
err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || err.message.match(/The DELETE statement conflicted with the REFERENCE constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) ||
err.message.match(/The INSERT statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || err.message.match(/The INSERT statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) ||
err.message.match(/The MERGE statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) || err.message.match(/The MERGE statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./) ||
err.message.match(/The UPDATE statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./); err.message.match(/The UPDATE statement conflicted with the FOREIGN KEY constraint "(.*)". The conflict occurred in database "(.*)", table "(.*)", column '(.*)'./);
if (match && match.length > 0) { if (match && match.length > 0) {
return new sequelizeErrors.ForeignKeyConstraintError({ return new sequelizeErrors.ForeignKeyConstraintError({
fields: null, fields: null,
...@@ -335,8 +379,8 @@ class Query extends AbstractQuery { ...@@ -335,8 +379,8 @@ class Query extends AbstractQuery {
let autoIncrementFieldAlias = null; let autoIncrementFieldAlias = null;
if (this.model.rawAttributes.hasOwnProperty(autoIncrementField) && if (this.model.rawAttributes.hasOwnProperty(autoIncrementField) &&
this.model.rawAttributes[autoIncrementField].field !== undefined) this.model.rawAttributes[autoIncrementField].field !== undefined)
autoIncrementFieldAlias = this.model.rawAttributes[autoIncrementField].field ; autoIncrementFieldAlias = this.model.rawAttributes[autoIncrementField].field;
id = id || results && results[0][this.getInsertIdField()]; id = id || results && results[0][this.getInsertIdField()];
id = id || metaData && metaData[this.getInsertIdField()]; id = id || metaData && metaData[this.getInsertIdField()];
......
...@@ -520,6 +520,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -520,6 +520,9 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
if (dialect === 'postgres' || dialect === 'sqlite') { if (dialect === 'postgres' || dialect === 'sqlite') {
expect(logSql.indexOf('$1')).to.be.above(-1); expect(logSql.indexOf('$1')).to.be.above(-1);
expect(logSql.indexOf('$2')).to.be.above(-1); expect(logSql.indexOf('$2')).to.be.above(-1);
} else if (dialect === 'mssql'){
expect(logSql.indexOf('@0')).to.be.above(-1);
expect(logSql.indexOf('@1')).to.be.above(-1);
} }
}); });
}); });
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!