sqlmath 2022.5.20 → 2022.6.30

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/sqlmath.mjs CHANGED
@@ -22,16 +22,91 @@
22
22
 
23
23
 
24
24
  /*jslint beta, bitwise, name, node*/
25
+ /*global FinalizationRegistry*/
25
26
  "use strict";
26
- import {createRequire} from "module";
27
- import jslint from "./jslint.mjs";
28
27
 
29
- let {
30
- assertOrThrow,
31
- debugInline,
32
- noop
33
- } = jslint;
34
- let local = Object.assign({}, jslint);
28
+ let FILENAME_DBTMP = "/tmp/__dbtmp1";
29
+ let IS_BROWSER;
30
+ let JSBATON_ARGC = 16;
31
+ let SQLITE_DATATYPE_BLOB = 0x04;
32
+ // let SQLITE_DATATYPE_BLOB_0 = 0x14;
33
+ let SQLITE_DATATYPE_EXTERNALBUFFER = -0x01;
34
+ let SQLITE_DATATYPE_FLOAT = 0x02;
35
+ // let SQLITE_DATATYPE_FLOAT_0 = 0x12;
36
+ let SQLITE_DATATYPE_INTEGER = 0x01;
37
+ let SQLITE_DATATYPE_INTEGER_0 = 0x11;
38
+ let SQLITE_DATATYPE_INTEGER_1 = 0x21;
39
+ let SQLITE_DATATYPE_NULL = 0x05;
40
+ let SQLITE_DATATYPE_OFFSET = 768;
41
+ let SQLITE_DATATYPE_TEXT = 0x03;
42
+ let SQLITE_DATATYPE_TEXT_0 = 0x13;
43
+ let SQLITE_MAX_LENGTH2 = 1_000_000_000;
44
+ let SQLITE_OPEN_AUTOPROXY = 0x00000020; /* VFS only */
45
+ let SQLITE_OPEN_CREATE = 0x00000004; /* Ok for sqlite3_open_v2() */
46
+ let SQLITE_OPEN_DELETEONCLOSE = 0x00000008; /* VFS only */
47
+ let SQLITE_OPEN_EXCLUSIVE = 0x00000010; /* VFS only */
48
+ let SQLITE_OPEN_FULLMUTEX = 0x00010000; /* Ok for sqlite3_open_v2() */
49
+ let SQLITE_OPEN_MAIN_DB = 0x00000100; /* VFS only */
50
+ let SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; /* VFS only */
51
+ let SQLITE_OPEN_MEMORY = 0x00000080; /* Ok for sqlite3_open_v2() */
52
+ let SQLITE_OPEN_NOFOLLOW = 0x01000000; /* Ok for sqlite3_open_v2() */
53
+ let SQLITE_OPEN_NOMUTEX = 0x00008000; /* Ok for sqlite3_open_v2() */
54
+ let SQLITE_OPEN_PRIVATECACHE = 0x00040000; /* Ok for sqlite3_open_v2() */
55
+ let SQLITE_OPEN_READONLY = 0x00000001; /* Ok for sqlite3_open_v2() */
56
+ let SQLITE_OPEN_READWRITE = 0x00000002; /* Ok for sqlite3_open_v2() */
57
+ let SQLITE_OPEN_SHAREDCACHE = 0x00020000; /* Ok for sqlite3_open_v2() */
58
+ let SQLITE_OPEN_SUBJOURNAL = 0x00002000; /* VFS only */
59
+ let SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; /* VFS only */
60
+ let SQLITE_OPEN_TEMP_DB = 0x00000200; /* VFS only */
61
+ let SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; /* VFS only */
62
+ let SQLITE_OPEN_TRANSIENT_DB = 0x00000400; /* VFS only */
63
+ let SQLITE_OPEN_URI = 0x00000040; /* Ok for sqlite3_open_v2() */
64
+ let SQLITE_OPEN_WAL = 0x00080000; /* VFS only */
65
+ let cModule;
66
+ let consoleError = console.error;
67
+ let dbDict = new WeakMap(); // private-dict of sqlite-database-connections
68
+ let dbFinalizationRegistry;
69
+ // init debugInline
70
+ let debugInline = (function () {
71
+ let __consoleError = function () {
72
+ return;
73
+ };
74
+ function debug(...argv) {
75
+
76
+ // This function will print <argv> to stderr and then return <argv>[0].
77
+
78
+ __consoleError("\n\ndebugInline");
79
+ __consoleError(...argv);
80
+ __consoleError("\n");
81
+ return argv[0];
82
+ }
83
+ debug(); // Coverage-hack.
84
+ __consoleError = console.error;
85
+ return debug;
86
+ }());
87
+ let sqlMessageDict = {}; // dict of web-worker-callbacks
88
+ let sqlMessageId = 0;
89
+ let sqlWorker;
90
+
91
+ function assertJsonEqual(aa, bb, message) {
92
+
93
+ // This function will assert JSON.stringify(<aa>) === JSON.stringify(<bb>).
94
+
95
+ aa = JSON.stringify(objectDeepCopyWithKeysSorted(aa), undefined, 1);
96
+ bb = JSON.stringify(objectDeepCopyWithKeysSorted(bb), undefined, 1);
97
+ if (aa !== bb) {
98
+ throw new Error(
99
+ "\n" + aa + "\n!==\n" + bb
100
+ + (
101
+ typeof message === "string"
102
+ ? " - " + message
103
+ : message
104
+ ? " - " + JSON.stringify(message)
105
+ : ""
106
+ )
107
+ );
108
+ }
109
+ }
35
110
 
36
111
  function assertNumericalEqual(aa, bb, message) {
37
112
 
@@ -49,276 +124,324 @@ function assertNumericalEqual(aa, bb, message) {
49
124
  }
50
125
  }
51
126
 
