UNPKG

tedious

Version:

A TDS driver, for connecting to MS SQLServer databases.

1,167 lines (1,078 loc) 41.6 kB
// Generated by CoffeeScript 1.7.1 var BulkLoad, Connection, ConnectionError, DEFAULT_CANCEL_TIMEOUT, DEFAULT_CLIENT_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, DEFAULT_PACKET_SIZE, DEFAULT_PORT, DEFAULT_TDS_VERSION, DEFAULT_TEXTSIZE, Debug, EventEmitter, ISOLATION_LEVEL, KEEP_ALIVE_INITIAL_DELAY, Login7Payload, MessageIO, NTLMResponsePayload, PreloginPayload, Request, RequestError, RpcRequestPayload, Socket, SqlBatchPayload, TYPE, TokenStreamParser, Transaction, crypto, instanceLookup, tls, _ref, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, __slice = [].slice; require('./buffertools'); BulkLoad = require('./bulk-load'); Debug = require('./debug'); EventEmitter = require('events').EventEmitter; instanceLookup = require('./instance-lookup').instanceLookup; TYPE = require('./packet').TYPE; PreloginPayload = require('./prelogin-payload'); Login7Payload = require('./login7-payload'); NTLMResponsePayload = require('./ntlm-payload'); Request = require('./request'); RpcRequestPayload = require('./rpcrequest-payload'); SqlBatchPayload = require('./sqlbatch-payload'); MessageIO = require('./message-io'); Socket = require('net').Socket; TokenStreamParser = require('./token/token-stream-parser').Parser; Transaction = require('./transaction').Transaction; ISOLATION_LEVEL = require('./transaction').ISOLATION_LEVEL; crypto = require('crypto'); tls = require('tls'); _ref = require('./errors'), ConnectionError = _ref.ConnectionError, RequestError = _ref.RequestError; KEEP_ALIVE_INITIAL_DELAY = 30 * 1000; DEFAULT_CONNECT_TIMEOUT = 15 * 1000; DEFAULT_CLIENT_REQUEST_TIMEOUT = 15 * 1000; DEFAULT_CANCEL_TIMEOUT = 5 * 1000; DEFAULT_PACKET_SIZE = 4 * 1024; DEFAULT_TEXTSIZE = '2147483647'; DEFAULT_PORT = 1433; DEFAULT_TDS_VERSION = '7_4'; Connection = (function(_super) { __extends(Connection, _super); Connection.prototype.STATE = { CONNECTING: { name: 'Connecting', enter: function() { return this.initialiseConnection(); }, events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, socketConnect: function() { this.sendPreLogin(); return this.transitionTo(this.STATE.SENT_PRELOGIN); } } }, SENT_PRELOGIN: { name: 'SentPrelogin', enter: function() { return this.emptyMessageBuffer(); }, events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.addToMessageBuffer(data); }, message: function() { return this.processPreLoginResponse(); }, noTls: function() { this.sendLogin7Packet(); if (this.config.domain) { return this.transitionTo(this.STATE.SENT_LOGIN7_WITH_NTLM); } else { return this.transitionTo(this.STATE.SENT_LOGIN7_WITH_STANDARD_LOGIN); } }, tls: function() { this.initiateTlsSslHandshake(); this.sendLogin7Packet(); return this.transitionTo(this.STATE.SENT_TLSSSLNEGOTIATION); } } }, REROUTING: { name: 'ReRouting', enter: function() { return this.cleanupConnection(true); }, events: { message: function() {}, socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, reconnect: function() { this.config.server = this.routingData.server; this.config.options.port = this.routingData.port; return this.transitionTo(this.STATE.CONNECTING); } } }, SENT_TLSSSLNEGOTIATION: { name: 'SentTLSSSLNegotiation', enter: function() { return this.tlsNegotiationComplete = false; }, events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.securePair.encrypted.write(data); }, tlsNegotiated: function() { return this.tlsNegotiationComplete = true; }, message: function() { if (this.tlsNegotiationComplete) { return this.transitionTo(this.STATE.SENT_LOGIN7_WITH_STANDARD_LOGIN); } else { } } } }, SENT_LOGIN7_WITH_STANDARD_LOGIN: { name: 'SentLogin7WithStandardLogin', events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, loggedIn: function() { return this.transitionTo(this.STATE.LOGGED_IN_SENDING_INITIAL_SQL); }, routingChange: function() { return this.transitionTo(this.STATE.REROUTING); }, loginFailed: function() { return this.transitionTo(this.STATE.FINAL); }, message: function() { return this.processLogin7Response(); } } }, SENT_LOGIN7_WITH_NTLM: { name: 'SentLogin7WithNTLMLogin', events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, receivedChallenge: function() { this.sendNTLMResponsePacket(); return this.transitionTo(this.STATE.SENT_NTLM_RESPONSE); }, loginFailed: function() { return this.transitionTo(this.STATE.FINAL); }, message: function() { return this.processLogin7NTLMResponse(); } } }, SENT_NTLM_RESPONSE: { name: 'SentNTLMResponse', events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, loggedIn: function() { return this.transitionTo(this.STATE.LOGGED_IN_SENDING_INITIAL_SQL); }, loginFailed: function() { return this.transitionTo(this.STATE.FINAL); }, routingChange: function() { return this.transitionTo(this.STATE.REROUTING); }, message: function() { return this.processLogin7NTLMAck(); } } }, LOGGED_IN_SENDING_INITIAL_SQL: { name: 'LoggedInSendingInitialSql', enter: function() { return this.sendInitialSql(); }, events: { connectTimeout: function() { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, message: function(error) { this.transitionTo(this.STATE.LOGGED_IN); return this.processedInitialSql(); } } }, LOGGED_IN: { name: 'LoggedIn', events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); } } }, SENT_CLIENT_REQUEST: { name: 'SentClientRequest', events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, message: function() { var sqlRequest; this.clearRequestTimer(); this.transitionTo(this.STATE.LOGGED_IN); sqlRequest = this.request; this.request = void 0; return sqlRequest.callback(sqlRequest.error, sqlRequest.rowCount, sqlRequest.rows); } } }, SENT_ATTENTION: { name: 'SentAttention', enter: function() { return this.attentionReceived = false; }, events: { socketError: function(error) { return this.transitionTo(this.STATE.FINAL); }, data: function(data) { return this.sendDataToTokenStreamParser(data); }, attention: function() { return this.attentionReceived = true; }, message: function() { var message, sqlRequest; if (this.attentionReceived) { sqlRequest = this.request; this.request = void 0; this.transitionTo(this.STATE.LOGGED_IN); if (sqlRequest.canceled) { return sqlRequest.callback(RequestError("Canceled.", 'ECANCEL')); } else { message = "Timeout: Request failed to complete in " + this.config.options.requestTimeout + "ms"; return sqlRequest.callback(RequestError(message, 'ETIMEOUT')); } } } } }, FINAL: { name: 'Final', enter: function() { return this.cleanupConnection(); }, events: { loginFailed: function() {}, connectTimeout: function() {}, message: function() {}, socketError: function() {} } } }; function Connection(config) { this.config = config; this.reset = __bind(this.reset, this); this.socketClose = __bind(this.socketClose, this); this.socketEnd = __bind(this.socketEnd, this); this.socketConnect = __bind(this.socketConnect, this); this.socketError = __bind(this.socketError, this); this.requestTimeout = __bind(this.requestTimeout, this); this.connectTimeout = __bind(this.connectTimeout, this); this.defaultConfig(); this.createDebug(); this.createTokenStreamParser(); this.inTransaction = false; this.transactionDescriptors = [new Buffer([0, 0, 0, 0, 0, 0, 0, 0])]; this.transitionTo(this.STATE.CONNECTING); } Connection.prototype.close = function() { return this.transitionTo(this.STATE.FINAL); }; Connection.prototype.initialiseConnection = function() { this.connect(); return this.createConnectTimer(); }; Connection.prototype.cleanupConnection = function(redirect) { this.redirect = redirect; if (!this.closed) { this.clearConnectTimer(); this.clearRequestTimer(); this.closeConnection(); if (!this.redirect) { this.emit('end'); } else { this.emit('rerouting'); } this.closed = true; this.loggedIn = false; return this.loginError = null; } }; Connection.prototype.defaultConfig = function() { var _base, _base1, _base10, _base11, _base12, _base13, _base2, _base3, _base4, _base5, _base6, _base7, _base8, _base9; (_base = this.config).options || (_base.options = {}); (_base1 = this.config.options).textsize || (_base1.textsize = DEFAULT_TEXTSIZE); (_base2 = this.config.options).connectTimeout || (_base2.connectTimeout = DEFAULT_CONNECT_TIMEOUT); if ((_base3 = this.config.options).requestTimeout == null) { _base3.requestTimeout = DEFAULT_CLIENT_REQUEST_TIMEOUT; } if ((_base4 = this.config.options).cancelTimeout == null) { _base4.cancelTimeout = DEFAULT_CANCEL_TIMEOUT; } (_base5 = this.config.options).packetSize || (_base5.packetSize = DEFAULT_PACKET_SIZE); (_base6 = this.config.options).tdsVersion || (_base6.tdsVersion = DEFAULT_TDS_VERSION); (_base7 = this.config.options).isolationLevel || (_base7.isolationLevel = ISOLATION_LEVEL.READ_COMMITTED); (_base8 = this.config.options).encrypt || (_base8.encrypt = false); (_base9 = this.config.options).cryptoCredentialsDetails || (_base9.cryptoCredentialsDetails = {}); if ((_base10 = this.config.options).useUTC == null) { _base10.useUTC = true; } if ((_base11 = this.config.options).useColumnNames == null) { _base11.useColumnNames = false; } (_base12 = this.config.options).connectionIsolationLevel || (_base12.connectionIsolationLevel = ISOLATION_LEVEL.READ_COMMITTED); if ((_base13 = this.config.options).readOnlyIntent == null) { _base13.readOnlyIntent = false; } if (!this.config.options.port && !this.config.options.instanceName) { this.config.options.port = DEFAULT_PORT; } else if (this.config.options.port && this.config.options.instanceName) { throw new Error("Port and instanceName are mutually exclusive, but " + this.config.options.port + " and " + this.config.options.instanceName + " provided"); } else if (this.config.options.port) { if (this.config.options.port < 0 || this.config.options.port > 65536) { throw new RangeError("Port should be > 0 and < 65536"); } } if (this.config.options.columnNameReplacer && typeof this.config.options.columnNameReplacer !== 'function') { throw new TypeError('options.columnNameReplacer must be a function or null.'); } }; Connection.prototype.createDebug = function() { this.debug = new Debug(this.config.options.debug); return this.debug.on('debug', (function(_this) { return function(message) { return _this.emit('debug', message); }; })(this)); }; Connection.prototype.createTokenStreamParser = function() { this.tokenStreamParser = new TokenStreamParser(this.debug, void 0, this.config.options); this.tokenStreamParser.on('infoMessage', (function(_this) { return function(token) { return _this.emit('infoMessage', token); }; })(this)); this.tokenStreamParser.on('sspichallenge', (function(_this) { return function(token) { if (token.ntlmpacket) { _this.ntlmpacket = token.ntlmpacket; } return _this.emit('sspichallenge', token); }; })(this)); this.tokenStreamParser.on('errorMessage', (function(_this) { return function(token) { _this.emit('errorMessage', token); if (_this.loggedIn) { if (_this.request) { return _this.request.error = RequestError(token.message, 'EREQUEST'); } } else { return _this.loginError = ConnectionError(token.message, 'ELOGIN'); } }; })(this)); this.tokenStreamParser.on('databaseChange', (function(_this) { return function(token) { return _this.emit('databaseChange', token.newValue); }; })(this)); this.tokenStreamParser.on('languageChange', (function(_this) { return function(token) { return _this.emit('languageChange', token.newValue); }; })(this)); this.tokenStreamParser.on('charsetChange', (function(_this) { return function(token) { return _this.emit('charsetChange', token.newValue); }; })(this)); this.tokenStreamParser.on('loginack', (function(_this) { return function(token) { if (!token.tdsVersion) { _this.loginError = ConnectionError("Server responded with unknown TDS version.", 'ETDS'); _this.loggedIn = false; return; } if (!token["interface"]) { _this.loginError = ConnectionError("Server responded with unsupported interface.", 'EINTERFACENOTSUPP'); _this.loggedIn = false; return; } _this.config.options.tdsVersion = token.tdsVersion; return _this.loggedIn = true; }; })(this)); this.tokenStreamParser.on('routingChange', (function(_this) { return function(token) { _this.routingData = token.newValue; return _this.dispatchEvent('routingChange'); }; })(this)); this.tokenStreamParser.on('packetSizeChange', (function(_this) { return function(token) { return _this.messageIo.packetSize(token.newValue); }; })(this)); this.tokenStreamParser.on('beginTransaction', (function(_this) { return function(token) { _this.transactionDescriptors.push(token.newValue); return _this.inTransaction = true; }; })(this)); this.tokenStreamParser.on('commitTransaction', (function(_this) { return function(token) { _this.transactionDescriptors.length = 1; return _this.inTransaction = false; }; })(this)); this.tokenStreamParser.on('rollbackTransaction', (function(_this) { return function(token) { _this.transactionDescriptors.length = 1; return _this.inTransaction = false; }; })(this)); this.tokenStreamParser.on('columnMetadata', (function(_this) { return function(token) { var col, columns, _i, _len, _ref1; if (_this.request) { if (_this.config.options.useColumnNames) { columns = {}; _ref1 = token.columns; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { col = _ref1[_i]; if (columns[col.colName] == null) { columns[col.colName] = col; } } } else { columns = token.columns; } return _this.request.emit('columnMetadata', columns); } else { _this.emit('error', new Error("Received 'columnMetadata' when no sqlRequest is in progress")); return _this.close(); } }; })(this)); this.tokenStreamParser.on('order', (function(_this) { return function(token) { if (_this.request) { return _this.request.emit('order', token.orderColumns); } else { _this.emit('error', new Error("Received 'order' when no sqlRequest is in progress")); return _this.close(); } }; })(this)); this.tokenStreamParser.on('row', (function(_this) { return function(token) { if (_this.request) { if (_this.config.options.rowCollectionOnRequestCompletion) { _this.request.rows.push(token.columns); } if (_this.config.options.rowCollectionOnDone) { _this.request.rst.push(token.columns); } return _this.request.emit('row', token.columns); } else { _this.emit('error', new Error("Received 'row' when no sqlRequest is in progress")); return _this.close(); } }; })(this)); this.tokenStreamParser.on('returnStatus', (function(_this) { return function(token) { if (_this.request) { return _this.procReturnStatusValue = token.value; } }; })(this)); this.tokenStreamParser.on('returnValue', (function(_this) { return function(token) { if (_this.request) { return _this.request.emit('returnValue', token.paramName, token.value, token.metadata); } }; })(this)); this.tokenStreamParser.on('doneProc', (function(_this) { return function(token) { if (_this.request) { _this.request.emit('doneProc', token.rowCount, token.more, _this.procReturnStatusValue, _this.request.rst); _this.procReturnStatusValue = void 0; if (token.rowCount !== void 0) { _this.request.rowCount += token.rowCount; } if (_this.config.options.rowCollectionOnDone) { return _this.request.rst = []; } } }; })(this)); this.tokenStreamParser.on('doneInProc', (function(_this) { return function(token) { if (_this.request) { _this.request.emit('doneInProc', token.rowCount, token.more, _this.request.rst); if (token.rowCount !== void 0) { _this.request.rowCount += token.rowCount; } if (_this.config.options.rowCollectionOnDone) { return _this.request.rst = []; } } }; })(this)); this.tokenStreamParser.on('done', (function(_this) { return function(token) { if (_this.request) { if (token.attention) { _this.dispatchEvent("attention"); } if (token.sqlError && !_this.request.error) { _this.request.error = RequestError('An unknown error has occurred.', 'UNKNOWN'); } _this.request.emit('done', token.rowCount, token.more, _this.request.rst); if (token.rowCount !== void 0) { _this.request.rowCount += token.rowCount; } if (_this.config.options.rowCollectionOnDone) { return _this.request.rst = []; } } }; })(this)); this.tokenStreamParser.on('resetConnection', (function(_this) { return function(token) { return _this.emit('resetConnection'); }; })(this)); return this.tokenStreamParser.on('tokenStreamError', (function(_this) { return function(error) { _this.emit('error', error); return _this.close(); }; })(this)); }; Connection.prototype.connect = function() { if (this.config.options.port) { return this.connectOnPort(this.config.options.port); } else { return instanceLookup(this.config.server, this.config.options.instanceName, (function(_this) { return function(message, port) { if (_this.state === _this.STATE.FINAL) { return; } if (message) { return _this.emit('connect', ConnectionError(message, 'EINSTLOOKUP')); } else { return _this.connectOnPort(port); } }; })(this), this.config.options.connectTimeout); } }; Connection.prototype.connectOnPort = function(port) { var connectOpts; this.socket = new Socket({}); connectOpts = { host: this.config.server, port: port }; if (this.config.options.localAddress) { connectOpts.localAddress = this.config.options.localAddress; } this.socket.connect(connectOpts); this.socket.on('error', this.socketError); this.socket.on('connect', this.socketConnect); this.socket.on('close', this.socketClose); this.socket.on('end', this.socketEnd); this.messageIo = new MessageIO(this.socket, this.config.options.packetSize, this.debug); this.messageIo.on('data', (function(_this) { return function(data) { return _this.dispatchEvent('data', data); }; })(this)); return this.messageIo.on('message', (function(_this) { return function() { return _this.dispatchEvent('message'); }; })(this)); }; Connection.prototype.closeConnection = function() { var _ref1; return (_ref1 = this.socket) != null ? _ref1.destroy() : void 0; }; Connection.prototype.createConnectTimer = function() { return this.connectTimer = setTimeout(this.connectTimeout, this.config.options.connectTimeout); }; Connection.prototype.createRequestTimer = function() { if (this.config.options.requestTimeout) { return this.requestTimer = setTimeout(this.requestTimeout, this.config.options.requestTimeout); } }; Connection.prototype.connectTimeout = function() { var message; message = "Failed to connect to " + this.config.server + ":" + this.config.options.port + " in " + this.config.options.connectTimeout + "ms"; this.debug.log(message); this.emit('connect', ConnectionError(message, 'ETIMEOUT')); this.connectTimer = void 0; return this.dispatchEvent('connectTimeout'); }; Connection.prototype.requestTimeout = function() { this.requestTimer = void 0; this.messageIo.sendMessage(TYPE.ATTENTION); return this.transitionTo(this.STATE.SENT_ATTENTION); }; Connection.prototype.clearConnectTimer = function() { if (this.connectTimer) { return clearTimeout(this.connectTimer); } }; Connection.prototype.clearRequestTimer = function() { if (this.requestTimer) { return clearTimeout(this.requestTimer); } }; Connection.prototype.transitionTo = function(newState) { var _ref1, _ref2; if (this.state === newState) { this.debug.log("State is already " + newState.name); return; } if ((_ref1 = this.state) != null ? _ref1.exit : void 0) { this.state.exit.apply(this); } this.debug.log("State change: " + ((_ref2 = this.state) != null ? _ref2.name : void 0) + " -> " + newState.name); this.state = newState; if (this.state.enter) { return this.state.enter.apply(this); } }; Connection.prototype.dispatchEvent = function() { var args, eventFunction, eventName, _ref1; eventName = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; if ((_ref1 = this.state) != null ? _ref1.events[eventName] : void 0) { return eventFunction = this.state.events[eventName].apply(this, args); } else { this.emit('error', new Error("No event '" + eventName + "' in state '" + this.state.name + "'")); return this.close(); } }; Connection.prototype.socketError = function(error) { var message; message = "Failed to connect to " + this.config.server + ":" + this.config.options.port + " - " + error.message; this.debug.log(message); if (this.state === this.STATE.CONNECTING) { this.emit('connect', ConnectionError(message, 'ESOCKET')); } else { this.emit('error', ConnectionError(message)); } return this.dispatchEvent('socketError', error); }; Connection.prototype.socketConnect = function() { this.socket.setKeepAlive(true, KEEP_ALIVE_INITIAL_DELAY); this.closed = false; this.debug.log("connected to " + this.config.server + ":" + this.config.options.port); return this.dispatchEvent('socketConnect'); }; Connection.prototype.socketEnd = function() { this.debug.log("socket ended"); return this.transitionTo(this.STATE.FINAL); }; Connection.prototype.socketClose = function() { 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); return this.dispatchEvent('reconnect'); } else { return this.transitionTo(this.STATE.FINAL); } }; Connection.prototype.sendPreLogin = function() { var payload; payload = new PreloginPayload({ encrypt: this.config.options.encrypt }); this.messageIo.sendMessage(TYPE.PRELOGIN, payload.data); return this.debug.payload(function() { return payload.toString(' '); }); }; Connection.prototype.emptyMessageBuffer = function() { return this.messageBuffer = new Buffer(0); }; Connection.prototype.addToMessageBuffer = function(data) { return this.messageBuffer = Buffer.concat([this.messageBuffer, data]); }; Connection.prototype.processPreLoginResponse = function() { var preloginPayload; preloginPayload = new PreloginPayload(this.messageBuffer); this.debug.payload(function() { return preloginPayload.toString(' '); }); if (preloginPayload.encryptionString === 'ON') { return this.dispatchEvent('tls'); } else { return this.dispatchEvent('noTls'); } }; Connection.prototype.sendLogin7Packet = function() { var loginData, payload; loginData = { domain: this.config.domain, userName: this.config.userName, password: this.config.password, database: this.config.options.database, serverName: 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 }; payload = new Login7Payload(loginData); this.messageIo.sendMessage(TYPE.LOGIN7, payload.data); return this.debug.payload(function() { return payload.toString(' '); }); }; Connection.prototype.sendNTLMResponsePacket = function() { var payload, responseData; responseData = { 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 }; payload = new NTLMResponsePayload(responseData); this.messageIo.sendMessage(TYPE.NTLMAUTH_PKT, payload.data); return this.debug.payload(function() { return payload.toString(' '); }); }; Connection.prototype.initiateTlsSslHandshake = function() { var credentials; credentials = crypto.createCredentials(this.config.options.cryptoCredentialsDetails); this.securePair = tls.createSecurePair(credentials); this.securePair.on('secure', (function(_this) { return function() { var cipher; cipher = _this.securePair.cleartext.getCipher(); _this.debug.log("TLS negotiated (" + cipher.name + ", " + cipher.version + ")"); _this.emit('secure', _this.securePair.cleartext); _this.messageIo.encryptAllFutureTraffic(); return _this.dispatchEvent('tlsNegotiated'); }; })(this)); this.securePair.encrypted.on('data', (function(_this) { return function(data) { return _this.messageIo.sendMessage(TYPE.PRELOGIN, data); }; })(this)); return this.messageIo.tlsNegotiationStarting(this.securePair); }; Connection.prototype.sendDataToTokenStreamParser = function(data) { return this.tokenStreamParser.addBuffer(data); }; Connection.prototype.sendInitialSql = function() { var payload; payload = new SqlBatchPayload(this.getInitialSql(), this.currentTransactionDescriptor(), this.config.options); return this.messageIo.sendMessage(TYPE.SQL_BATCH, payload.data); }; Connection.prototype.getInitialSql = function() { var xact_abort; xact_abort = this.config.options.abortTransactionOnError ? 'on' : 'off'; return "set textsize " + this.config.options.textsize + "\nset quoted_identifier on\nset arithabort off\nset numeric_roundabort off\nset ansi_warnings on\nset ansi_padding on\nset ansi_nulls on\nset concat_null_yields_null on\nset cursor_close_on_commit off\nset implicit_transactions off\nset language us_english\nset dateformat mdy\nset datefirst 7\nset transaction isolation level " + (this.getIsolationLevelText(this.config.options.connectionIsolationLevel)) + "\nset xact_abort " + xact_abort; }; Connection.prototype.processedInitialSql = function() { this.clearConnectTimer(); return this.emit('connect'); }; Connection.prototype.processLogin7Response = function() { if (this.loggedIn) { return this.dispatchEvent('loggedIn'); } else { if (this.loginError) { this.emit('connect', this.loginError); } else { this.emit('connect', ConnectionError('Login failed.', 'ELOGIN')); } return this.dispatchEvent('loginFailed'); } }; Connection.prototype.processLogin7NTLMResponse = function() { if (this.ntlmpacket) { return this.dispatchEvent('receivedChallenge'); } else { if (this.loginError) { this.emit('connect', this.loginError); } else { this.emit('connect', ConnectionError('Login failed.', 'ELOGIN')); } return this.dispatchEvent('loginFailed'); } }; Connection.prototype.processLogin7NTLMAck = function() { if (this.loggedIn) { return this.dispatchEvent('loggedIn'); } else { if (this.loginError) { this.emit('connect', this.loginError); } else { this.emit('connect', ConnectionError('Login failed.', 'ELOGIN')); } return this.dispatchEvent('loginFailed'); } }; Connection.prototype.execSqlBatch = function(request) { return this.makeRequest(request, TYPE.SQL_BATCH, new SqlBatchPayload(request.sqlTextOrProcedure, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.execSql = function(request) { request.transformIntoExecuteSqlRpc(); return this.makeRequest(request, TYPE.RPC_REQUEST, new RpcRequestPayload(request, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.newBulkLoad = function(table, callback) { return new BulkLoad(table, this.config.options, callback); }; Connection.prototype.execBulkLoad = function(bulkLoad) { var request; request = new Request(bulkLoad.getBulkInsertSql(), (function(_this) { return function(error) { if (error) { if (error.code === 'UNKNOWN') { error.message += ' This is likely because the schema of the BulkLoad does not match the schema of the table you are attempting to insert into.'; } bulkLoad.error = error; return bulkLoad.callback(error); } else { return _this.makeRequest(bulkLoad, TYPE.BULK_LOAD, bulkLoad.getPayload()); } }; })(this)); return this.execSqlBatch(request); }; Connection.prototype.prepare = function(request) { request.transformIntoPrepareRpc(); return this.makeRequest(request, TYPE.RPC_REQUEST, new RpcRequestPayload(request, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.unprepare = function(request) { request.transformIntoUnprepareRpc(); return this.makeRequest(request, TYPE.RPC_REQUEST, new RpcRequestPayload(request, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.execute = function(request, parameters) { request.transformIntoExecuteRpc(parameters); return this.makeRequest(request, TYPE.RPC_REQUEST, new RpcRequestPayload(request, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.callProcedure = function(request) { return this.makeRequest(request, TYPE.RPC_REQUEST, new RpcRequestPayload(request, this.currentTransactionDescriptor(), this.config.options)); }; Connection.prototype.beginTransaction = function(callback, name, isolationLevel) { var request, transaction; isolationLevel || (isolationLevel = this.config.options.isolationLevel); transaction = new Transaction(name || '', isolationLevel); if (this.config.options.tdsVersion < "7_2") { return this.execSqlBatch(new Request("SET TRANSACTION ISOLATION LEVEL " + (transaction.isolationLevelToTSQL()) + ";BEGIN TRAN " + transaction.name, callback)); } request = new Request(void 0, (function(_this) { return function(err) { return callback(err, _this.currentTransactionDescriptor()); }; })(this)); return this.makeRequest(request, TYPE.TRANSACTION_MANAGER, transaction.beginPayload(this.currentTransactionDescriptor())); }; Connection.prototype.commitTransaction = function(callback, name) { var request, transaction; transaction = new Transaction(name || ''); if (this.config.options.tdsVersion < "7_2") { return this.execSqlBatch(new Request("COMMIT TRAN " + transaction.name, callback)); } request = new Request(void 0, callback); return this.makeRequest(request, TYPE.TRANSACTION_MANAGER, transaction.commitPayload(this.currentTransactionDescriptor())); }; Connection.prototype.rollbackTransaction = function(callback, name) { var request, transaction; transaction = new Transaction(name || ''); if (this.config.options.tdsVersion < "7_2") { return this.execSqlBatch(new Request("ROLLBACK TRAN " + transaction.name, callback)); } request = new Request(void 0, callback); return this.makeRequest(request, TYPE.TRANSACTION_MANAGER, transaction.rollbackPayload(this.currentTransactionDescriptor())); }; Connection.prototype.saveTransaction = function(callback, name) { var request, transaction; transaction = new Transaction(name); if (this.config.options.tdsVersion < "7_2") { return this.execSqlBatch(new Request("SAVE TRAN " + transaction.name, callback)); } request = new Request(void 0, callback); return this.makeRequest(request, TYPE.TRANSACTION_MANAGER, transaction.savePayload(this.currentTransactionDescriptor())); }; Connection.prototype.transaction = function(cb, isolationLevel) { var name, txDone, useSavepoint; if (typeof cb !== 'function') { throw new TypeError('`cb` must be a function'); } useSavepoint = this.inTransaction; name = "_tedious_" + (crypto.randomBytes(10).toString('hex')); txDone = (function(_this) { return function(err, done) { var args, i, _i, _ref1; args = []; for (i = _i = 2, _ref1 = arguments.length; 2 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 2 <= _ref1 ? ++_i : --_i) { args.push(arguments[i]); } if (err) { if (_this.inTransaction) { return _this.rollbackTransaction(function(txErr) { args.unshift(txErr || err); return done.apply(null, args); }, name); } else { return process.nextTick(function() { args.unshift(err); return done.apply(null, args); }); } } else { if (useSavepoint) { return process.nextTick(function() { args.unshift(null); return done.apply(null, args); }); } else { return _this.commitTransaction(function(txErr) { args.unshift(txErr); return done.apply(null, args); }, name); } } }; })(this); if (useSavepoint) { return this.saveTransaction((function(_this) { return function(err) { if (err) { return cb(err); } if (isolationLevel) { return _this.execSqlBatch(new Request("SET transaction isolation level " + (_this.getIsolationLevelText(isolationLevel)), function(err) { return cb(err, txDone); })); } else { return cb(null, txDone); } }; })(this), name); } else { return this.beginTransaction(function(err) { if (err) { return cb(err); } return cb(null, txDone); }, name, isolationLevel); } }; Connection.prototype.makeRequest = function(request, packetType, payload) { var message; if (this.state !== this.STATE.LOGGED_IN) { message = "Requests can only be made in the " + this.STATE.LOGGED_IN.name + " state, not the " + this.state.name + " state"; this.debug.log(message); return request.callback(RequestError(message, 'EINVALIDSTATE')); } else { this.request = request; this.request.rowCount = 0; this.request.rows = []; this.request.rst = []; this.createRequestTimer(); this.messageIo.sendMessage(packetType, payload.data, this.resetConnectionOnNextRequest); this.resetConnectionOnNextRequest = false; this.debug.payload(function() { return payload.toString(' '); }); return this.transitionTo(this.STATE.SENT_CLIENT_REQUEST); } }; Connection.prototype.cancel = function() { var message; if (this.state !== this.STATE.SENT_CLIENT_REQUEST) { message = "Requests can only be canceled in the " + this.STATE.SENT_CLIENT_REQUEST.name + " state, not the " + this.state.name + " state"; this.debug.log(message); return false; } else { this.request.canceled = true; this.messageIo.sendMessage(TYPE.ATTENTION); this.transitionTo(this.STATE.SENT_ATTENTION); return true; } }; Connection.prototype.reset = function(callback) { var request; request = new Request(this.getInitialSql(), function(err, rowCount, rows) { return callback(err); }); this.resetConnectionOnNextRequest = true; return this.execSqlBatch(request); }; Connection.prototype.currentTransactionDescriptor = function() { return this.transactionDescriptors[this.transactionDescriptors.length - 1]; }; Connection.prototype.getIsolationLevelText = function(isolationLevel) { switch (isolationLevel) { case ISOLATION_LEVEL.READ_UNCOMMITTED: return 'read uncommitted'; case ISOLATION_LEVEL.REPEATABLE_READ: return 'repeatable read'; case ISOLATION_LEVEL.SERIALIZABLE: return 'serializable'; case ISOLATION_LEVEL.SNAPSHOT: return 'snapshot'; default: return 'read committed'; } }; return Connection; })(EventEmitter); module.exports = Connection;