w-orm-reladb 1.0.47 → 1.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,6 +11,42 @@ An operator for relational database in nodejs.
11
11
  ## Documentation
12
12
  To view documentation or get support, visit [docs](https://yuda-lyu.github.io/w-orm-reladb/WOrm.html).
13
13
 
14
+ ### `insert` and `insertBulk`
15
+
16
+ Both write rows that do not yet exist, and both return `{ n, nInserted, ok }`. They differ in what happens on a conflict, so `insertBulk` is **not** a faster `insert`:
17
+
18
+ | Situation | `insert` | `insertBulk` |
19
+ |---|---|---|
20
+ | Primary key already exists | that row is skipped, the batch still returns `ok: 1` | the whole call **rejects, and not a single row is written** |
21
+ | Duplicate primary keys inside one batch | only the first one counts toward `nInserted` | treated as a conflict, the whole call rejects |
22
+ | `nInserted` | rows actually inserted, `0 ≤ nInserted ≤ n` | equals `n` whenever the call succeeds |
23
+ | Use it for | ordinary writes against a table that may already hold data | bulk import where no conflict is expected |
24
+
25
+ With no conflict the two are indistinguishable, so you may swap them under that assumption — and if the assumption is wrong, `insertBulk` tells you by rejecting rather than silently skipping.
26
+
27
+ `insertBulk` is considerably faster here, because `insert` has to write row by row to report an exact `nInserted` (measured on this machine, Windows 11 / Node 24 / sequelize 6):
28
+
29
+ | Rows | sqlite `insert` → `insertBulk` | mssql `insert` → `insertBulk` |
30
+ |---|---|---|
31
+ | 1000 | 5549 ms → 25 ms (226x) | 6802 ms → 190 ms (36x) |
32
+ | 5000 | 29825 ms → 50 ms (592x) | 44116 ms → 489 ms (90x) |
33
+
34
+ The all-or-nothing guarantee is provided by a transaction, not by the batch statement alone: mssql splits a large batch into several statements because of its bind parameter limit, so without one an interrupted batch would leave earlier rows behind. When you pass `option.transaction`, a SAVEPOINT is used instead, so a failure rolls back only this call and leaves the rest of your transaction untouched.
35
+
36
+ ### Concurrency
37
+
38
+ The scope of the atomicity guarantee, by range:
39
+
40
+ | Range | Guaranteed | Provided by |
41
+ |---|---|---|
42
+ | Cross process — several processes against the same database | Yes | The database itself. `insert` and `save` write through single conditional statements, and the primary key unique constraint decides the winner. Independent of `opt.useStable`. |
43
+ | Single process — several parallel calls in one process | Yes **when `opt.useStable` is `true`** (the default) | A built-in queue that runs one operation at a time. |
44
+ | Single process, with `opt.useStable` set to `false` | **No** | — |
45
+
46
+ **Do not issue parallel calls from one process while `opt.useStable` is `false`.** Each instance holds one shared connection, opened at the start of a call and closed at its end, so a second call running in parallel closes the connection the first one is still using. Measured on 30 parallel calls in a single process: sqlite raised `SQLITE_MISUSE: Database handle is closed` and rejected 12 of the batches, mssql reported `ConnectionManager.getConnection was called after the connection manager was closed!` as a per row failure; both ended with 29 of the 30 rows written, so writes are silently lost. Keep the default `true`, or serialize the calls yourself (for example `await` them one by one).
47
+
48
+ Cross process was measured with 2 independent processes against the same 20 primary keys: `nInserted` summed to exactly 20 with 20 rows in the table, and with each process writing a different column to those keys all 40 operations returned `ok: 1` with both columns preserved on all 20 rows. sqlite was repeated over 6 rounds to rule out timing dependent `SQLITE_BUSY`. Platform: Windows 11, Node 24, sequelize 6, MSSQL 2022.
49
+
14
50
  ## Before Installation
15
51
  > If you need to use encrypted sqlite, you need to manually install `@journeyapps/sqlcipher`, as follows:
16
52
  >1. Open visual studio code by system administrator.
@@ -42,7 +78,7 @@ let opt = {
42
78
  cl: 'users',
43
79
  fdModels: './models',
44
80
  // modelType: 'json',
45
- // autoGenPK: false,
81
+ // autoGenPk: false,
46
82
  }
47
83
 
48
84
  let rs = [
@@ -106,8 +142,8 @@ async function test() {
106
142
  w.on('change', function(mode, data, res) {
107
143
  console.log('change', mode)
108
144
  })
109
- w.on('error', function(err) {
110
- console.log('error', err)
145
+ w.on('error', function(mode, data, err) {
146
+ console.log('error', mode, err)
111
147
  })
112
148
 
113
149
 
@@ -196,10 +232,10 @@ test()
196
232
  // insert then { n: 3, nInserted: 3, ok: 1 }
197
233
  // change save
198
234
  // save then [
199
- // { n: 1, nModified: 1, ok: 1 },
200
- // { n: 1, nModified: 1, ok: 1 },
201
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
202
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
235
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
236
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
237
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
238
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
203
239
  // ]
204
240
  // select all [
205
241
  // { id: 'id-peter', name: 'peter(modify)', value: 123 },
@@ -243,7 +279,7 @@ let opt = {
243
279
  cl: 'users',
244
280
  fdModels: './models',
245
281
  // modelType: 'json',
246
- // autoGenPK: false,
282
+ // autoGenPk: false,
247
283
  }
248
284
 
249
285
  let rs = [
@@ -307,8 +343,8 @@ async function testCommit() {
307
343
  w.on('change', function(mode, data, res) {
308
344
  console.log('change', mode)
309
345
  })
310
- w.on('error', function(err) {
311
- console.log('error', err)
346
+ w.on('error', function(mode, data, err) {
347
+ console.log('error', mode, err)
312
348
  })
313
349
 
314
350
 
@@ -424,10 +460,10 @@ testCommit()
424
460
  // insert then { n: 3, nInserted: 3, ok: 1 }
425
461
  // change save
426
462
  // save then [
427
- // { n: 1, nModified: 1, ok: 1 },
428
- // { n: 1, nModified: 1, ok: 1 },
429
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
430
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
463
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
464
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
465
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
466
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
431
467
  // ]
432
468
  // change del
433
469
  // del then [
@@ -460,7 +496,7 @@ let opt = {
460
496
  cl: 'users',
461
497
  fdModels: './models',
462
498
  // modelType: 'json',
463
- // autoGenPK: false,
499
+ // autoGenPk: false,
464
500
  }
465
501
 
466
502
  let rs = [
@@ -524,8 +560,8 @@ async function testRollback() {
524
560
  w.on('change', function(mode, data, res) {
525
561
  console.log('change', mode)
526
562
  })
527
- w.on('error', function(err) {
528
- console.log('error', err)
563
+ w.on('error', function(mode, data, err) {
564
+ console.log('error', mode, err)
529
565
  })
530
566
 
531
567
 
@@ -638,10 +674,10 @@ testRollback()
638
674
  // insert then { n: 3, nInserted: 3, ok: 1 }
639
675
  // change save
640
676
  // save then [
641
- // { n: 1, nModified: 1, ok: 1 },
642
- // { n: 1, nModified: 1, ok: 1 },
643
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
644
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
677
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
678
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
679
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
680
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
645
681
  // ]
646
682
  // change del
647
683
  // del then [
@@ -672,7 +708,7 @@ let opt = {
672
708
  cl: 'users',
673
709
  fdModels: './models',
674
710
  // modelType: 'json',
675
- // autoGenPK: false,
711
+ // autoGenPk: false,
676
712
  storage: './worm.sqlite',
677
713
  }
678
714
 
@@ -743,8 +779,8 @@ async function test() {
743
779
  w.on('change', function(mode, data, res) {
744
780
  console.log('change', mode)
745
781
  })
746
- w.on('error', function(err) {
747
- console.log('error', err)
782
+ w.on('error', function(mode, data, err) {
783
+ console.log('error', mode, err)
748
784
  })
749
785
 
750
786
 
@@ -833,10 +869,10 @@ test()
833
869
  // insert then { n: 3, nInserted: 3, ok: 1 }
834
870
  // change save
835
871
  // save then [
836
- // { n: 1, nModified: 1, ok: 1 },
837
- // { n: 1, nModified: 1, ok: 1 },
838
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
839
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
872
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
873
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
874
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
875
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
840
876
  // ]
841
877
  // select all [
842
878
  // { id: 'id-peter', name: 'peter(modify)', value: 123 },
@@ -881,7 +917,7 @@ let opt = {
881
917
  cl: 'users',
882
918
  fdModels: './models',
883
919
  // modelType: 'json',
884
- // autoGenPK: false,
920
+ // autoGenPk: false,
885
921
  storage: './worm.sqlite',
886
922
  }
887
923
 
@@ -951,8 +987,8 @@ async function testCommit() {
951
987
  w.on('change', function(mode, data, res) {
952
988
  console.log('change', mode)
953
989
  })
954
- w.on('error', function(err) {
955
- console.log('error', err)
990
+ w.on('error', function(mode, data, err) {
991
+ console.log('error', mode, err)
956
992
  })
957
993
 
958
994
 
@@ -1068,10 +1104,10 @@ testCommit()
1068
1104
  // insert then { n: 3, nInserted: 3, ok: 1 }
1069
1105
  // change save
1070
1106
  // save then [
1071
- // { n: 1, nModified: 1, ok: 1 },
1072
- // { n: 1, nModified: 1, ok: 1 },
1073
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
1074
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
1107
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1108
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1109
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
1110
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
1075
1111
  // ]
1076
1112
  // change del
1077
1113
  // del then [
@@ -1105,7 +1141,7 @@ let opt = {
1105
1141
  cl: 'users',
1106
1142
  fdModels: './models',
1107
1143
  // modelType: 'json',
1108
- // autoGenPK: false,
1144
+ // autoGenPk: false,
1109
1145
  storage: './worm.sqlite',
1110
1146
  }
1111
1147
 
@@ -1175,8 +1211,8 @@ async function testRollback() {
1175
1211
  w.on('change', function(mode, data, res) {
1176
1212
  console.log('change', mode)
1177
1213
  })
1178
- w.on('error', function(err) {
1179
- console.log('error', err)
1214
+ w.on('error', function(mode, data, err) {
1215
+ console.log('error', mode, err)
1180
1216
  })
1181
1217
 
1182
1218
 
@@ -1289,10 +1325,10 @@ testRollback()
1289
1325
  // insert then { n: 3, nInserted: 3, ok: 1 }
1290
1326
  // change save
1291
1327
  // save then [
1292
- // { n: 1, nModified: 1, ok: 1 },
1293
- // { n: 1, nModified: 1, ok: 1 },
1294
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
1295
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
1328
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1329
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1330
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
1331
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
1296
1332
  // ]
1297
1333
  // change del
1298
1334
  // del then [
@@ -1323,7 +1359,7 @@ let opt = {
1323
1359
  cl: 'users',
1324
1360
  fdModels: './models',
1325
1361
  // modelType: 'json',
1326
- // autoGenPK: false,
1362
+ // autoGenPk: false,
1327
1363
  storage: './worm.sqlite',
1328
1364
  useEncryption: true,
1329
1365
  }
@@ -1401,8 +1437,8 @@ async function test() {
1401
1437
  w.on('change', function(mode, data, res) {
1402
1438
  console.log('change', mode)
1403
1439
  })
1404
- w.on('error', function(err) {
1405
- console.log('error', err)
1440
+ w.on('error', function(mode, data, err) {
1441
+ console.log('error', mode, err)
1406
1442
  })
1407
1443
 
1408
1444
 
@@ -1491,10 +1527,10 @@ test()
1491
1527
  // insert then { n: 3, nInserted: 3, ok: 1 }
1492
1528
  // change save
1493
1529
  // save then [
1494
- // { n: 1, nModified: 1, ok: 1 },
1495
- // { n: 1, nModified: 1, ok: 1 },
1496
- // { n: 0, nModified: 0, ok: 1 } //autoInsert=false
1497
- // { n: 1, nInserted: 1, ok: 1 } //autoInsert=true
1530
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1531
+ // { n: 1, nInserted: 0, nModified: 1, ok: 1 },
1532
+ // { n: 0, nInserted: 0, nModified: 0, ok: 1 } //autoInsert=false
1533
+ // { n: 1, nInserted: 1, nModified: 0, ok: 1 } //autoInsert=true
1498
1534
  // ]
1499
1535
  // select all [
1500
1536
  // { id: 'id-peter', name: 'peter(modify)', value: 123 },
@@ -1538,7 +1574,7 @@ let opt = {
1538
1574
  cl: 'users',
1539
1575
  fdModels: './models',
1540
1576
  // modelType: 'json',
1541
- // autoGenPK: false,
1577
+ // autoGenPk: false,
1542
1578
  }
1543
1579
 
1544
1580
  let fd = opt.fdModels