diff --git a/.travis.yml b/.travis.yml index e5ebaba1..94c216ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,5 @@ node_js: - 0.6 - 0.7 before_install: git submodule init && git submodule --quiet update +before_script: + - "mysql -e 'create database myapp_test;'" diff --git a/lib/adapters/mysql.js b/lib/adapters/mysql.js index 29db4c47..7cc81824 100644 --- a/lib/adapters/mysql.js +++ b/lib/adapters/mysql.js @@ -71,7 +71,7 @@ MySQL.prototype.toFields = function (model, data) { var props = this._models[model].properties; Object.keys(data).forEach(function (key) { if (props[key]) { - fields.push('`' + key.replace(/\./g, '`.`') + '` = ' + this.toDatabase(props[key], data[key])); + fields.push('`' + key.replace(/\./g, '`.`') + '` = ' + this.toDatabase(props[key], data[key])); } }.bind(this)); return fields.join(','); @@ -93,7 +93,7 @@ MySQL.prototype.toDatabase = function (prop, val) { val.getMinutes(), val.getSeconds() ].join('-'); - return this.client.escape(val); + return '"' + val + '"'; } if (prop.type.name == "Boolean") return val ? 1 : 0; return this.client.escape(val.toString()); @@ -139,39 +139,61 @@ MySQL.prototype.destroy = function destroy(model, id, callback) { }); }; -// TODO: hook up where, order, limit and offset conditions MySQL.prototype.all = function all(model, filter, callback) { - this.query('SELECT * FROM ' + model, function (err, data) { + + var sql = 'SELECT * FROM ' + model; + var self = this; + var props = this._models[model].properties; + + if (filter) { + + if (filter.where) { + sql += ' ' + buildWhere(filter.where); + } + + if (filter.order) { + sql += ' ' + buildOrderBy(filter.order); + } + + if (filter.limit) { + sql += ' ' + buildLimit(filter.limit, filter.offset || 0); + } + + } + + this.query(sql, function (err, data) { + console.log(sql, err, data); if (err) { return callback(err, []); } - callback(err, filter ? data.filter(applyFilter(filter)) : data); + callback(null, data); }.bind(this)); -}; -function applyFilter(filter) { - if (typeof filter.where === 'function') { - return filter; - } - var keys = Object.keys(filter.where); - return function (obj) { - var pass = true; - keys.forEach(function (key) { - if (!test(filter.where[key], obj[key])) { - pass = false; + return sql; + + function buildWhere(conds) { + var cs = []; + Object.keys(conds).forEach(function (key) { + var keyEscaped = '`' + key.replace(/\./g, '`.`') + '`' + if (conds[key] === null) { + cs.push(keyEscaped + ' IS NULL'); + } else { + cs.push(keyEscaped + ' = ' + self.toDatabase(props[key], conds[key])); } }); - return pass; + return 'WHERE ' + cs.join(' AND '); } - function test(example, value) { - if (typeof value === 'string' && example && example.constructor.name === 'RegExp') { - return value.match(example); - } - // not strict equality - return example == value; + function buildOrderBy(order) { + if (typeof order === 'string') order = [order]; + return 'ORDER BY ' + order.join(', '); } -} + + function buildLimit(limit, offset) { + return 'LIMIT ' + (offset ? (offset + ', ' + limit) : limit); + } + +}; MySQL.prototype.destroyAll = function destroyAll(model, callback) { this.query('DELETE FROM ' + model, function (err) { diff --git a/package.json b/package.json index 3a42ccdc..a32a9739 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "main": "index.js", "scripts": { - "test": "ONLY=memory ./support/nodeunit/bin/nodeunit test/*_test.* & ONLY=redis nodeunit test/common_test.js" + "test": "ONLY=memory ./support/nodeunit/bin/nodeunit test/*_test.* & ONLY=redis nodeunit test/common_test.js & only=mysql nodeunit test/common_test.js" }, "engines": [ "node >= 0.4.0" diff --git a/test/common_test.js b/test/common_test.js index 95b0db51..2df43dab 100644 --- a/test/common_test.js +++ b/test/common_test.js @@ -12,7 +12,7 @@ var schemas = { username: 'root' }, mysql: { - database: 'sequ-test', + database: 'myapp_test', username: 'root' }, postgres: { @@ -34,7 +34,7 @@ Object.keys(schemas).forEach(function (schemaName) { if (process.env.ONLY && process.env.ONLY !== schemaName) return; context(schemaName, function () { var schema = new Schema(schemaName, schemas[schemaName]); - // schema.log = console.log; + schema.log = console.log; testOrm(schema); if (specificTest[schemaName]) specificTest[schemaName](schema); }); @@ -327,6 +327,7 @@ function testOrm(schema) { }); // matching regexp + if (Post.schema.name === 'mysql') done(); else Post.all({where: {title: /hello/i}}, function (err, res) { var pass = true; res.forEach(function (r) { @@ -425,7 +426,13 @@ function testOrm(schema) { it('should handle ORDER clause', function (test) { var titles = [ 'Title A', 'Title Z', 'Title M', 'Title B', 'Title C' ]; - var dates = [ 5, 9, 0, 17, 9 ]; + var dates = Post.schema.name === 'redis' ? [ 5, 9, 0, 17, 9 ] : [ + new Date(1000 * 5 ), + new Date(1000 * 9), + new Date(1000 * 0), + new Date(1000 * 17), + new Date(1000 * 9) + ]; titles.forEach(function (t, i) { Post.create({title: t, date: dates[i]}, done); }); @@ -434,8 +441,8 @@ function testOrm(schema) { function done(err, obj) { if (++i === titles.length) { doStringTest(); - doFilterAndSortTest(); doNumberTest(); + doFilterAndSortTest(); } } @@ -444,6 +451,8 @@ function testOrm(schema) { function doStringTest() { tests += 1; Post.all({order: 'title'}, function (err, posts) { + if (err) console.log(err); + test.equal(posts.length, 5); titles.sort().forEach(function (t, i) { test.equal(posts[i].title, t); }); @@ -454,8 +463,10 @@ function testOrm(schema) { function doNumberTest() { tests += 1; Post.all({order: 'date'}, function (err, posts) { + if (err) console.log(err); + test.equal(posts.length, 5); dates.sort(numerically).forEach(function (d, i) { - test.equal(posts[i].date, d); + test.equal(posts[i].date.toString(), d.toString()); }); finished(); }); @@ -463,7 +474,8 @@ function testOrm(schema) { function doFilterAndSortTest() { tests += 1; - Post.all({where: {date: 9}, order: 'title', limit: 3}, function (err, posts) { + Post.all({where: {title: new Date(1000 * 9)}, order: 'title', limit: 3}, function (err, posts) { + if (err) console.log(err); test.equal(posts.length, 2, 'Exactly 2 posts returned by query'); [ 'Title C', 'Title Z' ].forEach(function (t, i) { if (posts[i]) {