From 66dcf9b70d0d1fc45272e2caf83cc78fbc40de06 Mon Sep 17 00:00:00 2001 From: biniam Date: Mon, 5 Jun 2017 16:32:12 -0400 Subject: [PATCH] Handle null vals properly When converting null values to database column values, return null if column is nullable, otherwise try to cast it to the property type, and if not, return 'null'. --- lib/mysql.js | 22 +++++++-- test/mysql.test.js | 109 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 121 insertions(+), 10 deletions(-) diff --git a/lib/mysql.js b/lib/mysql.js index 89a84e9..d6481b6 100644 --- a/lib/mysql.js +++ b/lib/mysql.js @@ -331,12 +331,26 @@ MySQL.prototype.getInsertedId = function(model, info) { * @returns {*} The escaped value of DB column */ MySQL.prototype.toColumnValue = function(prop, val) { - if (val == null) { - if (prop.autoIncrement || prop.id) { - return new ParameterizedSQL('DEFAULT'); - } + if (val === undefined && this.isNullable(prop)) { return null; } + if (val === null) { + if (this.isNullable(prop)) { + return val; + } else { + try { + var castNull = prop.type(val); + if (prop.type === Object) { + return JSON.stringify(castNull); + } + return castNull; + } catch (err) { + //if we can't coerce null to a certain type, + //we just return it + return 'null'; + } + } + } if (!prop) { return val; } diff --git a/test/mysql.test.js b/test/mysql.test.js index f2cc402..2ba6f70 100644 --- a/test/mysql.test.js +++ b/test/mysql.test.js @@ -6,7 +6,7 @@ 'use strict'; var should = require('./init.js'); -var Post, PostWithStringId, PostWithUniqueTitle, db; +var Post, PostWithStringId, PostWithUniqueTitle, PostWithNumId, db; // Mock up mongodb ObjectID function ObjectID(id) { @@ -41,13 +41,22 @@ describe('mysql', function() { title: {type: String, length: 255, index: true}, content: {type: String}, }); - + PostWithNumId = db.define('PostWithNumId', { + id: {type: Number, id: true, null: false}, + title: {type: String, length: 255, null: false, index: true}, + content: {type: String, null: false}, + buffProp: {type: Buffer, null: false}, + objProp: {type: Object, null: false}, + arrProp: {type: [Number], null: false}, + dateProp: {type: Date, null: false}, + pointProp: {type: 'GeoPoint', null: false}, + }); PostWithUniqueTitle = db.define('PostWithUniqueTitle', { title: {type: String, length: 255, index: {unique: true}}, content: {type: String}, }); - db.automigrate(['PostWithDefaultId', 'PostWithStringId', 'PostWithUniqueTitle'], function(err) { + db.automigrate(['PostWithDefaultId', 'PostWithStringId', 'PostWithUniqueTitle', 'PostWithNumId'], function(err) { should.not.exist(err); done(err); }); @@ -56,9 +65,7 @@ describe('mysql', function() { beforeEach(function(done) { Post.destroyAll(function() { PostWithStringId.destroyAll(function() { - PostWithUniqueTitle.destroyAll(function() { - done(); - }); + PostWithUniqueTitle.destroyAll(done); }); }); }); @@ -424,6 +431,96 @@ describe('mysql', function() { }); }); }); + context('null vals in different operators', function() { + var defaultPost = { + id: 3, + title: 'defTitle', + content: 'defContent', + buffProp: new Buffer('defBuffer'), + objProp: {defKey: 'defVal'}, + arrProp: [0], + dateProp: new Date('2017-06-14'), + pointProp: {lng: 4.51515, lat: 57.2314}, + }; + beforeEach(function(done) { + PostWithNumId.destroyAll(done); + }); + after(function(done) { + PostWithNumId.destroyAll(done); + }); + it('should handle null in inq operator', function(done) { + defaultPost.id = 1; + defaultPost.title = 'Foo'; + defaultPost.content = 'Bar'; + PostWithNumId.create(defaultPost, function(err, post) { + should.not.exist(err); + post.id.should.equal(defaultPost.id); + PostWithNumId.find({where: {id: {inq: [null, 1]}}}, function(err, posts) { + should.not.exist(err); + posts.length.should.equal(1); + posts[0].title.should.equal('Foo'); + posts[0].id.should.equal(1); + done(); + }); + }); + }); + + it('should handle null in nin operator', function(done) { + defaultPost.id = 2; + defaultPost.title = 'Make'; + defaultPost.content = 'Toyota'; + PostWithNumId.create(defaultPost, function(err, post) { + should.not.exist(err); + post.id.should.equal(defaultPost.id); + PostWithNumId.find({where: {id: {nin: [null, 3]}}}, function(err, posts) { + should.not.exist(err); + posts.length.should.equal(1); + posts[0].content.should.equal('Toyota'); + posts[0].id.should.equal(2); + done(); + }); + }); + }); + + it('should handle null in neq operator', function(done) { + defaultPost.id = 3; + defaultPost.title = 'Model'; + defaultPost.content = 'Corolla'; + PostWithNumId.create(defaultPost, function(err, post) { + should.not.exist(err); + post.id.should.equal(defaultPost.id); + PostWithNumId.find({where: {id: {neq: null}}}, function(err, posts) { + should.not.exist(err); + posts.length.should.equal(1); + posts[0].content.should.equal('Corolla'); + posts[0].id.should.equal(3); + done(); + }); + }); + }); + + it('should handle null in nin op for different datatypes', function(done) { + PostWithNumId.create(defaultPost, function(err, post) { + should.not.exist(err); + post.id.should.equal(defaultPost.id); + PostWithNumId.find({where: {and: [ + {id: {nin: [null]}}, + {title: {nin: [null]}}, + {content: {nin: [null]}}, + {buffProp: {nin: [null]}}, + {objProp: {nin: [null]}}, + {arrProp: {nin: [null]}}, + {dateProp: {nin: [null]}}, + {pointProp: {nin: [null]}}, + ]}}, function(err, posts) { + should.not.exist(err); + posts.length.should.equal(1); + posts[0].toObject().should.deepEqual(defaultPost); + done(); + }); + }); + }); + }); // The where object should be parsed by the connector it('should support where for count', function(done) {