@tachybase/module-multi-app 1.6.0 → 1.6.2
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/README.md +34 -34
- package/README.zh-CN.md +34 -34
- package/client.d.ts +1 -1
- package/client.js +1 -1
- package/dist/externalVersion.js +5 -5
- package/dist/locale/en-US.json +48 -48
- package/dist/locale/es-ES.json +9 -9
- package/dist/locale/ko_KR.json +11 -11
- package/dist/locale/pt-BR.json +9 -9
- package/dist/locale/zh-CN.json +58 -58
- package/dist/node_modules/mariadb/callback.js +43 -8
- package/dist/node_modules/mariadb/check-node.js +30 -0
- package/dist/node_modules/mariadb/lib/cluster-callback.js +84 -0
- package/dist/node_modules/mariadb/lib/cluster.js +446 -0
- package/dist/node_modules/mariadb/lib/cmd/batch-bulk.js +576 -177
- package/dist/node_modules/mariadb/lib/cmd/change-user.js +54 -44
- package/dist/node_modules/mariadb/lib/cmd/class/ok-packet.js +3 -2
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-cache-wrapper.js +46 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-result-packet.js +141 -0
- package/dist/node_modules/mariadb/lib/cmd/class/prepare-wrapper.js +70 -0
- package/dist/node_modules/mariadb/lib/cmd/close-prepare.js +38 -0
- package/dist/node_modules/mariadb/lib/cmd/column-definition.js +145 -47
- package/dist/node_modules/mariadb/lib/cmd/command.js +41 -75
- package/dist/node_modules/mariadb/lib/cmd/decoder/binary-decoder.js +282 -0
- package/dist/node_modules/mariadb/lib/cmd/decoder/text-decoder.js +210 -0
- package/dist/node_modules/mariadb/lib/cmd/{common-binary-cmd.js → encoder/binary-encoder.js} +34 -77
- package/dist/node_modules/mariadb/lib/cmd/encoder/text-encoder.js +311 -0
- package/dist/node_modules/mariadb/lib/cmd/execute-stream.js +61 -0
- package/dist/node_modules/mariadb/lib/cmd/execute.js +338 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/caching-sha2-password-auth.js +25 -62
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/clear-password-auth.js +39 -6
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/ed25519-password-auth.js +48 -16
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/handshake.js +198 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/{initial-handshake.js → auth/initial-handshake.js} +10 -8
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/native-password-auth.js +22 -9
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/pam-password-auth.js +9 -4
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/parsec-auth.js +115 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/plugin-auth.js +12 -5
- package/dist/node_modules/mariadb/lib/cmd/handshake/auth/sha256-password-auth.js +44 -33
- package/dist/node_modules/mariadb/lib/cmd/handshake/authentication.js +335 -0
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-capabilities.js +20 -19
- package/dist/node_modules/mariadb/lib/cmd/handshake/ssl-request.js +6 -3
- package/dist/node_modules/mariadb/lib/cmd/parser.js +861 -0
- package/dist/node_modules/mariadb/lib/cmd/ping.js +17 -18
- package/dist/node_modules/mariadb/lib/cmd/prepare.js +170 -0
- package/dist/node_modules/mariadb/lib/cmd/query.js +281 -144
- package/dist/node_modules/mariadb/lib/cmd/quit.js +9 -6
- package/dist/node_modules/mariadb/lib/cmd/reset.js +15 -19
- package/dist/node_modules/mariadb/lib/cmd/stream.js +21 -6
- package/dist/node_modules/mariadb/lib/config/cluster-options.js +23 -0
- package/dist/node_modules/mariadb/lib/config/connection-options.js +196 -132
- package/dist/node_modules/mariadb/lib/config/pool-options.js +27 -19
- package/dist/node_modules/mariadb/lib/connection-callback.js +492 -120
- package/dist/node_modules/mariadb/lib/connection-promise.js +372 -0
- package/dist/node_modules/mariadb/lib/connection.js +1739 -1016
- package/dist/node_modules/mariadb/lib/const/capabilities.js +36 -30
- package/dist/node_modules/mariadb/lib/const/collations.js +972 -36
- package/dist/node_modules/mariadb/lib/const/connection_status.js +3 -0
- package/dist/node_modules/mariadb/lib/const/error-code.js +35 -11
- package/dist/node_modules/mariadb/lib/const/field-detail.js +3 -0
- package/dist/node_modules/mariadb/lib/const/field-type.js +7 -4
- package/dist/node_modules/mariadb/lib/const/server-status.js +4 -1
- package/dist/node_modules/mariadb/lib/const/state-change.js +3 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster-callback.js +136 -0
- package/dist/node_modules/mariadb/lib/filtered-cluster.js +118 -0
- package/dist/node_modules/mariadb/lib/io/compression-input-stream.js +14 -13
- package/dist/node_modules/mariadb/lib/io/compression-output-stream.js +21 -18
- package/dist/node_modules/mariadb/lib/io/packet-input-stream.js +75 -64
- package/dist/node_modules/mariadb/lib/io/packet-node-encoded.js +13 -9
- package/dist/node_modules/mariadb/lib/io/packet-node-iconv.js +12 -10
- package/dist/node_modules/mariadb/lib/io/packet-output-stream.js +402 -134
- package/dist/node_modules/mariadb/lib/io/packet.js +287 -202
- package/dist/node_modules/mariadb/lib/lru-prepare-cache.js +84 -0
- package/dist/node_modules/mariadb/lib/misc/connection-information.js +15 -32
- package/dist/node_modules/mariadb/lib/misc/errors.js +68 -25
- package/dist/node_modules/mariadb/lib/misc/parse.js +207 -711
- package/dist/node_modules/mariadb/lib/misc/utils.js +34 -62
- package/dist/node_modules/mariadb/lib/pool-callback.js +213 -174
- package/dist/node_modules/mariadb/lib/pool-promise.js +228 -94
- package/dist/node_modules/mariadb/lib/pool.js +951 -0
- package/dist/node_modules/mariadb/package.json +1 -1
- package/dist/node_modules/mariadb/promise.js +1 -34
- package/dist/node_modules/mariadb/types/callback.d.ts +207 -0
- package/dist/node_modules/mariadb/types/index.d.ts +94 -674
- package/dist/node_modules/mariadb/types/share.d.ts +804 -0
- package/dist/node_modules/qs/package.json +1 -1
- package/dist/server/actions/apps.js +2 -2
- package/dist/server/app-lifecycle.d.ts +1 -1
- package/dist/server/app-lifecycle.js +4 -4
- package/dist/server/models/application.d.ts +1 -1
- package/package.json +7 -7
- package/server.d.ts +2 -2
- package/server.js +1 -1
- package/dist/node_modules/mariadb/lib/cmd/batch-rewrite.js +0 -372
- package/dist/node_modules/mariadb/lib/cmd/common-text-cmd.js +0 -427
- package/dist/node_modules/mariadb/lib/cmd/handshake/client-handshake-response.js +0 -126
- package/dist/node_modules/mariadb/lib/cmd/handshake/handshake.js +0 -292
- package/dist/node_modules/mariadb/lib/cmd/resultset.js +0 -607
- package/dist/node_modules/mariadb/lib/config/pool-cluster-options.js +0 -19
- package/dist/node_modules/mariadb/lib/filtered-pool-cluster.js +0 -81
- package/dist/node_modules/mariadb/lib/io/bulk-packet.js +0 -590
- package/dist/node_modules/mariadb/lib/io/rewrite-packet.js +0 -481
- package/dist/node_modules/mariadb/lib/pool-base.js +0 -611
- package/dist/node_modules/mariadb/lib/pool-cluster-callback.js +0 -66
- package/dist/node_modules/mariadb/lib/pool-cluster.js +0 -407
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const QUOTE = 0x27;
|
|
7
|
+
|
|
8
|
+
// Cache common GeoJSON types
|
|
9
|
+
const GEO_TYPES = new Set([
|
|
10
|
+
'Point',
|
|
11
|
+
'LineString',
|
|
12
|
+
'Polygon',
|
|
13
|
+
'MultiPoint',
|
|
14
|
+
'MultiLineString',
|
|
15
|
+
'MultiPolygon',
|
|
16
|
+
'GeometryCollection'
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
// Optimized function to pad numbers with leading zeros
|
|
20
|
+
const formatDigit = function (val, significantDigit) {
|
|
21
|
+
const str = `${val}`;
|
|
22
|
+
return str.length < significantDigit ? '0'.repeat(significantDigit - str.length) + str : str;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
class TextEncoder {
|
|
26
|
+
/**
|
|
27
|
+
* Write (and escape) current parameter value to output writer
|
|
28
|
+
*
|
|
29
|
+
* @param out output writer
|
|
30
|
+
* @param value current parameter. Expected to be non-null
|
|
31
|
+
* @param opts connection options
|
|
32
|
+
* @param info connection information
|
|
33
|
+
*/
|
|
34
|
+
static writeParam(out, value, opts, info) {
|
|
35
|
+
switch (typeof value) {
|
|
36
|
+
case 'boolean':
|
|
37
|
+
out.writeStringAscii(value ? 'true' : 'false');
|
|
38
|
+
break;
|
|
39
|
+
case 'bigint':
|
|
40
|
+
case 'number':
|
|
41
|
+
out.writeStringAscii(`${value}`);
|
|
42
|
+
break;
|
|
43
|
+
case 'string':
|
|
44
|
+
out.writeStringEscapeQuote(value);
|
|
45
|
+
break;
|
|
46
|
+
case 'object':
|
|
47
|
+
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
48
|
+
out.writeStringAscii(TextEncoder.getLocalDate(value));
|
|
49
|
+
} else if (Buffer.isBuffer(value)) {
|
|
50
|
+
out.writeStringAscii("_BINARY '");
|
|
51
|
+
out.writeBufferEscape(value);
|
|
52
|
+
out.writeInt8(QUOTE);
|
|
53
|
+
} else if (typeof value.toSqlString === 'function') {
|
|
54
|
+
out.writeStringEscapeQuote(String(value.toSqlString()));
|
|
55
|
+
} else if (Array.isArray(value)) {
|
|
56
|
+
if (opts.arrayParenthesis) {
|
|
57
|
+
out.writeStringAscii('(');
|
|
58
|
+
}
|
|
59
|
+
for (let i = 0; i < value.length; i++) {
|
|
60
|
+
if (i !== 0) out.writeStringAscii(',');
|
|
61
|
+
if (value[i] == null) {
|
|
62
|
+
out.writeStringAscii('NULL');
|
|
63
|
+
} else TextEncoder.writeParam(out, value[i], opts, info);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (opts.arrayParenthesis) {
|
|
67
|
+
out.writeStringAscii(')');
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
if (value.type != null && GEO_TYPES.has(value.type)) {
|
|
71
|
+
//GeoJSON format.
|
|
72
|
+
const isMariaDb = info.isMariaDB();
|
|
73
|
+
const prefix =
|
|
74
|
+
(isMariaDb && info.hasMinVersion(10, 1, 4)) || (!isMariaDb && info.hasMinVersion(5, 7, 6)) ? 'ST_' : '';
|
|
75
|
+
|
|
76
|
+
switch (value.type) {
|
|
77
|
+
case 'Point':
|
|
78
|
+
out.writeStringAscii(
|
|
79
|
+
prefix + "PointFromText('POINT(" + TextEncoder.geoPointToString(value.coordinates) + ")')"
|
|
80
|
+
);
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case 'LineString':
|
|
84
|
+
out.writeStringAscii(
|
|
85
|
+
prefix + "LineFromText('LINESTRING(" + TextEncoder.geoArrayPointToString(value.coordinates) + ")')"
|
|
86
|
+
);
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'Polygon':
|
|
90
|
+
out.writeStringAscii(
|
|
91
|
+
prefix +
|
|
92
|
+
"PolygonFromText('POLYGON(" +
|
|
93
|
+
TextEncoder.geoMultiArrayPointToString(value.coordinates) +
|
|
94
|
+
")')"
|
|
95
|
+
);
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case 'MultiPoint':
|
|
99
|
+
out.writeStringAscii(
|
|
100
|
+
prefix +
|
|
101
|
+
"MULTIPOINTFROMTEXT('MULTIPOINT(" +
|
|
102
|
+
TextEncoder.geoArrayPointToString(value.coordinates) +
|
|
103
|
+
")')"
|
|
104
|
+
);
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case 'MultiLineString':
|
|
108
|
+
out.writeStringAscii(
|
|
109
|
+
prefix +
|
|
110
|
+
"MLineFromText('MULTILINESTRING(" +
|
|
111
|
+
TextEncoder.geoMultiArrayPointToString(value.coordinates) +
|
|
112
|
+
")')"
|
|
113
|
+
);
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case 'MultiPolygon':
|
|
117
|
+
out.writeStringAscii(
|
|
118
|
+
prefix +
|
|
119
|
+
"MPolyFromText('MULTIPOLYGON(" +
|
|
120
|
+
TextEncoder.geoMultiPolygonToString(value.coordinates) +
|
|
121
|
+
")')"
|
|
122
|
+
);
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case 'GeometryCollection':
|
|
126
|
+
out.writeStringAscii(
|
|
127
|
+
prefix +
|
|
128
|
+
"GeomCollFromText('GEOMETRYCOLLECTION(" +
|
|
129
|
+
TextEncoder.geometricCollectionToString(value.geometries) +
|
|
130
|
+
")')"
|
|
131
|
+
);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
} else if (String === value.constructor) {
|
|
135
|
+
out.writeStringEscapeQuote(value);
|
|
136
|
+
break;
|
|
137
|
+
} else {
|
|
138
|
+
if (opts.permitSetMultiParamEntries) {
|
|
139
|
+
let first = true;
|
|
140
|
+
for (const key in value) {
|
|
141
|
+
const val = value[key];
|
|
142
|
+
if (typeof val === 'function') continue;
|
|
143
|
+
|
|
144
|
+
if (first) {
|
|
145
|
+
first = false;
|
|
146
|
+
} else {
|
|
147
|
+
out.writeStringAscii(',');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
out.writeString('`' + key + '`');
|
|
151
|
+
|
|
152
|
+
if (val == null) {
|
|
153
|
+
out.writeStringAscii('=NULL');
|
|
154
|
+
} else {
|
|
155
|
+
out.writeStringAscii('=');
|
|
156
|
+
TextEncoder.writeParam(out, val, opts, info);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (first) out.writeStringEscapeQuote(JSON.stringify(value));
|
|
160
|
+
} else {
|
|
161
|
+
out.writeStringEscapeQuote(JSON.stringify(value));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static geometricCollectionToString(geo) {
|
|
170
|
+
if (!geo) return '';
|
|
171
|
+
|
|
172
|
+
const len = geo.length;
|
|
173
|
+
let st = '';
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < len; i++) {
|
|
176
|
+
const item = geo[i];
|
|
177
|
+
//GeoJSON format.
|
|
178
|
+
if (i !== 0) st += ',';
|
|
179
|
+
|
|
180
|
+
switch (item.type) {
|
|
181
|
+
case 'Point':
|
|
182
|
+
st += `POINT(${TextEncoder.geoPointToString(item.coordinates)})`;
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case 'LineString':
|
|
186
|
+
st += `LINESTRING(${TextEncoder.geoArrayPointToString(item.coordinates)})`;
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case 'Polygon':
|
|
190
|
+
st += `POLYGON(${TextEncoder.geoMultiArrayPointToString(item.coordinates)})`;
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
case 'MultiPoint':
|
|
194
|
+
st += `MULTIPOINT(${TextEncoder.geoArrayPointToString(item.coordinates)})`;
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case 'MultiLineString':
|
|
198
|
+
st += `MULTILINESTRING(${TextEncoder.geoMultiArrayPointToString(item.coordinates)})`;
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'MultiPolygon':
|
|
202
|
+
st += `MULTIPOLYGON(${TextEncoder.geoMultiPolygonToString(item.coordinates)})`;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return st;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
static geoMultiPolygonToString(coords) {
|
|
210
|
+
if (!coords) return '';
|
|
211
|
+
|
|
212
|
+
const len = coords.length;
|
|
213
|
+
if (len === 0) return '';
|
|
214
|
+
|
|
215
|
+
let st = '(';
|
|
216
|
+
|
|
217
|
+
for (let i = 0; i < len; i++) {
|
|
218
|
+
if (i !== 0) st += ',(';
|
|
219
|
+
st += TextEncoder.geoMultiArrayPointToString(coords[i]) + ')';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return st;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static geoMultiArrayPointToString(coords) {
|
|
226
|
+
if (!coords) return '';
|
|
227
|
+
|
|
228
|
+
const len = coords.length;
|
|
229
|
+
if (len === 0) return '';
|
|
230
|
+
|
|
231
|
+
let st = '(';
|
|
232
|
+
|
|
233
|
+
for (let i = 0; i < len; i++) {
|
|
234
|
+
if (i !== 0) st += ',(';
|
|
235
|
+
st += TextEncoder.geoArrayPointToString(coords[i]) + ')';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return st;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
static geoArrayPointToString(coords) {
|
|
242
|
+
if (!coords) return '';
|
|
243
|
+
|
|
244
|
+
const len = coords.length;
|
|
245
|
+
if (len === 0) return '';
|
|
246
|
+
|
|
247
|
+
let st = '';
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < len; i++) {
|
|
250
|
+
if (i !== 0) st += ',';
|
|
251
|
+
st += TextEncoder.geoPointToString(coords[i]);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return st;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
static geoPointToString(coords) {
|
|
258
|
+
if (!coords) return '';
|
|
259
|
+
const x = isNaN(coords[0]) ? '' : coords[0];
|
|
260
|
+
const y = isNaN(coords[1]) ? '' : coords[1];
|
|
261
|
+
return x + ' ' + y;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
static getLocalDate(date) {
|
|
265
|
+
const year = date.getFullYear();
|
|
266
|
+
const month = date.getMonth() + 1;
|
|
267
|
+
const day = date.getDate();
|
|
268
|
+
const hours = date.getHours();
|
|
269
|
+
const minutes = date.getMinutes();
|
|
270
|
+
const seconds = date.getSeconds();
|
|
271
|
+
const ms = date.getMilliseconds();
|
|
272
|
+
|
|
273
|
+
const d = "'" + year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
|
|
274
|
+
|
|
275
|
+
if (ms === 0) return d + "'";
|
|
276
|
+
|
|
277
|
+
return d + '.' + (ms < 10 ? '00' : ms < 100 ? '0' : '') + ms + "'";
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
static getFixedFormatDate(date) {
|
|
281
|
+
const year = date.getFullYear();
|
|
282
|
+
const mon = date.getMonth() + 1;
|
|
283
|
+
const day = date.getDate();
|
|
284
|
+
const hour = date.getHours();
|
|
285
|
+
const min = date.getMinutes();
|
|
286
|
+
const sec = date.getSeconds();
|
|
287
|
+
const ms = date.getMilliseconds();
|
|
288
|
+
|
|
289
|
+
let result =
|
|
290
|
+
"'" +
|
|
291
|
+
formatDigit(year, 4) +
|
|
292
|
+
'-' +
|
|
293
|
+
formatDigit(mon, 2) +
|
|
294
|
+
'-' +
|
|
295
|
+
formatDigit(day, 2) +
|
|
296
|
+
' ' +
|
|
297
|
+
formatDigit(hour, 2) +
|
|
298
|
+
':' +
|
|
299
|
+
formatDigit(min, 2) +
|
|
300
|
+
':' +
|
|
301
|
+
formatDigit(sec, 2);
|
|
302
|
+
|
|
303
|
+
if (ms > 0) {
|
|
304
|
+
result += '.' + formatDigit(ms, 3);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return result + "'";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = TextEncoder;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const Execute = require('./execute');
|
|
7
|
+
const { Readable } = require('stream');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Protocol COM_STMT_EXECUTE with streaming events.
|
|
11
|
+
* see : https://mariadb.com/kb/en/com_stmt_execute/
|
|
12
|
+
*/
|
|
13
|
+
class ExecuteStream extends Execute {
|
|
14
|
+
constructor(cmdParam, connOpts, prepare, socket) {
|
|
15
|
+
super(
|
|
16
|
+
() => {},
|
|
17
|
+
() => {},
|
|
18
|
+
connOpts,
|
|
19
|
+
cmdParam,
|
|
20
|
+
prepare
|
|
21
|
+
);
|
|
22
|
+
this.socket = socket;
|
|
23
|
+
this.inStream = new Readable({
|
|
24
|
+
objectMode: true,
|
|
25
|
+
read: () => {
|
|
26
|
+
this.socket.resume();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.on('fields', function (meta) {
|
|
31
|
+
this.inStream.emit('fields', meta);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
this.on('error', function (err) {
|
|
35
|
+
this.inStream.emit('error', err);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.on('close', function (err) {
|
|
39
|
+
this.inStream.emit('error', err);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.on('end', function (err) {
|
|
43
|
+
if (err) this.inStream.emit('error', err);
|
|
44
|
+
this.socket.resume();
|
|
45
|
+
this.inStream.push(null);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.inStream.close = function () {
|
|
49
|
+
this.handleNewRows = () => {};
|
|
50
|
+
this.socket.resume();
|
|
51
|
+
}.bind(this);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
handleNewRows(row) {
|
|
55
|
+
if (!this.inStream.push(row)) {
|
|
56
|
+
this.socket.pause();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = ExecuteStream;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
2
|
+
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
3
|
+
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
const Parser = require('./parser');
|
|
7
|
+
const Errors = require('../misc/errors');
|
|
8
|
+
const BinaryEncoder = require('./encoder/binary-encoder');
|
|
9
|
+
const FieldType = require('../const/field-type');
|
|
10
|
+
const Parse = require('../misc/parse');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Protocol COM_STMT_EXECUTE
|
|
14
|
+
* see : https://mariadb.com/kb/en/com_stmt_execute/
|
|
15
|
+
*/
|
|
16
|
+
class Execute extends Parser {
|
|
17
|
+
constructor(resolve, reject, connOpts, cmdParam, prepare) {
|
|
18
|
+
super(resolve, reject, connOpts, cmdParam);
|
|
19
|
+
this.binary = true;
|
|
20
|
+
this.prepare = prepare;
|
|
21
|
+
this.canSkipMeta = true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Send COM_QUERY
|
|
26
|
+
*
|
|
27
|
+
* @param out output writer
|
|
28
|
+
* @param opts connection options
|
|
29
|
+
* @param info connection information
|
|
30
|
+
*/
|
|
31
|
+
start(out, opts, info) {
|
|
32
|
+
this.onPacketReceive = this.readResponsePacket;
|
|
33
|
+
this.values = [];
|
|
34
|
+
|
|
35
|
+
if (this.opts.namedPlaceholders) {
|
|
36
|
+
if (this.prepare) {
|
|
37
|
+
// using named placeholders, so change values accordingly
|
|
38
|
+
this.values = new Array(this.prepare.parameterCount);
|
|
39
|
+
this.placeHolderIndex = this.prepare._placeHolderIndex;
|
|
40
|
+
} else {
|
|
41
|
+
const res = Parse.searchPlaceholder(this.sql);
|
|
42
|
+
this.placeHolderIndex = res.placeHolderIndex;
|
|
43
|
+
this.values = new Array(this.placeHolderIndex.length);
|
|
44
|
+
}
|
|
45
|
+
if (this.initialValues) {
|
|
46
|
+
for (let i = 0; i < this.placeHolderIndex.length; i++) {
|
|
47
|
+
this.values[i] = this.initialValues[this.placeHolderIndex[i]];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
if (this.initialValues)
|
|
52
|
+
this.values = Array.isArray(this.initialValues) ? this.initialValues : [this.initialValues];
|
|
53
|
+
}
|
|
54
|
+
this.parameterCount = this.prepare ? this.prepare.parameterCount : this.values.length;
|
|
55
|
+
|
|
56
|
+
if (!this.validateParameters(info)) return;
|
|
57
|
+
|
|
58
|
+
// fill parameter data type
|
|
59
|
+
this.parametersType = new Array(this.parameterCount);
|
|
60
|
+
let hasLongData = false; // send long data
|
|
61
|
+
let val;
|
|
62
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
63
|
+
val = this.values[i];
|
|
64
|
+
// special check for GEOJSON that can be null even if object is not
|
|
65
|
+
if (
|
|
66
|
+
val &&
|
|
67
|
+
val.type != null &&
|
|
68
|
+
[
|
|
69
|
+
'Point',
|
|
70
|
+
'LineString',
|
|
71
|
+
'Polygon',
|
|
72
|
+
'MultiPoint',
|
|
73
|
+
'MultiLineString',
|
|
74
|
+
'MultiPolygon',
|
|
75
|
+
'GeometryCollection'
|
|
76
|
+
].includes(val.type)
|
|
77
|
+
) {
|
|
78
|
+
const geoBuff = BinaryEncoder.getBufferFromGeometryValue(val);
|
|
79
|
+
if (geoBuff == null) {
|
|
80
|
+
this.values[i] = null;
|
|
81
|
+
val = null;
|
|
82
|
+
} else {
|
|
83
|
+
this.values[i] = Buffer.concat([
|
|
84
|
+
Buffer.from([0, 0, 0, 0]), // SRID
|
|
85
|
+
geoBuff // WKB
|
|
86
|
+
]);
|
|
87
|
+
val = this.values[i];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (val == null) {
|
|
91
|
+
this.parametersType[i] = NULL_PARAM_TYPE;
|
|
92
|
+
} else {
|
|
93
|
+
switch (typeof val) {
|
|
94
|
+
case 'boolean':
|
|
95
|
+
this.parametersType[i] = BOOLEAN_TYPE;
|
|
96
|
+
break;
|
|
97
|
+
case 'bigint':
|
|
98
|
+
if (val >= 2n ** 63n) {
|
|
99
|
+
this.parametersType[i] = BIG_BIGINT_TYPE;
|
|
100
|
+
} else {
|
|
101
|
+
this.parametersType[i] = BIGINT_TYPE;
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case 'number':
|
|
105
|
+
// additional verification, to permit query without type,
|
|
106
|
+
// like 'SELECT ?' returning same type of value
|
|
107
|
+
if (Number.isInteger(val) && val >= -2147483648 && val < 2147483647) {
|
|
108
|
+
this.parametersType[i] = INT_TYPE;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
this.parametersType[i] = DOUBLE_TYPE;
|
|
112
|
+
break;
|
|
113
|
+
case 'string':
|
|
114
|
+
this.parametersType[i] = STRING_TYPE;
|
|
115
|
+
break;
|
|
116
|
+
case 'object':
|
|
117
|
+
if (Object.prototype.toString.call(val) === '[object Date]') {
|
|
118
|
+
this.parametersType[i] = DATE_TYPE;
|
|
119
|
+
} else if (Buffer.isBuffer(val)) {
|
|
120
|
+
if (val.length < 16384 || !this.prepare) {
|
|
121
|
+
this.parametersType[i] = BLOB_TYPE;
|
|
122
|
+
} else {
|
|
123
|
+
this.parametersType[i] = LONGBLOB_TYPE;
|
|
124
|
+
hasLongData = true;
|
|
125
|
+
}
|
|
126
|
+
} else if (typeof val.toSqlString === 'function') {
|
|
127
|
+
this.parametersType[i] = STRING_FCT_TYPE;
|
|
128
|
+
} else if (typeof val.pipe === 'function' && typeof val.read === 'function') {
|
|
129
|
+
hasLongData = true;
|
|
130
|
+
this.parametersType[i] = STREAM_TYPE;
|
|
131
|
+
} else if (String === val.constructor) {
|
|
132
|
+
this.parametersType[i] = STRING_TOSTR_TYPE;
|
|
133
|
+
} else {
|
|
134
|
+
this.parametersType[i] = STRINGIFY_TYPE;
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// send long data using COM_STMT_SEND_LONG_DATA
|
|
142
|
+
this.longDataStep = false; // send long data
|
|
143
|
+
if (hasLongData) {
|
|
144
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
145
|
+
if (this.parametersType[i].isLongData()) {
|
|
146
|
+
if (opts.logger.query)
|
|
147
|
+
opts.logger.query(
|
|
148
|
+
`EXECUTE: (${this.prepare ? this.prepare.id : -1}) sql: ${opts.logParam ? this.displaySql() : this.sql}`
|
|
149
|
+
);
|
|
150
|
+
if (!this.longDataStep) {
|
|
151
|
+
this.longDataStep = true;
|
|
152
|
+
this.registerStreamSendEvent(out, info);
|
|
153
|
+
this.currentParam = i;
|
|
154
|
+
}
|
|
155
|
+
this.sendComStmtLongData(out, info, this.values[i]);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!this.longDataStep) {
|
|
162
|
+
// no stream parameter, so can send directly
|
|
163
|
+
if (opts.logger.query)
|
|
164
|
+
opts.logger.query(
|
|
165
|
+
`EXECUTE: (${this.prepare ? this.prepare.id : -1}) sql: ${opts.logParam ? this.displaySql() : this.sql}`
|
|
166
|
+
);
|
|
167
|
+
this.sendComStmtExecute(out, info);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Validate that parameters exists and are defined.
|
|
173
|
+
*
|
|
174
|
+
* @param info connection info
|
|
175
|
+
* @returns {boolean} return false if any error occur.
|
|
176
|
+
*/
|
|
177
|
+
validateParameters(info) {
|
|
178
|
+
//validate parameter size.
|
|
179
|
+
if (this.parameterCount > this.values.length) {
|
|
180
|
+
this.sendCancelled(
|
|
181
|
+
`Parameter at position ${this.values.length} is not set\\nsql: ${
|
|
182
|
+
this.opts.logParam ? this.displaySql() : this.sql
|
|
183
|
+
}`,
|
|
184
|
+
Errors.ER_MISSING_PARAMETER,
|
|
185
|
+
info
|
|
186
|
+
);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// validate placeholder
|
|
191
|
+
if (this.opts.namedPlaceholders && this.placeHolderIndex) {
|
|
192
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
193
|
+
if (this.values[i] === undefined) {
|
|
194
|
+
let errMsg = `Parameter named ${this.placeHolderIndex[i]} is not set`;
|
|
195
|
+
if (this.placeHolderIndex.length < this.parameterCount) {
|
|
196
|
+
errMsg = `Command expect ${this.parameterCount} parameters, but found only ${this.placeHolderIndex.length} named parameters. You probably use question mark in place of named parameters`;
|
|
197
|
+
}
|
|
198
|
+
this.sendCancelled(errMsg, Errors.ER_PARAMETER_UNDEFINED, info);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
sendComStmtLongData(out, info, value) {
|
|
207
|
+
out.startPacket(this);
|
|
208
|
+
out.writeInt8(0x18);
|
|
209
|
+
out.writeInt32(this.prepare.id);
|
|
210
|
+
out.writeInt16(this.currentParam);
|
|
211
|
+
|
|
212
|
+
if (Buffer.isBuffer(value)) {
|
|
213
|
+
out.writeBuffer(value, 0, value.length);
|
|
214
|
+
out.flush();
|
|
215
|
+
this.currentParam++;
|
|
216
|
+
return this.paramWritten();
|
|
217
|
+
}
|
|
218
|
+
this.sending = true;
|
|
219
|
+
|
|
220
|
+
// streaming
|
|
221
|
+
value.on('data', function (chunk) {
|
|
222
|
+
out.writeBuffer(chunk, 0, chunk.length);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
value.on(
|
|
226
|
+
'end',
|
|
227
|
+
function () {
|
|
228
|
+
out.flush();
|
|
229
|
+
this.currentParam++;
|
|
230
|
+
this.paramWritten();
|
|
231
|
+
}.bind(this)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Send a COM_STMT_EXECUTE
|
|
237
|
+
* @param out
|
|
238
|
+
* @param info
|
|
239
|
+
*/
|
|
240
|
+
sendComStmtExecute(out, info) {
|
|
241
|
+
let nullCount = ~~((this.parameterCount + 7) / 8);
|
|
242
|
+
const nullBitsBuffer = Buffer.alloc(nullCount);
|
|
243
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
244
|
+
if (this.values[i] == null) {
|
|
245
|
+
nullBitsBuffer[~~(i / 8)] |= 1 << i % 8;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
out.startPacket(this);
|
|
250
|
+
out.writeInt8(0x17); // COM_STMT_EXECUTE
|
|
251
|
+
out.writeInt32(this.prepare ? this.prepare.id : -1); // Statement id
|
|
252
|
+
out.writeInt8(0); // no cursor flag
|
|
253
|
+
out.writeInt32(1); // 1 command
|
|
254
|
+
out.writeBuffer(nullBitsBuffer, 0, nullCount); // null buffer
|
|
255
|
+
out.writeInt8(1); // always send type to server
|
|
256
|
+
|
|
257
|
+
// send types
|
|
258
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
259
|
+
out.writeInt8(this.parametersType[i].type);
|
|
260
|
+
out.writeInt8(0);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
//********************************************
|
|
264
|
+
// send not null / not streaming values
|
|
265
|
+
//********************************************
|
|
266
|
+
for (let i = 0; i < this.parameterCount; i++) {
|
|
267
|
+
const parameterType = this.parametersType[i];
|
|
268
|
+
if (parameterType.encoder) parameterType.encoder(out, this.values[i]);
|
|
269
|
+
}
|
|
270
|
+
out.flush();
|
|
271
|
+
this.sending = false;
|
|
272
|
+
this.emit('send_end');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Define params events.
|
|
277
|
+
* Each parameter indicate that he is written to socket,
|
|
278
|
+
* emitting event so next stream parameter can be written.
|
|
279
|
+
*/
|
|
280
|
+
registerStreamSendEvent(out, info) {
|
|
281
|
+
// note : Implementation use recursive calls, but stack won't get near v8 max call stack size
|
|
282
|
+
//since event launched for stream parameter only
|
|
283
|
+
this.paramWritten = function () {
|
|
284
|
+
if (this.longDataStep) {
|
|
285
|
+
for (; this.currentParam < this.parameterCount; this.currentParam++) {
|
|
286
|
+
if (this.parametersType[this.currentParam].isLongData()) {
|
|
287
|
+
const value = this.values[this.currentParam];
|
|
288
|
+
this.sendComStmtLongData(out, info, value);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
this.longDataStep = false; // all streams have been send
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!this.longDataStep) {
|
|
296
|
+
this.sendComStmtExecute(out, info);
|
|
297
|
+
}
|
|
298
|
+
}.bind(this);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
class ParameterType {
|
|
303
|
+
constructor(type, encoder, pipe = false, isNull = false) {
|
|
304
|
+
this.pipe = pipe;
|
|
305
|
+
this.type = type;
|
|
306
|
+
this.encoder = encoder;
|
|
307
|
+
this.isNull = isNull;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
isLongData() {
|
|
311
|
+
return this.encoder === null && !this.isNull;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const NULL_PARAM_TYPE = new ParameterType(FieldType.VAR_STRING, null, false, true);
|
|
316
|
+
const BOOLEAN_TYPE = new ParameterType(FieldType.TINY, (out, value) => out.writeInt8(value ? 0x01 : 0x00));
|
|
317
|
+
const BIG_BIGINT_TYPE = new ParameterType(FieldType.NEWDECIMAL, (out, value) =>
|
|
318
|
+
out.writeLengthEncodedString(value.toString())
|
|
319
|
+
);
|
|
320
|
+
const BIGINT_TYPE = new ParameterType(FieldType.BIGINT, (out, value) => out.writeBigInt(value));
|
|
321
|
+
const INT_TYPE = new ParameterType(FieldType.INT, (out, value) => out.writeInt32(value));
|
|
322
|
+
const DOUBLE_TYPE = new ParameterType(FieldType.DOUBLE, (out, value) => out.writeDouble(value));
|
|
323
|
+
const STRING_TYPE = new ParameterType(FieldType.VAR_STRING, (out, value) => out.writeLengthEncodedString(value));
|
|
324
|
+
const STRING_TOSTR_TYPE = new ParameterType(FieldType.VAR_STRING, (out, value) =>
|
|
325
|
+
out.writeLengthEncodedString(value.toString())
|
|
326
|
+
);
|
|
327
|
+
const DATE_TYPE = new ParameterType(FieldType.DATETIME, (out, value) => out.writeBinaryDate(value));
|
|
328
|
+
const BLOB_TYPE = new ParameterType(FieldType.BLOB, (out, value) => out.writeLengthEncodedBuffer(value));
|
|
329
|
+
const LONGBLOB_TYPE = new ParameterType(FieldType.BLOB, null);
|
|
330
|
+
const STRING_FCT_TYPE = new ParameterType(FieldType.VAR_STRING, (out, value) =>
|
|
331
|
+
out.writeLengthEncodedString(String(value.toSqlString()))
|
|
332
|
+
);
|
|
333
|
+
const STREAM_TYPE = new ParameterType(FieldType.BLOB, null, true);
|
|
334
|
+
const STRINGIFY_TYPE = new ParameterType(FieldType.VAR_STRING, (out, value) =>
|
|
335
|
+
out.writeLengthEncodedString(JSON.stringify(value))
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
module.exports = Execute;
|