@depup/mysql2 3.19.0-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.
Files changed (124) hide show
  1. package/License +19 -0
  2. package/README.md +114 -0
  3. package/index.d.ts +1 -0
  4. package/index.js +77 -0
  5. package/lib/auth_41.js +95 -0
  6. package/lib/auth_plugins/caching_sha2_password.js +114 -0
  7. package/lib/auth_plugins/caching_sha2_password.md +18 -0
  8. package/lib/auth_plugins/index.js +8 -0
  9. package/lib/auth_plugins/mysql_clear_password.js +17 -0
  10. package/lib/auth_plugins/mysql_native_password.js +34 -0
  11. package/lib/auth_plugins/sha256_password.js +68 -0
  12. package/lib/base/connection.js +988 -0
  13. package/lib/base/pool.js +282 -0
  14. package/lib/base/pool_connection.js +77 -0
  15. package/lib/commands/auth_switch.js +129 -0
  16. package/lib/commands/binlog_dump.js +109 -0
  17. package/lib/commands/change_user.js +68 -0
  18. package/lib/commands/client_handshake.js +383 -0
  19. package/lib/commands/close_statement.js +18 -0
  20. package/lib/commands/command.js +54 -0
  21. package/lib/commands/execute.js +112 -0
  22. package/lib/commands/index.js +27 -0
  23. package/lib/commands/ping.js +36 -0
  24. package/lib/commands/prepare.js +143 -0
  25. package/lib/commands/query.js +361 -0
  26. package/lib/commands/quit.js +29 -0
  27. package/lib/commands/register_slave.js +27 -0
  28. package/lib/commands/server_handshake.js +202 -0
  29. package/lib/compressed_protocol.js +153 -0
  30. package/lib/connection.js +12 -0
  31. package/lib/connection_config.js +293 -0
  32. package/lib/constants/charset_encodings.js +317 -0
  33. package/lib/constants/charsets.js +317 -0
  34. package/lib/constants/client.js +38 -0
  35. package/lib/constants/commands.js +36 -0
  36. package/lib/constants/cursor.js +8 -0
  37. package/lib/constants/encoding_charset.js +50 -0
  38. package/lib/constants/errors.js +3973 -0
  39. package/lib/constants/field_flags.js +20 -0
  40. package/lib/constants/server_status.js +44 -0
  41. package/lib/constants/session_track.js +11 -0
  42. package/lib/constants/ssl_profiles.js +11 -0
  43. package/lib/constants/types.js +64 -0
  44. package/lib/create_connection.js +10 -0
  45. package/lib/create_pool.js +10 -0
  46. package/lib/create_pool_cluster.js +9 -0
  47. package/lib/helpers.js +83 -0
  48. package/lib/packet_parser.js +195 -0
  49. package/lib/packets/auth_next_factor.js +35 -0
  50. package/lib/packets/auth_switch_request.js +38 -0
  51. package/lib/packets/auth_switch_request_more_data.js +33 -0
  52. package/lib/packets/auth_switch_response.js +30 -0
  53. package/lib/packets/binary_row.js +95 -0
  54. package/lib/packets/binlog_dump.js +33 -0
  55. package/lib/packets/binlog_query_statusvars.js +115 -0
  56. package/lib/packets/change_user.js +97 -0
  57. package/lib/packets/close_statement.js +21 -0
  58. package/lib/packets/column_definition.js +291 -0
  59. package/lib/packets/execute.js +214 -0
  60. package/lib/packets/handshake.js +112 -0
  61. package/lib/packets/handshake_response.js +173 -0
  62. package/lib/packets/index.js +152 -0
  63. package/lib/packets/packet.js +946 -0
  64. package/lib/packets/prepare_statement.js +27 -0
  65. package/lib/packets/prepared_statement_header.js +16 -0
  66. package/lib/packets/query.js +27 -0
  67. package/lib/packets/register_slave.js +46 -0
  68. package/lib/packets/resultset_header.js +124 -0
  69. package/lib/packets/ssl_request.js +25 -0
  70. package/lib/packets/text_row.js +47 -0
  71. package/lib/parsers/binary_parser.js +235 -0
  72. package/lib/parsers/parser_cache.js +68 -0
  73. package/lib/parsers/static_binary_parser.js +213 -0
  74. package/lib/parsers/static_text_parser.js +152 -0
  75. package/lib/parsers/string.js +50 -0
  76. package/lib/parsers/text_parser.js +214 -0
  77. package/lib/pool.js +12 -0
  78. package/lib/pool_cluster.js +375 -0
  79. package/lib/pool_config.js +30 -0
  80. package/lib/pool_connection.js +12 -0
  81. package/lib/promise/connection.js +228 -0
  82. package/lib/promise/inherit_events.js +27 -0
  83. package/lib/promise/make_done_cb.js +19 -0
  84. package/lib/promise/pool.js +118 -0
  85. package/lib/promise/pool_cluster.js +54 -0
  86. package/lib/promise/pool_connection.js +23 -0
  87. package/lib/promise/prepared_statement_info.js +32 -0
  88. package/lib/results_stream.js +38 -0
  89. package/lib/server.js +37 -0
  90. package/package.json +96 -0
  91. package/promise.d.ts +139 -0
  92. package/promise.js +208 -0
  93. package/typings/mysql/LICENSE.txt +15 -0
  94. package/typings/mysql/index.d.ts +84 -0
  95. package/typings/mysql/info.txt +1 -0
  96. package/typings/mysql/lib/Auth.d.ts +30 -0
  97. package/typings/mysql/lib/Connection.d.ts +442 -0
  98. package/typings/mysql/lib/Pool.d.ts +71 -0
  99. package/typings/mysql/lib/PoolCluster.d.ts +92 -0
  100. package/typings/mysql/lib/PoolConnection.d.ts +11 -0
  101. package/typings/mysql/lib/Server.d.ts +11 -0
  102. package/typings/mysql/lib/constants/CharsetToEncoding.d.ts +8 -0
  103. package/typings/mysql/lib/constants/Charsets.d.ts +326 -0
  104. package/typings/mysql/lib/constants/Types.d.ts +70 -0
  105. package/typings/mysql/lib/constants/index.d.ts +5 -0
  106. package/typings/mysql/lib/parsers/ParserCache.d.ts +4 -0
  107. package/typings/mysql/lib/parsers/index.d.ts +18 -0
  108. package/typings/mysql/lib/parsers/typeCast.d.ts +54 -0
  109. package/typings/mysql/lib/protocol/packets/Field.d.ts +10 -0
  110. package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +27 -0
  111. package/typings/mysql/lib/protocol/packets/OkPacket.d.ts +23 -0
  112. package/typings/mysql/lib/protocol/packets/ProcedurePacket.d.ts +13 -0
  113. package/typings/mysql/lib/protocol/packets/ResultSetHeader.d.ts +18 -0
  114. package/typings/mysql/lib/protocol/packets/RowDataPacket.d.ts +9 -0
  115. package/typings/mysql/lib/protocol/packets/index.d.ts +28 -0
  116. package/typings/mysql/lib/protocol/packets/params/ErrorPacketParams.d.ts +6 -0
  117. package/typings/mysql/lib/protocol/packets/params/OkPacketParams.d.ts +9 -0
  118. package/typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts +41 -0
  119. package/typings/mysql/lib/protocol/sequences/Prepare.d.ts +65 -0
  120. package/typings/mysql/lib/protocol/sequences/Query.d.ts +228 -0
  121. package/typings/mysql/lib/protocol/sequences/QueryableBase.d.ts +41 -0
  122. package/typings/mysql/lib/protocol/sequences/Sequence.d.ts +5 -0
  123. package/typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts +17 -0
  124. package/typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts +18 -0
