// If we created a transaction internally, we should clean it up

keep keep keep keep replace keep keep keep keep

/* jshint camelcase: false */
/* jshint expr: true */
var chai      = require('chai')
  , Sequelize = require('../../index')
  , Promise   = Sequelize.Promise
  , expect    = chai.expect
  , Support   = require(__dirname + '/../support')
  , DataTypes = require(__dirname + "/../../lib/data-types")
  , dialect   = Support.getTestDialect()
  , datetime  = require('chai-datetime')
  , _         = require('lodash')
  , assert    = require('assert')

chai.use(datetime)
chai.config.includeStack = true

describe(Support.getTestDialectTeaser("DAOFactory"), function () {
  beforeEach(function () {
    return Support.prepareTransactionTest(this.sequelize).bind(this).then(function(sequelize) {
      this.sequelize = sequelize;

      this.User = this.sequelize.define('User', {
        username:     DataTypes.STRING,
        secretValue:  DataTypes.STRING,
        data:         DataTypes.STRING,
        intVal:       DataTypes.INTEGER,
        theDate:      DataTypes.DATE,
        aBool:        DataTypes.BOOLEAN,
        uniqueName:   { type: DataTypes.STRING, unique: true }
      })
      this.Account = this.sequelize.define('Account', {
        accountName: DataTypes.STRING
      });
      this.Student = this.sequelize.define('Student', {
          no:     {type:DataTypes.INTEGER,primaryKey:true},
          name: {type:DataTypes.STRING,allowNull:false},
      })

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

  describe('findOrCreate', function () {
    it("supports transactions", function(done) {
      var self = this;
      this.sequelize.transaction().then(function(t) {
        self.User.findOrCreate({ where: { username: 'Username' }, defaults: { data: 'some data' }}, { transaction: t }).then(function() {
          self.User.count().success(function(count) {
            expect(count).to.equal(0)
            t.commit().success(function() {
              self.User.count().success(function(count) {
                expect(count).to.equal(1)
                done()
              })
            })
          })
        })
      })
    })

    it("supports more than one models per transaction", function(done) {
      var self = this;
      this.sequelize.transaction().then(function(t) {
        self.User.findOrCreate({ where: { username: 'Username'}, defaults: { data: 'some data' }}, { transaction: t }).then(function() {
          self.Account.findOrCreate({ where: { accountName: 'accountName'}}, { transaction: t}).then(function(){
            t.commit().success(function() {
              done()
            })
          })
        })
      })
    })

    it("returns instance if already existent. Single find field.", function(done) {
      var self = this,
        data = {
          username: 'Username'
        };

      this.User.create(data).success(function (user) {
        self.User.findOrCreate({ where: {
          username: user.username
        }}).spread(function (_user, created) {
          expect(_user.id).to.equal(user.id)
          expect(_user.username).to.equal('Username')
          expect(created).to.be.false
          done()
        })
      })
    })

    it("Returns instance if already existent. Multiple find fields.", function(done) {
      var self = this,
        data = {
          username: 'Username',
          data: 'ThisIsData'
        };

      this.User.create(data).success(function (user) {
        self.User.findOrCreate({where: data}).done(function (err, _user, created) {
          expect(_user.id).to.equal(user.id)
          expect(_user.username).to.equal('Username')
          expect(_user.data).to.equal('ThisIsData')
          expect(created).to.be.false
          done()
        })
      })
    })

    it("creates new instance with default value.", function(done) {
      var data = {
          username: 'Username'
        },
        default_values = {
          data: 'ThisIsData'
        };

      this.User.findOrCreate({ where: data, defaults: default_values}).success(function(user, created) {
        expect(user.username).to.equal('Username')
        expect(user.data).to.equal('ThisIsData')
        expect(created).to.be.true
        done()
      })
    })

    it("supports .or() (only using default values)", function (done) {
      this.User.findOrCreate({
        where: Sequelize.or({username: 'Fooobzz'}, {secretValue: 'Yolo'}),
        defaults: {username: 'Fooobzz', secretValue: 'Yolo'}
      }).done(function (err, user, created) {
        expect(err).not.to.be.ok
        expect(user.username).to.equal('Fooobzz')
        expect(user.secretValue).to.equal('Yolo')
        expect(created).to.be.true

        done()
      })
    })

    it("should release transaction when meeting errors", function(done){
        var self = this

        var test = function(times) {
            times = times || 0
            if (times > 10) {
                return true //  stop trying
            }

            /*without name, trigger ValidationError */
            return self.Student.findOrCreate({where:{no:1},defaults:{}})
            .timeout(1000)
            .catch(Promise.TimeoutError,function(e){
                throw new Error(e)
            })
            .then(function(){
                return true
            })
            .catch(Sequelize.ValidationError,function(err){
                return test(times+1)
            })
        }

        test()
        .then(function(){
            done()
        })
        .catch(function(err){
            done(err)
        })
    })

    describe('several concurrent calls', function () {
      it('works with a transaction', function () {
        return this.sequelize.transaction().bind(this).then(function (transaction) {
          return Promise.join(
            this.User.findOrCreate({ where: { uniqueName: 'winner' }}, { transaction: transaction }),
            this.User.findOrCreate({ where: { uniqueName: 'winner' }}, { transaction: transaction }),
            function (first, second) {

              expect(first[0]).to.be.ok;
              expect(first[1]).to.be.ok;
              expect(second[0]).to.be.ok;
              expect(second[1]).not.to.be.ok;

              expect(first[0].id).to.equal(second[0].id);

              return transaction.commit();
            }
          );
        });
      });

      // Creating two concurrent transactions and selecting / inserting from the same table throws sqlite off
      (dialect !== 'sqlite' ? it : it.skip)('works without a transaction', function () {
        return Promise.join(
          this.User.findOrCreate({ where: { uniqueName: 'winner' }}),
          this.User.findOrCreate({ where: { uniqueName: 'winner' }}),
          function (first, second) {
            expect(first[0]).to.be.ok;
            expect(first[1]).to.be.ok;
            expect(second[0]).to.be.ok;
            expect(second[1]).not.to.be.ok;

            expect(first[0].id).to.equal(second[0].id);
          }
        );
      });
    });
  });

  describe('create', function() {
    it('works with non-integer primary keys with a default value', function (done) {
      var User = this.sequelize.define('User', {
        'id': {
          primaryKey: true,
          type: DataTypes.UUID,
          defaultValue: DataTypes.UUIDV4
        },
        'email': {
          type: DataTypes.UUID,
          defaultValue: DataTypes.UUIDV4
        }
      })

      this.sequelize.sync({force: true}).done(function (err) {
        expect(err).not.to.be.ok
        User.create({}).done(function (err, user) {
          expect(err).not.to.be.ok
          expect(user).to.be.ok
          expect(user.id).to.be.ok
          done()
        })
      })
    })

    it('should return an error for a unique constraint error', function (done) {
      var User = this.sequelize.define('User', {
        'email': {
          type: DataTypes.STRING,
          unique: { name: 'email', msg: 'Email is already registered.' },
          validate: {
            notEmpty: true,
            isEmail: true
          }
        }
      })

      this.sequelize.sync({force: true}).done(function (err) {
        expect(err).not.to.be.ok
        User.create({email: 'hello@sequelize.com'}).done(function (err) {
          expect(err).not.to.be.ok
          User.create({email: 'hello@sequelize.com'}).then(function () {
            assert(false)
          }, function (err) {
            expect(err).to.be.ok
            expect(err).to.be.an.instanceof(Error)
            done()
          })
        })
      })
    })

    it('supports transactions', function(done) {
      var self = this;
      this.sequelize.transaction().then(function(t) {
        self.User.create({ username: 'user' }, { transaction: t }).success(function() {
          self.User.count().success(function(count) {
            expect(count).to.equal(0)
            t.commit().success(function() {
              self.User.count().success(function(count) {
                expect(count).to.equal(1)
                done()
              })
            })
          })
        })
      })
    })

    it('is possible to use casting when creating an instance', function (done) {
      var self = this
        , type = Support.dialectIsMySQL() ? 'signed' : 'integer'
        , _done = _.after(2, function() {
          done()
        })

      this.User.create({
        intVal: this.sequelize.cast('1', type)
      }).on('sql', function (sql) {
        expect(sql).to.match(new RegExp('CAST\\(1 AS ' + type.toUpperCase() + '\\)'))
        _done()
      })
      .success(function (user) {
        self.User.find(user.id).success(function (user) {
          expect(user.intVal).to.equal(1)
          _done()
        })
      })
    })

    it('is possible to use casting multiple times mixed in with other utilities', function (done) {
      var self  = this
        , type  = this.sequelize.cast(this.sequelize.cast(this.sequelize.literal('1-2'), 'integer'), 'integer')
        , _done = _.after(2, function() {
          done()
        })

      if (Support.dialectIsMySQL()) {
        type = this.sequelize.cast(this.sequelize.cast(this.sequelize.literal('1-2'), 'unsigned'), 'signed')
      }

      this.User.create({
        intVal: type
      }).on('sql', function (sql) {
        if (Support.dialectIsMySQL()) {
          expect(sql).to.contain('CAST(CAST(1-2 AS UNSIGNED) AS SIGNED)')
        } else {
          expect(sql).to.contain('CAST(CAST(1-2 AS INTEGER) AS INTEGER)')
        }

        _done()
      }).success(function (user) {
        self.User.find(user.id).success(function (user) {
          expect(user.intVal).to.equal(-1)
          _done()
        })
      })
    })

    it('is possible to just use .literal() to bypass escaping', function (done) {
      var self = this

      this.User.create({
        intVal: this.sequelize.literal('CAST(1-2 AS ' + (Support.dialectIsMySQL() ? 'SIGNED' : 'INTEGER') + ')')
      }).success(function (user) {
        self.User.find(user.id).success(function (user) {
          expect(user.intVal).to.equal(-1)
          done()
        })
      })
    })

    it('is possible for .literal() to contain other utility functions', function (done) {
      var self = this

      this.User.create({
        intVal: this.sequelize.literal(this.sequelize.cast('1-2', (Support.dialectIsMySQL() ? 'SIGNED' : 'INTEGER')))
      }).success(function (user) {
        self.User.find(user.id).success(function (user) {
          expect(user.intVal).to.equal(-1)
          done()
        })
      })
    })

    it('is possible to use funtions when creating an instance', function (done) {
      var self = this
      this.User.create({
        secretValue: this.sequelize.fn('upper', 'sequelize')
      }).success(function (user) {
        self.User.find(user.id).success(function (user) {
          expect(user.secretValue).to.equal('SEQUELIZE')
          done()
        })
      })
    })

    it('is possible to use functions as default values', function (done) {
      var self = this
        , userWithDefaults

      if (dialect.indexOf('postgres') === 0) {
        this.sequelize.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"').success(function() {
          userWithDefaults = self.sequelize.define('userWithDefaults', {
            uuid: {
              type: 'UUID',
              defaultValue: self.sequelize.fn('uuid_generate_v4')
            }
          })

          userWithDefaults.sync({force: true}).success(function () {
            userWithDefaults.create({}).success(function (user) {
              // uuid validation regex taken from http://stackoverflow.com/a/13653180/800016
              expect(user.uuid).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
              done()
            })
          })
        })
      } else if (dialect === 'sqlite') {
        // The definition here is a bit hacky. sqlite expects () around the expression for default values, so we call a function without a name
        // to enclose the date function in (). http://www.sqlite.org/syntaxdiagrams.html#column-constraint
        userWithDefaults = self.sequelize.define('userWithDefaults', {
          year: {
            type: Sequelize.STRING,
            defaultValue: self.sequelize.fn('', self.sequelize.fn('date', 'now'))
          }
        })

        userWithDefaults.sync({force: true}).done(function (err) {
          expect(err).not.to.be.ok
          userWithDefaults.create({}).done(function (err, user) {
            expect(err).not.to.be.ok
            userWithDefaults.find(user.id).done(function (err, user) {
              expect(err).not.to.be.ok
              var now = new Date()
                , pad = function (number) {
                  if (number > 9) {
                    return number
                  }
                  return '0' + number
                }

              expect(user.year).to.equal(now.getUTCFullYear() + '-' + pad(now.getUTCMonth() + 1) + '-' + pad(now.getUTCDate()))
              done()
            })
          })
        })
      } else {
        // functions as default values are not supported in mysql, see http://stackoverflow.com/a/270338/800016
        done()
      }
    })

    it("casts empty arrays correctly for postgresql insert", function(done) {
      if (dialect !== "postgres") {
        expect('').to.equal('')
        return done()
      }

      var User = this.sequelize.define('UserWithArray', {
        myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
        mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
      })

      User.sync({force: true}).success(function() {
        User.create({myvals: [], mystr: []}).on('sql', function(sql){
          expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1)
          expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1)
          done()
        })
      })
    })

    it("casts empty array correct for postgres update", function(done) {
      if (dialect !== "postgres") {
        expect('').to.equal('')
        return done()
      }

      var User = this.sequelize.define('UserWithArray', {
        myvals: { type: Sequelize.ARRAY(Sequelize.INTEGER) },
        mystr: { type: Sequelize.ARRAY(Sequelize.STRING) }
      })

      User.sync({force: true}).success(function() {
        User.create({myvals: [1,2,3,4], mystr: ["One", "Two", "Three", "Four"]}).on('success', function(user){
         user.myvals = []
          user.mystr = []
          user.save().on('sql', function(sql) {
            expect(sql.indexOf('ARRAY[]::INTEGER[]')).to.be.above(-1)
            expect(sql.indexOf('ARRAY[]::VARCHAR[]')).to.be.above(-1)
            done()
          })
        })
      })
    })

    it("doesn't allow duplicated records with unique:true", function(done) {
      var self = this
        , User = this.sequelize.define('UserWithUniqueUsername', {
            username: { type: Sequelize.STRING, unique: true }
          })

      User.sync({ force: true }).success(function() {
        User.create({ username:'foo' }).success(function() {
          User.create({ username: 'foo' }).catch(self.sequelize.UniqueConstraintError, function(err) {
            done()
          })
        })
      })
    })

    it("raises an error if created object breaks definition contraints", function(done) {
      var UserNull = this.sequelize.define('UserWithNonNullSmth', {
        username: { type: Sequelize.STRING, unique: true },
        smth:     { type: Sequelize.STRING, allowNull: false }
      })

      this.sequelize.options.omitNull = false

      UserNull.sync({ force: true }).success(function() {
        UserNull.create({ username: 'foo2', smth: null }).error(function(err) {
          expect(err).to.exist

          expect(err.get('smth')[0].path).to.equal('smth');
          if (Support.dialectIsMySQL()) {
            // We need to allow two different errors for MySQL, see:
            // http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_trans_tables
            expect(err.get('smth')[0].type).to.match(/notNull Violation/)
          }
          else if (dialect === "sqlite") {
            expect(err.get('smth')[0].type).to.match(/notNull Violation/)
          } else {
            expect(err.get('smth')[0].type).to.match(/notNull Violation/)
          }
          done()
        })
      })
    })
    it("raises an error if created object breaks definition contraints", function(done) {
      var self = this
        , UserNull = this.sequelize.define('UserWithNonNullSmth', {
            username: { type: Sequelize.STRING, unique: true },
            smth:     { type: Sequelize.STRING, allowNull: false }
          })

      this.sequelize.options.omitNull = false

      UserNull.sync({ force: true }).success(function() {
        UserNull.create({ username: 'foo', smth: 'foo' }).success(function() {
          UserNull.create({ username: 'foo', smth: 'bar' }).catch(self.sequelize.UniqueConstraintError, function(err) {
            done()
          })
        })
      })
    })

    it("raises an error if saving an empty string into a column allowing null or URL", function(done) {
      var StringIsNullOrUrl = this.sequelize.define('StringIsNullOrUrl', {
        str: { type: Sequelize.STRING, allowNull: true, validate: { isURL: true } }
      })

      this.sequelize.options.omitNull = false

      StringIsNullOrUrl.sync({ force: true }).success(function() {
        StringIsNullOrUrl.create({ str: null }).success(function(str1) {
          expect(str1.str).to.be.null
          StringIsNullOrUrl.create({ str: 'http://sequelizejs.org' }).success(function(str2) {
            expect(str2.str).to.equal('http://sequelizejs.org')
            StringIsNullOrUrl.create({ str: '' }).error(function(err) {
              expect(err).to.exist
              expect(err.get('str')[0].message).to.match(/Validation isURL failed/)

              done()
            })
          })
        }).error(done)
      })
    })

    it('raises an error if you mess up the datatype', function(done) {
      var self = this
      expect(function() {
        self.sequelize.define('UserBadDataType', {
          activity_date: Sequelize.DATe
        })
      }).to.throw(Error, 'Unrecognized data type for field activity_date')

      expect(function() {
        self.sequelize.define('UserBadDataType', {
          activity_date: {type: Sequelize.DATe}
        })
      }).to.throw(Error, 'Unrecognized data type for field activity_date')
      done()
    })

    it('sets a 64 bit int in bigint', function(done) {
      var User = this.sequelize.define('UserWithBigIntFields', {
        big: Sequelize.BIGINT
      })

      User.sync({ force: true }).success(function() {
        User.create({ big: '9223372036854775807' }).on('success', function(user) {
          expect(user.big).to.be.equal( '9223372036854775807' )
          done()
        })
      })
    })

    it('sets auto increment fields', function(done) {
      var User = this.sequelize.define('UserWithAutoIncrementField', {
        userid: { type: Sequelize.INTEGER, autoIncrement: true, primaryKey: true, allowNull: false }
      })

      User.sync({ force: true }).success(function() {
        User.create({}).on('success', function(user) {
          expect(user.userid).to.equal(1)

          User.create({}).on('success', function(user) {
            expect(user.userid).to.equal(2)
            done()
          })
        })
      })
    })

    it('allows the usage of options as attribute', function(done) {
      var User = this.sequelize.define('UserWithNameAndOptions', {
        name: Sequelize.STRING,
        options: Sequelize.TEXT
      })

      var options = JSON.stringify({ foo: 'bar', bar: 'foo' })

      User.sync({ force: true }).success(function() {
        User
          .create({ name: 'John Doe', options: options })
          .success(function(user) {
            expect(user.options).to.equal(options)
            done()
          })
      })
    })

    it('allows sql logging', function(done) {
      var User = this.sequelize.define('UserWithUniqueNameAndNonNullSmth', {
        name: {type: Sequelize.STRING, unique: true},
        smth: {type: Sequelize.STRING, allowNull: false}
      })

      User.sync({ force: true }).success(function() {
        User
          .create({ name: 'Fluffy Bunny', smth: 'else' })
          .on('sql', function(sql) {
            expect(sql).to.exist
            expect(sql.toUpperCase().indexOf("INSERT")).to.be.above(-1)
            done()
          })
      })
    })

    it('should only store the values passed in the whitelist', function(done) {
      var self = this
        , data = { username: 'Peter', secretValue: '42' }

      this.User.create(data, { fields: ['username'] }).success(function(user) {
        self.User.find(user.id).success(function(_user) {
          expect(_user.username).to.equal(data.username)
          expect(_user.secretValue).not.to.equal(data.secretValue)
          expect(_user.secretValue).to.equal(null)
          done()
        })
      })
    })

    it('should store all values if no whitelist is specified', function(done) {
      var self = this
        , data = { username: 'Peter', secretValue: '42' }

      this.User.create(data).success(function(user) {
        self.User.find(user.id).success(function(_user) {
          expect(_user.username).to.equal(data.username)
          expect(_user.secretValue).to.equal(data.secretValue)
          done()
        })
      })
    })

    it('can omit autoincremental columns', function(done) {
      var self = this
        , data = { title: 'Iliad' }
        , dataTypes = [Sequelize.INTEGER, Sequelize.BIGINT]
        , chain = new Sequelize.Utils.QueryChainer()
        , chain2 = new Sequelize.Utils.QueryChainer()
        , books = []

      dataTypes.forEach(function(dataType, index) {
        books[index] = self.sequelize.define('Book'+index, {
          id: { type: dataType, primaryKey: true, autoIncrement: true },
          title: Sequelize.TEXT
        })
      })

      books.forEach(function(b) {
        chain.add(b.sync({ force: true }))
      })

      chain.run().success(function() {
        books.forEach(function(b) {
          chain2.add(b.create(data))
        })
        chain2.run().success(function(results) {
          results.forEach(function(book, index) {
            expect(book.title).to.equal(data.title)
            expect(book.author).to.equal(data.author)
            expect(books[index].rawAttributes.id.type.toString())
              .to.equal(dataTypes[index].toString())
          })
          done()
        })
      })
    })

    it('saves data with single quote', function(done) {
      var quote = "single'quote"
        , self  = this

      this.User.create({ data: quote }).success(function(user) {
        expect(user.data).to.equal(quote)

        self.User.find({where: { id: user.id }}).success(function(user) {
          expect(user.data).to.equal(quote)
          done()
        })
      })
    })

    it('saves data with double quote', function(done) {
      var quote = 'double"quote'
        , self  = this

      this.User.create({ data: quote }).success(function(user) {
        expect(user.data).to.equal(quote)

        self.User.find({where: { id: user.id }}).success(function(user) {
          expect(user.data).to.equal(quote)
          done()
        })
      })
    })

    it('saves stringified JSON data', function(done) {
      var json = JSON.stringify({ key: 'value' })
        , self = this

      this.User.create({ data: json }).success(function(user) {
        expect(user.data).to.equal(json)
        self.User.find({where: { id: user.id }}).success(function(user) {
          expect(user.data).to.equal(json)
          done()
        })
      })
    })

    it('stores the current date in createdAt', function(done) {
      this.User.create({ username: 'foo' }).success(function(user) {
        expect(parseInt(+user.createdAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1.5)
        done()
      })
    })

    it('allows setting custom IDs', function(done) {
      var self = this
      this.User.create({ id: 42 }).success(function (user) {
        expect(user.id).to.equal(42)

        self.User.find(42).success(function (user) {
          expect(user).to.exist
          done()
        })
      })
    })

    it('should allow blank creates (with timestamps: false)', function (done) {
      var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
      Worker.sync().done(function() {
        Worker.create({}, {fields: []}).done(function (err, worker) {
          expect(err).not.to.be.ok
          expect(worker).to.be.ok
          done()
        })
      })
    })

    it('should allow truly blank creates', function (done) {
      var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
      Worker.sync().done(function() {
        Worker.create({}, {fields: []}).done(function (err, worker) {
          expect(err).not.to.be.ok
          expect(worker).to.be.ok
          done()
        })
      })
    })

    it('should only set passed fields', function (done) {
      var User = this.sequelize.define('User', {
        'email': {
          type: DataTypes.STRING
        },
        'name': {
          type: DataTypes.STRING
        }
      })

      this.sequelize.sync({force: true}).done(function (err) {
        expect(err).not.to.be.ok;

        User.create({
          name: 'Yolo Bear',
          email: 'yolo@bear.com'
        }, {
          fields: ['name']
        }).done(function (err, user) {
          expect(err).not.to.be.ok;
          expect(user.name).to.be.ok;
          expect(user.email).not.to.be.ok;

          User.find(user.id).done(function (err, user) {
            expect(err).not.to.be.ok;
            expect(user.name).to.be.ok;
            expect(user.email).not.to.be.ok;
            done();
          });
        });
      });
    });

    describe('enums', function() {
      it('correctly restores enum values', function(done) {
        var self = this
          , Item = self.sequelize.define('Item', {
          state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] }
        })

        Item.sync({ force: true }).success(function() {
          Item.create({ state: 'available' }).success(function(_item) {
            Item.find({ where: { state: 'available' }}).success(function(item) {
              expect(item.id).to.equal(_item.id)
              done()
            })
          })
        })
      })

      it('allows null values', function(done) {
        var Enum = this.sequelize.define('Enum', {
          state: {
            type: Sequelize.ENUM,
            values: ['happy', 'sad'],
            allowNull: true
          }
        })

        Enum.sync({ force: true }).success(function() {
          Enum.create({state: null}).success(function(_enum) {
            expect(_enum.state).to.be.null
            done()
          })
        })
      })

      describe('when defined via { field: Sequelize.ENUM }', function() {
        it('allows values passed as parameters', function(done) {
          var Enum = this.sequelize.define('Enum', {
            state: Sequelize.ENUM('happy', 'sad')
          })

          Enum.sync({ force: true }).success(function() {
            Enum.create({ state: 'happy' }).success(function() {
              done()
            });
          });
        })

        it('allows values passed as an array', function(done) {
          var Enum = this.sequelize.define('Enum', {
            state: Sequelize.ENUM(['happy', 'sad'])
          })

          Enum.sync({ force: true }).success(function() {
            Enum.create({ state: 'happy' }).success(function() {
              done()
            });
          });
        })
      })

      describe('when defined via { field: { type: Sequelize.ENUM } }', function() {
        it('allows values passed as parameters', function(done) {
          var Enum = this.sequelize.define('Enum', {
            state: {
              type: Sequelize.ENUM('happy', 'sad')
            }
          })

          Enum.sync({ force: true }).success(function() {
            Enum.create({ state: 'happy' }).success(function() {
              done()
            });
          });
        })

        it('allows values passed as an array', function(done) {
          var Enum = this.sequelize.define('Enum', {
            state: {
              type: Sequelize.ENUM(['happy', 'sad'])
            }
          })

          Enum.sync({ force: true }).success(function() {
            Enum.create({ state: 'happy' }).success(function() {
              done()
            });
          });
        })
      })

      describe('can safely sync multiple times', function() {
        it('through the factory', function(done) {
          var Enum = this.sequelize.define('Enum', {
            state: {
              type: Sequelize.ENUM,
              values: ['happy', 'sad'],
              allowNull: true
            }
          })

          Enum.sync({ force: true }).success(function() {
            Enum.sync().success(function() {
              Enum.sync({ force: true }).complete(done)
            })
          })
        })

        it('through sequelize', function(done) {
          var self = this
            , Enum = this.sequelize.define('Enum', {
            state: {
              type: Sequelize.ENUM,
              values: ['happy', 'sad'],
              allowNull: true
            }
          })

          this.sequelize.sync({ force: true }).success(function() {
            self.sequelize.sync().success(function() {
              self.sequelize.sync({ force: true }).complete(done)
            })
          })
        })
      })
    })
  })

  describe('bulkCreate', function() {
    it("supports transactions", function(done) {
      var self = this;
      this.sequelize.transaction().then(function(t) {
        self.User
          .bulkCreate([{ username: 'foo' }, { username: 'bar' }], { transaction: t })
          .success(function() {
            self.User.count().success(function(count1) {
              self.User.count({ transaction: t }).success(function(count2) {
                expect(count1).to.equal(0)
                expect(count2).to.equal(2)
                t.rollback().success(function(){ done() })
              })
            })
          })
      })
    })

    it('properly handles disparate field lists', function(done) {
      var self = this
        , data = [{username: 'Peter', secretValue: '42' },
                  {username: 'Paul'},
                  {username: 'Steve'}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({where: {username: 'Paul'}}).success(function(users) {
          expect(users.length).to.equal(1)
          expect(users[0].username).to.equal("Paul")
          expect(users[0].secretValue).to.be.null
          done()
        })
      })
    })

    it('inserts multiple values respecting the white list', function(done) {
      var self = this
        , data = [{ username: 'Peter', secretValue: '42' },
                  { username: 'Paul', secretValue: '23'}]

      this.User.bulkCreate(data, { fields: ['username'] }).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal("Peter")
          expect(users[0].secretValue).to.be.null;
          expect(users[1].username).to.equal("Paul")
          expect(users[1].secretValue).to.be.null;
          done()
        })
      })
    })

    it('should store all values if no whitelist is specified', function(done) {
      var self = this
        , data = [{ username: 'Peter', secretValue: '42' },
                  { username: 'Paul', secretValue: '23'}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal("Peter")
          expect(users[0].secretValue).to.equal('42')
          expect(users[1].username).to.equal("Paul")
          expect(users[1].secretValue).to.equal('23')
          done()
        })
      })
    })

    it('saves data with single quote', function(done) {
      var self = this
        , quote = "Single'Quote"
        , data = [{ username: 'Peter', data: quote},
                  { username: 'Paul', data: quote}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal("Peter")
          expect(users[0].data).to.equal(quote)
          expect(users[1].username).to.equal("Paul")
          expect(users[1].data).to.equal(quote)
          done()
        })
      })
    })

    it('saves data with double quote', function(done) {
      var self = this
        , quote = 'Double"Quote'
        , data = [{ username: 'Peter', data: quote},
                  { username: 'Paul', data: quote}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal("Peter")
          expect(users[0].data).to.equal(quote)
          expect(users[1].username).to.equal("Paul")
          expect(users[1].data).to.equal(quote)
          done()
        })
      })
    })

    it('saves stringified JSON data', function(done) {
      var self = this
        , json = JSON.stringify({ key: 'value' })
        , data = [{ username: 'Peter', data: json},
                  { username: 'Paul', data: json}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal("Peter")
          expect(users[0].data).to.equal(json)
          expect(users[1].username).to.equal("Paul")
          expect(users[1].data).to.equal(json)
          done()
        })
      })
    })

    it('properly handles a model with a length column', function (done) {
      var UserWithLength = this.sequelize.define('UserWithLength', {
        length: Sequelize.INTEGER
      })

      UserWithLength.sync({force:true}).success(function() {
        UserWithLength.bulkCreate([{ length: 42}, {length: 11}]).success(function () {
          done()
        })
      })
    })

    it('stores the current date in createdAt', function(done) {
      var self = this
        , data = [{ username: 'Peter'},
                  { username: 'Paul'}]

      this.User.bulkCreate(data).success(function() {
        self.User.findAll({order: 'id'}).success(function(users) {
          expect(users.length).to.equal(2)
          expect(users[0].username).to.equal('Peter')
          expect(parseInt(+users[0].createdAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1.5)
          expect(users[1].username).to.equal('Paul')
          expect(parseInt(+users[1].createdAt/5000, 10)).to.be.closeTo(parseInt(+new Date()/5000, 10), 1.5)
          done()
        })
      })
    })

    it('emits an error when validate is set to true', function(done) {
      var Tasks = this.sequelize.define('Task', {
        name: {
          type: Sequelize.STRING,
          allowNull: false,
        },
        code: {
          type: Sequelize.STRING,
          validate: {
            len: [3, 10]
          }
        }
      })

      Tasks.sync({ force: true }).success(function() {
        Tasks.bulkCreate([
          {name: 'foo', code: '123'},
          {code: '1234'},
          {name: 'bar', code: '1'}
        ], { validate: true }).error(function(errors) {
          expect(errors).to.not.be.null
          expect(errors).to.be.an('Array')
          expect(errors).to.have.length(2)
          expect(errors[0].record.code).to.equal('1234')
          expect(errors[0].errors.get('name')[0].type).to.equal('notNull Violation')
          expect(errors[1].record.name).to.equal('bar')
          expect(errors[1].record.code).to.equal('1')
          expect(errors[1].errors.get('code')[0].message).to.equal('Validation len failed')
          done()
        })
      })
    })

    it("doesn't emit an error when validate is set to true but our selectedValues are fine", function(done) {
      var Tasks = this.sequelize.define('Task', {
        name: {
          type: Sequelize.STRING,
          validate: {
            notEmpty: true,
          },
        },
        code: {
          type: Sequelize.STRING,
          validate: {
            len: [3, 10]
          }
        }
      })

      Tasks.sync({ force: true }).success(function() {
        Tasks.bulkCreate([
          {name: 'foo', code: '123'},
          {code: '1234'}
        ], { fields: ['code'], validate: true }).success(function() {
          // we passed!
          done()
        })
      })
    })

    it('should allow blank arrays (return immediatly)', function (done) {
      var Worker = this.sequelize.define('Worker', {})
      Worker.sync().done(function() {
        Worker.bulkCreate([]).done(function (err, workers) {
          expect(err).not.to.be.ok
          expect(workers).to.be.ok
          expect(workers.length).to.equal(0)
          done()
        })
      })
    })

    it('should allow blank creates (with timestamps: false)', function (done) {
      var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
      Worker.sync().done(function() {
        Worker.bulkCreate([{}, {}]).done(function (err, workers) {
          expect(err).not.to.be.ok
          expect(workers).to.be.ok
          done()
        })
      })
    })

    it('should allow autoincremented attributes to be set', function (done) {
      var Worker = this.sequelize.define('Worker', {}, {timestamps: false})
      Worker.sync().done(function() {
        Worker.bulkCreate([
          {id: 5},
          {id: 10}
        ]).done(function (err) {
          expect(err).not.to.be.ok
          Worker.findAll({order: 'id ASC'}).done(function (err, workers) {
            expect(workers[0].id).to.equal(5)
            expect(workers[1].id).to.equal(10)
            done()
          })
        })
      })
    })

    if (Support.getTestDialect() !== 'postgres') {
      it("should support the ignoreDuplicates option", function(done) {
        var self = this
          , data = [{ uniqueName: 'Peter', secretValue: '42' },
                    { uniqueName: 'Paul', secretValue: '23' }]

        this.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'] }).success(function() {
          data.push({ uniqueName: 'Michael', secretValue: '26' });
          self.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'], ignoreDuplicates: true }).success(function() {
            self.User.findAll({order: 'id'}).success(function(users) {
              expect(users.length).to.equal(3)
              expect(users[0].uniqueName).to.equal("Peter")
              expect(users[0].secretValue).to.equal("42");
              expect(users[1].uniqueName).to.equal("Paul")
              expect(users[1].secretValue).to.equal("23");
              expect(users[2].uniqueName).to.equal("Michael")
              expect(users[2].secretValue).to.equal("26");
              done()
            });
          });
        })
      })
    } else {
      it("should throw an error when the ignoreDuplicates option is passed", function(done) {
        var self = this
          , data = [{ uniqueName: 'Peter', secretValue: '42' },
                    { uniqueName: 'Paul', secretValue: '23' }]

        this.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'] }).success(function() {
          data.push({ uniqueName: 'Michael', secretValue: '26' });

          self.User.bulkCreate(data, { fields: ['uniqueName', 'secretValue'], ignoreDuplicates: true }).error(function(err) {
            expect(err).to.exist
            expect(err.message).to.match(/Postgres does not support the \'ignoreDuplicates\' option./)

            done();
          })
        })
      })
    }

    describe('enums', function() {
      it('correctly restores enum values', function(done) {
        var self = this
          , Item = self.sequelize.define('Item', {
          state: { type: Sequelize.ENUM, values: ['available', 'in_cart', 'shipped'] },
          name: Sequelize.STRING
        })

        Item.sync({ force: true }).success(function() {
          Item.bulkCreate([{state: 'in_cart', name: 'A'}, { state: 'available', name: 'B'}]).success(function() {
            Item.find({ where: { state: 'available' }}).success(function(item) {
              expect(item.name).to.equal('B')
              done()
            })
          })
        })
      })
    })
  })
})


