// Generated by CoffeeScript 1.10.0
(function() {
  var DECLARATIONS, JSON_COLUMN_ID, Pool, TYPES, Table, UDT, XML_COLUMN_ID, bindDomain, cast, createColumns, declare, getMssqlType, getTediousType, parameterCorrection, ref, tds, util, valueCorrection,
    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;

  Pool = require('generic-pool').Pool;

  tds = require('tedious');

  util = require('util');

  ref = require('./datatypes'), TYPES = ref.TYPES, declare = ref.declare, cast = ref.cast;

  DECLARATIONS = require('./datatypes').DECLARATIONS;

  UDT = require('./udt').PARSERS;

  Table = require('./table');

  JSON_COLUMN_ID = 'JSON_F52E2B61-18A1-11d1-B105-00805F49916B';

  XML_COLUMN_ID = 'XML_F52E2B61-18A1-11d1-B105-00805F49916B';


  /*
  @ignore
   */

  bindDomain = function(cb) {
    var ref1;
    if (process.domain) {
      return (ref1 = process.domain) != null ? ref1.bind(cb) : void 0;
    } else {
      return cb;
    }
  };


  /*
  @ignore
   */

  getTediousType = function(type) {
    switch (type) {
      case TYPES.VarChar:
        return tds.TYPES.VarChar;
      case TYPES.NVarChar:
        return tds.TYPES.NVarChar;
      case TYPES.Text:
        return tds.TYPES.Text;
      case TYPES.Int:
        return tds.TYPES.Int;
      case TYPES.BigInt:
        return tds.TYPES.BigInt;
      case TYPES.TinyInt:
        return tds.TYPES.TinyInt;
      case TYPES.SmallInt:
        return tds.TYPES.SmallInt;
      case TYPES.Bit:
        return tds.TYPES.Bit;
      case TYPES.Float:
        return tds.TYPES.Float;
      case TYPES.Decimal:
        return tds.TYPES.Decimal;
      case TYPES.Numeric:
        return tds.TYPES.Numeric;
      case TYPES.Real:
        return tds.TYPES.Real;
      case TYPES.Money:
        return tds.TYPES.Money;
      case TYPES.SmallMoney:
        return tds.TYPES.SmallMoney;
      case TYPES.Time:
        return tds.TYPES.TimeN;
      case TYPES.Date:
        return tds.TYPES.DateN;
      case TYPES.DateTime:
        return tds.TYPES.DateTime;
      case TYPES.DateTime2:
        return tds.TYPES.DateTime2N;
      case TYPES.DateTimeOffset:
        return tds.TYPES.DateTimeOffsetN;
      case TYPES.SmallDateTime:
        return tds.TYPES.SmallDateTime;
      case TYPES.UniqueIdentifier:
        return tds.TYPES.UniqueIdentifierN;
      case TYPES.Xml:
        return tds.TYPES.VarChar;
      case TYPES.Char:
        return tds.TYPES.Char;
      case TYPES.NChar:
        return tds.TYPES.NChar;
      case TYPES.NText:
        return tds.TYPES.NVarChar;
      case TYPES.Image:
        return tds.TYPES.Image;
      case TYPES.Binary:
        return tds.TYPES.Binary;
      case TYPES.VarBinary:
        return tds.TYPES.VarBinary;
      case TYPES.UDT:
      case TYPES.Geography:
      case TYPES.Geometry:
        return tds.TYPES.UDT;
      case TYPES.TVP:
        return tds.TYPES.TVP;
      case TYPES.Variant:
        return tds.TYPES.Variant;
      default:
        return type;
    }
  };


  /*
  @ignore
   */

  getMssqlType = function(type, length) {
    switch (type) {
      case tds.TYPES.Char:
        return TYPES.Char;
      case tds.TYPES.NChar:
        return TYPES.NChar;
      case tds.TYPES.VarChar:
        return TYPES.VarChar;
      case tds.TYPES.NVarChar:
        return TYPES.NVarChar;
      case tds.TYPES.Text:
        return TYPES.Text;
      case tds.TYPES.NText:
        return TYPES.NText;
      case tds.TYPES.Int:
        return TYPES.Int;
      case tds.TYPES.IntN:
        if (length === 8) {
          return TYPES.BigInt;
        }
        if (length === 4) {
          return TYPES.Int;
        }
        if (length === 2) {
          return TYPES.SmallInt;
        }
        return TYPES.TinyInt;
      case tds.TYPES.BigInt:
        return TYPES.BigInt;
      case tds.TYPES.TinyInt:
        return TYPES.TinyInt;
      case tds.TYPES.SmallInt:
        return TYPES.SmallInt;
      case tds.TYPES.Bit:
      case tds.TYPES.BitN:
        return TYPES.Bit;
      case tds.TYPES.Float:
        return TYPES.Float;
      case tds.TYPES.FloatN:
        if (length === 8) {
          return TYPES.FloatN;
        }
        return TYPES.Real;
      case tds.TYPES.Real:
        return TYPES.Real;
      case tds.TYPES.Money:
        return TYPES.Money;
      case tds.TYPES.MoneyN:
        if (length === 8) {
          return TYPES.Money;
        }
        return TYPES.SmallMoney;
      case tds.TYPES.SmallMoney:
        return TYPES.SmallMoney;
      case tds.TYPES.Numeric:
      case tds.TYPES.NumericN:
        return TYPES.Numeric;
      case tds.TYPES.Decimal:
      case tds.TYPES.DecimalN:
        return TYPES.Decimal;
      case tds.TYPES.DateTime:
        return TYPES.DateTime;
      case tds.TYPES.DateTimeN:
        if (length === 8) {
          return TYPES.DateTime;
        }
        return TYPES.SmallDateTime;
      case tds.TYPES.TimeN:
        return TYPES.Time;
      case tds.TYPES.DateN:
        return TYPES.Date;
      case tds.TYPES.DateTime2N:
        return TYPES.DateTime2;
      case tds.TYPES.DateTimeOffsetN:
        return TYPES.DateTimeOffset;
      case tds.TYPES.SmallDateTime:
        return TYPES.SmallDateTime;
      case tds.TYPES.UniqueIdentifierN:
        return TYPES.UniqueIdentifier;
      case tds.TYPES.Image:
        return TYPES.Image;
      case tds.TYPES.Binary:
        return TYPES.Binary;
      case tds.TYPES.VarBinary:
        return TYPES.VarBinary;
      case tds.TYPES.Xml:
        return TYPES.Xml;
      case tds.TYPES.UDT:
        return TYPES.UDT;
      case tds.TYPES.TVP:
        return TYPES.TVP;
      case tds.TYPES.Variant:
        return TYPES.Variant;
    }
  };


  /*
  @ignore
   */

  createColumns = function(metadata) {
    var column, i, index, len, out;
    out = {};
    for (index = i = 0, len = metadata.length; i < len; index = ++i) {
      column = metadata[index];
      out[column.colName] = {
        index: index,
        name: column.colName,
        length: column.dataLength,
        type: getMssqlType(column.type, column.dataLength),
        scale: column.scale,
        precision: column.precision,
        nullable: !!(column.flags & 0x01),
        caseSensitive: !!(column.flags & 0x02),
        identity: !!(column.flags & 0x10),
        readOnly: !(column.flags & 0x0C)
      };
      if (column.udtInfo != null) {
        out[column.colName].udt = {
          name: column.udtInfo.typeName,
          database: column.udtInfo.dbname,
          schema: column.udtInfo.owningSchema,
          assembly: column.udtInfo.assemblyName
        };
        if (DECLARATIONS[column.udtInfo.typeName]) {
          out[column.colName].type = DECLARATIONS[column.udtInfo.typeName];
        }
      }
    }
    return out;
  };


  /*
  @ignore
   */

  valueCorrection = function(value, metadata) {
    if (metadata.type === tds.TYPES.UDT && (value != null)) {
      if (UDT[metadata.udtInfo.typeName]) {
        return UDT[metadata.udtInfo.typeName](value);
      } else {
        return value;
      }
    } else {
      return value;
    }
  };


  /*
  @ignore
   */

  parameterCorrection = function(value) {
    var col, i, len, ref1, tvp;
    if (value instanceof Table) {
      tvp = {
        name: value.name,
        schema: value.schema,
        columns: [],
        rows: value.rows
      };
      ref1 = value.columns;
      for (i = 0, len = ref1.length; i < len; i++) {
        col = ref1[i];
        tvp.columns.push({
          name: col.name,
          type: getTediousType(col.type),
          length: col.length,
          scale: col.scale,
          precision: col.precision
        });
      }
      return tvp;
    } else {
      return value;
    }
  };


  /*
  @ignore
   */

  module.exports = function(Connection, Transaction, Request, ConnectionError, TransactionError, RequestError) {
    var TediousConnection, TediousRequest, TediousTransaction;
    TediousConnection = (function(superClass) {
      extend(TediousConnection, superClass);

      function TediousConnection() {
        return TediousConnection.__super__.constructor.apply(this, arguments);
      }

      TediousConnection.prototype.pool = null;

      TediousConnection.prototype.connect = function(config, callback) {
        var base, base1, base2, base3, base4, base5, cfg, cfg_pool, key, ref1, ref2, ref3, ref4, value;
        cfg = {
          userName: config.user,
          password: config.password,
          server: config.server,
          options: config.options,
          domain: config.domain
        };
        if ((base = cfg.options).database == null) {
          base.database = config.database;
        }
        if ((base1 = cfg.options).port == null) {
          base1.port = config.port;
        }
        if ((base2 = cfg.options).connectTimeout == null) {
          base2.connectTimeout = (ref1 = (ref2 = config.connectionTimeout) != null ? ref2 : config.timeout) != null ? ref1 : 15000;
        }
        if ((base3 = cfg.options).requestTimeout == null) {
          base3.requestTimeout = (ref3 = config.requestTimeout) != null ? ref3 : 15000;
        }
        if ((base4 = cfg.options).tdsVersion == null) {
          base4.tdsVersion = '7_4';
        }
        cfg.options.rowCollectionOnDone = false;
        cfg.options.rowCollectionOnRequestCompletion = false;
        cfg.options.useColumnNames = false;
        if ((base5 = cfg.options).appName == null) {
          base5.appName = 'node-mssql';
        }
        if (cfg.options.instanceName) {
          delete cfg.options.port;
        }
        if (isNaN(cfg.options.requestTimeout)) {
          cfg.options.requestTimeout = 15000;
        }
        if (cfg.options.requestTimeout === Infinity) {
          cfg.options.requestTimeout = 0;
        }
        if (cfg.options.requestTimeout < 0) {
          cfg.options.requestTimeout = 0;
        }
        if (config.debug) {
          cfg.options.debug = {
            packet: true,
            token: true,
            data: true,
            payload: true
          };
        }
        cfg_pool = {
          name: 'mssql',
          max: 10,
          min: 0,
          idleTimeoutMillis: 30000,
          create: (function(_this) {
            return function(callback) {
              var c;
              c = new tds.Connection(cfg);
              c.once('connect', function(err) {
                if (err) {
                  err = ConnectionError(err);
                }
                if (err) {
                  return callback(err, null);
                }
                return callback(null, c);
              });
              c.on('error', function(err) {
                if (err.code === 'ESOCKET') {
                  c.hasError = true;
                  return;
                }
                return _this.emit('error', err);
              });
              if (config.debug) {
                return c.on('debug', function(msg) {
                  return _this._debug(msg);
                });
              }
            };
          })(this),
          validate: function(c) {
            return (c != null) && !c.closed && !c.hasError;
          },
          destroy: function(c) {
            if (c != null) {
              c.close();
            }
            return setTimeout(function() {
              return c != null ? c.removeAllListeners() : void 0;
            }, 500);
          }
        };
        if (config.pool) {
          ref4 = config.pool;
          for (key in ref4) {
            value = ref4[key];
            cfg_pool[key] = value;
          }
        }
        this.pool = Pool(cfg_pool, cfg);
        return this.pool.acquire((function(_this) {
          return function(err, connection) {
            if (err) {
              _this.pool.drain(function() {
                var ref5;
                if ((ref5 = _this.pool) != null) {
                  ref5.destroyAllNow();
                }
                return _this.pool = null;
              });
            } else {
              _this.pool.release(connection);
            }
            return callback(err);
          };
        })(this));
      };

      TediousConnection.prototype.close = function(callback) {
        if (!this.pool) {
          return callback(null);
        }
        return this.pool.drain((function(_this) {
          return function() {
            var ref1;
            if ((ref1 = _this.pool) != null) {
              ref1.destroyAllNow();
            }
            _this.pool = null;
            return callback(null);
          };
        })(this));
      };

      return TediousConnection;

    })(Connection);
    TediousTransaction = (function(superClass) {
      extend(TediousTransaction, superClass);

      function TediousTransaction() {
        return TediousTransaction.__super__.constructor.apply(this, arguments);
      }

      TediousTransaction.prototype._abort = function() {
        var pc;
        if (!this._rollbackRequested) {
          pc = this._pooledConnection;
          setImmediate((function(_this) {
            return function() {
              return _this.connection.pool.release(pc);
            };
          })(this));
          this._pooledConnection.removeListener('rollbackTransaction', this._abort);
          this._pooledConnection = null;
          this._aborted = true;
          return this.emit('rollback', true);
        }
      };

      TediousTransaction.prototype.begin = function(callback) {
        this._aborted = false;
        this._rollbackRequested = false;
        return this.connection.pool.acquire((function(_this) {
          return function(err, connection) {
            if (err) {
              return callback(err);
            }
            _this._pooledConnection = connection;
            _this._pooledConnection.on('rollbackTransaction', _this._abort);
            return connection.beginTransaction(bindDomain(function(err) {
              if (err) {
                err = TransactionError(err);
              }
              return callback(err);
            }, _this.name, _this.isolationLevel));
          };
        })(this));
      };

      TediousTransaction.prototype.commit = function(callback) {
        return this._pooledConnection.commitTransaction(bindDomain((function(_this) {
          return function(err) {
            if (err) {
              err = TransactionError(err);
            }
            _this._pooledConnection.removeListener('rollbackTransaction', _this._abort);
            _this.connection.pool.release(_this._pooledConnection);
            _this._pooledConnection = null;
            return callback(err);
          };
        })(this)));
      };

      TediousTransaction.prototype.rollback = function(callback) {
        this._rollbackRequested = true;
        return this._pooledConnection.rollbackTransaction(bindDomain((function(_this) {
          return function(err) {
            if (err) {
              err = TransactionError(err);
            }
            _this._pooledConnection.removeListener('rollbackTransaction', _this._abort);
            _this.connection.pool.release(_this._pooledConnection);
            _this._pooledConnection = null;
            return callback(err);
          };
        })(this)));
      };

      return TediousTransaction;

    })(Transaction);
    TediousRequest = (function(superClass) {
      extend(TediousRequest, superClass);

      function TediousRequest() {
        return TediousRequest.__super__.constructor.apply(this, arguments);
      }


      /*
      		Execute specified sql batch.
       */

      TediousRequest.prototype.batch = function(batch, callback) {
        this._isBatch = true;
        return TediousRequest.prototype.query.call(this, batch, callback);
      };


      /*
      		Bulk load.
       */

      TediousRequest.prototype.bulk = function(table, callback) {
        var errorHandlers, errors, handleError, handleInfo, hasReturned, started;
        table._makeBulk();
        if (!table.name) {
          process.nextTick(function() {
            return callback(RequestError("Table name must be specified for bulk insert.", "ENAME"));
          });
        }
        if (table.name.charAt(0) === '@') {
          process.nextTick(function() {
            return callback(RequestError("You can't use table variables for bulk insert.", "ENAME"));
          });
        }
        started = Date.now();
        errors = [];
        errorHandlers = {};
        hasReturned = false;
        handleError = (function(_this) {
          return function(doReturn, connection, info) {
            var e, err, event, handler;
            err = new Error(info.message);
            err.info = info;
            e = RequestError(err, 'EREQUEST');
            if (_this.stream) {
              _this.emit('error', e);
            } else {
              if (doReturn && !hasReturned) {
                if (connection != null) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                }
                hasReturned = true;
                if (typeof callback === "function") {
                  callback(e);
                }
              }
            }
            return errors.push(e);
          };
        })(this);
        handleInfo = (function(_this) {
          return function(msg) {
            return _this.emit('info', {
              message: msg.message,
              number: msg.number,
              state: msg.state,
              "class": msg["class"],
              lineNumber: msg.lineNumber,
              serverName: msg.serverName,
              procName: msg.procName
            });
          };
        })(this);
        return this._acquire((function(_this) {
          return function(err, connection) {
            var bulk, col, done, i, j, len, len1, objectid, ref1, ref2, req, row;
            if (!err) {
              if (_this.verbose) {
                _this._log("-------- sql bulk load --------\n    table: " + table.name);
              }
              if (_this.canceled) {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                _this._release(connection);
                return typeof callback === "function" ? callback(new RequestError("Canceled.", 'ECANCEL')) : void 0;
              }
              _this._cancel = function() {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                return connection.cancel();
              };
              errorHandlers['infoMessage'] = handleInfo;
              errorHandlers['errorMessage'] = handleError.bind(void 0, false, connection);
              errorHandlers['error'] = handleError.bind(void 0, true, connection);
              connection.on('infoMessage', errorHandlers['infoMessage']);
              connection.on('errorMessage', errorHandlers['errorMessage']);
              connection.on('error', errorHandlers['error']);
              done = bindDomain(function(err, rowCount) {
                var elapsed, error, event, handler, i, len, ref1;
                if (err && err.message !== ((ref1 = errors[errors.length - 1]) != null ? ref1.message : void 0)) {
                  err = RequestError(err, 'EREQUEST');
                  if (_this.stream) {
                    _this.emit('error', err);
                  }
                  errors.push(err);
                }
                if (_this.verbose) {
                  if (errors.length) {
                    for (i = 0, len = errors.length; i < len; i++) {
                      error = errors[i];
                      _this._log("    error: " + error);
                    }
                  }
                  elapsed = Date.now() - started;
                  _this._log(" duration: " + elapsed + "ms");
                  _this._log("---------- completed ----------");
                }
                _this._cancel = null;
                if (errors.length && !_this.stream) {
                  error = errors.pop();
                  error.precedingErrors = errors;
                }
                if (!hasReturned) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                  hasReturned = true;
                  if (_this.stream) {
                    return callback(null, null);
                  } else {
                    return typeof callback === "function" ? callback(error, rowCount) : void 0;
                  }
                }
              });
              bulk = connection.newBulkLoad(table.path, done);
              ref1 = table.columns;
              for (i = 0, len = ref1.length; i < len; i++) {
                col = ref1[i];
                bulk.addColumn(col.name, getTediousType(col.type), {
                  nullable: col.nullable,
                  length: col.length,
                  scale: col.scale,
                  precision: col.precision
                });
              }
              ref2 = table.rows;
              for (j = 0, len1 = ref2.length; j < len1; j++) {
                row = ref2[j];
                bulk.addRow(row);
              }
              if (_this.verbose) {
                _this._log("---------- response -----------");
              }
              if (table.create) {
                if (table.temporary) {
                  objectid = "tempdb..[" + table.name + "]";
                } else {
                  objectid = table.path;
                }
                req = new tds.Request("if object_id('" + (objectid.replace(/'/g, '\'\'')) + "') is null " + (table.declare()), function(err) {
                  if (err) {
                    return done(err);
                  }
                  return connection.execBulkLoad(bulk);
                });
                return connection.execSqlBatch(req);
              } else {
                return connection.execBulkLoad(bulk);
              }
            }
          };
        })(this));
      };


      /*
      		Execute specified sql command.
       */

      TediousRequest.prototype.query = function(command, callback) {
        var batchHasOutput, batchLastRow, chunksBuffer, columns, errorHandlers, errors, handleError, handleInfo, hasReturned, isChunkedRecordset, isJSONRecordset, recordset, recordsets, started, xmlBuffer;
        columns = {};
        recordset = [];
        recordsets = [];
        started = Date.now();
        errors = [];
        batchLastRow = null;
        batchHasOutput = false;
        isJSONRecordset = false;
        isChunkedRecordset = false;
        chunksBuffer = null;
        xmlBuffer = null;
        hasReturned = false;
        errorHandlers = {};
        handleError = (function(_this) {
          return function(doReturn, connection, info) {
            var e, err, event, handler;
            err = new Error(info.message);
            err.info = info;
            e = RequestError(err, 'EREQUEST');
            if (_this.stream) {
              _this.emit('error', e);
            } else {
              if (doReturn && !hasReturned) {
                if (connection != null) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                }
                hasReturned = true;
                if (typeof callback === "function") {
                  callback(e);
                }
              }
            }
            return errors.push(e);
          };
        })(this);
        handleInfo = (function(_this) {
          return function(msg) {
            return _this.emit('info', {
              message: msg.message,
              number: msg.number,
              state: msg.state,
              "class": msg["class"],
              lineNumber: msg.lineNumber,
              serverName: msg.serverName,
              procName: msg.procName
            });
          };
        })(this);
        return this._acquire((function(_this) {
          return function(err, connection) {
            var assigns, declarations, doneHandler, name, param, ref1, ref2, req, selects, value;
            if (!err) {
              if (_this.verbose) {
                _this._log("---------- sql " + (_this._isBatch ? 'batch' : 'query') + " ----------\n    " + (_this._isBatch ? 'batch' : 'query') + ": " + command);
              }
              if (_this.canceled) {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                _this._release(connection);
                return typeof callback === "function" ? callback(new RequestError("Canceled.", 'ECANCEL')) : void 0;
              }
              _this._cancel = function() {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                return connection.cancel();
              };
              errorHandlers['infoMessage'] = handleInfo;
              errorHandlers['errorMessage'] = handleError.bind(void 0, false, connection);
              errorHandlers['error'] = handleError.bind(void 0, true, connection);
              connection.on('infoMessage', errorHandlers['infoMessage']);
              connection.on('errorMessage', errorHandlers['errorMessage']);
              connection.on('error', errorHandlers['error']);
              req = new tds.Request(command, bindDomain(function(err) {
                var elapsed, error, event, handler, i, len, name, ref1, value;
                if (err && err.message !== ((ref1 = errors[errors.length - 1]) != null ? ref1.message : void 0)) {
                  err = RequestError(err, 'EREQUEST');
                  if (_this.stream) {
                    _this.emit('error', err);
                  }
                  errors.push(err);
                }
                if (batchHasOutput) {
                  if (!_this.stream) {
                    batchLastRow = recordsets.pop()[0];
                  }
                  for (name in batchLastRow) {
                    value = batchLastRow[name];
                    if (!(name !== '___return___')) {
                      continue;
                    }
                    if (_this.verbose) {
                      if (value === tds.TYPES.Null) {
                        _this._log("   output: @" + name + ", null");
                      } else {
                        _this._log("   output: @" + name + ", " + (_this.parameters[name].type.declaration.toLowerCase()) + ", " + value);
                      }
                    }
                    _this.parameters[name].value = value === tds.TYPES.Null ? null : value;
                  }
                }
                if (_this.verbose) {
                  if (errors.length) {
                    for (i = 0, len = errors.length; i < len; i++) {
                      error = errors[i];
                      _this._log("    error: " + error);
                    }
                  }
                  elapsed = Date.now() - started;
                  _this._log(" duration: " + elapsed + "ms");
                  _this._log("---------- completed ----------");
                }
                _this._cancel = null;
                if (errors.length && !_this.stream) {
                  error = errors.pop();
                  error.precedingErrors = errors;
                }
                if (!hasReturned) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                  hasReturned = true;
                  if (_this.stream) {
                    return callback(null, null);
                  } else {
                    return typeof callback === "function" ? callback(error, _this.multiple ? recordsets : recordsets[0]) : void 0;
                  }
                }
              }));
              req.on('columnMetadata', function(metadata) {
                var ref1;
                columns = createColumns(metadata);
                isChunkedRecordset = false;
                if (metadata.length === 1 && ((ref1 = metadata[0].colName) === JSON_COLUMN_ID || ref1 === XML_COLUMN_ID)) {
                  isChunkedRecordset = true;
                  chunksBuffer = [];
                }
                if (_this.stream) {
                  if (_this._isBatch) {
                    if (columns["___return___"] == null) {
                      return _this.emit('recordset', columns);
                    }
                  } else {
                    return _this.emit('recordset', columns);
                  }
                }
              });
              doneHandler = function(rowCount, more) {
                var error1, ex, row;
                if (Object.keys(columns).length === 0) {
                  if (rowCount > 0) {
                    _this.rowsAffected += rowCount;
                  }
                  return;
                }
                if (isChunkedRecordset) {
                  if (columns[JSON_COLUMN_ID] && _this.connection.config.parseJSON === true) {
                    try {
                      row = JSON.parse(chunksBuffer.join(''));
                    } catch (error1) {
                      ex = error1;
                      row = null;
                      ex = RequestError(new Error("Failed to parse incoming JSON. " + ex.message), 'EJSON');
                      if (_this.stream) {
                        _this.emit('error', ex);
                      }
                      errors.push(ex);
                    }
                  } else {
                    row = {};
                    row[Object.keys(columns)[0]] = chunksBuffer.join('');
                  }
                  chunksBuffer = null;
                  if (_this.verbose) {
                    _this._log(util.inspect(row));
                    _this._log("---------- --------------------");
                  }
                  if (_this.stream) {
                    _this.emit('row', row);
                  } else {
                    recordset.push(row);
                  }
                }
                if (!_this.stream) {
                  Object.defineProperty(recordset, 'columns', {
                    enumerable: false,
                    value: columns
                  });
                  Object.defineProperty(recordset, 'toTable', {
                    enumerable: false,
                    value: function() {
                      return Table.fromRecordset(this);
                    }
                  });
                  recordsets.push(recordset);
                }
                recordset = [];
                return columns = {};
              };
              req.on('doneInProc', doneHandler);
              req.on('done', doneHandler);
              req.on('returnValue', function(parameterName, value, metadata) {
                if (_this.verbose) {
                  if (value === tds.TYPES.Null) {
                    _this._log("   output: @" + parameterName + ", null");
                  } else {
                    _this._log("   output: @" + parameterName + ", " + (_this.parameters[parameterName].type.declaration.toLowerCase()) + ", " + value);
                  }
                }
                return _this.parameters[parameterName].value = value === tds.TYPES.Null ? null : value;
              });
              req.on('row', function(columns) {
                var col, exi, i, len, row;
                if (!recordset) {
                  recordset = [];
                }
                if (isChunkedRecordset) {
                  return chunksBuffer.push(columns[0].value);
                } else {
                  row = {};
                  for (i = 0, len = columns.length; i < len; i++) {
                    col = columns[i];
                    col.value = valueCorrection(col.value, col.metadata);
                    exi = row[col.metadata.colName];
                    if (exi != null) {
                      if (exi instanceof Array) {
                        exi.push(col.value);
                      } else {
                        row[col.metadata.colName] = [exi, col.value];
                      }
                    } else {
                      row[col.metadata.colName] = col.value;
                    }
                  }
                  if (_this.verbose) {
                    _this._log(util.inspect(row));
                    _this._log("---------- --------------------");
                  }
                  if (_this.stream) {
                    if (_this._isBatch) {
                      if (row["___return___"] != null) {
                        return batchLastRow = row;
                      } else {
                        return _this.emit('row', row);
                      }
                    } else {
                      return _this.emit('row', row);
                    }
                  } else {
                    return recordset.push(row);
                  }
                }
              });
              if (_this._isBatch) {
                if (Object.keys(_this.parameters).length) {
                  ref1 = _this.parameters;
                  for (name in ref1) {
                    param = ref1[name];
                    value = getTediousType(param.type).validate(param.value);
                    if (value instanceof TypeError) {
                      value = new RequestError("Validation failed for parameter \'" + name + "\'. " + value.message, 'EPARAM');
                      if (_this.verbose) {
                        _this._log("    error: " + value);
                        _this._log("---------- completed ----------");
                      }
                      _this._release(connection);
                      return typeof callback === "function" ? callback(value) : void 0;
                    }
                    param.value = value;
                  }
                  declarations = (function() {
                    var ref2, results;
                    ref2 = this.parameters;
                    results = [];
                    for (name in ref2) {
                      param = ref2[name];
                      results.push("@" + name + " " + (declare(param.type, param)));
                    }
                    return results;
                  }).call(_this);
                  assigns = (function() {
                    var ref2, results;
                    ref2 = this.parameters;
                    results = [];
                    for (name in ref2) {
                      param = ref2[name];
                      results.push("@" + name + " = " + (cast(param.value, param.type, param)));
                    }
                    return results;
                  }).call(_this);
                  selects = (function() {
                    var ref2, results;
                    ref2 = this.parameters;
                    results = [];
                    for (name in ref2) {
                      param = ref2[name];
                      if (param.io === 2) {
                        results.push("@" + name + " as [" + name + "]");
                      }
                    }
                    return results;
                  }).call(_this);
                  batchHasOutput = selects.length > 0;
                  req.sqlTextOrProcedure = "declare " + (declarations.join(', ')) + ";select " + (assigns.join(', ')) + ";" + req.sqlTextOrProcedure + ";" + (batchHasOutput ? 'select 1 as [___return___], ' + selects.join(', ') : '');
                }
              } else {
                ref2 = _this.parameters;
                for (name in ref2) {
                  param = ref2[name];
                  if (_this.verbose) {
                    if (param.value === tds.TYPES.Null) {
                      _this._log("   " + (param.io === 1 ? " input" : "output") + ": @" + param.name + ", null");
                    } else {
                      _this._log("   " + (param.io === 1 ? " input" : "output") + ": @" + param.name + ", " + (param.type.declaration.toLowerCase()) + ", " + param.value);
                    }
                  }
                  if (param.io === 1) {
                    req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), {
                      length: param.length,
                      scale: param.scale,
                      precision: param.precision
                    });
                  } else {
                    req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), {
                      length: param.length,
                      scale: param.scale,
                      precision: param.precision
                    });
                  }
                }
              }
              if (_this.verbose) {
                _this._log("---------- response -----------");
              }
              return connection[_this._isBatch ? 'execSqlBatch' : 'execSql'](req);
            } else {
              if (connection) {
                _this._release(connection);
              }
              return typeof callback === "function" ? callback(err) : void 0;
            }
          };
        })(this));
      };


      /*
      		Execute stored procedure with specified parameters.
       */

      TediousRequest.prototype.execute = function(procedure, callback) {
        var chunksBuffer, columns, errorHandlers, errors, handleError, hasReturned, isChunkedRecordset, recordset, recordsets, returnValue, started;
        columns = {};
        recordset = [];
        recordsets = [];
        returnValue = 0;
        started = Date.now();
        errors = [];
        isChunkedRecordset = false;
        chunksBuffer = null;
        hasReturned = false;
        errorHandlers = {};
        handleError = (function(_this) {
          return function(doReturn, connection, info) {
            var e, err, event, handler;
            err = new Error(info.message);
            err.info = info;
            e = RequestError(err, 'EREQUEST');
            if (_this.stream) {
              _this.emit('error', e);
            } else {
              if (doReturn && !hasReturned) {
                if (connection != null) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                }
                hasReturned = true;
                if (typeof callback === "function") {
                  callback(e);
                }
              }
            }
            return errors.push(e);
          };
        })(this);
        return this._acquire((function(_this) {
          return function(err, connection) {
            var name, param, ref1, req;
            if (!err) {
              if (_this.verbose) {
                _this._log("---------- sql execute --------\n     proc: " + procedure);
              }
              if (_this.canceled) {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                _this._release(connection);
                return typeof callback === "function" ? callback(new RequestError("Canceled.", 'ECANCEL')) : void 0;
              }
              _this._cancel = function() {
                if (_this.verbose) {
                  _this._log("---------- canceling ----------");
                }
                return connection.cancel();
              };
              errorHandlers['errorMessage'] = handleError.bind(void 0, false, connection);
              errorHandlers['error'] = handleError.bind(void 0, true, connection);
              connection.on('errorMessage', errorHandlers['errorMessage']);
              connection.on('error', errorHandlers['error']);
              req = new tds.Request(procedure, bindDomain(function(err) {
                var elapsed, error, event, handler, i, len, ref1;
                if (err && err.message !== ((ref1 = errors[errors.length - 1]) != null ? ref1.message : void 0)) {
                  err = RequestError(err, 'EREQUEST');
                  if (_this.stream) {
                    _this.emit('error', err);
                  }
                  errors.push(err);
                }
                if (_this.verbose) {
                  if (errors.length) {
                    for (i = 0, len = errors.length; i < len; i++) {
                      error = errors[i];
                      _this._log("    error: " + error);
                    }
                  }
                  elapsed = Date.now() - started;
                  _this._log("   return: " + returnValue);
                  _this._log(" duration: " + elapsed + "ms");
                  _this._log("---------- completed ----------");
                }
                _this._cancel = null;
                if (errors.length && !_this.stream) {
                  error = errors.pop();
                  error.precedingErrors = errors;
                }
                if (!hasReturned) {
                  for (event in errorHandlers) {
                    handler = errorHandlers[event];
                    connection.removeListener(event, handler);
                  }
                  _this._release(connection);
                  hasReturned = true;
                  if (_this.stream) {
                    return callback(null, null, returnValue);
                  } else {
                    recordsets.returnValue = returnValue;
                    return typeof callback === "function" ? callback(error, recordsets, returnValue) : void 0;
                  }
                }
              }));
              req.on('columnMetadata', function(metadata) {
                var ref1;
                columns = createColumns(metadata);
                isChunkedRecordset = false;
                if (metadata.length === 1 && ((ref1 = metadata[0].colName) === JSON_COLUMN_ID || ref1 === XML_COLUMN_ID)) {
                  isChunkedRecordset = true;
                  chunksBuffer = [];
                }
                if (_this.stream) {
                  return _this.emit('recordset', columns);
                }
              });
              req.on('row', function(columns) {
                var col, exi, i, len, row;
                if (!recordset) {
                  recordset = [];
                }
                if (isChunkedRecordset) {
                  return chunksBuffer.push(columns[0].value);
                } else {
                  row = {};
                  for (i = 0, len = columns.length; i < len; i++) {
                    col = columns[i];
                    col.value = valueCorrection(col.value, col.metadata);
                    exi = row[col.metadata.colName];
                    if (exi != null) {
                      if (exi instanceof Array) {
                        exi.push(col.value);
                      } else {
                        row[col.metadata.colName] = [exi, col.value];
                      }
                    } else {
                      row[col.metadata.colName] = col.value;
                    }
                  }
                  if (_this.verbose) {
                    _this._log(util.inspect(row));
                    _this._log("---------- --------------------");
                  }
                  if (_this.stream) {
                    return _this.emit('row', row);
                  } else {
                    return recordset.push(row);
                  }
                }
              });
              req.on('doneInProc', function(rowCount, more) {
                var error1, ex, row;
                if (Object.keys(columns).length === 0) {
                  if (rowCount > 0) {
                    _this.rowsAffected += rowCount;
                  }
                  return;
                }
                if (isChunkedRecordset) {
                  if (columns[JSON_COLUMN_ID] && _this.connection.config.parseJSON === true) {
                    try {
                      row = JSON.parse(chunksBuffer.join(''));
                    } catch (error1) {
                      ex = error1;
                      row = null;
                      ex = RequestError(new Error("Failed to parse incoming JSON. " + ex.message), 'EJSON');
                      if (_this.stream) {
                        _this.emit('error', ex);
                      }
                      errors.push(ex);
                    }
                  } else {
                    row = {};
                    row[Object.keys(columns)[0]] = chunksBuffer.join('');
                  }
                  chunksBuffer = null;
                  if (_this.verbose) {
                    _this._log(util.inspect(row));
                    _this._log("---------- --------------------");
                  }
                  if (_this.stream) {
                    _this.emit('row', row);
                  } else {
                    recordset.push(row);
                  }
                }
                if (!_this.stream) {
                  Object.defineProperty(recordset, 'columns', {
                    enumerable: false,
                    value: columns
                  });
                  Object.defineProperty(recordset, 'toTable', {
                    enumerable: false,
                    value: function() {
                      return Table.fromRecordset(this);
                    }
                  });
                  recordsets.push(recordset);
                }
                recordset = [];
                return columns = {};
              });
              req.on('doneProc', function(rowCount, more, returnStatus) {
                return returnValue = returnStatus;
              });
              req.on('returnValue', function(parameterName, value, metadata) {
                if (_this.verbose) {
                  if (value === tds.TYPES.Null) {
                    _this._log("   output: @" + parameterName + ", null");
                  } else {
                    _this._log("   output: @" + parameterName + ", " + (_this.parameters[parameterName].type.declaration.toLowerCase()) + ", " + value);
                  }
                }
                return _this.parameters[parameterName].value = value === tds.TYPES.Null ? null : value;
              });
              ref1 = _this.parameters;
              for (name in ref1) {
                param = ref1[name];
                if (_this.verbose) {
                  if (param.value === tds.TYPES.Null) {
                    _this._log("   " + (param.io === 1 ? " input" : "output") + ": @" + param.name + ", null");
                  } else {
                    _this._log("   " + (param.io === 1 ? " input" : "output") + ": @" + param.name + ", " + (param.type.declaration.toLowerCase()) + ", " + param.value);
                  }
                }
                if (param.io === 1) {
                  req.addParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), {
                    length: param.length,
                    scale: param.scale,
                    precision: param.precision
                  });
                } else {
                  req.addOutputParameter(param.name, getTediousType(param.type), parameterCorrection(param.value), {
                    length: param.length,
                    scale: param.scale,
                    precision: param.precision
                  });
                }
              }
              if (_this.verbose) {
                _this._log("---------- response -----------");
              }
              return connection.callProcedure(req);
            } else {
              if (connection) {
                _this._release(connection);
              }
              return typeof callback === "function" ? callback(err) : void 0;
            }
          };
        })(this));
      };


      /*
      		Cancel currently executed request.
       */

      TediousRequest.prototype.cancel = function() {
        if (this._cancel) {
          return this._cancel();
        }
        return true;
      };

      return TediousRequest;

    })(Request);
    return {
      Connection: TediousConnection,
      Transaction: TediousTransaction,
      Request: TediousRequest,
      fix: function() {}
    };
  };

}).call(this);
