tedious
Version:
A TDS driver, for connecting to MS SQLServer databases.
1,167 lines (1,078 loc) • 41.6 kB
JavaScript
// 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;