'use strict';

/* jshint -W030 */
var chai = require('chai')
  , Sequelize = require('../../../../index')
  , expect = chai.expect
  , Support = require(__dirname + '/../../support')
  , DataTypes = require(__dirname + '/../../../../lib/data-types');

describe(Support.getTestDialectTeaser('Model'), function() {
  describe('create', function() {
    describe('include', function() {
      it('should create data for BelongsTo relations', function() {
        var Product = this.sequelize.define('Product', {
          title: Sequelize.STRING
        });
        var User = this.sequelize.define('User', {
          first_name: Sequelize.STRING,
          last_name: Sequelize.STRING
        });

        Product.belongsTo(User);

        return this.sequelize.sync({ force: true }).then(function() {
          return Product.create({
            title: 'Chair',
            User: {
              first_name: 'Mick',
              last_name: 'Broadstone'
            }
          }, {
            include: [ User ]
          }).then(function(savedProduct) {
            return Product.findOne({
              where: { id: savedProduct.id },
              include: [ User ]
            }).then(function(persistedProduct) {
              expect(persistedProduct.User).to.be.ok;
              expect(persistedProduct.User.first_name).to.be.equal('Mick');
              expect(persistedProduct.User.last_name).to.be.equal('Broadstone');
            });
          });
        });
      });

      it('should create data for BelongsTo relations with alias', function() {
        var Product = this.sequelize.define('Product', {
          title: Sequelize.STRING
        });
        var User = this.sequelize.define('User', {
          first_name: Sequelize.STRING,
          last_name: Sequelize.STRING
        });

        var Creator = Product.belongsTo(User, {as: 'creator'});

        return this.sequelize.sync({ force: true }).then(function() {
          return Product.create({
            title: 'Chair',
            creator: {
              first_name: 'Matt',
              last_name: 'Hansen'
            }
          }, {
            include: [ Creator ]
          }).then(function(savedProduct) {
            return Product.findOne({
              where: { id: savedProduct.id },
              include: [ Creator ]
            }).then(function(persistedProduct) {
              expect(persistedProduct.creator).to.be.ok;
              expect(persistedProduct.creator.first_name).to.be.equal('Matt');
              expect(persistedProduct.creator.last_name).to.be.equal('Hansen');
            });
          });
        });
      });

      it('should create data for HasMany relations', function() {
        var Product = this.sequelize.define('Product', {
          title: Sequelize.STRING
        });
        var Tag = this.sequelize.define('Tag', {
          name: Sequelize.STRING
        });

        Product.hasMany(Tag);

        return this.sequelize.sync({ force: true }).then(function() {
          return Product.create({
            id: 1,
            title: 'Chair',
            Tags: [
              {id: 1, name: 'Alpha'},
              {id: 2, name: 'Beta'}
            ]
          }, {
            include: [ Tag ]
          }).then(function(savedProduct) {
            return Product.find({
              where: { id: savedProduct.id },
              include: [ Tag ]
            }).then(function(persistedProduct) {
              expect(persistedProduct.Tags).to.be.ok;
              expect(persistedProduct.Tags.length).to.equal(2);
            });
          });
        });
      });

      it('should create data for HasMany relations with alias', function() {
        var Product = this.sequelize.define('Product', {
          title: Sequelize.STRING
        });
        var Tag = this.sequelize.define('Tag', {
          name: Sequelize.STRING
        });

        var Categories = Product.hasMany(Tag, {as: 'categories'});

        return this.sequelize.sync({ force: true }).then(function() {
          return Product.create({
            id: 1,
            title: 'Chair',
            categories: [
              {id: 1, name: 'Alpha'},
              {id: 2, name: 'Beta'}
            ]
          }, {
            include: [ Categories ]
          }).then(function(savedProduct) {
            return Product.find({
              where: { id: savedProduct.id },
              include: [ Categories ]
            }).then(function(persistedProduct) {
              expect(persistedProduct.categories).to.be.ok;
              expect(persistedProduct.categories.length).to.equal(2);
            });
          });
        });
      });

      it('should create data for HasOne relations', function() {
        var User = this.sequelize.define('User', {
          username: Sequelize.STRING
        });

        var Task = this.sequelize.define('Task', {
          title: Sequelize.STRING
        });

        User.hasOne(Task);

        return this.sequelize.sync({ force: true }).then(function() {
          return User.create({
            username: 'Muzzy',
            Task: {
              title: 'Eat Clocks'
            }
          }, {
            include: [ Task ]
          }).then(function(savedUser) {
            return User.find({
              where: { id: savedUser.id },
              include: [ Task ]
            }).then(function(persistedUser) {
              expect(persistedUser.Task).to.be.ok;
            });
          });
        });
      });

      it('should create data for HasOne relations with alias', function() {
        var User = this.sequelize.define('User', {
          username: Sequelize.STRING
        });

        var Task = this.sequelize.define('Task', {
          title: Sequelize.STRING
        });

        var Job = User.hasOne(Task, {as: 'job'});

        return this.sequelize.sync({ force: true }).then(function() {
          return User.create({
            username: 'Muzzy',
            job: {
              title: 'Eat Clocks'
            }
          }, {
            include: [ Job ]
          }).then(function(savedUser) {
            return User.find({
              where: { id: savedUser.id },
              include: [ Job ]
            }).then(function(persistedUser) {
              expect(persistedUser.job).to.be.ok;
            });
          });
        });
      });

      it('should create data for BelongsToMany relations', function() {
        var User = this.sequelize.define('User', {
          username: DataTypes.STRING
        });

        var Task = this.sequelize.define('Task', {
          title: DataTypes.STRING,
          active: DataTypes.BOOLEAN
        });

        User.belongsToMany(Task, {through: 'user_task'});
        Task.belongsToMany(User, {through: 'user_task'});

        return this.sequelize.sync({ force: true }).then(function() {
          return User.create({
            username: 'John',
            Tasks: [
              { title: 'Get rich', active: true },
              { title: 'Die trying', active: false }
            ]
          }, {
            include: [ Task ]
          }).then(function(savedUser) {
            return User.find({
              where: { id: savedUser.id },
              include: [ Task ]
            }).then(function(persistedUser) {
              expect(persistedUser.Tasks).to.be.ok;
              expect(persistedUser.Tasks.length).to.equal(2);
            });
          });
        });
      });

      it('should create data for BelongsToMany relations with alias', function() {
        var User = this.sequelize.define('User', {
          username: DataTypes.STRING
        });

        var Task = this.sequelize.define('Task', {
          title: DataTypes.STRING,
          active: DataTypes.BOOLEAN
        });

        var Jobs = User.belongsToMany(Task, {through: 'user_job', as: 'jobs'});
        Task.belongsToMany(User, {through: 'user_job'});

        return this.sequelize.sync({ force: true }).then(function() {
          return User.create({
            username: 'John',
            jobs: [
              { title: 'Get rich', active: true },
              { title: 'Die trying', active: false }
            ]
          }, {
            include: [ Jobs ]
          }).then(function(savedUser) {
            return User.find({
              where: { id: savedUser.id },
              include: [ Jobs ]
            }).then(function(persistedUser) {
              expect(persistedUser.jobs).to.be.ok;
              expect(persistedUser.jobs.length).to.equal(2);
            });
          });
        });
      });
    });
  });
});


, sequelizeErrors = require('../../errors')

, dataTypes = require('../../data-types');

ConnectionManager = function(dialect, sequelize) {

# Class Errors
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L11)

Sequelize provides a host of custom error classes, to allow you to do easier debugging. All of these errors are exposed on the sequelize object and the sequelize constructor.
All sequelize errors inherit from the base JS error object.

***

## `new BaseError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L20)

The Base Error all Sequelize Errors inherit from.
__Aliases:__ Error

***

## `new ValidationError(message, [errors])`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L41)

Validation Error. Thrown when the sequelize validation has failed. The error contains an `errors` property,
which is an array with 1 or more ValidationErrorItems, one for each validation that failed.

**Params:**

| Name | Type | Description |
| ---- | ---- | ----------- |
| message | string | Error message |
| [errors] | Array | Array of ValidationErrorItem objects describing the validation errors |

__Extends:__ BaseError

***

## `errors`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L49)

An array of ValidationErrorItems

***

## `get(path)`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L70)

Gets all validation error items for the path / field specified.

**Params:**

| Name | Type | Description |
| ---- | ---- | ----------- |
| path | string | The path to be checked for error items |

***

## `new DatabaseError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L84)

A base class for all database related errors.

__Extends:__ BaseError

***

## `parent`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L92)

The database specific error which triggered this one

***

## `sql`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L98)

The SQL that triggered the error

***

## `message()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L104)

The message from the DB.

***

## `fields()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L109)

The fields of the unique constraint

***

## `value()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L114)

The value(s) which triggered the error

***

## `index()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L119)

The name of the index that triggered the error

***

## `new TimeoutError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L127)

Thrown when a database query times out because of a deadlock

__Extends:__ DatabaseError

***

## `new UniqueConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L138)

Thrown when a unique constraint is violated in the database

__Extends:__ DatabaseError

***

## `new ForeignKeyConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L157)

Thrown when a foreign key constraint is violated in the database

__Extends:__ DatabaseError

***

## `new ExclusionConstraintError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L177)

Thrown when an exclusion constraint is violated in the database

__Extends:__ DatabaseError

***

