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

Commit 1149f3ea by Daniel Durante

Merge pull request #980 from sequelize/chore/remove_FOREIGN_KEY_CHECKS

Removed disabling of foreign keys while dropping tables for pg and mysql.
2 parents 858a3874 204e4cfa
......@@ -261,27 +261,13 @@ module.exports = (function() {
},
/*
Globally enable foreign key constraints
*/
enableForeignKeyConstraintsQuery: function() {
throwMethodUndefined('enableForeignKeyConstraintsQuery')
},
/*
Globally disable foreign key constraints
*/
disableForeignKeyConstraintsQuery: function() {
throwMethodUndefined('disableForeignKeyConstraintsQuery')
},
/*
Quote an object based on its type. This is a more general version of quoteIdentifiers
Strings: should proxy to quoteIdentifiers
Arrays: First argument should be qouted, second argument should be append without quoting
Objects:
* If raw is set, that value should be returned verbatim, without quoting
* If fn is set, the string should start with the value of fn, starting paren, followed by
the values of cols (which is assumed to be an array), quoted and joined with ', ',
the values of cols (which is assumed to be an array), quoted and joined with ', ',
unless they are themselves objects
* If direction is set, should be prepended
......@@ -366,10 +352,31 @@ module.exports = (function() {
if (value instanceof Utils.fn || value instanceof Utils.col) {
return value.toString(this)
} else {
return SqlString.escape(value, false, null, this.dialect, field)
}
}
return SqlString.escape(value, false, null, this.dialect, field)
}
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
throwMethodUndefined('getForeignKeysQuery')
},
/**
* Generates an SQL query that removes a foreign key from a table.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
throwMethodUndefined('dropForeignKeyQuery')
}
}
var throwMethodUndefined = function(methodName) {
......
......@@ -404,10 +404,10 @@ module.exports = (function() {
},
showIndexQuery: function(tableName, options) {
var sql = "SHOW INDEX FROM <%= tableName %><%= options %>"
var sql = "SHOW INDEX FROM `<%= tableName %>`<%= options %>"
return Utils._.template(sql)({
tableName: tableName,
options: (options || {}).database ? ' FROM ' + options.database : ''
options: (options || {}).database ? ' FROM `' + options.database + '`' : ''
})
},
......@@ -589,22 +589,34 @@ module.exports = (function() {
return fields
},
enableForeignKeyConstraintsQuery: function() {
var sql = "SET FOREIGN_KEY_CHECKS = 1;"
return Utils._.template(sql, {})
},
disableForeignKeyConstraintsQuery: function() {
var sql = "SET FOREIGN_KEY_CHECKS = 0;"
return Utils._.template(sql, {})
},
quoteIdentifier: function(identifier, force) {
return Utils.addTicks(identifier, "`")
},
quoteIdentifiers: function(identifiers, force) {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return "SELECT CONSTRAINT_NAME as constraint_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '" + tableName + "' AND CONSTRAINT_NAME!='PRIMARY' AND CONSTRAINT_SCHEMA='" + schemaName + "' AND REFERENCED_TABLE_NAME IS NOT NULL;"
},
/**
* Generates an SQL query that removes a foreign key from a table.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP FOREIGN KEY ' + this.quoteIdentifier(foreignKey) + ';'
}
}
......
......@@ -714,14 +714,6 @@ module.exports = (function() {
return fields
},
enableForeignKeyConstraintsQuery: function() {
return false // not supported by dialect
},
disableForeignKeyConstraintsQuery: function() {
return false // not supported by dialect
},
createTrigger: function(tableName, triggerName, eventType, fireOnSpec, functionName, functionParams, optionsArray) {
var sql = [
'CREATE <%= constraintVal %>TRIGGER <%= triggerName %>'
......@@ -1004,6 +996,28 @@ module.exports = (function() {
quoteIdentifiers: function(identifiers, force) {
return identifiers.split('.').map(function(t) { return this.quoteIdentifier(t, force) }.bind(this)).join('.')
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return "SELECT conname as constraint_name, pg_catalog.pg_get_constraintdef(r.oid, true) as condef FROM pg_catalog.pg_constraint r WHERE r.conrelid = (SELECT oid FROM pg_class WHERE relname = '" + tableName + "' LIMIT 1) AND r.contype = 'f' ORDER BY 1;"
},
/**
* Generates an SQL query that removes a foreign key from a table.
*
* @param {String} tableName The name of the table.
* @param {String} foreignKey The name of the foreign key constraint.
* @return {String} The generated sql query.
*/
dropForeignKeyQuery: function(tableName, foreignKey) {
return 'ALTER TABLE ' + this.quoteIdentifier(tableName) + ' DROP CONSTRAINT ' + this.quoteIdentifier(foreignKey) + ';'
}
}
......
......@@ -179,7 +179,7 @@ module.exports = (function() {
, attrLeft = ((primaryKeysLeft.length !== 1) ? 'id' : primaryKeysLeft[0])
, tableRight = ((include.association.associationType === 'BelongsTo') ? tableName : include.as)
, attrRight = include.association.identifier
joinQuery += " LEFT OUTER JOIN " + this.quoteIdentifier(table) + " AS " + this.quoteIdentifier(as) + " ON " + this.quoteIdentifier(tableLeft) + "." + this.quoteIdentifier(attrLeft) + " = " + this.quoteIdentifier(tableRight) + "." + this.quoteIdentifier(attrRight)
} else {
var primaryKeysSource = Object.keys(include.association.source.primaryKeys)
......@@ -377,16 +377,6 @@ module.exports = (function() {
return fields
},
enableForeignKeyConstraintsQuery: function() {
var sql = "PRAGMA foreign_keys = ON;"
return Utils._.template(sql, {})
},
disableForeignKeyConstraintsQuery: function() {
var sql = "PRAGMA foreign_keys = OFF;"
return Utils._.template(sql, {})
},
hashToWhereConditions: function(hash) {
for (var key in hash) {
if (hash.hasOwnProperty(key)) {
......@@ -486,6 +476,17 @@ module.exports = (function() {
quoteIdentifiers: function(identifiers, force) {
return identifiers.split('.').map(function(v) { return this.quoteIdentifier(v, force) }.bind(this)).join('.')
},
/**
* Generates an SQL query that returns all foreign keys of a table.
*
* @param {String} tableName The name of the table.
* @param {String} schemaName The name of the schema.
* @return {String} The generated sql query.
*/
getForeignKeysQuery: function(tableName, schemaName) {
return "PRAGMA foreign_key_list(\"" + tableName + "\")"
}
}
......
......@@ -118,5 +118,60 @@ var QueryInterface = module.exports = {
queryAndEmit.call(this, queries.splice(queries.length - 1)[0], methodName, {}, emitter)
}
}.bind(this))
},
dropAllTables: function() {
var self = this
return new Utils.CustomEventEmitter(function(dropAllTablesEmitter) {
var events = []
, chainer = new Utils.QueryChainer()
, onError = function(err) {
self.emit('dropAllTables', err)
dropAllTablesEmitter.emit('error', err)
}
self
.showAllTables()
.error(onError)
.success(function(tableNames) {
self
.sequelize
.query('PRAGMA foreign_keys;')
.proxy(dropAllTablesEmitter, { events: ['sql'] })
.error(onError)
.success(function(result) {
var foreignKeysAreEnabled = result.foreign_keys === 1
if (foreignKeysAreEnabled) {
var queries = []
queries.push('PRAGMA foreign_keys = OFF')
tableNames.forEach(function(tableName) {
queries.push(self.QueryGenerator.dropTableQuery(tableName).replace(';', ''))
})
queries.push('PRAGMA foreign_keys = ON')
QueryInterface.execMultiQuery.call(self, queries, 'dropAllTables', dropAllTablesEmitter, self.queryAndEmit)
} else {
// add the table removal query to the chainer
tableNames.forEach(function(tableName) {
chainer.add(self, 'dropTable', [ tableName, { cascade: true } ])
})
chainer
.runSerially()
.proxy(dropAllTablesEmitter, { events: ['sql'] })
.error(onError)
.success(function() {
self.emit('dropAllTables', null)
dropAllTablesEmitter.emit('success', null)
})
}
})
})
}).run()
}
}
......@@ -139,6 +139,10 @@ module.exports = (function() {
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, "")
}
})
} else if (this.sql.indexOf('PRAGMA foreign_keys;') !== -1) {
result = results[0]
} else if (this.sql.indexOf('PRAGMA foreign_keys') !== -1) {
result = results
}
this.emit('success', result)
......
......@@ -3,20 +3,19 @@ var util = require("util")
, Promise = require("promise")
, proxyEventKeys = ['success', 'error', 'sql']
, tick = (typeof setImmediate !== "undefined" ? setImmediate : process.nextTick)
, Utils = require('../utils')
var bindToProcess = function(fct) {
if (fct) {
if (process.domain) {
return process.domain.bind(fct);
}
if (fct && process.domain) {
return process.domain.bind(fct)
}
return fct;
return fct
};
module.exports = (function() {
var CustomEventEmitter = function(fct) {
this.fct = bindToProcess(fct);
this.fct = bindToProcess(fct)
}
util.inherits(CustomEventEmitter, EventEmitter)
......@@ -54,25 +53,40 @@ module.exports = (function() {
return this
}
CustomEventEmitter.prototype.sql =
function(fct) {
CustomEventEmitter.prototype.sql = function(fct) {
this.on('sql', bindToProcess(fct))
return this;
}
CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) {
/**
* Proxy every event of this custom event emitter to another one.
*
* @param {CustomEventEmitter} emitter The event emitter that should receive the events.
* @return {void}
*/
CustomEventEmitter.prototype.proxy = function(emitter, options) {
options = Utils._.extend({
events: proxyEventKeys,
skipEvents: []
}, options || {})
options.events = Utils._.difference(options.events, options.skipEvents)
options.events.forEach(function (eventKey) {
this.on(eventKey, function (result) {
emitter.emit(eventKey, result)
})
}.bind(this))
return this
}
CustomEventEmitter.prototype.then =
function (onFulfilled, onRejected) {
CustomEventEmitter.prototype.then = function(onFulfilled, onRejected) {
var self = this
onFulfilled = bindToProcess(onFulfilled)
onRejected = bindToProcess(onRejected)
return new Promise(function (resolve, reject) {
self.on('error', reject)
.on('success', resolve);
......
......@@ -223,34 +223,45 @@ module.exports = (function() {
QueryInterface.prototype.dropAllTables = function() {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
self.showAllTables().success(function(tableNames) {
chainer.add(self, 'disableForeignKeyConstraints', [])
tableNames.forEach(function(tableName) {
chainer.add(self, 'dropTable', [tableName, {cascade: true}])
})
if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot drop a column
return SQLiteQueryInterface.dropAllTables.call(this)
} else {
return new Utils.CustomEventEmitter(function(dropAllTablesEmitter) {
var events = []
, chainer = new Utils.QueryChainer()
, onError = function(err) {
self.emit('dropAllTables', err)
dropAllTablesEmitter.emit('error', err)
}
chainer.add(self, 'enableForeignKeyConstraints', [])
self.showAllTables().success(function(tableNames) {
self.getForeignKeysForTables(tableNames).success(function(foreignKeys) {
chainer
.runSerially()
.success(function() {
self.emit('dropAllTables', null)
emitter.emit('success', null)
// add the foreign key removal query to the chainer
Object.keys(foreignKeys).forEach(function(tableName) {
foreignKeys[tableName].forEach(function(foreignKey) {
var sql = self.QueryGenerator.dropForeignKeyQuery(tableName, foreignKey)
chainer.add(self.sequelize, 'query', [ sql ])
})
})
.error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('error', err)
// add the table removal query to the chainer
tableNames.forEach(function(tableName) {
chainer.add(self, 'dropTable', [ tableName, { cascade: true } ])
})
}).error(function(err) {
self.emit('dropAllTables', err)
emitter.emit('error', err)
})
}).run()
chainer
.runSerially()
.success(function() {
self.emit('dropAllTables', null)
dropAllTablesEmitter.emit('success', null)
})
.error(onError)
}).error(onError)
}).error(onError)
}).run()
}
}
QueryInterface.prototype.renameTable = function(before, after) {
......@@ -263,6 +274,7 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) {
var showTablesSql = self.QueryGenerator.showTablesQuery()
self.sequelize.query(showTablesSql, null, { raw: true }).success(function(tableNames) {
self.emit('showAllTables', null)
emitter.emit('success', Utils._.flatten(tableNames))
......@@ -397,6 +409,35 @@ module.exports = (function() {
return queryAndEmit.call(this, sql, 'showIndex')
}
QueryInterface.prototype.getForeignKeysForTables = function(tableNames) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
if (tableNames.length === 0) {
emitter.emit('success', {})
} else {
var chainer = new Utils.QueryChainer()
tableNames.forEach(function(tableName) {
var sql = self.QueryGenerator.getForeignKeysQuery(tableName, self.sequelize.config.database)
chainer.add(self.sequelize, 'query', [sql])
})
chainer.runSerially().proxy(emitter, {
skipEvents: ['success']
}).success(function(results) {
var result = {}
tableNames.forEach(function(tableName, i) {
result[tableName] = Utils._.compact(results[i]).map(function(r) { return r.constraint_name })
})
emitter.emit('success', result)
})
}
}).run()
}
QueryInterface.prototype.removeIndex = function(tableName, indexNameOrAttributes) {
var sql = this.QueryGenerator.removeIndexQuery(tableName, indexNameOrAttributes)
return queryAndEmit.call(this, sql, "removeIndex")
......@@ -434,13 +475,12 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
.success(function(results){
emitter.query = { sql: sql }
emitter.emit('success', results[1])
emitter.emit('success', results[0])
emitter.emit('sql', sql)
})
.error(function(err) {
......@@ -461,7 +501,6 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkUpdate'])
return chainer.runSerially()
......@@ -547,7 +586,6 @@ module.exports = (function() {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [[sql, dao], 'delete'])
chainer.runSerially()
......@@ -578,7 +616,6 @@ module.exports = (function() {
return new Utils.CustomEventEmitter(function(emitter) {
var chainer = new Utils.QueryChainer()
chainer.add(self, 'enableForeignKeyConstraints', [])
chainer.add(self, 'queryAndEmit', [sql, 'bulkDelete', options])
chainer.runSerially()
......@@ -660,30 +697,6 @@ module.exports = (function() {
}).run()
}
QueryInterface.prototype.enableForeignKeyConstraints = function() {
var sql = this.QueryGenerator.enableForeignKeyConstraintsQuery()
if(sql) {
return queryAndEmit.call(this, sql, 'enableForeignKeyConstraints')
} else {
return new Utils.CustomEventEmitter(function(emitter) {
this.emit('enableForeignKeyConstraints', null)
emitter.emit('success')
}).run()
}
}
QueryInterface.prototype.disableForeignKeyConstraints = function() {
var sql = this.QueryGenerator.disableForeignKeyConstraintsQuery()
if(sql){
return queryAndEmit.call(this, sql, 'disableForeignKeyConstraints')
} else {
return new Utils.CustomEventEmitter(function(emitter) {
this.emit('disableForeignKeyConstraints', null)
emitter.emit('success')
}).run()
}
}
QueryInterface.prototype.createTrigger = function(tableName, triggerName, timingType, fireOnArray,
functionName, functionParams, optionsArray) {
var sql = this.QueryGenerator.createTrigger(tableName, triggerName, timingType, fireOnArray, functionName
......
......@@ -28,12 +28,9 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
describe('hasSingle', function() {
beforeEach(function(done) {
var self = this
this.Article = this.sequelize.define('Article', {
'title': DataTypes.STRING
})
this.Label = this.sequelize.define('Label', {
'text': DataTypes.STRING
})
this.Article = this.sequelize.define('Article', { 'title': DataTypes.STRING })
this.Label = this.sequelize.define('Label', { 'text': DataTypes.STRING })
this.Article.hasMany(this.Label)
......
......@@ -182,8 +182,8 @@ if (dialect.match(/^mysql/)) {
}, {
title: 'functions can take functions as arguments',
arguments: ['myTable', function (sequelize) {
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
return {
order: [[sequelize.fn('f1', sequelize.fn('f2', sequelize.col('id'))), 'DESC']]
}
}],
expectation: "SELECT * FROM `myTable` ORDER BY f1(f2(`id`)) DESC;",
......@@ -194,7 +194,7 @@ if (dialect.match(/^mysql/)) {
arguments: ['myTable', function (sequelize) {
return {
order: [
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f1', sequelize.col('myTable.id')), 'DESC'],
[sequelize.fn('f2', 12, 'lalala', new Date(Date.UTC(2011, 2, 27, 10, 1, 55))), 'ASC']
]
}
......@@ -450,10 +450,10 @@ if (dialect.match(/^mysql/)) {
showIndexQuery: [
{
arguments: ['User'],
expectation: 'SHOW INDEX FROM User'
expectation: 'SHOW INDEX FROM `User`'
}, {
arguments: ['User', { database: 'sequelize' }],
expectation: "SHOW INDEX FROM User FROM sequelize"
expectation: "SHOW INDEX FROM `User` FROM `sequelize`"
}
],
......@@ -504,7 +504,7 @@ if (dialect.match(/^mysql/)) {
describe(suiteTitle, function() {
tests.forEach(function(test) {
var title = test.title || 'MySQL correctly returns ' + test.expectation + ' for ' + util.inspect(test.arguments)
it(title, function(done) {
it(title, function(done) {
// Options would normally be set by the query interface that instantiates the query-generator, but here we specify it explicitly
var context = test.context || {options: {}};
if (test.needsSequelize) {
......
......@@ -57,7 +57,7 @@ var Support = {
},
getSequelizeInstance: function(db, user, pass, options) {
options = options || {};
options = options || {}
options.dialect = options.dialect || this.getTestDialect()
return new Sequelize(db, user, pass, options)
},
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!