mysql2 2.3.3 → 3.0.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.
Files changed (42) hide show
  1. package/README.md +6 -2
  2. package/index.d.ts +23 -9
  3. package/index.js +2 -0
  4. package/lib/auth_plugins/index.js +8 -0
  5. package/lib/auth_plugins/mysql_clear_password.js +17 -0
  6. package/lib/auth_plugins/sha256_password.js +2 -2
  7. package/lib/commands/auth_switch.js +3 -1
  8. package/lib/commands/change_user.js +19 -0
  9. package/lib/commands/client_handshake.js +40 -1
  10. package/lib/commands/prepare.js +2 -1
  11. package/lib/commands/quit.js +3 -3
  12. package/lib/commands/server_handshake.js +36 -1
  13. package/lib/connection.js +56 -11
  14. package/lib/connection_config.js +20 -3
  15. package/lib/constants/charset_encodings.js +4 -1
  16. package/lib/constants/charsets.js +41 -13
  17. package/lib/constants/client.js +8 -0
  18. package/lib/constants/ssl_profiles.js +274 -0
  19. package/lib/constants/types.js +60 -28
  20. package/lib/helpers.js +3 -1
  21. package/lib/packets/auth_next_factor.js +35 -0
  22. package/lib/packets/binary_row.js +49 -2
  23. package/lib/packets/column_definition.js +2 -0
  24. package/lib/packets/execute.js +62 -0
  25. package/lib/packets/index.js +6 -0
  26. package/lib/parsers/parser_cache.js +1 -1
  27. package/lib/parsers/text_parser.js +11 -4
  28. package/lib/pool.js +1 -1
  29. package/package.json +21 -14
  30. package/promise.d.ts +22 -2
  31. package/promise.js +12 -2
  32. package/typings/mysql/index.d.ts +43 -4
  33. package/typings/mysql/lib/Connection.d.ts +52 -8
  34. package/typings/mysql/lib/Pool.d.ts +4 -2
  35. package/typings/mysql/lib/PoolCluster.d.ts +3 -3
  36. package/typings/mysql/lib/PoolConnection.d.ts +1 -0
  37. package/typings/mysql/lib/Server.d.ts +13 -0
  38. package/typings/mysql/lib/protocol/packets/index.d.ts +5 -1
  39. package/typings/mysql/lib/protocol/packets/params/ErrorPacketParams.d.ts +6 -0
  40. package/typings/mysql/lib/protocol/packets/params/OkPacketParams.d.ts +9 -0
  41. package/typings/mysql/lib/protocol/sequences/Prepare.d.ts +52 -0
  42. package/typings/mysql/lib/protocol/sequences/Query.d.ts +12 -2
package/README.md CHANGED
@@ -6,7 +6,9 @@
6
6
  [![Node.js Version][node-version-image]][node-version-url]
7
7
  [![Linux Build][travis-image]][travis-url]
8
8
  [![Windows Build][appveyor-image]][appveyor-url]
9
- [![License][license-image]][license-url]
9
+ [![License][license-image]][license-url]
10
+
11
+ English | [简体中文](./documentation_zh-cn/)
10
12
 
11
13
  > MySQL client for Node.js with focus on performance. Supports prepared statements, non-utf8 encodings, binary log protocol, compression, ssl [much more](https://github.com/sidorares/node-mysql2/tree/master/documentation)
12
14
 
@@ -246,7 +248,9 @@ con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err,
246
248
 
247
249
  MySQL2 is mostly API compatible with [Node MySQL][node-mysql]. You should check their API documentation to see all available API options.
248
250
 
249
- If you find any incompatibility with [Node MySQL][node-mysql], Please report via Issue tracker. We will fix reported incompatibility on priority basis.
251
+ One known incompatibility is that `DECIMAL` values are returned as strings whereas in [Node MySQL][node-mysql] they are returned as numbers. This includes the result of `SUM()` and `AVG()` functions when applied to `INTEGER` arguments. This is done deliberately to avoid loss of precision - see https://github.com/sidorares/node-mysql2/issues/935.
252
+
253
+ If you find any other incompatibility with [Node MySQL][node-mysql], Please report via Issue tracker. We will fix reported incompatibility on priority basis.
250
254
 
251
255
  ## Documentation
252
256
 
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Connection as PromiseConnection,
3
3
  Pool as PromisePool,
4
- PoolConnection as PromisePoolConnection
4
+ PoolConnection as PromisePoolConnection,
5
5
  } from './promise';
