'use strict' const tds = require('tedious') const debug = require('debug')('mssql:tedi') const BaseConnectionPool = require('../base/connection-pool') const { IDS } = require('../utils') const shared = require('../shared') const ConnectionError = require('../error/connection-error') class ConnectionPool extends BaseConnectionPool { _poolCreate () { return new shared.Promise((resolve, reject) => { const resolveOnce = (v) => { resolve(v) resolve = reject = () => {} } const rejectOnce = (e) => { reject(e) resolve = reject = () => {} } const cfg = { server: this.config.server, options: Object.assign({ encrypt: typeof this.config.encrypt === 'boolean' ? this.config.encrypt : true }, this.config.options), authentication: Object.assign({ type: this.config.domain !== undefined ? 'ntlm' : 'default', options: { userName: this.config.user, password: this.config.password, domain: this.config.domain } }, this.config.authentication) } cfg.options.database = this.config.database cfg.options.port = this.config.port cfg.options.connectTimeout = this.config.connectionTimeout || this.config.timeout || 15000 cfg.options.requestTimeout = this.config.requestTimeout != null ? this.config.requestTimeout : 15000 cfg.options.tdsVersion = cfg.options.tdsVersion || '7_4' cfg.options.rowCollectionOnDone = false cfg.options.rowCollectionOnRequestCompletion = false cfg.options.useColumnNames = false cfg.options.appName = cfg.options.appName || 'node-mssql' // tedious always connect via tcp when port is specified if (cfg.options.instanceName) delete cfg.options.port if (isNaN(cfg.options.requestTimeout)) cfg.options.requestTimeout = 15000 if (cfg.options.requestTimeout === Infinity) cfg.options.requestTimeout = 0 if (cfg.options.requestTimeout < 0) cfg.options.requestTimeout = 0 if (this.config.debug) { cfg.options.debug = { packet: true, token: true, data: true, payload: true } } const tedious = new tds.Connection(cfg) IDS.add(tedious, 'Connection') debug('pool(%d): connection #%d created', IDS.get(this), IDS.get(tedious)) debug('connection(%d): establishing', IDS.get(tedious)) tedious.once('connect', err => { if (err) { err = new ConnectionError(err) return rejectOnce(err) } debug('connection(%d): established', IDS.get(tedious)) resolveOnce(tedious) }) tedious.on('end', () => { const err = new ConnectionError('The connection ended without ever completing the connection') rejectOnce(err) }) tedious.on('error', err => { if (err.code === 'ESOCKET') { tedious.hasError = true } else { this.emit('error', err) } rejectOnce(err) }) if (this.config.debug) { tedious.on('debug', this.emit.bind(this, 'debug', tedious)) } if (typeof this.config.beforeConnect === 'function') { this.config.beforeConnect(tedious) } }) } _poolValidate (tedious) { return tedious && !tedious.closed && !tedious.hasError } _poolDestroy (tedious) { return new shared.Promise((resolve, reject) => { if (!tedious) { resolve() return } debug('connection(%d): destroying', IDS.get(tedious)) if (tedious.closed) { debug('connection(%d): already closed', IDS.get(tedious)) resolve() } else { tedious.once('end', () => { debug('connection(%d): destroyed', IDS.get(tedious)) resolve() }) tedious.close() } }) } } module.exports = ConnectionPool