UNPKG

mssql2

Version:

Microsoft SQL Server client for Node.js (fork)

1,542 lines (1,151 loc) 39.1 kB
{EventEmitter} = require 'events' util = require 'util' fs = require 'fs' {TYPES, declare} = require './datatypes' 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 = (jstype, sqltype) -> for item, index in @ when item.js is jstype @splice index, 1 break @push js: jstype sql: sqltype 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 = (value) -> if value is null or value is undefined then return TYPES.NVarChar switch typeof value when 'string' for item in map when item.js is String return item.sql return TYPES.NVarChar when 'number' for item in map when item.js is Number return item.sql return TYPES.Int when 'boolean' for item in map when item.js is Boolean return item.sql return TYPES.Bit when 'object' for item in map when value instanceof item.js return item.sql return TYPES.NVarChar else 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). ### class Connection extends EventEmitter connected: false connecting: false 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. ### constructor: (@config, callback) -> if 'string' is typeof @config try @config = ConnectionString.resolve @config catch ex if callback return callback ex else throw ex # set defaults @config.driver ?= 'tedious' @config.port ?= 1433 @config.options ?= {} @config.stream ?= false @config.parseJSON ?= false if /^(.*)\\(.*)$/.exec @config.server @config.server = RegExp.$1 @config.options.instanceName = RegExp.$2 if @config.driver in DRIVERS @driver = @initializeDriver require("./#{@config.driver}") else if typeof @config.driver is 'function' @driver = @initializeDriver @config.driver else err = new ConnectionError "Unknown driver #{@config.driver}!", 'EDRIVER' if callback return callback err else throw err # fix the driver by default if module.exports.fix then @driver.fix() if callback then @connect callback ### Write message to debug stream. ### _debug: (msg) -> @_debugStream?.write "#{String(msg).replace(/\x1B\[[0-9;]*m/g, '')}\n" ### Initializes driver for this connection. Separated from constructor and used by co-mssql. @private @param {Function} driver Loaded driver. @returns {Connection} ### initializeDriver: (driver) -> 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} ### connect: (callback) -> if callback? return @_connect callback new module.exports.Promise (resolve, reject) => @_connect (err) => if err then return reject err resolve @ _connect: (callback) -> if not @driver return callback new ConnectionError "Connection was closed. Create a new instance." if @connected return callback new ConnectionError "Database is already connected! Call close before connecting to different database.", 'EALREADYCONNECTED' if @connecting return callback new ConnectionError "Already connecting to database! Call close before connecting to different database.", 'EALREADYCONNECTING' go = => @connecting = true @driver.Connection::connect.call @, @config, (err) => unless @connecting then return @connecting = false if err if @_debugStream @_debugStream.removeAllListeners() @_debugStream.end() @_debugStream = null else @connected = true @emit 'connect' callback err if @config.debug @_debugStream = fs.createWriteStream "./mssql_debug_#{Date.now()}.log" @_debugStream.once 'open', go @_debugStream.on 'error', (err) -> if @connecting or @connected # error after successful open console.error err.stack else @_debugStream.removeListener 'open', go callback new ConnectionError("Failed to open debug stream. #{err.message}", 'EDEBUG') else go() @ ### 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} ### close: (callback) -> if callback? return @_close callback new module.exports.Promise (resolve, reject) => @_close (err) -> if err then return reject err resolve() _close: (callback) -> if @_debugStream @_debugStream.removeAllListeners() @_debugStream.end() @_debugStream = null if @connecting @connecting = false @driver.Connection::close.call @, (err) => callback err @driver = null else if @connected @connected = false @driver.Connection::close.call @, (err) => unless err @connected = false @emit 'close' callback err @driver = null else #if the connection was not connecting or connected, invoke the callback right away callback null @ ### Returns new request using this connection. @returns {Request} ### request: -> new Request @ ### Returns new transaction using this connection. @returns {Transaction} ### transaction: -> new Transaction @ ### Creates a new query using this connection from a tagged template string. @param {Array} strings Array of string literals. @param {...*} keys Values. @returns {Request} ### query: (strings, values...) -> new Request(@)._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} ### batch: (strings, values...) -> new Request(@)._template 'batch', strings, values ### 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. ### class PreparedStatement extends EventEmitter _pooledConnection: null _queue: null _working: false # if true, there is a request running at the moment _handle: 0 # sql prepared statement handle connection: null # sql.Connection transaction: null # !null in case we're in transaction prepared: false statement: null parameters: null multiple: false stream: null lastRequest: null ### Create new Prepared Statement. @param {String} statement SQL statement. @param {Connection} [connection] If ommited, global connection is used instead. ### constructor: (connection) -> if connection instanceof Transaction @transaction = connection @connection = connection.connection else if connection instanceof Connection @connection = connection else @connection = global_connection @_queue = [] @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} ### input: (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() @parameters[name] = name: name type: type.type io: 1 length: type.length scale: type.scale precision: type.precision tvpType: type.tvpType @ ### 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} ### output: (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() @parameters[name] = name: name type: type.type io: 2 length: type.length scale: type.scale precision: type.precision @ ### 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} ### prepare: (statement, callback) -> if callback? return @_prepare statement, callback new module.exports.Promise (resolve, reject) => @_prepare statement, (err) => if err then return reject err resolve @ _prepare: (statement, callback) -> if @_pooledConnection callback new PreparedStatementError "Statement is already prepared.", 'EALREADYPREPARED' return @ if typeof statement is 'function' callback = statement statement = undefined @statement = statement if statement? done = (err, connection) => if err then return callback err @_pooledConnection = connection req = new Request @ req.stream = false req.output 'handle', TYPES.Int req.input 'params', TYPES.NVarChar, ("@#{name} #{declare(param.type, param)}#{if param.io is 2 then " output" else ""}" for name, param of @parameters).join(',') req.input 'stmt', TYPES.NVarChar, @statement req.execute 'sp_prepare', (err) => if err if @transaction @transaction.next() else @connection.pool.release @_pooledConnection @_pooledConnection = null return callback err @_handle = req.parameters.handle.value callback null if @transaction unless @transaction._pooledConnection callback new TransactionError "Transaction has not begun. Call begin() first.", 'ENOTBEGUN' return @ @transaction.queue done else @connection.pool.acquire done @ ### Execute next request in queue. @private @returns {PreparedStatement} ### next: -> if @_queue.length # defer processing of next request process.nextTick => @_queue.shift() null, @_pooledConnection else @_working = false @ ### 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} ### queue: (callback) -> unless @_pooledConnection callback new PreparedStatementError "Statement is not prepared. Call prepare() first.", 'ENOTPREPARED' return @ if @_working @_queue.push callback else @_working = true callback null, @_pooledConnection @ ### 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} ### execute: (values, callback) -> if callback? return @_execute values, callback new module.exports.Promise (resolve, reject) => @_execute values, (err, recordset) -> if err then return reject err resolve recordset _execute: (values, callback) -> req = @lastRequest = new Request @ req.stream = @stream if @stream? req.input 'handle', TYPES.Int, @_handle # copy parameters with new values for name, param of @parameters 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', (err, recordsets, returnValue) => if err then return callback err callback null, (if @multiple then recordsets else recordsets[0]), req.rowsAffected 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} ### unprepare: (callback) -> if callback? return @_unprepare callback new module.exports.Promise (resolve, reject) => @_unprepare (err) -> if err then return reject err resolve() _unprepare: (callback) -> unless @_pooledConnection callback new PreparedStatementError "Statement is not prepared. Call prepare() first.", 'ENOTPREPARED' return @ done = (err) => if err then return callback err if @transaction @transaction.next() else @connection.pool.release @_pooledConnection @_pooledConnection = null @_handle = 0 callback null req = new Request @ req.stream = false req.input 'handle', TYPES.Int, @_handle req.execute 'sp_unprepare', done @ ### 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. ### class Transaction extends EventEmitter _pooledConnection: null _queue: null _aborted: false _working: false # if true, there is a request running at the moment name: "" connection: null # sql.Connection isolationLevel: ISOLATION_LEVEL.READ_COMMITTED ### Create new Transaction. @param {Connection} [connection] If ommited, global connection is used instead. ### constructor: (connection) -> @connection = connection ? global_connection @_queue = [] ### @private ### _abort: => @connection.driver.Transaction::_abort.call @ ### 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} ### begin: (isolationLevel, callback) -> if isolationLevel instanceof Function callback = isolationLevel isolationLevel = undefined if callback? return @_begin isolationLevel, callback new module.exports.Promise (resolve, reject) => @_begin isolationLevel, (err) => if err then return reject err resolve @ _begin: (isolationLevel, callback) -> @isolationLevel = isolationLevel if isolationLevel? if @_pooledConnection callback new TransactionError "Transaction has already begun.", 'EALREADYBEGUN' return @ @connection.driver.Transaction::begin.call @, (err) => unless err then @emit 'begin' callback err @ ### 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} ### commit: (callback) -> if callback? return @_commit callback new module.exports.Promise (resolve, reject) => @_commit (err) -> if err then return reject err resolve() _commit: (callback) -> unless @_pooledConnection callback new TransactionError "Transaction has not begun. Call begin() first.", 'ENOTBEGUN' return @ if @_working callback new TransactionError "Can't commit transaction. There is a request in progress.", 'EREQINPROG' return @ if @_queue.length callback new TransactionError "Can't commit transaction. There are request in queue.", 'EREQINPROG' return @ @connection.driver.Transaction::commit.call @, (err) => unless err then @emit 'commit' callback err @ ### Execute next request in queue. @private @returns {Transaction} ### next: -> if @_aborted toAbort = @_queue @_queue = [] # this must be async to ensure it is not processed earlier than the request that caused abortion of this transaction process.nextTick => while toAbort.length toAbort.shift() new TransactionError "Transaction aborted.", "EABORT" # this must be synchronous so we can rollback a transaction or commit transaction in last request's callback @_working = false if @_queue.length process.nextTick => if @_aborted then return @next() # transaction aborted manually @_working = true @_queue.shift() null, @_pooledConnection @ ### 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} ### queue: (callback) -> unless @_pooledConnection callback new TransactionError "Transaction has not begun. Call begin() first.", 'ENOTBEGUN' return @ if @_working or @_queue.length @_queue.push callback else @_working = true callback null, @_pooledConnection @ ### Returns new request using this transaction. @returns {Request} ### request: -> new Request @ ### 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} ### rollback: (callback) -> if callback? return @_rollback callback new module.exports.Promise (resolve, reject) => @_rollback (err) -> if err then return reject err resolve() _rollback: (callback) -> if @_aborted callback new TransactionError "Transaction has been aborted.", 'EABORT' return @ unless @_pooledConnection callback new TransactionError "Transaction has not begun. Call begin() first.", 'ENOTBEGUN' return @ if @_working callback new TransactionError "Can't rollback transaction. There is a request in progress.", 'EREQINPROG' return @ if @_queue.length @_aborted = true @connection.driver.Transaction::rollback.call @, (err) => unless err then @emit 'rollback', @_aborted callback err @ ### 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. ### class Request extends EventEmitter connection: null transaction: null pstatement: null parameters: null verbose: false multiple: false canceled: false stream: null rowsAffected: null ### Create new Request. @param {Connection|Transaction} connection If ommited, global connection is used instead. ### constructor: (connection) -> if connection instanceof Transaction @transaction = connection @connection = connection.connection else if connection instanceof PreparedStatement @pstatement = connection @connection = connection.connection else if connection instanceof Connection @connection = connection else @connection = global_connection @parameters = {} ### Log to a function if assigned. Else, use console.log. ### _log: (out) -> if typeof @logger is "function" then @logger out else console.log out ### Fetch request from tagged template string. ### _template: (method, strings, values) -> command = [strings[0]] for value, index in values @input "param#{index + 1}", value command.push "@param#{index + 1}", strings[index + 1] @[method] command.join '' ### Acquire connection for this request from connection. ### _acquire: (callback) -> if @transaction @transaction.queue callback else if @pstatement @pstatement.queue callback else unless @connection.pool return callback new ConnectionError "Connection not yet open.", 'ENOTOPEN' @connection.pool.acquire callback ### Makes the request dedicated to one connection. ### _dedicated: (connection) -> @_acquire = (callback) -> callback null, connection @_release = -> @ ### Release connection used by this request. ### _release: (connection) -> if @transaction @transaction.next() else if @pstatement @pstatement.next() else @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} ### input: (name, type, value) -> if (/(--| |\/\*|\*\/|')/).test name throw new RequestError "SQL injection warning for param '#{name}'", 'EINJECT' if arguments.length is 1 throw new RequestError "Invalid number of arguments. At least 2 arguments expected.", 'EARGS' else if arguments.length is 2 value = type type = getTypeByValue(value) # support for custom data types if value?.valueOf and value not instanceof Date then value = value.valueOf() # undefined to null if value is undefined then value = null # NaN to null if value isnt value then value = null if type instanceof Function type = type() @parameters[name] = name: name type: type.type io: 1 value: value length: type.length scale: type.scale precision: type.precision tvpType: type.tvpType @ ### 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} ### output: (name, type, value) -> unless type then type = TYPES.NVarChar if (/(--| |\/\*|\*\/|')/).test name throw new RequestError "SQL injection warning for param '#{name}'", 'EINJECT' if type is TYPES.Text or type is TYPES.NText or type is TYPES.Image throw new RequestError "Deprecated types (Text, NText, Image) are not supported as OUTPUT parameters.", 'EDEPRECATED' # support for custom data types if value?.valueOf and value not instanceof Date then value = value.valueOf() # undefined to null if value is undefined then value = null # NaN to null if value isnt value then value = null if type instanceof Function type = type() @parameters[name] = name: name type: type.type io: 2 value: value length: type.length scale: type.scale precision: type.precision @ ### 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} ### batch: (batch, callback) -> @stream ?= @connection?.config.stream @rowsAffected = 0 if @stream or callback? return @_batch batch, callback new module.exports.Promise (resolve, reject) => @_batch batch, (err, recordset) -> if err then return reject err resolve recordset _batch: (batch, callback) -> unless @connection return process.nextTick => e = new RequestError "No connection is specified for that request.", 'ENOCONN' if @stream @emit 'error', e @emit 'done' else callback e unless @connection.connected return process.nextTick => e = new ConnectionError "Connection is closed.", 'ECONNCLOSED' if @stream @emit 'error', e @emit 'done' else callback e @canceled = false @connection.driver.Request::batch.call @, batch, (err, recordsets) => if @stream if err then @emit 'error', err @emit 'done', @rowsAffected else callback err, recordsets, @rowsAffected @ ### 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} ### bulk: (table, callback) -> @stream ?= @connection?.config.stream if @stream or callback? return @_bulk table, callback new module.exports.Promise (resolve, reject) => @_bulk table, (err, rowCount) -> if err then return reject err resolve rowCount _bulk: (table, callback) -> unless @connection return process.nextTick => e = new RequestError "No connection is specified for that request.", 'ENOCONN' if @stream @emit 'error', e @emit 'done' else callback e unless @connection.connected return process.nextTick => e = new ConnectionError "Connection is closed.", 'ECONNCLOSED' if @stream @emit 'error', e @emit 'done' else callback e @canceled = false @connection.driver.Request::bulk.call @, table, (err, rowCount) => if @stream if err then @emit 'error', err @emit 'done' else callback err, rowCount @ ### 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} ### pipe: (stream) -> @stream = true @on 'row', stream.write.bind stream @on 'error', stream.emit.bind stream, 'error' @on 'done', -> setImmediate -> stream.end() stream.emit 'pipe', @ 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} ### query: (command, callback) -> @stream ?= @connection?.config.stream @rowsAffected = 0 if @stream or callback? return @_query command, callback new module.exports.Promise (resolve, reject) => @_query command, (err, recordsets) -> if err then return reject err resolve recordsets _query: (command, callback) -> unless @connection return process.nextTick => e = new RequestError "No connection is specified for that request.", 'ENOCONN' if @stream @emit 'error', e @emit 'done' else callback e unless @connection.connected return process.nextTick => e = new ConnectionError "Connection is closed.", 'ECONNCLOSED' if @stream @emit 'error', e @emit 'done' else callback e @canceled = false @connection.driver.Request::query.call @, command, (err, recordsets) => if @stream if err then @emit 'error', err @emit 'done', @rowsAffected else callback err, recordsets, @rowsAffected @ ### 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} ### execute: (command, callback) -> @stream ?= @connection?.config.stream @rowsAffected = 0 if @stream or callback? return @_execute command, callback new module.exports.Promise (resolve, reject) => @_execute command, (err, recordset) -> if err then return reject err resolve recordset _execute: (procedure, callback) -> unless @connection return process.nextTick -> e = new RequestError "No connection is specified for that request.", 'ENOCONN' if @stream @emit 'error', e @emit 'done' else callback e unless @connection.connected return process.nextTick => e = new ConnectionError "Connection is closed.", 'ECONNCLOSED' if @stream @emit 'error', e @emit 'done' else callback e @canceled = false @connection.driver.Request::execute.call @, procedure, (err, recordsets, returnValue) => if @stream if err then @emit 'error', err @emit 'done', returnValue, @rowsAffected else callback err, recordsets, returnValue, @rowsAffected @ ### Cancel currently executed request. @returns {Request} ### cancel: -> @canceled = true @connection.driver.Request::cancel.call @ @ class ConnectionError extends Error constructor: (message, code) -> unless @ instanceof ConnectionError if message instanceof Error err = new ConnectionError message.message, message.code Object.defineProperty err, 'originalError', value: message Error.captureStackTrace err, arguments.callee return err else err = new ConnectionError message Error.captureStackTrace err, arguments.callee return err @name = @constructor.name @message = message @code = code if code? super() Error.captureStackTrace @, @constructor class TransactionError extends Error constructor: (message, code) -> unless @ instanceof TransactionError if message instanceof Error err = new TransactionError message.message, message.code Object.defineProperty err, 'originalError', value: message Error.captureStackTrace err, arguments.callee return err else err = new TransactionError message Error.captureStackTrace err, arguments.callee return err @name = @constructor.name @message = message @code = code if code? super() Error.captureStackTrace @, @constructor class RequestError extends Error constructor: (message, code) -> unless @ instanceof RequestError if message instanceof Error err = new RequestError message.message, message.code ? code err.number = message.info?.number ? message.code # err.code is returned by msnodesql driver err.lineNumber = message.info?.lineNumber err.state = message.info?.state ? message.sqlstate # err.sqlstate is returned by msnodesql driver err.class = message.info?.class ? message.info?.severity # err.severity is returned by tds err.serverName = message.info?.serverName err.procName = message.info?.procName Object.defineProperty err, 'originalError', value: message Error.captureStackTrace err, arguments.callee return err else err = new RequestError message Error.captureStackTrace err, arguments.callee return err @name = @constructor.name @message = message @code = code if code? super() Error.captureStackTrace @, @constructor class PreparedStatementError extends Error constructor: (message, code) -> unless @ instanceof PreparedStatementError if message instanceof Error err = new PreparedStatementError message.message, message.code err.originalError = message Error.captureStackTrace err, arguments.callee return err else err = new PreparedStatementError message Error.captureStackTrace err, arguments.callee return err @name = @constructor.name @message = message @code = code super() Error.captureStackTrace @, @constructor ### Open global connection. @param {Object} config Connection configuration. @callback callback A callback which is called after connection has established, or an error has occurred. @param {Error} err Error on error, otherwise null. @returns {Connection} ### module.exports.connect = (config, callback) -> global_connection = new Connection config global_connection.connect callback ### Close global connection. @returns {Connection} ### module.exports.close = (callback) -> global_connection?.close callback ### Attach evnet handler to global connection. @param {String} event Event name. @param {Function} handler Event handler. @returns {Connection} ### module.exports.on = (event, handler) -> global_connection?.on event, handler ### Creates a new query using global connection from a tagged template string. @param {Array} strings Array of string literals. @param {...*} keys Values. @returns {Request} ### module.exports.query = (strings, values...) -> new Request()._template 'query', strings, values ### Creates a new batch using global connection from a tagged template string. @param {Array} strings Array of string literals. @param {...*} keys Values. @returns {Request} ### module.exports.batch = (strings, values...) -> new Request()._template 'batch', strings, values module.exports.Connection = Connection module.exports.Transaction = Transaction module.exports.Request = Request module.exports.Table = Table module.exports.PreparedStatement = PreparedStatement module.exports.ConnectionError = ConnectionError module.exports.TransactionError = TransactionError module.exports.RequestError = RequestError module.exports.PreparedStatementError = PreparedStatementError module.exports.ISOLATION_LEVEL = ISOLATION_LEVEL module.exports.DRIVERS = DRIVERS module.exports.TYPES = TYPES module.exports.MAX = 65535 # (1 << 16) - 1 module.exports.map = map module.exports.fix = true module.exports.Promise = global.Promise ? require('promise') # append datatypes to this modules export for key, value of TYPES module.exports[key] = value module.exports[key.toUpperCase()] = value # --- DEPRECATED IN 0.3.0 ------------------------------------------ module.exports.pool = max: 10 min: 0 idleTimeoutMillis: 30000 module.exports.connection = userName: '' password: '' server: '' ### Initialize Tedious connection pool. @deprecated ### module.exports.init = -> module.exports.connect user: module.exports.connection.userName password: module.exports.connection.password server: module.exports.connection.server options: module.exports.connection.options driver: 'tedious' pool: module.exports.pool