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

Commit 916473b5 by Sascha Depold

fixed renameColumn and changeColumn for sqlite (and partially pg)

1 parent 8183a0eb
...@@ -179,6 +179,7 @@ module.exports = (function() { ...@@ -179,6 +179,7 @@ module.exports = (function() {
tableName: addQuotes(tableName), tableName: addQuotes(tableName),
query: addQuotes(attributeName) + ' SET NOT NULL' query: addQuotes(attributeName) + ' SET NOT NULL'
}) })
definition = definition.replace('NOT NULL', '').trim() definition = definition.replace('NOT NULL', '').trim()
} else { } else {
attrSql += Utils._.template(query)({ attrSql += Utils._.template(query)({
...@@ -187,6 +188,20 @@ module.exports = (function() { ...@@ -187,6 +188,20 @@ module.exports = (function() {
}) })
} }
if (definition.indexOf('DEFAULT') > 0) {
attrSql += Utils._.template(query)({
tableName: addQuotes(tableName),
query: addQuotes(attributeName) + ' SET DEFAULT' + definition.match(/DEFAULT ([^;]+)/)[1]
})
definition = definition.replace(/(DEFAULT[^;]+)/, '').trim()
} else {
attrSql += Utils._.template(query)({
tableName: addQuotes(tableName),
query: addQuotes(attributeName) + ' DROP DEFAULT'
})
}
if (definition.match(/^ENUM\(/)) { if (definition.match(/^ENUM\(/)) {
query = pgEnum(tableName, attributeName, definition) + query query = pgEnum(tableName, attributeName, definition) + query
definition = definition.replace(/^ENUM\(.+\)/, Utils.escape("enum_" + tableName + "_" + attributeName)) definition = definition.replace(/^ENUM\(.+\)/, Utils.escape("enum_" + tableName + "_" + attributeName))
......
...@@ -90,6 +90,14 @@ module.exports = (function() { ...@@ -90,6 +90,14 @@ module.exports = (function() {
result[_result.Field].defaultValue = null result[_result.Field].defaultValue = null
} }
} }
if (typeof result[_result.Field].defaultValue === 'string') {
result[_result.Field].defaultValue = result[_result.Field].defaultValue.replace(/'/g, "")
if (result[_result.Field].defaultValue.indexOf('::') > -1) {
result[_result.Field].defaultValue = result[_result.Field].defaultValue.split('::')[0]
}
}
}) })
this.emit('success', result) this.emit('success', result)
......
var Utils = require("../../utils") var Utils = require("../../utils")
, DataTypes = require("../../data-types") , DataTypes = require("../../data-types")
var MySqlQueryGenerator = Utils._.extend( var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")), Utils._.clone(require("../query-generator")),
Utils._.clone(require("../mysql/query-generator")) Utils._.clone(require("../mysql/query-generator"))
...@@ -252,12 +253,36 @@ module.exports = (function() { ...@@ -252,12 +253,36 @@ module.exports = (function() {
"DROP TABLE <%= tableName %>;", "DROP TABLE <%= tableName %>;",
QueryGenerator.createTableQuery(tableName, attributes), QueryGenerator.createTableQuery(tableName, attributes),
"INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= tableName %>_backup;", "INSERT INTO <%= tableName %> SELECT <%= attributeNames %> FROM <%= tableName %>_backup;",
"DROP TABLE <%= tableName %>_backup;", "DROP TABLE <%= tableName %>_backup;"
].join("")
return Utils._.template(query, {
tableName: tableName,
attributeNames: Utils._.keys(attributes).join(', ')
})
},
renameColumnQuery: function(tableName, attrNameBefore, attrNameAfter, attributes) {
attributes = QueryGenerator.attributesToSQL(attributes)
var backupTableName = tableName + "_backup"
var query = [
QueryGenerator.createTableQuery(backupTableName, attributes).replace('CREATE TABLE', 'CREATE TEMPORARY TABLE'),
"INSERT INTO <%= tableName %>_backup SELECT <%= attributeNamesImport %> FROM <%= tableName %>;",
"DROP TABLE <%= tableName %>;",
QueryGenerator.createTableQuery(tableName, attributes),
"INSERT INTO <%= tableName %> SELECT <%= attributeNamesExport %> FROM <%= tableName %>_backup;",
"DROP TABLE <%= tableName %>_backup;"
].join("") ].join("")
return Utils._.template(query, { return Utils._.template(query, {
tableName: tableName, tableName: tableName,
attributeNames: Utils._.keys(attributes) attributeNamesImport: Utils._.keys(attributes).map(function(attr) {
return (attrNameAfter === attr) ? attrNameBefore + ' AS ' + attr : attr
}).join(', '),
attributeNamesExport: Utils._.keys(attributes).map(function(attr) {
return attr
}).join(', ')
}) })
}, },
......
Utils = require("../../utils")
/**
Returns an object that treats SQLite's inabilities to do certain queries.
@class QueryInterface
@static
*/
var QueryInterface = module.exports = {
/**
A wrapper that fixes SQLite's inability to remove columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a
new table with the same name but without the obsolete column.
@method removeColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {String} attributeName The name of the attribute that we want to remove.
@param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@since 1.6.0
*/
removeColumn: function(tableName, attributeName, emitter, queryAndEmit) {
this.describeTable(tableName).complete(function(err, fields) {
if (err) {
emitter.emit('error', err)
} else {
delete fields[attributeName]
var sql = this.QueryGenerator.removeColumnQuery(tableName, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== '' })
QueryInterface.execMultiQuery.call(this, subQueries, 'removeColumn', emitter, queryAndEmit)
}
}.bind(this))
},
/**
A wrapper that fixes SQLite's inability to change columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a
new table with the same name but with a modified version of the respective column.
@method changeColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {Object} attributes An object with the attribute's name as key and it's options as value object.
@param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@since 1.6.0
*/
changeColumn: function(tableName, attributes, emitter, queryAndEmit) {
var attributeName = Utils._.keys(attributes)[0]
this.describeTable(tableName).complete(function(err, fields) {
if (err) {
emitter.emit('error', err)
} else {
fields[attributeName] = attributes[attributeName]
var sql = this.QueryGenerator.removeColumnQuery(tableName, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== '' })
QueryInterface.execMultiQuery.call(this, subQueries, 'changeColumn', emitter, queryAndEmit)
}
}.bind(this))
},
/**
A wrapper that fixes SQLite's inability to rename columns from existing tables.
It will create a backup of the table, drop the table afterwards and create a
new table with the same name but with a renamed version of the respective column.
@method renameColumn
@for QueryInterface
@param {String} tableName The name of the table.
@param {String} attrNameBefore The name of the attribute before it was renamed.
@param {String} attrNameAfter The name of the attribute after it was renamed.
@param {CustomEventEmitter} emitter The EventEmitter from outside.
@param {Function} queryAndEmit The function from outside that triggers some events to get triggered.
@since 1.6.0
*/
renameColumn: function(tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit) {
this.describeTable(tableName).complete(function(err, fields) {
if (err) {
emitter.emit('error', err)
} else {
fields[attrNameAfter] = Utils._.clone(fields[attrNameBefore])
delete fields[attrNameBefore]
var sql = this.QueryGenerator.renameColumnQuery(tableName, attrNameBefore, attrNameAfter, fields)
, subQueries = sql.split(';').filter(function(q) { return q !== '' })
QueryInterface.execMultiQuery.call(this, subQueries, 'renameColumn', emitter, queryAndEmit)
}
}.bind(this))
},
execMultiQuery: function(queries, methodName, emitter, queryAndEmit) {
var chainer = new Utils.QueryChainer()
queries.splice(0, queries.length - 1).forEach(function(query) {
chainer.add(this.sequelize, 'query', [query + ";", null, { raw: true }])
}.bind(this))
chainer
.runSerially()
.complete(function(err) {
if (err) {
emitter.emit(methodName, err)
emitter.emit('error', err)
} else {
queryAndEmit.call(this, queries.splice(queries.length - 1)[0], methodName, {}, emitter)
}
}.bind(this))
}
}
...@@ -128,6 +128,10 @@ module.exports = (function() { ...@@ -128,6 +128,10 @@ module.exports = (function() {
if (result[_result.name].defaultValue === undefined) { if (result[_result.name].defaultValue === undefined) {
result[_result.name].defaultValue = null result[_result.name].defaultValue = null
} }
if (typeof result[_result.name].defaultValue === 'string') {
result[_result.name].defaultValue = result[_result.name].defaultValue.replace(/'/g, "")
}
}) })
} }
......
var Utils = require('./utils') var Utils = require('./utils')
, DataTypes = require('./data-types') , DataTypes = require('./data-types')
, SQLiteQueryInterface = require('./dialects/sqlite/query-interface')
module.exports = (function() { module.exports = (function() {
var QueryInterface = function(sequelize) { var QueryInterface = function(sequelize) {
...@@ -14,7 +15,7 @@ module.exports = (function() { ...@@ -14,7 +15,7 @@ module.exports = (function() {
Utils._.each(attributes, function(dataTypeOrOptions, attributeName) { Utils._.each(attributes, function(dataTypeOrOptions, attributeName) {
if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) { if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) {
attributeHashes[attributeName] = { type: dataTypeOrOptions } attributeHashes[attributeName] = { type: dataTypeOrOptions, allowNull: true }
} else { } else {
attributeHashes[attributeName] = dataTypeOrOptions attributeHashes[attributeName] = dataTypeOrOptions
} }
...@@ -117,32 +118,7 @@ module.exports = (function() { ...@@ -117,32 +118,7 @@ module.exports = (function() {
if (this.sequelize.options.dialect === 'sqlite') { if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot drop a column // sqlite needs some special treatment as it cannot drop a column
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
this.describeTable(tableName).complete(function(err, fields) { SQLiteQueryInterface.removeColumn.call(this, tableName, attributeName, emitter, queryAndEmit)
if (err) {
emitter.emit('error', err)
} else {
delete fields[attributeName]
var sql = this.QueryGenerator.removeColumnQuery(tableName, fields)
, chainer = new Utils.QueryChainer()
, subQueries = sql.split(';').filter(function(q) { return q !== '' })
subQueries.splice(0, subQueries.length - 1).forEach(function(subQuery) {
chainer.add(this.sequelize, 'query', [subQuery + ";", null, { raw: true }])
}.bind(this))
chainer
.runSerially()
.complete(function(err) {
if (err) {
emitter.emit('removeColumn', err)
emitter.emit('error', err)
} else {
queryAndEmit.call(this, subQueries.splice(subQueries.length - 1)[0], 'removeColumn')
}
}.bind(this))
}
}.bind(this))
}.bind(this)).run() }.bind(this)).run()
} else { } else {
var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName) var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName)
...@@ -154,50 +130,54 @@ module.exports = (function() { ...@@ -154,50 +130,54 @@ module.exports = (function() {
var attributes = {} var attributes = {}
if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) { if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) {
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false } attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true }
} else { } else {
attributes[attributeName] = dataTypeOrOptions attributes[attributeName] = dataTypeOrOptions
} }
var options = this.QueryGenerator.attributesToSQL(attributes) if (this.sequelize.options.dialect === 'sqlite') {
, sql = this.QueryGenerator.changeColumnQuery(tableName, options) // sqlite needs some special treatment as it cannot change a column
return new Utils.CustomEventEmitter(function(emitter) {
SQLiteQueryInterface.changeColumn.call(this, tableName, attributes, emitter, queryAndEmit)
}.bind(this)).run()
} else {
var options = this.QueryGenerator.attributesToSQL(attributes)
, sql = this.QueryGenerator.changeColumnQuery(tableName, options)
return queryAndEmit.call(this, sql, 'changeColumn') return queryAndEmit.call(this, sql, 'changeColumn')
}
} }
QueryInterface.prototype.renameColumn = function(tableName, attrNameBefore, attrNameAfter) { QueryInterface.prototype.renameColumn = function(tableName, attrNameBefore, attrNameAfter) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
self.describeTable(tableName).success(function(data) { this.describeTable(tableName).success(function(data) {
data = data[attrNameBefore] || {} data = data[attrNameBefore] || {}
var options = {} var options = {}
options[attrNameAfter] = { options[attrNameAfter] = {
attribute: attrNameAfter, attribute: attrNameAfter,
type: data.type, type: data.type,
allowNull: data.allowNull, allowNull: data.allowNull,
defaultValue: data.defaultValue defaultValue: data.defaultValue
} }
var sql = self.QueryGenerator.renameColumnQuery(tableName, if (this.sequelize.options.dialect === 'sqlite') {
attrNameBefore, // sqlite needs some special treatment as it cannot rename a column
self.QueryGenerator.attributesToSQL(options) SQLiteQueryInterface.renameColumn.call(this, tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit)
) } else {
var sql = this.QueryGenerator.renameColumnQuery(tableName,
self.sequelize.query(sql, null, {}).success(function() { attrNameBefore,
self.emit('renameColumn', null) this.QueryGenerator.attributesToSQL(options)
emitter.emit('success', null) )
}).error(function(err) { queryAndEmit.call(this, sql, 'renameColumn', {}, emitter)
self.emit('renameColumn', err) }
emitter.emit('error', err) }.bind(this))
}) .error(function(err) {
}).error(function(err) { this.emit('renameColumn', err)
self.emit('renameColumn', err)
emitter.emit('error', err) emitter.emit('error', err)
}) }.bind(this))
}).run() }.bind(this)).run()
} }
QueryInterface.prototype.addIndex = function(tableName, attributes, options) { QueryInterface.prototype.addIndex = function(tableName, attributes, options) {
......
var util = require("util") var util = require("util")
, DataTypes = require("./data-types") , DataTypes = require("./data-types")
, SqlString = require("./SqlString") , SqlString = require("./sql-string")
var Utils = module.exports = { var Utils = module.exports = {
_: (function() { _: (function() {
......
...@@ -170,7 +170,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() { ...@@ -170,7 +170,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() {
it("executes migration #20111205064000 and renames a table", function(done) { it("executes migration #20111205064000 and renames a table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) { this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' }) tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ]) expect(tableNames).toContain('Person')
this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) { this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() { migrator.migrate().success(function() {
...@@ -234,13 +234,17 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() { ...@@ -234,13 +234,17 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() {
}) })
describe('changeColumn', function() { describe('changeColumn', function() {
(dialect === 'mysql' ? it : itEventually)('changes the signature column from user to default "signature" + notNull', function(done) { it('changes the signature column from user to default "signature" + notNull', function(done) {
this.init({ to: 20111206063000 }, function(migrator) { this.init({ to: 20111206063000 }, function(migrator) {
migrator.migrate().success(function() { migrator.migrate().success(function() {
this.sequelize.getQueryInterface().describeTable('User').success(function(data) { this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
var signature = data.signature var signature = data.signature
expect(signature.type).toEqual('VARCHAR(255)') if (dialect === 'postgres') {
expect(signature.type).toEqual('CHARACTER VARYING')
} else {
expect(signature.type).toEqual('VARCHAR(255)')
}
expect(signature.allowNull).toEqual(false) expect(signature.allowNull).toEqual(false)
expect(signature.defaultValue).toEqual('Signature') expect(signature.defaultValue).toEqual('Signature')
...@@ -253,7 +257,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() { ...@@ -253,7 +257,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() {
}) })
describe('renameColumn', function() { describe('renameColumn', function() {
(dialect === 'mysql' ? it : itEventually)("renames the signature column from user to sig", function(done) { it("renames the signature column from user to sig", function(done) {
this.init({ to: 20111206163300 }, function(migrator) { this.init({ to: 20111206163300 }, function(migrator) {
migrator.migrate().success(function(){ migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) { this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
......
...@@ -10,7 +10,7 @@ if (dialect.match(/^postgres/)) { ...@@ -10,7 +10,7 @@ if (dialect.match(/^postgres/)) {
describe('[POSTGRES] DAO', function() { describe('[POSTGRES] DAO', function() {
before(function(done) { before(function(done) {
var self = this var self = this
console.log("to init");
Helpers.initTests({ Helpers.initTests({
dialect: dialect, dialect: dialect,
beforeComplete: function(sequelize, DataTypes) { beforeComplete: function(sequelize, DataTypes) {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!