module.exports = {
  up: function(migration, DataTypes, done) {
    migration.addColumn('User', 'level', { type: DataTypes.STRING }).complete(function() {
      migration.changeColumn('User', 'level', { type: DataTypes.ENUM, allowNull: false, values: ['basic', 'advanced'] }).complete(done);
    });
  },

  down: function(migration, DataTypes, done) {
    migration.removeColumn('User', 'level').complete(done);
  }
};


module.exports = {
  up: function(migration, DataTypes, done) {
    migration.renameColumn('User', 'signature', 'sig').complete(done)
  },

  down: function(migration, DataTypes, done) {
    migration.renameColumn('User', 'sig', 'signature').complete(done)
  }
}


var chai      = require('chai')
  , expect    = chai.expect
  , Sequelize = require(__dirname + '/../index')
  , Support   = require(__dirname + '/support')

chai.config.includeStack = true

describe(Support.getTestDialectTeaser('DataTypes'), function() {
  it('should return false when comparing DECIMAL and DECIMAL(10,2)', function(done) {
    expect(Sequelize.DECIMAL).to.not.equal(Sequelize.DECIMAL(10,2))
    done()
  })

  it('DECIMAL(10,2) should be an instance of DECIMAL', function(done) {
    expect(Sequelize.DECIMAL(10,2)).to.be.an.instanceof(Sequelize.DECIMAL)
    done()
  })

  it('should return false when comparing FLOAT and FLOAT(11)', function(done) {
    expect(Sequelize.FLOAT).to.not.equal(Sequelize.FLOAT(11))
    done()
  })

  it('FLOAT(11) should be an instance of FLOAT', function(done) {
    expect(Sequelize.FLOAT(11)).to.be.an.instanceof(Sequelize.FLOAT)
    done()
  })

  it('should return false when comparing STRING and STRING(4096)', function(done) {
    expect(Sequelize.STRING).to.not.equal(Sequelize.STRING(4096))
    done()
  })

  it('STRING(4096) should be an instance of STRING', function(done) {
    expect(Sequelize.STRING(4096)).to.be.an.instanceof(Sequelize.STRING)
    done()
  })

  it('should return false when comparing BIGINT and BIGINT(11)', function(done) {
    expect(Sequelize.BIGINT).to.not.equal(Sequelize.BIGINT(11))
    done()
  })

  it('BIGINT(11) should be an instance of BIGINT', function(done) {
    expect(Sequelize.BIGINT(11)).to.be.an.instanceof(Sequelize.BIGINT)
    done()
  })

  var tests = [
    [Sequelize.STRING, 'STRING', 'VARCHAR(255)'],
    [Sequelize.STRING(1234), 'STRING(1234)', 'VARCHAR(1234)'],
    [Sequelize.STRING(1234).BINARY, 'STRING(1234).BINARY', 'VARCHAR(1234) BINARY'],
    [Sequelize.STRING.BINARY, 'STRING.BINARY', 'VARCHAR(255) BINARY'],

    [Sequelize.CHAR, 'CHAR(255)', 'CHAR(255)'],
    [Sequelize.CHAR(12), 'CHAR(12)', 'CHAR(12)'],
    [Sequelize.CHAR(12).BINARY, 'CHAR(12).BINARY', 'CHAR(12) BINARY'],
    [Sequelize.CHAR.BINARY, 'CHAR(255).BINARY', 'CHAR(255) BINARY'],

    [Sequelize.TEXT, 'TEXT', 'TEXT'],
    [Sequelize.DATE, 'DATE', 'DATETIME'],
    [Sequelize.NOW, 'NOW', 'NOW'],
    [Sequelize.UUID, 'UUID', 'UUID'],
    [Sequelize.BOOLEAN, 'BOOLEAN', 'TINYINT(1)'],

    [Sequelize.BLOB, 'BLOB', 'BLOB'],
    [Sequelize.BLOB('tiny'), 'BLOB(\'tiny\')', 'TINYBLOB'],
    [Sequelize.BLOB('medium'), 'BLOB(\'medium\')', 'MEDIUMBLOB'],
    [Sequelize.BLOB('long'), 'BLOB(\'long\')', 'LONGBLOB'],

    [Sequelize.INTEGER, 'INTEGER', 'INTEGER'],
    [Sequelize.INTEGER.UNSIGNED, 'INTEGER.UNSIGNED', 'INTEGER UNSIGNED'],
    [Sequelize.INTEGER(11), 'INTEGER(11)','INTEGER(11)'],
    [Sequelize.INTEGER(11).UNSIGNED, 'INTEGER(11).UNSIGNED', 'INTEGER(11) UNSIGNED'],
    [Sequelize.INTEGER(11).UNSIGNED.ZEROFILL,'INTEGER(11).UNSIGNED.ZEROFILL','INTEGER(11) UNSIGNED ZEROFILL'],
    [Sequelize.INTEGER(11).ZEROFILL,'INTEGER(11).ZEROFILL', 'INTEGER(11) ZEROFILL'],
    [Sequelize.INTEGER(11).ZEROFILL.UNSIGNED,'INTEGER(11).ZEROFILL.UNSIGNED', 'INTEGER(11) UNSIGNED ZEROFILL'],

    [Sequelize.BIGINT, 'BIGINT', 'BIGINT'],
    [Sequelize.BIGINT.UNSIGNED, 'BIGINT.UNSIGNED', 'BIGINT UNSIGNED'],
    [Sequelize.BIGINT(11), 'BIGINT(11)','BIGINT(11)'],
    [Sequelize.BIGINT(11).UNSIGNED, 'BIGINT(11).UNSIGNED', 'BIGINT(11) UNSIGNED'],
    [Sequelize.BIGINT(11).UNSIGNED.ZEROFILL, 'BIGINT(11).UNSIGNED.ZEROFILL','BIGINT(11) UNSIGNED ZEROFILL'],
    [Sequelize.BIGINT(11).ZEROFILL, 'BIGINT(11).ZEROFILL', 'BIGINT(11) ZEROFILL'],
    [Sequelize.BIGINT(11).ZEROFILL.UNSIGNED, 'BIGINT(11).ZEROFILL.UNSIGNED', 'BIGINT(11) UNSIGNED ZEROFILL'],

    [Sequelize.FLOAT, 'FLOAT', 'FLOAT'],
    [Sequelize.FLOAT.UNSIGNED, 'FLOAT.UNSIGNED', 'FLOAT UNSIGNED'],
    [Sequelize.FLOAT(11), 'FLOAT(11)','FLOAT(11)'],
    [Sequelize.FLOAT(11).UNSIGNED, 'FLOAT(11).UNSIGNED', 'FLOAT(11) UNSIGNED'],
    [Sequelize.FLOAT(11).UNSIGNED.ZEROFILL,'FLOAT(11).UNSIGNED.ZEROFILL','FLOAT(11) UNSIGNED ZEROFILL'],
    [Sequelize.FLOAT(11).ZEROFILL,'FLOAT(11).ZEROFILL', 'FLOAT(11) ZEROFILL'],
    [Sequelize.FLOAT(11).ZEROFILL.UNSIGNED,'FLOAT(11).ZEROFILL.UNSIGNED', 'FLOAT(11) UNSIGNED ZEROFILL'],

    [Sequelize.FLOAT(11, 12), 'FLOAT(11,12)','FLOAT(11,12)'],
    [Sequelize.FLOAT(11, 12).UNSIGNED, 'FLOAT(11,12).UNSIGNED', 'FLOAT(11,12) UNSIGNED'],
    [Sequelize.FLOAT(11, 12).UNSIGNED.ZEROFILL,'FLOAT(11,12).UNSIGNED.ZEROFILL','FLOAT(11,12) UNSIGNED ZEROFILL'],
    [Sequelize.FLOAT(11, 12).ZEROFILL,'FLOAT(11,12).ZEROFILL', 'FLOAT(11,12) ZEROFILL'],
    [Sequelize.FLOAT(11, 12).ZEROFILL.UNSIGNED,'FLOAT(11,12).ZEROFILL.UNSIGNED', 'FLOAT(11,12) UNSIGNED ZEROFILL'],

    [Sequelize.DECIMAL, 'DECIMAL', 'DECIMAL'],
    [Sequelize.DECIMAL(10,2), 'DECIMAL(10,2)','DECIMAL(10,2)']
  ]

  tests.forEach(function(test) {
    it('transforms "' + test[1] + '" to "' + test[2] + '"', function(done) {
      expect(test[0].toString()).to.equal(test[2])
      done()
    })
  })
})


