connection-manager.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. 'use strict';
  2. const AbstractConnectionManager = require('../abstract/connection-manager');
  3. const AsyncQueue = require('./async-queue').default;
  4. const { logger } = require('../../utils/logger');
  5. const sequelizeErrors = require('../../errors');
  6. const DataTypes = require('../../data-types').mssql;
  7. const parserStore = require('../parserStore')('mssql');
  8. const debug = logger.debugContext('connection:mssql');
  9. const debugTedious = logger.debugContext('connection:mssql:tedious');
  10. class ConnectionManager extends AbstractConnectionManager {
  11. constructor(dialect, sequelize) {
  12. sequelize.config.port = sequelize.config.port || 1433;
  13. super(dialect, sequelize);
  14. this.lib = this._loadDialectModule('tedious');
  15. this.refreshTypeParser(DataTypes);
  16. }
  17. _refreshTypeParser(dataType) {
  18. parserStore.refresh(dataType);
  19. }
  20. _clearTypeParser() {
  21. parserStore.clear();
  22. }
  23. async connect(config) {
  24. const connectionConfig = {
  25. server: config.host,
  26. authentication: {
  27. type: 'default',
  28. options: {
  29. userName: config.username || undefined,
  30. password: config.password || undefined
  31. }
  32. },
  33. options: {
  34. port: parseInt(config.port, 10),
  35. database: config.database,
  36. trustServerCertificate: true
  37. }
  38. };
  39. if (config.dialectOptions) {
  40. // only set port if no instance name was provided
  41. if (
  42. config.dialectOptions.options &&
  43. config.dialectOptions.options.instanceName
  44. ) {
  45. delete connectionConfig.options.port;
  46. }
  47. if (config.dialectOptions.authentication) {
  48. Object.assign(connectionConfig.authentication, config.dialectOptions.authentication);
  49. }
  50. Object.assign(connectionConfig.options, config.dialectOptions.options);
  51. }
  52. try {
  53. return await new Promise((resolve, reject) => {
  54. const connection = new this.lib.Connection(connectionConfig);
  55. if (connection.state === connection.STATE.INITIALIZED) {
  56. connection.connect();
  57. }
  58. connection.queue = new AsyncQueue();
  59. connection.lib = this.lib;
  60. const connectHandler = error => {
  61. connection.removeListener('end', endHandler);
  62. connection.removeListener('error', errorHandler);
  63. if (error) return reject(error);
  64. debug('connection acquired');
  65. resolve(connection);
  66. };
  67. const endHandler = () => {
  68. connection.removeListener('connect', connectHandler);
  69. connection.removeListener('error', errorHandler);
  70. reject(new Error('Connection was closed by remote server'));
  71. };
  72. const errorHandler = error => {
  73. connection.removeListener('connect', connectHandler);
  74. connection.removeListener('end', endHandler);
  75. reject(error);
  76. };
  77. connection.once('error', errorHandler);
  78. connection.once('end', endHandler);
  79. connection.once('connect', connectHandler);
  80. /*
  81. * Permanently attach this event before connection is even acquired
  82. * tedious sometime emits error even after connect(with error).
  83. *
  84. * If we dont attach this even that unexpected error event will crash node process
  85. *
  86. * E.g. connectTimeout is set higher than requestTimeout
  87. */
  88. connection.on('error', error => {
  89. switch (error.code) {
  90. case 'ESOCKET':
  91. case 'ECONNRESET':
  92. this.pool.destroy(connection);
  93. }
  94. });
  95. if (config.dialectOptions && config.dialectOptions.debug) {
  96. connection.on('debug', debugTedious.log.bind(debugTedious));
  97. }
  98. });
  99. } catch (error) {
  100. if (!error.code) {
  101. throw new sequelizeErrors.ConnectionError(error);
  102. }
  103. switch (error.code) {
  104. case 'ESOCKET':
  105. if (error.message.includes('connect EHOSTUNREACH')) {
  106. throw new sequelizeErrors.HostNotReachableError(error);
  107. }
  108. if (error.message.includes('connect ENETUNREACH')) {
  109. throw new sequelizeErrors.HostNotReachableError(error);
  110. }
  111. if (error.message.includes('connect EADDRNOTAVAIL')) {
  112. throw new sequelizeErrors.HostNotReachableError(error);
  113. }
  114. if (error.message.includes('getaddrinfo ENOTFOUND')) {
  115. throw new sequelizeErrors.HostNotFoundError(error);
  116. }
  117. if (error.message.includes('connect ECONNREFUSED')) {
  118. throw new sequelizeErrors.ConnectionRefusedError(error);
  119. }
  120. throw new sequelizeErrors.ConnectionError(error);
  121. case 'ER_ACCESS_DENIED_ERROR':
  122. case 'ELOGIN':
  123. throw new sequelizeErrors.AccessDeniedError(error);
  124. case 'EINVAL':
  125. throw new sequelizeErrors.InvalidConnectionError(error);
  126. default:
  127. throw new sequelizeErrors.ConnectionError(error);
  128. }
  129. }
  130. }
  131. async disconnect(connection) {
  132. // Don't disconnect a connection that is already disconnected
  133. if (connection.closed) {
  134. return;
  135. }
  136. connection.queue.close();
  137. return new Promise(resolve => {
  138. connection.on('end', resolve);
  139. connection.close();
  140. debug('connection closed');
  141. });
  142. }
  143. validate(connection) {
  144. return connection && connection.loggedIn;
  145. }
  146. }
  147. module.exports = ConnectionManager;
  148. module.exports.ConnectionManager = ConnectionManager;
  149. module.exports.default = ConnectionManager;