jsql-neo 4.2.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -94
- package/bin/jsql +138 -0
- package/index.d.ts +351 -0
- package/index.js +18 -0
- package/lib/migrate.js +242 -0
- package/lib/mysql_server.js +335 -6
- package/lib/redis_server.js +448 -0
- package/lib/sql.js +533 -87
- package/lib/table.js +4 -2
- package/lib/web_ui.js +226 -0
- package/package.json +66 -47
- package/test/smoke.js +58 -0
- package/wasm/browser.d.ts +88 -0
- package/wasm/browser.mjs +404 -0
- package/wasm/browser_bg.mjs +462 -0
package/lib/mysql_server.js
CHANGED
|
@@ -8,7 +8,7 @@ const net = require('net');
|
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const fs = require('fs');
|
|
11
|
-
const { executeSQL, parseSQL, splitStatements } = require('./sql');
|
|
11
|
+
const { executeSQL, parseSQL, splitStatements, applyParams } = require('./sql');
|
|
12
12
|
const Database = require('./database');
|
|
13
13
|
|
|
14
14
|
const SERVER_VERSION = '8.0.0-jsql-neo';
|
|
@@ -31,6 +31,7 @@ const MYSQL_TYPE_TINY = 1, MYSQL_TYPE_LONG = 3, MYSQL_TYPE_LONGLONG = 8,
|
|
|
31
31
|
MYSQL_TYPE_JSON = 245, MYSQL_TYPE_NULL = 6;
|
|
32
32
|
|
|
33
33
|
function encodeLenenc(value) {
|
|
34
|
+
if (typeof value === 'bigint') value = Number(value);
|
|
34
35
|
if (value === null) return Buffer.from([0xfb]);
|
|
35
36
|
if (value < 0xfb) return Buffer.from([value]);
|
|
36
37
|
if (value <= 0xffff) {
|
|
@@ -62,6 +63,13 @@ function parseLenenc(buf, offset) {
|
|
|
62
63
|
return { value: buf.readUInt32LE(offset + 1), size: 5 };
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
function readLenenc(buf, offset) {
|
|
67
|
+
const { value, size } = parseLenenc(buf, offset);
|
|
68
|
+
if (value === null) return { value: null, off: offset + size };
|
|
69
|
+
const str = buf.slice(offset + size, offset + size + value).toString('utf8');
|
|
70
|
+
return { value: str, off: offset + size + value };
|
|
71
|
+
}
|
|
72
|
+
|
|
65
73
|
class PacketBuilder {
|
|
66
74
|
constructor() {
|
|
67
75
|
this.bufs = [];
|
|
@@ -128,7 +136,38 @@ function toMysqlErrno(e) {
|
|
|
128
136
|
if (e && typeof e.code === 'number') return e.code;
|
|
129
137
|
const m = e && e.message ? String(e.message).match(/^(ER_[A-Z_]+)/) : null;
|
|
130
138
|
if (m) {
|
|
131
|
-
const known = {
|
|
139
|
+
const known = {
|
|
140
|
+
ER_DUP_ENTRY: 1062,
|
|
141
|
+
ER_NO_SUCH_TABLE: 1146,
|
|
142
|
+
ER_TABLE_EXISTS: 1050,
|
|
143
|
+
ER_TABLE_EXISTS_ERROR: 1050,
|
|
144
|
+
ER_PARSE_ERROR: 1064,
|
|
145
|
+
ER_BAD_FIELD_ERROR: 1054,
|
|
146
|
+
ER_BAD_NULL_ERROR: 1048,
|
|
147
|
+
ER_ACCESS_DENIED_ERROR: 1045,
|
|
148
|
+
ER_DBACCESS_DENIED_ERROR: 1044,
|
|
149
|
+
ER_BAD_DB_ERROR: 1049,
|
|
150
|
+
ER_WRONG_DB_NAME: 1102,
|
|
151
|
+
ER_WRONG_TABLE_NAME: 1103,
|
|
152
|
+
ER_WRONG_COLUMN_NAME: 1166,
|
|
153
|
+
ER_DATA_TOO_LONG: 1406,
|
|
154
|
+
ER_OUT_OF_RANGE: 1264,
|
|
155
|
+
ER_CHECK_CONSTRAINT: 3819,
|
|
156
|
+
ER_NO_DEFAULT_FOR_FIELD: 1364,
|
|
157
|
+
ER_CANT_DROP_FIELD_OR_KEY: 1091,
|
|
158
|
+
ER_CANT_DROP_DATABASE: 1008,
|
|
159
|
+
ER_EMPTY_QUERY: 1065,
|
|
160
|
+
ER_UNKNOWN_TABLE: 1109,
|
|
161
|
+
ER_NON_UNIQ_ERROR: 1052,
|
|
162
|
+
ER_WRONG_FIELD_WITH_GROUP: 1055,
|
|
163
|
+
ER_WRONG_VALUE_COUNT_ON_ROW: 1136,
|
|
164
|
+
ER_MISSING_TABLE: 1052,
|
|
165
|
+
ER_SP_DOES_NOT_EXIST: 1305,
|
|
166
|
+
ER_NOT_SUPPORTED_YET: 1235,
|
|
167
|
+
ER_LOCK_DEADLOCK: 1213,
|
|
168
|
+
ER_LOCK_WAIT_TIMEOUT: 1205,
|
|
169
|
+
ER_UNKNOWN_ERROR: 1105,
|
|
170
|
+
};
|
|
132
171
|
if (known[m[1]]) return known[m[1]];
|
|
133
172
|
}
|
|
134
173
|
return 1105;
|
|
@@ -224,6 +263,102 @@ function resultSetPacket(result, tableSchema, baseSeq, rawRow) {
|
|
|
224
263
|
return { packets, sequence };
|
|
225
264
|
}
|
|
226
265
|
|
|
266
|
+
function binaryResultSetPacket(result, tableSchema, baseSeq) {
|
|
267
|
+
const packets = [];
|
|
268
|
+
let sequence = baseSeq || 0;
|
|
269
|
+
const push = (buf) => {
|
|
270
|
+
const header = Buffer.alloc(4);
|
|
271
|
+
header.writeUIntLE(buf.length, 0, 3);
|
|
272
|
+
header[3] = sequence;
|
|
273
|
+
sequence++;
|
|
274
|
+
packets.push(header);
|
|
275
|
+
packets.push(buf);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const inferType = (name, value) => {
|
|
279
|
+
if (tableSchema && tableSchema[name]) return columnTypeFromSchema(tableSchema[name]);
|
|
280
|
+
if (value === null || value === undefined) return MYSQL_TYPE_NULL;
|
|
281
|
+
if (typeof value === 'number') return Number.isInteger(value) ? MYSQL_TYPE_LONGLONG : MYSQL_TYPE_DOUBLE;
|
|
282
|
+
if (typeof value === 'boolean') return MYSQL_TYPE_TINY;
|
|
283
|
+
if (typeof value === 'object') return MYSQL_TYPE_JSON;
|
|
284
|
+
return MYSQL_TYPE_VAR_STRING;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const cols = result.columns || [];
|
|
288
|
+
push(encodeLenenc(cols.length));
|
|
289
|
+
const sampleRow = (result.rows && result.rows[0]) || null;
|
|
290
|
+
const types = [];
|
|
291
|
+
for (let ci = 0; ci < cols.length; ci++) {
|
|
292
|
+
let sample;
|
|
293
|
+
if (sampleRow !== null) {
|
|
294
|
+
if (Array.isArray(sampleRow)) sample = sampleRow[ci];
|
|
295
|
+
else sample = sampleRow[cols[ci]];
|
|
296
|
+
}
|
|
297
|
+
const t = inferType(cols[ci], sample);
|
|
298
|
+
types.push(t);
|
|
299
|
+
push(columnDefinition({ name: cols[ci], table: result.table || '', type: t, length: 1024 }));
|
|
300
|
+
}
|
|
301
|
+
push(eofPacket());
|
|
302
|
+
|
|
303
|
+
const encodeValue = (v, type) => {
|
|
304
|
+
if (v === null || v === undefined) return null;
|
|
305
|
+
if (type === MYSQL_TYPE_LONG || type === MYSQL_TYPE_LONGLONG || type === MYSQL_TYPE_DOUBLE) {
|
|
306
|
+
const num = Number(v);
|
|
307
|
+
if (type === MYSQL_TYPE_DOUBLE) {
|
|
308
|
+
if (!Number.isFinite(num)) return encodeLenencString(String(v));
|
|
309
|
+
const b = Buffer.alloc(8); b.writeDoubleLE(num, 0); return b;
|
|
310
|
+
}
|
|
311
|
+
if (Number.isInteger(num) && num <= 2147483647 && num >= -2147483648) {
|
|
312
|
+
const b = Buffer.alloc(4); b.writeInt32LE(num, 0); return b;
|
|
313
|
+
}
|
|
314
|
+
if (!Number.isFinite(num)) return encodeLenencString(String(v));
|
|
315
|
+
const b = Buffer.alloc(8); b.writeBigInt64LE(BigInt(Math.trunc(num)), 0); return b;
|
|
316
|
+
}
|
|
317
|
+
if (type === MYSQL_TYPE_TINY) {
|
|
318
|
+
const b = Buffer.alloc(1); b.writeInt8(v ? 1 : 0, 0); return b;
|
|
319
|
+
}
|
|
320
|
+
if (type === MYSQL_TYPE_DATE || type === MYSQL_TYPE_DATETIME) {
|
|
321
|
+
const s = String(v);
|
|
322
|
+
const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2}))?/);
|
|
323
|
+
if (!m) return encodeLenencString(String(v));
|
|
324
|
+
const hasTime = !!m[4];
|
|
325
|
+
const buf = hasTime ? Buffer.alloc(8) : Buffer.alloc(5);
|
|
326
|
+
let off = 0;
|
|
327
|
+
buf[off++] = hasTime ? 7 : 4;
|
|
328
|
+
buf.writeUInt16LE(parseInt(m[1], 10), off); off += 2;
|
|
329
|
+
buf[off++] = parseInt(m[2], 10);
|
|
330
|
+
buf[off++] = parseInt(m[3], 10);
|
|
331
|
+
if (hasTime) {
|
|
332
|
+
buf[off++] = parseInt(m[4], 10);
|
|
333
|
+
buf[off++] = parseInt(m[5], 10);
|
|
334
|
+
buf[off++] = parseInt(m[6], 10);
|
|
335
|
+
}
|
|
336
|
+
return buf;
|
|
337
|
+
}
|
|
338
|
+
return encodeLenencString(typeof v === 'object' ? JSON.stringify(v) : v);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
for (const row of result.rows || []) {
|
|
342
|
+
const parts = [Buffer.from([0x00])];
|
|
343
|
+
const nb = Buffer.alloc(Math.ceil(cols.length / 8));
|
|
344
|
+
const encoded = [];
|
|
345
|
+
for (let i = 0; i < cols.length; i++) {
|
|
346
|
+
const v = Array.isArray(row) ? row[i] : row[cols[i]];
|
|
347
|
+
if (v === null || v === undefined) {
|
|
348
|
+
nb[Math.floor(i / 8)] |= (1 << (i % 8));
|
|
349
|
+
encoded.push(null);
|
|
350
|
+
} else {
|
|
351
|
+
encoded.push(encodeValue(v, types[i]));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
parts.push(nb);
|
|
355
|
+
for (const e of encoded) if (e !== null) parts.push(e);
|
|
356
|
+
push(Buffer.concat(parts));
|
|
357
|
+
}
|
|
358
|
+
push(eofPacket());
|
|
359
|
+
return { packets, sequence };
|
|
360
|
+
}
|
|
361
|
+
|
|
227
362
|
class MysqlConnection {
|
|
228
363
|
constructor(socket, server) {
|
|
229
364
|
this.socket = socket;
|
|
@@ -236,6 +371,16 @@ class MysqlConnection {
|
|
|
236
371
|
this.user = null;
|
|
237
372
|
this.authFails = 0;
|
|
238
373
|
this.multiStatements = false;
|
|
374
|
+
this._stmts = new Map();
|
|
375
|
+
this._stmtSeq = 0;
|
|
376
|
+
this.session = {
|
|
377
|
+
lastInsertId: 0,
|
|
378
|
+
rowCount: 0,
|
|
379
|
+
foundRows: 0,
|
|
380
|
+
connectionId: server._connectionCounter,
|
|
381
|
+
currentDb: null,
|
|
382
|
+
sysvars: {},
|
|
383
|
+
};
|
|
239
384
|
if (server.handshakeTimeout > 0) {
|
|
240
385
|
this._authTimer = setTimeout(() => {
|
|
241
386
|
if (!this.authenticated) {
|
|
@@ -335,6 +480,11 @@ class MysqlConnection {
|
|
|
335
480
|
case 0x11: this._handleChangeUser(body, cmdSeq); break; // COM_CHANGE_USER
|
|
336
481
|
case 0x0e: this._send(okPacket()); break; // COM_PING
|
|
337
482
|
case 0x1f: this._send(okPacket()); break; // COM_RESET_CONNECTION
|
|
483
|
+
case 0x16: this._handleStmtPrepare(body); break; // COM_STMT_PREPARE
|
|
484
|
+
case 0x17: this._handleStmtExecute(body); break; // COM_STMT_EXECUTE
|
|
485
|
+
case 0x18: break; // COM_STMT_SEND_LONG_DATA (忽略)
|
|
486
|
+
case 0x19: this._handleStmtClose(body); break; // COM_STMT_CLOSE
|
|
487
|
+
case 0x1a: this._send(okPacket()); break; // COM_STMT_RESET
|
|
338
488
|
case 0x0a: { // COM_PROCESS_INFO
|
|
339
489
|
const seq = this.sequence;
|
|
340
490
|
this.sequence = (this.sequence + 1) & 0xff;
|
|
@@ -474,10 +624,173 @@ class MysqlConnection {
|
|
|
474
624
|
}
|
|
475
625
|
}
|
|
476
626
|
|
|
477
|
-
|
|
627
|
+
countPlaceholders(sql) {
|
|
628
|
+
let n = 0;
|
|
629
|
+
let inStr = null;
|
|
630
|
+
for (let i = 0; i < sql.length; i++) {
|
|
631
|
+
const c = sql[i];
|
|
632
|
+
if (inStr) {
|
|
633
|
+
if (c === '\\' && i + 1 < sql.length) { i++; continue; }
|
|
634
|
+
if (c === inStr) inStr = null;
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (c === "'" || c === '"' || c === '`') { inStr = c; continue; }
|
|
638
|
+
if (c === '?' && sql[i + 1] === '?') { i++; continue; }
|
|
639
|
+
if (c === '?') n++;
|
|
640
|
+
}
|
|
641
|
+
return n;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
_handleStmtPrepare(body) {
|
|
645
|
+
try {
|
|
646
|
+
const sql = body.toString('utf8');
|
|
647
|
+
const numParams = this.countPlaceholders(sql);
|
|
648
|
+
let numColumns = 0;
|
|
649
|
+
try {
|
|
650
|
+
const stmt = parseSQL(sql);
|
|
651
|
+
if (stmt && stmt.type === 'select' && stmt.columns) numColumns = stmt.columns.length;
|
|
652
|
+
} catch (e) { /* 无法解析的 SQL 仍可 prepare */ }
|
|
653
|
+
const stmtId = ++this._stmtSeq;
|
|
654
|
+
this._stmts.set(stmtId, { sql, numParams, numColumns });
|
|
655
|
+
|
|
656
|
+
const pkt = Buffer.alloc(12);
|
|
657
|
+
let off = 0;
|
|
658
|
+
pkt[off++] = 0x00;
|
|
659
|
+
pkt.writeUInt32LE(stmtId, off); off += 4;
|
|
660
|
+
pkt.writeUInt16LE(numColumns, off); off += 2;
|
|
661
|
+
pkt.writeUInt16LE(numParams, off); off += 2;
|
|
662
|
+
pkt[off++] = 0x00;
|
|
663
|
+
pkt.writeUInt16LE(0, off);
|
|
664
|
+
this._send(pkt);
|
|
665
|
+
|
|
666
|
+
if (numParams > 0) {
|
|
667
|
+
for (let i = 0; i < numParams; i++) {
|
|
668
|
+
this._send(columnDefinition({ name: '?', type: MYSQL_TYPE_VAR_STRING }));
|
|
669
|
+
}
|
|
670
|
+
this._send(eofPacket());
|
|
671
|
+
}
|
|
672
|
+
if (numColumns > 0) {
|
|
673
|
+
for (let i = 0; i < numColumns; i++) {
|
|
674
|
+
this._send(columnDefinition({ name: 'col' + (i + 1), type: MYSQL_TYPE_VAR_STRING }));
|
|
675
|
+
}
|
|
676
|
+
this._send(eofPacket());
|
|
677
|
+
}
|
|
678
|
+
} catch (e) {
|
|
679
|
+
this._send(errPacket(toMysqlErrno(e), e.message));
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
_handleStmtClose(body) {
|
|
684
|
+
if (body.length >= 4) {
|
|
685
|
+
const stmtId = body.readUInt32LE(0);
|
|
686
|
+
this._stmts.delete(stmtId);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
_handleStmtExecute(body) {
|
|
691
|
+
try {
|
|
692
|
+
if (body.length < 9) throw new Error('malformed COM_STMT_EXECUTE');
|
|
693
|
+
const stmtId = body.readUInt32LE(0);
|
|
694
|
+
const stmt = this._stmts.get(stmtId);
|
|
695
|
+
if (!stmt) {
|
|
696
|
+
this._send(errPacket(1243, 'Unknown prepared statement handler (' + stmtId + ') given to mysqld_stmt_execute'));
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
const flags = body[4];
|
|
700
|
+
const iteration = body.readUInt32LE(5);
|
|
701
|
+
let values = null;
|
|
702
|
+
if (stmt.numParams > 0) {
|
|
703
|
+
let off = 9;
|
|
704
|
+
const numParams = stmt.numParams;
|
|
705
|
+
const nullBitmapLen = Math.ceil(numParams / 8);
|
|
706
|
+
if (body.length < off + nullBitmapLen) throw new Error('malformed COM_STMT_EXECUTE params');
|
|
707
|
+
const nullBitmap = body.slice(off, off + nullBitmapLen);
|
|
708
|
+
off += nullBitmapLen;
|
|
709
|
+
if (off >= body.length) throw new Error('malformed COM_STMT_EXECUTE params');
|
|
710
|
+
const newParamsBound = body[off];
|
|
711
|
+
off += 1;
|
|
712
|
+
let types = stmt.types;
|
|
713
|
+
if (newParamsBound & 0x01) {
|
|
714
|
+
if (body.length < off + numParams * 2) throw new Error('malformed COM_STMT_EXECUTE types');
|
|
715
|
+
types = [];
|
|
716
|
+
for (let i = 0; i < numParams; i++) types.push(body.readUInt16LE(off + i * 2));
|
|
717
|
+
stmt.types = types;
|
|
718
|
+
off += numParams * 2;
|
|
719
|
+
}
|
|
720
|
+
if (!types) throw new Error('Parameter types unknown for prepared statement');
|
|
721
|
+
values = [];
|
|
722
|
+
for (let i = 0; i < numParams; i++) {
|
|
723
|
+
const isNull = nullBitmap[Math.floor(i / 8)] & (1 << (i % 8));
|
|
724
|
+
if (isNull) { values.push(null); continue; }
|
|
725
|
+
const type = types[i] & 0xff;
|
|
726
|
+
const parsed = this._readParamValue(body, off, type);
|
|
727
|
+
values.push(parsed.value);
|
|
728
|
+
off = parsed.off;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
this._handleQuery(stmt.sql, values !== null && values.length > 0 ? values : undefined);
|
|
732
|
+
} catch (e) {
|
|
733
|
+
this._send(errPacket(toMysqlErrno(e), e.message));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
_readParamValue(buf, off, type) {
|
|
738
|
+
switch (type) {
|
|
739
|
+
case MYSQL_TYPE_NULL: return { value: null, off };
|
|
740
|
+
case 0x01: { const v = buf.readInt8(off); return { value: v, off: off + 1 }; } // TINY
|
|
741
|
+
case 0x02: { const v = buf.readInt16LE(off); return { value: v, off: off + 2 }; } // SHORT
|
|
742
|
+
case 0x03: { const v = buf.readInt32LE(off); return { value: v, off: off + 4 }; } // LONG
|
|
743
|
+
case 0x08: { const v = buf.readBigInt64LE(off); return { value: Number(v), off: off + 8 }; } // LONGLONG
|
|
744
|
+
case 0x04: { const v = buf.readFloatLE(off); return { value: v, off: off + 4 }; } // FLOAT
|
|
745
|
+
case MYSQL_TYPE_DOUBLE: { const v = buf.readDoubleLE(off); return { value: v, off: off + 8 }; }
|
|
746
|
+
case 0x0a: case 0x07: case 0x0b: case MYSQL_TYPE_DATETIME: { // DATE/TIMESTAMP/DATETIME/TIME
|
|
747
|
+
const len = buf[off];
|
|
748
|
+
off += 1;
|
|
749
|
+
if (len === 0) return { value: null, off };
|
|
750
|
+
let value;
|
|
751
|
+
if (type === 0x0b) { // TIME
|
|
752
|
+
let sign = 1;
|
|
753
|
+
let p = off;
|
|
754
|
+
if (buf[p] !== 0) sign = -1;
|
|
755
|
+
p += 1;
|
|
756
|
+
const days = buf.readUInt32LE(p); p += 4;
|
|
757
|
+
const hour = buf[p++];
|
|
758
|
+
const min = buf[p++];
|
|
759
|
+
const sec = buf[p++];
|
|
760
|
+
value = sign * (days * 24 + hour) + ':' + String(min).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
|
|
761
|
+
} else {
|
|
762
|
+
let p = off;
|
|
763
|
+
const year = buf.readUInt16LE(p); p += 2;
|
|
764
|
+
const month = buf[p++];
|
|
765
|
+
const day = buf[p++];
|
|
766
|
+
let hour = 0, minute = 0, second = 0;
|
|
767
|
+
if (len >= 7) { hour = buf[p++]; minute = buf[p++]; second = buf[p++]; }
|
|
768
|
+
value = String(year).padStart(4, '0') + '-' + String(month).padStart(2, '0') + '-' + String(day).padStart(2, '0');
|
|
769
|
+
if (len >= 7) value += ' ' + String(hour).padStart(2, '0') + ':' + String(minute).padStart(2, '0') + ':' + String(second).padStart(2, '0');
|
|
770
|
+
}
|
|
771
|
+
off += len;
|
|
772
|
+
return { value, off };
|
|
773
|
+
}
|
|
774
|
+
case 0xfc: case 0xfd: case 0xfe: case 0xf9: case 0xf6: { // BLOB / VAR_STRING / STRING / ...
|
|
775
|
+
const { value, off: noff } = readLenenc(buf, off);
|
|
776
|
+
return { value, off: noff };
|
|
777
|
+
}
|
|
778
|
+
case 0xf0: { // NULL
|
|
779
|
+
return { value: null, off };
|
|
780
|
+
}
|
|
781
|
+
default: {
|
|
782
|
+
const { value, off: noff } = readLenenc(buf, off);
|
|
783
|
+
return { value, off: noff };
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
_handleQuery(sql, values) {
|
|
478
789
|
(async () => {
|
|
479
790
|
try {
|
|
480
|
-
const
|
|
791
|
+
const hasParams = values !== undefined && values !== null && values.length > 0;
|
|
792
|
+
const processedSql = hasParams ? applyParams(sql, values) : sql;
|
|
793
|
+
const statements = splitStatements(processedSql);
|
|
481
794
|
if (statements.length > 1 && !this.multiStatements) {
|
|
482
795
|
throw new Error(`too many statements (${statements.length} > 1)`);
|
|
483
796
|
}
|
|
@@ -533,18 +846,30 @@ class MysqlConnection {
|
|
|
533
846
|
allowComments: this.server.allowComments,
|
|
534
847
|
safety: this.server.safety,
|
|
535
848
|
maxStatements: 1,
|
|
849
|
+
session: this.session,
|
|
536
850
|
});
|
|
537
|
-
if (r.type === 'select' || r.type === 'showTables' || r.type === 'showDatabases' || r.type === 'describe'
|
|
851
|
+
if (r.type === 'select' || r.type === 'showTables' || r.type === 'showDatabases' || r.type === 'describe'
|
|
852
|
+
|| r.type === 'showColumns' || r.type === 'showIndex' || r.type === 'showCreateTable'
|
|
853
|
+
|| r.type === 'showVariables' || r.type === 'showStatus' || r.type === 'showGrants' || r.type === 'showWarnings') {
|
|
854
|
+
this.session.foundRows = (r.raw && r.raw.length !== undefined) ? r.raw.length : (r.rows ? r.rows.length : 0);
|
|
538
855
|
let schema = null;
|
|
539
856
|
if (r.table) {
|
|
540
857
|
schema = engine.getTableSchema
|
|
541
858
|
? await engine.getTableSchema(r.table)
|
|
542
859
|
: (engine._schemas ? engine._schemas[r.table] : null);
|
|
543
860
|
}
|
|
544
|
-
const { packets, sequence } =
|
|
861
|
+
const { packets, sequence } = hasParams
|
|
862
|
+
? binaryResultSetPacket(r, schema, this.sequence)
|
|
863
|
+
: resultSetPacket(r, schema, this.sequence);
|
|
545
864
|
this.sequence = sequence;
|
|
546
865
|
this.socket.write(Buffer.concat(packets));
|
|
547
866
|
} else {
|
|
867
|
+
if (r.type === 'insert') {
|
|
868
|
+
this.session.lastInsertId = r.insertId;
|
|
869
|
+
this.session.rowCount = r.affectedRows;
|
|
870
|
+
} else if (r.type === 'update' || r.type === 'delete' || r.type === 'truncate') {
|
|
871
|
+
this.session.rowCount = r.affectedRows;
|
|
872
|
+
}
|
|
548
873
|
this._send(okPacket(r.affectedRows || 0, r.insertId || 0));
|
|
549
874
|
}
|
|
550
875
|
}
|
|
@@ -630,6 +955,10 @@ class MysqlServer {
|
|
|
630
955
|
}
|
|
631
956
|
};
|
|
632
957
|
collect(stmt);
|
|
958
|
+
const isInfoSchema = tables.some(t => t && String(t).toLowerCase().startsWith('information_schema.'));
|
|
959
|
+
if (isInfoSchema) {
|
|
960
|
+
return { engine: await this._getEngine(currentDb), sql };
|
|
961
|
+
}
|
|
633
962
|
let db = null;
|
|
634
963
|
for (const t of tables) {
|
|
635
964
|
if (t && t.indexOf('.') !== -1) {
|