UNPKG

mssql2

Version:

Microsoft SQL Server client for Node.js (fork)

1,654 lines (1,395 loc) 59.2 kB
// Generated by CoffeeScript 1.10.0 (function() { var Connection, ConnectionError, ConnectionString, DRIVERS, EventEmitter, ISOLATION_LEVEL, PreparedStatement, PreparedStatementError, Request, RequestError, TYPES, Table, Transaction, TransactionError, declare, fs, getTypeByValue, global_connection, key, map, ref, ref1, util, value, extend = 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; }, hasProp = {}.hasOwnProperty, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, slice = [].slice, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; EventEmitter = require('events').EventEmitter; util = require('util'); fs = require('fs'); ref = require('./datatypes'), TYPES = ref.TYPES, declare = ref.declare; ISOLATION_LEVEL = require('./isolationlevel'); DRIVERS = ['msnodesql', 'tedious', 'tds', 'msnodesqlv8']; Table = require('./table'); ConnectionString = require('./connectionstring'); global_connection = null; map = []; /* Register you own type map. **Example:** ``` sql.map.register(MyClass, sql.Text); ``` You can also overwrite default type map. ``` sql.map.register(Number, sql.BigInt); ``` @path module.exports.map @param {*} jstype JS data type. @param {*} sqltype SQL data type. */ map.register = function(jstype, sqltype) { var i, index, item, len; for (index = i = 0, len = this.length; i < len; index = ++i) { item = this[index]; if (!(item.js === jstype)) { continue; } this.splice(index, 1); break; } this.push({ js: jstype, sql: sqltype }); return null; }; map.register(String, TYPES.NVarChar); map.register(Number, TYPES.Int); map.register(Boolean, TYPES.Bit); map.register(Date, TYPES.DateTime); map.register(Buffer, TYPES.VarBinary); map.register(Table, TYPES.TVP); /* @ignore */ getTypeByValue = function(value) { var i, item, j, k, l, len, len1, len2, len3; if (value === null || value === void 0) { return TYPES.NVarChar; } switch (typeof value) { case 'string': for (i = 0, len = map.length; i < len; i++) { item = map[i]; if (item.js === String) { return item.sql; } } return TYPES.NVarChar; case 'number': for (j = 0, len1 = map.length; j < len1; j++) { item = map[j]; if (item.js === Number) { return item.sql; } } return TYPES.Int; case 'boolean': for (k = 0, len2 = map.length; k < len2; k++) { item = map[k]; if (item.js === Boolean) { return item.sql; } } return TYPES.Bit; case 'object': for (l = 0, len3 = map.length; l < len3; l++) { item = map[l]; if (value instanceof item.js) { return item.sql; } } return TYPES.NVarChar; default: return TYPES.NVarChar; } }; /* Class Connection. Internally, each `Connection` instance is a separate pool of TDS connections. Once you create a new `Request`/`Transaction`/`Prepared Statement`, a new TDS connection is acquired from the pool and reserved for desired action. Once the action is complete, connection is released back to the pool. @property {Boolean} connected If true, connection is established. @property {Boolean} connecting If true, connection is being established. @property {*} driver Reference to configured Driver. @event connect Dispatched after connection has established. @event close Dispatched after connection has closed a pool (by calling close). */ Connection = (function(superClass) { extend(Connection, superClass); Connection.prototype.connected = false; Connection.prototype.connecting = false; Connection.prototype.driver = null; /* Create new Connection. @param {Object|String} config Connection configuration object or connection string. @callback [callback] A callback which is called after connection has established, or an error has occurred. @param {Error} err Error on error, otherwise null. */ function Connection(config1, callback) { var base, base1, base2, base3, base4, err, error, ex, ref1; this.config = config1; if ('string' === typeof this.config) { try { this.config = ConnectionString.resolve(this.config); } catch (error) { ex = error; if (callback) { return callback(ex); } else { throw ex; } } } if ((base = this.config).driver == null) { base.driver = 'tedious'; } if ((base1 = this.config).port == null) { base1.port = 1433; } if ((base2 = this.config).options == null) { base2.options = {}; } if ((base3 = this.config).stream == null) { base3.stream = false; } if ((base4 = this.config).parseJSON == null) { base4.parseJSON = false; } if (/^(.*)\\(.*)$/.exec(this.config.server)) { this.config.server = RegExp.$1; this.config.options.instanceName = RegExp.$2; } if (ref1 = this.config.driver, indexOf.call(DRIVERS, ref1) >= 0) { this.driver = this.initializeDriver(require("./" + this.config.driver)); } else if (typeof this.config.driver === 'function') { this.driver = this.initializeDriver(this.config.driver); } else { err = new ConnectionError("Unknown driver " + this.config.driver + "!", 'EDRIVER'); if (callback) { return callback(err); } else { throw err; } } if (module.exports.fix) { this.driver.fix(); } if (callback) { this.connect(callback); } } /* Write message to debug stream. */ Connection.prototype._debug = function(msg) { var ref1; return (ref1 = this._debugStream) != null ? ref1.write((String(msg).replace(/\x1B\[[0-9;]*m/g, '')) + "\n") : void 0; }; /* Initializes driver for this connection. Separated from constructor and used by co-mssql. @private @param {Function} driver Loaded driver. @returns {Connection} */ Connection.prototype.initializeDriver = function(driver) { return driver(Connection, Transaction, Request, ConnectionError, TransactionError, RequestError); }; /* Creates a new connection pool with one active connection. This one initial connection serves as a probe to find out whether the configuration is valid. @callback [callback] A callback which is called after connection has established, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Connection|Promise} */ Connection.prototype.connect = function(callback) { if (callback != null) { return this._connect(callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._connect(function(err) { if (err) { return reject(err); } return resolve(_this); }); }; })(this)); }; Connection.prototype._connect = function(callback) { var go; if (!this.driver) { return callback(new ConnectionError("Connection was closed. Create a new instance.")); } if (this.connected) { return callback(new ConnectionError("Database is already connected! Call close before connecting to different database.", 'EALREADYCONNECTED')); } if (this.connecting) { return callback(new ConnectionError("Already connecting to database! Call close before connecting to different database.", 'EALREADYCONNECTING')); } go = (function(_this) { return function() { _this.connecting = true; return _this.driver.Connection.prototype.connect.call(_this, _this.config, function(err) { if (!_this.connecting) { return; } _this.connecting = false; if (err) { if (_this._debugStream) { _this._debugStream.removeAllListeners(); _this._debugStream.end(); _this._debugStream = null; } } else { _this.connected = true; _this.emit('connect'); } return callback(err); }); }; })(this); if (this.config.debug) { this._debugStream = fs.createWriteStream("./mssql_debug_" + (Date.now()) + ".log"); this._debugStream.once('open', go); this._debugStream.on('error', function(err) { if (this.connecting || this.connected) { return console.error(err.stack); } else { this._debugStream.removeListener('open', go); return callback(new ConnectionError("Failed to open debug stream. " + err.message, 'EDEBUG')); } }); } else { go(); } return this; }; /* Close all active connections in the pool. @callback [callback] A callback which is called after connection has closed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Connection|Promise} */ Connection.prototype.close = function(callback) { if (callback != null) { return this._close(callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._close(function(err) { if (err) { return reject(err); } return resolve(); }); }; })(this)); }; Connection.prototype._close = function(callback) { if (this._debugStream) { this._debugStream.removeAllListeners(); this._debugStream.end(); this._debugStream = null; } if (this.connecting) { this.connecting = false; this.driver.Connection.prototype.close.call(this, (function(_this) { return function(err) { return callback(err); }; })(this)); this.driver = null; } else if (this.connected) { this.connected = false; this.driver.Connection.prototype.close.call(this, (function(_this) { return function(err) { if (!err) { _this.connected = false; _this.emit('close'); } return callback(err); }; })(this)); this.driver = null; } else { callback(null); } return this; }; /* Returns new request using this connection. @returns {Request} */ Connection.prototype.request = function() { return new Request(this); }; /* Returns new transaction using this connection. @returns {Transaction} */ Connection.prototype.transaction = function() { return new Transaction(this); }; /* Creates a new query using this connection from a tagged template string. @param {Array} strings Array of string literals. @param {...*} keys Values. @returns {Request} */ Connection.prototype.query = function() { var strings, values; strings = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : []; return new Request(this)._template('query', strings, values); }; /* Creates a new batch using this connection from a tagged template string. @param {Array} strings Array of string literals. @param {...*} keys Values. @returns {Request} */ Connection.prototype.batch = function() { var strings, values; strings = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : []; return new Request(this)._template('batch', strings, values); }; return Connection; })(EventEmitter); /* Class PreparedStatement. IMPORTANT: Rememeber that each prepared statement means one reserved connection from the pool. Don't forget to unprepare a prepared statement! @property {Connection} connection Reference to used connection. @property {Boolean} multiple If `true`, `execute` will handle multiple recordsets. @property {String} statement Prepared SQL statement. @property {Request} lastRequest References instance of most recent Request created by executing a statement. */ PreparedStatement = (function(superClass) { extend(PreparedStatement, superClass); PreparedStatement.prototype._pooledConnection = null; PreparedStatement.prototype._queue = null; PreparedStatement.prototype._working = false; PreparedStatement.prototype._handle = 0; PreparedStatement.prototype.connection = null; PreparedStatement.prototype.transaction = null; PreparedStatement.prototype.prepared = false; PreparedStatement.prototype.statement = null; PreparedStatement.prototype.parameters = null; PreparedStatement.prototype.multiple = false; PreparedStatement.prototype.stream = null; PreparedStatement.prototype.lastRequest = null; /* Create new Prepared Statement. @param {String} statement SQL statement. @param {Connection} [connection] If ommited, global connection is used instead. */ function PreparedStatement(connection) { if (connection instanceof Transaction) { this.transaction = connection; this.connection = connection.connection; } else if (connection instanceof Connection) { this.connection = connection; } else { this.connection = global_connection; } this._queue = []; this.parameters = {}; } /* Add an input parameter to the prepared statement. **Example:** ``` statement.input('input_parameter', sql.Int); statement.input('input_parameter', sql.VarChar(50)); ``` @param {String} name Name of the input parameter without @ char. @param {*} type SQL data type of input parameter. @returns {PreparedStatement} */ PreparedStatement.prototype.input = function(name, type) { if (/(--| |\/\*|\*\/|')/.test(name)) { throw new PreparedStatementError("SQL injection warning for param '" + name + "'", 'EINJECT'); } if (arguments.length < 2) { throw new PreparedStatementError("Invalid number of arguments. 2 arguments expected.", 'EARGS'); } if (type instanceof Function) { type = type(); } this.parameters[name] = { name: name, type: type.type, io: 1, length: type.length, scale: type.scale, precision: type.precision, tvpType: type.tvpType }; return this; }; /* Add an output parameter to the prepared statement. **Example:** ``` statement.output('output_parameter', sql.Int); statement.output('output_parameter', sql.VarChar(50)); ``` @param {String} name Name of the output parameter without @ char. @param {*} type SQL data type of output parameter. @returns {PreparedStatement} */ PreparedStatement.prototype.output = function(name, type) { if (/(--| |\/\*|\*\/|')/.test(name)) { throw new PreparedStatementError("SQL injection warning for param '" + name + "'", 'EINJECT'); } if (arguments.length < 2) { throw new PreparedStatementError("Invalid number of arguments. 2 arguments expected.", 'EARGS'); } if (type instanceof Function) { type = type(); } this.parameters[name] = { name: name, type: type.type, io: 2, length: type.length, scale: type.scale, precision: type.precision }; return this; }; /* Prepare a statement. @property {String} [statement] SQL statement to prepare. @callback [callback] A callback which is called after preparation has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {PreparedStatement|Promise} */ PreparedStatement.prototype.prepare = function(statement, callback) { if (callback != null) { return this._prepare(statement, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._prepare(statement, function(err) { if (err) { return reject(err); } return resolve(_this); }); }; })(this)); }; PreparedStatement.prototype._prepare = function(statement, callback) { var done; if (this._pooledConnection) { callback(new PreparedStatementError("Statement is already prepared.", 'EALREADYPREPARED')); return this; } if (typeof statement === 'function') { callback = statement; statement = void 0; } if (statement != null) { this.statement = statement; } done = (function(_this) { return function(err, connection) { var name, param, req; if (err) { return callback(err); } _this._pooledConnection = connection; req = new Request(_this); req.stream = false; req.output('handle', TYPES.Int); req.input('params', TYPES.NVarChar, ((function() { var ref1, results; ref1 = this.parameters; results = []; for (name in ref1) { param = ref1[name]; results.push("@" + name + " " + (declare(param.type, param)) + (param.io === 2 ? " output" : "")); } return results; }).call(_this)).join(',')); req.input('stmt', TYPES.NVarChar, _this.statement); return req.execute('sp_prepare', function(err) { if (err) { if (_this.transaction) { _this.transaction.next(); } else { _this.connection.pool.release(_this._pooledConnection); _this._pooledConnection = null; } return callback(err); } _this._handle = req.parameters.handle.value; return callback(null); }); }; })(this); if (this.transaction) { if (!this.transaction._pooledConnection) { callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN')); return this; } this.transaction.queue(done); } else { this.connection.pool.acquire(done); } return this; }; /* Execute next request in queue. @private @returns {PreparedStatement} */ PreparedStatement.prototype.next = function() { if (this._queue.length) { process.nextTick((function(_this) { return function() { return _this._queue.shift()(null, _this._pooledConnection); }; })(this)); } else { this._working = false; } return this; }; /* Add request to queue for connection. If queue is empty, execute the request immediately. @private @callback callback A callback to call when connection in ready to execute request. @param {Error} err Error on error, otherwise null. @param {*} conn Internal driver's connection. @returns {PreparedStatement} */ PreparedStatement.prototype.queue = function(callback) { if (!this._pooledConnection) { callback(new PreparedStatementError("Statement is not prepared. Call prepare() first.", 'ENOTPREPARED')); return this; } if (this._working) { this._queue.push(callback); } else { this._working = true; callback(null, this._pooledConnection); } return this; }; /* Execute a prepared statement. @property {String} values An object whose names correspond to the names of parameters that were added to the prepared statement before it was prepared. @callback [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Request|Promise} */ PreparedStatement.prototype.execute = function(values, callback) { if (callback != null) { return this._execute(values, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._execute(values, function(err, recordset) { if (err) { return reject(err); } return resolve(recordset); }); }; })(this)); }; PreparedStatement.prototype._execute = function(values, callback) { var name, param, ref1, req; req = this.lastRequest = new Request(this); if (this.stream != null) { req.stream = this.stream; } req.input('handle', TYPES.Int, this._handle); ref1 = this.parameters; for (name in ref1) { param = ref1[name]; req.parameters[name] = { name: name, type: param.type, io: param.io, value: values[name], length: param.length, scale: param.scale, precision: param.precision }; } req.execute('sp_execute', (function(_this) { return function(err, recordsets, returnValue) { if (err) { return callback(err); } return callback(null, (_this.multiple ? recordsets : recordsets[0]), req.rowsAffected); }; })(this)); return req; }; /* Unprepare a prepared statement. @callback [callback] A callback which is called after unpreparation has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {PreparedStatement|Promise} */ PreparedStatement.prototype.unprepare = function(callback) { if (callback != null) { return this._unprepare(callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._unprepare(function(err) { if (err) { return reject(err); } return resolve(); }); }; })(this)); }; PreparedStatement.prototype._unprepare = function(callback) { var done, req; if (!this._pooledConnection) { callback(new PreparedStatementError("Statement is not prepared. Call prepare() first.", 'ENOTPREPARED')); return this; } done = (function(_this) { return function(err) { if (err) { return callback(err); } if (_this.transaction) { _this.transaction.next(); } else { _this.connection.pool.release(_this._pooledConnection); _this._pooledConnection = null; } _this._handle = 0; return callback(null); }; })(this); req = new Request(this); req.stream = false; req.input('handle', TYPES.Int, this._handle); req.execute('sp_unprepare', done); return this; }; return PreparedStatement; })(EventEmitter); /* Class Transaction. @property {Connection} connection Reference to used connection. @property {Number} isolationLevel Controls the locking and row versioning behavior of TSQL statements issued by a connection. READ_COMMITTED by default. @property {String} name Transaction name. Empty string by default. @event begin Dispatched when transaction begin. @event commit Dispatched on successful commit. @event rollback Dispatched on successful rollback. */ Transaction = (function(superClass) { extend(Transaction, superClass); Transaction.prototype._pooledConnection = null; Transaction.prototype._queue = null; Transaction.prototype._aborted = false; Transaction.prototype._working = false; Transaction.prototype.name = ""; Transaction.prototype.connection = null; Transaction.prototype.isolationLevel = ISOLATION_LEVEL.READ_COMMITTED; /* Create new Transaction. @param {Connection} [connection] If ommited, global connection is used instead. */ function Transaction(connection) { this._abort = bind(this._abort, this); this.connection = connection != null ? connection : global_connection; this._queue = []; } /* @private */ Transaction.prototype._abort = function() { return this.connection.driver.Transaction.prototype._abort.call(this); }; /* Begin a transaction. @param {Number} [isolationLevel] Controls the locking and row versioning behavior of TSQL statements issued by a connection. @callback [callback] A callback which is called after transaction has began, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Transaction|Promise} */ Transaction.prototype.begin = function(isolationLevel, callback) { if (isolationLevel instanceof Function) { callback = isolationLevel; isolationLevel = void 0; } if (callback != null) { return this._begin(isolationLevel, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._begin(isolationLevel, function(err) { if (err) { return reject(err); } return resolve(_this); }); }; })(this)); }; Transaction.prototype._begin = function(isolationLevel, callback) { if (isolationLevel != null) { this.isolationLevel = isolationLevel; } if (this._pooledConnection) { callback(new TransactionError("Transaction has already begun.", 'EALREADYBEGUN')); return this; } this.connection.driver.Transaction.prototype.begin.call(this, (function(_this) { return function(err) { if (!err) { _this.emit('begin'); } return callback(err); }; })(this)); return this; }; /* Commit a transaction. @callback [callback] A callback which is called after transaction has commited, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Transaction|Promise} */ Transaction.prototype.commit = function(callback) { if (callback != null) { return this._commit(callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._commit(function(err) { if (err) { return reject(err); } return resolve(); }); }; })(this)); }; Transaction.prototype._commit = function(callback) { if (!this._pooledConnection) { callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN')); return this; } if (this._working) { callback(new TransactionError("Can't commit transaction. There is a request in progress.", 'EREQINPROG')); return this; } if (this._queue.length) { callback(new TransactionError("Can't commit transaction. There are request in queue.", 'EREQINPROG')); return this; } this.connection.driver.Transaction.prototype.commit.call(this, (function(_this) { return function(err) { if (!err) { _this.emit('commit'); } return callback(err); }; })(this)); return this; }; /* Execute next request in queue. @private @returns {Transaction} */ Transaction.prototype.next = function() { var toAbort; if (this._aborted) { toAbort = this._queue; this._queue = []; process.nextTick((function(_this) { return function() { var results; results = []; while (toAbort.length) { results.push(toAbort.shift()(new TransactionError("Transaction aborted.", "EABORT"))); } return results; }; })(this)); } this._working = false; if (this._queue.length) { process.nextTick((function(_this) { return function() { if (_this._aborted) { return _this.next(); } _this._working = true; return _this._queue.shift()(null, _this._pooledConnection); }; })(this)); } return this; }; /* Add request to queue for connection. If queue is empty, execute the request immediately. @private @callback callback A callback to call when connection in ready to execute request. @param {Error} err Error on error, otherwise null. @param {*} conn Internal driver's connection. @returns {Transaction} */ Transaction.prototype.queue = function(callback) { if (!this._pooledConnection) { callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN')); return this; } if (this._working || this._queue.length) { this._queue.push(callback); } else { this._working = true; callback(null, this._pooledConnection); } return this; }; /* Returns new request using this transaction. @returns {Request} */ Transaction.prototype.request = function() { return new Request(this); }; /* Rollback a transaction. @callback [callback] A callback which is called after transaction has rolled back, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Transaction|Promise} */ Transaction.prototype.rollback = function(callback) { if (callback != null) { return this._rollback(callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._rollback(function(err) { if (err) { return reject(err); } return resolve(); }); }; })(this)); }; Transaction.prototype._rollback = function(callback) { if (this._aborted) { callback(new TransactionError("Transaction has been aborted.", 'EABORT')); return this; } if (!this._pooledConnection) { callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN')); return this; } if (this._working) { callback(new TransactionError("Can't rollback transaction. There is a request in progress.", 'EREQINPROG')); return this; } if (this._queue.length) { this._aborted = true; } this.connection.driver.Transaction.prototype.rollback.call(this, (function(_this) { return function(err) { if (!err) { _this.emit('rollback', _this._aborted); } return callback(err); }; })(this)); return this; }; return Transaction; })(EventEmitter); /* Class Request. @property {Connection} connection Reference to used connection. @property {Transaction} transaction Reference to transaction when request was created in transaction. @property {*} parameters Collection of input and output parameters. @property {Boolean} verbose If `true`, debug messages are printed to message log. @property {Boolean} multiple If `true`, `query` will handle multiple recordsets (`execute` always expect multiple recordsets). @property {Boolean} canceled `true` if request was canceled. @event recordset Dispatched when metadata for new recordset are parsed. @event row Dispatched when new row is parsed. @event done Dispatched when request is complete. @event error Dispatched on error. */ Request = (function(superClass) { extend(Request, superClass); Request.prototype.connection = null; Request.prototype.transaction = null; Request.prototype.pstatement = null; Request.prototype.parameters = null; Request.prototype.verbose = false; Request.prototype.multiple = false; Request.prototype.canceled = false; Request.prototype.stream = null; Request.prototype.rowsAffected = null; /* Create new Request. @param {Connection|Transaction} connection If ommited, global connection is used instead. */ function Request(connection) { if (connection instanceof Transaction) { this.transaction = connection; this.connection = connection.connection; } else if (connection instanceof PreparedStatement) { this.pstatement = connection; this.connection = connection.connection; } else if (connection instanceof Connection) { this.connection = connection; } else { this.connection = global_connection; } this.parameters = {}; } /* Log to a function if assigned. Else, use console.log. */ Request.prototype._log = function(out) { if (typeof this.logger === "function") { return this.logger(out); } else { return console.log(out); } }; /* Fetch request from tagged template string. */ Request.prototype._template = function(method, strings, values) { var command, i, index, len, value; command = [strings[0]]; for (index = i = 0, len = values.length; i < len; index = ++i) { value = values[index]; this.input("param" + (index + 1), value); command.push("@param" + (index + 1), strings[index + 1]); } return this[method](command.join('')); }; /* Acquire connection for this request from connection. */ Request.prototype._acquire = function(callback) { if (this.transaction) { return this.transaction.queue(callback); } else if (this.pstatement) { return this.pstatement.queue(callback); } else { if (!this.connection.pool) { return callback(new ConnectionError("Connection not yet open.", 'ENOTOPEN')); } return this.connection.pool.acquire(callback); } }; /* Makes the request dedicated to one connection. */ Request.prototype._dedicated = function(connection) { this._acquire = function(callback) { return callback(null, connection); }; this._release = function() {}; return this; }; /* Release connection used by this request. */ Request.prototype._release = function(connection) { if (this.transaction) { return this.transaction.next(); } else if (this.pstatement) { return this.pstatement.next(); } else { return this.connection.pool.release(connection); } }; /* Add an input parameter to the request. **Example:** ``` request.input('input_parameter', value); request.input('input_parameter', sql.Int, value); ``` @param {String} name Name of the input parameter without @ char. @param {*} [type] SQL data type of input parameter. If you omit type, module automaticaly decide which SQL data type should be used based on JS data type. @param {*} value Input parameter value. `undefined` and `NaN` values are automatically converted to `null` values. @returns {Request} */ Request.prototype.input = function(name, type, value) { if (/(--| |\/\*|\*\/|')/.test(name)) { throw new RequestError("SQL injection warning for param '" + name + "'", 'EINJECT'); } if (arguments.length === 1) { throw new RequestError("Invalid number of arguments. At least 2 arguments expected.", 'EARGS'); } else if (arguments.length === 2) { value = type; type = getTypeByValue(value); } if ((value != null ? value.valueOf : void 0) && !(value instanceof Date)) { value = value.valueOf(); } if (value === void 0) { value = null; } if (value !== value) { value = null; } if (type instanceof Function) { type = type(); } this.parameters[name] = { name: name, type: type.type, io: 1, value: value, length: type.length, scale: type.scale, precision: type.precision, tvpType: type.tvpType }; return this; }; /* Add an output parameter to the request. **Example:** ``` request.output('output_parameter', sql.Int); request.output('output_parameter', sql.VarChar(50), 'abc'); ``` @param {String} name Name of the output parameter without @ char. @param {*} type SQL data type of output parameter. @param {*} [value] Output parameter value initial value. `undefined` and `NaN` values are automatically converted to `null` values. Optional. @returns {Request} */ Request.prototype.output = function(name, type, value) { if (!type) { type = TYPES.NVarChar; } if (/(--| |\/\*|\*\/|')/.test(name)) { throw new RequestError("SQL injection warning for param '" + name + "'", 'EINJECT'); } if (type === TYPES.Text || type === TYPES.NText || type === TYPES.Image) { throw new RequestError("Deprecated types (Text, NText, Image) are not supported as OUTPUT parameters.", 'EDEPRECATED'); } if ((value != null ? value.valueOf : void 0) && !(value instanceof Date)) { value = value.valueOf(); } if (value === void 0) { value = null; } if (value !== value) { value = null; } if (type instanceof Function) { type = type(); } this.parameters[name] = { name: name, type: type.type, io: 2, value: value, length: type.length, scale: type.scale, precision: type.precision }; return this; }; /* Execute the SQL batch. @param {String} batch T-SQL batch to be executed. @callback [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @param {*} recordset Recordset. @returns {Request|Promise} */ Request.prototype.batch = function(batch, callback) { var ref1; if (this.stream == null) { this.stream = (ref1 = this.connection) != null ? ref1.config.stream : void 0; } this.rowsAffected = 0; if (this.stream || (callback != null)) { return this._batch(batch, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._batch(batch, function(err, recordset) { if (err) { return reject(err); } return resolve(recordset); }); }; })(this)); }; Request.prototype._batch = function(batch, callback) { if (!this.connection) { return process.nextTick((function(_this) { return function() { var e; e = new RequestError("No connection is specified for that request.", 'ENOCONN'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } if (!this.connection.connected) { return process.nextTick((function(_this) { return function() { var e; e = new ConnectionError("Connection is closed.", 'ECONNCLOSED'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } this.canceled = false; this.connection.driver.Request.prototype.batch.call(this, batch, (function(_this) { return function(err, recordsets) { if (_this.stream) { if (err) { _this.emit('error', err); } return _this.emit('done', _this.rowsAffected); } else { return callback(err, recordsets, _this.rowsAffected); } }; })(this)); return this; }; /* Bulk load. @param {Table} table SQL table. @callback [callback] A callback which is called after bulk load has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @returns {Request|Promise} */ Request.prototype.bulk = function(table, callback) { var ref1; if (this.stream == null) { this.stream = (ref1 = this.connection) != null ? ref1.config.stream : void 0; } if (this.stream || (callback != null)) { return this._bulk(table, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._bulk(table, function(err, rowCount) { if (err) { return reject(err); } return resolve(rowCount); }); }; })(this)); }; Request.prototype._bulk = function(table, callback) { if (!this.connection) { return process.nextTick((function(_this) { return function() { var e; e = new RequestError("No connection is specified for that request.", 'ENOCONN'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } if (!this.connection.connected) { return process.nextTick((function(_this) { return function() { var e; e = new ConnectionError("Connection is closed.", 'ECONNCLOSED'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } this.canceled = false; this.connection.driver.Request.prototype.bulk.call(this, table, (function(_this) { return function(err, rowCount) { if (_this.stream) { if (err) { _this.emit('error', err); } return _this.emit('done'); } else { return callback(err, rowCount); } }; })(this)); return this; }; /* Sets request to `stream` mode and pulls all rows from all recordsets to a given stream. @param {Stream} stream Stream to pipe data into. @returns {Stream} */ Request.prototype.pipe = function(stream) { this.stream = true; this.on('row', stream.write.bind(stream)); this.on('error', stream.emit.bind(stream, 'error')); this.on('done', function() { return setImmediate(function() { return stream.end(); }); }); stream.emit('pipe', this); return stream; }; /* Execute the SQL command. **Example:** ``` var request = new sql.Request(); request.query('select 1 as number', function(err, recordset) { console.log(recordset[0].number); // return 1 // ... }); ``` You can enable multiple recordsets in querries by `request.multiple = true` command. ``` var request = new sql.Request(); request.multiple = true; request.query('select 1 as number; select 2 as number', function(err, recordsets) { console.log(recordsets[0][0].number); // return 1 console.log(recordsets[1][0].number); // return 2 // ... }); ``` @param {String} command T-SQL command to be executed. @callback [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @param {*} recordset Recordset. @returns {Request|Promise} */ Request.prototype.query = function(command, callback) { var ref1; if (this.stream == null) { this.stream = (ref1 = this.connection) != null ? ref1.config.stream : void 0; } this.rowsAffected = 0; if (this.stream || (callback != null)) { return this._query(command, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._query(command, function(err, recordsets) { if (err) { return reject(err); } return resolve(recordsets); }); }; })(this)); }; Request.prototype._query = function(command, callback) { if (!this.connection) { return process.nextTick((function(_this) { return function() { var e; e = new RequestError("No connection is specified for that request.", 'ENOCONN'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } if (!this.connection.connected) { return process.nextTick((function(_this) { return function() { var e; e = new ConnectionError("Connection is closed.", 'ECONNCLOSED'); if (_this.stream) { _this.emit('error', e); return _this.emit('done'); } else { return callback(e); } }; })(this)); } this.canceled = false; this.connection.driver.Request.prototype.query.call(this, command, (function(_this) { return function(err, recordsets) { if (_this.stream) { if (err) { _this.emit('error', err); } return _this.emit('done', _this.rowsAffected); } else { return callback(err, recordsets, _this.rowsAffected); } }; })(this)); return this; }; /* Call a stored procedure. **Example:** ``` var request = new sql.Request(); request.input('input_parameter', sql.Int, value); request.output('output_parameter', sql.Int); request.execute('procedure_name', function(err, recordsets, returnValue) { console.log(recordsets.length); // count of recordsets returned by procedure console.log(recordset[0].length); // count of rows contained in first recordset console.log(returnValue); // procedure return value console.log(recordsets.returnValue); // procedure return value console.log(request.parameters.output_parameter.value); // output value // ... }); ``` @param {String} procedure Name of the stored procedure to be executed. @callback [callback] A callback which is called after execution has completed, or an error has occurred. If omited, method returns Promise. @param {Error} err Error on error, otherwise null. @param {Array} recordsets Recordsets. @param {Number} returnValue Procedure return value. @returns {Request|Promise} */ Request.prototype.execute = function(command, callback) { var ref1; if (this.stream == null) { this.stream = (ref1 = this.connection) != null ? ref1.config.stream : void 0; } this.rowsAffected = 0; if (this.stream || (callback != null)) { return this._execute(command, callback); } return new module.exports.Promise((function(_this) { return function(resolve, reject) { return _this._execute(command, function(err, recordset) { if (err) { return reject(err); } return resolve(recordset); }); }; })(this)); }; Request.prototype._execute = function(procedure, callback) { if (!this.connection) { return process.nextTick(function() { var e; e = new RequestError("No connection is specified