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

Commit c266b9a5 by Jan Aagaard Meier

Merge master

2 parents 6b7c80fe 0ff2d0ac
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
- [FEATURE] Keep milliseconds in timestamps for postgres. [#712](https://github.com/sequelize/sequelize/pull/712). Thanks to seth-admittedly - [FEATURE] Keep milliseconds in timestamps for postgres. [#712](https://github.com/sequelize/sequelize/pull/712). Thanks to seth-admittedly
- [FEATURE] You can now set lingo's language through Sequelize. [#713](https://github.com/sequelize/sequelize/pull/713). Thanks to durango - [FEATURE] You can now set lingo's language through Sequelize. [#713](https://github.com/sequelize/sequelize/pull/713). Thanks to durango
- [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen - [FEATURE] Added a `findAndCountAll`, useful for pagination. [#533](https://github.com/sequelize/sequelize/pull/533). Thanks to iamjochen
- [FEATURE] Made explicit migrations possible. [#728](https://github.com/sequelize/sequelize/pull/728). Thanks to freezy
- [FEATURE] Added support for where clauses containing !=, < etc. and support for date ranges [#727](https://github.com/sequelize/sequelize/pull/727). Thanks to durango
- [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 - [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
- [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier - [REFACTORING] Consistent handling of offset across dialects. Offset is now always applied, and limit is set to max table size of not limit is given [#725](https://github.com/sequelize/sequelize/pull/725). janmeier
......
...@@ -437,11 +437,24 @@ module.exports = (function() { ...@@ -437,11 +437,24 @@ 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? if (!!value.join) {
//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("=")) } else {
for (var logic in value) {
var logicResult = this.getWhereLogic(logic)
if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") {
_value = this.escape(value[logic][0])
var _value2 = this.escape(value[logic][1])
result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ')
} else {
_value = this.escape(value[logic])
result.push([_key, _value].join(' ' + logicResult + ' '))
}
}
}
} else { } else {
_value = this.escape(value) _value = this.escape(value)
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
...@@ -451,6 +464,33 @@ module.exports = (function() { ...@@ -451,6 +464,33 @@ module.exports = (function() {
return result.join(" AND ") return result.join(" AND ")
}, },
getWhereLogic: function(logic) {
switch (logic) {
case 'gte':
return '>='
break
case 'gt':
return '>'
break
case 'lte':
return '<='
break
case 'lt':
return '<'
break
case 'ne':
return '!='
break
case 'between':
return 'BETWEEN'
break
case 'nbetween':
case 'notbetween':
return 'NOT BETWEEN'
break
}
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
......
...@@ -10,6 +10,10 @@ module.exports = (function() { ...@@ -10,6 +10,10 @@ module.exports = (function() {
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0)) this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
this.pg = this.config.native ? require('pg').native : require('pg') this.pg = this.config.native ? require('pg').native : require('pg')
// Better support for BigInts
// https://github.com/brianc/node-postgres/issues/166#issuecomment-9514935
this.pg.types.setTypeParser(20, String);
// set pooling parameters if specified // set pooling parameters if specified
if (this.pooling) { if (this.pooling) {
this.pg.defaults.poolSize = this.config.poolCfg.maxConnections this.pg.defaults.poolSize = this.config.poolCfg.maxConnections
......
...@@ -521,9 +521,24 @@ module.exports = (function() { ...@@ -521,9 +521,24 @@ module.exports = (function() {
result.push([_key, _value].join(" IN ")) result.push([_key, _value].join(" IN "))
} }
else if ((value) && (typeof value === "object")) { else if ((value) && (typeof value === "object")) {
//using as sentinel for join column => value if (!!value.join) {
_value = this.quoteIdentifiers(value.join) //using as sentinel for join column => value
result.push([_key, _value].join("=")) _value = this.quoteIdentifiers(value.join)
result.push([_key, _value].join("="))
} else {
for (var logic in value) {
var logicResult = this.getWhereLogic(logic)
if (logicResult === "BETWEEN" || logicResult === "NOT BETWEEN") {
_value = this.escape(value[logic][0])
var _value2 = this.escape(value[logic][1])
result.push(' (' + _key + ' ' + logicResult + ' ' + _value + ' AND ' + _value2 + ') ')
} else {
_value = this.escape(value[logic])
result.push([_key, _value].join(' ' + logicResult + ' '))
}
}
}
} else { } else {
_value = this.escape(value) _value = this.escape(value)
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("=")) result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
...@@ -533,6 +548,33 @@ module.exports = (function() { ...@@ -533,6 +548,33 @@ module.exports = (function() {
return result.join(' AND ') return result.join(' AND ')
}, },
getWhereLogic: function(logic) {
switch (logic) {
case 'gte':
return '>='
break
case 'gt':
return '>'
break
case 'lte':
return '<='
break
case 'lt':
return '<'
break
case 'ne':
return '!='
break
case 'between':
return 'BETWEEN'
break
case 'nbetween':
case 'notbetween':
return 'NOT BETWEEN'
break
}
},
attributesToSQL: function(attributes) { attributesToSQL: function(attributes) {
var result = {} var result = {}
......
var moment = require("moment") var moment = require("moment")
, path = require("path")
, Utils = require("./utils") , Utils = require("./utils")
, DataTypes = require("./data-types") , DataTypes = require("./data-types")
, QueryInterface = require("./query-interface") , QueryInterface = require("./query-interface")
module.exports = (function() { module.exports = (function() {
var Migration = function(migrator, path) { var Migration = function(migrator, p) {
this.migrator = migrator this.migrator = migrator
this.path = path this.path = path.normalize(p)
this.filename = Utils._.last(this.path.split('/')) this.filename = Utils._.last(this.path.split(path.sep))
var parsed = Migration.parseFilename(this.filename) var parsed = Migration.parseFilename(this.filename)
......
...@@ -177,6 +177,56 @@ module.exports = (function() { ...@@ -177,6 +177,56 @@ module.exports = (function() {
}).run() }).run()
} }
/**
* Explicitly executes one or multiple migrations.
*
* @param filename {String|Array} Absolute filename(s) of the migrations script
* @param options {Object} Can contain three functions, before, after and success, which are executed before
* or after each migration respectively, with one parameter, the migration.
*/
Migrator.prototype.exec = function(filename, options) {
var self = this;
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer;
var addMigration = function(filename) {
self.options.logging('Adding migration script at ' + filename)
var migration = new Migration(self, filename)
chainer.add(migration, 'execute', [{ method: 'up' }], {
before: function(migration) {
if (options && Utils._.isFunction(options.before)) {
options.before.call(self, migration);
}
},
after: function(migration) {
if (options && Utils._.isFunction(options.after)) {
options.after.call(self, migration);
}
},
success: function(migration, callback) {
if (options && Utils._.isFunction(options.success)) {
options.success.call(self, migration);
}
callback();
}
})
}
if (Utils._.isArray(filename)) {
Utils._.each(filename, function(f) {
addMigration(f);
})
} else {
addMigration(filename);
}
chainer
.runSerially({ skipOnError: true })
.success(function() { emitter.emit('success', null) })
.error(function(err) { emitter.emit('error', err) })
}).run()
}
// private // private
var getLastMigrationFromDatabase = function() { var getLastMigrationFromDatabase = function() {
......
...@@ -102,7 +102,11 @@ module.exports = (function() { ...@@ -102,7 +102,11 @@ module.exports = (function() {
maxConcurrentQueries: this.options.maxConcurrentQueries maxConcurrentQueries: this.options.maxConcurrentQueries
} }
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager") try {
var ConnectorManager = require("./dialects/" + this.options.dialect + "/connector-manager")
} catch(err) {
throw new Error("The dialect " + this.options.dialect + " is not supported.")
}
this.daoFactoryManager = new DAOFactoryManager(this) this.daoFactoryManager = new DAOFactoryManager(this)
this.connectorManager = new ConnectorManager(this, this.config) this.connectorManager = new ConnectorManager(this, this.config)
......
...@@ -50,7 +50,8 @@ ...@@ -50,7 +50,8 @@
"pg": "~2.0.0", "pg": "~2.0.0",
"buster": "~0.6.3", "buster": "~0.6.3",
"watchr": "~2.2.0", "watchr": "~2.2.0",
"yuidocjs": "~0.3.36" "yuidocjs": "~0.3.36",
"semver": "~2.0.8"
}, },
"keywords": [ "keywords": [
"mysql", "mysql",
......
if(typeof require === 'function') { if(typeof require === 'function') {
const buster = require("buster") const buster = require("buster")
, semver = require("semver")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter") , CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers') , Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config") , config = require(__dirname + "/config/config")
...@@ -10,27 +11,22 @@ buster.spec.expose() ...@@ -10,27 +11,22 @@ buster.spec.expose()
buster.testRunner.timeout = 1000 buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index') var Sequelize = require(__dirname + '/../index')
, noDomains = semver.lt(process.version, '0.8.0')
describe(Helpers.getTestDialectTeaser("Configuration"), function() { describe(Helpers.getTestDialectTeaser("Configuration"), function() {
describe('Connections problems should fail with a nice message', function() { describe('Connections problems should fail with a nice message', function() {
it('should give us an error for not having the correct server details', function(done) { it('when we don\'t have the correct server details', function(done) {
var domain if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster! expect('').toEqual('') // Silence Buster!
done() return done()
} }
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect}) var sequelize = new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {storage: '/path/to/no/where/land', logging: false, host: '0.0.0.1', port: config[dialect].port, dialect: dialect})
d.add(sequelize.query) , domain = require('domain')
, d = domain.create()
d.on('error', function(err){ d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to find SQL server. Please double check your settings.' var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") { if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.' msg = 'Failed to find PostgresSQL server. Please double check your settings.'
...@@ -40,39 +36,35 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() { ...@@ -40,39 +36,35 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() {
} }
expect(err.message).toEqual(msg) expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done() done()
}) })
d.run(function(){ d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello') sequelize.query('select 1 as hello')
.success(function(){}) .success(function(){})
}) })
}) })
it('should give us an error for not having the correct login information', function(done) { it('when we don\'t have the correct login information', function(done) {
if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") { if (dialect !== "postgres" && dialect !== "postgres-native" && dialect !== "mysql") {
// This dialect doesn't support incorrect login information console.log('This dialect doesn\'t support me :(')
expect('').toEqual('') // Silence Buster expect('').toEqual('') // Silence Buster
return done() return done()
} }
var domain if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
expect('').toEqual('') // Silence Buster! expect('').toEqual('') // Silence Buster!
done() return done()
} }
var d = domain.create()
var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect}) var sequelize = new Sequelize(config[dialect].database, config[dialect].username, 'fakepass123', {logging: false, host: config[dialect].host, port: 1, dialect: dialect})
d.add(sequelize.query) , domain = require('domain')
, d = domain.create()
d.on('error', function(err){ d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to authenticate for SQL. Please double check your settings.' var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") { if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.' msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
...@@ -82,14 +74,22 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() { ...@@ -82,14 +74,22 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() {
} }
expect(err.message).toEqual(msg) expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done() done()
}) })
d.run(function(){ d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello') sequelize.query('select 1 as hello')
.success(function(){}) .success(function(){})
}) })
}) })
it('when we don\'t have a valid dialect.', function() {
Helpers.assertException(function() {
new Sequelize(config[dialect].database, config[dialect].username, config[dialect].password, {host: '0.0.0.1', port: config[dialect].port, dialect: undefined})
}.bind(this), 'The dialect undefined is not supported.')
})
}) })
describe('Instantiation with a URL string', function() { describe('Instantiation with a URL string', function() {
......
...@@ -18,7 +18,9 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -18,7 +18,9 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
this.User = sequelize.define('User', { this.User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
secretValue: DataTypes.STRING, secretValue: DataTypes.STRING,
data: DataTypes.STRING data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
}) })
}.bind(this), }.bind(this),
onComplete: function() { onComplete: function() {
...@@ -807,7 +809,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -807,7 +809,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
, User = this.sequelize.define('ParanoidUser', { , User = this.sequelize.define('ParanoidUser', {
username: Sequelize.STRING, username: Sequelize.STRING,
secretValue: Sequelize.STRING, secretValue: Sequelize.STRING,
data: Sequelize.STRING data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, { }, {
paranoid: true paranoid: true
}) })
...@@ -842,6 +845,216 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() { ...@@ -842,6 +845,216 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) // - destroy }) // - destroy
describe('special where conditions', function() {
before(function(done) {
var self = this
this.User.create({
username: 'boo',
intVal: 5,
theDate: '2013-01-01 12:00'
}).success(function(user){
self.user = user
self.User.create({
username: 'boo2',
intVal: 10,
theDate: '2013-01-10 12:00'
}).success(function(user2){
self.user2 = user2
done()
})
})
})
it('should be able to find a row between a certain date', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row between a certain date and an additional where clause', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2013-01-02', '2013-01-11']
},
intVal: 10
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row not between a certain integer', function(done) {
this.User.findAll({
where: {
intVal: {
nbetween: [8, 10]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic', function(done) {
this.User.findAll({
where: {
theDate: {
between: ['2012-12-10', '2013-01-02'],
nbetween: ['2013-01-04', '2013-01-20']
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not between and between logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
between: [new Date('2012-12-10'), new Date('2013-01-02')],
nbetween: [new Date('2013-01-04'), new Date('2013-01-20')]
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using greater than or equal to logic with dates', function(done) {
this.User.findAll({
where: {
theDate: {
gte: new Date('2013-01-09')
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo2')
expect(users[0].intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than or equal to', function(done) {
this.User.find({
where: {
intVal: {
gte: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using greater than', function(done) {
this.User.find({
where: {
intVal: {
gt: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo2')
expect(user.intVal).toEqual(10)
done()
})
})
it('should be able to find a row using lesser than or equal to', function(done) {
this.User.find({
where: {
intVal: {
lte: 5
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find a row using lesser than', function(done) {
this.User.find({
where: {
intVal: {
lt: 6
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should have no problem finding a row using lesser and greater than', function(done) {
this.User.findAll({
where: {
intVal: {
lt: 6,
gt: 4
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
done()
})
})
it('should be able to find a row using not equal to logic', function(done) {
this.User.find({
where: {
intVal: {
ne: 10
}
}
}).success(function(user) {
expect(user.username).toEqual('boo')
expect(user.intVal).toEqual(5)
done()
})
})
it('should be able to find multiple users with any of the special where logic properties', function(done) {
this.User.findAll({
where: {
intVal: {
lte: 10
}
}
}).success(function(users) {
expect(users[0].username).toEqual('boo')
expect(users[0].intVal).toEqual(5)
expect(users[1].username).toEqual('boo2')
expect(users[1].intVal).toEqual(10)
done()
})
})
})
describe('find', function find() { describe('find', function find() {
before(function(done) { before(function(done) {
this.User.create({ this.User.create({
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!