it("should release transaction when meeting errors", function(done){

keep keep keep keep replace keep keep keep

it("should release transaction when meeting errors", function(){


'use strict';

var fs = require('fs')
  , moment = require('moment');

var Utils = require(__dirname + '/utils')
  , Migration = require(__dirname + '/migration')
  , DataTypes = require(__dirname + '/data-types');

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize;
    this.options = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: this.sequelize.log.bind(this.sequelize),
      filesFilter: /\.js$/
    }, options || {});
  };

  Object.defineProperty(Migrator.prototype, 'queryInterface', {
    get: function() {
      return this.sequelize.getQueryInterface();
    }
  });

  Migrator.prototype.migrate = function(options) {
    var self = this;

    options = Utils._.extend({
      method: 'up'
    }, options || {});

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if (err) {
          emitter.emit('error', err);
        } else {
          var chainer = new Utils.QueryChainer()
            , from = migrations[0];

          if (options.method === 'down') {
            migrations.reverse();
          }

          if (migrations.length === 0) {
            self.options.logging('There are no pending migrations.');
          } else {
            self.options.logging('Running migrations...');
          }

          migrations.forEach(function(migration) {
            var migrationTime;

            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                self.options.logging(migration.filename);
                migrationTime = process.hrtime();
              },

              after: function(migration) {
                migrationTime = process.hrtime(migrationTime);
                migrationTime = Math.round((migrationTime[0] * 1000) + (migrationTime[1] / 1000000));

                self.options.logging('Completed in ' + migrationTime + 'ms');
              },

              success: function(migration, callback) {
                if (options.method === 'down') {
                  deleteUndoneMigration.call(self, from, migration, callback);
                } else {
                  saveSuccessfulMigration.call(self, from, migration, callback);
                }
              }
            });
          });

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null); })
            .error(function(err) { emitter.emit('error', err); });
        }
      });
    }).run();
  };

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this;

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options); });
      callback && callback(null, result);
    };
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options); });
      callback && callback(null, result);
    };

    var filterByMigrationId = function(migrations, migrationIds, callback) {
      var result = migrations.filter(function(migration) { return migrationIds.indexOf(migration.migrationId) === -1;
      });
      callback && callback(null, result);
    };

    var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
      return self.options.filesFilter.test(file);
    });

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file);
    });

    migrations = migrations.sort(function(a, b) {
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0]);
    });

    var existingMigrationIds = migrations.map(function(migration) {
      return migration.migrationId;
    });

    var getPendingMigrations = function(callback) {
      getAllMigrationIdsFromDatabase.call(self).success(function(allMigrationIds) {
        allMigrationIds = allMigrationIds.map(function(migration) {
          return parseInt(migration.to);
        });
        if (allMigrationIds) {
          filterByMigrationId(migrations, allMigrationIds, callback);
        }

      });
    };

    if (this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if (self.options.to) {
          filterTo(migrations, self.options.to, callback);
        } else {
          callback && callback(null, migrations);
        }
      });
    } else {
      getPendingMigrations(function(err, pendingMigrations) {
        if (self.options.to) {
          filterTo(pendingMigrations, self.options.to, callback);
        }
        else {
          callback && callback(err, pendingMigrations);
        }
      });
    }
  };

  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
    var self = this;

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedDAO = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
        , SequelizeMeta = storedDAO;

      if (!storedDAO) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to: DataTypes.STRING
        }, {
          timestamps: false
        });
      }

      // force sync when model has newly created or if syncOptions are passed
      if (!storedDAO || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta); })
          .error(function(err) { emitter.emit('error', err); });
      } else {
        emitter.emit('success', SequelizeMeta);
      }
    }).run();
  };

  /**
   * Explicitly executes one or multiple migrations.
   *
   * @param filename {String|Array} Absolute filename(s) of the migrations script
   * @param options  {Object}       Can contain three functions, before, after and success, which are executed before
   *                                or after each migration respectively, with one parameter, the migration.
   */
  Migrator.prototype.exec = function(filename, options) {

    var self = this;
    return new Utils.CustomEventEmitter(function(emitter) {
      var chainer = new Utils.QueryChainer();
      var addMigration = function(filename) {
        var migration = new Migration(self, filename);

        self.options.logging('Adding migration script at ' + filename);

        chainer.add(migration, 'execute', [{ method: 'up' }], {
          before: function(migration) {
            if (options && Utils._.isFunction(options.before)) {
              options.before.call(self, migration);
            }
          },
          after: function(migration) {
            if (options && Utils._.isFunction(options.after)) {
              options.after.call(self, migration);
            }
          },
          success: function(migration, callback) {
            if (options && Utils._.isFunction(options.success)) {
              options.success.call(self, migration);
            }
            callback();
          }
        });
      };

      if (Utils._.isArray(filename)) {
        Utils._.each(filename, function(f) {
          addMigration(f);
        });
      } else {
        addMigration(filename);
      }

      chainer
        .runSerially({ skipOnError: true })
        .success(function() { emitter.emit('success', null); })
        .error(function(err) { emitter.emit('error', err); });
    }).run();
  };

  // private

  var getAllMigrationIdsFromDatabase = Migrator.prototype.getAllMigrationIdsFromDatabase = function() {
    var self = this;

    return new Utils.CustomEventEmitter(function(emitter) {
      self
        .findOrCreateSequelizeMetaDAO()
        .success(function(SequelizeMeta) {
          SequelizeMeta
            .findAll({
              order: 'id DESC',
              attributes: ['to']
            })
            .success(function(meta) {
              emitter.emit('success', meta ? meta : null);
            })
            .error(function(err) {
              emitter.emit('error', err);
            });
        })
        .error(function(err) {
          emitter.emit('error', err);
        });
    }).run();
  };

  var getLastMigrationFromDatabase = Migrator.prototype.getLastMigrationFromDatabase = function() {
    var self = this;

    return new Utils.CustomEventEmitter(function(emitter) {
      self
        .findOrCreateSequelizeMetaDAO()
        .success(function(SequelizeMeta) {
          SequelizeMeta
            .find({ order: 'id DESC' })
            .success(function(meta) {
              emitter.emit('success', meta ? meta : null);
            })
            .error(function(err) {
              emitter.emit('error', err);
            });
        })
        .error(function(err) {
          emitter.emit('error', err);
        });
    }).run();
  };

  var getLastMigrationIdFromDatabase = Migrator.prototype.getLastMigrationIdFromDatabase = function() {
    var self = this;

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase
        .call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null);
        })
        .error(function(err) {
          emitter.emit('error', err);
        });
    }).run();
  };

  var getFormattedDateString = Migrator.prototype.getFormattedDateString = function(s) {
    var result = null;

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-');
    } catch (e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!');
    }

    return result;
  };

  var stringToDate = Migrator.prototype.stringToDate = function(s) {
    return moment(getFormattedDateString(s), 'YYYYMMDDHHmmss');
  };

  var saveSuccessfulMigration = Migrator.prototype.saveSuccessfulMigration = function(from, to, callback) {
    var self = this;

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback);
    });
  };

  var deleteUndoneMigration = Migrator.prototype.deleteUndoneMigration = function(from, to, callback) {
    var self = this;

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
        .success(function(meta) {
          meta.destroy().success(callback);
        });
    });
  };

  return Migrator;
})();


