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

Commit f9fd3775 by Sascha Depold

Merge branch 'milestones/2.0.0' of github.com:sequelize/sequelize into milestones/2.0.0

2 parents 7a17497b 83b1b558
...@@ -18,5 +18,6 @@ ...@@ -18,5 +18,6 @@
"unused": true, "unused": true,
"asi": true, "asi": true,
"evil": false, "evil": false,
"laxcomma": true "laxcomma": true,
"es5": true
} }
\ No newline at end of file
...@@ -7,6 +7,12 @@ The Sequelize library provides easy access to MySQL, SQLite or PostgreSQL databa ...@@ -7,6 +7,12 @@ The Sequelize library provides easy access to MySQL, SQLite or PostgreSQL databa
## Important Notes ## ## Important Notes ##
### 2.0.0 ###
There is a parallel "branch" of the project, released as `2.0.0-alphaX` in NPM. All those releases are based on the master
and will get all the changes of the master. However, `2.0.0` will contain backwards compatibility breaking changes. Check the
changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/2.0.0/changelog.md
### 1.6.0 ### ### 1.6.0 ###
- We changed the way timestamps are handled. From v1.6.0 on timestamps are stored and loaded as UTC. - We changed the way timestamps are handled. From v1.6.0 on timestamps are stored and loaded as UTC.
...@@ -236,7 +242,8 @@ for (var key in obj) { ...@@ -236,7 +242,8 @@ for (var key in obj) {
"unused": true, "unused": true,
"asi": true, "asi": true,
"evil": false, "evil": false,
"laxcomma": true "laxcomma": true,
"es5": true
} }
``` ```
......
...@@ -12,7 +12,8 @@ ...@@ -12,7 +12,8 @@
- [BUG] Fixed empty where conditions in MySQL. [#619](https://github.com/sequelize/sequelize/pull/619). Thanks to terraflubb - [BUG] Fixed empty where conditions in MySQL. [#619](https://github.com/sequelize/sequelize/pull/619). Thanks to terraflubb
- [BUG] Allow overriding of default columns. [#635](https://github.com/sequelize/sequelize/pull/635). Thanks to sevastos - [BUG] Allow overriding of default columns. [#635](https://github.com/sequelize/sequelize/pull/635). Thanks to sevastos
- [BUG] Fix where params for belongsTo [#658](https://github.com/sequelize/sequelize/pull/658). Thanks to mweibel - [BUG] Fix where params for belongsTo [#658](https://github.com/sequelize/sequelize/pull/658). Thanks to mweibel
- [BUG] Default ports are now declared in the connector manager, which means the default port for PG correctly becomes 5432. [#633](https://github.com/sequelize/sequelize/issues/633) - [BUG] Default ports are now declared in the connector manager, which means the default port for PG correctly becomes 5432. [#633](https://github.com/sequelize/sequelize/issues/633). durango
- [BUG] Columns with type BOOLEAN were always added to toJSON output, even if they were not selected [see](https://gist.github.com/gchaincl/45aca14e93934bf4a05e). janmeier
- [FEATURE] Validate a model before it gets saved. [#601](https://github.com/sequelize/sequelize/pull/601). thanks to 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] 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 - [FEATURE] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
...@@ -26,6 +27,7 @@ ...@@ -26,6 +27,7 @@
- [FEATURE] `findOrCreate` now returns an additional flag (`created`), that is true if a model was created, and false if it was found [#648](https://github.com/sequelize/sequelize/pull/648). janmeier - [FEATURE] `findOrCreate` now returns an additional flag (`created`), that is true if a model was created, and false if it was found [#648](https://github.com/sequelize/sequelize/pull/648). janmeier
- [FEATURE] Field and table comments for MySQL and PG. [#523](https://github.com/sequelize/sequelize/pull/523). MySQL by iamjochen. PG by janmeier - [FEATURE] Field and table comments for MySQL and PG. [#523](https://github.com/sequelize/sequelize/pull/523). MySQL by iamjochen. PG by janmeier
- [FEATURE] BigInts can now be used for autoincrement/serial columns. [#673](https://github.com/sequelize/sequelize/pull/673). thanks to sevastos - [FEATURE] BigInts can now be used for autoincrement/serial columns. [#673](https://github.com/sequelize/sequelize/pull/673). thanks to sevastos
- [REFACTORING] hasMany now uses a single SQL statement when creating and destroying associations, instead of removing each association seperately [690](https://github.com/sequelize/sequelize/pull/690). Inspired by [#104](https://github.com/sequelize/sequelize/issues/104). janmeier
# v1.6.0 # # v1.6.0 #
- [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work - [DEPENDENCIES] upgrade mysql to alpha7. You *MUST* use this version or newer for DATETIMEs to work
......
...@@ -42,35 +42,52 @@ module.exports = (function() { ...@@ -42,35 +42,52 @@ module.exports = (function() {
HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations) { HasManyDoubleLinked.prototype.injectSetter = function(emitterProxy, oldAssociations, newAssociations) {
var self = this var self = this
, chainer = new Utils.QueryChainer()
destroyObsoleteAssociations , association = self.__factory.target.associations[self.__factory.associationAccessor]
.call(this, oldAssociations, newAssociations) , foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
.on('sql', function(sql) { emitterProxy.emit('sql', sql) }) , obsoleteAssociations = oldAssociations.filter(function (old) {
.error(function(err) { emitterProxy.emit('error', err) }) // Return only those old associations that are not found in new
.success(function() { return !Utils._.find(newAssociations, function (obj) {
var chainer = new Utils.QueryChainer return obj.id === old.id
, association = self.__factory.target.associations[self.__factory.associationAccessor] })
, foreignIdentifier = association.isSelfAssociation ? association.foreignIdentifier : association.identifier
, unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return obj.id === old.id
})
})
unassociatedObjects.forEach(function(unassociatedObject) {
var attributes = {}
attributes[self.__factory.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id
chainer.add(self.__factory.connectorDAO.create(attributes))
}) })
, unassociatedObjects = newAssociations.filter(function (obj) {
return !Utils._.find(oldAssociations, function (old) {
return obj.id === old.id
})
})
if (obsoleteAssociations.length > 0) {
var foreignIds = obsoleteAssociations.map(function (associatedObject) {
return associatedObject.id
})
, primaryKeys = Object.keys(self.__factory.connectorDAO.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
chainer var where = {}
.run() where[self.__factory.identifier] = self.instance.id
.success(function() { emitterProxy.emit('success', newAssociations) }) where[foreignKey] = foreignIds
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) }) chainer.add(self.__factory.connectorDAO.destroy(where))
}
if (unassociatedObjects.length > 0) {
var bulk = unassociatedObjects.map(function(unassociatedObject) {
var attributes = {}
attributes[self.__factory.identifier] = self.instance.id
attributes[foreignIdentifier] = unassociatedObject.id
return attributes
}) })
chainer.add(self.__factory.connectorDAO.bulkCreate(bulk))
}
chainer
.run()
.success(function() { emitterProxy.emit('success', newAssociations) })
.error(function(err) { emitterProxy.emit('error', err) })
.on('sql', function(sql) { emitterProxy.emit('sql', sql) })
} }
HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) { HasManyDoubleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) {
...@@ -87,57 +104,5 @@ module.exports = (function() { ...@@ -87,57 +104,5 @@ module.exports = (function() {
.on('sql', function(sql) { emitterProxy.emit('sql', sql) }) .on('sql', function(sql) { emitterProxy.emit('sql', sql) })
} }
// private
var destroyObsoleteAssociations = function(oldAssociations, newAssociations) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
var foreignIdentifier = self.__factory.target.associations[self.__factory.associationAccessor].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
})
})
if (obsoleteAssociations.length === 0) {
return emitter.emit('success', null)
}
obsoleteAssociations.forEach(function(associatedObject) {
var where = {}
, primaryKeys = Object.keys(self.__factory.connectorDAO.rawAttributes)
, foreignKey = primaryKeys.filter(function(pk) { return pk != self.__factory.identifier })[0]
, notFoundEmitters = []
where[self.__factory.identifier] = self.instance.id
where[foreignKey] = associatedObject.id
self.__factory.connectorDAO
.find({ where: where })
.success(function(connector) {
if (connector === null) {
notFoundEmitters.push(null)
} else {
chainer.add(connector.destroy())
}
if ((chainer.emitters.length + notFoundEmitters.length) === obsoleteAssociations.length) {
// found all obsolete connectors and will delete them now
chainer
.run()
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('error', err) })
.on('sql', function(sql) { emitter.emit('sql', sql) })
}
})
.error(function(err) { emitter.emit('error', err) })
.on('sql', function(sql) { emitter.emit('sql', sql) })
})
}).run()
}
return HasManyDoubleLinked return HasManyDoubleLinked
})() })()
...@@ -7,7 +7,8 @@ module.exports = (function() { ...@@ -7,7 +7,8 @@ module.exports = (function() {
} }
HasManySingleLinked.prototype.injectGetter = function(options) { HasManySingleLinked.prototype.injectGetter = function(options) {
var where = {}, options = options || {} var where = {}
options = options || {}
where[this.__factory.identifier] = this.instance.id where[this.__factory.identifier] = this.instance.id
...@@ -17,7 +18,6 @@ module.exports = (function() { ...@@ -17,7 +18,6 @@ module.exports = (function() {
HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) { HasManySingleLinked.prototype.injectSetter = function(emitter, oldAssociations, newAssociations) {
var self = this var self = this
, options = this.__factory.options
, chainer = new Utils.QueryChainer() , chainer = new Utils.QueryChainer()
, obsoleteAssociations = oldAssociations.filter(function (old) { , obsoleteAssociations = oldAssociations.filter(function (old) {
return !Utils._.find(newAssociations, function (obj) { return !Utils._.find(newAssociations, function (obj) {
...@@ -29,23 +29,37 @@ module.exports = (function() { ...@@ -29,23 +29,37 @@ module.exports = (function() {
return obj.id === old.id return obj.id === old.id
}) })
}) })
, update
// clear the old associations if (obsoleteAssociations.length > 0) {
obsoleteAssociations.forEach(function(associatedObject) { // clear the old associations
associatedObject[self.__factory.identifier] = null var obsoleteIds = obsoleteAssociations.map(function(associatedObject) {
chainer.add(associatedObject.save()) associatedObject[self.__factory.identifier] = null
}) return associatedObject.id
})
// set the new associations update = {}
unassociatedObjects.forEach(function(associatedObject) { update[self.__factory.identifier] = null
associatedObject[self.__factory.identifier] = self.instance.id chainer.add(this.__factory.target.update(update, { id: obsoleteIds }))
chainer.add(associatedObject.save()) }
})
if (unassociatedObjects.length > 0) {
// set the new associations
var unassociatedIds = unassociatedObjects.map(function(associatedObject) {
associatedObject[self.__factory.identifier] = self.instance.id
return associatedObject.id
})
update = {}
update[self.__factory.identifier] = self.instance.id
chainer.add(this.__factory.target.update(update, { id: unassociatedIds }))
}
chainer chainer
.run() .run()
.success(function() { emitter.emit('success', newAssociations) }) .success(function() { emitter.emit('success', newAssociations) })
.error(function(err) { emitter.emit('error', err) }) .error(function(err) { emitter.emit('error', err) })
.on('sql', function(sql) { emitter.emit('sql', sql) })
} }
HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) { HasManySingleLinked.prototype.injectAdder = function(emitterProxy, newAssociation) {
......
...@@ -254,7 +254,9 @@ module.exports = (function() { ...@@ -254,7 +254,9 @@ module.exports = (function() {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id }, var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : { id: this.id },
values = {} values = {}
if (count === undefined) count = 1; if (count === undefined) {
count = 1;
}
if (Utils._.isString(fields)) { if (Utils._.isString(fields)) {
values[fields] = count; values[fields] = count;
...@@ -303,22 +305,25 @@ module.exports = (function() { ...@@ -303,22 +305,25 @@ module.exports = (function() {
} }
DAO.prototype.addAttribute = function(attribute, value) { DAO.prototype.addAttribute = function(attribute, value) {
if (typeof this.dataValues[attribute] !== 'undefined') if (typeof this.dataValues[attribute] !== 'undefined') {
return; return
}
if (this.booleanValues.length && this.booleanValues.indexOf(attribute) !== -1) // transform integer 0,1 into boolean if (this.booleanValues.length && this.booleanValues.indexOf(attribute) !== -1 && value !== undefined) { // transform integer 0,1 into boolean
value = !!value; value = !!value
}
var has = (function(o) { var has = (function(o) {
var predef = Object.getOwnPropertyDescriptor(o, attribute); var predef = Object.getOwnPropertyDescriptor(o, attribute);
if (predef && predef.hasOwnProperty('value')) if (predef && predef.hasOwnProperty('value')) {
return true; // true here means 'this property exist as a simple value property, do not place setters or getters at all' return true // true here means 'this property exist as a simple value property, do not place setters or getters at all'
}
return { return {
get: (predef && predef.hasOwnProperty('get') ? predef.get : null) || o.__lookupGetter__(attribute), get: (predef && predef.hasOwnProperty('get') ? predef.get : null) || o.__lookupGetter__(attribute),
set: (predef && predef.hasOwnProperty('set') ? predef.set : null) || o.__lookupSetter__(attribute) set: (predef && predef.hasOwnProperty('set') ? predef.set : null) || o.__lookupSetter__(attribute)
}; };
})(this); })(this);
// @ node-v0.8.19: // @ node-v0.8.19:
...@@ -366,8 +371,9 @@ module.exports = (function() { ...@@ -366,8 +371,9 @@ module.exports = (function() {
if (isNewRecord) { if (isNewRecord) {
if (this.hasDefaultValues) { if (this.hasDefaultValues) {
Utils._.each(this.defaultValues, function(valueFn, key) { Utils._.each(this.defaultValues, function(valueFn, key) {
if (!defaults.hasOwnProperty(key)) if (!defaults.hasOwnProperty(key)) {
defaults[key] = valueFn() defaults[key] = valueFn()
}
}) })
} }
......
...@@ -43,7 +43,7 @@ module.exports = (function() { ...@@ -43,7 +43,7 @@ module.exports = (function() {
charset: null charset: null
}, options || {}) }, options || {})
var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comment %> ENGINE=<%= engine %> <%= charset %>" var query = "CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comment %> ENGINE=<%= engine %> <%= charset %> <%= collation %>"
, primaryKeys = [] , primaryKeys = []
, foreignKeys = {} , foreignKeys = {}
, attrStr = [] , attrStr = []
...@@ -71,7 +71,8 @@ module.exports = (function() { ...@@ -71,7 +71,8 @@ module.exports = (function() {
attributes: attrStr.join(", "), attributes: attrStr.join(", "),
comment: options.comment && Utils._.isString(options.comment) ? " COMMENT " + this.escape(options.comment) : "", comment: options.comment && Utils._.isString(options.comment) ? " COMMENT " + this.escape(options.comment) : "",
engine: options.engine, engine: options.engine,
charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : "") charset: (options.charset ? "DEFAULT CHARSET=" + options.charset : ""),
collation: (options.collate ? "COLLATE " + options.collate : "")
} }
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ") , pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk) }.bind(this)).join(", ")
...@@ -431,7 +432,7 @@ module.exports = (function() { ...@@ -431,7 +432,7 @@ module.exports = (function() {
result.push([_key, _value].join(" IN ")) result.push([_key, _value].join(" IN "))
} else if ((value) && (typeof value == 'object') && !(value instanceof Date)) { } else if ((value) && (typeof value == 'object') && !(value instanceof Date)) {
// is value an object? // is value an object?
//using as sentinel for join column => value //using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join) _value = this.quoteIdentifiers(value.join)
result.push([_key, _value].join("=")) result.push([_key, _value].join("="))
......
...@@ -292,7 +292,18 @@ module.exports = (function() { ...@@ -292,7 +292,18 @@ module.exports = (function() {
attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull) attrValueHash = Utils.removeNullValuesFromHash(attrValueHash, this.options.omitNull)
var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;" var query = "INSERT INTO <%= table %> (<%= attributes %>) VALUES (<%= values %>) RETURNING *;"
, returning = removeSerialsFromHash(tableName, attrValueHash)
// Remove serials that are null or undefined, which causes an error in PG
Utils._.forEach(attrValueHash, function(value, key, hash) {
if (tables[tableName]) {
switch (tables[tableName][key]) {
case 'bigserial':
case 'serial':
if ([null, undefined].indexOf(hash[key]) !== -1) delete hash[key]
break
}
}
});
var replacements = { var replacements = {
table: this.quoteIdentifiers(tableName) table: this.quoteIdentifiers(tableName)
......
...@@ -77,17 +77,21 @@ describe('QueryGenerator', function() { ...@@ -77,17 +77,21 @@ describe('QueryGenerator', function() {
}, },
{ {
arguments: ['myTable', {title: "INTEGER COMMENT 'I\\'m a comment!'"}], arguments: ['myTable', {title: "INTEGER COMMENT 'I\\'m a comment!'"}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER COMMENT 'I\\'m a comment!') ENGINE=InnoDB;", expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER COMMENT 'I\\'m a comment!') ENGINE=InnoDB;"
}, },
{ {
arguments: ['myTable', {title: "INTEGER"}, {comment: "I'm a comment!"}], arguments: ['myTable', {title: "INTEGER"}, {comment: "I'm a comment!"}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER) COMMENT 'I\\'m a comment!' ENGINE=InnoDB;", expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` INTEGER) COMMENT 'I\\'m a comment!' ENGINE=InnoDB;"
}, },
{ {
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {engine: 'MyISAM'}], arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {engine: 'MyISAM'}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=MyISAM;" expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=MyISAM;"
}, },
{ {
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {charset: 'utf8', collate: 'utf8_unicode_ci'}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;"
},
{
arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {charset: 'latin1'}], arguments: ['myTable', {title: 'VARCHAR(255)', name: 'VARCHAR(255)'}, {charset: 'latin1'}],
expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=latin1;" expectation: "CREATE TABLE IF NOT EXISTS `myTable` (`title` VARCHAR(255), `name` VARCHAR(255)) ENGINE=InnoDB DEFAULT CHARSET=latin1;"
}, },
......
...@@ -3,6 +3,7 @@ if (typeof require === 'function') { ...@@ -3,6 +3,7 @@ if (typeof require === 'function') {
, Helpers = require('../buster-helpers') , Helpers = require('../buster-helpers')
, Sequelize = require('../../index') , Sequelize = require('../../index')
, dialect = Helpers.getTestDialect() , dialect = Helpers.getTestDialect()
, _ = require('lodash')
} }
buster.spec.expose() buster.spec.expose()
...@@ -32,8 +33,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -32,8 +33,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
it('does not have any labels assigned to it initially', function(done) { it('does not have any labels assigned to it initially', function(done) {
var self = this
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }), this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }), this.Label.create({ text: 'Awesomeness' }),
...@@ -55,8 +54,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -55,8 +54,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
it('answers true if the label has been assigned', function(done) { it('answers true if the label has been assigned', function(done) {
var self = this
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }), this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }), this.Label.create({ text: 'Awesomeness' }),
...@@ -90,8 +87,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -90,8 +87,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
it('answers false if only some labels have been assigned', function(done) { it('answers false if only some labels have been assigned', function(done) {
var self = this
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }), this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }), this.Label.create({ text: 'Awesomeness' }),
...@@ -109,8 +104,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -109,8 +104,6 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
it('answers true if all label have been assigned', function(done) { it('answers true if all label have been assigned', function(done) {
var self = this
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
this.Article.create({ title: 'Article' }), this.Article.create({ title: 'Article' }),
this.Label.create({ text: 'Awesomeness' }), this.Label.create({ text: 'Awesomeness' }),
...@@ -186,7 +179,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -186,7 +179,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
describe("getting assocations with options", function() { describe("getting assocations with options", function() {
before(function(done) { before(function(done) {
var self = this; var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING }) this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN }) this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
...@@ -194,7 +187,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -194,7 +187,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.hasMany(self.Task) this.User.hasMany(self.Task)
this.sequelize.sync({ force: true }).done(function() { this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}), self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}), self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false}) self.Task.create({ title: 'Die trying', active: false})
...@@ -210,7 +203,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -210,7 +203,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.find({where: {username: 'John'}}).success(function (john) { this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) { john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2) expect(tasks.length).toEqual(2)
done(); done()
}) })
}) })
}) })
...@@ -219,17 +212,66 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -219,17 +212,66 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.find({ where: { username: 'John' } }).success(function (john) { this.User.find({ where: { username: 'John' } }).success(function (john) {
john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' }).success(function (tasks) { john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' }).success(function (tasks) {
expect(tasks.length).toEqual(1) expect(tasks.length).toEqual(1)
done(); done()
}) })
}) })
}) })
}) })
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.sequelize.sync({force: true}).success(done)
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECt, only UPDAET
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one UPDATE statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) { // We don't care about SELECT, only UPDATE
expect(sql).toMatch("UPDATE")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for UPDATE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
}) })
describe('(N:M)', function() { describe('(N:M)', function() {
describe("getting assocations with options", function() { describe("getting assocations with options", function() {
before(function(done) { before(function(done) {
var self = this; var self = this
this.User = this.sequelize.define('User', { username: Sequelize.STRING }) this.User = this.sequelize.define('User', { username: Sequelize.STRING })
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN }) this.Task = this.sequelize.define('Task', { title: Sequelize.STRING, active: Sequelize.BOOLEAN })
...@@ -238,7 +280,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -238,7 +280,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
self.Task.hasMany(self.User) self.Task.hasMany(self.User)
this.sequelize.sync({ force: true }).done(function() { this.sequelize.sync({ force: true }).done(function() {
var chainer = new Sequelize.Utils.QueryChainer([ var chainer = new Sequelize.Utils.QueryChainer([
self.User.create({ username: 'John'}), self.User.create({ username: 'John'}),
self.Task.create({ title: 'Get rich', active: true}), self.Task.create({ title: 'Get rich', active: true}),
self.Task.create({ title: 'Die trying', active: false}) self.Task.create({ title: 'Die trying', active: false})
...@@ -254,7 +296,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -254,7 +296,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.find({where: {username: 'John'}}).success(function (john) { this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks().success(function (tasks) { john.getTasks().success(function (tasks) {
expect(tasks.length).toEqual(2) expect(tasks.length).toEqual(2)
done(); done()
}) })
}) })
}) })
...@@ -263,7 +305,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -263,7 +305,7 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
this.User.find({where: {username: 'John'}}).success(function (john) { this.User.find({where: {username: 'John'}}).success(function (john) {
john.getTasks({where: {active: true}}).success(function (tasks) { john.getTasks({where: {active: true}}).success(function (tasks) {
expect(tasks.length).toEqual(1) expect(tasks.length).toEqual(1)
done(); done()
}) })
}) })
}) })
...@@ -323,6 +365,56 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -323,6 +365,56 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
describe('optimizations using bulk create, destroy and update', function () {
before(function (done) {
this.User = this.sequelize.define('User', { username: Sequelize.STRING }, {timestamps: false})
this.Task = this.sequelize.define('Task', { title: Sequelize.STRING }, {timestamps: false})
this.User.hasMany(this.Task)
this.Task.hasMany(this.User)
this.sequelize.sync({force: true}).success(done)
})
it('uses one insert into statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function(user) {
this.Task.create({ title: 'task1' }).success(function(task1) {
this.Task.create({ title: 'task2' }).success(function(task2) {
user.setTasks([task1, task2]).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("INSERT INTO")
expect(sql).toMatch("VALUES (1,1),(2,1)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for INSERT into
done()
})
}.bind(this))
}.bind(this))
}.bind(this))
})
it('uses one delete from statement', function (done) {
var spy = this.spy()
this.User.create({ username: 'foo' }).success(function (user) {
this.Task.create({ title: 'task1' }).success(function (task1) {
this.Task.create({ title: 'task2' }).success(function (task2) {
user.setTasks([task1, task2]).success(function () {
user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).toMatch("DELETE FROM")
expect(sql).toMatch("IN (1,2)")
})).success(function () {
expect(spy).toHaveBeenCalledTwice() // Once for SELECT, once for DELETE
done()
})
})
}.bind(this))
}.bind(this))
}.bind(this))
})
}) // end optimization using bulk create, destroy and update
}) })
describe("Foreign key constraints", function() { describe("Foreign key constraints", function() {
...@@ -480,5 +572,4 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() { ...@@ -480,5 +572,4 @@ describe(Helpers.getTestDialectTeaser("HasMany"), function() {
}) })
}) })
}) })
})
}) \ No newline at end of file
...@@ -518,6 +518,17 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -518,6 +518,17 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
}) })
it('allows setting custom IDs', function (done) {
this.User.create({ id: 42 }).success(function (user) {
expect(user.id).toEqual(42)
this.User.find(42).success(function (user) {
expect(user).toBeDefined()
done()
})
}.bind(this))
})
describe('enums', function() { describe('enums', function() {
before(function(done) { before(function(done) {
this.Item = this.sequelize.define('Item', { this.Item = this.sequelize.define('Item', {
...@@ -849,6 +860,22 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -849,6 +860,22 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) })
}) })
it("should not try to convert boolean values if they are not selected", function (done) {
var UserWithBoolean = this.sequelize.define('user', {
active: Sequelize.BOOLEAN
})
this.sequelize.sync({force: true}).success(function () {
UserWithBoolean.create({ active: true }).success(function (user) {
UserWithBoolean.find({ where: { id: user.id }, attributes: [ 'id' ] }).success(function (user) {
expect(user.active).not.toBeDefined()
done()
})
})
})
})
it('finds a specific user via where option', function(done) { it('finds a specific user via where option', function(done) {
this.User.find({ where: { username: 'barfooz' } }).success(function(user) { this.User.find({ where: { username: 'barfooz' } }).success(function(user) {
expect(user.username).toEqual('barfooz') expect(user.username).toEqual('barfooz')
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!