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

Commit 083af081 by Mick Hansen

Merge pull request #2318 from janmeier/master

Made sequelize.where smarter
2 parents a2d31971 23cc2178
# Next
- [FEATURE] Added to posibility of using a sequelize object as key in `sequelize.where`. Also added the option of specifying a comparator
# 2.0.0-rc1 # 2.0.0-rc1
- [BUG] Fixed an issue with foreign key object syntax for hasOne and belongsTo - [BUG] Fixed an issue with foreign key object syntax for hasOne and belongsTo
- [FEATURE] Added `field` and `name` to the object form of foreign key definitions - [FEATURE] Added `field` and `name` to the object form of foreign key definitions
......
...@@ -39,7 +39,7 @@ module.exports = (function() { ...@@ -39,7 +39,7 @@ module.exports = (function() {
throughWhere[attribute] = through.scope[attribute]; throughWhere[attribute] = through.scope[attribute];
}.bind(this)); }.bind(this));
} }
options.include = options.include || []; options.include = options.include || [];
options.include.push({ options.include.push({
model: through.model, model: through.model,
...@@ -56,6 +56,7 @@ module.exports = (function() { ...@@ -56,6 +56,7 @@ module.exports = (function() {
_pseudo: true _pseudo: true
}); });
} }
return self.association.target.findAll(options, queryOptions); return self.association.target.findAll(options, queryOptions);
}; };
......
...@@ -579,7 +579,7 @@ module.exports = (function() { ...@@ -579,7 +579,7 @@ module.exports = (function() {
} else if (obj._modelAttribute) { } else if (obj._modelAttribute) {
return this.quoteTable(obj.Model.name) + '.' + obj.fieldName; return this.quoteTable(obj.Model.name) + '.' + obj.fieldName;
} else if (obj._isSequelizeMethod) { } else if (obj._isSequelizeMethod) {
return obj.toString(this); return this.handleSequelizeMethod(obj);
} else if (Utils._.isObject(obj) && 'raw' in obj) { } else if (Utils._.isObject(obj) && 'raw' in obj) {
return obj.raw; return obj.raw;
} else { } else {
...@@ -661,7 +661,7 @@ module.exports = (function() { ...@@ -661,7 +661,7 @@ module.exports = (function() {
*/ */
escape: function(value, field) { escape: function(value, field) {
if (value && value._isSequelizeMethod) { if (value && value._isSequelizeMethod) {
return value.toString(this); return this.handleSequelizeMethod(value);
} else { } else {
return SqlString.escape(value, false, this.options.timezone, this.dialect, field); return SqlString.escape(value, false, this.options.timezone, this.dialect, field);
} }
...@@ -746,17 +746,18 @@ module.exports = (function() { ...@@ -746,17 +746,18 @@ module.exports = (function() {
}); });
} }
// Escape attributes // Escape attributes
mainAttributes = mainAttributes && mainAttributes.map(function(attr) { mainAttributes = mainAttributes && mainAttributes.map(function(attr) {
var addTable = true; var addTable = true;
if (attr._isSequelizeMethod) { if (attr._isSequelizeMethod) {
return attr.toString(self); return self.handleSequelizeMethod(attr);
} }
if (Array.isArray(attr) && attr.length === 2) { if (Array.isArray(attr) && attr.length === 2) {
if (attr[0]._isSequelizeMethod) { if (attr[0]._isSequelizeMethod) {
attr[0] = attr[0].toString(self); attr[0] = self.handleSequelizeMethod(attr[0]);
addTable = false; addTable = false;
} else { } else {
if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) { if (attr[0].indexOf('(') === -1 && attr[0].indexOf(')') === -1) {
...@@ -814,6 +815,7 @@ module.exports = (function() { ...@@ -814,6 +815,7 @@ module.exports = (function() {
var attrAs = attr, var attrAs = attr,
verbatim = false; verbatim = false;
if (Array.isArray(attr) && attr.length === 2) { if (Array.isArray(attr) && attr.length === 2) {
if (attr[0]._isSequelizeMethod) { if (attr[0]._isSequelizeMethod) {
if (attr[0] instanceof Utils.literal || if (attr[0] instanceof Utils.literal ||
...@@ -825,13 +827,13 @@ module.exports = (function() { ...@@ -825,13 +827,13 @@ module.exports = (function() {
} }
attr = attr.map(function($attr) { attr = attr.map(function($attr) {
return $attr._isSequelizeMethod ? $attr.toString(self) : $attr; return $attr._isSequelizeMethod ? self.handleSequelizeMethod($attr) : $attr;
}); });
attrAs = attr[1]; attrAs = attr[1];
attr = attr[0]; attr = attr[0];
} else if (attr instanceof Utils.literal) { } else if (attr instanceof Utils.literal) {
return attr.toString(self); // We trust the user to rename the field correctly return attr.val; // We trust the user to rename the field correctly
} else if (attr instanceof Utils.cast || } else if (attr instanceof Utils.cast ||
attr instanceof Utils.fn attr instanceof Utils.fn
) { ) {
...@@ -847,7 +849,6 @@ module.exports = (function() { ...@@ -847,7 +849,6 @@ module.exports = (function() {
} }
return prefix + ' AS ' + self.quoteIdentifier(as + '.' + attrAs); return prefix + ' AS ' + self.quoteIdentifier(as + '.' + attrAs);
}); });
if (include.subQuery && subQuery) { if (include.subQuery && subQuery) {
subQueryAttributes = subQueryAttributes.concat(attributes); subQueryAttributes = subQueryAttributes.concat(attributes);
} else { } else {
...@@ -855,6 +856,7 @@ module.exports = (function() { ...@@ -855,6 +856,7 @@ module.exports = (function() {
} }
} }
if (through) { if (through) {
var throughTable = through.model.getTableName() var throughTable = through.model.getTableName()
, throughAs = as + '.' + through.as , throughAs = as + '.' + through.as
...@@ -978,12 +980,11 @@ module.exports = (function() { ...@@ -978,12 +980,11 @@ module.exports = (function() {
')', 'IS NOT NULL'].join(' ')); ')', 'IS NOT NULL'].join(' '));
} }
} }
// Generate join SQL // Generate join SQL
joinQueryItem += joinType + self.quoteTable(table, as) + ' ON ' + joinOn; joinQueryItem += joinType + self.quoteTable(table, as) + ' ON ' + joinOn;
}
}
if (include.subQuery && subQuery) { if (include.subQuery && subQuery) {
joinQueries.subQuery.push(joinQueryItem); joinQueries.subQuery.push(joinQueryItem);
} else { } else {
...@@ -1209,26 +1210,9 @@ module.exports = (function() { ...@@ -1209,26 +1210,9 @@ module.exports = (function() {
return query; return query;
}, },
/* handleSequelizeMethod: function (smth, tableName, factory, options, prepend) {
Takes something and transforms it into values of a where condition. var self = this
*/ , result;
getWhereConditions: function(smth, tableName, factory, options, prepend) {
var result = null
, where = {}
, self = this;
if (Array.isArray(tableName)) {
tableName = tableName[0];
if (Array.isArray(tableName)) {
tableName = tableName[1];
}
}
options = options || {};
if (typeof prepend === 'undefined') {
prepend = true;
}
if ((smth instanceof Utils.and) || (smth instanceof Utils.or)) { if ((smth instanceof Utils.and) || (smth instanceof Utils.or)) {
var connector = (smth instanceof Utils.and) ? ' AND ' : ' OR '; var connector = (smth instanceof Utils.and) ? ' AND ' : ' OR ';
...@@ -1242,12 +1226,22 @@ module.exports = (function() { ...@@ -1242,12 +1226,22 @@ module.exports = (function() {
result = result.length && '(' + result + ')' || undefined; result = result.length && '(' + result + ')' || undefined;
} else if (smth instanceof Utils.where) { } else if (smth instanceof Utils.where) {
var value = smth.logic var value = smth.logic
, key = this.quoteTable(smth.attribute.Model.name) + '.' + this.quoteIdentifier(smth.attribute.fieldName) , key
, logic , logic
, _result = [] , _result = []
, _value; , _value;
if (_.isObject(value)) { if (smth.attribute._isSequelizeMethod) {
key = this.getWhereConditions(smth.attribute, tableName, factory, options, prepend);
} else {
key = this.quoteTable(smth.attribute.Model.name) + '.' + this.quoteIdentifier(smth.attribute.fieldName);
}
if (value._isSequelizeMethod) {
value = this.getWhereConditions(value, tableName, factory, options, prepend);
result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(smth.comparator);
} else if (_.isObject(value)) {
if (value.join) { if (value.join) {
//using as sentinel for join column => value //using as sentinel for join column => value
result = [key, value.join].join('='); result = [key, value.join].join('=');
...@@ -1265,16 +1259,70 @@ module.exports = (function() { ...@@ -1265,16 +1259,70 @@ module.exports = (function() {
value = this.escape(value); value = this.escape(value);
} }
result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join('='); result = (value === 'NULL') ? key + ' IS NULL' : [key, value].join(' ' + smth.comparator + ' ');
}
} else if (smth instanceof Utils.literal) {
result = smth.val;
} else if (smth instanceof Utils.cast) {
if (smth.val._isSequelizeMethod) {
result = this.handleSequelizeMethod(smth.val, tableName, factory, options, prepend);
} else {
result = this.escape(smth.val);
}
result = 'CAST(' + result + ' AS ' + smth.type.toUpperCase() + ')';
} else if (smth instanceof Utils.fn) {
result = smth.fn + '(' + smth.args.map(function(arg) {
if (arg._isSequelizeMethod) {
return self.handleSequelizeMethod(arg, tableName, factory, options, prepend);
} else {
return self.escape(arg);
}
}).join(', ') + ')';
} else if (smth instanceof Utils.col) {
if (Array.isArray(smth.col)) {
if (!factory) {
throw new Error('Cannot call Sequelize.col() with array outside of order / group clause');
}
} else if (smth.col.indexOf('*') === 0) {
return '*';
}
return this.quote(smth.col, factory);
} else {
result = smth.toString(this, factory);
}
return result;
},
/*
Takes something and transforms it into values of a where condition.
*/
getWhereConditions: function(smth, tableName, factory, options, prepend) {
var result = null
, where = {}
, self = this;
if (Array.isArray(tableName)) {
tableName = tableName[0];
if (Array.isArray(tableName)) {
tableName = tableName[1];
} }
} else if (smth instanceof Utils.json) { }
result = smth.toString(this);
options = options || {};
if (typeof prepend === 'undefined') {
prepend = true;
}
if (smth && smth._isSequelizeMethod === true) { // Checking a property is cheaper than a lot of instanceof calls
result = this.handleSequelizeMethod(smth, tableName, factory, options, prepend);
} else if (Utils._.isPlainObject(smth)) { } else if (Utils._.isPlainObject(smth)) {
if (prepend) { if (prepend) {
if (tableName) options.keysEscaped = true; if (tableName) options.keysEscaped = true;
smth = this.prependTableNameToHash(tableName, smth); smth = this.prependTableNameToHash(tableName, smth);
} }
result = this.hashToWhereConditions(smth, factory, options); result = this.hashToWhereConditions(smth, factory, options);
} else if (typeof smth === 'number') { } else if (typeof smth === 'number') {
var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : []; var primaryKeys = !!factory ? Object.keys(factory.primaryKeys) : [];
...@@ -1316,7 +1364,7 @@ module.exports = (function() { ...@@ -1316,7 +1364,7 @@ module.exports = (function() {
for (var key in hash) { for (var key in hash) {
if (key.indexOf('.') === -1) { if (key.indexOf('.') === -1) {
if (tableName instanceof Utils.literal) { if (tableName instanceof Utils.literal) {
_hash[tableName + '.' + this.quoteIdentifier(key)] = hash[key]; _hash[tableName.val + '.' + this.quoteIdentifier(key)] = hash[key];
} else { } else {
_hash[this.quoteTable(tableName) + '.' + this.quoteIdentifier(key)] = hash[key]; _hash[this.quoteTable(tableName) + '.' + this.quoteIdentifier(key)] = hash[key];
} }
...@@ -1473,8 +1521,8 @@ module.exports = (function() { ...@@ -1473,8 +1521,8 @@ module.exports = (function() {
var _key var _key
, _value = null; , _value = null;
if (value instanceof Utils.asIs) { if (value && value._isSequelizeMethod === true && (value instanceof Utils.literal)) {
result.push(value.toString(this)); result.push(value.val);
return; return;
} }
......
...@@ -6,6 +6,7 @@ var Utils = require('../../utils') ...@@ -6,6 +6,7 @@ var Utils = require('../../utils')
, DataTypes = require('../../data-types') , DataTypes = require('../../data-types')
, SqlString = require('../../sql-string') , SqlString = require('../../sql-string')
, tables = {} , tables = {}
, AbstractQueryGenerator = require('../abstract/query-generator')
, primaryKeys = {}; , primaryKeys = {};
module.exports = (function() { module.exports = (function() {
...@@ -105,6 +106,59 @@ module.exports = (function() { ...@@ -105,6 +106,59 @@ module.exports = (function() {
}); });
}, },
// A recursive parser for nested where conditions
parseConditionObject: function(_conditions, path) {
var self = this;
path = path || [];
return Utils._.reduce(_conditions, function (r, v, k) { // result, key, value
if (Utils._.isObject(v)) {
r = r.concat(self.parseConditionObject(v, path.concat(k))); // Recursively parse objects
} else {
r.push({ path: path.concat(k), value: v });
}
return r;
}, []);
},
handleSequelizeMethod: function (smth, tableName, factory, options, prepend) {
var _ = Utils._;
if (smth instanceof Utils.json) {
// Parse nested object
if (smth.conditions) {
var conditions = _.map(this.parseConditionObject(smth.conditions), function generateSql(condition) {
return util.format("%s#>>'{%s}' = '%s'",
_.first(condition.path),
_.rest(condition.path).join(','),
condition.value);
});
return conditions.join(' and ');
} else if (smth.path) {
var str;
// Allow specifying conditions using the postgres json syntax
if (_.any(['->', '->>', '#>'], _.partial(_.contains, smth.path))) {
str = smth.path;
} else {
// Also support json dot notation
var path = smth.path.split('.');
str = util.format("%s#>>'{%s}'",
_.first(path),
_.rest(path).join(','));
}
if (smth.value) {
str += util.format(" = %s", this.escape(smth.value));
}
return str;
}
} else {
return AbstractQueryGenerator.handleSequelizeMethod.call(this, smth, tableName, factory, options, prepend);
}
},
uniqueConstraintMapping: { uniqueConstraintMapping: {
code: '23505', code: '23505',
map: function(str) { map: function(str) {
...@@ -795,7 +849,7 @@ module.exports = (function() { ...@@ -795,7 +849,7 @@ module.exports = (function() {
*/ */
escape: function(value, field) { escape: function(value, field) {
if (value && value._isSequelizeMethod) { if (value && value._isSequelizeMethod) {
return value.toString(this); return this.handleSequelizeMethod(value);
} }
if (Utils._.isObject(value) && field && (field.type === DataTypes.HSTORE || field.type === DataTypes.ARRAY(DataTypes.HSTORE))) { if (Utils._.isObject(value) && field && (field.type === DataTypes.HSTORE || field.type === DataTypes.ARRAY(DataTypes.HSTORE))) {
...@@ -830,5 +884,5 @@ module.exports = (function() { ...@@ -830,5 +884,5 @@ module.exports = (function() {
} }
}; };
return Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator); return Utils._.extend(Utils._.clone(AbstractQueryGenerator), QueryGenerator);
})(); })();
...@@ -478,7 +478,7 @@ module.exports = (function() { ...@@ -478,7 +478,7 @@ module.exports = (function() {
this.runHooks('beforeDefine', attributes, options); this.runHooks('beforeDefine', attributes, options);
modelName = options.modelName; modelName = options.modelName;
delete options.modelName; delete options.modelName;
var factory = new Model(modelName, attributes, options); var factory = new Model(modelName, attributes, options);
this.modelManager.addDAO(factory.init(this.modelManager)); this.modelManager.addDAO(factory.init(this.modelManager));
...@@ -832,14 +832,10 @@ module.exports = (function() { ...@@ -832,14 +832,10 @@ module.exports = (function() {
* @since v2.0.0-dev3 * @since v2.0.0-dev3
* @return {Sequelize.literal} * @return {Sequelize.literal}
*/ */
Sequelize.literal = Sequelize.prototype.literal = function(val) { Sequelize.literal = Sequelize.asIs = Sequelize.prototype.asIs = Sequelize.prototype.literal = function(val) {
return new Utils.literal(val); return new Utils.literal(val);
}; };
Sequelize.asIs = Sequelize.prototype.asIs = function(val) {
return new Utils.asIs(val);
};
/** /**
* An AND query * An AND query
* @see {Model#find} * @see {Model#find}
...@@ -880,22 +876,23 @@ module.exports = (function() { ...@@ -880,22 +876,23 @@ module.exports = (function() {
}; };
/* /*
* A way of specifying attr = condition. Mostly used internally * A way of specifying attr = condition.
*
* The attr can either be an object taken from `Model.rawAttributes` (for example `Model.rawAttributes.id` or `Model.rawAttributes.name`). The
* attribute should be defined in your model definition. The attribute can also be an object from one of the sequelize utility functions (`sequelize.fn`, `sequelize.col` et.c).
*
* @see {Model#find} * @see {Model#find}
* *
* @param {string} attr The attribute * @param {Object} attr The attribute, which can be either an attribute object from `Model.rawAttributes` or a sequelize object, for example an instance of `sequelize.fn`. For simple string attributes, use the POJO syntax
* @param {String|Object} condition The condition. Can be both a simply type, or a further condition (`.or`, `.and`, `.literal` etc.) * @param {string} [comparator='=']
* @param {String|Object} logic The condition. Can be both a simply type, or a further condition (`.or`, `.and`, `.literal` etc.)
* @method where * @method where
* @alias condition * @alias condition
* @since v2.0.0-dev3 * @since v2.0.0-dev3
* @return {Sequelize.where} * @return {Sequelize.where}
*/ */
Sequelize.where = Sequelize.prototype.where = function() { Sequelize.where = Sequelize.condition = Sequelize.prototype.condition = Sequelize.prototype.where = function(attr, comparator, logic) {
return new Utils.where(Array.prototype.slice.call(arguments)); return new Utils.where(attr, comparator, logic);
};
Sequelize.condition = Sequelize.prototype.condition = function() {
return new Utils.condition(Array.prototype.slice.call(arguments));
}; };
/** /**
......
...@@ -535,10 +535,6 @@ var Utils = module.exports = { ...@@ -535,10 +535,6 @@ var Utils = module.exports = {
this.val = val; this.val = val;
}, },
asIs: function(val) {
this.val = val;
},
and: function(args) { and: function(args) {
this.args = args; this.args = args;
}, },
...@@ -558,8 +554,14 @@ var Utils = module.exports = { ...@@ -558,8 +554,14 @@ var Utils = module.exports = {
} }
}, },
where: function(attribute, logic) { where: function(attribute, comparator, logic) {
if (logic === undefined) {
logic = comparator;
comparator = '=';
}
this.attribute = attribute; this.attribute = attribute;
this.comparator = comparator;
this.logic = logic; this.logic = logic;
}, },
...@@ -575,8 +577,6 @@ var Utils = module.exports = { ...@@ -575,8 +577,6 @@ var Utils = module.exports = {
} }
}; };
Utils.condition = Utils.where;
Utils.and.prototype._isSequelizeMethod = Utils.and.prototype._isSequelizeMethod =
Utils.or.prototype._isSequelizeMethod = Utils.or.prototype._isSequelizeMethod =
Utils.where.prototype._isSequelizeMethod = Utils.where.prototype._isSequelizeMethod =
...@@ -586,93 +586,6 @@ Utils.fn.prototype._isSequelizeMethod = ...@@ -586,93 +586,6 @@ Utils.fn.prototype._isSequelizeMethod =
Utils.col.prototype._isSequelizeMethod = Utils.col.prototype._isSequelizeMethod =
Utils.json.prototype._isSequelizeMethod = true; Utils.json.prototype._isSequelizeMethod = true;
// I know this may seem silly, but this gives us the ability to recognize whether
// or not we should be escaping or if we should trust the user. Basically, it
// keeps things in perspective and organized.
Utils.literal.prototype.toString = function() {
return this.val;
};
Utils.asIs.prototype = Utils.literal.prototype;
Utils.cast.prototype.toString = function(queryGenerator) {
if (!this.val instanceof Utils.fn && !this.val instanceof Utils.col && !this.val instanceof Utils.literal) {
this.val = queryGenerator.escape(this.val);
} else {
this.val = this.val.toString(queryGenerator);
}
return 'CAST(' + this.val + ' AS ' + this.type.toUpperCase() + ')';
};
Utils.fn.prototype.toString = function(queryGenerator, parentModel) {
return this.fn + '(' + this.args.map(function(arg) {
if (arg._isSequelizeMethod) {
return arg.toString(queryGenerator, parentModel);
} else {
return queryGenerator.escape(arg);
}
}).join(', ') + ')';
};
Utils.col.prototype.toString = function(queryGenerator, parentModel) {
if (Array.isArray(this.col)) {
if (!parentModel) {
throw new Error('Cannot call Sequelize.col() with array outside of order / group clause');
}
} else if (this.col.indexOf('*') === 0) {
return '*';
}
return queryGenerator.quote(this.col, parentModel);
};
Utils.json.prototype.toString = function (queryGenerator) {
var _ = Utils._;
// A recursive parser for nested where conditions
function parseConditionObject(_conditions, path) {
path = path || [];
return _.reduce(_conditions, function (r, v, k) { // result, key, value
if (_.isObject(v)) {
r = r.concat(parseConditionObject(v, path.concat(k))); // Recursively parse objects
} else {
r.push({ path: path.concat(k), value: v });
}
return r;
}, []);
}
// Parse nested object
if (this.conditions) {
var conditions = _.map(parseConditionObject(this.conditions), function generateSql(condition) {
// TODO: Move this postgres specific logic to a more appropriate place
return util.format("%s#>>'{%s}' = '%s'",
_.first(condition.path),
_.rest(condition.path).join(','),
condition.value);
});
return conditions.join(' and ');
} else if (this.path) {
var str;
// Allow specifying conditions using the postgres json syntax
if (_.any(['->', '->>', '#>'], _.partial(_.contains, this.path))) { // TODO: Move postgres stuff somewhere else
str = this.path;
} else {
// Also support json dot notation
var path = this.path.split('.');
str = util.format("%s#>>'{%s}'",
_.first(path),
_.rest(path).join(','));
}
if (this.value) {
str += util.format(" = %s", queryGenerator.escape(this.value));
}
return str;
}
};
Utils.CustomEventEmitter = require(__dirname + '/emitters/custom-event-emitter'); Utils.CustomEventEmitter = require(__dirname + '/emitters/custom-event-emitter');
Utils.Promise = require(__dirname + '/promise'); Utils.Promise = require(__dirname + '/promise');
Utils.QueryChainer = require(__dirname + '/query-chainer'); Utils.QueryChainer = require(__dirname + '/query-chainer');
...@@ -314,7 +314,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -314,7 +314,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
this.User.create({ this.User.create({
intVal: this.sequelize.cast('1', type) intVal: this.sequelize.cast('1', type)
}).on('sql', function (sql) { }).on('sql', function (sql) {
expect(sql).to.match(new RegExp('CAST\\(1 AS ' + type.toUpperCase() + '\\)')) expect(sql).to.match(new RegExp("CAST\\('1' AS " + type.toUpperCase() + '\\)'))
_done() _done()
}) })
.success(function (user) { .success(function (user) {
...@@ -367,19 +367,6 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -367,19 +367,6 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
it('is possible for .literal() to contain other utility functions', function (done) {
var self = this
this.User.create({
intVal: this.sequelize.literal(this.sequelize.cast('1-2', (Support.dialectIsMySQL() ? 'SIGNED' : 'INTEGER')))
}).success(function (user) {
self.User.find(user.id).success(function (user) {
expect(user.intVal).to.equal(-1)
done()
})
})
})
it('is possible to use funtions when creating an instance', function (done) { it('is possible to use funtions when creating an instance', function (done) {
var self = this var self = this
this.User.create({ this.User.create({
......
...@@ -195,6 +195,32 @@ if (Support.dialectIsMySQL()) { ...@@ -195,6 +195,32 @@ if (Support.dialectIsMySQL()) {
context: QueryGenerator, context: QueryGenerator,
needsSequelize: true needsSequelize: true
}, { }, {
title: 'sequelize.where with .fn as attribute and default comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'jan'),
{ type: 1 }
)
};
}],
expectation: "SELECT * FROM `myTable` WHERE (LOWER(`user`.`name`) = 'jan' AND `myTable`.`type`=1);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'sequelize.where with .fn as attribute and LIKE comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'LIKE', '%t%'),
{ type: 1 }
)
};
}],
expectation: "SELECT * FROM `myTable` WHERE (LOWER(`user`.`name`) LIKE '%t%' AND `myTable`.`type`=1);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'single string argument is not quoted', title: 'single string argument is not quoted',
arguments: ['myTable', {group: "name"}], arguments: ['myTable', {group: "name"}],
expectation: "SELECT * FROM `myTable` GROUP BY name;", expectation: "SELECT * FROM `myTable` GROUP BY name;",
......
...@@ -257,7 +257,33 @@ if (dialect.match(/^postgres/)) { ...@@ -257,7 +257,33 @@ if (dialect.match(/^postgres/)) {
arguments: ['myTable', {order: [[{raw: 'f1(f2(id))'},'DESC']]}], arguments: ['myTable', {order: [[{raw: 'f1(f2(id))'},'DESC']]}],
expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2(id)) DESC;', expectation: 'SELECT * FROM "myTable" ORDER BY f1(f2(id)) DESC;',
context: QueryGenerator context: QueryGenerator
}, {
title: 'sequelize.where with .fn as attribute and default comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'jan'),
{ type: 1 }
)
};
}],
expectation: 'SELECT * FROM "myTable" WHERE (LOWER("user"."name") = \'jan\' AND "myTable"."type"=1);',
context: QueryGenerator,
needsSequelize: true
}, { }, {
title: 'sequelize.where with .fn as attribute and LIKE comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'LIKE', '%t%'),
{ type: 1 }
)
};
}],
expectation: 'SELECT * FROM "myTable" WHERE (LOWER("user"."name") LIKE \'%t%\' AND "myTable"."type"=1);',
context: QueryGenerator,
needsSequelize: true
},{
title: 'functions can take functions as arguments', title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
......
...@@ -163,6 +163,32 @@ if (dialect === 'sqlite') { ...@@ -163,6 +163,32 @@ if (dialect === 'sqlite') {
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(id)) DESC;", expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(id)) DESC;",
context: QueryGenerator context: QueryGenerator
}, { }, {
title: 'sequelize.where with .fn as attribute and default comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'jan'),
{ type: 1 }
)
};
}],
expectation: "SELECT * FROM `myTable` WHERE (LOWER(`user`.`name`) = 'jan' AND `myTable`.`type`=1);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'sequelize.where with .fn as attribute and LIKE comparator',
arguments: ['myTable', function (sequelize) {
return {
where: sequelize.and(
sequelize.where(sequelize.fn('LOWER', sequelize.col('user.name')), 'LIKE', '%t%'),
{ type: 1 }
)
};
}],
expectation: "SELECT * FROM `myTable` WHERE (LOWER(`user`.`name`) LIKE '%t%' AND `myTable`.`type`=1);",
context: QueryGenerator,
needsSequelize: true
}, {
title: 'functions can take functions as arguments', title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) { arguments: ['myTable', function (sequelize) {
return { return {
......
...@@ -147,37 +147,39 @@ describe(Support.getTestDialectTeaser("Utils"), function() { ...@@ -147,37 +147,39 @@ describe(Support.getTestDialectTeaser("Utils"), function() {
}); });
}); });
describe('json', function () { if (Support.getTestDialect() === 'postgres') {
var queryGeneratorStub = { escape: function (value) { return "'" + value + "'"; } }; describe('json', function () {
var queryGenerator = require('../lib/dialects/postgres/query-generator.js');
it('successfully parses a complex nested condition hash', function() {
var conditions = { it('successfully parses a complex nested condition hash', function() {
metadata: { var conditions = {
language: 'icelandic', metadata: {
pg_rating: { 'dk': 'G' } language: 'icelandic',
}, pg_rating: { 'dk': 'G' }
another_json_field: { x: 1 } },
}; another_json_field: { x: 1 }
var expected = "metadata#>>'{language}' = 'icelandic' and metadata#>>'{pg_rating,dk}' = 'G' and another_json_field#>>'{x}' = '1'"; };
expect((new Utils.json(conditions)).toString(queryGeneratorStub)).to.deep.equal(expected); var expected = "metadata#>>'{language}' = 'icelandic' and metadata#>>'{pg_rating,dk}' = 'G' and another_json_field#>>'{x}' = '1'";
}); expect(queryGenerator.handleSequelizeMethod(new Utils.json(conditions))).to.deep.equal(expected);
});
it('successfully parses a string using dot notation', function () { it('successfully parses a string using dot notation', function () {
var path = 'metadata.pg_rating.dk'; var path = 'metadata.pg_rating.dk';
expect((new Utils.json(path)).toString(queryGeneratorStub)).to.equal("metadata#>>'{pg_rating,dk}'"); expect(queryGenerator.handleSequelizeMethod(new Utils.json(path))).to.equal("metadata#>>'{pg_rating,dk}'");
}); });
it('allows postgres json syntax', function () { it('allows postgres json syntax', function () {
var path = 'metadata->pg_rating->>dk'; var path = 'metadata->pg_rating->>dk';
expect((new Utils.json(path)).toString(queryGeneratorStub)).to.equal(path); expect(queryGenerator.handleSequelizeMethod(new Utils.json(path))).to.equal(path);
}); });
it('can take a value to compare against', function () { it('can take a value to compare against', function () {
var path = 'metadata.pg_rating.is'; var path = 'metadata.pg_rating.is';
var value = 'U'; var value = 'U';
expect((new Utils.json(path, value)).toString(queryGeneratorStub)).to.equal("metadata#>>'{pg_rating,is}' = 'U'"); expect(queryGenerator.handleSequelizeMethod(new Utils.json(path, value))).to.equal("metadata#>>'{pg_rating,is}' = 'U'");
});
}); });
}); }
describe('inflection', function () { describe('inflection', function () {
it('works better than lingo ;)', function () { it('works better than lingo ;)', function () {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!