@vuept/loopback-connector-mssql
Version:
Microsoft SQL Server connector for LoopBack
1,292 lines (1,112 loc) • 72.7 kB
JavaScript
'use strict';
var _isInteger = require('babel-runtime/core-js/number/is-integer');
var _isInteger2 = _interopRequireDefault(_isInteger);
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var deprecate = require('depd')('tedious');
var BulkLoad = require('./bulk-load');
var Debug = require('./debug');
var EventEmitter = require('events').EventEmitter;
var InstanceLookup = require('./instance-lookup').InstanceLookup;
var TransientErrorLookup = require('./transient-error-lookup.js').TransientErrorLookup;
var TYPE = require('./packet').TYPE;
var PreloginPayload = require('./prelogin-payload');
var Login7Payload = require('./login7-payload');
var NTLMResponsePayload = require('./ntlm-payload');
var Request = require('./request');
var RpcRequestPayload = require('./rpcrequest-payload');
var SqlBatchPayload = require('./sqlbatch-payload');
var MessageIO = require('./message-io');
var TokenStreamParser = require('./token/token-stream-parser').Parser;
var Transaction = require('./transaction').Transaction;
var ISOLATION_LEVEL = require('./transaction').ISOLATION_LEVEL;
var crypto = require('crypto');
var ConnectionError = require('./errors').ConnectionError;
var RequestError = require('./errors').RequestError;
var Connector = require('./connector').Connector;
// A rather basic state machine for managing a connection.
// Implements something approximating s3.2.1.
var KEEP_ALIVE_INITIAL_DELAY = 30 * 1000;
var DEFAULT_CONNECT_TIMEOUT = 15 * 1000;
var DEFAULT_CLIENT_REQUEST_TIMEOUT = 15 * 1000;
var DEFAULT_CANCEL_TIMEOUT = 5 * 1000;
var DEFAULT_CONNECT_RETRY_INTERVAL = 500;
var DEFAULT_PACKET_SIZE = 4 * 1024;
var DEFAULT_TEXTSIZE = '2147483647';
var DEFAULT_DATEFIRST = 7;
var DEFAULT_PORT = 1433;
var DEFAULT_TDS_VERSION = '7_4';
var DEFAULT_LANGUAGE = 'us_english';
var DEFAULT_DATEFORMAT = 'mdy';
function deprecateNonBooleanConfigValue(optionName, value) {
if (typeof value !== 'boolean') {
deprecate(`Passing non-boolean values for ${optionName} is deprecated and will be removed. Please specify \`true\` or \`false\` instead.`);
}
}
function deprecateNullConfigValue(optionName, value) {
if (value === null) {
deprecate(`Passing \`null\` for ${optionName} is deprecated and will be removed. Please pass an explicit value or \`undefined\` instead.`);
}
}
function deprecateNullFallbackToDefaultConfigValue(optionName, value) {
if (value === null) {
deprecate(`Passing \`null\` for ${optionName} will not fallback to a default value in future tedious versions. Please set a value explicitly if you require a different value from the one configured for your target SQL Server.`);
}
}
function deprecateNonStringConfigValue(optionName, value) {
if (typeof value !== 'string') {
deprecate(`Passing non-string values for ${optionName} will throw an error in future tedious versions. Please pass a string instead.`);
}
}
function deprecateNonNumberConfigValue(optionName, value) {
if (typeof value !== 'number') {
deprecate(`Passing non-number values for ${optionName} will throw an error in future tedious versions. Please pass a number instead.`);
}
}
var Connection = function (_EventEmitter) {
(0, _inherits3.default)(Connection, _EventEmitter);
function Connection(config) {
(0, _classCallCheck3.default)(this, Connection);
var _this = (0, _possibleConstructorReturn3.default)(this, (Connection.__proto__ || (0, _getPrototypeOf2.default)(Connection)).call(this));
if (!config) {
throw new TypeError('No connection configuration given');
}
if (typeof config.server !== 'string') {
throw new TypeError('Invalid server: ' + config.server);
}
if (config.domain != undefined) {
deprecateNonStringConfigValue('domain', config.domain);
}
deprecateNullConfigValue('domain', config.domain);
if (config.userName != undefined) {
deprecateNonStringConfigValue('userName', config.userName);
}
deprecateNullConfigValue('userName', config.userName);
if (config.password != undefined) {
deprecateNonStringConfigValue('password', config.password);
}
deprecateNullConfigValue('password', config.password);
_this.config = {
server: config.server,
userName: config.userName,
password: config.password,
domain: config.domain && config.domain.toUpperCase(),
options: {
abortTransactionOnError: false,
appName: undefined,
camelCaseColumns: false,
cancelTimeout: DEFAULT_CANCEL_TIMEOUT,
columnNameReplacer: undefined,
connectionRetryInterval: DEFAULT_CONNECT_RETRY_INTERVAL,
connectTimeout: DEFAULT_CONNECT_TIMEOUT,
connectionIsolationLevel: ISOLATION_LEVEL.READ_COMMITTED,
cryptoCredentialsDetails: {},
database: undefined,
datefirst: DEFAULT_DATEFIRST,
dateFormat: DEFAULT_DATEFORMAT,
debug: {
data: false,
packet: false,
payload: false,
token: false
},
enableAnsiNull: true,
enableAnsiNullDefault: true,
enableAnsiPadding: true,
enableAnsiWarnings: true,
enableArithAbort: false,
enableConcatNullYieldsNull: true,
enableCursorCloseOnCommit: undefined,
enableImplicitTransactions: false,
enableNumericRoundabort: false,
enableQuotedIdentifier: true,
encrypt: false,
fallbackToDefaultDb: false,
instanceName: undefined,
isolationLevel: ISOLATION_LEVEL.READ_COMMITTED,
language: DEFAULT_LANGUAGE,
localAddress: undefined,
maxRetriesOnTransientErrors: 3,
multiSubnetFailover: false,
packetSize: DEFAULT_PACKET_SIZE,
port: DEFAULT_PORT,
readOnlyIntent: false,
requestTimeout: DEFAULT_CLIENT_REQUEST_TIMEOUT,
rowCollectionOnDone: false,
rowCollectionOnRequestCompletion: false,
tdsVersion: DEFAULT_TDS_VERSION,
textsize: DEFAULT_TEXTSIZE,
trustServerCertificate: true,
useColumnNames: false,
useUTC: true
}
};
if (config.options) {
if (config.options.port && config.options.instanceName) {
throw new Error('Port and instanceName are mutually exclusive, but ' + config.options.port + ' and ' + config.options.instanceName + ' provided');
}
if (config.options.abortTransactionOnError != undefined) {
if (typeof config.options.abortTransactionOnError !== 'boolean') {
throw new TypeError('options.abortTransactionOnError must be a boolean (true or false).');
}
_this.config.options.abortTransactionOnError = config.options.abortTransactionOnError;
}
deprecateNullFallbackToDefaultConfigValue('options.abortTransactionOnError', config.options.abortTransactionOnError);
if (config.options.appName != undefined) {
deprecateNonStringConfigValue('options.appName', config.options.appName);
_this.config.options.appName = config.options.appName;
}
deprecateNullConfigValue('options.appName', config.options.appName);
if (config.options.camelCaseColumns != undefined) {
deprecateNonBooleanConfigValue('options.camelCaseColumns', config.options.camelCaseColumns);
_this.config.options.camelCaseColumns = config.options.camelCaseColumns;
}
deprecateNullConfigValue('options.camelCaseColumns', config.options.camelCaseColumns);
if (config.options.cancelTimeout != undefined) {
_this.config.options.cancelTimeout = config.options.cancelTimeout;
}
deprecateNullConfigValue('options.cancelTimeout', config.options.cancelTimeout);
if (config.options.columnNameReplacer) {
if (typeof config.options.columnNameReplacer !== 'function') {
throw new TypeError('options.columnNameReplacer must be a function or null.');
}
_this.config.options.columnNameReplacer = config.options.columnNameReplacer;
}
deprecateNullConfigValue('options.columnNameReplacer', config.options.columnNameReplacer);
if (config.options.connectTimeout) {
_this.config.options.connectTimeout = config.options.connectTimeout;
}
deprecateNullConfigValue('options.connectTimeout', config.options.connectTimeout);
if (config.options.connectionIsolationLevel) {
_this.config.options.connectionIsolationLevel = config.options.connectionIsolationLevel;
}
deprecateNullFallbackToDefaultConfigValue('options.connectionIsolationLevel', config.options.connectionIsolationLevel);
if (config.options.cryptoCredentialsDetails) {
_this.config.options.cryptoCredentialsDetails = config.options.cryptoCredentialsDetails;
}
deprecateNullConfigValue('options.cryptoCredentialsDetails', config.options.cryptoCredentialsDetails);
if (config.options.database != undefined) {
deprecateNonStringConfigValue('options.database', config.options.database);
_this.config.options.database = config.options.database;
}
deprecateNullConfigValue('options.database', config.options.database);
if (config.options.datefirst) {
if (config.options.datefirst < 1 || config.options.datefirst > 7) {
throw new RangeError('DateFirst should be >= 1 and <= 7');
}
deprecateNonNumberConfigValue('options.datefirst', config.options.datefirst);
_this.config.options.datefirst = config.options.datefirst;
}
deprecateNullFallbackToDefaultConfigValue('options.datefirst', config.options.datefirst);
if (config.options.dateFormat != undefined) {
deprecateNonStringConfigValue('options.dateFormat', config.options.dateFormat);
_this.config.options.dateFormat = config.options.dateFormat;
}
deprecateNullFallbackToDefaultConfigValue('options.dateFormat', config.options.dateFormat);
if (config.options.debug) {
if (config.options.debug.data != undefined) {
deprecateNonBooleanConfigValue('options.debug.data', config.options.debug.data);
_this.config.options.debug.data = config.options.debug.data;
}
deprecateNullConfigValue('options.debug.data', config.options.debug.data);
if (config.options.debug.packet != undefined) {
deprecateNonBooleanConfigValue('options.debug.packet', config.options.debug.packet);
_this.config.options.debug.packet = config.options.debug.packet;
}
deprecateNullConfigValue('options.debug.packet', config.options.debug.packet);
if (config.options.debug.payload != undefined) {
deprecateNonBooleanConfigValue('options.debug.payload', config.options.debug.payload);
_this.config.options.debug.payload = config.options.debug.payload;
}
deprecateNullConfigValue('options.debug.payload', config.options.debug.payload);
if (config.options.debug.token != undefined) {
deprecateNonBooleanConfigValue('options.debug.token', config.options.debug.token);
_this.config.options.debug.token = config.options.debug.token;
}
deprecateNullConfigValue('options.debug.token', config.options.debug.token);
}
if (config.options.enableAnsiNull != undefined) {
if (typeof config.options.enableAnsiNull !== 'boolean') {
throw new TypeError('options.enableAnsiNull must be a boolean (true or false).');
}
_this.config.options.enableAnsiNull = config.options.enableAnsiNull;
}
deprecateNullFallbackToDefaultConfigValue('options.enableAnsiNull', config.options.enableAnsiNull);
if (config.options.enableAnsiNullDefault != undefined) {
if (typeof config.options.enableAnsiNullDefault !== 'boolean') {
throw new TypeError('options.enableAnsiNullDefault must be a boolean (true or false).');
}
_this.config.options.enableAnsiNullDefault = config.options.enableAnsiNullDefault;
}
deprecateNullFallbackToDefaultConfigValue('options.enableAnsiNullDefault', config.options.enableAnsiNullDefault);
if (config.options.enableAnsiPadding != undefined) {
if (typeof config.options.enableAnsiPadding !== 'boolean') {
throw new TypeError('options.enableAnsiPadding must be a boolean (true or false).');
}
_this.config.options.enableAnsiPadding = config.options.enableAnsiPadding;
}
deprecateNullFallbackToDefaultConfigValue('options.enableAnsiPadding', config.options.enableAnsiPadding);
if (config.options.enableAnsiWarnings != undefined) {
if (typeof config.options.enableAnsiWarnings !== 'boolean') {
throw new TypeError('options.enableAnsiWarnings must be a boolean (true or false).');
}
_this.config.options.enableAnsiWarnings = config.options.enableAnsiWarnings;
}
deprecateNullFallbackToDefaultConfigValue('options.enableAnsiWarnings', config.options.enableAnsiWarnings);
if (config.options.enableArithAbort !== undefined) {
if (typeof config.options.enableArithAbort !== 'boolean') {
throw new TypeError('options.enableArithAbort must be a boolean (true or false).');
}
_this.config.options.enableArithAbort = config.options.enableArithAbort;
}
deprecateNullFallbackToDefaultConfigValue('options.enableArithAbort', config.options.enableArithAbort);
if (config.options.enableConcatNullYieldsNull != undefined) {
if (typeof config.options.enableConcatNullYieldsNull !== 'boolean') {
throw new TypeError('options.enableConcatNullYieldsNull must be a boolean (true or false).');
}
_this.config.options.enableConcatNullYieldsNull = config.options.enableConcatNullYieldsNull;
}
deprecateNullFallbackToDefaultConfigValue('options.enableConcatNullYieldsNull', config.options.enableConcatNullYieldsNull);
if (config.options.enableCursorCloseOnCommit != undefined) {
if (typeof config.options.enableCursorCloseOnCommit !== 'boolean') {
throw new TypeError('options.enableCursorCloseOnCommit must be a boolean (true or false).');
}
_this.config.options.enableCursorCloseOnCommit = config.options.enableCursorCloseOnCommit;
}
deprecateNullFallbackToDefaultConfigValue('options.enableCursorCloseOnCommit', config.options.enableCursorCloseOnCommit);
if (config.options.enableImplicitTransactions != undefined) {
if (typeof config.options.enableImplicitTransactions !== 'boolean') {
throw new TypeError('options.enableImplicitTransactions must be a boolean (true or false).');
}
_this.config.options.enableImplicitTransactions = config.options.enableImplicitTransactions;
}
deprecateNullFallbackToDefaultConfigValue('options.enableImplicitTransactions', config.options.enableImplicitTransactions);
if (config.options.enableNumericRoundabort != undefined) {
if (typeof config.options.enableNumericRoundabort !== 'boolean') {
throw new TypeError('options.enableNumericRoundabort must be a boolean (true or false).');
}
_this.config.options.enableNumericRoundabort = config.options.enableNumericRoundabort;
}
deprecateNullFallbackToDefaultConfigValue('options.enableNumericRoundabort', config.options.enableNumericRoundabort);
if (config.options.enableQuotedIdentifier !== undefined) {
if (typeof config.options.enableQuotedIdentifier !== 'boolean') {
throw new TypeError('options.enableQuotedIdentifier must be a boolean (true or false).');
}
_this.config.options.enableQuotedIdentifier = config.options.enableQuotedIdentifier;
}
deprecateNullFallbackToDefaultConfigValue('options.enableQuotedIdentifier', config.options.enableQuotedIdentifier);
if (config.options.encrypt != undefined) {
deprecateNonBooleanConfigValue('options.encrypt', config.options.encrypt);
_this.config.options.encrypt = config.options.encrypt;
} else {
deprecate('The default value for `options.encrypt` will change from `false` to `true`. Please pass `false` explicitly if you want to retain current behaviour.');
}
deprecateNullConfigValue('options.encrypt', config.options.encrypt);
if (config.options.fallbackToDefaultDb != undefined) {
deprecateNonBooleanConfigValue('options.fallbackToDefaultDb', config.options.fallbackToDefaultDb);
_this.config.options.fallbackToDefaultDb = config.options.fallbackToDefaultDb;
}
deprecateNullConfigValue('options.fallbackToDefaultDb', config.options.fallbackToDefaultDb);
if (config.options.instanceName != undefined) {
deprecateNonStringConfigValue('options.instanceName', config.options.instanceName);
_this.config.options.instanceName = config.options.instanceName;
_this.config.options.port = undefined;
}
deprecateNullConfigValue('options.instanceName', config.options.instanceName);
if (config.options.isolationLevel) {
_this.config.options.isolationLevel = config.options.isolationLevel;
}
deprecateNullConfigValue('options.isolationLevel', config.options.isolationLevel);
if (config.options.language != undefined) {
deprecateNonStringConfigValue('options.language', config.options.language);
_this.config.options.language = config.options.language;
}
deprecateNullFallbackToDefaultConfigValue('options.language', config.options.language);
if (config.options.localAddress != undefined) {
_this.config.options.localAddress = config.options.localAddress;
}
deprecateNullConfigValue('options.localAddress', config.options.localAddress);
if (config.options.multiSubnetFailover != undefined) {
deprecateNonBooleanConfigValue('options.multiSubnetFailover', config.options.multiSubnetFailover);
_this.config.options.multiSubnetFailover = !!config.options.multiSubnetFailover;
}
deprecateNullConfigValue('options.multiSubnetFailover', config.options.multiSubnetFailover);
if (config.options.packetSize) {
deprecateNonNumberConfigValue('options.packetSize', config.options.packetSize);
_this.config.options.packetSize = config.options.packetSize;
}
deprecateNullConfigValue('options.packetSize', config.options.packetSize);
if (config.options.port) {
if (config.options.port <= 0 || config.options.port >= 65536) {
throw new RangeError('Port must be > 0 and < 65536');
}
deprecateNonNumberConfigValue('options.port', config.options.port);
_this.config.options.port = config.options.port;
_this.config.options.instanceName = undefined;
}
deprecateNullConfigValue('options.port', config.options.port);
if (config.options.readOnlyIntent != undefined) {
deprecateNonBooleanConfigValue('options.readOnlyIntent', config.options.readOnlyIntent);
_this.config.options.readOnlyIntent = config.options.readOnlyIntent;
}
deprecateNullConfigValue('options.readOnlyIntent', config.options.readOnlyIntent);
if (config.options.requestTimeout != undefined) {
deprecateNonNumberConfigValue('options.requestTimeout', config.options.requestTimeout);
_this.config.options.requestTimeout = config.options.requestTimeout;
}
deprecateNullConfigValue('options.requestTimeout', config.options.requestTimeout);
if (config.options.maxRetriesOnTransientErrors != undefined) {
if (!(0, _isInteger2.default)(config.options.maxRetriesOnTransientErrors) || config.options.maxRetriesOnTransientErrors < 0) {
throw new RangeError('options.maxRetriesOnTransientErrors must be a non-negative integer.');
}
_this.config.options.maxRetriesOnTransientErrors = config.options.maxRetriesOnTransientErrors;
}
deprecateNullConfigValue('options.maxRetriesOnTransientErrors', config.options.maxRetriesOnTransientErrors);
if (config.options.connectionRetryInterval != undefined) {
if (!(0, _isInteger2.default)(config.options.connectionRetryInterval) || config.options.connectionRetryInterval <= 0) {
throw new TypeError('options.connectionRetryInterval must be a non-zero positive integer.');
}
_this.config.options.connectionRetryInterval = config.options.connectionRetryInterval;
}
deprecateNullConfigValue('options.connectionRetryInterval', config.options.connectionRetryInterval);
if (config.options.rowCollectionOnDone != undefined) {
deprecateNonBooleanConfigValue('options.rowCollectionOnDone', config.options.rowCollectionOnDone);
_this.config.options.rowCollectionOnDone = config.options.rowCollectionOnDone;
}
deprecateNullConfigValue('options.rowCollectionOnDone', config.options.rowCollectionOnDone);
if (config.options.rowCollectionOnRequestCompletion != undefined) {
deprecateNonBooleanConfigValue('options.rowCollectionOnRequestCompletion', config.options.rowCollectionOnRequestCompletion);
_this.config.options.rowCollectionOnRequestCompletion = config.options.rowCollectionOnRequestCompletion;
}
deprecateNullConfigValue('options.rowCollectionOnRequestCompletion', config.options.rowCollectionOnRequestCompletion);
if (config.options.tdsVersion) {
deprecateNonStringConfigValue('options.tdsVersion', config.options.tdsVersion);
_this.config.options.tdsVersion = config.options.tdsVersion;
}
deprecateNullConfigValue('options.tdsVersion', config.options.tdsVersion);
if (config.options.textsize) {
deprecateNonNumberConfigValue('options.textsize', config.options.textsize);
_this.config.options.textsize = config.options.textsize;
}
deprecateNullFallbackToDefaultConfigValue('options.textsize', config.options.textsize);
if (config.options.trustServerCertificate != undefined) {
deprecateNonBooleanConfigValue('options.trustServerCertificate', config.options.trustServerCertificate);
_this.config.options.trustServerCertificate = config.options.trustServerCertificate;
}
deprecateNullConfigValue('options.trustServerCertificate', config.options.trustServerCertificate);
if (config.options.useColumnNames != undefined) {
deprecateNonBooleanConfigValue('options.useColumnNames', config.options.useColumnNames);
_this.config.options.useColumnNames = config.options.useColumnNames;
}
deprecateNullConfigValue('options.useColumnNames', config.options.useColumnNames);
if (config.options.useUTC != undefined) {
deprecateNonBooleanConfigValue('options.useUTC', config.options.useUTC);
_this.config.options.useUTC = config.options.useUTC;
}
deprecateNullConfigValue('options.useUTC', config.options.useUTC);
}
_this.createDebug();
_this.createTokenStreamParser();
_this.inTransaction = false;
_this.transactionDescriptors = [new Buffer([0, 0, 0, 0, 0, 0, 0, 0])];
_this.transitionTo(_this.STATE.CONNECTING);
if (_this.config.options.tdsVersion < '7_2') {
// 'beginTransaction', 'commitTransaction' and 'rollbackTransaction'
// events are utilized to maintain inTransaction property state which in
// turn is used in managing transactions. These events are only fired for
// TDS version 7.2 and beyond. The properties below are used to emulate
// equivalent behavior for TDS versions before 7.2.
_this.transactionDepth = 0;
_this.isSqlBatch = false;
}
_this.curTransientRetryCount = 0;
_this.transientErrorLookup = new TransientErrorLookup();
_this.cleanupTypeEnum = {
NORMAL: 0,
REDIRECT: 1,
RETRY: 2
};
return _this;
}
(0, _createClass3.default)(Connection, [{
key: 'close',
value: function close() {
this.transitionTo(this.STATE.FINAL);
}
}, {
key: 'initialiseConnection',
value: function initialiseConnection() {
this.connect();
this.createConnectTimer();
}
}, {
key: 'cleanupConnection',
value: function cleanupConnection(cleanupTypeEnum) {
if (!this.closed) {
this.clearConnectTimer();
this.clearRequestTimer();
this.clearRetryTimer();
this.closeConnection();
if (cleanupTypeEnum === this.cleanupTypeEnum.REDIRECT) {
this.emit('rerouting');
} else if (cleanupTypeEnum !== this.cleanupTypeEnum.RETRY) {
this.emit('end');
}
if (this.request) {
var err = RequestError('Connection closed before request completed.', 'ECLOSE');
this.request.callback(err);
this.request = undefined;
}
this.closed = true;
this.loggedIn = false;
this.loginError = null;
}
}
}, {
key: 'createDebug',
value: function createDebug() {
var _this2 = this;
this.debug = new Debug(this.config.options.debug);
this.debug.on('debug', function (message) {
_this2.emit('debug', message);
});
}
}, {
key: 'createTokenStreamParser',
value: function createTokenStreamParser() {
var _this3 = this;
this.tokenStreamParser = new TokenStreamParser(this.debug, undefined, this.config.options);
this.tokenStreamParser.on('infoMessage', function (token) {
_this3.emit('infoMessage', token);
});
this.tokenStreamParser.on('sspichallenge', function (token) {
if (token.ntlmpacket) {
_this3.ntlmpacket = token.ntlmpacket;
_this3.ntlmpacketBuffer = token.ntlmpacketBuffer;
}
_this3.emit('sspichallenge', token);
});
this.tokenStreamParser.on('errorMessage', function (token) {
_this3.emit('errorMessage', token);
if (_this3.loggedIn) {
if (_this3.request) {
_this3.request.error = RequestError(token.message, 'EREQUEST');
_this3.request.error.number = token.number;
_this3.request.error.state = token.state;
_this3.request.error['class'] = token['class'];
_this3.request.error.serverName = token.serverName;
_this3.request.error.procName = token.procName;
_this3.request.error.lineNumber = token.lineNumber;
}
} else {
var isLoginErrorTransient = _this3.transientErrorLookup.isTransientError(token.number);
if (isLoginErrorTransient && _this3.curTransientRetryCount !== _this3.config.options.maxRetriesOnTransientErrors) {
_this3.debug.log('Initiating retry on transient error = ', token.number);
_this3.transitionTo(_this3.STATE.TRANSIENT_FAILURE_RETRY);
} else {
_this3.loginError = ConnectionError(token.message, 'ELOGIN');
}
}
});
this.tokenStreamParser.on('databaseChange', function (token) {
_this3.emit('databaseChange', token.newValue);
});
this.tokenStreamParser.on('languageChange', function (token) {
_this3.emit('languageChange', token.newValue);
});
this.tokenStreamParser.on('charsetChange', function (token) {
_this3.emit('charsetChange', token.newValue);
});
this.tokenStreamParser.on('loginack', function (token) {
if (!token.tdsVersion) {
// unsupported TDS version
_this3.loginError = ConnectionError('Server responded with unknown TDS version.', 'ETDS');
_this3.loggedIn = false;
return;
}
if (!token['interface']) {
// unsupported interface
_this3.loginError = ConnectionError('Server responded with unsupported interface.', 'EINTERFACENOTSUPP');
_this3.loggedIn = false;
return;
}
// use negotiated version
_this3.config.options.tdsVersion = token.tdsVersion;
_this3.loggedIn = true;
});
this.tokenStreamParser.on('routingChange', function (token) {
_this3.routingData = token.newValue;
_this3.dispatchEvent('routingChange');
});
this.tokenStreamParser.on('packetSizeChange', function (token) {
_this3.messageIo.packetSize(token.newValue);
});
// A new top-level transaction was started. This is not fired
// for nested transactions.
this.tokenStreamParser.on('beginTransaction', function (token) {
_this3.transactionDescriptors.push(token.newValue);
_this3.inTransaction = true;
});
// A top-level transaction was committed. This is not fired
// for nested transactions.
this.tokenStreamParser.on('commitTransaction', function () {
_this3.transactionDescriptors.length = 1;
_this3.inTransaction = false;
});
// A top-level transaction was rolled back. This is not fired
// for nested transactions. This is also fired if a batch
// aborting error happened that caused a rollback.
this.tokenStreamParser.on('rollbackTransaction', function () {
_this3.transactionDescriptors.length = 1;
// An outermost transaction was rolled back. Reset the transaction counter
_this3.inTransaction = false;
_this3.emit('rollbackTransaction');
});
this.tokenStreamParser.on('columnMetadata', function (token) {
if (_this3.request) {
var columns = void 0;
if (_this3.config.options.useColumnNames) {
columns = {};
for (var j = 0, len = token.columns.length; j < len; j++) {
var col = token.columns[j];
if (columns[col.colName] == null) {
columns[col.colName] = col;
}
}
} else {
columns = token.columns;
}
_this3.request.emit('columnMetadata', columns);
} else {
_this3.emit('error', new Error("Received 'columnMetadata' when no sqlRequest is in progress"));
_this3.close();
}
});
this.tokenStreamParser.on('order', function (token) {
if (_this3.request) {
_this3.request.emit('order', token.orderColumns);
} else {
_this3.emit('error', new Error("Received 'order' when no sqlRequest is in progress"));
_this3.close();
}
});
this.tokenStreamParser.on('row', function (token) {
if (_this3.request) {
if (_this3.config.options.rowCollectionOnRequestCompletion) {
_this3.request.rows.push(token.columns);
}
if (_this3.config.options.rowCollectionOnDone) {
_this3.request.rst.push(token.columns);
}
if (!(_this3.state === _this3.STATE.SENT_ATTENTION && _this3.request.paused)) {
_this3.request.emit('row', token.columns);
}
} else {
_this3.emit('error', new Error("Received 'row' when no sqlRequest is in progress"));
_this3.close();
}
});
this.tokenStreamParser.on('returnStatus', function (token) {
if (_this3.request) {
// Keep value for passing in 'doneProc' event.
_this3.procReturnStatusValue = token.value;
}
});
this.tokenStreamParser.on('returnValue', function (token) {
if (_this3.request) {
_this3.request.emit('returnValue', token.paramName, token.value, token.metadata);
}
});
this.tokenStreamParser.on('doneProc', function (token) {
if (_this3.request) {
_this3.request.emit('doneProc', token.rowCount, token.more, _this3.procReturnStatusValue, _this3.request.rst);
_this3.procReturnStatusValue = undefined;
if (token.rowCount !== undefined) {
_this3.request.rowCount += token.rowCount;
}
if (_this3.config.options.rowCollectionOnDone) {
_this3.request.rst = [];
}
}
});
this.tokenStreamParser.on('doneInProc', function (token) {
if (_this3.request) {
_this3.request.emit('doneInProc', token.rowCount, token.more, _this3.request.rst);
if (token.rowCount !== undefined) {
_this3.request.rowCount += token.rowCount;
}
if (_this3.config.options.rowCollectionOnDone) {
_this3.request.rst = [];
}
}
});
this.tokenStreamParser.on('done', function (token) {
if (_this3.request) {
if (token.attention) {
_this3.dispatchEvent('attention');
}
if (token.sqlError && !_this3.request.error) {
// check if the DONE_ERROR flags was set, but an ERROR token was not sent.
_this3.request.error = RequestError('An unknown error has occurred.', 'UNKNOWN');
}
_this3.request.emit('done', token.rowCount, token.more, _this3.request.rst);
if (token.rowCount !== undefined) {
_this3.request.rowCount += token.rowCount;
}
if (_this3.config.options.rowCollectionOnDone) {
_this3.request.rst = [];
}
}
});
this.tokenStreamParser.on('endOfMessage', function () {
// EOM pseudo token received
if (_this3.state === _this3.STATE.SENT_CLIENT_REQUEST) {
_this3.dispatchEvent('endOfMessageMarkerReceived');
}
});
this.tokenStreamParser.on('resetConnection', function () {
_this3.emit('resetConnection');
});
this.tokenStreamParser.on('tokenStreamError', function (error) {
_this3.emit('error', error);
_this3.close();
});
this.tokenStreamParser.on('drain', function () {
// Bridge the release of backpressure from the token stream parser
// transform to the packet stream transform.
_this3.messageIo.resume();
});
}
}, {
key: 'connect',
value: function connect() {
var _this4 = this;
if (this.config.options.port) {
return this.connectOnPort(this.config.options.port, this.config.options.multiSubnetFailover);
} else {
return new InstanceLookup().instanceLookup({
server: this.config.server,
instanceName: this.config.options.instanceName,
timeout: this.config.options.connectTimeout
}, function (message, port) {
if (_this4.state === _this4.STATE.FINAL) {
return;
}
if (message) {
_this4.emit('connect', ConnectionError(message, 'EINSTLOOKUP'));
} else {
_this4.connectOnPort(port, _this4.config.options.multiSubnetFailover);
}
});
}
}
}, {
key: 'connectOnPort',
value: function connectOnPort(port, multiSubnetFailover) {
var _this5 = this;
var connectOpts = {
host: this.routingData ? this.routingData.server : this.config.server,
port: this.routingData ? this.routingData.port : port,
localAddress: this.config.options.localAddress
};
new Connector(connectOpts, multiSubnetFailover).execute(function (err, socket) {
if (err) {
return _this5.socketError(err);
}
if (_this5.state === _this5.STATE.FINAL) {
socket.destroy();
return;
}
_this5.socket = socket;
_this5.socket.on('error', function (error) {
_this5.socketError(error);
});
_this5.socket.on('close', function () {
_this5.socketClose();
});
_this5.socket.on('end', function () {
_this5.socketEnd();
});
_this5.messageIo = new MessageIO(_this5.socket, _this5.config.options.packetSize, _this5.debug);
_this5.messageIo.on('data', function (data) {
_this5.dispatchEvent('data', data);
});
_this5.messageIo.on('message', function () {
_this5.dispatchEvent('message');
});
_this5.messageIo.on('secure', function (cleartext) {
_this5.emit('secure', cleartext);
});
_this5.socketConnect();
});
}
}, {
key: 'closeConnection',
value: function closeConnection() {
if (this.socket) {
this.socket.destroy();
}
}
}, {
key: 'createConnectTimer',
value: function createConnectTimer() {
var _this6 = this;
this.connectTimer = setTimeout(function () {
_this6.connectTimeout();
}, this.config.options.connectTimeout);
}
}, {
key: 'createRequestTimer',
value: function createRequestTimer() {
var _this7 = this;
this.clearRequestTimer(); // release old timer, just to be safe
if (this.config.options.requestTimeout) {
this.requestTimer = setTimeout(function () {
_this7.requestTimeout();
}, this.config.options.requestTimeout);
}
}
}, {
key: 'createRetryTimer',
value: function createRetryTimer() {
var _this8 = this;
this.clearRetryTimer();
this.retryTimer = setTimeout(function () {
_this8.retryTimeout();
}, this.config.options.connectionRetryInterval);
}
}, {
key: 'connectTimeout',
value: function connectTimeout() {
var message = `Failed to connect to ${this.config.server}${this.config.options.port ? `:${this.config.options.port}` : `\\${this.config.options.instanceName}`} in ${this.config.options.connectTimeout}ms`;
this.debug.log(message);
this.emit('connect', ConnectionError(message, 'ETIMEOUT'));
this.connectTimer = undefined;
this.dispatchEvent('connectTimeout');
}
}, {
key: 'requestTimeout',
value: function requestTimeout() {
this.requestTimer = undefined;
this.messageIo.sendMessage(TYPE.ATTENTION);
this.transitionTo(this.STATE.SENT_ATTENTION);
}
}, {
key: 'retryTimeout',
value: function retryTimeout() {
this.retryTimer = undefined;
this.emit('retry');
this.transitionTo(this.STATE.CONNECTING);
}
}, {
key: 'clearConnectTimer',
value: function clearConnectTimer() {
if (this.connectTimer) {
clearTimeout(this.connectTimer);
}
}
}, {
key: 'clearRequestTimer',
value: function clearRequestTimer() {
if (this.requestTimer) {
clearTimeout(this.requestTimer);
this.requestTimer = undefined;
}
}
}, {
key: 'clearRetryTimer',
value: function clearRetryTimer() {
if (this.retryTimer) {
clearTimeout(this.retryTimer);
this.retryTimer = undefined;
}
}
}, {
key: 'transitionTo',
value: function transitionTo(newState) {
if (this.state === newState) {
this.debug.log('State is already ' + newState.name);
return;
}
if (this.state && this.state.exit) {
this.state.exit.call(this, newState);
}
this.debug.log('State change: ' + (this.state ? this.state.name : undefined) + ' -> ' + newState.name);
this.state = newState;
if (this.state.enter) {
this.state.enter.apply(this);
}
}
}, {
key: 'dispatchEvent',
value: function dispatchEvent(eventName) {
if (this.state.events[eventName]) {
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
this.state.events[eventName].apply(this, args);
} else {
this.emit('error', new Error(`No event '${eventName}' in state '${this.state.name}'`));
this.close();
}
}
}, {
key: 'socketError',
value: function socketError(error) {
if (this.state === this.STATE.CONNECTING || this.state === this.STATE.SENT_TLSSSLNEGOTIATION) {
var message = `Failed to connect to ${this.config.server}:${this.config.options.port} - ${error.message}`;
this.debug.log(message);
this.emit('connect', ConnectionError(message, 'ESOCKET'));
} else {
var _message = `Connection lost - ${error.message}`;
this.debug.log(_message);
this.emit('error', ConnectionError(_message, 'ESOCKET'));
}
this.dispatchEvent('socketError', error);
}
}, {
key: 'socketConnect',
value: function socketConnect() {
this.socket.setKeepAlive(true, KEEP_ALIVE_INITIAL_DELAY);
this.closed = false;
this.debug.log('connected to ' + this.config.server + ':' + this.config.options.port);
this.dispatchEvent('socketConnect');
}
}, {
key: 'socketEnd',
value: function socketEnd() {
this.debug.log('socket ended');
this.transitionTo(this.STATE.FINAL);
}
}, {
key: 'socketClose',
value: function socketClose() {
this.debug.log('connection to ' + this.config.server + ':' + this.config.options.port + ' closed');
if (this.state === this.STATE.REROUTING) {
this.debug.log('Rerouting to ' + this.routingData.server + ':' + this.routingData.port);
this.dispatchEvent('reconnect');
} else if (this.state === this.STATE.TRANSIENT_FAILURE_RETRY) {
var server = this.routingData ? this.routingData.server : this.server;
var port = this.routingData ? this.routingData.port : this.config.options.port;
this.debug.log('Retry after transient failure connecting to ' + server + ':' + port);
this.dispatchEvent('retry');
} else {
this.transitionTo(this.STATE.FINAL);
}
}
}, {
key: 'sendPreLogin',
value: function sendPreLogin() {
var payload = new PreloginPayload({
encrypt: this.config.options.encrypt
});
this.messageIo.sendMessage(TYPE.PRELOGIN, payload.data);
this.debug.payload(function () {
return payload.toString(' ');
});
}
}, {
key: 'emptyMessageBuffer',
value: function emptyMessageBuffer() {
this.messageBuffer = new Buffer(0);
}
}, {
key: 'addToMessageBuffer',
value: function addToMessageBuffer(data) {
this.messageBuffer = Buffer.concat([this.messageBuffer, data]);
}
}, {
key: 'processPreLoginResponse',
value: function processPreLoginResponse() {
var preloginPayload = new PreloginPayload(this.messageBuffer);
this.debug.payload(function () {
return preloginPayload.toString(' ');
});
if (preloginPayload.encryptionString === 'ON' || preloginPayload.encryptionString === 'REQ') {
if (!this.config.options.encrypt) {
this.emit('connect', ConnectionError("Server requires encryption, set 'encrypt' config option to true.", 'EENCRYPT'));
return this.close();
}
this.dispatchEvent('tls');
} else {
this.dispatchEvent('noTls');
}
}
}, {
key: 'sendLogin7Packet',
value: function sendLogin7Packet(cb) {
var sendPayload = function sendPayload(clientResponse) {
var payload = new Login7Payload({
domain: this.config.domain,
userName: this.config.userName,
password: this.config.password,
database: this.config.options.database,
serverName: this.routingData ? this.routingData.server : this.config.server,
appName: this.config.options.appName,
packetSize: this.config.options.packetSize,
tdsVersion: this.config.options.tdsVersion,
initDbFatal: !this.config.options.fallbackToDefaultDb,
readOnlyIntent: this.config.options.readOnlyIntent,
sspiBlob: clientResponse,
language: this.config.options.language
});
this.routingData = undefined;
this.messageIo.sendMessage(TYPE.LOGIN7, payload.data);
this.debug.payload(function () {
return payload.toString(' ');
});
};
sendPayload.call(this);
process.nextTick(cb);
}
}, {
key: 'sendNTLMResponsePacket',
value: function sendNTLMResponsePacket() {
var _this9 = this;
var payload = new NTLMResponsePayload({
domain: this.config.domain,
userName: this.config.userName,
password: this.config.password,
database: this.config.options.database,
appName: this.config.options.appName,
packetSize: this.config.options.packetSize,
tdsVersion: this.config.options.tdsVersion,
ntlmpacket: this.ntlmpacket,
additional: this.additional
});
this.messageIo.sendMessage(TYPE.NTLMAUTH_PKT, payload.data);
this.debug.payload(function () {
return payload.toString(' ');
});
process.nextTick(function () {
_this9.transitionTo(_this9.STATE.SENT_NTLM_RESPONSE);
});
}
// Returns false to apply backpressure.
}, {
key: 'sendDataToTokenStreamParser',
value: function sendDataToTokenStreamParser(data) {
return this.tokenStreamParser.addBuffer(data);
}
// This is an internal method that is called from Request.pause().
// It has to check whether the passed Request object represents the currently
// active request, because the application might have called Request.pause()
// on an old inactive Request object.
}, {
key: 'pauseRequest',
value: function pauseRequest(request) {
if (this.isRequestActive(request)) {
this.tokenStreamParser.pause();
}
}
// This is an internal method that is called from Request.resume().
}, {
key: 'resumeRequest',
value: function resumeRequest(request) {
if (this.isRequestActive(request)) {
this.tokenStreamParser.resume();
}
}
// Returns true if the passed request is the currently active request of the connection.
}, {
key: 'isRequestActive',
value: function isRequestActive(request) {
return request === this.request && this.state === this.STATE.SENT_CLIENT_REQUEST;
}
}, {
key: 'sendInitialSql',
value: function sendInitialSql() {
var payload = new SqlBatchPayload(this.getInitialSql(), this.currentTransactionDescriptor(), this.config.options);
return this.messageIo.sendMessage(TYPE.SQL_BATCH, payload.data);
}
}, {
key: 'getInitialSql',
value: function getInitialSql() {
var options = [];
if (this.config.options.enableAnsiNull) {
options.push('set ansi_nulls on');
} else {
options.push('set ansi_nulls off');
}
if (this.config.options.enableAnsiNullDefault) {
options.push('set ansi_null_dflt_on on');
} else {
options.push('set ansi_null_dflt_on off');
}
if (this.config.options.enableAnsiPadding) {
options.push('set ansi_padding on');
} else {
options.push('set ansi_padding off');
}
if (this.config.options.enableAnsiWarnings) {
options.push('set ansi_warnings on');
} else {
options.push('set ansi_warnings off');
}
if (this.config.options.enableArithAbort) {
options.push('set arithabort on');
} else {
options.push('set arithabort off');
}
if (this.config.options.enableConcatNullYieldsNull) {
options.push('set concat_null_yields_null on');
} else {
options.push('set concat_null_yields_null off');
}
if (this.config.options.enableCursorCloseOnCommit !== undefined) {
if (this.config.options.enableCursorCloseOnCommit) {
options.push('set cursor_close_on_commit on');
} else {
options.push('set cursor_close_on_commit off');
}
}
options.push(`set datefirst ${this.config.options.datefirst}`);
options.push(`set dateformat ${this.config.options.dateFormat}`);
if (this.config.options.enableImplicitTransactions) {
options.push('set implicit_transactions on');
} else {
options.push('set implicit_transactions off');
}
options.push(`set language ${this.config.options.language}`);
if (this.config.options.enableNumericRoundabort) {
options.push('set numeric_roundabort on');
} else {
options.push('set numeric_roundabort off');
}
if (this.config.options.enableQuotedIdentifier) {
options.push('set quoted_identifier on');
} else {
options.push('set quoted_identifier off');
}
options.push(`set textsize ${this.config.options.textsize}`);
options.push(`set transaction isolation level ${this.getIsolationLevelText(this.config.options.connectionIsolationLevel)}`);
if (this.config.options.abortTransactionOnError) {
options.push('set xact_abort on');
} else {
options.push('set xact_abort off');
}
return options.join('\n');
}
}, {
key: 'processedInitialSql',
value: function processedInitialSql() {
this.clearConnectTimer();
this.emit('connect');
}
}, {
key: 'processLogin7Response',
value: function processLogin7Response() {
if (this.loggedIn) {
this.dispatchEvent('loggedIn');
} else {
if (this.loginError) {
this.emit('connec