mysql2 3.12.0 → 3.12.1-canary.51da6534
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/execute.js +9 -4
- package/lib/commands/query.js +21 -15
- package/lib/connection_config.js +19 -11
- package/lib/helpers.js +2 -3
- package/lib/parsers/static_binary_parser.js +211 -0
- package/lib/parsers/static_text_parser.js +150 -0
- package/package.json +7 -6
- package/typings/mysql/lib/Connection.d.ts +2 -0
package/lib/commands/execute.js
CHANGED
|
@@ -5,6 +5,7 @@ const Query = require('./query.js');
|
|
|
5
5
|
const Packets = require('../packets/index.js');
|
|
6
6
|
|
|
7
7
|
const getBinaryParser = require('../parsers/binary_parser.js');
|
|
8
|
+
const getStaticBinaryParser = require('../parsers/static_binary_parser.js');
|
|
8
9
|
|
|
9
10
|
class Execute extends Command {
|
|
10
11
|
constructor(options, callback) {
|
|
@@ -25,12 +26,16 @@ class Execute extends Command {
|
|
|
25
26
|
this._executeOptions = options;
|
|
26
27
|
this._resultIndex = 0;
|
|
27
28
|
this._localStream = null;
|
|
28
|
-
this._unpipeStream = function() {};
|
|
29
|
+
this._unpipeStream = function () {};
|
|
29
30
|
this._streamFactory = options.infileStreamFactory;
|
|
30
31
|
this._connection = null;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
buildParserFromFields(fields, connection) {
|
|
35
|
+
if (this.options.disableEval) {
|
|
36
|
+
return getStaticBinaryParser(fields, this.options, connection.config);
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
return getBinaryParser(fields, this.options, connection.config);
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -42,7 +47,7 @@ class Execute extends Command {
|
|
|
42
47
|
this.statement.id,
|
|
43
48
|
this.parameters,
|
|
44
49
|
connection.config.charsetNumber,
|
|
45
|
-
connection.config.timezone
|
|
50
|
+
connection.config.timezone,
|
|
46
51
|
);
|
|
47
52
|
//For reasons why this try-catch is here, please see
|
|
48
53
|
// https://github.com/sidorares/node-mysql2/pull/689
|
|
@@ -68,7 +73,7 @@ class Execute extends Command {
|
|
|
68
73
|
// this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
|
|
69
74
|
const field = new Packets.ColumnDefinition(
|
|
70
75
|
packet,
|
|
71
|
-
connection.clientEncoding
|
|
76
|
+
connection.clientEncoding,
|
|
72
77
|
);
|
|
73
78
|
this._receivedFieldsCount++;
|
|
74
79
|
this._fields[this._resultIndex].push(field);
|
|
@@ -87,7 +92,7 @@ class Execute extends Command {
|
|
|
87
92
|
}
|
|
88
93
|
this._rowParser = new (this.buildParserFromFields(
|
|
89
94
|
this._fields[this._resultIndex],
|
|
90
|
-
connection
|
|
95
|
+
connection,
|
|
91
96
|
))();
|
|
92
97
|
return Execute.prototype.row;
|
|
93
98
|
}
|
package/lib/commands/query.js
CHANGED
|
@@ -8,6 +8,7 @@ const Readable = require('stream').Readable;
|
|
|
8
8
|
const Command = require('./command.js');
|
|
9
9
|
const Packets = require('../packets/index.js');
|
|
10
10
|
const getTextParser = require('../parsers/text_parser.js');
|
|
11
|
+
const staticParser = require('../parsers/static_text_parser.js');
|
|
11
12
|
const ServerStatus = require('../constants/server_status.js');
|
|
12
13
|
|
|
13
14
|
const EmptyPacket = new Packets.Packet(0, Buffer.allocUnsafe(4), 0, 4);
|
|
@@ -30,7 +31,7 @@ class Query extends Command {
|
|
|
30
31
|
this._receivedFieldsCount = 0;
|
|
31
32
|
this._resultIndex = 0;
|
|
32
33
|
this._localStream = null;
|
|
33
|
-
this._unpipeStream = function () {
|
|
34
|
+
this._unpipeStream = function () {};
|
|
34
35
|
this._streamFactory = options.infileStreamFactory;
|
|
35
36
|
this._connection = null;
|
|
36
37
|
}
|
|
@@ -55,7 +56,7 @@ class Query extends Command {
|
|
|
55
56
|
|
|
56
57
|
const cmdPacket = new Packets.Query(
|
|
57
58
|
this.sql,
|
|
58
|
-
connection.config.charsetNumber
|
|
59
|
+
connection.config.charsetNumber,
|
|
59
60
|
);
|
|
60
61
|
connection.writePacket(cmdPacket.toPacket(1));
|
|
61
62
|
return Query.prototype.resultsetHeader;
|
|
@@ -120,7 +121,7 @@ class Query extends Command {
|
|
|
120
121
|
if (connection.config.debug) {
|
|
121
122
|
// eslint-disable-next-line
|
|
122
123
|
console.log(
|
|
123
|
-
` Resultset header received, expecting ${rs.fieldCount} column definition packets
|
|
124
|
+
` Resultset header received, expecting ${rs.fieldCount} column definition packets`,
|
|
124
125
|
);
|
|
125
126
|
}
|
|
126
127
|
if (this._fieldCount === 0) {
|
|
@@ -140,7 +141,7 @@ class Query extends Command {
|
|
|
140
141
|
this._localStream = this._streamFactory(path);
|
|
141
142
|
} else {
|
|
142
143
|
this._localStreamError = new Error(
|
|
143
|
-
`As a result of LOCAL INFILE command server wants to read ${path} file, but as of v2.0 you must provide streamFactory option returning ReadStream
|
|
144
|
+
`As a result of LOCAL INFILE command server wants to read ${path} file, but as of v2.0 you must provide streamFactory option returning ReadStream.`,
|
|
144
145
|
);
|
|
145
146
|
connection.writePacket(EmptyPacket);
|
|
146
147
|
return this.infileOk;
|
|
@@ -159,14 +160,14 @@ class Query extends Command {
|
|
|
159
160
|
const dataWithHeader = Buffer.allocUnsafe(data.length + 4);
|
|
160
161
|
data.copy(dataWithHeader, 4);
|
|
161
162
|
connection.writePacket(
|
|
162
|
-
new Packets.Packet(0, dataWithHeader, 0, dataWithHeader.length)
|
|
163
|
+
new Packets.Packet(0, dataWithHeader, 0, dataWithHeader.length),
|
|
163
164
|
);
|
|
164
165
|
};
|
|
165
166
|
const onEnd = () => {
|
|
166
167
|
connection.removeListener('error', onConnectionError);
|
|
167
168
|
connection.writePacket(EmptyPacket);
|
|
168
169
|
};
|
|
169
|
-
const onError = err => {
|
|
170
|
+
const onError = (err) => {
|
|
170
171
|
this._localStreamError = err;
|
|
171
172
|
connection.removeListener('error', onConnectionError);
|
|
172
173
|
connection.writePacket(EmptyPacket);
|
|
@@ -196,7 +197,7 @@ class Query extends Command {
|
|
|
196
197
|
if (this._fields[this._resultIndex].length !== this._fieldCount) {
|
|
197
198
|
const field = new Packets.ColumnDefinition(
|
|
198
199
|
packet,
|
|
199
|
-
connection.clientEncoding
|
|
200
|
+
connection.clientEncoding,
|
|
200
201
|
);
|
|
201
202
|
this._fields[this._resultIndex].push(field);
|
|
202
203
|
if (connection.config.debug) {
|
|
@@ -212,7 +213,15 @@ class Query extends Command {
|
|
|
212
213
|
if (this._receivedFieldsCount === this._fieldCount) {
|
|
213
214
|
const fields = this._fields[this._resultIndex];
|
|
214
215
|
this.emit('fields', fields);
|
|
215
|
-
|
|
216
|
+
if (this.options.disableEval) {
|
|
217
|
+
this._rowParser = staticParser(fields, this.options, connection.config);
|
|
218
|
+
} else {
|
|
219
|
+
this._rowParser = new (getTextParser(
|
|
220
|
+
fields,
|
|
221
|
+
this.options,
|
|
222
|
+
connection.config,
|
|
223
|
+
))(fields);
|
|
224
|
+
}
|
|
216
225
|
return Query.prototype.fieldsEOF;
|
|
217
226
|
}
|
|
218
227
|
return Query.prototype.readField;
|
|
@@ -242,7 +251,7 @@ class Query extends Command {
|
|
|
242
251
|
row = this._rowParser.next(
|
|
243
252
|
packet,
|
|
244
253
|
this._fields[this._resultIndex],
|
|
245
|
-
this.options
|
|
254
|
+
this.options,
|
|
246
255
|
);
|
|
247
256
|
} catch (err) {
|
|
248
257
|
this._localStreamError = err;
|
|
@@ -274,13 +283,13 @@ class Query extends Command {
|
|
|
274
283
|
}
|
|
275
284
|
stream.emit('result', row, resultSetIndex); // replicate old emitter
|
|
276
285
|
});
|
|
277
|
-
this.on('error', err => {
|
|
286
|
+
this.on('error', (err) => {
|
|
278
287
|
stream.emit('error', err); // Pass on any errors
|
|
279
288
|
});
|
|
280
289
|
this.on('end', () => {
|
|
281
290
|
stream.push(null); // pushing null, indicating EOF
|
|
282
291
|
});
|
|
283
|
-
this.on('fields', fields => {
|
|
292
|
+
this.on('fields', (fields) => {
|
|
284
293
|
stream.emit('fields', fields); // replicate old emitter
|
|
285
294
|
});
|
|
286
295
|
stream.on('end', () => {
|
|
@@ -292,10 +301,7 @@ class Query extends Command {
|
|
|
292
301
|
_setTimeout() {
|
|
293
302
|
if (this.timeout) {
|
|
294
303
|
const timeoutHandler = this._handleTimeoutError.bind(this);
|
|
295
|
-
this.queryTimeout = Timers.setTimeout(
|
|
296
|
-
timeoutHandler,
|
|
297
|
-
this.timeout
|
|
298
|
-
);
|
|
304
|
+
this.queryTimeout = Timers.setTimeout(timeoutHandler, this.timeout);
|
|
299
305
|
}
|
|
300
306
|
}
|
|
301
307
|
|
package/lib/connection_config.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const { URL } = require('url');
|
|
11
11
|
const ClientConstants = require('./constants/client');
|
|
12
12
|
const Charsets = require('./constants/charsets');
|
|
13
|
-
const { version } = require('../package.json')
|
|
13
|
+
const { version } = require('../package.json');
|
|
14
14
|
let SSLProfiles = null;
|
|
15
15
|
|
|
16
16
|
const validOptions = {
|
|
@@ -59,6 +59,7 @@ const validOptions = {
|
|
|
59
59
|
typeCast: 1,
|
|
60
60
|
uri: 1,
|
|
61
61
|
user: 1,
|
|
62
|
+
disableEval: 1,
|
|
62
63
|
// These options are used for Pool
|
|
63
64
|
connectionLimit: 1,
|
|
64
65
|
maxIdle: 1,
|
|
@@ -66,7 +67,7 @@ const validOptions = {
|
|
|
66
67
|
Promise: 1,
|
|
67
68
|
queueLimit: 1,
|
|
68
69
|
waitForConnections: 1,
|
|
69
|
-
jsonStrings: 1
|
|
70
|
+
jsonStrings: 1,
|
|
70
71
|
};
|
|
71
72
|
|
|
72
73
|
class ConnectionConfig {
|
|
@@ -87,14 +88,17 @@ class ConnectionConfig {
|
|
|
87
88
|
// REVIEW: Should this be emitted somehow?
|
|
88
89
|
// eslint-disable-next-line no-console
|
|
89
90
|
console.error(
|
|
90
|
-
`Ignoring invalid configuration option passed to Connection: ${key}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection
|
|
91
|
+
`Ignoring invalid configuration option passed to Connection: ${key}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`,
|
|
91
92
|
);
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
this.isServer = options.isServer;
|
|
95
96
|
this.stream = options.stream;
|
|
96
97
|
this.host = options.host || 'localhost';
|
|
97
|
-
this.port =
|
|
98
|
+
this.port =
|
|
99
|
+
(typeof options.port === 'string'
|
|
100
|
+
? parseInt(options.port, 10)
|
|
101
|
+
: options.port) || 3306;
|
|
98
102
|
this.localAddress = options.localAddress;
|
|
99
103
|
this.socketPath = options.socketPath;
|
|
100
104
|
this.user = options.user || undefined;
|
|
@@ -128,7 +132,7 @@ class ConnectionConfig {
|
|
|
128
132
|
// https://github.com/mysqljs/mysql#user-content-connection-options
|
|
129
133
|
// eslint-disable-next-line no-console
|
|
130
134
|
console.error(
|
|
131
|
-
`Ignoring invalid timezone passed to Connection: ${options.timezone}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection
|
|
135
|
+
`Ignoring invalid timezone passed to Connection: ${options.timezone}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`,
|
|
132
136
|
);
|
|
133
137
|
// SqlStrings falls back to UTC on invalid timezone
|
|
134
138
|
this.timezone = 'Z';
|
|
@@ -147,6 +151,7 @@ class ConnectionConfig {
|
|
|
147
151
|
this.nestTables =
|
|
148
152
|
options.nestTables === undefined ? undefined : options.nestTables;
|
|
149
153
|
this.typeCast = options.typeCast === undefined ? true : options.typeCast;
|
|
154
|
+
this.disableEval = Boolean(options.disableEval);
|
|
150
155
|
if (this.timezone[0] === ' ') {
|
|
151
156
|
// "+" is a url encoded char for space so it
|
|
152
157
|
// gets translated to space when giving a
|
|
@@ -156,7 +161,7 @@ class ConnectionConfig {
|
|
|
156
161
|
if (this.ssl) {
|
|
157
162
|
if (typeof this.ssl !== 'object') {
|
|
158
163
|
throw new TypeError(
|
|
159
|
-
`SSL profile must be an object, instead it's a ${typeof this.ssl}
|
|
164
|
+
`SSL profile must be an object, instead it's a ${typeof this.ssl}`,
|
|
160
165
|
);
|
|
161
166
|
}
|
|
162
167
|
// Default rejectUnauthorized to true
|
|
@@ -171,15 +176,18 @@ class ConnectionConfig {
|
|
|
171
176
|
this.authSwitchHandler = options.authSwitchHandler;
|
|
172
177
|
this.clientFlags = ConnectionConfig.mergeFlags(
|
|
173
178
|
ConnectionConfig.getDefaultFlags(options),
|
|
174
|
-
options.flags || ''
|
|
179
|
+
options.flags || '',
|
|
175
180
|
);
|
|
176
181
|
// Default connection attributes
|
|
177
182
|
// https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html
|
|
178
|
-
const defaultConnectAttributes =
|
|
183
|
+
const defaultConnectAttributes = {
|
|
179
184
|
_client_name: 'Node-MySQL-2',
|
|
180
|
-
_client_version: version
|
|
185
|
+
_client_version: version,
|
|
186
|
+
};
|
|
187
|
+
this.connectAttributes = {
|
|
188
|
+
...defaultConnectAttributes,
|
|
189
|
+
...(options.connectAttributes || {}),
|
|
181
190
|
};
|
|
182
|
-
this.connectAttributes = { ...defaultConnectAttributes, ...(options.connectAttributes || {})};
|
|
183
191
|
this.maxPreparedStatements = options.maxPreparedStatements || 16000;
|
|
184
192
|
this.jsonStrings = options.jsonStrings || false;
|
|
185
193
|
}
|
|
@@ -229,7 +237,7 @@ class ConnectionConfig {
|
|
|
229
237
|
'MULTI_RESULTS',
|
|
230
238
|
'TRANSACTIONS',
|
|
231
239
|
'SESSION_TRACK',
|
|
232
|
-
'CONNECT_ATTRS'
|
|
240
|
+
'CONNECT_ATTRS',
|
|
233
241
|
];
|
|
234
242
|
if (options && options.multipleStatements) {
|
|
235
243
|
defaultFlags.push('MULTI_STATEMENTS');
|
package/lib/helpers.js
CHANGED
|
@@ -74,14 +74,13 @@ const privateObjectProps = new Set([
|
|
|
74
74
|
|
|
75
75
|
exports.privateObjectProps = privateObjectProps;
|
|
76
76
|
|
|
77
|
-
const fieldEscape = (field) => {
|
|
77
|
+
const fieldEscape = (field, isEval = true) => {
|
|
78
78
|
if (privateObjectProps.has(field)) {
|
|
79
79
|
throw new Error(
|
|
80
80
|
`The field name (${field}) can't be the same as an object's private property.`,
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
return srcEscape(field);
|
|
84
|
+
return isEval ? srcEscape(field) : field;
|
|
85
85
|
};
|
|
86
|
-
|
|
87
86
|
exports.fieldEscape = fieldEscape;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const FieldFlags = require('../constants/field_flags.js');
|
|
4
|
+
const Charsets = require('../constants/charsets.js');
|
|
5
|
+
const Types = require('../constants/types.js');
|
|
6
|
+
const helpers = require('../helpers');
|
|
7
|
+
|
|
8
|
+
const typeNames = [];
|
|
9
|
+
for (const t in Types) {
|
|
10
|
+
typeNames[Types[t]] = t;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function getBinaryParser(fields, _options, config) {
|
|
14
|
+
function readCode(field, config, options, fieldNum, packet) {
|
|
15
|
+
const supportBigNumbers = Boolean(
|
|
16
|
+
options.supportBigNumbers || config.supportBigNumbers,
|
|
17
|
+
);
|
|
18
|
+
const bigNumberStrings = Boolean(
|
|
19
|
+
options.bigNumberStrings || config.bigNumberStrings,
|
|
20
|
+
);
|
|
21
|
+
const timezone = options.timezone || config.timezone;
|
|
22
|
+
const dateStrings = options.dateStrings || config.dateStrings;
|
|
23
|
+
const unsigned = field.flags & FieldFlags.UNSIGNED;
|
|
24
|
+
|
|
25
|
+
switch (field.columnType) {
|
|
26
|
+
case Types.TINY:
|
|
27
|
+
return unsigned ? packet.readInt8() : packet.readSInt8();
|
|
28
|
+
case Types.SHORT:
|
|
29
|
+
return unsigned ? packet.readInt16() : packet.readSInt16();
|
|
30
|
+
case Types.LONG:
|
|
31
|
+
case Types.INT24: // in binary protocol int24 is encoded in 4 bytes int32
|
|
32
|
+
return unsigned ? packet.readInt32() : packet.readSInt32();
|
|
33
|
+
case Types.YEAR:
|
|
34
|
+
return packet.readInt16();
|
|
35
|
+
case Types.FLOAT:
|
|
36
|
+
return packet.readFloat();
|
|
37
|
+
case Types.DOUBLE:
|
|
38
|
+
return packet.readDouble();
|
|
39
|
+
case Types.NULL:
|
|
40
|
+
return null;
|
|
41
|
+
case Types.DATE:
|
|
42
|
+
case Types.DATETIME:
|
|
43
|
+
case Types.TIMESTAMP:
|
|
44
|
+
case Types.NEWDATE:
|
|
45
|
+
return helpers.typeMatch(field.columnType, dateStrings, Types)
|
|
46
|
+
? packet.readDateTimeString(
|
|
47
|
+
parseInt(field.decimals, 10),
|
|
48
|
+
null,
|
|
49
|
+
field.columnType,
|
|
50
|
+
)
|
|
51
|
+
: packet.readDateTime(timezone);
|
|
52
|
+
case Types.TIME:
|
|
53
|
+
return packet.readTimeString();
|
|
54
|
+
case Types.DECIMAL:
|
|
55
|
+
case Types.NEWDECIMAL:
|
|
56
|
+
return config.decimalNumbers
|
|
57
|
+
? packet.parseLengthCodedFloat()
|
|
58
|
+
: packet.readLengthCodedString('ascii');
|
|
59
|
+
case Types.GEOMETRY:
|
|
60
|
+
return packet.parseGeometryValue();
|
|
61
|
+
case Types.VECTOR:
|
|
62
|
+
return packet.parseVector();
|
|
63
|
+
case Types.JSON:
|
|
64
|
+
// Since for JSON columns mysql always returns charset 63 (BINARY),
|
|
65
|
+
// we have to handle it according to JSON specs and use "utf8",
|
|
66
|
+
// see https://github.com/sidorares/node-mysql2/issues/409
|
|
67
|
+
return config.jsonStrings
|
|
68
|
+
? packet.readLengthCodedString('utf8')
|
|
69
|
+
: JSON.parse(packet.readLengthCodedString('utf8'));
|
|
70
|
+
case Types.LONGLONG:
|
|
71
|
+
if (!supportBigNumbers)
|
|
72
|
+
return unsigned
|
|
73
|
+
? packet.readInt64JSNumber()
|
|
74
|
+
: packet.readSInt64JSNumber();
|
|
75
|
+
return bigNumberStrings
|
|
76
|
+
? unsigned
|
|
77
|
+
? packet.readInt64String()
|
|
78
|
+
: packet.readSInt64String()
|
|
79
|
+
: unsigned
|
|
80
|
+
? packet.readInt64()
|
|
81
|
+
: packet.readSInt64();
|
|
82
|
+
default:
|
|
83
|
+
return field.characterSet === Charsets.BINARY
|
|
84
|
+
? packet.readLengthCodedBuffer()
|
|
85
|
+
: packet.readLengthCodedString(fields[fieldNum].encoding);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return class BinaryRow {
|
|
90
|
+
constructor() {}
|
|
91
|
+
|
|
92
|
+
next(packet, fields, options) {
|
|
93
|
+
packet.readInt8(); // status byte
|
|
94
|
+
|
|
95
|
+
const nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
|
|
96
|
+
const nullBitmaskBytes = new Array(nullBitmapLength);
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < nullBitmapLength; i++) {
|
|
99
|
+
nullBitmaskBytes[i] = packet.readInt8();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const result = options.rowsAsArray ? new Array(fields.length) : {};
|
|
103
|
+
let currentFieldNullBit = 4;
|
|
104
|
+
let nullByteIndex = 0;
|
|
105
|
+
|
|
106
|
+
for (let i = 0; i < fields.length; i++) {
|
|
107
|
+
const field = fields[i];
|
|
108
|
+
const typeCast =
|
|
109
|
+
options.typeCast !== undefined ? options.typeCast : config.typeCast;
|
|
110
|
+
|
|
111
|
+
let value;
|
|
112
|
+
if (nullBitmaskBytes[nullByteIndex] & currentFieldNullBit) {
|
|
113
|
+
value = null;
|
|
114
|
+
} else if (options.typeCast === false) {
|
|
115
|
+
value = packet.readLengthCodedBuffer();
|
|
116
|
+
} else {
|
|
117
|
+
const next = () => readCode(field, config, options, i, packet);
|
|
118
|
+
value =
|
|
119
|
+
typeof typeCast === 'function'
|
|
120
|
+
? typeCast(
|
|
121
|
+
{
|
|
122
|
+
type: typeNames[field.columnType],
|
|
123
|
+
length: field.columnLength,
|
|
124
|
+
db: field.schema,
|
|
125
|
+
table: field.table,
|
|
126
|
+
name: field.name,
|
|
127
|
+
string: function (encoding = field.encoding) {
|
|
128
|
+
if (
|
|
129
|
+
field.columnType === Types.JSON &&
|
|
130
|
+
encoding === field.encoding
|
|
131
|
+
) {
|
|
132
|
+
// Since for JSON columns mysql always returns charset 63 (BINARY),
|
|
133
|
+
// we have to handle it according to JSON specs and use "utf8",
|
|
134
|
+
// see https://github.com/sidorares/node-mysql2/issues/1661
|
|
135
|
+
console.warn(
|
|
136
|
+
`typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (
|
|
141
|
+
[
|
|
142
|
+
Types.DATETIME,
|
|
143
|
+
Types.NEWDATE,
|
|
144
|
+
Types.TIMESTAMP,
|
|
145
|
+
Types.DATE,
|
|
146
|
+
].includes(field.columnType)
|
|
147
|
+
) {
|
|
148
|
+
return packet.readDateTimeString(
|
|
149
|
+
parseInt(field.decimals, 10),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (field.columnType === Types.TINY) {
|
|
154
|
+
const unsigned = field.flags & FieldFlags.UNSIGNED;
|
|
155
|
+
|
|
156
|
+
return String(
|
|
157
|
+
unsigned ? packet.readInt8() : packet.readSInt8(),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (field.columnType === Types.TIME) {
|
|
162
|
+
return packet.readTimeString();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return packet.readLengthCodedString(encoding);
|
|
166
|
+
},
|
|
167
|
+
buffer: function () {
|
|
168
|
+
return packet.readLengthCodedBuffer();
|
|
169
|
+
},
|
|
170
|
+
geometry: function () {
|
|
171
|
+
return packet.parseGeometryValue();
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
next,
|
|
175
|
+
)
|
|
176
|
+
: next();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (options.rowsAsArray) {
|
|
180
|
+
result[i] = value;
|
|
181
|
+
} else if (typeof options.nestTables === 'string') {
|
|
182
|
+
const key = helpers.fieldEscape(
|
|
183
|
+
field.table + options.nestTables + field.name,
|
|
184
|
+
false,
|
|
185
|
+
);
|
|
186
|
+
result[key] = value;
|
|
187
|
+
} else if (options.nestTables === true) {
|
|
188
|
+
const tableName = helpers.fieldEscape(field.table, false);
|
|
189
|
+
if (!result[tableName]) {
|
|
190
|
+
result[tableName] = {};
|
|
191
|
+
}
|
|
192
|
+
const fieldName = helpers.fieldEscape(field.name, false);
|
|
193
|
+
result[tableName][fieldName] = value;
|
|
194
|
+
} else {
|
|
195
|
+
const key = helpers.fieldEscape(field.name, false);
|
|
196
|
+
result[key] = value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
currentFieldNullBit *= 2;
|
|
200
|
+
if (currentFieldNullBit === 0x100) {
|
|
201
|
+
currentFieldNullBit = 1;
|
|
202
|
+
nullByteIndex++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = getBinaryParser;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Types = require('../constants/types.js');
|
|
4
|
+
const Charsets = require('../constants/charsets.js');
|
|
5
|
+
const helpers = require('../helpers');
|
|
6
|
+
|
|
7
|
+
const typeNames = [];
|
|
8
|
+
for (const t in Types) {
|
|
9
|
+
typeNames[Types[t]] = t;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function readField({ packet, type, charset, encoding, config, options }) {
|
|
13
|
+
const supportBigNumbers = Boolean(
|
|
14
|
+
options.supportBigNumbers || config.supportBigNumbers,
|
|
15
|
+
);
|
|
16
|
+
const bigNumberStrings = Boolean(
|
|
17
|
+
options.bigNumberStrings || config.bigNumberStrings,
|
|
18
|
+
);
|
|
19
|
+
const timezone = options.timezone || config.timezone;
|
|
20
|
+
const dateStrings = options.dateStrings || config.dateStrings;
|
|
21
|
+
|
|
22
|
+
switch (type) {
|
|
23
|
+
case Types.TINY:
|
|
24
|
+
case Types.SHORT:
|
|
25
|
+
case Types.LONG:
|
|
26
|
+
case Types.INT24:
|
|
27
|
+
case Types.YEAR:
|
|
28
|
+
return packet.parseLengthCodedIntNoBigCheck();
|
|
29
|
+
case Types.LONGLONG:
|
|
30
|
+
if (supportBigNumbers && bigNumberStrings) {
|
|
31
|
+
return packet.parseLengthCodedIntString();
|
|
32
|
+
}
|
|
33
|
+
return packet.parseLengthCodedInt(supportBigNumbers);
|
|
34
|
+
case Types.FLOAT:
|
|
35
|
+
case Types.DOUBLE:
|
|
36
|
+
return packet.parseLengthCodedFloat();
|
|
37
|
+
case Types.NULL:
|
|
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 config.jsonStrings
|
|
64
|
+
? packet.readLengthCodedString('utf8')
|
|
65
|
+
: JSON.parse(packet.readLengthCodedString('utf8'));
|
|
66
|
+
default:
|
|
67
|
+
if (charset === Charsets.BINARY) {
|
|
68
|
+
return packet.readLengthCodedBuffer();
|
|
69
|
+
}
|
|
70
|
+
return packet.readLengthCodedString(encoding);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function createTypecastField(field, packet) {
|
|
75
|
+
return {
|
|
76
|
+
type: typeNames[field.columnType],
|
|
77
|
+
length: field.columnLength,
|
|
78
|
+
db: field.schema,
|
|
79
|
+
table: field.table,
|
|
80
|
+
name: field.name,
|
|
81
|
+
string: function (encoding = field.encoding) {
|
|
82
|
+
if (field.columnType === Types.JSON && encoding === field.encoding) {
|
|
83
|
+
// Since for JSON columns mysql always returns charset 63 (BINARY),
|
|
84
|
+
// we have to handle it according to JSON specs and use "utf8",
|
|
85
|
+
// see https://github.com/sidorares/node-mysql2/issues/1661
|
|
86
|
+
console.warn(
|
|
87
|
+
`typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return packet.readLengthCodedString(encoding);
|
|
91
|
+
},
|
|
92
|
+
buffer: function () {
|
|
93
|
+
return packet.readLengthCodedBuffer();
|
|
94
|
+
},
|
|
95
|
+
geometry: function () {
|
|
96
|
+
return packet.parseGeometryValue();
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getTextParser(_fields, _options, config) {
|
|
102
|
+
return {
|
|
103
|
+
next(packet, fields, options) {
|
|
104
|
+
const result = options.rowsAsArray ? [] : {};
|
|
105
|
+
for (let i = 0; i < fields.length; i++) {
|
|
106
|
+
const field = fields[i];
|
|
107
|
+
const typeCast = options.typeCast ? options.typeCast : config.typeCast;
|
|
108
|
+
const next = () =>
|
|
109
|
+
readField({
|
|
110
|
+
packet,
|
|
111
|
+
type: field.columnType,
|
|
112
|
+
encoding: field.encoding,
|
|
113
|
+
charset: field.characterSet,
|
|
114
|
+
config,
|
|
115
|
+
options,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
let value;
|
|
119
|
+
|
|
120
|
+
if (options.typeCast === false) {
|
|
121
|
+
value = packet.readLengthCodedBuffer();
|
|
122
|
+
} else if (typeof typeCast === 'function') {
|
|
123
|
+
value = typeCast(createTypecastField(field, packet), next);
|
|
124
|
+
} else {
|
|
125
|
+
value = next();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (options.rowsAsArray) {
|
|
129
|
+
result.push(value);
|
|
130
|
+
} else if (typeof options.nestTables === 'string') {
|
|
131
|
+
result[
|
|
132
|
+
`${helpers.fieldEscape(field.table, false)}${options.nestTables}${helpers.fieldEscape(field.name, false)}`
|
|
133
|
+
] = value;
|
|
134
|
+
} else if (options.nestTables) {
|
|
135
|
+
const tableName = helpers.fieldEscape(field.table, false);
|
|
136
|
+
if (!result[tableName]) {
|
|
137
|
+
result[tableName] = {};
|
|
138
|
+
}
|
|
139
|
+
result[tableName][helpers.fieldEscape(field.name, false)] = value;
|
|
140
|
+
} else {
|
|
141
|
+
result[helpers.fieldEscape(field.name, false)] = value;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = getTextParser;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql2",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.1-canary.51da6534",
|
|
4
4
|
"description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "typings/mysql/index",
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"lint": "npm run lint:docs && npm run lint:code",
|
|
10
10
|
"lint:code": "eslint index.js promise.js index.d.ts promise.d.ts \"typings/**/*.ts\" \"lib/**/*.js\" \"test/**/*.{js,cjs,mjs,ts}\" \"benchmarks/**/*.js\"",
|
|
11
|
+
"lint:fix": "npm run lint:docs -- --fix &&npm run lint:code -- --fix",
|
|
11
12
|
"lint:docs": "eslint Contributing.md README.md",
|
|
12
13
|
"lint:typings": "npx prettier --check ./typings",
|
|
13
14
|
"lint:tests": "npx prettier --check ./test",
|
|
14
|
-
"test": "poku -d test/esm test/unit test/integration",
|
|
15
|
-
"test:bun": "poku -d --
|
|
16
|
-
"test:deno": "deno run --allow-read --allow-env --allow-run npm:poku -d --
|
|
15
|
+
"test": "poku -d -r=verbose --sequential test/esm test/unit test/integration",
|
|
16
|
+
"test:bun": "bun poku -d --sequential test/esm test/unit test/integration",
|
|
17
|
+
"test:deno": "deno run --allow-read --allow-env --allow-run npm:poku -d --sequential --denoAllow=\"read,env,net,sys\" test/esm test/unit test/integration",
|
|
17
18
|
"test:tsc-build": "cd \"test/tsc-build\" && npx tsc -p \"tsconfig.json\"",
|
|
18
19
|
"coverage-test": "c8 npm run test",
|
|
19
20
|
"benchmark": "node ./benchmarks/benchmark.js",
|
|
@@ -78,11 +79,11 @@
|
|
|
78
79
|
"c8": "^10.1.1",
|
|
79
80
|
"error-stack-parser": "^2.0.3",
|
|
80
81
|
"eslint": "^8.27.0",
|
|
81
|
-
"eslint-config-prettier": "^
|
|
82
|
+
"eslint-config-prettier": "^10.0.1",
|
|
82
83
|
"eslint-plugin-async-await": "0.0.0",
|
|
83
84
|
"eslint-plugin-markdown": "^5.0.0",
|
|
84
85
|
"lint-staged": "^15.0.1",
|
|
85
|
-
"poku": "^
|
|
86
|
+
"poku": "^3.0.0",
|
|
86
87
|
"portfinder": "^1.0.28",
|
|
87
88
|
"prettier": "^3.0.0",
|
|
88
89
|
"progress": "^2.0.3",
|