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

Commit 597fc8ae by Andy Edwards Committed by GitHub

refactor: replace bluebird .each (#12074)

1 parent 72a940d8
'use strict'; 'use strict';
const _ = require('lodash'); const _ = require('lodash');
const Promise = require('../../promise');
const sequelizeErrors = require('../../errors'); const sequelizeErrors = require('../../errors');
const QueryTypes = require('../../query-types'); const QueryTypes = require('../../query-types');
...@@ -27,17 +26,16 @@ const QueryTypes = require('../../query-types'); ...@@ -27,17 +26,16 @@ const QueryTypes = require('../../query-types');
@since 1.6.0 @since 1.6.0
@private @private
*/ */
function removeColumn(qi, tableName, attributeName, options) { async function removeColumn(qi, tableName, attributeName, options) {
options = options || {}; options = options || {};
return qi.describeTable(tableName, options).then(fields => { const fields = await qi.describeTable(tableName, options);
delete fields[attributeName]; delete fields[attributeName];
const sql = qi.QueryGenerator.removeColumnQuery(tableName, fields); const sql = qi.QueryGenerator.removeColumnQuery(tableName, fields);
const subQueries = sql.split(';').filter(q => q !== ''); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, subQuery => qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options))); for (const subQuery of subQueries) await qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options));
});
} }
exports.removeColumn = removeColumn; exports.removeColumn = removeColumn;
...@@ -55,18 +53,17 @@ exports.removeColumn = removeColumn; ...@@ -55,18 +53,17 @@ exports.removeColumn = removeColumn;
@since 1.6.0 @since 1.6.0
@private @private
*/ */
function changeColumn(qi, tableName, attributes, options) { async function changeColumn(qi, tableName, attributes, options) {
const attributeName = Object.keys(attributes)[0]; const attributeName = Object.keys(attributes)[0];
options = options || {}; options = options || {};
return qi.describeTable(tableName, options).then(fields => { const fields = await qi.describeTable(tableName, options);
fields[attributeName] = attributes[attributeName]; fields[attributeName] = attributes[attributeName];
const sql = qi.QueryGenerator.removeColumnQuery(tableName, fields); const sql = qi.QueryGenerator.removeColumnQuery(tableName, fields);
const subQueries = sql.split(';').filter(q => q !== ''); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, subQuery => qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options))); for (const subQuery of subQueries) await qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options));
});
} }
exports.changeColumn = changeColumn; exports.changeColumn = changeColumn;
...@@ -85,18 +82,17 @@ exports.changeColumn = changeColumn; ...@@ -85,18 +82,17 @@ exports.changeColumn = changeColumn;
@since 1.6.0 @since 1.6.0
@private @private
*/ */
function renameColumn(qi, tableName, attrNameBefore, attrNameAfter, options) { async function renameColumn(qi, tableName, attrNameBefore, attrNameAfter, options) {
options = options || {}; options = options || {};
return qi.describeTable(tableName, options).then(fields => { const fields = await qi.describeTable(tableName, options);
fields[attrNameAfter] = _.clone(fields[attrNameBefore]); fields[attrNameAfter] = _.clone(fields[attrNameBefore]);
delete fields[attrNameBefore]; delete fields[attrNameBefore];
const sql = qi.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields); const sql = qi.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields);
const subQueries = sql.split(';').filter(q => q !== ''); const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, subQuery => qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options))); for (const subQuery of subQueries) await qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options));
});
} }
exports.renameColumn = renameColumn; exports.renameColumn = renameColumn;
...@@ -108,45 +104,42 @@ exports.renameColumn = renameColumn; ...@@ -108,45 +104,42 @@ exports.renameColumn = renameColumn;
* *
* @private * @private
*/ */
function removeConstraint(qi, tableName, constraintName, options) { async function removeConstraint(qi, tableName, constraintName, options) {
let createTableSql; let createTableSql;
return qi.showConstraint(tableName, constraintName) const constraints = await qi.showConstraint(tableName, constraintName);
.then(constraints => { // sqlite can't show only one constraint, so we find here the one to remove
// sqlite can't show only one constraint, so we find here the one to remove const constraint = constraints.find(constaint => constaint.constraintName === constraintName);
const constraint = constraints.find(constaint => constaint.constraintName === constraintName);
if (!constraint) {
if (constraint) { throw new sequelizeErrors.UnknownConstraintError({
createTableSql = constraint.sql; message: `Constraint ${constraintName} on table ${tableName} does not exist`,
constraint.constraintName = qi.QueryGenerator.quoteIdentifier(constraint.constraintName); constraint: constraintName,
let constraintSnippet = `, CONSTRAINT ${constraint.constraintName} ${constraint.constraintType} ${constraint.constraintCondition}`; table: tableName
if (constraint.constraintType === 'FOREIGN KEY') {
const referenceTableName = qi.QueryGenerator.quoteTable(constraint.referenceTableName);
constraint.referenceTableKeys = constraint.referenceTableKeys.map(columnName => qi.QueryGenerator.quoteIdentifier(columnName));
const referenceTableKeys = constraint.referenceTableKeys.join(', ');
constraintSnippet += ` REFERENCES ${referenceTableName} (${referenceTableKeys})`;
constraintSnippet += ` ON UPDATE ${constraint.updateAction}`;
constraintSnippet += ` ON DELETE ${constraint.deleteAction}`;
}
createTableSql = createTableSql.replace(constraintSnippet, '');
createTableSql += ';';
return qi.describeTable(tableName, options);
}
throw new sequelizeErrors.UnknownConstraintError({
message: `Constraint ${constraintName} on table ${tableName} does not exist`,
constraint: constraintName,
table: tableName
});
})
.then(fields => {
const sql = qi.QueryGenerator._alterConstraintQuery(tableName, fields, createTableSql);
const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, subQuery => qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options)));
}); });
}
createTableSql = constraint.sql;
constraint.constraintName = qi.QueryGenerator.quoteIdentifier(constraint.constraintName);
let constraintSnippet = `, CONSTRAINT ${constraint.constraintName} ${constraint.constraintType} ${constraint.constraintCondition}`;
if (constraint.constraintType === 'FOREIGN KEY') {
const referenceTableName = qi.QueryGenerator.quoteTable(constraint.referenceTableName);
constraint.referenceTableKeys = constraint.referenceTableKeys.map(columnName => qi.QueryGenerator.quoteIdentifier(columnName));
const referenceTableKeys = constraint.referenceTableKeys.join(', ');
constraintSnippet += ` REFERENCES ${referenceTableName} (${referenceTableKeys})`;
constraintSnippet += ` ON UPDATE ${constraint.updateAction}`;
constraintSnippet += ` ON DELETE ${constraint.deleteAction}`;
}
createTableSql = createTableSql.replace(constraintSnippet, '');
createTableSql += ';';
const fields = await qi.describeTable(tableName, options);
const sql = qi.QueryGenerator._alterConstraintQuery(tableName, fields, createTableSql);
const subQueries = sql.split(';').filter(q => q !== '');
for (const subQuery of subQueries) await qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options));
} }
exports.removeConstraint = removeConstraint; exports.removeConstraint = removeConstraint;
...@@ -157,27 +150,22 @@ exports.removeConstraint = removeConstraint; ...@@ -157,27 +150,22 @@ exports.removeConstraint = removeConstraint;
* *
* @private * @private
*/ */
function addConstraint(qi, tableName, options) { async function addConstraint(qi, tableName, options) {
const constraintSnippet = qi.QueryGenerator.getConstraintSnippet(tableName, options); const constraintSnippet = qi.QueryGenerator.getConstraintSnippet(tableName, options);
const describeCreateTableSql = qi.QueryGenerator.describeCreateTableQuery(tableName); const describeCreateTableSql = qi.QueryGenerator.describeCreateTableQuery(tableName);
let createTableSql;
return qi.sequelize.query(describeCreateTableSql, Object.assign({}, options, { type: QueryTypes.SELECT, raw: true })) const constraints = await qi.sequelize.query(describeCreateTableSql, Object.assign({}, options, { type: QueryTypes.SELECT, raw: true }));
.then(constraints => { let sql = constraints[0].sql;
const sql = constraints[0].sql; const index = sql.length - 1;
const index = sql.length - 1; //Replace ending ')' with constraint snippet - Simulates String.replaceAt
//Replace ending ')' with constraint snippet - Simulates String.replaceAt //http://stackoverflow.com/questions/1431094
//http://stackoverflow.com/questions/1431094 const createTableSql = `${sql.substr(0, index)}, ${constraintSnippet})${sql.substr(index + 1)};`;
createTableSql = `${sql.substr(0, index)}, ${constraintSnippet})${sql.substr(index + 1)};`;
const fields = await qi.describeTable(tableName, options);
return qi.describeTable(tableName, options); sql = qi.QueryGenerator._alterConstraintQuery(tableName, fields, createTableSql);
}) const subQueries = sql.split(';').filter(q => q !== '');
.then(fields => {
const sql = qi.QueryGenerator._alterConstraintQuery(tableName, fields, createTableSql); for (const subQuery of subQueries) await qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options));
const subQueries = sql.split(';').filter(q => q !== '');
return Promise.each(subQueries, subQuery => qi.sequelize.query(`${subQuery};`, Object.assign({ raw: true }, options)));
});
} }
exports.addConstraint = addConstraint; exports.addConstraint = addConstraint;
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
const _ = require('lodash'); const _ = require('lodash');
const { logger } = require('./utils/logger'); const { logger } = require('./utils/logger');
const Promise = require('./promise');
const debug = logger.debugContext('hooks'); const debug = logger.debugContext('hooks');
const hookTypes = { const hookTypes = {
...@@ -89,7 +88,7 @@ const Hooks = { ...@@ -89,7 +88,7 @@ const Hooks = {
}); });
}, },
runHooks(hooks, ...hookArgs) { async runHooks(hooks, ...hookArgs) {
if (!hooks) throw new Error('runHooks requires at least 1 argument'); if (!hooks) throw new Error('runHooks requires at least 1 argument');
let hookType; let hookType;
...@@ -121,14 +120,14 @@ const Hooks = { ...@@ -121,14 +120,14 @@ const Hooks = {
} }
// asynchronous hooks (default) // asynchronous hooks (default)
return Promise.each(hooks, hook => { for (let hook of hooks) {
if (typeof hook === 'object') { if (typeof hook === 'object') {
hook = hook.fn; hook = hook.fn;
} }
debug(`running hook ${hookType}`); debug(`running hook ${hookType}`);
return hook.apply(this, hookArgs); await hook.apply(this, hookArgs);
}).then(() => undefined); }
}, },
/** /**
......
...@@ -1272,109 +1272,102 @@ class Model { ...@@ -1272,109 +1272,102 @@ class Model {
* *
* @returns {Promise<Model>} * @returns {Promise<Model>}
*/ */
static sync(options) { static async sync(options) {
options = Object.assign({}, this.options, options); options = Object.assign({}, this.options, options);
options.hooks = options.hooks === undefined ? true : !!options.hooks; options.hooks = options.hooks === undefined ? true : !!options.hooks;
const attributes = this.tableAttributes; const attributes = this.tableAttributes;
const rawAttributes = this.fieldRawAttributesMap; const rawAttributes = this.fieldRawAttributesMap;
return Promise.resolve().then(() => { if (options.hooks) {
if (options.hooks) { await this.runHooks('beforeSync', options);
return this.runHooks('beforeSync', options); }
} if (options.force) {
}).then(() => { await this.drop(options);
if (options.force) { }
return this.drop(options); await this.QueryInterface.createTable(this.getTableName(options), attributes, options, this);
}
}) if (options.alter) {
.then(() => this.QueryInterface.createTable(this.getTableName(options), attributes, options, this)) const tableInfos = await Promise.all([
.then(() => { this.QueryInterface.describeTable(this.getTableName(options)),
if (!options.alter) { this.QueryInterface.getForeignKeyReferencesForTable(this.getTableName(options))
return; ]);
const columns = tableInfos[0];
// Use for alter foreign keys
const foreignKeyReferences = tableInfos[1];
const removedConstraints = {};
for (const columnName in attributes) {
if (!Object.prototype.hasOwnProperty.call(attributes, columnName)) continue;
if (!columns[columnName] && !columns[attributes[columnName].field]) {
await this.QueryInterface.addColumn(this.getTableName(options), attributes[columnName].field || columnName, attributes[columnName]);
} }
return Promise.all([ }
this.QueryInterface.describeTable(this.getTableName(options)),
this.QueryInterface.getForeignKeyReferencesForTable(this.getTableName(options))
])
.then(tableInfos => {
const columns = tableInfos[0];
// Use for alter foreign keys
const foreignKeyReferences = tableInfos[1];
const changes = []; // array of promises to run
const removedConstraints = {};
_.each(attributes, (columnDesc, columnName) => {
if (!columns[columnName] && !columns[attributes[columnName].field]) {
changes.push(() => this.QueryInterface.addColumn(this.getTableName(options), attributes[columnName].field || columnName, attributes[columnName]));
}
});
if (options.alter === true || typeof options.alter === 'object' && options.alter.drop !== false) { if (options.alter === true || typeof options.alter === 'object' && options.alter.drop !== false) {
_.each(columns, (columnDesc, columnName) => { for (const columnName in columns) {
const currentAttribute = rawAttributes[columnName]; if (!Object.prototype.hasOwnProperty.call(columns, columnName)) continue;
if (!currentAttribute) { const currentAttribute = rawAttributes[columnName];
changes.push(() => this.QueryInterface.removeColumn(this.getTableName(options), columnName, options)); if (!currentAttribute) {
} else if (!currentAttribute.primaryKey) { await this.QueryInterface.removeColumn(this.getTableName(options), columnName, options);
// Check foreign keys. If it's a foreign key, it should remove constraint first. continue;
const references = currentAttribute.references; }
if (currentAttribute.references) { if (currentAttribute.primaryKey) continue;
const database = this.sequelize.config.database; // Check foreign keys. If it's a foreign key, it should remove constraint first.
const schema = this.sequelize.config.schema; const references = currentAttribute.references;
// Find existed foreign keys if (currentAttribute.references) {
_.each(foreignKeyReferences, foreignKeyReference => { const database = this.sequelize.config.database;
const constraintName = foreignKeyReference.constraintName; const schema = this.sequelize.config.schema;
if (!!constraintName // Find existed foreign keys
&& foreignKeyReference.tableCatalog === database for (const foreignKeyReference of foreignKeyReferences) {
&& (schema ? foreignKeyReference.tableSchema === schema : true) const constraintName = foreignKeyReference.constraintName;
&& foreignKeyReference.referencedTableName === references.model if (!!constraintName
&& foreignKeyReference.referencedColumnName === references.key && foreignKeyReference.tableCatalog === database
&& (schema ? foreignKeyReference.referencedTableSchema === schema : true) && (schema ? foreignKeyReference.tableSchema === schema : true)
&& !removedConstraints[constraintName]) { && foreignKeyReference.referencedTableName === references.model
// Remove constraint on foreign keys. && foreignKeyReference.referencedColumnName === references.key
changes.push(() => this.QueryInterface.removeConstraint(this.getTableName(options), constraintName, options)); && (schema ? foreignKeyReference.referencedTableSchema === schema : true)
removedConstraints[constraintName] = true; && !removedConstraints[constraintName]) {
} // Remove constraint on foreign keys.
}); await this.QueryInterface.removeConstraint(this.getTableName(options), constraintName, options);
} removedConstraints[constraintName] = true;
changes.push(() => this.QueryInterface.changeColumn(this.getTableName(options), columnName, currentAttribute)); }
}
});
} }
return Promise.each(changes, f => f());
});
})
.then(() => this.QueryInterface.showIndex(this.getTableName(options), options))
.then(indexes => {
indexes = this._indexes.filter(item1 =>
!indexes.some(item2 => item1.name === item2.name)
).sort((index1, index2) => {
if (this.sequelize.options.dialect === 'postgres') {
// move concurrent indexes to the bottom to avoid weird deadlocks
if (index1.concurrently === true) return 1;
if (index2.concurrently === true) return -1;
} }
await this.QueryInterface.changeColumn(this.getTableName(options), columnName, currentAttribute);
}
}
}
let indexes = await this.QueryInterface.showIndex(this.getTableName(options), options);
indexes = this._indexes.filter(item1 =>
!indexes.some(item2 => item1.name === item2.name)
).sort((index1, index2) => {
if (this.sequelize.options.dialect === 'postgres') {
// move concurrent indexes to the bottom to avoid weird deadlocks
if (index1.concurrently === true) return 1;
if (index2.concurrently === true) return -1;
}
return 0; return 0;
}); });
return Promise.each(indexes, index => this.QueryInterface.addIndex( for (const index of indexes) {
this.getTableName(options), await this.QueryInterface.addIndex(
Object.assign({ this.getTableName(options),
logging: options.logging, Object.assign({
benchmark: options.benchmark, logging: options.logging,
transaction: options.transaction, benchmark: options.benchmark,
schema: options.schema transaction: options.transaction,
}, index), schema: options.schema
this.tableName }, index),
)); this.tableName
}).then(() => { );
if (options.hooks) { }
return this.runHooks('afterSync', options); if (options.hooks) {
} await this.runHooks('afterSync', options);
}).then(() => this); }
return this;
} }
/** /**
...@@ -2341,7 +2334,8 @@ class Model { ...@@ -2341,7 +2334,8 @@ class Model {
} }
return [instance, true]; return [instance, true];
}).catch(sequelizeErrors.UniqueConstraintError, err => { }).catch(err => {
if (!(err instanceof sequelizeErrors.UniqueConstraintError)) throw err;
const flattenedWhere = Utils.flattenObjectDeep(options.where); const flattenedWhere = Utils.flattenObjectDeep(options.where);
const flattenedWhereKeys = Object.keys(flattenedWhere).map(name => _.last(name.split('.'))); const flattenedWhereKeys = Object.keys(flattenedWhere).map(name => _.last(name.split('.')));
const whereFields = flattenedWhereKeys.map(name => _.get(this.rawAttributes, `${name}.field`, name)); const whereFields = flattenedWhereKeys.map(name => _.get(this.rawAttributes, `${name}.field`, name));
...@@ -2413,7 +2407,10 @@ class Model { ...@@ -2413,7 +2407,10 @@ class Model {
return this.create(values, options) return this.create(values, options)
.then(result => [result, true]) .then(result => [result, true])
.catch(sequelizeErrors.UniqueConstraintError, () => this.findOne(options).then(result => [result, false])); .catch(err => {
if (!(err instanceof sequelizeErrors.UniqueConstraintError)) throw err;
return this.findOne(options).then(result => [result, false]);
});
}); });
} }
......
...@@ -289,48 +289,46 @@ class QueryInterface { ...@@ -289,48 +289,46 @@ class QueryInterface {
* *
* @returns {Promise} * @returns {Promise}
*/ */
dropAllTables(options) { async dropAllTables(options) {
options = options || {}; options = options || {};
const skip = options.skip || []; const skip = options.skip || [];
const dropAllTables = tableNames => Promise.each(tableNames, tableName => { const dropAllTables = async tableNames => {
// if tableName is not in the Array of tables names then don't drop it for (const tableName of tableNames) {
if (!skip.includes(tableName.tableName || tableName)) { // if tableName is not in the Array of tables names then don't drop it
return this.dropTable(tableName, Object.assign({}, options, { cascade: true }) ); if (!skip.includes(tableName.tableName || tableName)) {
} await this.dropTable(tableName, Object.assign({}, options, { cascade: true }) );
}); }
}
return this.showAllTables(options).then(tableNames => { };
if (this.sequelize.options.dialect === 'sqlite') {
return this.sequelize.query('PRAGMA foreign_keys;', options).then(result => {
const foreignKeysAreEnabled = result.foreign_keys === 1;
if (foreignKeysAreEnabled) { const tableNames = await this.showAllTables(options);
return this.sequelize.query('PRAGMA foreign_keys = OFF', options) if (this.sequelize.options.dialect === 'sqlite') {
.then(() => dropAllTables(tableNames)) const result = await this.sequelize.query('PRAGMA foreign_keys;', options);
.then(() => this.sequelize.query('PRAGMA foreign_keys = ON', options)); const foreignKeysAreEnabled = result.foreign_keys === 1;
}
return dropAllTables(tableNames); if (foreignKeysAreEnabled) {
}); await this.sequelize.query('PRAGMA foreign_keys = OFF', options);
await dropAllTables(tableNames);
await this.sequelize.query('PRAGMA foreign_keys = ON', options);
} else {
await dropAllTables(tableNames);
} }
return this.getForeignKeysForTables(tableNames, options).then(foreignKeys => { } else {
const queries = []; const foreignKeys = await this.getForeignKeysForTables(tableNames, options);
tableNames.forEach(tableName => {
let normalizedTableName = tableName;
if (_.isObject(tableName)) {
normalizedTableName = `${tableName.schema}.${tableName.tableName}`;
}
foreignKeys[normalizedTableName].forEach(foreignKey => { for (const tableName of tableNames) {
queries.push(this.QueryGenerator.dropForeignKeyQuery(tableName, foreignKey)); let normalizedTableName = tableName;
}); if (_.isObject(tableName)) {
}); normalizedTableName = `${tableName.schema}.${tableName.tableName}`;
}
return Promise.each(queries, q => this.sequelize.query(q, options)) for (const foreignKey of foreignKeys[normalizedTableName]) {
.then(() => dropAllTables(tableNames)); await this.sequelize.query(this.QueryGenerator.dropForeignKeyQuery(tableName, foreignKey));
}); }
}); }
await dropAllTables(tableNames);
}
} }
/** /**
...@@ -485,7 +483,7 @@ class QueryInterface { ...@@ -485,7 +483,7 @@ class QueryInterface {
return data; return data;
}).catch(e => { }).catch(e => {
if (e.original && e.original.code === 'ER_NO_SUCH_TABLE') { if (e.original && e.original.code === 'ER_NO_SUCH_TABLE') {
throw Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`); throw new Error(`No description found for "${tableName}" table. Check the table name and schema; remember, they _are_ case sensitive.`);
} }
throw e; throw e;
...@@ -1056,7 +1054,7 @@ class QueryInterface { ...@@ -1056,7 +1054,7 @@ class QueryInterface {
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
} }
delete(instance, tableName, identifier, options) { async delete(instance, tableName, identifier, options) {
const cascades = []; const cascades = [];
const sql = this.QueryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor); const sql = this.QueryGenerator.deleteQuery(tableName, identifier, {}, instance.constructor);
...@@ -1078,21 +1076,15 @@ class QueryInterface { ...@@ -1078,21 +1076,15 @@ class QueryInterface {
} }
} }
return Promise.each(cascades, cascade => { for (const cascade of cascades) {
return instance[cascade](options).then(instances => { let instances = await instance[cascade](options);
// Check for hasOne relationship with non-existing associate ("has zero") // Check for hasOne relationship with non-existing associate ("has zero")
if (!instances) { if (!instances) continue;
return Promise.resolve(); if (!Array.isArray(instances)) instances = [instances];
} for (const instance of instances) await instance.destroy(options);
}
if (!Array.isArray(instances)) instances = [instances]; options.instance = instance;
return await this.sequelize.query(sql, options);
return Promise.each(instances, instance => instance.destroy(options));
});
}).then(() => {
options.instance = instance;
return this.sequelize.query(sql, options);
});
} }
/** /**
......
...@@ -786,47 +786,45 @@ class Sequelize { ...@@ -786,47 +786,45 @@ class Sequelize {
* *
* @returns {Promise} * @returns {Promise}
*/ */
sync(options) { async sync(options) {
options = _.clone(options) || {}; options = _.clone(options) || {};
options.hooks = options.hooks === undefined ? true : !!options.hooks; options.hooks = options.hooks === undefined ? true : !!options.hooks;
options = _.defaults(options, this.options.sync, this.options); options = _.defaults(options, this.options.sync, this.options);
if (options.match) { if (options.match) {
if (!options.match.test(this.config.database)) { if (!options.match.test(this.config.database)) {
return Promise.reject(new Error(`Database "${this.config.database}" does not match sync match parameter "${options.match}"`)); throw new Error(`Database "${this.config.database}" does not match sync match parameter "${options.match}"`);
} }
} }
return Promise.resolve().then(() => { if (options.hooks) {
if (options.hooks) { await this.runHooks('beforeBulkSync', options);
return this.runHooks('beforeBulkSync', options); }
} if (options.force) {
}).then(() => { await this.drop(options);
if (options.force) { }
return this.drop(options); const models = [];
}
}).then(() => {
const models = [];
// Topologically sort by foreign key constraints to give us an appropriate
// creation order
this.modelManager.forEachModel(model => {
if (model) {
models.push(model);
} else {
// DB should throw an SQL error if referencing non-existent table
}
});
// no models defined, just authenticate
if (!models.length) return this.authenticate(options);
return Promise.each(models, model => model.sync(options)); // Topologically sort by foreign key constraints to give us an appropriate
}).then(() => { // creation order
if (options.hooks) { this.modelManager.forEachModel(model => {
return this.runHooks('afterBulkSync', options); if (model) {
models.push(model);
} else {
// DB should throw an SQL error if referencing non-existent table
} }
}).then(() => this); });
// no models defined, just authenticate
if (!models.length) {
await this.authenticate(options);
} else {
for (const model of models) await model.sync(options);
}
if (options.hooks) {
await this.runHooks('afterBulkSync', options);
}
return this;
} }
/** /**
...@@ -840,7 +838,7 @@ class Sequelize { ...@@ -840,7 +838,7 @@ class Sequelize {
* @see * @see
* {@link Model.truncate} for more information * {@link Model.truncate} for more information
*/ */
truncate(options) { async truncate(options) {
const models = []; const models = [];
this.modelManager.forEachModel(model => { this.modelManager.forEachModel(model => {
...@@ -849,12 +847,11 @@ class Sequelize { ...@@ -849,12 +847,11 @@ class Sequelize {
} }
}, { reverse: false }); }, { reverse: false });
const truncateModel = model => model.truncate(options);
if (options && options.cascade) { if (options && options.cascade) {
return Promise.each(models, truncateModel); for (const model of models) model.truncate(options);
} else {
await Promise.all(models.map(model => model.truncate(options)));
} }
return Promise.all(models.map(truncateModel));
} }
/** /**
...@@ -869,7 +866,7 @@ class Sequelize { ...@@ -869,7 +866,7 @@ class Sequelize {
* *
* @returns {Promise} * @returns {Promise}
*/ */
drop(options) { async drop(options) {
const models = []; const models = [];
this.modelManager.forEachModel(model => { this.modelManager.forEachModel(model => {
...@@ -878,7 +875,7 @@ class Sequelize { ...@@ -878,7 +875,7 @@ class Sequelize {
} }
}, { reverse: false }); }, { reverse: false });
return Promise.each(models, model => model.drop(options)); for (const model of models) await model.drop(options);
} }
/** /**
......
...@@ -52,28 +52,24 @@ class Transaction { ...@@ -52,28 +52,24 @@ class Transaction {
* *
* @returns {Promise} * @returns {Promise}
*/ */
commit() { async commit() {
if (this.finished) { if (this.finished) {
return Promise.reject(new Error(`Transaction cannot be committed because it has been finished with state: ${this.finished}`)); throw new Error(`Transaction cannot be committed because it has been finished with state: ${this.finished}`);
} }
this._clearCls(); this._clearCls();
return this try {
.sequelize return await this.sequelize.getQueryInterface().commitTransaction(this, this.options);
.getQueryInterface() } finally {
.commitTransaction(this, this.options) this.finished = 'commit';
.finally(() => { if (!this.parent) {
this.finished = 'commit'; await this.cleanup();
if (!this.parent) { }
return this.cleanup(); for (const hook of this._afterCommitHooks) {
} await hook.apply(this, [this]);
return null; }
}).then( }
result => Promise.resolve(Promise.each(
this._afterCommitHooks,
hook => Promise.resolve(hook.apply(this, [this])))).then(() => result)
);
} }
/** /**
......
...@@ -1300,7 +1300,8 @@ describe(Support.getTestDialectTeaser('HasMany'), () => { ...@@ -1300,7 +1300,8 @@ describe(Support.getTestDialectTeaser('HasMany'), () => {
ctx.task = task; ctx.task = task;
return user.setTasks([task]); return user.setTasks([task]);
}).then(() => { }).then(() => {
return ctx.user.destroy().catch(Sequelize.ForeignKeyConstraintError, () => { return ctx.user.destroy().catch(err => {
if (!(err instanceof Sequelize.ForeignKeyConstraintError)) throw err;
// Should fail due to FK violation // Should fail due to FK violation
return Task.findAll(); return Task.findAll();
}); });
...@@ -1329,8 +1330,9 @@ describe(Support.getTestDialectTeaser('HasMany'), () => { ...@@ -1329,8 +1330,9 @@ describe(Support.getTestDialectTeaser('HasMany'), () => {
const tableName = user.sequelize.getQueryInterface().QueryGenerator.addSchema(user.constructor); const tableName = user.sequelize.getQueryInterface().QueryGenerator.addSchema(user.constructor);
return user.sequelize.getQueryInterface().update(user, tableName, { id: 999 }, { id: user.id }) return user.sequelize.getQueryInterface().update(user, tableName, { id: 999 }, { id: user.id })
.catch(Sequelize.ForeignKeyConstraintError, () => { .catch(err => {
// Should fail due to FK violation if (!(err instanceof Sequelize.ForeignKeyConstraintError)) throw err;
// Should fail due to FK violation
return Task.findAll(); return Task.findAll();
}); });
}).then(tasks => { }).then(tasks => {
...@@ -1362,21 +1364,20 @@ describe(Support.getTestDialectTeaser('HasMany'), () => { ...@@ -1362,21 +1364,20 @@ describe(Support.getTestDialectTeaser('HasMany'), () => {
expect(Account.rawAttributes.UserId.field).to.equal('UserId'); expect(Account.rawAttributes.UserId.field).to.equal('UserId');
}); });
it('can specify data type for auto-generated relational keys', function() { it('can specify data type for auto-generated relational keys', async function() {
const User = this.sequelize.define('UserXYZ', { username: DataTypes.STRING }), const User = this.sequelize.define('UserXYZ', { username: DataTypes.STRING }),
dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING], dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING],
Tasks = {}; Tasks = {};
return Promise.each(dataTypes, dataType => { for (const dataType of dataTypes) {
const tableName = `TaskXYZ_${dataType.key}`; const tableName = `TaskXYZ_${dataType.key}`;
Tasks[dataType] = this.sequelize.define(tableName, { title: DataTypes.STRING }); Tasks[dataType] = this.sequelize.define(tableName, { title: DataTypes.STRING });
User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, constraints: false }); User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, constraints: false });
return Tasks[dataType].sync({ force: true }).then(() => { await Tasks[dataType].sync({ force: true });
expect(Tasks[dataType].rawAttributes.userId.type).to.be.an.instanceof(dataType); expect(Tasks[dataType].rawAttributes.userId.type).to.be.an.instanceof(dataType);
}); }
});
}); });
it('infers the keyType if none provided', function() { it('infers the keyType if none provided', function() {
......
...@@ -272,29 +272,25 @@ describe(Support.getTestDialectTeaser('associations'), () => { ...@@ -272,29 +272,25 @@ describe(Support.getTestDialectTeaser('associations'), () => {
expect(logs[0]).to.equal(logs[1]); expect(logs[0]).to.equal(logs[1]);
}); });
}); });
it('should created included association with scope values', function() { it('should created included association with scope values', async function() {
return this.sequelize.sync({ force: true }).then(() => { await this.sequelize.sync({ force: true });
return this.Post.create({ let post = await this.Post.create({
comments: [{ comments: [{
title: 'I am a comment created with a post' title: 'I am a comment created with a post'
}, {
title: 'I am a second comment created with a post'
}]
}, { }, {
include: [{ model: this.Comment, as: 'comments' }] title: 'I am a second comment created with a post'
}); }]
}).then(post => { }, {
this.post = post; include: [{ model: this.Comment, as: 'comments' }]
return post.comments; });
}).each(comment => { this.post = post;
for (const comment of post.comments) {
expect(comment.get('commentable')).to.equal('post'); expect(comment.get('commentable')).to.equal('post');
}).then(() => { }
return this.Post.scope('withComments').findByPk(this.post.id); post = await this.Post.scope('withComments').findByPk(this.post.id);
}).then(post => { for (const comment of post.comments) {
return post.getComments();
}).each(comment => {
expect(comment.get('commentable')).to.equal('post'); expect(comment.get('commentable')).to.equal('post');
}); }
}); });
it('should include associations with operator scope values', function() { it('should include associations with operator scope values', function() {
return this.sequelize.sync({ force: true }).then(() => { return this.sequelize.sync({ force: true }).then(() => {
......
...@@ -312,7 +312,10 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -312,7 +312,10 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
return this.sequelize.sync({ force: true }).then(() => { return this.sequelize.sync({ force: true }).then(() => {
return User.create(record); return User.create(record);
}).then(() => { }).then(() => {
return User.create(record).catch(constraintTest.exception, spy); return User.create(record).catch(err => {
if (!(err instanceof constraintTest.exception)) throw err;
return spy(err);
});
}).then(() => { }).then(() => {
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
}); });
...@@ -333,7 +336,10 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => { ...@@ -333,7 +336,10 @@ describe(Support.getTestDialectTeaser('Sequelize Errors'), () => {
return User.create({ name: 'jan' }); return User.create({ name: 'jan' });
}).then(() => { }).then(() => {
// If the error was successfully parsed, we can catch it! // If the error was successfully parsed, we can catch it!
return User.create({ name: 'jan' }).catch(Sequelize.UniqueConstraintError, spy); return User.create({ name: 'jan' }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
return spy(err);
});
}).then(() => { }).then(() => {
expect(spy).to.have.been.calledOnce; expect(spy).to.have.been.calledOnce;
}); });
......
...@@ -287,7 +287,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -287,7 +287,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
await Sequelize.Promise.all([ await Sequelize.Promise.all([
User.create({ username: 'tobi', email: 'tobi@tobi.me' }), User.create({ username: 'tobi', email: 'tobi@tobi.me' }),
User.create({ username: 'tobi', email: 'tobi@tobi.me' }) User.create({ username: 'tobi', email: 'tobi@tobi.me' })
]).catch(Sequelize.UniqueConstraintError, err => { ]).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err.message).to.equal('User and email must be unique'); expect(err.message).to.equal('User and email must be unique');
}); });
}); });
...@@ -321,7 +322,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -321,7 +322,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
await Sequelize.Promise.all([ await Sequelize.Promise.all([
User.create({ user_id: 1, email: 'tobi@tobi.me' }), User.create({ user_id: 1, email: 'tobi@tobi.me' }),
User.create({ user_id: 1, email: 'tobi@tobi.me' }) User.create({ user_id: 1, email: 'tobi@tobi.me' })
]).catch(Sequelize.UniqueConstraintError, err => { ]).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err.message).to.equal('User and email must be unique'); expect(err.message).to.equal('User and email must be unique');
}); });
}); });
......
...@@ -417,10 +417,12 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -417,10 +417,12 @@ describe(Support.getTestDialectTeaser('Model'), () => {
} }
}) })
.timeout(1000) .timeout(1000)
.catch(Promise.TimeoutError, e => { .catch(e => {
if (!(e instanceof Promise.TimeoutError)) throw e;
throw new Error(e); throw new Error(e);
}) })
.catch(Sequelize.ValidationError, () => { .catch(err => {
if (!(err instanceof Sequelize.ValidationError)) throw err;
return test(times + 1); return test(times + 1);
}); });
}; };
...@@ -1003,7 +1005,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -1003,7 +1005,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
return User.sync({ force: true }).then(() => { return User.sync({ force: true }).then(() => {
return User.create({ username: 'foo' }).then(() => { return User.create({ username: 'foo' }).then(() => {
return User.create({ username: 'foo' }).catch(Sequelize.UniqueConstraintError, err => { return User.create({ username: 'foo' }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err).to.be.ok; expect(err).to.be.ok;
}); });
}); });
...@@ -1020,7 +1023,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -1020,7 +1023,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
return User.create({ username: 'foo' }); return User.create({ username: 'foo' });
}).then(() => { }).then(() => {
return User.create({ username: 'fOO' }); return User.create({ username: 'fOO' });
}).catch(Sequelize.UniqueConstraintError, err => { }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err).to.be.ok; expect(err).to.be.ok;
}); });
}); });
...@@ -1040,7 +1044,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -1040,7 +1044,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
return User.create({ username: 'foo' }); return User.create({ username: 'foo' });
}).then(() => { }).then(() => {
return User.create({ username: 'foo' }); return User.create({ username: 'foo' });
}).catch(Sequelize.UniqueConstraintError, err => { }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err).to.be.ok; expect(err).to.be.ok;
}); });
}); });
...@@ -1075,7 +1080,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -1075,7 +1080,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
return UserNull.sync({ force: true }).then(() => { return UserNull.sync({ force: true }).then(() => {
return UserNull.create({ username: 'foo', smth: 'foo' }).then(() => { return UserNull.create({ username: 'foo', smth: 'foo' }).then(() => {
return UserNull.create({ username: 'foo', smth: 'bar' }).catch(Sequelize.UniqueConstraintError, err => { return UserNull.create({ username: 'foo', smth: 'bar' }).catch(err => {
if (!(err instanceof Sequelize.UniqueConstraintError)) throw err;
expect(err).to.be.ok; expect(err).to.be.ok;
}); });
}); });
......
...@@ -45,7 +45,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -45,7 +45,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
.then(() => { .then(() => {
expect.fail('expected to fail'); expect.fail('expected to fail');
}) })
.catch(/abort/, () => { .catch(err => {
if (!/abort/.test(err.message)) throw err;
expect(this.clsStub.calledOnce).to.equal(true, 'expected to ask for transaction'); expect(this.clsStub.calledOnce).to.equal(true, 'expected to ask for transaction');
}); });
...@@ -63,7 +64,8 @@ describe(Support.getTestDialectTeaser('Model'), () => { ...@@ -63,7 +64,8 @@ describe(Support.getTestDialectTeaser('Model'), () => {
.then(() => { .then(() => {
expect.fail('expected to fail'); expect.fail('expected to fail');
}) })
.catch(/abort/, () => { .catch(err => {
if (!/abort/.test(err.message)) throw err;
expect(this.clsStub.called).to.equal(false); expect(this.clsStub.called).to.equal(false);
}); });
}); });
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!