/* jshint camelcase: false */
var chai      = require('chai')
  , expect    = chai.expect
  , Support   = require(__dirname + '/../support')
  , dialect   = Support.getTestDialect()
  , hstore    = require(__dirname + '/../../lib/dialects/postgres/hstore')

chai.config.includeStack = true

if (dialect.match(/^postgres/)) {
  describe('[POSTGRES Specific] hstore', function() {
    describe('stringifyPart', function() {
      it("handles undefined values correctly", function(done) {
        expect(hstore.stringifyPart(undefined)).to.equal('NULL')
        done()
      })

      it("handles null values correctly", function(done) {
        expect(hstore.stringifyPart(null)).to.equal('NULL')
        done()
      })

      it("handles boolean values correctly", function(done) {
        expect(hstore.stringifyPart(false)).to.equal('false')
        expect(hstore.stringifyPart(true)).to.equal('true')
        done()
      })

      it("handles strings correctly", function(done) {
        expect(hstore.stringifyPart('foo')).to.equal('"foo"')
        done()
      })

      it("handles strings with backslashes correctly", function(done) {
        expect(hstore.stringifyPart("\\'literally\\'")).to.equal('"\\\\\'literally\\\\\'"')
        done()
      })

      it("handles arrays correctly", function(done) {
        expect(hstore.stringifyPart([1,['2'],'"3"'])).to.equal('"[1,[\\"2\\"],\\"\\\\\\"3\\\\\\"\\"]"')
        done()
      })

      it("handles simple objects correctly", function(done) {
        expect(hstore.stringifyPart({ test: 'value' })).to.equal('"{\\"test\\":\\"value\\"}"')
        done()
      })

      it("handles nested objects correctly", function(done) {
        expect(hstore.stringifyPart({ test: { nested: 'value' } })).to.equal('"{\\"test\\":{\\"nested\\":\\"value\\"}}"')
        done()
      })

      it("handles objects correctly", function(done) {
        expect(hstore.stringifyPart({test: {nested: {value: {including: '"string"'}}}})).to.equal('"{\\"test\\":{\\"nested\\":{\\"value\\":{\\"including\\":\\"\\\\\\"string\\\\\\"\\"}}}}"')
        done()
      })
    })

    describe('stringify', function() {
      it('should handle empty objects correctly', function(done) {
        expect(hstore.stringify({ })).to.equal('')
        done()
      })

      it('should handle null values correctly', function(done) {
        expect(hstore.stringify({ null: null })).to.equal('"null"=>NULL')
        done()
      })

      it('should handle simple objects correctly', function(done) {
        expect(hstore.stringify({ test: 'value' })).to.equal('"test"=>"value"')
        done()
      })

      it('should handle arrays correctly', function(done) {
        expect(hstore.stringify([{ test: 'value' }, { another: 'val' }])).to.deep.equal(['\"test\"=>\"value\"','\"another\"=>\"val\"'])
        done()
      });

      it('should handle nested objects correctly', function(done) {
        expect(hstore.stringify({ test: { nested: 'value' } })).to.equal('"test"=>"{\\"nested\\":\\"value\\"}"')
        done()
      })

      it('should handle nested arrays correctly', function(done) {
        expect(hstore.stringify({ test: [ 1, '2', [ '"3"' ] ] })).to.equal('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')
        done()
      })

      it('should handle multiple keys with different types of values', function(done) {
        expect(hstore.stringify({ true: true, false: false, null: null, undefined: undefined, integer: 1, array: [1,'2'], object: { object: 'value' }})).to.equal('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')
        done()
      })
    })

    describe('parse', function() {
      it('should handle null objects correctly', function(done) {
        expect(hstore.parse(null)).to.equal(null)
        done()
      })

      it('should handle empty objects correctly', function(done) {
        expect(hstore.parse('')).to.deep.equal({ })
        done()
      })

      it('should handle simple objects correctly', function(done) {
        expect(hstore.parse('"test"=>"value"')).to.deep.equal({ test: 'value' })
        done()
      })

      it('should handle arrays correctly', function(done) {
        expect(hstore.parse('{"\\"test\\"=>\\"value\\"","\\"another\\"=>\\"val\\""}')).to.deep.equal([{ test: 'value' }, { another: 'val' }])
        done()
      })

      it('should handle nested objects correctly', function(done) {
        expect(hstore.parse('"test"=>"{\\"nested\\":\\"value\\"}"')).to.deep.equal({ test: { nested: 'value' } })
        done()
      })

      it('should handle nested arrays correctly', function(done) {
        expect(hstore.parse('"test"=>"[1,\\"2\\",[\\"\\\\\\"3\\\\\\"\\"]]"')).to.deep.equal({ test: [ 1, '2', [ '"3"' ] ] })
        done()
      })

      it('should handle multiple keys with different types of values', function(done) {
        expect(hstore.parse('"true"=>true,"false"=>false,"null"=>NULL,"undefined"=>NULL,"integer"=>1,"array"=>"[1,\\"2\\"]","object"=>"{\\"object\\":\\"value\\"}"')).to.deep.equal({ true: true, false: false, null: null, undefined: null, integer: "1", array: [1,'2'], object: { object: 'value' }})
        done()
      })
    })
  })
}


