jsql-neo 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/table.js CHANGED
@@ -9,6 +9,102 @@ const BTree = require('./btree');
9
9
  const { validateDateType, now } = require('./date-types');
10
10
  const { createError } = require('./errors');
11
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
+
12
108
  class Table {
13
109
  constructor(name, schema, db) {
14
110
  this._name = name;
@@ -112,7 +208,7 @@ class Table {
112
208
  this._rows.push({ ...data });
113
209
  this._addToIndexes(rowIndex, data);
114
210
  this._dirty = true;
115
- this._db._markDirty();
211
+ this._db._markDirty(this._name);
116
212
 
117
213
  for (const hook of this._hooks.afterInsert) {
118
214
  hook(data);
@@ -131,7 +227,12 @@ class Table {
131
227
  }
132
228
  const N = items.length;
133
229
  const uniqueTracker = {};
134
- for (const field of this._cachedUniqueFields) uniqueTracker[field] = new Set();
230
+ for (const field of this._cachedUniqueFields) {
231
+ uniqueTracker[field] = new Set();
232
+ for (const row of this._rows) {
233
+ if (row[field] !== undefined && row[field] !== null) uniqueTracker[field].add(row[field]);
234
+ }
235
+ }
135
236
  const dateFields = this._cachedDateFields;
136
237
  const hasDateFields = dateFields.length > 0;
137
238
  const autoIncField = this._autoIncrementField;
@@ -194,7 +295,7 @@ class Table {
194
295
  items[i] = data;
195
296
  }
196
297
  this._dirty = true;
197
- this._db._markDirty();
298
+ this._db._markDirty(this._name);
198
299
  for (const hook of this._hooks.afterInsert) {
199
300
  for (let i = 0; i < N; i++) hook(items[i]);
200
301
  }
@@ -295,7 +396,7 @@ class Table {
295
396
 
296
397
  if (count > 0) {
297
398
  this._dirty = true;
298
- this._db._markDirty();
399
+ this._db._markDirty(this._name);
299
400
  for (const hook of this._hooks.afterUpdate) {
300
401
  hook(query, updates, count);
301
402
  }
@@ -333,7 +434,7 @@ class Table {
333
434
  Object.assign(row, updates);
334
435
  this._addToIndexes(rowIndex, row);
335
436
  this._dirty = true;
336
- this._db._markDirty();
437
+ this._db._markDirty(this._name);
337
438
  for (const hook of this._hooks.afterUpdate) {
338
439
  hook({ [this._primaryKey]: id }, updates, 1);
339
440
  }
@@ -388,7 +489,7 @@ class Table {
388
489
  this._rows.pop();
389
490
  }
390
491
  this._dirty = true;
391
- this._db._markDirty();
492
+ this._db._markDirty(this._name);
392
493
  for (const hook of this._hooks.afterRemove) {
393
494
  hook(query, count);
394
495
  }
@@ -434,7 +535,7 @@ class Table {
434
535
  }
435
536
  this._rows.pop();
436
537
  this._dirty = true;
437
- this._db._markDirty();
538
+ this._db._markDirty(this._name);
438
539
  for (const hook of this._hooks.afterRemove) {
439
540
  hook({ [this._primaryKey]: id }, 1);
440
541
  }
@@ -485,7 +586,7 @@ class Table {
485
586
  this._indexes = {};
486
587
  this._autoIncrement = 0;
487
588
  this._dirty = true;
488
- this._db._markDirty();
589
+ this._db._markDirty(this._name);
489
590
  return count;
490
591
  }
491
592
 
@@ -523,7 +624,7 @@ class Table {
523
624
  this._rebuildBTree(field);
524
625
  }
525
626
  this._dirty = true;
526
- this._db._markDirty();
627
+ this._db._markDirty(this._name);
527
628
  return this;
528
629
  }
529
630
 
@@ -543,7 +644,7 @@ class Table {
543
644
  if (this._primaryKey === field) this._primaryKey = null;
544
645
  if (this._autoIncrementField === field) this._autoIncrementField = null;
545
646
  this._dirty = true;
546
- this._db._markDirty();
647
+ this._db._markDirty(this._name);
547
648
  return this;
548
649
  }
549
650
 
@@ -567,7 +668,7 @@ class Table {
567
668
  if (this._primaryKey === oldName) this._primaryKey = newName;
568
669
  if (this._autoIncrementField === oldName) this._autoIncrementField = newName;
569
670
  this._dirty = true;
570
- this._db._markDirty();
671
+ this._db._markDirty(this._name);
571
672
  return this;
572
673
  }
573
674
 
@@ -582,7 +683,7 @@ class Table {
582
683
  this._rebuildBTree(field);
583
684
  }
584
685
  this._dirty = true;
585
- this._db._markDirty();
686
+ this._db._markDirty(this._name);
586
687
  return this;
587
688
  }
588
689
 
@@ -811,6 +912,7 @@ class Table {
811
912
  if (typeof value !== 'string' || typeof target !== 'string') return false;
812
913
  return new RegExp('^' + target.replace(/%/g, '.*').replace(/_/g, '.') + '$', 'i').test(value);
813
914
  case '$regex':
915
+ if (!isSafeRegex(target)) throw createError('ER_BAD_REGEX', String(target));
814
916
  return new RegExp(target).test(String(value));
815
917
  case '$exists':
816
918
  return target ? value !== undefined : value === undefined;
@@ -44,7 +44,15 @@ function encodeBatch(rows) {
44
44
  const sl = Buffer.byteLength(v, 'utf8');
45
45
  off = buf.writeUInt32LE(sl, off);
46
46
  off += buf.write(v, off, sl, 'utf8');
47
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
47
+ } else if (typeof v === 'boolean') {
48
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
49
+ } else {
50
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
51
+ off = buf.writeUInt8(STR_TAG, off);
52
+ const sl = Buffer.byteLength(s, 'utf8');
53
+ off = buf.writeUInt32LE(sl, off);
54
+ off += buf.write(s, off, sl, 'utf8');
55
+ }
48
56
  }
49
57
  if (nFields >= 2) {
50
58
  const v = row[fieldNames[1]];
@@ -57,7 +65,15 @@ function encodeBatch(rows) {
57
65
  const sl = Buffer.byteLength(v, 'utf8');
58
66
  off = buf.writeUInt32LE(sl, off);
59
67
  off += buf.write(v, off, sl, 'utf8');
60
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
68
+ } else if (typeof v === 'boolean') {
69
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
70
+ } else {
71
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
72
+ off = buf.writeUInt8(STR_TAG, off);
73
+ const sl = Buffer.byteLength(s, 'utf8');
74
+ off = buf.writeUInt32LE(sl, off);
75
+ off += buf.write(s, off, sl, 'utf8');
76
+ }
61
77
  }
62
78
  if (nFields >= 3) {
63
79
  const v = row[fieldNames[2]];
@@ -70,7 +86,15 @@ function encodeBatch(rows) {
70
86
  const sl = Buffer.byteLength(v, 'utf8');
71
87
  off = buf.writeUInt32LE(sl, off);
72
88
  off += buf.write(v, off, sl, 'utf8');
73
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
89
+ } else if (typeof v === 'boolean') {
90
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
91
+ } else {
92
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
93
+ off = buf.writeUInt8(STR_TAG, off);
94
+ const sl = Buffer.byteLength(s, 'utf8');
95
+ off = buf.writeUInt32LE(sl, off);
96
+ off += buf.write(s, off, sl, 'utf8');
97
+ }
74
98
  }
75
99
  if (nFields >= 4) {
76
100
  const v = row[fieldNames[3]];
@@ -83,7 +107,15 @@ function encodeBatch(rows) {
83
107
  const sl = Buffer.byteLength(v, 'utf8');
84
108
  off = buf.writeUInt32LE(sl, off);
85
109
  off += buf.write(v, off, sl, 'utf8');
86
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
110
+ } else if (typeof v === 'boolean') {
111
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
112
+ } else {
113
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
114
+ off = buf.writeUInt8(STR_TAG, off);
115
+ const sl = Buffer.byteLength(s, 'utf8');
116
+ off = buf.writeUInt32LE(sl, off);
117
+ off += buf.write(s, off, sl, 'utf8');
118
+ }
87
119
  }
88
120
  if (nFields >= 5) {
89
121
  const v = row[fieldNames[4]];
@@ -96,7 +128,15 @@ function encodeBatch(rows) {
96
128
  const sl = Buffer.byteLength(v, 'utf8');
97
129
  off = buf.writeUInt32LE(sl, off);
98
130
  off += buf.write(v, off, sl, 'utf8');
99
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
131
+ } else if (typeof v === 'boolean') {
132
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
133
+ } else {
134
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
135
+ off = buf.writeUInt8(STR_TAG, off);
136
+ const sl = Buffer.byteLength(s, 'utf8');
137
+ off = buf.writeUInt32LE(sl, off);
138
+ off += buf.write(s, off, sl, 'utf8');
139
+ }
100
140
  }
101
141
  // fallback for >5 fields
102
142
  for (let fi = 5; fi < nFields; fi++) {
@@ -110,7 +150,15 @@ function encodeBatch(rows) {
110
150
  const sl = Buffer.byteLength(v, 'utf8');
111
151
  off = buf.writeUInt32LE(sl, off);
112
152
  off += buf.write(v, off, sl, 'utf8');
113
- } else { off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off); }
153
+ } else if (typeof v === 'boolean') {
154
+ off = buf.writeUInt8(BOOL_TAG, off); off = buf.writeUInt8(v ? 1 : 0, off);
155
+ } else {
156
+ const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
157
+ off = buf.writeUInt8(STR_TAG, off);
158
+ const sl = Buffer.byteLength(s, 'utf8');
159
+ off = buf.writeUInt32LE(sl, off);
160
+ off += buf.write(s, off, sl, 'utf8');
161
+ }
114
162
  }
115
163
  }
116
164
 
@@ -213,6 +261,7 @@ class JSQL {
213
261
  }
214
262
 
215
263
  async start() {
264
+ wasmBindings.jsql_reset();
216
265
  this._runHooks('onStart', []);
217
266
  this._emit('start', {});
218
267
  }
@@ -264,6 +313,10 @@ class JSQL {
264
313
  return flushed && flushed[table] ? flushed[table] : null;
265
314
  }
266
315
 
316
+ async insertMany(table, data) {
317
+ return this.insert(table, data);
318
+ }
319
+
267
320
  async createTable(name, schema) {
268
321
  await this._flush();
269
322
  if (!this._runHooks('beforeCreateTable', [name, schema])) return null;
Binary file