oceanbase 0.0.1
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/License +19 -0
- package/README.md +250 -0
- package/index.d.ts +1 -0
- package/index.js +77 -0
- package/lib/auth_41.js +95 -0
- package/lib/auth_plugins/caching_sha2_password.js +108 -0
- package/lib/auth_plugins/caching_sha2_password.md +18 -0
- package/lib/auth_plugins/index.js +8 -0
- package/lib/auth_plugins/mysql_clear_password.js +17 -0
- package/lib/auth_plugins/mysql_native_password.js +34 -0
- package/lib/auth_plugins/sha256_password.js +68 -0
- package/lib/base/connection.js +978 -0
- package/lib/base/pool.js +237 -0
- package/lib/base/pool_connection.js +70 -0
- package/lib/commands/auth_switch.js +111 -0
- package/lib/commands/binlog_dump.js +109 -0
- package/lib/commands/change_user.js +68 -0
- package/lib/commands/client_handshake.js +241 -0
- package/lib/commands/close_statement.js +18 -0
- package/lib/commands/command.js +54 -0
- package/lib/commands/execute.js +112 -0
- package/lib/commands/index.js +27 -0
- package/lib/commands/ping.js +36 -0
- package/lib/commands/prepare.js +143 -0
- package/lib/commands/query.js +366 -0
- package/lib/commands/quit.js +29 -0
- package/lib/commands/register_slave.js +27 -0
- package/lib/commands/server_handshake.js +203 -0
- package/lib/compressed_protocol.js +127 -0
- package/lib/connection.js +12 -0
- package/lib/connection_config.js +326 -0
- package/lib/constants/charset_encodings.js +316 -0
- package/lib/constants/charsets.js +317 -0
- package/lib/constants/client.js +40 -0
- package/lib/constants/commands.js +36 -0
- package/lib/constants/cursor.js +8 -0
- package/lib/constants/encoding_charset.js +50 -0
- package/lib/constants/errors.js +3973 -0
- package/lib/constants/field_flags.js +20 -0
- package/lib/constants/server_status.js +44 -0
- package/lib/constants/session_track.js +11 -0
- package/lib/constants/ssl_profiles.js +11 -0
- package/lib/constants/types.js +64 -0
- package/lib/create_connection.js +10 -0
- package/lib/create_pool.js +10 -0
- package/lib/create_pool_cluster.js +9 -0
- package/lib/helpers.js +86 -0
- package/lib/packet_parser.js +195 -0
- package/lib/packets/auth_next_factor.js +35 -0
- package/lib/packets/auth_switch_request.js +38 -0
- package/lib/packets/auth_switch_request_more_data.js +33 -0
- package/lib/packets/auth_switch_response.js +30 -0
- package/lib/packets/binary_row.js +95 -0
- package/lib/packets/binlog_dump.js +33 -0
- package/lib/packets/binlog_query_statusvars.js +115 -0
- package/lib/packets/change_user.js +97 -0
- package/lib/packets/close_statement.js +21 -0
- package/lib/packets/column_definition.js +291 -0
- package/lib/packets/execute.js +214 -0
- package/lib/packets/handshake.js +112 -0
- package/lib/packets/handshake_response.js +144 -0
- package/lib/packets/index.js +152 -0
- package/lib/packets/packet.js +931 -0
- package/lib/packets/prepare_statement.js +27 -0
- package/lib/packets/prepared_statement_header.js +16 -0
- package/lib/packets/query.js +27 -0
- package/lib/packets/register_slave.js +46 -0
- package/lib/packets/resultset_header.js +124 -0
- package/lib/packets/ssl_request.js +25 -0
- package/lib/packets/text_row.js +47 -0
- package/lib/parsers/binary_parser.js +235 -0
- package/lib/parsers/parser_cache.js +68 -0
- package/lib/parsers/static_binary_parser.js +213 -0
- package/lib/parsers/static_text_parser.js +152 -0
- package/lib/parsers/string.js +50 -0
- package/lib/parsers/text_parser.js +214 -0
- package/lib/pool.js +12 -0
- package/lib/pool_cluster.js +369 -0
- package/lib/pool_config.js +30 -0
- package/lib/pool_connection.js +12 -0
- package/lib/promise/connection.js +222 -0
- package/lib/promise/inherit_events.js +27 -0
- package/lib/promise/make_done_cb.js +19 -0
- package/lib/promise/pool.js +112 -0
- package/lib/promise/pool_cluster.js +54 -0
- package/lib/promise/pool_connection.js +19 -0
- package/lib/promise/prepared_statement_info.js +32 -0
- package/lib/results_stream.js +38 -0
- package/lib/server.js +37 -0
- package/package.json +80 -0
- package/promise.d.ts +131 -0
- package/promise.js +202 -0
- package/typings/mysql/LICENSE.txt +15 -0
- package/typings/mysql/index.d.ts +95 -0
- package/typings/mysql/info.txt +1 -0
- package/typings/mysql/lib/Auth.d.ts +30 -0
- package/typings/mysql/lib/Connection.d.ts +453 -0
- package/typings/mysql/lib/Pool.d.ts +69 -0
- package/typings/mysql/lib/PoolCluster.d.ts +90 -0
- package/typings/mysql/lib/PoolConnection.d.ts +10 -0
- package/typings/mysql/lib/Server.d.ts +11 -0
- package/typings/mysql/lib/constants/CharsetToEncoding.d.ts +8 -0
- package/typings/mysql/lib/constants/Charsets.d.ts +326 -0
- package/typings/mysql/lib/constants/Types.d.ts +70 -0
- package/typings/mysql/lib/constants/index.d.ts +5 -0
- package/typings/mysql/lib/parsers/ParserCache.d.ts +4 -0
- package/typings/mysql/lib/parsers/index.d.ts +18 -0
- package/typings/mysql/lib/parsers/typeCast.d.ts +54 -0
- package/typings/mysql/lib/protocol/packets/Field.d.ts +10 -0
- package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +27 -0
- package/typings/mysql/lib/protocol/packets/OkPacket.d.ts +23 -0
- package/typings/mysql/lib/protocol/packets/ProcedurePacket.d.ts +13 -0
- package/typings/mysql/lib/protocol/packets/ResultSetHeader.d.ts +18 -0
- package/typings/mysql/lib/protocol/packets/RowDataPacket.d.ts +9 -0
- package/typings/mysql/lib/protocol/packets/index.d.ts +28 -0
- package/typings/mysql/lib/protocol/packets/params/ErrorPacketParams.d.ts +6 -0
- package/typings/mysql/lib/protocol/packets/params/OkPacketParams.d.ts +9 -0
- package/typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts +40 -0
- package/typings/mysql/lib/protocol/sequences/Prepare.d.ts +65 -0
- package/typings/mysql/lib/protocol/sequences/Query.d.ts +170 -0
- package/typings/mysql/lib/protocol/sequences/QueryableBase.d.ts +40 -0
- package/typings/mysql/lib/protocol/sequences/Sequence.d.ts +5 -0
- package/typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts +21 -0
- package/typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts +21 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const process = require('process');
|
|
4
|
+
const Timers = require('timers');
|
|
5
|
+
|
|
6
|
+
const Readable = require('stream').Readable;
|
|
7
|
+
|
|
8
|
+
const Command = require('./command.js');
|
|
9
|
+
const Packets = require('../packets/index.js');
|
|
10
|
+
const getTextParser = require('../parsers/text_parser.js');
|
|
11
|
+
const staticParser = require('../parsers/static_text_parser.js');
|
|
12
|
+
const ServerStatus = require('../constants/server_status.js');
|
|
13
|
+
|
|
14
|
+
const EmptyPacket = new Packets.Packet(0, Buffer.allocUnsafe(4), 0, 4);
|
|
15
|
+
|
|
16
|
+
// http://dev.mysql.com/doc/internals/en/com-query.html
|
|
17
|
+
class Query extends Command {
|
|
18
|
+
constructor(options, callback) {
|
|
19
|
+
super();
|
|
20
|
+
this.sql = options.sql;
|
|
21
|
+
this.values = options.values;
|
|
22
|
+
this._queryOptions = options;
|
|
23
|
+
this.namedPlaceholders = options.namedPlaceholders || false;
|
|
24
|
+
this.onResult = callback;
|
|
25
|
+
this.timeout = options.timeout;
|
|
26
|
+
this.queryTimeout = null;
|
|
27
|
+
this._fieldCount = 0;
|
|
28
|
+
this._rowParser = null;
|
|
29
|
+
this._fields = [];
|
|
30
|
+
this._rows = [];
|
|
31
|
+
this._receivedFieldsCount = 0;
|
|
32
|
+
this._resultIndex = 0;
|
|
33
|
+
this._localStream = null;
|
|
34
|
+
this._unpipeStream = function () {};
|
|
35
|
+
this._streamFactory = options.infileStreamFactory;
|
|
36
|
+
this._connection = null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
then() {
|
|
40
|
+
const err =
|
|
41
|
+
"You have tried to call .then(), .catch(), or invoked await on the result of query that is not a promise, which is a programming error. Try calling con.promise().query(), or require('mysql2/promise') instead of 'mysql2' for a promise-compatible version of the query interface. To learn how to use async/await or Promises check out documentation at https://sidorares.github.io/node-mysql2/docs#using-promise-wrapper, or the mysql2 documentation at https://sidorares.github.io/node-mysql2/docs/documentation/promise-wrapper";
|
|
42
|
+
// eslint-disable-next-line
|
|
43
|
+
console.log(err);
|
|
44
|
+
throw new Error(err);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
|
48
|
+
start(_packet, connection) {
|
|
49
|
+
if (connection.config.debug) {
|
|
50
|
+
// eslint-disable-next-line
|
|
51
|
+
console.log(' Sending query command: %s', this.sql);
|
|
52
|
+
}
|
|
53
|
+
this._connection = connection;
|
|
54
|
+
this.options = Object.assign({}, connection.config, this._queryOptions);
|
|
55
|
+
this._setTimeout();
|
|
56
|
+
|
|
57
|
+
const cmdPacket = new Packets.Query(
|
|
58
|
+
this.sql,
|
|
59
|
+
connection.config.charsetNumber
|
|
60
|
+
);
|
|
61
|
+
connection.writePacket(cmdPacket.toPacket(1));
|
|
62
|
+
return Query.prototype.resultsetHeader;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
done() {
|
|
66
|
+
this._unpipeStream();
|
|
67
|
+
// if all ready timeout, return null directly
|
|
68
|
+
if (this.timeout && !this.queryTimeout) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// else clear timer
|
|
72
|
+
if (this.queryTimeout) {
|
|
73
|
+
Timers.clearTimeout(this.queryTimeout);
|
|
74
|
+
this.queryTimeout = null;
|
|
75
|
+
}
|
|
76
|
+
if (this.onResult) {
|
|
77
|
+
let rows, fields;
|
|
78
|
+
if (this._resultIndex === 0) {
|
|
79
|
+
rows = this._rows[0];
|
|
80
|
+
fields = this._fields[0];
|
|
81
|
+
} else {
|
|
82
|
+
rows = this._rows;
|
|
83
|
+
fields = this._fields;
|
|
84
|
+
}
|
|
85
|
+
if (fields) {
|
|
86
|
+
process.nextTick(() => {
|
|
87
|
+
this.onResult(null, rows, fields);
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
process.nextTick(() => {
|
|
91
|
+
this.onResult(null, rows);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
doneInsert(rs) {
|
|
99
|
+
if (this._localStreamError) {
|
|
100
|
+
if (this.onResult) {
|
|
101
|
+
this.onResult(this._localStreamError, rs);
|
|
102
|
+
} else {
|
|
103
|
+
this.emit('error', this._localStreamError);
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
this._rows.push(rs);
|
|
108
|
+
this._fields.push(void 0);
|
|
109
|
+
this.emit('fields', void 0);
|
|
110
|
+
this.emit('result', rs);
|
|
111
|
+
if (rs.serverStatus & ServerStatus.SERVER_MORE_RESULTS_EXISTS) {
|
|
112
|
+
this._resultIndex++;
|
|
113
|
+
return this.resultsetHeader;
|
|
114
|
+
}
|
|
115
|
+
return this.done();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
resultsetHeader(packet, connection) {
|
|
119
|
+
const rs = new Packets.ResultSetHeader(packet, connection);
|
|
120
|
+
this._fieldCount = rs.fieldCount;
|
|
121
|
+
if (connection.config.debug) {
|
|
122
|
+
// eslint-disable-next-line
|
|
123
|
+
console.log(
|
|
124
|
+
` Resultset header received, expecting ${rs.fieldCount} column definition packets`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
if (this._fieldCount === 0) {
|
|
128
|
+
return this.doneInsert(rs);
|
|
129
|
+
}
|
|
130
|
+
if (this._fieldCount === null) {
|
|
131
|
+
return this._streamLocalInfile(connection, rs.infileName);
|
|
132
|
+
}
|
|
133
|
+
this._receivedFieldsCount = 0;
|
|
134
|
+
this._rows.push([]);
|
|
135
|
+
this._fields.push([]);
|
|
136
|
+
return this.readField;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_streamLocalInfile(connection, path) {
|
|
140
|
+
if (this._streamFactory) {
|
|
141
|
+
this._localStream = this._streamFactory(path);
|
|
142
|
+
} else {
|
|
143
|
+
this._localStreamError = new Error(
|
|
144
|
+
`As a result of LOCAL INFILE command server wants to read ${path} file, but as of v2.0 you must provide streamFactory option returning ReadStream.`
|
|
145
|
+
);
|
|
146
|
+
connection.writePacket(EmptyPacket);
|
|
147
|
+
return this.infileOk;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const onConnectionError = () => {
|
|
151
|
+
this._unpipeStream();
|
|
152
|
+
};
|
|
153
|
+
const onDrain = () => {
|
|
154
|
+
this._localStream.resume();
|
|
155
|
+
};
|
|
156
|
+
const onPause = () => {
|
|
157
|
+
this._localStream.pause();
|
|
158
|
+
};
|
|
159
|
+
const onData = function (data) {
|
|
160
|
+
const dataWithHeader = Buffer.allocUnsafe(data.length + 4);
|
|
161
|
+
data.copy(dataWithHeader, 4);
|
|
162
|
+
connection.writePacket(
|
|
163
|
+
new Packets.Packet(0, dataWithHeader, 0, dataWithHeader.length)
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
const onEnd = () => {
|
|
167
|
+
connection.removeListener('error', onConnectionError);
|
|
168
|
+
connection.writePacket(EmptyPacket);
|
|
169
|
+
};
|
|
170
|
+
const onError = (err) => {
|
|
171
|
+
this._localStreamError = err;
|
|
172
|
+
connection.removeListener('error', onConnectionError);
|
|
173
|
+
connection.writePacket(EmptyPacket);
|
|
174
|
+
};
|
|
175
|
+
this._unpipeStream = () => {
|
|
176
|
+
connection.stream.removeListener('pause', onPause);
|
|
177
|
+
connection.stream.removeListener('drain', onDrain);
|
|
178
|
+
this._localStream.removeListener('data', onData);
|
|
179
|
+
this._localStream.removeListener('end', onEnd);
|
|
180
|
+
this._localStream.removeListener('error', onError);
|
|
181
|
+
};
|
|
182
|
+
connection.stream.on('pause', onPause);
|
|
183
|
+
connection.stream.on('drain', onDrain);
|
|
184
|
+
this._localStream.on('data', onData);
|
|
185
|
+
this._localStream.on('end', onEnd);
|
|
186
|
+
this._localStream.on('error', onError);
|
|
187
|
+
connection.once('error', onConnectionError);
|
|
188
|
+
return this.infileOk;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
readField(packet, connection) {
|
|
192
|
+
this._receivedFieldsCount++;
|
|
193
|
+
// Often there is much more data in the column definition than in the row itself
|
|
194
|
+
// If you set manually _fields[0] to array of ColumnDefinition's (from previous call)
|
|
195
|
+
// you can 'cache' result of parsing. Field packets still received, but ignored in that case
|
|
196
|
+
// this is the reason _receivedFieldsCount exist (otherwise we could just use current length of fields array)
|
|
197
|
+
if (this._fields[this._resultIndex].length !== this._fieldCount) {
|
|
198
|
+
const field = new Packets.ColumnDefinition(
|
|
199
|
+
packet,
|
|
200
|
+
connection.clientEncoding
|
|
201
|
+
);
|
|
202
|
+
this._fields[this._resultIndex].push(field);
|
|
203
|
+
if (connection.config.debug) {
|
|
204
|
+
/* eslint-disable no-console */
|
|
205
|
+
console.log(' Column definition:');
|
|
206
|
+
console.log(` name: ${field.name}`);
|
|
207
|
+
console.log(` type: ${field.columnType}`);
|
|
208
|
+
console.log(` flags: ${field.flags}`);
|
|
209
|
+
/* eslint-enable no-console */
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// last field received
|
|
213
|
+
if (this._receivedFieldsCount === this._fieldCount) {
|
|
214
|
+
const fields = this._fields[this._resultIndex];
|
|
215
|
+
this.emit('fields', fields);
|
|
216
|
+
if (this.options.disableEval) {
|
|
217
|
+
this._rowParser = staticParser(fields, this.options, connection.config);
|
|
218
|
+
} else {
|
|
219
|
+
this._rowParser = new (getTextParser(
|
|
220
|
+
fields,
|
|
221
|
+
this.options,
|
|
222
|
+
connection.config
|
|
223
|
+
))(fields);
|
|
224
|
+
}
|
|
225
|
+
return Query.prototype.fieldsEOF;
|
|
226
|
+
}
|
|
227
|
+
return Query.prototype.readField;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
fieldsEOF(packet, connection) {
|
|
231
|
+
// check EOF
|
|
232
|
+
if (!packet.isEOF()) {
|
|
233
|
+
return connection.protocolError('Expected EOF packet');
|
|
234
|
+
}
|
|
235
|
+
return this.row;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
|
239
|
+
row(packet, _connection) {
|
|
240
|
+
if (packet.isEOF()) {
|
|
241
|
+
const status = packet.eofStatusFlags();
|
|
242
|
+
const moreResults = status & ServerStatus.SERVER_MORE_RESULTS_EXISTS;
|
|
243
|
+
if (moreResults) {
|
|
244
|
+
this._resultIndex++;
|
|
245
|
+
return Query.prototype.resultsetHeader;
|
|
246
|
+
}
|
|
247
|
+
return this.done();
|
|
248
|
+
}
|
|
249
|
+
let row;
|
|
250
|
+
try {
|
|
251
|
+
row = this._rowParser.next(
|
|
252
|
+
packet,
|
|
253
|
+
this._fields[this._resultIndex],
|
|
254
|
+
this.options
|
|
255
|
+
);
|
|
256
|
+
} catch (err) {
|
|
257
|
+
this._localStreamError = err;
|
|
258
|
+
return this.doneInsert(null);
|
|
259
|
+
}
|
|
260
|
+
if (this.onResult) {
|
|
261
|
+
this._rows[this._resultIndex].push(row);
|
|
262
|
+
} else {
|
|
263
|
+
this.emit('result', row, this._resultIndex);
|
|
264
|
+
}
|
|
265
|
+
return Query.prototype.row;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
infileOk(packet, connection) {
|
|
269
|
+
const rs = new Packets.ResultSetHeader(packet, connection);
|
|
270
|
+
return this.doneInsert(rs);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
stream(options) {
|
|
274
|
+
options = options || Object.create(null);
|
|
275
|
+
options.objectMode = true;
|
|
276
|
+
|
|
277
|
+
const stream = new Readable({
|
|
278
|
+
...options,
|
|
279
|
+
emitClose: true,
|
|
280
|
+
autoDestroy: true,
|
|
281
|
+
read: () => {
|
|
282
|
+
this._connection && this._connection.resume();
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Prevent a breaking change for users that rely on `end` event
|
|
287
|
+
stream.once('close', () => {
|
|
288
|
+
if (!stream.readableEnded) {
|
|
289
|
+
stream.emit('end');
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const onResult = (row, index) => {
|
|
294
|
+
if (stream.destroyed) return;
|
|
295
|
+
|
|
296
|
+
if (!stream.push(row)) {
|
|
297
|
+
this._connection && this._connection.pause();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
stream.emit('result', row, index); // replicate old emitter
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const onFields = (fields) => {
|
|
304
|
+
if (stream.destroyed) return;
|
|
305
|
+
|
|
306
|
+
stream.emit('fields', fields); // replicate old emitter
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const onEnd = () => {
|
|
310
|
+
if (stream.destroyed) return;
|
|
311
|
+
|
|
312
|
+
stream.push(null); // pushing null, indicating EOF
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const onError = (err) => {
|
|
316
|
+
stream.destroy(err);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
stream._destroy = (err, cb) => {
|
|
320
|
+
this._connection && this._connection.resume();
|
|
321
|
+
|
|
322
|
+
this.removeListener('result', onResult);
|
|
323
|
+
this.removeListener('fields', onFields);
|
|
324
|
+
this.removeListener('end', onEnd);
|
|
325
|
+
this.removeListener('error', onError);
|
|
326
|
+
|
|
327
|
+
cb(err); // Pass on any errors
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
this.on('result', onResult);
|
|
331
|
+
this.on('fields', onFields);
|
|
332
|
+
this.on('end', onEnd);
|
|
333
|
+
this.on('error', onError);
|
|
334
|
+
|
|
335
|
+
return stream;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_setTimeout() {
|
|
339
|
+
if (this.timeout) {
|
|
340
|
+
const timeoutHandler = this._handleTimeoutError.bind(this);
|
|
341
|
+
this.queryTimeout = Timers.setTimeout(timeoutHandler, this.timeout);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
_handleTimeoutError() {
|
|
346
|
+
if (this.queryTimeout) {
|
|
347
|
+
Timers.clearTimeout(this.queryTimeout);
|
|
348
|
+
this.queryTimeout = null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const err = new Error('Query inactivity timeout');
|
|
352
|
+
err.errorno = 'PROTOCOL_SEQUENCE_TIMEOUT';
|
|
353
|
+
err.code = 'PROTOCOL_SEQUENCE_TIMEOUT';
|
|
354
|
+
err.syscall = 'query';
|
|
355
|
+
|
|
356
|
+
if (this.onResult) {
|
|
357
|
+
this.onResult(err);
|
|
358
|
+
} else {
|
|
359
|
+
this.emit('error', err);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
Query.prototype.catch = Query.prototype.then;
|
|
365
|
+
|
|
366
|
+
module.exports = Query;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command.js');
|
|
4
|
+
const CommandCode = require('../constants/commands.js');
|
|
5
|
+
const Packet = require('../packets/packet.js');
|
|
6
|
+
|
|
7
|
+
class Quit extends Command {
|
|
8
|
+
constructor(callback) {
|
|
9
|
+
super();
|
|
10
|
+
this.onResult = callback;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
start(packet, connection) {
|
|
14
|
+
connection._closing = true;
|
|
15
|
+
const quit = new Packet(
|
|
16
|
+
0,
|
|
17
|
+
Buffer.from([1, 0, 0, 0, CommandCode.QUIT]),
|
|
18
|
+
0,
|
|
19
|
+
5
|
|
20
|
+
);
|
|
21
|
+
if (this.onResult) {
|
|
22
|
+
this.onResult();
|
|
23
|
+
}
|
|
24
|
+
connection.writePacket(quit);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = Quit;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Command = require('./command');
|
|
4
|
+
const Packets = require('../packets');
|
|
5
|
+
|
|
6
|
+
class RegisterSlave extends Command {
|
|
7
|
+
constructor(opts, callback) {
|
|
8
|
+
super();
|
|
9
|
+
this.onResult = callback;
|
|
10
|
+
this.opts = opts;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
start(packet, connection) {
|
|
14
|
+
const newPacket = new Packets.RegisterSlave(this.opts);
|
|
15
|
+
connection.writePacket(newPacket.toPacket(1));
|
|
16
|
+
return RegisterSlave.prototype.registerResponse;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
registerResponse() {
|
|
20
|
+
if (this.onResult) {
|
|
21
|
+
process.nextTick(this.onResult.bind(this));
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = RegisterSlave;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const CommandCode = require('../constants/commands.js');
|
|
4
|
+
const Errors = require('../constants/errors.js');
|
|
5
|
+
|
|
6
|
+
const Command = require('./command.js');
|
|
7
|
+
const Packets = require('../packets/index.js');
|
|
8
|
+
|
|
9
|
+
class ServerHandshake extends Command {
|
|
10
|
+
constructor(args) {
|
|
11
|
+
super();
|
|
12
|
+
this.args = args;
|
|
13
|
+
/*
|
|
14
|
+
this.protocolVersion = args.protocolVersion || 10;
|
|
15
|
+
this.serverVersion = args.serverVersion;
|
|
16
|
+
this.connectionId = args.connectionId,
|
|
17
|
+
this.statusFlags = args.statusFlags,
|
|
18
|
+
this.characterSet = args.characterSet,
|
|
19
|
+
this.capabilityFlags = args.capabilityFlags || 512;
|
|
20
|
+
*/
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
start(packet, connection) {
|
|
24
|
+
const serverHelloPacket = new Packets.Handshake(this.args);
|
|
25
|
+
this.serverHello = serverHelloPacket;
|
|
26
|
+
serverHelloPacket.setScrambleData((err) => {
|
|
27
|
+
if (err) {
|
|
28
|
+
connection.emit('error', new Error('Error generating random bytes'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
connection.writePacket(serverHelloPacket.toPacket(0));
|
|
32
|
+
});
|
|
33
|
+
return ServerHandshake.prototype.readClientReply;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
readClientReply(packet, connection) {
|
|
37
|
+
// check auth here
|
|
38
|
+
const clientHelloReply = Packets.HandshakeResponse.fromPacket(packet);
|
|
39
|
+
// TODO check we don't have something similar already
|
|
40
|
+
connection.clientHelloReply = clientHelloReply;
|
|
41
|
+
if (this.args.authCallback) {
|
|
42
|
+
this.args.authCallback(
|
|
43
|
+
{
|
|
44
|
+
user: clientHelloReply.user,
|
|
45
|
+
database: clientHelloReply.database,
|
|
46
|
+
address: connection.stream.remoteAddress,
|
|
47
|
+
authPluginData1: this.serverHello.authPluginData1,
|
|
48
|
+
authPluginData2: this.serverHello.authPluginData2,
|
|
49
|
+
authToken: clientHelloReply.authToken,
|
|
50
|
+
},
|
|
51
|
+
(err, mysqlError) => {
|
|
52
|
+
// if (err)
|
|
53
|
+
if (!mysqlError) {
|
|
54
|
+
connection.writeOk();
|
|
55
|
+
} else {
|
|
56
|
+
// TODO create constants / errorToCode
|
|
57
|
+
// 1045 = ER_ACCESS_DENIED_ERROR
|
|
58
|
+
connection.writeError({
|
|
59
|
+
message: mysqlError.message || '',
|
|
60
|
+
code: mysqlError.code || 1045,
|
|
61
|
+
});
|
|
62
|
+
connection.close();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
connection.writeOk();
|
|
68
|
+
}
|
|
69
|
+
return ServerHandshake.prototype.dispatchCommands;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_isStatement(query, name) {
|
|
73
|
+
const firstWord = query.split(' ')[0].toUpperCase();
|
|
74
|
+
return firstWord === name;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
dispatchCommands(packet, connection) {
|
|
78
|
+
// command from client to server
|
|
79
|
+
let knownCommand = true;
|
|
80
|
+
const encoding = connection.clientHelloReply.encoding;
|
|
81
|
+
const commandCode = packet.readInt8();
|
|
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: 'No query handler for prepared statements.',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case CommandCode.STMT_EXECUTE:
|
|
95
|
+
if (connection.listeners('stmt_execute').length) {
|
|
96
|
+
const { stmtId, flags, iterationCount, values } =
|
|
97
|
+
Packets.Execute.fromPacket(packet, encoding);
|
|
98
|
+
connection.emit(
|
|
99
|
+
'stmt_execute',
|
|
100
|
+
stmtId,
|
|
101
|
+
flags,
|
|
102
|
+
iterationCount,
|
|
103
|
+
values
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
connection.writeError({
|
|
107
|
+
code: Errors.HA_ERR_INTERNAL_ERROR,
|
|
108
|
+
message: 'No query handler for execute statements.',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case CommandCode.QUIT:
|
|
113
|
+
if (connection.listeners('quit').length) {
|
|
114
|
+
connection.emit('quit');
|
|
115
|
+
} else {
|
|
116
|
+
connection.stream.end();
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case CommandCode.INIT_DB:
|
|
120
|
+
if (connection.listeners('init_db').length) {
|
|
121
|
+
const schemaName = packet.readString(undefined, encoding);
|
|
122
|
+
connection.emit('init_db', schemaName);
|
|
123
|
+
} else {
|
|
124
|
+
connection.writeOk();
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
case CommandCode.QUERY:
|
|
128
|
+
if (connection.listeners('query').length) {
|
|
129
|
+
const query = packet.readString(undefined, encoding);
|
|
130
|
+
if (
|
|
131
|
+
this._isStatement(query, 'PREPARE') ||
|
|
132
|
+
this._isStatement(query, 'SET')
|
|
133
|
+
) {
|
|
134
|
+
connection.emit('stmt_prepare', query);
|
|
135
|
+
} else if (this._isStatement(query, 'EXECUTE')) {
|
|
136
|
+
connection.emit('stmt_execute', null, null, null, null, query);
|
|
137
|
+
} else connection.emit('query', query);
|
|
138
|
+
} else {
|
|
139
|
+
connection.writeError({
|
|
140
|
+
code: Errors.HA_ERR_INTERNAL_ERROR,
|
|
141
|
+
message: 'No query handler',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
case CommandCode.FIELD_LIST:
|
|
146
|
+
if (connection.listeners('field_list').length) {
|
|
147
|
+
const table = packet.readNullTerminatedString(encoding);
|
|
148
|
+
const fields = packet.readString(undefined, encoding);
|
|
149
|
+
connection.emit('field_list', table, fields);
|
|
150
|
+
} else {
|
|
151
|
+
connection.writeError({
|
|
152
|
+
code: Errors.ER_WARN_DEPRECATED_SYNTAX,
|
|
153
|
+
message:
|
|
154
|
+
'As of MySQL 5.7.11, COM_FIELD_LIST is deprecated and will be removed in a future version of MySQL.',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
case CommandCode.PING:
|
|
159
|
+
if (connection.listeners('ping').length) {
|
|
160
|
+
connection.emit('ping');
|
|
161
|
+
} else {
|
|
162
|
+
connection.writeOk();
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
knownCommand = false;
|
|
167
|
+
}
|
|
168
|
+
if (connection.listeners('packet').length) {
|
|
169
|
+
connection.emit('packet', packet.clone(), knownCommand, commandCode);
|
|
170
|
+
} else if (!knownCommand) {
|
|
171
|
+
// eslint-disable-next-line no-console
|
|
172
|
+
console.log('Unknown command:', commandCode);
|
|
173
|
+
}
|
|
174
|
+
return ServerHandshake.prototype.dispatchCommands;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = ServerHandshake;
|
|
179
|
+
|
|
180
|
+
// TODO: implement server-side 4.1 authentication
|
|
181
|
+
/*
|
|
182
|
+
4.1 authentication: (http://bazaar.launchpad.net/~mysql/mysql-server/5.5/view/head:/sql/password.c)
|
|
183
|
+
|
|
184
|
+
SERVER: public_seed=create_random_string()
|
|
185
|
+
send(public_seed)
|
|
186
|
+
|
|
187
|
+
CLIENT: recv(public_seed)
|
|
188
|
+
hash_stage1=sha1("password")
|
|
189
|
+
hash_stage2=sha1(hash_stage1)
|
|
190
|
+
reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
|
|
191
|
+
|
|
192
|
+
// this three steps are done in scramble()
|
|
193
|
+
|
|
194
|
+
send(reply)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
SERVER: recv(reply)
|
|
198
|
+
hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
|
|
199
|
+
candidate_hash2=sha1(hash_stage1)
|
|
200
|
+
check(candidate_hash2==hash_stage2)
|
|
201
|
+
|
|
202
|
+
server stores sha1(sha1(password)) ( hash_stag2)
|
|
203
|
+
*/
|