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

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';
var AbstractConnectionManager = require('../abstract/connection-manager')
, ConnectionManager
, Utils = require('../../utils')
, Promise = require('../../promise')
, sequelizeErrors = require('../../errors')
, dataTypes = require('../../data-types').mysql
, parserMap = {};
const AbstractConnectionManager = require('../abstract/connection-manager');
const Utils = require('../../utils');
const Promise = require('../../promise');
const sequelizeErrors = require('../../errors');
const dataTypes = require('../../data-types').mysql;
const parserMap = new Map();
ConnectionManager = function(dialect, sequelize) {
AbstractConnectionManager.call(this, dialect, sequelize);
class ConnectionManager extends AbstractConnectionManager {
constructor(dialect, sequelize) {
super(dialect, sequelize);
this.sequelize = sequelize;
this.sequelize.config.port = this.sequelize.config.port || 3306;
......@@ -27,53 +27,50 @@ ConnectionManager = function(dialect, sequelize) {
}
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
ConnectionManager.prototype.$refreshTypeParser = function (dataType) {
dataType.types.mysql.forEach(function (type) {
parserMap[type] = dataType.parse;
});
};
// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
$refreshTypeParser(dataType) {
for (const type of dataType.types.mysql) {
parserMap.set(type, dataType.parse);
}
}
ConnectionManager.prototype.$clearTypeParser = function () {
parserMap = {};
};
$clearTypeParser() {
parserMap.clear();
}
ConnectionManager.$typecast = function (field, next) {
if (parserMap[field.type]) {
return parserMap[field.type](field, this.sequelize.options);
static $typecast(field, next) {
if (parserMap.has(field.type)) {
return parserMap.get(field.type)(field, this.sequelize.options);
}
return next();
};
}
ConnectionManager.prototype.connect = function(config) {
var self = this;
return new Promise(function (resolve, reject) {
var connectionConfig = {
connect(config) {
return new Promise((resolve, reject) => {
const connectionConfig = {
host: config.host,
port: config.port,
user: config.username,
password: config.password,
database: config.database,
timezone: self.sequelize.options.timezone,
typeCast: ConnectionManager.$typecast.bind(self),
timezone: this.sequelize.options.timezone,
typeCast: ConnectionManager.$typecast.bind(this),
bigNumberStrings: false,
supportBigNumbers: true
};
if (config.dialectOptions) {
Object.keys(config.dialectOptions).forEach(function(key) {
for (const key of Object.keys(config.dialectOptions)) {
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.code) {
switch (err.code) {
......@@ -110,21 +107,22 @@ ConnectionManager.prototype.connect = function(config) {
// server variable configures this)
//
// 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') {
// Remove it from read/write pool
self.pool.destroy(connection);
this.pool.destroy(connection);
}
});
}
resolve(connection);
});
}).tap(function (connection) {
connection.query("SET time_zone = '" + self.sequelize.options.timezone + "'"); /* jshint ignore: line */
}).tap(connection => {
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
// That wil trigger a connection error
......@@ -132,15 +130,21 @@ ConnectionManager.prototype.disconnect = function(connection) {
return Promise.resolve();
}
return new Promise(function (resolve, reject) {
connection.end(function(err) {
return new Promise((resolve, reject) => {
connection.end(err => {
if (err) return reject(new sequelizeErrors.ConnectionError(err));
resolve();
});
});
};
ConnectionManager.prototype.validate = function(connection) {
}
validate(connection) {
return connection && ['disconnected', 'protocol_error'].indexOf(connection.state) === -1;
};
}
}
Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);
module.exports = ConnectionManager;
module.exports.ConnectionManager = ConnectionManager;
module.exports.default = ConnectionManager;
......@@ -7,7 +7,7 @@
@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.
......@@ -19,32 +19,27 @@ var _ = require('lodash');
@param {String} columnName The name of the attribute that we want to remove.
@param {Object} options
*/
var removeColumn = function (tableName, columnName, options) {
var self = this;
function removeColumn(tableName, columnName, options) {
options = options || {};
return self.sequelize.query(
self.QueryGenerator.getForeignKeyQuery(tableName, columnName),
/* jshint validthis:true */
return this.sequelize.query(
this.QueryGenerator.getForeignKeyQuery(tableName, columnName),
_.assign({ raw: true }, options)
)
.spread(function (results) {
.spread(results => {
if (!results.length) {
// No foreign key constraints found, so we can remove the column
return;
}
return self.sequelize.query(
self.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name),
return this.sequelize.query(
this.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name),
_.assign({ raw: true }, options)
);
})
.then(function () {
return self.sequelize.query(
self.QueryGenerator.removeColumnQuery(tableName, columnName),
.then(() => this.sequelize.query(
this.QueryGenerator.removeColumnQuery(tableName, columnName),
_.assign({ raw: true }, options)
);
});
};
module.exports = {
removeColumn: removeColumn
};
));
}
exports.removeColumn = removeColumn;
'use strict';
var Utils = require('../../utils')
, AbstractQuery = require('../abstract/query')
, uuid = require('node-uuid')
, sequelizeErrors = require('../../errors.js')
, _ = require('lodash');
var Query = function(connection, sequelize, options) {
const Utils = require('../../utils');
const AbstractQuery = require('../abstract/query');
const uuid = require('node-uuid');
const sequelizeErrors = require('../../errors.js');
const _ = require('lodash');
class Query extends AbstractQuery {
constructor(connection, sequelize, options) {
super();
this.connection = connection;
this.instance = options.instance;
this.model = options.model;
......@@ -20,54 +22,48 @@ var Query = function(connection, sequelize, options) {
}, options || {});
this.checkLoggingOption();
};
}
Utils.inherit(Query, AbstractQuery);
Query.formatBindParameters = AbstractQuery.formatBindParameters;
Query.prototype.run = function(sql, parameters) {
var self = this;
run(sql, parameters) {
this.sql = sql;
//do we need benchmark for this query execution
var benchmark = this.sequelize.options.benchmark || this.options.benchmark;
var showWarnings = this.sequelize.options.showWarnings || this.options.showWarnings;
const benchmark = this.sequelize.options.benchmark || this.options.benchmark;
const showWarnings = this.sequelize.options.showWarnings || this.options.showWarnings;
let queryBegin;
if (benchmark) {
var queryBegin = Date.now();
queryBegin = Date.now();
} else {
this.sequelize.log('Executing (' + (this.connection.uuid || 'default') + '): ' + this.sql, this.options);
}
var promise = new Utils.Promise(function(resolve, reject) {
self.connection.query(self.sql, function(err, results) {
return new Utils.Promise((resolve, reject) => {
this.connection.query(this.sql, (err, results) => {
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) {
err.sql = sql;
reject(self.formatError(err));
reject(this.formatError(err));
} else {
resolve(results);
}
}).setMaxListeners(100);
})
// Log warnings if we've got them.
.then(function(results){
.then(results => {
if (showWarnings && results && results.warningCount > 0) {
return self.logWarnings(results);
return this.logWarnings(results);
}
return results;
})
// Return formatted results...
.then(function(results){
return self.formatResults(results);
});
return promise;
};
.then(results => this.formatResults(results));
}
/**
* High level function that handles the results of a query execution.
......@@ -85,8 +81,8 @@ Query.prototype.run = function(sql, parameters) {
*
* @param {Array} data - The result of the query execution.
*/
Query.prototype.formatResults = function(data) {
var result = this.instance;
formatResults(data) {
let result = this.instance;
if (this.isInsertQuery(data)) {
this.handleInsertQuery(data);
......@@ -103,15 +99,15 @@ Query.prototype.formatResults = function(data) {
} else if (this.isDescribeQuery()) {
result = {};
data.forEach(function(_result) {
var enumRegex = /^enum/i;
for (const _result of data) {
const enumRegex = /^enum/i;
result[_result.Field] = {
type: enumRegex.test(_result.Type) ? _result.Type.replace(enumRegex, 'ENUM') : _result.Type.toUpperCase(),
allowNull: (_result.Null === 'YES'),
defaultValue: _result.Default,
primaryKey: _result.Key === 'PRI'
};
});
}
} else if (this.isShowIndexesQuery()) {
result = this.handleShowIndexesQuery(data);
......@@ -129,43 +125,40 @@ Query.prototype.formatResults = function(data) {
}
return result;
};
}
Query.prototype.logWarnings = function (results) {
var self = this;
return this.run('SHOW WARNINGS').then(function(warningResults) {
var warningMessage = 'MySQL Warnings (' + (self.connection.uuid||'default') + '): ';
var messages = [];
logWarnings(results) {
return this.run('SHOW WARNINGS').then(warningResults => {
const warningMessage = 'MySQL Warnings (' + (this.connection.uuid||'default') + '): ';
const messages = [];
warningResults.forEach(function(_warningRow){
_warningRow.forEach(function(_warningResult) {
for (const _warningRow of warningResults) {
for (const _warningResult of _warningRow) {
if (_warningResult.hasOwnProperty('Message')) {
messages.push(_warningResult.Message);
} else {
_warningResult.keys().forEach( function(_objectKey) {
for (const _objectKey of _warningResult.keys()) {
messages.push([_objectKey, _warningResult[_objectKey]].join(': '));
});
}
});
});
}
}
}
self.sequelize.log(warningMessage + messages.join('; '), self.options);
this.sequelize.log(warningMessage + messages.join('; '), this.options);
return results;
});
};
Query.prototype.formatError = function (err) {
var match;
}
formatError(err) {
switch (err.errno || err.code) {
case 1062:
match = err.message.match(/Duplicate entry '(.*)' for key '?((.|\s)*?)'?$/);
case 1062: {
const match = err.message.match(/Duplicate entry '(.*)' for key '?((.|\s)*?)'?$/);
var values = match ? match[1].split('-') : undefined
, fields = {}
, message = 'Validation error'
, uniqueKey = this.model && this.model.uniqueKeys[match[2]];
let fields = {};
let message = 'Validation error';
const values = match ? match[1].split('-') : undefined;
const uniqueKey = this.model && this.model.uniqueKeys[match[2]];
if (!!uniqueKey) {
if (!!uniqueKey.msg) message = uniqueKey.msg;
......@@ -174,47 +167,42 @@ Query.prototype.formatError = function (err) {
fields[match[2]] = match[1];
}
var errors = [];
var self = this;
Utils._.forOwn(fields, function(value, field) {
const errors = [];
Utils._.forOwn(fields, (value, field) => {
errors.push(new sequelizeErrors.ValidationErrorItem(
self.getUniqueConstraintErrorMessage(field),
'unique violation', field, value));
});
return new sequelizeErrors.UniqueConstraintError({
message: message,
errors: errors,
parent: err,
fields: fields
this.getUniqueConstraintErrorMessage(field),
'unique violation', field, value
));
});
case 1451:
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(?: ON .*)?\)$/);
return new sequelizeErrors.UniqueConstraintError({message, errors, parent: err, fields});
}
case 1451: {
const match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(?: ON .*)?\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match ? match[3] : undefined,
parent: err
});
case 1452:
match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(.*)\)$/);
}
case 1452: {
const match = err.message.match(/FOREIGN KEY \(`(.*)`\) REFERENCES `(.*)` \(`(.*)`\)(.*)\)$/);
return new sequelizeErrors.ForeignKeyConstraintError({
fields: null,
index: match ? match[1] : undefined,
parent: err
});
}
default:
return new sequelizeErrors.DatabaseError(err);
}
};
}
Query.prototype.handleShowIndexesQuery = function (data) {
handleShowIndexesQuery(data) {
// 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)) {
acc[item.Key_name] = item;
item.fields = [];
......@@ -230,16 +218,17 @@ Query.prototype.handleShowIndexesQuery = function (data) {
return acc;
}, {});
return Utils._.map(data, function(item) {
return {
return Utils._.map(data, item => ({
primary: item.Key_name === 'PRIMARY',
fields: item.fields,
name: item.Key_name,
tableName: item.Table,
unique: (item.Non_unique !== 1),
type: item.Index_type,
};
});
};
}));
}
}
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!