Skip to content

Commit

Permalink
Merge pull request #1338 from ruiquelhas/master
Browse files Browse the repository at this point in the history
Fix tests to work with the latest MySQL server versions
  • Loading branch information
sidorares authored Jul 4, 2021
2 parents 07a429d + 888e977 commit e76d196
Show file tree
Hide file tree
Showing 14 changed files with 236 additions and 30 deletions.
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# This file was modified by Oracle on June 14, 2021.
# The changes involve the introduction of a local cache for Docker images.
# Modifications copyright (c) 2021, Oracle and/or its affiliates.

sudo: required
dist: trusty

Expand All @@ -9,9 +13,19 @@ language: node_js
cache:
yarn: true
directories:
- docker_images
- node_modules
- $HOME/.yarn-cache

before_cache:
# save all docker images to a local cache in order to avoid the rate limit
# on Docker Hub
- docker save -o docker_images/images.tar $(docker images -a -q)

before_install:
# load docker images from the local cache
- docker load -i docker_images/images.tar || true

# Node.js version:
# we test only maintained LTS versions
# and lastest dev version
Expand Down
31 changes: 26 additions & 5 deletions lib/connection.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 1, 2021.
// The changes involve new logic to handle an additional ERR Packet sent by
// the MySQL server when the connection is closed unexpectedly.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const Net = require('net');
Expand Down Expand Up @@ -188,7 +193,7 @@ class Connection extends EventEmitter {
if (this.connectTimeout) {
Timers.clearTimeout(this.connectTimeout);
this.connectTimeout = null;
}
}
// prevent from emitting 'PROTOCOL_CONNECTION_LOST' after EPIPE or ECONNRESET
if (this._fatalError) {
return;
Expand Down Expand Up @@ -368,6 +373,14 @@ class Connection extends EventEmitter {
}

protocolError(message, code) {
// Starting with MySQL 8.0.24, if the client closes the connection
// unexpectedly, the server will send a last ERR Packet, which we can
// safely ignore.
// https://dev.mysql.com/worklog/task/?id=12999
if (this._closing) {
return;
}

const err = new Error(message);
err.fatal = true;
err.code = code || 'PROTOCOL_ERROR';
Expand Down Expand Up @@ -415,10 +428,18 @@ class Connection extends EventEmitter {
}
}
if (!this._command) {
this.protocolError(
'Unexpected packet while no commands in the queue',
'PROTOCOL_UNEXPECTED_PACKET'
);
const marker = packet.peekByte();
// If it's an Err Packet, we should use it.
if (marker === 0xff) {
const error = Packets.Error.fromPacket(packet);
this.protocolError(error.message, error.code);
} else {
// Otherwise, it means it's some other unexpected packet.
this.protocolError(
'Unexpected packet while no commands in the queue',
'PROTOCOL_UNEXPECTED_PACKET'
);
}
this.close();
return;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/constants/errors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 1, 2021.
// An entry was created for a new error reported by the MySQL server due to
// client inactivity.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

// copy from https://raw.githubusercontent.com/mysqljs/mysql/7770ee5bb13260c56a160b91fe480d9165dbeeba/lib/protocol/constants/errors.js
Expand Down Expand Up @@ -994,6 +999,7 @@ exports.ER_INNODB_FT_AUX_NOT_HEX_ID = 1879;
exports.ER_OLD_TEMPORALS_UPGRADED = 1880;
exports.ER_INNODB_FORCED_RECOVERY = 1881;
exports.ER_AES_INVALID_IV = 1882;
exports.ER_CLIENT_INTERACTION_TIMEOUT = 4031;

// Lookup-by-number table
exports[1] = 'EE_CANTCREATEFILE';
Expand Down Expand Up @@ -1982,3 +1988,4 @@ exports[1879] = 'ER_INNODB_FT_AUX_NOT_HEX_ID';
exports[1880] = 'ER_OLD_TEMPORALS_UPGRADED';
exports[1881] = 'ER_INNODB_FORCED_RECOVERY';
exports[1882] = 'ER_AES_INVALID_IV';
exports[4031] = 'ER_CLIENT_INTERACTION_TIMEOUT';
19 changes: 19 additions & 0 deletions lib/packets/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 1, 2021.
// A utility method was introduced to generate an Error instance from a
// binary server packet.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const process = require('process');
Expand Down Expand Up @@ -122,6 +127,20 @@ class Error {
packet._name = 'Error';
return packet;
}

static fromPacket(packet) {
packet.readInt8(); // marker
const code = packet.readInt16();
packet.readString(1, 'ascii'); // sql state marker
// The SQL state of the ERR_Packet which is always 5 bytes long.
// https://dev.mysql.com/doc/dev/mysql-server/8.0.11/page_protocol_basic_dt_strings.html#sect_protocol_basic_dt_string_fix
packet.readString(5, 'ascii'); // sql state (ignore for now)
const message = packet.readNullTerminatedString('utf8');
const error = new Error();
error.message = message;
error.code = code;
return error;
}
}

exports.Error = Error;
10 changes: 10 additions & 0 deletions lib/packets/packet.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 1, 2021.
// A comment describing some changes in the strict default SQL mode regarding
// non-standard dates was introduced.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const ErrorCodeToName = require('../constants/errors.js');
Expand Down Expand Up @@ -274,6 +279,11 @@ class Packet {
if (length > 10) {
ms = this.readInt32() / 1000;
}
// NO_ZERO_DATE mode and NO_ZERO_IN_DATE mode are part of the strict
// default SQL mode used by MySQL 8.0. This means that non-standard
// dates like '0000-00-00' become NULL. For older versions and other
// possible MySQL flavours we still need to account for the
// non-standard behaviour.
if (y + m + d + H + M + S + ms === 0) {
return INVALID_DATE;
}
Expand Down
15 changes: 12 additions & 3 deletions test/integration/connection/test-binary-multiple-results.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 2, 2021.
// The test has been updated to remove all expectations with regards to the
// "columnLength" metadata field.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const mysql = require('../../common.js').createConnection({
Expand Down Expand Up @@ -33,7 +38,6 @@ const fields1 = [
{
catalog: 'def',
characterSet: 63,
columnLength: 1,
columnType: 8,
decimals: 0,
flags: 129,
Expand All @@ -48,7 +52,6 @@ const nr_fields = [
{
catalog: 'def',
characterSet: 63,
columnLength: 11,
columnType: 3,
decimals: 0,
flags: 0,
Expand Down Expand Up @@ -140,7 +143,13 @@ function do_test(testIndex) {
return void 0;
}

return c.inspect();
const column = c.inspect();
// "columnLength" is non-deterministic and the display width for integer
// data types was deprecated on MySQL 8.0.17.
// https://dev.mysql.com/doc/refman/8.0/en/numeric-type-syntax.html
delete column.columnLength;

return column;
};

assert.deepEqual(expectation[0], _rows);
Expand Down
16 changes: 15 additions & 1 deletion test/integration/connection/test-disconnects.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
// This file was modified by Oracle on January 21, 2021.
// The connection with the mock server needs to happen in the same host where
// the tests are running in order to avoid connecting a potential MySQL server
// instance running in the host identified by the MYSQL_HOST environment
// variable.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const common = require('../../common');
Expand All @@ -10,7 +17,14 @@ const connections = [];

const server = common.createServer(
() => {
const connection = common.createConnection({ port: server._port });
const connection = common.createConnection({
// The mock server is running on the same host machine.
// We need to explicitly define the host to avoid connecting to a potential
// different host provided via MYSQL_HOST that identifies a real MySQL
// server instance.
host: 'localhost',
port: server._port
});
connection.query('SELECT 123', (err, _rows, _fields) => {
if (err) {
throw err;
Expand Down
20 changes: 8 additions & 12 deletions test/integration/connection/test-execute-nocolumndef.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 2, 2021.
// The test has been updated to remove all expectations with regards to the
// "columnLength" metadata field.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const common = require('../../common');
Expand Down Expand Up @@ -47,7 +52,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 63,
columnLength: 3,
columnType: 8,
flags: 161,
decimals: 0
Expand All @@ -60,7 +64,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 76,
columnType: 253,
flags: 1,
decimals: 31
Expand All @@ -73,7 +76,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 256,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -86,7 +88,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 25264128,
columnType: 250,
flags: 0,
decimals: 31
Expand All @@ -99,7 +100,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 40,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -112,7 +112,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 16384,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -125,7 +124,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 256,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -138,7 +136,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 16384,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -151,7 +148,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 4096,
columnType: 253,
flags: 0,
decimals: 31
Expand All @@ -164,7 +160,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 63,
columnLength: 10,
columnType: 8,
flags: 160,
decimals: 0
Expand All @@ -177,7 +172,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 63,
columnLength: 4,
columnType: 5,
flags: 128,
decimals: 2
Expand All @@ -190,7 +184,6 @@ const expectedFields = [
table: '',
orgTable: '',
characterSet: 224,
columnLength: 1020,
columnType: 253,
flags: 1,
decimals: 31
Expand All @@ -201,6 +194,9 @@ process.on('exit', () => {
assert.deepEqual(rows, expectedRows);
fields.forEach((f, index) => {
const fi = f.inspect();
// "columnLength" is non-deterministic
delete fi.columnLength;

assert.deepEqual(
Object.keys(fi).sort(),
Object.keys(expectedFields[index]).sort()
Expand Down
33 changes: 30 additions & 3 deletions test/integration/connection/test-invalid-date-result.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
// This file was modified by Oracle on June 1, 2021.
// The test has been updated to be able to pass with different default
// strict modes used by different MySQL server versions.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.

'use strict';

const common = require('../../common');
Expand All @@ -6,12 +11,34 @@ const assert = require('assert');

let rows = undefined;

connection.execute('SELECT TIMESTAMP(0000-00-00) t', [], (err, _rows) => {
// Disable NO_ZERO_DATE mode and NO_ZERO_IN_DATE mode to ensure the old
// behaviour.
const strictModes = ['NO_ZERO_DATE', 'NO_ZERO_IN_DATE'];

connection.query('SELECT variable_value as value FROM performance_schema.session_variables where variable_name = ?', ['sql_mode'], (err, _rows) => {
if (err) {
throw err;
}
rows = _rows;
connection.end();

const deprecatedSqlMode = _rows[0].value
.split(',')
.filter(mode => strictModes.indexOf(mode) === -1)
.join(',');

connection.query(`SET sql_mode=?`, [deprecatedSqlMode], err => {
if (err) {
throw err;
}

connection.execute('SELECT TIMESTAMP(0000-00-00) t', [], (err, _rows) => {
if (err) {
throw err;
}

rows = _rows;
connection.end();
});
});
});

function isInvalidTime(t) {
Expand Down
Loading

0 comments on commit e76d196

Please sign in to comment.