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

Commit 2f865e1c by Daniel Durante

Merge pull request #810 from durango/milestones/2.0.0

Milestones/2.0.0
2 parents a5862835 424c0461
......@@ -8,6 +8,8 @@ script:
notifications:
email:
- sascha@depold.com
hipchat:
- 40e8850aaba9854ac4c9963bd33f8b@253477
env:
- DB=mysql DIALECT=mysql
......
......@@ -30,6 +30,8 @@
- [BUG] Fixed BLOB/TEXT columns having a default value declared in MySQL [#793](https://github.com/sequelize/sequelize/pull/793). thanks to durango
- [BUG] You can now use .find() on any single integer primary key when throwing just a number as an argument [#796](https://github.com/sequelize/sequelize/pull/796). thanks to durango
- [BUG] Adding unique to a column for Postgres in the migrator should be fixed [#795](https://github.com/sequelize/sequelize/pull/795). thanks to durango
- [BUG] For MySQL users, if their collation allows case insensitivity then allow enums to be case insensitive as well [#794](https://github.com/sequelize/sequelize/pull/794). thanks to durango
- [BUG] Custom primary key (not keys, just singular) should no longer be a problem for models when using any of the data retrievals with just a number or through associations [#771](https://github.com/sequelize/sequelize/pull/771). thanks to sdephold & durango
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to durango
- [FEATURE] Schematics. [#564](https://github.com/sequelize/sequelize/pull/564). thanks to durango
- [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
......
......@@ -37,15 +37,20 @@ module.exports = (function() {
BelongsTo.prototype.injectGetter = function(obj) {
var self = this
, accessor = Utils._.camelize('get_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))
, primaryKeys = Object.keys(self.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
obj[accessor] = function(params) {
var id = this[self.identifier]
, where = {}
where[primaryKey] = id
if (!Utils._.isUndefined(params)) {
if (!Utils._.isUndefined(params.where)) {
params.where = Utils._.extend({id:id}, params.where)
params.where = Utils._.extend(where, params.where)
} else {
params.where = {id: id}
params.where = where
}
} else {
params = id
......@@ -62,10 +67,13 @@ module.exports = (function() {
, accessor = Utils._.camelize('set_' + (this.options.as || Utils.singularize(this.target.tableName, this.target.options.language)))
obj[accessor] = function(associatedObject) {
this[self.identifier] = associatedObject ? associatedObject.id : null
var primaryKeys = !!associatedObject && !!associatedObject.daoFactory ? Object.keys(associatedObject.daoFactory.primaryKeys) : []
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
this[self.identifier] = associatedObject ? associatedObject[primaryKey] : null
// passes the changed field to save, so only that field get updated.
return this.save([ self.identifier ])
return this.save([ self.identifier ], {allowNull: [self.identifier]})
}
return this
......
......@@ -10,15 +10,21 @@ module.exports = (function() {
var self = this, _options = options
var customEventEmitter = new Utils.CustomEventEmitter(function() {
var where = {}, options = _options || {};
var where = {}, options = _options || {}
//fully qualify
where[self.__factory.connectorDAO.tableName+"."+self.__factory.identifier] = self.instance.id
var instancePrimaryKeys = Object.keys(self.instance.daoFactory.primaryKeys)
, instancePrimaryKey = instancePrimaryKeys.length > 0 ? instancePrimaryKeys[0] : 'id'
where[self.__factory.connectorDAO.tableName+"."+self.__factory.identifier] = self.instance[instancePrimaryKey]
var primaryKeys = Object.keys(self.__factory.connectorDAO.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
, foreignPrimary = Object.keys(self.__factory.target.primaryKeys)
foreignPrimary = foreignPrimary.length === 1 ? foreignPrimary[0] : 'id'
where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+".id"}
where[self.__factory.connectorDAO.tableName+"."+foreignKey] = {join: self.__factory.target.tableName+"."+foreignPrimary}
if (options.where) {
if (Array.isArray(options.where)) {
......@@ -28,18 +34,11 @@ module.exports = (function() {
options.where = smart
}
} else {
Utils._.each(options.where, function(value, index) {
delete options.where[index];
smart = Utils.smartWhere(value, self.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.__factory.target, smart)
if (smart.length > 0) {
value = smart
}
options.where[self.__factory.target.tableName+"."+index] = value;
});
options.where = Utils._.extend(options.where, where)
smart = Utils.smartWhere([where, options.where], self.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.__factory.target, smart, self.__factory.target.daoFactoryManager.sequelize.options.dialect)
if (smart.length > 0) {
options.where = smart
}
}
} else {
options.where = where;
......@@ -58,16 +57,17 @@ module.exports = (function() {
var self = this
, chainer = new Utils.QueryChainer()
, association = self.__factory.target.associations[self.__factory.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, obsoleteAssociations = oldAssociations.filter(function (old) {
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier;
var obsoleteAssociations = oldAssociations.filter(function (old) {
// Return only those old associations that are not found in new
return !Utils._.find(newAssociations, function (obj) {
return obj.id === old.id
return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id)
})
})
, unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return obj.id === old.id
return (!!obj[foreignIdentifier] && !!old[foreignIdentifier] ? obj[foreignIdentifier] === old[foreignIdentifier] : obj.id === old.id)
})
})
......@@ -79,7 +79,7 @@ module.exports = (function() {
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
var where = {}
where[self.__factory.identifier] = self.instance.id
where[self.__factory.identifier] = self.instance[self.__factory.identifier] || self.instance.id
where[foreignKey] = foreignIds
chainer.add(self.__factory.connectorDAO.destroy(where))
......@@ -88,8 +88,8 @@ module.exports = (function() {
if (unassociatedObjects.length > 0) {
var bulk = unassociatedObjects.map(function(unassociatedObject) {
var attributes = {}
attributes[self.__factory.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id
attributes[self.__factory.identifier] = self.instance[self.__factory.identifier] || self.instance.id
attributes[foreignIdentifier] = unassociatedObject[foreignIdentifier] || unassociatedObject.id
return attributes
})
......@@ -109,8 +109,8 @@ module.exports = (function() {
, association = this.__factory.target.associations[this.__factory.associationAccessor]
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier;
attributes[this.__factory.identifier] = this.instance.id
attributes[foreignIdentifier] = newAssociation.id
attributes[this.__factory.identifier] = this.instance[this.__factory.identifier] || this.instance.id
attributes[foreignIdentifier] = newAssociation[foreignIdentifier] || newAssociation.id
this.__factory.connectorDAO.create(attributes)
.success(function() { emitterProxy.emit('success', newAssociation) })
......
......@@ -7,14 +7,17 @@ module.exports = (function() {
}
HasManySingleLinked.prototype.injectGetter = function(options) {
var where = {}
var self = this
, where = {}
options = options || {}
where[this.__factory.identifier] = this.instance.id
var primaryKey = Object.keys(this.instance.rawAttributes).filter(function(k) { return self.instance.rawAttributes[k].primaryKey === true })
primaryKey = primaryKey.length === 1 ? primaryKey[0] : 'id'
where[this.__factory.identifier] = this.instance[primaryKey]
if (options.where) {
smart = Utils.smartWhere([where, options.where], this.__factory.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(this.__factory.target, smart)
smart = Utils.compileSmartWhere.call(this.__factory.target, smart, this.__factory.target.daoFactoryManager.sequelize.options.dialect)
if (smart.length > 0) {
options.where = smart
}
......@@ -27,15 +30,17 @@ module.exports = (function() {
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this
, associationKeys = Object.keys((oldAssociations[0] || newAssociations[0] || {}).daoFactory.primaryKeys || {})
, associationKey = associationKeys.length === 1 ? associationKeys[0] : 'id'
, chainer = new Utils.QueryChainer()
, obsoleteAssociations = oldAssociations.filter(function (old) {
return !Utils._.find(newAssociations, function (obj) {
return obj.id === old.id
return obj[associationKey] === old[associationKey]
})
})
, unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return obj.id === old.id
return obj[associationKey] === old[associationKey]
})
})
, update
......@@ -44,24 +49,38 @@ module.exports = (function() {
// clear the old associations
var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id
return associatedObject[associationKey]
})
update = {}
update[self.__factory.identifier] = null
chainer.add(this.__factory.target.update(update, { id: obsoleteIds }))
var primaryKeys = Object.keys(this.__factory.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, updateWhere = {}
updateWhere[primaryKey] = obsoleteIds
chainer.add(this.__factory.target.update(update, updateWhere, {allowNull: [self.__factory.identifier]}))
}
if (unassociatedObjects.length > 0) {
// For the self.instance
var pkeys = Object.keys(self.instance.daoFactory.primaryKeys)
, pkey = pkeys.length === 1 ? pkeys[0] : 'id'
// For chainer
, primaryKeys = Object.keys(this.__factory.target.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, updateWhere = {}
// set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
return associatedObject.id
associatedObject[self.__factory.identifier] = self.instance[pkey] || self.instance.id
return associatedObject[associationKey]
})
update = {}
update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance.id)
chainer.add(this.__factory.target.update(update, { id: unassociatedIds }))
update[self.__factory.identifier] = (newAssociations.length < 1 ? null : self.instance[pkey] || self.instance.id)
updateWhere[primaryKey] = unassociatedIds
chainer.add(this.__factory.target.update(update, updateWhere, {allowNull: [self.__factory.identifier]}))
}
chainer
......@@ -72,7 +91,10 @@ module.exports = (function() {
}
HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) {
newAssociation[this.__factory.identifier] = this.instance.id
var primaryKeys = Object.keys(this.instance.daoFactory.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
newAssociation[this.__factory.identifier] = this.instance[primaryKey]
newAssociation.save()
.success(function() { emitterProxy.emit('success', newAssociation) })
......
......@@ -88,7 +88,7 @@ module.exports = (function() {
}
obj[this.accessors.hasAll] = function(objects) {
var instance = this;
var instance = this;
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.error(function(err){ customEventEmitter.emit('error', err)})
......@@ -108,7 +108,7 @@ module.exports = (function() {
}
obj[this.accessors.hasSingle] = function(o) {
var instance = this;
var instance = this;
var customEventEmitter = new Utils.CustomEventEmitter(function() {
instance[self.accessors.get]()
.error(function(err){ customEventEmitter.emit('error', err)})
......@@ -155,8 +155,13 @@ module.exports = (function() {
obj[this.accessors.add] = function(newAssociatedObject) {
var instance = this
, primaryKeys = Object.keys(newAssociatedObject.daoFactory.primaryKeys || {})
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, where = {}
where[newAssociatedObject.daoFactory.tableName+'.'+primaryKey] = newAssociatedObject[primaryKey]
return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]({ where: { id: newAssociatedObject.id }})
instance[self.accessors.get]({ where: where })
.error(function(err){ emitter.emit('error', err)})
.success(function(currentAssociatedObjects) {
if (currentAssociatedObjects.length === 0) {
......
......@@ -43,8 +43,10 @@ module.exports = (function() {
var self = this
obj[this.accessors.get] = function(params) {
var id = this.id
var primaryKeys = Object.keys(this.daoFactory.primaryKeys)
, primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
, where = {}
, id = this[primaryKey] || this.id
where[self.identifier] = id
......@@ -56,8 +58,8 @@ module.exports = (function() {
params = {where: where}
}
smart = Utils.smartWhere([where, params.where || []], self.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.target, smart)
smart = Utils.smartWhere(params.where || [], self.target.daoFactoryManager.sequelize.options.dialect)
smart = Utils.compileSmartWhere.call(self.target, smart, self.target.daoFactoryManager.sequelize.options.dialect)
if (smart.length > 0) {
params.where = smart
}
......@@ -69,17 +71,20 @@ module.exports = (function() {
}
HasOne.prototype.injectSetter = function(obj) {
var self = this
var self = this
obj[this.accessors.set] = function(associatedObject) {
var instance = this
, instanceKeys = Object.keys(instance.daoFactory.primaryKeys)
, instanceKey = instanceKeys.length === 1 ? instanceKeys[0] : 'id'
return new Utils.CustomEventEmitter(function(emitter) {
instance[self.accessors.get]().success(function(oldObj) {
if (oldObj) {
oldObj[self.identifier] = null
oldObj.save().success(function() {
oldObj.save([self.identifier], {allowNull: [self.identifier]}).success(function() {
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject[self.identifier] = instance[instanceKey]
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
......@@ -90,7 +95,7 @@ module.exports = (function() {
})
} else {
if (associatedObject) {
associatedObject[self.identifier] = instance.id
associatedObject[self.identifier] = instance[instanceKey]
associatedObject
.save()
.success(function() { emitter.emit('success', associatedObject) })
......
......@@ -62,6 +62,9 @@ module.exports = (function() {
get: function() { return this.QueryInterface.QueryGenerator }
})
// inject the node-sql methods to the dao factory in order to
// receive the syntax sugar ...
;(function() {
var instance = sql.define({ name: "dummy", columns: [] })
......@@ -571,12 +574,12 @@ module.exports = (function() {
* @param {Object} where Options to describe the scope of the search.
* @return {Object} A promise which fires `success`, `error`, `complete` and `sql`.
*/
DAOFactory.prototype.update = function(attrValueHash, where) {
DAOFactory.prototype.update = function(attrValueHash, where, options) {
if(this.options.timestamps) {
var attr = this.options.underscored ? 'updated_at' : 'updatedAt'
attrValueHash[attr] = Utils.now()
}
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where)
return this.QueryInterface.bulkUpdate(this.tableName, attrValueHash, where, options)
}
DAOFactory.prototype.describe = function() {
......
......@@ -96,7 +96,7 @@ module.exports = (function() {
// if an array with field names is passed to save()
// only those fields will be updated
DAO.prototype.save = function(fields) {
DAO.prototype.save = function(fields, options) {
var self = this
, values = fields ? {} : this.dataValues
, updatedAtAttr = this.__options.underscored ? 'updated_at' : 'updatedAt'
......@@ -179,7 +179,7 @@ module.exports = (function() {
this.isDirty = false
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier)
, query = this.QueryInterface.update(this, tableName, values, identifier, options)
query.proxy(emitter)
}
......
......@@ -162,7 +162,7 @@ module.exports = (function() {
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') })
},
selectQuery: function(tableName, options) {
selectQuery: function(tableName, options, factory) {
var table = null,
joinQuery = ""
......@@ -204,7 +204,7 @@ module.exports = (function() {
query += joinQuery
if (options.hasOwnProperty('where')) {
options.where = this.getWhereConditions(options.where, tableName)
options.where = this.getWhereConditions(options.where, tableName, factory)
query += " WHERE " + options.where
}
......@@ -266,8 +266,8 @@ module.exports = (function() {
return query
},
updateQuery: function(tableName, attrValueHash, where) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var values = []
......@@ -402,14 +402,24 @@ module.exports = (function() {
return Utils._.template(sql)({ tableName: tableName, indexName: indexName })
},
getWhereConditions: function(smth, tableName) {
getWhereConditions: function(smth, tableName, factory) {
var result = null
, where = {}
if (Utils.isHash(smth)) {
smth = Utils.prependTableNameToHash(tableName, smth)
result = this.hashToWhereConditions(smth)
} else if (typeof smth === 'number') {
smth = Utils.prependTableNameToHash(tableName, { id: smth })
var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : []
if (primaryKeys.length > 0) {
// Since we're just a number, assume only the first key
primaryKeys = primaryKeys[0]
} else {
primaryKeys = 'id'
}
where[primaryKeys] = smth
smth = Utils.prependTableNameToHash(tableName, where)
result = this.hashToWhereConditions(smth)
} else if (typeof smth === "string") {
result = smth
......
......@@ -36,6 +36,8 @@ module.exports = (function() {
ConnectorManager.prototype.endQuery = function() {
var self = this
self.pendingQueries--
if (!self.pooling && self.pendingQueries === 0) {
setTimeout(function() {
self.pendingQueries === 0 && self.disconnect.call(self)
......@@ -46,43 +48,31 @@ module.exports = (function() {
ConnectorManager.prototype.query = function(sql, callee, options) {
var self = this
// we really want pendingQueries to increment as fast as possible...
self.pendingQueries++
return new Utils.CustomEventEmitter(function(emitter) {
self.connect()
.on('error', function(err) {
// zero-out the previous increment
self.pendingQueries--
self.endQuery.call(self)
emitter.emit('error', err)
})
.on('success', function(done) {
var query = new Query(self.client, self.sequelize, callee, options || {})
done = done || null
query.run(sql, done)
.success(function(results) {
self.pendingQueries--
emitter.emit('success', results)
self.endQuery.call(self)
})
.error(function(err) {
self.pendingQueries--
emitter.emit('error', err)
self.endQuery.call(self)
})
.on('sql', function(sql) { emitter.emit('sql', sql) })
return query.run(sql, done)
.success(function(results) { self.endQuery.call(self) })
.error(function(err) { self.endQuery.call(self) })
.proxy(emitter)
})
}).run()
}
ConnectorManager.prototype.connect = function() {
ConnectorManager.prototype.connect = function(callback) {
var self = this
var emitter = new (require('events').EventEmitter)()
// in case database is slow to connect, prevent orphaning the client
if (this.isConnecting) {
if (this.isConnecting && !this.pooling) {
emitter.emit('success')
return emitter
}
......@@ -116,10 +106,10 @@ module.exports = (function() {
}
} else if (client) {
client.query("SET TIME ZONE 'UTC'").on('end', function() {
self.isConnected = true
self.client = client
emitter.emit('success', done)
});
self.isConnected = true
self.client = client
emitter.emit('success', done)
})
} else {
self.client = null
emitter.emit('success', done)
......@@ -131,8 +121,8 @@ module.exports = (function() {
this.poolIdentifier = this.pg.pools.getOrCreate(this.sequelize.config)
this.poolIdentifier.connect(connectCallback)
} else {
if (this.client !== null) {
connectCallback(null, this.client)
if (!!this.client) {
connectCallback(null, this.client);
} else {
//create one-off client
this.client = new this.pg.Client(uri)
......@@ -158,4 +148,4 @@ module.exports = (function() {
}
return ConnectorManager
})()
})()
\ No newline at end of file
......@@ -226,7 +226,7 @@ module.exports = (function() {
})
},
selectQuery: function(tableName, options) {
selectQuery: function(tableName, options, factory) {
var query = "SELECT <%= attributes %> FROM <%= table %>",
table = null
......@@ -267,7 +267,7 @@ module.exports = (function() {
}
if(options.hasOwnProperty('where')) {
options.where = this.getWhereConditions(options.where, tableName)
options.where = this.getWhereConditions(options.where, tableName, factory)
query += " WHERE <%= where %>"
}
......@@ -354,8 +354,8 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
updateQuery: function(tableName, attrValueHash, where) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %> RETURNING *"
, values = []
......@@ -492,18 +492,26 @@ module.exports = (function() {
})
},
getWhereConditions: function(smth, tableName) {
getWhereConditions: function(smth, tableName, factory) {
var result = null
, where = {}
if (Utils.isHash(smth)) {
smth = Utils.prependTableNameToHash(tableName, smth)
result = this.hashToWhereConditions(smth)
}
else if (typeof smth === "number") {
smth = Utils.prependTableNameToHash(tableName, { id: smth })
} else if (typeof smth === 'number') {
var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : []
if (primaryKeys.length > 0) {
// Since we're just a number, assume only the first key
primaryKeys = primaryKeys[0]
} else {
primaryKeys = 'id'
}
where[primaryKeys] = smth
smth = Utils.prependTableNameToHash(tableName, where)
result = this.hashToWhereConditions(smth)
}
else if (typeof smth === "string") {
} else if (typeof smth === "string") {
result = smth
}
else if (Array.isArray(smth)) {
......
......@@ -101,7 +101,7 @@ module.exports = (function() {
result[_result.Field].defaultValue = result[_result.Field].defaultValue.replace(/'/g, "")
if (result[_result.Field].defaultValue.indexOf('::') > -1) {
var split = result[_result.Field].defaultValue.split('::');
var split = result[_result.Field].defaultValue.split('::')
if (split[1].toLowerCase() !== "regclass)") {
result[_result.Field].defaultValue = split[0]
}
......@@ -163,4 +163,4 @@ module.exports = (function() {
}
return Query
})()
})()
\ No newline at end of file
......@@ -144,7 +144,7 @@ module.exports = (function() {
return Utils._.template(query)(replacements)
},
selectQuery: function(tableName, options) {
selectQuery: function(tableName, options, factory) {
var table = null,
joinQuery = ""
......@@ -186,7 +186,7 @@ module.exports = (function() {
query += joinQuery
if (options.hasOwnProperty('where')) {
options.where = this.getWhereConditions(options.where, tableName)
options.where = this.getWhereConditions(options.where, tableName, factory)
query += " WHERE " + options.where
}
......@@ -218,8 +218,8 @@ module.exports = (function() {
return query
},
updateQuery: function(tableName, attrValueHash, where) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
updateQuery: function(tableName, attrValueHash, where, options) {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull, options)
var query = "UPDATE <%= table %> SET <%= values %> WHERE <%= where %>"
, values = []
......
......@@ -125,15 +125,15 @@ module.exports = (function() {
this.finished = true
if (this.emitters.length > 0) {
this.finished = (this.finishedEmits == this.emitters.length)
this.finished = (this.finishedEmits === this.emitters.length)
}
else if (this.serials.length > 0) {
this.finished = (this.finishedEmits == this.serials.length)
this.finished = (this.finishedEmits === this.serials.length)
}
if (this.finished && this.wasRunning) {
var status = (this.fails.length == 0 ? 'success' : 'error')
, result = (this.fails.length == 0 ? this[resultsName] : this.fails)
var status = (this.fails.length === 0 ? 'success' : 'error')
, result = (this.fails.length === 0 ? this[resultsName] : this.fails)
this.eventEmitter.emit.apply(this.eventEmitter, [status, result].concat(result))
}
......
......@@ -91,25 +91,25 @@ module.exports = (function() {
var chainer = new Utils.QueryChainer()
self.showAllTables().success(function(tableNames) {
chainer.add(self, 'disableForeignKeyConstraints', [])
chainer.add(self, 'disableForeignKeyConstraints', [])
tableNames.forEach(function(tableName) {
chainer.add(self, 'dropTable', [tableName, {cascade: true}])
})
tableNames.forEach(function(tableName) {
chainer.add(self, 'dropTable', [tableName, {cascade: true}])
})
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer
.runSerially()
.success(function() {
self.emit('dropAllTables', null)
emitter.emit('success', null)
})
.error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('error', err)
})
chainer
.runSerially()
.success(function() {
self.emit('dropAllTables', null)
emitter.emit('success', null)
})
.error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('error', err)
})
}).error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('error', err)
......@@ -265,10 +265,10 @@ module.exports = (function() {
return queryAndEmit.call(this, sql, 'bulkInsert')
}
QueryInterface.prototype.update = function(dao, tableName, values, identifier) {
QueryInterface.prototype.update = function(dao, tableName, values, identifier, options) {
var self = this
, restrict = false
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier, options)
// Check for a restrict field
if (!!dao.daoFactory && !!dao.daoFactory.associations) {
......@@ -285,8 +285,8 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
......@@ -305,9 +305,9 @@ module.exports = (function() {
}).run()
}
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier) {
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options) {
var self = this
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier)
, sql = self.QueryGenerator.updateQuery(tableName, values, identifier, options)
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
......@@ -393,7 +393,7 @@ module.exports = (function() {
QueryInterface.prototype.select = function(factory, tableName, options, queryOptions) {
options = options || {}
var sql = this.QueryGenerator.selectQuery(tableName, options)
var sql = this.QueryGenerator.selectQuery(tableName, options, factory)
queryOptions = Utils._.extend({}, queryOptions, { include: options.include })
return queryAndEmit.call(this, [sql, factory, queryOptions], 'select')
}
......
......@@ -191,7 +191,6 @@ var Utils = module.exports = {
break
default: // lazy
text = text.concat(obj[column].lazy.conditions.map(function(val){ return columnName + ' ' + val }))
obj[column].lazy.bindings = obj[column].lazy.bindings.map(function(v) { return SqlString.escape(v, false, null, dialect) })
whereArgs = whereArgs.concat(obj[column].lazy.bindings)
}
})
......@@ -320,15 +319,18 @@ var Utils = module.exports = {
return hash
},
removeNullValuesFromHash: function(hash, omitNull) {
removeNullValuesFromHash: function(hash, omitNull, options) {
var result = hash
options = options || {}
options.allowNull = options.allowNull || []
if (omitNull) {
var _hash = {}
Utils._.each(hash, function(val, key) {
if (key.match(/Id$/) || ((val !== null) && (val !== undefined))) {
_hash[key] = val;
if (options.allowNull.indexOf(key) > -1 || key.match(/Id$/) || ((val !== null) && (val !== undefined))) {
_hash[key] = val
}
})
......
......@@ -39,7 +39,7 @@
"lodash": "~1.3.1",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "~1.3.0",
"validator": "~1.4.0",
"moment": "~2.1.0",
"commander": "~2.0.0",
"dottie": "0.0.8-0",
......@@ -51,8 +51,7 @@
"devDependencies": {
"sqlite3": "~2.1.12",
"mysql": "~2.0.0-alpha8",
"pg": "~2.2.0",
"buster": "~0.6.3",
"pg": "~2.3.1",
"watchr": "~2.4.3",
"yuidocjs": "~0.3.36",
"chai": "~1.7.2",
......
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("BelongsTo"), function() {
describe('setAssociation', function() {
it('can set the association with declared primary keys...', function(done) {
var User = this.sequelize.define('UserXYZ', { user_id: {type: DataTypes.INTEGER, primaryKey: true }, username: DataTypes.STRING })
, Task = this.sequelize.define('TaskXYZ', { task_id: {type: DataTypes.INTEGER, primaryKey: true }, title: DataTypes.STRING })
Task.belongsTo(User, { foreignKey: 'user_id' })
this.sequelize.sync({ force: true }).success(function() {
User.create({ user_id: 1, username: 'foo' }).success(function(user) {
Task.create({ task_id: 1, title: 'task' }).success(function(task) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).not.to.be.null
task.setUserXYZ(null).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).to.be.null
done()
})
})
})
})
})
})
})
})
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: DataTypes.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: DataTypes.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUserXYZ(user).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).not.to.be.null
task.setUserXYZ(null).success(function() {
task.getUserXYZ().success(function(user) {
expect(user).to.be.null
done()
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User)
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User, {onDelete: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(0)
done()
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User, {onDelete: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User, {onUpdate: 'cascade'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
expect(tasks[0].UserId).to.equal(999)
done()
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
, User = this.sequelize.define('User', { username: DataTypes.STRING })
Task.belongsTo(User, {onUpdate: 'restrict'})
this.sequelize.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
task.setUser(user).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: DataTypes.STRING })
, dataTypes = [DataTypes.INTEGER, DataTypes.BIGINT, DataTypes.STRING]
, self = this
, Tasks = {}
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING })
Tasks[dataType].belongsTo(User, { foreignKey: 'userId', keyType: dataType })
})
self.sequelize.sync({ force: true })
.success(function() {
dataTypes.forEach(function(dataType, i) {
expect(Tasks[dataType].rawAttributes.userId.type.toString())
.to.equal(dataType.toString())
if ((i+1) === dataTypes.length) {
done()
}
})
})
})
})
})
/* jshint camelcase: false */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, Sequelize = require('../../index')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("HasOne"), function() {
describe('getAssocation', function() {
it('should be able to handle a where object that\'s a first class citizen.', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING, status: Sequelize.STRING })
User.hasOne(Task)
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task', status: 'inactive' }).success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) {
expect(task).to.equal(null)
done()
})
})
})
})
})
})
})
})
describe('setAssociation', function() {
it('can set an association with predefined primary keys', function(done) {
var User = this.sequelize.define('UserXYZZ', { userCoolIdTag: { type: Sequelize.INTEGER, primaryKey: true }, username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZZ', { taskOrSomething: { type: Sequelize.INTEGER, primaryKey: true }, title: Sequelize.STRING })
User.hasOne(Task, {foreignKey: 'userCoolIdTag'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({userCoolIdTag: 1, username: 'foo'}).success(function(user) {
Task.create({taskOrSomething: 1, title: 'bar'}).success(function(task) {
user.setTaskXYZZ(task).success(function() {
user.getTaskXYZZ().success(function(task) {
expect(task).not.to.be.null
user.setTaskXYZZ(null).success(function() {
user.getTaskXYZZ().success(function(_task) {
expect(_task).to.be.null
done()
})
})
})
})
})
})
})
})
})
it('clears the association if null is passed', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, Task = this.sequelize.define('TaskXYZ', { title: Sequelize.STRING })
User.hasOne(Task)
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTaskXYZ(task).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).not.to.equal(null)
user.setTaskXYZ(null).success(function() {
user.getTaskXYZ().success(function(task) {
expect(task).to.equal(null)
done()
})
})
})
})
})
})
})
})
})
})
describe("Foreign key constraints", function() {
it("are not enabled by default", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task)
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
it("can cascade deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'cascade'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(0)
done()
})
})
})
})
})
})
})
})
it("can restrict deletes", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onDelete: 'restrict'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
user.destroy().error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
it("can cascade updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'cascade'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.success(function() {
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
expect(tasks[0].UserId).to.equal(999)
done()
})
})
})
})
})
})
})
})
it("can restrict updates", function(done) {
var Task = this.sequelize.define('Task', { title: Sequelize.STRING })
, User = this.sequelize.define('User', { username: Sequelize.STRING })
User.hasOne(Task, {onUpdate: 'restrict'})
User.sync({ force: true }).success(function() {
Task.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(user) {
Task.create({ title: 'task' }).success(function(task) {
user.setTask(task).success(function() {
// Changing the id of a DAO requires a little dance since
// the `UPDATE` query generated by `save()` uses `id` in the
// `WHERE` clause
var tableName = user.QueryInterface.QueryGenerator.addSchema(user.__factory)
user.QueryInterface.update(user, tableName, {id: 999}, user.id)
.error(function() {
// Should fail due to FK restriction
Task.findAll().success(function(tasks) {
expect(tasks).to.have.length(1)
done()
})
})
})
})
})
})
})
})
})
describe("Association options", function() {
it('can specify data type for autogenerated relational keys', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
, dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
, self = this
, Tasks = {}
dataTypes.forEach(function(dataType) {
var tableName = 'TaskXYZ_' + dataType.toString()
Tasks[dataType] = self.sequelize.define(tableName, { title: Sequelize.STRING })
User.hasOne(Tasks[dataType], { foreignKey: 'userId', keyType: dataType })
Tasks[dataType].sync({ force: true }).success(function() {
expect(Tasks[dataType].rawAttributes.userId.type.toString())
.to.equal(dataType.toString())
dataTypes.splice(dataTypes.indexOf(dataType), 1)
if (!dataTypes.length) {
done()
}
})
})
})
})
})
\ No newline at end of file
......@@ -336,8 +336,10 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
smth: { type: Sequelize.STRING, allowNull: false }
})
this.sequelize.options.omitNull = false
UserNull.sync({ force: true }).success(function() {
UserNull.create({ username: 'foo', smth: null }).error(function(err) {
UserNull.create({ username: 'foo2', smth: null }).error(function(err) {
expect(err).to.exist
if (dialect === "mysql") {
......
......@@ -26,6 +26,80 @@ if (dialect.match(/^postgres/)) {
done()
})
describe('integers', function() {
describe('integer', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
aNumber: DataTypes.INTEGER
})
this.User.sync({ force: true }).success(function() {
done()
})
})
it('positive', function(done) {
var User = this.User
User.create({aNumber: 2147483647}).success(function(user) {
expect(user.aNumber).to.equal(2147483647)
User.find({where: {aNumber: 2147483647}}).success(function(_user) {
expect(_user.aNumber).to.equal(2147483647)
done()
})
})
})
it('negative', function(done) {
var User = this.User
User.create({aNumber: -2147483647}).success(function(user) {
expect(user.aNumber).to.equal(-2147483647)
User.find({where: {aNumber: -2147483647}}).success(function(_user) {
expect(_user.aNumber).to.equal(-2147483647)
done()
})
})
})
})
describe('bigint', function() {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
aNumber: DataTypes.BIGINT
})
this.User.sync({ force: true }).success(function() {
done()
})
})
it('positive', function(done) {
var User = this.User
User.create({aNumber: '9223372036854775807'}).success(function(user) {
expect(user.aNumber).to.equal('9223372036854775807')
User.find({where: {aNumber: '9223372036854775807'}}).success(function(_user) {
expect(_user.aNumber).to.equal('9223372036854775807')
done()
})
})
})
it('negative', function(done) {
var User = this.User
User.create({aNumber: '-9223372036854775807'}).success(function(user) {
expect(user.aNumber).to.equal('-9223372036854775807')
User.find({where: {aNumber: '-9223372036854775807'}}).success(function(_user) {
expect(_user.aNumber).to.equal('-9223372036854775807')
done()
})
})
})
})
})
describe('model', function() {
it("create handles array correctly", function(done) {
this.User
......
......@@ -116,12 +116,8 @@ var Support = {
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
before(function(done) {
this.sequelize = sequelize
done()
})
beforeEach(function(done) {
this.sequelize = sequelize
Support.clearDatabase(this.sequelize, function() {
done()
})
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!