"use strict";

/* jshint camelcase: false */
var chai      = require('chai')
  , sinon     = require('sinon')
  , expect    = chai.expect
  , Support   = require(__dirname + '/support')
  , Sequelize = Support.Sequelize
  , Promise   = Sequelize.Promise;

chai.config.includeStack = true;

describe(Support.getTestDialectTeaser("Sequelize Errors"), function () {
  describe('API Surface', function() {
    it('Should have the Error constructors exposed', function() {
      expect(Sequelize).to.have.property('Error');
      expect(Sequelize).to.have.property('ValidationError');
      var sequelize = new Sequelize();
      expect(sequelize).to.have.property('Error');
      expect(sequelize).to.have.property('ValidationError');
    });
    it('Sequelize Errors instances should be instances of Error', function() {
      var error = new Sequelize.Error();
      var validationError = new Sequelize.ValidationError();

      var sequelize = new Sequelize();
      var instError = new sequelize.Error();
      var instValidationError = new sequelize.ValidationError();

      expect(error).to.be.instanceOf(Sequelize.Error);
      expect(error).to.be.instanceOf(Error);
      expect(error).to.have.property('name', 'SequelizeBaseError');

      expect(validationError).to.be.instanceOf(Sequelize.ValidationError);
      expect(validationError).to.be.instanceOf(Error);
      expect(validationError).to.have.property('name', 'SequelizeValidationError');

      expect(instError).to.be.instanceOf(Sequelize.Error);
      expect(instError).to.be.instanceOf(Error);
      expect(instValidationError).to.be.instanceOf(Sequelize.ValidationError);
      expect(instValidationError).to.be.instanceOf(Error);
    });
    it('SequelizeValidationError should find errors by path', function() {
      var errorItems = [
        new Sequelize.ValidationErrorItem('invalid', 'type', 'first_name', null),
        new Sequelize.ValidationErrorItem('invalid', 'type', 'last_name', null)
      ];
      var validationError = new Sequelize.ValidationError('Validation error', errorItems);
      expect(validationError).to.have.property('get');
      expect(validationError.get).to.be.a('function');

      var matches = validationError.get('first_name');
      expect(matches).to.be.instanceOf(Array);
      expect(matches).to.have.lengthOf(1);
      expect(matches[0]).to.have.property('message', 'invalid');
    });
  });

  describe('Constraint error', function () {
    it('Can be intercepted using .catch', function () {
      var spy = sinon.spy()
        , User = this.sequelize.define('user', {
          first_name: {
            type: Sequelize.STRING,
            unique: 'unique_name'
          },
          last_name: {
            type: Sequelize.STRING,
            unique: 'unique_name'
          }
        });

      return this.sequelize.sync({ force: true }).bind(this).then(function () {
        return User.create({ first_name: 'jan', last_name: 'meier' });
      }).then(function () {
        return User.create({ first_name: 'jan', last_name: 'meier' }).catch(this.sequelize.UniqueConstraintError, spy);
      }).then(function () {
        expect(spy).to.have.been.calledOnce;
      });
    });
  });
});


