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

Commit 319617ac by Sascha Depold

Merge branch 'association-key' of https://github.com/durango/sequelize into durango-association-key

2 parents 95a23b6d 28846d7f
......@@ -8,6 +8,8 @@ script:
notifications:
email:
- sascha@depold.com
hipchat:
- 40e8850aaba9854ac4c9963bd33f8b@253477
env:
- DB=mysql DIALECT=mysql
......
......@@ -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) })
......
......@@ -2,6 +2,8 @@ var Utils = require("./utils")
, DAO = require("./dao")
, DataTypes = require("./data-types")
, Util = require('util')
, sql = require('sql')
, SqlString = require('./sql-string')
module.exports = (function() {
var DAOFactory = function(name, attributes, options) {
......@@ -60,6 +62,32 @@ module.exports = (function() {
get: function() { return this.QueryInterface.QueryGenerator }
})
;(function() {
var instance = sql.define({ name: "dummy", columns: [] })
for (var methodName in instance) {
;(function(methodName) {
DAOFactory.prototype[methodName] = function() {
var dataset = this.dataset()
, result = dataset[methodName].apply(dataset, arguments)
, dialect = this.daoFactoryManager.sequelize.options.dialect
, self = this
result.toSql = function() {
var query = result.toQuery()
return SqlString.format(query.text.replace(/(\$\d)/g, '?'), query.values, null, dialect) + ';'
}
result.exec = function() {
return self.QueryInterface.queryAndEmit([result.toSql(), self, { type: 'SELECT' }], 'snafu')
}
return result
}
})(methodName)
}
})()
DAOFactory.prototype.init = function(daoFactoryManager) {
var self = this;
......@@ -531,37 +559,35 @@ 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() {
return this.QueryInterface.describeTable(this.tableName)
}
// private
var query = function() {
var args = Utils._.map(arguments, function(arg, _) { return arg })
, sequelize = this.daoFactoryManager.sequelize
// add this as the second argument
if (arguments.length === 1) {
args.push(this)
DAOFactory.prototype.dataset = function() {
if (!this.__sql) {
this.__sql = sql.setDialect(this.daoFactoryManager.sequelize.options.dialect)
}
// add {} as options
if (args.length === 2) {
args.push({})
}
var instance = this.__sql.define({ name: this.tableName, columns: [] })
, attributes = this.attributes
return sequelize.query.apply(sequelize, args)
Object.keys(attributes).forEach(function(key) {
instance.addColumn(key, attributes[key])
})
return instance
}
// private
var addOptionalClassMethods = function() {
var self = this
Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct })
......
......@@ -100,7 +100,7 @@ var prepareValidationOfAttribute = function(value, details, validatorType) {
}
// extract the error msg
errorMessage = details.hasOwnProperty("msg") ? details.msg : false
errorMessage = details.hasOwnProperty("msg") ? details.msg : undefined
// check method exists
var validator = Validator.check(value, errorMessage)
......
......@@ -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'
......@@ -108,7 +108,7 @@ module.exports = (function() {
fields.push(updatedAtAttr)
}
if (fields.indexOf(createdAtAttr) === -1) {
if (fields.indexOf(createdAtAttr) === -1 && this.isNewRecord === true) {
fields.push(createdAtAttr)
}
}
......@@ -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)
return query
}
......@@ -240,26 +240,38 @@ module.exports = (function() {
var self = this
var readOnlyAttributes = Object.keys(this.__factory.primaryKeys)
readOnlyAttributes.push('id')
readOnlyAttributes.push('createdAt')
readOnlyAttributes.push('updatedAt')
readOnlyAttributes.push('deletedAt')
if (this.isNewRecord !== true) {
readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'created_at' : 'createdAt')
}
// readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt')
readOnlyAttributes.push(this.daoFactory.options.underscored === true ? 'deleted_at' : 'deletedAt')
var isDirty = this.isDirty
Utils._.each(updates, function(value, attr) {
var updateAllowed = (
(readOnlyAttributes.indexOf(attr) == -1) &&
(readOnlyAttributes.indexOf(Utils._.underscored(attr)) == -1) &&
(self.attributes.indexOf(attr) > -1)
)
if (updateAllowed) {
if (Utils.hasChanged(self[attr], value)) {
isDirty = true
}
self[attr] = value
}
})
// since we're updating the record, we should be updating the updatedAt column..
if (this.daoFactory.options.timestamps === true) {
isDirty = true
self[this.daoFactory.options.underscored === true ? 'updated_at' : 'updatedAt'] = new Date()
}
this.isDirty = isDirty
}
......
......@@ -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
}
......@@ -119,7 +109,7 @@ module.exports = (function() {
self.isConnected = true
self.client = client
emitter.emit('success', done)
});
})
} else {
self.client = null
emitter.emit('success', done)
......@@ -131,13 +121,9 @@ 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)
} else {
//create one-off client
this.client = new this.pg.Client(uri)
this.client.connect(connectCallback)
}
//create one-off client
this.client = new this.pg.Client(uri)
this.client.connect(connectCallback)
}
return emitter
......@@ -158,4 +144,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,19 +39,19 @@
"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",
"toposort-class": "~0.2.0",
"generic-pool": "2.0.4",
"promise": "~3.2.0"
"promise": "~3.2.0",
"sql": "~0.26.0"
},
"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
......@@ -337,7 +337,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
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") {
......@@ -798,6 +798,24 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
describe('update', function() {
it('updates the attributes that we select only without updating createdAt', function(done) {
var User = this.sequelize.define('User1', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING
}, {
paranoid:true
})
User.sync({ force: true }).success(function() {
User.create({username: 'Peter', secretValue: '42'}).success(function(user) {
user.updateAttributes({ secretValue: '43' }, ['secretValue']).on('sql', function(sql) {
expect(sql).to.match(/UPDATE\s+[`"]+User1s[`"]+\s+SET\s+[`"]+secretValue[`"]='43',[`"]+updatedAt[`"]+='[^`",]+'\s+WHERE [`"]+id[`"]+=1/)
done()
})
})
})
})
it('allows sql logging of updated statements', function(done) {
var User = this.sequelize.define('User', {
name: Sequelize.STRING,
......@@ -2526,15 +2544,15 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
describe('references', function() {
this.timeout(3000)
beforeEach(function(done) {
var self = this
self.Author = self.sequelize.define('author', { firstName: Sequelize.STRING })
self.Author.sync({ force: true }).success(function() {
this.Author = this.sequelize.define('author', { firstName: Sequelize.STRING })
this.Author.sync({ force: true }).success(function() {
done()
})
})
afterEach(function(done) {
var self = this
self.sequelize.getQueryInterface().dropTable('posts', { force: true }).success(function() {
self.sequelize.getQueryInterface().dropTable('authors', { force: true }).success(function() {
done()
......@@ -2646,4 +2664,116 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
describe("syntax sugar", function() {
before(function(done) {
this.User = this.sequelize.define("user", {
username: Sequelize.STRING,
firstName: Sequelize.STRING,
lastName: Sequelize.STRING
})
this.User.sync({ force: true }).success(function() {
done()
})
})
describe("dataset", function() {
it("returns a node-sql instance with the correct dialect", function() {
expect(this.User.dataset().sql.dialectName).to.equal(dialect)
})
it("allows me to generate sql queries", function() {
var query = this.User.dataset().select("username").toQuery()
expect(Object.keys(query)).to.eql(['text', 'values'])
})
})
describe("select", function() {
it("sets .select() as an alias to .dataset().select()", function() {
var query1 = this.User.select("username").toQuery()
, query2 = this.User.dataset().select("username").toQuery()
expect(query1.text).to.equal(query2.text)
})
})
describe("toSql", function() {
it("transforms the node-sql instance into a proper sql string", function() {
var sql = this.User.select("username").toSql()
var sqlMap = {
postgres: 'SELECT username FROM "' + this.User.tableName + '";',
mysql: 'SELECT username FROM `' + this.User.tableName + '`;',
sqlite: 'SELECT username FROM "' + this.User.tableName + '";'
}
expect(sql).to.equal(sqlMap[dialect])
})
it("transforms node-sql instances with chaining into a proper sql string", function() {
var sql = this.User.select("username").select("firstName").group("username").toSql()
var sqlMap = {
postgres: 'SELECT username, firstName FROM "' + this.User.tableName + '" GROUP BY username;',
mysql: 'SELECT username, firstName FROM `' + this.User.tableName + '` GROUP BY username;',
sqlite: 'SELECT username, firstName FROM "' + this.User.tableName + '" GROUP BY username;'
}
expect(sql).to.equal(sqlMap[dialect])
})
})
describe("exec", function() {
beforeEach(function(done) {
var self = this
this
.User
.create({ username: "foo" })
.then(function() {
return self.User.create({ username: "bar" })
})
.then(function() {
return self.User.create({ username: "baz" })
})
.then(function() { done() })
})
it("selects all users with name 'foo'", function(done) {
this
.User
.where({ username: "foo" })
.exec()
.success(function(users) {
expect(users).to.have.length(1)
expect(users[0].username).to.equal("foo")
done()
})
})
it("returns an instanceof DAO", function(done) {
var DAO = require(__dirname + "/../lib/dao")
this.User.where({ username: "foo" }).exec().success(function(users) {
expect(users[0]).to.be.instanceOf(DAO)
done()
})
})
it("returns all users in the db", function(done) {
this.User.select().exec().success(function(users) {
expect(users).to.have.length(3)
done()
})
})
it("can handle or queries", function(done) {
this
.User
.where(this.User.dataset().username.equals("bar").or(this.User.dataset().username.equals("baz")))
.exec()
.success(function(users) {
expect(users).to.have.length(2)
done()
})
})
})
})
})
......@@ -169,16 +169,38 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
})
})
it("returns false for bulk non-changed attribute", function(done) {
it("returns true for bulk non-changed attribute + model with timestamps", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.setAttributes({
username: 'user'
})
expect(user.isDirty).to.be.false
expect(user.isDirty).to.be.rue
done()
})
})
it("returns false for bulk non-changed attribute + model without timestamps", function(done) {
var User = this.sequelize.define('User' + parseInt(Math.random() * 10000000), {
username: DataTypes.STRING
}, {
timestamps: false
})
User
.sync({ force: true })
.then(function() {
return User.create({ username: "user" })
})
.then(function(user) {
return user.setAttributes({ username: "user" })
expect(user.isDirty).to.be.false
})
.then(function() {
done()
})
})
it("returns true for changed and bulk non-changed attribute", function(done) {
this.User.create({ username: 'user' }).success(function(user) {
user.aNumber = 23
......@@ -1112,17 +1134,21 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
identifier: 'identifier'
}).success(function(user) {
var oldCreatedAt = user.createdAt
, oldUpdatedAt = user.updatedAt
, oldIdentifier = user.identifier
user.updateAttributes({
name: 'foobar',
createdAt: new Date(2000, 1, 1),
identifier: 'another identifier'
}).success(function(user) {
expect((new Date(user.createdAt)).getTime()).to.equal((new Date(oldCreatedAt)).getTime())
expect(user.identifier).to.equal(oldIdentifier)
done()
})
setTimeout(function () {
user.updateAttributes({
name: 'foobar',
createdAt: new Date(2000, 1, 1),
identifier: 'another identifier'
}).success(function(user) {
expect(new Date(user.createdAt)).to.equalDate(new Date(oldCreatedAt))
expect(new Date(user.updatedAt)).to.not.equalTime(new Date(oldUpdatedAt))
expect(user.identifier).to.equal(oldIdentifier)
done()
})
}, 1000)
})
})
})
......
......@@ -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!