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

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() {
Objects:
* 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
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
* If direction is set, should be prepended
......@@ -293,7 +293,7 @@ module.exports = (function() {
return this.quoteIdentifiers(obj, force)
} else if (Array.isArray(obj)) {
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)
} else if (Utils._.isObject(obj) && 'raw' in obj) {
return obj.raw
......@@ -363,11 +363,11 @@ 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) {
if (value instanceof Utils.fn || value instanceof Utils.col || value instanceof Utils.literal || value instanceof Utils.cast) {
return value.toString(this)
} 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() {
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
})()
......@@ -492,7 +492,31 @@ var Utils = module.exports = {
},
col: function (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) {
......@@ -501,9 +525,10 @@ Utils.fn.prototype.toString = function(queryGenerator) {
return arg.toString(queryGenerator)
} else {
return queryGenerator.escape(arg)
}
}
}).join(', ') + ')'
}
Utils.col.prototype.toString = function (queryGenerator) {
return queryGenerator.quote(this.col)
}
......
......@@ -383,6 +383,82 @@ describe(Support.getTestDialectTeaser("DAOFactory"), 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) {
var self = this
this.User.create({
......@@ -394,6 +470,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
})
})
})
it('is possible to use functions as default values', function (done) {
var self = this
, userWithDefaults
......@@ -446,6 +523,7 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done()
}
})
it("casts empty arrays correctly for postgresql insert", function(done) {
if (dialect !== "postgres" && dialect !== "postgresql-native") {
expect('').to.equal('')
......@@ -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) {
var self = this
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!