jsql-neo 5.4.2 → 5.5.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.
Files changed (72) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8097 -7971
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +385 -357
  7. package/index.js +124 -118
  8. package/lib/ast.js +214 -0
  9. package/lib/btree.js +413 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1744 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +732 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +34 -34
  26. package/lib/query.js +815 -815
  27. package/lib/redis_server.js +571 -571
  28. package/lib/sql.js +3460 -2925
  29. package/lib/sqlite_compat.js +323 -323
  30. package/lib/sqlite_worker.js +264 -264
  31. package/lib/table.js +1304 -1304
  32. package/lib/tui.js +502 -502
  33. package/lib/wasm.js +1 -1
  34. package/lib/wasm_client.js +718 -718
  35. package/lib/web_ui.js +257 -257
  36. package/native/jsql-neo-native.node +0 -0
  37. package/nativesrc/jsql-neo-core/Cargo.lock +455 -455
  38. package/nativesrc/jsql-neo-core/Cargo.toml +24 -24
  39. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +455 -455
  40. package/nativesrc/jsql-neo-core/src/engine/memory.rs +168 -168
  41. package/nativesrc/jsql-neo-core/src/engine/mod.rs +62 -62
  42. package/nativesrc/jsql-neo-core/src/engine/table.rs +700 -700
  43. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  44. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  45. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  46. package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -85
  47. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  48. package/nativesrc/jsql-neo-native/Cargo.lock +606 -606
  49. package/nativesrc/jsql-neo-native/Cargo.toml +16 -16
  50. package/nativesrc/jsql-neo-native/build.rs +3 -3
  51. package/nativesrc/jsql-neo-native/package.json +7 -7
  52. package/nativesrc/jsql-neo-native/src/lib.rs +269 -269
  53. package/nativesrc/jsql-neo-wasm/Cargo.lock +465 -465
  54. package/nativesrc/jsql-neo-wasm/Cargo.toml +15 -15
  55. package/nativesrc/jsql-neo-wasm/src/lib.rs +447 -447
  56. package/package.json +87 -86
  57. package/postinstall.js +158 -158
  58. package/test/btree.test.js +128 -128
  59. package/test/coverage.js +187 -187
  60. package/test/join.test.js +110 -110
  61. package/test/native.test.js +158 -158
  62. package/test/regress-5.1.0.js +271 -271
  63. package/test/smoke.js +58 -58
  64. package/test/sql-parser.test.js +273 -0
  65. package/test/wasm.test.js +110 -110
  66. package/wasm/browser.d.ts +89 -89
  67. package/wasm/browser.mjs +569 -569
  68. package/wasm/browser_bg.mjs +597 -597
  69. package/wasm/jsql_neo_wasm.d.ts +38 -38
  70. package/wasm/jsql_neo_wasm.js +520 -520
  71. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  72. package/wasm/package.json +11 -11
