jsql-neo 5.2.1 → 5.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 +7615 -210
- package/bin/jsql +13 -0
- package/index.js +16 -0
- package/lib/mongo_server.js +135 -10
- package/lib/redis_server.js +105 -0
- package/lib/tui.js +503 -0
- package/package.json +1 -1
package/bin/jsql
CHANGED
|
@@ -215,6 +215,19 @@ const cli = yaggs({ pkg: require('../package.json') })
|
|
|
215
215
|
setInterval(() => {}, 1 << 30);
|
|
216
216
|
} catch (e) { console.error(`Error: ${e.message}`); process.exitCode = 1; }
|
|
217
217
|
})
|
|
218
|
+
.command('tui', 'Interactive SQL terminal (zero-dependency TUI)', (sub) => {
|
|
219
|
+
sub.option('data-dir', { alias: ['d'], type: 'string', description: 'Data directory (default: in-memory)' });
|
|
220
|
+
sub.option('db', { type: 'string', description: 'Database name (default: default)' });
|
|
221
|
+
sub.option('dialect', { alias: ['t'], type: 'string', description: 'SQL dialect: mysql|pg (default: mysql)' });
|
|
222
|
+
}, (argv) => {
|
|
223
|
+
const { createTUI } = require('../lib/tui');
|
|
224
|
+
const tui = createTUI({
|
|
225
|
+
dataDir: argv['data-dir'],
|
|
226
|
+
db: argv.db || 'default',
|
|
227
|
+
dialect: argv.dialect || 'mysql',
|
|
228
|
+
});
|
|
229
|
+
tui.run().catch((e) => { console.error(`Error: ${e.message}`); process.exit(1); });
|
|
230
|
+
})
|
|
218
231
|
.command('redis', 'Run the Redis-compatible server', (sub) => {
|
|
219
232
|
sub.option('port', { alias: ['p'], type: 'number', description: 'Listen port (default 6379)' });
|
|
220
233
|
sub.option('host', { type: 'string', description: 'Listen host (default 127.0.0.1)' });
|
package/index.js
CHANGED
|
@@ -22,6 +22,10 @@ const { createMysqlServer, MysqlServer } = require('./lib/mysql_server');
|
|
|
22
22
|
const migrate = require('./lib/migrate');
|
|
23
23
|
const { WebUI } = require('./lib/web_ui');
|
|
24
24
|
const { RedisServer, createRedisServer } = require('./lib/redis_server');
|
|
25
|
+
const { PgServer, createPgServer } = require('./lib/pg_server');
|
|
26
|
+
const { MongoServer, createMongoServer } = require('./lib/mongo_server');
|
|
27
|
+
const { MultiServer, createMultiServer } = require('./lib/multiserver');
|
|
28
|
+
const { TUIShell, createTUI } = require('./lib/tui');
|
|
25
29
|
|
|
26
30
|
/**
|
|
27
31
|
* 全局注入:把项目内 `require('mysql2')` 全部替换为 jsql-neo 内存引擎兼容层。
|
|
@@ -99,4 +103,16 @@ module.exports = {
|
|
|
99
103
|
// Redis 兼容服务器
|
|
100
104
|
RedisServer,
|
|
101
105
|
createRedisServer,
|
|
106
|
+
// PostgreSQL wire protocol 服务器
|
|
107
|
+
PgServer,
|
|
108
|
+
createPgServer,
|
|
109
|
+
// MongoDB wire protocol 服务器
|
|
110
|
+
MongoServer,
|
|
111
|
+
createMongoServer,
|
|
112
|
+
// 多协议嗅探服务器(MySQL + PG + Redis + Mongo 同端口)
|
|
113
|
+
MultiServer,
|
|
114
|
+
createMultiServer,
|
|
115
|
+
// 交互式 TUI
|
|
116
|
+
TUIShell,
|
|
117
|
+
createTUI,
|
|
102
118
|
};
|
package/lib/mongo_server.js
CHANGED
|
@@ -322,7 +322,7 @@ class MongoServer {
|
|
|
322
322
|
localTime: new Date(), logicalSessionTimeoutMinutes: 30, connectionId: 1,
|
|
323
323
|
};
|
|
324
324
|
case 'ping': return { ok: 1 };
|
|
325
|
-
case 'buildInfo': return { ok: 1, version: '5.
|
|
325
|
+
case 'buildInfo': return { ok: 1, version: '5.3.0-jsql-neo', gitVersion: 'jsql-neo', versionArray: [5, 3, 0, 0] };
|
|
326
326
|
case 'getParameter': return { ok: 1 };
|
|
327
327
|
case 'endSessions': return { ok: 1 };
|
|
328
328
|
case 'listDatabases':
|
|
@@ -357,13 +357,53 @@ class MongoServer {
|
|
|
357
357
|
}
|
|
358
358
|
return { ok: 1, n: docs.length };
|
|
359
359
|
}
|
|
360
|
+
case 'findAndModify': {
|
|
361
|
+
const coll = String(doc.findAndModify);
|
|
362
|
+
const engine = await this._ensureCollection(coll);
|
|
363
|
+
const q = doc.query || {};
|
|
364
|
+
const rows = this._matching(engine, coll, q);
|
|
365
|
+
const found = rows[0] || null;
|
|
366
|
+
const set = doc.update && doc.update.$set ? doc.update.$set : (doc.update || {});
|
|
367
|
+
let value = null;
|
|
368
|
+
if (found) {
|
|
369
|
+
if (doc.remove) {
|
|
370
|
+
this._removeRows(engine, coll, [found]);
|
|
371
|
+
value = found;
|
|
372
|
+
} else {
|
|
373
|
+
const patched = { ...found, ...set };
|
|
374
|
+
this._patchRows(engine, coll, [found], set);
|
|
375
|
+
value = doc.new ? patched : found;
|
|
376
|
+
}
|
|
377
|
+
} else if (doc.upsert) {
|
|
378
|
+
const merged = { ...q, ...set };
|
|
379
|
+
const id = engine.insert(coll, merged);
|
|
380
|
+
value = doc.new ? { ...merged, _id: id } : null;
|
|
381
|
+
}
|
|
382
|
+
return { ok: 1, value, lastErrorObject: { n: found ? 1 : (doc.upsert ? 1 : 0), updatedExisting: !!found && !doc.remove } };
|
|
383
|
+
}
|
|
384
|
+
case 'distinct': {
|
|
385
|
+
const coll = String(doc.distinct);
|
|
386
|
+
const engine = await this._ensureCollection(coll);
|
|
387
|
+
const key = String(doc.key || '');
|
|
388
|
+
const rows = this._matching(engine, coll, doc.query);
|
|
389
|
+
const seen = new Set();
|
|
390
|
+
const values = [];
|
|
391
|
+
for (const r of rows) {
|
|
392
|
+
const v = r[key];
|
|
393
|
+
if (!seen.has(JSON.stringify(v))) { seen.add(JSON.stringify(v)); values.push(v); }
|
|
394
|
+
}
|
|
395
|
+
return { ok: 1, values };
|
|
396
|
+
}
|
|
397
|
+
case 'dropDatabase':
|
|
398
|
+
return { ok: 1, dropped: String(doc.dropDatabase || this._dbOf(doc)) };
|
|
360
399
|
case 'find': {
|
|
361
400
|
const coll = String(doc.find);
|
|
362
401
|
const engine = await this._ensureCollection(coll);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
402
|
+
let rows = this._matching(engine, coll, doc.filter);
|
|
403
|
+
const skip = typeof doc.skip === 'number' ? doc.skip : 0;
|
|
404
|
+
if (skip > 0) rows = rows.slice(skip);
|
|
405
|
+
const limit = typeof doc.limit === 'number' ? doc.limit : 0;
|
|
406
|
+
if (limit > 0) rows = rows.slice(0, limit);
|
|
367
407
|
return { ok: 1, cursor: { id: { $long: 0 }, ns: `${this._dbOf(doc)}.${coll}`, firstBatch: rows } };
|
|
368
408
|
}
|
|
369
409
|
case 'getMore':
|
|
@@ -377,7 +417,8 @@ class MongoServer {
|
|
|
377
417
|
const q = u.q || {};
|
|
378
418
|
const set = u.u && u.u.$set ? u.u.$set : (u.u || {});
|
|
379
419
|
// 单文档更新
|
|
380
|
-
let
|
|
420
|
+
let targets = this._matching(engine, coll, q);
|
|
421
|
+
let target = targets[0] || null;
|
|
381
422
|
if (!target && u.upsert) {
|
|
382
423
|
const merged = { ...q, ...set };
|
|
383
424
|
engine.insert(coll, merged);
|
|
@@ -385,7 +426,7 @@ class MongoServer {
|
|
|
385
426
|
continue;
|
|
386
427
|
}
|
|
387
428
|
if (target) {
|
|
388
|
-
|
|
429
|
+
this._patchRows(engine, coll, [target], set);
|
|
389
430
|
n++;
|
|
390
431
|
}
|
|
391
432
|
}
|
|
@@ -396,15 +437,16 @@ class MongoServer {
|
|
|
396
437
|
const engine = await this._ensureCollection(coll);
|
|
397
438
|
let n = 0;
|
|
398
439
|
for (const d of (doc.deletes || [])) {
|
|
399
|
-
const
|
|
400
|
-
|
|
440
|
+
const rows = this._matching(engine, coll, d.q || {});
|
|
441
|
+
if (rows.length > 0) this._removeRows(engine, coll, rows);
|
|
442
|
+
n += rows.length;
|
|
401
443
|
}
|
|
402
444
|
return { ok: 1, n };
|
|
403
445
|
}
|
|
404
446
|
case 'count': {
|
|
405
447
|
const coll = String(doc.count);
|
|
406
448
|
const engine = await this._ensureCollection(coll);
|
|
407
|
-
const rows =
|
|
449
|
+
const rows = this._matching(engine, coll, doc.query);
|
|
408
450
|
return { ok: 1, n: Number(doc.limit) > 0 ? Math.min(rows.length, doc.limit) : rows.length };
|
|
409
451
|
}
|
|
410
452
|
case 'aggregate': {
|
|
@@ -416,6 +458,46 @@ class MongoServer {
|
|
|
416
458
|
if (stage.$match) rows = rows.filter((r) => this._match(r, stage.$match));
|
|
417
459
|
else if (stage.$count) rows = [{ [stage.$count]: rows.length }];
|
|
418
460
|
else if (stage.$limit) rows = rows.slice(0, stage.$limit);
|
|
461
|
+
else if (stage.$skip) rows = rows.slice(Number(stage.$skip) || 0);
|
|
462
|
+
else if (stage.$sort) {
|
|
463
|
+
const sortKeys = Object.entries(stage.$sort);
|
|
464
|
+
rows = [...rows].sort((a, b) => {
|
|
465
|
+
for (const [k, dir] of sortKeys) {
|
|
466
|
+
const av = a[k]; const bv = b[k];
|
|
467
|
+
if (av === bv) continue;
|
|
468
|
+
if (av == null) return 1;
|
|
469
|
+
if (bv == null) return -1;
|
|
470
|
+
const cmp = av < bv ? -1 : 1;
|
|
471
|
+
return Number(dir) < 0 ? -cmp : cmp;
|
|
472
|
+
}
|
|
473
|
+
return 0;
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
else if (stage.$project) {
|
|
477
|
+
const proj = stage.$project;
|
|
478
|
+
rows = rows.map((r) => {
|
|
479
|
+
const out = {};
|
|
480
|
+
for (const [k, v] of Object.entries(proj)) {
|
|
481
|
+
if (k === '_id' && v === 0) continue;
|
|
482
|
+
if (v === 0) continue;
|
|
483
|
+
if (typeof v === 'string' && v.startsWith('$')) out[k] = r[v.slice(1)];
|
|
484
|
+
else if (v === 1) out[k] = r[k];
|
|
485
|
+
else if (v === 0) { /* exclude */ }
|
|
486
|
+
else out[k] = v;
|
|
487
|
+
}
|
|
488
|
+
return out;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
else if (stage.$unwind) {
|
|
492
|
+
const field = String(stage.$unwind).replace(/^\$/, '');
|
|
493
|
+
const out = [];
|
|
494
|
+
for (const r of rows) {
|
|
495
|
+
const arr = r[field];
|
|
496
|
+
if (!Array.isArray(arr) || arr.length === 0) { out.push({ ...r, [field]: null }); continue; }
|
|
497
|
+
for (const item of arr) out.push({ ...r, [field]: item });
|
|
498
|
+
}
|
|
499
|
+
rows = out;
|
|
500
|
+
}
|
|
419
501
|
else if (stage.$group) {
|
|
420
502
|
const acc = {};
|
|
421
503
|
for (const [k, v] of Object.entries(stage.$group)) {
|
|
@@ -435,12 +517,45 @@ class MongoServer {
|
|
|
435
517
|
return row._id != null ? row._id : row.id;
|
|
436
518
|
}
|
|
437
519
|
|
|
520
|
+
_matching(engine, coll, filter) {
|
|
521
|
+
return engine.find(coll, {}).filter((r) => this._match(r, filter || {}));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
_patchRows(engine, coll, rows, set) {
|
|
525
|
+
const table = engine._ensureTable(coll);
|
|
526
|
+
for (const r of rows) Object.assign(r, set);
|
|
527
|
+
table._rebuildPKIndex && table._rebuildPKIndex();
|
|
528
|
+
table._rebuildAllBTrees && table._rebuildAllBTrees();
|
|
529
|
+
engine._markDirty && engine._markDirty(coll);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
_removeRows(engine, coll, rows) {
|
|
533
|
+
const table = engine._ensureTable(coll);
|
|
534
|
+
const gone = new Set(rows);
|
|
535
|
+
table._rows = table._rows.filter((r) => !gone.has(r));
|
|
536
|
+
table._rebuildPKIndex && table._rebuildPKIndex();
|
|
537
|
+
table._rebuildAllBTrees && table._rebuildAllBTrees();
|
|
538
|
+
engine._markDirty && engine._markDirty(coll);
|
|
539
|
+
}
|
|
540
|
+
|
|
438
541
|
_match(row, filter) {
|
|
439
542
|
for (const [k, cond] of Object.entries(filter || {})) {
|
|
440
543
|
if (k === '$or') {
|
|
441
544
|
if (!cond.some((f) => this._match(row, f))) return false;
|
|
442
545
|
continue;
|
|
443
546
|
}
|
|
547
|
+
if (k === '$and') {
|
|
548
|
+
if (!cond.every((f) => this._match(row, f))) return false;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
if (k === '$nor') {
|
|
552
|
+
if (cond.some((f) => this._match(row, f))) return false;
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (k === '$not') {
|
|
556
|
+
if (this._match(row, cond)) return false;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
444
559
|
if (cond && typeof cond === 'object' && !Array.isArray(cond)) {
|
|
445
560
|
for (const [op, v] of Object.entries(cond)) {
|
|
446
561
|
const rv = row[k];
|
|
@@ -452,6 +567,16 @@ class MongoServer {
|
|
|
452
567
|
if (op === '$in' && !(v.includes(rv))) return false;
|
|
453
568
|
if (op === '$nin' && v.includes(rv)) return false;
|
|
454
569
|
if (op === '$exists' && (v ? (rv === undefined) : (rv !== undefined))) return false;
|
|
570
|
+
if (op === '$regex') {
|
|
571
|
+
const flags = String(cond.$options || '').includes('i') ? 'i' : '';
|
|
572
|
+
if (typeof rv !== 'string' || !new RegExp(String(v), flags).test(rv)) return false;
|
|
573
|
+
}
|
|
574
|
+
if (op === '$type') {
|
|
575
|
+
const t = v === 'string' ? 'string' : v === 'int' || v === 'long' || v === 'double' || v === 'number' ? 'number' : v === 'bool' ? 'boolean' : v === 'null' ? 'null' : v === 'array' ? 'array' : typeof rv;
|
|
576
|
+
if (typeof rv !== t) return false;
|
|
577
|
+
}
|
|
578
|
+
if (op === '$size' && !(Array.isArray(rv) && rv.length === v)) return false;
|
|
579
|
+
if (op === '$elemMatch' && !(Array.isArray(rv) && rv.some((el) => this._match(el, v)))) return false;
|
|
455
580
|
}
|
|
456
581
|
} else if (row[k] !== cond) {
|
|
457
582
|
return false;
|
package/lib/redis_server.js
CHANGED
|
@@ -24,9 +24,11 @@ const TYPE_SIGNATURES = {
|
|
|
24
24
|
PING: 0, ECHO: 1, SET: 2, SETNX: 2, GET: 1, DEL: -1, EXISTS: -1, KEYS: 1,
|
|
25
25
|
TYPE: 1, EXPIRE: 2, TTL: 1, PERSIST: 1,
|
|
26
26
|
INCR: 1, DECR: 1, INCRBY: 2, DECRBY: 2, APPEND: 2, STRLEN: 1,
|
|
27
|
+
MSET: -2, MGET: -1,
|
|
27
28
|
HSET: -3, HGET: 2, HGETALL: 1, HDEL: -2, HEXISTS: 2, HLEN: 1, HKEYS: 1, HVALS: 1,
|
|
28
29
|
LPUSH: -2, RPUSH: -2, LPOP: 1, RPOP: 1, LLEN: 1, LRANGE: 3, LINDEX: 2, LREM: 3,
|
|
29
30
|
SADD: -2, SREM: -2, SMEMBERS: 1, SISMEMBER: 2, SCARD: 1,
|
|
31
|
+
ZADD: -3, ZRANGE: -3, ZREVRANGE: -3, ZSCORE: 2, ZCARD: 1, ZREM: -2, ZINCRBY: 3,
|
|
30
32
|
DBSIZE: 0, FLUSHALL: 0, FLUSHDB: 0, SELECT: 1, INFO: 0, AUTH: 1, QUIT: 0,
|
|
31
33
|
};
|
|
32
34
|
|
|
@@ -333,6 +335,109 @@ class RedisServer {
|
|
|
333
335
|
const v = this._get(args[0]);
|
|
334
336
|
return v && v.type === 'set' ? v.val.length : 0;
|
|
335
337
|
}
|
|
338
|
+
case 'ZADD': {
|
|
339
|
+
const key = args[0];
|
|
340
|
+
const v = this._get(key);
|
|
341
|
+
let zset;
|
|
342
|
+
if (v && v.type === 'zset') zset = v.val;
|
|
343
|
+
else if (v) throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
344
|
+
else { this.db.set(key, { type: 'zset', val: {} }); zset = this.db.get(key).val; }
|
|
345
|
+
const rest = args.slice(1);
|
|
346
|
+
if (rest.length % 2 !== 0) throw new Error('ERR syntax error');
|
|
347
|
+
let added = 0;
|
|
348
|
+
for (let i = 0; i < rest.length; i += 2) {
|
|
349
|
+
const score = parseFloat(rest[i]);
|
|
350
|
+
const member = rest[i + 1];
|
|
351
|
+
if (isNaN(score)) throw new Error('ERR value is not a valid float');
|
|
352
|
+
if (!(member in zset)) added++;
|
|
353
|
+
zset[member] = score;
|
|
354
|
+
}
|
|
355
|
+
this._scheduleSnapshot();
|
|
356
|
+
return added;
|
|
357
|
+
}
|
|
358
|
+
case 'ZRANGE': {
|
|
359
|
+
const v = this._get(args[0]);
|
|
360
|
+
if (!v) return [];
|
|
361
|
+
if (v.type !== 'zset') throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
362
|
+
const start = parseInt(args[1], 10);
|
|
363
|
+
const stop = parseInt(args[2], 10);
|
|
364
|
+
const withScores = args[3] && args[3].toUpperCase() === 'WITHSCORES';
|
|
365
|
+
const entries = Object.entries(v.val).sort((a, b) => a[1] - b[1] || (a[0] < b[0] ? -1 : 1));
|
|
366
|
+
const from = start < 0 ? Math.max(0, entries.length + start) : start;
|
|
367
|
+
const to = stop < 0 ? entries.length + stop : stop;
|
|
368
|
+
const out = [];
|
|
369
|
+
for (let i = from; i <= to && i < entries.length; i++) {
|
|
370
|
+
out.push(entries[i][0]);
|
|
371
|
+
if (withScores) out.push(entries[i][1]);
|
|
372
|
+
}
|
|
373
|
+
return out;
|
|
374
|
+
}
|
|
375
|
+
case 'ZREVRANGE': {
|
|
376
|
+
const v = this._get(args[0]);
|
|
377
|
+
if (!v) return [];
|
|
378
|
+
if (v.type !== 'zset') throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
379
|
+
const start = parseInt(args[1], 10);
|
|
380
|
+
const stop = parseInt(args[2], 10);
|
|
381
|
+
const withScores = args[3] && args[3].toUpperCase() === 'WITHSCORES';
|
|
382
|
+
const entries = Object.entries(v.val).sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? 1 : -1));
|
|
383
|
+
const from = start < 0 ? Math.max(0, entries.length + start) : start;
|
|
384
|
+
const to = stop < 0 ? entries.length + stop : stop;
|
|
385
|
+
const out = [];
|
|
386
|
+
for (let i = from; i <= to && i < entries.length; i++) {
|
|
387
|
+
out.push(entries[i][0]);
|
|
388
|
+
if (withScores) out.push(entries[i][1]);
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
case 'ZSCORE': {
|
|
393
|
+
const v = this._get(args[0]);
|
|
394
|
+
if (!v || v.type !== 'zset' || !(args[1] in v.val)) return null;
|
|
395
|
+
return v.val[args[1]];
|
|
396
|
+
}
|
|
397
|
+
case 'ZCARD': {
|
|
398
|
+
const v = this._get(args[0]);
|
|
399
|
+
if (!v) return 0;
|
|
400
|
+
if (v.type !== 'zset') throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
401
|
+
return Object.keys(v.val).length;
|
|
402
|
+
}
|
|
403
|
+
case 'ZREM': {
|
|
404
|
+
const v = this._get(args[0]);
|
|
405
|
+
if (!v) return 0;
|
|
406
|
+
if (v.type !== 'zset') throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
407
|
+
let n = 0;
|
|
408
|
+
for (const m of args.slice(1)) {
|
|
409
|
+
if (m in v.val) { delete v.val[m]; n++; }
|
|
410
|
+
}
|
|
411
|
+
this._scheduleSnapshot();
|
|
412
|
+
return n;
|
|
413
|
+
}
|
|
414
|
+
case 'ZINCRBY': {
|
|
415
|
+
const key = args[0];
|
|
416
|
+
const inc = parseFloat(args[1]);
|
|
417
|
+
if (isNaN(inc)) throw new Error('ERR value is not a valid float');
|
|
418
|
+
const v = this._get(key);
|
|
419
|
+
let zset;
|
|
420
|
+
if (v && v.type === 'zset') zset = v.val;
|
|
421
|
+
else if (v) throw new Error('WRONGTYPE Operation against a key holding the wrong kind of value');
|
|
422
|
+
else { this.db.set(key, { type: 'zset', val: {} }); zset = this.db.get(key).val; }
|
|
423
|
+
const member = args[2];
|
|
424
|
+
zset[member] = (zset[member] || 0) + inc;
|
|
425
|
+
this._scheduleSnapshot();
|
|
426
|
+
return zset[member];
|
|
427
|
+
}
|
|
428
|
+
case 'MSET': {
|
|
429
|
+
const rest = args;
|
|
430
|
+
if (rest.length % 2 !== 0) throw new Error('ERR wrong number of arguments');
|
|
431
|
+
for (let i = 0; i < rest.length; i += 2) this.db.set(rest[i], { type: 'string', val: rest[i + 1], ttl: null });
|
|
432
|
+
this._scheduleSnapshot();
|
|
433
|
+
return 'OK';
|
|
434
|
+
}
|
|
435
|
+
case 'MGET': {
|
|
436
|
+
return args.map((k) => {
|
|
437
|
+
const v = this._get(k);
|
|
438
|
+
return v && v.type === 'string' ? v.val : null;
|
|
439
|
+
});
|
|
440
|
+
}
|
|
336
441
|
case 'DBSIZE': return this.db.size;
|
|
337
442
|
case 'FLUSHALL': case 'FLUSHDB':
|
|
338
443
|
this.db.clear();
|