Skip to content

Commit 1b8f399

Browse files
Mark Dierolfmdierolf
authored andcommitted
Added support for a fast text protocol row parser and integrated it with query logic.
1 parent 36c6de2 commit 1b8f399

2 files changed

Lines changed: 125 additions & 1 deletion

File tree

lib/commands/query.js

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

1415
const EmptyPacket = new Packets.Packet(0, Buffer.allocUnsafe(4), 0, 4);
@@ -213,7 +214,13 @@ class Query extends Command {
213214
if (this._receivedFieldsCount === this._fieldCount) {
214215
const fields = this._fields[this._resultIndex];
215216
this.emit('fields', fields);
216-
if (this.options.disableEval) {
217+
if (this.options.useFastTextParser) {
218+
this._rowParser = new (getFastTextParser(
219+
fields,
220+
this.options,
221+
connection.config
222+
))(fields);
223+
} else if (this.options.disableEval) {
217224
this._rowParser = staticParser(fields, this.options, connection.config);
218225
} else {
219226
this._rowParser = new (getTextParser(

lib/parsers/fast_text_parser.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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 readCodeFor(type, charset, encodingExpr, 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+
return 'packet.readLengthCodedNumber()';
38+
case Types.DECIMAL:
39+
case Types.NEWDECIMAL:
40+
if (config.decimalNumbers) {
41+
return 'packet.parseLengthCodedFloat()';
42+
}
43+
return 'packet.readLengthCodedString("ascii")';
44+
case Types.DATE:
45+
if (helpers.typeMatch(type, dateStrings, Types)) {
46+
return 'packet.readLengthCodedString("ascii")';
47+
}
48+
return `packet.parseDate('${timezone}')`;
49+
case Types.DATETIME:
50+
case Types.TIMESTAMP:
51+
if (helpers.typeMatch(type, dateStrings, Types)) {
52+
return 'packet.readLengthCodedString("ascii")';
53+
}
54+
return `packet.parseDateTime('${timezone}')`;
55+
case Types.TIME:
56+
return 'packet.readLengthCodedString("ascii")';
57+
case Types.GEOMETRY:
58+
return 'packet.parseGeometryValue()';
59+
case Types.JSON:
60+
// Since for JSON columns mysql always returns charset 63 (BINARY),
61+
// we have to handle it according to JSON specs and use "utf8",
62+
// see https://github.com/sidorares/node-mysql2/issues/409
63+
return 'JSON.parse(packet.readLengthCodedString("utf8"))';
64+
default:
65+
if (charset === Charsets.BINARY) {
66+
return 'packet.readLengthCodedBuffer()';
67+
}
68+
return `packet.readLengthCodedString(${encodingExpr})`;
69+
}
70+
}
71+
72+
function compile(fields, options, config) {
73+
const parserFn = genFunc();
74+
75+
/* eslint-disable no-trailing-spaces */
76+
/* eslint-disable no-spaced-func */
77+
/* eslint-disable no-unexpected-multiline */
78+
parserFn('(function () {')('return class TextRow {');
79+
80+
// next method
81+
parserFn('next(packet, fields, options) {');
82+
parserFn('return [');
83+
84+
for (let i = 0; i < fields.length; i++) {
85+
const encodingExpr = `fields[${i}].encoding`;
86+
const readCode = readCodeFor(
87+
fields[i].columnType,
88+
fields[i].characterSet,
89+
encodingExpr,
90+
config,
91+
options
92+
);
93+
parserFn(`${readCode},`);
94+
}
95+
96+
parserFn('];');
97+
parserFn('}');
98+
parserFn('};')('})()');
99+
100+
/* eslint-enable no-trailing-spaces */
101+
/* eslint-enable no-spaced-func */
102+
/* eslint-enable no-unexpected-multiline */
103+
104+
if (config.debug) {
105+
helpers.printDebugWithCode(
106+
'Compiled text protocol row parser',
107+
parserFn.toString()
108+
);
109+
}
110+
return parserFn.toFunction();
111+
}
112+
113+
function getTextParser(fields, options, config) {
114+
return parserCache.getParser('fast', fields, options, config, compile);
115+
}
116+
117+
module.exports = getTextParser;

0 commit comments

Comments
 (0)