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

Commit 85f931df by Daniel Durante

Adds sequelize.literal() and sequelize.cast()

sequelize.literal() will tell Sequelize to not escape whatever you type
into the argument, for example:

sequelize.literal('1-2') will result in "1-2" unescaped.

sequelize.cast(input, type) will cast input into type, for example:

sequelize.cast('1-2', 'integer') will result in:

```CAST('1-2' AS INTEGER)```
1 parent 94ec0379
...@@ -281,7 +281,7 @@ module.exports = (function() { ...@@ -281,7 +281,7 @@ module.exports = (function() {
Objects: Objects:
* If raw is set, that value should be returned verbatim, without quoting * 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 * 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 unless they are themselves objects
* If direction is set, should be prepended * If direction is set, should be prepended
...@@ -293,7 +293,7 @@ module.exports = (function() { ...@@ -293,7 +293,7 @@ module.exports = (function() {
return this.quoteIdentifiers(obj, force) return this.quoteIdentifiers(obj, force)
} else if (Array.isArray(obj)) { } else if (Array.isArray(obj)) {
return this.quote(obj[0], force) + ' ' + obj[1] return this.quote(obj[0], force) + ' ' + obj[1]
} else if (obj instanceof Utils.fn || obj instanceof Utils.col) { } else if (obj instanceof Utils.fn || obj instanceof Utils.col || obj instanceof Utils.literal || obj instanceof Utils.cast) {
return obj.toString(this) return obj.toString(this)
} else if (Utils._.isObject(obj) && 'raw' in obj) { } else if (Utils._.isObject(obj) && 'raw' in obj) {
return obj.raw return obj.raw
...@@ -363,11 +363,11 @@ module.exports = (function() { ...@@ -363,11 +363,11 @@ module.exports = (function() {
Escape a value (e.g. a string, number or date) Escape a value (e.g. a string, number or date)
*/ */
escape: function(value, field) { escape: function(value, field) {
if (value instanceof Utils.fn || value instanceof Utils.col) { if (value instanceof Utils.fn || value instanceof Utils.col || value instanceof Utils.literal || value instanceof Utils.cast) {
return value.toString(this) return value.toString(this)
} else { } else {
return SqlString.escape(value, false, null, this.dialect, field) return SqlString.escape(value, false, null, this.dialect, field)
} }
} }
} }
......
...@@ -363,5 +363,13 @@ module.exports = (function() { ...@@ -363,5 +363,13 @@ module.exports = (function() {
return new Utils.col(col) return new Utils.col(col)
} }
Sequelize.prototype.cast = function (val, type) {
return new Utils.cast(val, type)
}
Sequelize.prototype.literal = function (val) {
return new Utils.literal(val)
}
return Sequelize return Sequelize
})() })()
...@@ -492,7 +492,31 @@ var Utils = module.exports = { ...@@ -492,7 +492,31 @@ var Utils = module.exports = {
}, },
col: function (col) { col: function (col) {
this.col = col this.col = col
},
cast: function (val, type) {
this.val = val
this.type = (type || '').trim()
},
literal: function (val) {
this.val = val
}
}
// I know this may seem silly, but this gives us the ability to recognize whether
// or not we should be escaping or if we should trust the user. Basically, it
// keeps things in perspective and organized.
Utils.literal.prototype.toString = function() {
return this.val
}
Utils.cast.prototype.toString = function(queryGenerator) {
if (!this.val instanceof Utils.fn && !this.val instanceof Utils.col && !this.val instanceof Utils.literal) {
this.val = queryGenerator.escape(this.val)
} else {
this.val = this.val.toString(queryGenerator)
} }
return 'CAST(' + this.val + ' AS ' + this.type.toUpperCase() + ')'
} }
Utils.fn.prototype.toString = function(queryGenerator) { Utils.fn.prototype.toString = function(queryGenerator) {
...@@ -501,9 +525,10 @@ Utils.fn.prototype.toString = function(queryGenerator) { ...@@ -501,9 +525,10 @@ Utils.fn.prototype.toString = function(queryGenerator) {
return arg.toString(queryGenerator) return arg.toString(queryGenerator)
} else { } else {
return queryGenerator.escape(arg) return queryGenerator.escape(arg)
} }
}).join(', ') + ')' }).join(', ') + ')'
} }
Utils.col.prototype.toString = function (queryGenerator) { Utils.col.prototype.toString = function (queryGenerator) {
return queryGenerator.quote(this.col) return queryGenerator.quote(this.col)
} }
......
...@@ -383,6 +383,82 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -383,6 +383,82 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
describe('create', function() { describe('create', function() {
it('is possible to use casting when creating an instance', function (done) {
var self = this
, type = dialect === "mysql" ? 'signed' : 'integer'
, _done = _.after(2, function() {
done()
})
this.User.create({
intVal: this.sequelize.cast('1', type)
}).on('sql', function (sql) {
expect(sql).to.match(new RegExp('CAST\\(1 AS ' + type.toUpperCase() + '\\)'))
_done()
})
.success(function (user) {
self.User.find(user.id).success(function (user) {
expect(user.intVal).to.equal(1)
_done()
})
})
})
it('is possible to use casting multiple times mixed in with other utilities', function (done) {
var self = this
, type = this.sequelize.cast(this.sequelize.cast(this.sequelize.literal('1-2'), 'integer'), 'integer')
, _done = _.after(2, function() {
done()
})
if (dialect === "mysql") {
type = this.sequelize.cast(this.sequelize.cast(this.sequelize.literal('1-2'), 'unsigned'), 'signed')
}
this.User.create({
intVal: type
}).on('sql', function (sql) {
if (dialect === "mysql") {
expect(sql).to.contain('CAST(CAST(1-2 AS UNSIGNED) AS SIGNED)')
} else {
expect(sql).to.contain('CAST(CAST(1-2 AS INTEGER) AS INTEGER)')
}
_done()
}).success(function (user) {
self.User.find(user.id).success(function (user) {
expect(user.intVal).to.equal(-1)
_done()
})
})
})
it('is possible to just use .literal() to bypass escaping', function (done) {
var self = this
this.User.create({
intVal: this.sequelize.literal('CAST(1-2 AS ' + (dialect === "mysql" ? 'SIGNED' : 'INTEGER') + ')')
}).success(function (user) {
self.User.find(user.id).success(function (user) {
expect(user.intVal).to.equal(-1)
done()
})
})
})
it('is possible for .literal() to contain other utility functions', function (done) {
var self = this
this.User.create({
intVal: this.sequelize.literal(this.sequelize.cast('1-2', (dialect === "mysql" ? 'SIGNED' : 'INTEGER')))
}).success(function (user) {
self.User.find(user.id).success(function (user) {
expect(user.intVal).to.equal(-1)
done()
})
})
})
it('is possible to use funtions when creating an instance', function (done) { it('is possible to use funtions when creating an instance', function (done) {
var self = this var self = this
this.User.create({ this.User.create({
...@@ -394,6 +470,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -394,6 +470,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
}) })
it('is possible to use functions as default values', function (done) { it('is possible to use functions as default values', function (done) {
var self = this var self = this
, userWithDefaults , userWithDefaults
...@@ -446,6 +523,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -446,6 +523,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done() done()
} }
}) })
it("casts empty arrays correctly for postgresql insert", function(done) { it("casts empty arrays correctly for postgresql insert", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") { if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').to.equal('') expect('').to.equal('')
...@@ -1068,6 +1146,21 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -1068,6 +1146,21 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
it('updates with casting', function (done) {
var self = this
this.User.create({
username: 'John'
}).success(function(user) {
self.User.update({username: self.sequelize.cast('1', 'char')}, {username: 'John'}).success(function() {
self.User.all().success(function(users) {
expect(users[0].username).to.equal('1')
done()
})
})
})
})
it('updates with function and column value', function (done) { it('updates with function and column value', function (done) {
var self = this var self = this
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!