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

Commit 2294966a by overlookmotel Committed by Jan Aagaard Meier

Clone options object within all Sequelize methods (#5874)

* Clone options

* Remove whitespace
1 parent 81e41b2e
# Future # Future
- [FIXED] Type validation now works with non-strings due to updated validator@5.0.0 [#5861](https://github.com/sequelize/sequelize/pull/5861) - [FIXED] Type validation now works with non-strings due to updated validator@5.0.0 [#5861](https://github.com/sequelize/sequelize/pull/5861)
- [FIXED] Improved offset and limit support for SQL server 2008 [#5616](https://github.com/sequelize/sequelize/pull/5616) - [FIXED] Improved offset and limit support for SQL server 2008 [#5616](https://github.com/sequelize/sequelize/pull/5616)
- [FIXED] options object cloned in all Sequelize methods (so not modified within Sequelize)
# 3.23.1 # 3.23.1
- [FIXED] Postgres DECIMAL precision. (PostgreSQL) [#4893](https://github.com/sequelize/sequelize/issues/4893) - [FIXED] Postgres DECIMAL precision. (PostgreSQL) [#4893](https://github.com/sequelize/sequelize/issues/4893)
......
...@@ -432,7 +432,7 @@ BelongsToMany.prototype.injectGetter = function(obj) { ...@@ -432,7 +432,7 @@ BelongsToMany.prototype.injectGetter = function(obj) {
var association = this; var association = this;
obj[this.accessors.get] = function(options) { obj[this.accessors.get] = function(options) {
options = association.target.__optClone(options) || {}; options = Utils.cloneDeep(options) || {};
var instance = this var instance = this
, through = association.through , through = association.through
...@@ -494,7 +494,7 @@ BelongsToMany.prototype.injectGetter = function(obj) { ...@@ -494,7 +494,7 @@ BelongsToMany.prototype.injectGetter = function(obj) {
var model = association.target var model = association.target
, sequelize = model.sequelize; , sequelize = model.sequelize;
options = association.target.__optClone(options) || {}; options = Utils.cloneDeep(options);
options.attributes = [ options.attributes = [
[sequelize.fn('COUNT', sequelize.col([association.target.name, model.primaryKeyAttribute].join('.'))), 'count'] [sequelize.fn('COUNT', sequelize.col([association.target.name, model.primaryKeyAttribute].join('.'))), 'count']
]; ];
...@@ -514,11 +514,10 @@ BelongsToMany.prototype.injectGetter = function(obj) { ...@@ -514,11 +514,10 @@ BelongsToMany.prototype.injectGetter = function(obj) {
instances = [instances]; instances = [instances];
} }
options = options || {}; options = _.assign({
options.scope = false;
_.defaults(options, {
raw: true raw: true
}, options, {
scope: false
}); });
where.$or = instances.map(function (instance) { where.$or = instances.map(function (instance) {
...@@ -648,7 +647,7 @@ BelongsToMany.prototype.injectSetter = function(obj) { ...@@ -648,7 +647,7 @@ BelongsToMany.prototype.injectSetter = function(obj) {
// If newInstances is null or undefined, no-op // If newInstances is null or undefined, no-op
if (!newInstances) return Utils.Promise.resolve(); if (!newInstances) return Utils.Promise.resolve();
additionalAttributes = additionalAttributes || {}; additionalAttributes = _.clone(additionalAttributes) || {};
var instance = this var instance = this
, defaultAttributes = _.omit(additionalAttributes, ['transaction', 'hooks', 'individualHooks', 'ignoreDuplicates', 'validate', 'fields', 'logging']) , defaultAttributes = _.omit(additionalAttributes, ['transaction', 'hooks', 'individualHooks', 'ignoreDuplicates', 'validate', 'fields', 'logging'])
......
...@@ -150,7 +150,7 @@ BelongsTo.prototype.get = function(instances, options) { ...@@ -150,7 +150,7 @@ BelongsTo.prototype.get = function(instances, options) {
, instance , instance
, where = {}; , where = {};
options = association.target.$optClone(options) || {}; options = Utils.cloneDeep(options);
if (options.hasOwnProperty('scope')) { if (options.hasOwnProperty('scope')) {
if (!options.scope) { if (!options.scope) {
......
...@@ -272,7 +272,7 @@ HasMany.prototype.get = function(instances, options) { ...@@ -272,7 +272,7 @@ HasMany.prototype.get = function(instances, options) {
instances = undefined; instances = undefined;
} }
options = association.target.$optClone(options) || {}; options = Utils.cloneDeep(options) || {};
if (association.scope) { if (association.scope) {
_.assign(where, association.scope); _.assign(where, association.scope);
...@@ -340,7 +340,7 @@ HasMany.prototype.count = function(instance, options) { ...@@ -340,7 +340,7 @@ HasMany.prototype.count = function(instance, options) {
, model = association.target , model = association.target
, sequelize = model.sequelize; , sequelize = model.sequelize;
options = association.target.__optClone(options) || {}; options = Utils.cloneDeep(options);
options.attributes = [ options.attributes = [
[sequelize.fn('COUNT', sequelize.col(model.primaryKeyField)), 'count'] [sequelize.fn('COUNT', sequelize.col(model.primaryKeyField)), 'count']
]; ];
...@@ -360,9 +360,10 @@ HasMany.prototype.has = function(sourceInstance, targetInstances, options) { ...@@ -360,9 +360,10 @@ HasMany.prototype.has = function(sourceInstance, targetInstances, options) {
targetInstances = [targetInstances]; targetInstances = [targetInstances];
} }
options = options || {}; options = _.assign({}, options, {
options.scope = false; scope: false,
options.raw = true; raw: true
});
where.$or = targetInstances.map(function (instance) { where.$or = targetInstances.map(function (instance) {
if (instance instanceof association.target.Instance) { if (instance instanceof association.target.Instance) {
......
...@@ -148,7 +148,7 @@ HasOne.prototype.get = function(instances, options) { ...@@ -148,7 +148,7 @@ HasOne.prototype.get = function(instances, options) {
, instance , instance
, where = {}; , where = {};
options = association.target.$optClone(options) || {}; options = Utils.cloneDeep(options);
if (options.hasOwnProperty('scope')) { if (options.hasOwnProperty('scope')) {
if (!options.scope) { if (!options.scope) {
...@@ -209,8 +209,9 @@ HasOne.prototype.injectSetter = function(instancePrototype) { ...@@ -209,8 +209,9 @@ HasOne.prototype.injectSetter = function(instancePrototype) {
var instance = this, var instance = this,
alreadyAssociated; alreadyAssociated;
options = options || {}; options = _.assign({}, options, {
options.scope = false; scope: false
});
return instance[association.accessors.get](options).then(function(oldInstance) { return instance[association.accessors.get](options).then(function(oldInstance) {
// TODO Use equals method once #5605 is resolved // TODO Use equals method once #5605 is resolved
alreadyAssociated = oldInstance && associatedInstance && _.every(association.target.primaryKeyAttributes, function(attribute) { alreadyAssociated = oldInstance && associatedInstance && _.every(association.target.primaryKeyAttributes, function(attribute) {
......
...@@ -44,8 +44,8 @@ var QueryGenerator = { ...@@ -44,8 +44,8 @@ var QueryGenerator = {
var databaseVersion = Utils._.get(self, 'sequelize.options.databaseVersion', 0); var databaseVersion = Utils._.get(self, 'sequelize.options.databaseVersion', 0);
//Postgres 9.0 does not support CREATE TABLE IF NOT EXISTS, 9.1 and above do //Postgres 9.0 does not support CREATE TABLE IF NOT EXISTS, 9.1 and above do
var query = 'CREATE TABLE ' + var query = 'CREATE TABLE ' +
( (databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0')) ? 'IF NOT EXISTS ' : '') + ( (databaseVersion === 0 || semver.gte(databaseVersion, '9.1.0')) ? 'IF NOT EXISTS ' : '') +
'<%= table %> (<%= attributes%>)<%= comments %>' '<%= table %> (<%= attributes%>)<%= comments %>'
, comments = '' , comments = ''
, attrStr = [] , attrStr = []
, i; , i;
...@@ -163,7 +163,7 @@ var QueryGenerator = { ...@@ -163,7 +163,7 @@ var QueryGenerator = {
// Also support json dot notation // Also support json dot notation
var path = smth.path.split('.'); var path = smth.path.split('.');
str = util.format("%s#>>'{%s}'", str = util.format("%s#>>'{%s}'",
_.first(path), _.first(path),
_.tail(path).join(',')); _.tail(path).join(','));
} }
...@@ -427,7 +427,7 @@ var QueryGenerator = { ...@@ -427,7 +427,7 @@ var QueryGenerator = {
fragment += ' LIMIT ' + this.escape(options.limit); fragment += ' LIMIT ' + this.escape(options.limit);
} }
if (options.offset != null) { if (options.offset != null) {
fragment += ' OFFSET ' + this.escape(options.offset); fragment += ' OFFSET ' + this.escape(options.offset);
} }
return fragment; return fragment;
...@@ -448,8 +448,8 @@ var QueryGenerator = { ...@@ -448,8 +448,8 @@ var QueryGenerator = {
if (Array.isArray(attribute.values) && (attribute.values.length > 0)) { if (Array.isArray(attribute.values) && (attribute.values.length > 0)) {
replacements.type = 'ENUM(' + Utils._.map(attribute.values, function(value) { replacements.type = 'ENUM(' + Utils._.map(attribute.values, function(value) {
return this.escape(value); return this.escape(value);
}.bind(this)).join(', ') + ')'; }.bind(this)).join(', ') + ')';
} else { } else {
throw new Error("Values for ENUM haven't been defined."); throw new Error("Values for ENUM haven't been defined.");
} }
...@@ -563,12 +563,12 @@ var QueryGenerator = { ...@@ -563,12 +563,12 @@ var QueryGenerator = {
createTrigger: function(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) { createTrigger: function(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) {
var sql = [ var sql = [
'CREATE <%= constraintVal %>TRIGGER <%= triggerName %>' 'CREATE <%= constraintVal %>TRIGGER <%= triggerName %>'
, '<%= eventType %> <%= eventSpec %>' , '<%= eventType %> <%= eventSpec %>'
, 'ON <%= tableName %>' , 'ON <%= tableName %>'
, '<%= optionsSpec %>' , '<%= optionsSpec %>'
, 'EXECUTE PROCEDURE <%= functionName %>(<%= paramList %>);' , 'EXECUTE PROCEDURE <%= functionName %>(<%= paramList %>);'
].join('\n\t'); ].join('\n\t');
return Utils._.template(sql)({ return Utils._.template(sql)({
constraintVal: this.triggerEventTypeIsConstraint(eventType), constraintVal: this.triggerEventTypeIsConstraint(eventType),
...@@ -601,11 +601,11 @@ var QueryGenerator = { ...@@ -601,11 +601,11 @@ var QueryGenerator = {
createFunction: function(functionName, params, returnType, language, body, options) { createFunction: function(functionName, params, returnType, language, body, options) {
var sql = ['CREATE FUNCTION <%= functionName %>(<%= paramList %>)' var sql = ['CREATE FUNCTION <%= functionName %>(<%= paramList %>)'
, 'RETURNS <%= returnType %> AS $func$' , 'RETURNS <%= returnType %> AS $func$'
, 'BEGIN' , 'BEGIN'
, '\t<%= body %>' , '\t<%= body %>'
, 'END;' , 'END;'
, "$func$ language '<%= language %>'<%= options %>;" , "$func$ language '<%= language %>'<%= options %>;"
].join('\n'); ].join('\n');
return Utils._.template(sql)({ return Utils._.template(sql)({
...@@ -675,7 +675,7 @@ var QueryGenerator = { ...@@ -675,7 +675,7 @@ var QueryGenerator = {
expandOptions: function expandOptions(options) { expandOptions: function expandOptions(options) {
return Utils._.isUndefined(options) || Utils._.isEmpty(options) ? return Utils._.isUndefined(options) || Utils._.isEmpty(options) ?
'' : '\n\t' + options.join('\n\t'); '' : '\n\t' + options.join('\n\t');
}, },
decodeTriggerEventType: function decodeTriggerEventType(eventSpecifier) { decodeTriggerEventType: function decodeTriggerEventType(eventSpecifier) {
...@@ -851,7 +851,7 @@ var QueryGenerator = { ...@@ -851,7 +851,7 @@ var QueryGenerator = {
}, },
/* /*
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
* @param {String} tableName The name of the table. * @param {String} tableName The name of the table.
......
...@@ -16,7 +16,7 @@ var validator = require('./utils/validator-extras').validator ...@@ -16,7 +16,7 @@ var validator = require('./utils/validator-extras').validator
* @constructor * @constructor
*/ */
var InstanceValidator = module.exports = function(modelInstance, options) { var InstanceValidator = module.exports = function(modelInstance, options) {
options = options || {}; options = _.clone(options) || {};
if (options.fields && !options.skip) { if (options.fields && !options.skip) {
options.skip = Utils._.difference(Object.keys(modelInstance.Model.attributes), options.fields); options.skip = Utils._.difference(Object.keys(modelInstance.Model.attributes), options.fields);
......
...@@ -495,7 +495,7 @@ Instance.prototype.save = function(options) { ...@@ -495,7 +495,7 @@ Instance.prototype.save = function(options) {
throw new Error('The second argument was removed in favor of the options object.'); throw new Error('The second argument was removed in favor of the options object.');
} }
options = this.$Model.$optClone(options || {}); options = Utils.cloneDeep(options);
options = _.defaults(options, { options = _.defaults(options, {
hooks: true, hooks: true,
validate: true validate: true
...@@ -634,7 +634,7 @@ Instance.prototype.save = function(options) { ...@@ -634,7 +634,7 @@ Instance.prototype.save = function(options) {
var instance = self.get(include.as); var instance = self.get(include.as);
if (!instance) return Promise.resolve(); if (!instance) return Promise.resolve();
var includeOptions = _(self.$Model.$optClone(include)) var includeOptions = _(Utils.cloneDeep(include))
.omit(['association']) .omit(['association'])
.defaults({ .defaults({
transaction: options.transaction, transaction: options.transaction,
...@@ -712,7 +712,7 @@ Instance.prototype.save = function(options) { ...@@ -712,7 +712,7 @@ Instance.prototype.save = function(options) {
if (!Array.isArray(instances)) instances = [instances]; if (!Array.isArray(instances)) instances = [instances];
if (!instances.length) return Promise.resolve(); if (!instances.length) return Promise.resolve();
var includeOptions = _(self.$Model.$optClone(include)) var includeOptions = _(Utils.cloneDeep(include))
.omit(['association']) .omit(['association'])
.defaults({ .defaults({
transaction: options.transaction, transaction: options.transaction,
...@@ -815,8 +815,8 @@ Instance.prototype.update = function(values, options) { ...@@ -815,8 +815,8 @@ Instance.prototype.update = function(values, options) {
options = options || {}; options = options || {};
if (Array.isArray(options)) options = {fields: options}; if (Array.isArray(options)) options = {fields: options};
options = this.$Model.$optClone(options); options = Utils.cloneDeep(options);
setOptions = this.$Model.$optClone(options); setOptions = Utils.cloneDeep(options);
setOptions.attributes = options.fields; setOptions.attributes = options.fields;
this.set(values, setOptions); this.set(values, setOptions);
......
...@@ -8,7 +8,6 @@ var Utils = require('./utils') ...@@ -8,7 +8,6 @@ var Utils = require('./utils')
, HasMany = require('./associations/has-many') , HasMany = require('./associations/has-many')
, DataTypes = require('./data-types') , DataTypes = require('./data-types')
, Util = require('util') , Util = require('util')
, Transaction = require('./transaction')
, Promise = require('./promise') , Promise = require('./promise')
, QueryTypes = require('./query-types') , QueryTypes = require('./query-types')
, Hooks = require('./hooks') , Hooks = require('./hooks')
...@@ -307,27 +306,6 @@ function conformInclude(include, self) { ...@@ -307,27 +306,6 @@ function conformInclude(include, self) {
Model.$conformInclude = conformInclude; Model.$conformInclude = conformInclude;
var optClone = Model.prototype.__optClone = Model.prototype.$optClone = function(options) {
options = options || {};
return Utils.cloneDeep(options, function(elem) {
// The InstanceFactories used for include are pass by ref, so don't clone them.
if (elem && typeof elem === 'object' &&
(
elem._isSequelizeMethod ||
elem instanceof Model ||
elem instanceof Instance ||
elem instanceof Transaction ||
elem instanceof Association
)
) {
return elem;
}
// Otherwise return undefined, meaning, 'handle this lodash'
return undefined;
});
};
var expandIncludeAllElement = function(includes, include) { var expandIncludeAllElement = function(includes, include) {
// check 'all' attribute provided is valid // check 'all' attribute provided is valid
var all = include.all; var all = include.all;
...@@ -413,7 +391,7 @@ var expandIncludeAllElement = function(includes, include) { ...@@ -413,7 +391,7 @@ var expandIncludeAllElement = function(includes, include) {
used.push(parent); used.push(parent);
// include this model // include this model
var thisInclude = optClone(include); var thisInclude = Utils.cloneDeep(include);
thisInclude.model = model; thisInclude.model = model;
if (as) { if (as) {
thisInclude.as = as; thisInclude.as = as;
...@@ -990,9 +968,8 @@ Model.prototype.removeAttribute = function(attribute) { ...@@ -990,9 +968,8 @@ Model.prototype.removeAttribute = function(attribute) {
* @return {Promise<this>} * @return {Promise<this>}
*/ */
Model.prototype.sync = function(options) { Model.prototype.sync = function(options) {
options = options || {}; options = _.extend({}, this.options, options);
options.hooks = options.hooks === undefined ? true : !!options.hooks; options.hooks = options.hooks === undefined ? true : !!options.hooks;
options = Utils._.extend({}, this.options, options);
var self = this var self = this
, attributes = this.tableAttributes; , attributes = this.tableAttributes;
...@@ -1360,7 +1337,7 @@ Model.prototype.findAll = function(options) { ...@@ -1360,7 +1337,7 @@ Model.prototype.findAll = function(options) {
, originalOptions; , originalOptions;
tableNames[this.getTableName(options)] = true; tableNames[this.getTableName(options)] = true;
options = optClone(options); options = Utils.cloneDeep(options);
_.defaults(options, { hooks: true, rejectOnEmpty: this.options.rejectOnEmpty }); _.defaults(options, { hooks: true, rejectOnEmpty: this.options.rejectOnEmpty });
...@@ -1408,7 +1385,7 @@ Model.prototype.findAll = function(options) { ...@@ -1408,7 +1385,7 @@ Model.prototype.findAll = function(options) {
return this.runHooks('beforeFindAfterOptions', options); return this.runHooks('beforeFindAfterOptions', options);
} }
}).then(function() { }).then(function() {
originalOptions = optClone(options); originalOptions = Utils.cloneDeep(options);
options.tableNames = Object.keys(tableNames); options.tableNames = Object.keys(tableNames);
return this.QueryInterface.select(this, this.getTableName(options), options); return this.QueryInterface.select(this, this.getTableName(options), options);
}).tap(function(results) { }).tap(function(results) {
...@@ -1498,7 +1475,7 @@ Model.prototype.findById = function(param, options) { ...@@ -1498,7 +1475,7 @@ Model.prototype.findById = function(param, options) {
return Promise.resolve(null); return Promise.resolve(null);
} }
options = optClone(options) || {}; options = Utils.cloneDeep(options) || {};
if (typeof param === 'number' || typeof param === 'string' || Buffer.isBuffer(param)) { if (typeof param === 'number' || typeof param === 'string' || Buffer.isBuffer(param)) {
options.where = {}; options.where = {};
...@@ -1527,7 +1504,7 @@ Model.prototype.findOne = function(options) { ...@@ -1527,7 +1504,7 @@ Model.prototype.findOne = function(options) {
if (options !== undefined && !_.isPlainObject(options)) { if (options !== undefined && !_.isPlainObject(options)) {
throw new Error('The argument passed to findOne must be an options object, use findById if you wish to pass a single primary key value'); throw new Error('The argument passed to findOne must be an options object, use findById if you wish to pass a single primary key value');
} }
options = optClone(options); options = Utils.cloneDeep(options);
if (options.limit === undefined) { if (options.limit === undefined) {
var pkVal = options.where && options.where[this.primaryKeyAttribute]; var pkVal = options.where && options.where[this.primaryKeyAttribute];
...@@ -1563,7 +1540,8 @@ Model.prototype.find = Model.prototype.findOne; ...@@ -1563,7 +1540,8 @@ Model.prototype.find = Model.prototype.findOne;
* @return {Promise<options.dataType|object>} Returns the aggregate result cast to `options.dataType`, unless `options.plain` is false, in which case the complete data result is returned. * @return {Promise<options.dataType|object>} Returns the aggregate result cast to `options.dataType`, unless `options.plain` is false, in which case the complete data result is returned.
*/ */
Model.prototype.aggregate = function(attribute, aggregateFunction, options) { Model.prototype.aggregate = function(attribute, aggregateFunction, options) {
options = Utils._.extend({ attributes: [] }, options || {}); options = Utils.cloneDeep(options);
options = _.defaults(options, { attributes: [] });
conformOptions(options, this); conformOptions(options, this);
this.$injectScope(options); this.$injectScope(options);
...@@ -1617,8 +1595,7 @@ Model.prototype.aggregate = function(attribute, aggregateFunction, options) { ...@@ -1617,8 +1595,7 @@ Model.prototype.aggregate = function(attribute, aggregateFunction, options) {
* @return {Promise<Integer>} * @return {Promise<Integer>}
*/ */
Model.prototype.count = function(options) { Model.prototype.count = function(options) {
options = Utils._.clone(options || {}); options = Utils.cloneDeep(options);
_.defaults(options, { hooks: true }); _.defaults(options, { hooks: true });
var col = '*'; var col = '*';
...@@ -1862,7 +1839,7 @@ Model.prototype.bulkBuild = function(valueSets, options) { // testhint options:n ...@@ -1862,7 +1839,7 @@ Model.prototype.bulkBuild = function(valueSets, options) { // testhint options:n
* @return {Promise<Instance>} * @return {Promise<Instance>}
*/ */
Model.prototype.create = function(values, options) { Model.prototype.create = function(values, options) {
options = optClone(options || {}); options = Utils.cloneDeep(options || {});
return this.build(values, { return this.build(values, {
isNewRecord: true, isNewRecord: true,
...@@ -1937,6 +1914,8 @@ Model.prototype.findOrCreate = function(options) { ...@@ -1937,6 +1914,8 @@ Model.prototype.findOrCreate = function(options) {
); );
} }
options = _.assign({}, options);
if (options.transaction === undefined && this.sequelize.constructor.cls) { if (options.transaction === undefined && this.sequelize.constructor.cls) {
var t = this.sequelize.constructor.cls.get('transaction'); var t = this.sequelize.constructor.cls.get('transaction');
if (t) { if (t) {
...@@ -2065,7 +2044,7 @@ Model.prototype.findCreateFind = function(options) { ...@@ -2065,7 +2044,7 @@ Model.prototype.findCreateFind = function(options) {
* @return {Promise<created>} Returns a boolean indicating whether the row was created or updated. * @return {Promise<created>} Returns a boolean indicating whether the row was created or updated.
*/ */
Model.prototype.upsert = function (values, options) { Model.prototype.upsert = function (values, options) {
options = optClone(options) || {}; options = Utils.cloneDeep(options) || {};
if (!options.fields) { if (!options.fields) {
options.fields = Object.keys(this.attributes); options.fields = Object.keys(this.attributes);
...@@ -2281,7 +2260,7 @@ Model.prototype.bulkCreate = function(records, options) { ...@@ -2281,7 +2260,7 @@ Model.prototype.bulkCreate = function(records, options) {
* @see {Model#destroy} for more information * @see {Model#destroy} for more information
*/ */
Model.prototype.truncate = function(options) { Model.prototype.truncate = function(options) {
options = optClone(options) || {}; options = Utils.cloneDeep(options) || {};
options.truncate = true; options.truncate = true;
return this.destroy(options); return this.destroy(options);
}; };
...@@ -2314,12 +2293,13 @@ Model.prototype.destroy = function(options) { ...@@ -2314,12 +2293,13 @@ Model.prototype.destroy = function(options) {
throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.'); throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.destroy.');
} }
options = Utils._.extend({ options = Utils.cloneDeep(options);
options = _.defaults(options, {
hooks: true, hooks: true,
individualHooks: false, individualHooks: false,
force: false, force: false,
cascade: false cascade: false
}, options || {}); });
options.type = QueryTypes.BULKDELETE; options.type = QueryTypes.BULKDELETE;
this.$injectScope(options); this.$injectScope(options);
...@@ -2479,14 +2459,15 @@ Model.prototype.update = function(values, options) { ...@@ -2479,14 +2459,15 @@ Model.prototype.update = function(values, options) {
throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.update.'); throw new Error('Expected plain object, array or sequelize method in the options.where parameter of model.update.');
} }
options = Utils._.extend({ options = Utils.cloneDeep(options);
options = _.defaults(options, {
validate: true, validate: true,
hooks: true, hooks: true,
individualHooks: false, individualHooks: false,
returning: false, returning: false,
force: false, force: false,
sideEffects: true sideEffects: true
}, options || {}); });
options.type = QueryTypes.BULKUPDATE; options.type = QueryTypes.BULKUPDATE;
...@@ -2699,7 +2680,7 @@ Model.prototype.$expandAttributes = function (options) { ...@@ -2699,7 +2680,7 @@ Model.prototype.$expandAttributes = function (options) {
// Inject $scope into options. Includes should have been conformed (conformOptions) before calling this // Inject $scope into options. Includes should have been conformed (conformOptions) before calling this
Model.prototype.$injectScope = function (options) { Model.prototype.$injectScope = function (options) {
var self = this; var self = this;
var scope = optClone(this.$scope); var scope = Utils.cloneDeep(this.$scope);
var filteredScope = _.omit(scope, 'include'); // Includes need special treatment var filteredScope = _.omit(scope, 'include'); // Includes need special treatment
......
...@@ -49,8 +49,7 @@ QueryInterface.prototype.showAllSchemas = function(options) { ...@@ -49,8 +49,7 @@ QueryInterface.prototype.showAllSchemas = function(options) {
options = _.assign({}, options, { options = _.assign({}, options, {
raw: true, raw: true,
type: this.sequelize.QueryTypes.SELECT, type: this.sequelize.QueryTypes.SELECT
logging: false
}); });
var showSchemasSql = self.QueryGenerator.showSchemasQuery(); var showSchemasSql = self.QueryGenerator.showSchemasQuery();
...@@ -78,7 +77,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options, ...@@ -78,7 +77,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options,
, sql = '' , sql = ''
, i = 0; , i = 0;
options = options || {}; options = _.clone(options) || {};
attributes = Utils._.mapValues(attributes, function(attribute) { attributes = Utils._.mapValues(attributes, function(attribute) {
if (!Utils._.isPlainObject(attribute)) { if (!Utils._.isPlainObject(attribute)) {
...@@ -123,18 +122,19 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options, ...@@ -123,18 +122,19 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options,
vals.forEach(function(value, idx) { vals.forEach(function(value, idx) {
// reset out after/before options since it's for every enum value // reset out after/before options since it's for every enum value
options.before = null; var valueOptions = _.clone(options);
options.after = null; valueOptions.before = null;
valueOptions.after = null;
if (enumVals.indexOf(value) === -1) { if (enumVals.indexOf(value) === -1) {
if (!!vals[idx + 1]) { if (!!vals[idx + 1]) {
options.before = vals[idx + 1]; valueOptions.before = vals[idx + 1];
} }
else if (!!vals[idx - 1]) { else if (!!vals[idx - 1]) {
options.after = vals[idx - 1]; valueOptions.after = vals[idx - 1];
} }
options.supportsSearchPath = false; valueOptions.supportsSearchPath = false;
promises.push(self.sequelize.query(self.QueryGenerator.pgEnumAdd(tableName, keys[i], value, options), options)); promises.push(self.sequelize.query(self.QueryGenerator.pgEnumAdd(tableName, keys[i], value, valueOptions), valueOptions));
} }
}); });
enumIdx++; enumIdx++;
...@@ -179,7 +179,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options, ...@@ -179,7 +179,7 @@ QueryInterface.prototype.createTable = function(tableName, attributes, options,
QueryInterface.prototype.dropTable = function(tableName, options) { QueryInterface.prototype.dropTable = function(tableName, options) {
// if we're forcing we should be cascading unless explicitly stated otherwise // if we're forcing we should be cascading unless explicitly stated otherwise
options = options || {}; options = _.clone(options) || {};
options.cascade = options.cascade || options.force || false; options.cascade = options.cascade || options.force || false;
var sql = this.QueryGenerator.dropTableQuery(tableName, options) var sql = this.QueryGenerator.dropTableQuery(tableName, options)
...@@ -278,7 +278,7 @@ QueryInterface.prototype.dropAllEnums = function(options) { ...@@ -278,7 +278,7 @@ QueryInterface.prototype.dropAllEnums = function(options) {
var self = this; var self = this;
return this.pgListEnums(options).map(function(result) { return this.pgListEnums(null, options).map(function(result) {
return self.sequelize.query( return self.sequelize.query(
self.QueryGenerator.pgEnumDrop(null, null, self.QueryGenerator.pgEscapeAndQuote(result.enum_name)), self.QueryGenerator.pgEnumDrop(null, null, self.QueryGenerator.pgEscapeAndQuote(result.enum_name)),
_.assign({}, options, { raw: true }) _.assign({}, options, { raw: true })
...@@ -443,7 +443,7 @@ QueryInterface.prototype.addIndex = function(tableName, attributes, options, raw ...@@ -443,7 +443,7 @@ QueryInterface.prototype.addIndex = function(tableName, attributes, options, raw
rawTablename = tableName; rawTablename = tableName;
} }
options = options || {}; options = Utils.cloneDeep(options);
options.fields = attributes; options.fields = attributes;
var sql = this.QueryGenerator.addIndexQuery(tableName, options, rawTablename); var sql = this.QueryGenerator.addIndexQuery(tableName, options, rawTablename);
return this.sequelize.query(sql, _.assign({}, options, { supportsSearchPath: false })); return this.sequelize.query(sql, _.assign({}, options, { supportsSearchPath: false }));
...@@ -492,7 +492,7 @@ QueryInterface.prototype.removeIndex = function(tableName, indexNameOrAttributes ...@@ -492,7 +492,7 @@ QueryInterface.prototype.removeIndex = function(tableName, indexNameOrAttributes
}; };
QueryInterface.prototype.insert = function(instance, tableName, values, options) { QueryInterface.prototype.insert = function(instance, tableName, values, options) {
options = options || {}; options = Utils.cloneDeep(options);
options.hasTrigger = instance && instance.Model.options.hasTrigger; options.hasTrigger = instance && instance.Model.options.hasTrigger;
var sql = this.QueryGenerator.insertQuery(tableName, values, instance && instance.Model.rawAttributes, options); var sql = this.QueryGenerator.insertQuery(tableName, values, instance && instance.Model.rawAttributes, options);
...@@ -511,6 +511,8 @@ QueryInterface.prototype.upsert = function(tableName, valuesByField, updateValue ...@@ -511,6 +511,8 @@ QueryInterface.prototype.upsert = function(tableName, valuesByField, updateValue
, indexes = [] , indexes = []
, attributes = Object.keys(valuesByField); , attributes = Object.keys(valuesByField);
options = _.clone(options);
if (!Utils._.isEmpty(where)) { if (!Utils._.isEmpty(where)) {
wheres.push(where); wheres.push(where);
} }
...@@ -561,14 +563,14 @@ QueryInterface.prototype.upsert = function(tableName, valuesByField, updateValue ...@@ -561,14 +563,14 @@ QueryInterface.prototype.upsert = function(tableName, valuesByField, updateValue
}; };
QueryInterface.prototype.bulkInsert = function(tableName, records, options, attributes) { QueryInterface.prototype.bulkInsert = function(tableName, records, options, attributes) {
options = options || {}; options = _.clone(options) || {};
options.type = QueryTypes.INSERT; options.type = QueryTypes.INSERT;
var sql = this.QueryGenerator.bulkInsertQuery(tableName, records, options, attributes); var sql = this.QueryGenerator.bulkInsertQuery(tableName, records, options, attributes);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
}; };
QueryInterface.prototype.update = function(instance, tableName, values, identifier, options) { QueryInterface.prototype.update = function(instance, tableName, values, identifier, options) {
options = options || {}; options = _.clone(options || {});
options.hasTrigger = !!(instance && instance.$modelOptions && instance.$modelOptions.hasTrigger); options.hasTrigger = !!(instance && instance.$modelOptions && instance.$modelOptions.hasTrigger);
...@@ -596,11 +598,13 @@ QueryInterface.prototype.update = function(instance, tableName, values, identifi ...@@ -596,11 +598,13 @@ QueryInterface.prototype.update = function(instance, tableName, values, identifi
}; };
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options, attributes) { QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options, attributes) {
options = Utils.cloneDeep(options);
if (typeof identifier === 'object') identifier = Utils.cloneDeep(identifier);
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options, attributes) var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options, attributes)
, table = Utils._.isObject(tableName) ? tableName : { tableName: tableName } , table = Utils._.isObject(tableName) ? tableName : { tableName: tableName }
, model = Utils._.find(this.sequelize.modelManager.models, { tableName: table.tableName }); , model = Utils._.find(this.sequelize.modelManager.models, { tableName: table.tableName });
options = options || {};
options.model = model; options.model = model;
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
}; };
...@@ -610,7 +614,7 @@ QueryInterface.prototype.delete = function(instance, tableName, identifier, opti ...@@ -610,7 +614,7 @@ QueryInterface.prototype.delete = function(instance, tableName, identifier, opti
, cascades = [] , cascades = []
, sql = self.QueryGenerator.deleteQuery(tableName, identifier, null, instance.Model); , sql = self.QueryGenerator.deleteQuery(tableName, identifier, null, instance.Model);
options = options || {}; options = _.clone(options) || {};
// Check for a restrict field // Check for a restrict field
if (!!instance.Model && !!instance.Model.associations) { if (!!instance.Model && !!instance.Model.associations) {
...@@ -648,12 +652,16 @@ QueryInterface.prototype.delete = function(instance, tableName, identifier, opti ...@@ -648,12 +652,16 @@ QueryInterface.prototype.delete = function(instance, tableName, identifier, opti
}; };
QueryInterface.prototype.bulkDelete = function(tableName, identifier, options, model) { QueryInterface.prototype.bulkDelete = function(tableName, identifier, options, model) {
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, Utils._.defaults(options || {}, {limit: null}), model); options = Utils.cloneDeep(options);
options = _.defaults(options, {limit: null});
if (typeof identifier === 'object') identifier = Utils.cloneDeep(identifier);
var sql = this.QueryGenerator.deleteQuery(tableName, identifier, options, model);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
}; };
QueryInterface.prototype.select = function(model, tableName, options) { QueryInterface.prototype.select = function(model, tableName, options) {
options = options || {}; options = Utils.cloneDeep(options);
options.type = QueryTypes.SELECT; options.type = QueryTypes.SELECT;
options.model = model; options.model = model;
...@@ -666,7 +674,7 @@ QueryInterface.prototype.select = function(model, tableName, options) { ...@@ -666,7 +674,7 @@ QueryInterface.prototype.select = function(model, tableName, options) {
QueryInterface.prototype.increment = function(instance, tableName, values, identifier, options) { QueryInterface.prototype.increment = function(instance, tableName, values, identifier, options) {
var sql = this.QueryGenerator.incrementQuery(tableName, values, identifier, options.attributes); var sql = this.QueryGenerator.incrementQuery(tableName, values, identifier, options.attributes);
options = options || {}; options = _.clone(options) || {};
options.type = QueryTypes.UPDATE; options.type = QueryTypes.UPDATE;
options.instance = instance; options.instance = instance;
...@@ -681,7 +689,8 @@ QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelec ...@@ -681,7 +689,8 @@ QueryInterface.prototype.rawSelect = function(tableName, options, attributeSelec
}); });
} }
options = _.defaults(options || {}, { options = Utils.cloneDeep(options);
options = _.defaults(options, {
raw: true, raw: true,
plain: true, plain: true,
type: QueryTypes.SELECT type: QueryTypes.SELECT
......
...@@ -739,7 +739,7 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -739,7 +739,7 @@ Sequelize.prototype.query = function(sql, options) {
var self = this; var self = this;
options = options || {}; options = _.assign({}, this.options.query, options);
if (options.instance && !options.model) { if (options.instance && !options.model) {
options.model = options.instance.Model; options.model = options.instance.Model;
...@@ -750,7 +750,7 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -750,7 +750,7 @@ Sequelize.prototype.query = function(sql, options) {
options.fieldMap = options.model.fieldAttributeMap; options.fieldMap = options.model.fieldAttributeMap;
} }
if (Utils._.isPlainObject(sql)) { if (_.isPlainObject(sql)) {
if (sql.values !== undefined) { if (sql.values !== undefined) {
if (options.replacements !== undefined) { if (options.replacements !== undefined) {
throw new Error('Both `sql.values` and `options.replacements` cannot be set at the same time'); throw new Error('Both `sql.values` and `options.replacements` cannot be set at the same time');
...@@ -796,8 +796,7 @@ Sequelize.prototype.query = function(sql, options) { ...@@ -796,8 +796,7 @@ Sequelize.prototype.query = function(sql, options) {
bindParameters = bindSql[1]; bindParameters = bindSql[1];
} }
options = Utils._.extend(Utils._.clone(this.options.query), options); options = _.defaults(options, {
options = Utils._.defaults(options, {
logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log, logging: this.options.hasOwnProperty('logging') ? this.options.logging : console.log,
searchPath: this.options.hasOwnProperty('searchPath') ? this.options.searchPath : 'DEFAULT', searchPath: this.options.hasOwnProperty('searchPath') ? this.options.searchPath : 'DEFAULT',
}); });
...@@ -973,7 +972,7 @@ Sequelize.prototype.dropAllSchemas = function(options) { ...@@ -973,7 +972,7 @@ Sequelize.prototype.dropAllSchemas = function(options) {
Sequelize.prototype.sync = function(options) { Sequelize.prototype.sync = function(options) {
var self = this; var self = this;
options = options || {}; options = _.clone(options) || {};
options.hooks = options.hooks === undefined ? true : !!options.hooks; options.hooks = options.hooks === undefined ? true : !!options.hooks;
options.logging = options.logging === undefined ? false : options.logging; options.logging = options.logging === undefined ? false : options.logging;
options = Utils._.defaults(options, this.options.sync, this.options); options = Utils._.defaults(options, this.options.sync, this.options);
......
...@@ -87,20 +87,23 @@ var Utils = module.exports = { ...@@ -87,20 +87,23 @@ var Utils = module.exports = {
var timeZone = null; var timeZone = null;
return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect); return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect);
}, },
cloneDeep: function(obj, fn) { cloneDeep: function(obj) {
obj = obj || {};
return _.cloneDeepWith(obj, function (elem) { return _.cloneDeepWith(obj, function (elem) {
// Do not try to customize cloning of plain objects and strings // Do not try to customize cloning of arrays or POJOs
if (Array.isArray(elem) || _.isPlainObject(elem)) { if (Array.isArray(elem) || _.isPlainObject(elem)) {
return undefined; return undefined;
} }
// Don't clone stuff that's an object, but not a plain one - fx example sequelize models and instances
if (typeof elem === 'object') {
return elem;
}
// Preserve special data-types like `fn` across clones. _.get() is used for checking up the prototype chain // Preserve special data-types like `fn` across clones. _.get() is used for checking up the prototype chain
if (elem && typeof elem.clone === 'function') { if (elem && typeof elem.clone === 'function') {
return elem.clone(); return elem.clone();
} }
// Unfortunately, lodash.cloneDeep doesn't preserve Buffer.isBuffer, which we have to rely on for binary data
if (Buffer.isBuffer(elem)) { return elem; }
return fn ? fn(elem) : undefined;
}); });
}, },
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!