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

Commit b4f3e544 by Mick Hansen

Merge pull request #5522 from treythomas123/removeColumn-with-fk-constraint

removeColumn for column with foreign key constraint
2 parents 382ea2aa 5aca6108
...@@ -507,7 +507,7 @@ var QueryGenerator = { ...@@ -507,7 +507,7 @@ var QueryGenerator = {
return '[' + identifier.replace(/[\[\]']+/g,'') + ']'; return '[' + identifier.replace(/[\[\]']+/g,'') + ']';
}, },
getForeignKeysQuery: function(table, databaseName) { getForeignKeysQuery: function(table) {
var tableName = table.tableName || table; var tableName = table.tableName || table;
var sql = [ var sql = [
'SELECT', 'SELECT',
...@@ -525,6 +525,27 @@ var QueryGenerator = { ...@@ -525,6 +525,27 @@ var QueryGenerator = {
return sql; return sql;
}, },
getForeignKeyQuery: function(table, attributeName) {
var tableName = table.tableName || table;
var sql = [
'SELECT',
'constraint_name = TC.CONSTRAINT_NAME',
'FROM',
'INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC',
'JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE CCU',
'ON TC.CONSTRAINT_NAME = CCU.CONSTRAINT_NAME',
"WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY'",
'AND TC.TABLE_NAME =', wrapSingleQuote(tableName),
'AND CCU.COLUMN_NAME =', wrapSingleQuote(attributeName),
].join(' ');
if (table.schema) {
sql += ' AND TC.TABLE_SCHEMA =' + wrapSingleQuote(table.schema);
}
return sql;
},
dropForeignKeyQuery: function(tableName, foreignKey) { dropForeignKeyQuery: function(tableName, foreignKey) {
return Utils._.template('ALTER TABLE <%= table %> DROP <%= key %>')({ return Utils._.template('ALTER TABLE <%= table %> DROP <%= key %>')({
table: this.quoteTable(tableName), table: this.quoteTable(tableName),
......
...@@ -33,6 +33,18 @@ var removeColumn = function (tableName, attributeName, options) { ...@@ -33,6 +33,18 @@ var removeColumn = function (tableName, attributeName, options) {
return self.sequelize.query(dropConstraintSql, { raw: true, logging: options.logging}); return self.sequelize.query(dropConstraintSql, { raw: true, logging: options.logging});
}) })
.then(function () { .then(function () {
var findForeignKeySql = self.QueryGenerator.getForeignKeyQuery(tableName, attributeName);
return self.sequelize.query(findForeignKeySql , { raw: true, logging: options.logging});
})
.spread(function (results) {
if (!results.length) {
// No foreign key constraints found, so we can remove the column
return;
}
var dropForeignKeySql = self.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name);
return self.sequelize.query(dropForeignKeySql , { raw: true, logging: options.logging});
})
.then(function () {
var removeSql = self.QueryGenerator.removeColumnQuery(tableName, attributeName); var removeSql = self.QueryGenerator.removeColumnQuery(tableName, attributeName);
return self.sequelize.query(removeSql, { raw: true, logging: options.logging}); return self.sequelize.query(removeSql, { raw: true, logging: options.logging});
}); });
......
...@@ -332,6 +332,26 @@ var QueryGenerator = { ...@@ -332,6 +332,26 @@ var QueryGenerator = {
}, },
/** /**
* Generates an SQL query that returns the foreign key constraint of a given column.
*
* @param {String} tableName The name of the table.
* @param {String} columnName The name of the column.
* @return {String} The generated sql query.
*/
getForeignKeyQuery: function(table, columnName) {
var tableName = table.tableName || table;
if (table.schema) {
tableName = table.schema + '.' + tableName;
}
return [
'SELECT CONSTRAINT_NAME as constraint_name',
'FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE',
'WHERE TABLE_NAME = ' + wrapSingleQuote(tableName),
'AND COLUMN_NAME = ' + wrapSingleQuote(columnName),
].join(' ');
},
/**
* Generates an SQL query that removes a foreign key from a table. * Generates an SQL query that removes a foreign key from a table.
* *
* @param {String} tableName The name of the table. * @param {String} tableName The name of the table.
...@@ -343,4 +363,9 @@ var QueryGenerator = { ...@@ -343,4 +363,9 @@ var QueryGenerator = {
} }
}; };
// private methods
function wrapSingleQuote(identifier){
return Utils.addTicks(identifier, '\'');
}
module.exports = Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator); module.exports = Utils._.extend(Utils._.clone(require('../abstract/query-generator')), QueryGenerator);
'use strict';
/**
Returns an object that treats MySQL's inabilities to do certain queries.
@class QueryInterface
@static
*/
var _ = require('lodash');
/**
A wrapper that fixes MySQL's inability to cleanly remove columns from existing tables if they have a foreign key constraint.
@method removeColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {String} columnName The name of the attribute that we want to remove.
@param {Object} options
*/
var removeColumn = function (tableName, columnName, options) {
var self = this;
options = options || {};
return self.sequelize.query(
self.QueryGenerator.getForeignKeyQuery(tableName, columnName),
_.assign({ raw: true }, options)
)
.spread(function (results) {
if (!results.length) {
// No foreign key constraints found, so we can remove the column
return;
}
return self.sequelize.query(
self.QueryGenerator.dropForeignKeyQuery(tableName, results[0].constraint_name),
_.assign({ raw: true }, options)
);
})
.then(function () {
return self.sequelize.query(
self.QueryGenerator.removeColumnQuery(tableName, columnName),
_.assign({ raw: true }, options)
);
});
};
module.exports = {
removeColumn: removeColumn
};
...@@ -5,6 +5,7 @@ var Utils = require('./utils') ...@@ -5,6 +5,7 @@ var Utils = require('./utils')
, DataTypes = require('./data-types') , DataTypes = require('./data-types')
, SQLiteQueryInterface = require('./dialects/sqlite/query-interface') , SQLiteQueryInterface = require('./dialects/sqlite/query-interface')
, MSSSQLQueryInterface = require('./dialects/mssql/query-interface') , MSSSQLQueryInterface = require('./dialects/mssql/query-interface')
, MySQLQueryInterface = require('./dialects/mysql/query-interface')
, Transaction = require('./transaction') , Transaction = require('./transaction')
, Promise = require('./promise') , Promise = require('./promise')
, QueryTypes = require('./query-types'); , QueryTypes = require('./query-types');
...@@ -360,8 +361,12 @@ QueryInterface.prototype.removeColumn = function(tableName, attributeName, optio ...@@ -360,8 +361,12 @@ QueryInterface.prototype.removeColumn = function(tableName, attributeName, optio
// sqlite needs some special treatment as it cannot drop a column // sqlite needs some special treatment as it cannot drop a column
return SQLiteQueryInterface.removeColumn.call(this, tableName, attributeName, options); return SQLiteQueryInterface.removeColumn.call(this, tableName, attributeName, options);
case 'mssql': case 'mssql':
// mssql needs special treatment as it cannot drop a column with a default constraint // mssql needs special treatment as it cannot drop a column with a default or foreign key constraint
return MSSSQLQueryInterface.removeColumn.call(this, tableName, attributeName, options); return MSSSQLQueryInterface.removeColumn.call(this, tableName, attributeName, options);
case 'mysql':
case 'mariadb':
// mysql/maria needs special treatment as it cannot drop a column with a foreign key constraint
return MySQLQueryInterface.removeColumn.call(this, tableName, attributeName, options);
default: default:
var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName); var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName);
return this.sequelize.query(sql, options); return this.sequelize.query(sql, options);
......
...@@ -560,6 +560,13 @@ describe(Support.getTestDialectTeaser('QueryInterface'), function() { ...@@ -560,6 +560,13 @@ describe(Support.getTestDialectTeaser('QueryInterface'), function() {
}, },
lastName: { lastName: {
type: DataTypes.STRING type: DataTypes.STRING
},
manager: {
type: DataTypes.INTEGER,
references: {
model: 'users',
key: 'id'
}
} }
}); });
}); });
...@@ -579,6 +586,14 @@ describe(Support.getTestDialectTeaser('QueryInterface'), function() { ...@@ -579,6 +586,14 @@ describe(Support.getTestDialectTeaser('QueryInterface'), function() {
expect(table).to.not.have.property('lastName'); expect(table).to.not.have.property('lastName');
}); });
}); });
it('should be able to remove a column with a foreign key constraint', function() {
return this.queryInterface.removeColumn('users', 'manager').bind(this).then(function() {
return this.queryInterface.describeTable('users');
}).then(function(table) {
expect(table).to.not.have.property('manager');
});
});
}); });
describe('(with a schema)', function() { describe('(with a schema)', function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!