jsql-neo 4.2.0-beta.1 → 4.3.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 +34 -4
- package/lib/mysql_server.js +284 -5
- package/lib/sql.js +425 -53
- package/lib/table.js +4 -2
- package/package.json +10 -1
- package/wasm/browser.mjs +404 -0
- package/wasm/browser_bg.mjs +462 -0
package/README.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
# JSQL-NEO v4.
|
|
1
|
+
# JSQL-NEO v4.0.1
|
|
2
2
|
|
|
3
3
|
Rust-powered embedded database with **three engines + SQL + Redis-style storage** in one npm package.
|
|
4
4
|
|
|
5
|
-
> **Docs & Help:** [https://help.vexify.top](https://help.vexify.top)
|
|
6
|
-
> **GitHub:** [https://github.com/vexify-org/JSQL-neo](https://github.com/vexify-org/JSQL-neo)
|
|
7
|
-
|
|
8
5
|
## Engines
|
|
9
6
|
|
|
10
7
|
| Engine | Entry | Use Case |
|
|
@@ -122,6 +119,39 @@ db.save();
|
|
|
122
119
|
}
|
|
123
120
|
```
|
|
124
121
|
|
|
122
|
+
## ORM Compatibility
|
|
123
|
+
|
|
124
|
+
The MySQL-compatible server mode accepts standard MySQL clients, so popular Node.js ORMs work without a plugin. Tested against a live `createMysqlServer()` instance:
|
|
125
|
+
|
|
126
|
+
| ORM | Version | Results |
|
|
127
|
+
|-----|---------|---------|
|
|
128
|
+
| [Sequelize](https://sequelize.org) | v6 | ✅ 10/10 — connect, authenticate, sync (CREATE TABLE), create, bulkCreate, find, count, update, destroy, MAX() |
|
|
129
|
+
| [Knex](https://knexjs.org) | v3 | ✅ 9/9 — schema builder, insert, select, where + orderBy, count, update, delete, raw `SELECT VERSION()` |
|
|
130
|
+
| [TypeORM](https://typeorm.io) | v0.3 | ✅ 8/8 — initialize, synchronize, save, findOne, find, count, update, delete |
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
// Sequelize
|
|
134
|
+
const { Sequelize, DataTypes } = require('sequelize');
|
|
135
|
+
const sequelize = new Sequelize('default', 'root', '', {
|
|
136
|
+
host: '127.0.0.1', port: 33306, dialect: 'mysql',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Knex
|
|
140
|
+
const knex = require('knex')({
|
|
141
|
+
client: 'mysql2',
|
|
142
|
+
connection: { host: '127.0.0.1', port: 33306, user: 'root', database: 'default' },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// TypeORM
|
|
146
|
+
const ds = new DataSource({
|
|
147
|
+
type: 'mysql',
|
|
148
|
+
host: '127.0.0.1', port: 33306, username: 'root', database: 'default',
|
|
149
|
+
synchronize: true, entities: [User],
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Supported for ORM compatibility: prepared statements (`COM_STMT_PREPARE`/`EXECUTE`), binary protocol result sets, `SHOW COLUMNS` / `SHOW INDEX` / `SHOW CREATE TABLE` / `SHOW VARIABLES` / `SHOW GRANTS`, `information_schema` queries, `START TRANSACTION`, `TRUNCATE TABLE`, `SET` statements, scalar functions (`VERSION()`, `NOW()`, `CONCAT()`, `IFNULL()`, ...), and MySQL DDL forms (`int unsigned`, `auto_increment`, `ENGINE=InnoDB`, `DEFAULT CHARSET`).
|
|
154
|
+
|
|
125
155
|
## Features
|
|
126
156
|
|
|
127
157
|
- Three engines: Native (N-API Rust), WASM (wasm-pack Rust), Pure JS (local JSON)
|
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';
|
|
@@ -62,6 +62,13 @@ function parseLenenc(buf, offset) {
|
|
|
62
62
|
return { value: buf.readUInt32LE(offset + 1), size: 5 };
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function readLenenc(buf, offset) {
|
|
66
|
+
const { value, size } = parseLenenc(buf, offset);
|
|
67
|
+
if (value === null) return { value: null, off: offset + size };
|
|
68
|
+
const str = buf.slice(offset + size, offset + size + value).toString('utf8');
|
|
69
|
+
return { value: str, off: offset + size + value };
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
class PacketBuilder {
|
|
66
73
|
constructor() {
|
|
67
74
|
this.bufs = [];
|
|
@@ -224,6 +231,100 @@ function resultSetPacket(result, tableSchema, baseSeq, rawRow) {
|
|
|
224
231
|
return { packets, sequence };
|
|
225
232
|
}
|
|
226
233
|
|
|
234
|
+
function binaryResultSetPacket(result, tableSchema, baseSeq) {
|
|
235
|
+
const packets = [];
|
|
236
|
+
let sequence = baseSeq || 0;
|
|
237
|
+
const push = (buf) => {
|
|
238
|
+
const header = Buffer.alloc(4);
|
|
239
|
+
header.writeUIntLE(buf.length, 0, 3);
|
|
240
|
+
header[3] = sequence;
|
|
241
|
+
sequence++;
|
|
242
|
+
packets.push(header);
|
|
243
|
+
packets.push(buf);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const inferType = (name, value) => {
|
|
247
|
+
if (tableSchema && tableSchema[name]) return columnTypeFromSchema(tableSchema[name]);
|
|
248
|
+
if (value === null || value === undefined) return MYSQL_TYPE_NULL;
|
|
249
|
+
if (typeof value === 'number') return Number.isInteger(value) ? MYSQL_TYPE_LONGLONG : MYSQL_TYPE_DOUBLE;
|
|
250
|
+
if (typeof value === 'boolean') return MYSQL_TYPE_TINY;
|
|
251
|
+
if (typeof value === 'object') return MYSQL_TYPE_JSON;
|
|
252
|
+
return MYSQL_TYPE_VAR_STRING;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const cols = result.columns || [];
|
|
256
|
+
push(encodeLenenc(cols.length));
|
|
257
|
+
const sampleRow = (result.rows && result.rows[0]) || null;
|
|
258
|
+
const types = [];
|
|
259
|
+
for (let ci = 0; ci < cols.length; ci++) {
|
|
260
|
+
let sample;
|
|
261
|
+
if (sampleRow !== null) {
|
|
262
|
+
if (Array.isArray(sampleRow)) sample = sampleRow[ci];
|
|
263
|
+
else sample = sampleRow[cols[ci]];
|
|
264
|
+
}
|
|
265
|
+
const t = inferType(cols[ci], sample);
|
|
266
|
+
types.push(t);
|
|
267
|
+
push(columnDefinition({ name: cols[ci], table: result.table || '', type: t, length: 1024 }));
|
|
268
|
+
}
|
|
269
|
+
push(eofPacket());
|
|
270
|
+
|
|
271
|
+
const encodeValue = (v, type) => {
|
|
272
|
+
if (v === null || v === undefined) return null;
|
|
273
|
+
if (type === MYSQL_TYPE_LONG || type === MYSQL_TYPE_LONGLONG || type === MYSQL_TYPE_DOUBLE) {
|
|
274
|
+
const num = Number(v);
|
|
275
|
+
if (type === MYSQL_TYPE_DOUBLE) {
|
|
276
|
+
const b = Buffer.alloc(8); b.writeDoubleLE(num, 0); return b;
|
|
277
|
+
}
|
|
278
|
+
if (Number.isInteger(num) && num <= 2147483647 && num >= -2147483648) {
|
|
279
|
+
const b = Buffer.alloc(4); b.writeInt32LE(num, 0); return b;
|
|
280
|
+
}
|
|
281
|
+
const b = Buffer.alloc(8); b.writeBigInt64LE(BigInt(Math.trunc(num)), 0); return b;
|
|
282
|
+
}
|
|
283
|
+
if (type === MYSQL_TYPE_TINY) {
|
|
284
|
+
const b = Buffer.alloc(1); b.writeInt8(v ? 1 : 0, 0); return b;
|
|
285
|
+
}
|
|
286
|
+
if (type === MYSQL_TYPE_DATE || type === MYSQL_TYPE_DATETIME) {
|
|
287
|
+
const s = String(v);
|
|
288
|
+
const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2}))?/);
|
|
289
|
+
if (!m) return encodeLenencString(String(v));
|
|
290
|
+
const hasTime = !!m[4];
|
|
291
|
+
const buf = hasTime ? Buffer.alloc(8) : Buffer.alloc(5);
|
|
292
|
+
let off = 0;
|
|
293
|
+
buf[off++] = hasTime ? 7 : 4;
|
|
294
|
+
buf.writeUInt16LE(parseInt(m[1], 10), off); off += 2;
|
|
295
|
+
buf[off++] = parseInt(m[2], 10);
|
|
296
|
+
buf[off++] = parseInt(m[3], 10);
|
|
297
|
+
if (hasTime) {
|
|
298
|
+
buf[off++] = parseInt(m[4], 10);
|
|
299
|
+
buf[off++] = parseInt(m[5], 10);
|
|
300
|
+
buf[off++] = parseInt(m[6], 10);
|
|
301
|
+
}
|
|
302
|
+
return buf;
|
|
303
|
+
}
|
|
304
|
+
return encodeLenencString(typeof v === 'object' ? JSON.stringify(v) : v);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
for (const row of result.rows || []) {
|
|
308
|
+
const parts = [Buffer.from([0x00])];
|
|
309
|
+
const nb = Buffer.alloc(Math.ceil(cols.length / 8));
|
|
310
|
+
const encoded = [];
|
|
311
|
+
for (let i = 0; i < cols.length; i++) {
|
|
312
|
+
const v = Array.isArray(row) ? row[i] : row[cols[i]];
|
|
313
|
+
if (v === null || v === undefined) {
|
|
314
|
+
nb[Math.floor(i / 8)] |= (1 << (i % 8));
|
|
315
|
+
encoded.push(null);
|
|
316
|
+
} else {
|
|
317
|
+
encoded.push(encodeValue(v, types[i]));
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
parts.push(nb);
|
|
321
|
+
for (const e of encoded) if (e !== null) parts.push(e);
|
|
322
|
+
push(Buffer.concat(parts));
|
|
323
|
+
}
|
|
324
|
+
push(eofPacket());
|
|
325
|
+
return { packets, sequence };
|
|
326
|
+
}
|
|
327
|
+
|
|
227
328
|
class MysqlConnection {
|
|
228
329
|
constructor(socket, server) {
|
|
229
330
|
this.socket = socket;
|
|
@@ -236,6 +337,8 @@ class MysqlConnection {
|
|
|
236
337
|
this.user = null;
|
|
237
338
|
this.authFails = 0;
|
|
238
339
|
this.multiStatements = false;
|
|
340
|
+
this._stmts = new Map();
|
|
341
|
+
this._stmtSeq = 0;
|
|
239
342
|
if (server.handshakeTimeout > 0) {
|
|
240
343
|
this._authTimer = setTimeout(() => {
|
|
241
344
|
if (!this.authenticated) {
|
|
@@ -335,6 +438,11 @@ class MysqlConnection {
|
|
|
335
438
|
case 0x11: this._handleChangeUser(body, cmdSeq); break; // COM_CHANGE_USER
|
|
336
439
|
case 0x0e: this._send(okPacket()); break; // COM_PING
|
|
337
440
|
case 0x1f: this._send(okPacket()); break; // COM_RESET_CONNECTION
|
|
441
|
+
case 0x16: this._handleStmtPrepare(body); break; // COM_STMT_PREPARE
|
|
442
|
+
case 0x17: this._handleStmtExecute(body); break; // COM_STMT_EXECUTE
|
|
443
|
+
case 0x18: break; // COM_STMT_SEND_LONG_DATA (忽略)
|
|
444
|
+
case 0x19: this._handleStmtClose(body); break; // COM_STMT_CLOSE
|
|
445
|
+
case 0x1a: this._send(okPacket()); break; // COM_STMT_RESET
|
|
338
446
|
case 0x0a: { // COM_PROCESS_INFO
|
|
339
447
|
const seq = this.sequence;
|
|
340
448
|
this.sequence = (this.sequence + 1) & 0xff;
|
|
@@ -474,10 +582,173 @@ class MysqlConnection {
|
|
|
474
582
|
}
|
|
475
583
|
}
|
|
476
584
|
|
|
477
|
-
|
|
585
|
+
countPlaceholders(sql) {
|
|
586
|
+
let n = 0;
|
|
587
|
+
let inStr = null;
|
|
588
|
+
for (let i = 0; i < sql.length; i++) {
|
|
589
|
+
const c = sql[i];
|
|
590
|
+
if (inStr) {
|
|
591
|
+
if (c === '\\' && i + 1 < sql.length) { i++; continue; }
|
|
592
|
+
if (c === inStr) inStr = null;
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
if (c === "'" || c === '"' || c === '`') { inStr = c; continue; }
|
|
596
|
+
if (c === '?' && sql[i + 1] === '?') { i++; continue; }
|
|
597
|
+
if (c === '?') n++;
|
|
598
|
+
}
|
|
599
|
+
return n;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
_handleStmtPrepare(body) {
|
|
603
|
+
try {
|
|
604
|
+
const sql = body.toString('utf8');
|
|
605
|
+
const numParams = this.countPlaceholders(sql);
|
|
606
|
+
let numColumns = 0;
|
|
607
|
+
try {
|
|
608
|
+
const stmt = parseSQL(sql);
|
|
609
|
+
if (stmt && stmt.type === 'select' && stmt.columns) numColumns = stmt.columns.length;
|
|
610
|
+
} catch (e) { /* 无法解析的 SQL 仍可 prepare */ }
|
|
611
|
+
const stmtId = ++this._stmtSeq;
|
|
612
|
+
this._stmts.set(stmtId, { sql, numParams, numColumns });
|
|
613
|
+
|
|
614
|
+
const pkt = Buffer.alloc(12);
|
|
615
|
+
let off = 0;
|
|
616
|
+
pkt[off++] = 0x00;
|
|
617
|
+
pkt.writeUInt32LE(stmtId, off); off += 4;
|
|
618
|
+
pkt.writeUInt16LE(numColumns, off); off += 2;
|
|
619
|
+
pkt.writeUInt16LE(numParams, off); off += 2;
|
|
620
|
+
pkt[off++] = 0x00;
|
|
621
|
+
pkt.writeUInt16LE(0, off);
|
|
622
|
+
this._send(pkt);
|
|
623
|
+
|
|
624
|
+
if (numParams > 0) {
|
|
625
|
+
for (let i = 0; i < numParams; i++) {
|
|
626
|
+
this._send(columnDefinition({ name: '?', type: MYSQL_TYPE_VAR_STRING }));
|
|
627
|
+
}
|
|
628
|
+
this._send(eofPacket());
|
|
629
|
+
}
|
|
630
|
+
if (numColumns > 0) {
|
|
631
|
+
for (let i = 0; i < numColumns; i++) {
|
|
632
|
+
this._send(columnDefinition({ name: 'col' + (i + 1), type: MYSQL_TYPE_VAR_STRING }));
|
|
633
|
+
}
|
|
634
|
+
this._send(eofPacket());
|
|
635
|
+
}
|
|
636
|
+
} catch (e) {
|
|
637
|
+
this._send(errPacket(toMysqlErrno(e), e.message));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
_handleStmtClose(body) {
|
|
642
|
+
if (body.length >= 4) {
|
|
643
|
+
const stmtId = body.readUInt32LE(0);
|
|
644
|
+
this._stmts.delete(stmtId);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
_handleStmtExecute(body) {
|
|
649
|
+
try {
|
|
650
|
+
if (body.length < 9) throw new Error('malformed COM_STMT_EXECUTE');
|
|
651
|
+
const stmtId = body.readUInt32LE(0);
|
|
652
|
+
const stmt = this._stmts.get(stmtId);
|
|
653
|
+
if (!stmt) {
|
|
654
|
+
this._send(errPacket(1243, 'Unknown prepared statement handler (' + stmtId + ') given to mysqld_stmt_execute'));
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
const flags = body[4];
|
|
658
|
+
const iteration = body.readUInt32LE(5);
|
|
659
|
+
let values = null;
|
|
660
|
+
if (stmt.numParams > 0) {
|
|
661
|
+
let off = 9;
|
|
662
|
+
const numParams = stmt.numParams;
|
|
663
|
+
const nullBitmapLen = Math.ceil(numParams / 8);
|
|
664
|
+
if (body.length < off + nullBitmapLen) throw new Error('malformed COM_STMT_EXECUTE params');
|
|
665
|
+
const nullBitmap = body.slice(off, off + nullBitmapLen);
|
|
666
|
+
off += nullBitmapLen;
|
|
667
|
+
if (off >= body.length) throw new Error('malformed COM_STMT_EXECUTE params');
|
|
668
|
+
const newParamsBound = body[off];
|
|
669
|
+
off += 1;
|
|
670
|
+
let types = stmt.types;
|
|
671
|
+
if (newParamsBound & 0x01) {
|
|
672
|
+
if (body.length < off + numParams * 2) throw new Error('malformed COM_STMT_EXECUTE types');
|
|
673
|
+
types = [];
|
|
674
|
+
for (let i = 0; i < numParams; i++) types.push(body.readUInt16LE(off + i * 2));
|
|
675
|
+
stmt.types = types;
|
|
676
|
+
off += numParams * 2;
|
|
677
|
+
}
|
|
678
|
+
if (!types) throw new Error('Parameter types unknown for prepared statement');
|
|
679
|
+
values = [];
|
|
680
|
+
for (let i = 0; i < numParams; i++) {
|
|
681
|
+
const isNull = nullBitmap[Math.floor(i / 8)] & (1 << (i % 8));
|
|
682
|
+
if (isNull) { values.push(null); continue; }
|
|
683
|
+
const type = types[i] & 0xff;
|
|
684
|
+
const parsed = this._readParamValue(body, off, type);
|
|
685
|
+
values.push(parsed.value);
|
|
686
|
+
off = parsed.off;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
this._handleQuery(stmt.sql, values !== null && values.length > 0 ? values : undefined);
|
|
690
|
+
} catch (e) {
|
|
691
|
+
this._send(errPacket(toMysqlErrno(e), e.message));
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
_readParamValue(buf, off, type) {
|
|
696
|
+
switch (type) {
|
|
697
|
+
case MYSQL_TYPE_NULL: return { value: null, off };
|
|
698
|
+
case 0x01: { const v = buf.readInt8(off); return { value: v, off: off + 1 }; } // TINY
|
|
699
|
+
case 0x02: { const v = buf.readInt16LE(off); return { value: v, off: off + 2 }; } // SHORT
|
|
700
|
+
case 0x03: { const v = buf.readInt32LE(off); return { value: v, off: off + 4 }; } // LONG
|
|
701
|
+
case 0x08: { const v = buf.readBigInt64LE(off); return { value: Number(v), off: off + 8 }; } // LONGLONG
|
|
702
|
+
case 0x04: { const v = buf.readFloatLE(off); return { value: v, off: off + 4 }; } // FLOAT
|
|
703
|
+
case MYSQL_TYPE_DOUBLE: { const v = buf.readDoubleLE(off); return { value: v, off: off + 8 }; }
|
|
704
|
+
case 0x0a: case 0x07: case 0x0b: case MYSQL_TYPE_DATETIME: { // DATE/TIMESTAMP/DATETIME/TIME
|
|
705
|
+
const len = buf[off];
|
|
706
|
+
off += 1;
|
|
707
|
+
if (len === 0) return { value: null, off };
|
|
708
|
+
let value;
|
|
709
|
+
if (type === 0x0b) { // TIME
|
|
710
|
+
let sign = 1;
|
|
711
|
+
let p = off;
|
|
712
|
+
if (buf[p] !== 0) sign = -1;
|
|
713
|
+
p += 1;
|
|
714
|
+
const days = buf.readUInt32LE(p); p += 4;
|
|
715
|
+
const hour = buf[p++];
|
|
716
|
+
const min = buf[p++];
|
|
717
|
+
const sec = buf[p++];
|
|
718
|
+
value = sign * (days * 24 + hour) + ':' + String(min).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
|
|
719
|
+
} else {
|
|
720
|
+
let p = off;
|
|
721
|
+
const year = buf.readUInt16LE(p); p += 2;
|
|
722
|
+
const month = buf[p++];
|
|
723
|
+
const day = buf[p++];
|
|
724
|
+
let hour = 0, minute = 0, second = 0;
|
|
725
|
+
if (len >= 7) { hour = buf[p++]; minute = buf[p++]; second = buf[p++]; }
|
|
726
|
+
value = String(year).padStart(4, '0') + '-' + String(month).padStart(2, '0') + '-' + String(day).padStart(2, '0');
|
|
727
|
+
if (len >= 7) value += ' ' + String(hour).padStart(2, '0') + ':' + String(minute).padStart(2, '0') + ':' + String(second).padStart(2, '0');
|
|
728
|
+
}
|
|
729
|
+
off += len;
|
|
730
|
+
return { value, off };
|
|
731
|
+
}
|
|
732
|
+
case 0xfc: case 0xfd: case 0xfe: case 0xf9: case 0xf6: { // BLOB / VAR_STRING / STRING / ...
|
|
733
|
+
const { value, off: noff } = readLenenc(buf, off);
|
|
734
|
+
return { value, off: noff };
|
|
735
|
+
}
|
|
736
|
+
case 0xf0: { // NULL
|
|
737
|
+
return { value: null, off };
|
|
738
|
+
}
|
|
739
|
+
default: {
|
|
740
|
+
const { value, off: noff } = readLenenc(buf, off);
|
|
741
|
+
return { value, off: noff };
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
_handleQuery(sql, values) {
|
|
478
747
|
(async () => {
|
|
479
748
|
try {
|
|
480
|
-
const
|
|
749
|
+
const hasParams = values !== undefined && values !== null && values.length > 0;
|
|
750
|
+
const processedSql = hasParams ? applyParams(sql, values) : sql;
|
|
751
|
+
const statements = splitStatements(processedSql);
|
|
481
752
|
if (statements.length > 1 && !this.multiStatements) {
|
|
482
753
|
throw new Error(`too many statements (${statements.length} > 1)`);
|
|
483
754
|
}
|
|
@@ -534,14 +805,18 @@ class MysqlConnection {
|
|
|
534
805
|
safety: this.server.safety,
|
|
535
806
|
maxStatements: 1,
|
|
536
807
|
});
|
|
537
|
-
if (r.type === 'select' || r.type === 'showTables' || r.type === 'showDatabases' || r.type === 'describe'
|
|
808
|
+
if (r.type === 'select' || r.type === 'showTables' || r.type === 'showDatabases' || r.type === 'describe'
|
|
809
|
+
|| r.type === 'showColumns' || r.type === 'showIndex' || r.type === 'showCreateTable'
|
|
810
|
+
|| r.type === 'showVariables' || r.type === 'showStatus' || r.type === 'showGrants' || r.type === 'showWarnings') {
|
|
538
811
|
let schema = null;
|
|
539
812
|
if (r.table) {
|
|
540
813
|
schema = engine.getTableSchema
|
|
541
814
|
? await engine.getTableSchema(r.table)
|
|
542
815
|
: (engine._schemas ? engine._schemas[r.table] : null);
|
|
543
816
|
}
|
|
544
|
-
const { packets, sequence } =
|
|
817
|
+
const { packets, sequence } = hasParams
|
|
818
|
+
? binaryResultSetPacket(r, schema, this.sequence)
|
|
819
|
+
: resultSetPacket(r, schema, this.sequence);
|
|
545
820
|
this.sequence = sequence;
|
|
546
821
|
this.socket.write(Buffer.concat(packets));
|
|
547
822
|
} else {
|
|
@@ -630,6 +905,10 @@ class MysqlServer {
|
|
|
630
905
|
}
|
|
631
906
|
};
|
|
632
907
|
collect(stmt);
|
|
908
|
+
const isInfoSchema = tables.some(t => t && String(t).toLowerCase().startsWith('information_schema.'));
|
|
909
|
+
if (isInfoSchema) {
|
|
910
|
+
return { engine: await this._getEngine(currentDb), sql };
|
|
911
|
+
}
|
|
633
912
|
let db = null;
|
|
634
913
|
for (const t of tables) {
|
|
635
914
|
if (t && t.indexOf('.') !== -1) {
|