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,11 +42,13 @@ function mixinMigration(MySQL, mysql) {
|
||||||
var table = self.tableEscaped(model);
|
var table = self.tableEscaped(model);
|
||||||
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
||||||
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
||||||
if (!err && fields && fields.length) {
|
self.discoverForeignKeys(model, {}, function(discoverErr, foreignKeys) {
|
||||||
self.alterTable(model, fields, indexes, done);
|
if (!err && fields && fields.length) {
|
||||||
} else {
|
self.alterTable(model, fields, indexes, foreignKeys, done);
|
||||||
self.createTable(model, done);
|
} else {
|
||||||
}
|
self.createTable(model, done);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, cb);
|
}, cb);
|
||||||
|
@ -93,14 +95,16 @@ function mixinMigration(MySQL, mysql) {
|
||||||
var table = self.tableEscaped(model);
|
var table = self.tableEscaped(model);
|
||||||
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
self.execute('SHOW FIELDS FROM ' + table, function(err, fields) {
|
||||||
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
self.execute('SHOW INDEXES FROM ' + table, function(err, indexes) {
|
||||||
self.alterTable(model, fields, indexes, function(err, needAlter) {
|
self.discoverForeignKeys(model, {}, function(discoverErr, foreignKeys) {
|
||||||
if (err) {
|
self.alterTable(model, fields, indexes, foreignKeys, function(err, needAlter) {
|
||||||
return done(err);
|
if (err) {
|
||||||
} else {
|
return done(err);
|
||||||
ok = ok || needAlter;
|
} else {
|
||||||
done(err);
|
ok = ok || needAlter;
|
||||||
}
|
done(err);
|
||||||
}, true);
|
}
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, function(err) {
|
}, function(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 self = this;
|
||||||
var m = this.getModelDefinition(model);
|
var m = this.getModelDefinition(model);
|
||||||
var propNames = Object.keys(m.properties).filter(function(name) {
|
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) {
|
var indexNames = Object.keys(indexes).filter(function(name) {
|
||||||
return !!m.settings.indexes[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 sql = [];
|
||||||
var ai = {};
|
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
|
// drop columns
|
||||||
if (actualFields) {
|
if (actualFields) {
|
||||||
actualFields.forEach(function(f) {
|
actualFields.forEach(function(f) {
|
||||||
|
@ -177,6 +214,8 @@ function mixinMigration(MySQL, mysql) {
|
||||||
aiNames.forEach(function(indexName) {
|
aiNames.forEach(function(indexName) {
|
||||||
if (indexName === 'PRIMARY' ||
|
if (indexName === 'PRIMARY' ||
|
||||||
(m.properties[indexName] && self.id(model, indexName))) return;
|
(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] ||
|
if (indexNames.indexOf(indexName) === -1 && !m.properties[indexName] ||
|
||||||
m.properties[indexName] && !m.properties[indexName].index) {
|
m.properties[indexName] && !m.properties[indexName].index) {
|
||||||
sql.push('DROP INDEX ' + self.client.escapeId(indexName));
|
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) {
|
if (sql.length) {
|
||||||
var query = 'ALTER TABLE ' + self.tableEscaped(model) + ' ' +
|
var query = 'ALTER TABLE ' + self.tableEscaped(model) + ' ' +
|
||||||
sql.join(',\n');
|
sql.join(',\n');
|
||||||
|
@ -336,7 +385,11 @@ function mixinMigration(MySQL, mysql) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectedColName(propName) {
|
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') {
|
if (typeof mysql === 'undefined') {
|
||||||
return propName;
|
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.buildColumnDefinitions =
|
||||||
MySQL.prototype.propertiesSQL = function(model) {
|
MySQL.prototype.propertiesSQL = function(model) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
Loading…
Reference in New Issue