UNPKG

sequelize

Version:

Sequelize is a promise-based Node.js ORM tool for Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server, Amazon Redshift and Snowflake’s Data Cloud. It features solid transaction support, relations, eager and lazy loading, read replication and more.

153 lines (135 loc) 4.96 kB
'use strict'; const AbstractConnectionManager = require('../abstract/connection-manager'); const SequelizeErrors = require('../../errors'); const { logger } = require('../../utils/logger'); const DataTypes = require('../../data-types').mysql; const momentTz = require('moment-timezone'); const debug = logger.debugContext('connection:mysql'); const parserStore = require('../parserStore')('mysql'); const { promisify } = require('util'); /** * MySQL Connection Manager * * Get connections, validate and disconnect them. * AbstractConnectionManager pooling use it to handle MySQL specific connections * Use https://github.com/sidorares/node-mysql2 to connect with MySQL server * * @private */ class ConnectionManager extends AbstractConnectionManager { constructor(dialect, sequelize) { sequelize.config.port = sequelize.config.port || 3306; super(dialect, sequelize); this.lib = this._loadDialectModule('mysql2'); this.refreshTypeParser(DataTypes); } _refreshTypeParser(dataType) { parserStore.refresh(dataType); } _clearTypeParser() { parserStore.clear(); } static _typecast(field, next) { if (parserStore.get(field.type)) { return parserStore.get(field.type)(field, this.sequelize.options, next); } return next(); } /** * Connect with MySQL database based on config, Handle any errors in connection * Set the pool handlers on connection.error * Also set proper timezone once connection is connected. * * @param {object} config * @returns {Promise<Connection>} * @private */ async connect(config) { const connectionConfig = { host: config.host, port: config.port, user: config.username, flags: '-FOUND_ROWS', password: config.password, database: config.database, timezone: this.sequelize.options.timezone, typeCast: ConnectionManager._typecast.bind(this), bigNumberStrings: false, supportBigNumbers: true, ...config.dialectOptions }; try { const connection = await new Promise((resolve, reject) => { const connection = this.lib.createConnection(connectionConfig); const errorHandler = e => { // clean up connect & error event if there is error connection.removeListener('connect', connectHandler); connection.removeListener('error', connectHandler); reject(e); }; const connectHandler = () => { // clean up error event if connected connection.removeListener('error', errorHandler); resolve(connection); }; // don't use connection.once for error event handling here // mysql2 emit error two times in case handshake was failed // first error is protocol_lost and second is timeout // if we will use `once.error` node process will crash on 2nd error emit connection.on('error', errorHandler); connection.once('connect', connectHandler); }); debug('connection acquired'); connection.on('error', error => { switch (error.code) { case 'ESOCKET': case 'ECONNRESET': case 'EPIPE': case 'PROTOCOL_CONNECTION_LOST': this.pool.destroy(connection); } }); if (!this.sequelize.config.keepDefaultTimezone) { // set timezone for this connection // but named timezone are not directly supported in mysql, so get its offset first let tzOffset = this.sequelize.options.timezone; tzOffset = /\//.test(tzOffset) ? momentTz.tz(tzOffset).format('Z') : tzOffset; await promisify(cb => connection.query(`SET time_zone = '${tzOffset}'`, cb))(); } return connection; } catch (err) { switch (err.code) { case 'ECONNREFUSED': throw new SequelizeErrors.ConnectionRefusedError(err); case 'ER_ACCESS_DENIED_ERROR': throw new SequelizeErrors.AccessDeniedError(err); case 'ENOTFOUND': throw new SequelizeErrors.HostNotFoundError(err); case 'EHOSTUNREACH': throw new SequelizeErrors.HostNotReachableError(err); case 'EINVAL': throw new SequelizeErrors.InvalidConnectionError(err); default: throw new SequelizeErrors.ConnectionError(err); } } } async disconnect(connection) { // Don't disconnect connections with CLOSED state if (connection._closing) { debug('connection tried to disconnect but was already at CLOSED state'); return; } return await promisify(callback => connection.end(callback))(); } validate(connection) { return connection && !connection._fatalError && !connection._protocolError && !connection._closing && !connection.stream.destroyed; } } module.exports = ConnectionManager; module.exports.ConnectionManager = ConnectionManager; module.exports.default = ConnectionManager;