var self = this
var test = function(times) {
times = times || 0
if (times > 10) {


/* jshint camelcase: false */
/* jshint expr: true */
var chai      = require('chai')
  , expect    = chai.expect
  , Support   = require(__dirname + '/../support')
  , DataTypes = require(__dirname + "/../../lib/data-types")
  , datetime  = require('chai-datetime')

chai.use(datetime)
chai.config.includeStack = true

describe(Support.getTestDialectTeaser("Include"), function () {
  describe('findAndCountAll', function () {

    it( 'Try to include a required model. Result rows should match count', function ( done ) {
      var DT = DataTypes,
          S = this.sequelize,
          User = S.define('User', { name: DT.STRING(40) }, { paranoid: true }),
          SomeConnection = S.define('SomeConnection', {
            m: DT.STRING(40),
            fk: DT.INTEGER,
            u: DT.INTEGER,
          }, { paranoid: true }),
          A = S.define('A', { name: DT.STRING(40) }, { paranoid: true }),
          B = S.define('B', { name: DT.STRING(40) }, { paranoid: true }),
          C = S.define('C', { name: DT.STRING(40) }, { paranoid: true })

      // Associate them
      User.hasMany( SomeConnection, { foreignKey: 'u' })

      SomeConnection.belongsTo( User, { foreignKey: 'u' })
      SomeConnection.belongsTo( A, { foreignKey: 'fk', constraints: false })
      SomeConnection.belongsTo( B, { foreignKey: 'fk', constraints: false })
      SomeConnection.belongsTo( C, { foreignKey: 'fk', constraints: false })

      A.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })
      B.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })
      C.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })

      // Sync them
      S.sync({ force: true }).done( function ( err ) { expect( err ).not.to.be.ok;

        // Create an enviroment
        User.bulkCreate([
          { name: 'Youtube' },
          { name: 'Facebook' },
          { name: 'Google' },
          { name: 'Yahoo' },
          { name: '404' }
        ]).done( function ( err, users ) { expect( err ).not.to.be.ok; expect( users ).to.be.length( 5 )

        SomeConnection.bulkCreate([ // Lets count, m: A and u: 1
          { u: 1, m: 'A', fk: 1 }, // 1  // Will be deleted
          { u: 2, m: 'A', fk: 1 },
          { u: 3, m: 'A', fk: 1 },
          { u: 4, m: 'A', fk: 1 },
          { u: 5, m: 'A', fk: 1 },
          { u: 1, m: 'B', fk: 1 },
          { u: 2, m: 'B', fk: 1 },
          { u: 3, m: 'B', fk: 1 },
          { u: 4, m: 'B', fk: 1 },
          { u: 5, m: 'B', fk: 1 },
          { u: 1, m: 'C', fk: 1 },
          { u: 2, m: 'C', fk: 1 },
          { u: 3, m: 'C', fk: 1 },
          { u: 4, m: 'C', fk: 1 },
          { u: 5, m: 'C', fk: 1 },
          { u: 1, m: 'A', fk: 2 }, // 2 // Will be deleted
          { u: 4, m: 'A', fk: 2 },
          { u: 2, m: 'A', fk: 2 },
          { u: 1, m: 'A', fk: 3 }, // 3
          { u: 2, m: 'A', fk: 3 },
          { u: 3, m: 'A', fk: 3 },
          { u: 2, m: 'B', fk: 2 },
          { u: 1, m: 'A', fk: 4 }, // 4
          { u: 4, m: 'A', fk: 2 },
        ]).done( function ( err, conns ) { expect( err ).not.to.be.ok; expect( conns ).to.be.length( 24 )

        A.bulkCreate([
          { name: 'Just' },
          { name: 'for' },
          { name: 'testing' },
          { name: 'proposes' },
          { name: 'only' },
        ]).done( function ( err, as ) { expect( err ).not.to.be.ok; expect( as ).to.be.length( 5 )

        B.bulkCreate([
          { name: 'this should not' },
          { name: 'be loaded' }
        ]).done( function ( err, bs ) { expect( err ).not.to.be.ok; expect( bs ).to.be.length( 2 )

        C.bulkCreate([
          { name: 'because we only want A' }
        ]).done( function ( err, cs ) { expect( err ).not.to.be.ok; expect( cs ).to.be.length( 1 )

          // Delete some of conns to prove the concept
          SomeConnection.destroy({
            m: 'A',
            u: 1,
            fk: [ 1, 2 ],
          }).done( function ( err ) { expect( err ).not.to.be.ok

            // Last and most important queries ( we connected 4, but deleted 2, witch means we must get 2 only )
            A.findAndCountAll({

              include: [{
                model: SomeConnection, required: true,
                where: {
                  m: 'A', // Pseudo Polymorphy
                  u: 1
                }
              }],

              limit: 5,

            })
            .done( function ( err, result ) {

              // Test variables
              expect( err ).not.to.be.ok
              expect( result.count ).to.be.equal( 2 )
              expect( result.rows.length ).to.be.equal( 2 )

              done()

            // Last and most important queries - END
            })

          // Delete some of conns to prove the concept - END
          })

        // Create an enviroment - END
        })
        })
        })
        })
        })

      // Sync them - END
      })

    })

  })
})


'use strict';

var Utils = require('../../utils')
  , DataTypes = require('../../data-types')
  , util = require('util');

