diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 6d682aa2..f9152d7a 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -320,7 +320,9 @@ MySQL.prototype.isActual = function (cb) { Object.keys(this._models).forEach(function (model) { wait += 1; self.query('SHOW FIELDS FROM ' + model, function (err, fields) { - self.alterTable(model, fields, null, done, true); + self.query('SHOW INDEXES FROM ' + model, function (err, indexes) { + self.alterTable(model, fields, indexes, done, true); + }); }); }); @@ -341,7 +343,25 @@ MySQL.prototype.alterTable = function (model, actualFields, actualIndexes, done, var propNames = Object.keys(m.properties).filter(function (name) { return !!m.properties[name]; }); + var indexNames = m.settings.indexes ? Object.keys(m.settings.indexes).filter(function (name) { + return !!m.settings.indexes[name]; + }) : []; var sql = []; + var ai = {}; + + if (actualIndexes) { + actualIndexes.forEach(function (i) { + var name = i.Key_name; + if (!ai[name]) { + ai[name] = { + info: i, + columns: [] + }; + } + ai[name].columns[i.Seq_in_index - 1] = i.Column_name; + }); + } + var aiNames = Object.keys(ai); // change/add new fields propNames.forEach(function (propName) { @@ -368,34 +388,74 @@ MySQL.prototype.alterTable = function (model, actualFields, actualIndexes, done, } }); - // add single-column indexes - propNames.forEach(function (propName) { - if (!m.properties[propName].index) { - return; - } - var found; - if (actualIndexes) { - actualIndexes.forEach(function (f) { - if (f.Column_name === propName) { - found = f; - } - }); - } - if (!found) { - sql.push('ADD INDEX `' + propName + '` (`' + propName + '`)'); + // remove indexes + aiNames.forEach(function (indexName) { + if (indexName === 'id' || indexName === 'PRIMARY') return; + if (indexNames.indexOf(indexName) === -1 || m.properties[indexName] && !m.properties[indexName].index) { + sql.push('DROP INDEX `' + indexName + '`'); + } else { + // first: check single (only type and kind) + if (m.properties[indexName] && !m.properties[indexName].index) { + // TODO + } + // second: check multiple indexes + var orderMatched = true; + if (indexNames.indexOf(indexName) !== -1) { + m.settings.indexes[indexName].columns.split(/,\s*/).forEach(function (columnName, i) { + if (ai[indexName].columns[i] !== columnName) orderMatched = false; + }); + } + if (!orderMatched) { + sql.push('DROP INDEX `' + indexName + '`'); + delete ai[indexName]; + } } }); - // remove single-column indexes - if (actualIndexes) { - actualIndexes.forEach(function (f) { - var propName = f.Key_name; - if (propName === 'id') return; - if (m.properties[propName] && !m.properties[propName].index) { - sql.push('DROP INDEX `' + propName + '`'); + // add single-column indexes + propNames.forEach(function (propName) { + var i = m.properties[propName].index; + if (!i) { + return; + } + var found = ai[propName] && ai[propName].info; + if (!found) { + var type = ''; + var kind = ''; + if (i.type) { + type = 'USING ' + i.type; } - }); - } + if (i.kind) { + // kind = i.kind; + } + if (kind && type) { + sql.push('ADD ' + kind + ' INDEX `' + propName + '` (`' + propName + '`) ' + type); + } else { + sql.push('ADD ' + kind + ' INDEX `' + propName + '` ' + type + ' (`' + propName + '`) '); + } + } + }); + + // add multi-column indexes + indexNames.forEach(function (indexName) { + var i = m.settings.indexes[indexName]; + var found = ai[indexName] && ai[indexName].info; + if (!found) { + var type = ''; + var kind = ''; + if (i.type) { + type = 'USING ' + i.kind; + } + if (i.kind) { + kind = i.kind; + } + if (kind && type) { + sql.push('ADD ' + kind + ' INDEX `' + indexName + '` (' + i.columns + ') ' + type); + } else { + sql.push('ADD ' + kind + ' INDEX ' + type + ' `' + indexName + '` (' + i.columns + ')'); + } + } + }); if (sql.length) { if (checkOnly) { diff --git a/test/migration_test.coffee b/test/migration_test.coffee index b90646f0..6b4c6288 100644 --- a/test/migration_test.coffee +++ b/test/migration_test.coffee @@ -23,6 +23,9 @@ User = schema.define 'User', birthDate: Date pendingPeriod: Number createdByAdmin: Boolean +, indexes: + index1: + columns: 'email, createdByAdmin' withBlankDatabase = (cb) -> db = schema.settings.database = DBNAME @@ -45,7 +48,7 @@ getIndexes = (model, cb) -> cb err else indexes = {} - res.forEach (index) -> indexes[index.Key_name] = index + res.forEach (index) -> indexes[index.Key_name] = index if index.Seq_in_index == 1 cb err, indexes it 'should run migration', (test) -> @@ -146,18 +149,32 @@ it 'should autoupgrade', (test) -> it 'should check actuality of schema', (test) -> # drop column User.schema.isActual (err, ok) -> - test.ok ok + test.ok ok, 'schema is actual' User.defineProperty 'email', false User.schema.isActual (err, ok) -> - test.ok not ok + test.ok not ok, 'schema is not actual' test.done() it 'should add single-column index', (test) -> - User.defineProperty 'email', type: String, index: true + User.defineProperty 'email', type: String, index: { kind: 'FULLTEXT', type: 'HASH'} User.schema.autoupdate (err) -> return console.log(err) if err getIndexes 'User', (err, ixs) -> test.ok ixs.email && ixs.email.Column_name == 'email' + console.log(ixs) + test.equal ixs.email.Index_type, 'BTREE' # default + test.done() + +it 'should change type of single-column index', (test) -> + User.defineProperty 'email', type: String, index: { type: 'BTREE' } + User.schema.isActual (err, ok) -> + test.ok not ok, 'schema is not actual' + User.schema.autoupdate (err) -> + return console.log(err) if err + getIndexes 'User', (err, ixs) -> + console.log ixs.email + test.ok ixs.email && ixs.email.Column_name == 'email' + test.equal ixs.email.Index_type, 'BTREE' test.done() it 'should remove single-column index', (test) -> @@ -168,6 +185,16 @@ it 'should remove single-column index', (test) -> test.ok !ixs.email test.done() +it 'should update multi-column index when order of columns changed', (test) -> + User.schema.adapter._models.User.settings.indexes.index1.columns = 'createdByAdmin, email' + User.schema.isActual (err, ok) -> + test.ok not ok, 'schema is not actual' + User.schema.autoupdate (err) -> + return console.log(err) if err + getIndexes 'User', (err, ixs) -> + test.equals ixs.index1.Column_name, 'createdByAdmin' + test.done() + it 'should disconnect when done', (test) -> schema.disconnect() test.done()