## `new ValidationErrorItem(message, type, path, value)`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L201)

Validation Error Item
Instances of this class are included in the `ValidationError.errors` property.

**Params:**

| Name | Type | Description |
| ---- | ---- | ----------- |
| message | string | An error message |
| type | string | The type of the validation error |
| path | string | The field that triggered the validation error |
| value | string | The value that generated the error |

***

## `new ConnectionError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L213)

A base class for all connection related errors.

__Extends:__ BaseError

***

## `parent`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L220)

The connection specific error which triggered this one

***

## `new ConnectionRefusedError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L230)

Thrown when a connection to a database is refused

__Extends:__ ConnectionError

***

## `new AccessDeniedError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L241)

Thrown when a connection to a database is refused due to insufficient privileges

__Extends:__ ConnectionError

***

## `new HostNotFoundError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L252)

Thrown when a connection to a database has a hostname that was not found

__Extends:__ ConnectionError

***

## `new HostNotReachableError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L263)

Thrown when a connection to a database has a hostname that was not reachable

__Extends:__ ConnectionError

***

## `new InvalidConnectionError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L274)

Thrown when a connection to a database has invalid values for any of the connection parameters

__Extends:__ ConnectionError

***

## `new ConnectionTimedOutError()`
[View code](https://github.com/sequelize/sequelize/blob/0de404640d4c71e2d1f1259356650dfb586a248b/lib/errors.js#L285)

Thrown when a connection to a database times out

__Extends:__ ConnectionError

***

_This document is automatically generated based on source code comments. Please do not edit it directly, as your changes will be ignored. Please write on IRC, open an issue or a create a pull request if you feel something can be improved. For help on how to write source code documentation see [JSDoc](http://usejsdoc.org) and [dox](https://github.com/tj/dox)_


keep keep keep keep keep replace keep keep keep

var isZone = !!moment.tz.zone(self.sequelize.options.timezone);

query += 'SET client_min_messages TO warning; SET TIME ZONE \'' + self.sequelize.options.timezone + '\'';

'use strict';

var AbstractConnectionManager = require('../abstract/connection-manager')
  , ConnectionManager
  , Utils = require('../../utils')
  , Promise = require('../../promise')
  , sequelizeErrors = require('../../errors')
  , semver = require('semver')
  , dataTypes = require('../../data-types');

ConnectionManager = function(dialect, sequelize) {
  AbstractConnectionManager.call(this, dialect, sequelize);

  this.sequelize = sequelize;
  this.sequelize.config.port = this.sequelize.config.port || 5432;
  try {
    this.lib = sequelize.config.native ? require(sequelize.config.dialectModulePath || 'pg').native : require(sequelize.config.dialectModulePath || 'pg');
  } catch (err) {
    throw new Error('Please install \'' + (sequelize.config.dialectModulePath || 'pg') + '\' module manually');
  }

  this.refreshTypeParser(dataTypes.postgres);
};

Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);

// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.$refreshTypeParser = function (dataType) {
  var self = this;

  if (dataType.types.postgres.oids) {
    dataType.types.postgres.oids.forEach(function (oid) {
      self.lib.types.setTypeParser(oid, function (value) {
        return dataType.parse(value, oid, self.lib.types.getTypeParser);
      });
    });
  }

  if (dataType.types.postgres.array_oids) {
    dataType.types.postgres.array_oids.forEach(function (oid) {
      self.lib.types.setTypeParser(oid, function (value) {
        return self.lib.types.arrayParser.create(value, function (value) {
          return dataType.parse(value, oid, self.lib.types.getTypeParser);
        }).parse();
      });
    });
  }
};

ConnectionManager.prototype.connect = function(config) {
  var self = this
    , connectionConfig = {};

  config.user = config.username;
  connectionConfig = Utils._.pick(config, [
    'user', 'password', 'host', 'database', 'port'
  ]);

  if (config.dialectOptions) {
    Utils._.merge(connectionConfig,
                  Utils._.pick(config.dialectOptions, [
      // see [http://www.postgresql.org/docs/9.3/static/runtime-config-logging.html#GUC-APPLICATION-NAME]
      'application_name',
      // choose the SSL mode with the PGSSLMODE environment variable
      // object format: [https://github.com/brianc/node-postgres/blob/master/lib/connection.js#L79]
      // see also [http://www.postgresql.org/docs/9.3/static/libpq-ssl.html]
      'ssl',
      // In addition to the values accepted by the corresponding server,
      // you can use "auto" to determine the right encoding from the
      // current locale in the client (LC_CTYPE environment variable on Unix systems)
      'client_encoding',
      // !! DONT SET THIS TO TRUE !!
      // (unless you know what you're doing)
      // see [http://www.postgresql.org/message-id/flat/bc9549a50706040852u27633f41ib1e6b09f8339d845@mail.gmail.com#bc9549a50706040852u27633f41ib1e6b09f8339d845@mail.gmail.com]
      'binary'
    ]));
  }

  return new Promise(function (resolve, reject) {
    var connection = new self.lib.Client(connectionConfig)
      , responded = false;

    connection.connect(function(err) {
      if (err) {
        if (err.code) {
          switch (err.code) {
          case 'ECONNREFUSED':
            reject(new sequelizeErrors.ConnectionRefusedError(err));
            break;
          case 'ENOTFOUND':
            reject(new sequelizeErrors.HostNotFoundError(err));
            break;
          case 'EHOSTUNREACH':
            reject(new sequelizeErrors.HostNotReachableError(err));
            break;
          case 'EINVAL':
            reject(new sequelizeErrors.InvalidConnectionError(err));
            break;
          default:
            reject(new sequelizeErrors.ConnectionError(err));
            break;
          }
        } else {
          reject(new sequelizeErrors.ConnectionError(err));
        }
        return;
      }
      responded = true;
      resolve(connection);
    });

    // If we didn't ever hear from the client.connect() callback the connection timeout, node-postgres does not treat this as an error since no active query was ever emitted
    connection.on('end', function () {
      if (!responded) {
        reject(new sequelizeErrors.ConnectionTimedOutError(new Error('Connection timed out')));
      }
    });

    // Don't let a Postgres restart (or error) to take down the whole app
    connection.on('error', function() {
      connection._invalid = true;
    });
  }).tap(function (connection) {
    // Disable escape characters in strings, see https://github.com/sequelize/sequelize/issues/3545
    var query = '';

    if (self.sequelize.options.databaseVersion !== 0 && semver.gte(self.sequelize.options.databaseVersion, '8.2.0')) {
      query += 'SET standard_conforming_strings=on;';
    }

    if (!self.sequelize.config.keepDefaultTimezone) {
      query += 'SET client_min_messages TO warning; SET TIME ZONE INTERVAL \'' + self.sequelize.options.timezone + '\' HOUR TO MINUTE;';
    }

    // oids for hstore and geometry are dynamic - so select them at connection time
    if (dataTypes.HSTORE.types.postgres.oids.length === 0) {
      query += 'SELECT typname, oid, typarray FROM pg_type WHERE typtype = \'b\' AND typname IN (\'hstore\', \'geometry\')';
    }

    return new Promise(function (resolve, reject) {
      connection.query(query).on('error', function (err) {
        reject(err);
      }).on('row', function (row) {
        var type;
        if (row.typname === 'geometry') {
          type = dataTypes.postgres.GEOMETRY;
        } else if (row.typname === 'hstore') {
          type = dataTypes.postgres.HSTORE;
        }

        type.types.postgres.oids.push(row.oid);
        type.types.postgres.array_oids.push(row.typarray);

        self.$refreshTypeParser(type);
      }).on('end', function () {
        resolve();
      });
    });
  });
};
ConnectionManager.prototype.disconnect = function(connection) {
  return new Promise(function (resolve, reject) {
    connection.end();
    resolve();
  });
};

ConnectionManager.prototype.validate = function(connection) {
  return connection._invalid === undefined;
};

module.exports = ConnectionManager;


'use strict';

var AbstractConnectionManager = require('../abstract/connection-manager')
  , ConnectionManager
  , Utils = require('../../utils')
  , Promise = require('../../promise')
  , dataTypes = require('../../data-types').sqlite
  , sequelizeErrors = require('../../errors')
  , parserStore = require('../parserStore')('sqlite');

ConnectionManager = function(dialect, sequelize) {
  this.sequelize = sequelize;
  this.config = sequelize.config;
  this.dialect = dialect;
  this.dialectName = this.sequelize.options.dialect;
  this.connections = {};

  // We attempt to parse file location from a connection uri but we shouldn't match sequelize default host.
  if (this.sequelize.options.host === 'localhost') delete this.sequelize.options.host;

  try {
    this.lib = require(sequelize.config.dialectModulePath || 'sqlite3').verbose();
  } catch (err) {
    throw new Error('Please install sqlite3 package manually');
  }

  this.refreshTypeParser(dataTypes);
};

Utils._.extend(ConnectionManager.prototype, AbstractConnectionManager.prototype);

// Expose this as a method so that the parsing may be updated when the user has added additional, custom types
ConnectionManager.prototype.$refreshTypeParser = function (dataType) {
  parserStore.refresh(dataType);
};

ConnectionManager.prototype.$clearTypeParser = function () {
  parserStore.clear();
};

