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

Commit df2ae8b5 by Sascha Depold

Merge branch 'master' into terraflubb-specify_config_in_binary

2 parents 21506e33 bdd9005d
...@@ -354,14 +354,8 @@ module.exports = (function() { ...@@ -354,14 +354,8 @@ module.exports = (function() {
DAOFactory.prototype.findAndCountAll = function(options) { DAOFactory.prototype.findAndCountAll = function(options) {
var self = this var self = this
, opts = Utils._.cloneDeep(options) // no limit, offset, order, attributes or include for the options given to count()
, copts = Utils._.extend({}, Utils._.cloneDeep(options) || {}, { , copts = Utils._.omit(options || {}, ['offset', 'limit', 'order', 'include', 'attributes'])
// no limit, offset, order or include for the options given to count()
offset : 0,
limit : 0,
order : null,
include : null
})
return new Utils.CustomEventEmitter(function (emitter) { return new Utils.CustomEventEmitter(function (emitter) {
var emit = { var emit = {
...@@ -377,20 +371,21 @@ module.exports = (function() { ...@@ -377,20 +371,21 @@ module.exports = (function() {
, sql : function(s) { // emit SQL , sql : function(s) { // emit SQL
emitter.emit('sql', s); emitter.emit('sql', s);
} }
} }
self.count(copts) self.count(copts)
.on('sql', emit.sql) .on('sql', emit.sql)
.error(emit.err) .error(emit.err)
.success(function(cnt) { .success(function(cnt) {
if (cnt === 0) if (cnt === 0) {
return emit.okay(cnt) // no records, no need for another query return emit.okay(cnt) // no records, no need for another query
}
self.findAll(options) self.findAll(options)
.on('sql', emit.sql) .on('sql', emit.sql)
.error(emit.err) .error(emit.err)
.success(function(rows) { .success(function(rows) {
emit.okay(cnt, rows) emit.okay(cnt, rows)
}) })
}) })
......
...@@ -183,7 +183,7 @@ module.exports = { ...@@ -183,7 +183,7 @@ module.exports = {
} }
result.type = 'HSTORE' result.type = 'HSTORE'
result.toString = result.valueOf = function() { return 'TEXT' } result.toString = result.valueOf = function() { return 'HSTORE' }
return result return result
} }
......
...@@ -5,6 +5,7 @@ var Utils = require("../../utils") ...@@ -5,6 +5,7 @@ var Utils = require("../../utils")
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
dialect: 'mysql',
addSchema: function(opts) { addSchema: function(opts) {
var tableName var tableName
var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined) var schema = (!!opts && !!opts.options && !!opts.options.schema ? opts.options.schema : undefined)
...@@ -437,7 +438,7 @@ module.exports = (function() { ...@@ -437,7 +438,7 @@ module.exports = (function() {
} else if (typeof smth === "string") { } else if (typeof smth === "string") {
result = smth result = smth
} else if (Array.isArray(smth)) { } else if (Array.isArray(smth)) {
result = Utils.format(smth) result = Utils.format(smth, this.dialect)
} }
return result ? result : '1=1' return result ? result : '1=1'
......
...@@ -8,6 +8,7 @@ var Utils = require("../../utils") ...@@ -8,6 +8,7 @@ var Utils = require("../../utils")
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
options: {}, options: {},
dialect: 'postgres',
addSchema: function(opts) { addSchema: function(opts) {
var tableName = undefined var tableName = undefined
...@@ -117,7 +118,7 @@ module.exports = (function() { ...@@ -117,7 +118,7 @@ module.exports = (function() {
schema = 'public'; schema = 'public';
} }
var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", c.data_type as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %> AND table_schema = <%= schema %>' var query = 'SELECT c.column_name as "Field", c.column_default as "Default", c.is_nullable as "Null", CASE WHEN c.udt_name = \'hstore\' THEN c.udt_name ELSE c.data_type END as "Type", (SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special" FROM information_schema.columns c WHERE table_name = <%= table %> AND table_schema = <%= schema %>'
return Utils._.template(query)({ return Utils._.template(query)({
table: this.escape(tableName), table: this.escape(tableName),
......
...@@ -12,6 +12,7 @@ var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions ...@@ -12,6 +12,7 @@ var hashToWhereConditions = MySqlQueryGenerator.hashToWhereConditions
module.exports = (function() { module.exports = (function() {
var QueryGenerator = { var QueryGenerator = {
options: {}, options: {},
dialect: 'sqlite',
addSchema: function(opts) { addSchema: function(opts) {
var tableName = undefined var tableName = undefined
......
...@@ -215,7 +215,12 @@ module.exports = (function() { ...@@ -215,7 +215,12 @@ module.exports = (function() {
Sequelize.prototype.query = function(sql, callee, options, replacements) { Sequelize.prototype.query = function(sql, callee, options, replacements) {
if (arguments.length === 4) { if (arguments.length === 4) {
sql = Utils.format([sql].concat(replacements), this.options.dialect) if (Array.isArray(replacements)) {
sql = Utils.format([sql].concat(replacements), this.options.dialect)
}
else {
sql = Utils.formatNamedParameters(sql, replacements, this.options.dialect)
}
} else if (arguments.length === 3) { } else if (arguments.length === 3) {
options = options options = options
} else if (arguments.length === 2) { } else if (arguments.length === 2) {
......
...@@ -106,6 +106,17 @@ SqlString.format = function(sql, values, timeZone, dialect) { ...@@ -106,6 +106,17 @@ SqlString.format = function(sql, values, timeZone, dialect) {
}); });
}; };
SqlString.formatNamedParameters = function(sql, values, timeZone, dialect) {
return sql.replace(/\:(\w+)/g, function (value, key) {
if (values.hasOwnProperty(key)) {
return SqlString.escape(values[key], false, timeZone, dialect);
}
else {
throw new Error('Named parameter "' + value + '" has no value in the given object.');
}
});
};
SqlString.dateToString = function(date, timeZone, dialect) { SqlString.dateToString = function(date, timeZone, dialect) {
var dt = new Date(date); var dt = new Date(date);
......
...@@ -29,6 +29,22 @@ var Utils = module.exports = { ...@@ -29,6 +29,22 @@ var Utils = module.exports = {
} }
return result return result
},
/*
* Returns an array with some falsy values removed. The values null, "", undefined and NaN are considered falsey.
*/
compactLite: function(array) {
var index = -1,
length = array ? array.length : 0,
result = [];
while (++index < length) {
var value = array[index];
if (typeof value === "boolean" || value === 0 || value) {
result.push(value);
}
}
return result;
} }
}) })
...@@ -41,6 +57,10 @@ var Utils = module.exports = { ...@@ -41,6 +57,10 @@ var Utils = module.exports = {
var timeZone = null; var timeZone = null;
return SqlString.format(arr.shift(), arr, timeZone, dialect) return SqlString.format(arr.shift(), arr, timeZone, dialect)
}, },
formatNamedParameters: function(sql, parameters, dialect) {
var timeZone = null;
return SqlString.formatNamedParameters(sql, parameters, timeZone, dialect)
},
// smartWhere can accept an array of {where} objects, or a single {where} object. // smartWhere can accept an array of {where} objects, or a single {where} object.
// The smartWhere function breaks down the collection of where objects into a more // The smartWhere function breaks down the collection of where objects into a more
// centralized object for each column so we can avoid duplicates // centralized object for each column so we can avoid duplicates
...@@ -197,7 +217,7 @@ var Utils = module.exports = { ...@@ -197,7 +217,7 @@ var Utils = module.exports = {
} }
} }
return lodash.compact([text.join(' AND ')].concat(whereArgs)) return Utils._.compactLite([text.join(' AND ')].concat(whereArgs))
}, },
getWhereLogic: function(logic) { getWhereLogic: function(logic) {
switch (logic) { switch (logic) {
......
...@@ -19,7 +19,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -19,7 +19,7 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
Task.create({ title: 'task', status: 'inactive' }).success(function(task) { Task.create({ title: 'task', status: 'inactive' }).success(function(task) {
user.setTaskXYZ(task).success(function() { user.setTaskXYZ(task).success(function() {
user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) { user.getTaskXYZ({where: ['status = ?', 'active']}).success(function(task) {
expect(task).to.equal(null) expect(task).to.be.null
done() done()
}) })
}) })
......
...@@ -21,7 +21,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -21,7 +21,8 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
secretValue: DataTypes.STRING, secretValue: DataTypes.STRING,
data: DataTypes.STRING, data: DataTypes.STRING,
intVal: DataTypes.INTEGER, intVal: DataTypes.INTEGER,
theDate: DataTypes.DATE theDate: DataTypes.DATE,
aBool: DataTypes.BOOLEAN
}) })
this.User.sync({ force: true }).success(function() { this.User.sync({ force: true }).success(function() {
done() done()
...@@ -999,6 +1000,72 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -999,6 +1000,72 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
}) })
}) })
it('should be able to handle false/true values just fine...', function(done) {
var User = this.User
, escapeChar = (dialect === "postgres" || dialect === "postgres-native") ? '"' : '`'
User.bulkCreate([
{username: 'boo5', aBool: false},
{username: 'boo6', aBool: true}
]).success(function() {
User.all({where: [escapeChar + 'aBool' + escapeChar + ' = ?', false]}).success(function(users) {
expect(users).to.have.length(1)
expect(users[0].username).to.equal('boo5')
User.all({where: [escapeChar + 'aBool' + escapeChar + ' = ?', true]}).success(function(_users) {
expect(_users).to.have.length(1)
expect(_users[0].username).to.equal('boo6')
done()
})
})
})
})
it('should be able to handle false/true values through associations as well...', function(done) {
var User = this.User
, escapeChar = (dialect === "postgres" || dialect === "postgres-native") ? '"' : '`'
var Passports = this.sequelize.define('Passports', {
isActive: Sequelize.BOOLEAN
})
User.hasMany(Passports)
Passports.belongsTo(User)
User.sync({ force: true }).success(function() {
Passports.sync({ force: true }).success(function() {
User.bulkCreate([
{username: 'boo5', aBool: false},
{username: 'boo6', aBool: true}
]).success(function() {
Passports.bulkCreate([
{isActive: true},
{isActive: false}
]).success(function() {
User.find(1).success(function(user) {
Passports.find(1).success(function(passport) {
user.setPassports([passport]).success(function() {
User.find(2).success(function(_user) {
Passports.find(2).success(function(_passport) {
_user.setPassports([_passport]).success(function() {
_user.getPassports({where: [escapeChar + 'isActive' + escapeChar + ' = ?', false]}).success(function(theFalsePassport) {
user.getPassports({where: [escapeChar + 'isActive' + escapeChar + ' = ?', true]}).success(function(theTruePassport) {
expect(theFalsePassport).to.have.length(1)
expect(theFalsePassport[0].isActive).to.be.false
expect(theTruePassport).to.have.length(1)
expect(theTruePassport[0].isActive).to.be.true
done()
})
})
})
})
})
})
})
})
})
})
})
})
})
it('should be able to retun a record with primaryKey being null for new inserts', function(done) { it('should be able to retun a record with primaryKey being null for new inserts', function(done) {
var Session = this.sequelize.define('Session', { var Session = this.sequelize.define('Session', {
token: { type: DataTypes.TEXT, allowNull: false }, token: { type: DataTypes.TEXT, allowNull: false },
...@@ -2231,6 +2298,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -2231,6 +2298,16 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
done() done()
}) })
}) })
it("handles attributes", function(done) {
this.User.findAndCountAll({where: "id != " + this.users[0].id, attributes: ['data']}).success(function(info) {
expect(info.count).to.equal(2)
expect(Array.isArray(info.rows)).to.be.ok
expect(info.rows.length).to.equal(2)
expect(info.rows[0].selectedValues).to.not.have.property('username')
expect(info.rows[1].selectedValues).to.not.have.property('username')
done()
})
})
}) })
describe('all', function() { describe('all', function() {
......
...@@ -2,7 +2,6 @@ var chai = require('chai') ...@@ -2,7 +2,6 @@ var chai = require('chai')
, expect = chai.expect , expect = chai.expect
, Support = require(__dirname + '/../support') , Support = require(__dirname + '/../support')
, dialect = Support.getTestDialect() , dialect = Support.getTestDialect()
, config = require(__dirname + '/../config/config')
, DataTypes = require(__dirname + "/../../lib/data-types") , DataTypes = require(__dirname + "/../../lib/data-types")
chai.Assertion.includeStack = true chai.Assertion.includeStack = true
...@@ -26,6 +25,13 @@ if (dialect.match(/^postgres/)) { ...@@ -26,6 +25,13 @@ if (dialect.match(/^postgres/)) {
done() done()
}) })
it('describeTable should tell me that a column is hstore and not USER-DEFINED', function(done) {
this.sequelize.queryInterface.describeTable('Users').success(function(table) {
expect(table.document.type).to.equal('HSTORE')
done()
})
})
describe('integers', function() { describe('integers', function() {
describe('integer', function() { describe('integer', function() {
beforeEach(function(done) { beforeEach(function(done) {
......
...@@ -177,6 +177,67 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () { ...@@ -177,6 +177,67 @@ describe(Support.getTestDialectTeaser("Sequelize"), function () {
}) })
}) })
it('replaces named parameters with the passed object', function(done) {
this.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, { one: 1, two: 2 }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2 }])
done()
})
})
it('replaces named parameters with the passed object using the same key twice', function(done) {
this.sequelize.query('select :one as foo, :two as bar, :one as baz', null, { raw: true }, { one: 1, two: 2 }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: 2, baz: 1 }])
done()
})
})
it('replaces named parameters with the passed object having a null property', function(done) {
this.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, { one: 1, two: null }).success(function(result) {
expect(result).to.deep.equal([{ foo: 1, bar: null }])
done()
})
})
it('throw an exception when key is missing in the passed object', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar, :three as baz', null, { raw: true }, { one: 1, two: 2 })
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed number', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, 2)
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed empty object', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, {})
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed string', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, 'foobar')
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('throw an exception with the passed date', function(done) {
var self = this
expect(function() {
self.sequelize.query('select :one as foo, :two as bar', null, { raw: true }, new Date())
}).to.throw(Error, /Named parameter ":\w+" has no value in the given object\./g)
done()
})
it('handles AS in conjunction with functions just fine', function(done) { it('handles AS in conjunction with functions just fine', function(done) {
this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) { this.sequelize.query('SELECT ' + (dialect === "sqlite" ? 'date(\'now\')' : 'NOW()') + ' AS t').success(function(result) {
expect(moment(result[0].t).isValid()).to.be.true expect(moment(result[0].t).isValid()).to.be.true
......
...@@ -116,6 +116,18 @@ var Support = { ...@@ -116,6 +116,18 @@ var Support = {
var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() }) var sequelize = Support.createSequelizeInstance({ dialect: Support.getTestDialect() })
// For Postgres' HSTORE functionality and to properly execute it's commands we'll need this...
before(function(done) {
var dialect = Support.getTestDialect()
if (dialect !== "postgres" && dialect !== "postgres-native") {
return done()
}
sequelize.query('CREATE EXTENSION IF NOT EXISTS hstore', null, {raw: true}).success(function() {
done()
})
})
beforeEach(function(done) { beforeEach(function(done) {
this.sequelize = sequelize this.sequelize = sequelize
Support.clearDatabase(this.sequelize, function() { Support.clearDatabase(this.sequelize, function() {
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!