Skip to content

Commit 2d7fe4a

Browse files
committed
Fix Bad logic to decode the column name #1413
Introduce character_set_results tracking to ensure that we can read the column name properly
1 parent 11dd45b commit 2d7fe4a

File tree

8 files changed

+86
-14
lines changed

8 files changed

+86
-14
lines changed

lib/commands/query.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Query extends Command {
5555

5656
const cmdPacket = new Packets.Query(
5757
this.sql,
58-
connection.config.charsetNumber
58+
connection.clientEncoding
5959
);
6060
connection.writePacket(cmdPacket.toPacket(1));
6161
return Query.prototype.resultsetHeader;
@@ -196,7 +196,7 @@ class Query extends Command {
196196
if (this._fields[this._resultIndex].length !== this._fieldCount) {
197197
const field = new Packets.ColumnDefinition(
198198
packet,
199-
connection.clientEncoding
199+
connection.resultEncoding
200200
);
201201
this._fields[this._resultIndex].push(field);
202202
if (connection.config.debug) {

lib/connection.js

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Connection extends EventEmitter {
7979
this._protocolError = null;
8080
this._outOfOrderPackets = [];
8181
this.clientEncoding = CharsetToEncoding[this.config.charsetNumber];
82+
this.resultEncoding = CharsetToEncoding[this.config.charsetNumber];
8283
this.stream.on('error', this._handleNetworkError.bind(this));
8384
// see https://gist.github.com/khoomeister/4985691#use-that-instead-of-bind
8485
this.packetParser = new PacketParser(p => {

lib/constants/encoding_charset.js

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module.exports = {
3636
macroman: 39,
3737
cp852: 40,
3838
utf8: 45,
39+
utf8mb3: 45,
3940
utf8mb4: 45,
4041
utf16: 54,
4142
utf16le: 56,

lib/packets/column_definition.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ const fields = ['catalog', 'schema', 'table', 'orgTable', 'name', 'orgName'];
1919
// see https://github.com/sidorares/node-mysql2/pull/137
2020
//
2121
class ColumnDefinition {
22-
constructor(packet, clientEncoding) {
22+
constructor(packet, resultEncoding) {
2323
this._buf = packet.buffer;
24-
this._clientEncoding = clientEncoding;
24+
this._resultEncoding = resultEncoding;
2525
this._catalogLength = packet.readLengthCodedNumber();
2626
this._catalogStart = packet.offset;
2727
packet.offset += this._catalogLength;
@@ -46,7 +46,7 @@ class ColumnDefinition {
4646
this.encoding = CharsetToEncoding[this.characterSet];
4747
this.name = StringParser.decode(
4848
this._buf,
49-
this.encoding === 'binary' ? this._clientEncoding : this.encoding,
49+
this._resultEncoding !== undefined ? this._resultEncoding : "utf8",
5050
_nameStart,
5151
_nameStart + _nameLength
5252
);
@@ -114,7 +114,7 @@ const addString = function(name) {
114114
const end = start + this[`_${name}Length`];
115115
const val = StringParser.decode(
116116
this._buf,
117-
this.encoding === 'binary' ? this._clientEncoding : this.encoding,
117+
this._resultEncoding !== undefined ? this._resultEncoding : "utf8",
118118
start,
119119
end
120120
);

lib/packets/query.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@
33
const Packet = require('../packets/packet.js');
44
const CommandCode = require('../constants/commands.js');
55
const StringParser = require('../parsers/string.js');
6-
const CharsetToEncoding = require('../constants/charset_encodings.js');
7-
86
class Query {
9-
constructor(sql, charsetNumber) {
7+
constructor(sql, encoding) {
108
this.query = sql;
11-
this.charsetNumber = charsetNumber;
12-
this.encoding = CharsetToEncoding[charsetNumber];
9+
this.encoding = encoding;
1310
}
1411

1512
toPacket() {

lib/packets/resultset_header.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const ClientConstants = require('../constants/client.js');
88
const ServerSatusFlags = require('../constants/server_status.js');
99

1010
const EncodingToCharset = require('../constants/encoding_charset.js');
11+
const CharsetToEncoding = require('../constants/charset_encodings.js');
1112
const sessionInfoTypes = require('../constants/session_track.js');
1213

1314
class ResultSetHeader {
@@ -62,8 +63,10 @@ class ResultSetHeader {
6263
const val = packet.readLengthCodedString(encoding);
6364
stateChanges.systemVariables[key] = val;
6465
if (key === 'character_set_client') {
65-
const charsetNumber = EncodingToCharset[val];
66-
connection.config.charsetNumber = charsetNumber;
66+
connection.clientEncoding = CharsetToEncoding[EncodingToCharset[val]];
67+
}
68+
if (key === 'character_set_results') {
69+
connection.resultEncoding = val === '' ? undefined : CharsetToEncoding[EncodingToCharset[val]];
6770
}
6871
} else if (type === sessionInfoTypes.SCHEMA) {
6972
key = packet.readLengthCodedString(encoding);

test/integration/connection/encoding/test-track-encodings.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const assert = require('assert');
66
const connection = common.createConnection({ charset: 'UTF8MB4_GENERAL_CI' });
77
const text = 'привет, мир';
88

9-
connection.query('SET character_set_client=koi8r', err => {
9+
connection.query('SET character_set_client=koi8r, character_set_results=koi8r', err => {
1010
assert.ifError(err);
1111
connection.query('SELECT ?', [text], (err, rows) => {
1212
assert.ifError(err);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict';
2+
3+
const common = require('../../common');
4+
const connection = common.createConnection().promise();
5+
const assert = require('assert');
6+
7+
// test data stores
8+
const testData = [
9+
{
10+
column_charset: 'tis620',
11+
column_name: '平仮名',
12+
data: 'กขค',
13+
result_charset: 'utf8',
14+
},
15+
{
16+
column_charset: 'tis620',
17+
column_name: '平仮名',
18+
data: 'กขค',
19+
result_charset: null,
20+
},
21+
{
22+
column_charset: 'tis620',
23+
column_name: '平仮名',
24+
data: 'กขค',
25+
result_charset: 'utf8',
26+
},
27+
{
28+
column_charset: 'tis620',
29+
column_name: '平仮名',
30+
data: 'กขค',
31+
result_charset: 'utf8mb3',
32+
},
33+
{
34+
column_charset: 'utf16',
35+
column_name: 'กขค',
36+
data: 'กขค',
37+
result_charset: 'tis620',
38+
},
39+
];
40+
41+
const resultData = [];
42+
43+
(async () => {
44+
for (let i = 0; i < testData.length; ++i) {
45+
const entry = testData[i];
46+
47+
await connection.query('DROP TABLE IF EXISTS `test-charset-encoding2`');
48+
await connection.query('SET NAMES "utf8mb4"');
49+
await connection.query(
50+
'CREATE TABLE IF NOT EXISTS `test-charset-encoding2` ' +
51+
`( \`${entry.column_name}\` VARCHAR(1000) CHARACTER SET "${entry.column_charset}")`
52+
);
53+
await connection.query('INSERT INTO `test-charset-encoding2` values(?)', [
54+
entry.data,
55+
]);
56+
await connection.query('SET character_set_results = ?', [
57+
entry.result_charset,
58+
]);
59+
const result = await connection.query(
60+
'SELECT * from `test-charset-encoding2`'
61+
);
62+
resultData.push(result[0][0]);
63+
}
64+
connection.end();
65+
for (let i = 0; i < testData.length; ++i) {
66+
const data = {};
67+
data[testData[i].column_name] = testData[i].data;
68+
assert.deepEqual(resultData[i], data);
69+
}
70+
})();

0 commit comments

Comments
 (0)