127
+ function assertOrThrow(condition, message) {
52
128
 
53
- /*
54
- file sqlmath.js
55
- */
56
- (function () {
57
- let JSBATON_ARGC = 16;
58
- // let SIZEOF_MESSAGE_DEFAULT = 768;
59
- let SQLITE_MAX_LENGTH2 = 1000000000;
60
- let SQLITE_OPEN_AUTOPROXY = 0x00000020; /* VFS only */
61
- let SQLITE_OPEN_CREATE = 0x00000004; /* Ok for sqlite3_open_v2() */
62
- let SQLITE_OPEN_DELETEONCLOSE = 0x00000008; /* VFS only */
63
- let SQLITE_OPEN_EXCLUSIVE = 0x00000010; /* VFS only */
64
- let SQLITE_OPEN_FULLMUTEX = 0x00010000; /* Ok for sqlite3_open_v2() */
65
- let SQLITE_OPEN_MAIN_DB = 0x00000100; /* VFS only */
66
- let SQLITE_OPEN_MAIN_JOURNAL = 0x00000800; /* VFS only */
67
- let SQLITE_OPEN_MEMORY = 0x00000080; /* Ok for sqlite3_open_v2() */
68
- let SQLITE_OPEN_NOFOLLOW = 0x01000000; /* Ok for sqlite3_open_v2() */
69
- let SQLITE_OPEN_NOMUTEX = 0x00008000; /* Ok for sqlite3_open_v2() */
70
- let SQLITE_OPEN_PRIVATECACHE = 0x00040000; /* Ok for sqlite3_open_v2() */
71
- let SQLITE_OPEN_READONLY = 0x00000001; /* Ok for sqlite3_open_v2() */
72
- let SQLITE_OPEN_READWRITE = 0x00000002; /* Ok for sqlite3_open_v2() */
73
- let SQLITE_OPEN_SHAREDCACHE = 0x00020000; /* Ok for sqlite3_open_v2() */
74
- let SQLITE_OPEN_SUBJOURNAL = 0x00002000; /* VFS only */
75
- let SQLITE_OPEN_SUPER_JOURNAL = 0x00004000; /* VFS only */
76
- let SQLITE_OPEN_TEMP_DB = 0x00000200; /* VFS only */
77
- let SQLITE_OPEN_TEMP_JOURNAL = 0x00001000; /* VFS only */
78
- let SQLITE_OPEN_TRANSIENT_DB = 0x00000400; /* VFS only */
79
- let SQLITE_OPEN_URI = 0x00000040; /* Ok for sqlite3_open_v2() */
80
- let SQLITE_OPEN_WAL = 0x00080000; /* VFS only */
81
- let addon;
82
- let consoleError = console.error;
83
- let dbDict = new WeakMap(); // private map of sqlite-database-connections
84
- let requireCjs = createRequire(import.meta.url);
85
-
86
- function cCall(func, argList) {
129
+ // This function will throw <message> if <condition> is falsy.
130
+
131
+ if (!condition) {
132
+ throw (
133
+ (!message || typeof message === "string")
134
+ ? new Error(String(message).slice(0, 2048))
135
+ : message
136
+ );
137
+ }
138
+ }
139
+
140
+ async function cCallAsync(baton, cFuncName, ...argList) {
87
141
  // this function will serialize <argList> to a c <baton>,
88
142
  // suitable for passing into napi
89
- let baton = new BigInt64Array(2048);
90
- let errStack;
91
- let result;
92
- // serialize js-args to c-args
93
- argList = argList.map(function (arg, ii) {
94
- switch (typeof arg) {
95
- case "bigint":
96
- case "boolean":
97
- case "number":
98
- try {
99
- baton[ii] = BigInt(arg);
100
- } catch (ignore) {
101
- return;
102
- }
103
- break;
104
- // case "object":
105
- // break;
106
- case "string":
107
- // append null-terminator to string
108
- arg = new TextEncoder().encode(arg + "\u0000");
109
- break;
110
- }
111
- if (ArrayBuffer.isView(arg)) {
112
- baton[ii] = BigInt(arg.byteLength);
113
- return new DataView(
114
- arg.buffer,
115
- arg.byteOffset,
116
- arg.byteLength
117
- );
118
- }
119
- return arg;
120
- });
121
- // pad argList to length = JSBATON_ARGC
122
- argList = argList.concat(
123
- Array.from(new Array(JSBATON_ARGC))
124
- ).slice(0, JSBATON_ARGC);
125
- // prepend baton to argList
126
- argList.unshift(baton);
127
- // call napi with func and argList
128
- result = addon[func](argList);
129
- if (typeof result?.catch === "function") {
130
- errStack = new Error().stack.replace((
131
- /.*$/m
132
- ), "");
133
- return result.catch(function (err) {
134
- err.stack += errStack;
135
- throw err;
143
+ let argi = 0;
144
+ let errStack;
145
+ assertOrThrow(
146
+ argList.length < 16,
147
+ "cCallAsync - argList.length must be less than than 16"
148
+ );
149
+ baton = baton || jsbatonCreate();
150
+ // pad argList to length JSBATON_ARGC
151
+ while (argList.length < 2 * JSBATON_ARGC) {
152
+ argList.push(0n);
153
+ }
154
+ // serialize js-value to c-value
155
+ argList = argList.map(function (value, ii) {
156
+ argi = ii;
157
+ switch (typeof value) {
158
+ case "bigint":
159
+ case "boolean":
160
+ baton.setBigInt64(8 + argi * 8, BigInt(value), true);
161
+ return value;
162
+ case "number":
163
+ // check for min/max safe-integer
164
+ assertOrThrow(
165
+ (
166
+ -9_007_199_254_740_991 <= value
167
+ && value <= 9_007_199_254_740_991
168
+ ),
169
+ (
170
+ "non-bigint integer must be within inclusive-range"
171
+ + " -9,007,199,254,740,991 to 9,007,199,254,740,991"
172
+ )
173
+ );
174
+ baton.setBigInt64(8 + argi * 8, BigInt(value), true);
175
+ return value;
176
+ // case "object":
177
+ // break;
178
+ case "string":
179
+ baton = jsbatonValuePush({
180
+ argi,
181
+ baton,
182
+ value: (
183
+ value.endsWith("\u0000")
184
+ ? value
185
+ // append null-terminator to string
186
+ : value + "\u0000"
187
+ )
136
188
  });
189
+ return;
137
190
  }
138
- return result;
191
+ if (ArrayBuffer.isView(value)) {
192
+ return new DataView(
193
+ value.buffer,
194
+ value.byteOffset,
195
+ value.byteLength
196
+ );
197
+ }
198
+ if (isExternalBuffer(value)) {
199
+ return value;
200
+ }
201
+ });
202
+ // encode cFuncName into baton
203
+ argi += 1;
204
+ baton = jsbatonValuePush({
205
+ argi,
206
+ baton,
207
+ value: cFuncName + "\u0000"
208
+ });
209
+ // prepend baton, cFuncName to argList
210
+ argList = [
211
+ baton, cFuncName, ...argList
212
+ ];
213
+ // preserve stack-trace
214
+ errStack = new Error().stack.replace((
215
+ /.*$/m
216
+ ), "");
217
+ try {
218
+ return (
219
+ IS_BROWSER
220
+ ? await sqlMessagePost(...argList)
221
+ : await cModule[cFuncName](argList)
222
+ );
223
+ } catch (err) {
224
+ err.stack += errStack;
225
+ assertOrThrow(undefined, err);
139
226
  }
227
+ }
140
228
 
141
- function dbCallAsync(func, db, argList) {
142
- // this function will call <func> using <db>
143
- db = dbDeref(db);
144
- // increment db.busy
145
- db.busy += 1;
146
- return cCall(func, [
147
- db.ptr
148
- ].concat(argList)).finally(function () {
149
- // decrement db.busy
150
- db.busy -= 1;
151
- assertOrThrow(db.busy >= 0, "invalid db.busy " + db.busy);
152
- });
153
- }
229
+ function dbCallAsync(baton, cFuncName, db, ...argList) {
230
+ // this function will call <cFuncName> using db <argList>[0]
231
+ let __db = dbDeref(db);
232
+ // increment __db.busy
233
+ __db.busy += 1;
234
+ return cCallAsync(
235
+ baton,
236
+ cFuncName,
237
+ __db.ptr,
238
+ ...argList
239
+ ).finally(function () {
240
+ // decrement __db.busy
241
+ __db.busy -= 1;
242
+ assertOrThrow(__db.busy >= 0, `invalid __db.busy ${__db.busy}`);
243
+ });
244
+ }
154
245
 
155
- async function dbCloseAsync({
156
- db
157
- }) {
246
+ async function dbCloseAsync({
247
+ db
248
+ }) {
158
249
  // this function will close sqlite-database-connection <db>
159
- let __db = dbDeref(db);
160
- // prevent segfault - do not close db if actions are pending
161
- assertOrThrow(
162
- __db.busy === 0,
163
- "db cannot close with " + __db.busy + " actions pending"
250
+ let __db = dbDeref(db);
251
+ // prevent segfault - do not close db if actions are pending
252
+ assertOrThrow(
253
+ __db.busy === 0,
254
+ "db cannot close with " + __db.busy + " actions pending"
255
+ );
256
+ // cleanup connPool
257
+ await Promise.all(__db.connPool.map(async function (ptr) {
258
+ let val = ptr[0];
259
+ ptr[0] = 0n;
260
+ await cCallAsync(
261
+ undefined,
262
+ "_dbClose",
263
+ val,
264
+ __db.filename
164
265
  );
165
- // cleanup connPool
166
- await Promise.all(__db.connPool.map(async function (ptr) {
167
- let val = ptr[0];
168
- ptr[0] = 0n;
169
- await cCall("__dbCloseAsync", [
170
- val
171
- ]);
172
- }));
173
- dbDict.delete(db);
174
- }
266
+ }));
267
+ dbDict.delete(db);
268
+ }
175
269
 
176
- function dbDeref(db) {
270
+ function dbDeref(db) {
177
271
  // this function will get private-object mapped to <db>
178
- let __db = dbDict.get(db);
179
- assertOrThrow(__db?.connPool[0] > 0, "invalid or closed db");
180
- assertOrThrow(__db.busy >= 0, "invalid db.busy " + __db.busy);
181
- __db.ii = (__db.ii + 1) % __db.connPool.length;
182
- __db.ptr = __db.connPool[__db.ii][0];
183
- assertOrThrow(__db.ptr > 0n, "invalid or closed db");
184
- return __db;
185
- }
272
+ let __db = dbDict.get(db);
273
+ assertOrThrow(__db?.connPool[0] > 0, "invalid or closed db");
274
+ assertOrThrow(__db.busy >= 0, "invalid db.busy " + __db.busy);
275
+ __db.ii = (__db.ii + 1) % __db.connPool.length;
276
+ __db.ptr = __db.connPool[__db.ii][0];
277
+ assertOrThrow(__db.ptr > 0n, "invalid or closed db");
278
+ return __db;
279
+ }
186
280
 
187
- async function dbExecAsync({
188
- bindList = [],
281
+ function dbExecAndReturnLastBlobAsync({
282
+ bindList = [],
283
+ db,
284
+ sql
285
+ }) {
286
+ // this function will exec <sql> in <db> and return last value retrieved
287
+ // from execution as raw blob/buffer
288
+ return dbExecAsync({
289
+ bindList,
189
290
  db,
190
- responseType,
191
- sql,
192
- tmpColList,
193
- tmpColListPriority,
194
- tmpCsv,
195
- tmpRowList,
196
- tmpTableName
197
- }) {
291
+ responseType: "lastBlob",
292
+ sql
293
+ });
294
+ }
295
+
296
+ async function dbExecAsync({
297
+ bindList = [],
298
+ db,
299
+ modeRetry,
300
+ responseType,
301
+ sql
302
+ }) {
198
303
  // this function will exec <sql> in <db> and return <result>
199
- let bindByKey = !Array.isArray(bindList);
200
- let bindListLength = (
201
- Array.isArray(bindList)
202
- ? bindList.length
203
- : Object.keys(bindList).length
204
- );
205
- let result;
206
- let serialize = jsToSqlSerializer();
207
- if (tmpCsv || tmpRowList) {
208
- await dbTableInsertAsync({
209
- colList: tmpColList,
210
- colListPriority: tmpColListPriority,
211
- csv: tmpCsv,
304
+ let baton;
305
+ let bindByKey;
306
+ let bindListLength;
307
+ let externalbufferList;
308
+ let result;
309
+ while (modeRetry > 0) {
310
+ try {
311
+ return await dbExecAsync({
312
+ bindList,
212
313
  db,
213
- rowList: tmpRowList,
214
- tableName: tmpTableName
314
+ responseType,
315
+ sql
215
316
  });
216
- }
217
- Object.entries(bindList).forEach(function ([
218
- key, val
219
- ]) {
220
- if (bindByKey) {
221
- serialize(":" + key + "\u0000");
222
- }
223
- serialize(val);
224
- });
225
- result = await dbCallAsync("__dbExecAsync", db, [
226
- String(sql) + "\n;\nPRAGMA noop",
227
- bindListLength,
228
- serialize.bufResult,
229
- bindByKey,
230
- (
231
- responseType === "lastBlob"
232
- ? 1
233
- : 0
234
- )
235
- ].concat(serialize.bufSharedList));
236
- result = result[1];
237
- switch (responseType) {
238
- case "arraybuffer":
239
- case "lastBlob":
240
- return result;
241
- case "list":
242
- return JSON.parse(new TextDecoder().decode(result));
243
- default:
244
- result = JSON.parse(new TextDecoder().decode(result));
245
- return result.map(function (rowList) {
246
- let colList = rowList.shift();
247
- return rowList.map(function (row) {
248
- let dict = {};
249
- colList.forEach(function (key, ii) {
250
- dict[key] = row[ii];
251
- });
252
- return dict;
253
- });
317
+ } catch (err) {
318
+ assertOrThrow(modeRetry > 0, err);
319
+ consoleError(err);
320
+ consoleError(
321
+ "dbExecAsync - retry failed sql-query with "
322
+ + modeRetry
323
+ + " remaining retries"
324
+ );
325
+ modeRetry -= 1;
326
+ await new Promise(function (resolve) {
327
+ setTimeout(resolve, 50);
254
328
  });
255
329
  }
256
330
  }
257
-
258
- async function dbExecWithRetryAsync(option) {
259
- // this function will exec <sql> in <db> and return <result> with <retryLimit>
260
- let retry = option.retryLimit || 1;
261
- while (true) {
262
- try {
263
- return await dbExecAsync(option);
264
- } catch (err) {
265
- assertOrThrow(retry > 0, err);
266
- consoleError(err);
267
- consoleError(
268
- "dbExecWithRetryAsync - retry failed sql-query with "
269
- + retry
270
- + " remaining retry"
271
- );
272
- retry -= 1;
273
- await new Promise(function (resolve) {
274
- setTimeout(resolve, 50);
275
- });
276
- }
331
+ baton = jsbatonCreate();
332
+ bindByKey = !Array.isArray(bindList);
333
+ bindListLength = (
334
+ Array.isArray(bindList)
335
+ ? bindList.length
336
+ : Object.keys(bindList).length
337
+ );
338
+ externalbufferList = [];
339
+ Object.entries(bindList).forEach(function ([
340
+ key, val
341
+ ]) {
342
+ if (bindByKey) {
343
+ baton = jsbatonValuePush({
344
+ baton,
345
+ value: ":" + key + "\u0000"
346
+ });
277
347
  }
278
- }
279
-
280
- function dbGetLastBlobAsync({
281
- bindList = [],
282
- db,
283
- sql
284
- }) {
285
- // this function will exec <sql> in <db> and return last value retrieved
286
- // from execution as raw blob/buffer
287
- return dbExecAsync({
288
- bindList,
289
- db,
290
- responseType: "lastBlob",
291
- sql
348
+ baton = jsbatonValuePush({
349
+ baton,
350
+ externalbufferList,
351
+ value: val
352
+ });
353
+ });
354
+ result = await dbCallAsync(
355
+ baton,
356
+ "_dbExec",
357
+ db, // 0
358
+ String(sql) + "\n;\nPRAGMA noop", // 1
359
+ bindListLength, // 2
360
+ bindByKey, // 3
361
+ ( // 4
362
+ responseType === "lastBlob"
363
+ ? 1
364
+ : 0
365
+ ),
366
+ undefined, // 5
367
+ undefined, // 6
368
+ undefined, // 7 - response
369
+ ...externalbufferList // 8
370
+ );
371
+ result = result[2 + 7];
372
+ switch (responseType) {
373
+ case "arraybuffer":
374
+ case "lastBlob":
375
+ return result;
376
+ case "list":
377
+ return JSON.parse(new TextDecoder().decode(result));
378
+ default:
379
+ result = JSON.parse(new TextDecoder().decode(result));
380
+ return result.map(function (rowList) {
381
+ let colList = rowList.shift();
382
+ return rowList.map(function (row) {
383
+ let dict = {};
384
+ colList.forEach(function (key, ii) {
385
+ dict[key] = row[ii];
386
+ });
387
+ return dict;
388
+ });
292
389
  });
293
390
  }
391
+ }
294
392
 
295
- async function dbMemoryLoadAsync({
296
- db,
297
- filename
298
- }) {
299
- // This function will load <filename> to <db>
300
- assertOrThrow(filename, "invalid filename " + filename);
301
- await dbCallAsync("__dbMemoryLoadOrSave", db, [
302
- String(filename), 0
303
- ]);
393
+ async function dbFileExportAsync({
394
+ db,
395
+ dbData,
396
+ filename,
397
+ modeExport = 1
398
+ }) {
399
+ // This function will export <db> to <filename>
400
+ if (IS_BROWSER) {
401
+ filename = FILENAME_DBTMP;
304
402
  }
403
+ assertOrThrow(
404
+ typeof filename === "string" && filename,
405
+ `invalid filename ${filename}`
406
+ );
407
+ return await dbCallAsync(
408
+ undefined,
409
+ "_dbFileImportOrExport",
410
+ db, // 0. sqlite3 * pInMemory,
411
+ String(filename), // 1. char *zFilename,
412
+ modeExport, // 2. const int isSave
413
+ undefined, // 3. undefined
414
+ dbData // 4. dbData
415
+ );
416
+ }
305
417
 
306
- async function dbMemorySaveAsync({
418
+ async function dbFileImportAsync({
419
+ db,
420
+ dbData,
421
+ filename
422
+ }) {
423
+ // This function will import <filename> to <db>
424
+ await dbFileExportAsync({
307
425
  db,
308
- filename
309
- }) {
310
- // This function will save <db> to <filename>
311
- assertOrThrow(filename, "invalid filename " + filename);
312
- await dbCallAsync("__dbMemoryLoadOrSave", db, [
313
- String(filename), 1
314
- ]);
315
- }
316
-
317
- async function dbOpenAsync({
426
+ dbData,
318
427
  filename,
319
- flags,
320
- threadCount = 1
321
- }) {
428
+ modeExport: 0
429
+ });
430
+ }
431
+
432
+ async function dbNoopAsync(...argList) {
433
+ // this function will do nothing except return argList
434
+ return await cCallAsync(undefined, "_dbNoop", ...argList);
435
+ }
436
+
437
+ async function dbOpenAsync({
438
+ afterFinalization,
439
+ dbData,
440
+ filename,
441
+ flags,
442
+ rawPtr,
443
+ threadCount = 1
444
+ }) {
322
445
  // this function will open and return sqlite-database-connection <db>
323
446
  // int sqlite3_open_v2(
324
447
  // const char *filename, /* Database filename (UTF-8) */
@@ -326,588 +449,496 @@ file sqlmath.js
326
449
  // int flags, /* Flags */
327
450
  // const char *zVfs /* Name of VFS module to use */
328
451
  // );
329
- let connPool;
330
- let db = {
331
- filename
332
- };
333
- assertOrThrow(
334
- typeof filename === "string",
335
- "invalid filename " + filename
336
- );
337
- connPool = await Promise.all(Array.from(new Array(
338
- threadCount
339
- ), async function () {
340
- let finalizer;
341
- let ptr = await cCall("__dbOpenAsync", [
342
- String(filename),
343
- undefined,
344
- flags ?? (
345
- SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI
346
- ),
347
- undefined
348
- ]);
349
- ptr = ptr[0][0];
350
- finalizer = new BigInt64Array(addon.__dbFinalizerCreate());
351
- finalizer[0] = BigInt(ptr);
352
- return finalizer;
353
- }));
354
- dbDict.set(db, {
355
- busy: 0,
356
- connPool,
357
- filename,
358
- ii: 0,
359
- ptr: 0n
360
- });
361
- return db;
452
+ let connPool;
453
+ let db = {
454
+ filename
455
+ };
456
+ assertOrThrow(
457
+ typeof filename === "string",
458
+ `invalid filename ${filename}`
459
+ );
460
+ assertOrThrow(
461
+ !dbData || isExternalBuffer(dbData),
462
+ "dbData must be ArrayBuffer"
463
+ );
464
+ if (rawPtr) {
465
+ rawPtr = [
466
+ BigInt(rawPtr)
467
+ ];
468
+ threadCount = 1;
362
469
  }
363
-
364
- async function dbTableInsertAsync({
365
- colList,
366
- colListPriority,
367
- csv,
368
- db,
369
- rowList,
370
- tableName
371
- }) {
372
- // this function will create-or-replace temp <tablename> with <rowList>
373
- let serialize = jsToSqlSerializer();
374
- let sqlCreateTable;
375
- let sqlInsertRow;
376
- // normalize and validate tableName
377
- tableName = tableName || "__tmp1";
378
- tableName = "temp." + JSON.stringify(tableName.replace((
379
- /^temp\./
380
- ), ""));
381
- assertOrThrow((
382
- /^temp\."[A-Z_a-z][0-9A-Z_a-z]*?"$/
383
- ).test(tableName), "invalid tableName " + tableName);
384
- // parse csv
385
- if (!rowList && csv) {
386
- rowList = jsonRowListFromCsv({
387
- csv
388
- });
389
- }
390
- rowList = jsonRowListNormalize({
391
- colList,
392
- colListPriority,
393
- rowList
394
- });
395
- colList = rowList.shift();
396
- sqlCreateTable = (
397
- "DROP TABLE IF EXISTS " + tableName + ";"
398
- + "CREATE TEMP TABLE " + tableName + "(" + colList.join(",") + ");"
399
- );
400
- sqlInsertRow = (
401
- "INSERT INTO " + tableName + " VALUES("
402
- + ",?".repeat(colList.length).slice(1) + ");"
470
+ connPool = await Promise.all(Array.from(new Array(
471
+ threadCount
472
+ ), async function () {
473
+ let ptr = rawPtr || await cCallAsync(
474
+ undefined,
475
+ "_dbOpen",
476
+ // 0. const char *filename, Database filename (UTF-8)
477
+ String(filename),
478
+ // 1. sqlite3 **ppDb, OUT: SQLite db handle
479
+ undefined,
480
+ // 2. int flags, Flags
481
+ flags ?? (
482
+ SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI
483
+ ),
484
+ // 3. const char *zVfs Name of VFS module to use
485
+ undefined,
486
+ // 4. wasm-only - arraybuffer of raw sqlite-database to open in wasm
487
+ dbData
403
488
  );
404
- rowList.forEach(function (row) {
405
- row.forEach(serialize);
489
+ ptr = rawPtr || [
490
+ ptr[0].getBigInt64(4 + 4, true)
491
+ ];
492
+ dbFinalizationRegistry.register(db, {
493
+ afterFinalization,
494
+ ptr
406
495
  });
407
- await dbCallAsync("__dbTableInsertAsync", db, [
408
- String(sqlCreateTable),
409
- String(sqlInsertRow),
410
- serialize.bufResult,
411
- colList.length,
412
- rowList.length
413
- ]);
414
- }
496
+ return ptr;
497
+ }));
498
+ dbDict.set(db, {
499
+ busy: 0,
500
+ connPool,
501
+ filename,
502
+ ii: 0
503
+ });
504
+ return db;
505
+ }
415
506
 
416
- function jsToSqlSerializer() {
417
- // this function will return another function that serializes javascript <val>
418
- // to <bufResult> as sqlite-values
419
- let BIGINT64_MAX = 2n ** 63n - 1n;
420
- let BIGINT64_MIN = -(2n ** 63n - 1n);
421
- let SQLITE_DATATYPE_BLOB = 0x04;
422
- // let SQLITE_DATATYPE_BLOB_0 = 0x14;
423
- let SQLITE_DATATYPE_FLOAT = 0x02;
424
- // let SQLITE_DATATYPE_FLOAT_0 = 0x12;
425
- let SQLITE_DATATYPE_INTEGER = 0x01;
426
- let SQLITE_DATATYPE_INTEGER_0 = 0x11;
427
- let SQLITE_DATATYPE_INTEGER_1 = 0x21;
428
- let SQLITE_DATATYPE_NULL = 0x05;
429
- let SQLITE_DATATYPE_SHAREDARRAYBUFFER = -0x01;
430
- let SQLITE_DATATYPE_TEXT = 0x03;
431
- let SQLITE_DATATYPE_TEXT_0 = 0x13;
432
- let bufResult = new DataView(new ArrayBuffer(2048));
433
- let bufSharedList = [];
434
- let offset = 0;
435
- function bufferAppendDatatype(datatype, byteLength) {
436
- // this function will grow <bufResult> by <bytelength> and append <datatype>
437
- let nn = offset + 1 + byteLength;
438
- let tmp;
439
- // exponentially grow bufResult as needed
440
- if (bufResult.byteLength < nn) {
441
- assertOrThrow(nn <= SQLITE_MAX_LENGTH2, (
442
- "sqlite - string or blob exceeds size limit of "
443
- + SQLITE_MAX_LENGTH2 + " bytes"
444
- ));
445
- tmp = bufResult;
446
- bufResult = new DataView(new ArrayBuffer(
447
- Math.min(2 ** Math.ceil(Math.log2(nn)), SQLITE_MAX_LENGTH2)
448
- ));
449
- // copy tmp to bufResult with offset
450
- bufferSetBuffer(bufResult, tmp, 0);
451
- // save bufResult
452
- serialize.bufResult = bufResult;
453
- }
454
- bufResult.setUint8(offset, datatype);
455
- offset += 1;
456
- }
457
- function bufferSetBigint64(offset, val) {
458
- // this function will set bigint <val> to buffer <bufResult> at <offset>
459
- assertOrThrow(
460
- BIGINT64_MIN <= val && val <= BIGINT64_MAX,
461
- (
462
- "The value of \"value\" is out of range."
463
- + " It must be >= -(2n ** 63n) and < 2n ** 63n."
464
- )
465
- );
466
- bufResult.setBigInt64(offset, val, true);
467
- }
468
- function bufferSetBuffer(aa, bb, offset) {
469
- // this function will set buffer <bb> to buffer <aa> at <offset>
470
- aa = new Uint8Array(aa.buffer, aa.byteOffset, aa.byteLength);
471
- bb = new Uint8Array(bb.buffer, bb.byteOffset, bb.byteLength);
472
- aa.set(bb, offset);
473
- return bb.byteLength;
474
- }
475
- function serialize(val) {
476
- // this function will write to <bufResult>, <val> at given <offset>
507
+ function isExternalBuffer(buf) {
508
+ // this function will check if <buf> is ArrayBuffer or SharedArrayBuffer
509
+ return buf && (
510
+ buf.constructor === ArrayBuffer
511
+ || (
512
+ typeof SharedArrayBuffer === "function"
513
+ && buf.constructor === SharedArrayBuffer
514
+ )
515
+ );
516
+ }
517
+
518
+ function jsbatonCreate() {
519
+ // this function will create buffer <baton>
520
+ let baton = new DataView(new ArrayBuffer(1024));
521
+ // offset nalloc, nused
522
+ baton.setInt32(4, SQLITE_DATATYPE_OFFSET, true);
523
+ return baton;
524
+ }
525
+
526
+ function jsbatonValuePush({
527
+ argi,
528
+ baton,
529
+ externalbufferList,
530
+ value
531
+ }) {
532
+ // this function will push <value> to buffer <baton>
533
+ let nn;
534
+ let nused;
535
+ let tmp;
536
+ let vsize;
537
+ let vtype;
477
538
  /*
478
539
  #define SQLITE_DATATYPE_BLOB 0x04
479
- #define SQLITE_DATATYPE_BLOB_0 0x14
540
+ // #define SQLITE_DATATYPE_BLOB_0 0x14
480
541
  #define SQLITE_DATATYPE_FLOAT 0x02
481
- #define SQLITE_DATATYPE_FLOAT_0 0x12
542
+ // #define SQLITE_DATATYPE_FLOAT_0 0x12
482
543
  #define SQLITE_DATATYPE_INTEGER 0x01
483
544
  #define SQLITE_DATATYPE_INTEGER_0 0x11
484
545
  #define SQLITE_DATATYPE_INTEGER_1 0x21
485
546
  #define SQLITE_DATATYPE_NULL 0x05
547
+ #define SQLITE_DATATYPE_EXTERNALBUFFER -0x01
486
548
  #define SQLITE_DATATYPE_TEXT 0x03
487
549
  #define SQLITE_DATATYPE_TEXT_0 0x13
488
- // 1. false.bigint
489
- // 2. false.boolean
490
- // 3. false.function
491
- // 4. false.number
492
- // 5. false.object
493
- // 6. false.string
494
- // 7. false.symbol
495
- // 8. false.undefined
496
- // 11. true.bigint
497
- // 12. true.boolean
498
- // 13. true.function
499
- // 14. true.number
500
- // 15. true.object
501
- // 16. true.string
502
- // 17. true.symbol
503
- // 18. true.undefined
550
+ // 1. false.bigint
551
+ // 2. false.boolean
552
+ // 3. false.function
553
+ // 4. false.number
554
+ // 5. false.object
555
+ // 6. false.string
556
+ // 7. false.symbol
557
+ // 8. false.undefined
558
+ // 9. true.bigint
559
+ // 10. true.boolean
560
+ // 11. true.function
561
+ // 12. true.number
562
+ // 13. true.object
563
+ // 14. true.string
564
+ // 15. true.symbol
565
+ // 16. true.undefined
566
+ // 17. true.buffer
567
+ // 18. true.externalbuffer
504
568
  */
505
- // -1. SharedArrayBuffer
506
- if (val && val.constructor === SharedArrayBuffer) {
507
- assertOrThrow(
508
- bufSharedList.length <= 0.5 * JSBATON_ARGC,
509
- (
510
- "too many SharedArrayBuffer's " + bufSharedList.length
511
- + " > " + (0.5 * JSBATON_ARGC)
512
- )
513
- );
514
- bufferAppendDatatype(SQLITE_DATATYPE_SHAREDARRAYBUFFER, 0);
515
- bufSharedList.push(new DataView(val));
516
- return;
517
- }
518
- // 12. true.boolean
519
- if (val === 1 || val === 1n || val === true) {
520
- bufferAppendDatatype(SQLITE_DATATYPE_INTEGER_1, 0);
521
- return;
522
- }
523
- switch (Boolean(val) + "." + typeof(val)) {
524
- // 1. false.bigint
525
- case "false.bigint":
526
- // 2. false.boolean
527
- case "false.boolean":
528
- // 4. false.number
529
- case "false.number":
530
- bufferAppendDatatype(SQLITE_DATATYPE_INTEGER_0, 0);
531
- return;
532
- // 3. false.function
533
- // case "false.function":
534
- // 5. false.object
535
- case "false.object":
536
- // 7. false.symbol
537
- // case "false.symbol":
538
- // 8. false.undefined
539
- case "false.undefined":
540
- // 13. true.function
541
- case "true.function":
542
- // 17. true.symbol
543
- case "true.symbol":
544
- // 18. true.undefined
545
- // case "true.undefined":
546
- bufferAppendDatatype(SQLITE_DATATYPE_NULL, 0);
547
- return;
548
- // 6. false.string
549
- case "false.string":
550
- bufferAppendDatatype(SQLITE_DATATYPE_TEXT_0, 0);
551
- return;
552
- // 11. true.bigint
553
- case "true.bigint":
554
- bufferAppendDatatype(SQLITE_DATATYPE_INTEGER, 8);
555
- bufferSetBigint64(offset, val);
556
- offset += 8;
557
- return;
558
- // 14. true.number
559
- case "true.number":
560
- bufferAppendDatatype(SQLITE_DATATYPE_FLOAT, 8);
561
- bufResult.setFloat64(offset, val, true);
562
- offset += 8;
563
- return;
564
- // 16. true.string
565
- case "true.string":
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
- return;
572
- // 15. true.object
573
- default:
574
- assertOrThrow(
575
- val && typeof val === "object",
576
- "invalid data " + (typeof val) + " " + val
577
- );
578
- // write buffer
579
- if (ArrayBuffer.isView(val)) {
580
- if (val.byteLength === 0) {
581
- bufferAppendDatatype(SQLITE_DATATYPE_NULL, 0);
582
- return;
583
- }
584
- bufferAppendDatatype(
585
- SQLITE_DATATYPE_BLOB,
586
- 8 + val.byteLength
587
- );
588
- bufferSetBigint64(offset, BigInt(val.byteLength));
589
- offset += 8;
590
- // copy val to bufResult with offset
591
- bufferSetBuffer(bufResult, val, offset);
592
- offset += val.byteLength;
593
- return;
594
- }
595
- // write JSON.stringify(val)
596
- val = String(
597
- typeof val.toJSON === "function"
598
- ? val.toJSON()
599
- : JSON.stringify(val)
600
- );
601
- val = new TextEncoder().encode(val);
602
- bufferAppendDatatype(SQLITE_DATATYPE_TEXT, 8 + val.byteLength);
603
- bufferSetBigint64(offset, BigInt(val.byteLength));
604
- offset += 8;
605
- offset += bufferSetBuffer(bufResult, val, offset);
606
- }
607
- }
608
- // save bufResult
609
- serialize.bufResult = bufResult;
610
- // save bufSharedList
611
- serialize.bufSharedList = bufSharedList;
612
- return serialize;
569
+ // 10. true.boolean
570
+ if (value === 1 || value === 1n) {
571
+ value = true;
613
572
  }
614
-
615
- function jsonRowListFromCsv({
616
- csv
617
- }) {
618
- // this function will convert <csv>-text to json list-of-list
619
- /*
620
- https://tools.ietf.org/html/rfc4180#section-2
621
- Definition of the CSV Format
622
- While there are various specifications and implementations for the
623
- CSV format (for ex. [4], [5], [6] and [7]), there is no formal
624
- specification in existence, which allows for a wide variety of
625
- interpretations of CSV files. This section documents the format that
626
- seems to be followed by most implementations:
627
- 1. Each record is located on a separate line, delimited by a line
628
- break (CRLF). For example:
629
- aaa,bbb,ccc CRLF
630
- zzz,yyy,xxx CRLF
631
- 2. The last record in the file may or may not have an ending line
632
- break. For example:
633
- aaa,bbb,ccc CRLF
634
- zzz,yyy,xxx
635
- 3. There maybe an optional header line appearing as the first line
636
- of the file with the same format as normal record lines. This
637
- header will contain names corresponding to the fields in the file
638
- and should contain the same number of fields as the records in
639
- the rest of the file (the presence or absence of the header line
640
- should be indicated via the optional "header" parameter of this
641
- MIME type). For example:
642
- field_name,field_name,field_name CRLF
643
- aaa,bbb,ccc CRLF
644
- zzz,yyy,xxx CRLF
645
- 4. Within the header and each record, there may be one or more
646
- fields, separated by commas. Each line should contain the same
647
- number of fields throughout the file. Spaces are considered part
648
- of a field and should not be ignored. The last field in the
649
- record must not be followed by a comma. For example:
650
- aaa,bbb,ccc
651
- 5. Each field may or may not be enclosed in double quotes (however
652
- some programs, such as Microsoft Excel, do not use double quotes
653
- at all). If fields are not enclosed with double quotes, then
654
- double quotes may not appear inside the fields. For example:
655
- "aaa","bbb","ccc" CRLF
656
- zzz,yyy,xxx
657
- 6. Fields containing line breaks (CRLF), double quotes, and commas
658
- should be enclosed in double-quotes. For example:
659
- "aaa","b CRLF
660
- bb","ccc" CRLF
661
- zzz,yyy,xxx
662
- 7. If double-quotes are used to enclose fields, then a double-quote
663
- appearing inside a field must be escaped by preceding it with
664
- another double quote. For example:
665
- "aaa","b""bb","ccc"
666
- */
667
- let match;
668
- let quote = false;
669
- let rgx = (
670
- /(.*?)(""|"|,|\n)/g
671
- );
672
- let row = [];
673
- let rowList = [];
674
- let val = "";
675
- // normalize "\r\n" to "\n"
676
- csv = csv.replace((
677
- /\r\n?/g
678
- ), "\n");
679
- /*
680
- 2. The last record in the file may or may not have an ending line
681
- break. For example:
682
- aaa,bbb,ccc CRLF
683
- zzz,yyy,xxx
684
- */
685
- if (csv[csv.length - 1] !== "\n") {
686
- csv += "\n";
573
+ switch (Boolean(value) + "." + typeof(value)) {
574
+ // 1. false.bigint
575
+ case "false.bigint":
576
+ // 2. false.boolean
577
+ case "false.boolean":
578
+ // 4. false.number
579
+ case "false.number":
580
+ vtype = SQLITE_DATATYPE_INTEGER_0;
581
+ vsize = 0;
582
+ break;
583
+ // 3. false.function
584
+ // case "false.function":
585
+ // 5. false.object
586
+ case "false.object":
587
+ // 7. false.symbol
588
+ case "false.symbol":
589
+ // 8. false.undefined
590
+ case "false.undefined":
591
+ // 11. true.function
592
+ case "true.function":
593
+ // 15. true.symbol
594
+ case "true.symbol":
595
+ // 16. true.undefined
596
+ // case "true.undefined":
597
+ vtype = SQLITE_DATATYPE_NULL;
598
+ vsize = 0;
599
+ break;
600
+ // 6. false.string
601
+ case "false.string":
602
+ vtype = SQLITE_DATATYPE_TEXT_0;
603
+ vsize = 0;
604
+ break;
605
+ // 9. true.bigint
606
+ case "true.bigint":
607
+ vtype = SQLITE_DATATYPE_INTEGER;
608
+ vsize = 8;
609
+ break;
610
+ // 10. true.boolean
611
+ case "true.boolean":
612
+ vtype = SQLITE_DATATYPE_INTEGER_1;
613
+ vsize = 0;
614
+ break;
615
+ // 12. true.number
616
+ case "true.number":
617
+ vtype = SQLITE_DATATYPE_FLOAT;
618
+ vsize = 8;
619
+ break;
620
+ // 13. true.object
621
+ // 14. true.string
622
+ default:
623
+ // 18. true.externalbuffer
624
+ if (isExternalBuffer(value)) {
625
+ assertOrThrow(
626
+ !IS_BROWSER,
627
+ "external ArrayBuffer cannot be passed directly to wasm"
628
+ );
629
+ assertOrThrow(
630
+ externalbufferList.length <= 8,
631
+ "externalbufferList.length must be less than 8"
632
+ );
633
+ externalbufferList.push(new DataView(value));
634
+ vtype = SQLITE_DATATYPE_EXTERNALBUFFER;
635
+ vsize = 4;
636
+ break;
687
637
  }
688
- while (true) {
689
- match = rgx.exec(csv);
690
- if (!match) {
691
- return rowList;
692
- }
693
- // build val
694
- val += match[1];
695
- match = match[2];
696
- switch (quote + "." + match) {
697
- case "false.,":
698
- /*
699
- 4. Within the header and each record, there may be one or more
700
- fields, separated by commas. Each line should contain the same
701
- number of fields throughout the file. Spaces are considered part
702
- of a field and should not be ignored. The last field in the
703
- record must not be followed by a comma. For example:
704
- aaa,bbb,ccc
705
- */
706
- // delimit val
707
- row.push(val);
708
- val = "";
709
- break;
710
- case "false.\"":
711
- case "true.\"":
712
- /*
713
- 5. Each field may or may not be enclosed in double quotes (however
714
- some programs, such as Microsoft Excel, do not use double quotes
715
- at all). If fields are not enclosed with double quotes, then
716
- double quotes may not appear inside the fields. For example:
717
- "aaa","bbb","ccc" CRLF
718
- zzz,yyy,xxx
719
- */
720
- assertOrThrow(quote || val === "", (
721
- "invalid csv - naked double-quote in unquoted-string "
722
- + JSON.stringify(val + "\"")
723
- ));
724
- quote = !quote;
725
- break;
726
- // backtrack for naked-double-double-quote
727
- case "false.\"\"":
728
- quote = true;
729
- rgx.lastIndex -= 1;
730
- break;
731
- case "false.\n":
732
- case "false.\r\n":
733
- /*
734
- 1. Each record is located on a separate line, delimited by a line
735
- break (CRLF). For example:
736
- aaa,bbb,ccc CRLF
737
- zzz,yyy,xxx CRLF
738
- */
739
- // delimit val
740
- row.push(val);
741
- val = "";
742
- // append row
743
- rowList.push(row);
744
- // reset row
745
- row = [];
746
- break;
747
- case "true.\"\"":
748
- /*
749
- 7. If double-quotes are used to enclose fields, then a double-quote
750
- appearing inside a field must be escaped by preceding it with
751
- another double quote. For example:
752
- "aaa","b""bb","ccc"
753
- */
754
- val += "\"";
638
+ // 17. true.buffer
639
+ if (ArrayBuffer.isView(value)) {
640
+ if (value.byteLength === 0) {
641
+ vtype = SQLITE_DATATYPE_NULL;
642
+ vsize = 0;
755
643
  break;
756
- default:
757
- /*
758
- 6. Fields containing line breaks (CRLF), double quotes, and commas
759
- should be enclosed in double-quotes. For example:
760
- "aaa","b CRLF
761
- bb","ccc" CRLF
762
- zzz,yyy,xxx
763
- */
764
- assertOrThrow(quote, (
765
- "invalid csv - illegal character in unquoted-string "
766
- + JSON.stringify(match)
767
- ));
768
- val += match;
769
644
  }
645
+ vtype = SQLITE_DATATYPE_BLOB;
646
+ vsize = 4 + value.byteLength;
647
+ break;
648
+ }
649
+ // 13. true.object
650
+ value = String(
651
+ typeof value === "string"
652
+ ? value
653
+ : typeof value.toJSON === "function"
654
+ ? value.toJSON()
655
+ : JSON.stringify(value)
656
+ );
657
+ // 14. true.string
658
+ value = new TextEncoder().encode(value);
659
+ vtype = SQLITE_DATATYPE_TEXT;
660
+ vsize = 4 + value.byteLength;
661
+ }
662
+ nused = baton.getInt32(4, true);
663
+ nn = nused + 1 + vsize;
664
+ assertOrThrow(
665
+ nn <= 0xffff_ffff,
666
+ "jsbaton cannot exceed 0x7fff_ffff / 2,147,483,647 bytes"
667
+ );
668
+ // exponentially grow baton as needed
669
+ if (baton.byteLength < nn) {
670
+ tmp = baton;
671
+ baton = new DataView(new ArrayBuffer(
672
+ Math.min(2 ** Math.ceil(Math.log2(nn)), 0x7fff_ffff)
673
+ ));
674
+ // update nalloc
675
+ baton.setInt32(0, baton.byteLength, true);
676
+ // copy tmp to baton
677
+ new Uint8Array(
678
+ baton.buffer,
679
+ baton.byteOffset,
680
+ nused
681
+ ).set(new Uint8Array(tmp.buffer, tmp.byteOffset, nused), 0);
682
+ }
683
+ // push vtype
684
+ baton.setUint8(nused, vtype);
685
+ // update nused
686
+ baton.setInt32(4, nused + 1 + vsize, true);
687
+ // handle blob-value
688
+ switch (vtype) {
689
+ case SQLITE_DATATYPE_BLOB:
690
+ case SQLITE_DATATYPE_TEXT:
691
+ // set argv[ii] to blob/text location
692
+ if (argi !== undefined) {
693
+ baton.setInt32(8 + argi * 8, nused, true);
770
694
  }
695
+ vsize -= 4;
696
+ // push vsize
697
+ assertOrThrow(
698
+ 0 <= vsize && vsize <= 1_000_000_000,
699
+ (
700
+ "sqlite-blob byte-length must be within inclusive-range"
701
+ + " 0 to 1,000,000,000"
702
+ )
703
+ );
704
+ baton.setInt32(nused + 1, vsize, true);
705
+ new Uint8Array(
706
+ baton.buffer,
707
+ nused + 1 + 4,
708
+ vsize
709
+ ).set(new Uint8Array(value.buffer, value.byteOffset, vsize), 0);
710
+ break;
711
+ case SQLITE_DATATYPE_EXTERNALBUFFER:
712
+ vsize = value.byteLength;
713
+ // push vsize
714
+ assertOrThrow(
715
+ 0 <= vsize && vsize <= 1_000_000_000,
716
+ (
717
+ "sqlite-blob byte-length must be within inclusive-range"
718
+ + " 0 to 1,000,000,000"
719
+ )
720
+ );
721
+ baton.setInt32(nused + 1, vsize, true);
722
+ break;
723
+ case SQLITE_DATATYPE_FLOAT:
724
+ baton.setFloat64(nused + 1, value, true);
725
+ break;
726
+ case SQLITE_DATATYPE_INTEGER:
727
+ assertOrThrow(
728
+ (
729
+ -9_223_372_036_854_775_808n <= value
730
+ && value <= 9_223_372_036_854_775_807n
731
+ ),
732
+ (
733
+ "sqlite-integer must be within inclusive-range "
734
+ + "-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807"
735
+ )
736
+ );
737
+ baton.setBigInt64(nused + 1, value, true);
738
+ break;
739
+ }
740
+ return baton;
741
+ }
742
+
743
+ function jsbatonValueString({
744
+ argi,
745
+ baton
746
+ }) {
747
+ // this function will return string-value from <baton> at given <offset>
748
+ let offset = baton.getInt32(4 + 4 + argi * 8, true);
749
+ return new TextDecoder().decode(new Uint8Array(
750
+ baton.buffer,
751
+ offset + 1 + 4,
752
+ // remove null-terminator from string
753
+ baton.getInt32(offset + 1, true) - 1
754
+ ));
755
+ }
756
+
757
+ function noop(val) {
758
+
759
+ // This function will do nothing except return <val>.
760
+
761
+ return val;
762
+ }
763
+
764
+ function objectDeepCopyWithKeysSorted(obj) {
765
+
766
+ // This function will recursively deep-copy <obj> with keys sorted.
767
+
768
+ let sorted;
769
+ if (typeof obj !== "object" || !obj) {
770
+ return obj;
771
+ }
772
+
773
+ // Recursively deep-copy list with child-keys sorted.
774
+
775
+ if (Array.isArray(obj)) {
776
+ return obj.map(objectDeepCopyWithKeysSorted);
777
+ }
778
+
779
+ // Recursively deep-copy obj with keys sorted.
780
+
781
+ sorted = Object.create(null);
782
+ Object.keys(obj).sort().forEach(function (key) {
783
+ sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
784
+ });
785
+ return sorted;
786
+ }
787
+
788
+ async function sqlMessagePost(baton, cFuncName, ...argList) {
789
+
790
+ // This function will post msg to <sqlWorker> and return result
791
+
792
+ let errStack;
793
+ let id;
794
+ let result;
795
+ let timeElapsed = Date.now();
796
+ // increment sqlMessageId
797
+ sqlMessageId += 1;
798
+ id = sqlMessageId;
799
+ // postMessage to web-worker
800
+ sqlWorker.postMessage(
801
+ {
802
+ argList,
803
+ baton,
804
+ cFuncName,
805
+ id
806
+ },
807
+ // transfer arraybuffer without copying
808
+ [
809
+ baton.buffer,
810
+ ...argList.filter(function (elem) {
811
+ return elem && elem.constructor === ArrayBuffer;
812
+ })
813
+ ]
814
+ );
815
+ // preserve stack-trace
816
+ errStack = new Error().stack.replace((
817
+ /.*$/m
818
+ ), "");
819
+ // await result from web-worker
820
+ result = await new Promise(function (resolve) {
821
+ sqlMessageDict[id] = resolve;
822
+ });
823
+ // cleanup sqlMessageDict
824
+ delete sqlMessageDict[id];
825
+ // debug slow postMessage
826
+ timeElapsed = Date.now() - timeElapsed;
827
+ if (timeElapsed > 500) {
828
+ consoleError(
829
+ "sqlMessagePost - " + JSON.stringify({
830
+ cFuncName,
831
+ timeElapsed
832
+ }) + errStack
833
+ );
771
834
  }
835
+ assertOrThrow(!result.errmsg, result.errmsg);
836
+ return [
837
+ result.baton, result.cFuncName, ...result.argList
838
+ ];
839
+ }
772
840
 
773
- function jsonRowListNormalize({
774
- colList,
775
- colListPriority,
776
- rowList
841
+ async function sqlmathInit() {
842
+ dbFinalizationRegistry = new FinalizationRegistry(function ({
843
+ afterFinalization,
844
+ ptr
777
845
  }) {
778
- // this function will normalize <rowList> with given <colList>
779
- let colDict = {};
780
- if (!(rowList?.length > 0)) {
781
- throw new Error("invalid rowList " + JSON.stringify(rowList));
782
- }
783
- // convert list-of-dict to list-of-list
784
- if (!Array.isArray(rowList[0])) {
785
- colList = new Map(Array.from(
786
- colList || []
787
- ).map(function (key, ii) {
788
- return [
789
- key, ii
790
- ];
791
- }));
792
- rowList = rowList.map(function (row) {
793
- Object.keys(row).forEach(function (key) {
794
- if (!colList.has(key)) {
795
- colList.set(key, colList.size);
796
- }
797
- });
798
- return Array.from(colList.keys()).map(function (key) {
799
- return row[key];
800
- });
801
- });
802
- colList = Array.from(colList.keys());
803
- }
804
- if (!colList) {
805
- colList = rowList[0];
806
- rowList = rowList.slice(1);
846
+ // This function will auto-close any open sqlite3-db-pointer,
847
+ // after its js-wrapper has been garbage-collected
848
+ cCallAsync(undefined, "_dbClose", ptr[0]);
849
+ if (afterFinalization) {
850
+ afterFinalization();
807
851
  }
808
- if (!(colList?.length > 0)) {
809
- throw new Error("invalid colList " + JSON.stringify(colList));
810
- }
811
- colList = colList.map(function (key) {
812
- // sanitize column-name
813
- key = String(key).replace((
814
- /^[^A-Z_a-z]/
815
- ), "c_" + key);
816
- key = key.replace((
817
- /[^0-9A-Z_a-z]/g
818
- ), "_");
819
- // for duplicate column-name, add ordinal _2, _3, _4, ...
820
- colDict[key] = colDict[key] || 0;
821
- colDict[key] += 1;
822
- if (colDict[key] > 1) {
823
- key += "_" + colDict[key];
824
- }
825
- return key;
826
- });
827
- // normalize rowList
828
- rowList = rowList.map(function (row) {
829
- return (
830
- row.length === colList.length
831
- ? row
832
- : colList.map(function (ignore, ii) {
833
- return row[ii];
834
- })
835
- );
836
- });
837
- if (!colListPriority) {
838
- rowList.unshift(colList);
839
- return rowList;
852
+ });
853
+
854
+ // Feature-detect nodejs.
855
+
856
+ if (
857
+ typeof process === "object"
858
+ && typeof process?.versions?.node === "string"
859
+ ) {
860
+ cModule = await import("module");
861
+ cModule = cModule.createRequire(import.meta.url);
862
+ cModule = cModule(
863
+ "./_binary_sqlmath"
864
+ + "_napi8"
865
+ + "_" + process.platform
866
+ + "_" + process.arch
867
+ + ".node"
868
+ );
869
+ if (process.env.npm_config_mode_test) {
870
+ // mock consoleError
871
+ consoleError = noop;
840
872
  }
841
- // sort colList by colListPriority
842
- colListPriority = new Map([].concat(
843
- colListPriority,
844
- colList
845
- ).map(function (key) {
846
- return [
847
- key, colList.indexOf(key)
848
- ];
849
- }).filter(function ([
850
- ignore, ii
851
- ]) {
852
- return ii >= 0;
853
- }));
854
- colList = Array.from(colListPriority.keys());
855
- colListPriority = Array.from(colListPriority.values());
856
- rowList = rowList.map(function (row) {
857
- return colListPriority.map(function (ii) {
858
- return row[ii];
859
- });
860
- });
861
- rowList.unshift(colList);
862
- return rowList;
863
873
  }
874
+ }
864
875
 
865
- addon = requireCjs(
866
- "./_binary_sqlmath"
867
- + "_napi8"
868
- + "_" + process.platform
869
- + "_" + process.arch
870
- + ".node"
871
- );
872
- Object.assign(local, jslint, {
873
- SQLITE_MAX_LENGTH2,
874
- SQLITE_OPEN_AUTOPROXY,
875
- SQLITE_OPEN_CREATE,
876
- SQLITE_OPEN_DELETEONCLOSE,
877
- SQLITE_OPEN_EXCLUSIVE,
878
- SQLITE_OPEN_FULLMUTEX,
879
- SQLITE_OPEN_MAIN_DB,
880
- SQLITE_OPEN_MAIN_JOURNAL,
881
- SQLITE_OPEN_MEMORY,
882
- SQLITE_OPEN_NOFOLLOW,
883
- SQLITE_OPEN_NOMUTEX,
884
- SQLITE_OPEN_PRIVATECACHE,
885
- SQLITE_OPEN_READONLY,
886
- SQLITE_OPEN_READWRITE,
887
- SQLITE_OPEN_SHAREDCACHE,
888
- SQLITE_OPEN_SUBJOURNAL,
889
- SQLITE_OPEN_SUPER_JOURNAL,
890
- SQLITE_OPEN_TEMP_DB,
891
- SQLITE_OPEN_TEMP_JOURNAL,
892
- SQLITE_OPEN_TRANSIENT_DB,
893
- SQLITE_OPEN_URI,
894
- SQLITE_OPEN_WAL,
895
- assertNumericalEqual,
896
- cCall,
897
- dbCloseAsync,
898
- dbExecAsync,
899
- dbExecWithRetryAsync,
900
- dbGetLastBlobAsync,
901
- dbMemoryLoadAsync,
902
- dbMemorySaveAsync,
903
- dbOpenAsync,
904
- dbTableInsertAsync,
905
- debugInline
876
+ function sqlmathWebworkerInit({
877
+ Worker
878
+ }) {
879
+
880
+ // Feature-detect browser.
881
+
882
+ IS_BROWSER = true;
883
+ Worker = Worker || globalThis.Worker;
884
+ sqlWorker = new Worker("sqlmath_wasm.js");
885
+ sqlWorker.onmessage = function ({
886
+ data
887
+ }) {
888
+ sqlMessageDict[data.id](data);
889
+ };
890
+ /*
891
+ let db = await dbOpenAsync({ //jslint-quiet
892
+ filename: ":memory:"
906
893
  });
907
- if (process.env.npm_config_mode_test) {
908
- // mock consoleError
909
- consoleError = noop;
910
- }
911
- }());
894
+ debugInline(
895
+ await dbExecAsync({
896
+ db,
897
+ sql: "aSELECT 1234"
898
+ })
899
+ );
900
+ */
901
+ }
912
902
 
913
- export default Object.freeze(local);
903
+ await sqlmathInit({});
904
+
905
+ export {
906
+ SQLITE_MAX_LENGTH2,
907
+ SQLITE_OPEN_AUTOPROXY,
908
+ SQLITE_OPEN_CREATE,
909
+ SQLITE_OPEN_DELETEONCLOSE,
910
+ SQLITE_OPEN_EXCLUSIVE,
911
+ SQLITE_OPEN_FULLMUTEX,
912
+ SQLITE_OPEN_MAIN_DB,
913
+ SQLITE_OPEN_MAIN_JOURNAL,
914
+ SQLITE_OPEN_MEMORY,
915
+ SQLITE_OPEN_NOFOLLOW,
916
+ SQLITE_OPEN_NOMUTEX,
917
+ SQLITE_OPEN_PRIVATECACHE,
918
+ SQLITE_OPEN_READONLY,
919
+ SQLITE_OPEN_READWRITE,
920
+ SQLITE_OPEN_SHAREDCACHE,
921
+ SQLITE_OPEN_SUBJOURNAL,
922
+ SQLITE_OPEN_SUPER_JOURNAL,
923
+ SQLITE_OPEN_TEMP_DB,
924
+ SQLITE_OPEN_TEMP_JOURNAL,
925
+ SQLITE_OPEN_TRANSIENT_DB,
926
+ SQLITE_OPEN_URI,
927
+ SQLITE_OPEN_WAL,
928
+ assertJsonEqual,
929
+ assertNumericalEqual,
930
+ assertOrThrow,
931
+ dbCloseAsync,
932
+ dbExecAndReturnLastBlobAsync,
933
+ dbExecAsync,
934
+ dbFileExportAsync,
935
+ dbFileImportAsync,
936
+ dbNoopAsync,
937
+ dbOpenAsync,
938
+ debugInline,
939
+ jsbatonValueString,
940
+ noop,
941
+ objectDeepCopyWithKeysSorted,
942
+ sqlmathInit,
943
+ sqlmathWebworkerInit
944
+ };