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

Commit 1e6d842d by Daniel Durante

Added basic HSTORE support for PostgreSQL

1 parent 0aba244e
...@@ -122,12 +122,27 @@ module.exports = (function() { ...@@ -122,12 +122,27 @@ module.exports = (function() {
if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) { if (this.daoFactory.rawAttributes.hasOwnProperty(attrName)) {
var definition = this.daoFactory.rawAttributes[attrName] var definition = this.daoFactory.rawAttributes[attrName]
, isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString())) , isEnum = (definition.type && (definition.type.toString() === DataTypes.ENUM.toString()))
, isHstore = (!!definition.type && !!definition.type.type && definition.type.type === DataTypes.HSTORE.type)
, hasValue = (typeof values[attrName] !== 'undefined') , hasValue = (typeof values[attrName] !== 'undefined')
, valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1) , valueOutOfScope = ((definition.values || []).indexOf(values[attrName]) === -1)
if (isEnum && hasValue && valueOutOfScope) { if (isEnum && hasValue && valueOutOfScope) {
throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', ')) throw new Error('Value "' + values[attrName] + '" for ENUM ' + attrName + ' is out of allowed scope. Allowed values: ' + definition.values.join(', '))
} }
if (isHstore) {
if (typeof values[attrName] === "object") {
var text = []
Utils._.each(values[attrName], function(value, key){
if (typeof value !== "string" && typeof value !== "number") {
throw new Error('Value for HSTORE must be a string or number.')
}
text.push(this.QueryInterface.QueryGenerator.addQuotes(key) + '=>' + (typeof value === "string" ? this.QueryInterface.QueryGenerator.addQuotes(value) : value))
}.bind(this))
values[attrName] = text.join(',')
}
}
} }
} }
...@@ -355,6 +370,10 @@ module.exports = (function() { ...@@ -355,6 +370,10 @@ module.exports = (function() {
// add all passed values to the dao and store the attribute names in this.attributes // add all passed values to the dao and store the attribute names in this.attributes
for (var key in values) { for (var key in values) {
if (values.hasOwnProperty(key)) { if (values.hasOwnProperty(key)) {
if (typeof values[key] === "string" && !!this.__factory && !!this.__factory.rawAttributes[key] && !!this.__factory.rawAttributes[key].type && !!this.__factory.rawAttributes[key].type.type && this.__factory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
values[key] = this.QueryInterface.QueryGenerator.toHstore(values[key])
}
this.addAttribute(key, values[key]) this.addAttribute(key, values[key])
} }
} }
......
...@@ -32,5 +32,19 @@ module.exports = { ...@@ -32,5 +32,19 @@ module.exports = {
return result return result
}, },
ARRAY: function(type) { return type + '[]' }
ARRAY: function(type) { return type + '[]' },
get HSTORE() {
var result = function() {
return {
type: 'HSTORE'
}
}
result.type = 'HSTORE'
result.toString = result.valueOf = function() { return 'TEXT' }
return result
}
} }
...@@ -604,6 +604,21 @@ module.exports = (function() { ...@@ -604,6 +604,21 @@ module.exports = (function() {
return "DROP TYPE IF EXISTS " + enumName + "; CREATE TYPE " + enumName + " AS " + dataType.match(/^ENUM\(.+\)/)[0] + "; " return "DROP TYPE IF EXISTS " + enumName + "; CREATE TYPE " + enumName + " AS " + dataType.match(/^ENUM\(.+\)/)[0] + "; "
}, },
toHstore: function(text) {
var obj = {}
, pattern = '("\\\\.|[^"\\\\]*"\s*=|[^=]*)\s*=\s*>\s*("(?:\\.|[^"\\\\])*"|[^,]*)(?:\s*,\s*|$)'
, rex = new RegExp(pattern,'g')
, r = null
while ((r = rex.exec(text)) !== null) {
if (!!r[1] && !!r[2]) {
obj[r[1].replace(/^"/, '').replace(/"$/, '')] = r[2].replace(/^"/, '').replace(/"$/, '')
}
}
return obj
},
padInt: function (i) { padInt: function (i) {
return (i < 10) ? '0' + i.toString() : i.toString() return (i < 10) ? '0' + i.toString() : i.toString()
}, },
......
var Utils = require("../../utils") var Utils = require("../../utils")
, AbstractQuery = require('../abstract/query') , AbstractQuery = require('../abstract/query')
, DataTypes = require('../../data-types')
module.exports = (function() { module.exports = (function() {
var Query = function(client, sequelize, callee, options) { var Query = function(client, sequelize, callee, options) {
...@@ -109,7 +110,12 @@ module.exports = (function() { ...@@ -109,7 +110,12 @@ module.exports = (function() {
} else if (this.send('isInsertQuery')) { } else if (this.send('isInsertQuery')) {
for (var key in rows[0]) { for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) { if (rows[0].hasOwnProperty(key)) {
this.callee[key] = rows[0][key] var record = rows[0][key]
if (!!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = this.callee.daoFactory.daoFactoryManager.sequelize.queryInterface.QueryGenerator.toHstore(record)
}
this.callee[key] = record
} }
} }
...@@ -117,7 +123,12 @@ module.exports = (function() { ...@@ -117,7 +123,12 @@ module.exports = (function() {
} else if (this.send('isUpdateQuery')) { } else if (this.send('isUpdateQuery')) {
for (var key in rows[0]) { for (var key in rows[0]) {
if (rows[0].hasOwnProperty(key)) { if (rows[0].hasOwnProperty(key)) {
this.callee[key] = rows[0][key] var record = rows[0][key]
if (!!this.callee.daoFactory.rawAttributes[key].type && !!this.callee.daoFactory.rawAttributes[key].type.type && this.callee.daoFactory.rawAttributes[key].type.type === DataTypes.HSTORE.type) {
record = this.callee.daoFactory.daoFactoryManager.sequelize.queryInterface.QueryGenerator.toHstore(record)
}
this.callee[key] = record
} }
} }
......
...@@ -18,7 +18,8 @@ if (dialect.match(/^postgres/)) { ...@@ -18,7 +18,8 @@ if (dialect.match(/^postgres/)) {
self.User = sequelize.define('User', { self.User = sequelize.define('User', {
username: DataTypes.STRING, username: DataTypes.STRING,
email: {type: DataTypes.ARRAY(DataTypes.TEXT)} email: {type: DataTypes.ARRAY(DataTypes.TEXT)},
document: {type: DataTypes.HSTORE, defaultValue: 'default=>value'}
}) })
}, },
onComplete: function() { onComplete: function() {
...@@ -34,13 +35,34 @@ if (dialect.match(/^postgres/)) { ...@@ -34,13 +35,34 @@ if (dialect.match(/^postgres/)) {
this.User this.User
.create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] }) .create({ username: 'user', email: ['foo@bar.com', 'bar@baz.com'] })
.success(function(oldUser) { .success(function(oldUser) {
expect(oldUser.email).toEqual(['foo@bar.com', 'bar@baz.com']); expect(oldUser.email).toEqual(['foo@bar.com', 'bar@baz.com'])
done(); done()
}) })
.error(function(err) { .error(function(err) {
console.log(err) console.log(err)
}) })
}) })
it("should handle hstore correctly", function(done) {
var self = this
this.User
.create({ username: 'user', email: ['foo@bar.com'], document: {hello: 'world'}})
.success(function(newUser) {
expect(newUser.document).toEqual({hello: 'world'})
// Check to see if updating an hstore field works
newUser.updateAttributes({document: {should: 'update', to: 'this', first: 'place'}}).success(function(oldUser){
// Postgres always returns keys in alphabetical order (ascending)
expect(oldUser.document).toEqual({first: 'place', should: 'update', to: 'this'})
// Check to see if the default value for an hstore field works
self.User.create({ username: 'user2', email: ['bar@baz.com']}).success(function(defaultUser){
expect(defaultUser.document).toEqual({default: 'value'})
done()
})
})
})
.error(console.log)
})
}) })
}) })
} }
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!