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

Commit 9f5ae3b7 by Jonathan M. Altman

Merge branch 'master' of https://github.com/sequelize/sequelize into basic_funct…

…ion_and_trigger_migrations
2 parents 3b07b657 8c7f557e
......@@ -33,10 +33,13 @@
- [BUG] Fixed eager loading for many-to-many associations. [#834](https://github.com/sequelize/sequelize/pull/834). thanks to lemon-tree
- [BUG] allowNull: true enums can now be null [#857](https://github.com/sequelize/sequelize/pull/857). thanks to durango
- [BUG] Fixes Postgres' ability to search within arrays. [#879](https://github.com/sequelize/sequelize/pull/879). thanks to durango
- [BUG] Find and finAll would modify the options objects, now the objects are cloned at the start of the method [#884](https://github.com/sequelize/sequelize/pull/884) thanks to janmeier
- [BUG] Find and finAll would modify the options objects, now the objects are cloned at the start of the method [#884](https://github.com/sequelize/sequelize/pull/884) thanks to janmeier. Improved in [#899](https://github.com/sequelize/sequelize/pull/899) thanks to hackwaly
- [BUG] Add support for typed arrays in SqlString.escape and SqlString.arrayToList [#891](https://github.com/sequelize/sequelize/pull/891). thanks to LJ1102
- [BUG] Postgres requires empty array to be explicitly cast on update [#890](https://github.com/sequelize/sequelize/pull/890). thanks to robraux
- [BUG] Added tests & bugfixes for DAO-Factory.update and array of values in where clause [#880](https://github.com/sequelize/sequelize/pull/880). thanks to domasx2
- [BUG] sqlite no longer leaks a global `db` variable [#900](https://github.com/sequelize/sequelize/pull/900). thanks to xming
- [BUG] Fix for counts queries with no result [#906](https://github.com/sequelize/sequelize/pull/906). thanks to iamjochem
- [BUG] Allow include when the same table is referenced multiple times using hasMany [#913](https://github.com/sequelize/sequelize/pull/913). thanks to janmeier
- [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] Foreign key constraints. [#595](https://github.com/sequelize/sequelize/pull/595). thanks to optilude
......@@ -67,6 +70,7 @@
- [FEATURE] Scopes. [#748](https://github.com/sequelize/sequelize/pull/748). durango
- [FEATURE] Model#find() / Model#findAll() is now working with strings. [#855](https://github.com/sequelize/sequelize/pull/855). Thanks to whito.
- [FEATURE] Shortcut method for getting a defined model. [#868](https://github.com/sequelize/sequelize/pull/868). Thanks to jwilm.
- [FEATURE] Added Sequelize.fn() and Sequelize.col() to properly call columns and functions within Sequelize. [#882](https://github.com/sequelize/sequelize/pull/882). thanks to 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] Moved Jasmine to Buster and then Buster to Mocha + Chai. sdepold and durango
......
......@@ -18,8 +18,8 @@ module.exports = (function() {
this.source.tableName,
this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
)
this.associationAccessor = this.combinedName = (this.options.joinTableName || combinedTableName)
this.options.tableName = this.combinedName
this.options.tableName = this.combinedName = (this.options.joinTableName || combinedTableName)
this.associationAccessor = this.options.as || this.combinedName
var as = (this.options.as || Utils.pluralize(this.target.tableName, this.target.options.language))
......
......@@ -326,7 +326,6 @@ module.exports = (function() {
var hasJoin = false
options = optClone(options)
if (typeof options === 'object') {
if (options.hasOwnProperty('include')) {
hasJoin = true
......@@ -379,7 +378,6 @@ module.exports = (function() {
var primaryKeys = this.primaryKeys
, keys = Object.keys(primaryKeys)
, keysLength = keys.length
options = optClone(options)
// options is not a hash but an id
if (typeof options === 'number') {
......
var Utils = require("../../utils")
module.exports = (function() {
var QueryGenerator = {
addSchema: function(opts) {
......@@ -268,7 +270,35 @@ module.exports = (function() {
Globally disable foreign key constraints
*/
disableForeignKeyConstraintsQuery: function() {
throwMethodUndefined('disableForeignKeyConstraintsQuery')
throwMethodUndefined('disableForeignKeyConstraintsQuery')
},
/*
Quote an object based on its type. This is a more general version of quoteIdentifiers
Strings: should proxy to quoteIdentifiers
Arrays: First argument should be qouted, second argument should be append without quoting
Objects:
* If raw is set, that value should be returned verbatim, without quoting
* If fn is set, the string should start with the value of fn, starting paren, followed by
the values of cols (which is assumed to be an array), quoted and joined with ', ',
unless they are themselves objects
* If direction is set, should be prepended
Currently this function is only used for ordering / grouping columns, but it could
potentially also be used for other places where we want to be able to call SQL functions (e.g. as default values)
*/
quote: function(obj, force) {
if (Utils._.isString(obj)) {
return this.quoteIdentifiers(obj, force)
} else if (Array.isArray(obj)) {
return this.quote(obj[0], force) + ' ' + obj[1]
} else if (obj instanceof Utils.fn || obj instanceof Utils.col) {
return obj.toString(this)
} else if (Utils._.isObject(obj) && 'raw' in obj) {
return obj.raw
} else {
throw new Error('Unknown structure passed to order / group: ' + JSON.stringify(ojb))
}
},
/*
......
......@@ -228,11 +228,12 @@ module.exports = (function() {
}
if (options.group) {
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t)}.bind(this)).join(', ') : this.quoteIdentifiers(options.group)
options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group
query += " GROUP BY " + options.group
}
if (options.order) {
options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order
query += " ORDER BY " + options.order
}
......@@ -612,5 +613,5 @@ module.exports = (function() {
}
return Utils._.extend(Utils._.clone(require("../query-generator")), QueryGenerator)
return Utils._.extend(Utils._.clone(require("../abstract/query-generator")), QueryGenerator)
})()
......@@ -300,14 +300,12 @@ module.exports = (function() {
}
if(options.group) {
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t) }.bind(this)).join(', ') : this.quoteIdentifiers(options.group)
options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group
query += " GROUP BY <%= group %>"
}
if(options.order) {
options.order = options.order.replace(/([^ ]+)(.*)/, function(m, g1, g2) {
return this.quoteIdentifiers(g1) + g2
}.bind(this))
options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order
query += " ORDER BY <%= order %>"
}
......@@ -1034,5 +1032,5 @@ module.exports = (function() {
return returning;
}
return Utils._.extend(Utils._.clone(require("../query-generator")), QueryGenerator)
return Utils._.extend(Utils._.clone(require("../abstract/query-generator")), QueryGenerator)
})()
......@@ -18,6 +18,7 @@ module.exports = (function() {
ConnectorManager.prototype.connect = function() {
var emitter = new (require('events').EventEmitter)()
, self = this
, db
this.database = db = new sqlite3.Database(self.sequelize.options.storage || ':memory:', function(err) {
if (err) {
......
......@@ -3,7 +3,7 @@ var Utils = require("../../utils")
, SqlString = require("../../sql-string")
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
Utils._.clone(require("../abstract/query-generator")),
Utils._.clone(require("../mysql/query-generator"))
)
......@@ -211,11 +211,12 @@ module.exports = (function() {
}
if (options.group) {
options.group = Array.isArray(options.group) ? options.group.map(function(t) { return this.quoteIdentifiers(t)}.bind(this)).join(', ') : qa(options.group)
options.group = Array.isArray(options.group) ? options.group.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.group
query += " GROUP BY " + options.group
}
if (options.order) {
options.order = Array.isArray(options.order) ? options.order.map(function (t) { return this.quote(t) }.bind(this)).join(', ') : options.order
query += " ORDER BY " + options.order
}
......
......@@ -581,10 +581,10 @@ module.exports = (function() {
qry
.success(function(data) {
var result = data[attributeSelector]
var result = data ? data[attributeSelector] : null
if (options && options.parseInt) {
result = parseInt(result)
result = parseInt(result, 10)
}
if (options && options.parseFloat) {
......
......@@ -318,5 +318,13 @@ module.exports = (function() {
}).run()
}
Sequelize.prototype.fn = function (fn) {
return new Utils.fn(fn, Array.prototype.slice.call(arguments, 1))
}
Sequelize.prototype.col = function (col) {
return new Utils.col(col)
}
return Sequelize
})()
......@@ -55,7 +55,8 @@ var Utils = module.exports = {
},
format: function(arr, dialect) {
var timeZone = null;
return SqlString.format(arr.shift(), arr, timeZone, dialect)
// Make a clone of the array beacuse format modifies the passed args
return SqlString.format(arr[0], arr.slice(1), timeZone, dialect)
},
formatNamedParameters: function(sql, parameters, dialect) {
var timeZone = null;
......@@ -468,9 +469,33 @@ var Utils = module.exports = {
removeTicks: function(s, tickChar) {
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.
*/
fn: function (fn, args) {
this.fn = fn
this.args = args
},
col: function (col) {
this.col = col
}
}
Utils.fn.prototype.toString = function(queryGenerator) {
return this.fn + '(' + this.args.map(function (arg) {
if (arg instanceof Utils.fn || arg instanceof Utils.col) {
return arg.toString(queryGenerator)
} else {
return queryGenerator.escape(arg)
}
}).join(', ') + ')'
}
Utils.col.prototype.toString = function (queryGenerator) {
return queryGenerator.quote(this.col)
}
Utils.CustomEventEmitter = require(__dirname + "/emitters/custom-event-emitter")
Utils.QueryChainer = require(__dirname + "/query-chainer")
Utils.Lingo = require("lingo")
......@@ -36,11 +36,11 @@
"url": "https://github.com/sequelize/sequelize/issues"
},
"dependencies": {
"lodash": "~1.3.1",
"lodash": "~2.0.0",
"underscore.string": "~2.3.0",
"lingo": "~0.0.5",
"validator": "~1.5.0",
"moment": "~2.1.0",
"moment": "~2.2.1",
"commander": "~2.0.0",
"dottie": "0.0.8-0",
"toposort-class": "~0.2.0",
......@@ -55,7 +55,7 @@
"watchr": "~2.4.3",
"yuidocjs": "~0.3.36",
"chai": "~1.7.2",
"mocha": "~1.12.0",
"mocha": "~1.13.0",
"chai-datetime": "~1.1.1",
"sinon": "~1.7.3"
},
......@@ -77,4 +77,4 @@
"node": ">=0.4.6"
},
"license": "MIT"
}
\ No newline at end of file
}
/* jshint camelcase: false */
/* jshint camelcase: false, expr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
......@@ -7,6 +7,19 @@ var chai = require('chai')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("BelongsTo"), function() {
describe("Model.associations", function () {
it("should store all assocations when associting to the same table multiple times", function () {
var User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
Group.belongsTo(User)
Group.belongsTo(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' })
Group.belongsTo(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' })
expect(Object.keys(Group.associations)).to.deep.equal(['Users', 'primaryUsers', 'secondaryUsers'])
})
})
describe('setAssociation', function() {
it('can set the association with declared primary keys...', function(done) {
var User = this.sequelize.define('UserXYZ', { user_id: {type: DataTypes.INTEGER, primaryKey: true }, username: DataTypes.STRING })
......
/* jshint camelcase: false */
/* jshint camelcase: false, expr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
......@@ -11,6 +11,19 @@ var chai = require('chai')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("HasMany"), function() {
describe("Model.associations", function () {
it("should store all assocations when associting to the same table multiple times", function () {
var User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
Group.hasMany(User)
Group.hasMany(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' })
Group.hasMany(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' })
expect(Object.keys(Group.associations)).to.deep.equal(['GroupsUsers', 'primaryUsers', 'secondaryUsers'])
})
})
describe('(1:N)', function() {
describe('hasSingle', function() {
beforeEach(function(done) {
......
/* jshint camelcase: false */
/* jshint camelcase: false, expr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
......@@ -7,6 +7,19 @@ var chai = require('chai')
chai.Assertion.includeStack = true
describe(Support.getTestDialectTeaser("HasOne"), function() {
describe("Model.associations", function () {
it("should store all assocations when associting to the same table multiple times", function () {
var User = this.sequelize.define('User', {})
, Group = this.sequelize.define('Group', {})
Group.hasOne(User)
Group.hasOne(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' })
Group.hasOne(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' })
expect(Object.keys(Group.associations)).to.deep.equal(['Users', 'primaryUsers', 'secondaryUsers'])
})
})
describe('getAssocation', function() {
it('should be able to handle a where object that\'s a first class citizen.', function(done) {
var User = this.sequelize.define('UserXYZ', { username: Sequelize.STRING })
......
......@@ -1887,6 +1887,24 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
it('allows mulitple assocations of the same model with different alias', function (done) {
var self = this
this.Worker.belongsTo(this.Task, { as: 'ToDo' })
this.Worker.belongsTo(this.Task, { as: 'DoTo' })
this.init(function () {
self.Worker.find({
include: [
{ model: self.Task, as: 'ToDo' },
{ model: self.Task, as: 'DoTo' }
]
}).success(function () {
// Just being able to include both shows that this test works, so no assertions needed
done()
})
})
})
})
describe('hasOne', function() {
......@@ -2008,6 +2026,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
})
it('allows mulitple assocations of the same model with different alias', function (done) {
var self = this
this.Worker.hasOne(this.Task, { as: 'DoTo' })
this.init(function () {
self.Worker.find({
include: [
{ model: self.Task, as: 'ToDo' },
{ model: self.Task, as: 'DoTo' }
]
}).success(function () {
// Just being able to include both shows that this test works, so no assertions needed
done()
})
})
})
})
})
......@@ -2134,6 +2169,23 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
})
})
it('allows mulitple assocations of the same model with different alias', function (done) {
var self = this
this.Worker.hasMany(this.Task, { as: 'DoTos' })
this.init(function () {
self.Worker.find({
include: [
{ model: self.Task, as: 'ToDos' },
{ model: self.Task, as: 'DoTos' }
]
}).success(function () {
// Just being able to include both shows that this test works, so no assertions needed
done()
})
})
})
})
})
})
......@@ -2555,7 +2607,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
it("sorts the results via a date column", function(done) {
var self = this
self.User.create({username: 'user3', data: 'bar', theDate: moment().add('hours', 2).toDate()}).success(function(){
self.User.findAll({ order: 'theDate DESC' }).success(function(users) {
self.User.findAll({ order: [['theDate', 'DESC']] }).success(function(users) {
expect(users[0].id).to.be.above(users[2].id)
done()
})
......@@ -2752,6 +2804,15 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
it('does not modify the passed arguments', function (done) {
var options = { where: ['username = ?', 'user1']}
this.User.count(options).success(function(count) {
expect(options).to.deep.equal({ where: ['username = ?', 'user1']})
done()
})
})
it('allows sql logging', function(done) {
this.User.count().on('sql', function(sql) {
expect(sql).to.exist
......
......@@ -163,20 +163,77 @@ if (dialect.match(/^mysql/)) {
expectation: "SELECT * FROM `myTable` ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: ["id"]}],
expectation: "SELECT * FROM `myTable` ORDER BY `id`;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: ["myTable.id"]}],
expectation: "SELECT * FROM `myTable` ORDER BY `myTable`.`id`;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: [["id", 'DESC']]}],
expectation: "SELECT * FROM `myTable` ORDER BY `id` DESC;",
context: QueryGenerator
}, {
title: 'raw arguments are neither quoted nor escaped',
arguments: ['myTable', {order: [[{raw: 'f1(f2(id))'}, 'DESC']]}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(id)) DESC;",
context: QueryGenerator
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'functions can take all types as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(`myTable`.`id`) DESC, f2(12, 'lalala', '2011-03-27 10:01:55') ASC;",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'single string argument is not quoted',
arguments: ['myTable', {group: "name"}],
expectation: "SELECT * FROM `myTable` GROUP BY `name`;",
expectation: "SELECT * FROM `myTable` GROUP BY name;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: ["name"]}],
arguments: ['myTable', { group: ["name"] }],
expectation: "SELECT * FROM `myTable` GROUP BY `name`;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: ["name", "title"]}],
expectation: "SELECT * FROM `myTable` GROUP BY `name`, `title`;",
context: QueryGenerator
title: 'functions work for group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'It is possible to mix sequelize.fn and string arguments to group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`), `title`;",
context: QueryGenerator,
needsSequelize: true
}, {
arguments: ['myTable', {group: "name", order: "id DESC"}],
expectation: "SELECT * FROM `myTable` GROUP BY `name` ORDER BY id DESC;",
expectation: "SELECT * FROM `myTable` GROUP BY name ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {limit: 10}],
......@@ -423,9 +480,12 @@ if (dialect.match(/^mysql/)) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
it(title, function(done) {
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
......
......@@ -247,13 +247,73 @@ if (dialect.match(/^postgres/)) {
expectation: "SELECT * FROM \"myTable\" WHERE foo='bar';"
}, {
arguments: ['myTable', {order: "id DESC"}],
expectation: "SELECT * FROM \"myTable\" ORDER BY \"id\" DESC;"
}, {
expectation: "SELECT * FROM \"myTable\" ORDER BY id DESC;"
}, {
arguments: ['myTable', {order: ["id"]}],
expectation: 'SELECT * FROM "myTable" ORDER BY "id";',
context: QueryGenerator
}, {
arguments: ['myTable', {order: ["myTable.id"]}],
expectation: 'SELECT * FROM "myTable" ORDER BY "myTable"."id";',
context: QueryGenerator
}, {
arguments: ['myTable', {order: [["id", 'DESC']]}],
expectation: 'SELECT * FROM "myTable" ORDER BY "id" DESC;',
context: QueryGenerator
}, {
title: 'raw arguments are neither quoted nor escaped',
arguments: ['myTable', {order: [[{raw: 'f1(f2(id))'},'DESC']]}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2(id)) DESC;',
context: QueryGenerator
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2("id")) DESC;',
context: QueryGenerator,
needsSequelize: true
}, {
title: 'functions can take all types as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1("myTable"."id") DESC, f2(12, \'lalala\', \'2011-03-27 10:01:55.000 +00:00\') ASC;',
context: QueryGenerator,
needsSequelize: true
}, {
title: 'single string argument is not quoted',
arguments: ['myTable', {group: "name"}],
expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\";"
expectation: "SELECT * FROM \"myTable\" GROUP BY name;"
}, {
arguments: ['myTable', {group: ["name"]}],
expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\";"
}, {
title: 'functions work for group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
}],
expectation: "SELECT * FROM \"myTable\" GROUP BY YEAR(\"createdAt\");",
needsSequelize: true
},{
title: 'It is possible to mix sequelize.fn and string arguments to group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
}],
expectation: "SELECT * FROM \"myTable\" GROUP BY YEAR(\"createdAt\"), \"title\";",
context: QueryGenerator,
needsSequelize: true
}, {
arguments: ['myTable', {group: ["name","title"]}],
expectation: "SELECT * FROM \"myTable\" GROUP BY \"name\", \"title\";"
......@@ -807,6 +867,9 @@ if (dialect.match(/^postgres/)) {
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
......
......@@ -104,6 +104,156 @@ if (dialect === 'sqlite') {
}
],
selectQuery: [
{
arguments: ['myTable'],
expectation: "SELECT * FROM `myTable`;",
context: QueryGenerator
}, {
arguments: ['myTable', {attributes: ['id', 'name']}],
expectation: "SELECT `id`, `name` FROM `myTable`;",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {id: 2}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`id`=2;",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {name: 'foo'}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`name`='foo';",
context: QueryGenerator
}, {
arguments: ['myTable', {where: {name: "foo';DROP TABLE myTable;"}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`name`='foo\'\';DROP TABLE myTable;';",
context: QueryGenerator
}, {
arguments: ['myTable', {where: 2}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`id`=2;",
context: QueryGenerator
}, {
arguments: ['foo', { attributes: [['count(*)', 'count']] }],
expectation: 'SELECT count(*) as `count` FROM `foo`;',
context: QueryGenerator
}, {
arguments: ['myTable', {where: "foo='bar'"}],
expectation: "SELECT * FROM `myTable` WHERE foo='bar';",
context: QueryGenerator
}, {
arguments: ['myTable', {order: "id DESC"}],
expectation: "SELECT * FROM `myTable` ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: ["id"]}],
expectation: "SELECT * FROM `myTable` ORDER BY `id`;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: ["myTable.id"]}],
expectation: "SELECT * FROM `myTable` ORDER BY `myTable`.`id`;",
context: QueryGenerator
}, {
arguments: ['myTable', {order: [["id", 'DESC']]}],
expectation: "SELECT * FROM `myTable` ORDER BY `id` DESC;",
context: QueryGenerator
}, {
title: 'raw arguments are neither quoted nor escaped',
arguments: ['myTable', {order: [[{raw: 'f1(f2(id))'}, 'DESC']]}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(id)) DESC;",
context: QueryGenerator
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'functions can take all types as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(`myTable`.`id`) DESC, f2(12, 'lalala', '2011-03-27 10:01:55') ASC;",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'single string argument is not quoted',
arguments: ['myTable', {group: "name"}],
expectation: "SELECT * FROM `myTable` GROUP BY name;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: ["name"]}],
expectation: "SELECT * FROM `myTable` GROUP BY `name`;",
context: QueryGenerator
}, {
title: 'functions work for group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt'))]
}
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'It is possible to mix sequelize.fn and string arguments to group by',
arguments: ['myTable', function (sequelize) {
return {
group: [sequelize.fn('YEAR', sequelize.col('createdAt')), 'title']
}
}],
expectation: "SELECT * FROM `myTable` GROUP BY YEAR(`createdAt`), `title`;",
context: QueryGenerator,
needsSequelize: true
}, {
arguments: ['myTable', {group: ["name", "title"]}],
expectation: "SELECT * FROM `myTable` GROUP BY `name`, `title`;",
context: QueryGenerator
}, {
arguments: ['myTable', {group: "name", order: "id DESC"}],
expectation: "SELECT * FROM `myTable` GROUP BY name ORDER BY id DESC;",
context: QueryGenerator
}, {
arguments: ['myTable', {limit: 10}],
expectation: "SELECT * FROM `myTable` LIMIT 10;",
context: QueryGenerator
}, {
arguments: ['myTable', {limit: 10, offset: 2}],
expectation: "SELECT * FROM `myTable` LIMIT 2, 10;",
context: QueryGenerator
}, {
title: 'uses default limit if only offset is specified',
arguments: ['myTable', {offset: 2}],
expectation: "SELECT * FROM `myTable` LIMIT 2, 10000000000000;",
context: QueryGenerator
}, {
title: 'multiple where arguments',
arguments: ['myTable', {where: {boat: 'canoe', weather: 'cold'}}],
expectation: "SELECT * FROM `myTable` WHERE `myTable`.`boat`='canoe' AND `myTable`.`weather`='cold';",
context: QueryGenerator
}, {
title: 'no where arguments (object)',
arguments: ['myTable', {where: {}}],
expectation: "SELECT * FROM `myTable` WHERE 1=1;",
context: QueryGenerator
}, {
title: 'no where arguments (string)',
arguments: ['myTable', {where: ''}],
expectation: "SELECT * FROM `myTable` WHERE 1=1;",
context: QueryGenerator
}, {
title: 'no where arguments (null)',
arguments: ['myTable', {where: null}],
expectation: "SELECT * FROM `myTable` WHERE 1=1;",
context: QueryGenerator
}
],
insertQuery: [
{
arguments: ['myTable', { name: 'foo' }],
......@@ -257,6 +407,9 @@ if (dialect === 'sqlite') {
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
test.arguments[1] = test.arguments[1](this.sequelize)
}
QueryGenerator.options = context.options
var conditions = QueryGenerator[suiteTitle].apply(QueryGenerator, test.arguments)
expect(conditions).to.deep.equal(test.expectation)
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!