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

Commit a014bd8d by Mick Hansen

Merge pull request #1594 from seegno/fix-hstore-update

Escape hstore values in all query types
2 parents 34bfda27 3d1a9ff0
...@@ -1163,7 +1163,7 @@ module.exports = (function() { ...@@ -1163,7 +1163,7 @@ module.exports = (function() {
return emitter.emit('error', err) return emitter.emit('error', err)
} }
query = self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options) query = self.QueryInterface.bulkUpdate(self.getTableName(), attrValueHash, where, options, self.rawAttributes)
query.on('sql', function(sql) { query.on('sql', function(sql) {
emitter.emit('sql', sql) emitter.emit('sql', sql)
}) })
......
...@@ -2,7 +2,6 @@ var Utils = require("./utils") ...@@ -2,7 +2,6 @@ var Utils = require("./utils")
, Mixin = require("./associations/mixin") , Mixin = require("./associations/mixin")
, DaoValidator = require("./dao-validator") , DaoValidator = require("./dao-validator")
, DataTypes = require("./data-types") , DataTypes = require("./data-types")
, hstore = require('./dialects/postgres/hstore')
, _ = require('lodash') , _ = require('lodash')
, defaultsOptions = { raw: true } , defaultsOptions = { raw: true }
...@@ -318,7 +317,6 @@ module.exports = (function() { ...@@ -318,7 +317,6 @@ module.exports = (function() {
for (var attrName in self.Model.rawAttributes) { for (var attrName in self.Model.rawAttributes) {
if (self.Model.rawAttributes.hasOwnProperty(attrName)) { if (self.Model.rawAttributes.hasOwnProperty(attrName)) {
var definition = self.Model.rawAttributes[attrName] var definition = self.Model.rawAttributes[attrName]
, isHstore = !!definition.type && (definition.type.toString() === DataTypes.HSTORE.toString())
, isEnum = !!definition.type && (definition.type.toString() === DataTypes.ENUM.toString()) , isEnum = !!definition.type && (definition.type.toString() === DataTypes.ENUM.toString())
, isMySQL = ['mysql', 'mariadb'].indexOf(self.Model.daoFactoryManager.sequelize.options.dialect) !== -1 , isMySQL = ['mysql', 'mariadb'].indexOf(self.Model.daoFactoryManager.sequelize.options.dialect) !== -1
, ciCollation = !!self.Model.options.collate && self.Model.options.collate.match(/_ci$/i) , ciCollation = !!self.Model.options.collate && self.Model.options.collate.match(/_ci$/i)
...@@ -334,12 +332,6 @@ module.exports = (function() { ...@@ -334,12 +332,6 @@ module.exports = (function() {
values[attrName] = definition.values[scopeIndex] values[attrName] = definition.values[scopeIndex]
} }
} }
if (isHstore) {
if (typeof values[attrName] === "object") {
values[attrName] = hstore.stringify(values[attrName])
}
}
} }
} }
......
var Utils = require("../../utils") var Utils = require("../../utils")
, hstore = require('./hstore')
, util = require("util") , util = require("util")
, DataTypes = require("../../data-types") , DataTypes = require("../../data-types")
, SqlString = require("../../sql-string") , SqlString = require("../../sql-string")
...@@ -273,7 +274,7 @@ module.exports = (function() { ...@@ -273,7 +274,7 @@ module.exports = (function() {
if (serials.indexOf(key) !== -1) { if (serials.indexOf(key) !== -1) {
return attrValueHash[key] || 'DEFAULT'; return attrValueHash[key] || 'DEFAULT';
} }
return this.escape(attrValueHash[key]) return this.escape(attrValueHash[key], modelAttributes && modelAttributes[key])
}.bind(this)).join(",") + }.bind(this)).join(",") +
")") ")")
}.bind(this)) }.bind(this))
...@@ -445,7 +446,7 @@ module.exports = (function() { ...@@ -445,7 +446,7 @@ module.exports = (function() {
// get populated on all databases as DEFAULT value // get populated on all databases as DEFAULT value
// i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP // i.e. mysql requires: DEFAULT CURRENT_TIMESTAMP
template += " DEFAULT <%= defaultValue %>" template += " DEFAULT <%= defaultValue %>"
replacements.defaultValue = this.escape(dataType.defaultValue) replacements.defaultValue = this.escape(dataType.defaultValue, dataType)
} }
if (dataType.unique === true) { if (dataType.unique === true) {
...@@ -796,6 +797,21 @@ module.exports = (function() { ...@@ -796,6 +797,21 @@ module.exports = (function() {
} }
}, },
/*
Escape a value (e.g. a string, number or date)
*/
escape: function(value, field) {
if (value instanceof Utils.fn || value instanceof Utils.col || value instanceof Utils.literal || value instanceof Utils.cast) {
return value.toString(this)
} else {
if (field && field.type && field.type.toString() === DataTypes.HSTORE.type && Utils._.isObject(value)) {
value = hstore.stringify(value);
}
return SqlString.escape(value, false, null, this.dialect, field)
}
},
/** /**
* Generates an SQL query that returns all foreign keys of a table. * Generates an SQL query that returns all foreign keys of a table.
* *
......
...@@ -499,8 +499,8 @@ module.exports = (function() { ...@@ -499,8 +499,8 @@ module.exports = (function() {
return this.queryAndEmit([sql, dao, options], 'update') return this.queryAndEmit([sql, dao, options], 'update')
} }
QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options) { QueryInterface.prototype.bulkUpdate = function(tableName, values, identifier, options, attributes) {
var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options) var sql = this.QueryGenerator.updateQuery(tableName, values, identifier, options, attributes)
return this.queryAndEmit([sql, null, options], 'bulkUpdate') return this.queryAndEmit([sql, null, options], 'bulkUpdate')
} }
......
...@@ -13,9 +13,9 @@ if (dialect.match(/^postgres/)) { ...@@ -13,9 +13,9 @@ if (dialect.match(/^postgres/)) {
this.sequelize.options.quoteIdentifiers = true this.sequelize.options.quoteIdentifiers = true
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)}, email: { type: DataTypes.ARRAY(DataTypes.TEXT) },
settings: DataTypes.HSTORE, settings: DataTypes.HSTORE,
document: {type: DataTypes.HSTORE, defaultValue: '"default"=>"value"'} document: { type: DataTypes.HSTORE, defaultValue: { default: 'value' } }
}) })
this.User.sync({ force: true }).success(function() { this.User.sync({ force: true }).success(function() {
done() done()
...@@ -272,6 +272,28 @@ if (dialect.match(/^postgres/)) { ...@@ -272,6 +272,28 @@ if (dialect.match(/^postgres/)) {
.error(console.log) .error(console.log)
}) })
it("should update hstore correctly", function(done) {
var self = this
this.User
.create({ username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}})
.success(function(newUser) {
// Check to see if the default value for an hstore field works
expect(newUser.document).to.deep.equal({default: 'value'})
expect(newUser.settings).to.deep.equal({ created: { test: '"value"' }})
// Check to see if updating an hstore field works
self.User.update({settings: {should: 'update', to: 'this', first: 'place'}}, newUser.identifiers).success(function() {
newUser.reload().success(function() {
// Postgres always returns keys in alphabetical order (ascending)
expect(newUser.settings).to.deep.equal({first: 'place', should: 'update', to: 'this'})
done()
});
})
})
.error(console.log)
})
it("should read hstore correctly", function(done) { it("should read hstore correctly", function(done) {
var self = this var self = this
var data = { username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}} var data = { username: 'user', email: ['foo@bar.com'], settings: { created: { test: '"value"' }}}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!