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

Commit 916473b5 by Sascha Depold

fixed renameColumn and changeColumn for sqlite (and partially pg)

1 parent 8183a0eb
......@@ -179,6 +179,7 @@ module.exports = (function() {
tableName: addQuotes(tableName),
query: addQuotes(attributeName) + ' SET NOT NULL'
})
definition = definition.replace('NOT NULL', '').trim()
} else {
attrSql += Utils._.template(query)({
......@@ -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\(/)) {
query = pgEnum(tableName, attributeName, definition) + query
definition = definition.replace(/^ENUM\(.+\)/, Utils.escape("enum_" + tableName + "_" + attributeName))
......
......@@ -90,6 +90,14 @@ module.exports = (function() {
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)
......
var Utils = require("../../utils")
, DataTypes = require("../../data-types")
var MySqlQueryGenerator = Utils._.extend(
Utils._.clone(require("../query-generator")),
Utils._.clone(require("../mysql/query-generator"))
......@@ -252,12 +253,36 @@ module.exports = (function() {
"DROP TABLE <%= tableName %>;",
QueryGenerator.createTableQuery(tableName, attributes),
"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("")
return Utils._.template(query, {
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() {
if (result[_result.name].defaultValue === undefined) {
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')
, DataTypes = require('./data-types')
var Utils = require('./utils')
, DataTypes = require('./data-types')
, SQLiteQueryInterface = require('./dialects/sqlite/query-interface')
module.exports = (function() {
var QueryInterface = function(sequelize) {
......@@ -14,7 +15,7 @@ module.exports = (function() {
Utils._.each(attributes, function(dataTypeOrOptions, attributeName) {
if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) {
attributeHashes[attributeName] = { type: dataTypeOrOptions }
attributeHashes[attributeName] = { type: dataTypeOrOptions, allowNull: true }
} else {
attributeHashes[attributeName] = dataTypeOrOptions
}
......@@ -117,32 +118,7 @@ module.exports = (function() {
if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot drop a column
return new Utils.CustomEventEmitter(function(emitter) {
this.describeTable(tableName).complete(function(err, fields) {
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))
SQLiteQueryInterface.removeColumn.call(this, tableName, attributeName, emitter, queryAndEmit)
}.bind(this)).run()
} else {
var sql = this.QueryGenerator.removeColumnQuery(tableName, attributeName)
......@@ -154,50 +130,54 @@ module.exports = (function() {
var attributes = {}
if (Utils._.values(DataTypes).indexOf(dataTypeOrOptions) > -1) {
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: false }
attributes[attributeName] = { type: dataTypeOrOptions, allowNull: true }
} else {
attributes[attributeName] = dataTypeOrOptions
}
var options = this.QueryGenerator.attributesToSQL(attributes)
, sql = this.QueryGenerator.changeColumnQuery(tableName, options)
if (this.sequelize.options.dialect === 'sqlite') {
// 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) {
var self = this
return new Utils.CustomEventEmitter(function(emitter) {
self.describeTable(tableName).success(function(data) {
this.describeTable(tableName).success(function(data) {
data = data[attrNameBefore] || {}
var options = {}
options[attrNameAfter] = {
attribute: attrNameAfter,
type: data.type,
allowNull: data.allowNull,
attribute: attrNameAfter,
type: data.type,
allowNull: data.allowNull,
defaultValue: data.defaultValue
}
var sql = self.QueryGenerator.renameColumnQuery(tableName,
attrNameBefore,
self.QueryGenerator.attributesToSQL(options)
)
self.sequelize.query(sql, null, {}).success(function() {
self.emit('renameColumn', null)
emitter.emit('success', null)
}).error(function(err) {
self.emit('renameColumn', err)
emitter.emit('error', err)
})
}).error(function(err) {
self.emit('renameColumn', err)
if (this.sequelize.options.dialect === 'sqlite') {
// sqlite needs some special treatment as it cannot rename a column
SQLiteQueryInterface.renameColumn.call(this, tableName, attrNameBefore, attrNameAfter, emitter, queryAndEmit)
} else {
var sql = this.QueryGenerator.renameColumnQuery(tableName,
attrNameBefore,
this.QueryGenerator.attributesToSQL(options)
)
queryAndEmit.call(this, sql, 'renameColumn', {}, emitter)
}
}.bind(this))
.error(function(err) {
this.emit('renameColumn', err)
emitter.emit('error', err)
})
}).run()
}.bind(this))
}.bind(this)).run()
}
QueryInterface.prototype.addIndex = function(tableName, attributes, options) {
......
var util = require("util")
, DataTypes = require("./data-types")
, SqlString = require("./SqlString")
, SqlString = require("./sql-string")
var Utils = module.exports = {
_: (function() {
......
......@@ -170,7 +170,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), function() {
it("executes migration #20111205064000 and renames a table", function(done) {
this.sequelize.getQueryInterface().showAllTables().success(function(tableNames) {
tableNames = tableNames.filter(function(e){ return e != 'SequelizeMeta' })
expect(tableNames).toEqual([ 'Person' ])
expect(tableNames).toContain('Person')
this.init({ from: 20111205064000, to: 20111205064000 }, function(migrator) {
migrator.migrate().success(function() {
......@@ -234,13 +234,17 @@ describe(Helpers.getTestDialectTeaser("Migrator"), 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) {
migrator.migrate().success(function() {
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
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.defaultValue).toEqual('Signature')
......@@ -253,7 +257,7 @@ describe(Helpers.getTestDialectTeaser("Migrator"), 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) {
migrator.migrate().success(function(){
this.sequelize.getQueryInterface().describeTable('User').success(function(data) {
......
......@@ -10,7 +10,7 @@ if (dialect.match(/^postgres/)) {
describe('[POSTGRES] DAO', function() {
before(function(done) {
var self = this
console.log("to init");
Helpers.initTests({
dialect: dialect,
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!