mysql2 3.22.6 → 3.22.7-canary.5034e57
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/base/connection.js +2 -0
- package/lib/commands/client_handshake.js +25 -1
- package/lib/commands/execute.js +4 -2
- package/lib/commands/prepare.js +10 -2
- package/lib/commands/query.js +2 -1
- package/lib/constants/charset_encodings.js +25 -1
- package/lib/constants/mariadb_client.js +19 -0
- package/lib/packets/column_definition.js +35 -1
- package/lib/packets/encode_parameter.js +12 -2
- package/lib/packets/execute.js +8 -2
- package/lib/packets/handshake.js +13 -1
- package/lib/packets/handshake_response.js +5 -1
- package/lib/packets/ssl_request.js +6 -1
- package/lib/parsers/binary_parser.js +9 -0
- package/lib/parsers/parser_cache.js +3 -0
- package/lib/parsers/static_binary_parser.js +10 -0
- package/lib/parsers/static_text_parser.js +20 -1
- package/lib/parsers/text_parser.js +14 -8
- package/package.json +1 -1
- package/typings/mysql/lib/parsers/typeCast.d.ts +11 -0
- package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +11 -0
package/lib/base/connection.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
const Command = require('./command.js');
|
|
14
14
|
const Packets = require('../packets/index.js');
|
|
15
15
|
const ClientConstants = require('../constants/client.js');
|
|
16
|
+
const MariaDBClientConstants = require('../constants/mariadb_client.js');
|
|
16
17
|
const CharsetToEncoding = require('../constants/charset_encodings.js');
|
|
17
18
|
const auth41 = require('../auth_41.js');
|
|
18
19
|
const { getAuthPlugin } = require('./auth_switch.js');
|
|
@@ -35,6 +36,7 @@ class ClientHandshake extends Command {
|
|
|
35
36
|
super();
|
|
36
37
|
this.handshake = null;
|
|
37
38
|
this.clientFlags = clientFlags;
|
|
39
|
+
this.mariadbExtendedClientFlags = 0;
|
|
38
40
|
this.authenticationFactor = 0;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -45,7 +47,8 @@ class ClientHandshake extends Command {
|
|
|
45
47
|
sendSSLRequest(connection) {
|
|
46
48
|
const sslRequest = new Packets.SSLRequest(
|
|
47
49
|
this.clientFlags,
|
|
48
|
-
connection.config.charsetNumber
|
|
50
|
+
connection.config.charsetNumber,
|
|
51
|
+
this.mariadbExtendedClientFlags
|
|
49
52
|
);
|
|
50
53
|
connection.writePacket(sslRequest.toPacket());
|
|
51
54
|
}
|
|
@@ -130,6 +133,7 @@ class ClientHandshake extends Command {
|
|
|
130
133
|
|
|
131
134
|
const handshakeResponse = new Packets.HandshakeResponse({
|
|
132
135
|
flags: this.clientFlags,
|
|
136
|
+
mariadbExtendedClientFlags: this.mariadbExtendedClientFlags,
|
|
133
137
|
user: this.user,
|
|
134
138
|
database: this.database,
|
|
135
139
|
password: this.password,
|
|
@@ -265,6 +269,26 @@ class ClientHandshake extends Command {
|
|
|
265
269
|
connection.serverCapabilityFlags = this.handshake.capabilityFlags;
|
|
266
270
|
connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
|
|
267
271
|
connection.connectionId = this.handshake.connectionId;
|
|
272
|
+
// MariaDB needs a few protocol deviations, e.g. it rejects the JSON
|
|
273
|
+
// parameter type in COM_STMT_EXECUTE
|
|
274
|
+
connection._isMariaDB = /mariadb/i.test(this.handshake.serverVersion);
|
|
275
|
+
// If a MariaDB server offers extended column metadata, request it so
|
|
276
|
+
// MariaDB-specific column types (UUID, INET4, INET6, VECTOR and the JSON
|
|
277
|
+
// alias) can be identified. The server only reads the extended client
|
|
278
|
+
// capability bytes when the client leaves CLIENT_MYSQL (LONG_PASSWORD)
|
|
279
|
+
// unset, mirroring the server side of the negotiation.
|
|
280
|
+
if (
|
|
281
|
+
this.handshake.mariadbExtendedCapabilityFlags &
|
|
282
|
+
MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA
|
|
283
|
+
) {
|
|
284
|
+
this.clientFlags &= ~ClientConstants.LONG_PASSWORD;
|
|
285
|
+
this.mariadbExtendedClientFlags |=
|
|
286
|
+
MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA;
|
|
287
|
+
}
|
|
288
|
+
connection._mariadbExtendedMetadata = Boolean(
|
|
289
|
+
this.mariadbExtendedClientFlags &
|
|
290
|
+
MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA
|
|
291
|
+
);
|
|
268
292
|
const serverSSLSupport =
|
|
269
293
|
this.handshake.capabilityFlags & ClientConstants.SSL;
|
|
270
294
|
// multi factor authentication is enabled with the
|
package/lib/commands/execute.js
CHANGED
|
@@ -51,7 +51,8 @@ class Execute extends Command {
|
|
|
51
51
|
connection.config.charsetNumber,
|
|
52
52
|
connection.config.timezone,
|
|
53
53
|
this._executeOptions.attributes,
|
|
54
|
-
clientFlags
|
|
54
|
+
clientFlags,
|
|
55
|
+
connection._isMariaDB
|
|
55
56
|
);
|
|
56
57
|
//For reasons why this try-catch is here, please see
|
|
57
58
|
// https://github.com/sidorares/node-mysql2/pull/689
|
|
@@ -77,7 +78,8 @@ class Execute extends Command {
|
|
|
77
78
|
// this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
|
|
78
79
|
const field = new Packets.ColumnDefinition(
|
|
79
80
|
packet,
|
|
80
|
-
connection.clientEncoding
|
|
81
|
+
connection.clientEncoding,
|
|
82
|
+
connection._mariadbExtendedMetadata
|
|
81
83
|
);
|
|
82
84
|
this._receivedFieldsCount++;
|
|
83
85
|
this._fields[this._resultIndex].push(field);
|
package/lib/commands/prepare.js
CHANGED
|
@@ -87,7 +87,11 @@ class Prepare extends Command {
|
|
|
87
87
|
}
|
|
88
88
|
return this.prepareDone(connection);
|
|
89
89
|
}
|
|
90
|
-
const def = new Packets.ColumnDefinition(
|
|
90
|
+
const def = new Packets.ColumnDefinition(
|
|
91
|
+
packet,
|
|
92
|
+
connection.clientEncoding,
|
|
93
|
+
connection._mariadbExtendedMetadata
|
|
94
|
+
);
|
|
91
95
|
this.parameterDefinitions.push(def);
|
|
92
96
|
if (this.parameterDefinitions.length === this.parameterCount) {
|
|
93
97
|
return Prepare.prototype.parametersEOF;
|
|
@@ -99,7 +103,11 @@ class Prepare extends Command {
|
|
|
99
103
|
if (packet.isEOF()) {
|
|
100
104
|
return this.prepareDone(connection);
|
|
101
105
|
}
|
|
102
|
-
const def = new Packets.ColumnDefinition(
|
|
106
|
+
const def = new Packets.ColumnDefinition(
|
|
107
|
+
packet,
|
|
108
|
+
connection.clientEncoding,
|
|
109
|
+
connection._mariadbExtendedMetadata
|
|
110
|
+
);
|
|
103
111
|
this.fields.push(def);
|
|
104
112
|
if (this.fields.length === this.fieldCount) {
|
|
105
113
|
return Prepare.prototype.fieldsEOF;
|
package/lib/commands/query.js
CHANGED
|
@@ -199,7 +199,8 @@ class Query extends Command {
|
|
|
199
199
|
if (this._fields[this._resultIndex].length !== this._fieldCount) {
|
|
200
200
|
const field = new Packets.ColumnDefinition(
|
|
201
201
|
packet,
|
|
202
|
-
connection.clientEncoding
|
|
202
|
+
connection.clientEncoding,
|
|
203
|
+
connection._mariadbExtendedMetadata
|
|
203
204
|
);
|
|
204
205
|
this._fields[this._resultIndex].push(field);
|
|
205
206
|
if (connection.config.debug) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// see tools/generate-charset-mapping.js
|
|
4
4
|
// basicalliy result of "SHOW COLLATION" query
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const encodings = [
|
|
7
7
|
'utf8',
|
|
8
8
|
'big5',
|
|
9
9
|
'latin2',
|
|
@@ -315,3 +315,27 @@ module.exports = [
|
|
|
315
315
|
'utf8',
|
|
316
316
|
'utf8',
|
|
317
317
|
];
|
|
318
|
+
|
|
319
|
+
// MariaDB NO PAD collations mirror their PAD SPACE counterparts:
|
|
320
|
+
// collation id = PAD SPACE collation id + 1024
|
|
321
|
+
const padSpaceLength = encodings.length;
|
|
322
|
+
for (let id = 1025; id < 1024 + padSpaceLength; id++) {
|
|
323
|
+
encodings[id] = encodings[id - 1024];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// MariaDB UCA-14.0.0 (uca1400) collations use fixed 256-id blocks
|
|
327
|
+
// per character set, each covering the PAD SPACE and NO PAD variants
|
|
328
|
+
const uca1400Blocks = [
|
|
329
|
+
[2048, 'cesu8'], // utf8mb3
|
|
330
|
+
[2304, 'utf8'], // utf8mb4
|
|
331
|
+
[2560, 'ucs2'], // ucs2
|
|
332
|
+
[2816, 'utf16'], // utf16
|
|
333
|
+
[3072, 'utf32'], // utf32
|
|
334
|
+
];
|
|
335
|
+
for (const [start, encoding] of uca1400Blocks) {
|
|
336
|
+
for (let id = start; id < start + 256; id++) {
|
|
337
|
+
encodings[id] = encoding;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
module.exports = encodings;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// MariaDB-specific extended capability flags.
|
|
4
|
+
//
|
|
5
|
+
// MariaDB servers (10.2+) do not set the CLIENT_MYSQL (LONG_PASSWORD)
|
|
6
|
+
// capability and instead use 4 reserved bytes of the initial handshake packet
|
|
7
|
+
// to advertise these flags. Clients answer with their own extended
|
|
8
|
+
// capabilities in the last 4 reserved bytes of the handshake response
|
|
9
|
+
// (the server only reads them when the client leaves CLIENT_MYSQL unset).
|
|
10
|
+
//
|
|
11
|
+
// The flags below are the lower 32 bits shifted down, i.e.
|
|
12
|
+
// MARIADB_CLIENT_PROGRESS is documented as 1 << 32.
|
|
13
|
+
// https://mariadb.com/docs/server/reference/clientserver-protocol/1-connecting/connection
|
|
14
|
+
exports.MARIADB_CLIENT_PROGRESS = 0x00000001;
|
|
15
|
+
exports.MARIADB_CLIENT_COM_MULTI = 0x00000002; /* deprecated */
|
|
16
|
+
exports.MARIADB_CLIENT_STMT_BULK_OPERATIONS = 0x00000004;
|
|
17
|
+
exports.MARIADB_CLIENT_EXTENDED_METADATA = 0x00000008;
|
|
18
|
+
exports.MARIADB_CLIENT_CACHE_METADATA = 0x00000010;
|
|
19
|
+
exports.MARIADB_CLIENT_BULK_UNIT_RESULTS = 0x00000020;
|
|
@@ -19,7 +19,7 @@ const fields = ['catalog', 'schema', 'table', 'orgTable', 'name', 'orgName'];
|
|
|
19
19
|
// see https://github.com/sidorares/node-mysql2/pull/137
|
|
20
20
|
//
|
|
21
21
|
class ColumnDefinition {
|
|
22
|
-
constructor(packet, clientEncoding) {
|
|
22
|
+
constructor(packet, clientEncoding, mariadbExtendedMetadata) {
|
|
23
23
|
this._buf = packet.buffer;
|
|
24
24
|
this._clientEncoding = clientEncoding;
|
|
25
25
|
this._catalogLength = packet.readLengthCodedNumber();
|
|
@@ -41,6 +41,14 @@ class ColumnDefinition {
|
|
|
41
41
|
this._orgNameLength = packet.readLengthCodedNumber();
|
|
42
42
|
this._orgNameStart = packet.offset;
|
|
43
43
|
packet.offset += this._orgNameLength;
|
|
44
|
+
// MariaDB-specific column types (UUID, INET4, INET6, VECTOR and the JSON
|
|
45
|
+
// alias) are sent as standard string/blob types on the wire; the real
|
|
46
|
+
// type is only visible here, in the extended metadata block negotiated
|
|
47
|
+
// via the MARIADB_CLIENT_EXTENDED_METADATA capability (parsed out of
|
|
48
|
+
// line and with prototype defaults to keep this hot constructor small)
|
|
49
|
+
if (mariadbExtendedMetadata) {
|
|
50
|
+
this._parseMariadbExtendedMetadata(packet);
|
|
51
|
+
}
|
|
44
52
|
packet.skip(1); // length of the following fields (always 0x0c)
|
|
45
53
|
this.characterSet = packet.readInt16();
|
|
46
54
|
this.encoding = CharsetToEncoding[this.characterSet];
|
|
@@ -57,6 +65,27 @@ class ColumnDefinition {
|
|
|
57
65
|
this.decimals = packet.readInt8();
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
_parseMariadbExtendedMetadata(packet) {
|
|
69
|
+
// length-encoded block of { int<1> id, string<lenenc> value } pairs
|
|
70
|
+
const extendedMetadataLength = packet.readLengthCodedNumber() || 0;
|
|
71
|
+
const extendedMetadataEnd = Math.min(
|
|
72
|
+
packet.offset + extendedMetadataLength,
|
|
73
|
+
packet.end
|
|
74
|
+
);
|
|
75
|
+
while (packet.offset < extendedMetadataEnd) {
|
|
76
|
+
const id = packet.readInt8();
|
|
77
|
+
const value = packet.readLengthCodedString('ascii');
|
|
78
|
+
if (id === 0) {
|
|
79
|
+
this.extendedTypeName = value; // e.g. 'uuid', 'inet4', 'inet6', 'point'
|
|
80
|
+
} else if (id === 1) {
|
|
81
|
+
this.extendedFormat = value; // e.g. 'json'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// a malformed entry could read past the block; resynchronise so the
|
|
85
|
+
// fixed column definition fields that follow parse from the right offset
|
|
86
|
+
packet.offset = extendedMetadataEnd;
|
|
87
|
+
}
|
|
88
|
+
|
|
60
89
|
inspect() {
|
|
61
90
|
return {
|
|
62
91
|
catalog: this.catalog,
|
|
@@ -288,4 +317,9 @@ addString('table');
|
|
|
288
317
|
addString('orgTable');
|
|
289
318
|
addString('orgName');
|
|
290
319
|
|
|
320
|
+
// MariaDB extended metadata defaults; instances only carry own values when
|
|
321
|
+
// the server actually tagged the column
|
|
322
|
+
ColumnDefinition.prototype.extendedTypeName = undefined;
|
|
323
|
+
ColumnDefinition.prototype.extendedFormat = undefined;
|
|
324
|
+
|
|
291
325
|
module.exports = ColumnDefinition;
|
|
@@ -11,7 +11,7 @@ function isJSON(value) {
|
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function toParameter(value, encoding, timezone) {
|
|
14
|
+
function toParameter(value, encoding, timezone, jsonAsString) {
|
|
15
15
|
let type = Types.VAR_STRING;
|
|
16
16
|
let length;
|
|
17
17
|
let writer = function (value) {
|
|
@@ -46,8 +46,18 @@ function toParameter(value, encoding, timezone) {
|
|
|
46
46
|
};
|
|
47
47
|
} else if (isJSON(value)) {
|
|
48
48
|
value = JSON.stringify(value);
|
|
49
|
-
type
|
|
49
|
+
// MariaDB rejects the JSON parameter type with "Incorrect
|
|
50
|
+
// arguments to mysqld_stmt_execute"; it expects JSON values
|
|
51
|
+
// as plain strings
|
|
52
|
+
if (!jsonAsString) {
|
|
53
|
+
type = Types.JSON;
|
|
54
|
+
}
|
|
50
55
|
} else if (Buffer.isBuffer(value)) {
|
|
56
|
+
// send buffers as BLOB so servers treat the value as binary data
|
|
57
|
+
// rather than a string in the connection charset (MariaDB converts
|
|
58
|
+
// string parameters when storing into binary columns such as
|
|
59
|
+
// VECTOR, corrupting the value)
|
|
60
|
+
type = Types.BLOB;
|
|
51
61
|
length = Packet.lengthCodedNumberLength(value.length) + value.length;
|
|
52
62
|
writer = Packet.prototype.writeLengthCodedBuffer;
|
|
53
63
|
}
|
package/lib/packets/execute.js
CHANGED
|
@@ -15,7 +15,8 @@ class Execute {
|
|
|
15
15
|
charsetNumber,
|
|
16
16
|
timezone,
|
|
17
17
|
attributes,
|
|
18
|
-
clientFlags
|
|
18
|
+
clientFlags,
|
|
19
|
+
jsonAsString
|
|
19
20
|
) {
|
|
20
21
|
this.id = id;
|
|
21
22
|
this.parameters = parameters;
|
|
@@ -23,6 +24,7 @@ class Execute {
|
|
|
23
24
|
this.timezone = timezone;
|
|
24
25
|
this.attributes = attributes;
|
|
25
26
|
this.clientFlags = clientFlags || 0;
|
|
27
|
+
this.jsonAsString = jsonAsString || false;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
static fromPacket(packet, encoding) {
|
|
@@ -34,6 +36,7 @@ class Execute {
|
|
|
34
36
|
while (i < packet.end - 1) {
|
|
35
37
|
if (
|
|
36
38
|
(packet.buffer[i + 1] === Types.VAR_STRING ||
|
|
39
|
+
packet.buffer[i + 1] === Types.BLOB ||
|
|
37
40
|
packet.buffer[i + 1] === Types.NULL ||
|
|
38
41
|
packet.buffer[i + 1] === Types.DOUBLE ||
|
|
39
42
|
packet.buffer[i + 1] === Types.TINY ||
|
|
@@ -54,6 +57,7 @@ class Execute {
|
|
|
54
57
|
for (let i = packet.offset + 1; i < packet.end - 1; i++) {
|
|
55
58
|
if (
|
|
56
59
|
(packet.buffer[i] === Types.VAR_STRING ||
|
|
60
|
+
packet.buffer[i] === Types.BLOB ||
|
|
57
61
|
packet.buffer[i] === Types.NULL ||
|
|
58
62
|
packet.buffer[i] === Types.DOUBLE ||
|
|
59
63
|
packet.buffer[i] === Types.TINY ||
|
|
@@ -72,6 +76,8 @@ class Execute {
|
|
|
72
76
|
for (let i = 0; i < types.length; i++) {
|
|
73
77
|
if (types[i] === Types.VAR_STRING) {
|
|
74
78
|
values.push(packet.readLengthCodedString(encoding));
|
|
79
|
+
} else if (types[i] === Types.BLOB) {
|
|
80
|
+
values.push(packet.readLengthCodedBuffer());
|
|
75
81
|
} else if (types[i] === Types.DOUBLE) {
|
|
76
82
|
values.push(packet.readDouble());
|
|
77
83
|
} else if (types[i] === Types.TINY) {
|
|
@@ -119,7 +125,7 @@ class Execute {
|
|
|
119
125
|
const bindParams =
|
|
120
126
|
numParams > 0
|
|
121
127
|
? this.parameters.map((v) =>
|
|
122
|
-
toParameter(v, this.encoding, this.timezone)
|
|
128
|
+
toParameter(v, this.encoding, this.timezone, this.jsonAsString)
|
|
123
129
|
)
|
|
124
130
|
: [];
|
|
125
131
|
const attrParams = attrNames.map((name) =>
|
package/lib/packets/handshake.js
CHANGED
|
@@ -16,6 +16,7 @@ class Handshake {
|
|
|
16
16
|
this.characterSet = args.characterSet;
|
|
17
17
|
this.statusFlags = args.statusFlags;
|
|
18
18
|
this.authPluginName = args.authPluginName;
|
|
19
|
+
this.mariadbExtendedCapabilityFlags = args.mariadbExtendedCapabilityFlags;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
setScrambleData(cb) {
|
|
@@ -79,9 +80,20 @@ class Handshake {
|
|
|
79
80
|
args.authPluginDataLength = 0;
|
|
80
81
|
packet.skip(1);
|
|
81
82
|
}
|
|
82
|
-
packet.skip(
|
|
83
|
+
packet.skip(6);
|
|
84
|
+
if (args.capabilityFlags & ClientConstants.LONG_PASSWORD) {
|
|
85
|
+
// CLIENT_MYSQL (LONG_PASSWORD) set: a MySQL server, the remaining
|
|
86
|
+
// 4 reserved bytes are filler
|
|
87
|
+
packet.skip(4);
|
|
88
|
+
args.mariadbExtendedCapabilityFlags = 0;
|
|
89
|
+
} else {
|
|
90
|
+
// MariaDB servers (10.2+) leave CLIENT_MYSQL unset and use these
|
|
91
|
+
// 4 bytes to advertise MariaDB-specific capabilities
|
|
92
|
+
args.mariadbExtendedCapabilityFlags = packet.readInt32();
|
|
93
|
+
}
|
|
83
94
|
} else {
|
|
84
95
|
args.capabilityFlags = capabilityFlagsBuffer.readUInt16LE(0);
|
|
96
|
+
args.mariadbExtendedCapabilityFlags = 0;
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
const isSecureConnection =
|
|
@@ -16,6 +16,7 @@ class HandshakeResponse {
|
|
|
16
16
|
this.authPluginData2 = handshake.authPluginData2;
|
|
17
17
|
this.compress = handshake.compress;
|
|
18
18
|
this.clientFlags = handshake.flags;
|
|
19
|
+
this.mariadbExtendedClientFlags = handshake.mariadbExtendedClientFlags || 0;
|
|
19
20
|
|
|
20
21
|
// Accept pre-calculated authToken and authPluginName from caller
|
|
21
22
|
// This allows the caller to optimize by using the server's preferred auth method
|
|
@@ -69,7 +70,10 @@ class HandshakeResponse {
|
|
|
69
70
|
packet.writeInt32(this.clientFlags);
|
|
70
71
|
packet.writeInt32(0); // max packet size. todo: move to config
|
|
71
72
|
packet.writeInt8(this.charsetNumber);
|
|
72
|
-
|
|
73
|
+
// the last 4 of the 23 reserved bytes carry the MariaDB extended client
|
|
74
|
+
// capabilities (zero when not negotiated, i.e. plain filler)
|
|
75
|
+
packet.skip(19);
|
|
76
|
+
packet.writeInt32(this.mariadbExtendedClientFlags);
|
|
73
77
|
const encoding = this.encoding;
|
|
74
78
|
packet.writeNullTerminatedString(this.user, encoding);
|
|
75
79
|
let k;
|
|
@@ -4,9 +4,10 @@ const ClientConstants = require('../constants/client');
|
|
|
4
4
|
const Packet = require('../packets/packet');
|
|
5
5
|
|
|
6
6
|
class SSLRequest {
|
|
7
|
-
constructor(flags, charset) {
|
|
7
|
+
constructor(flags, charset, mariadbExtendedClientFlags) {
|
|
8
8
|
this.clientFlags = flags | ClientConstants.SSL;
|
|
9
9
|
this.charset = charset;
|
|
10
|
+
this.mariadbExtendedClientFlags = mariadbExtendedClientFlags || 0;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
toPacket() {
|
|
@@ -18,6 +19,10 @@ class SSLRequest {
|
|
|
18
19
|
packet.writeInt32(this.clientFlags);
|
|
19
20
|
packet.writeInt32(0); // max packet size. todo: move to config
|
|
20
21
|
packet.writeInt8(this.charset);
|
|
22
|
+
// the last 4 of the 23 reserved bytes carry the MariaDB extended client
|
|
23
|
+
// capabilities (zero when not negotiated, i.e. plain filler)
|
|
24
|
+
packet.skip(19);
|
|
25
|
+
packet.writeInt32(this.mariadbExtendedClientFlags);
|
|
21
26
|
return packet;
|
|
22
27
|
}
|
|
23
28
|
}
|
|
@@ -21,6 +21,13 @@ function readCodeFor(field, config, options, fieldNum) {
|
|
|
21
21
|
const timezone = options.timezone || config.timezone;
|
|
22
22
|
const dateStrings = options.dateStrings || config.dateStrings;
|
|
23
23
|
const unsigned = field.flags & FieldFlags.UNSIGNED;
|
|
24
|
+
// MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
|
|
25
|
+
// the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
|
|
26
|
+
if (field.extendedFormat === 'json') {
|
|
27
|
+
return config.jsonStrings
|
|
28
|
+
? `packet.readLengthCodedString(fields[${fieldNum}].encoding)`
|
|
29
|
+
: `JSON.parse(packet.readLengthCodedString(fields[${fieldNum}].encoding));`;
|
|
30
|
+
}
|
|
24
31
|
switch (field.columnType) {
|
|
25
32
|
case Types.TINY:
|
|
26
33
|
return unsigned ? 'packet.readInt8();' : 'packet.readSInt8();';
|
|
@@ -92,6 +99,8 @@ function compile(fields, options, config) {
|
|
|
92
99
|
function wrap(field, packet) {
|
|
93
100
|
return {
|
|
94
101
|
type: typeNames[field.columnType],
|
|
102
|
+
extendedTypeName: field.extendedTypeName,
|
|
103
|
+
extendedFormat: field.extendedFormat,
|
|
95
104
|
length: field.columnLength,
|
|
96
105
|
db: field.schema,
|
|
97
106
|
table: field.table,
|
|
@@ -20,6 +20,7 @@ function keyFromFields(type, fields, options, config) {
|
|
|
20
20
|
options.timezone || config.timezone,
|
|
21
21
|
Boolean(options.decimalNumbers),
|
|
22
22
|
options.dateStrings,
|
|
23
|
+
Boolean(config.jsonStrings),
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
for (let i = 0; i < fields.length; ++i) {
|
|
@@ -33,6 +34,8 @@ function keyFromFields(type, fields, options, config) {
|
|
|
33
34
|
field.table,
|
|
34
35
|
field.flags,
|
|
35
36
|
field.characterSet,
|
|
37
|
+
field.extendedTypeName,
|
|
38
|
+
field.extendedFormat,
|
|
36
39
|
]);
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -22,6 +22,14 @@ function getBinaryParser(fields, _options, config) {
|
|
|
22
22
|
const dateStrings = options.dateStrings || config.dateStrings;
|
|
23
23
|
const unsigned = field.flags & FieldFlags.UNSIGNED;
|
|
24
24
|
|
|
25
|
+
// MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
|
|
26
|
+
// the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
|
|
27
|
+
if (field.extendedFormat === 'json') {
|
|
28
|
+
return config.jsonStrings
|
|
29
|
+
? packet.readLengthCodedString(field.encoding)
|
|
30
|
+
: JSON.parse(packet.readLengthCodedString(field.encoding));
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
switch (field.columnType) {
|
|
26
34
|
case Types.TINY:
|
|
27
35
|
return unsigned ? packet.readInt8() : packet.readSInt8();
|
|
@@ -120,6 +128,8 @@ function getBinaryParser(fields, _options, config) {
|
|
|
120
128
|
? typeCast(
|
|
121
129
|
{
|
|
122
130
|
type: typeNames[field.columnType],
|
|
131
|
+
extendedTypeName: field.extendedTypeName,
|
|
132
|
+
extendedFormat: field.extendedFormat,
|
|
123
133
|
length: field.columnLength,
|
|
124
134
|
db: field.schema,
|
|
125
135
|
table: field.table,
|
|
@@ -9,7 +9,15 @@ for (const t in Types) {
|
|
|
9
9
|
typeNames[Types[t]] = t;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function readField({
|
|
12
|
+
function readField({
|
|
13
|
+
packet,
|
|
14
|
+
field,
|
|
15
|
+
type,
|
|
16
|
+
charset,
|
|
17
|
+
encoding,
|
|
18
|
+
config,
|
|
19
|
+
options,
|
|
20
|
+
}) {
|
|
13
21
|
const supportBigNumbers = Boolean(
|
|
14
22
|
options.supportBigNumbers || config.supportBigNumbers
|
|
15
23
|
);
|
|
@@ -19,6 +27,14 @@ function readField({ packet, type, charset, encoding, config, options }) {
|
|
|
19
27
|
const timezone = options.timezone || config.timezone;
|
|
20
28
|
const dateStrings = options.dateStrings || config.dateStrings;
|
|
21
29
|
|
|
30
|
+
// MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
|
|
31
|
+
// the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
|
|
32
|
+
if (field.extendedFormat === 'json') {
|
|
33
|
+
return config.jsonStrings
|
|
34
|
+
? packet.readLengthCodedString(encoding)
|
|
35
|
+
: JSON.parse(packet.readLengthCodedString(encoding));
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
switch (type) {
|
|
23
39
|
case Types.TINY:
|
|
24
40
|
case Types.SHORT:
|
|
@@ -76,6 +92,8 @@ function readField({ packet, type, charset, encoding, config, options }) {
|
|
|
76
92
|
function createTypecastField(field, packet) {
|
|
77
93
|
return {
|
|
78
94
|
type: typeNames[field.columnType],
|
|
95
|
+
extendedTypeName: field.extendedTypeName,
|
|
96
|
+
extendedFormat: field.extendedFormat,
|
|
79
97
|
length: field.columnLength,
|
|
80
98
|
db: field.schema,
|
|
81
99
|
table: field.table,
|
|
@@ -110,6 +128,7 @@ function getTextParser(_fields, _options, config) {
|
|
|
110
128
|
const next = () =>
|
|
111
129
|
readField({
|
|
112
130
|
packet,
|
|
131
|
+
field,
|
|
113
132
|
type: field.columnType,
|
|
114
133
|
encoding: field.encoding,
|
|
115
134
|
charset: field.characterSet,
|
|
@@ -11,7 +11,9 @@ for (const t in Types) {
|
|
|
11
11
|
typeNames[Types[t]] = t;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function readCodeFor(
|
|
14
|
+
function readCodeFor(field, encodingExpr, config, options) {
|
|
15
|
+
const type = field.columnType;
|
|
16
|
+
const charset = field.characterSet;
|
|
15
17
|
const supportBigNumbers = Boolean(
|
|
16
18
|
options.supportBigNumbers || config.supportBigNumbers
|
|
17
19
|
);
|
|
@@ -21,6 +23,14 @@ function readCodeFor(type, charset, encodingExpr, config, options) {
|
|
|
21
23
|
const timezone = options.timezone || config.timezone;
|
|
22
24
|
const dateStrings = options.dateStrings || config.dateStrings;
|
|
23
25
|
|
|
26
|
+
// MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
|
|
27
|
+
// the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
|
|
28
|
+
if (field.extendedFormat === 'json') {
|
|
29
|
+
return config.jsonStrings
|
|
30
|
+
? `packet.readLengthCodedString(${encodingExpr})`
|
|
31
|
+
: `JSON.parse(packet.readLengthCodedString(${encodingExpr}))`;
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
switch (type) {
|
|
25
35
|
case Types.TINY:
|
|
26
36
|
case Types.SHORT:
|
|
@@ -88,6 +98,8 @@ function compile(fields, options, config) {
|
|
|
88
98
|
function wrap(field, _this) {
|
|
89
99
|
return {
|
|
90
100
|
type: typeNames[field.columnType],
|
|
101
|
+
extendedTypeName: field.extendedTypeName,
|
|
102
|
+
extendedFormat: field.extendedFormat,
|
|
91
103
|
length: field.columnLength,
|
|
92
104
|
db: field.schema,
|
|
93
105
|
table: field.table,
|
|
@@ -174,13 +186,7 @@ function compile(fields, options, config) {
|
|
|
174
186
|
parserFn(`${lvalue} = packet.readLengthCodedBuffer();`);
|
|
175
187
|
} else {
|
|
176
188
|
const encodingExpr = `fields[${i}].encoding`;
|
|
177
|
-
const readCode = readCodeFor(
|
|
178
|
-
fields[i].columnType,
|
|
179
|
-
fields[i].characterSet,
|
|
180
|
-
encodingExpr,
|
|
181
|
-
config,
|
|
182
|
-
options
|
|
183
|
-
);
|
|
189
|
+
const readCode = readCodeFor(fields[i], encodingExpr, config, options);
|
|
184
190
|
if (typeof options.typeCast === 'function') {
|
|
185
191
|
parserFn(
|
|
186
192
|
`${lvalue} = options.typeCast(this.wrap${i}, function() { return ${readCode} });`
|
package/package.json
CHANGED
|
@@ -40,6 +40,17 @@ export type Type = {
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export type Field = Type & {
|
|
43
|
+
/**
|
|
44
|
+
* MariaDB extended data type name (e.g. `uuid`, `inet4`, `inet6`, `point`),
|
|
45
|
+
* available when the server supports the MARIADB_CLIENT_EXTENDED_METADATA
|
|
46
|
+
* capability (MariaDB 10.5+)
|
|
47
|
+
*/
|
|
48
|
+
extendedTypeName?: string;
|
|
49
|
+
/**
|
|
50
|
+
* MariaDB extended format name (e.g. `json`), available when the server
|
|
51
|
+
* supports the MARIADB_CLIENT_EXTENDED_METADATA capability (MariaDB 10.5+)
|
|
52
|
+
*/
|
|
53
|
+
extendedFormat?: string;
|
|
43
54
|
length: number;
|
|
44
55
|
db: string;
|
|
45
56
|
table: string;
|
|
@@ -22,6 +22,17 @@ declare interface FieldPacket {
|
|
|
22
22
|
typeName?: string;
|
|
23
23
|
encoding?: string;
|
|
24
24
|
columnLength?: number;
|
|
25
|
+
/**
|
|
26
|
+
* MariaDB extended data type name (e.g. `uuid`, `inet4`, `inet6`, `point`),
|
|
27
|
+
* available when the server supports the MARIADB_CLIENT_EXTENDED_METADATA
|
|
28
|
+
* capability (MariaDB 10.5+)
|
|
29
|
+
*/
|
|
30
|
+
extendedTypeName?: string;
|
|
31
|
+
/**
|
|
32
|
+
* MariaDB extended format name (e.g. `json`), available when the server
|
|
33
|
+
* supports the MARIADB_CLIENT_EXTENDED_METADATA capability (MariaDB 10.5+)
|
|
34
|
+
*/
|
|
35
|
+
extendedFormat?: string;
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
export { FieldPacket };
|