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

Commit 2fba6364 by Felix Becker Committed by Mick Hansen

ES6 Refactor: dialects / MySQL (#6047)

* Make Mysql ConnectionManager an ES6 class

* ES6 refactor of MySQL ConnectionManager

let, const, for of, arrow functions, Map, export default

* Make MySQL Query an ES6 class

* ES6 refactor of MySQL Query

let, const, for of, arrow functions, export default

* ES6 refactor of MySQL QueryInterface

* ES6 refactor of MySQL QueryGenerator
1 parent c032ec90
'use strict'; 'use strict';
var AbstractConnectionManager = require('../abstract/connection-manager') const AbstractConnectionManager = require('../abstract/connection-manager');
, ConnectionManager const Utils = require('../../utils');
, Utils = require('../../utils') const Promise = require('../../promise');
, Promise = require('../../promise') const sequelizeErrors = require('../../errors');
, sequelizeErrors = require('../../errors') const dataTypes = require('../../data-types').mysql;
, dataTypes = require('../../data-types').mysql const parserMap = new Map();
, parserMap = {};
ConnectionManager = function(dialect, sequelize) { class ConnectionManager extends AbstractConnectionManager {
AbstractConnectionManager.call(this, dialect, sequelize); constructor(dialect, sequelize) {
super(dialect, sequelize);
this.sequelize = sequelize; this.sequelize = sequelize;
this.sequelize.config.port = this.sequelize.config.port || 3306; this.sequelize.config.port = this.sequelize.config.port || 3306;
...@@ -27,53 +27,50 @@ ConnectionManager = function(dialect, sequelize) { ...@@ -27,53 +27,50 @@ ConnectionManager = function(dialect, sequelize) {
} }
this.refreshTypeParser(dataTypes); this.refreshTypeParser(dataTypes);
}; }
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types // Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.$refreshTypeParser = function (dataType) { $refreshTypeParser(dataType) {
dataType.types.mysql.forEach(function (type) { for (const type of dataType.types.mysql) {
parserMap[type] = dataType.parse; parserMap.set(type, dataType.parse);
}); }
}; }
ConnectionManager.prototype.$clearTypeParser = function () { $clearTypeParser() {
parserMap = {}; parserMap.clear();
}; }
ConnectionManager.$typecast = function (field, next) { static $typecast(field, next) {
if (parserMap[field.type]) { if (parserMap.has(field.type)) {
return parserMap[field.type](field, this.sequelize.options); return parserMap.get(field.type)(field, this.sequelize.options);
} }
return next(); return next();
}; }
ConnectionManager.prototype.connect = function(config) { connect(config) {
var self = this; return new Promise((resolve, reject) => {
return new Promise(function (resolve, reject) { const connectionConfig = {
var connectionConfig = {
host: config.host, host: config.host,
port: config.port, port: config.port,
user: config.username, user: config.username,
password: config.password, password: config.password,
database: config.database, database: config.database,
timezone: self.sequelize.options.timezone, timezone: this.sequelize.options.timezone,
typeCast: ConnectionManager.$typecast.bind(self), typeCast: ConnectionManager.$typecast.bind(this),
bigNumberStrings: false, bigNumberStrings: false,
supportBigNumbers: true supportBigNumbers: true
}; };
if (config.dialectOptions) { if (config.dialectOptions) {
Object.keys(config.dialectOptions).forEach(function(key) { for (const key of Object.keys(config.dialectOptions)) {
connectionConfig[key] = config.dialectOptions[key]; connectionConfig[key] = config.dialectOptions[key];
}); }
} }
var connection = self.lib.createConnection(connectionConfig); const connection = this.lib.createConnection(connectionConfig);
connection.connect(function(err) { connection.connect(err => {
if (err) { if (err) {
if (err.code) { if (err.code) {
switch (err.code) { switch (err.code) {
...@@ -110,21 +107,22 @@ ConnectionManager.prototype.connect = function(config) { ...@@ -110,21 +107,22 @@ ConnectionManager.prototype.connect = function(config) {
// server variable configures this) // server variable configures this)
// //
// See [stackoverflow answer](http://stackoverflow.com/questions/20210522/nodejs-mysql-error-connection-lost-the-server-closed-the-connection) // See [stackoverflow answer](http://stackoverflow.com/questions/20210522/nodejs-mysql-error-connection-lost-the-server-closed-the-connection)
connection.on('error', function (err) { connection.on('error', err => {
if (err.code === 'PROTOCOL_CONNECTION_LOST') { if (err.code === 'PROTOCOL_CONNECTION_LOST') {
// Remove it from read/write pool // Remove it from read/write pool
self.pool.destroy(connection); this.pool.destroy(connection);
} }
}); });
} }
resolve(connection); resolve(connection);
}); });
}).tap(function (connection) { }).tap(connection => {
connection.query("SET time_zone = '" + self.sequelize.options.timezone + "'"); /* jshint ignore: line */ connection.query("SET time_zone = '" + this.sequelize.options.timezone + "'"); /* jshint ignore: line */
}); });
}; }
ConnectionManager.prototype.disconnect = function(connection) {
disconnect(connection) {
// Dont disconnect connections with an ended protocol // Dont disconnect connections with an ended protocol
// That wil trigger a connection error // That wil trigger a connection error
...@@ -132,15 +130,21 @@ ConnectionManager.prototype.disconnect = function(connection) { ...@@ -132,15 +130,21 @@ ConnectionManager.prototype.disconnect = function(connection) {
return Promise.resolve(); return Promise.resolve();
} }
return new Promise(function (resolve, reject) { return new Promise((resolve, reject) => {
connection.end(function(err) { connection.end(err => {
if (err) return reject(new sequelizeErrors.ConnectionError(err)); if (err) return reject(new sequelizeErrors.ConnectionError(err));
resolve(); resolve();
}); });
}); });
}; }
ConnectionManager.prototype.validate = function(connection) {
validate(connection) {
return connection && ['disconnected', 'protocol_error'].indexOf(connection.state) === -1; return connection && ['disconnected', 'protocol_error'].indexOf(connection.state) === -1;
}; }
}
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
module.exports = ConnectionManager; module.exports = ConnectionManager;
module.exports.ConnectionManager = ConnectionManager;
module.exports.default = ConnectionManager;
'use strict'; 'use strict';
var Utils = require('../../utils'); const Utils = require('../../utils');
var QueryGenerator = { const QueryGenerator = {
dialect: 'mysql', dialect: 'mysql',
createSchema: function() { createSchema() {
var query = 'SHOW TABLES'; return 'SHOW TABLES';
return Utils._.template(query)({});
}, },
showSchemasQuery: function() { showSchemasQuery() {
return 'SHOW TABLES'; return 'SHOW TABLES';
}, },
versionQuery: function() { versionQuery() {
return 'SELECT VERSION() as `version`'; return 'SELECT VERSION() as `version`';
}, },
createTableQuery: function(tableName, attributes, options) { createTableQuery(tableName, attributes, options) {
options = Utils._.extend({ options = Utils._.extend({
engine: 'InnoDB', engine: 'InnoDB',
charset: null charset: null
}, options || {}); }, options || {});
var self = this; const query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %><%= comment %><%= charset %><%= collation %><%= initialAutoIncrement %>';
const primaryKeys = [];
var query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>) ENGINE=<%= engine %><%= comment %><%= charset %><%= collation %><%= initialAutoIncrement %>' const foreignKeys = {};
, primaryKeys = [] const attrStr = [];
, foreignKeys = {}
, attrStr = [];
for (var attr in attributes) { for (const attr in attributes) {
if (attributes.hasOwnProperty(attr)) { if (attributes.hasOwnProperty(attr)) {
var dataType = attributes[attr] const dataType = attributes[attr];
, match; let match;
if (Utils._.includes(dataType, 'PRIMARY KEY')) { if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys.push(attr); primaryKeys.push(attr);
...@@ -58,7 +55,7 @@ var QueryGenerator = { ...@@ -58,7 +55,7 @@ var QueryGenerator = {
} }
} }
var values = { const values = {
table: this.quoteTable(tableName), table: this.quoteTable(tableName),
attributes: attrStr.join(', '), attributes: attrStr.join(', '),
comment: options.comment && Utils._.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '', comment: options.comment && Utils._.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '',
...@@ -66,16 +63,16 @@ var QueryGenerator = { ...@@ -66,16 +63,16 @@ var QueryGenerator = {
charset: (options.charset ? ' DEFAULT CHARSET=' + options.charset : ''), charset: (options.charset ? ' DEFAULT CHARSET=' + options.charset : ''),
collation: (options.collate ? ' COLLATE ' + options.collate : ''), collation: (options.collate ? ' COLLATE ' + options.collate : ''),
initialAutoIncrement: (options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : '') initialAutoIncrement: (options.initialAutoIncrement ? ' AUTO_INCREMENT=' + options.initialAutoIncrement : '')
} };
, pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk); }.bind(this)).join(', '); const pkString = primaryKeys.map(pk => this.quoteIdentifier(pk)).join(', ');
if (!!options.uniqueKeys) { if (!!options.uniqueKeys) {
Utils._.each(options.uniqueKeys, function(columns, indexName) { Utils._.each(options.uniqueKeys, (columns, indexName) => {
if (!columns.singleField) { // If it's a single field its handled in column def, not as an index if (!columns.singleField) { // If it's a single field its handled in column def, not as an index
if (!Utils._.isString(indexName)) { if (!Utils._.isString(indexName)) {
indexName = 'uniq_' + tableName + '_' + columns.fields.join('_'); indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
} }
values.attributes += ', UNIQUE ' + self.quoteIdentifier(indexName) + ' (' + Utils._.map(columns.fields, self.quoteIdentifier).join(', ') + ')'; values.attributes += ', UNIQUE ' + this.quoteIdentifier(indexName) + ' (' + Utils._.map(columns.fields, this.quoteIdentifier).join(', ') + ')';
} }
}); });
} }
...@@ -84,7 +81,7 @@ var QueryGenerator = { ...@@ -84,7 +81,7 @@ var QueryGenerator = {
values.attributes += ', PRIMARY KEY (' + pkString + ')'; values.attributes += ', PRIMARY KEY (' + pkString + ')';
} }
for (var fkey in foreignKeys) { for (const fkey in foreignKeys) {
if (foreignKeys.hasOwnProperty(fkey)) { if (foreignKeys.hasOwnProperty(fkey)) {
values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey]; values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey];
} }
...@@ -93,56 +90,40 @@ var QueryGenerator = { ...@@ -93,56 +90,40 @@ var QueryGenerator = {
return Utils._.template(query)(values).trim() + ';'; return Utils._.template(query)(values).trim() + ';';
}, },
showTablesQuery: function() { showTablesQuery() {
return 'SHOW TABLES;'; return 'SHOW TABLES;';
}, },
addColumnQuery: function(table, key, dataType) { addColumnQuery(table, key, dataType) {
var query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;' const definition = this.attributeToSQL(dataType, {
, attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: this.attributeToSQL(dataType, {
context: 'addColumn', context: 'addColumn',
tableName: table, tableName: table,
foreignKey: key foreignKey: key
})
});
return Utils._.template(query)({
table: this.quoteTable(table),
attribute: attribute
}); });
return `ALTER TABLE ${this.quoteTable(table)} ADD ${this.quoteIdentifier(key)} ${definition};`;
}, },
removeColumnQuery: function(tableName, attributeName) { removeColumnQuery(tableName, attributeName) {
var query = 'ALTER TABLE <%= tableName %> DROP <%= attributeName %>;'; return `ALTER TABLE ${this.quoteTable(tableName)} DROP ${this.quoteIdentifier(attributeName)};`;
return Utils._.template(query)({
tableName: this.quoteTable(tableName),
attributeName: this.quoteIdentifier(attributeName)
});
}, },
changeColumnQuery: function(tableName, attributes) { changeColumnQuery(tableName, attributes) {
var query = 'ALTER TABLE <%= tableName %> <%= query %>;'; const attrString = [];
var attrString = [], constraintString = []; const constraintString = [];
for (var attributeName in attributes) { for (const attributeName in attributes) {
var definition = attributes[attributeName]; let definition = attributes[attributeName];
if (definition.match(/REFERENCES/)) { if (definition.match(/REFERENCES/)) {
constraintString.push(Utils._.template('<%= fkName %> FOREIGN KEY (<%= attrName %>) <%= definition %>')({ const fkName = this.quoteIdentifier(tableName + '_' + attributeName + '_foreign_idx');
fkName: this.quoteIdentifier(tableName + '_' + attributeName + '_foreign_idx'), const attrName = this.quoteIdentifier(attributeName);
attrName: this.quoteIdentifier(attributeName), definition = definition.replace(/.+?(?=REFERENCES)/,'');
definition: definition.replace(/.+?(?=REFERENCES)/,'') constraintString.push(`${fkName} FOREIGN KEY (${attrName}) ${definition}`);
}));
} else { } else {
attrString.push(Utils._.template('`<%= attrName %>` `<%= attrName %>` <%= definition %>')({ attrString.push('`' + attributeName + '` `' + attributeName + '` ' + definition);
attrName: attributeName,
definition: definition
}));
} }
} }
var finalQuery = ''; let finalQuery = '';
if (attrString.length) { if (attrString.length) {
finalQuery += 'CHANGE ' + attrString.join(', '); finalQuery += 'CHANGE ' + attrString.join(', ');
finalQuery += constraintString.length ? ' ' : ''; finalQuery += constraintString.length ? ' ' : '';
...@@ -151,51 +132,42 @@ var QueryGenerator = { ...@@ -151,51 +132,42 @@ var QueryGenerator = {
finalQuery += 'ADD CONSTRAINT ' + constraintString.join(', '); finalQuery += 'ADD CONSTRAINT ' + constraintString.join(', ');
} }
return Utils._.template(query)({ return `ALTER TABLE ${this.quoteTable(tableName)} ${finalQuery};`;
tableName: this.quoteTable(tableName),
query: finalQuery
});
}, },
renameColumnQuery: function(tableName, attrBefore, attributes) { renameColumnQuery(tableName, attrBefore, attributes) {
var query = 'ALTER TABLE <%= tableName %> CHANGE <%= attributes %>;'; const attrString = [];
var attrString = [];
for (var attrName in attributes) {
var definition = attributes[attrName];
attrString.push(Utils._.template('`<%= before %>` `<%= after %>` <%= definition %>')({ for (const attrName in attributes) {
before: attrBefore, const definition = attributes[attrName];
after: attrName, attrString.push('`' + attrBefore + '` `' + attrName + '` ' + definition);
definition: definition
}));
} }
return Utils._.template(query)({ tableName: this.quoteTable(tableName), attributes: attrString.join(', ') }); return `ALTER TABLE ${this.quoteTable(tableName)} CHANGE ${attrString.join(', ')};`;
}, },
upsertQuery: function (tableName, insertValues, updateValues, where, rawAttributes, options) { upsertQuery(tableName, insertValues, updateValues, where, rawAttributes, options) {
options.onDuplicate = 'UPDATE '; options.onDuplicate = 'UPDATE ';
options.onDuplicate += Object.keys(updateValues).map(function (key) { options.onDuplicate += Object.keys(updateValues).map(key => {
key = this.quoteIdentifier(key); key = this.quoteIdentifier(key);
return key + '=VALUES(' + key +')'; return key + '=VALUES(' + key +')';
}, this).join(', '); }).join(', ');
return this.insertQuery(tableName, insertValues, rawAttributes, options); return this.insertQuery(tableName, insertValues, rawAttributes, options);
}, },
deleteQuery: function(tableName, where, options) { deleteQuery(tableName, where, options) {
options = options || {}; options = options || {};
var table = this.quoteTable(tableName); const table = this.quoteTable(tableName);
if (options.truncate === true) { if (options.truncate === true) {
// Truncate does not allow LIMIT and WHERE // Truncate does not allow LIMIT and WHERE
return 'TRUNCATE ' + table; return 'TRUNCATE ' + table;
} }
where = this.getWhereConditions(where); where = this.getWhereConditions(where);
var limit = ''; let limit = '';
if (Utils._.isUndefined(options.limit)) { if (Utils._.isUndefined(options.limit)) {
options.limit = 1; options.limit = 1;
...@@ -205,40 +177,35 @@ var QueryGenerator = { ...@@ -205,40 +177,35 @@ var QueryGenerator = {
limit = ' LIMIT ' + this.escape(options.limit); limit = ' LIMIT ' + this.escape(options.limit);
} }
var query = 'DELETE FROM ' + table; let query = 'DELETE FROM ' + table;
if (where) query += ' WHERE ' + where; if (where) query += ' WHERE ' + where;
query += limit; query += limit;
return query; return query;
}, },
showIndexesQuery: function(tableName, options) { showIndexesQuery(tableName, options) {
var sql = 'SHOW INDEX FROM <%= tableName %><%= options %>'; return 'SHOW INDEX FROM ' + this.quoteTable(tableName) + ((options || {}).database ? ' FROM `' + options.database + '`' : '');
return Utils._.template(sql)({
tableName: this.quoteTable(tableName),
options: (options || {}).database ? ' FROM `' + options.database + '`' : ''
});
}, },
removeIndexQuery: function(tableName, indexNameOrAttributes) { removeIndexQuery(tableName, indexNameOrAttributes) {
var sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>' let indexName = indexNameOrAttributes;
, indexName = indexNameOrAttributes;
if (typeof indexName !== 'string') { if (typeof indexName !== 'string') {
indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_')); indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
} }
return Utils._.template(sql)({ tableName: this.quoteIdentifiers(tableName), indexName: indexName }); return `DROP INDEX ${indexName} ON ${this.quoteTable(tableName)}`;
}, },
attributeToSQL: function(attribute, options) { attributeToSQL(attribute, options) {
if (!Utils._.isPlainObject(attribute)) { if (!Utils._.isPlainObject(attribute)) {
attribute = { attribute = {
type: attribute type: attribute
}; };
} }
var template = attribute.type.toString({ escape: this.escape.bind(this) }); let template = attribute.type.toString({ escape: this.escape.bind(this) });
if (attribute.allowNull === false) { if (attribute.allowNull === false) {
template += ' NOT NULL'; template += ' NOT NULL';
...@@ -268,12 +235,10 @@ var QueryGenerator = { ...@@ -268,12 +235,10 @@ var QueryGenerator = {
if (attribute.references) { if (attribute.references) {
if (options && options.context === 'addColumn' && options.foreignKey) { if (options && options.context === 'addColumn' && options.foreignKey) {
var attrName = options.foreignKey; const attrName = this.quoteIdentifier(options.foreignKey);
const fkName = this.quoteIdentifier(`${options.tableName}_${attrName}_foreign_idx`);
template += Utils._.template(', ADD CONSTRAINT <%= fkName %> FOREIGN KEY (<%= attrName %>)')({ template += `, ADD CONSTRAINT ${fkName} FOREIGN KEY (${attrName})`;
fkName: this.quoteIdentifier(options.tableName + '_' + attrName + '_foreign_idx'),
attrName: this.quoteIdentifier(attrName)
});
} }
template += ' REFERENCES ' + this.quoteTable(attribute.references.model); template += ' REFERENCES ' + this.quoteTable(attribute.references.model);
...@@ -296,25 +261,23 @@ var QueryGenerator = { ...@@ -296,25 +261,23 @@ var QueryGenerator = {
return template; return template;
}, },
attributesToSQL: function(attributes, options) { attributesToSQL(attributes, options) {
var result = {} const result = {};
, key
, attribute;
for (key in attributes) { for (const key in attributes) {
attribute = attributes[key]; const attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options); result[attribute.field || key] = this.attributeToSQL(attribute, options);
} }
return result; return result;
}, },
findAutoIncrementField: function(factory) { findAutoIncrementField(factory) {
var fields = []; const fields = [];
for (var name in factory.attributes) { for (const name in factory.attributes) {
if (factory.attributes.hasOwnProperty(name)) { if (factory.attributes.hasOwnProperty(name)) {
var definition = factory.attributes[name]; const definition = factory.attributes[name];
if (definition && definition.autoIncrement) { if (definition && definition.autoIncrement) {
fields.push(name); fields.push(name);
...@@ -325,7 +288,7 @@ var QueryGenerator = { ...@@ -325,7 +288,7 @@ var QueryGenerator = {
return fields; return fields;
}, },
quoteIdentifier: function(identifier) { quoteIdentifier(identifier) {
if (identifier === '*') return identifier; if (identifier === '*') return identifier;
return Utils.addTicks(identifier, '`'); return Utils.addTicks(identifier, '`');
}, },
...@@ -337,7 +300,7 @@ var QueryGenerator = { ...@@ -337,7 +300,7 @@ var QueryGenerator = {
* @param {String} schemaName The name of the schema. * @param {String} schemaName The name of the schema.
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
getForeignKeysQuery: function(tableName, schemaName) { getForeignKeysQuery(tableName, schemaName) {
return "SELECT CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '" + tableName + /* jshint ignore: line */ return "SELECT CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '" + tableName + /* jshint ignore: line */
"' AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='" + schemaName + "' AND REFERENCED_TABLE_NAME IS NOT NULL;"; /* jshint ignore: line */ "' AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='" + schemaName + "' AND REFERENCED_TABLE_NAME IS NOT NULL;"; /* jshint ignore: line */
}, },
...@@ -349,20 +312,18 @@ var QueryGenerator = { ...@@ -349,20 +312,18 @@ var QueryGenerator = {
* @param {String} columnName The name of the column. * @param {String} columnName The name of the column.
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
getForeignKeyQuery: function(table, columnName) { getForeignKeyQuery(table, columnName) {
var tableName = table.tableName || table; let tableName = table.tableName || table;
if (table.schema) { if (table.schema) {
tableName = table.schema + '.' + tableName; tableName = table.schema + '.' + tableName;
} }
return [ return 'SELECT CONSTRAINT_NAME as constraint_name'
'SELECT CONSTRAINT_NAME as constraint_name', + ' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE'
'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE', + ' WHERE (REFERENCED_TABLE_NAME = ' + wrapSingleQuote(tableName)
'WHERE (REFERENCED_TABLE_NAME = ' + wrapSingleQuote(tableName), + ' AND REFERENCED_COLUMN_NAME = ' + wrapSingleQuote(columnName)
'AND REFERENCED_COLUMN_NAME = ' + wrapSingleQuote(columnName), + ') OR (TABLE_NAME = ' + wrapSingleQuote(tableName)
') OR (TABLE_NAME = ' + wrapSingleQuote(tableName), + ' AND COLUMN_NAME = ' + wrapSingleQuote(columnName)
'AND COLUMN_NAME = ' + wrapSingleQuote(columnName), + ')';
')',
].join(' ');
}, },
/** /**
...@@ -372,7 +333,7 @@ var QueryGenerator = { ...@@ -372,7 +333,7 @@ var QueryGenerator = {
* @param {String} foreignKey The name of the foreign key constraint. * @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query. * @return {String} The generated sql query.
*/ */
dropForeignKeyQuery: function(tableName, foreignKey) { dropForeignKeyQuery(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';'; return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';';
} }
}; };
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
@static @static
*/ */
var _ = require('lodash'); const _ = require('lodash');
/** /**
A wrapper that fixes MySQL's inability to cleanly remove columns from existing tables if they have a foreign key constraint. A wrapper that fixes MySQL's inability to cleanly remove columns from existing tables if they have a foreign key constraint.
...@@ -19,32 +19,27 @@ var _ = require('lodash'); ...@@ -19,32 +19,27 @@ var _ = require('lodash');
@param {String} columnName The name of the attribute that we want to remove. @param {String} columnName The name of the attribute that we want to remove.
@param {Object} options @param {Object} options
*/ */
var removeColumn = function (tableName, columnName, options) { function removeColumn(tableName, columnName, options) {
var self = this;
options = options || {}; options = options || {};
return self.sequelize.query( /* jshint validthis:true */
self.QueryGenerator.getForeignKeyQuery(tableName, columnName), return this.sequelize.query(
this.QueryGenerator.getForeignKeyQuery(tableName, columnName),
_.assign({ raw: true }, options) _.assign({ raw: true }, options)
) )
.spread(function (results) { .spread(results => {
if (!results.length) { if (!results.length) {
// No foreign key constraints found, so we can remove the column // No foreign key constraints found, so we can remove the column
return; return;
} }
return self.sequelize.query( return this.sequelize.query(
self.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name), this.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name),
_.assign({ raw: true }, options) _.assign({ raw: true }, options)
); );
}) })
.then(function () { .then(() => this.sequelize.query(
return self.sequelize.query( this.QueryGenerator.removeColumnQuery(tableName, columnName),
self.QueryGenerator.removeColumnQuery(tableName, columnName),
_.assign({ raw: true }, options) _.assign({ raw: true }, options)
); ));
}); }
}; exports.removeColumn = removeColumn;
module.exports = {
removeColumn: removeColumn
};
'use strict'; 'use strict';
var Utils = require('../../utils') const Utils = require('../../utils');
, AbstractQuery = require('../abstract/query') const AbstractQuery = require('../abstract/query');
, uuid = require('node-uuid') const uuid = require('node-uuid');
, sequelizeErrors = require('../../errors.js') const sequelizeErrors = require('../../errors.js');
, _ = require('lodash'); const _ = require('lodash');
var Query = function(connection, sequelize, options) { class Query extends AbstractQuery {
constructor(connection, sequelize, options) {
super();
this.connection = connection; this.connection = connection;
this.instance = options.instance; this.instance = options.instance;
this.model = options.model; this.model = options.model;
...@@ -20,54 +22,48 @@ var Query = function(connection, sequelize, options) { ...@@ -20,54 +22,48 @@ var Query = function(connection, sequelize, options) {
}, options || {}); }, options || {});
this.checkLoggingOption(); this.checkLoggingOption();
}; }
Utils.inherit(Query, AbstractQuery); run(sql, parameters) {
Query.formatBindParameters = AbstractQuery.formatBindParameters;
Query.prototype.run = function(sql, parameters) {
var self = this;
this.sql = sql; this.sql = sql;
//do we need benchmark for this query execution //do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark; const benchmark = this.sequelize.options.benchmark || this.options.benchmark;
var showWarnings = this.sequelize.options.showWarnings || this.options.showWarnings; const showWarnings = this.sequelize.options.showWarnings || this.options.showWarnings;
let queryBegin;
if (benchmark) { if (benchmark) {
var queryBegin = Date.now(); queryBegin = Date.now();
} else { } else {
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options); this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
} }
var promise = new Utils.Promise(function(resolve, reject) { return new Utils.Promise((resolve, reject) => {
self.connection.query(self.sql, function(err, results) { this.connection.query(this.sql, (err, results) => {
if (benchmark) { if (benchmark) {
self.sequelize.log('Executed (' + (self.connection.uuid || 'default') + '): ' + self.sql, (Date.now() - queryBegin), self.options); this.sequelize.log('Executed (' + (this.connection.uuid || 'default') + '): ' + this.sql, (Date.now() - queryBegin), this.options);
} }
if (err) { if (err) {
err.sql = sql; err.sql = sql;
reject(self.formatError(err)); reject(this.formatError(err));
} else { } else {
resolve(results); resolve(results);
} }
}).setMaxListeners(100); }).setMaxListeners(100);
}) })
// Log warnings if we've got them. // Log warnings if we've got them.
.then(function(results){ .then(results => {
if (showWarnings && results && results.warningCount > 0) { if (showWarnings && results && results.warningCount > 0) {
return self.logWarnings(results); return this.logWarnings(results);
} }
return results; return results;
}) })
// Return formatted results... // Return formatted results...
.then(function(results){ .then(results => this.formatResults(results));
return self.formatResults(results); }
});
return promise;
};
/** /**
* High level function that handles the results of a query execution. * High level function that handles the results of a query execution.
...@@ -85,8 +81,8 @@ Query.prototype.run = function(sql, parameters) { ...@@ -85,8 +81,8 @@ Query.prototype.run = function(sql, parameters) {
* *
* @param {Array} data - The result of the query execution. * @param {Array} data - The result of the query execution.
*/ */
Query.prototype.formatResults = function(data) { formatResults(data) {
var result = this.instance; let result = this.instance;
if (this.isInsertQuery(data)) { if (this.isInsertQuery(data)) {
this.handleInsertQuery(data); this.handleInsertQuery(data);
...@@ -103,15 +99,15 @@ Query.prototype.formatResults = function(data) { ...@@ -103,15 +99,15 @@ Query.prototype.formatResults = function(data) {
} else if (this.isDescribeQuery()) { } else if (this.isDescribeQuery()) {
result = {}; result = {};
data.forEach(function(_result) { for (const _result of data) {
var enumRegex = /^enum/i; const enumRegex = /^enum/i;
result[_result.Field] = { result[_result.Field] = {
type: enumRegex.test(_result.Type) ? _result.Type.replace(enumRegex, 'ENUM') : _result.Type.toUpperCase(), type: enumRegex.test(_result.Type) ? _result.Type.replace(enumRegex, 'ENUM') : _result.Type.toUpperCase(),
allowNull: (_result.Null === 'YES'), allowNull: (_result.Null === 'YES'),
defaultValue: _result.Default, defaultValue: _result.Default,
primaryKey: _result.Key === 'PRI' primaryKey: _result.Key === 'PRI'
}; };
}); }
} else if (this.isShowIndexesQuery()) { } else if (this.isShowIndexesQuery()) {
result = this.handleShowIndexesQuery(data); result = this.handleShowIndexesQuery(data);
...@@ -129,43 +125,40 @@ Query.prototype.formatResults = function(data) { ...@@ -129,43 +125,40 @@ Query.prototype.formatResults = function(data) {
} }
return result; return result;
}; }
Query.prototype.logWarnings = function (results) { logWarnings(results) {
var self = this; return this.run('SHOW WARNINGS').then(warningResults => {
return this.run('SHOW WARNINGS').then(function(warningResults) { const warningMessage = 'MySQL Warnings (' + (this.connection.uuid||'default') + '): ';
var warningMessage = 'MySQL Warnings (' + (self.connection.uuid||'default') + '): '; const messages = [];
var messages = [];
warningResults.forEach(function(_warningRow){ for (const _warningRow of warningResults) {
_warningRow.forEach(function(_warningResult) { for (const _warningResult of _warningRow) {
if (_warningResult.hasOwnProperty('Message')) { if (_warningResult.hasOwnProperty('Message')) {
messages.push(_warningResult.Message); messages.push(_warningResult.Message);
} else { } else {
_warningResult.keys().forEach( function(_objectKey) { for (const _objectKey of _warningResult.keys()) {
messages.push([_objectKey, _warningResult[_objectKey]].join(': ')); messages.push([_objectKey, _warningResult[_objectKey]].join(': '));
});
} }
}); }
}); }
}
self.sequelize.log(warningMessage + messages.join('; '), self.options); this.sequelize.log(warningMessage + messages.join('; '), this.options);
return results; return results;
}); });
}; }
Query.prototype.formatError = function (err) {
var match;
formatError(err) {
switch (err.errno || err.code) { switch (err.errno || err.code) {
case 1062: case 1062: {
match = err.message.match(/Duplicate entry '(.*)' for key '?((.|\s)*?)'?$/); const match = err.message.match(/Duplicate entry '(.*)' for key '?((.|\s)*?)'?$/);
var values = match ? match[1].split('-') : undefined let fields = {};
, fields = {} let message = 'Validation error';
, message = 'Validation error' const values = match ? match[1].split('-') : undefined;
, uniqueKey = this.model && this.model.uniqueKeys[match[2]]; const uniqueKey = this.model && this.model.uniqueKeys[match[2]];
if (!!uniqueKey) { if (!!uniqueKey) {
if (!!uniqueKey.msg) message = uniqueKey.msg; if (!!uniqueKey.msg) message = uniqueKey.msg;
...@@ -174,47 +167,42 @@ Query.prototype.formatError = function (err) { ...@@ -174,47 +167,42 @@ Query.prototype.formatError = function (err) {
fields[match[2]] = match[1]; fields[match[2]] = match[1];
} }
var errors = []; const errors = [];
var self = this; Utils._.forOwn(fields, (value, field) => {
Utils._.forOwn(fields, function(value, field) {
errors.push(new sequelizeErrors.ValidationErrorItem( errors.push(new sequelizeErrors.ValidationErrorItem(
self.getUniqueConstraintErrorMessage(field), this.getUniqueConstraintErrorMessage(field),
'unique violation', field, value)); 'unique violation', field, value
}); ));
return new sequelizeErrors.UniqueConstraintError({
message: message,
errors: errors,
parent: err,
fields: fields
}); });
case 1451: return new sequelizeErrors.UniqueConstraintError({message, errors, parent: err, fields});
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(?: ON .*)?\)$/); }
case 1451: {
const match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(?: ON .*)?\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({ return new sequelizeErrors.ForeignKeyConstraintError({
fields: null, fields: null,
index: match ? match[3] : undefined, index: match ? match[3] : undefined,
parent: err parent: err
}); });
}
case 1452: case 1452: {
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(.*)\)$/); const match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(.*)\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({ return new sequelizeErrors.ForeignKeyConstraintError({
fields: null, fields: null,
index: match ? match[1] : undefined, index: match ? match[1] : undefined,
parent: err parent: err
}); });
}
default: default:
return new sequelizeErrors.DatabaseError(err); return new sequelizeErrors.DatabaseError(err);
} }
}; }
Query.prototype.handleShowIndexesQuery = function (data) { handleShowIndexesQuery(data) {
// Group by index name, and collect all fields // Group by index name, and collect all fields
data = _.reduce(data, function (acc, item) { data = _.reduce(data, (acc, item) => {
if (!(item.Key_name in acc)) { if (!(item.Key_name in acc)) {
acc[item.Key_name] = item; acc[item.Key_name] = item;
item.fields = []; item.fields = [];
...@@ -230,16 +218,17 @@ Query.prototype.handleShowIndexesQuery = function (data) { ...@@ -230,16 +218,17 @@ Query.prototype.handleShowIndexesQuery = function (data) {
return acc; return acc;
}, {}); }, {});
return Utils._.map(data, function(item) { return Utils._.map(data, item => ({
return {
primary: item.Key_name === 'PRIMARY', primary: item.Key_name === 'PRIMARY',
fields: item.fields, fields: item.fields,
name: item.Key_name, name: item.Key_name,
tableName: item.Table, tableName: item.Table,
unique: (item.Non_unique !== 1), unique: (item.Non_unique !== 1),
type: item.Index_type, type: item.Index_type,
}; }));
}); }
}; }
module.exports = Query; module.exports = Query;
module.exports.Query = Query;
module.exports.default = Query;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!