// 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));
        if (module.exports.fix) {
          this.driver.fix();
        }
      } else {
        err = new ConnectionError("Unknown driver " + this.config.driver + "!", 'EDRIVER');
        if (callback) {
          return 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);
    };


    /*
    	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;
      }
      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 for that request.", 'ENOCONN');
          if (this.stream) {
            this.emit('error', e);
            return this.emit('done');
          } else {
            return callback(e);
          }
        });
      }
      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.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, _this.rowsAffected);
          } else {
            return callback(err, recordsets, returnValue, _this.rowsAffected);
          }
        };
      })(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(superClass) {
    extend(ConnectionError, superClass);

    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(superClass) {
    extend(TransactionError, superClass);

    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(superClass) {
    extend(RequestError, superClass);

    function RequestError(message, code) {
      var err, ref1, ref10, ref11, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9;
      if (!(this instanceof RequestError)) {
        if (message instanceof Error) {
          err = new RequestError(message.message, (ref1 = message.code) != null ? ref1 : code);
          err.number = (ref2 = (ref3 = message.info) != null ? ref3.number : void 0) != null ? ref2 : message.code;
          err.lineNumber = (ref4 = message.info) != null ? ref4.lineNumber : void 0;
          err.state = (ref5 = (ref6 = message.info) != null ? ref6.state : void 0) != null ? ref5 : message.sqlstate;
          err["class"] = (ref7 = (ref8 = message.info) != null ? ref8["class"] : void 0) != null ? ref7 : (ref9 = message.info) != null ? ref9.severity : void 0;
          err.serverName = (ref10 = message.info) != null ? ref10.serverName : void 0;
          err.procName = (ref11 = message.info) != null ? ref11.procName : void 0;
          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(superClass) {
    extend(PreparedStatementError, superClass);

    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;
  };


  /*
  Attach evnet handler to global connection.
  
  @param {String} event Event name.
  @param {Function} handler Event handler.
  @returns {Connection}
   */

  module.exports.on = function(event, handler) {
    return global_connection != null ? global_connection.on(event, handler) : void 0;
  };


  /*
  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 = function() {
    var strings, values;
    strings = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    return 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 = function() {
    var strings, values;
    strings = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    return 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;

  module.exports.map = map;

  module.exports.fix = true;

  module.exports.Promise = (ref1 = global.Promise) != null ? ref1 : require('promise');

  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);