6
6
 
7
7
  import * as mysql from './typings/mysql';
@@ -72,6 +72,15 @@ export interface Connection extends mysql.Connection {
72
72
  ): mysql.Query;
73
73
  ping(callback?: (err: mysql.QueryError | null) => any): void;
74
74
  promise(promiseImpl?: PromiseConstructor): PromiseConnection;
75
+ unprepare(sql: string): mysql.PrepareStatementInfo;
76
+ prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
77
+ serverHandshake(args: any): any;
78
+ writeOk(args?: mysql.OkPacketParams): void;
79
+ writeError(args?: mysql.ErrorPacketParams): void;
80
+ writeEof(warnings?: number, statusFlags?: number): void;
81
+ writeTextResult(rows?: Array<any>, columns?: Array<any>): void;
82
+ writePacket(packet: any): void;
83
+ sequenceId: number;
75
84
  }
76
85
 
77
86
  export interface PoolConnection extends mysql.PoolConnection, Connection {
@@ -149,14 +158,11 @@ export interface Pool extends mysql.Connection {
149
158
  on(event: 'release', listener: (connection: PoolConnection) => any): this;
150
159
  on(event: 'enqueue', listener: () => any): this;
151
160
  promise(promiseImpl?: PromiseConstructor): PromisePool;
152
- }
161
+ unprepare(sql: string): mysql.PrepareStatementInfo;
162
+ prepare(sql: string, callback?: (err: mysql.QueryError | null, statement: mysql.PrepareStatementInfo) => any): mysql.Prepare;
153
163
 
154
- type authPlugins = (pluginMetadata: {
155
- connection: Connection;
156
- command: string;
157
- }) => (
158
- pluginData: Buffer
159
- ) => Promise<string> | string | Buffer | Promise<Buffer> | null;
164
+ config: mysql.PoolOptions;
165
+ }
160
166
 