package/lib/table.js CHANGED
@@ -1,1305 +1,1305 @@
1
- // © Vexify 2026 All Rights Reserved.
2
- /**
3
- * JSQL Table — 表操作引擎 v2.0
4
- * B-Tree 自动索引、日期类型、错误码、外键 CASCADE、长度/精度校验
5
- */
6
-
7
- const Query = require('./query');
8
- const BTree = require('./btree');
9
- const { validateDateType, now } = require('./date-types');
10
- const { createError } = require('./errors');
11
-
12
- function isSafeRegex(pattern) {
13
- if (typeof pattern !== 'string') return false;
14
- if (pattern.length > 256) return false;
15
- const groups = [];
16
- let i = 0;
17
- const n = pattern.length;
18
- while (i < n) {
19
- const c = pattern[i];
20
- if (c === '\\') { i += 2; continue; }
21
- if (c === '[') {
22
- while (i < n && pattern[i] !== ']') i++;
23
- i++;
24
- continue;
25
- }
26
- if (c === '(') {
27
- const nxt = pattern[i + 1];
28
- if (nxt === '?') {
29
- const t = pattern[i + 2];
30
- if (t === ':') {
31
- groups.push({ hasQuant: false, hasAlt: false });
32
- i += 3;
33
- continue;
34
- }
35
- if (t === '=' || t === '!') {
36
- let depth = 1, j = i + 3;
37
- for (; j < n && depth > 0; j++) {
38
- if (pattern[j] === '\\') { j++; continue; }
39
- if (pattern[j] === '(') depth++;
40
- else if (pattern[j] === ')') depth--;
41
- }
42
- i = j;
43
- continue;
44
- }
45
- if (t === '<') {
46
- const u = pattern[i + 3];
47
- if (u === '=' || u === '!') {
48
- let depth = 1, j = i + 4;
49
- for (; j < n && depth > 0; j++) {
50
- if (pattern[j] === '\\') { j++; continue; }
51
- if (pattern[j] === '(') depth++;
52
- else if (pattern[j] === ')') depth--;
53
- }
54
- i = j;
55
- continue;
56
- }
57
- const gt = pattern.indexOf('>', i + 3);
58
- if (gt === -1) return false;
59
- groups.push({ hasQuant: false, hasAlt: false });
60
- i = gt + 1;
61
- continue;
62
- }
63
- return false;
64
- }
65
- groups.push({ hasQuant: false, hasAlt: false });
66
- i++;
67
- continue;
68
- }
69
- if (c === ')') {
70
- const g = groups.pop();
71
- if (!g) return false;
72
- let qi = i + 1;
73
- let quant = null;
74
- if (pattern[qi] === '+' || pattern[qi] === '*' || pattern[qi] === '?') {
75
- quant = pattern[qi];
76
- } else if (pattern[qi] === '{') {
77
- const close = pattern.indexOf('}', qi);
78
- if (close === -1) return false;
79
- quant = pattern.slice(qi, close + 1);
80
- qi = close + 1;
81
- }
82
- if (quant && (g.hasQuant || g.hasAlt)) return false;
83
- i = qi;
84
- continue;
85
- }
86
- if (c === '+' || c === '*' || c === '?') {
87
- if (groups.length > 0) groups[groups.length - 1].hasQuant = true;
88
- i++;
89
- continue;
90
- }
91
- if (c === '{' && /[0-9]/.test(pattern[i + 1] || '')) {
92
- const close = pattern.indexOf('}', i);
93
- if (close === -1) return false;
94
- if (groups.length > 0) groups[groups.length - 1].hasQuant = true;
95
- i = close + 1;
96
- continue;
97
- }
98
- if (c === '|') {
99
- if (groups.length > 0) groups[groups.length - 1].hasAlt = true;
100
- i++;
101
- continue;
102
- }
103
- i++;
104
- }
105
- return groups.length === 0;
106
- }
107
-
108
- class Table {
109
- constructor(name, schema, db) {
110
- this._name = name;
111
- this._schema = schema;
112
- this._db = db;
113
- this._rows = [];
114
- this._indexes = {}; // 旧版 hash 索引(兼容)
115
- this._btrees = {}; // B-Tree 索引 { fieldName: BTree }
116
- this._autoIncrement = 0;
117
- this._dirty = false;
118
- this._hooks = {
119
- beforeInsert: [], afterInsert: [],
120
- beforeUpdate: [], afterUpdate: [],
121
- beforeRemove: [], afterRemove: []
122
- };
123
- this._softDelete = false;
124
- this._computedFields = {};
125
- this._btreesDirty = false;
126
-
127
- this._primaryKey = null;
128
- this._pkIndex = null;
129
- this._autoIncrementField = null;
130
- this._foreignKeys = [];
131
- this._checkConstraints = [];
132
- this._defaults = {};
133
- this._dateFields = {}; // { fieldName: 'date'|'datetime'|'timestamp' }
134
-
135
- for (const [field, def] of Object.entries(schema)) {
136
- const isPk = !!(def.primaryKey || def.primary === true);
137
- if (isPk) this._primaryKey = field;
138
- if (def.autoIncrement) {
139
- this._autoIncrementField = field;
140
- if (isPk) this._primaryKey = field;
141
- }
142
- if (def.default !== undefined) {
143
- this._defaults[field] = def.default;
144
- }
145
- if (def.check) {
146
- this._checkConstraints.push({ field, fn: def.check });
147
- }
148
- if (def.foreignKey) {
149
- this._foreignKeys.push({
150
- field,
151
- table: def.foreignKey.table,
152
- refField: def.foreignKey.field || 'id',
153
- onDelete: def.foreignKey.onDelete || 'restrict',
154
- onUpdate: def.foreignKey.onUpdate || 'restrict'
155
- });
156
- }
157
- if (def.computed) {
158
- this._computedFields[field] = def.computed;
159
- }
160
- // 日期类型
161
- if (['date', 'datetime', 'timestamp', 'time'].includes(def.type)) {
162
- this._dateFields[field] = def.type;
163
- }
164
- }
165
-
166
- if (schema._softDelete) {
167
- this._softDelete = true;
168
- }
169
-
170
- this._pkIndex = this._primaryKey ? new Map() : null;
171
- this._cachedSchemaFields = Object.keys(schema).filter(f => f !== '_softDelete');
172
- this._cachedDateFields = Object.keys(this._dateFields);
173
- this._cachedUniqueFields = this._cachedSchemaFields.filter(f => schema[f].unique);
174
- this._cachedRequiredFields = this._cachedSchemaFields.filter(f => schema[f].required);
175
- this._autoCreateBTrees();
176
- }
177
-
178
- _autoCreateBTrees() {
179
- for (const [field, def] of Object.entries(this._schema)) {
180
- if (field === '_softDelete') continue;
181
- if (def.primaryKey || def.primary === true || def.unique) {
182
- this._btrees[field] = new BTree(64, true);
183
- }
184
- }
185
- }
186
-
187
- // ============================================================
188
- // 插入
189
- // ============================================================
190
-
191
- insert(data) {
192
- for (const hook of this._hooks.beforeInsert) {
193
- data = hook({ ...data }) || data;
194
- }
195
-
196
- data = this._applyDefaults(data);
197
- data = this._applyComputed(data);
198
- data = this._validateDataTypes(data);
199
- this._validateConstraints(data, -1);
200
-
201
- if (this._autoIncrementField && data[this._autoIncrementField] === undefined) {
202
- this._autoIncrement++;
203
- data[this._autoIncrementField] = this._autoIncrement;
204
- } else if (this._autoIncrementField && data[this._autoIncrementField] > this._autoIncrement) {
205
- this._autoIncrement = data[this._autoIncrementField];
206
- }
207
-
208
- const rowIndex = this._rows.length;
209
- this._rows.push({ ...data });
210
- this._addToIndexes(rowIndex, data);
211
- this._dirty = true;
212
- this._db._markDirty(this._name);
213
-
214
- for (const hook of this._hooks.afterInsert) {
215
- hook(data);
216
- }
217
- this._db._emitChange('insert', this._name, data);
218
-
219
- return data;
220
- }
221
-
222
- insertMany(items) {
223
- if (items.length === 0) return [];
224
- for (const hook of this._hooks.beforeInsert) {
225
- const mapped = new Array(items.length);
226
- for (let i = 0; i < items.length; i++) mapped[i] = hook({ ...items[i] }) || items[i];
227
- items = mapped;
228
- }
229
- const N = items.length;
230
- const uniqueTracker = {};
231
- for (const field of this._cachedUniqueFields) {
232
- uniqueTracker[field] = new Set();
233
- for (const row of this._rows) {
234
- if (row[field] !== undefined && row[field] !== null) uniqueTracker[field].add(row[field]);
235
- }
236
- }
237
- const dateFields = this._cachedDateFields;
238
- const hasDateFields = dateFields.length > 0;
239
- const autoIncField = this._autoIncrementField;
240
- const defaults = this._defaults;
241
- const computedFields = this._computedFields;
242
- const schema = this._schema;
243
- const checkConstraints = this._checkConstraints;
244
- const foreignKeys = this._foreignKeys;
245
- // 批量预分配自增 ID 范围:先扫描显式提供的最大值,一次性推进计数器,
246
- // 再在循环内用本地序号递增,避免批量内重复读取/写入同一计数器字段。
247
- const baseAutoInc = this._autoIncrement;
248
- if (autoIncField) {
249
- let maxExplicit = 0;
250
- for (const it of items) {
251
- const v = it[autoIncField];
252
- if (v !== undefined && v !== null) {
253
- const n = Number(v);
254
- if (!isNaN(n) && n > maxExplicit) maxExplicit = n;
255
- }
256
- }
257
- if (maxExplicit > baseAutoInc) {
258
- this._autoIncrement = maxExplicit;
259
- }
260
- }
261
- let autoIncSeq = 0;
262
- for (let i = 0; i < N; i++) {
263
- let data = items[i];
264
- for (const [f, dv] of Object.entries(defaults)) {
265
- if (data[f] === undefined) data[f] = typeof dv === 'function' ? dv() : dv === 'CURRENT_TIMESTAMP' ? now() : dv;
266
- }
267
- for (const f of Object.keys(computedFields)) data[f] = computedFields[f](data);
268
- if (hasDateFields) {
269
- for (const f of dateFields) {
270
- if (data[f] !== undefined && data[f] !== null) data[f] = validateDateType(data[f], this._dateFields[f], f);
271
- }
272
- }
273
- for (const field of this._cachedSchemaFields) {
274
- const def = schema[field];
275
- if (def.required && (data[field] === undefined || data[field] === null)
276
- && !(def.autoIncrement && data[field] === undefined)) throw createError('ER_BAD_NULL_ERROR', field);
277
- if (def.unique && data[field] !== undefined) {
278
- if (uniqueTracker[field].has(data[field])) throw createError('ER_DUP_ENTRY', String(data[field]), field);
279
- uniqueTracker[field].add(data[field]);
280
- }
281
- if (def.length && typeof data[field] === 'string' && data[field].length > def.length) throw createError('ER_DATA_TOO_LONG', field);
282
- if (def.type === 'integer' || def.type === 'number') {
283
- const v = def.type === 'integer' ? Number(data[field]) : data[field];
284
- if (data[field] !== undefined && data[field] !== null && def.min !== undefined && v < def.min) throw createError('ER_OUT_OF_RANGE', field);
285
- if (data[field] !== undefined && data[field] !== null && def.max !== undefined && v > def.max) throw createError('ER_OUT_OF_RANGE', field);
286
- }
287
- if (def.check && data[field] !== undefined && !def.check(data[field], data)) throw createError('ER_CHECK_CONSTRAINT', this._name + '.' + field);
288
- }
289
- for (const fk of foreignKeys) {
290
- if (data[fk.field] !== undefined && data[fk.field] !== null) {
291
- const refTable = this._resolveTable(fk.table);
292
- if (refTable) {
293
- const exists = refTable.findOne({ [fk.refField]: data[fk.field] });
294
- if (!exists) throw createError('ER_NO_REFERENCED_ROW');
295
- }
296
- }
297
- }
298
- if (autoIncField && data[autoIncField] === undefined) {
299
- autoIncSeq++;
300
- data[autoIncField] = baseAutoInc + autoIncSeq;
301
- this._autoIncrement = data[autoIncField];
302
- } else if (autoIncField && data[autoIncField] > this._autoIncrement) {
303
- this._autoIncrement = data[autoIncField];
304
- }
305
- const rowIdx = this._rows.length;
306
- this._rows.push(Object.assign({}, data));
307
- if (this._pkIndex && this._primaryKey) {
308
- const pkVal = data[this._primaryKey];
309
- if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, rowIdx);
310
- }
311
- for (const [field, tree] of Object.entries(this._btrees)) {
312
- const val = data[field];
313
- if (val !== undefined && val !== null) tree.insert(val, rowIdx);
314
- }
315
- items[i] = data;
316
- }
317
- this._dirty = true;
318
- this._db._markDirty(this._name);
319
- for (const hook of this._hooks.afterInsert) {
320
- for (let i = 0; i < N; i++) hook(items[i]);
321
- }
322
- this._db._emitChange('insertMany', this._name, { count: N });
323
- return items;
324
- }
325
-
326
- upsert(data) {
327
- if (this._primaryKey && data[this._primaryKey] !== undefined) {
328
- const existing = this.findById(data[this._primaryKey]);
329
- if (existing) {
330
- this.updateById(data[this._primaryKey], data);
331
- return this.findById(data[this._primaryKey]);
332
- }
333
- }
334
- return this.insert(data);
335
- }
336
-
337
- // ============================================================
338
- // 查询(支持 B-Tree 加速)
339
- // ============================================================
340
-
341
- find(query = {}) {
342
- let rows = this._getVisibleRows();
343
- if (Object.keys(query).length > 0) {
344
- rows = this._applyFilterOptimized(rows, query);
345
- }
346
- return rows.map(r => ({ ...r }));
347
- }
348
-
349
- findOne(query = {}) {
350
- const rows = this.find(query);
351
- return rows.length > 0 ? rows[0] : null;
352
- }
353
-
354
- findById(id) {
355
- if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
356
- // Hash 索引 O(1)
357
- if (this._pkIndex) {
358
- const rowIndex = this._pkIndex.get(id);
359
- if (rowIndex !== undefined) {
360
- const row = this._rows[rowIndex];
361
- if (row && !row._deleted) return { ...row };
362
- }
363
- return null;
364
- }
365
- this._ensureBTrees();
366
- if (this._btrees[this._primaryKey]) {
367
- const indices = this._btrees[this._primaryKey].search(id);
368
- if (indices.length > 0) {
369
- const row = this._rows[indices[0]];
370
- if (row && !row._deleted) return { ...row };
371
- }
372
- return null;
373
- }
374
- return this.findOne({ [this._primaryKey]: id });
375
- }
376
-
377
- findAll() {
378
- return this._getVisibleRows().map(r => ({ ...r }));
379
- }
380
-
381
- count(query = {}) {
382
- if (Object.keys(query).length === 0) return this._getVisibleRows().length;
383
- return this._applyFilterOptimized(this._getVisibleRows(), query).length;
384
- }
385
-
386
- where(conditions) {
387
- return new Query(this, conditions);
388
- }
389
-
390
- // ============================================================
391
- // 更新
392
- // ============================================================
393
-
394
- update(query, updates) {
395
- for (const hook of this._hooks.beforeUpdate) {
396
- updates = hook(query, { ...updates }) || updates;
397
- }
398
-
399
- updates = this._validateDataTypes(updates, true);
400
- const matched = this._applyFilterOptimized(this._getVisibleRows(), query);
401
- let count = 0;
402
-
403
- for (const row of matched) {
404
- const index = this._rows.indexOf(row);
405
- if (index === -1) continue;
406
-
407
- const oldData = { ...this._rows[index] };
408
- const merged = { ...this._rows[index], ...updates };
409
- this._validateConstraints(merged, index);
410
-
411
- this._removeFromIndexes(index, oldData);
412
- Object.assign(this._rows[index], updates);
413
- this._addToIndexes(index, this._rows[index]);
414
- count++;
415
- }
416
-
417
- if (count > 0) {
418
- this._dirty = true;
419
- this._db._markDirty(this._name);
420
- for (const hook of this._hooks.afterUpdate) {
421
- hook(query, updates, count);
422
- }
423
- this._db._emitChange('update', this._name, { query, updates, count });
424
- }
425
- return count;
426
- }
427
-
428
- updateById(id, updates) {
429
- if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
430
- for (const hook of this._hooks.beforeUpdate) {
431
- updates = hook({ [this._primaryKey]: id }, { ...updates }) || updates;
432
- }
433
- updates = this._validateDataTypes(updates, true);
434
- let rowIndex;
435
- if (this._pkIndex) {
436
- rowIndex = this._pkIndex.get(id);
437
- } else {
438
- this._ensureBTrees();
439
- if (this._btrees[this._primaryKey]) {
440
- const indices = this._btrees[this._primaryKey].search(id);
441
- rowIndex = indices.length > 0 ? indices[0] : undefined;
442
- } else {
443
- rowIndex = this._rows.findIndex(r => r[this._primaryKey] === id);
444
- if (rowIndex === -1) rowIndex = undefined;
445
- }
446
- }
447
- if (rowIndex === undefined) return 0;
448
- const row = this._rows[rowIndex];
449
- if (row && row._deleted) return 0;
450
- const oldData = { ...row };
451
- const merged = { ...row, ...updates };
452
- this._validateConstraints(merged, rowIndex);
453
- this._removeFromIndexes(rowIndex, oldData);
454
- Object.assign(row, updates);
455
- this._addToIndexes(rowIndex, row);
456
- this._dirty = true;
457
- this._db._markDirty(this._name);
458
- for (const hook of this._hooks.afterUpdate) {
459
- hook({ [this._primaryKey]: id }, updates, 1);
460
- }
461
- this._db._emitChange('update', this._name, { query: { [this._primaryKey]: id }, updates, count: 1 });
462
- return 1;
463
- }
464
-
465
- // ============================================================
466
- // 删除 & 外键 CASCADE
467
- // ============================================================
468
-
469
- remove(query = {}) {
470
- for (const hook of this._hooks.beforeRemove) {
471
- if (hook(query) === false) return 0;
472
- }
473
-
474
- if (Object.keys(query).length === 0) {
475
- return this._truncate();
476
- }
477
-
478
- const toRemove = this._applyFilterOptimized(this._getVisibleRows(), query);
479
- let count = 0;
480
- const removeIndices = [];
481
-
482
- for (const row of toRemove) {
483
- const index = this._rows.indexOf(row);
484
- if (index === -1) continue;
485
- this._cascadeForeignKeys(row);
486
- this._removeFromIndexes(index, row);
487
- removeIndices.push(index);
488
- count++;
489
- }
490
-
491
- if (count > 0) {
492
- removeIndices.sort((a, b) => b - a);
493
- for (const idx of removeIndices) {
494
- const lastIdx = this._rows.length - 1;
495
- if (idx < lastIdx) {
496
- const lastRow = this._rows[lastIdx];
497
- this._rows[idx] = lastRow;
498
- if (this._pkIndex && this._primaryKey) {
499
- this._pkIndex.set(lastRow[this._primaryKey], idx);
500
- }
501
- for (const [field, tree] of Object.entries(this._btrees)) {
502
- const val = lastRow[field];
503
- if (val !== undefined && val !== null) {
504
- tree.remove(val, lastIdx);
505
- tree.insert(val, idx);
506
- }
507
- }
508
- for (const field of Object.keys(this._indexes)) {
509
- const val = lastRow[field];
510
- if (val !== undefined && this._indexes[field].has(val)) {
511
- const positions = this._indexes[field].get(val);
512
- const pi = positions.indexOf(lastIdx);
513
- if (pi !== -1) positions[pi] = idx;
514
- }
515
- }
516
- }
517
- this._rows.pop();
518
- }
519
- this._dirty = true;
520
- this._db._markDirty(this._name);
521
- for (const hook of this._hooks.afterRemove) {
522
- hook(query, count);
523
- }
524
- this._db._emitChange('remove', this._name, { query, count });
525
- }
526
- return count;
527
- }
528
-
529
- removeById(id) {
530
- if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
531
- let rowIndex;
532
- if (this._pkIndex) {
533
- rowIndex = this._pkIndex.get(id);
534
- } else {
535
- this._ensureBTrees();
536
- if (this._btrees[this._primaryKey]) {
537
- const indices = this._btrees[this._primaryKey].search(id);
538
- rowIndex = indices.length > 0 ? indices[0] : undefined;
539
- } else {
540
- rowIndex = this._rows.findIndex(r => r[this._primaryKey] === id);
541
- if (rowIndex === -1) rowIndex = undefined;
542
- }
543
- }
544
- if (rowIndex === undefined) return 0;
545
- const row = this._rows[rowIndex];
546
- if (!row || row._deleted) return 0;
547
- this._cascadeForeignKeys(row);
548
- this._removeFromIndexes(rowIndex, row);
549
- const lastIdx = this._rows.length - 1;
550
- if (rowIndex < lastIdx) {
551
- const lastRow = this._rows[lastIdx];
552
- this._rows[rowIndex] = lastRow;
553
- if (this._pkIndex && this._primaryKey) {
554
- this._pkIndex.set(lastRow[this._primaryKey], rowIndex);
555
- }
556
- for (const [field, tree] of Object.entries(this._btrees)) {
557
- const val = lastRow[field];
558
- if (val !== undefined && val !== null) {
559
- tree.remove(val, lastIdx);
560
- tree.insert(val, rowIndex);
561
- }
562
- }
563
- for (const field of Object.keys(this._indexes)) {
564
- const val = lastRow[field];
565
- if (val !== undefined && this._indexes[field].has(val)) {
566
- const positions = this._indexes[field].get(val);
567
- const pi = positions.indexOf(lastIdx);
568
- if (pi !== -1) positions[pi] = rowIndex;
569
- }
570
- }
571
- }
572
- this._rows.pop();
573
- this._dirty = true;
574
- this._db._markDirty(this._name);
575
- for (const hook of this._hooks.afterRemove) {
576
- hook({ [this._primaryKey]: id }, 1);
577
- }
578
- this._db._emitChange('remove', this._name, { query: { [this._primaryKey]: id }, count: 1 });
579
- return 1;
580
- }
581
-
582
- removeSoft(query = {}) {
583
- this._softDelete = true;
584
- return this.remove(query);
585
- }
586
-
587
- _findRowIndex(id) {
588
- if (this._pkIndex && this._primaryKey) {
589
- const idx = this._pkIndex.get(id);
590
- if (idx !== undefined) return idx;
591
- }
592
- for (let i = 0; i < this._rows.length; i++) {
593
- if (this._rows[i][this._primaryKey] === id) return i;
594
- }
595
- return -1;
596
- }
597
-
598
- restore(query = {}) {
599
- if (!this._softDelete) throw createError('ER_NOT_SUPPORTED', 'Soft delete is not enabled');
600
- const matched = this._applyFilter(this._rows, { ...query, _deleted: true });
601
- for (const row of matched) {
602
- const index = this._rows.indexOf(row);
603
- if (index !== -1) {
604
- delete this._rows[index]._deleted;
605
- delete this._rows[index]._deletedAt;
606
- }
607
- }
608
- return matched.length;
609
- }
610
-
611
- truncate() {
612
- return this._truncate();
613
- }
614
-
615
- _truncate() {
616
- const count = this._rows.length;
617
- this._rows = [];
618
- if (this._pkIndex) this._pkIndex.clear();
619
- for (const key of Object.keys(this._btrees)) {
620
- this._btrees[key].clear();
621
- }
622
- this._indexes = {};
623
- this._autoIncrement = 0;
624
- this._dirty = true;
625
- this._db._markDirty(this._name);
626
- return count;
627
- }
628
-
629
- // ============================================================
630
- // ALTER TABLE
631
- // ============================================================
632
-
633
- addColumn(field, definition) {
634
- if (this._schema[field]) throw createError('ER_DUP_FIELDNAME', field);
635
- this._schema[field] = definition;
636
- if (definition.default !== undefined) {
637
- this._defaults[field] = definition.default;
638
- for (const row of this._rows) {
639
- if (row[field] === undefined) {
640
- row[field] = typeof definition.default === 'function' ? definition.default() : definition.default;
641
- }
642
- }
643
- }
644
- if (definition.check) this._checkConstraints.push({ field, fn: definition.check });
645
- if (definition.foreignKey) {
646
- this._foreignKeys.push({
647
- field,
648
- table: definition.foreignKey.table,
649
- refField: definition.foreignKey.field || 'id',
650
- onDelete: definition.foreignKey.onDelete || 'restrict',
651
- onUpdate: definition.foreignKey.onUpdate || 'restrict'
652
- });
653
- }
654
- if (definition.computed) this._computedFields[field] = definition.computed;
655
- if (['date', 'datetime', 'timestamp', 'time'].includes(definition.type)) {
656
- this._dateFields[field] = definition.type;
657
- }
658
- if (definition.primaryKey || definition.unique) {
659
- this._btrees[field] = new BTree(64, true);
660
- this._rebuildBTree(field);
661
- }
662
- this._dirty = true;
663
- this._db._markDirty(this._name);
664
- return this;
665
- }
666
-
667
- dropColumn(field) {
668
- if (!this._schema[field]) throw createError('ER_CANT_DROP_FIELD', field);
669
- delete this._schema[field];
670
- delete this._defaults[field];
671
- delete this._btrees[field];
672
- delete this._indexes[field];
673
- this._checkConstraints = this._checkConstraints.filter(c => c.field !== field);
674
- this._foreignKeys = this._foreignKeys.filter(fk => fk.field !== field);
675
- delete this._computedFields[field];
676
- delete this._dateFields[field];
677
- for (const row of this._rows) {
678
- delete row[field];
679
- }
680
- if (this._primaryKey === field) this._primaryKey = null;
681
- if (this._autoIncrementField === field) this._autoIncrementField = null;
682
- this._dirty = true;
683
- this._db._markDirty(this._name);
684
- return this;
685
- }
686
-
687
- renameColumn(oldName, newName) {
688
- if (!this._schema[oldName]) throw createError('ER_BAD_FIELD_ERROR', oldName, this._name);
689
- if (this._schema[newName]) throw createError('ER_DUP_FIELDNAME', newName);
690
- this._schema[newName] = this._schema[oldName];
691
- delete this._schema[oldName];
692
- if (this._defaults[oldName] !== undefined) {
693
- this._defaults[newName] = this._defaults[oldName];
694
- delete this._defaults[oldName];
695
- }
696
- if (this._btrees[oldName]) {
697
- this._btrees[newName] = this._btrees[oldName];
698
- delete this._btrees[oldName];
699
- }
700
- for (const row of this._rows) {
701
- row[newName] = row[oldName];
702
- delete row[oldName];
703
- }
704
- if (this._primaryKey === oldName) this._primaryKey = newName;
705
- if (this._autoIncrementField === oldName) this._autoIncrementField = newName;
706
- this._dirty = true;
707
- this._db._markDirty(this._name);
708
- return this;
709
- }
710
-
711
- modifyColumn(field, definition) {
712
- if (!this._schema[field]) throw createError('ER_BAD_FIELD_ERROR', field, this._name);
713
- this._schema[field] = { ...this._schema[field], ...definition };
714
- if (definition.default !== undefined) this._defaults[field] = definition.default;
715
- this._checkConstraints = this._checkConstraints.filter(c => c.field !== field);
716
- if (definition.check) this._checkConstraints.push({ field, fn: definition.check });
717
- if (definition.primaryKey || definition.unique) {
718
- if (!this._btrees[field]) this._btrees[field] = new BTree(64, true);
719
- this._rebuildBTree(field);
720
- }
721
- this._dirty = true;
722
- this._db._markDirty(this._name);
723
- return this;
724
- }
725
-
726
- describe() {
727
- return {
728
- name: this._name,
729
- columns: { ...this._schema },
730
- primaryKey: this._primaryKey,
731
- rowCount: this._getVisibleRows().length,
732
- btreeIndexes: Object.keys(this._btrees),
733
- hashIndexes: Object.keys(this._indexes),
734
- softDelete: this._softDelete,
735
- foreignKeys: this._foreignKeys.map(fk => ({ field: fk.field, table: fk.table }))
736
- };
737
- }
738
-
739
- selectInto(newTableName, newSchema, query = {}) {
740
- const rows = this._applyFilterOptimized(this._getVisibleRows(), query);
741
- this._db.createTable(newTableName, newSchema);
742
- const newTable = this._db._tables[newTableName];
743
- for (const row of rows) {
744
- newTable.insert(row);
745
- }
746
- return newTable;
747
- }
748
-
749
- textSearch(field, terms) {
750
- const searchTerms = Array.isArray(terms) ? terms : terms.split(/\s+/);
751
- const rows = this._getVisibleRows();
752
- const scored = [];
753
- for (const row of rows) {
754
- const value = String(row[field] || '').toLowerCase();
755
- let score = 0;
756
- for (const term of searchTerms) {
757
- const t = term.toLowerCase();
758
- const idx = value.indexOf(t);
759
- if (idx !== -1) {
760
- score += 10 + (idx === 0 ? 5 : 0) + (value === t ? 20 : 0)
761
- + (value.length - idx) / value.length * 5;
762
- }
763
- }
764
- if (score > 0) scored.push({ ...row, _score: score });
765
- }
766
- return scored.sort((a, b) => b._score - a._score);
767
- }
768
-
769
- // ============================================================
770
- // 索引
771
- // ============================================================
772
-
773
- createIndex(field) {
774
- this._indexes[field] = new Map();
775
- for (let i = 0; i < this._rows.length; i++) {
776
- const value = this._rows[i][field];
777
- if (value !== undefined) {
778
- if (!this._indexes[field].has(value)) {
779
- this._indexes[field].set(value, []);
780
- }
781
- this._indexes[field].get(value).push(i);
782
- }
783
- }
784
- return this._indexes[field];
785
- }
786
-
787
- dropIndex(field) {
788
- delete this._indexes[field];
789
- delete this._btrees[field];
790
- }
791
-
792
- /**
793
- * 创建 B-Tree 索引(O(log n) 查询)
794
- */
795
- createBTreeIndex(field, unique = false) {
796
- this._btrees[field] = new BTree(64, unique);
797
- this._rebuildBTree(field);
798
- return this._btrees[field];
799
- }
800
-
801
- _rebuildBTree(field) {
802
- if (this._btrees[field]) {
803
- this._btrees[field].clear();
804
- for (let i = 0; i < this._rows.length; i++) {
805
- const val = this._rows[i][field];
806
- if (val !== undefined && val !== null) {
807
- this._btrees[field].insert(val, i);
808
- }
809
- }
810
- }
811
- }
812
-
813
- _ensureBTrees() {
814
- if (!this._btreesDirty) return;
815
- this._btreesDirty = false;
816
- for (const field of Object.keys(this._btrees)) {
817
- this._btrees[field].clear();
818
- }
819
- for (let i = 0; i < this._rows.length; i++) {
820
- const row = this._rows[i];
821
- for (const [field, tree] of Object.entries(this._btrees)) {
822
- const v = row[field];
823
- if (v !== undefined && v !== null) tree.insert(v, i);
824
- }
825
- }
826
- }
827
-
828
- _rebuildAllBTrees() {
829
- for (const field of Object.keys(this._btrees)) {
830
- this._rebuildBTree(field);
831
- }
832
- }
833
-
834
- // ============================================================
835
- // 事件钩子
836
- // ============================================================
837
-
838
- on(event, callback) {
839
- if (this._hooks[event]) {
840
- this._hooks[event].push(callback);
841
- }
842
- return this;
843
- }
844
-
845
- off(event, callback) {
846
- if (this._hooks[event]) {
847
- this._hooks[event] = this._hooks[event].filter(cb => cb !== callback);
848
- }
849
- return this;
850
- }
851
-
852
- // ============================================================
853
- // 内部: 过滤器(B-Tree 加速)
854
- // ============================================================
855
-
856
- _applyFilterOptimized(rows, query) {
857
- this._ensureBTrees();
858
- // 尝试用 B-Tree 加速
859
- for (const [field, condition] of Object.entries(query)) {
860
- if (field === '$or' || field === '$and' || field === '$not') continue;
861
- // 等值查询用 B-Tree
862
- if (typeof condition !== 'object' || condition === null || Array.isArray(condition) || condition instanceof RegExp) {
863
- if (this._btrees[field]) {
864
- const indices = this._btrees[field].search(condition);
865
- if (indices.length > 0) {
866
- const subset = indices.map(i => this._rows[i]).filter(r => r);
867
- return this._applyFilter(subset, query);
868
- }
869
- return [];
870
- }
871
- if (this._indexes[field] && this._indexes[field].has(condition)) {
872
- const indices = this._indexes[field].get(condition);
873
- const subset = indices.map(i => this._rows[i]).filter(r => r);
874
- return this._applyFilter(subset, query);
875
- }
876
- if (this._indexes[field]) return [];
877
- }
878
- // 范围查询用 B-Tree
879
- if (condition && typeof condition === 'object') {
880
- if (condition.$gt !== undefined && condition.$lt !== undefined && this._btrees[field]) {
881
- const indices = this._btrees[field].range(condition.$gt, condition.$lt);
882
- const subset = indices.map(i => this._rows[i]).filter(r => r);
883
- return this._applyFilter(subset, query);
884
- }
885
- if (condition.$gte !== undefined && condition.$lte !== undefined && this._btrees[field]) {
886
- const indices = this._btrees[field].range(condition.$gte, condition.$lte);
887
- const subset = indices.map(i => this._rows[i]).filter(r => r);
888
- return this._applyFilter(subset, query);
889
- }
890
- }
891
- }
892
- return this._applyFilter(rows, query);
893
- }
894
-
895
- _applyFilter(rows, query) {
896
- const result = [];
897
- for (let i = 0; i < rows.length; i++) {
898
- if (this._matchRow(rows[i], query)) {
899
- result.push(rows[i]);
900
- }
901
- }
902
- return result;
903
- }
904
-
905
- _matchRow(row, query) {
906
- for (const [field, condition] of Object.entries(query)) {
907
- if (field === '$or') {
908
- if (!Array.isArray(condition) || !condition.some(sub => this._matchRow(row, sub))) return false;
909
- continue;
910
- }
911
- if (field === '$and') {
912
- if (!Array.isArray(condition) || !condition.every(sub => this._matchRow(row, sub))) return false;
913
- continue;
914
- }
915
- if (field === '$not') {
916
- if (this._matchRow(row, condition)) return false;
917
- continue;
918
- }
919
-
920
- const value = this._resolvePath(row, field);
921
-
922
- if (condition !== null && typeof condition === 'object' && !Array.isArray(condition) && !(condition instanceof RegExp)) {
923
- const keys = Object.keys(condition);
924
- const isOperator = keys.every(k => k.startsWith('$'));
925
- if (isOperator && keys.length > 0) {
926
- for (const [op, target] of Object.entries(condition)) {
927
- if (!this._matchOperator(value, op, target)) return false;
928
- }
929
- } else {
930
- if (value !== null && typeof value === 'object') {
931
- if (!this._matchRow(value, condition)) return false;
932
- } else {
933
- return false;
934
- }
935
- }
936
- } else {
937
- if (value !== condition) return false;
938
- }
939
- }
940
- return true;
941
- }
942
-
943
- _matchOperator(value, op, target) {
944
- switch (op) {
945
- case '$eq': return value === target;
946
- case '$ne': return value !== target;
947
- case '$gt': return value > target;
948
- case '$gte': return value >= target;
949
- case '$lt': return value < target;
950
- case '$lte': return value <= target;
951
- case '$in': return Array.isArray(target) && target.includes(value);
952
- case '$nin': return Array.isArray(target) && !target.includes(value);
953
- case '$like':
954
- if (typeof value !== 'string' || typeof target !== 'string') return false;
955
- {
956
- const escaped = target
957
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
958
- .replace(/%/g, '.*')
959
- .replace(/_/g, '.');
960
- return new RegExp('^' + escaped + '$', 'i').test(value);
961
- }
962
- case '$regex':
963
- if (target instanceof RegExp) return target.test(String(value));
964
- if (!isSafeRegex(target)) throw createError('ER_BAD_REGEX', String(target));
965
- return new RegExp(target).test(String(value));
966
- case '$exists':
967
- return target ? value !== undefined : value === undefined;
968
- case '$between':
969
- return Array.isArray(target) && target.length === 2 && value >= target[0] && value <= target[1];
970
- case '$and':
971
- return Array.isArray(target) && target.every(sub => this._matchRow({ _val: value }, { _val: sub }));
972
- case '$or':
973
- return Array.isArray(target) && target.some(sub => this._matchRow({ _val: value }, { _val: sub }));
974
- case '$not':
975
- return !this._matchRow({ _val: value }, { _val: target });
976
- case '$contains':
977
- return Array.isArray(value) && value.includes(target);
978
- case '$type':
979
- return typeof value === target;
980
- case '$size':
981
- return Array.isArray(value) && value.length === target;
982
- case '$elemMatch':
983
- return Array.isArray(value) && value.some(el =>
984
- typeof el === 'object' ? this._matchRow(el, target) : el === target);
985
- case '$isNull':
986
- return target ? value === null || value === undefined : value !== null && value !== undefined;
987
- case '$startsWith':
988
- return typeof value === 'string' && value.startsWith(target);
989
- case '$endsWith':
990
- return typeof value === 'string' && value.endsWith(target);
991
- case '$search':
992
- if (typeof value !== 'string') return false;
993
- const terms = Array.isArray(target) ? target : [target];
994
- return terms.every(t => value.toLowerCase().includes(t.toLowerCase()));
995
- case '$textSearch':
996
- if (typeof value !== 'string') return false;
997
- const sTerms = Array.isArray(target) ? target : target.split(/\s+/);
998
- return sTerms.some(t => value.toLowerCase().includes(t.toLowerCase()));
999
- case '$inSub':
1000
- if (!target || !target.table) return false;
1001
- const subRows = target.table._applyFilter(target.table._rows, target.query || {});
1002
- return subRows.some(r => r[target.field] === value);
1003
- case '$notInSub':
1004
- if (!target || !target.table) return false;
1005
- const subRows2 = target.table._applyFilter(target.table._rows, target.query || {});
1006
- return !subRows2.some(r => r[target.field] === value);
1007
- case '$expr':
1008
- if (typeof target === 'function') return target(value);
1009
- return false;
1010
- default:
1011
- return false;
1012
- }
1013
- }
1014
-
1015
- _resolvePath(row, field) {
1016
- if (field.includes('.') && !field.startsWith('$')) {
1017
- const parts = field.split('.');
1018
- let value = row;
1019
- for (const part of parts) {
1020
- if (value === null || value === undefined) return undefined;
1021
- value = value[part];
1022
- }
1023
- return value;
1024
- }
1025
- return row[field];
1026
- }
1027
-
1028
- // ============================================================
1029
- // 内部: 约束校验
1030
- // ============================================================
1031
-
1032
- _validateConstraints(data, excludeIndex, uniqueTracker) {
1033
- for (const [field, def] of Object.entries(this._schema)) {
1034
- if (field === '_softDelete') continue;
1035
-
1036
- // NOT NULL
1037
- if (def.required && (data[field] === undefined || data[field] === null)
1038
- && !(def.autoIncrement && data[field] === undefined)) {
1039
- throw createError('ER_BAD_NULL_ERROR', field);
1040
- }
1041
-
1042
- // PRIMARY KEY 唯一性
1043
- if ((def.primaryKey || def.primary === true) && data[field] !== undefined && data[field] !== null) {
1044
- let dup = false;
1045
- if (uniqueTracker && uniqueTracker[field]) {
1046
- dup = uniqueTracker[field].has(data[field]);
1047
- } else if (this._pkIndex) {
1048
- const idx = this._pkIndex.get(data[field]);
1049
- dup = idx !== undefined && idx !== null && idx !== excludeIndex;
1050
- } else {
1051
- const idx = this._rows.findIndex(r => r[field] === data[field]);
1052
- dup = idx !== -1 && idx !== excludeIndex;
1053
- }
1054
- if (dup) {
1055
- throw createError('ER_DUP_ENTRY', String(data[field]), field);
1056
- }
1057
- }
1058
-
1059
- // UNIQUE(B-Tree 优化 O(log n))
1060
- if (def.unique && data[field] !== undefined) {
1061
- if (uniqueTracker && uniqueTracker[field]) {
1062
- if (uniqueTracker[field].has(data[field])) {
1063
- throw createError('ER_DUP_ENTRY', String(data[field]), field);
1064
- }
1065
- uniqueTracker[field].add(data[field]);
1066
- } else if (this._btrees[field]) {
1067
- const found = this._btrees[field].search(data[field]);
1068
- if (found.length > 0) {
1069
- for (const idx of found) {
1070
- if (idx !== excludeIndex && this._rows[idx] && this._rows[idx][field] === data[field]) {
1071
- throw createError('ER_DUP_ENTRY', String(data[field]), field);
1072
- }
1073
- }
1074
- }
1075
- } else {
1076
- for (let i = 0; i < this._rows.length; i++) {
1077
- if (i === excludeIndex) continue;
1078
- if (this._rows[i][field] === data[field]) {
1079
- throw createError('ER_DUP_ENTRY', String(data[field]), field);
1080
- }
1081
- }
1082
- }
1083
- }
1084
-
1085
- // CHECK
1086
- for (const { field: cf, fn } of this._checkConstraints) {
1087
- if (cf === field && data[field] !== undefined && !fn(data[field], data)) {
1088
- throw createError('ER_CHECK_CONSTRAINT', this._name + '.' + field);
1089
- }
1090
- }
1091
-
1092
- // 长度限制
1093
- if (def.length && typeof data[field] === 'string' && data[field].length > def.length) {
1094
- throw createError('ER_DATA_TOO_LONG', field);
1095
- }
1096
-
1097
- // 数值范围
1098
- if (def.type === 'integer' && data[field] !== undefined && data[field] !== null) {
1099
- const v = Number(data[field]);
1100
- if (def.min !== undefined && v < def.min) throw createError('ER_OUT_OF_RANGE', field);
1101
- if (def.max !== undefined && v > def.max) throw createError('ER_OUT_OF_RANGE', field);
1102
- }
1103
- }
1104
-
1105
- // FOREIGN KEY 校验
1106
- for (const fk of this._foreignKeys) {
1107
- if (data[fk.field] !== undefined && data[fk.field] !== null) {
1108
- const refTable = this._resolveTable(fk.table);
1109
- if (refTable) {
1110
- const exists = refTable.findOne({ [fk.refField]: data[fk.field] });
1111
- if (!exists) {
1112
- throw createError('ER_NO_REFERENCED_ROW');
1113
- }
1114
- }
1115
- }
1116
- }
1117
- }
1118
-
1119
- _resolveTable(tableRef) {
1120
- if (typeof tableRef === 'string') {
1121
- return this._db._tables[tableRef] || this._db._attached[tableRef]?._tables[tableRef];
1122
- }
1123
- return tableRef;
1124
- }
1125
-
1126
- /**
1127
- * 级联外键操作:当删除当前表的行时,查找所有引用此表的子表
1128
- * 并执行 CASCADE 或 SET NULL
1129
- */
1130
- _cascadeForeignKeys(row) {
1131
- for (const [, table] of Object.entries(this._db._tables)) {
1132
- if (table._name === this._name) continue;
1133
- for (const fk of table._foreignKeys) {
1134
- const refTable = table._resolveTable(fk.table);
1135
- if (!refTable || refTable._name !== this._name) continue;
1136
-
1137
- // 检查子表行是否引用当前行
1138
- if (fk.onDelete === 'cascade') {
1139
- table.remove({ [fk.field]: row[fk.refField] });
1140
- } else if (fk.onDelete === 'set null') {
1141
- table.update({ [fk.field]: row[fk.refField] }, { [fk.field]: null });
1142
- }
1143
- }
1144
- }
1145
- }
1146
-
1147
- _validateDataTypes(data, partial = false) {
1148
- const result = { ...data };
1149
- for (const [field, type] of Object.entries(this._dateFields)) {
1150
- if (partial && data[field] === undefined) continue;
1151
- if (data[field] !== undefined && data[field] !== null) {
1152
- result[field] = validateDateType(data[field], type, field);
1153
- }
1154
- }
1155
- for (const [field, def] of Object.entries(this._schema)) {
1156
- if (field === '_softDelete') continue;
1157
- if (partial && data[field] === undefined) continue;
1158
- const v = data[field];
1159
- if (v === undefined || v === null) continue;
1160
- const t = String(def.type || '').toLowerCase();
1161
- if (t === 'integer' || t === 'int' || t === 'bigint') {
1162
- if (typeof v === 'boolean') throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1163
- if (typeof v === 'number') {
1164
- if (Number.isInteger(v)) result[field] = v;
1165
- else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1166
- } else if (typeof v === 'string' && v.trim() !== '' && Number.isInteger(Number(v))) {
1167
- result[field] = Number(v);
1168
- } else {
1169
- throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1170
- }
1171
- } else if (t === 'float' || t === 'double' || t === 'number' || t === 'real') {
1172
- if (typeof v === 'boolean') throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1173
- if (typeof v === 'number') result[field] = v;
1174
- else if (typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v))) result[field] = Number(v);
1175
- else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1176
- } else if (t === 'boolean' || t === 'bool') {
1177
- if (typeof v === 'boolean') result[field] = v;
1178
- else if (v === 0 || v === 1) result[field] = !!v;
1179
- else if (typeof v === 'string' && ['true', 'false', '1', '0'].includes(v.toLowerCase())) {
1180
- result[field] = v.toLowerCase() === 'true' || v === '1';
1181
- } else {
1182
- throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1183
- }
1184
- } else if (t === 'string' || t === 'text' || t === 'varchar' || t === 'char') {
1185
- if (typeof v === 'string') result[field] = v;
1186
- else if (typeof v === 'number' || typeof v === 'boolean' || v instanceof Date) result[field] = String(v);
1187
- else if (Buffer.isBuffer(v)) result[field] = v.toString('utf8');
1188
- else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1189
- }
1190
- }
1191
- return result;
1192
- }
1193
-
1194
- // ============================================================
1195
- // 内部: 辅助
1196
- // ============================================================
1197
-
1198
- _applyDefaults(data) {
1199
- const result = { ...data };
1200
- for (const [field, defaultValue] of Object.entries(this._defaults)) {
1201
- if (result[field] === undefined) {
1202
- result[field] = typeof defaultValue === 'function'
1203
- ? defaultValue()
1204
- : (defaultValue === 'CURRENT_TIMESTAMP' ? now() : defaultValue);
1205
- }
1206
- }
1207
- return result;
1208
- }
1209
-
1210
- _applyComputed(data) {
1211
- for (const [field, fn] of Object.entries(this._computedFields)) {
1212
- data[field] = fn(data);
1213
- }
1214
- return data;
1215
- }
1216
-
1217
- _getVisibleRows() {
1218
- if (this._softDelete) return this._rows.filter(r => !r._deleted);
1219
- return this._rows;
1220
- }
1221
-
1222
- _addToIndexes(rowIndex, data) {
1223
- if (this._pkIndex && this._primaryKey) {
1224
- const pkVal = data[this._primaryKey];
1225
- if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, rowIndex);
1226
- }
1227
- for (const field of Object.keys(this._indexes)) {
1228
- const value = data[field];
1229
- if (value !== undefined) {
1230
- if (!this._indexes[field].has(value)) {
1231
- this._indexes[field].set(value, []);
1232
- }
1233
- this._indexes[field].get(value).push(rowIndex);
1234
- }
1235
- }
1236
- for (const [field, tree] of Object.entries(this._btrees)) {
1237
- const value = data[field];
1238
- if (value !== undefined && value !== null) {
1239
- tree.insert(value, rowIndex);
1240
- }
1241
- }
1242
- }
1243
-
1244
- _removeFromIndexes(rowIndex, data) {
1245
- if (this._pkIndex && this._primaryKey) {
1246
- this._pkIndex.delete(data[this._primaryKey]);
1247
- }
1248
- for (const field of Object.keys(this._indexes)) {
1249
- const value = data[field];
1250
- if (value !== undefined && this._indexes[field].has(value)) {
1251
- const positions = this._indexes[field].get(value).filter(p => p !== rowIndex);
1252
- if (positions.length === 0) {
1253
- this._indexes[field].delete(value);
1254
- } else {
1255
- this._indexes[field].set(value, positions);
1256
- }
1257
- }
1258
- }
1259
- for (const [field, tree] of Object.entries(this._btrees)) {
1260
- const value = data[field];
1261
- if (value !== undefined && value !== null) {
1262
- tree.remove(value, rowIndex);
1263
- }
1264
- }
1265
- }
1266
-
1267
- _rebuildPKIndex() {
1268
- if (!this._pkIndex || !this._primaryKey) return;
1269
- this._pkIndex.clear();
1270
- for (let i = 0; i < this._rows.length; i++) {
1271
- const pkVal = this._rows[i][this._primaryKey];
1272
- if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, i);
1273
- }
1274
- }
1275
-
1276
- _adjustBTreeAfterRemove(removedIndex) {
1277
- this._rebuildPKIndex();
1278
- for (const field of Object.keys(this._btrees)) {
1279
- this._rebuildBTree(field);
1280
- }
1281
- }
1282
-
1283
- _loadRows(rows) {
1284
- this._rows = rows;
1285
- if (this._autoIncrementField) {
1286
- for (const row of rows) {
1287
- const val = row[this._autoIncrementField];
1288
- if (typeof val === 'number' && val > this._autoIncrement) {
1289
- this._autoIncrement = val;
1290
- }
1291
- }
1292
- }
1293
- this._rebuildPKIndex();
1294
- this._rebuildAllBTrees();
1295
- for (const field of Object.keys(this._indexes)) {
1296
- this.createIndex(field);
1297
- }
1298
- }
1299
-
1300
- toJSON() {
1301
- return this._rows;
1302
- }
1303
- }
1304
-
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * JSQL Table — 表操作引擎 v2.0
4
+ * B-Tree 自动索引、日期类型、错误码、外键 CASCADE、长度/精度校验
5
+ */
6
+
7
+ const Query = require('./query');
8
+ const BTree = require('./btree');
9
+ const { validateDateType, now } = require('./date-types');
10
+ const { createError } = require('./errors');
11
+
12
+ function isSafeRegex(pattern) {
13
+ if (typeof pattern !== 'string') return false;
14
+ if (pattern.length > 256) return false;
15
+ const groups = [];
16
+ let i = 0;
17
+ const n = pattern.length;
18
+ while (i < n) {
19
+ const c = pattern[i];
20
+ if (c === '\\') { i += 2; continue; }
21
+ if (c === '[') {
22
+ while (i < n && pattern[i] !== ']') i++;
23
+ i++;
24
+ continue;
25
+ }
26
+ if (c === '(') {
27
+ const nxt = pattern[i + 1];
28
+ if (nxt === '?') {
29
+ const t = pattern[i + 2];
30
+ if (t === ':') {
31
+ groups.push({ hasQuant: false, hasAlt: false });
32
+ i += 3;
33
+ continue;
34
+ }
35
+ if (t === '=' || t === '!') {
36
+ let depth = 1, j = i + 3;
37
+ for (; j < n && depth > 0; j++) {
38
+ if (pattern[j] === '\\') { j++; continue; }
39
+ if (pattern[j] === '(') depth++;
40
+ else if (pattern[j] === ')') depth--;
41
+ }
42
+ i = j;
43
+ continue;
44
+ }
45
+ if (t === '<') {
46
+ const u = pattern[i + 3];
47
+ if (u === '=' || u === '!') {
48
+ let depth = 1, j = i + 4;
49
+ for (; j < n && depth > 0; j++) {
50
+ if (pattern[j] === '\\') { j++; continue; }
51
+ if (pattern[j] === '(') depth++;
52
+ else if (pattern[j] === ')') depth--;
53
+ }
54
+ i = j;
55
+ continue;
56
+ }
57
+ const gt = pattern.indexOf('>', i + 3);
58
+ if (gt === -1) return false;
59
+ groups.push({ hasQuant: false, hasAlt: false });
60
+ i = gt + 1;
61
+ continue;
62
+ }
63
+ return false;
64
+ }
65
+ groups.push({ hasQuant: false, hasAlt: false });
66
+ i++;
67
+ continue;
68
+ }
69
+ if (c === ')') {
70
+ const g = groups.pop();
71
+ if (!g) return false;
72
+ let qi = i + 1;
73
+ let quant = null;
74
+ if (pattern[qi] === '+' || pattern[qi] === '*' || pattern[qi] === '?') {
75
+ quant = pattern[qi];
76
+ } else if (pattern[qi] === '{') {
77
+ const close = pattern.indexOf('}', qi);
78
+ if (close === -1) return false;
79
+ quant = pattern.slice(qi, close + 1);
80
+ qi = close + 1;
81
+ }
82
+ if (quant && (g.hasQuant || g.hasAlt)) return false;
83
+ i = qi;
84
+ continue;
85
+ }
86
+ if (c === '+' || c === '*' || c === '?') {
87
+ if (groups.length > 0) groups[groups.length - 1].hasQuant = true;
88
+ i++;
89
+ continue;
90
+ }
91
+ if (c === '{' && /[0-9]/.test(pattern[i + 1] || '')) {
92
+ const close = pattern.indexOf('}', i);
93
+ if (close === -1) return false;
94
+ if (groups.length > 0) groups[groups.length - 1].hasQuant = true;
95
+ i = close + 1;
96
+ continue;
97
+ }
98
+ if (c === '|') {
99
+ if (groups.length > 0) groups[groups.length - 1].hasAlt = true;
100
+ i++;
101
+ continue;
102
+ }
103
+ i++;
104
+ }
105
+ return groups.length === 0;
106
+ }
107
+
108
+ class Table {
109
+ constructor(name, schema, db) {
110
+ this._name = name;
111
+ this._schema = schema;
112
+ this._db = db;
113
+ this._rows = [];
114
+ this._indexes = {}; // 旧版 hash 索引(兼容)
115
+ this._btrees = {}; // B-Tree 索引 { fieldName: BTree }
116
+ this._autoIncrement = 0;
117
+ this._dirty = false;
118
+ this._hooks = {
119
+ beforeInsert: [], afterInsert: [],
120
+ beforeUpdate: [], afterUpdate: [],
121
+ beforeRemove: [], afterRemove: []
122
+ };
123
+ this._softDelete = false;
124
+ this._computedFields = {};
125
+ this._btreesDirty = false;
126
+
127
+ this._primaryKey = null;
128
+ this._pkIndex = null;
129
+ this._autoIncrementField = null;
130
+ this._foreignKeys = [];
131
+ this._checkConstraints = [];
132
+ this._defaults = {};
133
+ this._dateFields = {}; // { fieldName: 'date'|'datetime'|'timestamp' }
134
+
135
+ for (const [field, def] of Object.entries(schema)) {
136
+ const isPk = !!(def.primaryKey || def.primary === true);
137
+ if (isPk) this._primaryKey = field;
138
+ if (def.autoIncrement) {
139
+ this._autoIncrementField = field;
140
+ if (isPk) this._primaryKey = field;
141
+ }
142
+ if (def.default !== undefined) {
143
+ this._defaults[field] = def.default;
144
+ }
145
+ if (def.check) {
146
+ this._checkConstraints.push({ field, fn: def.check });
147
+ }
148
+ if (def.foreignKey) {
149
+ this._foreignKeys.push({
150
+ field,
151
+ table: def.foreignKey.table,
152
+ refField: def.foreignKey.field || 'id',
153
+ onDelete: def.foreignKey.onDelete || 'restrict',
154
+ onUpdate: def.foreignKey.onUpdate || 'restrict'
155
+ });
156
+ }
157
+ if (def.computed) {
158
+ this._computedFields[field] = def.computed;
159
+ }
160
+ // 日期类型
161
+ if (['date', 'datetime', 'timestamp', 'time'].includes(def.type)) {
162
+ this._dateFields[field] = def.type;
163
+ }
164
+ }
165
+
166
+ if (schema._softDelete) {
167
+ this._softDelete = true;
168
+ }
169
+
170
+ this._pkIndex = this._primaryKey ? new Map() : null;
171
+ this._cachedSchemaFields = Object.keys(schema).filter(f => f !== '_softDelete');
172
+ this._cachedDateFields = Object.keys(this._dateFields);
173
+ this._cachedUniqueFields = this._cachedSchemaFields.filter(f => schema[f].unique);
174
+ this._cachedRequiredFields = this._cachedSchemaFields.filter(f => schema[f].required);
175
+ this._autoCreateBTrees();
176
+ }
177
+
178
+ _autoCreateBTrees() {
179
+ for (const [field, def] of Object.entries(this._schema)) {
180
+ if (field === '_softDelete') continue;
181
+ if (def.primaryKey || def.primary === true || def.unique) {
182
+ this._btrees[field] = new BTree(64, true);
183
+ }
184
+ }
185
+ }
186
+
187
+ // ============================================================
188
+ // 插入
189
+ // ============================================================
190
+
191
+ insert(data) {
192
+ for (const hook of this._hooks.beforeInsert) {
193
+ data = hook({ ...data }) || data;
194
+ }
195
+
196
+ data = this._applyDefaults(data);
197
+ data = this._applyComputed(data);
198
+ data = this._validateDataTypes(data);
199
+ this._validateConstraints(data, -1);
200
+
201
+ if (this._autoIncrementField && data[this._autoIncrementField] === undefined) {
202
+ this._autoIncrement++;
203
+ data[this._autoIncrementField] = this._autoIncrement;
204
+ } else if (this._autoIncrementField && data[this._autoIncrementField] > this._autoIncrement) {
205
+ this._autoIncrement = data[this._autoIncrementField];
206
+ }
207
+
208
+ const rowIndex = this._rows.length;
209
+ this._rows.push({ ...data });
210
+ this._addToIndexes(rowIndex, data);
211
+ this._dirty = true;
212
+ this._db._markDirty(this._name);
213
+
214
+ for (const hook of this._hooks.afterInsert) {
215
+ hook(data);
216
+ }
217
+ this._db._emitChange('insert', this._name, data);
218
+
219
+ return data;
220
+ }
221
+
222
+ insertMany(items) {
223
+ if (items.length === 0) return [];
224
+ for (const hook of this._hooks.beforeInsert) {
225
+ const mapped = new Array(items.length);
226
+ for (let i = 0; i < items.length; i++) mapped[i] = hook({ ...items[i] }) || items[i];
227
+ items = mapped;
228
+ }
229
+ const N = items.length;
230
+ const uniqueTracker = {};
231
+ for (const field of this._cachedUniqueFields) {
232
+ uniqueTracker[field] = new Set();
233
+ for (const row of this._rows) {
234
+ if (row[field] !== undefined && row[field] !== null) uniqueTracker[field].add(row[field]);
235
+ }
236
+ }
237
+ const dateFields = this._cachedDateFields;
238
+ const hasDateFields = dateFields.length > 0;
239
+ const autoIncField = this._autoIncrementField;
240
+ const defaults = this._defaults;
241
+ const computedFields = this._computedFields;
242
+ const schema = this._schema;
243
+ const checkConstraints = this._checkConstraints;
244
+ const foreignKeys = this._foreignKeys;
245
+ // 批量预分配自增 ID 范围:先扫描显式提供的最大值,一次性推进计数器,
246
+ // 再在循环内用本地序号递增,避免批量内重复读取/写入同一计数器字段。
247
+ const baseAutoInc = this._autoIncrement;
248
+ if (autoIncField) {
249
+ let maxExplicit = 0;
250
+ for (const it of items) {
251
+ const v = it[autoIncField];
252
+ if (v !== undefined && v !== null) {
253
+ const n = Number(v);
254
+ if (!isNaN(n) && n > maxExplicit) maxExplicit = n;
255
+ }
256
+ }
257
+ if (maxExplicit > baseAutoInc) {
258
+ this._autoIncrement = maxExplicit;
259
+ }
260
+ }
261
+ let autoIncSeq = 0;
262
+ for (let i = 0; i < N; i++) {
263
+ let data = items[i];
264
+ for (const [f, dv] of Object.entries(defaults)) {
265
+ if (data[f] === undefined) data[f] = typeof dv === 'function' ? dv() : dv === 'CURRENT_TIMESTAMP' ? now() : dv;
266
+ }
267
+ for (const f of Object.keys(computedFields)) data[f] = computedFields[f](data);
268
+ if (hasDateFields) {
269
+ for (const f of dateFields) {
270
+ if (data[f] !== undefined && data[f] !== null) data[f] = validateDateType(data[f], this._dateFields[f], f);
271
+ }
272
+ }
273
+ for (const field of this._cachedSchemaFields) {
274
+ const def = schema[field];
275
+ if (def.required && (data[field] === undefined || data[field] === null)
276
+ && !(def.autoIncrement && data[field] === undefined)) throw createError('ER_BAD_NULL_ERROR', field);
277
+ if (def.unique && data[field] !== undefined) {
278
+ if (uniqueTracker[field].has(data[field])) throw createError('ER_DUP_ENTRY', String(data[field]), field);
279
+ uniqueTracker[field].add(data[field]);
280
+ }
281
+ if (def.length && typeof data[field] === 'string' && data[field].length > def.length) throw createError('ER_DATA_TOO_LONG', field);
282
+ if (def.type === 'integer' || def.type === 'number') {
283
+ const v = def.type === 'integer' ? Number(data[field]) : data[field];
284
+ if (data[field] !== undefined && data[field] !== null && def.min !== undefined && v < def.min) throw createError('ER_OUT_OF_RANGE', field);
285
+ if (data[field] !== undefined && data[field] !== null && def.max !== undefined && v > def.max) throw createError('ER_OUT_OF_RANGE', field);
286
+ }
287
+ if (def.check && data[field] !== undefined && !def.check(data[field], data)) throw createError('ER_CHECK_CONSTRAINT', this._name + '.' + field);
288
+ }
289
+ for (const fk of foreignKeys) {
290
+ if (data[fk.field] !== undefined && data[fk.field] !== null) {
291
+ const refTable = this._resolveTable(fk.table);
292
+ if (refTable) {
293
+ const exists = refTable.findOne({ [fk.refField]: data[fk.field] });
294
+ if (!exists) throw createError('ER_NO_REFERENCED_ROW');
295
+ }
296
+ }
297
+ }
298
+ if (autoIncField && data[autoIncField] === undefined) {
299
+ autoIncSeq++;
300
+ data[autoIncField] = baseAutoInc + autoIncSeq;
301
+ this._autoIncrement = data[autoIncField];
302
+ } else if (autoIncField && data[autoIncField] > this._autoIncrement) {
303
+ this._autoIncrement = data[autoIncField];
304
+ }
305
+ const rowIdx = this._rows.length;
306
+ this._rows.push(Object.assign({}, data));
307
+ if (this._pkIndex && this._primaryKey) {
308
+ const pkVal = data[this._primaryKey];
309
+ if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, rowIdx);
310
+ }
311
+ for (const [field, tree] of Object.entries(this._btrees)) {
312
+ const val = data[field];
313
+ if (val !== undefined && val !== null) tree.insert(val, rowIdx);
314
+ }
315
+ items[i] = data;
316
+ }
317
+ this._dirty = true;
318
+ this._db._markDirty(this._name);
319
+ for (const hook of this._hooks.afterInsert) {
320
+ for (let i = 0; i < N; i++) hook(items[i]);
321
+ }
322
+ this._db._emitChange('insertMany', this._name, { count: N });
323
+ return items;
324
+ }
325
+
326
+ upsert(data) {
327
+ if (this._primaryKey && data[this._primaryKey] !== undefined) {
328
+ const existing = this.findById(data[this._primaryKey]);
329
+ if (existing) {
330
+ this.updateById(data[this._primaryKey], data);
331
+ return this.findById(data[this._primaryKey]);
332
+ }
333
+ }
334
+ return this.insert(data);
335
+ }
336
+
337
+ // ============================================================
338
+ // 查询(支持 B-Tree 加速)
339
+ // ============================================================
340
+
341
+ find(query = {}) {
342
+ let rows = this._getVisibleRows();
343
+ if (Object.keys(query).length > 0) {
344
+ rows = this._applyFilterOptimized(rows, query);
345
+ }
346
+ return rows.map(r => ({ ...r }));
347
+ }
348
+
349
+ findOne(query = {}) {
350
+ const rows = this.find(query);
351
+ return rows.length > 0 ? rows[0] : null;
352
+ }
353
+
354
+ findById(id) {
355
+ if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
356
+ // Hash 索引 O(1)
357
+ if (this._pkIndex) {
358
+ const rowIndex = this._pkIndex.get(id);
359
+ if (rowIndex !== undefined) {
360
+ const row = this._rows[rowIndex];
361
+ if (row && !row._deleted) return { ...row };
362
+ }
363
+ return null;
364
+ }
365
+ this._ensureBTrees();
366
+ if (this._btrees[this._primaryKey]) {
367
+ const indices = this._btrees[this._primaryKey].search(id);
368
+ if (indices.length > 0) {
369
+ const row = this._rows[indices[0]];
370
+ if (row && !row._deleted) return { ...row };
371
+ }
372
+ return null;
373
+ }
374
+ return this.findOne({ [this._primaryKey]: id });
375
+ }
376
+
377
+ findAll() {
378
+ return this._getVisibleRows().map(r => ({ ...r }));
379
+ }
380
+
381
+ count(query = {}) {
382
+ if (Object.keys(query).length === 0) return this._getVisibleRows().length;
383
+ return this._applyFilterOptimized(this._getVisibleRows(), query).length;
384
+ }
385
+
386
+ where(conditions) {
387
+ return new Query(this, conditions);
388
+ }
389
+
390
+ // ============================================================
391
+ // 更新
392
+ // ============================================================
393
+
394
+ update(query, updates) {
395
+ for (const hook of this._hooks.beforeUpdate) {
396
+ updates = hook(query, { ...updates }) || updates;
397
+ }
398
+
399
+ updates = this._validateDataTypes(updates, true);
400
+ const matched = this._applyFilterOptimized(this._getVisibleRows(), query);
401
+ let count = 0;
402
+
403
+ for (const row of matched) {
404
+ const index = this._rows.indexOf(row);
405
+ if (index === -1) continue;
406
+
407
+ const oldData = { ...this._rows[index] };
408
+ const merged = { ...this._rows[index], ...updates };
409
+ this._validateConstraints(merged, index);
410
+
411
+ this._removeFromIndexes(index, oldData);
412
+ Object.assign(this._rows[index], updates);
413
+ this._addToIndexes(index, this._rows[index]);
414
+ count++;
415
+ }
416
+
417
+ if (count > 0) {
418
+ this._dirty = true;
419
+ this._db._markDirty(this._name);
420
+ for (const hook of this._hooks.afterUpdate) {
421
+ hook(query, updates, count);
422
+ }
423
+ this._db._emitChange('update', this._name, { query, updates, count });
424
+ }
425
+ return count;
426
+ }
427
+
428
+ updateById(id, updates) {
429
+ if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
430
+ for (const hook of this._hooks.beforeUpdate) {
431
+ updates = hook({ [this._primaryKey]: id }, { ...updates }) || updates;
432
+ }
433
+ updates = this._validateDataTypes(updates, true);
434
+ let rowIndex;
435
+ if (this._pkIndex) {
436
+ rowIndex = this._pkIndex.get(id);
437
+ } else {
438
+ this._ensureBTrees();
439
+ if (this._btrees[this._primaryKey]) {
440
+ const indices = this._btrees[this._primaryKey].search(id);
441
+ rowIndex = indices.length > 0 ? indices[0] : undefined;
442
+ } else {
443
+ rowIndex = this._rows.findIndex(r => r[this._primaryKey] === id);
444
+ if (rowIndex === -1) rowIndex = undefined;
445
+ }
446
+ }
447
+ if (rowIndex === undefined) return 0;
448
+ const row = this._rows[rowIndex];
449
+ if (row && row._deleted) return 0;
450
+ const oldData = { ...row };
451
+ const merged = { ...row, ...updates };
452
+ this._validateConstraints(merged, rowIndex);
453
+ this._removeFromIndexes(rowIndex, oldData);
454
+ Object.assign(row, updates);
455
+ this._addToIndexes(rowIndex, row);
456
+ this._dirty = true;
457
+ this._db._markDirty(this._name);
458
+ for (const hook of this._hooks.afterUpdate) {
459
+ hook({ [this._primaryKey]: id }, updates, 1);
460
+ }
461
+ this._db._emitChange('update', this._name, { query: { [this._primaryKey]: id }, updates, count: 1 });
462
+ return 1;
463
+ }
464
+
465
+ // ============================================================
466
+ // 删除 & 外键 CASCADE
467
+ // ============================================================
468
+
469
+ remove(query = {}) {
470
+ for (const hook of this._hooks.beforeRemove) {
471
+ if (hook(query) === false) return 0;
472
+ }
473
+
474
+ if (Object.keys(query).length === 0) {
475
+ return this._truncate();
476
+ }
477
+
478
+ const toRemove = this._applyFilterOptimized(this._getVisibleRows(), query);
479
+ let count = 0;
480
+ const removeIndices = [];
481
+
482
+ for (const row of toRemove) {
483
+ const index = this._rows.indexOf(row);
484
+ if (index === -1) continue;
485
+ this._cascadeForeignKeys(row);
486
+ this._removeFromIndexes(index, row);
487
+ removeIndices.push(index);
488
+ count++;
489
+ }
490
+
491
+ if (count > 0) {
492
+ removeIndices.sort((a, b) => b - a);
493
+ for (const idx of removeIndices) {
494
+ const lastIdx = this._rows.length - 1;
495
+ if (idx < lastIdx) {
496
+ const lastRow = this._rows[lastIdx];
497
+ this._rows[idx] = lastRow;
498
+ if (this._pkIndex && this._primaryKey) {
499
+ this._pkIndex.set(lastRow[this._primaryKey], idx);
500
+ }
501
+ for (const [field, tree] of Object.entries(this._btrees)) {
502
+ const val = lastRow[field];
503
+ if (val !== undefined && val !== null) {
504
+ tree.remove(val, lastIdx);
505
+ tree.insert(val, idx);
506
+ }
507
+ }
508
+ for (const field of Object.keys(this._indexes)) {
509
+ const val = lastRow[field];
510
+ if (val !== undefined && this._indexes[field].has(val)) {
511
+ const positions = this._indexes[field].get(val);
512
+ const pi = positions.indexOf(lastIdx);
513
+ if (pi !== -1) positions[pi] = idx;
514
+ }
515
+ }
516
+ }
517
+ this._rows.pop();
518
+ }
519
+ this._dirty = true;
520
+ this._db._markDirty(this._name);
521
+ for (const hook of this._hooks.afterRemove) {
522
+ hook(query, count);
523
+ }
524
+ this._db._emitChange('remove', this._name, { query, count });
525
+ }
526
+ return count;
527
+ }
528
+
529
+ removeById(id) {
530
+ if (!this._primaryKey) throw createError('ER_PARSE_ERROR', 'Table has no primary key');
531
+ let rowIndex;
532
+ if (this._pkIndex) {
533
+ rowIndex = this._pkIndex.get(id);
534
+ } else {
535
+ this._ensureBTrees();
536
+ if (this._btrees[this._primaryKey]) {
537
+ const indices = this._btrees[this._primaryKey].search(id);
538
+ rowIndex = indices.length > 0 ? indices[0] : undefined;
539
+ } else {
540
+ rowIndex = this._rows.findIndex(r => r[this._primaryKey] === id);
541
+ if (rowIndex === -1) rowIndex = undefined;
542
+ }
543
+ }
544
+ if (rowIndex === undefined) return 0;
545
+ const row = this._rows[rowIndex];
546
+ if (!row || row._deleted) return 0;
547
+ this._cascadeForeignKeys(row);
548
+ this._removeFromIndexes(rowIndex, row);
549
+ const lastIdx = this._rows.length - 1;
550
+ if (rowIndex < lastIdx) {
551
+ const lastRow = this._rows[lastIdx];
552
+ this._rows[rowIndex] = lastRow;
553
+ if (this._pkIndex && this._primaryKey) {
554
+ this._pkIndex.set(lastRow[this._primaryKey], rowIndex);
555
+ }
556
+ for (const [field, tree] of Object.entries(this._btrees)) {
557
+ const val = lastRow[field];
558
+ if (val !== undefined && val !== null) {
559
+ tree.remove(val, lastIdx);
560
+ tree.insert(val, rowIndex);
561
+ }
562
+ }
563
+ for (const field of Object.keys(this._indexes)) {
564
+ const val = lastRow[field];
565
+ if (val !== undefined && this._indexes[field].has(val)) {
566
+ const positions = this._indexes[field].get(val);
567
+ const pi = positions.indexOf(lastIdx);
568
+ if (pi !== -1) positions[pi] = rowIndex;
569
+ }
570
+ }
571
+ }
572
+ this._rows.pop();
573
+ this._dirty = true;
574
+ this._db._markDirty(this._name);
575
+ for (const hook of this._hooks.afterRemove) {
576
+ hook({ [this._primaryKey]: id }, 1);
577
+ }
578
+ this._db._emitChange('remove', this._name, { query: { [this._primaryKey]: id }, count: 1 });
579
+ return 1;
580
+ }
581
+
582
+ removeSoft(query = {}) {
583
+ this._softDelete = true;
584
+ return this.remove(query);
585
+ }
586
+
587
+ _findRowIndex(id) {
588
+ if (this._pkIndex && this._primaryKey) {
589
+ const idx = this._pkIndex.get(id);
590
+ if (idx !== undefined) return idx;
591
+ }
592
+ for (let i = 0; i < this._rows.length; i++) {
593
+ if (this._rows[i][this._primaryKey] === id) return i;
594
+ }
595
+ return -1;
596
+ }
597
+
598
+ restore(query = {}) {
599
+ if (!this._softDelete) throw createError('ER_NOT_SUPPORTED', 'Soft delete is not enabled');
600
+ const matched = this._applyFilter(this._rows, { ...query, _deleted: true });
601
+ for (const row of matched) {
602
+ const index = this._rows.indexOf(row);
603
+ if (index !== -1) {
604
+ delete this._rows[index]._deleted;
605
+ delete this._rows[index]._deletedAt;
606
+ }
607
+ }
608
+ return matched.length;
609
+ }
610
+
611
+ truncate() {
612
+ return this._truncate();
613
+ }
614
+
615
+ _truncate() {
616
+ const count = this._rows.length;
617
+ this._rows = [];
618
+ if (this._pkIndex) this._pkIndex.clear();
619
+ for (const key of Object.keys(this._btrees)) {
620
+ this._btrees[key].clear();
621
+ }
622
+ this._indexes = {};
623
+ this._autoIncrement = 0;
624
+ this._dirty = true;
625
+ this._db._markDirty(this._name);
626
+ return count;
627
+ }
628
+
629
+ // ============================================================
630
+ // ALTER TABLE
631
+ // ============================================================
632
+
633
+ addColumn(field, definition) {
634
+ if (this._schema[field]) throw createError('ER_DUP_FIELDNAME', field);
635
+ this._schema[field] = definition;
636
+ if (definition.default !== undefined) {
637
+ this._defaults[field] = definition.default;
638
+ for (const row of this._rows) {
639
+ if (row[field] === undefined) {
640
+ row[field] = typeof definition.default === 'function' ? definition.default() : definition.default;
641
+ }
642
+ }
643
+ }
644
+ if (definition.check) this._checkConstraints.push({ field, fn: definition.check });
645
+ if (definition.foreignKey) {
646
+ this._foreignKeys.push({
647
+ field,
648
+ table: definition.foreignKey.table,
649
+ refField: definition.foreignKey.field || 'id',
650
+ onDelete: definition.foreignKey.onDelete || 'restrict',
651
+ onUpdate: definition.foreignKey.onUpdate || 'restrict'
652
+ });
653
+ }
654
+ if (definition.computed) this._computedFields[field] = definition.computed;
655
+ if (['date', 'datetime', 'timestamp', 'time'].includes(definition.type)) {
656
+ this._dateFields[field] = definition.type;
657
+ }
658
+ if (definition.primaryKey || definition.unique) {
659
+ this._btrees[field] = new BTree(64, true);
660
+ this._rebuildBTree(field);
661
+ }
662
+ this._dirty = true;
663
+ this._db._markDirty(this._name);
664
+ return this;
665
+ }
666
+
667
+ dropColumn(field) {
668
+ if (!this._schema[field]) throw createError('ER_CANT_DROP_FIELD', field);
669
+ delete this._schema[field];
670
+ delete this._defaults[field];
671
+ delete this._btrees[field];
672
+ delete this._indexes[field];
673
+ this._checkConstraints = this._checkConstraints.filter(c => c.field !== field);
674
+ this._foreignKeys = this._foreignKeys.filter(fk => fk.field !== field);
675
+ delete this._computedFields[field];
676
+ delete this._dateFields[field];
677
+ for (const row of this._rows) {
678
+ delete row[field];
679
+ }
680
+ if (this._primaryKey === field) this._primaryKey = null;
681
+ if (this._autoIncrementField === field) this._autoIncrementField = null;
682
+ this._dirty = true;
683
+ this._db._markDirty(this._name);
684
+ return this;
685
+ }
686
+
687
+ renameColumn(oldName, newName) {
688
+ if (!this._schema[oldName]) throw createError('ER_BAD_FIELD_ERROR', oldName, this._name);
689
+ if (this._schema[newName]) throw createError('ER_DUP_FIELDNAME', newName);
690
+ this._schema[newName] = this._schema[oldName];
691
+ delete this._schema[oldName];
692
+ if (this._defaults[oldName] !== undefined) {
693
+ this._defaults[newName] = this._defaults[oldName];
694
+ delete this._defaults[oldName];
695
+ }
696
+ if (this._btrees[oldName]) {
697
+ this._btrees[newName] = this._btrees[oldName];
698
+ delete this._btrees[oldName];
699
+ }
700
+ for (const row of this._rows) {
701
+ row[newName] = row[oldName];
702
+ delete row[oldName];
703
+ }
704
+ if (this._primaryKey === oldName) this._primaryKey = newName;
705
+ if (this._autoIncrementField === oldName) this._autoIncrementField = newName;
706
+ this._dirty = true;
707
+ this._db._markDirty(this._name);
708
+ return this;
709
+ }
710
+
711
+ modifyColumn(field, definition) {
712
+ if (!this._schema[field]) throw createError('ER_BAD_FIELD_ERROR', field, this._name);
713
+ this._schema[field] = { ...this._schema[field], ...definition };
714
+ if (definition.default !== undefined) this._defaults[field] = definition.default;
715
+ this._checkConstraints = this._checkConstraints.filter(c => c.field !== field);
716
+ if (definition.check) this._checkConstraints.push({ field, fn: definition.check });
717
+ if (definition.primaryKey || definition.unique) {
718
+ if (!this._btrees[field]) this._btrees[field] = new BTree(64, true);
719
+ this._rebuildBTree(field);
720
+ }
721
+ this._dirty = true;
722
+ this._db._markDirty(this._name);
723
+ return this;
724
+ }
725
+
726
+ describe() {
727
+ return {
728
+ name: this._name,
729
+ columns: { ...this._schema },
730
+ primaryKey: this._primaryKey,
731
+ rowCount: this._getVisibleRows().length,
732
+ btreeIndexes: Object.keys(this._btrees),
733
+ hashIndexes: Object.keys(this._indexes),
734
+ softDelete: this._softDelete,
735
+ foreignKeys: this._foreignKeys.map(fk => ({ field: fk.field, table: fk.table }))
736
+ };
737
+ }
738
+
739
+ selectInto(newTableName, newSchema, query = {}) {
740
+ const rows = this._applyFilterOptimized(this._getVisibleRows(), query);
741
+ this._db.createTable(newTableName, newSchema);
742
+ const newTable = this._db._tables[newTableName];
743
+ for (const row of rows) {
744
+ newTable.insert(row);
745
+ }
746
+ return newTable;
747
+ }
748
+
749
+ textSearch(field, terms) {
750
+ const searchTerms = Array.isArray(terms) ? terms : terms.split(/\s+/);
751
+ const rows = this._getVisibleRows();
752
+ const scored = [];
753
+ for (const row of rows) {
754
+ const value = String(row[field] || '').toLowerCase();
755
+ let score = 0;
756
+ for (const term of searchTerms) {
757
+ const t = term.toLowerCase();
758
+ const idx = value.indexOf(t);
759
+ if (idx !== -1) {
760
+ score += 10 + (idx === 0 ? 5 : 0) + (value === t ? 20 : 0)
761
+ + (value.length - idx) / value.length * 5;
762
+ }
763
+ }
764
+ if (score > 0) scored.push({ ...row, _score: score });
765
+ }
766
+ return scored.sort((a, b) => b._score - a._score);
767
+ }
768
+
769
+ // ============================================================
770
+ // 索引
771
+ // ============================================================
772
+
773
+ createIndex(field) {
774
+ this._indexes[field] = new Map();
775
+ for (let i = 0; i < this._rows.length; i++) {
776
+ const value = this._rows[i][field];
777
+ if (value !== undefined) {
778
+ if (!this._indexes[field].has(value)) {
779
+ this._indexes[field].set(value, []);
780
+ }
781
+ this._indexes[field].get(value).push(i);
782
+ }
783
+ }
784
+ return this._indexes[field];
785
+ }
786
+
787
+ dropIndex(field) {
788
+ delete this._indexes[field];
789
+ delete this._btrees[field];
790
+ }
791
+
792
+ /**
793
+ * 创建 B-Tree 索引(O(log n) 查询)
794
+ */
795
+ createBTreeIndex(field, unique = false) {
796
+ this._btrees[field] = new BTree(64, unique);
797
+ this._rebuildBTree(field);
798
+ return this._btrees[field];
799
+ }
800
+
801
+ _rebuildBTree(field) {
802
+ if (this._btrees[field]) {
803
+ this._btrees[field].clear();
804
+ for (let i = 0; i < this._rows.length; i++) {
805
+ const val = this._rows[i][field];
806
+ if (val !== undefined && val !== null) {
807
+ this._btrees[field].insert(val, i);
808
+ }
809
+ }
810
+ }
811
+ }
812
+
813
+ _ensureBTrees() {
814
+ if (!this._btreesDirty) return;
815
+ this._btreesDirty = false;
816
+ for (const field of Object.keys(this._btrees)) {
817
+ this._btrees[field].clear();
818
+ }
819
+ for (let i = 0; i < this._rows.length; i++) {
820
+ const row = this._rows[i];
821
+ for (const [field, tree] of Object.entries(this._btrees)) {
822
+ const v = row[field];
823
+ if (v !== undefined && v !== null) tree.insert(v, i);
824
+ }
825
+ }
826
+ }
827
+
828
+ _rebuildAllBTrees() {
829
+ for (const field of Object.keys(this._btrees)) {
830
+ this._rebuildBTree(field);
831
+ }
832
+ }
833
+
834
+ // ============================================================
835
+ // 事件钩子
836
+ // ============================================================
837
+
838
+ on(event, callback) {
839
+ if (this._hooks[event]) {
840
+ this._hooks[event].push(callback);
841
+ }
842
+ return this;
843
+ }
844
+
845
+ off(event, callback) {
846
+ if (this._hooks[event]) {
847
+ this._hooks[event] = this._hooks[event].filter(cb => cb !== callback);
848
+ }
849
+ return this;
850
+ }
851
+
852
+ // ============================================================
853
+ // 内部: 过滤器(B-Tree 加速)
854
+ // ============================================================
855
+
856
+ _applyFilterOptimized(rows, query) {
857
+ this._ensureBTrees();
858
+ // 尝试用 B-Tree 加速
859
+ for (const [field, condition] of Object.entries(query)) {
860
+ if (field === '$or' || field === '$and' || field === '$not') continue;
861
+ // 等值查询用 B-Tree
862
+ if (typeof condition !== 'object' || condition === null || Array.isArray(condition) || condition instanceof RegExp) {
863
+ if (this._btrees[field]) {
864
+ const indices = this._btrees[field].search(condition);
865
+ if (indices.length > 0) {
866
+ const subset = indices.map(i => this._rows[i]).filter(r => r);
867
+ return this._applyFilter(subset, query);
868
+ }
869
+ return [];
870
+ }
871
+ if (this._indexes[field] && this._indexes[field].has(condition)) {
872
+ const indices = this._indexes[field].get(condition);
873
+ const subset = indices.map(i => this._rows[i]).filter(r => r);
874
+ return this._applyFilter(subset, query);
875
+ }
876
+ if (this._indexes[field]) return [];
877
+ }
878
+ // 范围查询用 B-Tree
879
+ if (condition && typeof condition === 'object') {
880
+ if (condition.$gt !== undefined && condition.$lt !== undefined && this._btrees[field]) {
881
+ const indices = this._btrees[field].range(condition.$gt, condition.$lt);
882
+ const subset = indices.map(i => this._rows[i]).filter(r => r);
883
+ return this._applyFilter(subset, query);
884
+ }
885
+ if (condition.$gte !== undefined && condition.$lte !== undefined && this._btrees[field]) {
886
+ const indices = this._btrees[field].range(condition.$gte, condition.$lte);
887
+ const subset = indices.map(i => this._rows[i]).filter(r => r);
888
+ return this._applyFilter(subset, query);
889
+ }
890
+ }
891
+ }
892
+ return this._applyFilter(rows, query);
893
+ }
894
+
895
+ _applyFilter(rows, query) {
896
+ const result = [];
897
+ for (let i = 0; i < rows.length; i++) {
898
+ if (this._matchRow(rows[i], query)) {
899
+ result.push(rows[i]);
900
+ }
901
+ }
902
+ return result;
903
+ }
904
+
905
+ _matchRow(row, query) {
906
+ for (const [field, condition] of Object.entries(query)) {
907
+ if (field === '$or') {
908
+ if (!Array.isArray(condition) || !condition.some(sub => this._matchRow(row, sub))) return false;
909
+ continue;
910
+ }
911
+ if (field === '$and') {
912
+ if (!Array.isArray(condition) || !condition.every(sub => this._matchRow(row, sub))) return false;
913
+ continue;
914
+ }
915
+ if (field === '$not') {
916
+ if (this._matchRow(row, condition)) return false;
917
+ continue;
918
+ }
919
+
920
+ const value = this._resolvePath(row, field);
921
+
922
+ if (condition !== null && typeof condition === 'object' && !Array.isArray(condition) && !(condition instanceof RegExp)) {
923
+ const keys = Object.keys(condition);
924
+ const isOperator = keys.every(k => k.startsWith('$'));
925
+ if (isOperator && keys.length > 0) {
926
+ for (const [op, target] of Object.entries(condition)) {
927
+ if (!this._matchOperator(value, op, target)) return false;
928
+ }
929
+ } else {
930
+ if (value !== null && typeof value === 'object') {
931
+ if (!this._matchRow(value, condition)) return false;
932
+ } else {
933
+ return false;
934
+ }
935
+ }
936
+ } else {
937
+ if (value !== condition) return false;
938
+ }
939
+ }
940
+ return true;
941
+ }
942
+
943
+ _matchOperator(value, op, target) {
944
+ switch (op) {
945
+ case '$eq': return value === target;
946
+ case '$ne': return value !== target;
947
+ case '$gt': return value > target;
948
+ case '$gte': return value >= target;
949
+ case '$lt': return value < target;
950
+ case '$lte': return value <= target;
951
+ case '$in': return Array.isArray(target) && target.includes(value);
952
+ case '$nin': return Array.isArray(target) && !target.includes(value);
953
+ case '$like':
954
+ if (typeof value !== 'string' || typeof target !== 'string') return false;
955
+ {
956
+ const escaped = target
957
+ .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
958
+ .replace(/%/g, '.*')
959
+ .replace(/_/g, '.');
960
+ return new RegExp('^' + escaped + '$', 'i').test(value);
961
+ }
962
+ case '$regex':
963
+ if (target instanceof RegExp) return target.test(String(value));
964
+ if (!isSafeRegex(target)) throw createError('ER_BAD_REGEX', String(target));
965
+ return new RegExp(target).test(String(value));
966
+ case '$exists':
967
+ return target ? value !== undefined : value === undefined;
968
+ case '$between':
969
+ return Array.isArray(target) && target.length === 2 && value >= target[0] && value <= target[1];
970
+ case '$and':
971
+ return Array.isArray(target) && target.every(sub => this._matchRow({ _val: value }, { _val: sub }));
972
+ case '$or':
973
+ return Array.isArray(target) && target.some(sub => this._matchRow({ _val: value }, { _val: sub }));
974
+ case '$not':
975
+ return !this._matchRow({ _val: value }, { _val: target });
976
+ case '$contains':
977
+ return Array.isArray(value) && value.includes(target);
978
+ case '$type':
979
+ return typeof value === target;
980
+ case '$size':
981
+ return Array.isArray(value) && value.length === target;
982
+ case '$elemMatch':
983
+ return Array.isArray(value) && value.some(el =>
984
+ typeof el === 'object' ? this._matchRow(el, target) : el === target);
985
+ case '$isNull':
986
+ return target ? value === null || value === undefined : value !== null && value !== undefined;
987
+ case '$startsWith':
988
+ return typeof value === 'string' && value.startsWith(target);
989
+ case '$endsWith':
990
+ return typeof value === 'string' && value.endsWith(target);
991
+ case '$search':
992
+ if (typeof value !== 'string') return false;
993
+ const terms = Array.isArray(target) ? target : [target];
994
+ return terms.every(t => value.toLowerCase().includes(t.toLowerCase()));
995
+ case '$textSearch':
996
+ if (typeof value !== 'string') return false;
997
+ const sTerms = Array.isArray(target) ? target : target.split(/\s+/);
998
+ return sTerms.some(t => value.toLowerCase().includes(t.toLowerCase()));
999
+ case '$inSub':
1000
+ if (!target || !target.table) return false;
1001
+ const subRows = target.table._applyFilter(target.table._rows, target.query || {});
1002
+ return subRows.some(r => r[target.field] === value);
1003
+ case '$notInSub':
1004
+ if (!target || !target.table) return false;
1005
+ const subRows2 = target.table._applyFilter(target.table._rows, target.query || {});
1006
+ return !subRows2.some(r => r[target.field] === value);
1007
+ case '$expr':
1008
+ if (typeof target === 'function') return target(value);
1009
+ return false;
1010
+ default:
1011
+ return false;
1012
+ }
1013
+ }
1014
+
1015
+ _resolvePath(row, field) {
1016
+ if (field.includes('.') && !field.startsWith('$')) {
1017
+ const parts = field.split('.');
1018
+ let value = row;
1019
+ for (const part of parts) {
1020
+ if (value === null || value === undefined) return undefined;
1021
+ value = value[part];
1022
+ }
1023
+ return value;
1024
+ }
1025
+ return row[field];
1026
+ }
1027
+
1028
+ // ============================================================
1029
+ // 内部: 约束校验
1030
+ // ============================================================
1031
+
1032
+ _validateConstraints(data, excludeIndex, uniqueTracker) {
1033
+ for (const [field, def] of Object.entries(this._schema)) {
1034
+ if (field === '_softDelete') continue;
1035
+
1036
+ // NOT NULL
1037
+ if (def.required && (data[field] === undefined || data[field] === null)
1038
+ && !(def.autoIncrement && data[field] === undefined)) {
1039
+ throw createError('ER_BAD_NULL_ERROR', field);
1040
+ }
1041
+
1042
+ // PRIMARY KEY 唯一性
1043
+ if ((def.primaryKey || def.primary === true) && data[field] !== undefined && data[field] !== null) {
1044
+ let dup = false;
1045
+ if (uniqueTracker && uniqueTracker[field]) {
1046
+ dup = uniqueTracker[field].has(data[field]);
1047
+ } else if (this._pkIndex) {
1048
+ const idx = this._pkIndex.get(data[field]);
1049
+ dup = idx !== undefined && idx !== null && idx !== excludeIndex;
1050
+ } else {
1051
+ const idx = this._rows.findIndex(r => r[field] === data[field]);
1052
+ dup = idx !== -1 && idx !== excludeIndex;
1053
+ }
1054
+ if (dup) {
1055
+ throw createError('ER_DUP_ENTRY', String(data[field]), field);
1056
+ }
1057
+ }
1058
+
1059
+ // UNIQUE(B-Tree 优化 O(log n))
1060
+ if (def.unique && data[field] !== undefined) {
1061
+ if (uniqueTracker && uniqueTracker[field]) {
1062
+ if (uniqueTracker[field].has(data[field])) {
1063
+ throw createError('ER_DUP_ENTRY', String(data[field]), field);
1064
+ }
1065
+ uniqueTracker[field].add(data[field]);
1066
+ } else if (this._btrees[field]) {
1067
+ const found = this._btrees[field].search(data[field]);
1068
+ if (found.length > 0) {
1069
+ for (const idx of found) {
1070
+ if (idx !== excludeIndex && this._rows[idx] && this._rows[idx][field] === data[field]) {
1071
+ throw createError('ER_DUP_ENTRY', String(data[field]), field);
1072
+ }
1073
+ }
1074
+ }
1075
+ } else {
1076
+ for (let i = 0; i < this._rows.length; i++) {
1077
+ if (i === excludeIndex) continue;
1078
+ if (this._rows[i][field] === data[field]) {
1079
+ throw createError('ER_DUP_ENTRY', String(data[field]), field);
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+
1085
+ // CHECK
1086
+ for (const { field: cf, fn } of this._checkConstraints) {
1087
+ if (cf === field && data[field] !== undefined && !fn(data[field], data)) {
1088
+ throw createError('ER_CHECK_CONSTRAINT', this._name + '.' + field);
1089
+ }
1090
+ }
1091
+
1092
+ // 长度限制
1093
+ if (def.length && typeof data[field] === 'string' && data[field].length > def.length) {
1094
+ throw createError('ER_DATA_TOO_LONG', field);
1095
+ }
1096
+
1097
+ // 数值范围
1098
+ if (def.type === 'integer' && data[field] !== undefined && data[field] !== null) {
1099
+ const v = Number(data[field]);
1100
+ if (def.min !== undefined && v < def.min) throw createError('ER_OUT_OF_RANGE', field);
1101
+ if (def.max !== undefined && v > def.max) throw createError('ER_OUT_OF_RANGE', field);
1102
+ }
1103
+ }
1104
+
1105
+ // FOREIGN KEY 校验
1106
+ for (const fk of this._foreignKeys) {
1107
+ if (data[fk.field] !== undefined && data[fk.field] !== null) {
1108
+ const refTable = this._resolveTable(fk.table);
1109
+ if (refTable) {
1110
+ const exists = refTable.findOne({ [fk.refField]: data[fk.field] });
1111
+ if (!exists) {
1112
+ throw createError('ER_NO_REFERENCED_ROW');
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+ }
1118
+
1119
+ _resolveTable(tableRef) {
1120
+ if (typeof tableRef === 'string') {
1121
+ return this._db._tables[tableRef] || this._db._attached[tableRef]?._tables[tableRef];
1122
+ }
1123
+ return tableRef;
1124
+ }
1125
+
1126
+ /**
1127
+ * 级联外键操作:当删除当前表的行时,查找所有引用此表的子表
1128
+ * 并执行 CASCADE 或 SET NULL
1129
+ */
1130
+ _cascadeForeignKeys(row) {
1131
+ for (const [, table] of Object.entries(this._db._tables)) {
1132
+ if (table._name === this._name) continue;
1133
+ for (const fk of table._foreignKeys) {
1134
+ const refTable = table._resolveTable(fk.table);
1135
+ if (!refTable || refTable._name !== this._name) continue;
1136
+
1137
+ // 检查子表行是否引用当前行
1138
+ if (fk.onDelete === 'cascade') {
1139
+ table.remove({ [fk.field]: row[fk.refField] });
1140
+ } else if (fk.onDelete === 'set null') {
1141
+ table.update({ [fk.field]: row[fk.refField] }, { [fk.field]: null });
1142
+ }
1143
+ }
1144
+ }
1145
+ }
1146
+
1147
+ _validateDataTypes(data, partial = false) {
1148
+ const result = { ...data };
1149
+ for (const [field, type] of Object.entries(this._dateFields)) {
1150
+ if (partial && data[field] === undefined) continue;
1151
+ if (data[field] !== undefined && data[field] !== null) {
1152
+ result[field] = validateDateType(data[field], type, field);
1153
+ }
1154
+ }
1155
+ for (const [field, def] of Object.entries(this._schema)) {
1156
+ if (field === '_softDelete') continue;
1157
+ if (partial && data[field] === undefined) continue;
1158
+ const v = data[field];
1159
+ if (v === undefined || v === null) continue;
1160
+ const t = String(def.type || '').toLowerCase();
1161
+ if (t === 'integer' || t === 'int' || t === 'bigint') {
1162
+ if (typeof v === 'boolean') throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1163
+ if (typeof v === 'number') {
1164
+ if (Number.isInteger(v)) result[field] = v;
1165
+ else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1166
+ } else if (typeof v === 'string' && v.trim() !== '' && Number.isInteger(Number(v))) {
1167
+ result[field] = Number(v);
1168
+ } else {
1169
+ throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1170
+ }
1171
+ } else if (t === 'float' || t === 'double' || t === 'number' || t === 'real') {
1172
+ if (typeof v === 'boolean') throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1173
+ if (typeof v === 'number') result[field] = v;
1174
+ else if (typeof v === 'string' && v.trim() !== '' && !Number.isNaN(Number(v))) result[field] = Number(v);
1175
+ else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1176
+ } else if (t === 'boolean' || t === 'bool') {
1177
+ if (typeof v === 'boolean') result[field] = v;
1178
+ else if (v === 0 || v === 1) result[field] = !!v;
1179
+ else if (typeof v === 'string' && ['true', 'false', '1', '0'].includes(v.toLowerCase())) {
1180
+ result[field] = v.toLowerCase() === 'true' || v === '1';
1181
+ } else {
1182
+ throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1183
+ }
1184
+ } else if (t === 'string' || t === 'text' || t === 'varchar' || t === 'char') {
1185
+ if (typeof v === 'string') result[field] = v;
1186
+ else if (typeof v === 'number' || typeof v === 'boolean' || v instanceof Date) result[field] = String(v);
1187
+ else if (Buffer.isBuffer(v)) result[field] = v.toString('utf8');
1188
+ else throw createError('ER_TRUNCATED_WRONG_VALUE', t, v);
1189
+ }
1190
+ }
1191
+ return result;
1192
+ }
1193
+
1194
+ // ============================================================
1195
+ // 内部: 辅助
1196
+ // ============================================================
1197
+
1198
+ _applyDefaults(data) {
1199
+ const result = { ...data };
1200
+ for (const [field, defaultValue] of Object.entries(this._defaults)) {
1201
+ if (result[field] === undefined) {
1202
+ result[field] = typeof defaultValue === 'function'
1203
+ ? defaultValue()
1204
+ : (defaultValue === 'CURRENT_TIMESTAMP' ? now() : defaultValue);
1205
+ }
1206
+ }
1207
+ return result;
1208
+ }
1209
+
1210
+ _applyComputed(data) {
1211
+ for (const [field, fn] of Object.entries(this._computedFields)) {
1212
+ data[field] = fn(data);
1213
+ }
1214
+ return data;
1215
+ }
1216
+
1217
+ _getVisibleRows() {
1218
+ if (this._softDelete) return this._rows.filter(r => !r._deleted);
1219
+ return this._rows;
1220
+ }
1221
+
1222
+ _addToIndexes(rowIndex, data) {
1223
+ if (this._pkIndex && this._primaryKey) {
1224
+ const pkVal = data[this._primaryKey];
1225
+ if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, rowIndex);
1226
+ }
1227
+ for (const field of Object.keys(this._indexes)) {
1228
+ const value = data[field];
1229
+ if (value !== undefined) {
1230
+ if (!this._indexes[field].has(value)) {
1231
+ this._indexes[field].set(value, []);
1232
+ }
1233
+ this._indexes[field].get(value).push(rowIndex);
1234
+ }
1235
+ }
1236
+ for (const [field, tree] of Object.entries(this._btrees)) {
1237
+ const value = data[field];
1238
+ if (value !== undefined && value !== null) {
1239
+ tree.insert(value, rowIndex);
1240
+ }
1241
+ }
1242
+ }
1243
+
1244
+ _removeFromIndexes(rowIndex, data) {
1245
+ if (this._pkIndex && this._primaryKey) {
1246
+ this._pkIndex.delete(data[this._primaryKey]);
1247
+ }
1248
+ for (const field of Object.keys(this._indexes)) {
1249
+ const value = data[field];
1250
+ if (value !== undefined && this._indexes[field].has(value)) {
1251
+ const positions = this._indexes[field].get(value).filter(p => p !== rowIndex);
1252
+ if (positions.length === 0) {
1253
+ this._indexes[field].delete(value);
1254
+ } else {
1255
+ this._indexes[field].set(value, positions);
1256
+ }
1257
+ }
1258
+ }
1259
+ for (const [field, tree] of Object.entries(this._btrees)) {
1260
+ const value = data[field];
1261
+ if (value !== undefined && value !== null) {
1262
+ tree.remove(value, rowIndex);
1263
+ }
1264
+ }
1265
+ }
1266
+
1267
+ _rebuildPKIndex() {
1268
+ if (!this._pkIndex || !this._primaryKey) return;
1269
+ this._pkIndex.clear();
1270
+ for (let i = 0; i < this._rows.length; i++) {
1271
+ const pkVal = this._rows[i][this._primaryKey];
1272
+ if (pkVal !== undefined && pkVal !== null) this._pkIndex.set(pkVal, i);
1273
+ }
1274
+ }
1275
+
1276
+ _adjustBTreeAfterRemove(removedIndex) {
1277
+ this._rebuildPKIndex();
1278
+ for (const field of Object.keys(this._btrees)) {
1279
+ this._rebuildBTree(field);
1280
+ }
1281
+ }
1282
+
1283
+ _loadRows(rows) {
1284
+ this._rows = rows;
1285
+ if (this._autoIncrementField) {
1286
+ for (const row of rows) {
1287
+ const val = row[this._autoIncrementField];
1288
+ if (typeof val === 'number' && val > this._autoIncrement) {
1289
+ this._autoIncrement = val;
1290
+ }
1291
+ }
1292
+ }
1293
+ this._rebuildPKIndex();
1294
+ this._rebuildAllBTrees();
1295
+ for (const field of Object.keys(this._indexes)) {
1296
+ this.createIndex(field);
1297
+ }
1298
+ }
1299
+
1300
+ toJSON() {
1301
+ return this._rows;
1302
+ }
1303
+ }
1304
+
1305
1305
  module.exports = Table;