// Generated by CoffeeScript 1.7.1
(function() {
  var Connection, ConnectionError, DRIVERS, EventEmitter, ISOLATION_LEVEL, PreparedStatement, PreparedStatementError, Request, RequestError, TYPES, Table, Transaction, TransactionError, declare, fs, getTypeByValue, global_connection, key, map, util, value, _ref,
    __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; },
    __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; };

  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'];

  Table = require('./table');

  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 index, item, _i, _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 item, _i, _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.
  
  @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(_super) {
    __extends(Connection, _super);

    Connection.prototype.connected = false;

    Connection.prototype.connecting = false;

    Connection.prototype.driver = null;


    /*
    	Create new 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.
     */

    function Connection(config, callback) {
      var err, _base, _base1, _base2, _base3, _ref1;
      this.config = config;
      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 (/^(.*)\\(.*)$/.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));
        if (module.exports.fix) {
          this.driver.fix();
        }
      } else {
        err = new ConnectionError("Unknown driver " + this.config.driver + "!", 'EDRIVER');
        if (callback) {
          callback(err);
        } else {
          throw err;
        }
      }
      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);
    };


    /*
    	Create connection to the server.
    	
    	@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}
     */

    Connection.prototype.connect = function(callback) {
      var err, go;
      if (this.connected) {
        err = new ConnectionError("Database is already connected! Call close before connecting to different database.", 'EALREADYCONNECTED');
        if (callback) {
          callback(err);
        } else {
          throw err;
        }
      }
      if (this.connecting) {
        err = new ConnectionError("Already connecting to database! Call close before connecting to different database.", 'EALREADYCONNECTING');
        if (callback) {
          callback(err);
        } else {
          throw err;
        }
      }
      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 typeof callback === "function" ? callback(err) : void 0;
          });
        };
      })(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 typeof callback === "function" ? callback(new ConnectionError("Failed to open debug stream. " + err.message, 'EDEBUG')) : void 0;
          }
        });
      } else {
        go();
      }
      return this;
    };


    /*
    	Close connection to the server.
    	
    	@callback [callback] A callback which is called after connection has closed, or an error has occurred.
    		@param {Error} err Error on error, otherwise null.
    	
    	@returns {Connection}
     */

    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 typeof callback === "function" ? callback(err) : void 0;
          };
        })(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 typeof callback === "function" ? callback(err) : void 0;
          };
        })(this));
        this.driver = 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);
    };

    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.
   */

  PreparedStatement = (function(_super) {
    __extends(PreparedStatement, _super);

    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;


    /*
    	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
      };
      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.
    		@param {Error} err Error on error, otherwise null.
    	@returns {PreparedStatement}
     */

    PreparedStatement.prototype.prepare = function(statement, callback) {
      var done;
      if (this._pooledConnection) {
        if (typeof callback === "function") {
          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 typeof callback === "function" ? callback(err) : void 0;
          }
          _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 typeof callback === "function" ? callback(err) : void 0;
            }
            _this._handle = req.parameters.handle.value;
            return typeof callback === "function" ? callback(null) : void 0;
          });
        };
      })(this);
      if (this.transaction) {
        if (!this.transaction._pooledConnection) {
          if (typeof callback === "function") {
            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) {
        this._queue.shift()(null, this._pooledConnection);
      } 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.
    		@param {Error} err Error on error, otherwise null.
    	@returns {Request}
     */

    PreparedStatement.prototype.execute = function(values, callback) {
      var name, param, req, _ref1;
      req = 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]));
        };
      })(this));
      return req;
    };


    /*
    	Unprepare a prepared statement.
    	
    	@callback [callback] A callback which is called after unpreparation has completed, or an error has occurred.
    		@param {Error} err Error on error, otherwise null.
    	@returns {PreparedStatement}
     */

    PreparedStatement.prototype.unprepare = function(callback) {
      var done, req;
      if (!this._pooledConnection) {
        if (typeof callback === "function") {
          callback(new PreparedStatementError("Statement is not prepared. Call prepare() first.", 'ENOTPREPARED'));
        }
        return this;
      }
      done = (function(_this) {
        return function(err) {
          if (err) {
            return typeof callback === "function" ? callback(err) : void 0;
          }
          if (_this.transaction) {
            _this.transaction.next();
          } else {
            _this.connection.pool.release(_this._pooledConnection);
            _this._pooledConnection = null;
          }
          _this._handle = 0;
          return typeof callback === "function" ? callback(null) : void 0;
        };
      })(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(_super) {
    __extends(Transaction, _super);

    Transaction.prototype._pooledConnection = null;

    Transaction.prototype._queue = null;

    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.connection = connection != null ? connection : global_connection;
      this._queue = [];
    }


    /*
    	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.
    		@param {Error} err Error on error, otherwise null.
    	@returns {Transaction}
     */

    Transaction.prototype.begin = function(isolationLevel, callback) {
      if (isolationLevel instanceof Function) {
        callback = isolationLevel;
        isolationLevel = void 0;
      }
      if (isolationLevel != null) {
        this.isolationLevel = isolationLevel;
      }
      if (this._pooledConnection) {
        if (typeof callback === "function") {
          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 typeof callback === "function" ? callback(err) : void 0;
        };
      })(this));
      return this;
    };


    /*
    	Commit a transaction.
    	
    	@callback [callback] A callback which is called after transaction has commited, or an error has occurred.
    		@param {Error} err Error on error, otherwise null.
    	@returns {Transaction}
     */

    Transaction.prototype.commit = function(callback) {
      if (!this._pooledConnection) {
        if (typeof callback === "function") {
          callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN'));
        }
        return this;
      }
      if (this._working) {
        if (typeof callback === "function") {
          callback(new TransactionError("Can't commit transaction. There is a request in progress.", 'EREQINPROG'));
        }
        return this;
      }
      this.connection.driver.Transaction.prototype.commit.call(this, (function(_this) {
        return function(err) {
          if (!err) {
            _this.emit('commit');
          }
          return typeof callback === "function" ? callback(err) : void 0;
        };
      })(this));
      return this;
    };


    /*
    	Execute next request in queue.
    	
    	@private
    	@returns {Transaction}
     */

    Transaction.prototype.next = function() {
      if (this._queue.length) {
        this._queue.shift()(null, this._pooledConnection);
      } 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 {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.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.
    		@param {Error} err Error on error, otherwise null.
    	@returns {Transaction}
     */

    Transaction.prototype.rollback = function(callback) {
      if (!this._pooledConnection) {
        if (typeof callback === "function") {
          callback(new TransactionError("Transaction has not begun. Call begin() first.", 'ENOTBEGUN'));
        }
        return this;
      }
      if (this._working) {
        if (typeof callback === "function") {
          callback(new TransactionError("Can't rollback transaction. There is a request in progress.", 'EREQINPROG'));
        }
        return this;
      }
      this.connection.driver.Transaction.prototype.rollback.call(this, (function(_this) {
        return function(err) {
          if (!err) {
            _this.emit('rollback');
          }
          return typeof callback === "function" ? callback(err) : void 0;
        };
      })(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(_super) {
    __extends(Request, _super);

    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;


    /*
    	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);
      }
    };


    /*
    	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);
      }
    };


    /*
    	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
      };
      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.
    		@param {Error} err Error on error, otherwise null.
    		@param {*} recordset Recordset.
    	
    	@returns {Request}
     */

    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 typeof callback === "function" ? callback(e) : void 0;
            }
          };
        })(this));
      }
      this.canceled = false;
      if (this.stream == null) {
        this.stream = this.connection.config.stream;
      }
      this.connection.driver.Request.prototype.batch.call(this, batch, (function(_this) {
        return function(err, recordset) {
          if (_this.stream) {
            if (err) {
              _this.emit('error', err);
            }
            return _this.emit('done');
          } else {
            return typeof callback === "function" ? callback(err, recordset) : void 0;
          }
        };
      })(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.
    		@param {Error} err Error on error, otherwise null.
    	
    	@returns {Request}
     */

    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 typeof callback === "function" ? callback(e) : void 0;
            }
          };
        })(this));
      }
      this.canceled = false;
      if (this.stream == null) {
        this.stream = this.connection.config.stream;
      }
      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 typeof callback === "function" ? callback(err, rowCount) : void 0;
          }
        };
      })(this));
      return this;
    };


    /*
    	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.
    		@param {Error} err Error on error, otherwise null.
    		@param {*} recordset Recordset.
    	
    	@returns {Request}
     */

    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 typeof callback === "function" ? callback(e) : void 0;
            }
          };
        })(this));
      }
      this.canceled = false;
      if (this.stream == null) {
        this.stream = this.connection.config.stream;
      }
      this.connection.driver.Request.prototype.query.call(this, command, (function(_this) {
        return function(err, recordset) {
          if (_this.stream) {
            if (err) {
              _this.emit('error', err);
            }
            return _this.emit('done');
          } else {
            return typeof callback === "function" ? callback(err, recordset) : void 0;
          }
        };
      })(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.
    		@param {Error} err Error on error, otherwise null.
    		@param {Array} recordsets Recordsets.
    		@param {Number} returnValue Procedure return value.
    	
    	@returns {Request}
     */

    Request.prototype.execute = function(procedure, callback) {
      if (!this.connection) {
        return process.nextTick(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 typeof callback === "function" ? callback(e) : void 0;
          }
        });
      }
      this.canceled = false;
      if (this.stream == null) {
        this.stream = this.connection.config.stream;
      }
      this.connection.driver.Request.prototype.execute.call(this, procedure, (function(_this) {
        return function(err, recordsets, returnValue) {
          if (_this.stream) {
            if (err) {
              _this.emit('error', err);
            }
            return _this.emit('done', returnValue);
          } else {
            return typeof callback === "function" ? callback(err, recordsets, returnValue) : void 0;
          }
        };
      })(this));
      return this;
    };


    /*
    	Cancel currently executed request.
    	
    	@returns {Request}
     */

    Request.prototype.cancel = function() {
      this.canceled = true;
      this.connection.driver.Request.prototype.cancel.call(this);
      return this;
    };

    return Request;

  })(EventEmitter);

  ConnectionError = (function(_super) {
    __extends(ConnectionError, _super);

    function ConnectionError(message, code) {
      var err;
      if (!(this 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;
        }
      }
      this.name = this.constructor.name;
      this.message = message;
      if (code != null) {
        this.code = code;
      }
      ConnectionError.__super__.constructor.call(this);
      Error.captureStackTrace(this, this.constructor);
    }

    return ConnectionError;

  })(Error);

  TransactionError = (function(_super) {
    __extends(TransactionError, _super);

    function TransactionError(message, code) {
      var err;
      if (!(this 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;
        }
      }
      this.name = this.constructor.name;
      this.message = message;
      if (code != null) {
        this.code = code;
      }
      TransactionError.__super__.constructor.call(this);
      Error.captureStackTrace(this, this.constructor);
    }

    return TransactionError;

  })(Error);

  RequestError = (function(_super) {
    __extends(RequestError, _super);

    function RequestError(message, code) {
      var err;
      if (!(this instanceof RequestError)) {
        if (message instanceof Error) {
          err = new RequestError(message.message, message.code);
          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;
        }
      }
      this.name = this.constructor.name;
      this.message = message;
      if (code != null) {
        this.code = code;
      }
      RequestError.__super__.constructor.call(this);
      Error.captureStackTrace(this, this.constructor);
    }

    return RequestError;

  })(Error);

  PreparedStatementError = (function(_super) {
    __extends(PreparedStatementError, _super);

    function PreparedStatementError(message, code) {
      var err;
      if (!(this 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;
        }
      }
      this.name = this.constructor.name;
      this.message = message;
      this.code = code;
      PreparedStatementError.__super__.constructor.call(this);
      Error.captureStackTrace(this, this.constructor);
    }

    return PreparedStatementError;

  })(Error);


  /*
  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 = function(config, callback) {
    global_connection = new Connection(config);
    return global_connection.connect(callback);
  };


  /*
  Close global connection.
  	
  @returns {Connection}
   */

  module.exports.close = function(callback) {
    return global_connection != null ? global_connection.close(callback) : void 0;
  };

  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;

  module.exports.map = map;

  module.exports.fix = true;

  for (key in TYPES) {
    value = TYPES[key];
    module.exports[key] = value;
    module.exports[key.toUpperCase()] = value;
  }

  module.exports.pool = {
    max: 10,
    min: 0,
    idleTimeoutMillis: 30000
  };

  module.exports.connection = {
    userName: '',
    password: '',
    server: ''
  };


  /*
  Initialize Tedious connection pool.
  
  @deprecated
   */

  module.exports.init = function() {
    return 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
    });
  };

}).call(this);