ConnectionManager.prototype.getConnection = function(options) {
  var self = this;
  options = options || {};
  options.uuid = options.uuid || 'default';
  options.inMemory = ((self.sequelize.options.storage || self.sequelize.options.host || ':memory:') === ':memory:') ? 1 : 0;

  var dialectOptions = self.sequelize.options.dialectOptions;
  options.readWriteMode = dialectOptions && dialectOptions.mode;

  if (self.connections[options.inMemory || options.uuid]) {
    return Promise.resolve(self.connections[options.inMemory || options.uuid]);
  }

  return new Promise(function (resolve, reject) {
    self.connections[options.inMemory || options.uuid] = new self.lib.Database(
      self.sequelize.options.storage || self.sequelize.options.host || ':memory:',
      options.readWriteMode || (self.lib.OPEN_READWRITE | self.lib.OPEN_CREATE), // default mode
      function(err) {
        if (err) {
          if (err.code === 'SQLITE_CANTOPEN') return reject(new sequelizeErrors.ConnectionError(err));
          return reject(new sequelizeErrors.ConnectionError(err));
        }
        resolve(self.connections[options.inMemory || options.uuid]);
      }
    );
  }).tap(function (connection) {
    if (self.sequelize.options.foreignKeys !== false) {
      // Make it possible to define and use foreign key constraints unless
      // explicitly disallowed. It's still opt-in per relation
      connection.run('PRAGMA FOREIGN_KEYS=ON');
    }
  });
};

ConnectionManager.prototype.releaseConnection = function(connection, force) {
  if (connection.filename === ':memory:' && force !== true) return;

  if (connection.uuid) {
    connection.close();
    delete this.connections[connection.uuid];
  }
};

module.exports = ConnectionManager;


'use strict';

/* jshint -W030 */
var chai = require('chai')
  , sinon = require('sinon')
  , Sequelize = require('../../../index')
  , expect = chai.expect
  , Support = require(__dirname + '/../support')
  , dialect = Support.getTestDialect()
  , DataTypes = require(__dirname + '/../../../lib/data-types')
  , config = require(__dirname + '/../../config/config')
  , current = Support.sequelize;

describe(Support.getTestDialectTeaser('Instance'), function() {
  describe('update', function() {
    beforeEach(function () {
      this.User = this.sequelize.define('User', {
        username: { type: DataTypes.STRING },
        uuidv1: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV1 },
        uuidv4: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4 },
        touchedAt: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
        aNumber: { type: DataTypes.INTEGER },
        bNumber: { type: DataTypes.INTEGER },
        aDate: { type: DataTypes.DATE },

        validateTest: {
          type: DataTypes.INTEGER,
          allowNull: true,
          validate: {isInt: true}
        },
        validateCustom: {
          type: DataTypes.STRING,
          allowNull: true,
          validate: {len: {msg: 'Length failed.', args: [1, 20]}}
        },
        validateSideEffect: {
          type: DataTypes.VIRTUAL,
          allowNull: true,
          validate: {isInt: true},
          set: function (val) {
            this.setDataValue('validateSideEffect', val);
            this.setDataValue('validateSideAffected', val*2);
          }
        },
        validateSideAffected: {
          type: DataTypes.INTEGER,
          allowNull: true,
          validate: {isInt: true}
        },

        dateAllowNullTrue: {
          type: DataTypes.DATE,
          allowNull: true
        }
      });
      return this.User.sync({ force: true });
    });

    if (current.dialect.supports.transactions) {
      it('supports transactions', function() {
        return Support.prepareTransactionTest(this.sequelize).bind({}).then(function(sequelize) {
          var User = sequelize.define('User', { username: Support.Sequelize.STRING });

          return User.sync({ force: true }).then(function() {
            return User.create({ username: 'foo' }).then(function(user) {
              return sequelize.transaction().then(function(t) {
                return user.update({ username: 'bar' }, { transaction: t }).then(function() {
                  return User.findAll().then(function(users1) {
                    return User.findAll({ transaction: t }).then(function(users2) {
                      expect(users1[0].username).to.equal('foo');
                      expect(users2[0].username).to.equal('bar');
                      return t.rollback();
                    });
                  });
                });
              });
            });
          });
        });
      });
    }

    it('should update fields that are not specified on create', function() {
      var User = this.sequelize.define('User' + config.rand(), {
        name: DataTypes.STRING,
        bio: DataTypes.TEXT,
        email: DataTypes.STRING
      });

     return User.sync({force: true}).then(function() {
        return User.create({
          name: 'snafu',
          email: 'email'
        }, {
          fields: ['name', 'email']
        }).then(function(user) {
          return user.update({bio: 'swag'});
        }).then(function(user) {
          return user.reload();
        }).then(function(user) {
          expect(user.get('name')).to.equal('snafu');
          expect(user.get('email')).to.equal('email');
          expect(user.get('bio')).to.equal('swag');
        });
      });
    });

    it('should succeed in updating when values are unchanged (without timestamps)', function() {
      var User = this.sequelize.define('User' + config.rand(), {
        name: DataTypes.STRING,
        bio: DataTypes.TEXT,
        email: DataTypes.STRING
      }, {
        timestamps: false
      });

     return User.sync({force: true}).then(function() {
        return User.create({
          name: 'snafu',
          email: 'email'
        }, {
          fields: ['name', 'email']
        }).then(function(user) {
          return user.update({
            name: 'snafu',
            email: 'email'
          });
        }).then(function(user) {
          return user.reload();
        }).then(function(user) {
          expect(user.get('name')).to.equal('snafu');
          expect(user.get('email')).to.equal('email');
        });
      });
    });

    it('should only save passed attributes', function () {
      var user = this.User.build();
      return user.save().then(function () {
        user.set('validateTest', 5);
        expect(user.changed('validateTest')).to.be.ok;
        return user.update({
          validateCustom: '1'
        });
      }).then(function () {
        expect(user.changed('validateTest')).to.be.ok;
        expect(user.validateTest).to.be.equal(5);
      }).then(function () {
        return user.reload();
      }).then(function () {
        expect(user.validateTest).to.not.be.equal(5);
      });
    });

    it('should save attributes affected by setters', function () {
      var user = this.User.build();
      return user.update({validateSideEffect: 5}).then(function () {
        expect(user.validateSideEffect).to.be.equal(5);
      }).then(function () {
        return user.reload();
      }).then(function () {
        expect(user.validateSideAffected).to.be.equal(10);
        expect(user.validateSideEffect).not.to.be.ok;
      });
    });

    describe('hooks', function () {
      it('should update attributes added in hooks when default fields are used', function () {
        var User = this.sequelize.define('User' + config.rand(), {
          name: DataTypes.STRING,
          bio: DataTypes.TEXT,
          email: DataTypes.STRING
        });

        User.beforeUpdate(function(instance) {
          instance.set('email', 'B');
        });

        return User.sync({force: true}).then(function() {
          return User.create({
            name: 'A',
            bio: 'A',
            email: 'A'
          }).then(function (user) {
            return user.update({
              name: 'B',
              bio: 'B'
            });
          }).then(function () {
            return User.findOne({});
          }).then(function (user) {
            expect(user.get('name')).to.equal('B');
            expect(user.get('bio')).to.equal('B');
            expect(user.get('email')).to.equal('B');
          });
        });
      });

      it('should update attributes changed in hooks when default fields are used', function () {
        var User = this.sequelize.define('User' + config.rand(), {
          name: DataTypes.STRING,
          bio: DataTypes.TEXT,
          email: DataTypes.STRING
        });

        User.beforeUpdate(function(instance) {
          instance.set('email', 'C');
        });

        return User.sync({force: true}).then(function() {
          return User.create({
            name: 'A',
            bio: 'A',
            email: 'A'
          }).then(function (user) {
            return user.update({
              name: 'B',
              bio: 'B',
              email: 'B'
            });
          }).then(function () {
            return User.findOne({});
          }).then(function (user) {
            expect(user.get('name')).to.equal('B');
            expect(user.get('bio')).to.equal('B');
            expect(user.get('email')).to.equal('C');
          });
        });
      });

      it('should validate attributes added in hooks when default fields are used', function () {
        var User = this.sequelize.define('User' + config.rand(), {
          name: DataTypes.STRING,
          bio: DataTypes.TEXT,
          email: {
            type: DataTypes.STRING,
            validate: {
              isEmail: true
            }
          }
        });

        User.beforeUpdate(function(instance) {
          instance.set('email', 'B');
        });

        return User.sync({force: true}).then(function () {
          return User.create({
            name: 'A',
            bio: 'A',
            email: 'valid.email@gmail.com'
          }).then(function (user) {
            return expect(user.update({
              name: 'B'
            })).to.be.rejectedWith(Sequelize.ValidationError);
          }).then(function () {
            return User.findOne({}).then(function (user) {
              expect(user.get('email')).to.equal('valid.email@gmail.com');
            });
          });
        });
      });

      it('should validate attributes changed in hooks when default fields are used', function () {
        var User = this.sequelize.define('User' + config.rand(), {
          name: DataTypes.STRING,
          bio: DataTypes.TEXT,
          email: {
            type: DataTypes.STRING,
            validate: {
              isEmail: true
            }
          }
        });

        User.beforeUpdate(function(instance) {
          instance.set('email', 'B');
        });

        return User.sync({force: true}).then(function () {
          return User.create({
            name: 'A',
            bio: 'A',
            email: 'valid.email@gmail.com'
          }).then(function (user) {
            return expect(user.update({
              name: 'B',
              email: 'still.valid.email@gmail.com'
            })).to.be.rejectedWith(Sequelize.ValidationError);
          }).then(function () {
            return User.findOne({}).then(function (user) {
              expect(user.get('email')).to.equal('valid.email@gmail.com');
            });
          });
        });
      });
    });

    it('should not set attributes that are not specified by fields', function () {
      var User = this.sequelize.define('User' + config.rand(), {
        name: DataTypes.STRING,
        bio: DataTypes.TEXT,
        email: DataTypes.STRING
      });

     return User.sync({force: true}).then(function() {
        return User.create({
          name: 'snafu',
          email: 'email'
        }).then(function(user) {
          return user.update({
            bio: 'heyo',
            email: 'heho'
          }, {
            fields: ['bio']
          });
        }).then(function(user) {
          expect(user.get('name')).to.equal('snafu');
          expect(user.get('email')).to.equal('email');
          expect(user.get('bio')).to.equal('heyo');
        });
      });
    });

    it('updates attributes in the database', function() {
      return this.User.create({ username: 'user' }).then(function(user) {
        expect(user.username).to.equal('user');
        return user.update({ username: 'person' }).then(function(user) {
          expect(user.username).to.equal('person');
        });
      });
    });

    it('ignores unknown attributes', function() {
      return this.User.create({ username: 'user' }).then(function(user) {
        return user.update({ username: 'person', foo: 'bar'}).then(function(user) {
          expect(user.username).to.equal('person');
          expect(user.foo).not.to.exist;
        });
      });
    });

    it('doesn\'t update primary keys or timestamps', function() {
      var User = this.sequelize.define('User' + config.rand(), {
        name: DataTypes.STRING,
        bio: DataTypes.TEXT,
        identifier: {type: DataTypes.STRING, primaryKey: true}
      });

      return User.sync({ force: true }).then(function() {
        return User.create({
          name: 'snafu',
          identifier: 'identifier'
        }).then(function(user) {
          var oldCreatedAt = user.createdAt
            , oldUpdatedAt = user.updatedAt
            , oldIdentifier = user.identifier;

          return this.sequelize.Promise.delay(1000).then(function() {
            return user.update({
              name: 'foobar',
              createdAt: new Date(2000, 1, 1),
              identifier: 'another identifier'
            }).then(function(user) {
              expect(new Date(user.createdAt)).to.equalDate(new Date(oldCreatedAt));
              expect(new Date(user.updatedAt)).to.not.equalTime(new Date(oldUpdatedAt));
              expect(user.identifier).to.equal(oldIdentifier);
            });
          });
        });
      });
    });

    it('stores and restores null values', function() {
      var Download = this.sequelize.define('download', {
        startedAt: DataTypes.DATE,
        canceledAt: DataTypes.DATE,
        finishedAt: DataTypes.DATE
      });

      return Download.sync().then(function() {
        return Download.create({
          startedAt: new Date()
        }).then(function(download) {
          expect(download.startedAt instanceof Date).to.be.true;
          expect(download.canceledAt).to.not.be.ok;
          expect(download.finishedAt).to.not.be.ok;

          return download.update({
            canceledAt: new Date()
          }).then(function(download) {
            expect(download.startedAt instanceof Date).to.be.true;
            expect(download.canceledAt instanceof Date).to.be.true;
            expect(download.finishedAt).to.not.be.ok;

            return Download.findAll({
              where: (dialect === 'postgres' || dialect === 'mssql' ? ['"finishedAt" IS NULL'] : ['`finishedAt` IS NULL'])
            }).then(function(downloads) {
              downloads.forEach(function(download) {
                expect(download.startedAt instanceof Date).to.be.true;
                expect(download.canceledAt instanceof Date).to.be.true;
                expect(download.finishedAt).to.not.be.ok;
              });
            });
          });
        });
      });
    });

    it('should support logging', function () {
      var spy = sinon.spy();

      return this.User.create({}).then(function (user) {
        return user.update({username: 'yolo'}, {logging: spy}).then(function () {
          expect(spy.called).to.be.ok;
        });
      });
    });
  });
});


