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

Commit c266b9a5 by Jan Aagaard Meier

Merge master

2 parents 6b7c80fe 0ff2d0ac
......@@ -34,6 +34,8 @@
- [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] 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] 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() {
result.push([_key, _value].join(" IN "))
} else if ((value) && (typeof value == 'object') && !(value instanceof Date)) {
// is value an object?
//using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join)
result.push([_key, _value].join("="))
if (!!value.join) {
//using as sentinel for join column => value
_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 {
_value = this.escape(value)
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
......@@ -451,6 +464,33 @@ module.exports = (function() {
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) {
var result = {}
......
......@@ -10,6 +10,10 @@ module.exports = (function() {
this.pooling = (!!this.config.poolCfg && (this.config.poolCfg.maxConnections > 0))
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
if (this.pooling) {
this.pg.defaults.poolSize = this.config.poolCfg.maxConnections
......
......@@ -521,9 +521,24 @@ module.exports = (function() {
result.push([_key, _value].join(" IN "))
}
else if ((value) && (typeof value === "object")) {
//using as sentinel for join column => value
_value = this.quoteIdentifiers(value.join)
result.push([_key, _value].join("="))
if (!!value.join) {
//using as sentinel for join column => value
_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 {
_value = this.escape(value)
result.push((_value == 'NULL') ? _key + " IS NULL" : [_key, _value].join("="))
......@@ -533,6 +548,33 @@ module.exports = (function() {
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) {
var result = {}
......
var moment = require("moment")
, path = require("path")
, Utils = require("./utils")
, DataTypes = require("./data-types")
, QueryInterface = require("./query-interface")
module.exports = (function() {
var Migration = function(migrator, path) {
var Migration = function(migrator, p) {
this.migrator = migrator
this.path = path
this.filename = Utils._.last(this.path.split('/'))
this.path = path.normalize(p)
this.filename = Utils._.last(this.path.split(path.sep))
var parsed = Migration.parseFilename(this.filename)
......
......@@ -177,6 +177,56 @@ module.exports = (function() {
}).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
var getLastMigrationFromDatabase = function() {
......
......@@ -102,7 +102,11 @@ module.exports = (function() {
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.connectorManager = new ConnectorManager(this, this.config)
......
......@@ -50,7 +50,8 @@
"pg": "~2.0.0",
"buster": "~0.6.3",
"watchr": "~2.2.0",
"yuidocjs": "~0.3.36"
"yuidocjs": "~0.3.36",
"semver": "~2.0.8"
},
"keywords": [
"mysql",
......
if(typeof require === 'function') {
const buster = require("buster")
, semver = require("semver")
, CustomEventEmitter = require("../lib/emitters/custom-event-emitter")
, Helpers = require('./buster-helpers')
, config = require(__dirname + "/config/config")
......@@ -10,27 +11,22 @@ buster.spec.expose()
buster.testRunner.timeout = 1000
var Sequelize = require(__dirname + '/../index')
, noDomains = semver.lt(process.version, '0.8.0')
describe(Helpers.getTestDialectTeaser("Configuration"), 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) {
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
it('when we don\'t have the correct server details', function(done) {
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
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})
d.add(sequelize.query)
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to find SQL server. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to find PostgresSQL server. Please double check your settings.'
......@@ -40,39 +36,35 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() {
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.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") {
// This dialect doesn't support incorrect login information
console.log('This dialect doesn\'t support me :(')
expect('').toEqual('') // Silence Buster
return done()
}
var domain
try {
domain = require('domain')
} catch (err) {
console.log('WARNING: Configuration specs requires Node version >= 0.8')
if (noDomains === true) {
console.log('WARNING: Configuration specs requires NodeJS version >= 0.8 for full compatibility')
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})
d.add(sequelize.query)
, domain = require('domain')
, d = domain.create()
d.on('error', function(err){
d.remove(sequelize.query)
var msg = 'Failed to authenticate for SQL. Please double check your settings.'
if (dialect === "postgres" || dialect === "postgres-native") {
msg = 'Failed to authenticate for PostgresSQL. Please double check your settings.'
......@@ -82,14 +74,22 @@ describe(Helpers.getTestDialectTeaser("Configuration"), function() {
}
expect(err.message).toEqual(msg)
d.remove(sequelize.query)
done()
})
d.run(function(){
d.add(sequelize.query)
sequelize.query('select 1 as hello')
.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() {
......
......@@ -18,7 +18,9 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
this.User = sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE
})
}.bind(this),
onComplete: function() {
......@@ -807,7 +809,8 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
, User = this.sequelize.define('ParanoidUser', {
username: Sequelize.STRING,
secretValue: Sequelize.STRING,
data: Sequelize.STRING
data: Sequelize.STRING,
intVal: { type: Sequelize.INTEGER, defaultValue: 1}
}, {
paranoid: true
})
......@@ -842,6 +845,216 @@ describe(Helpers.getTestDialectTeaser("DAOFactory"), function() {
}) // - 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() {
before(function(done) {
this.User.create({
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!