sqlmath 0.0.1 → 2021.11.20

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 (96) hide show
  1. package/.npmignore +10 -0
  2. package/CHANGELOG.md +53 -2
  3. package/LICENSE +16 -22
  4. package/README.md +18 -219
  5. package/_binary_sqlmath_napi8_darwin_x64.node +0 -0
  6. package/_binary_sqlmath_napi8_linux_x64.node +0 -0
  7. package/_binary_sqlmath_napi8_win32_x64.node +0 -0
  8. package/jslint.mjs +10998 -0
  9. package/package.json +23 -8
  10. package/sqlmath.mjs +1713 -0
  11. package/.gitconfig +0 -25
  12. package/.github/workflows/ci.yml +0 -61
  13. package/.gitignore +0 -24
  14. package/extension-functions.c +0 -2047
  15. package/jslint_ci.sh +0 -1968
  16. package/node_sqlite3.cc +0 -11877
  17. package/sqlite-autoconf-3360000/INSTALL +0 -370
  18. package/sqlite-autoconf-3360000/Makefile.am +0 -20
  19. package/sqlite-autoconf-3360000/Makefile.fallback +0 -19
  20. package/sqlite-autoconf-3360000/Makefile.in +0 -1028
  21. package/sqlite-autoconf-3360000/Makefile.msc +0 -1037
  22. package/sqlite-autoconf-3360000/README.txt +0 -113
  23. package/sqlite-autoconf-3360000/Replace.cs +0 -223
  24. package/sqlite-autoconf-3360000/aclocal.m4 +0 -10199
  25. package/sqlite-autoconf-3360000/compile +0 -347
  26. package/sqlite-autoconf-3360000/config.guess +0 -1480
  27. package/sqlite-autoconf-3360000/config.sub +0 -1801
  28. package/sqlite-autoconf-3360000/configure +0 -16135
  29. package/sqlite-autoconf-3360000/configure.ac +0 -285
  30. package/sqlite-autoconf-3360000/depcomp +0 -791
  31. package/sqlite-autoconf-3360000/install-sh +0 -508
  32. package/sqlite-autoconf-3360000/ltmain.sh +0 -11156
  33. package/sqlite-autoconf-3360000/missing +0 -215
  34. package/sqlite-autoconf-3360000/shell.c +0 -22381
  35. package/sqlite-autoconf-3360000/sqlite3.1 +0 -286
  36. package/sqlite-autoconf-3360000/sqlite3.c +0 -235517
  37. package/sqlite-autoconf-3360000/sqlite3.h +0 -12353
  38. package/sqlite-autoconf-3360000/sqlite3.pc.in +0 -13
  39. package/sqlite-autoconf-3360000/sqlite3.rc +0 -83
  40. package/sqlite-autoconf-3360000/sqlite3ext.h +0 -663
  41. package/sqlite-autoconf-3360000/sqlite3rc.h +0 -3
  42. package/sqlite-autoconf-3360000/tea/Makefile.in +0 -440
  43. package/sqlite-autoconf-3360000/tea/README +0 -36
  44. package/sqlite-autoconf-3360000/tea/aclocal.m4 +0 -9
  45. package/sqlite-autoconf-3360000/tea/configure +0 -9989
  46. package/sqlite-autoconf-3360000/tea/configure.ac +0 -201
  47. package/sqlite-autoconf-3360000/tea/doc/sqlite3.n +0 -15
  48. package/sqlite-autoconf-3360000/tea/generic/tclsqlite3.c +0 -4016
  49. package/sqlite-autoconf-3360000/tea/license.terms +0 -6
  50. package/sqlite-autoconf-3360000/tea/pkgIndex.tcl.in +0 -7
  51. package/sqlite-autoconf-3360000/tea/tclconfig/install-sh +0 -528
  52. package/sqlite-autoconf-3360000/tea/tclconfig/tcl.m4 +0 -4168
  53. package/sqlite-autoconf-3360000/tea/win/makefile.vc +0 -419
  54. package/sqlite-autoconf-3360000/tea/win/nmakehlp.c +0 -815
  55. package/sqlite-autoconf-3360000/tea/win/rules.vc +0 -711
  56. package/sqlmath.js +0 -238
  57. package/test/backup.test.js +0 -279
  58. package/test/blob.test.js +0 -54
  59. package/test/cache.test.js +0 -42
  60. package/test/constants.test.js +0 -44
  61. package/test/database_fail.test.js +0 -153
  62. package/test/each.test.js +0 -39
  63. package/test/exec.test.js +0 -39
  64. package/test/extension.test.js +0 -26
  65. package/test/extension_functions.test.js +0 -29
  66. package/test/fts-content.test.js +0 -13
  67. package/test/interrupt.test.js +0 -80
  68. package/test/issue-108.test.js +0 -28
  69. package/test/json.test.js +0 -22
  70. package/test/map.test.js +0 -63
  71. package/test/named_columns.test.js +0 -38
  72. package/test/named_params.test.js +0 -69
  73. package/test/null_error.test.js +0 -41
  74. package/test/nw/.gitignore +0 -3
  75. package/test/nw/Makefile +0 -39
  76. package/test/nw/index.html +0 -14
  77. package/test/nw/package.json +0 -9
  78. package/test/open_close.test.js +0 -187
  79. package/test/other_objects.test.js +0 -98
  80. package/test/parallel_insert.test.js +0 -44
  81. package/test/prepare.test.js +0 -427
  82. package/test/profile.test.js +0 -57
  83. package/test/rerun.test.js +0 -50
  84. package/test/scheduling.test.js +0 -44
  85. package/test/serialization.test.js +0 -104
  86. package/test/support/createdb-electron.js +0 -10
  87. package/test/support/createdb.js +0 -47
  88. package/test/support/elmo.png +0 -0
  89. package/test/support/helper.js +0 -37
  90. package/test/support/script.sql +0 -70
  91. package/test/trace.test.js +0 -67
  92. package/test/unicode.test.js +0 -114
  93. package/test/upsert.test.js +0 -27
  94. package/test/verbose.test.js +0 -60
  95. package/test.js +0 -141
  96. package/test.slr.mjs +0 -212
