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

Commit 1e709899 by User4martin

implement bind-parameter for custom queries on postgres / sqlite

1 parent 7d5ecfcb
......@@ -381,6 +381,13 @@ var groupJoinData = function(rows, includeOptions, options) {
};
/**
* rewrite query with parameters
*/
AbstractQuery.prototype.formatBindParameters = function(sql, parameters, dialect) {
sql = Utils.formatBindParameters(sql, parameters, dialect);
return [sql, []];
};
/**
* Execute the passed sql query.
*
* Examples:
......
......@@ -24,7 +24,7 @@ Query.prototype.getInsertIdField = function() {
return 'id';
};
Query.prototype.run = function(sql) {
Query.prototype.run = function(sql, parameters) {
var self = this;
this.sql = sql;
......
......@@ -21,7 +21,7 @@ var Query = function(connection, sequelize, options) {
};
Utils.inherit(Query, AbstractQuery);
Query.prototype.run = function(sql) {
Query.prototype.run = function(sql, parameters) {
var self = this;
this.sql = sql;
......
......@@ -87,7 +87,50 @@ Utils.inherit(Query, AbstractQuery);
Query.prototype.parseDialectSpecificFields = parseDialectSpecificFields;
Query.prototype.run = function(sql) {
/**
* rewrite query with parameters
*/
Query.prototype.formatBindParameters = function(sql, values, timeZone, dialect) {
var bindParam = [];
if (Array.isArray(values)) {
bindParam = values;
sql = sql.replace(/\$(\$|(?:\d+))/g, function(value, key) {
if ('$' === key) {
return key;
}
key = key - 1;
if (values[key] !== undefined) {
return value;
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
} else {
var i = 0;
var seen = {};
sql = sql.replace(/\$(\$|(?!\d)(?:\w+))/g, function(value, key) {
if ('$' === key) {
return key;
}
if (seen[key] !== undefined) {
return seen[key];
}
if (values[key] !== undefined) {
i = i + 1;
bindParam.push(values[key]);
seen[key] = '$'+i;
return '$'+i;
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
}
return [sql, bindParam];
};
Query.prototype.run = function(sql, parameters) {
this.sql = sql;
if(!Utils._.isEmpty(this.options.searchPath)){
......@@ -96,7 +139,7 @@ Query.prototype.run = function(sql) {
var self = this
, receivedError = false
, query = this.client.query(this.sql)
, query = ((parameters && parameters.length) ? this.client.query(this.sql, parameters) : this.client.query(this.sql))
, rows = [];
this.sequelize.log('Executing (' + (this.client.uuid || 'default') + '): ' + this.sql, this.options);
......
......@@ -24,11 +24,61 @@ Query.prototype.getInsertIdField = function() {
return 'lastID';
};
Query.prototype.run = function(sql) {
/**
* rewrite query with parameters
*/
Query.prototype.formatBindParameters = function(sql, values, timeZone, dialect) {
var bindParam = [];
if (Array.isArray(values)) {
bindParam = {};
values.forEach(function(v, i) {
bindParam['$'+(i+1)] = v;
});
sql = sql.replace(/\$(\$|(?:\d+))/g, function(value, key) {
if ('$' === key) {
return key;
}
key = key - 1;
if (values[key] !== undefined) {
return value;
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
} else {
bindParam = {};
if (typeof values === 'object') {
Object.keys(values).forEach(function(k) {
bindParam['$'+k] = values[k];
});
}
sql = sql.replace(/\$(\$|(?!\d)(?:\w+))/g, function(value, key) {
if ('$' === key) {
return key;
}
if (values[key] !== undefined) {
return value;
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
}
return [sql, bindParam];
};
Query.prototype.run = function(sql, parameters) {
var self = this
, promise;
this.sql = sql;
var method = self.getDatabaseMethod();
if (method === 'exec') {
// exec does not support bind parameter
sql = Utils.formatBindParameters(sql, self.options.bind, self.options.dialect, { noUnescape: true });
this.sql = sql;
}
this.sequelize.log('Executing (' + (this.database.uuid || 'default') + '): ' + this.sql, this.options);
......@@ -41,7 +91,7 @@ Query.prototype.run = function(sql) {
return resolve();
} else {
resolve(new Utils.Promise(function(resolve, reject) {
self.database[self.getDatabaseMethod()](self.sql, function(err, results) {
var afterExecute = function(err, results) {
if (err) {
err.sql = self.sql;
reject(self.formatError(err));
......@@ -139,7 +189,15 @@ Query.prototype.run = function(sql) {
resolve(result);
}
});
};
if (method === 'exec') {
// exec does not support bind parameter
self.database[method](self.sql, afterExecute);
} else {
if (!parameters) parameters = [];
self.database[method](self.sql, parameters, afterExecute);
}
}));
}
};
......
......@@ -707,6 +707,14 @@ Sequelize.prototype.query = function(sql, options) {
options.replacements = sql.values;
}
if (sql.bind !== undefined) {
if (options.bind !== undefined) {
throw new Error('Both `sql.bind` and `options.bind` cannot be set at the same time');
}
options.bind= sql.bind;
}
if (sql.query !== undefined) {
sql = sql.query;
}
......@@ -718,6 +726,9 @@ Sequelize.prototype.query = function(sql, options) {
options.raw = true;
}
if (options.replacements && options.bind) {
throw new Error('Both `replacements` and `bind` cannot be set at the same time');
}
if (options.replacements) {
if (Array.isArray(options.replacements)) {
sql = Utils.format([sql].concat(options.replacements), this.options.dialect);
......@@ -726,6 +737,12 @@ Sequelize.prototype.query = function(sql, options) {
sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
}
}
var bindParameters;
if (options.bind) {
var bindSql = self.dialect.Query.prototype.formatBindParameters(sql, options.bind, this.options.dialect);
sql = bindSql[0];
bindParameters = bindSql[1];
}
options = Utils._.extend(Utils._.clone(this.options.query), options);
options = Utils._.defaults(options, {
......@@ -768,7 +785,7 @@ Sequelize.prototype.query = function(sql, options) {
options.transaction ? options.transaction.connection : self.connectionManager.getConnection(options)
).then(function (connection) {
var query = new self.dialect.Query(connection, self, options);
return query.run(sql).finally(function() {
return query.run(sql, bindParameters).finally(function() {
if (options.transaction) return;
return self.connectionManager.releaseConnection(connection);
});
......
......@@ -161,6 +161,36 @@ SqlString.formatNamedParameters = function(sql, values, timeZone, dialect) {
});
};
SqlString.formatBindParameters = function(sql, values, timeZone, dialect, options) {
options = options || {};
if (Array.isArray(values)) {
return sql.replace(/\$(\$|(?:\d+))/g, function(value, key) {
if ('$' === key) {
return options.noUnescape ? value : key;
}
key = key - 1;
if (values[key] !== undefined) {
return SqlString.escape(values[key], false, timeZone, dialect);
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
} else {
return sql.replace(/\$(\$|(?!\d)(?:\w+))/g, function(value, key) {
if ('$' === key) {
return options.noUnescape ? value : key;
}
if (values[key] !== undefined) {
return SqlString.escape(values[key], false, timeZone, dialect);
} else {
throw new Error('Named bind parameter "' + value + '" has no value in the given object.');
}
});
}
};
SqlString.dateToString = function(date, timeZone, dialect) {
if (moment.tz.zone(timeZone)) {
date = moment(date).tz(timeZone);
......
......@@ -63,6 +63,13 @@ var Utils = module.exports = {
var timeZone = null;
return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect);
},
formatBindParameters: function(sql, parameters, dialect, options) {
if (parameters) {
var timeZone = null;
return SqlString.formatBindParameters(sql, parameters, timeZone, dialect, options);
}
return sql;
},
cloneDeep: function(obj, fn) {
return _.cloneDeep(obj, function (elem) {
// Do not try to customize cloning of plain objects and strings
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!