161
167
  export interface ConnectionOptions extends mysql.ConnectionOptions {
162
168
  charsetNumber?: number;
@@ -178,10 +184,18 @@ export interface ConnectionOptions extends mysql.ConnectionOptions {
178
184
  queueLimit?: number;
179
185
  waitForConnections?: boolean;
180
186
  authPlugins?: {
181
- [key: string]: authPlugins;
187
+ [key: string]: mysql.AuthPlugin;
182
188
  };
183
189
  }
184
190
 
191
+ export interface ConnectionConfig extends ConnectionOptions {
192
+ mergeFlags(defaultFlags: string[], userFlags: string[] | string): number;
193
+ getDefaultFlags(options?: ConnectionOptions): string[];
194
+ getCharsetNumber(charset: string): number;
195
+ getSSLProfile(name: string): { ca: string[] };
196
+ parseUrl(url: string): { host: string, port: number, database: string, user: string, password: string, [key: string]: any };
197
+ }
198
+
185
199
  export interface PoolOptions extends mysql.PoolOptions, ConnectionOptions {}
186
200
 
187
201
  export function createConnection(connectionUri: string): Connection;
package/index.js CHANGED
@@ -12,6 +12,7 @@ exports.createConnection = function(opts) {
12
12
 
13
13
  exports.connect = exports.createConnection;
14
14
  exports.Connection = Connection;
15
+ exports.ConnectionConfig = ConnectionConfig;
15
16
 
16
17
  const Pool = require('./lib/pool.js');
17
18
  const PoolCluster = require('./lib/pool_cluster.js');
@@ -42,6 +43,7 @@ exports.createServer = function(handler) {
42
43
  };
43
44
 
44
45
  exports.PoolConnection = require('./lib/pool_connection');
46
+ exports.authPlugins = require('./lib/auth_plugins');
45
47
  exports.escape = SqlString.escape;
46
48
  exports.escapeId = SqlString.escapeId;
47
49
  exports.format = SqlString.format;
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ caching_sha2_password: require('./caching_sha2_password'),
5
+ mysql_clear_password: require('./mysql_clear_password'),
6
+ mysql_native_password: require('./mysql_native_password'),
7
+ sha256_password: require('./sha256_password'),
8
+ };
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ function bufferFromStr(str) {
4
+ return Buffer.from(`${str}\0`);
5
+ }
6
+
7
+ const create_mysql_clear_password_plugin = pluginOptions =>
8
+ function mysql_clear_password_plugin({ connection, command }) {
9
+ const password =
10
+ command.password || pluginOptions.password || connection.config.password;
11
+
12
+ return function (/* pluginData */) {
13
+ return bufferFromStr(password);
14
+ };
15
+ };
16
+
17
+ module.exports = create_mysql_clear_password_plugin;
@@ -2,7 +2,7 @@
2
2
 
3
3
  const PLUGIN_NAME = 'sha256_password';
4
4
  const crypto = require('crypto');
5
- const { xor } = require('../auth_41');
5
+ const { xorRotating } = require('../auth_41');
6
6
 
7
7
  const REQUEST_SERVER_KEY_PACKET = Buffer.from([1]);
8
8
 
@@ -11,7 +11,7 @@ const STATE_WAIT_SERVER_KEY = 1;
11
11
  const STATE_FINAL = -1;
12
12
 
13
13
  function encrypt(password, scramble, key) {
14
- const stage1 = xor(
14
+ const stage1 = xorRotating(
15
15
  Buffer.from(`${password}\0`, 'utf8').toString('binary'),
16
16
  scramble.toString('binary')
17
17
  );
@@ -9,11 +9,13 @@ const Packets = require('../packets/index.js');
9
9
  const sha256_password = require('../auth_plugins/sha256_password');
10
10
  const caching_sha2_password = require('../auth_plugins/caching_sha2_password.js');
11
11
  const mysql_native_password = require('../auth_plugins/mysql_native_password.js');
12
+ const mysql_clear_password = require('../auth_plugins/mysql_clear_password.js');
12
13
 
13
14
  const standardAuthPlugins = {
14
15
  sha256_password: sha256_password({}),
15
16
  caching_sha2_password: caching_sha2_password({}),
16
- mysql_native_password: mysql_native_password({})
17
+ mysql_native_password: mysql_native_password({}),
18
+ mysql_clear_password: mysql_clear_password({})
17
19
  };
18
20
 
19
21
  function warnLegacyAuthSwitch() {
@@ -1,7 +1,14 @@
1
+ // This file was modified by Oracle on September 21, 2021.
2
+ // The changes involve saving additional authentication factor passwords
3
+ // in the command scope and enabling multi-factor authentication in the
4
+ // client-side when the server supports it.
5
+ // Modifications copyright (c) 2021, Oracle and/or its affiliates.
6
+
1
7
  'use strict';
2
8
 
3
9
  const Command = require('./command.js');
4
10
  const Packets = require('../packets/index.js');
11
+ const ClientConstants = require('../constants/client');
5
12
  const ClientHandshake = require('./client_handshake.js');
6
13
  const CharsetToEncoding = require('../constants/charset_encodings.js');
7
14
 
@@ -11,10 +18,15 @@ class ChangeUser extends Command {
11
18
  this.onResult = callback;
12
19
  this.user = options.user;
13
20
  this.password = options.password;
21
+ // "password1" is an alias of "password"
22
+ this.password1 = options.password;
23
+ this.password2 = options.password2;
24
+ this.password3 = options.password3;
14
25
  this.database = options.database;
15
26
  this.passwordSha1 = options.passwordSha1;
16
27
  this.charsetNumber = options.charsetNumber;
17
28
  this.currentConfig = options.currentConfig;
29
+ this.authenticationFactor = 0;
18
30
  }
19
31
  start(packet, connection) {
20
32
  const newPacket = new Packets.ChangeUser({
@@ -35,6 +47,13 @@ class ChangeUser extends Command {
35
47
  // reset prepared statements cache as all statements become invalid after changeUser
36
48
  connection._statements.reset();
37
49
  connection.writePacket(newPacket.toPacket());
50
+ // check if the server supports multi-factor authentication
51
+ const multiFactorAuthentication = connection.serverCapabilityFlags & ClientConstants.MULTI_FACTOR_AUTHENTICATION;
52
+ if (multiFactorAuthentication) {
53
+ // if the server supports multi-factor authentication, we enable it in
54
+ // the client
55
+ this.authenticationFactor = 1;
56
+ }
38
57
  return ChangeUser.prototype.handshakeResult;
39
58
  }
40
59
  }
@@ -3,6 +3,11 @@
3
3
  // emitted in the command instance itself.
4
4
  // Modifications copyright (c) 2021, Oracle and/or its affiliates.
5
5
 
6
+ // This file was modified by Oracle on September 21, 2021.
7
+ // Handshake workflow now supports additional authentication factors requested
8
+ // by the server.
9
+ // Modifications copyright (c) 2021, Oracle and/or its affiliates.
10
+
6
11
  'use strict';
7
12
 
8
13
  const Command = require('./command.js');
@@ -26,6 +31,7 @@ class ClientHandshake extends Command {
26
31
  super();
27
32
  this.handshake = null;
28
33
  this.clientFlags = clientFlags;
34
+ this.authenticationFactor = 0;
29
35
  }
30
36
 
31
37
  start() {
@@ -51,6 +57,14 @@ class ClientHandshake extends Command {
51
57
  }
52
58
  this.user = connection.config.user;
53
59
  this.password = connection.config.password;
60
+ // "password1" is an alias to the original "password" value
61
+ // to make it easier to integrate multi-factor authentication
62
+ this.password1 = connection.config.password;
63
+ // "password2" and "password3" are the 2nd and 3rd factor authentication
64
+ // passwords, which can be undefined depending on the authentication
65
+ // plugin being used
66
+ this.password2 = connection.config.password2;
67
+ this.password3 = connection.config.password3;
54
68
  this.passwordSha1 = connection.config.passwordSha1;
55
69
  this.database = connection.config.database;
56
70
  this.autPluginName = this.handshake.autPluginName;
@@ -109,6 +123,12 @@ class ClientHandshake extends Command {
109
123
  connection.connectionId = this.handshake.connectionId;
110
124
  const serverSSLSupport =
111
125
  this.handshake.capabilityFlags & ClientConstants.SSL;
126
+ // multi factor authentication is enabled with the
127
+ // "MULTI_FACTOR_AUTHENTICATION" capability and should only be used if it
128
+ // is supported by the server
129
+ const multiFactorAuthentication =
130
+ this.handshake.capabilityFlags & ClientConstants.MULTI_FACTOR_AUTHENTICATION;
131
+ this.clientFlags = this.clientFlags | multiFactorAuthentication;
112
132
  // use compression only if requested by client and supported by server
113
133
  connection.config.compress =
114
134
  connection.config.compress &&
@@ -141,17 +161,36 @@ class ClientHandshake extends Command {
141
161
  } else {
142
162
  this.sendCredentials(connection);
143
163
  }
164
+ if (multiFactorAuthentication) {
165
+ // if the server supports multi-factor authentication, we enable it in
166
+ // the client
167
+ this.authenticationFactor = 1;
168
+ }
144
169
  return ClientHandshake.prototype.handshakeResult;
145
170
  }
146
171
 
147
172
  handshakeResult(packet, connection) {
148
173
  const marker = packet.peekByte();
149
- if (marker === 0xfe || marker === 1) {
174
+ // packet can be OK_Packet, ERR_Packet, AuthSwitchRequest, AuthNextFactor
175
+ // or AuthMoreData
176
+ if (marker === 0xfe || marker === 1 || marker === 0x02) {
150
177
  const authSwitch = require('./auth_switch');
151
178
  try {
152
179
  if (marker === 1) {
153
180
  authSwitch.authSwitchRequestMoreData(packet, connection, this);
154
181
  } else {
182
+ // if authenticationFactor === 0, it means the server does not support
183
+ // the multi-factor authentication capability
184
+ if (this.authenticationFactor !== 0) {
185
+ // if we are past the first authentication factor, we should use the
186
+ // corresponding password (if there is one)
187
+ connection.config.password = this[`password${this.authenticationFactor}`];
188
+ // update the current authentication factor
189
+ this.authenticationFactor += 1;
190
+ }
191
+ // if marker === 0x02, it means it is an AuthNextFactor packet,
192
+ // which is similar in structure to an AuthSwitchRequest packet,
193
+ // so, we can use it directly
155
194
  authSwitch.authSwitchRequest(packet, connection, this);
156
195
  }
157
196
  return ClientHandshake.prototype.handshakeResult;
@@ -55,7 +55,8 @@ class Prepare extends Command {
55
55
  }
56
56
  const cmdPacket = new Packets.PrepareStatement(
57
57
  this.query,
58
- connection.config.charsetNumber
58
+ connection.config.charsetNumber,
59
+ this.options.values
59
60
  );
60
61
  connection.writePacket(cmdPacket.toPacket(1));
61
62
  return Prepare.prototype.prepareHeader;
@@ -7,7 +7,7 @@ const Packet = require('../packets/packet.js');
7
7
  class Quit extends Command {
8
8
  constructor(callback) {
9
9
  super();
10
- this.done = callback;
10
+ this.onResult = callback;
11
11
  }
12
12
 
13
13
  start(packet, connection) {
@@ -18,8 +18,8 @@ class Quit extends Command {
18
18
  0,
19
19
  5
20
20
  );
21
- if (this.done) {
22
- this.done();
21
+ if (this.onResult) {
22
+ this.onResult();
23
23
  }
24
24
  connection.writePacket(quit);
25
25
  return null;
@@ -69,12 +69,41 @@ class ServerHandshake extends Command {
69
69
  return ServerHandshake.prototype.dispatchCommands;
70
70
  }
71
71
 
72
+ _isStatement(query, name) {
73
+ const firstWord = query.split(' ')[0].toUpperCase();
74
+ return firstWord === name;
75
+ }
76
+
72
77
  dispatchCommands(packet, connection) {
73
78
  // command from client to server
74
79
  let knownCommand = true;
75
80
  const encoding = connection.clientHelloReply.encoding;
76
81
  const commandCode = packet.readInt8();
77
82
  switch (commandCode) {
83
+ case CommandCode.STMT_PREPARE:
84
+ if (connection.listeners('stmt_prepare').length) {
85
+ const query = packet.readString(undefined, encoding);
86
+ connection.emit('stmt_prepare', query);
87
+ } else {
88
+ connection.writeError({
89
+ code: Errors.HA_ERR_INTERNAL_ERROR,
90
+ message:
91
+ 'No query handler for prepared statements.'
92
+ });
93
+ }
94
+ break;
95
+ case CommandCode.STMT_EXECUTE:
96
+ if (connection.listeners('stmt_execute').length) {
97
+ const { stmtId, flags, iterationCount, values } = Packets.Execute.fromPacket(packet, encoding);
98
+ connection.emit('stmt_execute', stmtId, flags, iterationCount, values);
99
+ } else {
100
+ connection.writeError({
101
+ code: Errors.HA_ERR_INTERNAL_ERROR,
102
+ message:
103
+ 'No query handler for execute statements.'
104
+ });
105
+ }
106
+ break;
78
107
  case CommandCode.QUIT:
79
108
  if (connection.listeners('quit').length) {
80
109
  connection.emit('quit');
@@ -93,7 +122,13 @@ class ServerHandshake extends Command {
93
122
  case CommandCode.QUERY:
94
123
  if (connection.listeners('query').length) {
95
124
  const query = packet.readString(undefined, encoding);
96
- connection.emit('query', query);
125
+ if (this._isStatement(query, 'PREPARE') || this._isStatement(query, 'SET')) {
126
+ connection.emit('stmt_prepare', query);
127
+ }
128
+ else if (this._isStatement(query, 'EXECUTE')) {
129
+ connection.emit('stmt_execute', null, null, null, null, query);
130
+ }
131
+ else connection.emit('query', query);
97
132
  } else {
98
133
  connection.writeError({
99
134
  code: Errors.HA_ERR_INTERNAL_ERROR,
package/lib/connection.js CHANGED
@@ -8,6 +8,11 @@
8
8
  // there is a fatal error.
9
9
  // Modifications copyright (c) 2021, Oracle and/or its affiliates.
10
10
 
11
+ // This file was modified by Oracle on September 21, 2021.
12
+ // The changes involve passing additional authentication factor passwords
13
+ // to the ChangeUser Command instance.
14
+ // Modifications copyright (c) 2021, Oracle and/or its affiliates.
15
+
11
16
  'use strict';
12
17
 
13
18
  const Net = require('net');
@@ -50,6 +55,10 @@ class Connection extends EventEmitter {
50
55
  // Enable keep-alive on the socket. It's disabled by default, but the
51
56
  // user can enable it and supply an initial delay.
52
57
  this.stream.setKeepAlive(true, this.config.keepAliveInitialDelay);
58
+
59
+ // Enable TCP_NODELAY flag. This is needed so that the network packets
60
+ // are sent immediately to the server
61
+ this.stream.setNoDelay(true);
53
62
  }
54
63
  // if stream is a function, treat it as "stream agent / factory"
55
64
  } else if (typeof opts.config.stream === 'function') {
@@ -65,7 +74,7 @@ class Connection extends EventEmitter {
65
74
  this._paused_packets = new Queue();
66
75
  this._statements = new LRU({
67
76
  max: this.config.maxPreparedStatements,
68
- dispose: function(key, statement) {
77
+ dispose: function(statement) {
69
78
  statement.close();
70
79
  }
71
80
  });
@@ -91,6 +100,10 @@ class Connection extends EventEmitter {
91
100
  }
92
101
  this.packetParser.execute(data);
93
102
  });
103
+ this.stream.on('end', () => {
104
+ // emit the end event so that the pooled connection can close the connection
105
+ this.emit('end');
106
+ });
94
107
  this.stream.on('close', () => {
95
108
  // we need to set this flag everywhere where we want connection to close
96
109
  if (this._closing) {
@@ -112,7 +125,7 @@ class Connection extends EventEmitter {
112
125
  handshakeCommand.on('end', () => {
113
126
  // this happens when handshake finishes early either because there was
114
127
  // some fatal error or the server sent an error packet instead of
115
- // an hello packet (for example, 'Too many connactions' error)
128
+ // an hello packet (for example, 'Too many connections' error)
116
129
  if (!handshakeCommand.handshake || this._fatalError || this._protocolError) {
117
130
  return;
118
131
  }
@@ -126,9 +139,9 @@ class Connection extends EventEmitter {
126
139
  });
127
140
  this.addCommand(handshakeCommand);
128
141
  }
129
- // in case there was no initiall handshake but we need to read sting, assume it utf-8
142
+ // in case there was no initial handshake but we need to read sting, assume it utf-8
130
143
  // most common example: "Too many connections" error ( packet is sent immediately on connection attempt, we don't know server encoding yet)
131
- // will be overwrittedn with actial encoding value as soon as server handshake packet is received
144
+ // will be overwritten with actual encoding value as soon as server handshake packet is received
132
145
  this.serverEncoding = 'utf8';
133
146
  if (this.config.connectTimeout) {
134
147
  const timeoutHandler = this._handleTimeoutError.bind(this);
@@ -174,7 +187,7 @@ class Connection extends EventEmitter {
174
187
  this.connectTimeout = null;
175
188
  }
176
189
  // Do not throw an error when a connection ends with a RST,ACK packet
177
- if (err.errno === 'ECONNRESET' && this._closing) {
190
+ if (err.code === 'ECONNRESET' && this._closing) {
178
191
  return;
179
192
  }
180
193
  this._handleFatalError(err);
@@ -337,6 +350,9 @@ class Connection extends EventEmitter {
337
350
  minVersion: this.config.ssl.minVersion
338
351
  });
339
352
  const rejectUnauthorized = this.config.ssl.rejectUnauthorized;
353
+ const verifyIdentity = this.config.ssl.verifyIdentity;
354
+ const host = this.config.host;
355
+
340
356
  let secureEstablished = false;
341
357
  const secureSocket = new Tls.TLSSocket(this.stream, {
342
358
  rejectUnauthorized: rejectUnauthorized,
@@ -344,6 +360,9 @@ class Connection extends EventEmitter {
344
360
  secureContext: secureContext,
345
361
  isServer: false
346
362
  });
363
+ if (typeof host === 'string') {
364
+ secureSocket.setServername(host);
365
+ }
347
366
  // error handler for secure socket
348
367
  secureSocket.on('_tlsError', err => {
349
368
  if (secureEstablished) {
@@ -354,7 +373,15 @@ class Connection extends EventEmitter {
354
373
  });
355
374
  secureSocket.on('secure', () => {
356
375
  secureEstablished = true;
357
- onSecure(rejectUnauthorized ? secureSocket.ssl.verifyError() : null);
376
+ let callbackValue = null;
377
+ if (rejectUnauthorized) {
378
+ callbackValue = secureSocket.ssl.verifyError()
379
+ if (!callbackValue && typeof host === 'string' && verifyIdentity) {
380
+ const cert = secureSocket.ssl.getPeerCertificate(true);
381
+ callbackValue = Tls.checkServerIdentity(host, cert)
382
+ }
383
+ }
384
+ onSecure(callbackValue);
358
385
  });
359
386
  secureSocket.on('data', data => {
360
387
  this.packetParser.execute(data);
@@ -396,6 +423,10 @@ class Connection extends EventEmitter {
396
423
  err.code = code || 'PROTOCOL_ERROR';
397
424
  this.emit('error', err);
398
425
  }
426
+
427
+ get fatalError() {
428
+ return this._fatalError;
429
+ }
399
430
 
400
431
  handlePacket(packet) {
401
432
  if (this._paused) {
@@ -556,7 +587,7 @@ class Connection extends EventEmitter {
556
587
  this._paused = false;
557
588
  while ((packet = this._paused_packets.shift())) {
558
589
  this.handlePacket(packet);
559
- // don't resume if packet hander paused connection
590
+ // don't resume if packet handler paused connection
560
591
  if (this._paused) {
561
592
  return;
562
593
  }
@@ -582,7 +613,7 @@ class Connection extends EventEmitter {
582
613
  const key = Connection.statementKey(options);
583
614
  const stmt = this._statements.get(key);
584
615
  if (stmt) {
585
- this._statements.del(key);
616
+ this._statements.delete(key);
586
617
  stmt.close();
587
618
  }
588
619
  return stmt;
@@ -671,7 +702,12 @@ class Connection extends EventEmitter {
671
702
  new Commands.ChangeUser(
672
703
  {
673
704
  user: options.user || this.config.user,
674
- password: options.password || this.config.password,
705
+ // for the purpose of multi-factor authentication, or not, the main
706
+ // password (used for the 1st authentication factor) can also be
707
+ // provided via the "password1" option
708
+ password: options.password || options.password1 || this.config.password || this.config.password1,
709
+ password2: options.password2 || this.config.password2,
710
+ password3: options.password3 || this.config.password3,
675
711
  passwordSha1: options.passwordSha1 || this.config.passwordSha1,
676
712
  database: options.database || this.config.database,
677
713
  timeout: options.timeout,
@@ -804,14 +840,23 @@ class Connection extends EventEmitter {
804
840
  );
805
841
  }
806
842
 
807
- writeTextResult(rows, columns) {
843
+ writeBinaryRow(column) {
844
+ this.writePacket(
845
+ Packets.BinaryRow.toPacket(column, this.serverConfig.encoding)
846
+ );
847
+ }
848
+
849
+ writeTextResult(rows, columns, binary=false) {
808
850
  this.writeColumns(columns);
809
851
  rows.forEach(row => {
810
852
  const arrayRow = new Array(columns.length);
811
853
  columns.forEach(column => {
812
854
  arrayRow.push(row[column.name]);
813
855
  });
814
- this.writeTextRow(arrayRow);
856
+ if(binary) {
857
+ this.writeBinaryRow(arrayRow);
858
+ }
859
+ else this.writeTextRow(arrayRow);
815
860
  });
816
861
  this.writeEof();
817
862
  }
@@ -1,3 +1,10 @@
1
+ // This file was modified by Oracle on September 21, 2021.
2
+ // New connection options for additional authentication factors were
3
+ // introduced.
4
+ // Multi-factor authentication capability is now enabled if one of these
5
+ // options is used.
6
+ // Modifications copyright (c) 2021, Oracle and/or its affiliates.
7
+
1
8
  'use strict';
2
9
 
3
10
  const { URL } = require('url');
@@ -30,6 +37,11 @@ const validOptions = {
30
37
  namedPlaceholders: 1,
31
38
  nestTables: 1,
32
39
  password: 1,
40
+ // with multi-factor authentication, the main password (used for the first
41
+ // authentication factor) can be provided via password1
42
+ password1: 1,
43
+ password2: 1,
44
+ password3: 1,
33
45
  passwordSha1: 1,
34
46
  pool: 1,
35
47
  port: 1,
@@ -81,7 +93,12 @@ class ConnectionConfig {
81
93
  this.localAddress = options.localAddress;
82
94
  this.socketPath = options.socketPath;
83
95
  this.user = options.user || undefined;
84
- this.password = options.password || undefined;
96
+ // for the purpose of multi-factor authentication, or not, the main
97
+ // password (used for the 1st authentication factor) can also be
98
+ // provided via the "password1" option
99
+ this.password = options.password || options.password1 || undefined;
100
+ this.password2 = options.password2 || undefined;
101
+ this.password3 = options.password3 || undefined;
85
102
  this.passwordSha1 = options.passwordSha1 || undefined;
86
103
  this.database = options.database;
87
104
  this.connectTimeout = isNaN(options.connectTimeout)
@@ -128,7 +145,7 @@ class ConnectionConfig {
128
145
  // "+" is a url encoded char for space so it
129
146
  // gets translated to space when giving a
130
147
  // connection string..
131
- this.timezone = `+${this.timezone.substr(1)}`;
148
+ this.timezone = `+${this.timezone.slice(1)}`;
132
149
  }
133
150
  if (this.ssl) {
134
151
  if (typeof this.ssl !== 'object') {
@@ -236,7 +253,7 @@ class ConnectionConfig {
236
253
  const options = {
237
254
  host: parsedUrl.hostname,
238
255
  port: parsedUrl.port,
239
- database: parsedUrl.pathname.substr(1),
256
+ database: parsedUrl.pathname.slice(1),
240
257
  user: parsedUrl.username,
241
258
  password: parsedUrl.password
242
259
  };
@@ -104,7 +104,7 @@ module.exports = [
104
104
  'eucjpms',
105
105
  'eucjpms',
106
106
  'cp1250',
107
- 'utf8',
107
+ 'utf16',
108
108
  'utf16',
109
109
  'utf16',
110
110
  'utf16',
@@ -309,5 +309,8 @@ module.exports = [
309
309
  'utf8',
310
310
  'utf8',
311
311
  'utf8',
312
+ 'utf8',
313
+ 'utf8',
314
+ 'utf8',
312
315
  'utf8'
313
316
  ];