@tachybase/module-multi-app 0.23.8
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/.turbo/turbo-build.log +14 -0
- package/README.md +34 -0
- package/README.zh-CN.md +34 -0
- package/client.d.ts +1 -0
- package/client.js +1 -0
- package/dist/client/AppManager.d.ts +2 -0
- package/dist/client/AppNameInput.d.ts +2 -0
- package/dist/client/MultiAppBlockInitializer.d.ts +2 -0
- package/dist/client/MultiAppManagerProvider.d.ts +2 -0
- package/dist/client/MultiAppManagerProvider.style.d.ts +5 -0
- package/dist/client/Settings.d.ts +2 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +1 -0
- package/dist/client/settings/schemas/applications.d.ts +13 -0
- package/dist/client/utils.d.ts +4 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +27 -0
- package/dist/externalVersion.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -0
- package/dist/locale/en-US.json +27 -0
- package/dist/locale/es-ES.json +9 -0
- package/dist/locale/ko_KR.json +11 -0
- package/dist/locale/pt-BR.json +9 -0
- package/dist/locale/zh-CN.json +27 -0
- package/dist/node_modules/mariadb/LICENSE +502 -0
- package/dist/node_modules/mariadb/callback.js +41 -0
- package/dist/node_modules/mariadb/lib/cmd/batch-bulk.js +278 -0
- package/dist/node_modules/mariadb/lib/cmd/batch-rewrite.js +372 -0
- package/dist/node_modules/mariadb/lib/cmd/change-user.js +149 -0
- package/dist/node_modules/mariadb/lib/cmd/class/ok-packet.js +17 -0
- package/dist/node_modules/mariadb/lib/cmd/column-definition.js +102 -0
- package/dist/node_modules/mariadb/lib/cmd/command.js +168 -0
- package/dist/node_modules/mariadb/lib/cmd/common-binary-cmd.js +327 -0
- package/dist/node_modules/mariadb/lib/cmd/common-text-cmd.js +427 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/caching-sha2-password-auth.js +168 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/clear-password-auth.js +23 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/ed25519-password-auth.js +761 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/native-password-auth.js +55 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/pam-password-auth.js +58 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/plugin-auth.js +19 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/sha256-password-auth.js +142 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-capabilities.js +74 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-handshake-response.js +126 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/handshake.js +292 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/initial-handshake.js +74 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/ssl-request.js +29 -0
- package/dist/node_modules/mariadb/lib/cmd/ping.js +52 -0
- package/dist/node_modules/mariadb/lib/cmd/query.js +255 -0
- package/dist/node_modules/mariadb/lib/cmd/quit.js +28 -0
- package/dist/node_modules/mariadb/lib/cmd/reset.js +54 -0
- package/dist/node_modules/mariadb/lib/cmd/resultset.js +607 -0
- package/dist/node_modules/mariadb/lib/cmd/stream.js +45 -0
- package/dist/node_modules/mariadb/lib/config/connection-options.js +258 -0
- package/dist/node_modules/mariadb/lib/config/pool-cluster-options.js +19 -0
- package/dist/node_modules/mariadb/lib/config/pool-options.js +47 -0
- package/dist/node_modules/mariadb/lib/connection-callback.js +160 -0
- package/dist/node_modules/mariadb/lib/connection.js +1460 -0
- package/dist/node_modules/mariadb/lib/const/capabilities.js +64 -0
- package/dist/node_modules/mariadb/lib/const/collations.js +473 -0
- package/dist/node_modules/mariadb/lib/const/connection_status.js +13 -0
- package/dist/node_modules/mariadb/lib/const/error-code.js +1282 -0
- package/dist/node_modules/mariadb/lib/const/field-detail.js +35 -0
- package/dist/node_modules/mariadb/lib/const/field-type.js +71 -0
- package/dist/node_modules/mariadb/lib/const/server-status.js +30 -0
- package/dist/node_modules/mariadb/lib/const/state-change.js +12 -0
- package/dist/node_modules/mariadb/lib/filtered-pool-cluster.js +81 -0
- package/dist/node_modules/mariadb/lib/io/bulk-packet.js +590 -0
- package/dist/node_modules/mariadb/lib/io/compression-input-stream.js +141 -0
- package/dist/node_modules/mariadb/lib/io/compression-output-stream.js +171 -0
- package/dist/node_modules/mariadb/lib/io/packet-input-stream.js +193 -0
- package/dist/node_modules/mariadb/lib/io/packet-node-encoded.js +36 -0
- package/dist/node_modules/mariadb/lib/io/packet-node-iconv.js +37 -0
- package/dist/node_modules/mariadb/lib/io/packet-output-stream.js +502 -0
- package/dist/node_modules/mariadb/lib/io/packet.js +515 -0
- package/dist/node_modules/mariadb/lib/io/rewrite-packet.js +481 -0
- package/dist/node_modules/mariadb/lib/misc/connection-information.js +96 -0
- package/dist/node_modules/mariadb/lib/misc/errors.js +123 -0
- package/dist/node_modules/mariadb/lib/misc/parse.js +1033 -0
- package/dist/node_modules/mariadb/lib/misc/utils.js +298 -0
- package/dist/node_modules/mariadb/lib/pool-base.js +611 -0
- package/dist/node_modules/mariadb/lib/pool-callback.js +202 -0
- package/dist/node_modules/mariadb/lib/pool-cluster-callback.js +66 -0
- package/dist/node_modules/mariadb/lib/pool-cluster.js +407 -0
- package/dist/node_modules/mariadb/lib/pool-promise.js +108 -0
- package/dist/node_modules/mariadb/package.json +1 -0
- package/dist/node_modules/mariadb/promise.js +34 -0
- package/dist/node_modules/mariadb/types/index.d.ts +870 -0
- package/dist/server/actions/apps.d.ts +5 -0
- package/dist/server/actions/apps.js +117 -0
- package/dist/server/app-lifecycle.d.ts +8 -0
- package/dist/server/app-lifecycle.js +99 -0
- package/dist/server/app-start-env.d.ts +2 -0
- package/dist/server/app-start-env.js +105 -0
- package/dist/server/collections/applications.d.ts +2 -0
- package/dist/server/collections/applications.js +82 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server/index.js +29 -0
- package/dist/server/middlewares/app-selector.d.ts +1 -0
- package/dist/server/middlewares/app-selector.js +47 -0
- package/dist/server/middlewares/index.d.ts +2 -0
- package/dist/server/middlewares/index.js +23 -0
- package/dist/server/middlewares/inject-app-list.d.ts +1 -0
- package/dist/server/middlewares/inject-app-list.js +48 -0
- package/dist/server/migrations/20240820153000-add-apps-tmpl.d.ts +6 -0
- package/dist/server/migrations/20240820153000-add-apps-tmpl.js +47 -0
- package/dist/server/migrations/20241126124904-add-createdBy.d.ts +6 -0
- package/dist/server/migrations/20241126124904-add-createdBy.js +41 -0
- package/dist/server/models/application.d.ts +10 -0
- package/dist/server/models/application.js +57 -0
- package/dist/server/server.d.ts +19 -0
- package/dist/server/server.js +246 -0
- package/dist/swagger/index.d.ts +197 -0
- package/dist/swagger/index.js +227 -0
- package/package.json +38 -0
- package/server.d.ts +2 -0
- package/server.js +1 -0
|
@@ -0,0 +1,1460 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const EventEmitter = require('events');
|
|
4
|
+
const util = require('util');
|
|
5
|
+
const Queue = require('denque');
|
|
6
|
+
const Net = require('net');
|
|
7
|
+
const PacketInputStream = require('./io/packet-input-stream');
|
|
8
|
+
const PacketOutputStream = require('./io/packet-output-stream');
|
|
9
|
+
const CompressionInputStream = require('./io/compression-input-stream');
|
|
10
|
+
const CompressionOutputStream = require('./io/compression-output-stream');
|
|
11
|
+
const ServerStatus = require('./const/server-status');
|
|
12
|
+
const ConnectionInformation = require('./misc/connection-information');
|
|
13
|
+
const tls = require('tls');
|
|
14
|
+
const Errors = require('./misc/errors');
|
|
15
|
+
const Utils = require('./misc/utils');
|
|
16
|
+
const Capabilities = require('./const/capabilities');
|
|
17
|
+
const moment = require('moment-timezone');
|
|
18
|
+
|
|
19
|
+
/*commands*/
|
|
20
|
+
const Handshake = require('./cmd/handshake/handshake');
|
|
21
|
+
const Quit = require('./cmd/quit');
|
|
22
|
+
const Ping = require('./cmd/ping');
|
|
23
|
+
const Reset = require('./cmd/reset');
|
|
24
|
+
const Query = require('./cmd/query');
|
|
25
|
+
const BatchRewrite = require('./cmd/batch-rewrite');
|
|
26
|
+
const BatchBulk = require('./cmd/batch-bulk');
|
|
27
|
+
const Stream = require('./cmd/stream');
|
|
28
|
+
const ChangeUser = require('./cmd/change-user');
|
|
29
|
+
const { Status } = require('./const/connection_status');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* New Connection instance.
|
|
33
|
+
*
|
|
34
|
+
* @param options connection options
|
|
35
|
+
* @returns Connection instance
|
|
36
|
+
* @constructor
|
|
37
|
+
* @fires Connection#connect
|
|
38
|
+
* @fires Connection#end
|
|
39
|
+
* @fires Connection#error
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
function Connection(options) {
|
|
43
|
+
//*****************************************************************
|
|
44
|
+
// public API functions
|
|
45
|
+
//*****************************************************************
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Connect event
|
|
49
|
+
*
|
|
50
|
+
* @returns {Promise} promise
|
|
51
|
+
*/
|
|
52
|
+
this.connect = () => {
|
|
53
|
+
switch (_status) {
|
|
54
|
+
case Status.NOT_CONNECTED:
|
|
55
|
+
_status = Status.CONNECTING;
|
|
56
|
+
return new Promise(function (resolve, reject) {
|
|
57
|
+
_registerHandshakeCmd(resolve, reject);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
case Status.CLOSING:
|
|
61
|
+
case Status.CLOSED:
|
|
62
|
+
return Promise.reject(
|
|
63
|
+
Errors.createError(
|
|
64
|
+
'Connection closed',
|
|
65
|
+
null,
|
|
66
|
+
true,
|
|
67
|
+
info,
|
|
68
|
+
'08S01',
|
|
69
|
+
Errors.ER_CONNECTION_ALREADY_CLOSED
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
case Status.CONNECTING:
|
|
74
|
+
case Status.AUTHENTICATING:
|
|
75
|
+
return Promise.reject(
|
|
76
|
+
Errors.createError(
|
|
77
|
+
'Connection is already connecting',
|
|
78
|
+
null,
|
|
79
|
+
true,
|
|
80
|
+
info,
|
|
81
|
+
'08S01',
|
|
82
|
+
Errors.ER_ALREADY_CONNECTING
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
//status Connected
|
|
87
|
+
return Promise.resolve(this);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Permit to change user during connection.
|
|
92
|
+
* All user variables will be reset, Prepare commands will be released.
|
|
93
|
+
* !!! mysql has a bug when CONNECT_ATTRS capability is set, that is default !!!!
|
|
94
|
+
*
|
|
95
|
+
* @param options connection options
|
|
96
|
+
* @returns {Promise} promise
|
|
97
|
+
*/
|
|
98
|
+
this.changeUser = (options) => {
|
|
99
|
+
if (!info.isMariaDB()) {
|
|
100
|
+
return Promise.reject(
|
|
101
|
+
Errors.createError(
|
|
102
|
+
'method changeUser not available for MySQL server due to Bug #83472',
|
|
103
|
+
null,
|
|
104
|
+
false,
|
|
105
|
+
info,
|
|
106
|
+
'0A000',
|
|
107
|
+
Errors.ER_MYSQL_CHANGE_USER_BUG
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return new Promise(function (resolve, reject) {
|
|
113
|
+
_addCommand(
|
|
114
|
+
new ChangeUser(
|
|
115
|
+
options,
|
|
116
|
+
(res) => {
|
|
117
|
+
if (options && options.collation) opts.collation = options.collation;
|
|
118
|
+
resolve(res);
|
|
119
|
+
},
|
|
120
|
+
_authFailHandler.bind(this, reject),
|
|
121
|
+
_addCommand.bind(this)
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Start transaction
|
|
129
|
+
*
|
|
130
|
+
* @returns {Promise} promise
|
|
131
|
+
*/
|
|
132
|
+
this.beginTransaction = () => {
|
|
133
|
+
return this.query('START TRANSACTION');
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Commit a transaction.
|
|
138
|
+
*
|
|
139
|
+
* @returns {Promise} command if commit was needed only
|
|
140
|
+
*/
|
|
141
|
+
this.commit = () => {
|
|
142
|
+
return _changeTransaction('COMMIT');
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Roll back a transaction.
|
|
147
|
+
*
|
|
148
|
+
* @returns {Promise} promise
|
|
149
|
+
*/
|
|
150
|
+
this.rollback = () => {
|
|
151
|
+
return _changeTransaction('ROLLBACK');
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Execute query using text protocol.
|
|
156
|
+
*
|
|
157
|
+
* @param sql sql parameter Object can be used to supersede default option.
|
|
158
|
+
* Object must then have sql property.
|
|
159
|
+
* @param values object / array of placeholder values (not mandatory)
|
|
160
|
+
* @returns {Promise} promise
|
|
161
|
+
*/
|
|
162
|
+
this._queryPromise = (sql, values) => {
|
|
163
|
+
let _cmdOpt,
|
|
164
|
+
_sql,
|
|
165
|
+
_values = values;
|
|
166
|
+
if (typeof sql === 'object') {
|
|
167
|
+
_cmdOpt = sql;
|
|
168
|
+
_sql = _cmdOpt.sql;
|
|
169
|
+
if (_cmdOpt.values) _values = _cmdOpt.values;
|
|
170
|
+
} else {
|
|
171
|
+
_sql = sql;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return new Promise(function (resolve, reject) {
|
|
175
|
+
const cmd = new Query(resolve, reject, _cmdOpt, opts, _sql, _values);
|
|
176
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
177
|
+
_addCommand(cmd);
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Execute batch using text protocol.
|
|
183
|
+
*
|
|
184
|
+
* @param sql sql parameter Object can be used to supersede default option.
|
|
185
|
+
* Object must then have sql property.
|
|
186
|
+
* @param initialValues object / array of placeholder values (not mandatory)
|
|
187
|
+
* @returns {Promise} promise
|
|
188
|
+
*/
|
|
189
|
+
this.batch = (sql, initialValues) => {
|
|
190
|
+
let _options,
|
|
191
|
+
_sql,
|
|
192
|
+
_values = initialValues;
|
|
193
|
+
if (typeof sql === 'object') {
|
|
194
|
+
_options = sql;
|
|
195
|
+
_sql = _options.sql;
|
|
196
|
+
if (_options.values) _values = _options.values;
|
|
197
|
+
} else {
|
|
198
|
+
_sql = sql;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!_values) {
|
|
202
|
+
return Promise.reject(
|
|
203
|
+
Errors.createError(
|
|
204
|
+
'Batch must have values set',
|
|
205
|
+
_sql,
|
|
206
|
+
false,
|
|
207
|
+
info,
|
|
208
|
+
'HY000',
|
|
209
|
+
Errors.ER_BATCH_WITH_NO_VALUES
|
|
210
|
+
)
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const vals = Array.isArray(_values) ? _values : [_values];
|
|
215
|
+
|
|
216
|
+
return new Promise(function (resolve, reject) {
|
|
217
|
+
let useBulk = canUseBulk(vals);
|
|
218
|
+
|
|
219
|
+
const cmd = useBulk
|
|
220
|
+
? new BatchBulk(resolve, reject, _options, opts, _sql, vals)
|
|
221
|
+
: new BatchRewrite(resolve, reject, _options, opts, _sql, vals);
|
|
222
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
223
|
+
_addCommand(cmd);
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Execute query returning a Readable Object that will emit columns/data/end/error events
|
|
229
|
+
* to permit streaming big result-set
|
|
230
|
+
*
|
|
231
|
+
* @param sql sql parameter Object can be used to supersede default option.
|
|
232
|
+
* Object must then have sql property.
|
|
233
|
+
* @param values object / array of placeholder values (not mandatory)
|
|
234
|
+
* @returns {Readable}
|
|
235
|
+
*/
|
|
236
|
+
this.queryStream = (sql, values) => {
|
|
237
|
+
let _cmdOpt,
|
|
238
|
+
_sql,
|
|
239
|
+
_values = values;
|
|
240
|
+
if (typeof sql === 'object') {
|
|
241
|
+
_cmdOpt = sql;
|
|
242
|
+
_sql = _cmdOpt.sql;
|
|
243
|
+
if (sql.values) _values = sql.values;
|
|
244
|
+
} else {
|
|
245
|
+
_sql = sql;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const cmd = new Stream(_cmdOpt, opts, _sql, _values, _socket);
|
|
249
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
250
|
+
_addCommand(cmd);
|
|
251
|
+
return cmd.inStream;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Send an empty MySQL packet to ensure connection is active, and reset @@wait_timeout
|
|
256
|
+
* @param timeout (optional) timeout value in ms. If reached, throw error and close connection
|
|
257
|
+
* @returns {Promise} promise
|
|
258
|
+
*/
|
|
259
|
+
this.ping = (timeout) => {
|
|
260
|
+
return new Promise(function (resolve, reject) {
|
|
261
|
+
if (timeout) {
|
|
262
|
+
if (timeout < 0) {
|
|
263
|
+
reject(
|
|
264
|
+
Errors.createError(
|
|
265
|
+
'Ping cannot have negative timeout value',
|
|
266
|
+
null,
|
|
267
|
+
false,
|
|
268
|
+
info,
|
|
269
|
+
'0A000',
|
|
270
|
+
Errors.ER_BAD_PARAMETER_VALUE
|
|
271
|
+
)
|
|
272
|
+
);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const tOut = setTimeout(() => {
|
|
276
|
+
reject(
|
|
277
|
+
Errors.createError('Ping timeout', null, true, info, '0A000', Errors.ER_PING_TIMEOUT)
|
|
278
|
+
);
|
|
279
|
+
// close connection
|
|
280
|
+
_addCommand = _addCommandDisabled;
|
|
281
|
+
clearTimeout(_timeout);
|
|
282
|
+
if (_status !== Status.CLOSING && _status !== Status.CLOSED) {
|
|
283
|
+
_sendQueue.clear();
|
|
284
|
+
_status = Status.CLOSED;
|
|
285
|
+
_socket.destroy();
|
|
286
|
+
}
|
|
287
|
+
_clear();
|
|
288
|
+
}, timeout);
|
|
289
|
+
return _addCommand(
|
|
290
|
+
new Ping(
|
|
291
|
+
() => {
|
|
292
|
+
clearTimeout(tOut);
|
|
293
|
+
resolve();
|
|
294
|
+
},
|
|
295
|
+
(err) => {
|
|
296
|
+
clearTimeout(tOut);
|
|
297
|
+
reject(err);
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return _addCommand(new Ping(resolve, reject));
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Send a reset command that will
|
|
308
|
+
* - rollback any open transaction
|
|
309
|
+
* - reset transaction isolation level
|
|
310
|
+
* - reset session variables
|
|
311
|
+
* - delete user variables
|
|
312
|
+
* - remove temporary tables
|
|
313
|
+
* - remove all PREPARE statement
|
|
314
|
+
*
|
|
315
|
+
* @returns {Promise} promise
|
|
316
|
+
*/
|
|
317
|
+
this.reset = () => {
|
|
318
|
+
if (
|
|
319
|
+
(info.isMariaDB() && info.hasMinVersion(10, 2, 4)) ||
|
|
320
|
+
(!info.isMariaDB() && info.hasMinVersion(5, 7, 3))
|
|
321
|
+
) {
|
|
322
|
+
return new Promise(function (resolve, reject) {
|
|
323
|
+
return _addCommand(new Reset(resolve, reject));
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return Promise.reject(
|
|
327
|
+
new Error(
|
|
328
|
+
'Reset command not permitted for server ' +
|
|
329
|
+
this.info.serverVersion +
|
|
330
|
+
' (requires server MariaDB version 10.2.4+ or MySQL 5.7.3+)'
|
|
331
|
+
)
|
|
332
|
+
);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Indicates the state of the connection as the driver knows it
|
|
337
|
+
* @returns {boolean}
|
|
338
|
+
*/
|
|
339
|
+
this.isValid = () => {
|
|
340
|
+
return _status === Status.CONNECTED;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Terminate connection gracefully.
|
|
345
|
+
*
|
|
346
|
+
* @returns {Promise} promise
|
|
347
|
+
*/
|
|
348
|
+
this.end = () => {
|
|
349
|
+
_addCommand = _addCommandDisabled;
|
|
350
|
+
clearTimeout(_timeout);
|
|
351
|
+
|
|
352
|
+
if (
|
|
353
|
+
_status !== Status.CLOSING &&
|
|
354
|
+
_status !== Status.CLOSED &&
|
|
355
|
+
_status !== Status.NOT_CONNECTED
|
|
356
|
+
) {
|
|
357
|
+
_status = Status.CLOSING;
|
|
358
|
+
return new Promise(function (resolve, reject) {
|
|
359
|
+
const ended = () => {
|
|
360
|
+
_status = Status.CLOSED;
|
|
361
|
+
_socket.destroy();
|
|
362
|
+
_socket.unref();
|
|
363
|
+
_clear();
|
|
364
|
+
_receiveQueue.clear();
|
|
365
|
+
resolve();
|
|
366
|
+
};
|
|
367
|
+
const quitCmd = new Quit(ended, ended);
|
|
368
|
+
_sendQueue.push(quitCmd);
|
|
369
|
+
_receiveQueue.push(quitCmd);
|
|
370
|
+
if (_sendQueue.length === 1) {
|
|
371
|
+
process.nextTick(_nextSendCmd.bind(this));
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return Promise.resolve();
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Alias for destroy.
|
|
380
|
+
*/
|
|
381
|
+
this.close = function () {
|
|
382
|
+
this.destroy();
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Force connection termination by closing the underlying socket and killing server process if any.
|
|
387
|
+
*/
|
|
388
|
+
this.destroy = () => {
|
|
389
|
+
_addCommand = _addCommandDisabled;
|
|
390
|
+
clearTimeout(_timeout);
|
|
391
|
+
if (_status !== Status.CLOSING && _status !== Status.CLOSED) {
|
|
392
|
+
_status = Status.CLOSING;
|
|
393
|
+
_sendQueue.clear();
|
|
394
|
+
if (_receiveQueue.length > 0) {
|
|
395
|
+
//socket is closed, but server may still be processing a huge select
|
|
396
|
+
//only possibility is to kill process by another thread
|
|
397
|
+
//TODO reuse a pool connection to avoid connection creation
|
|
398
|
+
const self = this;
|
|
399
|
+
const killCon = new Connection(opts);
|
|
400
|
+
killCon
|
|
401
|
+
.connect()
|
|
402
|
+
.then(() => {
|
|
403
|
+
//*************************************************
|
|
404
|
+
//kill connection
|
|
405
|
+
//*************************************************
|
|
406
|
+
const killResHandler = () => {
|
|
407
|
+
const destroyError = Errors.createError(
|
|
408
|
+
'Connection destroyed, command was killed',
|
|
409
|
+
null,
|
|
410
|
+
true,
|
|
411
|
+
info,
|
|
412
|
+
'08S01',
|
|
413
|
+
Errors.ER_CMD_NOT_EXECUTED_DESTROYED
|
|
414
|
+
);
|
|
415
|
+
socketErrorDispatchToQueries(destroyError);
|
|
416
|
+
process.nextTick(() => {
|
|
417
|
+
if (_socket) _socket.destroy();
|
|
418
|
+
});
|
|
419
|
+
_status = Status.CLOSED;
|
|
420
|
+
killCon.end().catch(() => {});
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
killCon
|
|
424
|
+
.query('KILL ' + info.threadId)
|
|
425
|
+
.then(killResHandler)
|
|
426
|
+
.catch(killResHandler);
|
|
427
|
+
})
|
|
428
|
+
.catch((err) => {
|
|
429
|
+
//*************************************************
|
|
430
|
+
//failing to create a kill connection, end normally
|
|
431
|
+
//*************************************************
|
|
432
|
+
const ended = () => {
|
|
433
|
+
let sock = _socket;
|
|
434
|
+
_clear();
|
|
435
|
+
_status = Status.CLOSED;
|
|
436
|
+
setImmediate(resolve);
|
|
437
|
+
sock.destroy();
|
|
438
|
+
_receiveQueue.clear();
|
|
439
|
+
};
|
|
440
|
+
const quitCmd = new Quit(ended, ended);
|
|
441
|
+
_sendQueue.push(quitCmd);
|
|
442
|
+
_receiveQueue.push(quitCmd);
|
|
443
|
+
if (_sendQueue.length === 1) {
|
|
444
|
+
process.nextTick(_nextSendCmd.bind(self));
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
} else {
|
|
448
|
+
_status = Status.CLOSED;
|
|
449
|
+
_socket.destroy();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
_clear();
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
this.pause = () => {
|
|
456
|
+
_socket.pause();
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
this.resume = () => {
|
|
460
|
+
_socket.resume();
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
this.format = (sql, values) => {
|
|
464
|
+
throw Errors.createError(
|
|
465
|
+
'"Connection.format intentionally not implemented. please use Connection.query(sql, values), it will be more secure and faster',
|
|
466
|
+
null,
|
|
467
|
+
false,
|
|
468
|
+
info,
|
|
469
|
+
'0A000',
|
|
470
|
+
Errors.ER_NOT_IMPLEMENTED_FORMAT
|
|
471
|
+
);
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
//*****************************************************************
|
|
475
|
+
// additional public methods
|
|
476
|
+
//*****************************************************************
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* return current connected server version information.
|
|
480
|
+
*
|
|
481
|
+
* @returns {*}
|
|
482
|
+
*/
|
|
483
|
+
this.serverVersion = () => {
|
|
484
|
+
if (!info.serverVersion)
|
|
485
|
+
throw new Error('cannot know if server information until connection is established');
|
|
486
|
+
return info.serverVersion.raw;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Change option "debug" during connection.
|
|
491
|
+
* @param val debug value
|
|
492
|
+
*/
|
|
493
|
+
this.debug = (val) => {
|
|
494
|
+
opts.debug = val;
|
|
495
|
+
opts.emit('debug', opts.logPackets, opts.debug);
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
this.debugCompress = (val) => {
|
|
499
|
+
opts.debugCompress = val;
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
//*****************************************************************
|
|
503
|
+
// internal public testing methods
|
|
504
|
+
//*****************************************************************
|
|
505
|
+
|
|
506
|
+
function TestMethods() {}
|
|
507
|
+
|
|
508
|
+
TestMethods.prototype.getCollation = () => {
|
|
509
|
+
return opts.collation;
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
TestMethods.prototype.getSocket = () => {
|
|
513
|
+
return _socket;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
this.__tests = new TestMethods();
|
|
517
|
+
|
|
518
|
+
//*****************************************************************
|
|
519
|
+
// internal methods
|
|
520
|
+
//*****************************************************************
|
|
521
|
+
|
|
522
|
+
this._status = () => {
|
|
523
|
+
return _status;
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Execute query using text protocol with callback emit columns/data/end/error
|
|
528
|
+
* events to permit streaming big result-set
|
|
529
|
+
*
|
|
530
|
+
* @param sql sql parameter Object can be used to supersede default option.
|
|
531
|
+
* Object must then have sql property.
|
|
532
|
+
* @param values object / array of placeholder values (not mandatory)
|
|
533
|
+
* @param cb callback
|
|
534
|
+
* @returns {Query} query
|
|
535
|
+
*/
|
|
536
|
+
this._queryCallback = (sql, values, cb) => {
|
|
537
|
+
let _cmdOpts,
|
|
538
|
+
_sql,
|
|
539
|
+
_values = values,
|
|
540
|
+
_cb = cb;
|
|
541
|
+
|
|
542
|
+
if (typeof values === 'function') {
|
|
543
|
+
_cb = values;
|
|
544
|
+
_values = undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (typeof sql === 'object') {
|
|
548
|
+
_cmdOpts = sql;
|
|
549
|
+
_sql = _cmdOpts.sql;
|
|
550
|
+
if (sql.values) _values = sql.values;
|
|
551
|
+
} else {
|
|
552
|
+
_sql = sql;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
let cmd;
|
|
556
|
+
if (_cb) {
|
|
557
|
+
const resolve = (rows) => {
|
|
558
|
+
const meta = rows.meta;
|
|
559
|
+
delete rows.meta;
|
|
560
|
+
_cb(null, rows, meta);
|
|
561
|
+
};
|
|
562
|
+
cmd = new Query(resolve, _cb, _cmdOpts, opts, _sql, _values);
|
|
563
|
+
} else {
|
|
564
|
+
cmd = new Query(
|
|
565
|
+
() => {},
|
|
566
|
+
() => {},
|
|
567
|
+
_cmdOpts,
|
|
568
|
+
opts,
|
|
569
|
+
_sql,
|
|
570
|
+
_values
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
cmd.handleNewRows = (row) => {
|
|
574
|
+
cmd._rows[cmd._responseIndex].push(row);
|
|
575
|
+
cmd.emit('data', row);
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
579
|
+
_addCommand(cmd);
|
|
580
|
+
return cmd;
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Execute a batch using text protocol with callback emit columns/data/end/error
|
|
585
|
+
* events to permit streaming big result-set
|
|
586
|
+
*
|
|
587
|
+
* @param sql sql parameter Object can be used to supersede default option.
|
|
588
|
+
* Object must then have sql property.
|
|
589
|
+
* @param values object / array of placeholder values (not mandatory)
|
|
590
|
+
* @param cb callback
|
|
591
|
+
* @returns {Query} query
|
|
592
|
+
*/
|
|
593
|
+
this._batchCallback = (sql, values, cb) => {
|
|
594
|
+
let _cmdOpts,
|
|
595
|
+
_sql,
|
|
596
|
+
_values = values,
|
|
597
|
+
_cb = cb;
|
|
598
|
+
|
|
599
|
+
if (typeof values === 'function') {
|
|
600
|
+
_cb = values;
|
|
601
|
+
_values = undefined;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (typeof sql === 'object') {
|
|
605
|
+
_cmdOpts = sql;
|
|
606
|
+
_sql = _cmdOpts.sql;
|
|
607
|
+
if (sql.values) _values = sql.values;
|
|
608
|
+
} else {
|
|
609
|
+
_sql = sql;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (_values !== undefined) {
|
|
613
|
+
_values = Array.isArray(_values) ? _values : [_values];
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
let cmd;
|
|
617
|
+
|
|
618
|
+
if (!_values) {
|
|
619
|
+
if (_cb) {
|
|
620
|
+
_cb(
|
|
621
|
+
Errors.createError(
|
|
622
|
+
'Batch must have values set',
|
|
623
|
+
_sql,
|
|
624
|
+
false,
|
|
625
|
+
info,
|
|
626
|
+
'HY000',
|
|
627
|
+
Errors.ER_BATCH_WITH_NO_VALUES
|
|
628
|
+
)
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
let useBulk = canUseBulk(_values);
|
|
635
|
+
|
|
636
|
+
const fct = useBulk ? BatchBulk : BatchRewrite;
|
|
637
|
+
|
|
638
|
+
if (_cb) {
|
|
639
|
+
const resolve = (rows) => {
|
|
640
|
+
const meta = rows.meta;
|
|
641
|
+
delete rows.meta;
|
|
642
|
+
_cb(null, rows, meta);
|
|
643
|
+
};
|
|
644
|
+
cmd = new fct(resolve, _cb, _cmdOpts, opts, _sql, _values);
|
|
645
|
+
} else {
|
|
646
|
+
cmd = new fct(
|
|
647
|
+
() => {},
|
|
648
|
+
() => {},
|
|
649
|
+
_cmdOpts,
|
|
650
|
+
opts,
|
|
651
|
+
_sql,
|
|
652
|
+
_values
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
cmd.handleNewRows = (row) => {
|
|
656
|
+
cmd._rows[cmd._responseIndex].push(row);
|
|
657
|
+
cmd.emit('data', row);
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
661
|
+
_addCommand(cmd);
|
|
662
|
+
return cmd;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Use Batch rewrite or MariaDB bulk protocol.
|
|
667
|
+
*
|
|
668
|
+
* @param values current batch values
|
|
669
|
+
* @return {boolean} indicating if must use rewrite or bulk
|
|
670
|
+
*/
|
|
671
|
+
const canUseBulk = (values) => {
|
|
672
|
+
// not using info.isMariaDB() directly in case of callback use,
|
|
673
|
+
// without connection beeing completly finished.
|
|
674
|
+
let useBulk =
|
|
675
|
+
info.serverVersion &&
|
|
676
|
+
info.serverVersion.mariaDb &&
|
|
677
|
+
info.hasMinVersion(10, 2, 7) &&
|
|
678
|
+
opts.bulk &&
|
|
679
|
+
(info.serverCapabilities & Capabilities.MARIADB_CLIENT_STMT_BULK_OPERATIONS) > BigInt(0);
|
|
680
|
+
|
|
681
|
+
if (useBulk) {
|
|
682
|
+
//ensure that there is no stream object
|
|
683
|
+
if (values !== undefined) {
|
|
684
|
+
if (!opts.namedPlaceholders) {
|
|
685
|
+
//ensure that all parameters have same length
|
|
686
|
+
//single array is considered as an array of single element.
|
|
687
|
+
const paramLen = Array.isArray(values[0]) ? values[0].length : values[0] ? 1 : 0;
|
|
688
|
+
if (paramLen == 0) return false;
|
|
689
|
+
for (let r = 0; r < values.length; r++) {
|
|
690
|
+
let row = values[r];
|
|
691
|
+
if (!Array.isArray(row)) row = [row];
|
|
692
|
+
if (paramLen !== row.length) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
for (let j = 0; j < paramLen; j++) {
|
|
696
|
+
const val = row[j];
|
|
697
|
+
if (
|
|
698
|
+
val !== null &&
|
|
699
|
+
typeof val === 'object' &&
|
|
700
|
+
typeof val.pipe === 'function' &&
|
|
701
|
+
typeof val.read === 'function'
|
|
702
|
+
) {
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
} else {
|
|
708
|
+
for (let r = 0; r < values.length; r++) {
|
|
709
|
+
let row = values[r];
|
|
710
|
+
const keys = Object.keys(row);
|
|
711
|
+
for (let j = 0; j < keys.length; j++) {
|
|
712
|
+
const val = row[keys[j]];
|
|
713
|
+
if (
|
|
714
|
+
val !== null &&
|
|
715
|
+
typeof val === 'object' &&
|
|
716
|
+
typeof val.pipe === 'function' &&
|
|
717
|
+
typeof val.read === 'function'
|
|
718
|
+
) {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return useBulk;
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Add handshake command to queue.
|
|
731
|
+
*
|
|
732
|
+
* @private
|
|
733
|
+
*/
|
|
734
|
+
const _registerHandshakeCmd = (resolve, rejected) => {
|
|
735
|
+
const _authFail = _authFailHandler.bind(this, rejected);
|
|
736
|
+
const _authSucceed = _authSucceedHandler.bind(this, resolve, _authFail);
|
|
737
|
+
|
|
738
|
+
const handshake = new Handshake(
|
|
739
|
+
_authSucceed,
|
|
740
|
+
_authFail,
|
|
741
|
+
_createSecureContext.bind(this, _authFail),
|
|
742
|
+
_addCommandEnable.bind(this),
|
|
743
|
+
_getSocket
|
|
744
|
+
);
|
|
745
|
+
Error.captureStackTrace(handshake);
|
|
746
|
+
|
|
747
|
+
handshake.once('end', () => {
|
|
748
|
+
process.nextTick(_nextSendCmd);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
_receiveQueue.push(handshake);
|
|
752
|
+
_initSocket(_authFail);
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const _executeSessionVariableQuery = () => {
|
|
756
|
+
if (opts.sessionVariables) {
|
|
757
|
+
const values = [];
|
|
758
|
+
let sessionQuery = 'set ';
|
|
759
|
+
let keys = Object.keys(opts.sessionVariables);
|
|
760
|
+
if (keys.length > 0) {
|
|
761
|
+
return new Promise(function (resolve, reject) {
|
|
762
|
+
for (let k = 0; k < keys.length; ++k) {
|
|
763
|
+
sessionQuery +=
|
|
764
|
+
(k !== 0 ? ',' : '') + '@@' + keys[k].replace(/[^a-z0-9_]/gi, '') + '=?';
|
|
765
|
+
values.push(opts.sessionVariables[keys[k]]);
|
|
766
|
+
}
|
|
767
|
+
const errorHandling = (initialErr) => {
|
|
768
|
+
reject(
|
|
769
|
+
Errors.createError(
|
|
770
|
+
'Error setting session variable (value ' +
|
|
771
|
+
JSON.stringify(opts.sessionVariables) +
|
|
772
|
+
'). Error: ' +
|
|
773
|
+
initialErr.message,
|
|
774
|
+
sessionQuery,
|
|
775
|
+
true,
|
|
776
|
+
info,
|
|
777
|
+
'08S01',
|
|
778
|
+
Errors.ER_SETTING_SESSION_ERROR,
|
|
779
|
+
null
|
|
780
|
+
)
|
|
781
|
+
);
|
|
782
|
+
};
|
|
783
|
+
const cmd = new Query(resolve, errorHandling, null, opts, sessionQuery, values);
|
|
784
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
785
|
+
_addCommand(cmd);
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return Promise.resolve();
|
|
790
|
+
};
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Asking server timezone if not set in case of 'auto'
|
|
794
|
+
* @returns {Promise<void>}
|
|
795
|
+
* @private
|
|
796
|
+
*/
|
|
797
|
+
const _checkServerTimezone = () => {
|
|
798
|
+
if (opts.timezone === 'auto') {
|
|
799
|
+
return this._queryPromise('SELECT @@system_time_zone stz, @@time_zone tz').then((res) => {
|
|
800
|
+
const serverTimezone = res[0].tz === 'SYSTEM' ? res[0].stz : res[0].tz;
|
|
801
|
+
const serverZone = moment.tz.zone(serverTimezone);
|
|
802
|
+
if (serverZone) {
|
|
803
|
+
const localTz = moment.tz.guess();
|
|
804
|
+
if (serverTimezone === localTz) {
|
|
805
|
+
//db server and client use same timezone, avoid any conversion
|
|
806
|
+
opts.tz = null;
|
|
807
|
+
} else {
|
|
808
|
+
opts._localTz = localTz;
|
|
809
|
+
opts.tz = serverTimezone;
|
|
810
|
+
}
|
|
811
|
+
} else {
|
|
812
|
+
return Promise.reject(
|
|
813
|
+
Errors.createError(
|
|
814
|
+
"Automatic timezone setting fails. Server timezone '" +
|
|
815
|
+
serverTimezone +
|
|
816
|
+
"' does't have a corresponding IANA timezone. Option timezone must be set according to server timezone",
|
|
817
|
+
null,
|
|
818
|
+
true,
|
|
819
|
+
info,
|
|
820
|
+
'08S01',
|
|
821
|
+
Errors.ER_WRONG_AUTO_TIMEZONE
|
|
822
|
+
)
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
return Promise.resolve();
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
if (opts.tz && !opts.skipSetTimezone) {
|
|
829
|
+
let tz = opts.tz;
|
|
830
|
+
if (opts.tz === 'Etc/UTC') {
|
|
831
|
+
tz = '+00:00';
|
|
832
|
+
} else if (opts.tz.startsWith('Etc/GMT')) {
|
|
833
|
+
let zone = moment.tz.zone(opts.tz);
|
|
834
|
+
tz = zone.abbrs[0] + ':00';
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return this._queryPromise('SET time_zone=?', tz)
|
|
838
|
+
.then((res) => {
|
|
839
|
+
return Promise.resolve();
|
|
840
|
+
})
|
|
841
|
+
.catch((err) => {
|
|
842
|
+
console.log(
|
|
843
|
+
`warning: setting timezone '${opts.tz}' fails on server.\n look at https://mariadb.com/kb/en/mysql_tzinfo_to_sql/ to load IANA timezone.\nSetting timezone can be disabled with option \`skipSetTimezone\``
|
|
844
|
+
);
|
|
845
|
+
return Promise.resolve();
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
return Promise.resolve();
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
const _checkServerVersion = () => {
|
|
852
|
+
if (!opts.forceVersionCheck) {
|
|
853
|
+
return Promise.resolve();
|
|
854
|
+
}
|
|
855
|
+
return this._queryPromise('SELECT @@VERSION AS v').then((res) => {
|
|
856
|
+
info.serverVersion.raw = res[0].v;
|
|
857
|
+
info.serverVersion.mariaDb = info.serverVersion.raw.includes('MariaDB');
|
|
858
|
+
ConnectionInformation.parseVersionString(info);
|
|
859
|
+
return Promise.resolve();
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
const _executeInitQuery = () => {
|
|
864
|
+
if (opts.initSql) {
|
|
865
|
+
const initialArr = Array.isArray(opts.initSql) ? opts.initSql : [opts.initSql];
|
|
866
|
+
const initialPromises = [];
|
|
867
|
+
initialArr.forEach((sql) => {
|
|
868
|
+
initialPromises.push(this._queryPromise(sql));
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
return Promise.all(initialPromises).catch((initialErr) => {
|
|
872
|
+
return Promise.reject(
|
|
873
|
+
Errors.createError(
|
|
874
|
+
'Error executing initial sql command: ' + initialErr.message,
|
|
875
|
+
null,
|
|
876
|
+
true,
|
|
877
|
+
info,
|
|
878
|
+
'08S01',
|
|
879
|
+
Errors.ER_INITIAL_SQL_ERROR,
|
|
880
|
+
null
|
|
881
|
+
)
|
|
882
|
+
);
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
return Promise.resolve();
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
const _executeSessionTimeout = () => {
|
|
889
|
+
if (opts.queryTimeout) {
|
|
890
|
+
if (info.isMariaDB() && info.hasMinVersion(10, 1, 2)) {
|
|
891
|
+
const query = 'SET max_statement_time=' + opts.queryTimeout / 1000;
|
|
892
|
+
this._queryPromise(query).catch((initialErr) => {
|
|
893
|
+
return Promise.reject(
|
|
894
|
+
Errors.createError(
|
|
895
|
+
'Error setting session queryTimeout: ' + initialErr.message,
|
|
896
|
+
query,
|
|
897
|
+
true,
|
|
898
|
+
info,
|
|
899
|
+
'08S01',
|
|
900
|
+
Errors.ER_INITIAL_TIMEOUT_ERROR,
|
|
901
|
+
null
|
|
902
|
+
)
|
|
903
|
+
);
|
|
904
|
+
});
|
|
905
|
+
} else {
|
|
906
|
+
return Promise.reject(
|
|
907
|
+
Errors.createError(
|
|
908
|
+
'Can only use queryTimeout for MariaDB server after 10.1.1. queryTimeout value: ' +
|
|
909
|
+
null,
|
|
910
|
+
opts.queryTimeout,
|
|
911
|
+
false,
|
|
912
|
+
info,
|
|
913
|
+
'HY000',
|
|
914
|
+
Errors.ER_TIMEOUT_NOT_SUPPORTED
|
|
915
|
+
)
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return Promise.resolve();
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
const _getSocket = () => {
|
|
923
|
+
return _socket;
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Initialize socket and associate events.
|
|
928
|
+
* @private
|
|
929
|
+
*/
|
|
930
|
+
const _initSocket = (authFailHandler) => {
|
|
931
|
+
if (opts.socketPath) {
|
|
932
|
+
_socket = Net.connect(opts.socketPath);
|
|
933
|
+
} else {
|
|
934
|
+
_socket = Net.connect(opts.port, opts.host);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (opts.connectTimeout) {
|
|
938
|
+
_timeout = setTimeout(
|
|
939
|
+
_connectTimeoutReached,
|
|
940
|
+
opts.connectTimeout,
|
|
941
|
+
authFailHandler,
|
|
942
|
+
Date.now()
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
const _socketError = _socketErrorHandler.bind(this, authFailHandler);
|
|
947
|
+
|
|
948
|
+
_socket.on('data', _in.onData.bind(_in));
|
|
949
|
+
_socket.on('error', _socketError);
|
|
950
|
+
_socket.on('end', _socketError);
|
|
951
|
+
_socket.on(
|
|
952
|
+
'connect',
|
|
953
|
+
function () {
|
|
954
|
+
clearTimeout(_timeout);
|
|
955
|
+
if (_status === Status.CONNECTING) {
|
|
956
|
+
_status = Status.AUTHENTICATING;
|
|
957
|
+
_socketConnected = true;
|
|
958
|
+
_socket.setTimeout(opts.socketTimeout, _socketTimeoutReached.bind(this, authFailHandler));
|
|
959
|
+
_socket.setNoDelay(true);
|
|
960
|
+
|
|
961
|
+
// keep alive for socket. This won't reset server wait_timeout use pool option idleTimeout for that
|
|
962
|
+
if (opts.keepAliveDelay) {
|
|
963
|
+
_socket.setKeepAlive(true, opts.keepAliveDelay);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}.bind(this)
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
_socket.writeBuf = (buf) => _socket.write(buf);
|
|
970
|
+
_socket.flush = () => {};
|
|
971
|
+
_out.setStream(_socket);
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Authentication success result handler.
|
|
976
|
+
*
|
|
977
|
+
* @private
|
|
978
|
+
*/
|
|
979
|
+
const _authSucceedHandler = (resolve, rejected) => {
|
|
980
|
+
//enable packet compression according to option
|
|
981
|
+
if (opts.logPackets) info.enableLogPacket();
|
|
982
|
+
if (opts.compress) {
|
|
983
|
+
if (info.serverCapabilities & Capabilities.COMPRESS) {
|
|
984
|
+
_out.setStream(new CompressionOutputStream(_socket, opts, info));
|
|
985
|
+
_in = new CompressionInputStream(_in, _receiveQueue, opts, info);
|
|
986
|
+
_socket.removeAllListeners('data');
|
|
987
|
+
_socket.on('data', _in.onData.bind(_in));
|
|
988
|
+
} else {
|
|
989
|
+
console.error(
|
|
990
|
+
"connection is configured to use packet compression, but the server doesn't have this capability"
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
_addCommand = opts.pipelining ? _addCommandEnablePipeline : _addCommandEnable;
|
|
996
|
+
|
|
997
|
+
const commands = _waitingAuthenticationQueue.toArray();
|
|
998
|
+
commands.forEach((cmd) => {
|
|
999
|
+
_addCommand(cmd);
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
const errorInitialQueries = (err) => {
|
|
1003
|
+
if (!err.fatal) this.end().catch((err) => {});
|
|
1004
|
+
process.nextTick(rejected, err);
|
|
1005
|
+
};
|
|
1006
|
+
_status = Status.INIT_CMD;
|
|
1007
|
+
_executeSessionVariableQuery()
|
|
1008
|
+
.then(() => {
|
|
1009
|
+
return _checkServerTimezone();
|
|
1010
|
+
})
|
|
1011
|
+
.then(() => {
|
|
1012
|
+
return _checkServerVersion();
|
|
1013
|
+
})
|
|
1014
|
+
.then(() => {
|
|
1015
|
+
return _executeInitQuery();
|
|
1016
|
+
})
|
|
1017
|
+
.then(() => {
|
|
1018
|
+
return _executeSessionTimeout();
|
|
1019
|
+
})
|
|
1020
|
+
.then(() => {
|
|
1021
|
+
_status = Status.CONNECTED;
|
|
1022
|
+
process.nextTick(resolve, this);
|
|
1023
|
+
})
|
|
1024
|
+
.catch(errorInitialQueries);
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Authentication failed result handler.
|
|
1029
|
+
*
|
|
1030
|
+
* @private
|
|
1031
|
+
*/
|
|
1032
|
+
const _authFailHandler = (reject, err) => {
|
|
1033
|
+
process.nextTick(reject, err);
|
|
1034
|
+
//remove handshake command
|
|
1035
|
+
_receiveQueue.shift();
|
|
1036
|
+
|
|
1037
|
+
_fatalError(err, true);
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Create TLS socket and associate events.
|
|
1042
|
+
*
|
|
1043
|
+
* @param rejected rejected function when error
|
|
1044
|
+
* @param callback callback function when done
|
|
1045
|
+
* @private
|
|
1046
|
+
*/
|
|
1047
|
+
const _createSecureContext = (rejected, callback) => {
|
|
1048
|
+
const _socketError = _socketErrorHandler.bind(this, rejected);
|
|
1049
|
+
const sslOption = Object.assign({}, opts.ssl, {
|
|
1050
|
+
servername: opts.host,
|
|
1051
|
+
socket: _socket
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
try {
|
|
1055
|
+
const secureSocket = tls.connect(sslOption, callback);
|
|
1056
|
+
|
|
1057
|
+
secureSocket.on('data', _in.onData.bind(_in));
|
|
1058
|
+
secureSocket.on('error', _socketError);
|
|
1059
|
+
secureSocket.on('end', _socketError);
|
|
1060
|
+
secureSocket.writeBuf = (buf) => secureSocket.write(buf);
|
|
1061
|
+
secureSocket.flush = () => {};
|
|
1062
|
+
|
|
1063
|
+
_socket.removeAllListeners('data');
|
|
1064
|
+
_socket = secureSocket;
|
|
1065
|
+
|
|
1066
|
+
_out.setStream(secureSocket);
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
_socketError(err);
|
|
1069
|
+
}
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Handle packet when no packet is expected.
|
|
1074
|
+
* (there can be an ERROR packet send by server/proxy to inform that connection is ending).
|
|
1075
|
+
*
|
|
1076
|
+
* @param packet packet
|
|
1077
|
+
* @private
|
|
1078
|
+
*/
|
|
1079
|
+
const _unexpectedPacket = function (packet) {
|
|
1080
|
+
if (packet && packet.peek() === 0xff) {
|
|
1081
|
+
//can receive unexpected error packet from server/proxy
|
|
1082
|
+
//to inform that connection is closed (usually by timeout)
|
|
1083
|
+
let err = packet.readError(info);
|
|
1084
|
+
if (err.fatal && _status !== Status.CLOSING && _status !== Status.CLOSED) {
|
|
1085
|
+
this.emit('error', err);
|
|
1086
|
+
this.end();
|
|
1087
|
+
}
|
|
1088
|
+
} else if (_status !== Status.CLOSING && _status !== Status.CLOSED) {
|
|
1089
|
+
this.emit(
|
|
1090
|
+
'error',
|
|
1091
|
+
Errors.createError(
|
|
1092
|
+
'receiving packet from server without active commands\n' +
|
|
1093
|
+
'conn:' +
|
|
1094
|
+
(info.threadId ? info.threadId : -1) +
|
|
1095
|
+
'(' +
|
|
1096
|
+
packet.pos +
|
|
1097
|
+
',' +
|
|
1098
|
+
packet.end +
|
|
1099
|
+
')\n' +
|
|
1100
|
+
Utils.log(opts, packet.buf, packet.pos, packet.end),
|
|
1101
|
+
null,
|
|
1102
|
+
true,
|
|
1103
|
+
info,
|
|
1104
|
+
'08S01',
|
|
1105
|
+
Errors.ER_UNEXPECTED_PACKET
|
|
1106
|
+
)
|
|
1107
|
+
);
|
|
1108
|
+
this.destroy();
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* Change transaction state.
|
|
1114
|
+
*
|
|
1115
|
+
* @param sql sql
|
|
1116
|
+
* @returns {Promise} promise
|
|
1117
|
+
* @private
|
|
1118
|
+
*/
|
|
1119
|
+
const _changeTransaction = (sql) => {
|
|
1120
|
+
//if command in progress, driver cannot rely on status and must execute query
|
|
1121
|
+
if (_status === Status.CLOSING || _status === Status.CLOSED) {
|
|
1122
|
+
return Promise.reject(
|
|
1123
|
+
Errors.createError(
|
|
1124
|
+
'Cannot execute new commands: connection closed',
|
|
1125
|
+
sql,
|
|
1126
|
+
true,
|
|
1127
|
+
info,
|
|
1128
|
+
'08S01',
|
|
1129
|
+
Errors.ER_CMD_CONNECTION_CLOSED
|
|
1130
|
+
)
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
//Command in progress => must execute query
|
|
1135
|
+
//or if no command in progress, can rely on status to know if query is needed
|
|
1136
|
+
if (_receiveQueue.peekFront() || info.status & ServerStatus.STATUS_IN_TRANS) {
|
|
1137
|
+
return new Promise(function (resolve, reject) {
|
|
1138
|
+
const cmd = new Query(resolve, reject, null, opts, sql, null);
|
|
1139
|
+
if (opts.trace) Error.captureStackTrace(cmd);
|
|
1140
|
+
_addCommand(cmd);
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
return Promise.resolve();
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Handle connection timeout.
|
|
1148
|
+
*
|
|
1149
|
+
* @private
|
|
1150
|
+
*/
|
|
1151
|
+
const _connectTimeoutReached = function (authFailHandler, initialConnectionTime) {
|
|
1152
|
+
_timeout = null;
|
|
1153
|
+
const handshake = _receiveQueue.peekFront();
|
|
1154
|
+
authFailHandler(
|
|
1155
|
+
Errors.createError(
|
|
1156
|
+
'Connection timeout: failed to create socket after ' +
|
|
1157
|
+
(Date.now() - initialConnectionTime) +
|
|
1158
|
+
'ms',
|
|
1159
|
+
null,
|
|
1160
|
+
true,
|
|
1161
|
+
info,
|
|
1162
|
+
'08S01',
|
|
1163
|
+
Errors.ER_CONNECTION_TIMEOUT,
|
|
1164
|
+
handshake ? handshake.stack : null
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Handle socket timeout.
|
|
1171
|
+
*
|
|
1172
|
+
* @private
|
|
1173
|
+
*/
|
|
1174
|
+
const _socketTimeoutReached = function () {
|
|
1175
|
+
const err = Errors.createError(
|
|
1176
|
+
'socket timeout',
|
|
1177
|
+
null,
|
|
1178
|
+
true,
|
|
1179
|
+
info,
|
|
1180
|
+
'08S01',
|
|
1181
|
+
Errors.ER_SOCKET_TIMEOUT
|
|
1182
|
+
);
|
|
1183
|
+
const packetMsgs = info.getLastPackets();
|
|
1184
|
+
if (packetMsgs !== '') {
|
|
1185
|
+
err.message = err.message + '\nlast received packets:\n' + packetMsgs;
|
|
1186
|
+
}
|
|
1187
|
+
_fatalError(err, true);
|
|
1188
|
+
};
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Add command to waiting queue until authentication.
|
|
1192
|
+
*
|
|
1193
|
+
* @param cmd command
|
|
1194
|
+
* @returns {*} current command
|
|
1195
|
+
* @private
|
|
1196
|
+
*/
|
|
1197
|
+
const _addCommandQueue = (cmd) => {
|
|
1198
|
+
_waitingAuthenticationQueue.push(cmd);
|
|
1199
|
+
return cmd;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* Add command to command sending and receiving queue.
|
|
1204
|
+
*
|
|
1205
|
+
* @param cmd command
|
|
1206
|
+
* @returns {*} current command
|
|
1207
|
+
* @private
|
|
1208
|
+
*/
|
|
1209
|
+
const _addCommandEnable = (cmd) => {
|
|
1210
|
+
cmd.once('end', () => {
|
|
1211
|
+
setImmediate(_nextSendCmd);
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
//send immediately only if no current active receiver
|
|
1215
|
+
if (_sendQueue.isEmpty() && (_status === Status.INIT_CMD || _status === Status.CONNECTED)) {
|
|
1216
|
+
if (_receiveQueue.peekFront()) {
|
|
1217
|
+
_receiveQueue.push(cmd);
|
|
1218
|
+
_sendQueue.push(cmd);
|
|
1219
|
+
return cmd;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
_receiveQueue.push(cmd);
|
|
1223
|
+
cmd.start(_out, opts, info);
|
|
1224
|
+
} else {
|
|
1225
|
+
_receiveQueue.push(cmd);
|
|
1226
|
+
_sendQueue.push(cmd);
|
|
1227
|
+
}
|
|
1228
|
+
return cmd;
|
|
1229
|
+
};
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Add command to command sending and receiving queue using pipelining
|
|
1233
|
+
*
|
|
1234
|
+
* @param cmd command
|
|
1235
|
+
* @returns {*} current command
|
|
1236
|
+
* @private
|
|
1237
|
+
*/
|
|
1238
|
+
const _addCommandEnablePipeline = (cmd) => {
|
|
1239
|
+
cmd.once('send_end', () => {
|
|
1240
|
+
setImmediate(_nextSendCmd);
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
_receiveQueue.push(cmd);
|
|
1244
|
+
if (_sendQueue.isEmpty()) {
|
|
1245
|
+
cmd.start(_out, opts, info);
|
|
1246
|
+
if (cmd.sending) {
|
|
1247
|
+
_sendQueue.push(cmd);
|
|
1248
|
+
cmd.prependOnceListener('send_end', () => {
|
|
1249
|
+
_sendQueue.shift();
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1253
|
+
_sendQueue.push(cmd);
|
|
1254
|
+
}
|
|
1255
|
+
return cmd;
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Replacing command when connection is closing or closed to send a proper error message.
|
|
1260
|
+
*
|
|
1261
|
+
* @param cmd command
|
|
1262
|
+
* @private
|
|
1263
|
+
*/
|
|
1264
|
+
const _addCommandDisabled = (cmd) => {
|
|
1265
|
+
cmd.throwNewError(
|
|
1266
|
+
'Cannot execute new commands: connection closed',
|
|
1267
|
+
true,
|
|
1268
|
+
info,
|
|
1269
|
+
'08S01',
|
|
1270
|
+
Errors.ER_CMD_CONNECTION_CLOSED
|
|
1271
|
+
);
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Handle socket error.
|
|
1276
|
+
*
|
|
1277
|
+
* @param authFailHandler authentication handler
|
|
1278
|
+
* @param err socket error
|
|
1279
|
+
* @private
|
|
1280
|
+
*/
|
|
1281
|
+
const _socketErrorHandler = function (authFailHandler, err) {
|
|
1282
|
+
if (_status === Status.CLOSING || _status === Status.CLOSED) return;
|
|
1283
|
+
if (_socket) {
|
|
1284
|
+
_socket.writeBuf = () => {};
|
|
1285
|
+
_socket.flush = () => {};
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
//socket has been ended without error
|
|
1289
|
+
if (!err) {
|
|
1290
|
+
err = Errors.createError(
|
|
1291
|
+
'socket has unexpectedly been closed',
|
|
1292
|
+
null,
|
|
1293
|
+
true,
|
|
1294
|
+
info,
|
|
1295
|
+
'08S01',
|
|
1296
|
+
Errors.ER_SOCKET_UNEXPECTED_CLOSE
|
|
1297
|
+
);
|
|
1298
|
+
} else {
|
|
1299
|
+
err.fatal = true;
|
|
1300
|
+
this.sqlState = 'HY000';
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
const packetMsgs = info.getLastPackets();
|
|
1304
|
+
if (packetMsgs !== '') {
|
|
1305
|
+
err.message += '\nlast received packets:\n' + packetMsgs;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
switch (_status) {
|
|
1309
|
+
case Status.CONNECTING:
|
|
1310
|
+
case Status.AUTHENTICATING:
|
|
1311
|
+
const currentCmd = _receiveQueue.peekFront();
|
|
1312
|
+
if (currentCmd && currentCmd.stack && err) {
|
|
1313
|
+
err.stack +=
|
|
1314
|
+
'\n From event:\n' + currentCmd.stack.substring(currentCmd.stack.indexOf('\n') + 1);
|
|
1315
|
+
}
|
|
1316
|
+
authFailHandler(err);
|
|
1317
|
+
break;
|
|
1318
|
+
|
|
1319
|
+
default:
|
|
1320
|
+
_fatalError(err, false);
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Fatal unexpected error : closing connection, and throw exception.
|
|
1326
|
+
*
|
|
1327
|
+
* @param self current connection
|
|
1328
|
+
* @private
|
|
1329
|
+
*/
|
|
1330
|
+
const _fatalErrorHandler = function (self) {
|
|
1331
|
+
return function (err, avoidThrowError) {
|
|
1332
|
+
if (_status === Status.CLOSING || _status === Status.CLOSED) {
|
|
1333
|
+
socketErrorDispatchToQueries(err);
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
const mustThrowError = _status !== Status.CONNECTING;
|
|
1337
|
+
_status = Status.CLOSING;
|
|
1338
|
+
|
|
1339
|
+
//prevent executing new commands
|
|
1340
|
+
_addCommand = _addCommandDisabled;
|
|
1341
|
+
|
|
1342
|
+
if (_socket) {
|
|
1343
|
+
_socket.removeAllListeners('error');
|
|
1344
|
+
_socket.removeAllListeners('timeout');
|
|
1345
|
+
_socket.removeAllListeners('close');
|
|
1346
|
+
_socket.removeAllListeners('data');
|
|
1347
|
+
if (!_socket.destroyed) _socket.destroy();
|
|
1348
|
+
_socket = undefined;
|
|
1349
|
+
}
|
|
1350
|
+
_status = Status.CLOSED;
|
|
1351
|
+
|
|
1352
|
+
const errorThrownByCmd = socketErrorDispatchToQueries(err);
|
|
1353
|
+
if (mustThrowError) {
|
|
1354
|
+
if (self.listenerCount('error') > 0) {
|
|
1355
|
+
self.emit('error', err);
|
|
1356
|
+
self.emit('end');
|
|
1357
|
+
_clear();
|
|
1358
|
+
} else {
|
|
1359
|
+
self.emit('end');
|
|
1360
|
+
_clear();
|
|
1361
|
+
//error will be thrown if no error listener and no command did throw the exception
|
|
1362
|
+
if (!avoidThrowError && !errorThrownByCmd) throw err;
|
|
1363
|
+
}
|
|
1364
|
+
} else {
|
|
1365
|
+
_clear();
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
/**
|
|
1371
|
+
* Dispatch fatal error to current running queries.
|
|
1372
|
+
*
|
|
1373
|
+
* @param err the fatal error
|
|
1374
|
+
* @return {boolean} return if error has been relayed to queries
|
|
1375
|
+
*/
|
|
1376
|
+
const socketErrorDispatchToQueries = (err) => {
|
|
1377
|
+
let receiveCmd;
|
|
1378
|
+
let errorThrownByCmd = false;
|
|
1379
|
+
while ((receiveCmd = _receiveQueue.shift())) {
|
|
1380
|
+
if (receiveCmd && receiveCmd.onPacketReceive) {
|
|
1381
|
+
errorThrownByCmd = true;
|
|
1382
|
+
setImmediate(receiveCmd.throwError.bind(receiveCmd), err, info);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
return errorThrownByCmd;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* Will send next command in queue if any.
|
|
1390
|
+
*
|
|
1391
|
+
* @private
|
|
1392
|
+
*/
|
|
1393
|
+
const _nextSendCmd = () => {
|
|
1394
|
+
let sendCmd;
|
|
1395
|
+
if ((sendCmd = _sendQueue.shift())) {
|
|
1396
|
+
if (sendCmd.sending) {
|
|
1397
|
+
_sendQueue.unshift(sendCmd);
|
|
1398
|
+
} else {
|
|
1399
|
+
sendCmd.start(_out, opts, info);
|
|
1400
|
+
if (sendCmd.sending) {
|
|
1401
|
+
sendCmd.prependOnceListener('send_end', () => {
|
|
1402
|
+
_sendQueue.shift();
|
|
1403
|
+
});
|
|
1404
|
+
_sendQueue.unshift(sendCmd);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
/**
|
|
1411
|
+
* Clearing connection variables when ending.
|
|
1412
|
+
*
|
|
1413
|
+
* @private
|
|
1414
|
+
*/
|
|
1415
|
+
const _clear = () => {
|
|
1416
|
+
_sendQueue.clear();
|
|
1417
|
+
opts.removeAllListeners();
|
|
1418
|
+
_out = undefined;
|
|
1419
|
+
_socket = undefined;
|
|
1420
|
+
};
|
|
1421
|
+
|
|
1422
|
+
//*****************************************************************
|
|
1423
|
+
// internal variables
|
|
1424
|
+
//*****************************************************************
|
|
1425
|
+
|
|
1426
|
+
EventEmitter.call(this);
|
|
1427
|
+
const opts = Object.assign(new EventEmitter(), options);
|
|
1428
|
+
const info = new ConnectionInformation();
|
|
1429
|
+
const _sendQueue = new Queue();
|
|
1430
|
+
const _receiveQueue = new Queue();
|
|
1431
|
+
const _waitingAuthenticationQueue = new Queue();
|
|
1432
|
+
let _status = Status.NOT_CONNECTED;
|
|
1433
|
+
let _socketConnected = false;
|
|
1434
|
+
let _socket = null;
|
|
1435
|
+
let _timeout = null;
|
|
1436
|
+
let _addCommand = _addCommandQueue;
|
|
1437
|
+
const _fatalError = _fatalErrorHandler(this);
|
|
1438
|
+
let _out = new PacketOutputStream(opts, info);
|
|
1439
|
+
let _in = new PacketInputStream(_unexpectedPacket.bind(this), _receiveQueue, _out, opts, info);
|
|
1440
|
+
|
|
1441
|
+
this.query = this._queryPromise;
|
|
1442
|
+
this.escape = Utils.escape.bind(this, opts, info);
|
|
1443
|
+
this.escapeId = Utils.escapeId.bind(this, opts, info);
|
|
1444
|
+
|
|
1445
|
+
//add alias threadId for mysql/mysql2 compatibility
|
|
1446
|
+
Object.defineProperty(this, 'threadId', {
|
|
1447
|
+
get() {
|
|
1448
|
+
return info ? info.threadId : undefined;
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
Object.defineProperty(this, 'info', {
|
|
1452
|
+
get() {
|
|
1453
|
+
return info;
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
util.inherits(Connection, EventEmitter);
|
|
1459
|
+
|
|
1460
|
+
module.exports = Connection;
|