Skip to content

Commit 50e47fa

Browse files
sidoraresthomasgauvin
authored andcommitted
add static parser, currently disabled
1 parent f2cc820 commit 50e47fa

File tree

3 files changed

+162
-4
lines changed

3 files changed

+162
-4
lines changed

lib/commands/query.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const Readable = require('stream').Readable;
88
const Command = require('./command.js');
99
const Packets = require('../packets/index.js');
1010
const getTextParser = require('../parsers/text_parser.js');
11+
const staticParser = require('../parsers/static_text_parser.js');
1112
const ServerStatus = require('../constants/server_status.js');
1213

1314
const EmptyPacket = new Packets.Packet(0, Buffer.allocUnsafe(4), 0, 4);
@@ -212,7 +213,11 @@ class Query extends Command {
212213
if (this._receivedFieldsCount === this._fieldCount) {
213214
const fields = this._fields[this._resultIndex];
214215
this.emit('fields', fields);
215-
this._rowParser = new (getTextParser(fields, this.options, connection.config))(fields);
216+
if (this.options.useStaticParser) {
217+
this._rowParser = staticParser(fields, this.options, connection.config);
218+
} else {
219+
this._rowParser = new (getTextParser(fields, this.options, connection.config))(fields);
220+
}
216221
return Query.prototype.fieldsEOF;
217222
}
218223
return Query.prototype.readField;

lib/parsers/static_text_parser.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use strict';
2+
3+
const Types = require('../constants/types.js');
4+
const Charsets = require('../constants/charsets.js');
5+
const helpers = require('../helpers');
6+
const genFunc = require('generate-function');
7+
const parserCache = require('./parser_cache.js');
8+
9+
const typeNames = [];
10+
for (const t in Types) {
11+
typeNames[Types[t]] = t;
12+
}
13+
14+
function readField({ packet, type, charset, encoding, config, options }) {
15+
const supportBigNumbers =
16+
options.supportBigNumbers || config.supportBigNumbers;
17+
const bigNumberStrings = options.bigNumberStrings || config.bigNumberStrings;
18+
const timezone = options.timezone || config.timezone;
19+
const dateStrings = options.dateStrings || config.dateStrings;
20+
21+
switch (type) {
22+
case Types.TINY:
23+
case Types.SHORT:
24+
case Types.LONG:
25+
case Types.INT24:
26+
case Types.YEAR:
27+
return packet.parseLengthCodedIntNoBigCheck();
28+
case Types.LONGLONG:
29+
if (supportBigNumbers && bigNumberStrings) {
30+
return packet.parseLengthCodedIntString();
31+
}
32+
return packet.parseLengthCodedInt(supportBigNumbers);
33+
case Types.FLOAT:
34+
case Types.DOUBLE:
35+
return packet.parseLengthCodedFloat();
36+
case Types.NULL:
37+
// case Types.BIT:
38+
// return 'packet.readBuffer(2)[1]';
39+
case Types.DECIMAL:
40+
case Types.NEWDECIMAL:
41+
if (config.decimalNumbers) {
42+
return packet.parseLengthCodedFloat();
43+
}
44+
return packet.readLengthCodedString("ascii");
45+
case Types.DATE:
46+
if (helpers.typeMatch(type, dateStrings, Types)) {
47+
return packet.readLengthCodedString("ascii");
48+
}
49+
return packet.parseDate(timezone);
50+
case Types.DATETIME:
51+
case Types.TIMESTAMP:
52+
if (helpers.typeMatch(type, dateStrings, Types)) {
53+
return packet.readLengthCodedString('ascii');
54+
}
55+
return packet.parseDateTime(timezone);
56+
case Types.TIME:
57+
return packet.readLengthCodedString('ascii');
58+
case Types.GEOMETRY:
59+
return packet.parseGeometryValue();
60+
case Types.JSON:
61+
// Since for JSON columns mysql always returns charset 63 (BINARY),
62+
// we have to handle it according to JSON specs and use "utf8",
63+
// see https://github.com/sidorares/node-mysql2/issues/409
64+
return JSON.parse(packet.readLengthCodedString('utf8'));
65+
default:
66+
if (charset === Charsets.BINARY) {
67+
return packet.readLengthCodedBuffer();
68+
}
69+
return packet.readLengthCodedString(encoding);
70+
}
71+
}
72+
73+
function compile(fields, options, config) {
74+
if (
75+
typeof config.typeCast === 'function' &&
76+
typeof options.typeCast !== 'function'
77+
) {
78+
options.typeCast = config.typeCast;
79+
}
80+
}
81+
82+
function createTypecastField(field, packet) {
83+
return {
84+
type: typeNames[field.columnType],
85+
length: field.columnLength,
86+
db: field.schema,
87+
table: field.table,
88+
name: field.name,
89+
string: function(encoding = field.encoding) {
90+
if (field.columnType === Types.JSON && encoding === field.encoding) {
91+
// Since for JSON columns mysql always returns charset 63 (BINARY),
92+
// we have to handle it according to JSON specs and use "utf8",
93+
// see https://github.com/sidorares/node-mysql2/issues/1661
94+
console.warn(`typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``);
95+
}
96+
return packet.readLengthCodedString(encoding);
97+
},
98+
buffer: function() {
99+
return packet.readLengthCodedBuffer();
100+
},
101+
geometry: function() {
102+
return packet.parseGeometryValue();
103+
}
104+
};
105+
}
106+
107+
function getTextParser(fields, options, config) {
108+
return {
109+
next(packet, fields, options) {
110+
const result = options.rowsAsArray ? [] : {};
111+
for (let i = 0; i < fields.length; i++) {
112+
const field = fields[i];
113+
const typeCast = options.typeCast ? options.typeCast : config.typeCast;
114+
const next = () => {
115+
return readField({
116+
packet,
117+
type: field.columnType,
118+
encoding: field.encoding,
119+
charset: field.characterSet,
120+
config,
121+
options
122+
})
123+
}
124+
let value;
125+
if (typeof typeCast === 'function') {
126+
value = typeCast(createTypecastField(field, packet), next);
127+
} else {
128+
value = next();
129+
}
130+
if (options.rowsAsArray) {
131+
result.push(value);
132+
} else if (options.nestTables) {
133+
if (!result[field.table]) {
134+
result[field.table] = {};
135+
}
136+
result[field.table][field.name] = value;
137+
} else {
138+
result[field.name] = value;
139+
}
140+
}
141+
return result;
142+
}
143+
}
144+
}
145+
146+
module.exports = getTextParser;

test/integration/regressions/test-#433.test.cjs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ connection.query(
6464
);
6565

6666
/* eslint quotes: 0 */
67-
const expectedError =
67+
const expectedErrorMysql =
6868
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '`МояТаблица' at line 1";
6969

70+
const expectedErrorMariaDB =
71+
"You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '`МояТаблица' at line 1";
72+
7073
process.on('exit', () => {
7174
testRows.map((tRow, index) => {
7275
const cols = testFields;
@@ -76,6 +79,10 @@ process.on('exit', () => {
7679
assert.equal(aRow[cols[2]], tRow[2]);
7780
assert.equal(aRow[cols[3]], tRow[3]);
7881
});
79-
80-
assert.equal(actualError, expectedError);
82+
83+
if (connection._handshakePacket.serverVersion.match(/MariaDB/)) {
84+
assert.equal(actualError, expectedErrorMariaDB);
85+
} else {
86+
assert.equal(actualError, expectedErrorMysql);
87+
}
8188
});

0 commit comments

Comments
 (0)