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

Commit 0c425fb4 by Mick Hansen

Merge pull request #1287 from sequelize/feature/or-queries

Support for OR queries
2 parents e1d048e2 8a96b618
......@@ -4,6 +4,7 @@ Notice: All 1.7.x changes are present in 2.0.x aswell
- dropAllTables now takes an option parameter with `skip` as an option [#1280](https://github.com/sequelize/sequelize/pull/1280)
- implements .spread for eventemitters [#1277](https://github.com/sequelize/sequelize/pull/1277)
- fixes some of the mysql connection error bugs [#1282](https://github.com/sequelize/sequelize/pull/1282)
- [Feature] Support for OR queries.
# v1.7.0-rc2
- fixes unixSocket connections for mariadb [#1248](https://github.com/sequelize/sequelize/pull/1248)
......
......@@ -807,12 +807,22 @@ module.exports = (function() {
getWhereConditions: function(smth, tableName, factory, options) {
var result = null
, where = {}
, self = this
if ((smth instanceof Utils.and) || (smth instanceof Utils.or)) {
var connector = (smth instanceof Utils.and) ? ' AND ' : ' OR '
result = smth.args.map(function(arg) {
return self.getWhereConditions(arg, tableName, factory, options)
}).join(connector)
if (Utils.isHash(smth)) {
result = "(" + result + ")"
} else if (Utils.isHash(smth)) {
smth = Utils.prependTableNameToHash(tableName, smth)
result = this.hashToWhereConditions(smth, factory, options)
} 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]
......@@ -826,8 +836,21 @@ module.exports = (function() {
} else if (typeof smth === "string") {
result = smth
} else if (Array.isArray(smth)) {
var treatAsAnd = smth.reduce(function(treatAsAnd, arg) {
if (treatAsAnd) {
return treatAsAnd
} else {
return !(arg instanceof Date) && ((arg instanceof Utils.and) || (arg instanceof Utils.or) || Utils.isHash(arg) || Array.isArray(arg))
}
}, false)
if (treatAsAnd) {
var _smth = self.sequelize.and.apply(null, smth)
result = self.getWhereConditions(_smth, tableName, factory, options)
} else {
result = Utils.format(smth, this.dialect)
}
}
return result ? result : '1=1'
},
......
......@@ -422,6 +422,14 @@ module.exports = (function() {
return new Utils.asIs(val)
}
Sequelize.and = Sequelize.prototype.and = function() {
return new Utils.and(Array.prototype.slice.call(arguments))
}
Sequelize.or = Sequelize.prototype.or = function() {
return new Utils.or(Array.prototype.slice.call(arguments))
}
Sequelize.prototype.transaction = function(_options, _callback) {
var options = (typeof _options === 'function') ? {} : _options
, callback = (typeof _options === 'function') ? _options : _callback
......
......@@ -523,6 +523,7 @@ var Utils = module.exports = {
tickChar = tickChar || Utils.TICK_CHAR
return s.replace(new RegExp(tickChar, 'g'), "")
},
/*
* Utility functions for representing SQL functions, and columns that should be escaped.
* Please do not use these functions directly, use Sequelize.fn and Sequelize.col instead.
......@@ -531,13 +532,16 @@ var Utils = module.exports = {
this.fn = fn
this.args = args
},
col: function (col) {
this.col = col
},
cast: function (val, type) {
this.val = val
this.type = (type || '').trim()
},
literal: function (val) {
this.val = val
},
......@@ -546,6 +550,14 @@ var Utils = module.exports = {
this.val = val
},
and: function(args) {
this.args = args
},
or: function(args) {
this.args = args
},
generateUUID: function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8)
......
/* jshint camelcase: false */
/* jshint expr: true */
var chai = require('chai')
, Sequelize = require('../../index')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, dialect = Support.getTestDialect()
, config = require(__dirname + "/../config/config")
, sinon = require('sinon')
, datetime = require('chai-datetime')
, _ = require('lodash')
, moment = require('moment')
, async = require('async')
chai.use(datetime)
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("DAOFactory"), function () {
beforeEach(function(done) {
this.User = this.sequelize.define('User', {
username: DataTypes.STRING,
secretValue: DataTypes.STRING,
data: DataTypes.STRING,
intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE,
aBool: DataTypes.BOOLEAN
})
this.User.sync({ force: true }).success(function() {
done()
})
})
;(['or', 'and']).forEach(function(method) {
var word = method.toUpperCase()
describe('Sequelize.' + method, function() {
it('can handle plain strings', function(done) {
this.User.find({
where: Sequelize[method]( "1=1", "2=2" )
}).on('sql', function(sql) {
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1")
done()
})
})
it('can handle arrays', function(done) {
this.User.find({
where: Sequelize[method]( ["1=?", 1], ["2=?", 2] )
}).on('sql', function(sql) {
expect(sql).to.contain("WHERE (1=1 " + word + " 2=2) LIMIT 1")
done()
})
})
it('can handle objects', function(done) {
this.User.find({
where: Sequelize[method]( { username: "foo", intVal: 2 }, { secretValue: 'bar' } )
}).on('sql', function(sql) {
var expectation = ({
mysql: "WHERE (`Users`.`username`='foo' AND `Users`.`intVal`=2 " + word + " `Users`.`secretValue`='bar')",
sqlite: "WHERE (`Users`.`username`='foo' AND `Users`.`intVal`=2 " + word + " `Users`.`secretValue`='bar')",
postgres: 'WHERE ("Users"."username"=\'foo\' AND "Users"."intVal"=2 ' + word + ' "Users"."secretValue"=\'bar\')',
mariadb: "WHERE (`Users`.`username`='foo' AND `Users`.`intVal`=2 " + word + " `Users`.`secretValue`='bar')"
})[Support.getTestDialect()]
if (!expectation) {
console.log(sql)
throw new Error('Undefined expectation for ' + Support.getTestDialect())
}
expect(sql).to.contain(expectation)
done()
})
})
it('can handle numbers', function(done) {
this.User.find({
where: Sequelize[method]( 1, 2 )
}).on('sql', function(sql) {
var expectation = ({
mysql: "WHERE (`Users`.`id`=1 " + word + " `Users`.`id`=2)",
sqlite: "WHERE (`Users`.`id`=1 " + word + " `Users`.`id`=2)",
postgres: 'WHERE ("Users"."id"=1 ' + word + ' "Users"."id"=2)',
mariadb: "WHERE (`Users`.`id`=1 " + word + " `Users`.`id`=2)"
})[Support.getTestDialect()]
if (!expectation) {
console.log(sql)
throw new Error('Undefined expectation for ' + Support.getTestDialect())
}
expect(sql).to.contain(expectation)
done()
})
})
})
})
describe('Combinations of Sequelize.and and Sequelize.or', function() {
it('allows nesting of Sequelize.or', function(done) {
this.User.find({
where: Sequelize.and( Sequelize.or("1=1", "2=2"), Sequelize.or("3=3", "4=4") )
}).on('sql', function(sql) {
expect(sql).to.contain("WHERE ((1=1 OR 2=2) AND (3=3 OR 4=4)) LIMIT 1")
done()
})
})
it('allows nesting of Sequelize.and', function(done) {
this.User.find({
where: Sequelize.or( Sequelize.and("1=1", "2=2"), Sequelize.and("3=3", "4=4") )
}).on('sql', function(sql) {
expect(sql).to.contain("WHERE ((1=1 AND 2=2) OR (3=3 AND 4=4)) LIMIT 1")
done()
})
})
it('correctly handles complex combinations', function(done) {
this.User.find({
where: [
42, "2=2", ["1=?", 1], { username: "foo" },
Sequelize.or(
42, "2=2", ["1=?", 1], { username: "foo" },
Sequelize.and( 42, "2=2", ["1=?", 1], { username: "foo" } ),
Sequelize.or( 42, "2=2", ["1=?", 1], { username: "foo" } )
),
Sequelize.and(
42, "2=2", ["1=?", 1], { username: "foo" },
Sequelize.or( 42, "2=2", ["1=?", 1], { username: "foo" } ),
Sequelize.and( 42, "2=2", ["1=?", 1], { username: "foo" } )
)
]
}).on('sql', function(sql) {
if (Support.getTestDialect() === 'postgres') {
expect(sql).to.contain(
'WHERE (' + [
'"Users"."id"=42 AND 2=2 AND 1=1 AND "Users"."username"=\'foo\' AND ',
'(',
'"Users"."id"=42 OR 2=2 OR 1=1 OR "Users"."username"=\'foo\' OR ',
'("Users"."id"=42 AND 2=2 AND 1=1 AND "Users"."username"=\'foo\') OR ',
'("Users"."id"=42 OR 2=2 OR 1=1 OR "Users"."username"=\'foo\')',
') AND ',
'(',
'"Users"."id"=42 AND 2=2 AND 1=1 AND "Users"."username"=\'foo\' AND ',
'("Users"."id"=42 OR 2=2 OR 1=1 OR "Users"."username"=\'foo\') AND ',
'("Users"."id"=42 AND 2=2 AND 1=1 AND "Users"."username"=\'foo\')',
')'
].join("") +
')'
)
} else {
expect(sql).to.contain(
"WHERE (" + [
"`Users`.`id`=42 AND 2=2 AND 1=1 AND `Users`.`username`='foo' AND ",
"(",
"`Users`.`id`=42 OR 2=2 OR 1=1 OR `Users`.`username`='foo' OR ",
"(`Users`.`id`=42 AND 2=2 AND 1=1 AND `Users`.`username`='foo') OR ",
"(`Users`.`id`=42 OR 2=2 OR 1=1 OR `Users`.`username`='foo')",
") AND ",
"(",
"`Users`.`id`=42 AND 2=2 AND 1=1 AND `Users`.`username`='foo' AND ",
"(`Users`.`id`=42 OR 2=2 OR 1=1 OR `Users`.`username`='foo') AND ",
"(`Users`.`id`=42 AND 2=2 AND 1=1 AND `Users`.`username`='foo')",
")"
].join("") +
")"
)
}
done()
})
})
})
})
......@@ -85,6 +85,15 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
it('treats questionmarks in an array', function(done) {
this.UserPrimary.find({
where: ['specialkey = ?', 'awesome']
}).on('sql', function(sql) {
expect(sql).to.contain("WHERE specialkey = 'awesome'")
done()
})
})
it('doesn\'t throw an error when entering in a non integer value for a specified primary field', function(done) {
this.UserPrimary.find('a string').success(function(user) {
expect(user.specialkey).to.equal('a string')
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!