keep keep keep replace keep keep keep keep

'use strict';

/* jshint -W030 */
var chai = require('chai')
  , expect = chai.expect
  , Support = require(__dirname + '/../../support')
  , dialect = Support.getTestDialect()
  , config = require(__dirname + '/../../../config/config')
  , DataTypes = require(__dirname + '/../../../../lib/data-types')
  , _ = require('lodash');

if (dialect.match(/^postgres/)) {
  describe('[POSTGRES Specific] Sequelize', function() {
    it('should correctly parse the moment based timezone', function() {
      var options = _.extend(this.sequelize.options, { timezone: 'Asia/Kolkata', timestamps: true });
      var sequelize = Support.createSequelizeInstance(options);

      var tzTable = sequelize.define('tz_table', { foo: DataTypes.STRING });
      return tzTable.sync({force: true}).then(function() {
        return tzTable.create({foo: 'test'}).then(function(row) {
          expect(row).to.be.not.null;
        }).catch(function(e) {
          expect(e).to.be.null;
        });
      })
    });
  });
}


## Syncing

`sequelize.sync()` will, based on your model definitions, create any missing tables.
If `force: true` it will first drop tables before recreating them.

## Migrations / Manual schema changes

Sequelize has a [sister library](https://github.com/sequelize/umzug) for handling execution and logging of migration tasks.
Sequelize provides a list of ways to programmatically create or change a table schema.

### createTable

### addColumn

### changeColumn

### removeColumn

### addIndex

### removeIndex

### addConstraint

### removeConstraint


'use strict';

var Support   = require(__dirname + '/../support')
  , expectsql = Support.expectsql
  , current   = Support.sequelize
  , sql       = current.dialect.QueryGenerator;

// Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation

if (current.dialect.name !== 'sqlite') {
  suite(Support.getTestDialectTeaser('SQL'), function() {
    suite('removeColumn', function () {
      test('schema', function () {
        expectsql(sql.removeColumnQuery({
          schema: 'archive',
          tableName: 'user'
        }, 'email'), {
          mssql: 'ALTER TABLE [archive].[user] DROP COLUMN [email];',
          mysql: 'ALTER TABLE `archive.user` DROP `email`;',
          postgres: 'ALTER TABLE "archive"."user" DROP COLUMN "email";',
        });
      });
    });
  });
}


describe('[POSTGRES Specific] Sequelize', function() {

it('should correctly parse the moment based timezone', function() {

var options = _.extend(this.sequelize.options, { timezone: 'Asia/Kolkata', timestamps: true });

var sequelize = Support.createSequelizeInstance(options);

keep keep keep keep replace keep keep keep

describe('[POSTGRES] Sequelize', function() {


'use strict';

module.exports = function(sequelize, DataTypes) {
  return sequelize.define('Project' + parseInt(Math.random() * 9999999999999999), {
     name: DataTypes.STRING
   });
};


'use strict';

/* jshint -W030 */
var chai = require('chai')
  , expect = chai.expect
  , Support = require(__dirname + '/../support')
  , DataTypes = require(__dirname + '/../../../lib/data-types')
  , Sequelize = require('../../../index')
  , moment = require('moment')
  , sinon = require('sinon')
  , Promise = Sequelize.Promise
  , current = Support.sequelize
  , dialect = Support.getTestDialect();

describe(Support.getTestDialectTeaser('HasMany'), function() {
  describe('Model.associations', function() {
    it('should store all assocations when associting to the same table multiple times', function() {
      var User = this.sequelize.define('User', {})
        , Group = this.sequelize.define('Group', {});

      Group.hasMany(User);
      Group.hasMany(User, { foreignKey: 'primaryGroupId', as: 'primaryUsers' });
      Group.hasMany(User, { foreignKey: 'secondaryGroupId', as: 'secondaryUsers' });

      expect(Object.keys(Group.associations)).to.deep.equal(['Users', 'primaryUsers', 'secondaryUsers']);
    });
  });

  describe('get', function () {
    if (current.dialect.supports.groupedLimit) {
      describe('multiple', function () {
        it('should fetch associations for multiple instances', function () {
          var User = this.sequelize.define('User', {})
            , Task = this.sequelize.define('Task', {});

          User.Tasks = User.hasMany(Task, {as: 'tasks'});

          return this.sequelize.sync({force: true}).then(function () {
            return Promise.join(
              User.create({
                id: 1,
                tasks: [
                  {},
                  {},
                  {}
                ]
              }, {
                include: [User.Tasks]
              }),
              User.create({
                id: 2,
                tasks: [
                  {}
                ]
              }, {
                include: [User.Tasks]
              }),
              User.create({
                id: 3
              })
            );
          }).then(function (users) {
            return User.Tasks.get(users).then(function (result) {
              expect(result[users[0].id].length).to.equal(3);
              expect(result[users[1].id].length).to.equal(1);
              expect(result[users[2].id].length).to.equal(0);
            });
          });
        });

        it('should fetch associations for multiple instances with limit and order', function () {
          var User = this.sequelize.define('User', {})
            , Task = this.sequelize.define('Task', {
                title: DataTypes.STRING
              });

          User.Tasks = User.hasMany(Task, {as: 'tasks'});

          return this.sequelize.sync({force: true}).then(function () {
            return Promise.join(
              User.create({
                tasks: [
                  {title: 'b'},
                  {title: 'd'},
                  {title: 'c'},
                  {title: 'a'}
                ]
              }, {
                include: [User.Tasks]
              }),
              User.create({
                tasks: [
                  {title: 'a'},
                  {title: 'c'},
                  {title: 'b'}
                ]
              }, {
                include: [User.Tasks]
              })
            );
          }).then(function (users) {
            return User.Tasks.get(users, {
              limit: 2,
              order: [
                ['title', 'ASC']
              ]
            }).then(function (result) {
              expect(result[users[0].id].length).to.equal(2);
              expect(result[users[0].id][0].title).to.equal('a');
              expect(result[users[0].id][1].title).to.equal('b');

              expect(result[users[1].id].length).to.equal(2);
              expect(result[users[1].id][0].title).to.equal('a');
              expect(result[users[1].id][1].title).to.equal('b');
            });
          });
        });

        it('should fetch associations for multiple instances with limit and order and a belongsTo relation', function () {
          var User = this.sequelize.define('User', {})
            , Task = this.sequelize.define('Task', {
                title: DataTypes.STRING,
                categoryId: {
                  type: DataTypes.INTEGER,
                  field: 'category_id'
                }
              })
            , Category = this.sequelize.define('Category', {});

          User.Tasks = User.hasMany(Task, {as: 'tasks'});
          Task.Category = Task.belongsTo(Category, {as: 'category', foreignKey: 'categoryId'});

          return this.sequelize.sync({force: true}).then(function () {
            return Promise.join(
              User.create({
                tasks: [
                  {title: 'b', category: {}},
                  {title: 'd', category: {}},
                  {title: 'c', category: {}},
                  {title: 'a', category: {}}
                ]
              }, {
                include: [{association: User.Tasks, include: [Task.Category]}]
              }),
              User.create({
                tasks: [
                  {title: 'a', category: {}},
                  {title: 'c', category: {}},
                  {title: 'b', category: {}}
                ]
              }, {
                include: [{association: User.Tasks, include: [Task.Category]}]
              })
            );
          }).then(function (users) {
            return User.Tasks.get(users, {
              limit: 2,
              order: [
                ['title', 'ASC']
              ],
              include: [Task.Category],
            }).then(function (result) {
              expect(result[users[0].id].length).to.equal(2);
              expect(result[users[0].id][0].title).to.equal('a');
              expect(result[users[0].id][0].category).to.be.ok;
              expect(result[users[0].id][1].title).to.equal('b');
              expect(result[users[0].id][1].category).to.be.ok;

              expect(result[users[1].id].length).to.equal(2);
              expect(result[users[1].id][0].title).to.equal('a');
              expect(result[users[1].id][0].category).to.be.ok;
              expect(result[users[1].id][1].title).to.equal('b');
              expect(result[users[1].id][1].category).to.be.ok;
            });
          });
        });
      });
    }
  });

  describe('(1:N)', function() {

    describe('hasSingle', function() {
      beforeEach(function() {
        this.Article = this.sequelize.define('Article', { 'title': DataTypes.STRING });
        this.Label = this.sequelize.define('Label', { 'text': DataTypes.STRING });

        this.Article.hasMany(this.Label);

        return this.sequelize.sync({ force: true });
      });

      it('should only generate one set of foreignKeys', function() {
        this.Article = this.sequelize.define('Article', { 'title': DataTypes.STRING }, {timestamps: false});
        this.Label = this.sequelize.define('Label', { 'text': DataTypes.STRING }, {timestamps: false});

        this.Label.belongsTo(this.Article);
        this.Article.hasMany(this.Label);

        expect(Object.keys(this.Label.rawAttributes)).to.deep.equal(['id', 'text', 'ArticleId']);
        expect(Object.keys(this.Label.rawAttributes).length).to.equal(3);
      });

      if (current.dialect.supports.transactions) {
        it('supports transactions', function() {
          var Article, Label, sequelize, article, label, t;
          return Support.prepareTransactionTest(this.sequelize).then(function(_sequelize) {
            sequelize = _sequelize;
            Article = sequelize.define('Article', { 'title': DataTypes.STRING });
            Label = sequelize.define('Label', { 'text': DataTypes.STRING });

            Article.hasMany(Label);

            return sequelize.sync({ force: true });
          }).then(function() {
            return Promise.all([
              Article.create({ title: 'foo' }),
              Label.create({ text: 'bar' })
            ]);
          }).spread(function(_article, _label) {
            article = _article;
            label = _label;
            return sequelize.transaction();
          }).then(function(_t) {
            t = _t;
            return article.setLabels([label], { transaction: t });
          }).then(function() {
            return Article.all({ transaction: t });
          }).then(function(articles) {
            return articles[0].hasLabel(label).then(function(hasLabel) {
              expect(hasLabel).to.be.false;
            });
          }).then(function() {
            return Article.all({ transaction: t });
          }).then(function(articles) {
            return articles[0].hasLabel(label, { transaction: t }).then(function(hasLabel) {
              expect(hasLabel).to.be.true;
              return t.rollback();
            });
          });
        });
      }

      it('does not have any labels assigned to it initially', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return Promise.all([
            article.hasLabel(label1),
            article.hasLabel(label2)
          ]);
        }).spread(function(hasLabel1, hasLabel2) {
          expect(hasLabel1).to.be.false;
          expect(hasLabel2).to.be.false;
        });
      });

      it('answers true if the label has been assigned', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.addLabel(label1).then(function() {
            return Promise.all([
              article.hasLabel(label1),
              article.hasLabel(label2)
            ]);
          });
        }).spread(function(hasLabel1, hasLabel2) {
          expect(hasLabel1).to.be.true;
          expect(hasLabel2).to.be.false;
        });
      });

      it('answers correctly if the label has been assigned when passing a primary key instead of an object', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.addLabel(label1).then(function() {
            return Promise.all([
              article.hasLabel(label1.id),
              article.hasLabel(label2.id)
            ]);
          });
        }).spread(function(hasLabel1, hasLabel2) {
          expect(hasLabel1).to.be.true;
          expect(hasLabel2).to.be.false;
        });
      });
    });

    describe('hasAll', function() {
      beforeEach(function() {
        this.Article = this.sequelize.define('Article', {
          'title': DataTypes.STRING
        });
        this.Label = this.sequelize.define('Label', {
          'text': DataTypes.STRING
        });

        this.Article.hasMany(this.Label);

        return this.sequelize.sync({ force: true });
      });

      if (current.dialect.supports.transactions) {
        it('supports transactions', function() {
          return Support.prepareTransactionTest(this.sequelize).bind({}).then(function(sequelize) {
            this.sequelize = sequelize;
            this.Article = sequelize.define('Article', { 'title': DataTypes.STRING });
            this.Label = sequelize.define('Label', { 'text': DataTypes.STRING });

            this.Article.hasMany(this.Label);

            return this.sequelize.sync({ force: true });
          }).then(function() {
            return Promise.all([
              this.Article.create({ title: 'foo' }),
              this.Label.create({ text: 'bar' })
            ]);
          }).spread(function(article, label) {
            this.article = article;
            this.label = label;
            return this.sequelize.transaction();
          }).then(function(t) {
            this.t = t;
            return this.article.setLabels([this.label], { transaction: t });
          }).then(function() {
            return this.Article.all({ transaction: this.t });
          }).then(function(articles) {
            return Promise.all([
              articles[0].hasLabels([this.label]),
              articles[0].hasLabels([this.label], { transaction: this.t })
            ]);
          }).spread(function(hasLabel1, hasLabel2) {
            expect(hasLabel1).to.be.false;
            expect(hasLabel2).to.be.true;

            return this.t.rollback();
          });
        });
      }

      it('answers false if only some labels have been assigned', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.addLabel(label1).then(function() {
            return article.hasLabels([label1, label2]);
          });
        }).then(function(result) {
          expect(result).to.be.false;
        });
      });

      it('answers false if only some labels have been assigned when passing a primary key instead of an object', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.addLabel(label1).then(function() {
            return article.hasLabels([label1.id, label2.id]).then(function(result) {
              expect(result).to.be.false;
            });
          });
        });
      });

      it('answers true if all label have been assigned', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.setLabels([label1, label2]).then(function() {
            return article.hasLabels([label1, label2]).then(function(result) {
              expect(result).to.be.true;
            });
          });
        });
      });

      it('answers true if all label have been assigned when passing a primary key instead of an object', function() {
        return Promise.all([
          this.Article.create({ title: 'Article' }),
          this.Label.create({ text: 'Awesomeness' }),
          this.Label.create({ text: 'Epicness' })
        ]).spread(function(article, label1, label2) {
          return article.setLabels([label1, label2]).then(function() {
            return article.hasLabels([label1.id, label2.id]).then(function(result) {
              expect(result).to.be.true;
            });
          });
        });
      });
    });

    describe('setAssociations', function() {

      if (current.dialect.supports.transactions) {
        it('supports transactions', function() {
          return Support.prepareTransactionTest(this.sequelize).bind({}).then(function(sequelize) {
            this.Article = sequelize.define('Article', { 'title': DataTypes.STRING });
            this.Label = sequelize.define('Label', { 'text': DataTypes.STRING });

            this.Article.hasMany(this.Label);

            this.sequelize = sequelize;
            return sequelize.sync({ force: true });
          }).then(function() {
            return Promise.all([
              this.Article.create({ title: 'foo' }),
              this.Label.create({ text: 'bar' }),
              this.sequelize.transaction()
            ]);
          }).spread(function(article, label, t) {
            this.article = article;
            this. t = t;
            return article.setLabels([label], { transaction: t });
          }).then(function() {
            return this.Label.findAll({ where: { ArticleId: this.article.id }, transaction: undefined });
          }).then(function(labels) {
            expect(labels.length).to.equal(0);

            return this.Label.findAll({ where: { ArticleId: this.article.id }, transaction: this.t });
          }).then(function(labels) {
            expect(labels.length).to.equal(1);
            return this.t.rollback();
          });
        });
      }

      it('clears associations when passing null to the set-method', function() {
        var User = this.sequelize.define('User', { username: DataTypes.STRING })
          , Task = this.sequelize.define('Task', { title: DataTypes.STRING });

        Task.hasMany(User);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            User.create({ username: 'foo' }),
            Task.create({ title: 'task' })
          ]);
        }).bind({}).spread(function(user, task) {
          this.task = task;
          return task.setUsers([user]);
        }).then(function() {
          return this.task.getUsers();
        }).then(function(users) {
          expect(users).to.have.length(1);

          return this.task.setUsers(null);
        }).then(function() {
          return this.task.getUsers();
        }).then(function(users) {
          expect(users).to.have.length(0);
        });
      });

      it('supports passing the primary key instead of an object', function() {
        var Article = this.sequelize.define('Article', { title: DataTypes.STRING })
          , Label = this.sequelize.define('Label', { text: DataTypes.STRING });

        Article.hasMany(Label);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            Article.create({}),
            Label.create({ text: 'label one' }),
            Label.create({ text: 'label two' })
          ]);
        }).bind({}).spread(function(article, label1, label2) {
          this.article = article;
          this.label1 = label1;
          this.label2 = label2;
          return article.addLabel(label1.id);
        }).then(function() {
          return this.article.setLabels([this.label2.id]);
        }).then(function() {
          return this.article.getLabels();
        }).then(function(labels) {
          expect(labels).to.have.length(1);
          expect(labels[0].text).to.equal('label two');
        });
      });
    });

    describe('addAssociations', function() {
      if (current.dialect.supports.transactions) {
        it('supports transactions', function() {
          return Support.prepareTransactionTest(this.sequelize).bind({}).then(function(sequelize) {
            this.Article = sequelize.define('Article', { 'title': DataTypes.STRING });
            this.Label = sequelize.define('Label', { 'text': DataTypes.STRING });
            this.Article.hasMany(this.Label);

            this.sequelize = sequelize;
            return sequelize.sync({ force: true });
          }).then(function() {
            return Promise.all([
              this.Article.create({ title: 'foo' }),
              this.Label.create({ text: 'bar' })
            ]);
          }).spread(function(article, label) {
            this.article = article;
            this.label = label;
            return this.sequelize.transaction();
          }).then(function(t) {
            this.t = t;
            return this.article.addLabel(this.label, { transaction: this.t });
          }).then(function() {
            return this.Label.findAll({ where: { ArticleId: this.article.id }, transaction: undefined });
          }).then(function(labels) {
            expect(labels.length).to.equal(0);

            return this.Label.findAll({ where: { ArticleId: this.article.id }, transaction: this.t });
          }).then(function(labels) {
            expect(labels.length).to.equal(1);
            return this.t.rollback();
          });
        });
      }

      it('supports passing the primary key instead of an object', function() {
        var Article = this.sequelize.define('Article', { 'title': DataTypes.STRING })
          , Label = this.sequelize.define('Label', { 'text': DataTypes.STRING });

        Article.hasMany(Label);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            Article.create({}),
            Label.create({ text: 'label one' })
          ]);
        }).bind({}).spread(function(article, label) {
          this.article = article;
          return article.addLabel(label.id);
        }).then(function() {
          return this.article.getLabels();
        }).then(function(labels) {
          expect(labels[0].text).to.equal('label one'); // Make sure that we didn't modify one of the other attributes while building / saving a new instance
        });
      });
    });

    describe('addMultipleAssociations', function() {
      it('adds associations without removing the current ones', function() {
        var User = this.sequelize.define('User', { username: DataTypes.STRING })
          , Task = this.sequelize.define('Task', { title: DataTypes.STRING });

        Task.hasMany(User);

        return this.sequelize.sync({ force: true }).then(function() {
          return User.bulkCreate([
            { username: 'foo '},
            { username: 'bar '},
            { username: 'baz '}
          ]);
        }).bind({}).then(function() {
          return Task.create({ title: 'task' });
        }).then(function(task) {
          this.task = task;
          return User.findAll();
        }).then(function(users) {
          this.users = users;
          return this.task.setUsers([users[0]]);
        }).then(function() {
          return this.task.addUsers([this.users[1], this.users[2]]);
        }).then(function() {
          return this.task.getUsers();
        }).then(function(users) {
          expect(users).to.have.length(3);
        });
      });
    });

    it('clears associations when passing null to the set-method with omitNull set to true', function() {
      this.sequelize.options.omitNull = true;

      var User = this.sequelize.define('User', { username: DataTypes.STRING })
        , Task = this.sequelize.define('Task', { title: DataTypes.STRING });

      Task.hasMany(User);

      return this.sequelize.sync({ force: true }).then(function() {
        return User.create({ username: 'foo' });
      }).bind({}).then(function(user) {
        this.user = user;
        return Task.create({ title: 'task' });
      }).then(function(task) {
        this.task = task;
        return task.setUsers([this.user]);
      }).then(function() {
        return this.task.getUsers();
      }).then(function(_users) {
        expect(_users).to.have.length(1);

        return this.task.setUsers(null);
      }).then(function() {
        return this.task.getUsers();
      }).then(function(_users) {
        expect(_users).to.have.length(0);
      });
    });

    describe('createAssociations', function() {
      it('creates a new associated object', function() {
        var Article = this.sequelize.define('Article', { 'title': DataTypes.STRING })
          , Label = this.sequelize.define('Label', { 'text': DataTypes.STRING });

        Article.hasMany(Label);

        return this.sequelize.sync({ force: true }).then(function() {
          return Article.create({ title: 'foo' });
        }).then(function(article) {
          return article.createLabel({ text: 'bar' }).return (article);
        }).then(function(article) {
          return Label.findAll({ where: { ArticleId: article.id }});
        }).then(function(labels) {
          expect(labels.length).to.equal(1);
        });
      });

      it('creates the object with the association directly', function() {
        var spy = sinon.spy();

        var Article = this.sequelize.define('Article', {
          'title': DataTypes.STRING

        }), Label = this.sequelize.define('Label', {
          'text': DataTypes.STRING
        });

        Article.hasMany(Label);

        return this.sequelize.sync({ force: true }).then(function() {
          return Article.create({ title: 'foo' });
        }).bind({}).then(function(article) {
          this.article = article;
          return article.createLabel({ text: 'bar' }, {logging: spy});
        }).then(function(label) {
          expect(spy.calledOnce).to.be.true;
          expect(label.ArticleId).to.equal(this.article.id);
        });
      });

      if (current.dialect.supports.transactions) {
        it('supports transactions', function() {
          return Support.prepareTransactionTest(this.sequelize).bind({}).then(function(sequelize) {
            this.sequelize = sequelize;
            this.Article = sequelize.define('Article', { 'title': DataTypes.STRING });
            this.Label = sequelize.define('Label', { 'text': DataTypes.STRING });

            this.Article.hasMany(this.Label);

            return sequelize.sync({ force: true});
          }).then(function() {
            return this.Article.create({ title: 'foo' });
          }).then(function(article) {
            this.article = article;
            return this.sequelize.transaction();
          }).then(function(t) {
            this.t = t;
            return this.article.createLabel({ text: 'bar' }, { transaction: this.t });
          }).then(function() {
            return this.Label.findAll();
          }).then(function(labels) {
            expect(labels.length).to.equal(0);
            return this.Label.findAll({ where: { ArticleId: this.article.id }});
          }).then(function(labels) {
            expect(labels.length).to.equal(0);
            return this.Label.findAll({ where: { ArticleId: this.article.id }, transaction: this.t });
          }).then(function(labels) {
            expect(labels.length).to.equal(1);
            return this.t.rollback();
          });
        });
      }

      it('supports passing the field option', function() {
        var Article = this.sequelize.define('Article', {
              'title': DataTypes.STRING
            })
          , Label = this.sequelize.define('Label', {
              'text': DataTypes.STRING
            });

        Article.hasMany(Label);

        return this.sequelize.sync({force: true}).then(function() {
          return Article.create();
        }).then(function(article) {
          return article.createLabel({
            text: 'yolo'
          }, {
            fields: ['text']
          }).return (article);
        }).then(function(article) {
          return article.getLabels();
        }).then(function(labels) {
          expect(labels.length).to.be.ok;
        });
      });
    });

    describe('getting assocations with options', function() {
      beforeEach(function() {
        var self = this;

        this.User = this.sequelize.define('User', { username: DataTypes.STRING });
        this.Task = this.sequelize.define('Task', { title: DataTypes.STRING, active: DataTypes.BOOLEAN });

        this.User.hasMany(self.Task);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            self.User.create({ username: 'John'}),
            self.Task.create({ title: 'Get rich', active: true}),
            self.Task.create({ title: 'Die trying', active: false})
          ]);
        }).spread(function(john, task1, task2) {
          return john.setTasks([task1, task2]);
        });
      });

      it('does not modify the passed arguments', function() {
        return this.User.create({}).bind(this).then(function(user) {
          this.options = {};

          return user.getTasks(this.options);
        }).then(function() {
          expect(this.options).to.deep.equal({});
        });
      });

      it('should treat the where object of associations as a first class citizen', function() {
        var self = this;
        this.Article = this.sequelize.define('Article', {
          'title': DataTypes.STRING
        });
        this.Label = this.sequelize.define('Label', {
          'text': DataTypes.STRING,
          'until': DataTypes.DATE
        });

        this.Article.hasMany(this.Label);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            self.Article.create({ title: 'Article' }),
            self.Label.create({ text: 'Awesomeness', until: '2014-01-01 01:00:00' }),
            self.Label.create({ text: 'Epicness', until: '2014-01-03 01:00:00' })
          ]);
        }).bind({}).spread(function(article, label1, label2) {
          this.article = article;
          return article.setLabels([label1, label2]);
        }).then(function() {
          return this.article.getLabels({where: ['until > ?', moment('2014-01-02').toDate()]});
        }).then(function(labels) {
          expect(labels).to.be.instanceof(Array);
          expect(labels).to.have.length(1);
          expect(labels[0].text).to.equal('Epicness');
        });
      });

      it('gets all associated objects when no options are passed', function() {
        return this.User.find({where: {username: 'John'}}).then(function(john) {
          return john.getTasks();
        }).then(function(tasks) {
          expect(tasks).to.have.length(2);
        });
      });

      it('only get objects that fulfill the options', function() {
        return this.User.find({ where: { username: 'John' } }).then(function(john) {
          return john.getTasks({ where: { active: true }, limit: 10, order: 'id DESC' });
        }).then(function(tasks) {
          expect(tasks).to.have.length(1);
        });
      });
    });

    describe('countAssociations', function () {
      beforeEach(function() {
        var self = this;

        this.User = this.sequelize.define('User', { username: DataTypes.STRING });
        this.Task = this.sequelize.define('Task', { title: DataTypes.STRING, active: DataTypes.BOOLEAN });

        this.User.hasMany(self.Task, {
          foreignKey: 'userId'
        });

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            self.User.create({ username: 'John'}),
            self.Task.create({ title: 'Get rich', active: true}),
            self.Task.create({ title: 'Die trying', active: false})
          ]);
        }).spread(function(john, task1, task2) {
          self.user = john;
          return john.setTasks([task1, task2]);
        });
      });

      it('should count all associations', function () {
        return expect(this.user.countTasks({})).to.eventually.equal(2);
      });

      it('should count filtered associations', function () {
        return expect(this.user.countTasks({
          where: {
            active: true
          }
        })).to.eventually.equal(1);
      });

      it('should count scoped associations', function () {
        this.User.hasMany(this.Task, {
          foreignKey: 'userId',
          as: 'activeTasks',
          scope: {
            active: true
          }
        });

        return expect(this.user.countActiveTasks({})).to.eventually.equal(1);
      });
    });

    describe('selfAssociations', function() {
      it('should work with alias', function() {
        var Person = this.sequelize.define('Group', {});

        Person.hasMany(Person, { as: 'Children'});

        return this.sequelize.sync();
      });
    });
  });

  describe('Foreign key constraints', function() {
    describe('1:m', function() {
      it('sets null by default', function() {
        var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
          , User = this.sequelize.define('User', { username: DataTypes.STRING });

        User.hasMany(Task);

        return this.sequelize.sync({ force: true }).then(function() {
          return Promise.all([
            User.create({ username: 'foo' }),
            Task.create({ title: 'task' })
          ]);
        }).spread(function(user, task) {
          return user.setTasks([task]).then(function() {
            return user.destroy().then(function() {
              return task.reload();
            });
          });
        }).then(function(task) {
          expect(task.UserId).to.equal(null);
        });
      });

      it('sets to CASCADE if allowNull: false', function() {
        var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
          , User = this.sequelize.define('User', { username: DataTypes.STRING });

        User.hasMany(Task, { foreignKey: { allowNull: false }}); // defaults to CASCADE

        return this.sequelize.sync({ force: true }).then(function() {
          return User.create({ username: 'foo' }).then(function(user) {
            return Task.create({ title: 'task', UserId: user.id }).then(function() {
              return user.destroy().then(function() {
                return Task.findAll();
              });
            });
          }).then(function(tasks) {
            expect(tasks).to.be.empty;
          });
        });
      });

      it('should be possible to remove all constraints', function() {
        var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
          , User = this.sequelize.define('User', { username: DataTypes.STRING });

        User.hasMany(Task, { constraints: false });

        return this.sequelize.sync({ force: true }).bind({}).then(function() {
          return Promise.all([
            User.create({ username: 'foo' }),
            Task.create({ title: 'task' })
          ]);
        }).spread(function(user, task) {
          this.user = user;
          this.task = task;
          return user.setTasks([task]);
        }).then(function() {
          return this.user.destroy();
        }).then(function() {
          return this.task.reload();
        }).then(function(task) {
          expect(task.UserId).to.equal(this.user.id);
        });
      });

      it('can cascade deletes', function() {
        var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
          , User = this.sequelize.define('User', { username: DataTypes.STRING });

        User.hasMany(Task, {onDelete: 'cascade'});

        return this.sequelize.sync({ force: true }).bind({}).then(function() {
          return Promise.all([
            User.create({ username: 'foo' }),
            Task.create({ title: 'task' })
          ]);
        }).spread(function(user, task) {
          this.user = user;
          this.task = task;
          return user.setTasks([task]);
        }).then(function() {
          return this.user.destroy();
        }).then(function() {
          return Task.findAll();
        }).then(function(tasks) {
          expect(tasks).to.have.length(0);
        });
      });

      // NOTE: mssql does not support changing an autoincrement primary key
      if (dialect !== 'mssql') {
        it('can cascade updates', function() {
          var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
            , User = this.sequelize.define('User', { username: DataTypes.STRING });

          User.hasMany(Task, {onUpdate: 'cascade'});

          return this.sequelize.sync({ force: true }).then(function() {
            return Promise.all([
              User.create({ username: 'foo' }),
              Task.create({ title: 'task' })
            ]);
          }).spread(function(user, task) {
            return user.setTasks([task]).return (user);
          }).then(function(user) {
            // Changing the id of a DAO requires a little dance since
            // the `UPDATE` query generated by `save()` uses `id` in the
            // `WHERE` clause

            var tableName = user.sequelize.getQueryInterface().QueryGenerator.addSchema(user.Model);
            return user.sequelize.getQueryInterface().update(user, tableName, {id: 999}, {id: user.id});
          }).then(function() {
            return Task.findAll();
          }).then(function(tasks) {
            expect(tasks).to.have.length(1);
            expect(tasks[0].UserId).to.equal(999);
          });
        });
      }

      if (current.dialect.supports.constraints.restrict) {
        it('can restrict deletes', function() {
          var self = this;
          var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
            , User = this.sequelize.define('User', { username: DataTypes.STRING });

          User.hasMany(Task, {onDelete: 'restrict'});

          return this.sequelize.sync({ force: true }).bind({}).then(function() {
            return Promise.all([
              User.create({ username: 'foo' }),
              Task.create({ title: 'task' })
            ]);
          }).spread(function(user, task) {
            this.user = user;
            this.task = task;
            return user.setTasks([task]);
          }).then(function() {
            return this.user.destroy().catch (self.sequelize.ForeignKeyConstraintError, function() {
              // Should fail due to FK violation
              return Task.findAll();
            });
          }).then(function(tasks) {
            expect(tasks).to.have.length(1);
          });
        });

        it('can restrict updates', function() {
          var self = this;
          var Task = this.sequelize.define('Task', { title: DataTypes.STRING })
            , User = this.sequelize.define('User', { username: DataTypes.STRING });

          User.hasMany(Task, {onUpdate: 'restrict'});

          return this.sequelize.sync({ force: true }).then(function() {
            return Promise.all([
              User.create({ username: 'foo' }),
              Task.create({ title: 'task' })
            ]);
          }).spread(function(user, task) {
            return user.setTasks([task]).return (user);
          }).then(function(user) {
            // Changing the id of a DAO requires a little dance since
            // the `UPDATE` query generated by `save()` uses `id` in the
            // `WHERE` clause

            var tableName = user.sequelize.getQueryInterface().QueryGenerator.addSchema(user.Model);
            return user.sequelize.getQueryInterface().update(user, tableName, {id: 999}, {id: user.id})
            .catch (self.sequelize.ForeignKeyConstraintError, function() {
              // Should fail due to FK violation
              return Task.findAll();
            });
          }).then(function(tasks) {
            expect(tasks).to.have.length(1);
          });
        });

      }

    });
  });

  describe('Association options', function() {
    it('can specify data type for autogenerated relational keys', function() {
      var User = this.sequelize.define('UserXYZ', { username: DataTypes.STRING })
        , dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT, Sequelize.STRING]
        , self = this
        , Tasks = {};

      return Promise.each(dataTypes, function(dataType) {
        var tableName = 'TaskXYZ_' + dataType.key;
        Tasks[dataType] = self.sequelize.define(tableName, { title: DataTypes.STRING });

        User.hasMany(Tasks[dataType], { foreignKey: 'userId', keyType: dataType, constraints: false });

        return Tasks[dataType].sync({ force: true }).then(function() {
          expect(Tasks[dataType].rawAttributes.userId.type).to.be.an.instanceof(dataType);
        });
      });
    });

    it('infers the keyType if none provided', function() {
      var User = this.sequelize.define('User', {
        id: { type: DataTypes.STRING, primaryKey: true },
        username: DataTypes.STRING
      })
      , Task = this.sequelize.define('Task', {
        title: DataTypes.STRING
      });

      User.hasMany(Task);

      return this.sequelize.sync({ force: true }).then(function() {
        expect(Task.rawAttributes.UserId.type instanceof DataTypes.STRING).to.be.ok;
      });
    });

    describe('allows the user to provide an attribute definition object as foreignKey', function() {
      it('works with a column that hasnt been defined before', function() {
        var Task = this.sequelize.define('task', {})
          , User = this.sequelize.define('user', {});

        User.hasMany(Task, {
          foreignKey: {
            name: 'uid',
            allowNull: false
          }
        });

        expect(Task.rawAttributes.uid).to.be.ok;
        expect(Task.rawAttributes.uid.allowNull).to.be.false;
        expect(Task.rawAttributes.uid.references.model).to.equal(User.getTableName());
        expect(Task.rawAttributes.uid.references.key).to.equal('id');
      });

      it('works when taking a column directly from the object', function() {
        var Project = this.sequelize.define('project', {
            user_id: {
              type: Sequelize.INTEGER,
              defaultValue: 42
            }
          })
        , User = this.sequelize.define('user', {
            uid: {
              type: Sequelize.INTEGER,
              primaryKey: true
            }
          });

        User.hasMany(Project, { foreignKey: Project.rawAttributes.user_id});

        expect(Project.rawAttributes.user_id).to.be.ok;
        expect(Project.rawAttributes.user_id.references.model).to.equal(User.getTableName());
        expect(Project.rawAttributes.user_id.references.key).to.equal('uid');
        expect(Project.rawAttributes.user_id.defaultValue).to.equal(42);
      });

      it('works when merging with an existing definition', function() {
        var Task = this.sequelize.define('task', {
            userId: {
              defaultValue: 42,
              type: Sequelize.INTEGER
            }
          })
        , User = this.sequelize.define('user', {});

        User.hasMany(Task, { foreignKey: { allowNull: true }});

        expect(Task.rawAttributes.userId).to.be.ok;
        expect(Task.rawAttributes.userId.defaultValue).to.equal(42);
        expect(Task.rawAttributes.userId.allowNull).to.be.ok;
      });
    });

    it('should throw an error if foreignKey and as result in a name clash', function() {
      var User = this.sequelize.define('user', {
            user: Sequelize.INTEGER
          });

      expect(User.hasMany.bind(User, User, { as: 'user' })).to
        .throw ('Naming collision between attribute \'user\' and association \'user\' on model user. To remedy this, change either foreignKey or as in your association definition');
    });
  });
});


