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

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() {
}
},
addColumnQuery: function(tableName, attributes) {
var query = 'ALTER TABLE `<%= tableName %>` ADD <%= attributes %>;'
, attrString = [];
for (var attrName in attributes) {
var definition = attributes[attrName];
attrString.push(Utils._.template('`<%= attrName %>` <%= definition %>')({
attrName: attrName,
definition: this.mysqlDataTypeMapping(tableName, attrName, definition)
}));
}
addColumnQuery: function(table, key, dataType) {
var query = 'ALTER TABLE <%= table %> ADD <%= attribute %>;'
, attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: this.attributeToSQL(dataType, {
context: 'addColumn'
})
});
return Utils._.template(query)({ tableName: tableName, attributes: attrString.join(', ') });
return Utils._.template(query)({
table: this.quoteTable(table),
attribute: attribute
});
},
removeColumnQuery: function(tableName, attributeName) {
......@@ -278,10 +277,6 @@ module.exports = (function() {
template += ' PRIMARY KEY';
}
if (attribute.comment && Utils._.isString(attribute.comment) && attribute.comment.length) {
template += ' COMMENT ' + this.escape(attribute.comment);
}
if (attribute.references) {
template += ' REFERENCES ' + this.quoteTable(attribute.references);
......
......@@ -119,26 +119,24 @@ module.exports = (function() {
}
},
addColumnQuery: function(tableName, attributes) {
var query = 'ALTER TABLE <%= tableName %> ADD COLUMN <%= attributes %>;'
, attrString = [];
addColumnQuery: function(table, key, dataType) {
var query = 'ALTER TABLE <%= table %> ADD COLUMN <%= attribute %>;'
, attribute;
for (var attrName in attributes) {
var definition = attributes[attrName];
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;
}
if (dataType.toString() === DataTypes.ENUM.toString()) {
query = this.pgEnum(table, key, dataType) + query;
}
attribute = Utils._.template('<%= key %> <%= definition %>')({
key: this.quoteIdentifier(key),
definition: this.attributeToSQL(dataType, {
context: 'addColumn'
})
});
return Utils._.template(query)({
tableName: this.quoteTable(tableName),
attributes: attrString.join(', ')
table: this.quoteTable(table),
attribute: attribute
});
},
......@@ -386,92 +384,88 @@ module.exports = (function() {
return query;
},
attributesToSQL: function(attributes) {
var result = {};
attributeToSQL: function(attribute, options) {
if (!Utils._.isPlainObject(attribute)) {
attribute = {
type: attribute
};
}
for (var name in attributes) {
var dataType = attributes[name];
var fieldName = dataType.field || name;
var template = '<%= type %>'
, replacements = { type: attribute.type };
if (Utils._.isObject(dataType)) {
var template = '<%= type %>'
, replacements = { type: dataType.type };
if (attribute.type.toString() === DataTypes.ENUM.toString()) {
if (Array.isArray(attribute.values) && (attribute.values.length > 0)) {
replacements.type = 'ENUM(' + Utils._.map(attribute.values, function(value) {
return this.escape(value);
}.bind(this)).join(', ') + ')';
} else {
throw new Error('Values for ENUM haven\'t been defined.');
}
}
if (dataType.type.toString() === DataTypes.ENUM.toString()) {
if (Array.isArray(dataType.values) && (dataType.values.length > 0)) {
replacements.type = 'ENUM(' + Utils._.map(dataType.values, function(value) {
return this.escape(value);
}.bind(this)).join(', ') + ')';
} else {
throw new Error('Values for ENUM haven\'t been defined.');
}
}
if (attribute.type === attribute.BOOLEAN) {
attribute.type = 'BOOLEAN';
}
if (dataType.type === DataTypes.BOOLEAN) {
dataType.type = 'BOOLEAN';
}
if (attribute.type === attribute.DATE) {
attribute._typeName = 'DATETIME';
attribute.type = 'TIMESTAMP WITH TIME ZONE';
}
if (dataType.type === DataTypes.DATE) {
dataType._typeName = 'DATETIME';
dataType.type = 'TIMESTAMP WITH TIME ZONE';
}
if (attribute.hasOwnProperty('allowNull') && (!attribute.allowNull)) {
template += ' NOT NULL';
}
if (dataType.hasOwnProperty('allowNull') && (!dataType.allowNull)) {
template += ' NOT NULL';
}
if (attribute.autoIncrement) {
template += ' SERIAL';
}
if (dataType.autoIncrement) {
template += ' SERIAL';
}
if (Utils.defaultValueSchemable(attribute.defaultValue)) {
template += ' DEFAULT <%= defaultValue %>';
replacements.defaultValue = this.escape(attribute.defaultValue, attribute);
}
if (Utils.defaultValueSchemable(dataType.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 %>';
replacements.defaultValue = this.escape(dataType.defaultValue, dataType);
}
if (attribute.unique === true) {
template += ' UNIQUE';
}
if (dataType.unique === true) {
template += ' UNIQUE';
}
if (attribute.primaryKey) {
template += ' PRIMARY KEY';
}
if (dataType.primaryKey) {
template += ' PRIMARY KEY';
}
if (attribute.references) {
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(attribute.references);
if (dataType.references) {
template += ' REFERENCES <%= referencesTable %> (<%= referencesKey %>)';
replacements.referencesTable = this.quoteTable(dataType.references);
if (attribute.referencesKey) {
replacements.referencesKey = this.quoteIdentifiers(attribute.referencesKey);
} else {
replacements.referencesKey = this.quoteIdentifier('id');
}
if (dataType.referencesKey) {
replacements.referencesKey = this.quoteIdentifiers(dataType.referencesKey);
} else {
replacements.referencesKey = this.quoteIdentifier('id');
}
if (attribute.onDelete) {
template += ' ON DELETE <%= onDeleteAction %>';
replacements.onDeleteAction = attribute.onDelete.toUpperCase();
}
if (dataType.onDelete) {
template += ' ON DELETE <%= onDeleteAction %>';
replacements.onDeleteAction = dataType.onDelete.toUpperCase();
}
if (attribute.onUpdate) {
template += ' ON UPDATE <%= onUpdateAction %>';
replacements.onUpdateAction = attribute.onUpdate.toUpperCase();
}
}
if (dataType.onUpdate) {
template += ' ON UPDATE <%= onUpdateAction %>';
replacements.onUpdateAction = dataType.onUpdate.toUpperCase();
}
}
return Utils._.template(template)(replacements);
},
if (dataType.comment && Utils._.isString(dataType.comment)) {
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);
}
attributesToSQL: function(attributes, options) {
var result = {}
, key
, attribute;
result[fieldName] = Utils._.template(template)(replacements);
} else {
result[fieldName] = dataType;
}
for (key in attributes) {
attribute = attributes[key];
result[attribute.field || key] = this.attributeToSQL(attribute, options);
}
return result;
......@@ -733,7 +727,7 @@ module.exports = (function() {
pgDataTypeMapping: function(tableName, attr, dataType) {
return this.dataTypeMapping(tableName, attr, dataType);
},
dataTypeMapping: function(tableName, attr, dataType) {
if (Utils._.includes(dataType, 'PRIMARY KEY')) {
primaryKeys[tableName].push(attr);
......
......@@ -300,19 +300,8 @@ module.exports = (function() {
});
};
QueryInterface.prototype.addColumn = function(tableName, attributeName, dataTypeOrOptions) {
var attributes = {};
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.addColumn = function(table, key, dataType) {
return this.sequelize.query(this.QueryGenerator.addColumnQuery(table, key, dataType), null, {logging: this.sequelize.options.logging});
};
QueryInterface.prototype.removeColumn = function(tableName, attributeName) {
......
......@@ -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 () {
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!