@depup/mysql2 3.21.1-depup.0 → 3.23.1-depup.0
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/README.md +9 -3
- package/changes.json +8 -3
- package/lib/base/connection.js +8 -2
- package/lib/base/pool.js +35 -1
- package/lib/commands/auth_switch.js +22 -0
- package/lib/commands/client_handshake.js +32 -5
- package/lib/commands/execute.js +5 -3
- package/lib/commands/index.js +2 -0
- package/lib/commands/ping.js +1 -1
- package/lib/commands/prepare.js +10 -2
- package/lib/commands/query.js +2 -1
- package/lib/commands/reset_connection.js +29 -0
- package/lib/compressed_protocol.js +1 -1
- package/lib/connection_config.js +3 -0
- package/lib/constants/charset_encodings.js +25 -1
- package/lib/constants/commands.js +1 -0
- package/lib/constants/mariadb_client.js +19 -0
- package/lib/packets/binary_row.js +2 -2
- package/lib/packets/binlog_query_statusvars.js +1 -1
- 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/index.js +2 -0
- package/lib/packets/packet.js +51 -3
- package/lib/packets/reset_connection.js +17 -0
- package/lib/packets/resultset_header.js +1 -1
- package/lib/packets/ssl_request.js +6 -1
- package/lib/packets/text_row.js +1 -1
- package/lib/parsers/binary_parser.js +45 -6
- package/lib/parsers/parser_cache.js +3 -0
- package/lib/parsers/static_binary_parser.js +37 -2
- package/lib/parsers/static_text_parser.js +21 -2
- package/lib/parsers/text_parser.js +15 -9
- package/lib/pool_config.js +4 -0
- package/lib/promise/capture_local_err.js +25 -0
- package/lib/promise/connection.js +47 -38
- package/lib/promise/make_done_cb.js +5 -8
- package/lib/promise/pool.js +11 -11
- package/lib/promise/pool_cluster.js +9 -4
- package/lib/promise/prepared_statement_info.js +5 -2
- package/package.json +14 -9
- package/promise.d.ts +2 -0
- package/promise.js +16 -17
- package/typings/mysql/lib/Connection.d.ts +21 -1
- package/typings/mysql/lib/Pool.d.ts +7 -0
- package/typings/mysql/lib/parsers/typeCast.d.ts +11 -0
- package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +11 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +7 -1
package/README.md
CHANGED
|
@@ -13,10 +13,16 @@ npm install @depup/mysql2
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [mysql2](https://www.npmjs.com/package/mysql2) @ 3.
|
|
17
|
-
| Processed | 2026-
|
|
16
|
+
| Original | [mysql2](https://www.npmjs.com/package/mysql2) @ 3.23.1 |
|
|
17
|
+
| Processed | 2026-07-21 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
|
-
| Deps updated |
|
|
19
|
+
| Deps updated | 1 |
|
|
20
|
+
|
|
21
|
+
## Dependency Changes
|
|
22
|
+
|
|
23
|
+
| Dependency | From | To |
|
|
24
|
+
|------------|------|-----|
|
|
25
|
+
| iconv-lite | ^0.7.2 | ^0.7.3 |
|
|
20
26
|
|
|
21
27
|
---
|
|
22
28
|
|
package/changes.json
CHANGED
package/lib/base/connection.js
CHANGED
|
@@ -87,6 +87,8 @@ class BaseConnection extends EventEmitter {
|
|
|
87
87
|
},
|
|
88
88
|
});
|
|
89
89
|
this.serverCapabilityFlags = 0;
|
|
90
|
+
this._isMariaDB = false;
|
|
91
|
+
this._mariadbExtendedMetadata = false;
|
|
90
92
|
this.authorized = false;
|
|
91
93
|
this.sequenceId = 0;
|
|
92
94
|
this.compressedSequenceId = 0;
|
|
@@ -186,7 +188,7 @@ class BaseConnection extends EventEmitter {
|
|
|
186
188
|
});
|
|
187
189
|
}
|
|
188
190
|
}
|
|
189
|
-
// in case there was no initial handshake but we need to read
|
|
191
|
+
// in case there was no initial handshake but we need to read string, assume it utf-8
|
|
190
192
|
// most common example: "Too many connections" error ( packet is sent immediately on connection attempt, we don't know server encoding yet)
|
|
191
193
|
// will be overwritten with actual encoding value as soon as server handshake packet is received
|
|
192
194
|
this.serverEncoding = 'utf8';
|
|
@@ -285,7 +287,7 @@ class BaseConnection extends EventEmitter {
|
|
|
285
287
|
bubbleErrorToConnection = true;
|
|
286
288
|
}
|
|
287
289
|
}
|
|
288
|
-
// notify connection if some
|
|
290
|
+
// notify connection if some commands in the queue did not have callbacks
|
|
289
291
|
// or if this is pool connection ( so it can be removed from pool )
|
|
290
292
|
if (bubbleErrorToConnection || this._pool) {
|
|
291
293
|
this.emit('error', err);
|
|
@@ -928,6 +930,10 @@ class BaseConnection extends EventEmitter {
|
|
|
928
930
|
return this.addCommand(new Commands.Ping(cb));
|
|
929
931
|
}
|
|
930
932
|
|
|
933
|
+
reset(cb) {
|
|
934
|
+
return this.addCommand(new Commands.ResetConnection(cb));
|
|
935
|
+
}
|
|
936
|
+
|
|
931
937
|
_registerSlave(opts, cb) {
|
|
932
938
|
return this.addCommand(new Commands.RegisterSlave(opts, cb));
|
|
933
939
|
}
|
package/lib/base/pool.js
CHANGED
|
@@ -127,7 +127,37 @@ class BasePool extends EventEmitter {
|
|
|
127
127
|
cb = this._connectionQueue.shift();
|
|
128
128
|
process.nextTick(this.getConnection.bind(this, cb));
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Reset connection state if configured
|
|
134
|
+
if (this.config.resetOnRelease && connection.reset) {
|
|
135
|
+
connection.reset((err) => {
|
|
136
|
+
if (err) {
|
|
137
|
+
// If reset fails, remove connection from pool
|
|
138
|
+
connection._pool = null;
|
|
139
|
+
spliceConnection(this._allConnections, connection);
|
|
140
|
+
connection.destroy();
|
|
141
|
+
|
|
142
|
+
// Try to create a new connection for waiting callbacks
|
|
143
|
+
if (this._connectionQueue.length) {
|
|
144
|
+
cb = this._connectionQueue.shift();
|
|
145
|
+
process.nextTick(this.getConnection.bind(this, cb));
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Reset successful, continue with normal release flow
|
|
151
|
+
this._handleSuccessfulRelease(connection);
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
this._handleSuccessfulRelease(connection);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_handleSuccessfulRelease(connection) {
|
|
159
|
+
let cb;
|
|
160
|
+
if (this._connectionQueue.length) {
|
|
131
161
|
cb = this._connectionQueue.shift();
|
|
132
162
|
process.nextTick(() => {
|
|
133
163
|
connection._released = false;
|
|
@@ -155,6 +185,10 @@ class BasePool extends EventEmitter {
|
|
|
155
185
|
}
|
|
156
186
|
};
|
|
157
187
|
}
|
|
188
|
+
while (this._connectionQueue.length > 0) {
|
|
189
|
+
const queuedCallback = this._connectionQueue.shift();
|
|
190
|
+
process.nextTick(() => queuedCallback(new Error('Pool is closed.')));
|
|
191
|
+
}
|
|
158
192
|
let calledBack = false;
|
|
159
193
|
let closedConnections = 0;
|
|
160
194
|
let connection;
|
|
@@ -72,6 +72,28 @@ function authSwitchRequest(packet, connection, command) {
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
if (pluginName === 'mysql_clear_password') {
|
|
76
|
+
const hasCustomPlugin =
|
|
77
|
+
connection.config.authPlugins &&
|
|
78
|
+
Object.prototype.hasOwnProperty.call(
|
|
79
|
+
connection.config.authPlugins,
|
|
80
|
+
'mysql_clear_password'
|
|
81
|
+
);
|
|
82
|
+
if (!hasCustomPlugin && !connection.config.enableCleartextPlugin) {
|
|
83
|
+
const err = new Error(
|
|
84
|
+
'Server requested authentication using mysql_clear_password, ' +
|
|
85
|
+
'which sends the password in plaintext over the network and is ' +
|
|
86
|
+
'disabled by default. To enable it, set the `enableCleartextPlugin` ' +
|
|
87
|
+
'option to `true` in your connection configuration, or provide a ' +
|
|
88
|
+
'custom `mysql_clear_password` auth plugin via the `authPlugins` ' +
|
|
89
|
+
'option. Only use this over a secure connection (TLS/SSL).'
|
|
90
|
+
);
|
|
91
|
+
err.code = 'MYSQL_CLEAR_PASSWORD_NOT_ENABLED';
|
|
92
|
+
err.fatal = true;
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
75
97
|
const authPlugin = getAuthPlugin(pluginName, connection);
|
|
76
98
|
if (!authPlugin) {
|
|
77
99
|
throw new Error(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// This file was modified by Oracle on June 17, 2021.
|
|
2
|
-
// Handshake errors are now
|
|
2
|
+
// Handshake errors are now marked as fatal and the corresponding events are
|
|
3
3
|
// emitted in the command instance itself.
|
|
4
4
|
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
5
|
|
|
@@ -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
|
}
|
|
@@ -105,7 +108,9 @@ class ClientHandshake extends Command {
|
|
|
105
108
|
const canUseDirectAuth =
|
|
106
109
|
!hasCustomAuthPlugin &&
|
|
107
110
|
!hasLegacyAuthSwitchHandler &&
|
|
108
|
-
this.canUseAuthMethodDirectly(serverAuthMethod, isSecureConnection)
|
|
111
|
+
this.canUseAuthMethodDirectly(serverAuthMethod, isSecureConnection) &&
|
|
112
|
+
(serverAuthMethod !== 'mysql_clear_password' ||
|
|
113
|
+
connection.config.enableCleartextPlugin);
|
|
109
114
|
|
|
110
115
|
const clientAuthMethod = canUseDirectAuth
|
|
111
116
|
? serverAuthMethod
|
|
@@ -128,6 +133,7 @@ class ClientHandshake extends Command {
|
|
|
128
133
|
|
|
129
134
|
const handshakeResponse = new Packets.HandshakeResponse({
|
|
130
135
|
flags: this.clientFlags,
|
|
136
|
+
mariadbExtendedClientFlags: this.mariadbExtendedClientFlags,
|
|
131
137
|
user: this.user,
|
|
132
138
|
database: this.database,
|
|
133
139
|
password: this.password,
|
|
@@ -263,6 +269,26 @@ class ClientHandshake extends Command {
|
|
|
263
269
|
connection.serverCapabilityFlags = this.handshake.capabilityFlags;
|
|
264
270
|
connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
|
|
265
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
|
+
);
|
|
266
292
|
const serverSSLSupport =
|
|
267
293
|
this.handshake.capabilityFlags & ClientConstants.SSL;
|
|
268
294
|
// multi factor authentication is enabled with the
|
|
@@ -339,8 +365,9 @@ class ClientHandshake extends Command {
|
|
|
339
365
|
}
|
|
340
366
|
return ClientHandshake.prototype.handshakeResult;
|
|
341
367
|
} catch (err) {
|
|
342
|
-
|
|
343
|
-
|
|
368
|
+
if (!err.code) {
|
|
369
|
+
err.code = 'AUTH_SWITCH_PLUGIN_ERROR';
|
|
370
|
+
}
|
|
344
371
|
err.fatal = true;
|
|
345
372
|
|
|
346
373
|
if (this.onResult) {
|
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
|
|
@@ -72,12 +73,13 @@ class Execute extends Command {
|
|
|
72
73
|
// disabling for now, but would be great to find reliable way to parse fields only once
|
|
73
74
|
// fields reported by prepare can be empty at all or just incorrect - see #169
|
|
74
75
|
//
|
|
75
|
-
//
|
|
76
|
+
// performance optimisation: if we already have this field parsed in statement header, use one from header
|
|
76
77
|
// const field = this.statement.columns.length == this._fieldCount ?
|
|
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/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const Ping = require('./ping.js');
|
|
|
10
10
|
const RegisterSlave = require('./register_slave.js');
|
|
11
11
|
const BinlogDump = require('./binlog_dump.js');
|
|
12
12
|
const ChangeUser = require('./change_user.js');
|
|
13
|
+
const ResetConnection = require('./reset_connection.js');
|
|
13
14
|
const Quit = require('./quit.js');
|
|
14
15
|
|
|
15
16
|
module.exports = {
|
|
@@ -23,5 +24,6 @@ module.exports = {
|
|
|
23
24
|
RegisterSlave,
|
|
24
25
|
BinlogDump,
|
|
25
26
|
ChangeUser,
|
|
27
|
+
ResetConnection,
|
|
26
28
|
Quit,
|
|
27
29
|
};
|
package/lib/commands/ping.js
CHANGED
|
@@ -5,7 +5,7 @@ const CommandCode = require('../constants/commands');
|
|
|
5
5
|
const Packet = require('../packets/packet');
|
|
6
6
|
|
|
7
7
|
// TODO: time statistics?
|
|
8
|
-
//
|
|
8
|
+
// useful for queue size and network latency monitoring
|
|
9
9
|
// store created,sent,reply timestamps
|
|
10
10
|
class Ping extends Command {
|
|
11
11
|
constructor(callback) {
|
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) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command');
|
|
4
|
+
const Packets = require('../packets/index.js');
|
|
5
|
+
|
|
6
|
+
class ResetConnection extends Command {
|
|
7
|
+
constructor(callback) {
|
|
8
|
+
super();
|
|
9
|
+
this.onResult = callback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
start(packet, connection) {
|
|
13
|
+
const req = new Packets.ResetConnection();
|
|
14
|
+
connection.writePacket(req.toPacket());
|
|
15
|
+
return ResetConnection.prototype.resetConnectionResponse;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
resetConnectionResponse(packet, connection) {
|
|
19
|
+
if (connection._statements) {
|
|
20
|
+
connection._statements.clear();
|
|
21
|
+
}
|
|
22
|
+
if (this.onResult) {
|
|
23
|
+
process.nextTick(this.onResult.bind(this, null));
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = ResetConnection;
|
|
@@ -40,7 +40,7 @@ function handleCompressedPacket(packet) {
|
|
|
40
40
|
|
|
41
41
|
if (deflatedLength !== 0) {
|
|
42
42
|
connection.inflateQueue.push((task) => {
|
|
43
|
-
zlib.inflate(body, (err, data) => {
|
|
43
|
+
zlib.inflate(body, { maxOutputLength: deflatedLength }, (err, data) => {
|
|
44
44
|
if (err) {
|
|
45
45
|
connection._handleNetworkError(err);
|
|
46
46
|
return;
|
package/lib/connection_config.js
CHANGED
|
@@ -60,12 +60,14 @@ const validOptions = {
|
|
|
60
60
|
uri: 1,
|
|
61
61
|
user: 1,
|
|
62
62
|
disableEval: 1,
|
|
63
|
+
enableCleartextPlugin: 1,
|
|
63
64
|
// These options are used for Pool
|
|
64
65
|
connectionLimit: 1,
|
|
65
66
|
maxIdle: 1,
|
|
66
67
|
idleTimeout: 1,
|
|
67
68
|
Promise: 1,
|
|
68
69
|
queueLimit: 1,
|
|
70
|
+
resetOnRelease: 1,
|
|
69
71
|
waitForConnections: 1,
|
|
70
72
|
jsonStrings: 1,
|
|
71
73
|
gracefulEnd: 1,
|
|
@@ -152,6 +154,7 @@ class ConnectionConfig {
|
|
|
152
154
|
options.nestTables === undefined ? undefined : options.nestTables;
|
|
153
155
|
this.typeCast = options.typeCast === undefined ? true : options.typeCast;
|
|
154
156
|
this.disableEval = Boolean(options.disableEval);
|
|
157
|
+
this.enableCleartextPlugin = Boolean(options.enableCleartextPlugin);
|
|
155
158
|
if (this.timezone[0] === ' ') {
|
|
156
159
|
// "+" is a url encoded char for space so it
|
|
157
160
|
// gets translated to space when giving a
|
|
@@ -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;
|
|
@@ -12,7 +12,7 @@ class BinaryRow {
|
|
|
12
12
|
|
|
13
13
|
static toPacket(columns, encoding) {
|
|
14
14
|
// throw new Error('Not implemented');
|
|
15
|
-
const sequenceId = 0; // TODO remove, this is calculated now in
|
|
15
|
+
const sequenceId = 0; // TODO remove, this is calculated now in connection
|
|
16
16
|
let length = 0;
|
|
17
17
|
columns.forEach((val) => {
|
|
18
18
|
if (val === null || typeof val === 'undefined') {
|
|
@@ -82,7 +82,7 @@ binaryReader[2] = Packet.prototype.readInt16; // short
|
|
|
82
82
|
binaryReader[3] = Packet.prototype.readInt32; // long
|
|
83
83
|
binaryReader[4] = Packet.prototype.readFloat; // float
|
|
84
84
|
binaryReader[5] = Packet.prototype.readDouble; // double
|
|
85
|
-
binaryReader[6] = Packet.prototype.assertInvalid; // null, should be skipped
|
|
85
|
+
binaryReader[6] = Packet.prototype.assertInvalid; // null, should be skipped via null bitmap
|
|
86
86
|
binaryReader[7] = Packet.prototype.readTimestamp; // timestamp, http://dev.mysql.com/doc/internals/en/prepared-statements.html#packet-ProtocolBinary::MYSQL_TYPE_TIMESTAMP
|
|
87
87
|
binaryReader[8] = Packet.prototype.readInt64; // long long
|
|
88
88
|
binaryReader[9] = Packet.prototype.readInt32; // int24
|
|
@@ -31,7 +31,7 @@ module.exports = function parseStatusVars(buffer) {
|
|
|
31
31
|
offset += 4;
|
|
32
32
|
break;
|
|
33
33
|
case keys.SQL_MODE:
|
|
34
|
-
// value is 8 bytes, but all
|
|
34
|
+
// value is 8 bytes, but all documented flags are in first 4 bytes
|
|
35
35
|
result.sqlMode = buffer.readUInt32LE(offset);
|
|
36
36
|
offset += 8;
|
|
37
37
|
break;
|
|
@@ -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 =
|