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

Commit 2ff6c4de by iuri aranda

Merge branch 'master' into hotfix/query_hangup

2 parents 94aa2acd cf7aa753
......@@ -4,7 +4,7 @@ MySQL, MariaDB, PostgresSQL, and SQLite Object Relational Mapper (ORM) for [node
### Install
To install 1.x.x (currently 1.7.x) - which has a stable API and is backwards compatible:
To install 1.x.x (currently 1.7.x) - which has a stable API and is mostly backwards compatible:
`npm install sequelize`
......@@ -26,7 +26,7 @@ To install 2.x.x branch - which has a unstable API and will break backwards comp
### 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
and will get all the changes of the master. However, `2.0.0` will contain major backwards compatibility breaking changes. Check the
changelog of the branch: https://github.com/sequelize/sequelize/blob/milestones/2.0.0/changelog.md
##### 2.0.0 API should be considered unstable
......
......@@ -166,7 +166,7 @@ if(typeof program.env === 'string') {
configuration.environment = program.env
}
if (program.migrate) {
if (program.migrate || program.undo) {
if (configFileExists() || program.url) {
var config
, options = {}
......
......@@ -4,6 +4,11 @@ Notice: All 1.7.x changed are present in 2.0.x aswell
- instance.createAssociationInstance functionality added [#1213](https://github.com/sequelize/sequelize/pull/1213)
- fixes a few bugs with transactions in regards to associations
- add error handling for transaction creation
- `sequelize --undo` will now actually undo migrations. Its basically an alias for `sequelize --migrate --undo`. [#1059](https://github.com/sequelize/sequelize/pull/1059)
- fix bug where `{where: {ne: null}}` would result in `!= NULL` instead of `IS NOT NULL` [#1231](https://github.com/sequelize/sequelize/pull/1059)
- fixes a bug with validation skipping using the `fields` options. [#1233](https://github.com/sequelize/sequelize/pull/1233)
- fixes a bug with postgres and setters [#1234](https://github.com/sequelize/sequelize/issues/1234)
- fixes it so `field: {type: Sequelize.ENUM(value1, value2)}` works
#### Backwards compatability changes
- Hooks are no longer passing value hashes. Instead, they are now passing instances of the model.
......@@ -22,7 +27,7 @@ Notice: All 1.7.x changed are present in 2.0.x aswell
- Eager loading / prefetching now supports inner joins and extending the ON statement [#1199](https://github.com/sequelize/sequelize/pull/1199)
- Eager loading / prefetching now returns the attributes of through models aswell [#1198](https://github.com/sequelize/sequelize/pull/1198)
- New set/get/changed/previous feature [#1182](https://github.com/sequelize/sequelize/pull/1182)
- Various bug fixes
- Various bug fixes
#### Backwards compatibility changes
None
......
......@@ -870,9 +870,14 @@ module.exports = (function() {
options.fields = newFields || options.fields
if (options.validate === true) {
if (options.fields.length) {
var skippedFields = Utils._.difference(Object.keys(self.attributes), options.fields);
}
if (options.hooks === true) {
var iterate = function(i) {
daos[i].hookValidate({skip: options.fields}).error(function(err) {
daos[i].hookValidate({skip: skippedFields}).error(function(err) {
errors[errors.length] = {record: v, errors: err}
i++
if (i > daos.length) {
......@@ -887,7 +892,7 @@ module.exports = (function() {
}
} else {
daos.forEach(function(v) {
var valid = v.validate({skip: options.fields})
var valid = v.validate({skip: skippedFields})
if (valid !== null) {
errors[errors.length] = {record: v, errors: valid}
}
......@@ -1145,7 +1150,12 @@ module.exports = (function() {
if (options.validate === true) {
var build = self.build(attrValueHash)
build.hookValidate({skip: Object.keys(attrValueHash)}).error(function(err) {
// We want to skip validations for all other fields
var updatedFields = Object.keys(attrValueHash)
var skippedFields = Utils._.difference(Object.keys(self.attributes), updatedFields)
build.hookValidate({skip: skippedFields}).error(function(err) {
emitter.emit('error', err)
}).success(function(attributes) {
if (!!attributes && !!attributes.dataValues) {
......
......@@ -71,7 +71,7 @@ var validateAttributes = function() {
Utils._.each(this.model.rawAttributes, function(rawAttribute, field) {
var value = self.model.dataValues[field] || undefined
, hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) === -1
, isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) !== -1
if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) {
errors = Utils._.merge(errors, validateAttribute.call(self, value, field))
......
......@@ -254,10 +254,11 @@ module.exports = {
get ENUM() {
var result = function() {
return {
type: 'ENUM',
type: 'ENUM',
values: Array.prototype.slice.call(arguments).reduce(function(result, element) {
return result.concat(Array.isArray(element) ? element : [ element ])
}, [])
}, []),
toString: result.toString
}
}
......
......@@ -5,7 +5,7 @@ var MariaDialect = function(sequelize) {
this.sequelize = sequelize
}
MariaDialect.prototype = _.extend({
MariaDialect.prototype = _.defaults({
}, MySQL.prototype)
......
......@@ -5,8 +5,8 @@ var MysqlDialect = function(sequelize) {
this.sequelize = sequelize
}
MysqlDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
MysqlDialect.prototype.supports = _.defaults({
'VALUES ()': true
})
}, Abstract.prototype.supports)
module.exports = MysqlDialect
......@@ -5,9 +5,9 @@ var PostgresDialect = function(sequelize) {
this.sequelize = sequelize
}
PostgresDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
PostgresDialect.prototype.supports = _.defaults({
'RETURNING': true,
'DEFAULT VALUES': true
})
}, Abstract.prototype.supports)
module.exports = PostgresDialect
......@@ -139,7 +139,7 @@ module.exports = (function() {
if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = hstore.parse(record)
}
this.callee[key] = record
this.callee.dataValues[key] = record
}
}
}
......@@ -152,7 +152,7 @@ module.exports = (function() {
if (!!this.callee.daoFactory && !!this.callee.daoFactory.rawAttributes && !!this.callee.daoFactory.rawAttributes[key] && !!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = hstore.parse(record)
}
this.callee[key] = record
this.callee.dataValues[key] = record
}
}
}
......
......@@ -5,9 +5,9 @@ var SqliteDialect = function(sequelize) {
this.sequelize = sequelize
}
SqliteDialect.prototype.supports = _.extend(Abstract.prototype.supports, {
SqliteDialect.prototype.supports = _.defaults({
'DEFAULT': false,
'DEFAULT VALUES': true
})
}, Abstract.prototype.supports)
module.exports = SqliteDialect
......@@ -28,6 +28,25 @@ module.exports = (function() {
return this
}
CustomEventEmitter.prototype.emit = function(type) {
this._events = this._events || {};
// Override default 'error' event logic
if (type === 'error' && !this._events.error) {
// No error listener
var er = arguments[1];
// If error argument is an array, make sure we
// pass only the first error to the original
// .emit() function of EventEmitter
if (er instanceof Array) {
arguments[1] = Utils._.flatten(er)[0]
}
}
EventEmitter.prototype.emit.apply(this, arguments);
};
/**
Shortcut methods (success, ok) for listening for success events.
......
......@@ -203,6 +203,13 @@ module.exports = (function() {
// If you don't specify a valid data type lets help you debug it
Utils._.each(attributes, function(dataType, name) {
if (Utils.isHash(dataType)) {
// We have special cases where the type is an object containing
// the values (e.g. Sequelize.ENUM(value, value2) returns an object
// instead of a function)
// Copy these values to the dataType
dataType.values = (dataType.type && dataType.type.values) || dataType.values;
// We keep on working with the actual type object
dataType = dataType.type
}
......
......@@ -299,7 +299,7 @@ var Utils = module.exports = {
case 'eq':
return '='
case 'ne':
return val ? '!=' : 'IS NOT'
return val === null ? 'IS NOT' : '!='
case 'between':
case '..':
return 'BETWEEN'
......@@ -451,7 +451,7 @@ var Utils = module.exports = {
for (var key in hash) {
if (key instanceof Utils.literal) {
_hash[key] = hash[key]
_hash[key] = hash[key]
} else if (key.indexOf('.') === -1) {
_hash[tableName + '.' + key] = hash[key]
} else {
......
{
"name": "sequelize",
"description": "Multi dialect ORM for Node.JS",
"version": "1.7.0-beta8",
"version": "1.7.0-rc1",
"author": "Sascha Depold <sascha@depold.com>",
"contributors": [
{
......
......@@ -597,7 +597,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
......@@ -608,7 +608,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow truly blank creates', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function(err) {
Worker.create({}, {fields: []}).done(function (err, worker) {
expect(err).not.to.be.ok
expect(worker).to.be.ok
......@@ -651,6 +651,62 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
describe('when defined via { field: Sequelize.ENUM }', function() {
it('allows values passed as parameters', function(done) {
var Enum = this.sequelize.define('Enum', {
state: Sequelize.ENUM('happy', 'sad')
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
done()
});
});
})
it('allows values passed as an array', function(done) {
var Enum = this.sequelize.define('Enum', {
state: Sequelize.ENUM(['happy', 'sad'])
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
done()
});
});
})
})
describe('when defined via { field: { type: Sequelize.ENUM } }', function() {
it('allows values passed as parameters', function(done) {
var Enum = this.sequelize.define('Enum', {
state: {
type: Sequelize.ENUM('happy', 'sad')
}
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
done()
});
});
})
it('allows values passed as an array', function(done) {
var Enum = this.sequelize.define('Enum', {
state: {
type: Sequelize.ENUM(['happy', 'sad'])
}
})
Enum.sync({ force: true }).success(function() {
Enum.create({ state: 'happy' }).success(function(_item) {
done()
});
});
})
})
describe('can safely sync multiple times', function(done) {
it('through the factory', function(done) {
var Enum = this.sequelize.define('Enum', {
......@@ -896,7 +952,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank arrays (return immediatly)', function (done) {
var Worker = this.sequelize.define('Worker', {})
Worker.sync().done(function(err) {
Worker.sync().done(function(err) {
Worker.bulkCreate([]).done(function (err, workers) {
expect(err).not.to.be.ok
expect(workers).to.be.ok
......@@ -908,7 +964,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow blank creates (with timestamps: false)', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function(err) {
Worker.bulkCreate([{}, {}]).done(function (err, workers) {
expect(err).not.to.be.ok
expect(workers).to.be.ok
......@@ -919,7 +975,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it('should allow autoincremented attributes to be set', function (done) {
var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
Worker.sync().done(function(err) {
Worker.sync().done(function(err) {
Worker.bulkCreate([
{id: 5},
{id: 10}
......@@ -953,4 +1009,4 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
})
\ No newline at end of file
})
......@@ -562,5 +562,24 @@ describe(Support.getTestDialectTeaser("DaoValidator"), function() {
expect(errors.field).to.have.length(1)
expect(errors.field[0]).to.equal("Unexpected value or invalid argument: field")
})
it('skips validations for the given fields', function() {
var values = ['value1', 'value2']
var Bar = this.sequelize.define('Bar' + config.rand(), {
field: {
type: Sequelize.ENUM,
values: values,
validate: {
isIn: [values]
}
}
})
var failingBar = Bar.build({ field: 'value3' })
, errors = failingBar.validate({ skip: ['field'] })
expect(errors).to.be.null
})
})
})
......@@ -193,6 +193,40 @@ describe(Support.getTestDialectTeaser("DAO"), function () {
})
expect(product.toJSON()).to.deep.equal({withTaxes: 1250, price: 1000, id: null})
})
it('should work with save', function (done) {
var Contact = this.sequelize.define('Contact', {
first: { type: Sequelize.STRING },
last: { type: Sequelize.STRING },
tags: {
type: Sequelize.STRING,
get: function(field) {
var val = this.getDataValue(field);
return JSON.parse(val);
},
set: function(val, field) {
this.setDataValue(field, JSON.stringify(val));
}
}
});
this.sequelize.sync().done(function () {
var contact = Contact.build({
first: 'My',
last: 'Name',
tags: ['yes','no']
});
expect(contact.get('tags')).to.deep.equal(['yes', 'no'])
contact.save().done(function(err, me) {
expect(err).not.to.be.ok
var idToTest = me.id;
expect(me.get('tags')).to.deep.equal(['yes', 'no'])
done();
});
});
})
})
describe('changed', function () {
......
......@@ -60,4 +60,46 @@ describe(Support.getTestDialectTeaser("CustomEventEmitter"), function () {
proxy.emit('success')
})
})
describe("when emitting an error event with an array of errors", function() {
describe("if no error handler is given", function() {
it("should throw the first error", function(done) {
var emitter = new CustomEventEmitter()
expect(function () {
emitter.emit("error", [
[
new Error("First error"),
new Error("Second error")
], [
new Error("Third error")
]
])
}).to.throw("First error")
done()
})
})
describe("if an error handler is given", function() {
it("should return the whole array", function(done) {
var emitter = new CustomEventEmitter()
var errors = [
[
new Error("First error"),
new Error("Second error")
], [
new Error("Third error")
]
]
emitter.error(function (err) {
expect(err).to.equal(errors)
done()
})
emitter.emit("error", errors)
})
})
})
})
......@@ -273,6 +273,16 @@ if (Support.dialectIsMySQL()) {
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` != 0;",
context: QueryGenerator
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` IS NOT NULL;",
context: QueryGenerator
}
],
......
......@@ -268,8 +268,8 @@ if (dialect.match(/^postgres/)) {
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2("id")) DESC;',
......@@ -280,7 +280,7 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
......@@ -410,6 +410,16 @@ if (dialect.match(/^postgres/)) {
arguments: ['mySchema.myTable', {where: {name: "foo';DROP TABLE mySchema.myTable;"}}],
expectation: "SELECT * FROM mySchema.myTable WHERE mySchema.myTable.name='foo'';DROP TABLE mySchema.myTable;';",
context: {options: {quoteIdentifiers: false}}
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM myTable WHERE myTable.field != 0;",
context: {options: {quoteIdentifiers: false}}
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM myTable WHERE myTable.field IS NOT NULL;",
context: {options: {quoteIdentifiers: false}}
}
],
......
......@@ -305,7 +305,7 @@ describe(Support.getTestDialectTeaser("Executable"), function() {
})
})
})
})(['--migrate --undo', '-mu'])
})(['--migrate --undo', '-mu', '--undo', '-u'])
;(function(flags) {
flags.forEach(function(flag) {
......
......@@ -165,8 +165,8 @@ if (dialect === 'sqlite') {
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
......@@ -177,7 +177,7 @@ if (dialect === 'sqlite') {
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
......@@ -260,6 +260,16 @@ if (dialect === 'sqlite') {
arguments: ['myTable', {where: { field: new Buffer("Sequelize")}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field`=X'53657175656c697a65';",
context: QueryGenerator
}, {
title: 'use != if ne !== null',
arguments: ['myTable', {where: {field: {ne: 0}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` != 0;",
context: QueryGenerator
}, {
title: 'use IS NOT if ne === null',
arguments: ['myTable', {where: {field: {ne: null}}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`field` IS NOT NULL;",
context: QueryGenerator
}
],
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!