Autoupdate multicolumn indexes

This commit is contained in:
Anatoliy Chakkaev 2012-08-19 19:40:21 +04:00
parent 62d0e459c0
commit 12eadb80ae
2 changed files with 116 additions and 29 deletions

View File

@ -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) {

View File

@ -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()