sqlmath 0.0.1 → 0.0.2
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/.gitconfig +2 -2
- package/.github/workflows/ci.yml +73 -15
- package/.github/workflows/publish.yml +66 -0
- package/.gitignore +1 -5
- package/CHANGELOG.md +53 -2
- package/LICENSE +16 -22
- package/README.md +18 -219
- package/asset_image_folder_open_solid.svg +1 -0
- package/jslint.mjs +10998 -0
- package/jslint_ci.sh +1542 -728
- package/package.json +23 -8
- package/{sqlite-autoconf-3360000/sqlite3.c → sqlite3.c} +13116 -85
- package/sqlite3_ext.c +8372 -0
- package/{sqlite-autoconf-3360000/shell.c → sqlite3_shell.c} +330 -275
- package/sqlmath.mjs +1713 -0
- package/sqlmath_base.c +1832 -0
- package/sqlmath_custom.c +78 -0
- package/sqlmath_custom.cpp +0 -0
- package/sqlmath_custom.mjs +4 -0
- package/sqlmath_old.js +31038 -0
- package/extension-functions.c +0 -2047
- package/node_sqlite3.cc +0 -11877
- package/sqlite-autoconf-3360000/INSTALL +0 -370
- package/sqlite-autoconf-3360000/Makefile.am +0 -20
- package/sqlite-autoconf-3360000/Makefile.fallback +0 -19
- package/sqlite-autoconf-3360000/Makefile.in +0 -1028
- package/sqlite-autoconf-3360000/Makefile.msc +0 -1037
- package/sqlite-autoconf-3360000/README.txt +0 -113
- package/sqlite-autoconf-3360000/Replace.cs +0 -223
- package/sqlite-autoconf-3360000/aclocal.m4 +0 -10199
- package/sqlite-autoconf-3360000/compile +0 -347
- package/sqlite-autoconf-3360000/config.guess +0 -1480
- package/sqlite-autoconf-3360000/config.sub +0 -1801
- package/sqlite-autoconf-3360000/configure +0 -16135
- package/sqlite-autoconf-3360000/configure.ac +0 -285
- package/sqlite-autoconf-3360000/depcomp +0 -791
- package/sqlite-autoconf-3360000/install-sh +0 -508
- package/sqlite-autoconf-3360000/ltmain.sh +0 -11156
- package/sqlite-autoconf-3360000/missing +0 -215
- package/sqlite-autoconf-3360000/sqlite3.1 +0 -286
- package/sqlite-autoconf-3360000/sqlite3.h +0 -12353
- package/sqlite-autoconf-3360000/sqlite3.pc.in +0 -13
- package/sqlite-autoconf-3360000/sqlite3.rc +0 -83
- package/sqlite-autoconf-3360000/sqlite3ext.h +0 -663
- package/sqlite-autoconf-3360000/sqlite3rc.h +0 -3
- package/sqlite-autoconf-3360000/tea/Makefile.in +0 -440
- package/sqlite-autoconf-3360000/tea/README +0 -36
- package/sqlite-autoconf-3360000/tea/aclocal.m4 +0 -9
- package/sqlite-autoconf-3360000/tea/configure +0 -9989
- package/sqlite-autoconf-3360000/tea/configure.ac +0 -201
- package/sqlite-autoconf-3360000/tea/doc/sqlite3.n +0 -15
- package/sqlite-autoconf-3360000/tea/generic/tclsqlite3.c +0 -4016
- package/sqlite-autoconf-3360000/tea/license.terms +0 -6
- package/sqlite-autoconf-3360000/tea/pkgIndex.tcl.in +0 -7
- package/sqlite-autoconf-3360000/tea/tclconfig/install-sh +0 -528
- package/sqlite-autoconf-3360000/tea/tclconfig/tcl.m4 +0 -4168
- package/sqlite-autoconf-3360000/tea/win/makefile.vc +0 -419
- package/sqlite-autoconf-3360000/tea/win/nmakehlp.c +0 -815
- package/sqlite-autoconf-3360000/tea/win/rules.vc +0 -711
- package/sqlmath.js +0 -238
- package/test/backup.test.js +0 -279
- package/test/blob.test.js +0 -54
- package/test/cache.test.js +0 -42
- package/test/constants.test.js +0 -44
- package/test/database_fail.test.js +0 -153
- package/test/each.test.js +0 -39
- package/test/exec.test.js +0 -39
- package/test/extension.test.js +0 -26
- package/test/extension_functions.test.js +0 -29
- package/test/fts-content.test.js +0 -13
- package/test/interrupt.test.js +0 -80
- package/test/issue-108.test.js +0 -28
- package/test/json.test.js +0 -22
- package/test/map.test.js +0 -63
- package/test/named_columns.test.js +0 -38
- package/test/named_params.test.js +0 -69
- package/test/null_error.test.js +0 -41
- package/test/nw/.gitignore +0 -3
- package/test/nw/Makefile +0 -39
- package/test/nw/index.html +0 -14
- package/test/nw/package.json +0 -9
- package/test/open_close.test.js +0 -187
- package/test/other_objects.test.js +0 -98
- package/test/parallel_insert.test.js +0 -44
- package/test/prepare.test.js +0 -427
- package/test/profile.test.js +0 -57
- package/test/rerun.test.js +0 -50
- package/test/scheduling.test.js +0 -44
- package/test/serialization.test.js +0 -104
- package/test/support/createdb-electron.js +0 -10
- package/test/support/createdb.js +0 -47
- package/test/support/elmo.png +0 -0
- package/test/support/helper.js +0 -37
- package/test/support/script.sql +0 -70
- package/test/trace.test.js +0 -67
- package/test/unicode.test.js +0 -114
- package/test/upsert.test.js +0 -27
- package/test/verbose.test.js +0 -60
- package/test.js +0 -141
- 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);
|