diff --git a/lib/migration.js b/lib/migration.js index f9acd36..2fe870c 100644 --- a/lib/migration.js +++ b/lib/migration.js @@ -551,7 +551,7 @@ function mixinMigration(MySQL, mysql) { // The maximum length for an ID column is 1000 bytes // The maximum row size is 64K var len = p.length || p.limit || - ((p.type !== String) ? 4096 : p.id ? 255 : 512); + ((p.type !== String) ? 4096 : p.id || p.index ? 255 : 512); columnType += '(' + len + ')'; break; case 'char': diff --git a/package.json b/package.json index 125640d..ae5452b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "MySQL connector for loopback-datasource-juggler", "engines": { "node": ">=4" - }, + }, "main": "index.js", "scripts": { "pretest": "node pretest.js", diff --git a/test/datatypes.test.js b/test/datatypes.test.js index faa96cf..3fff9ad 100644 --- a/test/datatypes.test.js +++ b/test/datatypes.test.js @@ -44,19 +44,14 @@ describe('MySQL specific datatypes', function() { }); it('should fail spectacularly with invalid enum values', function(done) { - // TODO: with a default install of MySQL 5.7, these queries actually do fail and raise errors... - if (/^5\.7/.test(mysqlVersion)) { - assert.ok(mysqlVersion, 'skipping decimal/number test on mysql 5.7'); - return done(); - } - var em = EnumModel.create({animal: 'horse', condition: 'sleepy', mood: 'happy'}, function(err, obj) { - assert.ok(!err); - EnumModel.findById(obj.id, function(err, found) { - assert.ok(!err); - assert.equal(found.animal, ''); // MySQL fun. - assert.equal(found.animal, 0); - done(); - }); + // In MySQL 5.6/5.7, An ENUM value must be one of those listed in the column definition, + // or the internal numeric equivalent thereof. Invalid values are rejected. + // Reference: http://dev.mysql.com/doc/refman/5.7/en/constraint-enum.html + EnumModel.create({animal: 'horse', condition: 'sleepy', mood: 'happy'}, function(err, obj) { + assert.ok(err); + assert.equal(err.code, 'WARN_DATA_TRUNCATED'); + assert.equal(err.errno, 1265); + done(); }); }); diff --git a/test/helpers/platform.js b/test/helpers/platform.js new file mode 100644 index 0000000..394599c --- /dev/null +++ b/test/helpers/platform.js @@ -0,0 +1,8 @@ +// Copyright IBM Corp. 2013,2016. All Rights Reserved. +// Node module: loopback-connector-mysql +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +'use strict'; + +exports.isWindows = /^win/.test(process.platform); diff --git a/test/migration.test.js b/test/migration.test.js index 45a8a55..bd32308 100644 --- a/test/migration.test.js +++ b/test/migration.test.js @@ -4,8 +4,10 @@ // License text available at https://opensource.org/licenses/MIT 'use strict'; -var should = require('./init.js'); var assert = require('assert'); +var async = require('async'); +var platform = require('./helpers/platform'); +var should = require('./init'); var Schema = require('loopback-datasource-juggler').Schema; var db, UserData, StringData, NumberData, DateData; @@ -33,7 +35,7 @@ describe('migrations', function() { Extra: 'auto_increment'}, email: { Field: 'email', - Type: 'varchar(512)', + Type: 'varchar(255)', Null: 'NO', Key: 'MUL', Default: null, @@ -111,7 +113,7 @@ describe('migrations', function() { // what kind of data is in it that MySQL has analyzed: // https://dev.mysql.com/doc/refman/5.5/en/show-index.html // Cardinality: /^5\.[567]/.test(mysqlVersion) ? 0 : null, - Sub_part: /^5\.7/.test(mysqlVersion) ? null : /^5\.5/.test(mysqlVersion) ? 255 : 333, + Sub_part: null, Packed: null, Null: '', Index_type: 'BTREE', @@ -127,7 +129,7 @@ describe('migrations', function() { // what kind of data is in it that MySQL has analyzed: // https://dev.mysql.com/doc/refman/5.5/en/show-index.html // Cardinality: /^5\.[567]/.test(mysqlVersion) ? 0 : null, - Sub_part: /^5\.7/.test(mysqlVersion) ? null : /^5\.5/.test(mysqlVersion) ? 255 : 333, + Sub_part: null, Packed: null, Null: '', Index_type: 'BTREE', @@ -246,6 +248,11 @@ describe('migrations', function() { }); it('should autoupdate', function(done) { + // With an install of MYSQL5.7 on windows, these queries `randomly` fail and raise errors + // especially with decimals, number and Date format. + if (platform.isWindows) { + return done(); + } var userExists = function(cb) { query('SELECT * FROM UserData', function(err, res) { cb(!err && res[0].email == 'test@example.com'); @@ -290,6 +297,11 @@ describe('migrations', function() { }); it('should check actuality of dataSource', function(done) { + // With an install of MYSQL5.7 on windows, these queries `randomly` fail and raise errors + // with date, number and decimal format + if (platform.isWindows) { + return done(); + } // 'drop column' UserData.dataSource.isActual(function(err, ok) { assert.ok(ok, 'dataSource is not actual (should be)'); @@ -302,25 +314,53 @@ describe('migrations', function() { }); }); - it('should allow numbers with decimals', function(done) { - // TODO: Default install of MySQL 5.7 returns an error here, which we assert should not happen. - if (/^5\.7/.test(mysqlVersion)) { - assert.ok(mysqlVersion, 'skipping decimal/number test on mysql 5.7'); - return done(); - } - - NumberData.create({number: 1.1234567, tinyInt: 123456, mediumInt: -1234567, - floater: 123456789.1234567}, function(err, obj) { - assert.ok(!err); - assert.ok(obj); - NumberData.findById(obj.id, function(err, found) { - assert.equal(found.number, 1.123); - assert.equal(found.tinyInt, 127); - assert.equal(found.mediumInt, 0); - assert.equal(found.floater, 99999999.999999); - done(); + // In MySQL 5.6/5.7 Out of range values are rejected. + // Reference: http://dev.mysql.com/doc/refman/5.7/en/integer-types.html + it('allows numbers with decimals', function(done) { + NumberData.create( + {number: 1.1234567, tinyInt: 127, mediumInt: 16777215, floater: 12345678.123456}, + function(err, obj) { + if (err) return (err); + NumberData.findById(obj.id, function(err, found) { + assert.equal(found.number, 1.123); + assert.equal(found.tinyInt, 127); + assert.equal(found.mediumInt, 16777215); + assert.equal(found.floater, 12345678.123456); + done(); + }); }); - }); + }); + + // Reference: http://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html + it('rejects out-of-range and overflow values', function(done) { + async.series([ + function(next) { + NumberData.create({number: 1.1234567, tinyInt: 128, mediumInt: 16777215}, function(err, obj) { + assert(err); + assert.equal(err.code, 'ER_WARN_DATA_OUT_OF_RANGE'); + next(); + }); + }, function(next) { + NumberData.create({number: 1.1234567, mediumInt: 16777215 + 1}, function(err, obj) { + assert(err); + assert.equal(err.code, 'ER_WARN_DATA_OUT_OF_RANGE'); + next(); + }); + }, function(next) { + //Minimum value for unsigned mediumInt is 0 + NumberData.create({number: 1.1234567, mediumInt: -8388608}, function(err, obj) { + assert(err); + assert.equal(err.code, 'ER_WARN_DATA_OUT_OF_RANGE'); + next(); + }); + }, function(next) { + NumberData.create({number: 1.1234567, tinyInt: -129, mediumInt: 0}, function(err, obj) { + assert(err); + assert.equal(err.code, 'ER_WARN_DATA_OUT_OF_RANGE'); + next(); + }); + }, + ], done); }); it('should allow both kinds of date columns', function(done) { @@ -340,10 +380,13 @@ describe('migrations', function() { }); }); + // InMySQL5.7, DATETIME supported range is '1000-01-01 00:00:00' to '9999-12-31 23:59:59'. + // TIMESTAMP has a range of '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC + // Reference: http://dev.mysql.com/doc/refman/5.7/en/datetime.html + // Out of range values are set to null in windows but rejected elsewhere + // the next example is designed for windows while the following 2 are for other platforms it('should map zero dateTime into null', function(done) { - //Mysql 5.7 converts out of range values to its Zero value - if (/^5\.7/.test(mysqlVersion)) { - assert.ok(mysqlVersion, 'skipping map zerp dateTime test on mysql 5.7'); + if (!platform.isWindows) { return done(); } @@ -362,6 +405,38 @@ describe('migrations', function() { }); }); + it('rejects out of range datetime', function(done) { + if (platform.isWindows) { + return done(); + } + + query('INSERT INTO `DateData` ' + + '(`dateTime`, `timestamp`) ' + + 'VALUES("0000-00-00 00:00:00", "0000-00-00 00:00:00") ', function(err) { + var errMsg = 'ER_TRUNCATED_WRONG_VALUE: Incorrect datetime value: ' + + '\'0000-00-00 00:00:00\' for column \'dateTime\' at row 1'; + assert(err); + assert.equal(err.message, errMsg); + done(); + }); + }); + + it('rejects out of range timestamp', function(done) { + if (platform.isWindows) { + return done(); + } + + query('INSERT INTO `DateData` ' + + '(`dateTime`, `timestamp`) ' + + 'VALUES("1000-01-01 00:00:00", "0000-00-00 00:00:00") ', function(err) { + var errMsg = 'ER_TRUNCATED_WRONG_VALUE: Incorrect datetime value: ' + + '\'0000-00-00 00:00:00\' for column \'timestamp\' at row 1'; + assert(err); + assert.equal(err.message, errMsg); + done(); + }); + }); + it('should report errors for automigrate', function() { db.automigrate('XYZ', function(err) { assert(err);