module.exports = (function() {
  var QueryGenerator = {
    dialect: 'mysql',

    createSchema: function() {
      var query = 'SHOW TABLES';
      return Utils._.template(query)({});
    },

    dropSchema: function(tableName, options) {
      return QueryGenerator.dropTableQuery(tableName, options);
    },

    showSchemasQuery: function() {
      return 'SHOW TABLES';
    },

    createTableQuery: function(tableName, attributes, options) {
      options = Utils._.extend({
        engine: 'InnoDB',
        charset: null
      }, options || {});

      var self = this;

      var query = 'CREATE TABLE IF NOT EXISTS <%= table %> (<%= attributes%>)<%= comment %> ENGINE=<%= engine %> <%= charset %> <%= collation %>'
        , primaryKeys = []
        , foreignKeys = {}
        , attrStr = [];

      for (var attr in attributes) {
        if (attributes.hasOwnProperty(attr)) {
          var dataType = this.mysqlDataTypeMapping(tableName, attr, attributes[attr])
            , match;

          if (Utils._.includes(dataType, 'PRIMARY KEY')) {
            primaryKeys.push(attr);

            if (Utils._.includes(dataType, 'REFERENCES')) {
               // MySQL doesn't support inline REFERENCES declarations: move to the end
              match = dataType.match(/^(.+) (REFERENCES.*)$/);
              attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1].replace(/PRIMARY KEY/, ''));
              foreignKeys[attr] = match[2];
            } else {
              attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType.replace(/PRIMARY KEY/, ''));
            }
          } else if (Utils._.includes(dataType, 'REFERENCES')) {
            // MySQL doesn't support inline REFERENCES declarations: move to the end
            match = dataType.match(/^(.+) (REFERENCES.*)$/);
            attrStr.push(this.quoteIdentifier(attr) + ' ' + match[1]);
            foreignKeys[attr] = match[2];
          } else {
            attrStr.push(this.quoteIdentifier(attr) + ' ' + dataType);
          }
        }
      }

      var values = {
        table: this.quoteTable(tableName),
        attributes: attrStr.join(', '),
        comment: options.comment && Utils._.isString(options.comment) ? ' COMMENT ' + this.escape(options.comment) : '',
        engine: options.engine,
        charset: (options.charset ? 'DEFAULT CHARSET=' + options.charset : ''),
        collation: (options.collate ? 'COLLATE ' + options.collate : '')
      }
      , pkString = primaryKeys.map(function(pk) { return this.quoteIdentifier(pk); }.bind(this)).join(', ');

      if (!!options.uniqueKeys) {
        Utils._.each(options.uniqueKeys, function(columns, indexName) {
          if (!Utils._.isString(indexName)) {
            indexName = 'uniq_' + tableName + '_' + columns.fields.join('_');
          }
          values.attributes += ', UNIQUE ' + indexName + ' (' + Utils._.map(columns.fields, self.quoteIdentifier).join(', ') + ')';
        });
      }

      if (pkString.length > 0) {
        values.attributes += ', PRIMARY KEY (' + pkString + ')';
      }

      for (var fkey in foreignKeys) {
        if (foreignKeys.hasOwnProperty(fkey)) {
          values.attributes += ', FOREIGN KEY (' + this.quoteIdentifier(fkey) + ') ' + foreignKeys[fkey];
        }
      }

      return Utils._.template(query)(values).trim() + ';';
    },

    showTablesQuery: function() {
      return 'SHOW TABLES;';
    },

    uniqueConstraintMapping: {
      code: 'ER_DUP_ENTRY',
      map: function(str) {
        // we're manually remvoving uniq_ here for a future capability of defining column names explicitly
        var match = str.replace('uniq_', '').match(/Duplicate entry .* for key '(.*?)'$/);
        if (match === null || match.length < 2) {
          return false;
        }

        return {
          indexName: match[1],
          fields: match[1].split('_')
        };
      }
    },

    addColumnQuery: function(tableName, attributes) {
      var query = 'ALTER TABLE `<%= tableName %>` ADD <%= attributes %>;'
        , attrString = [];

      for (var attrName in attributes) {
        var definition = attributes[attrName];

        attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({
          attrName: attrName,
          definition: this.mysqlDataTypeMapping(tableName, attrName, definition)
        }));
      }

      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') });
    },

    removeColumnQuery: function(tableName, attributeName) {
      var query = 'ALTER TABLE `<%= tableName %>` DROP `<%= attributeName %>`;';
      return Utils._.template(query)({ tableName: tableName, attributeName: attributeName });
    },

    changeColumnQuery: function(tableName, attributes) {
      var query = 'ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;';
      var attrString = [];

      for (var attrName in attributes) {
        var definition = attributes[attrName];

        attrString.push(Utils._.template('`<%= attrName %>` `<%= attrName %>` <%= definition %>')({
          attrName: attrName,
          definition: definition
        }));
      }

      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') });
    },

    renameColumnQuery: function(tableName, attrBefore, attributes) {
      var query = 'ALTER TABLE `<%= tableName %>` CHANGE <%= attributes %>;';
      var attrString = [];

      for (var attrName in attributes) {
        var definition = attributes[attrName];

        attrString.push(Utils._.template('`<%= before %>` `<%= after %>` <%= definition %>')({
          before: attrBefore,
          after: attrName,
          definition: definition
        }));
      }

      return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') });
    },

    bulkInsertQuery: function(tableName, attrValueHashes, options) {
      var query = 'INSERT<%= ignoreDuplicates %> INTO <%= table %> (<%= attributes %>) VALUES <%= tuples %>;'
        , tuples = []
        , allAttributes = [];

      Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
        Utils._.forOwn(attrValueHash, function(value, key, hash) {
          if (allAttributes.indexOf(key) === -1) allAttributes.push(key);
        });
      });

      Utils._.forEach(attrValueHashes, function(attrValueHash, i) {
        tuples.push('(' +
          allAttributes.map(function(key) {
            return this.escape(attrValueHash[key]);
          }.bind(this)).join(',') +
        ')');
      }.bind(this));

      var replacements = {
        ignoreDuplicates: options && options.ignoreDuplicates ? ' IGNORE' : '',
        table: this.quoteTable(tableName),
        attributes: allAttributes.map(function(attr) {
                      return this.quoteIdentifier(attr);
                    }.bind(this)).join(','),
        tuples: tuples
      };

      return Utils._.template(query)(replacements);
    },

    deleteQuery: function(tableName, where, options) {
      options = options || {};

      var table = this.quoteTable(tableName);
      if (options.truncate === true) {
        // Truncate does not allow LIMIT and WHERE
        return 'TRUNCATE ' + table;
      }

      where = this.getWhereConditions(where);
      var limit = '';

      if (Utils._.isUndefined(options.limit)) {
        options.limit = 1;
      }

      if (!!options.limit) {
        limit = ' LIMIT ' + this.escape(options.limit);
      }

      return 'DELETE FROM ' + table + ' WHERE ' + where + limit;
    },

    showIndexQuery: function(tableName, options) {
      var sql = 'SHOW INDEX FROM <%= tableName %><%= options %>';
      return Utils._.template(sql)({
        tableName: this.quoteIdentifiers(tableName),
        options: (options || {}).database ? ' FROM `' + options.database + '`' : ''
      });
    },

    removeIndexQuery: function(tableName, indexNameOrAttributes) {
      var sql = 'DROP INDEX <%= indexName %> ON <%= tableName %>'
        , indexName = indexNameOrAttributes;

      if (typeof indexName !== 'string') {
        indexName = Utils.inflection.underscore(tableName + '_' + indexNameOrAttributes.join('_'));
      }

      return Utils._.template(sql)({ tableName: this.quoteIdentifiers(tableName), indexName: indexName });
    },

    attributesToSQL: function(attributes) {
      var result = {};

      for (var name in attributes) {
        var dataType = attributes[name];
        var fieldName = dataType.field || name;

        if (Utils._.isPlainObject(dataType)) {
          var template;

          if (dataType.type.toString() === DataTypes.ENUM.toString()) {
            if (Array.isArray(dataType.values) && (dataType.values.length > 0)) {
              template = 'ENUM(' + Utils._.map(dataType.values, function(value) {
                return this.escape(value);
              }.bind(this)).join(', ') + ')';
            } else {
              throw new Error('Values for ENUM haven\'t been defined.');
            }
          } else {
            template = dataType.type.toString();
          }

          if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) {
            template += ' NOT NULL';
          }

          if (dataType.autoIncrement) {
            template += ' auto_increment';
          }

          // Blobs/texts cannot have a defaultValue
          if (dataType.type !== 'TEXT' && dataType.type._binary !== true && Utils.defaultValueSchemable(dataType.defaultValue)) {
            template += ' DEFAULT ' + this.escape(dataType.defaultValue);
          }

          if (dataType.unique === true) {
            template += ' UNIQUE';
          }

          if (dataType.primaryKey) {
            template += ' PRIMARY KEY';
          }

          if (dataType.comment && Utils._.isString(dataType.comment) && dataType.comment.length) {
            template += ' COMMENT ' + this.escape(dataType.comment);
          }

          if (dataType.references) {
            template += ' REFERENCES ' + this.quoteTable(dataType.references);

            if (dataType.referencesKey) {
              template += ' (' + this.quoteIdentifier(dataType.referencesKey) + ')';
            } else {
              template += ' (' + this.quoteIdentifier('id') + ')';
            }

            if (dataType.onDelete) {
              template += ' ON DELETE ' + dataType.onDelete.toUpperCase();
            }

            if (dataType.onUpdate) {
              template += ' ON UPDATE ' + dataType.onUpdate.toUpperCase();
            }

          }

          result[fieldName] = template;
        } else {
          result[fieldName] = dataType;
        }
      }

      return result;
    },

    findAutoIncrementField: function(factory) {
      var fields = [];

      for (var name in factory.attributes) {
        if (factory.attributes.hasOwnProperty(name)) {
          var definition = factory.attributes[name];

          if (definition && definition.autoIncrement) {
            fields.push(name);
          }
        }
      }

      return fields;
    },

    addLimitAndOffset: function(options, query) {
      query = query || '';
      if (options.offset && !options.limit) {
        query += ' LIMIT ' + options.offset + ', ' + 18440000000000000000;
      } else if (options.limit) {
        if (options.offset) {
          query += ' LIMIT ' + options.offset + ', ' + options.limit;
        } else {
          query += ' LIMIT ' + options.limit;
        }
      }
      return query;
    },

    quoteIdentifier: function(identifier, force) {
      if (identifier === '*') return identifier;
      return Utils.addTicks(identifier, '`');
    },
    /**
     * Generates an SQL query that returns all foreign keys of a table.
     *
     * @param  {String} tableName  The name of the table.
     * @param  {String} schemaName The name of the schema.
     * @return {String}            The generated sql query.
     */
    getForeignKeysQuery: function(tableName, schemaName) {
      return "SELECT CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '" + tableName + "' AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='" + schemaName + "' AND REFERENCED_TABLE_NAME IS NOT NULL;";
    },

    /**
     * Generates an SQL query that removes a foreign key from a table.
     *
     * @param  {String} tableName  The name of the table.
     * @param  {String} foreignKey The name of the foreign key constraint.
     * @return {String}            The generated sql query.
     */
    dropForeignKeyQuery: function(tableName, foreignKey) {
      return 'ALTER TABLE ' + this.quoteTable(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';';
    },

    mysqlDataTypeMapping: function(tableName, attr, dataType) {
      if (Utils._.includes(dataType, 'UUID')) {
        dataType = dataType.replace(/UUID/, 'CHAR(36) BINARY');
      }

      return dataType;
    }
  };

  return Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator);
})();


'use strict';

var _ = require('lodash')
  , Abstract = require('../abstract')
  , ConnectionManager = require('./connection-manager')
  , Query = require('./query');

var SqliteDialect = function(sequelize) {
  this.sequelize = sequelize;
  this.connectionManager = new ConnectionManager(this, sequelize);
};

SqliteDialect.prototype.supports = _.merge(_.cloneDeep(Abstract.prototype.supports), {
  'DEFAULT': false,
  'DEFAULT VALUES': true,
  index: {
    using: false
  }
});

