Add rough handling for automatic foreign keys
This handles basic creating/dropping of FKs based on model relations
This commit is contained in:
parent
39d67fa1f9
commit
1324333ffd
|
@ -42,13 +42,15 @@ function mixinMigration(MySQL, mysql) {
|
|||
var table = self.tableEscaped(model);
|
||||
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
||||
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
||||
self.discoverForeignKeys(model, {}, function(discoverErr, foreignKeys) {
|
||||
if (!err && fields && fields.length) {
|
||||
self.alterTable(model, fields, indexes, done);
|
||||
self.alterTable(model, fields, indexes, foreignKeys, done);
|
||||
} else {
|
||||
self.createTable(model, done);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}, cb);
|
||||
};
|
||||
|
||||
|
@ -93,7 +95,8 @@ function mixinMigration(MySQL, mysql) {
|
|||
var table = self.tableEscaped(model);
|
||||
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
||||
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
||||
self.alterTable(model, fields, indexes, function(err, needAlter) {
|
||||
self.discoverForeignKeys(model, {}, function(discoverErr, foreignKeys) {
|
||||
self.alterTable(model, fields, indexes, foreignKeys, function(err, needAlter) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
} else {
|
||||
|
@ -103,6 +106,7 @@ function mixinMigration(MySQL, mysql) {
|
|||
}, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
return err;
|
||||
|
@ -111,7 +115,7 @@ function mixinMigration(MySQL, mysql) {
|
|||
});
|
||||
};
|
||||
|
||||
MySQL.prototype.alterTable = function(model, actualFields, actualIndexes, done, checkOnly) {
|
||||
MySQL.prototype.alterTable = function(model, actualFields, actualIndexes, actualFks, done, checkOnly) {
|
||||
var self = this;
|
||||
var m = this.getModelDefinition(model);
|
||||
var propNames = Object.keys(m.properties).filter(function(name) {
|
||||
|
@ -121,6 +125,10 @@ function mixinMigration(MySQL, mysql) {
|
|||
var indexNames = Object.keys(indexes).filter(function(name) {
|
||||
return !!m.settings.indexes[name];
|
||||
});
|
||||
var newFks = m.settings.foreignKeys || {};
|
||||
var newFkNames = Object.keys(newFks).filter(function(name) {
|
||||
return !!m.settings.foreignKeys[name];
|
||||
});
|
||||
var sql = [];
|
||||
var ai = {};
|
||||
|
||||
|
@ -159,6 +167,35 @@ function mixinMigration(MySQL, mysql) {
|
|||
}
|
||||
});
|
||||
|
||||
//drop foreign keys for removed fields
|
||||
if (actualFks) {
|
||||
var removedFks = [];
|
||||
actualFks.forEach(function(fk) {
|
||||
var needsToDrop = false;
|
||||
var newFk = newFks[fk.fkName];
|
||||
if (newFk) {
|
||||
var fkCol = expectedColName(newFk.foreignKey);
|
||||
var fkRefKey = expectedColNameForModel(newFk.entityKey, newFk.entity);
|
||||
var fkRefTable = newFk.entity.name; //TODO check for mysql name
|
||||
needsToDrop = fkCol != fk.fkColumnName ||
|
||||
fkRefKey != fk.pkColumnName ||
|
||||
fkRefTable != fk.pkTableName;
|
||||
} else {
|
||||
needsToDrop = true;
|
||||
}
|
||||
|
||||
if (needsToDrop) {
|
||||
sql.push('DROP FOREIGN KEY ' + fk.fkName);
|
||||
removedFks.push(fk); //keep track that we removed these
|
||||
}
|
||||
});
|
||||
|
||||
//update out list of existing keys by removing dropped keys
|
||||
actualFks = actualFks.filter(function(k) {
|
||||
return removedFks.indexOf(k) == -1;
|
||||
});
|
||||
}
|
||||
|
||||
// drop columns
|
||||
if (actualFields) {
|
||||
actualFields.forEach(function(f) {
|
||||
|
@ -177,6 +214,8 @@ function mixinMigration(MySQL, mysql) {
|
|||
aiNames.forEach(function(indexName) {
|
||||
if (indexName === 'PRIMARY' ||
|
||||
(m.properties[indexName] && self.id(model, indexName))) return;
|
||||
|
||||
if (newFkNames.indexOf(indexName) > -1) return; //this index is from an FK
|
||||
if (indexNames.indexOf(indexName) === -1 && !m.properties[indexName] ||
|
||||
m.properties[indexName] && !m.properties[indexName].index) {
|
||||
sql.push('DROP INDEX ' + self.client.escapeId(indexName));
|
||||
|
@ -293,6 +332,16 @@ function mixinMigration(MySQL, mysql) {
|
|||
}
|
||||
});
|
||||
|
||||
//add new foreign keys
|
||||
if (newFkNames.length) {
|
||||
//TODO validate that these are in the same DB, etc.
|
||||
var oldKeyNames = actualFks.map(function(oldKey) { return oldKey.fkName; });
|
||||
newFkNames.filter(function(key) { return !~oldKeyNames.indexOf(key); })
|
||||
.forEach(function(key) {
|
||||
sql.push(self.buildForeignKeyDefinition(m, key));
|
||||
});
|
||||
}
|
||||
|
||||
if (sql.length) {
|
||||
var query = 'ALTER TABLE ' + self.tableEscaped(model) + ' ' +
|
||||
sql.join(',\n');
|
||||
|
@ -336,7 +385,11 @@ function mixinMigration(MySQL, mysql) {
|
|||
}
|
||||
|
||||
function expectedColName(propName) {
|
||||
var mysql = m.properties[propName].mysql;
|
||||
return expectedColNameForModel(propName, m);
|
||||
}
|
||||
|
||||
function expectedColNameForModel(propName, modelToCheck) {
|
||||
var mysql = modelToCheck.properties[propName].mysql;
|
||||
if (typeof mysql === 'undefined') {
|
||||
return propName;
|
||||
}
|
||||
|
@ -348,6 +401,18 @@ function mixinMigration(MySQL, mysql) {
|
|||
}
|
||||
};
|
||||
|
||||
MySQL.prototype.buildForeignKeyDefinition = function(model, keyName) {
|
||||
var fk = model.settings.foreignKeys[keyName];
|
||||
if (fk) {
|
||||
//TODO verify that the other model in the same DB
|
||||
return ' ADD CONSTRAINT ' + this.client.escapeId(fk.name) +
|
||||
' FOREIGN KEY (' + fk.foreignKey + ')' +
|
||||
' REFERENCES ' + this.tableEscaped(fk.entity.name) +
|
||||
'(' + this.client.escapeId(fk.entityKey) + ')';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
MySQL.prototype.buildColumnDefinitions =
|
||||
MySQL.prototype.propertiesSQL = function(model) {
|
||||
var self = this;
|
||||
|
|
Loading…
Reference in New Issue