package/sqlmath.mjs ADDED
@@ -0,0 +1,1713 @@
1
+ /*jslint beta, bitwise, name, node*/
2
+ "use strict";
3
+ import {createRequire} from "module";
4
+ import jslint from "./jslint.mjs";
5
+
6
+ let {
7
+ assertErrorThrownAsync,
8
+ assertJsonEqual,
9
+ assertOrThrow,
10
+ debugInline,
11
+ noop
12
+ } = jslint;
13
+ let local = Object.assign({}, jslint);
14
+
15
+ function assertNumericalEqual(aa, bb, message) {
16
+
17
+ // This function will assert aa - bb <= Number.EPSILON
18
+
19
+ assertOrThrow(aa, "value cannot be 0 or falsy");
20
+ if (!(Math.abs(aa - bb) <= Number.EPSILON)) {
21
+ throw new Error(
22
+ JSON.stringify(aa) + " != " + JSON.stringify(bb) + (
23
+ message
24
+ ? " - " + message
25
+ : ""
26
+ )
27
+ );
28
+ }
29
+ }
30
+
31
+
32
+ /*
33
+ file sqlmath.js
34
+ */
35
+ (function () {
36
+ let JSBATON_ARGC = 16;
37
+ // let SIZEOF_MESSAGE_DEFAULT = 768;
38
+ let SQLITE_MAX_LENGTH2 = 1000000000;
39
+ let SQLITE_OPEN_AUTOPROXY = 0x00000020; /* VFS only */
40
+ let SQLITE_OPEN_CREATE = 0x00000004; /* Ok for sqlite3_open_v2() */
41
+ let SQLITE_OPEN_DELETEONCLOSE = 0x00000008; /* VFS only */
42
+ let SQLITE_OPEN_EXCLUSIVE = 0x00000010; /* VFS only */
43
+ let SQLITE_OPEN_FULLMUTEX = 0x00010000; /* Ok for sqlite3_open_v2() */
44
+ let SQLITE_OPEN_MAIN_DB = 0x00000100; /* VFS only */
45
+ let SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; /* VFS only */
46
+ let SQLITE_OPEN_MEMORY = 0x00000080; /* Ok for sqlite3_open_v2() */
47
+ let SQLITE_OPEN_NOFOLLOW = 0x01000000; /* Ok for sqlite3_open_v2() */
48
+ let SQLITE_OPEN_NOMUTEX = 0x00008000; /* Ok for sqlite3_open_v2() */
49
+ let SQLITE_OPEN_PRIVATECACHE = 0x00040000; /* Ok for sqlite3_open_v2() */
50
+ let SQLITE_OPEN_READONLY = 0x00000001; /* Ok for sqlite3_open_v2() */
51
+ let SQLITE_OPEN_READWRITE = 0x00000002; /* Ok for sqlite3_open_v2() */
52
+ let SQLITE_OPEN_SHAREDCACHE = 0x00020000; /* Ok for sqlite3_open_v2() */
53
+ let SQLITE_OPEN_SUBJOURNAL = 0x00002000; /* VFS only */
54
+ let SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; /* VFS only */
55
+ let SQLITE_OPEN_TEMP_DB = 0x00000200; /* VFS only */
56
+ let SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; /* VFS only */
57
+ let SQLITE_OPEN_TRANSIENT_DB = 0x00000400; /* VFS only */
58
+ let SQLITE_OPEN_URI = 0x00000040; /* Ok for sqlite3_open_v2() */
59
+ let SQLITE_OPEN_WAL = 0x00080000; /* VFS only */
60
+ let addon;
61
+ let dbDict = new WeakMap();
62
+ let requireCjs = createRequire(import.meta.url);
63
+ let testList;
64
+ // private map of sqlite-database-connections
65
+
66
+ function cCall(func, argList) {
67
+ // this function will serialize <argList> to a c <baton>,
68
+ // suitable for passing into napi
69
+ let baton = new BigInt64Array(2048);
70
+ let errStack;
71
+ let result;
72
+ // serialize js-args to c-args
73
+ argList = argList.map(function (arg, ii) {
74
+ switch (typeof arg) {
75
+ case "bigint":
76
+ case "boolean":
77
+ case "number":
78
+ try {
79
+ baton[ii] = BigInt(arg);
80
+ } catch (ignore) {
81
+ return;
82
+ }
83
+ break;
84
+ // case "object":
85
+ // break;
86
+ case "string":
87
+ // append null-terminator to string
88
+ arg = new TextEncoder().encode(arg + "\u0000");
89
+ break;
90
+ }
91
+ if (ArrayBuffer.isView(arg)) {
92
+ baton[ii] = BigInt(arg.byteLength);
93
+ return new DataView(
94
+ arg.buffer,
95
+ arg.byteOffset,
96
+ arg.byteLength
97
+ );
98
+ }
99
+ return arg;
100
+ });
101
+ // pad argList to length = JSBATON_ARGC
102
+ argList = argList.concat(
103
+ Array.from(new Array(JSBATON_ARGC))
104
+ ).slice(0, JSBATON_ARGC);
105
+ // prepend baton to argList
106
+ argList.unshift(baton);
107
+ // call napi with func and argList
108
+ result = addon[func](argList);
109
+ if (typeof result?.catch === "function") {
110
+ errStack = new Error().stack.replace((
111
+ /.*$/m
112
+ ), "");
113
+ return result.catch(function (err) {
114
+ err.stack += errStack;
115
+ throw err;
116
+ });
117
+ }
118
+ return result;
119
+ }
120
+
121
+ function dbCallAsync(func, db, argList) {
122
+ // this function will call <func> using <db>
123
+ db = dbDeref(db);
124
+ // increment db.busy
125
+ db.busy += 1;
126
+ return cCall(func, [
127
+ db.ptr
128
+ ].concat(argList)).finally(function () {
129
+ // decrement db.busy
130
+ db.busy -= 1;
131
+ assertOrThrow(db.busy >= 0, "invalid db.busy " + db.busy);
132
+ });
133
+ }
134
+
135
+ async function dbCloseAsync({
136
+ db
137
+ }) {
138
+ // this function will close sqlite-database-connection <db>
139
+ let __db = dbDeref(db);
140
+ // prevent segfault - do not close db if actions are pending
141
+ assertOrThrow(
142
+ __db.busy === 0,
143
+ "db cannot close with " + __db.busy + " actions pending"
144
+ );
145
+ // cleanup connPool
146
+ await Promise.all(__db.connPool.map(async function (ptr) {
147
+ let val = ptr[0];
148
+ ptr[0] = 0n;
149
+ await cCall("__dbCloseAsync", [
150
+ val
151
+ ]);
152
+ }));
153
+ dbDict.delete(db);
154
+ }
155
+
156
+ function dbDeref(db) {
157
+ // this function will get private-object mapped to <db>
158
+ let __db = dbDict.get(db);
159
+ assertOrThrow(__db?.connPool[0] > 0, "invalid or closed db");
160
+ assertOrThrow(__db.busy >= 0, "invalid db.busy " + __db.busy);
161
+ __db.ii = (__db.ii + 1) % __db.connPool.length;
162
+ __db.ptr = __db.connPool[__db.ii][0];
163
+ assertOrThrow(__db.ptr > 0n, "invalid or closed db");
164
+ return __db;
165
+ }
166
+
167
+ async function dbExecAsync({
168
+ bindList = [],
169
+ db,
170
+ responseType,
171
+ rowList,
172
+ sql
173
+ }) {
174
+ // this function will exec <sql> in <db> and return <result>
175
+ let bindByKey = !Array.isArray(bindList);
176
+ let bindListLength = (
177
+ Array.isArray(bindList)
178
+ ? bindList.length
179
+ : Object.keys(bindList).length
180
+ );
181
+ let result;
182
+ let serialize = jsToSqlSerializer();
183
+ if (rowList) {
184
+ await dbTableInsertAsync({
185
+ db,
186
+ rowList
187
+ });
188
+ }
189
+ Object.entries(bindList).forEach(function ([
190
+ key, val
191
+ ]) {
192
+ if (bindByKey) {
193
+ serialize(":" + key + "\u0000");
194
+ }
195
+ serialize(val);
196
+ });
197
+ result = await dbCallAsync("__dbExecAsync", db, [
198
+ String(sql) + "\n;\nPRAGMA noop",
199
+ bindListLength,
200
+ serialize.bufResult,
201
+ bindByKey,
202
+ (
203
+ responseType === "lastBlob"
204
+ ? 1
205
+ : responseType === "lastMatrixDouble"
206
+ ? 2
207
+ : 0
208
+ )
209
+ ].concat(serialize.bufSharedList));
210
+ result = result[1];
211
+ switch (responseType) {
212
+ case "arraybuffer":
213
+ case "lastBlob":
214
+ return result;
215
+ case "lastMatrixDouble":
216
+ return new Float64Array(result);
217
+ case "list":
218
+ return JSON.parse(new TextDecoder().decode(result));
219
+ default:
220
+ result = JSON.parse(new TextDecoder().decode(result));
221
+ return result.map(function (rowList) {
222
+ let colList = rowList.shift();
223
+ return rowList.map(function (row) {
224
+ let dict = {};
225
+ colList.forEach(function (key, ii) {
226
+ dict[key] = row[ii];
227
+ });
228
+ return dict;
229
+ });
230
+ });
231
+ }
232
+ }
233
+
234
+ function dbGetLastBlobAsync({
235
+ bindList = [],
236
+ db,
237
+ sql
238
+ }) {
239
+ // this function will exec <sql> in <db> and return last value retrieved
240
+ // from execution as raw blob/buffer
241
+ return dbExecAsync({
242
+ bindList,
243
+ db,
244
+ responseType: "lastBlob",
245
+ sql
246
+ });
247
+ }
248
+
249
+ function dbGetLastMatrixDouble({
250
+ bindList = [],
251
+ db,
252
+ sql
253
+ }) {
254
+ // this function will exec <sql> in <db> and return last SELECT-statement
255
+ // from execution as row x col matrix of doubles
256
+ return dbExecAsync({
257
+ bindList,
258
+ db,
259
+ responseType: "lastMatrixDouble",
260
+ sql
261
+ });
262
+ }
263
+
264
+ async function dbMemoryLoadAsync({
265
+ db,
266
+ filename
267
+ }) {
268
+ // This function will load <filename> to <db>
269
+ assertOrThrow(filename, "invalid filename " + filename);
270
+ await dbCallAsync("__dbMemoryLoadOrSave", db, [
271
+ String(filename), 0
272
+ ]);
273
+ }
274
+
275
+ async function dbMemorySaveAsync({
276
+ db,
277
+ filename
278
+ }) {
279
+ // This function will save <db> to <filename>
280
+ assertOrThrow(filename, "invalid filename " + filename);
281
+ await dbCallAsync("__dbMemoryLoadOrSave", db, [
282
+ String(filename), 1
283
+ ]);
284
+ }
285
+
286
+ async function dbOpenAsync({
287
+ filename,
288
+ flags,
289
+ threadCount = 1
290
+ }) {
291
+ // this function will open and return sqlite-database-connection <db>
292
+ // int sqlite3_open_v2(
293
+ // const char *filename, /* Database filename (UTF-8) */
294
+ // sqlite3 **ppDb, /* OUT: SQLite db handle */
295
+ // int flags, /* Flags */
296
+ // const char *zVfs /* Name of VFS module to use */
297
+ // );
298
+ let connPool;
299
+ let db = {};
300
+ assertOrThrow(
301
+ typeof filename === "string",
302
+ "invalid filename " + filename
303
+ );
304
+ connPool = await Promise.all(Array.from(new Array(
305
+ threadCount
306
+ ), async function () {
307
+ let finalizer;
308
+ let ptr = await cCall("__dbOpenAsync", [
309
+ String(filename), undefined, flags ?? (
310
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI
311
+ ), undefined
312
+ ]);
313
+ ptr = ptr[0][0];
314
+ finalizer = new BigInt64Array(addon.__dbFinalizerCreate());
315
+ finalizer[0] = BigInt(ptr);
316
+ return finalizer;
317
+ }));
318
+ dbDict.set(db, {
319
+ busy: 0,
320
+ connPool,
321
+ ii: 0,
322
+ ptr: 0n
323
+ });
324
+ return db;
325
+ }
326
+
327
+ async function dbTableInsertAsync({
328
+ colList,
329
+ colListPriority,
330
+ csv,
331
+ db,
332
+ rowList,
333
+ tableName = "tmp1"
334
+ }) {
335
+ // this function will create-or-replace temp <tablename> with <rowList>
336
+ let serialize = jsToSqlSerializer();
337
+ let sqlCreateTable;
338
+ let sqlInsertRow;
339
+ // normalize and validate tableName
340
+ tableName = "temp." + JSON.stringify(tableName.replace((
341
+ /^temp\./
342
+ ), ""));
343
+ assertOrThrow((
344
+ /^temp\."[A-Z_a-z][0-9A-Z_a-z]*?"$/
345
+ ).test(tableName), "invalid tableName " + tableName);
346
+ // parse csv
347
+ if (!rowList && csv) {
348
+ rowList = jsonRowListFromCsv({
349
+ csv
350
+ });
351
+ }
352
+ rowList = jsonRowListNormalize({
353
+ colList,
354
+ colListPriority,
355
+ rowList
356
+ });
357
+ colList = rowList.shift();
358
+ sqlCreateTable = (
359
+ "DROP TABLE IF EXISTS " + tableName + ";"
360
+ + "CREATE TEMP TABLE " + tableName + "(" + colList.join(",") + ");"
361
+ );
362
+ sqlInsertRow = (
363
+ "INSERT INTO " + tableName + " VALUES("
364
+ + ",?".repeat(colList.length).slice(1) + ");"
365
+ );
366
+ rowList.forEach(function (row) {
367
+ row.forEach(serialize);
368
+ });
369
+ await dbCallAsync("__dbTableInsertAsync", db, [
370
+ String(sqlCreateTable),
371
+ String(sqlInsertRow),
372
+ serialize.bufResult,
373
+ colList.length,
374
+ rowList.length
375
+ ]);
376
+ }
377
+
378
+ function jsToSqlSerializer() {
379
+ // this function will return another function that serializes javascript <val>
380
+ // to <bufResult> as sqlite-values
381
+ let BIGINT64_MAX = 2n ** 63n - 1n;
382
+ let BIGINT64_MIN = -(2n ** 63n - 1n);
383
+ let SQLITE_DATATYPE_BLOB = 0x04;
384
+ // let SQLITE_DATATYPE_BLOB_0 = 0x14;
385
+ let SQLITE_DATATYPE_FLOAT = 0x02;
386
+ // let SQLITE_DATATYPE_FLOAT_0 = 0x12;
387
+ let SQLITE_DATATYPE_INTEGER = 0x01;
388
+ let SQLITE_DATATYPE_INTEGER_0 = 0x11;
389
+ let SQLITE_DATATYPE_INTEGER_1 = 0x21;
390
+ let SQLITE_DATATYPE_NULL = 0x05;
391
+ let SQLITE_DATATYPE_SHAREDARRAYBUFFER = -0x01;
392
+ let SQLITE_DATATYPE_TEXT = 0x03;
393
+ let SQLITE_DATATYPE_TEXT_0 = 0x13;
394
+ let bufResult = new DataView(new ArrayBuffer(2048));
395
+ let bufSharedList = [];
396
+ let offset = 0;
397
+ function bufferAppendDatatype(datatype, byteLength) {
398
+ // this function will grow <bufResult> by <bytelength> and append <datatype>
399
+ let nn = offset + 1 + byteLength;
400
+ let tmp;
401
+ // exponentially grow bufResult as needed
402
+ if (bufResult.byteLength < nn) {
403
+ assertOrThrow(nn <= SQLITE_MAX_LENGTH2, (
404
+ "sqlite - string or blob exceeds size limit of "
405
+ + SQLITE_MAX_LENGTH2 + " bytes"
406
+ ));
407
+ tmp = bufResult;
408
+ bufResult = new DataView(new ArrayBuffer(
409
+ Math.min(2 ** Math.ceil(Math.log2(nn)), SQLITE_MAX_LENGTH2)
410
+ ));
411
+ // copy tmp to bufResult with offset
412
+ bufferSetBuffer(bufResult, tmp, 0);
413
+ // save bufResult
414
+ serialize.bufResult = bufResult;
415
+ }
416
+ bufResult.setUint8(offset, datatype);
417
+ offset += 1;
418
+ }
419
+ function bufferSetBigint64(offset, val) {
420
+ // this function will set bigint <val> to buffer <bufResult> at <offset>
421
+ assertOrThrow(
422
+ BIGINT64_MIN <= val && val <= BIGINT64_MAX,
423
+ (
424
+ "The value of \"value\" is out of range."
425
+ + " It must be >= -(2n ** 63n) and < 2n ** 63n."
426
+ )
427
+ );
428
+ bufResult.setBigInt64(offset, val, true);
429
+ }
430
+ function bufferSetBuffer(aa, bb, offset) {
431
+ // this function will set buffer <bb> to buffer <aa> at <offset>
432
+ if (typeof bb === "string") {
433
+ bb = new TextEncoder().encode(bb);
434
+ }
435
+ aa = new Uint8Array(aa.buffer, aa.byteOffset, aa.byteLength);
436
+ bb = new Uint8Array(bb.buffer, bb.byteOffset, bb.byteLength);
437
+ aa.set(bb, offset);
438
+ return bb.byteLength;
439
+ }
440
+ function serialize(val) {
441
+ // this function will write to <bufResult>, <val> at given <offset>
442
+ /*
443
+ #define SQLITE_DATATYPE_BLOB 0x04
444
+ #define SQLITE_DATATYPE_BLOB_0 0x14
445
+ #define SQLITE_DATATYPE_FLOAT 0x02
446
+ #define SQLITE_DATATYPE_FLOAT_0 0x12
447
+ #define SQLITE_DATATYPE_INTEGER 0x01
448
+ #define SQLITE_DATATYPE_INTEGER_0 0x11
449
+ #define SQLITE_DATATYPE_INTEGER_1 0x21
450
+ #define SQLITE_DATATYPE_NULL 0x05
451
+ #define SQLITE_DATATYPE_TEXT 0x03
452
+ #define SQLITE_DATATYPE_TEXT_0 0x13
453
+ // 1. false.bigint
454
+ // 2. false.boolean
455
+ // 3. false.function
456
+ // 4. false.number
457
+ // 5. false.object
458
+ // 6. false.string
459
+ // 7. false.symbol
460
+ // 8. false.undefined
461
+ // 11. true.bigint
462
+ // 12. true.boolean
463
+ // 13. true.function
464
+ // 14. true.number
465
+ // 15. true.object
466
+ // 16. true.string
467
+ // 17. true.symbol
468
+ // 18. true.undefined
469
+ */
470
+ // -1. SharedArrayBuffer
471
+ if (val && val.constructor === SharedArrayBuffer) {
472
+ assertOrThrow(
473
+ bufSharedList.length <= 0.5 * JSBATON_ARGC,
474
+ (
475
+ "too many SharedArrayBuffer's " + bufSharedList.length
476
+ + " > " + (0.5 * JSBATON_ARGC)
477
+ )
478
+ );
479
+ bufferAppendDatatype(SQLITE_DATATYPE_SHAREDARRAYBUFFER, 0);
480
+ bufSharedList.push(new DataView(val));
481
+ return;
482
+ }
483
+ // 12. true.boolean
484
+ if (val === 1 || val === 1n || val === true) {
485
+ bufferAppendDatatype(SQLITE_DATATYPE_INTEGER_1, 0);
486
+ return;
487
+ }
488
+ switch (Boolean(val) + "." + typeof(val)) {
489
+ // 1. false.bigint
490
+ case "false.bigint":
491
+ // 2. false.boolean
492
+ case "false.boolean":
493
+ // 4. false.number
494
+ case "false.number":
495
+ bufferAppendDatatype(SQLITE_DATATYPE_INTEGER_0, 0);
496
+ return;
497
+ // 3. false.function
498
+ // case "false.function":
499
+ // 5. false.object
500
+ case "false.object":
501
+ // 7. false.symbol
502
+ // case "false.symbol":
503
+ // 8. false.undefined
504
+ case "false.undefined":
505
+ // 13. true.function
506
+ case "true.function":
507
+ // 17. true.symbol
508
+ case "true.symbol":
509
+ // 18. true.undefined
510
+ // case "true.undefined":
511
+ bufferAppendDatatype(SQLITE_DATATYPE_NULL, 0);
512
+ return;
513
+ // 6. false.string
514
+ case "false.string":
515
+ bufferAppendDatatype(SQLITE_DATATYPE_TEXT_0, 0);
516
+ return;
517
+ // 11. true.bigint
518
+ case "true.bigint":
519
+ bufferAppendDatatype(SQLITE_DATATYPE_INTEGER, 8);
520
+ bufferSetBigint64(offset, val);
521
+ offset += 8;
522
+ return;
523
+ // 14. true.number
524
+ case "true.number":
525
+ bufferAppendDatatype(SQLITE_DATATYPE_FLOAT, 8);
526
+ bufResult.setFloat64(offset, val, true);
527
+ offset += 8;
528
+ return;
529
+ // 16. true.string
530
+ case "true.string":
531
+ val = new TextEncoder().encode(val);
532
+ bufferAppendDatatype(SQLITE_DATATYPE_TEXT, 8 + val.byteLength);
533
+ bufferSetBigint64(offset, BigInt(val.byteLength));
534
+ offset += 8;
535
+ offset += bufferSetBuffer(bufResult, val, offset);
536
+ return;
537
+ // 15. true.object
538
+ default:
539
+ assertOrThrow(
540
+ val && typeof val === "object",
541
+ "invalid data " + (typeof val) + " " + val
542
+ );
543
+ // write buffer
544
+ if (ArrayBuffer.isView(val)) {
545
+ if (val.byteLength === 0) {
546
+ bufferAppendDatatype(SQLITE_DATATYPE_NULL, 0);
547
+ return;
548
+ }
549
+ bufferAppendDatatype(
550
+ SQLITE_DATATYPE_BLOB,
551
+ 8 + val.byteLength
552
+ );
553
+ bufferSetBigint64(offset, BigInt(val.byteLength));
554
+ offset += 8;
555
+ // copy val to bufResult with offset
556
+ bufferSetBuffer(bufResult, val, offset);
557
+ offset += val.byteLength;
558
+ return;
559
+ }
560
+ // write JSON.stringify(val)
561
+ val = String(
562
+ typeof val.toJSON === "function"
563
+ ? val.toJSON()
564
+ : JSON.stringify(val)
565
+ );
566
+ val = new TextEncoder().encode(val);
567
+ bufferAppendDatatype(SQLITE_DATATYPE_TEXT, 8 + val.byteLength);
568
+ bufferSetBigint64(offset, BigInt(val.byteLength));
569
+ offset += 8;
570
+ offset += bufferSetBuffer(bufResult, val, offset);
571
+ }
572
+ }
573
+ // save bufResult
574
+ serialize.bufResult = bufResult;
575
+ // save bufSharedList
576
+ serialize.bufSharedList = bufSharedList;
577
+ return serialize;
578
+ }
579
+
580
+ function jsonRowListFromCsv({
581
+ csv
582
+ }) {
583
+ // this function will convert <csv>-text to json list-of-list
584
+ /*
585
+ https://tools.ietf.org/html/rfc4180#section-2
586
+ Definition of the CSV Format
587
+ While there are various specifications and implementations for the
588
+ CSV format (for ex. [4], [5], [6] and [7]), there is no formal
589
+ specification in existence, which allows for a wide variety of
590
+ interpretations of CSV files. This section documents the format that
591
+ seems to be followed by most implementations:
592
+ 1. Each record is located on a separate line, delimited by a line
593
+ break (CRLF). For example:
594
+ aaa,bbb,ccc CRLF
595
+ zzz,yyy,xxx CRLF
596
+ 2. The last record in the file may or may not have an ending line
597
+ break. For example:
598
+ aaa,bbb,ccc CRLF
599
+ zzz,yyy,xxx
600
+ 3. There maybe an optional header line appearing as the first line
601
+ of the file with the same format as normal record lines. This
602
+ header will contain names corresponding to the fields in the file
603
+ and should contain the same number of fields as the records in
604
+ the rest of the file (the presence or absence of the header line
605
+ should be indicated via the optional "header" parameter of this
606
+ MIME type). For example:
607
+ field_name,field_name,field_name CRLF
608
+ aaa,bbb,ccc CRLF
609
+ zzz,yyy,xxx CRLF
610
+ 4. Within the header and each record, there may be one or more
611
+ fields, separated by commas. Each line should contain the same
612
+ number of fields throughout the file. Spaces are considered part
613
+ of a field and should not be ignored. The last field in the
614
+ record must not be followed by a comma. For example:
615
+ aaa,bbb,ccc
616
+ 5. Each field may or may not be enclosed in double quotes (however
617
+ some programs, such as Microsoft Excel, do not use double quotes
618
+ at all). If fields are not enclosed with double quotes, then
619
+ double quotes may not appear inside the fields. For example:
620
+ "aaa","bbb","ccc" CRLF
621
+ zzz,yyy,xxx
622
+ 6. Fields containing line breaks (CRLF), double quotes, and commas
623
+ should be enclosed in double-quotes. For example:
624
+ "aaa","b CRLF
625
+ bb","ccc" CRLF
626
+ zzz,yyy,xxx
627
+ 7. If double-quotes are used to enclose fields, then a double-quote
628
+ appearing inside a field must be escaped by preceding it with
629
+ another double quote. For example:
630
+ "aaa","b""bb","ccc"
631
+ */
632
+ let match;
633
+ let quote = false;
634
+ let rgx = (
635
+ /(.*?)(""|"|,|\n)/g
636
+ );
637
+ let row = [];
638
+ let rowList = [];
639
+ let val = "";
640
+ // normalize "\r\n" to "\n"
641
+ csv = csv.replace((
642
+ /\r\n?/g
643
+ ), "\n");
644
+ /*
645
+ 2. The last record in the file may or may not have an ending line
646
+ break. For example:
647
+ aaa,bbb,ccc CRLF
648
+ zzz,yyy,xxx
649
+ */
650
+ if (csv[csv.length - 1] !== "\n") {
651
+ csv += "\n";
652
+ }
653
+ while (true) {
654
+ match = rgx.exec(csv);
655
+ if (!match) {
656
+ return rowList;
657
+ }
658
+ // build val
659
+ val += match[1];
660
+ match = match[2];
661
+ switch (quote + "." + match) {
662
+ case "false.,":
663
+ /*
664
+ 4. Within the header and each record, there may be one or more
665
+ fields, separated by commas. Each line should contain the same
666
+ number of fields throughout the file. Spaces are considered part
667
+ of a field and should not be ignored. The last field in the
668
+ record must not be followed by a comma. For example:
669
+ aaa,bbb,ccc
670
+ */
671
+ // delimit val
672
+ row.push(val);
673
+ val = "";
674
+ break;
675
+ case "false.\"":
676
+ case "true.\"":
677
+ /*
678
+ 5. Each field may or may not be enclosed in double quotes (however
679
+ some programs, such as Microsoft Excel, do not use double quotes
680
+ at all). If fields are not enclosed with double quotes, then
681
+ double quotes may not appear inside the fields. For example:
682
+ "aaa","bbb","ccc" CRLF
683
+ zzz,yyy,xxx
684
+ */
685
+ assertOrThrow(quote || val === "", (
686
+ "invalid csv - naked double-quote in unquoted-string "
687
+ + JSON.stringify(val + "\"")
688
+ ));
689
+ quote = !quote;
690
+ break;
691
+ // backtrack for naked-double-double-quote
692
+ case "false.\"\"":
693
+ quote = true;
694
+ rgx.lastIndex -= 1;
695
+ break;
696
+ case "false.\n":
697
+ case "false.\r\n":
698
+ /*
699
+ 1. Each record is located on a separate line, delimited by a line
700
+ break (CRLF). For example:
701
+ aaa,bbb,ccc CRLF
702
+ zzz,yyy,xxx CRLF
703
+ */
704
+ // delimit val
705
+ row.push(val);
706
+ val = "";
707
+ // append row
708
+ rowList.push(row);
709
+ // reset row
710
+ row = [];
711
+ break;
712
+ case "true.\"\"":
713
+ /*
714
+ 7. If double-quotes are used to enclose fields, then a double-quote
715
+ appearing inside a field must be escaped by preceding it with
716
+ another double quote. For example:
717
+ "aaa","b""bb","ccc"
718
+ */
719
+ val += "\"";
720
+ break;
721
+ default:
722
+ /*
723
+ 6. Fields containing line breaks (CRLF), double quotes, and commas
724
+ should be enclosed in double-quotes. For example:
725
+ "aaa","b CRLF
726
+ bb","ccc" CRLF
727
+ zzz,yyy,xxx
728
+ */
729
+ assertOrThrow(quote, (
730
+ "invalid csv - illegal character in unquoted-string "
731
+ + JSON.stringify(match)
732
+ ));
733
+ val += match;
734
+ }
735
+ }
736
+ }
737
+
738
+ function jsonRowListNormalize({
739
+ colList,
740
+ colListPriority,
741
+ rowList
742
+ }) {
743
+ // this function will normalize <rowList> with given <colList>
744
+ let colDict = {};
745
+ if (!(rowList?.length > 0)) {
746
+ throw new Error("invalid rowList " + JSON.stringify(rowList));
747
+ }
748
+ // convert list-of-dict to list-of-list
749
+ if (!Array.isArray(rowList[0])) {
750
+ colList = new Map(Array.from(
751
+ colList || []
752
+ ).map(function (key, ii) {
753
+ return [
754
+ key, ii
755
+ ];
756
+ }));
757
+ rowList = rowList.map(function (row) {
758
+ Object.keys(row).forEach(function (key) {
759
+ if (!colList.has(key)) {
760
+ colList.set(key, colList.size);
761
+ }
762
+ });
763
+ return Array.from(colList.keys()).map(function (key) {
764
+ return row[key];
765
+ });
766
+ });
767
+ colList = Array.from(colList.keys());
768
+ }
769
+ if (!colList) {
770
+ colList = rowList[0];
771
+ rowList = rowList.slice(1);
772
+ }
773
+ if (!(colList?.length > 0)) {
774
+ throw new Error("invalid colList " + JSON.stringify(colList));
775
+ }
776
+ colList = colList.map(function (key) {
777
+ // sanitize column-name
778
+ key = String(key).replace((
779
+ /^[^A-Z_a-z]/
780
+ ), "c_" + key);
781
+ key = key.replace((
782
+ /[^0-9A-Z_a-z]/g
783
+ ), "_");
784
+ // for duplicate column-name, add ordinal _2, _3, _4, ...
785
+ colDict[key] = colDict[key] || 0;
786
+ colDict[key] += 1;
787
+ if (colDict[key] > 1) {
788
+ key += "_" + colDict[key];
789
+ }
790
+ return key;
791
+ });
792
+ // normalize rowList
793
+ rowList = rowList.map(function (row) {
794
+ return (
795
+ row.length === colList.length
796
+ ? row
797
+ : colList.map(function (ignore, ii) {
798
+ return row[ii];
799
+ })
800
+ );
801
+ });
802
+ if (!colListPriority) {
803
+ rowList.unshift(colList);
804
+ return rowList;
805
+ }
806
+ // sort colList by colListPriority
807
+ colListPriority = new Map([].concat(
808
+ colListPriority,
809
+ colList
810
+ ).map(function (key) {
811
+ return [
812
+ key, colList.indexOf(key)
813
+ ];
814
+ }).filter(function ([
815
+ ignore, ii
816
+ ]) {
817
+ return ii >= 0;
818
+ }));
819
+ colList = Array.from(colListPriority.keys());
820
+ colListPriority = Array.from(colListPriority.values());
821
+ rowList = rowList.map(function (row) {
822
+ return colListPriority.map(function (ii) {
823
+ return row[ii];
824
+ });
825
+ });
826
+ rowList.unshift(colList);
827
+ return rowList;
828
+ }
829
+
830
+ function testAll() {
831
+ // this function will run all tests
832
+ testList.forEach(function (testFunc) {
833
+ testFunc();
834
+ });
835
+ }
836
+
837
+ function testAssertXxx() {
838
+ // this function will test assertXxx's handling-behavior
839
+ // test assertNumericalEqual's handling-behavior
840
+ assertNumericalEqual(1, 1);
841
+ assertErrorThrownAsync(function () {
842
+ assertNumericalEqual(0, 0);
843
+ }, "value cannot be 0 or falsy");
844
+ assertErrorThrownAsync(function () {
845
+ assertNumericalEqual(1, 2);
846
+ }, "1 != 2");
847
+ assertErrorThrownAsync(function () {
848
+ assertNumericalEqual(1, 2, "aa");
849
+ }, "aa");
850
+ }
851
+
852
+ function testCcall() {
853
+ // this function will test cCall's handling-behavior
854
+ [
855
+ [-0, "0"],
856
+ [-Infinity, "0"],
857
+ [0, "0"],
858
+ [1 / 0, "0"],
859
+ [Infinity, "0"],
860
+ [false, "0"],
861
+ [null, "0"],
862
+ [true, "1"],
863
+ [undefined, "0"],
864
+ [{}, "0"]
865
+ ].forEach(async function ([
866
+ aa, bb
867
+ ]) {
868
+ let cc;
869
+ cc = String(
870
+ await cCall("noopAsync", [
871
+ aa
872
+ ])
873
+ )[0][0];
874
+ assertOrThrow(bb === cc, [aa, bb, cc]);
875
+ cc = String(cCall("noopSync", [
876
+ aa
877
+ ]))[0][0];
878
+ assertOrThrow(bb === cc, [aa, bb, cc]);
879
+ });
880
+ }
881
+
882
+ async function testDbBind() {
883
+ // this function will test db's bind handling-behavior
884
+ let db = await dbOpenAsync({
885
+ filename: ":memory:"
886
+ });
887
+ async function testDbGetLastBlobAsync(val) {
888
+ return await dbGetLastBlobAsync({
889
+ bindList: [
890
+ val
891
+ ],
892
+ db,
893
+ sql: "SELECT 1, 2, 3; SELECT 1, 2, ?"
894
+ });
895
+ }
896
+ // test bigint-error handling-behavior
897
+ noop([
898
+ -(2n ** 63n),
899
+ 2n ** 63n
900
+ ]).forEach(function (val) {
901
+ assertErrorThrownAsync(testDbGetLastBlobAsync.bind(undefined, val));
902
+ });
903
+ // test datatype handling-behavior
904
+ [
905
+ // -1. SharedArrayBuffer
906
+ // new SharedArrayBuffer(0), null,
907
+ // 1. bigint
908
+ -0n, -0,
909
+ -0x7fffffffffffffffn, "-9223372036854775807",
910
+ -1n, -1,
911
+ -2n, -2,
912
+ 0n, 0,
913
+ 0x7fffffffffffffffn, "9223372036854775807",
914
+ 1n, 1,
915
+ 2n, 2,
916
+ // 2. boolean
917
+ false, 0,
918
+ true, 1,
919
+ // 3. function
920
+ noop, null,
921
+ // 4. number
922
+ -0, 0,
923
+ -1 / 0, null,
924
+ -1e-999, 0,
925
+ -1e999, null,
926
+ -2, -2,
927
+ -Infinity, null,
928
+ -NaN, 0,
929
+ 0, 0,
930
+ 1 / 0, null,
931
+ 1e-999, 0,
932
+ 1e999, null,
933
+ 2, 2,
934
+ Infinity, null,
935
+ NaN, 0,
936
+ // 5. object
937
+ new Uint8Array(0), null,
938
+ new TextEncoder().encode(""), null,
939
+ new TextEncoder().encode("\u0000"), null,
940
+ new TextEncoder().encode("\u0000\u{1f600}\u0000"), null,
941
+ [], "[]",
942
+ new Date(0), "1970-01-01T00:00:00.000Z",
943
+ new RegExp(), "{}",
944
+ null, null,
945
+ {}, "{}",
946
+ // 6. string
947
+ "", "",
948
+ "0", "0",
949
+ "1", "1",
950
+ "2", "2",
951
+ "\u0000", "\u0000",
952
+ "\u0000\u{1f600}\u0000", "\u0000\u{1f600}\u0000",
953
+ "a".repeat(9999), "a".repeat(9999),
954
+ // 7. symbol
955
+ Symbol(), null,
956
+ // 8. undefined
957
+ undefined, null
958
+ ].forEach(function (aa, ii, list) {
959
+ let bb = list[ii + 1];
960
+ if (ii % 2 === 1) {
961
+ return;
962
+ }
963
+ ii *= 0.5;
964
+ // test dbGetLastBlobAsync's bind handling-behavior
965
+ [
966
+ aa
967
+ ].forEach(async function (aa) {
968
+ let cc = String(bb);
969
+ let dd = new TextDecoder().decode(
970
+ await testDbGetLastBlobAsync(aa)
971
+ );
972
+ switch (typeof(aa)) {
973
+ case "bigint":
974
+ aa = Number(aa);
975
+ break;
976
+ case "function":
977
+ case "symbol":
978
+ case "undefined":
979
+ cc = "";
980
+ break;
981
+ case "number":
982
+ switch (aa) {
983
+ case -2:
984
+ cc = "-2.0";
985
+ break;
986
+ case -Infinity:
987
+ cc = "-Inf";
988
+ break;
989
+ case 2:
990
+ cc = "2.0";
991
+ break;
992
+ case Infinity:
993
+ cc = "Inf";
994
+ break;
995
+ }
996
+ break;
997
+ case "object":
998
+ if (ArrayBuffer.isView(aa)) {
999
+ cc = new TextDecoder().decode(aa);
1000
+ break;
1001
+ }
1002
+ if (aa === null) {
1003
+ cc = "";
1004
+ }
1005
+ break;
1006
+ }
1007
+ // debugInline(ii, aa, bb, cc, dd);
1008
+ assertJsonEqual(cc, dd, {
1009
+ ii,
1010
+ aa, //jslint-quiet
1011
+ bb,
1012
+ cc,
1013
+ dd
1014
+ });
1015
+ });
1016
+ // test dbGetLastMatrixDouble's bind handling-behavior
1017
+ [
1018
+ aa
1019
+ ].forEach(async function (aa) {
1020
+ let cc = Number(
1021
+ typeof aa === "symbol"
1022
+ ? 0
1023
+ : aa
1024
+ ) || 0;
1025
+ let dd = await dbGetLastMatrixDouble({
1026
+ bindList: [
1027
+ aa
1028
+ ],
1029
+ db,
1030
+ sql: "SELECT 1, 2, 3; SELECT 1, 2, ?"
1031
+ });
1032
+ switch (typeof(aa)) {
1033
+ case "bigint":
1034
+ aa = String(aa);
1035
+ break;
1036
+ case "object":
1037
+ if (typeof aa?.getUTCFullYear === "function") {
1038
+ cc = aa.getUTCFullYear();
1039
+ }
1040
+ break;
1041
+ }
1042
+ cc = new Float64Array([
1043
+ 1, 3, 1, 2, cc
1044
+ ]);
1045
+ // debugInline(ii, aa, bb, cc, dd);
1046
+ cc.forEach(function (val, jj) {
1047
+ assertJsonEqual(
1048
+ val,
1049
+ dd[jj],
1050
+ {
1051
+ ii,
1052
+ aa, //jslint-quiet
1053
+ bb,
1054
+ cc,
1055
+ dd
1056
+ }
1057
+ );
1058
+ });
1059
+ });
1060
+ // test dbExecAsync's responseType handling-behavior
1061
+ [
1062
+ "arraybuffer",
1063
+ "list",
1064
+ undefined
1065
+ ].forEach(async function (responseType) {
1066
+ let cc = noop(
1067
+ await dbExecAsync({
1068
+ bindList: [
1069
+ aa
1070
+ ],
1071
+ db,
1072
+ responseType,
1073
+ sql: "SELECT ? AS val"
1074
+ })
1075
+ );
1076
+ // debugInline(ii, responseType, aa, bb, cc);
1077
+ switch (responseType) {
1078
+ case "arraybuffer":
1079
+ cc = JSON.parse(new TextDecoder().decode(cc))[0][1][0];
1080
+ break;
1081
+ case "list":
1082
+ cc = cc[0][1][0];
1083
+ break;
1084
+ default:
1085
+ cc = cc[0][0].val;
1086
+ }
1087
+ assertJsonEqual(bb, cc, {
1088
+ aa,
1089
+ bb,
1090
+ cc
1091
+ });
1092
+ });
1093
+ // test dbExecAsync's bind handling-behavior
1094
+ [
1095
+ [
1096
+ [
1097
+ bb, bb, 0
1098
+ ],
1099
+ (
1100
+ "SELECT 0;"
1101
+ + " SELECT ? AS c1, ? AS c2, ? AS c3, ? AS c4"
1102
+ + " UNION ALL SELECT ?1, ?2, ?3, ?4"
1103
+ + " UNION ALL SELECT ?1, ?2, ?3, ?4"
1104
+ )
1105
+ ], [
1106
+ {
1107
+ k1: bb,
1108
+ k2: bb,
1109
+ k3: 0
1110
+ },
1111
+ (
1112
+ "SELECT 0;"
1113
+ + " SELECT $k1 AS c1, $k2 AS c2, $k3 AS c3, $k4 AS c4"
1114
+ + " UNION ALL SELECT :k1, :k2, :k3, :k4"
1115
+ + " UNION ALL SELECT @k1, @k2, @k3, @k4"
1116
+ )
1117
+ ]
1118
+ ].forEach(async function ([
1119
+ bindList, sql
1120
+ ]) {
1121
+ let cc = noop(
1122
+ await dbExecAsync({
1123
+ bindList,
1124
+ db,
1125
+ responseType: "list",
1126
+ sql
1127
+ })
1128
+ );
1129
+ // debugInline(ii, aa, bb, cc);
1130
+ assertJsonEqual(
1131
+ [
1132
+ [
1133
+ [
1134
+ "0"
1135
+ ], [
1136
+ 0
1137
+ ]
1138
+ ], [
1139
+ [
1140
+ "c1", "c2", "c3", "c4"
1141
+ ], [
1142
+ bb, bb, 0, undefined
1143
+ ], [
1144
+ bb, bb, 0, undefined
1145
+ ], [
1146
+ bb, bb, 0, undefined
1147
+ ]
1148
+ ]
1149
+ ],
1150
+ cc
1151
+ );
1152
+ });
1153
+ // test dbTableInsertAsync's bind handling-behavior
1154
+ [
1155
+ {
1156
+ // test list-of-list handling-behavior
1157
+ rowList: [
1158
+ [
1159
+ "c1", "c2", "c3"
1160
+ ],
1161
+ [
1162
+ aa, aa
1163
+ ]
1164
+ ]
1165
+ }, {
1166
+ // test list-of-dict handling-behavior
1167
+ rowList: [
1168
+ {
1169
+ "c1": aa,
1170
+ "c2": aa,
1171
+ "c3": undefined
1172
+ }
1173
+ ]
1174
+ }, {
1175
+ // test colList and list-of-list handling-behavior
1176
+ colList: [
1177
+ "c1", "c2", "c3"
1178
+ ],
1179
+ rowList: [
1180
+ [
1181
+ aa, aa
1182
+ ]
1183
+ ]
1184
+ }, {
1185
+ // test colList and list-of-dict handling-behavior
1186
+ colList: [
1187
+ "c1", "c2", "c3"
1188
+ ],
1189
+ rowList: [
1190
+ {
1191
+ "c1": aa,
1192
+ "c2": aa,
1193
+ "c3": undefined
1194
+ }
1195
+ ]
1196
+ }, {
1197
+ // test colList and list-of-list handling-behavior
1198
+ colList: [
1199
+ "c1", "c3", "c2"
1200
+ ],
1201
+ colListPriority: [
1202
+ "c1", "c2"
1203
+ ],
1204
+ rowList: [
1205
+ [
1206
+ aa, undefined, aa
1207
+ ]
1208
+ ]
1209
+ }
1210
+ ].forEach(async function ({
1211
+ colList,
1212
+ colListPriority,
1213
+ rowList
1214
+ }, jj) {
1215
+ let cc;
1216
+ await dbTableInsertAsync({
1217
+ colList,
1218
+ colListPriority,
1219
+ db,
1220
+ rowList,
1221
+ tableName: "datatype_" + ii + "_" + jj
1222
+ });
1223
+ cc = noop(
1224
+ await dbExecAsync({
1225
+ db,
1226
+ responseType: "list",
1227
+ sql: "SELECT * FROM datatype_" + ii + "_" + jj
1228
+ })
1229
+ )[0];
1230
+ // debugInline(ii, jj, aa, bb, cc);
1231
+ assertJsonEqual([
1232
+ [
1233
+ "c1", "c2", "c3"
1234
+ ], [
1235
+ bb, bb, undefined
1236
+ ]
1237
+ ], cc);
1238
+ });
1239
+ });
1240
+ }
1241
+
1242
+ async function testDbCloseAsync() {
1243
+ // this function will test dbCloseAsync's handling-behavior
1244
+ let db = await dbOpenAsync({
1245
+ filename: ":memory:"
1246
+ });
1247
+ // test null-case handling-behavior
1248
+ assertErrorThrownAsync(function () {
1249
+ return dbCloseAsync({});
1250
+ }, "invalid or closed db");
1251
+ // test close handling-behavior
1252
+ await dbCloseAsync({
1253
+ db
1254
+ });
1255
+ }
1256
+
1257
+ async function testDbExecAsync() {
1258
+ // this function will test dbExecAsync's handling-behavior
1259
+ let db = await dbOpenAsync({
1260
+ filename: ":memory:"
1261
+ });
1262
+ // test null-case handling-behavior
1263
+ assertErrorThrownAsync(function () {
1264
+ return dbExecAsync({
1265
+ db,
1266
+ sql: undefined
1267
+ });
1268
+ }, "near \"undefined\": syntax error");
1269
+ // test race-condition handling-behavior
1270
+ Array.from(new Array(4)).forEach(async function () {
1271
+ let result;
1272
+ try {
1273
+ result = JSON.stringify(
1274
+ await dbExecAsync({
1275
+ bindList: [
1276
+ new TextEncoder().encode("foob"),
1277
+ new TextEncoder().encode("fooba"),
1278
+ new TextEncoder().encode("foobar")
1279
+ ],
1280
+ db,
1281
+ responseType: "list",
1282
+ sql: (`
1283
+ CREATE TABLE testDbExecAsync1 AS
1284
+ SELECT 101 AS c101, 102 AS c102
1285
+ UNION ALL
1286
+ VALUES
1287
+ (201, 202),
1288
+ (301, NULL);
1289
+ CREATE TABLE testDbExecAsync2 AS
1290
+ SELECT 401 AS c401, 402 AS c402, 403 AS c403
1291
+ UNION ALL
1292
+ VALUES
1293
+ (501, 502.0123, 5030123456789),
1294
+ (601, '602', '603_\"\x01\x08\x09\x0a\x0b\x0c\x0d\x0e'),
1295
+ (?1, ?2, ?3),
1296
+ (tostring(?1), tostring(?2), tostring(?3)),
1297
+ (tobase64(?1), tobase64(?2), tobase64(?3)),
1298
+ (
1299
+ tobase64(uncompress(compress(?1))),
1300
+ tobase64(uncompress(compress(?2))),
1301
+ tobase64(uncompress(compress(?3)))
1302
+ );
1303
+ SELECT * FROM testDbExecAsync1;
1304
+ SELECT * FROM testDbExecAsync2;
1305
+ `)
1306
+ })
1307
+ );
1308
+ assertJsonEqual(result, JSON.stringify([
1309
+ [
1310
+ ["c101", "c102"],
1311
+ [101, 102],
1312
+ [201, 202],
1313
+ [301, null]
1314
+ ],
1315
+ [
1316
+ ["c401", "c402", "c403"],
1317
+ [401, 402, 403],
1318
+ [501, 502.0123, 5030123456789],
1319
+ [601, "602", "603_\"\u0001\b\t\n\u000b\f\r\u000e"],
1320
+ [
1321
+ null, null, null
1322
+ ],
1323
+ [
1324
+ "foob", "fooba", "foobar"
1325
+ ],
1326
+ [
1327
+ "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"
1328
+ ],
1329
+ [
1330
+ "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"
1331
+ ]
1332
+ ]
1333
+ ]));
1334
+ } catch (err) {
1335
+ assertOrThrow(
1336
+ err.message.indexOf(
1337
+ "table testDbExecAsync1 already exists"
1338
+ ) >= 0,
1339
+ err
1340
+ );
1341
+ }
1342
+ });
1343
+ // test close-while-busy handling-behavior
1344
+ assertErrorThrownAsync(function () {
1345
+ return dbCloseAsync({
1346
+ db
1347
+ });
1348
+ }, (
1349
+ /db cannot close with \d+? actions pending/
1350
+ ));
1351
+ // test sqlmath-defined-func handling-behavior
1352
+ [
1353
+ "COT('-1')", -0.642092615934331,
1354
+ "COT('0')", null,
1355
+ "COT('1')", 0.642092615934331,
1356
+ "COT(-1)", -0.642092615934331,
1357
+ "COT(0)", null,
1358
+ "COT(1)", 0.642092615934331,
1359
+ "COTH('-1')", -1.31303528549933,
1360
+ "COTH('0')", null,
1361
+ "COTH('1')", 1.31303528549933,
1362
+ "COTH(-1)", -1.31303528549933,
1363
+ "COTH(0)", null,
1364
+ "COTH(1)", 1.31303528549933,
1365
+ "SIGN('-1')", -1,
1366
+ "SIGN('0')", 0,
1367
+ "SIGN('1')", 1,
1368
+ "SIGN(-0x7fffffffffffffff)", -1,
1369
+ "SIGN(-1)", -1,
1370
+ "SIGN(-1e999)", -1,
1371
+ "SIGN(0)", 0,
1372
+ "SIGN(0x7fffffffffffffff)", 1,
1373
+ "SIGN(0x8000000000000000)", -1,
1374
+ "SIGN(0xffffffffffffffff)", -1,
1375
+ "SIGN(1)", 1,
1376
+ "SIGN(1e999)", 1,
1377
+ "SIGN(NULL)", null,
1378
+ // sentinel
1379
+ "NULL", null
1380
+ ].forEach(async function (sql, ii, list) {
1381
+ let bb = list[ii + 1];
1382
+ let cc;
1383
+ if (ii % 2 === 1) {
1384
+ return;
1385
+ }
1386
+ ii *= 0.5;
1387
+ cc = noop(
1388
+ await dbExecAsync({
1389
+ db,
1390
+ responseType: "dict",
1391
+ sql: "SELECT " + sql + " AS val"
1392
+ })
1393
+ )[0][0].val;
1394
+ assertOrThrow(bb === cc, JSON.stringify([
1395
+ ii, sql, bb, cc
1396
+ ]));
1397
+ });
1398
+ }
1399
+
1400
+ async function testDbMemoryXxx() {
1401
+ // this function will test dbMemoryXxx's handling-behavior
1402
+ let data;
1403
+ let db = await dbOpenAsync({
1404
+ filename: ":memory:"
1405
+ });
1406
+ // test null-case handling-behavior
1407
+ assertErrorThrownAsync(function () {
1408
+ return dbMemoryLoadAsync({
1409
+ db
1410
+ });
1411
+ }, "invalid filename undefined");
1412
+ assertErrorThrownAsync(function () {
1413
+ return dbMemorySaveAsync({
1414
+ db
1415
+ });
1416
+ }, "invalid filename undefined");
1417
+ await dbExecAsync({
1418
+ db,
1419
+ sql: "CREATE TABLE t01 AS SELECT 1 AS c01"
1420
+ });
1421
+ await dbMemorySaveAsync({
1422
+ db,
1423
+ filename: ".testDbMemoryXxx.sqlite"
1424
+ });
1425
+ db = await dbOpenAsync({
1426
+ filename: ":memory:"
1427
+ });
1428
+ await dbMemoryLoadAsync({
1429
+ db,
1430
+ filename: ".testDbMemoryXxx.sqlite"
1431
+ });
1432
+ data = await dbExecAsync({
1433
+ db,
1434
+ sql: "SELECT * FROM t01"
1435
+ });
1436
+ assertJsonEqual(data, [
1437
+ [
1438
+ {
1439
+ c01: 1
1440
+ }
1441
+ ]
1442
+ ]);
1443
+ }
1444
+
1445
+ function testDbOpenAsync() {
1446
+ // this function will test dbOpenAsync's handling-behavior
1447
+ // test null-case handling-behavior
1448
+ assertErrorThrownAsync(function () {
1449
+ return dbOpenAsync({});
1450
+ }, "invalid filename undefined");
1451
+ }
1452
+
1453
+ async function testDbTableInsertAsync() {
1454
+ // this function will test dbTableInsertAsync's handling-behavior
1455
+ let db = await dbOpenAsync({
1456
+ filename: ":memory:"
1457
+ });
1458
+ // test error handling-behavior
1459
+ [
1460
+ [
1461
+ undefined,
1462
+ (
1463
+ /invalid rowList undefined/
1464
+ )
1465
+ ], [
1466
+ [
1467
+ []
1468
+ ],
1469
+ (
1470
+ /invalid colList \[\]/
1471
+ )
1472
+ ], [
1473
+ [
1474
+ {}
1475
+ ],
1476
+ (
1477
+ /invalid colList \[\]/
1478
+ )
1479
+ ]
1480
+ ].forEach(function ([
1481
+ rowList, rgx
1482
+ ]) {
1483
+ assertErrorThrownAsync(function () {
1484
+ return dbTableInsertAsync({
1485
+ rowList
1486
+ });
1487
+ }, rgx);
1488
+ });
1489
+ // test csv handling-behavior
1490
+ [
1491
+ [
1492
+ "0", undefined
1493
+ ],
1494
+ [
1495
+ "0,0,0\n1,1,1",
1496
+ [
1497
+ [
1498
+ "c_0", "c_0_2", "c_0_3"
1499
+ ], [
1500
+ "1", "1", "1"
1501
+ ]
1502
+ ]
1503
+ ],
1504
+ [
1505
+ (
1506
+ "c1,c1,c2\n"
1507
+ + "1, 2 \n"
1508
+ + `"1","""2""","3\r\n"\n`
1509
+ + "\n"
1510
+ + "1,2,3\n"
1511
+ ),
1512
+ [
1513
+ [
1514
+ "c1", "c1_2", "c2"
1515
+ ], [
1516
+ "1", " 2 ", null
1517
+ ], [
1518
+ "1", "\"2\"", "3\n"
1519
+ ], [
1520
+ "", null, null
1521
+ ], [
1522
+ "1", "2", "3"
1523
+ ]
1524
+ ]
1525
+ ]
1526
+ ].forEach(async function ([
1527
+ aa, bb
1528
+ ], ii) {
1529
+ let cc;
1530
+ await dbTableInsertAsync({
1531
+ csv: aa,
1532
+ db,
1533
+ tableName: "csv_" + ii
1534
+ });
1535
+ cc = noop(
1536
+ await dbExecAsync({
1537
+ db,
1538
+ responseType: "list",
1539
+ sql: "SELECT * FROM temp.csv_" + ii
1540
+ })
1541
+ )[0];
1542
+ assertOrThrow(
1543
+ JSON.stringify(bb) === JSON.stringify(cc),
1544
+ JSON.stringify([
1545
+ ii, aa, bb, cc
1546
+ ], undefined, 4)
1547
+ );
1548
+ });
1549
+ }
1550
+
1551
+ async function testSqlError() {
1552
+ // this function will test sql-error's handling-behavior
1553
+ let db = await dbOpenAsync({
1554
+ filename: ":memory:"
1555
+ });
1556
+ [
1557
+ 1, "SQL logic error",
1558
+ 2, "unknown error",
1559
+ 3, "access permission denied",
1560
+ 4, "query aborted",
1561
+ 5, "database is locked",
1562
+ 6, "database table is locked",
1563
+ 7, "out of memory",
1564
+ 8, "attempt to write a readonly database",
1565
+ 9, "interrupted",
1566
+ 10, "disk I/O error",
1567
+ 11, "database disk image is malformed",
1568
+ 12, "unknown operation",
1569
+ 13, "database or disk is full",
1570
+ 14, "unable to open database file",
1571
+ 15, "locking protocol",
1572
+ 16, "unknown error",
1573
+ 17, "database schema has changed",
1574
+ 18, "string or blob too big",
1575
+ 19, "constraint failed",
1576
+ 20, "datatype mismatch",
1577
+ 21, "bad parameter or other API misuse",
1578
+ 22, "unknown error",
1579
+ 23, "authorization denied",
1580
+ 24, "unknown error",
1581
+ 25, "column index out of range",
1582
+ 26, "file is not a database",
1583
+ 27, "notification message",
1584
+ 28, "warning message",
1585
+ 100, "unknown error",
1586
+ 101, "unknown error"
1587
+ ].forEach(function (sql, ii, list) {
1588
+ let bb = list[ii + 1];
1589
+ if (ii % 2 === 1) {
1590
+ return;
1591
+ }
1592
+ ii *= 0.5;
1593
+ sql = "SELECT throwerror(" + sql + ")";
1594
+ assertErrorThrownAsync(function () {
1595
+ return dbExecAsync({
1596
+ db,
1597
+ sql
1598
+ });
1599
+ }, bb);
1600
+ });
1601
+ }
1602
+
1603
+ async function testSqlExt() {
1604
+ // this function will test sql-ext's handling-behavior
1605
+ let db = await dbOpenAsync({
1606
+ filename: ":memory:"
1607
+ });
1608
+ [
1609
+ {
1610
+ aa: [
1611
+ [
1612
+ {
1613
+ "c01": ""
1614
+ }
1615
+ ]
1616
+ ],
1617
+ sql: "SELECT tobase64(NULL) AS c01"
1618
+ },
1619
+ {
1620
+ aa: [
1621
+ [
1622
+ {
1623
+ "c01": ""
1624
+ }
1625
+ ]
1626
+ ],
1627
+ sql: "SELECT tobase64(?) AS c01"
1628
+ },
1629
+ {
1630
+ aa: [
1631
+ [
1632
+ {
1633
+ "c01": "AAAAAAAAAAA="
1634
+ }
1635
+ ]
1636
+ ],
1637
+ bindList: [
1638
+ new Uint8Array(8)
1639
+ ],
1640
+ sql: "SELECT tobase64(uncompress(compress(?))) AS c01"
1641
+ }
1642
+ ].forEach(async function ({
1643
+ aa,
1644
+ bindList,
1645
+ sql
1646
+ }) {
1647
+ let bb = await dbExecAsync({
1648
+ bindList,
1649
+ db,
1650
+ sql
1651
+ });
1652
+ assertJsonEqual(aa, bb);
1653
+ });
1654
+ }
1655
+
1656
+ addon = requireCjs(
1657
+ "./_binary_sqlmath"
1658
+ + "_napi8"
1659
+ + "_" + process.platform
1660
+ + "_" + process.arch
1661
+ + ".node"
1662
+ );
1663
+ testList = [
1664
+ testAssertXxx,
1665
+ testCcall,
1666
+ testDbBind,
1667
+ testDbCloseAsync,
1668
+ testDbExecAsync,
1669
+ testDbMemoryXxx,
1670
+ testDbOpenAsync,
1671
+ testDbTableInsertAsync,
1672
+ testSqlError,
1673
+ testSqlExt
1674
+ ];
1675
+ Object.assign(local, {
1676
+ SQLITE_MAX_LENGTH2,
1677
+ SQLITE_OPEN_AUTOPROXY,
1678
+ SQLITE_OPEN_CREATE,
1679
+ SQLITE_OPEN_DELETEONCLOSE,
1680
+ SQLITE_OPEN_EXCLUSIVE,
1681
+ SQLITE_OPEN_FULLMUTEX,
1682
+ SQLITE_OPEN_MAIN_DB,
1683
+ SQLITE_OPEN_MAIN_JOURNAL,
1684
+ SQLITE_OPEN_MEMORY,
1685
+ SQLITE_OPEN_NOFOLLOW,
1686
+ SQLITE_OPEN_NOMUTEX,
1687
+ SQLITE_OPEN_PRIVATECACHE,
1688
+ SQLITE_OPEN_READONLY,
1689
+ SQLITE_OPEN_READWRITE,
1690
+ SQLITE_OPEN_SHAREDCACHE,
1691
+ SQLITE_OPEN_SUBJOURNAL,
1692
+ SQLITE_OPEN_SUPER_JOURNAL,
1693
+ SQLITE_OPEN_TEMP_DB,
1694
+ SQLITE_OPEN_TEMP_JOURNAL,
1695
+ SQLITE_OPEN_TRANSIENT_DB,
1696
+ SQLITE_OPEN_URI,
1697
+ SQLITE_OPEN_WAL,
1698
+ assertErrorThrownAsync,
1699
+ assertNumericalEqual,
1700
+ dbCloseAsync,
1701
+ dbExecAsync,
1702
+ dbGetLastBlobAsync,
1703
+ dbMemoryLoadAsync,
1704
+ dbMemorySaveAsync,
1705
+ dbOpenAsync,
1706
+ dbTableInsertAsync,
1707
+ debugInline,
1708
+ testAll,
1709
+ testList
1710
+ });
1711
+ }());
1712
+
1713
+ export default Object.freeze(local);