jsql-neo 5.3.1 → 5.4.1
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/LICENSE +0 -0
- package/README.md +240 -33
- package/bin/jsql +0 -0
- package/bin/jsql-server +0 -0
- package/bin/jsql-server-control.js +0 -0
- package/index.d.ts +0 -0
- package/index.js +0 -0
- package/lib/btree.js +0 -0
- package/lib/cache.js +0 -0
- package/lib/client.js +0 -0
- package/lib/database.js +0 -0
- package/lib/date-types.js +0 -0
- package/lib/errors.js +0 -0
- package/lib/jsql_format.js +0 -0
- package/lib/migrate.js +0 -0
- package/lib/mod.js +0 -0
- package/lib/mongo_server.js +1 -1
- package/lib/multiserver.js +0 -0
- package/lib/mysql_compat.js +0 -0
- package/lib/mysql_server.js +0 -0
- package/lib/native_client.js +182 -23
- package/lib/nedb_compat.js +0 -0
- package/lib/pg_server.js +0 -0
- package/lib/plugin.js +0 -0
- package/lib/query.js +0 -0
- package/lib/redis_server.js +0 -0
- package/lib/sql.js +0 -0
- package/lib/sqlite_compat.js +0 -0
- package/lib/sqlite_worker.js +5 -1
- package/lib/table.js +0 -0
- package/lib/tui.js +0 -0
- package/lib/wasm.js +0 -0
- package/lib/wasm_client.js +200 -23
- package/lib/web_ui.js +1 -1
- package/nativesrc/jsql-neo-core/Cargo.lock +0 -0
- package/nativesrc/jsql-neo-core/Cargo.toml +1 -1
- package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +0 -0
- package/nativesrc/jsql-neo-core/src/engine/memory.rs +0 -0
- package/nativesrc/jsql-neo-core/src/engine/mod.rs +0 -0
- package/nativesrc/jsql-neo-core/src/engine/table.rs +0 -0
- package/nativesrc/jsql-neo-core/src/lib.rs +0 -0
- package/nativesrc/jsql-neo-core/src/storage/mod.rs +0 -0
- package/nativesrc/jsql-neo-core/src/storage/persistent.rs +0 -0
- package/nativesrc/jsql-neo-core/src/storage/wal.rs +0 -0
- package/nativesrc/jsql-neo-core/src/types.rs +0 -0
- package/nativesrc/jsql-neo-native/Cargo.lock +0 -0
- package/nativesrc/jsql-neo-native/Cargo.toml +1 -1
- package/nativesrc/jsql-neo-native/build.rs +0 -0
- package/nativesrc/jsql-neo-native/jsql-neo-native.node +0 -0
- package/nativesrc/jsql-neo-native/package.json +0 -0
- package/nativesrc/jsql-neo-native/src/lib.rs +0 -0
- package/nativesrc/jsql-neo-wasm/Cargo.lock +2 -2
- package/nativesrc/jsql-neo-wasm/Cargo.toml +1 -1
- package/nativesrc/jsql-neo-wasm/src/lib.rs +24 -0
- package/package.json +3 -3
- package/postinstall.js +146 -25
- package/test/btree.test.js +0 -0
- package/test/coverage.js +0 -0
- package/test/join.test.js +110 -0
- package/test/native.test.js +159 -0
- package/test/regress-5.1.0.js +0 -0
- package/test/smoke.js +0 -0
- package/test/wasm.test.js +111 -0
- package/wasm/browser.d.ts +81 -80
- package/wasm/browser.mjs +407 -242
- package/wasm/browser_bg.mjs +166 -31
- package/wasm/jsql_neo_wasm.d.ts +6 -0
- package/wasm/jsql_neo_wasm.js +57 -0
- package/wasm/jsql_neo_wasm_bg.wasm +0 -0
- package/wasm/jsql_neo_wasm_bg.wasm.d.ts +3 -0
- package/wasm/package.json +1 -1
- package/bin/jsql-neo-server +0 -0
- package/lib/compress_pool.js +0 -122
- package/lib/compress_worker.js +0 -32
- package/wasm/jsql_neo_wasm_bg.js +0 -373
package/lib/native_client.js
CHANGED
|
@@ -30,14 +30,52 @@ const NATIVE_TYPE_MAP = {
|
|
|
30
30
|
int: 'integer',
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
function parseShorthandSchema(str) {
|
|
34
|
+
const def = { type: str };
|
|
35
|
+
const strip = (re) => {
|
|
36
|
+
const next = def.type.replace(re, '').trim();
|
|
37
|
+
def.type = next || def.type;
|
|
38
|
+
};
|
|
39
|
+
if (/\bprimary\s+key\b/i.test(def.type)) { def.primaryKey = true; strip(/\bprimary\s+key\b/gi); }
|
|
40
|
+
if (/\bauto_?increment\b/i.test(def.type)) { def.autoIncrement = true; strip(/\bauto_?increment\b/gi); }
|
|
41
|
+
if (/\bnot\s+null\b/i.test(def.type)) { def.required = true; strip(/\bnot\s+null\b/gi); }
|
|
42
|
+
if (/\bunique\b/i.test(def.type)) { def.unique = true; strip(/\bunique\b/gi); }
|
|
43
|
+
if (/\bdefault\s+(\S+)/i.test(def.type)) {
|
|
44
|
+
const m = def.type.match(/\bdefault\s+(\S+)/i);
|
|
45
|
+
def.default = m[1].replace(/^['"]|['"]$/g, '');
|
|
46
|
+
strip(/\bdefault\s+\S+/gi);
|
|
47
|
+
}
|
|
48
|
+
return def;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseSchemaString(str) {
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const seg of str.split(',')) {
|
|
54
|
+
const trimmed = seg.trim();
|
|
55
|
+
if (!trimmed) continue;
|
|
56
|
+
const parts = trimmed.split(/\s+/);
|
|
57
|
+
const name = parts.shift();
|
|
58
|
+
const def = parseShorthandSchema(parts.join(' ').toLowerCase().trim());
|
|
59
|
+
if (def.type.length === 0) def.type = 'string';
|
|
60
|
+
out[name] = def;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
|
|
33
65
|
function mapNativeSchema(schema) {
|
|
66
|
+
if (typeof schema === 'string') schema = parseSchemaString(schema);
|
|
34
67
|
const mapped = {};
|
|
35
68
|
for (const [field, def] of Object.entries(schema || {})) {
|
|
36
69
|
if (typeof def === 'string') {
|
|
37
|
-
|
|
70
|
+
const parsed = parseShorthandSchema(def.toLowerCase().trim());
|
|
71
|
+
if (parsed.type.length === 0) parsed.type = 'string';
|
|
72
|
+
mapped[field] = { ...parsed, type: NATIVE_TYPE_MAP[parsed.type] || parsed.type };
|
|
38
73
|
} else if (def && typeof def === 'object') {
|
|
39
74
|
const t = (def.type || 'string').toLowerCase();
|
|
40
|
-
|
|
75
|
+
const out = { ...def, type: NATIVE_TYPE_MAP[t] || t };
|
|
76
|
+
if (out.primary_key !== undefined && out.primaryKey === undefined) { out.primaryKey = out.primary_key; delete out.primary_key; }
|
|
77
|
+
if (out.auto_increment !== undefined && out.autoIncrement === undefined) { out.autoIncrement = out.auto_increment; delete out.auto_increment; }
|
|
78
|
+
mapped[field] = out;
|
|
41
79
|
} else {
|
|
42
80
|
mapped[field] = { type: 'string' };
|
|
43
81
|
}
|
|
@@ -46,25 +84,46 @@ function mapNativeSchema(schema) {
|
|
|
46
84
|
}
|
|
47
85
|
|
|
48
86
|
function restoreRow(row, schema) {
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
87
|
+
if (typeof row !== 'object' || row === null) return row;
|
|
88
|
+
let src = row;
|
|
89
|
+
if (row.fields && typeof row.fields === 'object' && row.fields !== null) {
|
|
90
|
+
src = { ...row.fields };
|
|
91
|
+
if (row.id !== undefined) {
|
|
92
|
+
const pkCols = [];
|
|
93
|
+
if (schema) {
|
|
94
|
+
for (const [k, def] of Object.entries(schema)) {
|
|
95
|
+
if (def && typeof def === 'object' && def.autoIncrement) continue;
|
|
96
|
+
const isPk = typeof def === 'string'
|
|
97
|
+
? /\bprimary\s+key\b/i.test(def)
|
|
98
|
+
: !!(def && (def.primaryKey || def.primary_key));
|
|
99
|
+
if (isPk) pkCols.push(k);
|
|
100
|
+
}
|
|
101
|
+
} else if (src.id === undefined) {
|
|
102
|
+
pkCols.push('id');
|
|
103
|
+
}
|
|
104
|
+
for (const c of pkCols) {
|
|
105
|
+
const v = src[c];
|
|
106
|
+
if (v === undefined || v === null || v === 0 || v === '' || v === false) src[c] = row.id;
|
|
107
|
+
}
|
|
55
108
|
}
|
|
109
|
+
if (row.created_at !== undefined) src.created_at = row.created_at;
|
|
110
|
+
if (row.updated_at !== undefined) src.updated_at = row.updated_at;
|
|
111
|
+
}
|
|
112
|
+
if (!schema) return src;
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const [field, value] of Object.entries(src)) {
|
|
56
115
|
const colDef = schema[field];
|
|
57
116
|
let t = null;
|
|
58
117
|
if (typeof colDef === 'string') t = colDef.toLowerCase();
|
|
59
118
|
else if (colDef && typeof colDef === 'object' && colDef.type) t = String(colDef.type).toLowerCase();
|
|
60
|
-
if (['json', 'array', 'object'].includes(t) && typeof
|
|
61
|
-
try { out[field] = JSON.parse(
|
|
62
|
-
} else if ((t === 'integer' || t === 'int' || t === 'bigint') && typeof
|
|
63
|
-
out[field] = Number(
|
|
64
|
-
} else if ((t === 'float' || t === 'double' || t === 'number') && typeof
|
|
65
|
-
out[field] = Number(
|
|
119
|
+
if (['json', 'array', 'object'].includes(t) && typeof value === 'string') {
|
|
120
|
+
try { out[field] = JSON.parse(value); } catch (e) { out[field] = value; }
|
|
121
|
+
} else if ((t === 'integer' || t === 'int' || t === 'bigint') && typeof value === 'string' && /^-?\d+$/.test(value)) {
|
|
122
|
+
out[field] = Number(value);
|
|
123
|
+
} else if ((t === 'float' || t === 'double' || t === 'number') && typeof value === 'string' && !Number.isNaN(Number(value))) {
|
|
124
|
+
out[field] = Number(value);
|
|
66
125
|
} else {
|
|
67
|
-
out[field] =
|
|
126
|
+
out[field] = value;
|
|
68
127
|
}
|
|
69
128
|
}
|
|
70
129
|
return out;
|
|
@@ -154,6 +213,7 @@ class JSQL {
|
|
|
154
213
|
this._opFlushInterval = opts.opFlushInterval || 50;
|
|
155
214
|
this._tableNames = new Set();
|
|
156
215
|
this._schemas = {};
|
|
216
|
+
this._txId = undefined;
|
|
157
217
|
this._autoPlugins = opts.modules !== false;
|
|
158
218
|
|
|
159
219
|
// 持久化模式: 'memory' | 'hybrid' | 'disk'
|
|
@@ -313,6 +373,62 @@ class JSQL {
|
|
|
313
373
|
}
|
|
314
374
|
}
|
|
315
375
|
|
|
376
|
+
_pkOf(table) {
|
|
377
|
+
const schema = this._schemas[table];
|
|
378
|
+
if (!schema) return null;
|
|
379
|
+
for (const [k, def] of Object.entries(schema)) {
|
|
380
|
+
const isPk = (typeof def === 'string' && /\bprimary\s+key\b/i.test(def))
|
|
381
|
+
|| !!(def && typeof def === 'object' && (def.primaryKey || def.primary_key));
|
|
382
|
+
if (isPk) return k;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
_pkAutoIncrement(table) {
|
|
388
|
+
const schema = this._schemas[table];
|
|
389
|
+
const pk = this._pkOf(table);
|
|
390
|
+
if (!pk || !schema) return false;
|
|
391
|
+
const def = schema[pk];
|
|
392
|
+
if (typeof def === 'string') return /\bauto_?increment\b/i.test(def);
|
|
393
|
+
return !!(def && def.autoIncrement);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
_coercePkId(table, id) {
|
|
397
|
+
const schema = this._schemas[table];
|
|
398
|
+
const pk = this._pkOf(table);
|
|
399
|
+
if (!schema || !pk) return id;
|
|
400
|
+
const def = schema[pk];
|
|
401
|
+
let t = null;
|
|
402
|
+
if (typeof def === 'string') t = def.toLowerCase();
|
|
403
|
+
else if (def && typeof def === 'object' && def.type) t = String(def.type).toLowerCase();
|
|
404
|
+
if (['integer', 'int', 'bigint'].includes(t)) return Number(id);
|
|
405
|
+
return id;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
_findRawById(table, id) {
|
|
409
|
+
const pk = this._pkOf(table);
|
|
410
|
+
if (pk) {
|
|
411
|
+
const filter = {};
|
|
412
|
+
filter[pk] = this._coercePkId(table, id);
|
|
413
|
+
const r = safeParse(native.jsqlFind(table, JSON.stringify(filter), 1, 0));
|
|
414
|
+
if (r && r.error) return r;
|
|
415
|
+
return Array.isArray(r) && r.length > 0 ? r[0] : null;
|
|
416
|
+
}
|
|
417
|
+
return safeParse(native.jsqlFindById(table, Number(id)));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
_seqIdByPk(table, id) {
|
|
421
|
+
const pk = this._pkOf(table);
|
|
422
|
+
if (pk) {
|
|
423
|
+
const filter = {};
|
|
424
|
+
filter[pk] = this._coercePkId(table, id);
|
|
425
|
+
const r = safeParse(native.jsqlFind(table, JSON.stringify(filter), 1, 0));
|
|
426
|
+
if (Array.isArray(r) && r.length > 0) return r[0].id;
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
return Number(id);
|
|
430
|
+
}
|
|
431
|
+
|
|
316
432
|
async _insertBatch(table, rows) {
|
|
317
433
|
const schema = this._schemas[table];
|
|
318
434
|
if (schema) {
|
|
@@ -328,6 +444,15 @@ class JSQL {
|
|
|
328
444
|
const bin = encodeBatch(rows);
|
|
329
445
|
const r = safeParse(native.jsqlInsertBuf(table, bin));
|
|
330
446
|
if (r && r.error) throw new Error(r.error);
|
|
447
|
+
if (r && Array.isArray(r) && !this._pkAutoIncrement(table)) {
|
|
448
|
+
const pk = this._pkOf(table);
|
|
449
|
+
if (pk) {
|
|
450
|
+
return r.map((seq, i) => {
|
|
451
|
+
const row = rows[i];
|
|
452
|
+
return (row && row[pk] !== undefined && row[pk] !== null) ? row[pk] : seq;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
331
456
|
return r;
|
|
332
457
|
}
|
|
333
458
|
|
|
@@ -419,7 +544,7 @@ class JSQL {
|
|
|
419
544
|
const r = safeParse(native.jsqlCreateTable(name, JSON.stringify(mapNativeSchema(schema))));
|
|
420
545
|
if (r && r.ok === false) throw new Error(r.error || 'create table failed');
|
|
421
546
|
this._tableNames.add(name);
|
|
422
|
-
this._schemas[name] = schema;
|
|
547
|
+
this._schemas[name] = typeof schema === 'string' ? mapNativeSchema(schema) : schema;
|
|
423
548
|
this._emit('createTable', { name, schema });
|
|
424
549
|
this._runHooks('afterCreateTable', [name, schema]);
|
|
425
550
|
return r;
|
|
@@ -440,7 +565,7 @@ class JSQL {
|
|
|
440
565
|
findById(table, id) {
|
|
441
566
|
this._flushOpsNow();
|
|
442
567
|
if (!this._runHooks('beforeFind', [table, { id }])) return null;
|
|
443
|
-
const raw =
|
|
568
|
+
const raw = this._findRawById(table, id);
|
|
444
569
|
if (raw && raw.error) throw new Error(raw.error);
|
|
445
570
|
this._runHooks('afterFind', [table, { id }, raw]);
|
|
446
571
|
const schema = this._schemas[table];
|
|
@@ -451,8 +576,12 @@ class JSQL {
|
|
|
451
576
|
findByIds(table, ids) {
|
|
452
577
|
this._flushOpsNow();
|
|
453
578
|
if (!this._runHooks('beforeFind', [table, { ids }])) return null;
|
|
454
|
-
|
|
455
|
-
|
|
579
|
+
let r;
|
|
580
|
+
if (this._pkOf(table)) {
|
|
581
|
+
r = ids.map(id => this._findRawById(table, id)).filter(x => x !== null && !(x && x.error));
|
|
582
|
+
} else {
|
|
583
|
+
r = safeParse(native.jsqlFindByIds(table, JSON.stringify(ids)));
|
|
584
|
+
}
|
|
456
585
|
if (r && r.error) throw new Error(r.error);
|
|
457
586
|
this._runHooks('afterFind', [table, { ids }, r]);
|
|
458
587
|
const schema = this._schemas[table];
|
|
@@ -487,7 +616,11 @@ class JSQL {
|
|
|
487
616
|
}
|
|
488
617
|
|
|
489
618
|
updateByIds(table, entries) {
|
|
490
|
-
const pairs =
|
|
619
|
+
const pairs = [];
|
|
620
|
+
for (const [id, data] of entries) {
|
|
621
|
+
const seq = this._seqIdByPk(table, id);
|
|
622
|
+
if (seq !== null) pairs.push([seq, data]);
|
|
623
|
+
}
|
|
491
624
|
if (!this._runHooks('beforeUpdate', [table, pairs])) return;
|
|
492
625
|
if (!this._opBuffer.update[table]) this._opBuffer.update[table] = [];
|
|
493
626
|
this._opBuffer.update[table].push(...pairs);
|
|
@@ -497,21 +630,28 @@ class JSQL {
|
|
|
497
630
|
removeByIds(table, ids) {
|
|
498
631
|
if (!this._runHooks('beforeDelete', [table, ids])) return;
|
|
499
632
|
if (!this._opBuffer.remove[table]) this._opBuffer.remove[table] = new Set();
|
|
500
|
-
for (const id of ids)
|
|
633
|
+
for (const id of ids) {
|
|
634
|
+
const seq = this._seqIdByPk(table, id);
|
|
635
|
+
if (seq !== null) this._opBuffer.remove[table].add(seq);
|
|
636
|
+
}
|
|
501
637
|
this._scheduleOpFlush();
|
|
502
638
|
}
|
|
503
639
|
|
|
504
640
|
updateById(table, id, data) {
|
|
505
641
|
if (!this._runHooks('beforeUpdate', [table, id, data])) return;
|
|
642
|
+
const seq = this._seqIdByPk(table, id);
|
|
643
|
+
if (seq === null) return;
|
|
506
644
|
if (!this._opBuffer.update[table]) this._opBuffer.update[table] = [];
|
|
507
|
-
this._opBuffer.update[table].push([
|
|
645
|
+
this._opBuffer.update[table].push([seq, data]);
|
|
508
646
|
this._scheduleOpFlush();
|
|
509
647
|
}
|
|
510
648
|
|
|
511
649
|
removeById(table, id) {
|
|
512
650
|
if (!this._runHooks('beforeDelete', [table, id])) return;
|
|
651
|
+
const seq = this._seqIdByPk(table, id);
|
|
652
|
+
if (seq === null) return;
|
|
513
653
|
if (!this._opBuffer.remove[table]) this._opBuffer.remove[table] = new Set();
|
|
514
|
-
this._opBuffer.remove[table].add(
|
|
654
|
+
this._opBuffer.remove[table].add(seq);
|
|
515
655
|
this._scheduleOpFlush();
|
|
516
656
|
}
|
|
517
657
|
|
|
@@ -543,9 +683,28 @@ class JSQL {
|
|
|
543
683
|
this._flushOpsNow();
|
|
544
684
|
const r = safeParse(native.jsqlBeginTx());
|
|
545
685
|
if (r && r.ok === false) throw new Error(r.error || 'begin transaction failed');
|
|
686
|
+
this._txId = r.txId;
|
|
546
687
|
return r.txId;
|
|
547
688
|
}
|
|
548
689
|
|
|
690
|
+
async beginTransaction() {
|
|
691
|
+
return this.beginTx();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async commit() {
|
|
695
|
+
if (this._txId === undefined) return true;
|
|
696
|
+
const r = await this.commitTx(this._txId);
|
|
697
|
+
this._txId = undefined;
|
|
698
|
+
return r;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async rollback() {
|
|
702
|
+
if (this._txId === undefined) return true;
|
|
703
|
+
const r = await this.rollbackTx(this._txId);
|
|
704
|
+
this._txId = undefined;
|
|
705
|
+
return r;
|
|
706
|
+
}
|
|
707
|
+
|
|
549
708
|
async commitTx(txId) {
|
|
550
709
|
const r = safeParse(native.jsqlCommitTx(String(txId)));
|
|
551
710
|
if (r && r.ok === false) throw new Error(r.error || 'commit transaction failed');
|
package/lib/nedb_compat.js
CHANGED
|
File without changes
|
package/lib/pg_server.js
CHANGED
|
File without changes
|
package/lib/plugin.js
CHANGED
|
File without changes
|
package/lib/query.js
CHANGED
|
File without changes
|
package/lib/redis_server.js
CHANGED
|
File without changes
|
package/lib/sql.js
CHANGED
|
File without changes
|
package/lib/sqlite_compat.js
CHANGED
|
File without changes
|
package/lib/sqlite_worker.js
CHANGED
|
@@ -20,7 +20,7 @@ function startEngine(filename, options) {
|
|
|
20
20
|
const opts = options || {};
|
|
21
21
|
const isMemory = filename === ':memory:' || !filename;
|
|
22
22
|
if (opts.engine === 'native') {
|
|
23
|
-
const { NativeJSQL } = require('./native_client');
|
|
23
|
+
const { JSQL: NativeJSQL } = require('./native_client');
|
|
24
24
|
const engine = new NativeJSQL({ mode: 'memory' });
|
|
25
25
|
db = {
|
|
26
26
|
start: async () => { await engine.start(); },
|
|
@@ -30,10 +30,14 @@ function startEngine(filename, options) {
|
|
|
30
30
|
dropTable: n => engine.dropTable(n),
|
|
31
31
|
insert: (n, rows) => engine.insert(n, rows),
|
|
32
32
|
find: (n, f, o) => engine.find(n, f, o),
|
|
33
|
+
updateById: (n, id, data) => engine.updateById(n, id, data),
|
|
33
34
|
updateByIds: (n, e) => engine.updateByIds(n, e),
|
|
34
35
|
removeByIds: (n, ids) => engine.removeByIds(n, ids),
|
|
35
36
|
count: (n, f) => engine.count ? engine.count(n, f) : engine.find(n, f || {}, {}).then(rs => rs.length),
|
|
36
37
|
getTableSchema: n => engine._schemas[n] || null,
|
|
38
|
+
beginTx: () => engine.beginTx(),
|
|
39
|
+
commitTx: id => engine.commitTx(id),
|
|
40
|
+
rollbackTx: id => engine.rollbackTx(id),
|
|
37
41
|
truncate: async () => { throw new Error('truncate not supported on native engine'); },
|
|
38
42
|
flush: async () => { if (engine.flush) await engine.flush(); },
|
|
39
43
|
listTables: () => Array.from(engine._tableNames || []),
|
package/lib/table.js
CHANGED
|
File without changes
|
package/lib/tui.js
CHANGED
|
File without changes
|
package/lib/wasm.js
CHANGED
|
File without changes
|
package/lib/wasm_client.js
CHANGED
|
@@ -23,14 +23,52 @@ const NATIVE_TYPE_MAP = {
|
|
|
23
23
|
int: 'integer',
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
function parseShorthandSchema(str) {
|
|
27
|
+
const def = { type: str };
|
|
28
|
+
const strip = (re) => {
|
|
29
|
+
const next = def.type.replace(re, '').trim();
|
|
30
|
+
def.type = next || def.type;
|
|
31
|
+
};
|
|
32
|
+
if (/\bprimary\s+key\b/i.test(def.type)) { def.primaryKey = true; strip(/\bprimary\s+key\b/gi); }
|
|
33
|
+
if (/\bauto_?increment\b/i.test(def.type)) { def.autoIncrement = true; strip(/\bauto_?increment\b/gi); }
|
|
34
|
+
if (/\bnot\s+null\b/i.test(def.type)) { def.required = true; strip(/\bnot\s+null\b/gi); }
|
|
35
|
+
if (/\bunique\b/i.test(def.type)) { def.unique = true; strip(/\bunique\b/gi); }
|
|
36
|
+
if (/\bdefault\s+(\S+)/i.test(def.type)) {
|
|
37
|
+
const m = def.type.match(/\bdefault\s+(\S+)/i);
|
|
38
|
+
def.default = m[1].replace(/^['"]|['"]$/g, '');
|
|
39
|
+
strip(/\bdefault\s+\S+/gi);
|
|
40
|
+
}
|
|
41
|
+
return def;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseSchemaString(str) {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const seg of str.split(',')) {
|
|
47
|
+
const trimmed = seg.trim();
|
|
48
|
+
if (!trimmed) continue;
|
|
49
|
+
const parts = trimmed.split(/\s+/);
|
|
50
|
+
const name = parts.shift();
|
|
51
|
+
const def = parseShorthandSchema(parts.join(' ').toLowerCase().trim());
|
|
52
|
+
if (def.type.length === 0) def.type = 'string';
|
|
53
|
+
out[name] = def;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
26
58
|
function mapNativeSchema(schema) {
|
|
59
|
+
if (typeof schema === 'string') schema = parseSchemaString(schema);
|
|
27
60
|
const mapped = {};
|
|
28
61
|
for (const [field, def] of Object.entries(schema || {})) {
|
|
29
62
|
if (typeof def === 'string') {
|
|
30
|
-
|
|
63
|
+
const parsed = parseShorthandSchema(def.toLowerCase().trim());
|
|
64
|
+
if (parsed.type.length === 0) parsed.type = 'string';
|
|
65
|
+
mapped[field] = { ...parsed, type: NATIVE_TYPE_MAP[parsed.type] || parsed.type };
|
|
31
66
|
} else if (def && typeof def === 'object') {
|
|
32
67
|
const t = (def.type || 'string').toLowerCase();
|
|
33
|
-
|
|
68
|
+
const out = { ...def, type: NATIVE_TYPE_MAP[t] || t };
|
|
69
|
+
if (out.primary_key !== undefined && out.primaryKey === undefined) { out.primaryKey = out.primary_key; delete out.primary_key; }
|
|
70
|
+
if (out.auto_increment !== undefined && out.autoIncrement === undefined) { out.autoIncrement = out.auto_increment; delete out.auto_increment; }
|
|
71
|
+
mapped[field] = out;
|
|
34
72
|
} else {
|
|
35
73
|
mapped[field] = { type: 'string' };
|
|
36
74
|
}
|
|
@@ -39,25 +77,46 @@ function mapNativeSchema(schema) {
|
|
|
39
77
|
}
|
|
40
78
|
|
|
41
79
|
function restoreRow(row, schema) {
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
80
|
+
if (typeof row !== 'object' || row === null) return row;
|
|
81
|
+
let src = row;
|
|
82
|
+
if (row.fields && typeof row.fields === 'object' && row.fields !== null) {
|
|
83
|
+
src = { ...row.fields };
|
|
84
|
+
if (row.id !== undefined) {
|
|
85
|
+
const pkCols = [];
|
|
86
|
+
if (schema) {
|
|
87
|
+
for (const [k, def] of Object.entries(schema)) {
|
|
88
|
+
if (def && typeof def === 'object' && def.autoIncrement) continue;
|
|
89
|
+
const isPk = typeof def === 'string'
|
|
90
|
+
? /\bprimary\s+key\b/i.test(def)
|
|
91
|
+
: !!(def && (def.primaryKey || def.primary_key));
|
|
92
|
+
if (isPk) pkCols.push(k);
|
|
93
|
+
}
|
|
94
|
+
} else if (src.id === undefined) {
|
|
95
|
+
pkCols.push('id');
|
|
96
|
+
}
|
|
97
|
+
for (const c of pkCols) {
|
|
98
|
+
const v = src[c];
|
|
99
|
+
if (v === undefined || v === null || v === 0 || v === '' || v === false) src[c] = row.id;
|
|
100
|
+
}
|
|
48
101
|
}
|
|
102
|
+
if (row.created_at !== undefined) src.created_at = row.created_at;
|
|
103
|
+
if (row.updated_at !== undefined) src.updated_at = row.updated_at;
|
|
104
|
+
}
|
|
105
|
+
if (!schema) return src;
|
|
106
|
+
const out = {};
|
|
107
|
+
for (const [field, value] of Object.entries(src)) {
|
|
49
108
|
const colDef = schema[field];
|
|
50
109
|
let t = null;
|
|
51
110
|
if (typeof colDef === 'string') t = colDef.toLowerCase();
|
|
52
111
|
else if (colDef && typeof colDef === 'object' && colDef.type) t = String(colDef.type).toLowerCase();
|
|
53
|
-
if (['json', 'array', 'object'].includes(t) && typeof
|
|
54
|
-
try { out[field] = JSON.parse(
|
|
55
|
-
} else if ((t === 'integer' || t === 'int' || t === 'bigint') && typeof
|
|
56
|
-
out[field] = Number(
|
|
57
|
-
} else if ((t === 'float' || t === 'double' || t === 'number') && typeof
|
|
58
|
-
out[field] = Number(
|
|
112
|
+
if (['json', 'array', 'object'].includes(t) && typeof value === 'string') {
|
|
113
|
+
try { out[field] = JSON.parse(value); } catch (e) { out[field] = value; }
|
|
114
|
+
} else if ((t === 'integer' || t === 'int' || t === 'bigint') && typeof value === 'string' && /^-?\d+$/.test(value)) {
|
|
115
|
+
out[field] = Number(value);
|
|
116
|
+
} else if ((t === 'float' || t === 'double' || t === 'number') && typeof value === 'string' && !Number.isNaN(Number(value))) {
|
|
117
|
+
out[field] = Number(value);
|
|
59
118
|
} else {
|
|
60
|
-
out[field] =
|
|
119
|
+
out[field] = value;
|
|
61
120
|
}
|
|
62
121
|
}
|
|
63
122
|
return out;
|
|
@@ -230,6 +289,7 @@ class JSQL {
|
|
|
230
289
|
this._bufferSize = 0;
|
|
231
290
|
this._tableNames = new Set();
|
|
232
291
|
this._schemas = {};
|
|
292
|
+
this._txId = undefined;
|
|
233
293
|
this._autoPlugins = opts.modules !== false;
|
|
234
294
|
|
|
235
295
|
this._plugins = [];
|
|
@@ -324,6 +384,62 @@ class JSQL {
|
|
|
324
384
|
this._emit('start', {});
|
|
325
385
|
}
|
|
326
386
|
|
|
387
|
+
_pkOf(table) {
|
|
388
|
+
const schema = this._schemas[table];
|
|
389
|
+
if (!schema) return null;
|
|
390
|
+
for (const [k, def] of Object.entries(schema)) {
|
|
391
|
+
const isPk = (typeof def === 'string' && /\bprimary\s+key\b/i.test(def))
|
|
392
|
+
|| !!(def && typeof def === 'object' && (def.primaryKey || def.primary_key));
|
|
393
|
+
if (isPk) return k;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
_pkAutoIncrement(table) {
|
|
399
|
+
const schema = this._schemas[table];
|
|
400
|
+
const pk = this._pkOf(table);
|
|
401
|
+
if (!pk || !schema) return false;
|
|
402
|
+
const def = schema[pk];
|
|
403
|
+
if (typeof def === 'string') return /\bauto_?increment\b/i.test(def);
|
|
404
|
+
return !!(def && def.autoIncrement);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_coercePkId(table, id) {
|
|
408
|
+
const schema = this._schemas[table];
|
|
409
|
+
const pk = this._pkOf(table);
|
|
410
|
+
if (!schema || !pk) return id;
|
|
411
|
+
const def = schema[pk];
|
|
412
|
+
let t = null;
|
|
413
|
+
if (typeof def === 'string') t = def.toLowerCase();
|
|
414
|
+
else if (def && typeof def === 'object' && def.type) t = String(def.type).toLowerCase();
|
|
415
|
+
if (['integer', 'int', 'bigint'].includes(t)) return Number(id);
|
|
416
|
+
return id;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
_findRawById(table, id) {
|
|
420
|
+
const pk = this._pkOf(table);
|
|
421
|
+
if (pk) {
|
|
422
|
+
const filter = {};
|
|
423
|
+
filter[pk] = this._coercePkId(table, id);
|
|
424
|
+
const r = safeJsonParse(wasmBindings.jsql_find(table, JSON.stringify(filter), 1, 0));
|
|
425
|
+
if (r && r.error) return r;
|
|
426
|
+
return Array.isArray(r) && r.length > 0 ? r[0] : null;
|
|
427
|
+
}
|
|
428
|
+
return safeJsonParse(wasmBindings.jsql_find_by_id(table, BigInt(id)));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
_seqIdByPk(table, id) {
|
|
432
|
+
const pk = this._pkOf(table);
|
|
433
|
+
if (pk) {
|
|
434
|
+
const filter = {};
|
|
435
|
+
filter[pk] = this._coercePkId(table, id);
|
|
436
|
+
const r = safeJsonParse(wasmBindings.jsql_find(table, JSON.stringify(filter), 1, 0));
|
|
437
|
+
if (Array.isArray(r) && r.length > 0) return r[0].id;
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
return Number(id);
|
|
441
|
+
}
|
|
442
|
+
|
|
327
443
|
async _insertBatch(table, rows) {
|
|
328
444
|
const schema = this._schemas[table];
|
|
329
445
|
if (schema) {
|
|
@@ -347,6 +463,15 @@ class JSQL {
|
|
|
347
463
|
}
|
|
348
464
|
const r = safeJsonParse(wasmBindings.jsql_insert_json(table, JSON.stringify(rows)));
|
|
349
465
|
if (r && r.error) throw new Error(r.error);
|
|
466
|
+
if (r && Array.isArray(r) && !this._pkAutoIncrement(table)) {
|
|
467
|
+
const pk = this._pkOf(table);
|
|
468
|
+
if (pk) {
|
|
469
|
+
return r.map((seq, i) => {
|
|
470
|
+
const row = rows[i];
|
|
471
|
+
return (row && row[pk] !== undefined && row[pk] !== null) ? row[pk] : seq;
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
350
475
|
return r;
|
|
351
476
|
}
|
|
352
477
|
|
|
@@ -401,7 +526,7 @@ class JSQL {
|
|
|
401
526
|
const r = safeJsonParse(wasmBindings.jsql_create_table(name, JSON.stringify(mapNativeSchema(schema))));
|
|
402
527
|
if (r && r.ok === false) throw new Error(r.error || 'create table failed');
|
|
403
528
|
this._tableNames.add(name);
|
|
404
|
-
this._schemas[name] = schema;
|
|
529
|
+
this._schemas[name] = typeof schema === 'string' ? mapNativeSchema(schema) : schema;
|
|
405
530
|
this._emit('createTable', { name, schema });
|
|
406
531
|
this._runHooks('afterCreateTable', [name, schema]);
|
|
407
532
|
return r;
|
|
@@ -422,7 +547,7 @@ class JSQL {
|
|
|
422
547
|
async findById(table, id) {
|
|
423
548
|
await this._flush();
|
|
424
549
|
if (!this._runHooks('beforeFind', [table, { id }])) return null;
|
|
425
|
-
const r =
|
|
550
|
+
const r = this._findRawById(table, id);
|
|
426
551
|
if (r && r.error) throw new Error(r.error);
|
|
427
552
|
this._runHooks('afterFind', [table, { id }, r]);
|
|
428
553
|
const schema = this._schemas[table];
|
|
@@ -453,7 +578,11 @@ class JSQL {
|
|
|
453
578
|
}
|
|
454
579
|
|
|
455
580
|
async updateByIds(table, entries) {
|
|
456
|
-
const pairs =
|
|
581
|
+
const pairs = [];
|
|
582
|
+
for (const [id, data] of entries) {
|
|
583
|
+
const seq = this._seqIdByPk(table, id);
|
|
584
|
+
if (seq !== null) pairs.push([seq, data]);
|
|
585
|
+
}
|
|
457
586
|
if (!this._runHooks('beforeUpdate', [table, pairs])) return;
|
|
458
587
|
await this._flush();
|
|
459
588
|
const r = safeJsonParse(wasmBindings.jsql_update_by_ids(table, JSON.stringify(pairs)));
|
|
@@ -466,9 +595,10 @@ class JSQL {
|
|
|
466
595
|
async removeByIds(table, ids) {
|
|
467
596
|
if (!this._runHooks('beforeDelete', [table, ids])) return;
|
|
468
597
|
await this._flush();
|
|
469
|
-
const
|
|
598
|
+
const seqs = ids.map(id => this._seqIdByPk(table, id)).filter(s => s !== null);
|
|
599
|
+
const r = safeJsonParse(wasmBindings.jsql_remove_by_ids(table, JSON.stringify(seqs)));
|
|
470
600
|
if (r && r.error) throw new Error(r.error);
|
|
471
|
-
this._emit('delete', { table, ids, result: r });
|
|
601
|
+
this._emit('delete', { table, ids: seqs, result: r });
|
|
472
602
|
this._runHooks('afterDelete', [table, ids, r]);
|
|
473
603
|
return r;
|
|
474
604
|
}
|
|
@@ -476,7 +606,12 @@ class JSQL {
|
|
|
476
606
|
async findByIds(table, ids) {
|
|
477
607
|
await this._flush();
|
|
478
608
|
if (!this._runHooks('beforeFind', [table, { ids }])) return null;
|
|
479
|
-
|
|
609
|
+
let r;
|
|
610
|
+
if (this._pkOf(table)) {
|
|
611
|
+
r = ids.map(id => this._findRawById(table, id)).filter(x => x !== null && !(x && x.error));
|
|
612
|
+
} else {
|
|
613
|
+
r = safeJsonParse(wasmBindings.jsql_find_by_ids(table, JSON.stringify(ids)));
|
|
614
|
+
}
|
|
480
615
|
if (r && r.error) throw new Error(r.error);
|
|
481
616
|
this._runHooks('afterFind', [table, { ids }, r]);
|
|
482
617
|
const schema = this._schemas[table];
|
|
@@ -491,7 +626,9 @@ class JSQL {
|
|
|
491
626
|
async updateById(table, id, data) {
|
|
492
627
|
if (!this._runHooks('beforeUpdate', [table, id, data])) return;
|
|
493
628
|
await this._flush();
|
|
494
|
-
const
|
|
629
|
+
const seq = this._seqIdByPk(table, id);
|
|
630
|
+
if (seq === null) return null;
|
|
631
|
+
const r = safeJsonParse(wasmBindings.jsql_update_by_id(table, BigInt(seq), JSON.stringify(data)));
|
|
495
632
|
if (r && r.ok === false) throw new Error(r.error || 'update failed');
|
|
496
633
|
this._emit('update', { table, id, data });
|
|
497
634
|
this._runHooks('afterUpdate', [table, id, data, r]);
|
|
@@ -501,13 +638,53 @@ class JSQL {
|
|
|
501
638
|
async removeById(table, id) {
|
|
502
639
|
if (!this._runHooks('beforeDelete', [table, id])) return;
|
|
503
640
|
await this._flush();
|
|
504
|
-
const
|
|
641
|
+
const seq = this._seqIdByPk(table, id);
|
|
642
|
+
if (seq === null) return null;
|
|
643
|
+
const r = safeJsonParse(wasmBindings.jsql_remove_by_id(table, BigInt(seq)));
|
|
505
644
|
if (r && r.ok === false) throw new Error(r.error || 'remove failed');
|
|
506
645
|
this._emit('delete', { table, id });
|
|
507
646
|
this._runHooks('afterDelete', [table, id, r]);
|
|
508
647
|
return r;
|
|
509
648
|
}
|
|
510
649
|
|
|
650
|
+
async beginTx() {
|
|
651
|
+
await this._flush();
|
|
652
|
+
const r = safeJsonParse(wasmBindings.jsql_begin_tx());
|
|
653
|
+
if (r && r.ok === false) throw new Error(r.error || 'begin transaction failed');
|
|
654
|
+
this._txId = r.txId;
|
|
655
|
+
return r.txId;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async commitTx(txId) {
|
|
659
|
+
const r = safeJsonParse(wasmBindings.jsql_commit_tx(String(txId)));
|
|
660
|
+
if (r && r.ok === false) throw new Error(r.error || 'commit transaction failed');
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async rollbackTx(txId) {
|
|
665
|
+
const r = safeJsonParse(wasmBindings.jsql_rollback_tx(String(txId)));
|
|
666
|
+
if (r && r.ok === false) throw new Error(r.error || 'rollback transaction failed');
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
async beginTransaction() {
|
|
671
|
+
return this.beginTx();
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
async commit() {
|
|
675
|
+
if (this._txId === undefined) return true;
|
|
676
|
+
const r = await this.commitTx(this._txId);
|
|
677
|
+
this._txId = undefined;
|
|
678
|
+
return r;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
async rollback() {
|
|
682
|
+
if (this._txId === undefined) return true;
|
|
683
|
+
const r = await this.rollbackTx(this._txId);
|
|
684
|
+
this._txId = undefined;
|
|
685
|
+
return r;
|
|
686
|
+
}
|
|
687
|
+
|
|
511
688
|
async hasTable(name) {
|
|
512
689
|
return this._tableNames.has(name);
|
|
513
690
|
}
|