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

Commit 89cdf3a9 by Mick Hansen

Merge branch 'master' of github.com:sequelize/sequelize

2 parents 92bdd72f bdd547d0
......@@ -295,6 +295,7 @@ HasMany.prototype.get = function(instances, options) {
where[association.foreignKey] = {
$in: values
};
delete options.groupedLimit;
}
} else {
where[association.foreignKey] = instance.get(association.source.primaryKeyAttribute, {raw: true});
......
......@@ -30,7 +30,14 @@ Query.prototype.run = function(sql, parameters) {
var self = this;
this.sql = sql;
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
//do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark;
if (!benchmark) {
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
} else {
var queryBegin = Date.now();
}
var promise = new Utils.Promise(function(resolve, reject) {
// TRANSACTION SUPPORT
......@@ -63,6 +70,11 @@ Query.prototype.run = function(sql, parameters) {
var results = [];
var request = new self.connection.lib.Request(self.sql, function(err) {
if (benchmark) {
self.sequelize.log('Executed (' + (self.connection.uuid || 'default') + '): ' + self.sql + ' in ' + (Date.now() - queryBegin) + 'ms', self.options);
}
if (err) {
err.sql = sql;
reject(self.formatError(err));
......
......@@ -26,10 +26,22 @@ Query.prototype.run = function(sql, parameters) {
var self = this;
this.sql = sql;
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
//do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark;
if (!benchmark) {
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
} else {
var queryBegin = Date.now();
}
var promise = new Utils.Promise(function(resolve, reject) {
self.connection.query(self.sql, function(err, results) {
if (benchmark) {
self.sequelize.log('Executed (' + (self.connection.uuid || 'default') + '): ' + self.sql + ' in ' + (Date.now() - queryBegin) + 'ms', self.options);
}
if (err) {
err.sql = sql;
......
......@@ -62,7 +62,14 @@ Query.prototype.run = function(sql, parameters) {
, query = ((parameters && parameters.length) ? this.client.query(this.sql, parameters) : this.client.query(this.sql))
, rows = [];
this.sequelize.log('Executing (' + (this.client.uuid || 'default') + '): ' + this.sql, this.options);
//do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark;
if (!benchmark) {
this.sequelize.log('Executing (' + (this.client.uuid || 'default') + '): ' + this.sql, this.options);
} else {
var queryBegin = Date.now();
}
var promise = new Promise(function(resolve, reject) {
query.on('row', function(row) {
......@@ -82,6 +89,11 @@ Query.prototype.run = function(sql, parameters) {
});
query.on('end', function(result) {
if (benchmark) {
self.sequelize.log('Executed (' + (self.client.uuid || 'default') + '): ' + self.sql + ' in ' + (Date.now() - queryBegin) + 'ms', self.options);
}
if (receivedError) {
return;
}
......
......@@ -84,7 +84,14 @@ Query.prototype.run = function(sql, parameters) {
this.sql = sql;
}
this.sequelize.log('Executing (' + (this.database.uuid || 'default') + '): ' + this.sql, this.options);
//do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark;
if (!benchmark) {
this.sequelize.log('Executing (' + (this.database.uuid || 'default') + '): ' + this.sql, this.options);
} else {
var queryBegin = Date.now();
}
promise = new Promise(function(resolve) {
var columnTypes = {};
......@@ -95,6 +102,11 @@ Query.prototype.run = function(sql, parameters) {
} else {
resolve(new Promise(function(resolve, reject) {
var afterExecute = function(err, results) {
if (benchmark) {
self.sequelize.log('Executed (' + (self.database.uuid || 'default') + '): ' + self.sql + ' in ' + (Date.now() - queryBegin) + 'ms', self.options);
}
if (err) {
err.sql = self.sql;
reject(self.formatError(err));
......
......@@ -480,7 +480,7 @@ Instance.prototype._setInclude = function(key, value, options) {
* This error will have a property for each of the fields for which validation failed, with the error message for that field.
*
* @param {Object} [options]
* @param {Object} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
* @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
* @param {Boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
* @param {Boolean} [options.validate=true] If false, validations won't be run.
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
......
......@@ -992,7 +992,7 @@ Model.prototype.sync = function(options) {
});
return Promise.map(indexes, function (index) {
return self.QueryInterface.addIndex(self.getTableName(options), _.assign({logging: options.logging}, index), self.tableName);
return self.QueryInterface.addIndex(self.getTableName(options), _.assign({logging: options.logging, benchmark: options.benchmark}, index), self.tableName);
});
}).then(function () {
if (options.hooks) {
......@@ -1003,9 +1003,10 @@ Model.prototype.sync = function(options) {
/**
* Drop the table represented by this Model
* @param {Object} [options]
* @param {Boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Object} [options]
* @param {Boolean} [options.cascade=false] Also drop all objects depending on this table, such as views. Only works in postgres
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @return {Promise}
*/
Model.prototype.drop = function(options) {
......@@ -1024,6 +1025,7 @@ Model.prototype.dropSchema = function(schema) {
* @param {Object} [options]
* @param {String} [options.schemaDelimiter='.'] The character(s) that separates the schema name from the table name
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @return {this}
*/
Model.prototype.schema = function(schema, options) { // testhint options:none
......@@ -1057,6 +1059,7 @@ Model.prototype.schema = function(schema, options) { // testhint options:none
*
* @param {Object} [options] The hash of options from any query. You can use one model to access tables with matching schemas by overriding `getTableName` and using custom key/values to alter the name of the table. (eg. subscribers_1, subscribers_2)
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @return {String|Object}
*/
Model.prototype.getTableName = function(options) { // testhint options:none
......@@ -1304,6 +1307,7 @@ Model.prototype.all = function(options) {
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Object} [options.having]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @see {Sequelize#query}
* @return {Promise<Array<Instance>>}
......@@ -1504,7 +1508,8 @@ Model.prototype.find = Model.prototype.findOne;
* @param {DataType|String} [options.dataType] The type of the result. If `field` is a field in this Model, the default will be the type of that field, otherwise defaults to float.
* @param {boolean} [options.distinct] Applies DISTINCT to the field being aggregated over
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {boolean} [options.plain] When `true`, the first returned value of `aggregateFunction` is cast to `dataType` and returned. If additional attributes are specified, along with `group` clauses, set `plain` to `false` to return all values of all returned rows. Defaults to `true`
* @param {Boolean} [options.plain] When `true`, the first returned value of `aggregateFunction` is cast to `dataType` and returned. If additional attributes are specified, along with `group` clauses, set `plain` to `false` to return all values of all returned rows. Defaults to `true`
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise<options.dataType|object>} Returns the aggregate result cast to `options.dataType`, unless `options.plain` is false, in which case the complete data result is returned.
*/
......@@ -1558,6 +1563,7 @@ Model.prototype.aggregate = function(attribute, aggregateFunction, options) {
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise<Integer>}
*/
......@@ -1792,7 +1798,8 @@ Model.prototype.bulkBuild = function(valueSets, options) { // testhint options:n
* @param {String} [options.onDuplicate]
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise<Instance>}
*/
......@@ -1817,6 +1824,7 @@ Model.prototype.create = function(values, options) {
* @param {Object} [options.defaults] Default values to use if building a new instance
* @param {Object} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise<Instance,initialized>}
* @alias findOrBuild
......@@ -1993,6 +2001,7 @@ Model.prototype.findCreateFind = function(options) {
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @alias insertOrUpdate
* @return {Promise<created>} Returns a boolean indicating whether the row was created or updated.
......@@ -2056,6 +2065,7 @@ Model.prototype.insertOrUpdate = Model.prototype.upsert;
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.returning=false] Append RETURNING * to get back auto generated values (Postgres only)
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise<Array<Instance>>}
*/
......@@ -2205,7 +2215,9 @@ Model.prototype.bulkCreate = function(records, options) {
* @param {Boolean|function} [options.cascade = false] Only used in conjunction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {Boolean|function} [options.logging] A function that logs sql queries, or false for no logging
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*
* @return {Promise}
*
* @see {Model#destroy} for more information
......@@ -2229,6 +2241,7 @@ Model.prototype.truncate = function(options) {
* @param {Boolean} [options.cascade=false] Only used in conjunction with TRUNCATE. Truncates all tables that have foreign-key references to the named table, or to any tables added to the group due to CASCADE.
* @param {Transaction} [options.transaction] Transaction to run query under
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @return {Promise<Integer>} The number of destroyed rows
*/
Model.prototype.destroy = function(options) {
......@@ -2259,7 +2272,7 @@ Model.prototype.destroy = function(options) {
}).then(function() {
// Get daos and run beforeDestroy hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging}).map(function(instance) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark}).map(function(instance) {
return self.runHooks('beforeDestroy', instance, options).then(function() {
return instance;
});
......@@ -2308,6 +2321,7 @@ Model.prototype.destroy = function(options) {
* @param {Boolean} [options.individualHooks=false] If set to true, restore will find all records within the where parameter and will execute before / after bulkRestore hooks on each row
* @param {Number} [options.limit] How many rows to undelete
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @param {Transaction} [options.transaction] Transaction to run query under
*
* @return {Promise<undefined>}
......@@ -2335,7 +2349,7 @@ Model.prototype.restore = function(options) {
}).then(function() {
// Get daos and run beforeRestore hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, paranoid: false}).map(function(instance) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark, paranoid: false}).map(function(instance) {
return self.runHooks('beforeRestore', instance, options).then(function() {
return instance;
});
......@@ -2385,6 +2399,7 @@ Model.prototype.restore = function(options) {
* @param {Boolean} [options.returning=false] Return the affected rows (only for postgres)
* @param {Number} [options.limit] How many rows to update (only for mysql and mariadb)
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
* @param {Transaction} [options.transaction] Transaction to run query under
*
* @return {Promise<Array<affectedCount,affectedRows>>}
......@@ -2470,7 +2485,7 @@ Model.prototype.update = function(values, options) {
// Get instances and run beforeUpdate hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging}).then(function(_instances) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark}).then(function(_instances) {
instances = _instances;
if (!instances.length) {
return [];
......
......@@ -81,7 +81,8 @@ var url = require('url')
* @param {Boolean} [options.quoteIdentifiers=true] Set to `false` to make table names and attributes case-insensitive on Postgres and skip double quoting of them.
* @param {String} [options.transactionType='DEFERRED'] Set the default transaction type. See `Sequelize.Transaction.TYPES` for possible options. Sqlite only.
* @param {String} [options.isolationLevel='REPEATABLE_READ'] Set the default transaction isolation level. See `Sequelize.Transaction.ISOLATION_LEVELS` for possible options.
* @param {Boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like
* @param {Boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like.
* @param {Boolean} [options.benchmark=false] Print query execution time in milliseconds when logging SQL.
*/
/**
......@@ -147,7 +148,8 @@ var Sequelize = function(database, username, password, options) {
transactionType: Transaction.TYPES.DEFERRED,
isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ,
databaseVersion: 0,
typeValidation: false
typeValidation: false,
benchmark: false
}, options || {});
if (this.options.dialect === 'postgresql') {
......
......@@ -185,8 +185,9 @@ Transaction.prototype.commit = function() {
.finally(function() {
self.finished = 'commit';
if (!self.parent) {
self.cleanup();
return self.cleanup();
}
return null;
});
};
......@@ -212,8 +213,9 @@ Transaction.prototype.rollback = function() {
.finally(function() {
self.finished = 'rollback';
if (!self.parent) {
self.cleanup();
return self.cleanup();
}
return null;
});
};
......@@ -241,6 +243,7 @@ Transaction.prototype.prepareEnvironment = function() {
if (self.sequelize.constructor.cls) {
self.sequelize.constructor.cls.set('transaction', self);
}
return null;
});
};
......
......@@ -38,7 +38,7 @@
"generic-pool": "2.3.0",
"inflection": "^1.6.0",
"lodash": "^3.9.3",
"moment": "^2.11.0",
"moment": "^2.11.1",
"moment-timezone": "^0.5.0",
"node-uuid": "~1.4.4",
"semver": "^5.0.1",
......
......@@ -116,6 +116,100 @@ describe(Support.getTestDialectTeaser('HasMany'), function() {
});
});
it('should fetch multiple layers of associations with limit and order with separate=true', function () {
var User = this.sequelize.define('User', {})
, Task = this.sequelize.define('Task', {
title: DataTypes.STRING
})
, SubTask = this.sequelize.define('SubTask', {
title: DataTypes.STRING
});
User.Tasks = User.hasMany(Task, {as: 'tasks'});
Task.SubTasks = Task.hasMany(SubTask, {as: 'subtasks'});
return this.sequelize.sync({force: true}).then(function() {
return Promise.join(
User.create({
tasks: [
{title: 'b', subtasks: [
{title:'c'},
{title:'a'}
]},
{title: 'd'},
{title: 'c', subtasks: [
{title:'b'},
{title:'a'},
{title:'c'}
]},
{title: 'a', subtasks: [
{title:'c'},
{title:'a'},
{title:'b'}
]}
]
}, {
include: [{association: User.Tasks, include: [Task.SubTasks]}]
}),
User.create({
tasks: [
{title: 'a', subtasks: [
{title:'b'},
{title:'a'},
{title:'c'}
]},
{title: 'c', subtasks: [
{title:'a'}
]},
{title: 'b', subtasks: [
{title:'a'},
{title:'b'}
]}
]
}, {
include: [{association: User.Tasks, include: [Task.SubTasks]}]
})
);
}).then(function(users) {
return User.findAll(
{
include: [{
association: User.Tasks,
limit: 2,
order: [['title', 'ASC']],
separate: true,
as: 'tasks',
include: [{association: Task.SubTasks, order: [['title', 'DESC']], separate: true, as: 'subtasks'}]
}]
}).then(function(users) {
expect(users[0].tasks.length).to.equal(2);
expect(users[0].tasks[0].title).to.equal('a');
expect(users[0].tasks[0].subtasks.length).to.equal(3);
expect(users[0].tasks[0].subtasks[0].title).to.equal('c');
expect(users[0].tasks[0].subtasks[1].title).to.equal('b');
expect(users[0].tasks[0].subtasks[2].title).to.equal('a');
expect(users[0].tasks[1].title).to.equal('b');
expect(users[0].tasks[1].subtasks.length).to.equal(2);
expect(users[0].tasks[1].subtasks[0].title).to.equal('c');
expect(users[0].tasks[1].subtasks[1].title).to.equal('a');
expect(users[1].tasks.length).to.equal(2);
expect(users[1].tasks[0].title).to.equal('a');
expect(users[1].tasks[0].subtasks.length).to.equal(3);
expect(users[1].tasks[0].subtasks[0].title).to.equal('c');
expect(users[1].tasks[0].subtasks[1].title).to.equal('b');
expect(users[1].tasks[0].subtasks[2].title).to.equal('a');
expect(users[1].tasks[1].title).to.equal('b');
expect(users[1].tasks[1].subtasks.length).to.equal(2);
expect(users[1].tasks[1].subtasks[0].title).to.equal('b');
expect(users[1].tasks[1].subtasks[1].title).to.equal('a');
});
});
});
it('should fetch associations for multiple instances with limit and order and a belongsTo relation', function () {
var User = this.sequelize.define('User', {})
, Task = this.sequelize.define('Task', {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!