'use strict';

var Support   = require(__dirname + '/../support')
  , expectsql = Support.expectsql
  , current   = Support.sequelize
  , sql       = current.dialect.QueryGenerator
  , current = Support.sequelize;

// Notice: [] will be replaced by dialect specific tick/quote character when there is not dialect specific expectation but only a default expectation

suite(Support.getTestDialectTeaser('SQL'), function() {
  suite('addIndex', function () {
    test('naming', function () {
      expectsql(sql.addIndexQuery('table', ['column1', 'column2'], {}, 'table'), {
        default: 'CREATE INDEX [table_column1_column2] ON [table] ([column1], [column2])',
        mysql: 'ALTER TABLE `table` ADD INDEX `table_column1_column2` (`column1`, `column2`)'
      });

      if (current.dialect.supports.schemas) {
        expectsql(sql.addIndexQuery('schema.table', ['column1', 'column2'], {}), {
          default: 'CREATE INDEX [schema_table_column1_column2] ON [schema].[table] ([column1], [column2])',
        });

        expectsql(sql.addIndexQuery({
          schema: 'schema',
          tableName: 'table'
        }, ['column1', 'column2'], {}, 'schema_table'), {
          default: 'CREATE INDEX [schema_table_column1_column2] ON [schema].[table] ([column1], [column2])'
        });

        expectsql(sql.addIndexQuery(sql.quoteTable(sql.addSchema({
          $schema: 'schema',
          tableName: 'table'
        })), ['column1', 'column2'], {}), {
          default: 'CREATE INDEX [schema_table_column1_column2] ON [schema].[table] ([column1], [column2])'
        });
      }
    });

    test('type and method', function () {
      expectsql(sql.addIndexQuery('User', ['fieldC'], {
          type: 'FULLTEXT',
          concurrently: true
        }), {
        sqlite: 'CREATE INDEX `user_field_c` ON `User` (`fieldC`)',
        mssql: 'CREATE FULLTEXT INDEX [user_field_c] ON [User] ([fieldC])',
        postgres: 'CREATE INDEX CONCURRENTLY "user_field_c" ON "User" ("fieldC")',
        mysql: 'ALTER TABLE `User` ADD FULLTEXT INDEX `user_field_c` (`fieldC`)'
      });

      expectsql(sql.addIndexQuery('User', ['fieldB', {attribute: 'fieldA', collate: 'en_US', order: 'DESC', length: 5}], {
          name: 'a_b_uniq',
          unique: true,
          method: 'BTREE',
          parser: 'foo'
        }), {
        sqlite: 'CREATE UNIQUE INDEX `a_b_uniq` ON `User` (`fieldB`, `fieldA` COLLATE `en_US` DESC)',
        mssql: 'CREATE UNIQUE INDEX [a_b_uniq] ON [User] ([fieldB], [fieldA] DESC)',
        postgres: 'CREATE UNIQUE INDEX "a_b_uniq" ON "User" USING BTREE ("fieldB", "fieldA" COLLATE "en_US" DESC)',
        mysql: 'ALTER TABLE `User` ADD UNIQUE INDEX `a_b_uniq` USING BTREE (`fieldB`, `fieldA`(5) DESC) WITH PARSER foo',
      });
    });

    test('POJO field', function () {
      expectsql(sql.addIndexQuery('table', [{ attribute: 'column', collate: 'BINARY', length: 5, order: 'DESC'}], {}, 'table'), {
        default: 'CREATE INDEX [table_column] ON [table] ([column] COLLATE [BINARY] DESC)',
        mssql: 'CREATE INDEX [table_column] ON [table] ([column] DESC)',
        mysql: 'ALTER TABLE `table` ADD INDEX `table_column` (`column`(5) DESC)'
      });
    });

    test('function', function () {
      expectsql(sql.addIndexQuery('table', [current.fn('UPPER', current.col('test'))], { name: 'myindex'}), {
        default: 'CREATE INDEX [myindex] ON [table] (UPPER([test]))',
        mysql: 'ALTER TABLE `table` ADD INDEX `myindex` (UPPER(`test`))'
      });
    });

    if (current.dialect.supports.index.using === 2) {
      test('USING', function () {
        expectsql(sql.addIndexQuery('table', {
          fields: ['event'],
          using: 'gin'
        }), {
          postgres: 'CREATE INDEX "table_event" ON "table" USING gin ("event")'
        });
      });
    }

    if (current.dialect.supports.index.using === 2) {
      test('WHERE', function () {
        expectsql(sql.addIndexQuery('table', {
          fields: ['type'],
          where: {
            type: 'public'
          }
        }), {
          postgres: 'CREATE INDEX "table_type" ON "table" ("type") WHERE "type" = \'public\''
        });

        expectsql(sql.addIndexQuery('table', {
          fields: ['type'],
          where: {
            type: {
              $or: [
                'group',
                'private'
              ]
            }
          }
        }), {
          postgres: 'CREATE INDEX "table_type" ON "table" ("type") WHERE ("type" = \'group\' OR "type" = \'private\')'
        });
      });
    }

    if (current.dialect.supports.JSON) {
      test('operator', function () {
        expectsql(sql.addIndexQuery('table', {
          fields: ['event'],
          using: 'gin',
          operator: 'jsonb_path_ops'
        }), {
          postgres: 'CREATE INDEX "table_event" ON "table" USING gin ("event" jsonb_path_ops)'
        });
      });
    }

    if (current.dialect.name === 'postgres') {
      test('show indexes', function () {
        expectsql(sql.showIndexesQuery('table'), {
          postgres: 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' +
          'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' +
          'AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a ' +
          'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND ' +
          't.relkind = \'r\' and t.relname = \'table\' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;'
        });

        expectsql(sql.showIndexesQuery({tableName: 'table', schema: 'schema'}), {
          postgres: 'SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, ' +
          'array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) ' +
          'AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a, pg_namespace s ' +
          'WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND ' +
          't.relkind = \'r\' and t.relname = \'table\' AND s.oid = t.relnamespace AND s.nspname = \'schema\' ' +
          'GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;'
        });
      });
    }
  });
});


var tzTable = sequelize.define('tz_table', { foo: DataTypes.STRING });

return tzTable.sync({force: true}).then(function() {

return tzTable.create({foo: 'test'}).then(function(row) {

keep keep keep keep keep replace replace keep