diff --git a/lib/commands/query.js b/lib/commands/query.js index d5c6cdd1ae..8f85684107 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -55,7 +55,7 @@ class Query extends Command { const cmdPacket = new Packets.Query( this.sql, - connection.config.charsetNumber + connection.clientEncoding ); connection.writePacket(cmdPacket.toPacket(1)); return Query.prototype.resultsetHeader; @@ -196,7 +196,7 @@ class Query extends Command { if (this._fields[this._resultIndex].length !== this._fieldCount) { const field = new Packets.ColumnDefinition( packet, - connection.clientEncoding + connection.resultEncoding ); this._fields[this._resultIndex].push(field); if (connection.config.debug) { diff --git a/lib/connection.js b/lib/connection.js index 47970e971c..ec69b4af3f 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -79,6 +79,7 @@ class Connection extends EventEmitter { this._protocolError = null; this._outOfOrderPackets = []; this.clientEncoding = CharsetToEncoding[this.config.charsetNumber]; + this.resultEncoding = CharsetToEncoding[this.config.charsetNumber]; this.stream.on('error', this._handleNetworkError.bind(this)); // see https://gist.github.com/khoomeister/4985691#use-that-instead-of-bind this.packetParser = new PacketParser(p => { diff --git a/lib/constants/encoding_charset.js b/lib/constants/encoding_charset.js index 3e076e85fd..0693404bf2 100644 --- a/lib/constants/encoding_charset.js +++ b/lib/constants/encoding_charset.js @@ -36,6 +36,7 @@ module.exports = { macroman: 39, cp852: 40, utf8: 45, + utf8mb3: 45, utf8mb4: 45, utf16: 54, utf16le: 56, diff --git a/lib/packets/column_definition.js b/lib/packets/column_definition.js index 29df8466b2..3b42feb1aa 100644 --- a/lib/packets/column_definition.js +++ b/lib/packets/column_definition.js @@ -19,9 +19,9 @@ const fields = ['catalog', 'schema', 'table', 'orgTable', 'name', 'orgName']; // see https://github.com/sidorares/node-mysql2/pull/137 // class ColumnDefinition { - constructor(packet, clientEncoding) { + constructor(packet, resultEncoding) { this._buf = packet.buffer; - this._clientEncoding = clientEncoding; + this._resultEncoding = resultEncoding; this._catalogLength = packet.readLengthCodedNumber(); this._catalogStart = packet.offset; packet.offset += this._catalogLength; @@ -46,7 +46,7 @@ class ColumnDefinition { this.encoding = CharsetToEncoding[this.characterSet]; this.name = StringParser.decode( this._buf, - this.encoding === 'binary' ? this._clientEncoding : this.encoding, + this._resultEncoding !== undefined ? this._resultEncoding : "utf8", _nameStart, _nameStart + _nameLength ); @@ -114,7 +114,7 @@ const addString = function(name) { const end = start + this[`_${name}Length`]; const val = StringParser.decode( this._buf, - this.encoding === 'binary' ? this._clientEncoding : this.encoding, + this._resultEncoding !== undefined ? this._resultEncoding : "utf8", start, end ); diff --git a/lib/packets/query.js b/lib/packets/query.js index b650b3524c..81e96d5005 100644 --- a/lib/packets/query.js +++ b/lib/packets/query.js @@ -3,13 +3,10 @@ const Packet = require('../packets/packet.js'); const CommandCode = require('../constants/commands.js'); const StringParser = require('../parsers/string.js'); -const CharsetToEncoding = require('../constants/charset_encodings.js'); - class Query { - constructor(sql, charsetNumber) { + constructor(sql, encoding) { this.query = sql; - this.charsetNumber = charsetNumber; - this.encoding = CharsetToEncoding[charsetNumber]; + this.encoding = encoding; } toPacket() { diff --git a/lib/packets/resultset_header.js b/lib/packets/resultset_header.js index e6a8cd492f..f968082f55 100644 --- a/lib/packets/resultset_header.js +++ b/lib/packets/resultset_header.js @@ -8,6 +8,7 @@ const ClientConstants = require('../constants/client.js'); const ServerSatusFlags = require('../constants/server_status.js'); const EncodingToCharset = require('../constants/encoding_charset.js'); +const CharsetToEncoding = require('../constants/charset_encodings.js'); const sessionInfoTypes = require('../constants/session_track.js'); class ResultSetHeader { @@ -62,8 +63,10 @@ class ResultSetHeader { const val = packet.readLengthCodedString(encoding); stateChanges.systemVariables[key] = val; if (key === 'character_set_client') { - const charsetNumber = EncodingToCharset[val]; - connection.config.charsetNumber = charsetNumber; + connection.clientEncoding = CharsetToEncoding[EncodingToCharset[val]]; + } + if (key === 'character_set_results') { + connection.resultEncoding = val === '' ? undefined : CharsetToEncoding[EncodingToCharset[val]]; } } else if (type === sessionInfoTypes.SCHEMA) { key = packet.readLengthCodedString(encoding); diff --git a/test/integration/connection/encoding/test-track-encodings.js b/test/integration/connection/encoding/test-track-encodings.js index 88135b3944..750ceb544b 100644 --- a/test/integration/connection/encoding/test-track-encodings.js +++ b/test/integration/connection/encoding/test-track-encodings.js @@ -6,7 +6,7 @@ const assert = require('assert'); const connection = common.createConnection({ charset: 'UTF8MB4_GENERAL_CI' }); const text = 'привет, мир'; -connection.query('SET character_set_client=koi8r', err => { +connection.query('SET character_set_client=koi8r, character_set_results=koi8r', err => { assert.ifError(err); connection.query('SELECT ?', [text], (err, rows) => { assert.ifError(err); diff --git a/test/integration/connection/test-column-encoding.js b/test/integration/connection/test-column-encoding.js new file mode 100644 index 0000000000..eb32e7b4d0 --- /dev/null +++ b/test/integration/connection/test-column-encoding.js @@ -0,0 +1,70 @@ +'use strict'; + +const common = require('../../common'); +const connection = common.createConnection().promise(); +const assert = require('assert'); + +// test data stores +const testData = [ + { + column_charset: 'tis620', + column_name: '平仮名', + data: 'กขค', + result_charset: 'utf8', + }, + { + column_charset: 'tis620', + column_name: '平仮名', + data: 'กขค', + result_charset: null, + }, + { + column_charset: 'tis620', + column_name: '平仮名', + data: 'กขค', + result_charset: 'utf8', + }, + { + column_charset: 'tis620', + column_name: '平仮名', + data: 'กขค', + result_charset: 'utf8mb3', + }, + { + column_charset: 'utf16', + column_name: 'กขค', + data: 'กขค', + result_charset: 'tis620', + }, +]; + +const resultData = []; + +(async () => { + for (let i = 0; i < testData.length; ++i) { + const entry = testData[i]; + + await connection.query('DROP TABLE IF EXISTS `test-charset-encoding2`'); + await connection.query('SET NAMES "utf8mb4"'); + await connection.query( + 'CREATE TABLE IF NOT EXISTS `test-charset-encoding2` ' + + `( \`${entry.column_name}\` VARCHAR(1000) CHARACTER SET "${entry.column_charset}")` + ); + await connection.query('INSERT INTO `test-charset-encoding2` values(?)', [ + entry.data, + ]); + await connection.query('SET character_set_results = ?', [ + entry.result_charset, + ]); + const result = await connection.query( + 'SELECT * from `test-charset-encoding2`' + ); + resultData.push(result[0][0]); + } + connection.end(); + for (let i = 0; i < testData.length; ++i) { + const data = {}; + data[testData[i].column_name] = testData[i].data; + assert.deepEqual(resultData[i], data); + } +})();