@@ -0,0 +1,383 @@
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
+
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
+
11
+ 'use strict';
12
+
13
+ const Command = require('./command.js');
14
+ const Packets = require('../packets/index.js');
15
+ const ClientConstants = require('../constants/client.js');
16
+ const CharsetToEncoding = require('../constants/charset_encodings.js');
17
+ const auth41 = require('../auth_41.js');
18
+ const { getAuthPlugin } = require('./auth_switch.js');
19
+ const {
20
+ calculateToken: calculateSha2Token,
21
+ } = require('../auth_plugins/caching_sha2_password.js');
22
+
23
+ function flagNames(flags) {
24
+ const res = [];
25
+ for (const c in ClientConstants) {
26
+ if (flags & ClientConstants[c]) {
27
+ res.push(c.replace(/_/g, ' ').toLowerCase());
28
+ }
29
+ }
30
+ return res;
31
+ }
32
+
33
+ class ClientHandshake extends Command {
34
+ constructor(clientFlags) {
35
+ super();
36
+ this.handshake = null;
37
+ this.clientFlags = clientFlags;
38
+ this.authenticationFactor = 0;
39
+ }
40
+
41
+ start() {
42
+ return ClientHandshake.prototype.handshakeInit;
43
+ }
44
+
45
+ sendSSLRequest(connection) {
46
+ const sslRequest = new Packets.SSLRequest(
47
+ this.clientFlags,
48
+ connection.config.charsetNumber
49
+ );
50
+ connection.writePacket(sslRequest.toPacket());
51
+ }
52
+
53
+ sendCredentials(connection) {
54
+ if (connection.config.debug) {
55
+ console.log(
56
+ 'Sending handshake packet: flags:%d=(%s)',
57
+ this.clientFlags,
58
+ flagNames(this.clientFlags).join(', ')
59
+ );
60
+ }
61
+ this.user = connection.config.user;
62
+ this.password = connection.config.password;
63
+ // "password1" is an alias to the original "password" value
64
+ // to make it easier to integrate multi-factor authentication
65
+ this.password1 = connection.config.password;
66
+ // "password2" and "password3" are the 2nd and 3rd factor authentication
67
+ // passwords, which can be undefined depending on the authentication
68
+ // plugin being used
69
+ this.password2 = connection.config.password2;
70
+ this.password3 = connection.config.password3;
71
+ this.passwordSha1 = connection.config.passwordSha1;
72
+ this.database = connection.config.database;
73
+ this.authPluginName = this.handshake.authPluginName;
74
+
75
+ // Optimization: Try to use the server's preferred authentication method
76
+ // to avoid an unnecessary auth switch roundtrip
77
+ const serverAuthMethod = this.handshake.authPluginName;
78
+ const isSecureConnection =
79
+ connection.config.ssl || connection.config.socketPath;
80
+
81
+ // Combine auth plugin data for easier handling
82
+ // Note: authPluginData2 can include a trailing NUL byte when PLUGIN_AUTH is set
83
+ // We must ensure exactly 20 bytes for the scramble
84
+ const authPluginData =
85
+ this.handshake.authPluginData1 && this.handshake.authPluginData2
86
+ ? Buffer.concat([
87
+ this.handshake.authPluginData1,
88
+ this.handshake.authPluginData2,
89
+ ]).slice(0, 20)
90
+ : Buffer.alloc(20);
91
+
92
+ // Check if user has custom auth plugin or legacy handler for the server-advertised method
93
+ // If so, we must not bypass the auth switch flow with our built-in implementation
94
+ const hasCustomAuthPlugin =
95
+ connection.config.authPlugins &&
96
+ Object.prototype.hasOwnProperty.call(
97
+ connection.config.authPlugins,
98
+ serverAuthMethod
99
+ );
100
+ const hasLegacyAuthSwitchHandler =
101
+ typeof connection.config.authSwitchHandler === 'function';
102
+
103
+ // Determine which auth method to use
104
+ // Try to use server's preferred method if we can, otherwise fallback to native
105
+ const canUseDirectAuth =
106
+ !hasCustomAuthPlugin &&
107
+ !hasLegacyAuthSwitchHandler &&
108
+ this.canUseAuthMethodDirectly(serverAuthMethod, isSecureConnection);
109
+
110
+ const clientAuthMethod = canUseDirectAuth
111
+ ? serverAuthMethod
112
+ : 'mysql_native_password';
113
+
114
+ // Calculate the auth token for the chosen method
115
+ const authToken = this.calculateAuthToken(
116
+ clientAuthMethod,
117
+ this.password,
118
+ authPluginData
119
+ );
120
+
121
+ if (connection.config.debug) {
122
+ console.log(
123
+ 'Server auth method: %s, Using auth method: %s',
124
+ serverAuthMethod,
125
+ clientAuthMethod
126
+ );
127
+ }
128
+
129
+ const handshakeResponse = new Packets.HandshakeResponse({
130
+ flags: this.clientFlags,
131
+ user: this.user,
132
+ database: this.database,
133
+ password: this.password,
134
+ passwordSha1: this.passwordSha1,
135
+ charsetNumber: connection.config.charsetNumber,
136
+ authPluginData1: this.handshake.authPluginData1,
137
+ authPluginData2: this.handshake.authPluginData2,
138
+ compress: connection.config.compress,
139
+ connectAttributes: connection.config.connectAttributes,
140
+ authToken: authToken,
141
+ authPluginName: clientAuthMethod,
142
+ });
143
+ connection.writePacket(handshakeResponse.toPacket());
144
+
145
+ // If we used a non-native auth method in the initial handshake response,
146
+ // we need to prepare for potential AuthMoreData packets by creating
147
+ // the appropriate auth plugin instance
148
+ if (clientAuthMethod !== 'mysql_native_password') {
149
+ this.initializeAuthPlugin(clientAuthMethod, authPluginData, connection);
150
+ }
151
+ }
152
+
153
+ calculateNativePasswordAuthToken(authPluginData) {
154
+ // TODO: dont split into authPluginData1 and authPluginData2, instead join when 1 & 2 received
155
+ const authPluginData1 = authPluginData.slice(0, 8);
156
+ const authPluginData2 = authPluginData.slice(8, 20);
157
+ let authToken;
158
+ if (this.passwordSha1) {
159
+ authToken = auth41.calculateTokenFromPasswordSha(
160
+ this.passwordSha1,
161
+ authPluginData1,
162
+ authPluginData2
163
+ );
164
+ } else {
165
+ authToken = auth41.calculateToken(
166
+ this.password,
167
+ authPluginData1,
168
+ authPluginData2
169
+ );
170
+ }
171
+ return authToken;
172
+ }
173
+
174
+ calculateSha256Token(password, scramble) {
175
+ // Reuse the token calculation from caching_sha2_password plugin
176
+ // to avoid code duplication and ensure consistency
177
+ return calculateSha2Token(password, scramble);
178
+ }
179
+
180
+ // Helper: Calculate auth token for a specific auth method
181
+ calculateAuthToken(authMethod, password, authPluginData) {
182
+ switch (authMethod) {
183
+ case 'mysql_native_password':
184
+ return this.calculateNativePasswordAuthToken(authPluginData);
185
+
186
+ case 'caching_sha2_password':
187
+ return this.calculateSha256Token(password, authPluginData);
188
+
189
+ case 'sha256_password':
190
+ case 'mysql_clear_password':
191
+ // These methods send plaintext password over secure connections
192
+ return password
193
+ ? Buffer.from(`${password}\0`, 'utf8')
194
+ : Buffer.alloc(0);
195
+
196
+ default:
197
+ // Unknown method - use native password as fallback
198
+ return this.calculateNativePasswordAuthToken(authPluginData);
199
+ }
200
+ }
201
+
202
+ // Helper: Determine if we can use a specific auth method directly
203
+ canUseAuthMethodDirectly(authMethod, isSecureConnection) {
204
+ switch (authMethod) {
205
+ case 'mysql_native_password':
206
+ case 'caching_sha2_password':
207
+ // These methods work with or without SSL
208
+ return true;
209
+
210
+ case 'sha256_password':
211
+ case 'mysql_clear_password':
212
+ // These methods require secure connection for direct use
213
+ return isSecureConnection;
214
+
215
+ default:
216
+ // Unknown methods - fallback to native password
217
+ return false;
218
+ }
219
+ }
220
+
221
+ // Helper: Initialize auth plugin for handling subsequent AuthMoreData packets
222
+ initializeAuthPlugin(authMethod, authPluginData, connection) {
223
+ const authPlugin = getAuthPlugin(authMethod, connection);
224
+ if (!authPlugin) {
225
+ return; // Plugin not found, will fallback to auth switch if needed
226
+ }
227
+
228
+ // Initialize the plugin with connection and command context
229
+ const pluginHandler = authPlugin({ connection, command: this });
230
+ connection._authPlugin = pluginHandler;
231
+
232
+ // Prime the plugin by calling it with the scramble data
233
+ // This advances the plugin's state machine (e.g., to STATE_TOKEN_SENT)
234
+ // We don't send the result because we already included it in the handshake response
235
+ try {
236
+ Promise.resolve(pluginHandler(authPluginData)).catch((err) => {
237
+ // Ignore errors during initialization since we already sent the token
238
+ if (connection.config.debug) {
239
+ console.log('Auth plugin initialization:', err.message);
240
+ }
241
+ });
242
+ } catch (err) {
243
+ // Ignore synchronous errors during initialization
244
+ if (connection.config.debug) {
245
+ console.log('Auth plugin initialization error:', err.message);
246
+ }
247
+ }
248
+ }
249
+
250
+ handshakeInit(helloPacket, connection) {
251
+ this.on('error', (e) => {
252
+ connection._fatalError = e;
253
+ connection._protocolError = e;
254
+ });
255
+ this.handshake = Packets.Handshake.fromPacket(helloPacket);
256
+ if (connection.config.debug) {
257
+ console.log(
258
+ 'Server hello packet: capability flags:%d=(%s)',
259
+ this.handshake.capabilityFlags,
260
+ flagNames(this.handshake.capabilityFlags).join(', ')
261
+ );
262
+ }
263
+ connection.serverCapabilityFlags = this.handshake.capabilityFlags;
264
+ connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
265
+ connection.connectionId = this.handshake.connectionId;
266
+ const serverSSLSupport =
267
+ this.handshake.capabilityFlags & ClientConstants.SSL;
268
+ // multi factor authentication is enabled with the
269
+ // "MULTI_FACTOR_AUTHENTICATION" capability and should only be used if it
270
+ // is supported by the server
271
+ const multiFactorAuthentication =
272
+ this.handshake.capabilityFlags &
273
+ ClientConstants.MULTI_FACTOR_AUTHENTICATION;
274
+ this.clientFlags = this.clientFlags | multiFactorAuthentication;
275
+ // use compression only if requested by client and supported by server
276
+ connection.config.compress =
277
+ connection.config.compress &&
278
+ this.handshake.capabilityFlags & ClientConstants.COMPRESS;
279
+ this.clientFlags = this.clientFlags | connection.config.compress;
280
+ if (connection.config.ssl) {
281
+ // client requires SSL but server does not support it
282
+ if (!serverSSLSupport) {
283
+ const err = new Error('Server does not support secure connection');
284
+ err.code = 'HANDSHAKE_NO_SSL_SUPPORT';
285
+ err.fatal = true;
286
+ this.emit('error', err);
287
+ return false;
288
+ }
289
+ // send ssl upgrade request and immediately upgrade connection to secure
290
+ this.clientFlags |= ClientConstants.SSL;
291
+ this.sendSSLRequest(connection);
292
+ connection.startTLS((err) => {
293
+ // after connection is secure
294
+ if (err) {
295
+ // SSL negotiation error are fatal
296
+ err.code = 'HANDSHAKE_SSL_ERROR';
297
+ err.fatal = true;
298
+ this.emit('error', err);
299
+ return;
300
+ }
301
+ // rest of communication is encrypted
302
+ this.sendCredentials(connection);
303
+ });
304
+ } else {
305
+ this.sendCredentials(connection);
306
+ }
307
+ if (multiFactorAuthentication) {
308
+ // if the server supports multi-factor authentication, we enable it in
309
+ // the client
310
+ this.authenticationFactor = 1;
311
+ }
312
+ return ClientHandshake.prototype.handshakeResult;
313
+ }
314
+
315
+ handshakeResult(packet, connection) {
316
+ const marker = packet.peekByte();
317
+ // packet can be OK_Packet, ERR_Packet, AuthSwitchRequest, AuthNextFactor
318
+ // or AuthMoreData
319
+ if (marker === 0xfe || marker === 1 || marker === 0x02) {
320
+ const authSwitch = require('./auth_switch');
321
+ try {
322
+ if (marker === 1) {
323
+ authSwitch.authSwitchRequestMoreData(packet, connection, this);
324
+ } else {
325
+ // if authenticationFactor === 0, it means the server does not support
326
+ // the multi-factor authentication capability
327
+ if (this.authenticationFactor !== 0) {
328
+ // if we are past the first authentication factor, we should use the
329
+ // corresponding password (if there is one)
330
+ connection.config.password =
331
+ this[`password${this.authenticationFactor}`];
332
+ // update the current authentication factor
333
+ this.authenticationFactor += 1;
334
+ }
335
+ // if marker === 0x02, it means it is an AuthNextFactor packet,
336
+ // which is similar in structure to an AuthSwitchRequest packet,
337
+ // so, we can use it directly
338
+ authSwitch.authSwitchRequest(packet, connection, this);
339
+ }
340
+ return ClientHandshake.prototype.handshakeResult;
341
+ } catch (err) {
342
+ // Authentication errors are fatal
343
+ err.code = 'AUTH_SWITCH_PLUGIN_ERROR';
344
+ err.fatal = true;
345
+
346
+ if (this.onResult) {
347
+ this.onResult(err);
348
+ } else {
349
+ this.emit('error', err);
350
+ }
351
+ return null;
352
+ }
353
+ }
354
+ if (marker !== 0) {
355
+ const err = new Error('Unexpected packet during handshake phase');
356
+ // Unknown handshake errors are fatal
357
+ err.code = 'HANDSHAKE_UNKNOWN_ERROR';
358
+ err.fatal = true;
359
+
360
+ if (this.onResult) {
361
+ this.onResult(err);
362
+ } else {
363
+ this.emit('error', err);
364
+ }
365
+ return null;
366
+ }
367
+ // this should be called from ClientHandshake command only
368
+ // and skipped when called from ChangeUser command
369
+ if (!connection.authorized) {
370
+ connection.authorized = true;
371
+ if (connection.config.compress) {
372
+ const enableCompression =
373
+ require('../compressed_protocol.js').enableCompression;
374
+ enableCompression(connection);
375
+ }
376
+ }
377
+ if (this.onResult) {
378
+ this.onResult(null);
379
+ }
380
+ return null;
381
+ }
382
+ }
383
+ module.exports = ClientHandshake;
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const Command = require('./command');
4
+ const Packets = require('../packets/index.js');
5
+
6
+ class CloseStatement extends Command {
7
+ constructor(id) {
8
+ super();
9
+ this.id = id;
10
+ }
11
+
12
+ start(packet, connection) {
13
+ connection.writePacket(new Packets.CloseStatement(this.id).toPacket(1));
14
+ return null;
15
+ }
16
+ }
17
+
18
+ module.exports = CloseStatement;
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ const EventEmitter = require('events').EventEmitter;
4
+ const Timers = require('timers');
5
+
6
+ class Command extends EventEmitter {
7
+ constructor() {
8
+ super();
9
+ this.next = null;
10
+ }
11
+
12
+ // slow. debug only
13
+ stateName() {
14
+ const state = this.next;
15
+ for (const i in this) {
16
+ if (this[i] === state && i !== 'next') {
17
+ return i;
18
+ }
19
+ }
20
+ return 'unknown name';
21
+ }
22
+
23
+ execute(packet, connection) {
24
+ if (!this.next) {
25
+ this.next = this.start;
26
+ connection._resetSequenceId();
27
+ }
28
+ if (packet && packet.isError()) {
29
+ const err = packet.asError(connection.clientEncoding);
30
+ err.sql = this.sql || this.query;
31
+ if (this.queryTimeout) {
32
+ Timers.clearTimeout(this.queryTimeout);
33
+ this.queryTimeout = null;
34
+ }
35
+ if (this.onResult) {
36
+ this.onResult(err);
37
+ this.emit('end');
38
+ } else {
39
+ this.emit('error', err);
40
+ this.emit('end');
41
+ }
42
+ return true;
43
+ }
44
+ // TODO: don't return anything from execute, it's ugly and error-prone. Listen for 'end' event in connection
45
+ this.next = this.next(packet, connection);
46
+ if (this.next) {
47
+ return false;
48
+ }
49
+ this.emit('end');
50
+ return true;
51
+ }
52
+ }
53
+
54
+ module.exports = Command;
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ const Command = require('./command.js');
4
+ const Query = require('./query.js');
5
+ const Packets = require('../packets/index.js');
6
+
7
+ const getBinaryParser = require('../parsers/binary_parser.js');
8
+ const getStaticBinaryParser = require('../parsers/static_binary_parser.js');
9
+
10
+ class Execute extends Command {
11
+ constructor(options, callback) {
12
+ super();
13
+ this.statement = options.statement;
14
+ this.sql = options.sql;
15
+ this.values = options.values;
16
+ this.onResult = callback;
17
+ this.parameters = options.values;
18
+ this.insertId = 0;
19
+ this.timeout = options.timeout;
20
+ this.queryTimeout = null;
21
+ this._rows = [];
22
+ this._fields = [];
23
+ this._result = [];
24
+ this._fieldCount = 0;
25
+ this._rowParser = null;
26
+ this._executeOptions = options;
27
+ this._resultIndex = 0;
28
+ this._localStream = null;
29
+ this._unpipeStream = function () {};
30
+ this._streamFactory = options.infileStreamFactory;
31
+ this._connection = null;
32
+ }
33
+
34
+ buildParserFromFields(fields, connection) {
35
+ if (this.options.disableEval) {
36
+ return getStaticBinaryParser(fields, this.options, connection.config);
37
+ }
38
+
39
+ return getBinaryParser(fields, this.options, connection.config);
40
+ }
41
+
42
+ start(packet, connection) {
43
+ this._connection = connection;
44
+ this.options = Object.assign({}, connection.config, this._executeOptions);
45
+ this._setTimeout();
46
+ const executePacket = new Packets.Execute(
47
+ this.statement.id,
48
+ this.parameters,
49
+ connection.config.charsetNumber,
50
+ connection.config.timezone
51
+ );
52
+ //For reasons why this try-catch is here, please see
53
+ // https://github.com/sidorares/node-mysql2/pull/689
54
+ //For additional discussion, see
55
+ // 1. https://github.com/sidorares/node-mysql2/issues/493
56
+ // 2. https://github.com/sidorares/node-mysql2/issues/187
57
+ // 3. https://github.com/sidorares/node-mysql2/issues/480
58
+ try {
59
+ connection.writePacket(executePacket.toPacket(1));
60
+ } catch (error) {
61
+ this.onResult(error);
62
+ }
63
+ return Execute.prototype.resultsetHeader;
64
+ }
65
+
66
+ readField(packet, connection) {
67
+ let fields;
68
+ // disabling for now, but would be great to find reliable way to parse fields only once
69
+ // fields reported by prepare can be empty at all or just incorrect - see #169
70
+ //
71
+ // perfomance optimisation: if we already have this field parsed in statement header, use one from header
72
+ // const field = this.statement.columns.length == this._fieldCount ?
73
+ // this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
74
+ const field = new Packets.ColumnDefinition(
75
+ packet,
76
+ connection.clientEncoding
77
+ );
78
+ this._receivedFieldsCount++;
79
+ this._fields[this._resultIndex].push(field);
80
+ if (this._receivedFieldsCount === this._fieldCount) {
81
+ fields = this._fields[this._resultIndex];
82
+ this.emit('fields', fields, this._resultIndex);
83
+ return Execute.prototype.fieldsEOF;
84
+ }
85
+ return Execute.prototype.readField;
86
+ }
87
+
88
+ fieldsEOF(packet, connection) {
89
+ // check EOF
90
+ if (!packet.isEOF()) {
91
+ return connection.protocolError('Expected EOF packet');
92
+ }
93
+ this._rowParser = new (this.buildParserFromFields(
94
+ this._fields[this._resultIndex],
95
+ connection
96
+ ))();
97
+ return Execute.prototype.row;
98
+ }
99
+ }
100
+
101
+ Execute.prototype.done = Query.prototype.done;
102
+ Execute.prototype.doneInsert = Query.prototype.doneInsert;
103
+ Execute.prototype.resultsetHeader = Query.prototype.resultsetHeader;
104
+ Execute.prototype._findOrCreateReadStream =
105
+ Query.prototype._findOrCreateReadStream;
106
+ Execute.prototype._streamLocalInfile = Query.prototype._streamLocalInfile;
107
+ Execute.prototype._setTimeout = Query.prototype._setTimeout;
108
+ Execute.prototype._handleTimeoutError = Query.prototype._handleTimeoutError;
109
+ Execute.prototype.row = Query.prototype.row;
110
+ Execute.prototype.stream = Query.prototype.stream;
111
+
112
+ module.exports = Execute;
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const ClientHandshake = require('./client_handshake.js');
4
+ const ServerHandshake = require('./server_handshake.js');
5
+ const Query = require('./query.js');
6
+ const Prepare = require('./prepare.js');
7
+ const CloseStatement = require('./close_statement.js');
8
+ const Execute = require('./execute.js');
9
+ const Ping = require('./ping.js');
10
+ const RegisterSlave = require('./register_slave.js');
11
+ const BinlogDump = require('./binlog_dump.js');
12
+ const ChangeUser = require('./change_user.js');
13
+ const Quit = require('./quit.js');
14
+
15
+ module.exports = {
16
+ ClientHandshake,
17
+ ServerHandshake,
18
+ Query,
19
+ Prepare,
20
+ CloseStatement,
21
+ Execute,
22
+ Ping,
23
+ RegisterSlave,
24
+ BinlogDump,
25
+ ChangeUser,
26
+ Quit,
27
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const Command = require('./command');
4
+ const CommandCode = require('../constants/commands');
5
+ const Packet = require('../packets/packet');
6
+
7
+ // TODO: time statistics?
8
+ // usefull for queue size and network latency monitoring
9
+ // store created,sent,reply timestamps
10
+ class Ping extends Command {
11
+ constructor(callback) {
12
+ super();
13
+ this.onResult = callback;
14
+ }
15
+
16
+ start(packet, connection) {
17
+ const ping = new Packet(
18
+ 0,
19
+ Buffer.from([1, 0, 0, 0, CommandCode.PING]),
20
+ 0,
21
+ 5
22
+ );
23
+ connection.writePacket(ping);
24
+ return Ping.prototype.pingResponse;
25
+ }
26
+
27
+ pingResponse() {
28
+ // TODO: check it's OK packet. error check already done in caller
29
+ if (this.onResult) {
30
+ process.nextTick(this.onResult.bind(this));
31
+ }
32
+ return null;
33
+ }
34
+ }
35
+
36
+ module.exports = Ping;