mysql2 2.2.5 → 2.3.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/Changelog.md +17 -0
- package/README.md +26 -0
- package/index.d.ts +7 -4
- package/index.js +3 -0
- package/lib/commands/auth_switch.js +20 -5
- package/lib/commands/client_handshake.js +15 -2
- package/lib/commands/command.js +1 -0
- package/lib/commands/execute.js +5 -0
- package/lib/commands/query.js +42 -0
- package/lib/connection.js +44 -8
- package/lib/connection_config.js +15 -21
- package/lib/constants/errors.js +7 -0
- package/lib/packets/index.js +19 -0
- package/lib/packets/packet.js +10 -0
- package/lib/pool_cluster.js +57 -0
- package/package.json +3 -2
- package/promise.js +113 -0
- package/typings/mysql/lib/Connection.d.ts +9 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +6 -0
package/Changelog.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
2.3.0
|
|
2
|
+
- Add PoolCluster promise wrappers #1369, #1363
|
|
3
|
+
- support for connect and query timeouts #1364
|
|
4
|
+
- add missing query() method on PoolCluster #1362
|
|
5
|
+
- fix incorrect parsing of passwords
|
|
6
|
+
containing ":" #1357
|
|
7
|
+
- handle errors generated by asynchronous
|
|
8
|
+
authentication plugins #1354
|
|
9
|
+
- add proper handshake fatal error handling #1352
|
|
10
|
+
- fix tests to work with the latest MySQL
|
|
11
|
+
server versions (up to 8.0.25) #1338
|
|
12
|
+
- expose SQL query in errors #1295
|
|
13
|
+
- typing and readme docs for rowAsArray #1288
|
|
14
|
+
- allow unnamed placeholders even if the
|
|
15
|
+
namedPlaceholders flag is enabled #1251
|
|
16
|
+
- better ESM support #1217
|
|
17
|
+
|
|
1
18
|
2.2.5 ( 21/09/2020 )
|
|
2
19
|
- typings: add ResultSetHeader #1213
|
|
3
20
|
|
package/README.md
CHANGED
|
@@ -216,6 +216,32 @@ con.promise().query("SELECT 1")
|
|
|
216
216
|
.then( () => con.end());
|
|
217
217
|
```
|
|
218
218
|
|
|
219
|
+
## Array results
|
|
220
|
+
|
|
221
|
+
If you have two columns with the same name, you might want to get results as an array rather than an object to prevent them from clashing. This is a deviation from the [Node MySQL][node-mysql] library.
|
|
222
|
+
|
|
223
|
+
For example: `select 1 as foo, 2 as foo`.
|
|
224
|
+
|
|
225
|
+
You can enable this setting at either the connection level (applies to all queries), or at the query level (applies only to that specific query).
|
|
226
|
+
|
|
227
|
+
### Connection Option
|
|
228
|
+
```js
|
|
229
|
+
const con = mysql.createConnection(
|
|
230
|
+
{ host: 'localhost', database: 'test', user: 'root', rowsAsArray: true }
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Query Option
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err, results, fields) {
|
|
239
|
+
console.log(results) // will be an array of arrays rather than an array of objects
|
|
240
|
+
console.log(fields) // these are unchanged
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
|
|
219
245
|
## API and Configuration
|
|
220
246
|
|
|
221
247
|
MySQL2 is mostly API compatible with [Node MySQL][node-mysql]. You should check their API documentation to see all available API options.
|
package/index.d.ts
CHANGED
|
@@ -151,9 +151,12 @@ export interface Pool extends mysql.Connection {
|
|
|
151
151
|
promise(promiseImpl?: PromiseConstructor): PromisePool;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
type authPlugins =
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
type authPlugins = (pluginMetadata: {
|
|
155
|
+
connection: Connection;
|
|
156
|
+
command: string;
|
|
157
|
+
}) => (
|
|
158
|
+
pluginData: Buffer
|
|
159
|
+
) => Promise<string> | string | Buffer | Promise<Buffer> | null;
|
|
157
160
|
|
|
158
161
|
export interface ConnectionOptions extends mysql.ConnectionOptions {
|
|
159
162
|
charsetNumber?: number;
|
|
@@ -175,7 +178,7 @@ export interface ConnectionOptions extends mysql.ConnectionOptions {
|
|
|
175
178
|
queueLimit?: number;
|
|
176
179
|
waitForConnections?: boolean;
|
|
177
180
|
authPlugins?: {
|
|
178
|
-
|
|
181
|
+
[key: string]: authPlugins;
|
|
179
182
|
};
|
|
180
183
|
}
|
|
181
184
|
|
package/index.js
CHANGED
|
@@ -14,6 +14,7 @@ exports.connect = exports.createConnection;
|
|
|
14
14
|
exports.Connection = Connection;
|
|
15
15
|
|
|
16
16
|
const Pool = require('./lib/pool.js');
|
|
17
|
+
const PoolCluster = require('./lib/pool_cluster.js');
|
|
17
18
|
|
|
18
19
|
exports.createPool = function(config) {
|
|
19
20
|
const PoolConfig = require('./lib/pool_config.js');
|
|
@@ -29,6 +30,8 @@ exports.createQuery = Connection.createQuery;
|
|
|
29
30
|
|
|
30
31
|
exports.Pool = Pool;
|
|
31
32
|
|
|
33
|
+
exports.PoolCluster = PoolCluster;
|
|
34
|
+
|
|
32
35
|
exports.createServer = function(handler) {
|
|
33
36
|
const Server = require('./lib/server.js');
|
|
34
37
|
const s = new Server();
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// This file was modified by Oracle on July 5, 2021.
|
|
2
|
+
// Errors generated by asynchronous authentication plugins are now being
|
|
3
|
+
// handled and subsequently emitted at the command level.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const Packets = require('../packets/index.js');
|
|
@@ -17,6 +22,14 @@ function warnLegacyAuthSwitch() {
|
|
|
17
22
|
);
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
function authSwitchPluginError(error, command) {
|
|
26
|
+
// Authentication errors are fatal
|
|
27
|
+
error.code = 'AUTH_SWITCH_PLUGIN_ERROR';
|
|
28
|
+
error.fatal = true;
|
|
29
|
+
|
|
30
|
+
command.emit('error', error);
|
|
31
|
+
}
|
|
32
|
+
|
|
20
33
|
function authSwitchRequest(packet, connection, command) {
|
|
21
34
|
const { pluginName, pluginData } = Packets.AuthSwitchRequest.fromPacket(
|
|
22
35
|
packet
|
|
@@ -34,8 +47,7 @@ function authSwitchRequest(packet, connection, command) {
|
|
|
34
47
|
warnLegacyAuthSwitch();
|
|
35
48
|
legacySwitchHandler({ pluginName, pluginData }, (err, data) => {
|
|
36
49
|
if (err) {
|
|
37
|
-
|
|
38
|
-
return;
|
|
50
|
+
return authSwitchPluginError(err, command);
|
|
39
51
|
}
|
|
40
52
|
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
|
|
41
53
|
});
|
|
@@ -54,10 +66,12 @@ function authSwitchRequest(packet, connection, command) {
|
|
|
54
66
|
if (data) {
|
|
55
67
|
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
|
|
56
68
|
}
|
|
69
|
+
}).catch(err => {
|
|
70
|
+
authSwitchPluginError(err, command);
|
|
57
71
|
});
|
|
58
72
|
}
|
|
59
73
|
|
|
60
|
-
function authSwitchRequestMoreData(packet, connection) {
|
|
74
|
+
function authSwitchRequestMoreData(packet, connection, command) {
|
|
61
75
|
const { data } = Packets.AuthSwitchRequestMoreData.fromPacket(packet);
|
|
62
76
|
|
|
63
77
|
if (connection.config.authSwitchHandler) {
|
|
@@ -65,8 +79,7 @@ function authSwitchRequestMoreData(packet, connection) {
|
|
|
65
79
|
warnLegacyAuthSwitch();
|
|
66
80
|
legacySwitchHandler({ pluginData: data }, (err, data) => {
|
|
67
81
|
if (err) {
|
|
68
|
-
|
|
69
|
-
return;
|
|
82
|
+
return authSwitchPluginError(err, command);
|
|
70
83
|
}
|
|
71
84
|
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
|
|
72
85
|
});
|
|
@@ -82,6 +95,8 @@ function authSwitchRequestMoreData(packet, connection) {
|
|
|
82
95
|
if (data) {
|
|
83
96
|
connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());
|
|
84
97
|
}
|
|
98
|
+
}).catch(err => {
|
|
99
|
+
authSwitchPluginError(err, command);
|
|
85
100
|
});
|
|
86
101
|
}
|
|
87
102
|
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 17, 2021.
|
|
2
|
+
// Handshake errors are now maked as fatal and the corresponding events are
|
|
3
|
+
// emitted in the command instance itself.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const Command = require('./command.js');
|
|
@@ -151,20 +156,28 @@ class ClientHandshake extends Command {
|
|
|
151
156
|
}
|
|
152
157
|
return ClientHandshake.prototype.handshakeResult;
|
|
153
158
|
} catch (err) {
|
|
159
|
+
// Authentication errors are fatal
|
|
160
|
+
err.code = 'AUTH_SWITCH_PLUGIN_ERROR';
|
|
161
|
+
err.fatal = true;
|
|
162
|
+
|
|
154
163
|
if (this.onResult) {
|
|
155
164
|
this.onResult(err);
|
|
156
165
|
} else {
|
|
157
|
-
|
|
166
|
+
this.emit('error', err);
|
|
158
167
|
}
|
|
159
168
|
return null;
|
|
160
169
|
}
|
|
161
170
|
}
|
|
162
171
|
if (marker !== 0) {
|
|
163
172
|
const err = new Error('Unexpected packet during handshake phase');
|
|
173
|
+
// Unknown handshake errors are fatal
|
|
174
|
+
err.code = 'HANDSHAKE_UNKNOWN_ERROR';
|
|
175
|
+
err.fatal = true;
|
|
176
|
+
|
|
164
177
|
if (this.onResult) {
|
|
165
178
|
this.onResult(err);
|
|
166
179
|
} else {
|
|
167
|
-
|
|
180
|
+
this.emit('error', err);
|
|
168
181
|
}
|
|
169
182
|
return null;
|
|
170
183
|
}
|
package/lib/commands/command.js
CHANGED
package/lib/commands/execute.js
CHANGED
|
@@ -15,6 +15,8 @@ class Execute extends Command {
|
|
|
15
15
|
this.onResult = callback;
|
|
16
16
|
this.parameters = options.values;
|
|
17
17
|
this.insertId = 0;
|
|
18
|
+
this.timeout = options.timeout;
|
|
19
|
+
this.queryTimeout = null;
|
|
18
20
|
this._rows = [];
|
|
19
21
|
this._fields = [];
|
|
20
22
|
this._result = [];
|
|
@@ -35,6 +37,7 @@ class Execute extends Command {
|
|
|
35
37
|
start(packet, connection) {
|
|
36
38
|
this._connection = connection;
|
|
37
39
|
this.options = Object.assign({}, connection.config, this._executeOptions);
|
|
40
|
+
this._setTimeout();
|
|
38
41
|
const executePacket = new Packets.Execute(
|
|
39
42
|
this.statement.id,
|
|
40
43
|
this.parameters,
|
|
@@ -96,6 +99,8 @@ Execute.prototype.resultsetHeader = Query.prototype.resultsetHeader;
|
|
|
96
99
|
Execute.prototype._findOrCreateReadStream =
|
|
97
100
|
Query.prototype._findOrCreateReadStream;
|
|
98
101
|
Execute.prototype._streamLocalInfile = Query.prototype._streamLocalInfile;
|
|
102
|
+
Execute.prototype._setTimeout = Query.prototype._setTimeout;
|
|
103
|
+
Execute.prototype._handleTimeoutError = Query.prototype._handleTimeoutError;
|
|
99
104
|
Execute.prototype.row = Query.prototype.row;
|
|
100
105
|
Execute.prototype.stream = Query.prototype.stream;
|
|
101
106
|
|
package/lib/commands/query.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const process = require('process');
|
|
4
|
+
const Timers = require('timers');
|
|
4
5
|
|
|
5
6
|
const Readable = require('stream').Readable;
|
|
6
7
|
|
|
@@ -21,6 +22,8 @@ class Query extends Command {
|
|
|
21
22
|
this._queryOptions = options;
|
|
22
23
|
this.namedPlaceholders = options.namedPlaceholders || false;
|
|
23
24
|
this.onResult = callback;
|
|
25
|
+
this.timeout = options.timeout;
|
|
26
|
+
this.queryTimeout = null;
|
|
24
27
|
this._fieldCount = 0;
|
|
25
28
|
this._rowParser = null;
|
|
26
29
|
this._fields = [];
|
|
@@ -48,6 +51,8 @@ class Query extends Command {
|
|
|
48
51
|
}
|
|
49
52
|
this._connection = connection;
|
|
50
53
|
this.options = Object.assign({}, connection.config, this._queryOptions);
|
|
54
|
+
this._setTimeout();
|
|
55
|
+
|
|
51
56
|
const cmdPacket = new Packets.Query(
|
|
52
57
|
this.sql,
|
|
53
58
|
connection.config.charsetNumber
|
|
@@ -58,6 +63,15 @@ class Query extends Command {
|
|
|
58
63
|
|
|
59
64
|
done() {
|
|
60
65
|
this._unpipeStream();
|
|
66
|
+
// if all ready timeout, return null directly
|
|
67
|
+
if (this.timeout && !this.queryTimeout) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
// else clear timer
|
|
71
|
+
if (this.queryTimeout) {
|
|
72
|
+
Timers.clearTimeout(this.queryTimeout);
|
|
73
|
+
this.queryTimeout = null;
|
|
74
|
+
}
|
|
61
75
|
if (this.onResult) {
|
|
62
76
|
let rows, fields;
|
|
63
77
|
if (this._resultIndex === 0) {
|
|
@@ -272,6 +286,34 @@ class Query extends Command {
|
|
|
272
286
|
});
|
|
273
287
|
return stream;
|
|
274
288
|
}
|
|
289
|
+
|
|
290
|
+
_setTimeout() {
|
|
291
|
+
if (this.timeout) {
|
|
292
|
+
const timeoutHandler = this._handleTimeoutError.bind(this);
|
|
293
|
+
this.queryTimeout = Timers.setTimeout(
|
|
294
|
+
timeoutHandler,
|
|
295
|
+
this.timeout
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
_handleTimeoutError() {
|
|
301
|
+
if (this.queryTimeout) {
|
|
302
|
+
Timers.clearTimeout(this.queryTimeout);
|
|
303
|
+
this.queryTimeout = null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const err = new Error('Query inactivity timeout');
|
|
307
|
+
err.errorno = 'PROTOCOL_SEQUENCE_TIMEOUT';
|
|
308
|
+
err.code = 'PROTOCOL_SEQUENCE_TIMEOUT';
|
|
309
|
+
err.syscall = 'query';
|
|
310
|
+
|
|
311
|
+
if (this.onResult) {
|
|
312
|
+
this.onResult(err);
|
|
313
|
+
} else {
|
|
314
|
+
this.emit('error', err);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
275
317
|
}
|
|
276
318
|
|
|
277
319
|
Query.prototype.catch = Query.prototype.then;
|
package/lib/connection.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 1, 2021.
|
|
2
|
+
// The changes involve new logic to handle an additional ERR Packet sent by
|
|
3
|
+
// the MySQL server when the connection is closed unexpectedly.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
6
|
+
// This file was modified by Oracle on June 17, 2021.
|
|
7
|
+
// The changes involve logic to ensure the socket connection is closed when
|
|
8
|
+
// there is a fatal error.
|
|
9
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
10
|
+
|
|
1
11
|
'use strict';
|
|
2
12
|
|
|
3
13
|
const Net = require('net');
|
|
@@ -100,9 +110,10 @@ class Connection extends EventEmitter {
|
|
|
100
110
|
if (!this.config.isServer) {
|
|
101
111
|
handshakeCommand = new Commands.ClientHandshake(this.config.clientFlags);
|
|
102
112
|
handshakeCommand.on('end', () => {
|
|
103
|
-
// this happens when handshake finishes early
|
|
104
|
-
//
|
|
105
|
-
|
|
113
|
+
// this happens when handshake finishes early either because there was
|
|
114
|
+
// some fatal error or the server sent an error packet instead of
|
|
115
|
+
// an hello packet (for example, 'Too many connactions' error)
|
|
116
|
+
if (!handshakeCommand.handshake || this._fatalError || this._protocolError) {
|
|
106
117
|
return;
|
|
107
118
|
}
|
|
108
119
|
this._handshakePacket = handshakeCommand.handshake;
|
|
@@ -188,7 +199,7 @@ class Connection extends EventEmitter {
|
|
|
188
199
|
if (this.connectTimeout) {
|
|
189
200
|
Timers.clearTimeout(this.connectTimeout);
|
|
190
201
|
this.connectTimeout = null;
|
|
191
|
-
}
|
|
202
|
+
}
|
|
192
203
|
// prevent from emitting 'PROTOCOL_CONNECTION_LOST' after EPIPE or ECONNRESET
|
|
193
204
|
if (this._fatalError) {
|
|
194
205
|
return;
|
|
@@ -224,6 +235,10 @@ class Connection extends EventEmitter {
|
|
|
224
235
|
if (bubbleErrorToConnection || this._pool) {
|
|
225
236
|
this.emit('error', err);
|
|
226
237
|
}
|
|
238
|
+
// close connection after emitting the event in case of a fatal error
|
|
239
|
+
if (err.fatal) {
|
|
240
|
+
this.close();
|
|
241
|
+
}
|
|
227
242
|
}
|
|
228
243
|
|
|
229
244
|
write(buffer) {
|
|
@@ -368,6 +383,14 @@ class Connection extends EventEmitter {
|
|
|
368
383
|
}
|
|
369
384
|
|
|
370
385
|
protocolError(message, code) {
|
|
386
|
+
// Starting with MySQL 8.0.24, if the client closes the connection
|
|
387
|
+
// unexpectedly, the server will send a last ERR Packet, which we can
|
|
388
|
+
// safely ignore.
|
|
389
|
+
// https://dev.mysql.com/worklog/task/?id=12999
|
|
390
|
+
if (this._closing) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
371
394
|
const err = new Error(message);
|
|
372
395
|
err.fatal = true;
|
|
373
396
|
err.code = code || 'PROTOCOL_ERROR';
|
|
@@ -415,10 +438,18 @@ class Connection extends EventEmitter {
|
|
|
415
438
|
}
|
|
416
439
|
}
|
|
417
440
|
if (!this._command) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
441
|
+
const marker = packet.peekByte();
|
|
442
|
+
// If it's an Err Packet, we should use it.
|
|
443
|
+
if (marker === 0xff) {
|
|
444
|
+
const error = Packets.Error.fromPacket(packet);
|
|
445
|
+
this.protocolError(error.message, error.code);
|
|
446
|
+
} else {
|
|
447
|
+
// Otherwise, it means it's some other unexpected packet.
|
|
448
|
+
this.protocolError(
|
|
449
|
+
'Unexpected packet while no commands in the queue',
|
|
450
|
+
'PROTOCOL_UNEXPECTED_PACKET'
|
|
451
|
+
);
|
|
452
|
+
}
|
|
422
453
|
this.close();
|
|
423
454
|
return;
|
|
424
455
|
}
|
|
@@ -488,6 +519,11 @@ class Connection extends EventEmitter {
|
|
|
488
519
|
_resolveNamedPlaceholders(options) {
|
|
489
520
|
let unnamed;
|
|
490
521
|
if (this.config.namedPlaceholders || options.namedPlaceholders) {
|
|
522
|
+
if (Array.isArray(options.values)) {
|
|
523
|
+
// if an array is provided as the values, assume the conversion is not necessary.
|
|
524
|
+
// this allows the usage of unnamed placeholders even if the namedPlaceholders flag is enabled.
|
|
525
|
+
return
|
|
526
|
+
}
|
|
491
527
|
if (convertNamedPlaceholders === null) {
|
|
492
528
|
convertNamedPlaceholders = require('named-placeholders')();
|
|
493
529
|
}
|
package/lib/connection_config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { URL } = require('url');
|
|
4
4
|
const ClientConstants = require('./constants/client');
|
|
5
5
|
const Charsets = require('./constants/charsets');
|
|
6
6
|
let SSLProfiles = null;
|
|
@@ -232,29 +232,23 @@ class ConnectionConfig {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
static parseUrl(url) {
|
|
235
|
-
|
|
235
|
+
const parsedUrl = new URL(url);
|
|
236
236
|
const options = {
|
|
237
|
-
host:
|
|
238
|
-
port:
|
|
239
|
-
database:
|
|
237
|
+
host: parsedUrl.hostname,
|
|
238
|
+
port: parsedUrl.port,
|
|
239
|
+
database: parsedUrl.pathname.substr(1),
|
|
240
|
+
user: parsedUrl.username,
|
|
241
|
+
password: parsedUrl.password
|
|
240
242
|
};
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const value = url.query[key];
|
|
249
|
-
try {
|
|
250
|
-
// Try to parse this as a JSON expression first
|
|
251
|
-
options[key] = JSON.parse(value);
|
|
252
|
-
} catch (err) {
|
|
253
|
-
// Otherwise assume it is a plain string
|
|
254
|
-
options[key] = value;
|
|
255
|
-
}
|
|
243
|
+
parsedUrl.searchParams.forEach((value, key) => {
|
|
244
|
+
try {
|
|
245
|
+
// Try to parse this as a JSON expression first
|
|
246
|
+
options[key] = JSON.parse(value);
|
|
247
|
+
} catch (err) {
|
|
248
|
+
// Otherwise assume it is a plain string
|
|
249
|
+
options[key] = value;
|
|
256
250
|
}
|
|
257
|
-
}
|
|
251
|
+
});
|
|
258
252
|
return options;
|
|
259
253
|
}
|
|
260
254
|
}
|
package/lib/constants/errors.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 1, 2021.
|
|
2
|
+
// An entry was created for a new error reported by the MySQL server due to
|
|
3
|
+
// client inactivity.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
// copy from https://raw.githubusercontent.com/mysqljs/mysql/7770ee5bb13260c56a160b91fe480d9165dbeeba/lib/protocol/constants/errors.js
|
|
@@ -994,6 +999,7 @@ exports.ER_INNODB_FT_AUX_NOT_HEX_ID = 1879;
|
|
|
994
999
|
exports.ER_OLD_TEMPORALS_UPGRADED = 1880;
|
|
995
1000
|
exports.ER_INNODB_FORCED_RECOVERY = 1881;
|
|
996
1001
|
exports.ER_AES_INVALID_IV = 1882;
|
|
1002
|
+
exports.ER_CLIENT_INTERACTION_TIMEOUT = 4031;
|
|
997
1003
|
|
|
998
1004
|
// Lookup-by-number table
|
|
999
1005
|
exports[1] = 'EE_CANTCREATEFILE';
|
|
@@ -1982,3 +1988,4 @@ exports[1879] = 'ER_INNODB_FT_AUX_NOT_HEX_ID';
|
|
|
1982
1988
|
exports[1880] = 'ER_OLD_TEMPORALS_UPGRADED';
|
|
1983
1989
|
exports[1881] = 'ER_INNODB_FORCED_RECOVERY';
|
|
1984
1990
|
exports[1882] = 'ER_AES_INVALID_IV';
|
|
1991
|
+
exports[4031] = 'ER_CLIENT_INTERACTION_TIMEOUT';
|
package/lib/packets/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 1, 2021.
|
|
2
|
+
// A utility method was introduced to generate an Error instance from a
|
|
3
|
+
// binary server packet.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const process = require('process');
|
|
@@ -122,6 +127,20 @@ class Error {
|
|
|
122
127
|
packet._name = 'Error';
|
|
123
128
|
return packet;
|
|
124
129
|
}
|
|
130
|
+
|
|
131
|
+
static fromPacket(packet) {
|
|
132
|
+
packet.readInt8(); // marker
|
|
133
|
+
const code = packet.readInt16();
|
|
134
|
+
packet.readString(1, 'ascii'); // sql state marker
|
|
135
|
+
// The SQL state of the ERR_Packet which is always 5 bytes long.
|
|
136
|
+
// https://dev.mysql.com/doc/dev/mysql-server/8.0.11/page_protocol_basic_dt_strings.html#sect_protocol_basic_dt_string_fix
|
|
137
|
+
packet.readString(5, 'ascii'); // sql state (ignore for now)
|
|
138
|
+
const message = packet.readNullTerminatedString('utf8');
|
|
139
|
+
const error = new Error();
|
|
140
|
+
error.message = message;
|
|
141
|
+
error.code = code;
|
|
142
|
+
return error;
|
|
143
|
+
}
|
|
125
144
|
}
|
|
126
145
|
|
|
127
146
|
exports.Error = Error;
|
package/lib/packets/packet.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// This file was modified by Oracle on June 1, 2021.
|
|
2
|
+
// A comment describing some changes in the strict default SQL mode regarding
|
|
3
|
+
// non-standard dates was introduced.
|
|
4
|
+
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
|
|
5
|
+
|
|
1
6
|
'use strict';
|
|
2
7
|
|
|
3
8
|
const ErrorCodeToName = require('../constants/errors.js');
|
|
@@ -274,6 +279,11 @@ class Packet {
|
|
|
274
279
|
if (length > 10) {
|
|
275
280
|
ms = this.readInt32() / 1000;
|
|
276
281
|
}
|
|
282
|
+
// NO_ZERO_DATE mode and NO_ZERO_IN_DATE mode are part of the strict
|
|
283
|
+
// default SQL mode used by MySQL 8.0. This means that non-standard
|
|
284
|
+
// dates like '0000-00-00' become NULL. For older versions and other
|
|
285
|
+
// possible MySQL flavours we still need to account for the
|
|
286
|
+
// non-standard behaviour.
|
|
277
287
|
if (y + m + d + H + M + S + ms === 0) {
|
|
278
288
|
return INVALID_DATE;
|
|
279
289
|
}
|
package/lib/pool_cluster.js
CHANGED
|
@@ -4,6 +4,7 @@ const process = require('process');
|
|
|
4
4
|
|
|
5
5
|
const Pool = require('./pool.js');
|
|
6
6
|
const PoolConfig = require('./pool_config.js');
|
|
7
|
+
const Connection = require('./connection.js');
|
|
7
8
|
const EventEmitter = require('events').EventEmitter;
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -46,6 +47,62 @@ class PoolNamespace {
|
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* pool cluster query
|
|
52
|
+
* @param {*} sql
|
|
53
|
+
* @param {*} values
|
|
54
|
+
* @param {*} cb
|
|
55
|
+
* @returns query
|
|
56
|
+
*/
|
|
57
|
+
query(sql, values, cb) {
|
|
58
|
+
const query = Connection.createQuery(sql, values, cb, {});
|
|
59
|
+
this.getConnection((err, conn) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
if (typeof query.onResult === 'function') {
|
|
62
|
+
query.onResult(err);
|
|
63
|
+
} else {
|
|
64
|
+
query.emit('error', err);
|
|
65
|
+
}
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
conn.query(query).once('end', () => {
|
|
70
|
+
conn.release();
|
|
71
|
+
});
|
|
72
|
+
} catch (e) {
|
|
73
|
+
conn.release();
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return query;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* pool cluster execute
|
|
82
|
+
* @param {*} sql
|
|
83
|
+
* @param {*} values
|
|
84
|
+
* @param {*} cb
|
|
85
|
+
*/
|
|
86
|
+
execute(sql, values, cb) {
|
|
87
|
+
if (typeof values === 'function') {
|
|
88
|
+
cb = values;
|
|
89
|
+
values = [];
|
|
90
|
+
}
|
|
91
|
+
this.getConnection((err, conn) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
return cb(err);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
conn.execute(sql, values, cb).once('end', () => {
|
|
97
|
+
conn.release();
|
|
98
|
+
});
|
|
99
|
+
} catch (e) {
|
|
100
|
+
conn.release();
|
|
101
|
+
throw e;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
49
106
|
_getClusterNode() {
|
|
50
107
|
const foundNodeIds = this._cluster._findNodeIds(this._pattern);
|
|
51
108
|
if (foundNodeIds.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql2",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
],
|
|
45
45
|
"exports": {
|
|
46
46
|
".": "./index.js",
|
|
47
|
-
"./promise": "./promise.js"
|
|
47
|
+
"./promise": "./promise.js",
|
|
48
|
+
"./promise.js": "./promise.js"
|
|
48
49
|
},
|
|
49
50
|
"engines": {
|
|
50
51
|
"node": ">= 8.0"
|
package/promise.js
CHANGED
|
@@ -9,6 +9,7 @@ function makeDoneCb(resolve, reject, localErr) {
|
|
|
9
9
|
localErr.message = err.message;
|
|
10
10
|
localErr.code = err.code;
|
|
11
11
|
localErr.errno = err.errno;
|
|
12
|
+
localErr.sql = err.sql;
|
|
12
13
|
localErr.sqlState = err.sqlState;
|
|
13
14
|
localErr.sqlMessage = err.sqlMessage;
|
|
14
15
|
reject(localErr);
|
|
@@ -427,8 +428,120 @@ function createPool(opts) {
|
|
|
427
428
|
'format'
|
|
428
429
|
]);
|
|
429
430
|
|
|
431
|
+
class PromisePoolCluster extends EventEmitter {
|
|
432
|
+
constructor(poolCluster, thePromise) {
|
|
433
|
+
super();
|
|
434
|
+
this.poolCluster = poolCluster;
|
|
435
|
+
this.Promise = thePromise || Promise;
|
|
436
|
+
inheritEvents(poolCluster, this, ['acquire', 'connection', 'enqueue', 'release']);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
getConnection() {
|
|
440
|
+
const corePoolCluster = this.poolCluster;
|
|
441
|
+
return new this.Promise((resolve, reject) => {
|
|
442
|
+
corePoolCluster.getConnection((err, coreConnection) => {
|
|
443
|
+
if (err) {
|
|
444
|
+
reject(err);
|
|
445
|
+
} else {
|
|
446
|
+
resolve(new PromisePoolConnection(coreConnection, this.Promise));
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
query(sql, args) {
|
|
453
|
+
const corePoolCluster = this.poolCluster;
|
|
454
|
+
const localErr = new Error();
|
|
455
|
+
if (typeof args === 'function') {
|
|
456
|
+
throw new Error(
|
|
457
|
+
'Callback function is not available with promise clients.'
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
return new this.Promise((resolve, reject) => {
|
|
461
|
+
const done = makeDoneCb(resolve, reject, localErr);
|
|
462
|
+
corePoolCluster.query(sql, args, done);
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
execute(sql, args) {
|
|
467
|
+
const corePoolCluster = this.poolCluster;
|
|
468
|
+
const localErr = new Error();
|
|
469
|
+
if (typeof args === 'function') {
|
|
470
|
+
throw new Error(
|
|
471
|
+
'Callback function is not available with promise clients.'
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
return new this.Promise((resolve, reject) => {
|
|
475
|
+
const done = makeDoneCb(resolve, reject, localErr);
|
|
476
|
+
corePoolCluster.execute(sql, args, done);
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
of(pattern, selector) {
|
|
481
|
+
return new PromisePoolCluster(
|
|
482
|
+
this.poolCluster.of(pattern, selector),
|
|
483
|
+
this.Promise
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
end() {
|
|
488
|
+
const corePoolCluster = this.poolCluster;
|
|
489
|
+
const localErr = new Error();
|
|
490
|
+
return new this.Promise((resolve, reject) => {
|
|
491
|
+
corePoolCluster.end(err => {
|
|
492
|
+
if (err) {
|
|
493
|
+
localErr.message = err.message;
|
|
494
|
+
localErr.code = err.code;
|
|
495
|
+
localErr.errno = err.errno;
|
|
496
|
+
localErr.sqlState = err.sqlState;
|
|
497
|
+
localErr.sqlMessage = err.sqlMessage;
|
|
498
|
+
reject(localErr);
|
|
499
|
+
} else {
|
|
500
|
+
resolve();
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* proxy poolCluster synchronous functions
|
|
509
|
+
*/
|
|
510
|
+
(function (functionsToWrap) {
|
|
511
|
+
for (let i = 0; functionsToWrap && i < functionsToWrap.length; i++) {
|
|
512
|
+
const func = functionsToWrap[i];
|
|
513
|
+
|
|
514
|
+
if (
|
|
515
|
+
typeof core.PoolCluster.prototype[func] === 'function' &&
|
|
516
|
+
PromisePoolCluster.prototype[func] === undefined
|
|
517
|
+
) {
|
|
518
|
+
PromisePoolCluster.prototype[func] = (function factory(funcName) {
|
|
519
|
+
return function () {
|
|
520
|
+
return core.PoolCluster.prototype[funcName].apply(this.poolCluster, arguments);
|
|
521
|
+
};
|
|
522
|
+
})(func);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
})([
|
|
526
|
+
'add'
|
|
527
|
+
]);
|
|
528
|
+
|
|
529
|
+
function createPoolCluster(opts) {
|
|
530
|
+
const corePoolCluster = core.createPoolCluster(opts);
|
|
531
|
+
const thePromise = (opts && opts.Promise) || Promise;
|
|
532
|
+
if (!thePromise) {
|
|
533
|
+
throw new Error(
|
|
534
|
+
'no Promise implementation available.' +
|
|
535
|
+
'Use promise-enabled node version or pass userland Promise' +
|
|
536
|
+
" implementation as parameter, for example: { Promise: require('bluebird') }"
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
return new PromisePoolCluster(corePoolCluster, thePromise);
|
|
540
|
+
}
|
|
541
|
+
|
|
430
542
|
exports.createConnection = createConnection;
|
|
431
543
|
exports.createPool = createPool;
|
|
544
|
+
exports.createPoolCluster = createPoolCluster;
|
|
432
545
|
exports.escape = core.escape;
|
|
433
546
|
exports.escapeId = core.escapeId;
|
|
434
547
|
exports.format = core.format;
|
|
@@ -147,6 +147,14 @@ declare namespace Connection {
|
|
|
147
147
|
* object with ssl parameters or a string containing name of ssl profile
|
|
148
148
|
*/
|
|
149
149
|
ssl?: string | SslOptions;
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Return each row as an array, not as an object.
|
|
154
|
+
* This is useful when you have duplicate column names.
|
|
155
|
+
* This can also be set in the `QueryOption` object to be applied per-query.
|
|
156
|
+
*/
|
|
157
|
+
rowsAsArray?: boolean
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
export interface SslOptions {
|
|
@@ -196,6 +204,7 @@ declare class Connection extends EventEmitter {
|
|
|
196
204
|
|
|
197
205
|
config: Connection.ConnectionOptions;
|
|
198
206
|
threadId: number;
|
|
207
|
+
authorized: boolean;
|
|
199
208
|
|
|
200
209
|
static createQuery<T extends RowDataPacket[][] | RowDataPacket[] | OkPacket | OkPacket[] | ResultSetHeader>(sql: string, callback?: (err: Query.QueryError | null, result: T, fields: FieldPacket[]) => any): Query;
|
|
201
210
|
static createQuery<T extends RowDataPacket[][] | RowDataPacket[] | OkPacket | OkPacket[] | ResultSetHeader>(sql: string, values: any | any[] | { [param: string]: any }, callback?: (err: Query.QueryError | null, result: T, fields: FieldPacket[]) => any): Query;
|
|
@@ -51,6 +51,12 @@ declare namespace Query {
|
|
|
51
51
|
* You can find which field function you need to use by looking at: RowDataPacket.prototype._typeCast
|
|
52
52
|
*/
|
|
53
53
|
typeCast?: any;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* This overrides the same option set at the connection level.
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
rowsAsArray?: boolean
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export interface StreamOptions {
|