不要怂,就是干,撸起袖子干!

Commit 0c1ec1cc by Mick Hansen

feat(query-interface/migrator): provide foreign key constraint/references support to addColumn

1 parent cb6a51cb
...@@ -113,20 +113,19 @@ module.exports = (function() { ...@@ -113,20 +113,19 @@ module.exports = (function() {
} }
}, },
addColumnQuery: function(tableName, attributes) { addColumnQuery: function(table, key, dataType) {
var query = 'ALTER TABLE `<%= tableName %>` ADD <%= attributes %>;' var query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;'
, attrString = []; , attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
for (var attrName in attributes) { definition: this.attributeToSQL(dataType, {
var definition = attributes[attrName]; context: 'addColumn'
})
attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({ });
attrName: attrName,
definition: this.mysqlDataTypeMapping(tableName, attrName, definition)
}));
}
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') }); return Utils._.template(query)({
table: this.quoteTable(table),
attribute: attribute
});
}, },
removeColumnQuery: function(tableName, attributeName) { removeColumnQuery: function(tableName, attributeName) {
...@@ -278,10 +277,6 @@ module.exports = (function() { ...@@ -278,10 +277,6 @@ module.exports = (function() {
template += ' PRIMARY KEY'; template += ' PRIMARY KEY';
} }
if (attribute.comment && Utils._.isString(attribute.comment) && attribute.comment.length) {
template += ' COMMENT ' + this.escape(attribute.comment);
}
if (attribute.references) { if (attribute.references) {
template += ' REFERENCES ' + this.quoteTable(attribute.references); template += ' REFERENCES ' + this.quoteTable(attribute.references);
......
...@@ -119,26 +119,24 @@ module.exports = (function() { ...@@ -119,26 +119,24 @@ module.exports = (function() {
} }
}, },
addColumnQuery: function(tableName, attributes) { addColumnQuery: function(table, key, dataType) {
var query = 'ALTER TABLE <%= tableName %> ADD COLUMN <%= attributes %>;' var query = 'ALTER TABLE <%= table %> ADD COLUMN <%= attribute %>;'
, attrString = []; , attribute;
for (var attrName in attributes) { if (dataType.toString() === DataTypes.ENUM.toString()) {
var definition = attributes[attrName]; query = this.pgEnum(table, key, dataType) + query;
attrString.push(Utils._.template('<%= attrName %> <%= definition %>')({
attrName: this.quoteIdentifier(attrName),
definition: this.pgDataTypeMapping(tableName, attrName, definition)
}));
if (definition.match(/^ENUM\(/)) {
query = this.pgEnum(tableName, attrName, definition) + query;
}
} }
attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: this.attributeToSQL(dataType, {
context: 'addColumn'
})
});
return Utils._.template(query)({ return Utils._.template(query)({
tableName: this.quoteTable(tableName), table: this.quoteTable(table),
attributes: attrString.join(', ') attribute: attribute
}); });
}, },
...@@ -386,20 +384,19 @@ module.exports = (function() { ...@@ -386,20 +384,19 @@ module.exports = (function() {
return query; return query;
}, },
attributesToSQL: function(attributes) { attributeToSQL: function(attribute, options) {
var result = {}; if (!Utils._.isPlainObject(attribute)) {
attribute = {
for (var name in attributes) { type: attribute
var dataType = attributes[name]; };
var fieldName = dataType.field || name; }
if (Utils._.isObject(dataType)) {
var template = '<%= type %>' var template = '<%= type %>'
, replacements = { type: dataType.type }; , replacements = { type: attribute.type };
if (dataType.type.toString() === DataTypes.ENUM.toString()) { if (attribute.type.toString() === DataTypes.ENUM.toString()) {
if (Array.isArray(dataType.values) && (dataType.values.length > 0)) { if (Array.isArray(attribute.values) && (attribute.values.length > 0)) {
replacements.type = 'ENUM(' + Utils._.map(dataType.values, function(value) { replacements.type = 'ENUM(' + Utils._.map(attribute.values, function(value) {
return this.escape(value); return this.escape(value);
}.bind(this)).join(', ') + ')'; }.bind(this)).join(', ') + ')';
} else { } else {
...@@ -407,71 +404,68 @@ module.exports = (function() { ...@@ -407,71 +404,68 @@ module.exports = (function() {
} }
} }
if (dataType.type === DataTypes.BOOLEAN) { if (attribute.type === attribute.BOOLEAN) {
dataType.type = 'BOOLEAN'; attribute.type = 'BOOLEAN';
} }
if (dataType.type === DataTypes.DATE) { if (attribute.type === attribute.DATE) {
dataType._typeName = 'DATETIME'; attribute._typeName = 'DATETIME';
dataType.type = 'TIMESTAMP WITH TIME ZONE'; attribute.type = 'TIMESTAMP WITH TIME ZONE';
} }
if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) { if (attribute.hasOwnProperty('allowNull') && (!attribute.allowNull)) {
template += ' NOT NULL'; template += ' NOT NULL';
} }
if (dataType.autoIncrement) { if (attribute.autoIncrement) {
template += ' SERIAL'; template += ' SERIAL';
} }
if (Utils.defaultValueSchemable(dataType.defaultValue)) { if (Utils.defaultValueSchemable(attribute.defaultValue)) {
// TODO thoroughly check that DataTypes.NOW will properly
// get populated on all databases as DEFAULT value
// i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP
template += ' DEFAULT <%= defaultValue %>'; template += ' DEFAULT <%= defaultValue %>';
replacements.defaultValue = this.escape(dataType.defaultValue, dataType); replacements.defaultValue = this.escape(attribute.defaultValue, attribute);
} }
if (dataType.unique === true) { if (attribute.unique === true) {
template += ' UNIQUE'; template += ' UNIQUE';
} }
if (dataType.primaryKey) { if (attribute.primaryKey) {
template += ' PRIMARY KEY'; template += ' PRIMARY KEY';
} }
if (dataType.references) { if (attribute.references) {
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)'; template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(dataType.references); replacements.referencesTable = this.quoteTable(attribute.references);
if (dataType.referencesKey) { if (attribute.referencesKey) {
replacements.referencesKey = this.quoteIdentifiers(dataType.referencesKey); replacements.referencesKey = this.quoteIdentifiers(attribute.referencesKey);
} else { } else {
replacements.referencesKey = this.quoteIdentifier('id'); replacements.referencesKey = this.quoteIdentifier('id');
} }
if (dataType.onDelete) { if (attribute.onDelete) {
template += ' ON DELETE <%= onDeleteAction %>'; template += ' ON DELETE <%= onDeleteAction %>';
replacements.onDeleteAction = dataType.onDelete.toUpperCase(); replacements.onDeleteAction = attribute.onDelete.toUpperCase();
} }
if (dataType.onUpdate) { if (attribute.onUpdate) {
template += ' ON UPDATE <%= onUpdateAction %>'; template += ' ON UPDATE <%= onUpdateAction %>';
replacements.onUpdateAction = dataType.onUpdate.toUpperCase(); replacements.onUpdateAction = attribute.onUpdate.toUpperCase();
} }
} }
if (dataType.comment && Utils._.isString(dataType.comment)) { return Utils._.template(template)(replacements);
template += ' COMMENT ON COLUMN <%= tableName %>.<%= columnName %> IS <%= comment %>'; },
replacements.columnName = this.quoteIdentifier(name);
replacements.tableName = '<%= table %>'; // Hacky, table name will be inserted by create table
replacements.comment = this.escape(dataType.comment);
}
result[fieldName] = Utils._.template(template)(replacements); attributesToSQL: function(attributes, options) {
} else { var result = {}
result[fieldName] = dataType; , key
} , attribute;
for (key in attributes) {
attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options);
} }
return result; return result;
......
...@@ -300,19 +300,8 @@ module.exports = (function() { ...@@ -300,19 +300,8 @@ module.exports = (function() {
}); });
}; };
QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) { QueryInterface.prototype.addColumn = function(table, key, dataType) {
var attributes = {}; return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, dataType), null, {logging: this.sequelize.options.logging});
if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) {
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true };
} else {
attributes[attributeName] = dataTypeOrOptions;
}
var options = this.QueryGenerator.attributesToSQL(attributes)
, sql = this.QueryGenerator.addColumnQuery(tableName, options);
return this.sequelize.query(sql, null, {logging: this.sequelize.options.logging});
}; };
QueryInterface.prototype.removeColumn = function(tableName, attributeName) { QueryInterface.prototype.removeColumn = function(tableName, attributeName) {
......
...@@ -2377,4 +2377,29 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -2377,4 +2377,29 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
it('should be possible to use a key named UUID as foreign key', function () {
var project = this.sequelize.define('project', {
UserId: {
type: Sequelize.STRING,
references: 'Users',
referencesKey: 'UUID'
}
});
var user = this.sequelize.define('Users', {
UUID: {
type: Sequelize.STRING,
primaryKey: true,
unique: true,
allowNull: false,
validate: {
notNull: true,
notEmpty: true
}
}
});
return this.sequelize.sync({force: true});
});
}) })
...@@ -218,5 +218,35 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () { ...@@ -218,5 +218,35 @@ describe(Support.getTestDialectTeaser("QueryInterface"), function () {
return self.queryInterface.renameColumn('Fruit', 'fruitId', 'fruit_id') return self.queryInterface.renameColumn('Fruit', 'fruitId', 'fruit_id')
}) })
}) })
}) });
describe('addColumn', function () {
beforeEach(function () {
return this.queryInterface.createTable('users', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoincrement: true
}
});
});
it('should be able to add a foreign key reference', function () {
return this.queryInterface.createTable('level', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoincrement: true
}
}).bind(this).then(function () {
return this.queryInterface.addColumn('users', 'level_id', {
type: DataTypes.INTEGER,
references: 'level',
referenceKey: 'id',
onUpdate: 'cascade',
onDelete: 'restrict'
});
});
});
});
}) })
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!