SqliteDialect.prototype.Query = Query;
SqliteDialect.prototype.name = 'sqlite';

module.exports = SqliteDialect;


'use strict';

var Promise = require('sequelize-bluebird')
  , EventEmitter = require('events').EventEmitter
  , proxyEventKeys = ['success', 'error', 'sql']
  , Utils = require('./utils');

/**
 * A slightly modified version of bluebird promises. This means that, on top of the methods below, you can also call all the methods listed on the link below.
 *
 * The main difference is that sequelize promises allows you to attach a listener that will be called with the generated SQL, each time a query is run.
 *
 * @mixes https://github.com/petkaantonov/bluebird/blob/master/API.md
 * @class Promise
 */
var SequelizePromise = Promise;

/**
 * Listen for events, event emitter style. Mostly for backwards compat. with EventEmitter
 *
 * @param {String} evt
 * @param {Function} fct
 */
SequelizePromise.prototype.on = function(evt, fct) {
  if (evt === 'success') {
    this.then(fct);
  }
  else if (evt === 'error') {
    this.then(null, fct);
  }
  else {
    EventEmitter.prototype.on.call(this, evt, fct);
  }

  return this;
};

/**
 * Emit an event from the emitter
 * @param {string} type The type of event
 * @param {any}    value(s)* All other arguments will be passed to the event listeners
 */
SequelizePromise.prototype.emit = function(evt) {
  var args = arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : [];

  if (evt === 'success') {
    this.seqResolve.apply(this, args);
  } else if (evt === 'error') {
    this.seqReject.apply(this, args);
  } else {
    // Needed to transfer sql across .then() calls
    if (evt === 'sql') {
      this.$sql.push(args[0]);
    }

    EventEmitter.prototype.emit.apply(this, [evt].concat(args));
  }

  return this;
};

/**
 * Listen for success events.
 *
 * ```js
 * promise.success(function (result) {
 *  //...
 * });
 * ```
 *
 * @param {function} onSuccess
 * @method success
 * @alias ok
 * @return this
 */
SequelizePromise.prototype.success =
SequelizePromise.prototype.ok = function(fct) {
  if (fct.length > 1) {
    return this.spread(fct);
  } else {
    return this.then(fct);
  }
};

/**
 * Listen for error events
 *
 * ```js
 * promise.error(function (err) {
 *  //...
 * });
 * ```
 *
 * @param {function} onError
 * @method error
 * @alias fail
 * @alias failure
 * @return this
 */
SequelizePromise.prototype.failure =
SequelizePromise.prototype.fail =
SequelizePromise.prototype.error = function(fct) {
  return this.then(null, fct);
};

/**
* Listen for both success and error events.
*
* ```js
* promise.done(function (err, result) {
*  //...
* });
* ```
*
* @param {function} onDone
* @method done
* @alias complete
* @return this
*/
SequelizePromise.prototype.done =
SequelizePromise.prototype.complete = function(fct) {
  if (fct.length > 2) {
    return this.spread(function() {
      fct.apply(null, [null].concat(Array.prototype.slice.call(arguments)));
    }, fct);
  } else {
    return this.then(function() {
      fct.apply(null, [null].concat(Array.prototype.slice.call(arguments)));
    }, fct);
  }
};

/*
 * Attach a function that is called every time the function that created this emitter executes a query.
 * @param {function} onSQL
 * @return this
 */
SequelizePromise.prototype.sql = function(fct) {
  this.on('sql', fct);
  return this;
};

/**
 * Proxy every event of this promise to another one.
 *
 * @param  {SequelizePromise} promise The promise that should receive the events.
 * @param  {Object}           [options]
 * @param  {Array}            [options.events] An array of the events to proxy. Defaults to sql, error and success
 * @return this
 */
SequelizePromise.prototype.proxy = function(promise, options) {
  options = Utils._.extend({
    events: proxyEventKeys,
    skipEvents: []
  }, options || {});

  options.events = Utils._.difference(options.events, options.skipEvents);

  options.events.forEach(function(eventKey) {
    this.on(eventKey, function() {
      var args = [eventKey].concat([].slice.apply(arguments));
      promise.emit.apply(promise, args);
    });
  }.bind(this));

  return this;
};

SequelizePromise.prototype.proxySql = function(promise) {
  return this.proxy(promise, {
    events: ['sql']
  });
};

module.exports = SequelizePromise;


docs
examples
test
README.md
.watchr.js
changelog.md
Makefile
coverage*


{
  "bitwise":false,
  "boss":true,
  "expr":true,
  "camelcase":false,
  "curly":false,
  "eqeqeq":true,
  "freeze":true,
  "immed":true,
  "indent":2,
  "latedef":"nofunc",
  "laxbreak":true,
  "laxcomma":true,
  "newcap":true,
  "noarg":true,
  "node":true,
  "strict":true,
  "trailing":true,
  "undef":true,
  "esnext":false,
  "sub":true,

  /* questionable */
  "loopfunc":true,

  "globals": {
    "Promise": true
  },

  "predef": [
    "alert",
    "describe",
    "it",
    "before",
    "beforeEach",
    "after",
    "afterEach"
  ]
}


/*without name, trigger ValidationError */

keep replace replace replace keep keep keep keep


'use strict';

var Utils = require('./../utils')
  , Helpers = require('./helpers')
  , Transaction = require('../transaction');

module.exports = (function() {
  var HasOne = function(srcDAO, targetDAO, options) {
    this.associationType = 'HasOne';
    this.source = srcDAO;
    this.target = targetDAO;
    this.options = options;
    this.isSingleAssociation = true;
    this.isSelfAssociation = (this.source === this.target);
    this.as = this.options.as;

    if (Utils._.isObject(this.options.foreignKey)) {
      this.foreignKeyAttribute = this.options.foreignKey;
      this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
    } else {
      this.foreignKeyAttribute = {};
      this.foreignKey = this.options.foreignKey;
    }

    if (this.as) {
      this.isAliased = true;
      this.options.name = {
        singular: this.as
      };
    } else {
      this.as = this.target.options.name.singular;
      this.options.name = this.target.options.name;
    }

    if (!this.options.foreignKey) {
      this.options.foreignKey = Utils._.camelizeIf(
        [
          Utils._.underscoredIf(Utils.singularize(this.source.name), this.target.options.underscored),
          this.source.primaryKeyAttribute
        ].join('_'),
        !this.source.options.underscored
      );
    }

    this.identifier = this.foreignKey || Utils._.camelizeIf(
      [
        Utils._.underscoredIf(this.source.options.name.singular, this.source.options.underscored),
        this.source.primaryKeyAttribute
      ].join('_'),
      !this.source.options.underscored
    );

    this.sourceIdentifier = this.source.primaryKeyAttribute;
    this.associationAccessor = this.as;
    this.options.useHooks = options.useHooks;

    // Get singular name, trying to uppercase the first letter, unless the model forbids it
    var singular = Utils.uppercaseFirst(this.options.name.singular);

    this.accessors = {
      get: 'get' + singular,
      set: 'set' + singular,
      create: 'create' + singular
    };
  };

  // the id is in the target table
  HasOne.prototype.injectAttributes = function() {
    var newAttributes = {}
      , keyType = this.source.rawAttributes[this.sourceIdentifier].type;

    newAttributes[this.identifier] = Utils._.defaults(this.foreignKeyAttribute, { type: this.options.keyType || keyType });
    Utils.mergeDefaults(this.target.rawAttributes, newAttributes);

    if (this.options.constraints !== false) {
      this.options.onDelete = this.options.onDelete || 'SET NULL';
      this.options.onUpdate = this.options.onUpdate || 'CASCADE';
    }
    Helpers.addForeignKeyConstraints(this.target.rawAttributes[this.identifier], this.source, this.target, this.options);

    // Sync attributes and setters/getters to DAO prototype
    this.target.refreshAttributes();

    Helpers.checkNamingCollision(this);

    return this;
  };

  HasOne.prototype.injectGetter = function(instancePrototype) {
    var association = this;

    instancePrototype[this.accessors.get] = function(params) {
      var where = {};

      params = params || {};
      params.where = (params.where && [params.where]) || [];

      where[association.identifier] = this.get(association.sourceIdentifier);
      params.where.push(where);

      params.where = new Utils.and(params.where);

      return association.target.find(params);
    };

    return this;
  };

  HasOne.prototype.injectSetter = function(instancePrototype) {
    var association = this;

    instancePrototype[this.accessors.set] = function(associatedInstance, options) {
      var instance = this;

      return instance[association.accessors.get](options).then(function(oldInstance) {
        if (oldInstance) {
          oldInstance[association.identifier] = null;
          return oldInstance.save(Utils._.extend({}, options, {
            fields: [association.identifier],
            allowNull: [association.identifier],
            association: true
          }));
        }
      }).then(function() {
        if (associatedInstance) {
          if (!(associatedInstance instanceof association.target.Instance)) {
            var tmpInstance = {};
            tmpInstance[association.target.primaryKeyAttribute] = associatedInstance;
            associatedInstance = association.target.build(tmpInstance, {
              isNewRecord: false
            });
          }
          associatedInstance.set(association.identifier, instance.get(association.sourceIdentifier));
          return associatedInstance.save(options);
        }
        return null;
      });
    };

    return this;
  };

  HasOne.prototype.injectCreator = function(instancePrototype) {
    var association = this;

    instancePrototype[this.accessors.create] = function(values, fieldsOrOptions) {
      var instance = this
        , options = {};

      if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
        options.transaction = fieldsOrOptions.transaction;
      }

      return association.target.create(values, fieldsOrOptions).then(function(associationInstance) {
        return instance[association.accessors.set](associationInstance, options);
      });
    };

    return this;
  };

  return HasOne;
})();


var Sequelize    = require(__dirname + "/../../index")
  , config    = require(__dirname + "/../../spec/config/config")
  , sequelize    = new Sequelize(config.database, config.username, config.password, {logging: false, host: config.host})
  , QueryChainer = Sequelize.Utils.QueryChainer
  , sys          = require("sys")

var Person = sequelize.define('person', { name: Sequelize.STRING })

Person.sync({force: true}).on('success', function() {
  var start = Date.now()
    , count = 10000
    , done  = 0

  var createPerson = function() {
    Person.create({name: 'someone'}).on('success', function() {
      if(++done == count) {
        var duration = (Date.now() - start)
        console.log("\nFinished creation of " + count + " people. Took: " + duration + "ms (avg: " + (duration/count) + "ms)")

        start = Date.now()
        console.log("Will now read them from the database:")

        Person.findAll().on('success', function(people) {
          console.log("Reading " + people.length + " items took: " + (Date.now() - start) + "ms")
        })
      } else {
        (done % 100 == 0) && sys.print('.')
      }
    }).on('failure', function(err) {
      console.log(err)
    })
  }

  console.log('Creating people :)')
  for(var i = 0; i < count; i++) {
    createPerson()
  }

}).on('failure', function(err) {
  console.log(err)
})