1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
|
/*
2022-05-22
The author disclaims copyright to this source code. In place of a
legal notice, here is a blessing:
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
***********************************************************************
This file is intended to be combined at build-time with other
related code, most notably a header and footer which wraps this
whole file into an Emscripten Module.postRun() handler. The sqlite3
JS API has no hard requirements on Emscripten and does not expose
any Emscripten APIs to clients. It is structured such that its build
can be tweaked to include it in arbitrary WASM environments which
can supply the necessary underlying features (e.g. a POSIX file I/O
layer).
Main project home page: https://sqlite.org
Documentation home page: https://sqlite.org/wasm
*/
/**
sqlite3ApiBootstrap() is the only global symbol persistently
exposed by this API. It is intended to be called one time at the
end of the API amalgamation process, passed configuration details
for the current environment, and then optionally be removed from
the global object using `delete globalThis.sqlite3ApiBootstrap`.
This function is not intended for client-level use. It is intended
for use in creating bundles configured for specific WASM
environments.
This function expects a configuration object, intended to abstract
away details specific to any given WASM environment, primarily so
that it can be used without any direct dependency on
Emscripten. (Note the default values for the config object!) The
config object is only honored the first time this is
called. Subsequent calls ignore the argument and return the same
(configured) object which gets initialized by the first call. This
function will throw if any of the required config options are
missing.
The config object properties include:
- `exports`[^1]: the "exports" object for the current WASM
environment. In an Emscripten-based build, this should be set to
`Module['asm']`.
- `memory`[^1]: optional WebAssembly.Memory object, defaulting to
`exports.memory`. In Emscripten environments this should be set
to `Module.wasmMemory` if the build uses `-sIMPORTED_MEMORY`, or be
left undefined/falsy to default to `exports.memory` when using
WASM-exported memory.
- `bigIntEnabled`: true if BigInt support is enabled. Defaults to
true if `globalThis.BigInt64Array` is available, else false. Some APIs
will throw exceptions if called without BigInt support, as BigInt
is required for marshalling C-side int64 into and out of JS.
(Sidebar: it is technically possible to add int64 support via
marshalling of int32 pairs, but doing so is unduly invasive.)
- `allocExportName`: the name of the function, in `exports`, of the
`malloc(3)`-compatible routine for the WASM environment. Defaults
to `"sqlite3_malloc"`. Beware that using any allocator other than
sqlite3_malloc() may require care in certain client-side code
regarding which allocator is uses. Notably, sqlite3_deserialize()
and sqlite3_serialize() can only safely use memory from different
allocators under very specific conditions. The canonical builds
of this API guaranty that `sqlite3_malloc()` is the JS-side
allocator implementation.
- `deallocExportName`: the name of the function, in `exports`, of
the `free(3)`-compatible routine for the WASM
environment. Defaults to `"sqlite3_free"`.
- `reallocExportName`: the name of the function, in `exports`, of
the `realloc(3)`-compatible routine for the WASM
environment. Defaults to `"sqlite3_realloc"`.
- `debug`, `log`, `warn`, and `error` may be functions equivalent
to the like-named methods of the global `console` object. By
default, these map directly to their `console` counterparts, but
can be replaced with (e.g.) empty functions to squelch all such
output.
- `wasmfsOpfsDir`[^1]: Specifies the "mount point" of the OPFS-backed
filesystem in WASMFS-capable builds.
[^1] = This property may optionally be a function, in which case
this function calls that function to fetch the value,
enabling delayed evaluation.
The returned object is the top-level sqlite3 namespace object.
Client code may optionally assign sqlite3ApiBootstrap.defaultConfig
an object-type value before calling sqlite3ApiBootstrap() (without
arguments) in order to tell that call to use this object as its
default config value. The intention of this is to provide
downstream clients with a reasonably flexible approach for plugging
in an environment-suitable configuration without having to define a
new global-scope symbol.
However, because clients who access this library via an
Emscripten-hosted module will not have an opportunity to call
sqlite3ApiBootstrap() themselves, nor to access it before it is
called, an alternative option for setting the configuration is to
define globalThis.sqlite3ApiConfig to an object. If it is set, it
is used instead of sqlite3ApiBootstrap.defaultConfig if
sqlite3ApiBootstrap() is called without arguments.
Both sqlite3ApiBootstrap.defaultConfig and
globalThis.sqlite3ApiConfig get deleted by sqlite3ApiBootstrap()
because any changes to them made after that point would have no
useful effect.
*/
'use strict';
globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
apiConfig = (globalThis.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig)
){
if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
(sqlite3ApiBootstrap.sqlite3.config || console).warn(
"sqlite3ApiBootstrap() called multiple times.",
"Config and external initializers are ignored on calls after the first."
);
return sqlite3ApiBootstrap.sqlite3;
}
const config = Object.assign(Object.create(null),{
exports: undefined,
memory: undefined,
bigIntEnabled: (()=>{
if('undefined'!==typeof Module){
/* Emscripten module will contain HEAPU64 when built with
-sWASM_BIGINT=1, else it will not.
As of emsdk 3.1.55, when building in strict mode, HEAPxyz
are only available if _explicitly_ included in the exports,
else they are not. We do not (as of 2024-03-04) use -sSTRICT
for the canonical builds.
*/
if( !!Module.HEAPU64 ) return true;
/* Else fall through and hope for the best. Nobody _really_
builds this without BigInt support, do they? */
}
return !!globalThis.BigInt64Array;
})(),
debug: console.debug.bind(console),
warn: console.warn.bind(console),
error: console.error.bind(console),
log: console.log.bind(console),
wasmfsOpfsDir: '/opfs',
/**
useStdAlloc is just for testing allocator discrepancies. The
docs guarantee that this is false in the canonical builds. For
99% of purposes it doesn't matter which allocators we use, but
it becomes significant with, e.g., sqlite3_deserialize() and
certain wasm.xWrap.resultAdapter()s.
*/
useStdAlloc: false
}, apiConfig || {});
Object.assign(config, {
allocExportName: config.useStdAlloc ? 'malloc' : 'sqlite3_malloc',
deallocExportName: config.useStdAlloc ? 'free' : 'sqlite3_free',
reallocExportName: config.useStdAlloc ? 'realloc' : 'sqlite3_realloc'
}, config);
[
// If any of these config options are functions, replace them with
// the result of calling that function...
'exports', 'memory', 'wasmfsOpfsDir'
].forEach((k)=>{
if('function' === typeof config[k]){
config[k] = config[k]();
}
});
/**
Eliminate any confusion about whether these config objects may
be used after library initialization by eliminating the outward-facing
objects...
*/
delete globalThis.sqlite3ApiConfig;
delete sqlite3ApiBootstrap.defaultConfig;
/**
The main sqlite3 binding API gets installed into this object,
mimicking the C API as closely as we can. The numerous members
names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as
possible, identically to the C-native counterparts, as documented at:
https://www.sqlite.org/c3ref/intro.html
A very few exceptions require an additional level of proxy
function or may otherwise require special attention in the WASM
environment, and all such cases are documented somewhere below
in this file or in sqlite3-api-glue.js. capi members which are
not documented are installed as 1-to-1 proxies for their
C-side counterparts.
*/
const capi = Object.create(null);
/**
Holds state which are specific to the WASM-related
infrastructure and glue code.
Note that a number of members of this object are injected
dynamically after the api object is fully constructed, so
not all are documented in this file.
*/
const wasm = Object.create(null);
/** Internal helper for SQLite3Error ctor. */
const __rcStr = (rc)=>{
return (capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc))
|| ("Unknown result code #"+rc);
};
/** Internal helper for SQLite3Error ctor. */
const __isInt = (n)=>'number'===typeof n && n===(n | 0);
/**
An Error subclass specifically for reporting DB-level errors and
enabling clients to unambiguously identify such exceptions.
The C-level APIs never throw, but some of the higher-level
C-style APIs do and the object-oriented APIs use exceptions
exclusively to report errors.
*/
class SQLite3Error extends Error {
/**
Constructs this object with a message depending on its arguments:
If its first argument is an integer, it is assumed to be
an SQLITE_... result code and it is passed to
sqlite3.capi.sqlite3_js_rc_str() to stringify it.
If called with exactly 2 arguments and the 2nd is an object,
that object is treated as the 2nd argument to the parent
constructor.
The exception's message is created by concatenating its
arguments with a space between each, except for the
two-args-with-an-object form and that the first argument will
get coerced to a string, as described above, if it's an
integer.
If passed an integer first argument, the error object's
`resultCode` member will be set to the given integer value,
else it will be set to capi.SQLITE_ERROR.
*/
constructor(...args){
let rc;
if(args.length){
if(__isInt(args[0])){
rc = args[0];
if(1===args.length){
super(__rcStr(args[0]));
}else{
const rcStr = __rcStr(rc);
if('object'===typeof args[1]){
super(rcStr,args[1]);
}else{
args[0] = rcStr+':';
super(args.join(' '));
}
}
}else{
if(2===args.length && 'object'===typeof args[1]){
super(...args);
}else{
super(args.join(' '));
}
}
}
this.resultCode = rc || capi.SQLITE_ERROR;
this.name = 'SQLite3Error';
}
};
/**
Functionally equivalent to the SQLite3Error constructor but may
be used as part of an expression, e.g.:
```
return someFunction(x) || SQLite3Error.toss(...);
```
*/
SQLite3Error.toss = (...args)=>{
throw new SQLite3Error(...args);
};
const toss3 = SQLite3Error.toss;
if(config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)){
toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'.");
}
/**
Returns true if n is a 32-bit (signed) integer, else
false. This is used for determining when we need to switch to
double-type DB operations for integer values in order to keep
more precision.
*/
const isInt32 = (n)=>{
return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/)
&& !!(n===(n|0) && n<=2147483647 && n>=-2147483648);
};
/**
Returns true if the given BigInt value is small enough to fit
into an int64 value, else false.
*/
const bigIntFits64 = function f(b){
if(!f._max){
f._max = BigInt("0x7fffffffffffffff");
f._min = ~f._max;
}
return b >= f._min && b <= f._max;
};
/**
Returns true if the given BigInt value is small enough to fit
into an int32, else false.
*/
const bigIntFits32 = (b)=>(b >= (-0x7fffffffn - 1n) && b <= 0x7fffffffn);
/**
Returns true if the given BigInt value is small enough to fit
into a double value without loss of precision, else false.
*/
const bigIntFitsDouble = function f(b){
if(!f._min){
f._min = Number.MIN_SAFE_INTEGER;
f._max = Number.MAX_SAFE_INTEGER;
}
return b >= f._min && b <= f._max;
};
/** Returns v if v appears to be a TypedArray, else false. */
const isTypedArray = (v)=>{
return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false;
};
/** Internal helper to use in operations which need to distinguish
between TypedArrays which are backed by a SharedArrayBuffer
from those which are not. */
const __SAB = ('undefined'===typeof SharedArrayBuffer)
? function(){} : SharedArrayBuffer;
/** Returns true if the given TypedArray object is backed by a
SharedArrayBuffer, else false. */
const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB);
/**
Returns either aTypedArray.slice(begin,end) (if
aTypedArray.buffer is a SharedArrayBuffer) or
aTypedArray.subarray(begin,end) (if it's not).
This distinction is important for APIs which don't like to
work on SABs, e.g. TextDecoder, and possibly for our
own APIs which work on memory ranges which "might" be
modified by other threads while they're working.
*/
const typedArrayPart = (aTypedArray, begin, end)=>{
return isSharedTypedArray(aTypedArray)
? aTypedArray.slice(begin, end)
: aTypedArray.subarray(begin, end);
};
/**
Returns true if v appears to be one of our bind()-able TypedArray
types: Uint8Array or Int8Array or ArrayBuffer. Support for
TypedArrays with element sizes >1 is a potential TODO just
waiting on a use case to justify them. Until then, their `buffer`
property can be used to pass them as an ArrayBuffer. If it's not
a bindable array type, a falsy value is returned.
*/
const isBindableTypedArray = (v)=>{
return v && (v instanceof Uint8Array
|| v instanceof Int8Array
|| v instanceof ArrayBuffer);
};
/**
Returns true if v appears to be one of the TypedArray types
which is legal for holding SQL code (as opposed to binary blobs).
Currently this is the same as isBindableTypedArray() but it
seems likely that we'll eventually want to add Uint32Array
and friends to the isBindableTypedArray() list but not to the
isSQLableTypedArray() list.
*/
const isSQLableTypedArray = (v)=>{
return v && (v instanceof Uint8Array
|| v instanceof Int8Array
|| v instanceof ArrayBuffer);
};
/** Returns true if isBindableTypedArray(v) does, else throws with a message
that v is not a supported TypedArray value. */
const affirmBindableTypedArray = (v)=>{
return isBindableTypedArray(v)
|| toss3("Value is not of a supported TypedArray type.");
};
const utf8Decoder = new TextDecoder('utf-8');
/**
Uses TextDecoder to decode the given half-open range of the
given TypedArray to a string. This differs from a simple
call to TextDecoder in that it accounts for whether the
first argument is backed by a SharedArrayBuffer or not,
and can work more efficiently if it's not (TextDecoder
refuses to act upon an SAB).
*/
const typedArrayToString = function(typedArray, begin, end){
return utf8Decoder.decode(typedArrayPart(typedArray, begin,end));
};
/**
If v is-a Array, its join("") result is returned. If
isSQLableTypedArray(v) is true then typedArrayToString(v) is
returned. If it looks like a WASM pointer, wasm.cstrToJs(v) is
returned. Else v is returned as-is.
*/
const flexibleString = function(v){
if(isSQLableTypedArray(v)){
return typedArrayToString(
(v instanceof ArrayBuffer) ? new Uint8Array(v) : v
);
}
else if(Array.isArray(v)) return v.join("");
else if(wasm.isPtr(v)) v = wasm.cstrToJs(v);
return v;
};
/**
An Error subclass specifically for reporting Wasm-level malloc()
failure and enabling clients to unambiguously identify such
exceptions.
*/
class WasmAllocError extends Error {
/**
If called with 2 arguments and the 2nd one is an object, it
behaves like the Error constructor, else it concatenates all
arguments together with a single space between each to
construct an error message string. As a special case, if
called with no arguments then it uses a default error
message.
*/
constructor(...args){
if(2===args.length && 'object'===typeof args[1]){
super(...args);
}else if(args.length){
super(args.join(' '));
}else{
super("Allocation failed.");
}
this.resultCode = capi.SQLITE_NOMEM;
this.name = 'WasmAllocError';
}
};
/**
Functionally equivalent to the WasmAllocError constructor but may
be used as part of an expression, e.g.:
```
return someAllocatingFunction(x) || WasmAllocError.toss(...);
```
*/
WasmAllocError.toss = (...args)=>{
throw new WasmAllocError(...args);
};
Object.assign(capi, {
/**
sqlite3_bind_blob() works exactly like its C counterpart unless
its 3rd argument is one of:
- JS string: the 3rd argument is converted to a C string, the
4th argument is ignored, and the C-string's length is used
in its place.
- Array: converted to a string as defined for "flexible
strings" and then it's treated as a JS string.
- Int8Array or Uint8Array: wasm.allocFromTypedArray() is used to
conver the memory to the WASM heap. If the 4th argument is
0 or greater, it is used as-is, otherwise the array's byteLength
value is used. This is an exception to the C API's undefined
behavior for a negative 4th argument, but results are undefined
if the given 4th argument value is greater than the byteLength
of the input array.
- If it's an ArrayBuffer, it gets wrapped in a Uint8Array and
treated as that type.
In all of those cases, the final argument (destructor) is
ignored and capi.SQLITE_WASM_DEALLOC is assumed.
A 3rd argument of `null` is treated as if it were a WASM pointer
of 0.
If the 3rd argument is neither a WASM pointer nor one of the
above-described types, capi.SQLITE_MISUSE is returned.
The first argument may be either an `sqlite3_stmt*` WASM
pointer or an sqlite3.oo1.Stmt instance.
For consistency with the C API, it requires the same number of
arguments. It returns capi.SQLITE_MISUSE if passed any other
argument count.
*/
sqlite3_bind_blob: undefined/*installed later*/,
/**
sqlite3_bind_text() works exactly like its C counterpart unless
its 3rd argument is one of:
- JS string: the 3rd argument is converted to a C string, the
4th argument is ignored, and the C-string's length is used
in its place.
- Array: converted to a string as defined for "flexible
strings". The 4th argument is ignored and a value of -1
is assumed.
- Int8Array or Uint8Array: is assumed to contain UTF-8 text, is
converted to a string. The 4th argument is ignored, replaced
by the array's byteLength value.
- If it's an ArrayBuffer, it gets wrapped in a Uint8Array and
treated as that type.
In each of those cases, the final argument (text destructor) is
ignored and capi.SQLITE_WASM_DEALLOC is assumed.
A 3rd argument of `null` is treated as if it were a WASM pointer
of 0.
If the 3rd argument is neither a WASM pointer nor one of the
above-described types, capi.SQLITE_MISUSE is returned.
The first argument may be either an `sqlite3_stmt*` WASM
pointer or an sqlite3.oo1.Stmt instance.
For consistency with the C API, it requires the same number of
arguments. It returns capi.SQLITE_MISUSE if passed any other
argument count.
If client code needs to bind partial strings, it needs to
either parcel the string up before passing it in here or it
must pass in a WASM pointer for the 3rd argument and a valid
4th-argument value, taking care not to pass a value which
truncates a multi-byte UTF-8 character. When passing
WASM-format strings, it is important that the final argument be
valid or unexpected content can result can result, or even a
crash if the application reads past the WASM heap bounds.
*/
sqlite3_bind_text: undefined/*installed later*/,
/**
sqlite3_create_function_v2() differs from its native
counterpart only in the following ways:
1) The fourth argument (`eTextRep`) argument must not specify
any encoding other than sqlite3.SQLITE_UTF8. The JS API does not
currently support any other encoding and likely never
will. This function does not replace that argument on its own
because it may contain other flags. As a special case, if
the bottom 4 bits of that argument are 0, SQLITE_UTF8 is
assumed.
2) Any of the four final arguments may be either WASM pointers
(assumed to be function pointers) or JS Functions. In the
latter case, each gets bound to WASM using
sqlite3.capi.wasm.installFunction() and that wrapper is passed
on to the native implementation.
For consistency with the C API, it requires the same number of
arguments. It returns capi.SQLITE_MISUSE if passed any other
argument count.
The semantics of JS functions are:
xFunc: is passed `(pCtx, ...values)`. Its return value becomes
the new SQL function's result.
xStep: is passed `(pCtx, ...values)`. Its return value is
ignored.
xFinal: is passed `(pCtx)`. Its return value becomes the new
aggregate SQL function's result.
xDestroy: is passed `(void*)`. Its return value is ignored. The
pointer passed to it is the one from the 5th argument to
sqlite3_create_function_v2().
Note that:
- `pCtx` in the above descriptions is a `sqlite3_context*`. At
least 99 times out of a hundred, that initial argument will
be irrelevant for JS UDF bindings, but it needs to be there
so that the cases where it _is_ relevant, in particular with
window and aggregate functions, have full access to the
lower-level sqlite3 APIs.
- When wrapping JS functions, the remaining arguments are passd
to them as positional arguments, not as an array of
arguments, because that allows callback definitions to be
more JS-idiomatic than C-like. For example `(pCtx,a,b)=>a+b`
is more intuitive and legible than
`(pCtx,args)=>args[0]+args[1]`. For cases where an array of
arguments would be more convenient, the callbacks simply need
to be declared like `(pCtx,...args)=>{...}`, in which case
`args` will be an array.
- If a JS wrapper throws, it gets translated to
sqlite3_result_error() or sqlite3_result_error_nomem(),
depending on whether the exception is an
sqlite3.WasmAllocError object or not.
- When passing on WASM function pointers, arguments are _not_
converted or reformulated. They are passed on as-is in raw
pointer form using their native C signatures. Only JS
functions passed in to this routine, and thus wrapped by this
routine, get automatic conversions of arguments and result
values. The routines which perform those conversions are
exposed for client-side use as
sqlite3_create_function_v2.convertUdfArgs() and
sqlite3_create_function_v2.setUdfResult(). sqlite3_create_function()
and sqlite3_create_window_function() have those same methods.
For xFunc(), xStep(), and xFinal():
- When called from SQL, arguments to the UDF, and its result,
will be converted between JS and SQL with as much fidelity as
is feasible, triggering an exception if a type conversion
cannot be determined. Some freedom is afforded to numeric
conversions due to friction between the JS and C worlds:
integers which are larger than 32 bits may be treated as
doubles or BigInts.
If any JS-side bound functions throw, those exceptions are
intercepted and converted to database-side errors with the
exception of xDestroy(): any exception from it is ignored,
possibly generating a console.error() message. Destructors
must not throw.
Once installed, there is currently no way to uninstall the
automatically-converted WASM-bound JS functions from WASM. They
can be uninstalled from the database as documented in the C
API, but this wrapper currently has no infrastructure in place
to also free the WASM-bound JS wrappers, effectively resulting
in a memory leak if the client uninstalls the UDF. Improving that
is a potential TODO, but removing client-installed UDFs is rare
in practice. If this factor is relevant for a given client,
they can create WASM-bound JS functions themselves, hold on to their
pointers, and pass the pointers in to here. Later on, they can
free those pointers (using `wasm.uninstallFunction()` or
equivalent).
C reference: https://www.sqlite.org/c3ref/create_function.html
Maintenance reminder: the ability to add new
WASM-accessible functions to the runtime requires that the
WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
flag.
*/
sqlite3_create_function_v2: (
pDb, funcName, nArg, eTextRep, pApp,
xFunc, xStep, xFinal, xDestroy
)=>{/*installed later*/},
/**
Equivalent to passing the same arguments to
sqlite3_create_function_v2(), with 0 as the final argument.
*/
sqlite3_create_function: (
pDb, funcName, nArg, eTextRep, pApp,
xFunc, xStep, xFinal
)=>{/*installed later*/},
/**
The sqlite3_create_window_function() JS wrapper differs from
its native implementation in the exact same way that
sqlite3_create_function_v2() does. The additional function,
xInverse(), is treated identically to xStep() by the wrapping
layer.
*/
sqlite3_create_window_function: (
pDb, funcName, nArg, eTextRep, pApp,
xStep, xFinal, xValue, xInverse, xDestroy
)=>{/*installed later*/},
/**
The sqlite3_prepare_v3() binding handles two different uses
with differing JS/WASM semantics:
1) sqlite3_prepare_v3(pDb, sqlString, -1, prepFlags, ppStmt , null)
2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer)
Note that the SQL length argument (the 3rd argument) must, for
usage (1), always be negative because it must be a byte length
and that value is expensive to calculate from JS (where only
the character length of strings is readily available). It is
retained in this API's interface for code/documentation
compatibility reasons but is currently _always_ ignored. With
usage (2), the 3rd argument is used as-is but is is still
critical that the C-style input string (2nd argument) be
terminated with a 0 byte.
In usage (1), the 2nd argument must be of type string,
Uint8Array, Int8Array, or ArrayBuffer (all of which are assumed
to hold SQL). If it is, this function assumes case (1) and
calls the underyling C function with the equivalent of:
(pDb, sqlAsString, -1, prepFlags, ppStmt, null)
The `pzTail` argument is ignored in this case because its
result is meaningless when a string-type value is passed
through: the string goes through another level of internal
conversion for WASM's sake and the result pointer would refer
to that transient conversion's memory, not the passed-in
string.
If the sql argument is not a string, it must be a _pointer_ to
a NUL-terminated string which was allocated in the WASM memory
(e.g. using capi.wasm.alloc() or equivalent). In that case,
the final argument may be 0/null/undefined or must be a pointer
to which the "tail" of the compiled SQL is written, as
documented for the C-side sqlite3_prepare_v3(). In case (2),
the underlying C function is called with the equivalent of:
(pDb, sqlAsPointer, sqlByteLen, prepFlags, ppStmt, pzTail)
It returns its result and compiled statement as documented in
the C API. Fetching the output pointers (5th and 6th
parameters) requires using `capi.wasm.peek()` (or
equivalent) and the `pzTail` will point to an address relative to
the `sqlAsPointer` value.
If passed an invalid 2nd argument type, this function will
return SQLITE_MISUSE and sqlite3_errmsg() will contain a string
describing the problem.
Side-note: if given an empty string, or one which contains only
comments or an empty SQL expression, 0 is returned but the result
output pointer will be NULL.
*/
sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags,
stmtPtrPtr, strPtrPtr)=>{}/*installed later*/,
/**
Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument.
*/
sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen,
stmtPtrPtr,strPtrPtr)=>{}/*installed later*/,
/**
This binding enables the callback argument to be a JavaScript.
If the callback is a function, then for the duration of the
sqlite3_exec() call, it installs a WASM-bound function which
acts as a proxy for the given callback. That proxy will also
perform a conversion of the callback's arguments from
`(char**)` to JS arrays of strings. However, for API
consistency's sake it will still honor the C-level callback
parameter order and will call it like:
`callback(pVoid, colCount, listOfValues, listOfColNames)`
If the callback is not a JS function then this binding performs
no translation of the callback, but the sql argument is still
converted to a WASM string for the call using the
"string:flexible" argument converter.
*/
sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/,
/**
If passed a single argument which appears to be a byte-oriented
TypedArray (Int8Array or Uint8Array), this function treats that
TypedArray as an output target, fetches `theArray.byteLength`
bytes of randomness, and populates the whole array with it. As
a special case, if the array's length is 0, this function
behaves as if it were passed (0,0). When called this way, it
returns its argument, else it returns the `undefined` value.
If called with any other arguments, they are passed on as-is
to the C API. Results are undefined if passed any incompatible
values.
*/
sqlite3_randomness: (n, outPtr)=>{/*installed later*/},
}/*capi*/);
/**
Various internal-use utilities are added here as needed. They
are bound to an object only so that we have access to them in
the differently-scoped steps of the API bootstrapping
process. At the end of the API setup process, this object gets
removed. These are NOT part of the public API.
*/
const util = {
affirmBindableTypedArray, flexibleString,
bigIntFits32, bigIntFits64, bigIntFitsDouble,
isBindableTypedArray,
isInt32, isSQLableTypedArray, isTypedArray,
typedArrayToString,
isUIThread: ()=>(globalThis.window===globalThis && !!globalThis.document),
// is this true for ESM?: 'undefined'===typeof WorkerGlobalScope
isSharedTypedArray,
toss: function(...args){throw new Error(args.join(' '))},
toss3,
typedArrayPart,
/**
Given a byte array or ArrayBuffer, this function throws if the
lead bytes of that buffer do not hold a SQLite3 database header,
else it returns without side effects.
Added in 3.44.
*/
affirmDbHeader: function(bytes){
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
const header = "SQLite format 3";
if( header.length > bytes.byteLength ){
toss3("Input does not contain an SQLite3 database header.");
}
for(let i = 0; i < header.length; ++i){
if( header.charCodeAt(i) !== bytes[i] ){
toss3("Input does not contain an SQLite3 database header.");
}
}
},
/**
Given a byte array or ArrayBuffer, this function throws if the
database does not, at a cursory glance, appear to be an SQLite3
database. It only examines the size and header, but further
checks may be added in the future.
Added in 3.44.
*/
affirmIsDb: function(bytes){
if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes);
const n = bytes.byteLength;
if(n<512 || n%512!==0) {
toss3("Byte array size",n,"is invalid for an SQLite3 db.");
}
util.affirmDbHeader(bytes);
}
}/*util*/;
Object.assign(wasm, {
/**
Emscripten APIs have a deep-seated assumption that all pointers
are 32 bits. We'll remain optimistic that that won't always be
the case and will use this constant in places where we might
otherwise use a hard-coded 4.
*/
ptrSizeof: config.wasmPtrSizeof || 4,
/**
The WASM IR (Intermediate Representation) value for
pointer-type values. It MUST refer to a value type of the
size described by this.ptrSizeof.
*/
ptrIR: config.wasmPtrIR || "i32",
/**
True if BigInt support was enabled via (e.g.) the
Emscripten -sWASM_BIGINT flag, else false. When
enabled, certain 64-bit sqlite3 APIs are enabled which
are not otherwise enabled due to JS/WASM int64
impedence mismatches.
*/
bigIntEnabled: !!config.bigIntEnabled,
/**
The symbols exported by the WASM environment.
*/
exports: config.exports
|| toss3("Missing API config.exports (WASM module exports)."),
/**
When Emscripten compiles with `-sIMPORTED_MEMORY`, it
initalizes the heap and imports it into wasm, as opposed to
the other way around. In this case, the memory is not
available via this.exports.memory.
*/
memory: config.memory || config.exports['memory']
|| toss3("API config object requires a WebAssembly.Memory object",
"in either config.exports.memory (exported)",
"or config.memory (imported)."),
/**
The API's primary point of access to the WASM-side memory
allocator. Works like sqlite3_malloc() but throws a
WasmAllocError if allocation fails. It is important that any
code which might pass through the sqlite3 C API NOT throw and
must instead return SQLITE_NOMEM (or equivalent, depending on
the context).
Very few cases in the sqlite3 JS APIs can result in
client-defined functions propagating exceptions via the C-style
API. Most notably, this applies to WASM-bound JS functions
which are created directly by clients and passed on _as WASM
function pointers_ to functions such as
sqlite3_create_function_v2(). Such bindings created
transparently by this API will automatically use wrappers which
catch exceptions and convert them to appropriate error codes.
For cases where non-throwing allocation is required, use
this.alloc.impl(), which is direct binding of the
underlying C-level allocator.
Design note: this function is not named "malloc" primarily
because Emscripten uses that name and we wanted to avoid any
confusion early on in this code's development, when it still
had close ties to Emscripten's glue code.
*/
alloc: undefined/*installed later*/,
/**
Rarely necessary in JS code, this routine works like
sqlite3_realloc(M,N), where M is either NULL or a pointer
obtained from this function or this.alloc() and N is the number
of bytes to reallocate the block to. Returns a pointer to the
reallocated block or 0 if allocation fails.
If M is NULL and N is positive, this behaves like
this.alloc(N). If N is 0, it behaves like this.dealloc().
Results are undefined if N is negative (sqlite3_realloc()
treats that as 0, but if this code is built with a different
allocator it may misbehave with negative values).
Like this.alloc.impl(), this.realloc.impl() is a direct binding
to the underlying realloc() implementation which does not throw
exceptions, instead returning 0 on allocation error.
*/
realloc: undefined/*installed later*/,
/**
The API's primary point of access to the WASM-side memory
deallocator. Works like sqlite3_free().
Design note: this function is not named "free" for the same
reason that this.alloc() is not called this.malloc().
*/
dealloc: undefined/*installed later*/
/* Many more wasm-related APIs get installed later on. */
}/*wasm*/);
/**
wasm.alloc()'s srcTypedArray.byteLength bytes,
populates them with the values from the source
TypedArray, and returns the pointer to that memory. The
returned pointer must eventually be passed to
wasm.dealloc() to clean it up.
The argument may be a Uint8Array, Int8Array, or ArrayBuffer,
and it throws if passed any other type.
As a special case, to avoid further special cases where
this is used, if srcTypedArray.byteLength is 0, it
allocates a single byte and sets it to the value
0. Even in such cases, calls must behave as if the
allocated memory has exactly srcTypedArray.byteLength
bytes.
*/
wasm.allocFromTypedArray = function(srcTypedArray){
if(srcTypedArray instanceof ArrayBuffer){
srcTypedArray = new Uint8Array(srcTypedArray);
}
affirmBindableTypedArray(srcTypedArray);
const pRet = wasm.alloc(srcTypedArray.byteLength || 1);
wasm.heapForSize(srcTypedArray.constructor).set(
srcTypedArray.byteLength ? srcTypedArray : [0], pRet
);
return pRet;
};
{
// Set up allocators...
const keyAlloc = config.allocExportName,
keyDealloc = config.deallocExportName,
keyRealloc = config.reallocExportName;
for(const key of [keyAlloc, keyDealloc, keyRealloc]){
const f = wasm.exports[key];
if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function.");
}
wasm.alloc = function f(n){
return f.impl(n) || WasmAllocError.toss("Failed to allocate",n," bytes.");
};
wasm.alloc.impl = wasm.exports[keyAlloc];
wasm.realloc = function f(m,n){
const m2 = f.impl(m,n);
return n ? (m2 || WasmAllocError.toss("Failed to reallocate",n," bytes.")) : 0;
};
wasm.realloc.impl = wasm.exports[keyRealloc];
wasm.dealloc = wasm.exports[keyDealloc];
}
/**
Reports info about compile-time options using
sqlite3_compileoption_get() and sqlite3_compileoption_used(). It
has several distinct uses:
If optName is an array then it is expected to be a list of
compilation options and this function returns an object
which maps each such option to true or false, indicating
whether or not the given option was included in this
build. That object is returned.
If optName is an object, its keys are expected to be compilation
options and this function sets each entry to true or false,
indicating whether the compilation option was used or not. That
object is returned.
If passed no arguments then it returns an object mapping
all known compilation options to their compile-time values,
or boolean true if they are defined with no value. This
result, which is relatively expensive to compute, is cached
and returned for future no-argument calls.
In all other cases it returns true if the given option was
active when when compiling the sqlite3 module, else false.
Compile-time option names may optionally include their
"SQLITE_" prefix. When it returns an object of all options,
the prefix is elided.
*/
wasm.compileOptionUsed = function f(optName){
if(!arguments.length){
if(f._result) return f._result;
else if(!f._opt){
f._rx = /^([^=]+)=(.+)/;
f._rxInt = /^-?\d+$/;
f._opt = function(opt, rv){
const m = f._rx.exec(opt);
rv[0] = (m ? m[1] : opt);
rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true;
};
}
const rc = {}, ov = [0,0];
let i = 0, k;
while((k = capi.sqlite3_compileoption_get(i++))){
f._opt(k,ov);
rc[ov[0]] = ov[1];
}
return f._result = rc;
}else if(Array.isArray(optName)){
const rc = {};
optName.forEach((v)=>{
rc[v] = capi.sqlite3_compileoption_used(v);
});
return rc;
}else if('object' === typeof optName){
Object.keys(optName).forEach((k)=> {
optName[k] = capi.sqlite3_compileoption_used(k);
});
return optName;
}
return (
'string'===typeof optName
) ? !!capi.sqlite3_compileoption_used(optName) : false;
}/*compileOptionUsed()*/;
/**
sqlite3.wasm.pstack (pseudo-stack) holds a special-case
stack-style allocator intended only for use with _small_ data of
not more than (in total) a few kb in size, managed as if it were
stack-based.
It has only a single intended usage:
```
const stackPos = pstack.pointer;
try{
const ptr = pstack.alloc(8);
// ==> pstack.pointer === ptr
const otherPtr = pstack.alloc(8);
// ==> pstack.pointer === otherPtr
...
}finally{
pstack.restore(stackPos);
// ==> pstack.pointer === stackPos
}
```
This allocator is much faster than a general-purpose one but is
limited to usage patterns like the one shown above.
It operates from a static range of memory which lives outside of
space managed by Emscripten's stack-management, so does not
collide with Emscripten-provided stack allocation APIs. The
memory lives in the WASM heap and can be used with routines such
as wasm.poke() and wasm.heap8u().slice().
*/
wasm.pstack = Object.assign(Object.create(null),{
/**
Sets the current pstack position to the given pointer. Results
are undefined if the passed-in value did not come from
this.pointer.
*/
restore: wasm.exports.sqlite3__wasm_pstack_restore,
/**
Attempts to allocate the given number of bytes from the
pstack. On success, it zeroes out a block of memory of the
given size, adjusts the pstack pointer, and returns a pointer
to the memory. On error, throws a WasmAllocError. The
memory must eventually be released using restore().
If n is a string, it must be a WASM "IR" value in the set
accepted by wasm.sizeofIR(), which is mapped to the size of
that data type. If passed a string not in that set, it throws a
WasmAllocError.
This method always adjusts the given value to be a multiple
of 8 bytes because failing to do so can lead to incorrect
results when reading and writing 64-bit values from/to the WASM
heap. Similarly, the returned address is always 8-byte aligned.
*/
alloc: function(n){
if('string'===typeof n && !(n = wasm.sizeofIR(n))){
WasmAllocError.toss("Invalid value for pstack.alloc(",arguments[0],")");
}
return wasm.exports.sqlite3__wasm_pstack_alloc(n)
|| WasmAllocError.toss("Could not allocate",n,
"bytes from the pstack.");
},
/**
alloc()'s n chunks, each sz bytes, as a single memory block and
returns the addresses as an array of n element, each holding
the address of one chunk.
sz may optionally be an IR string accepted by wasm.sizeofIR().
Throws a WasmAllocError if allocation fails.
Example:
```
const [p1, p2, p3] = wasm.pstack.allocChunks(3,4);
```
*/
allocChunks: function(n,sz){
if('string'===typeof sz && !(sz = wasm.sizeofIR(sz))){
WasmAllocError.toss("Invalid size value for allocChunks(",arguments[1],")");
}
const mem = wasm.pstack.alloc(n * sz);
const rc = [];
let i = 0, offset = 0;
for(; i < n; ++i, offset += sz) rc.push(mem + offset);
return rc;
},
/**
A convenience wrapper for allocChunks() which sizes each chunk
as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if
safePtrSize is falsy).
How it returns its result differs depending on its first
argument: if it's 1, it returns a single pointer value. If it's
more than 1, it returns the same as allocChunks().
When a returned pointers will refer to a 64-bit value, e.g. a
double or int64, and that value must be written or fetched,
e.g. using wasm.poke() or wasm.peek(), it is
important that the pointer in question be aligned to an 8-byte
boundary or else it will not be fetched or written properly and
will corrupt or read neighboring memory.
However, when all pointers involved point to "small" data, it
is safe to pass a falsy value to save a tiny bit of memory.
*/
allocPtr: (n=1,safePtrSize=true)=>{
return 1===n
? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof)
: wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof);
},
/**
Records the current pstack position, calls the given function,
passing it the sqlite3 object, then restores the pstack
regardless of whether the function throws. Returns the result
of the call or propagates an exception on error.
Added in 3.44.
*/
call: function(f){
const stackPos = wasm.pstack.pointer;
try{ return f(sqlite3) } finally{
wasm.pstack.restore(stackPos);
}
}
})/*wasm.pstack*/;
Object.defineProperties(wasm.pstack, {
/**
sqlite3.wasm.pstack.pointer resolves to the current pstack
position pointer. This value is intended _only_ to be saved
for passing to restore(). Writing to this memory, without
first reserving it via wasm.pstack.alloc() and friends, leads
to undefined results.
*/
pointer: {
configurable: false, iterable: true, writeable: false,
get: wasm.exports.sqlite3__wasm_pstack_ptr
//Whether or not a setter as an alternative to restore() is
//clearer or would just lead to confusion is unclear.
//set: wasm.exports.sqlite3__wasm_pstack_restore
},
/**
sqlite3.wasm.pstack.quota to the total number of bytes
available in the pstack, including any space which is currently
allocated. This value is a compile-time constant.
*/
quota: {
configurable: false, iterable: true, writeable: false,
get: wasm.exports.sqlite3__wasm_pstack_quota
},
/**
sqlite3.wasm.pstack.remaining resolves to the amount of space
remaining in the pstack.
*/
remaining: {
configurable: false, iterable: true, writeable: false,
get: wasm.exports.sqlite3__wasm_pstack_remaining
}
})/*wasm.pstack properties*/;
capi.sqlite3_randomness = (...args)=>{
if(1===args.length && util.isTypedArray(args[0])
&& 1===args[0].BYTES_PER_ELEMENT){
const ta = args[0];
if(0===ta.byteLength){
wasm.exports.sqlite3_randomness(0,0);
return ta;
}
const stack = wasm.pstack.pointer;
try {
let n = ta.byteLength, offset = 0;
const r = wasm.exports.sqlite3_randomness;
const heap = wasm.heap8u();
const nAlloc = n < 512 ? n : 512;
const ptr = wasm.pstack.alloc(nAlloc);
do{
const j = (n>nAlloc ? nAlloc : n);
r(j, ptr);
ta.set(typedArrayPart(heap, ptr, ptr+j), offset);
n -= j;
offset += j;
} while(n > 0);
}catch(e){
console.error("Highly unexpected (and ignored!) "+
"exception in sqlite3_randomness():",e);
}finally{
wasm.pstack.restore(stack);
}
return ta;
}
wasm.exports.sqlite3_randomness(...args);
};
/** State for sqlite3_wasmfs_opfs_dir(). */
let __wasmfsOpfsDir = undefined;
/**
If the wasm environment has a WASMFS/OPFS-backed persistent
storage directory, its path is returned by this function. If it
does not then it returns "" (noting that "" is a falsy value).
The first time this is called, this function inspects the current
environment to determine whether persistence support is available
and, if it is, enables it (if needed). After the first call it
always returns the cached result.
If the returned string is not empty, any files stored under the
given path (recursively) are housed in OPFS storage. If the
returned string is empty, this particular persistent storage
option is not available on the client.
Though the mount point name returned by this function is intended
to remain stable, clients should not hard-coded it anywhere. Always call this function to get the path.
Note that this function is a no-op in must builds of this
library, as the WASMFS capability requires a custom
build.
*/
capi.sqlite3_wasmfs_opfs_dir = function(){
if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir;
// If we have no OPFS, there is no persistent dir
const pdir = config.wasmfsOpfsDir;
if(!pdir
|| !globalThis.FileSystemHandle
|| !globalThis.FileSystemDirectoryHandle
|| !globalThis.FileSystemFileHandle){
return __wasmfsOpfsDir = "";
}
try{
if(pdir && 0===wasm.xCallWrapped(
'sqlite3__wasm_init_wasmfs', 'i32', ['string'], pdir
)){
return __wasmfsOpfsDir = pdir;
}else{
return __wasmfsOpfsDir = "";
}
}catch(e){
// sqlite3__wasm_init_wasmfs() is not available
return __wasmfsOpfsDir = "";
}
};
/**
Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a
non-empty string and the given name starts with (that string +
'/'), else returns false.
*/
capi.sqlite3_wasmfs_filename_is_persistent = function(name){
const p = capi.sqlite3_wasmfs_opfs_dir();
return (p && name) ? name.startsWith(p+'/') : false;
};
/**
Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name
(defaulting to "main"), returns a truthy value (see below) if
that db uses that VFS, else returns false. If pDb is falsy then
the 3rd argument is ignored and this function returns a truthy
value if the default VFS name matches that of the 2nd
argument. Results are undefined if pDb is truthy but refers to an
invalid pointer. The 3rd argument specifies the database name of
the given database connection to check, defaulting to the main
db.
The 2nd and 3rd arguments may either be a JS string or a WASM
C-string. If the 2nd argument is a NULL WASM pointer, the default
VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is
assumed.
The truthy value it returns is a pointer to the `sqlite3_vfs`
object.
To permit safe use of this function from APIs which may be called
via the C stack (like SQL UDFs), this function does not throw: if
bad arguments cause a conversion error when passing into
wasm-space, false is returned.
*/
capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName=0){
try{
const pK = capi.sqlite3_vfs_find(vfsName);
if(!pK) return false;
else if(!pDb){
return pK===capi.sqlite3_vfs_find(0) ? pK : false;
}else{
return pK===capi.sqlite3_js_db_vfs(pDb,dbName) ? pK : false;
}
}catch(e){
/* Ignore - probably bad args to a wasm-bound function. */
return false;
}
};
/**
Returns an array of the names of all currently-registered sqlite3
VFSes.
*/
capi.sqlite3_js_vfs_list = function(){
const rc = [];
let pVfs = capi.sqlite3_vfs_find(0);
while(pVfs){
const oVfs = new capi.sqlite3_vfs(pVfs);
rc.push(wasm.cstrToJs(oVfs.$zName));
pVfs = oVfs.$pNext;
oVfs.dispose();
}
return rc;
};
/**
A convenience wrapper around sqlite3_serialize() which serializes
the given `sqlite3*` pointer to a Uint8Array. The first argument
may be either an `sqlite3*` or an sqlite3.oo1.DB instance.
On success it returns a Uint8Array. If the schema is empty, an
empty array is returned.
`schema` is the schema to serialize. It may be a WASM C-string
pointer or a JS string. If it is falsy, it defaults to `"main"`.
On error it throws with a description of the problem.
*/
capi.sqlite3_js_db_export = function(pDb, schema=0){
pDb = wasm.xWrap.testConvertArg('sqlite3*', pDb);
if(!pDb) toss3('Invalid sqlite3* argument.');
if(!wasm.bigIntEnabled) toss3('BigInt64 support is not enabled.');
const scope = wasm.scopedAllocPush();
let pOut;
try{
const pSize = wasm.scopedAlloc(8/*i64*/ + wasm.ptrSizeof);
const ppOut = pSize + 8;
/**
Maintenance reminder, since this cost a full hour of grief
and confusion: if the order of pSize/ppOut are reversed in
that memory block, fetching the value of pSize after the
export reads a garbage size because it's not on an 8-byte
memory boundary!
*/
const zSchema = schema
? (wasm.isPtr(schema) ? schema : wasm.scopedAllocCString(''+schema))
: 0;
let rc = wasm.exports.sqlite3__wasm_db_serialize(
pDb, zSchema, ppOut, pSize, 0
);
if(rc){
toss3("Database serialization failed with code",
sqlite3.capi.sqlite3_js_rc_str(rc));
}
pOut = wasm.peekPtr(ppOut);
const nOut = wasm.peek(pSize, 'i64');
rc = nOut
? wasm.heap8u().slice(pOut, pOut + Number(nOut))
: new Uint8Array();
return rc;
}finally{
if(pOut) wasm.exports.sqlite3_free(pOut);
wasm.scopedAllocPop(scope);
}
};
/**
Given a `sqlite3*` and a database name (JS string or WASM
C-string pointer, which may be 0), returns a pointer to the
sqlite3_vfs responsible for it. If the given db name is null/0,
or not provided, then "main" is assumed.
*/
capi.sqlite3_js_db_vfs =
(dbPointer, dbName=0)=>util.sqlite3__wasm_db_vfs(dbPointer, dbName);
/**
A thin wrapper around capi.sqlite3_aggregate_context() which
behaves the same except that it throws a WasmAllocError if that
function returns 0. As a special case, if n is falsy it does
_not_ throw if that function returns 0. That special case is
intended for use with xFinal() implementations.
*/
capi.sqlite3_js_aggregate_context = (pCtx, n)=>{
return capi.sqlite3_aggregate_context(pCtx, n)
|| (n ? WasmAllocError.toss("Cannot allocate",n,
"bytes for sqlite3_aggregate_context()")
: 0);
};
/**
If the current environment supports the POSIX file APIs, this routine
creates (or overwrites) the given file using those APIs. This is
primarily intended for use in Emscripten-based builds where the POSIX
APIs are transparently proxied by an in-memory virtual filesystem.
It may behave diffrently in other environments.
The first argument must be either a JS string or WASM C-string
holding the filename. Note that this routine does _not_ create
intermediary directories if the filename has a directory part.
The 2nd argument may either a valid WASM memory pointer, an
ArrayBuffer, or a Uint8Array. The 3rd must be the length, in
bytes, of the data array to copy. If the 2nd argument is an
ArrayBuffer or Uint8Array and the 3rd is not a positive integer
then the 3rd defaults to the array's byteLength value.
Results are undefined if data is a WASM pointer and dataLen is
exceeds data's bounds.
Throws if any arguments are invalid or if creating or writing to
the file fails.
Added in 3.43 as an alternative for the deprecated
sqlite3_js_vfs_create_file().
*/
capi.sqlite3_js_posix_create_file = function(filename, data, dataLen){
let pData;
if(data && wasm.isPtr(data)){
pData = data;
}else if(data instanceof ArrayBuffer || data instanceof Uint8Array){
pData = wasm.allocFromTypedArray(data);
if(arguments.length<3 || !util.isInt32(dataLen) || dataLen<0){
dataLen = data.byteLength;
}
}else{
SQLite3Error.toss("Invalid 2nd argument for sqlite3_js_posix_create_file().");
}
try{
if(!util.isInt32(dataLen) || dataLen<0){
SQLite3Error.toss("Invalid 3rd argument for sqlite3_js_posix_create_file().");
}
const rc = util.sqlite3__wasm_posix_create_file(filename, pData, dataLen);
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
capi.sqlite3_js_rc_str(rc));
}finally{
wasm.dealloc(pData);
}
};
/**
Deprecation warning: this function does not work properly in
debug builds of sqlite3 because its out-of-scope use of the
sqlite3_vfs API triggers assertions in the core library. That
was unfortunately not discovered until 2023-08-11. This function
is now deprecated and should not be used in new code.
Alternative options:
- "unix" VFS and its variants can get equivalent functionality
with sqlite3_js_posix_create_file().
- OPFS: use either sqlite3.oo1.OpfsDb.importDb(), for the "opfs"
VFS, or the importDb() method of the PoolUtil object provided
by the "opfs-sahpool" OPFS (noting that its VFS name may differ
depending on client-side configuration). We cannot proxy those
from here because the former is necessarily asynchronous and
the latter requires information not available to this function.
Creates a file using the storage appropriate for the given
sqlite3_vfs. The first argument may be a VFS name (JS string
only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or
a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the
default VFS. If passed a string which does not resolve using
sqlite3_vfs_find(), an exception is thrown. (Note that a WASM
C-string is not accepted because it is impossible to
distinguish from a C-level `sqlite3_vfs*`.)
The second argument, the filename, must be a JS or WASM C-string.
The 3rd may either be falsy, a valid WASM memory pointer, an
ArrayBuffer, or a Uint8Array. The 4th must be the length, in
bytes, of the data array to copy. If the 3rd argument is an
ArrayBuffer or Uint8Array and the 4th is not a positive integer
then the 4th defaults to the array's byteLength value.
If data is falsy then a file is created with dataLen bytes filled
with uninitialized data (whatever truncate() leaves there). If
data is not falsy then a file is created or truncated and it is
filled with the first dataLen bytes of the data source.
Throws if any arguments are invalid or if creating or writing to
the file fails.
Note that most VFSes do _not_ automatically create directory
parts of filenames, nor do all VFSes have a concept of
directories. If the given filename is not valid for the given
VFS, an exception will be thrown. This function exists primarily
to assist in implementing file-upload capability, with the caveat
that clients must have some idea of the VFS into which they want
to upload and that VFS must support the operation.
VFS-specific notes:
- "memdb": results are undefined.
- "kvvfs": will fail with an I/O error due to strict internal
requirments of that VFS's xTruncate().
- "unix" and related: will use the WASM build's equivalent of the
POSIX I/O APIs. This will work so long as neither a specific
VFS nor the WASM environment imposes requirements which break it.
- "opfs": uses OPFS storage and creates directory parts of the
filename. It can only be used to import an SQLite3 database
file and will fail if given anything else.
*/
capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
config.warn("sqlite3_js_vfs_create_file() is deprecated and",
"should be avoided because it can lead to C-level crashes.",
"See its documentation for alternative options.");
let pData;
if(data){
if(wasm.isPtr(data)){
pData = data;
}else if(data instanceof ArrayBuffer){
data = new Uint8Array(data);
}
if(data instanceof Uint8Array){
pData = wasm.allocFromTypedArray(data);
if(arguments.length<4 || !util.isInt32(dataLen) || dataLen<0){
dataLen = data.byteLength;
}
}else{
SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
}
}else{
pData = 0;
}
if(!util.isInt32(dataLen) || dataLen<0){
wasm.dealloc(pData);
SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
}
try{
const rc = util.sqlite3__wasm_vfs_create_file(vfs, filename, pData, dataLen);
if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
capi.sqlite3_js_rc_str(rc));
}finally{
wasm.dealloc(pData);
}
};
/**
Converts SQL input from a variety of convenient formats
to plain strings.
If v is a string, it is returned as-is. If it is-a Array, its
join("") result is returned. If is is a Uint8Array, Int8Array,
or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is
decoded to a string. If it looks like a WASM pointer,
wasm.cstrToJs(sql) is returned. Else undefined is returned.
Added in 3.44
*/
capi.sqlite3_js_sql_to_string = (sql)=>{
if('string' === typeof sql){
return sql;
}
const x = flexibleString(v);
return x===v ? undefined : x;
}
if( util.isUIThread() ){
/* Features specific to the main window thread... */
/**
Internal helper for sqlite3_js_kvvfs_clear() and friends.
Its argument should be one of ('local','session',"").
*/
const __kvvfsInfo = function(which){
const rc = Object.create(null);
rc.prefix = 'kvvfs-'+which;
rc.stores = [];
if('session'===which || ""===which) rc.stores.push(globalThis.sessionStorage);
if('local'===which || ""===which) rc.stores.push(globalThis.localStorage);
return rc;
};
/**
Clears all storage used by the kvvfs DB backend, deleting any
DB(s) stored there. Its argument must be either 'session',
'local', or "". In the first two cases, only sessionStorage
resp. localStorage is cleared. If it's an empty string (the
default) then both are cleared. Only storage keys which match
the pattern used by kvvfs are cleared: any other client-side
data are retained.
This function is only available in the main window thread.
Returns the number of entries cleared.
*/
capi.sqlite3_js_kvvfs_clear = function(which=""){
let rc = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
const toRm = [] /* keys to remove */;
let i;
for( i = 0; i < s.length; ++i ){
const k = s.key(i);
if(k.startsWith(kvinfo.prefix)) toRm.push(k);
}
toRm.forEach((kk)=>s.removeItem(kk));
rc += toRm.length;
});
return rc;
};
/**
This routine guesses the approximate amount of
window.localStorage and/or window.sessionStorage in use by the
kvvfs database backend. Its argument must be one of
('session', 'local', ""). In the first two cases, only
sessionStorage resp. localStorage is counted. If it's an empty
string (the default) then both are counted. Only storage keys
which match the pattern used by kvvfs are counted. The returned
value is the "length" value of every matching key and value,
noting that JavaScript stores each character in 2 bytes.
Note that the returned size is not authoritative from the
perspective of how much data can fit into localStorage and
sessionStorage, as the precise algorithms for determining
those limits are unspecified and may include per-entry
overhead invisible to clients.
*/
capi.sqlite3_js_kvvfs_size = function(which=""){
let sz = 0;
const kvinfo = __kvvfsInfo(which);
kvinfo.stores.forEach((s)=>{
let i;
for(i = 0; i < s.length; ++i){
const k = s.key(i);
if(k.startsWith(kvinfo.prefix)){
sz += k.length;
sz += s.getItem(k).length;
}
}
});
return sz * 2 /* because JS uses 2-byte char encoding */;
};
}/* main-window-only bits */
/**
Wraps all known variants of the C-side variadic
sqlite3_db_config().
Full docs: https://sqlite.org/c3ref/db_config.html
Returns capi.SQLITE_MISUSE if op is not a valid operation ID.
The variants which take `(int, int*)` arguments treat a
missing or falsy pointer argument as 0.
*/
capi.sqlite3_db_config = function(pDb, op, ...args){
if(!this.s){
this.s = wasm.xWrap('sqlite3__wasm_db_config_s','int',
['sqlite3*', 'int', 'string:static']
/* MAINDBNAME requires a static string */);
this.pii = wasm.xWrap('sqlite3__wasm_db_config_pii', 'int',
['sqlite3*', 'int', '*','int', 'int']);
this.ip = wasm.xWrap('sqlite3__wasm_db_config_ip','int',
['sqlite3*', 'int', 'int','*']);
}
switch(op){
case capi.SQLITE_DBCONFIG_ENABLE_FKEY:
case capi.SQLITE_DBCONFIG_ENABLE_TRIGGER:
case capi.SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
case capi.SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
case capi.SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
case capi.SQLITE_DBCONFIG_ENABLE_QPSG:
case capi.SQLITE_DBCONFIG_TRIGGER_EQP:
case capi.SQLITE_DBCONFIG_RESET_DATABASE:
case capi.SQLITE_DBCONFIG_DEFENSIVE:
case capi.SQLITE_DBCONFIG_WRITABLE_SCHEMA:
case capi.SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
case capi.SQLITE_DBCONFIG_DQS_DML:
case capi.SQLITE_DBCONFIG_DQS_DDL:
case capi.SQLITE_DBCONFIG_ENABLE_VIEW:
case capi.SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
case capi.SQLITE_DBCONFIG_TRUSTED_SCHEMA:
case capi.SQLITE_DBCONFIG_STMT_SCANSTATUS:
case capi.SQLITE_DBCONFIG_REVERSE_SCANORDER:
return this.ip(pDb, op, args[0], args[1] || 0);
case capi.SQLITE_DBCONFIG_LOOKASIDE:
return this.pii(pDb, op, args[0], args[1], args[2]);
case capi.SQLITE_DBCONFIG_MAINDBNAME:
return this.s(pDb, op, args[0]);
default:
return capi.SQLITE_MISUSE;
}
}.bind(Object.create(null));
/**
Given a (sqlite3_value*), this function attempts to convert it
to an equivalent JS value with as much fidelity as feasible and
return it.
By default it throws if it cannot determine any sensible
conversion. If passed a falsy second argument, it instead returns
`undefined` if no suitable conversion is found. Note that there
is no conversion from SQL to JS which results in the `undefined`
value, so `undefined` has an unambiguous meaning here. It will
always throw a WasmAllocError if allocating memory for a
conversion fails.
Caveats:
- It does not support sqlite3_value_to_pointer() conversions
because those require a type name string which this function
does not have and cannot sensibly be given at the level of the
API where this is used (e.g. automatically converting UDF
arguments). Clients using sqlite3_value_to_pointer(), and its
related APIs, will need to manage those themselves.
*/
capi.sqlite3_value_to_js = function(pVal,throwIfCannotConvert=true){
let arg;
const valType = capi.sqlite3_value_type(pVal);
switch(valType){
case capi.SQLITE_INTEGER:
if(wasm.bigIntEnabled){
arg = capi.sqlite3_value_int64(pVal);
if(util.bigIntFitsDouble(arg)) arg = Number(arg);
}
else arg = capi.sqlite3_value_double(pVal)/*yes, double, for larger integers*/;
break;
case capi.SQLITE_FLOAT:
arg = capi.sqlite3_value_double(pVal);
break;
case capi.SQLITE_TEXT:
arg = capi.sqlite3_value_text(pVal);
break;
case capi.SQLITE_BLOB:{
const n = capi.sqlite3_value_bytes(pVal);
const pBlob = capi.sqlite3_value_blob(pVal);
if(n && !pBlob) sqlite3.WasmAllocError.toss(
"Cannot allocate memory for blob argument of",n,"byte(s)"
);
arg = n ? wasm.heap8u().slice(pBlob, pBlob + Number(n)) : null;
break;
}
case capi.SQLITE_NULL:
arg = null; break;
default:
if(throwIfCannotConvert){
toss3(capi.SQLITE_MISMATCH,
"Unhandled sqlite3_value_type():",valType);
}
arg = undefined;
}
return arg;
};
/**
Requires a C-style array of `sqlite3_value*` objects and the
number of entries in that array. Returns a JS array containing
the results of passing each C array entry to
sqlite3_value_to_js(). The 3rd argument to this function is
passed on as the 2nd argument to that one.
*/
capi.sqlite3_values_to_js = function(argc,pArgv,throwIfCannotConvert=true){
let i;
const tgt = [];
for(i = 0; i < argc; ++i){
/**
Curiously: despite ostensibly requiring 8-byte
alignment, the pArgv array is parcelled into chunks of
4 bytes (1 pointer each). The values those point to
have 8-byte alignment but the individual argv entries
do not.
*/
tgt.push(capi.sqlite3_value_to_js(
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
throwIfCannotConvert
));
}
return tgt;
};
/**
Calls either sqlite3_result_error_nomem(), if e is-a
WasmAllocError, or sqlite3_result_error(). In the latter case,
the second argument is coerced to a string to create the error
message.
The first argument is a (sqlite3_context*). Returns void.
Does not throw.
*/
capi.sqlite3_result_error_js = function(pCtx,e){
if(e instanceof WasmAllocError){
capi.sqlite3_result_error_nomem(pCtx);
}else{
/* Maintenance reminder: ''+e, rather than e.message,
will prefix e.message with e.name, so it includes
the exception's type name in the result. */;
capi.sqlite3_result_error(pCtx, ''+e, -1);
}
};
/**
This function passes its 2nd argument to one of the
sqlite3_result_xyz() routines, depending on the type of that
argument:
- If (val instanceof Error), this function passes it to
sqlite3_result_error_js().
- `null`: `sqlite3_result_null()`
- `boolean`: `sqlite3_result_int()` with a value of 0 or 1.
- `number`: `sqlite3_result_int()`, `sqlite3_result_int64()`, or
`sqlite3_result_double()`, depending on the range of the number
and whether or not int64 support is enabled.
- `bigint`: similar to `number` but will trigger an error if the
value is too big to store in an int64.
- `string`: `sqlite3_result_text()`
- Uint8Array or Int8Array or ArrayBuffer: `sqlite3_result_blob()`
- `undefined`: is a no-op provided to simplify certain use cases.
Anything else triggers `sqlite3_result_error()` with a
description of the problem.
The first argument to this function is a `(sqlite3_context*)`.
Returns void. Does not throw.
*/
capi.sqlite3_result_js = function(pCtx,val){
if(val instanceof Error){
capi.sqlite3_result_error_js(pCtx, val);
return;
}
try{
switch(typeof val) {
case 'undefined':
/* This is a no-op. This routine originated in the create_function()
family of APIs and in that context, passing in undefined indicated
that the caller was responsible for calling sqlite3_result_xxx()
(if needed). */
break;
case 'boolean':
capi.sqlite3_result_int(pCtx, val ? 1 : 0);
break;
case 'bigint':
if(util.bigIntFits32(val)){
capi.sqlite3_result_int(pCtx, Number(val));
}else if(util.bigIntFitsDouble(val)){
capi.sqlite3_result_double(pCtx, Number(val));
}else if(wasm.bigIntEnabled){
if(util.bigIntFits64(val)) capi.sqlite3_result_int64(pCtx, val);
else toss3("BigInt value",val.toString(),"is too BigInt for int64.");
}else{
toss3("BigInt value",val.toString(),"is too BigInt.");
}
break;
case 'number': {
let f;
if(util.isInt32(val)){
f = capi.sqlite3_result_int;
}else if(wasm.bigIntEnabled
&& Number.isInteger(val)
&& util.bigIntFits64(BigInt(val))){
f = capi.sqlite3_result_int64;
}else{
f = capi.sqlite3_result_double;
}
f(pCtx, val);
break;
}
case 'string': {
const [p, n] = wasm.allocCString(val,true);
capi.sqlite3_result_text(pCtx, p, n, capi.SQLITE_WASM_DEALLOC);
break;
}
case 'object':
if(null===val/*yes, typeof null === 'object'*/) {
capi.sqlite3_result_null(pCtx);
break;
}else if(util.isBindableTypedArray(val)){
const pBlob = wasm.allocFromTypedArray(val);
capi.sqlite3_result_blob(
pCtx, pBlob, val.byteLength,
capi.SQLITE_WASM_DEALLOC
);
break;
}
// else fall through
default:
toss3("Don't not how to handle this UDF result value:",(typeof val), val);
}
}catch(e){
capi.sqlite3_result_error_js(pCtx, e);
}
};
/**
Returns the result sqlite3_column_value(pStmt,iCol) passed to
sqlite3_value_to_js(). The 3rd argument of this function is
ignored by this function except to pass it on as the second
argument of sqlite3_value_to_js(). If the sqlite3_column_value()
returns NULL (e.g. because the column index is out of range),
this function returns `undefined`, regardless of the 3rd
argument. If the 3rd argument is falsy and conversion fails,
`undefined` will be returned.
Note that sqlite3_column_value() returns an "unprotected" value
object, but in a single-threaded environment (like this one)
there is no distinction between protected and unprotected values.
*/
capi.sqlite3_column_js = function(pStmt, iCol, throwIfCannotConvert=true){
const v = capi.sqlite3_column_value(pStmt, iCol);
return (0===v) ? undefined : capi.sqlite3_value_to_js(v, throwIfCannotConvert);
};
/**
Internal impl of sqlite3_preupdate_new/old_js() and
sqlite3changeset_new/old_js().
*/
const __newOldValue = function(pObj, iCol, impl){
impl = capi[impl];
if(!this.ptr) this.ptr = wasm.allocPtr();
else wasm.pokePtr(this.ptr, 0);
const rc = impl(pObj, iCol, this.ptr);
if(rc) return SQLite3Error.toss(rc,arguments[2]+"() failed with code "+rc);
const pv = wasm.peekPtr(this.ptr);
return pv ? capi.sqlite3_value_to_js( pv, true ) : undefined;
}.bind(Object.create(null));
/**
A wrapper around sqlite3_preupdate_new() which fetches the
sqlite3_value at the given index and returns the result of
passing it to sqlite3_value_to_js(). Throws on error.
*/
capi.sqlite3_preupdate_new_js =
(pDb, iCol)=>__newOldValue(pDb, iCol, 'sqlite3_preupdate_new');
/**
The sqlite3_preupdate_old() counterpart of
sqlite3_preupdate_new_js(), with an identical interface.
*/
capi.sqlite3_preupdate_old_js =
(pDb, iCol)=>__newOldValue(pDb, iCol, 'sqlite3_preupdate_old');
/**
A wrapper around sqlite3changeset_new() which fetches the
sqlite3_value at the given index and returns the result of
passing it to sqlite3_value_to_js(). Throws on error.
If sqlite3changeset_new() succeeds but has no value to report,
this function returns the undefined value, noting that undefined
is a valid conversion from an `sqlite3_value`, so is unambiguous.
*/
capi.sqlite3changeset_new_js =
(pChangesetIter, iCol) => __newOldValue(pChangesetIter, iCol,
'sqlite3changeset_new');
/**
The sqlite3changeset_old() counterpart of
sqlite3changeset_new_js(), with an identical interface.
*/
capi.sqlite3changeset_old_js =
(pChangesetIter, iCol)=>__newOldValue(pChangesetIter, iCol,
'sqlite3changeset_old');
/* The remainder of the API will be set up in later steps. */
const sqlite3 = {
WasmAllocError: WasmAllocError,
SQLite3Error: SQLite3Error,
capi,
util,
wasm,
config,
/**
Holds the version info of the sqlite3 source tree from which
the generated sqlite3-api.js gets built. Note that its version
may well differ from that reported by sqlite3_libversion(), but
that should be considered a source file mismatch, as the JS and
WASM files are intended to be built and distributed together.
This object is initially a placeholder which gets replaced by a
build-generated object.
*/
version: Object.create(null),
/**
The library reserves the 'client' property for client-side use
and promises to never define a property with this name nor to
ever rely on specific contents of it. It makes no such guarantees
for other properties.
*/
client: undefined,
/**
This function is not part of the public interface, but a
piece of internal bootstrapping infrastructure.
Performs any optional asynchronous library-level initialization
which might be required. This function returns a Promise which
resolves to the sqlite3 namespace object. Any error in the
async init will be fatal to the init as a whole, but init
routines are themselves welcome to install dummy catch()
handlers which are not fatal if their failure should be
considered non-fatal. If called more than once, the second and
subsequent calls are no-ops which return a pre-resolved
Promise.
Ideally this function is called as part of the Promise chain
which handles the loading and bootstrapping of the API. If not
then it must be called by client-level code, which must not use
the library until the returned promise resolves.
If called multiple times it will return the same promise on
subsequent calls. The current build setup precludes that
possibility, so it's only a hypothetical problem if/when this
function ever needs to be invoked by clients.
In Emscripten-based builds, this function is called
automatically and deleted from this object.
*/
asyncPostInit: async function ff(){
if(ff.isReady instanceof Promise) return ff.isReady;
let lia = sqlite3ApiBootstrap.initializersAsync;
delete sqlite3ApiBootstrap.initializersAsync;
const postInit = async ()=>{
if(!sqlite3.__isUnderTest){
/* Delete references to internal-only APIs which are used by
some initializers. Retain them when running in test mode
so that we can add tests for them. */
delete sqlite3.util;
/* It's conceivable that we might want to expose
StructBinder to client-side code, but it's only useful if
clients build their own sqlite3.wasm which contains their
own C struct types. */
delete sqlite3.StructBinder;
}
return sqlite3;
};
const catcher = (e)=>{
config.error("an async sqlite3 initializer failed:",e);
throw e;
};
if(!lia || !lia.length){
return ff.isReady = postInit().catch(catcher);
}
lia = lia.map((f)=>{
return (f instanceof Function) ? async x=>f(sqlite3) : f;
});
lia.push(postInit);
let p = Promise.resolve(sqlite3);
while(lia.length) p = p.then(lia.shift());
return ff.isReady = p.catch(catcher);
},
/**
scriptInfo ideally gets injected into this object by the
infrastructure which assembles the JS/WASM module. It contains
state which must be collected before sqlite3ApiBootstrap() can
be declared. It is not necessarily available to any
sqlite3ApiBootstrap.initializers but "should" be in place (if
it's added at all) by the time that
sqlite3ApiBootstrap.initializersAsync is processed.
This state is not part of the public API, only intended for use
with the sqlite3 API bootstrapping and wasm-loading process.
*/
scriptInfo: undefined
};
try{
sqlite3ApiBootstrap.initializers.forEach((f)=>{
f(sqlite3);
});
}catch(e){
/* If we don't report this here, it can get completely swallowed
up and disappear into the abyss of Promises and Workers. */
console.error("sqlite3 bootstrap initializer threw:",e);
throw e;
}
delete sqlite3ApiBootstrap.initializers;
sqlite3ApiBootstrap.sqlite3 = sqlite3;
return sqlite3;
}/*sqlite3ApiBootstrap()*/;
/**
globalThis.sqlite3ApiBootstrap.initializers is an internal detail used by
the various pieces of the sqlite3 API's amalgamation process. It
must not be modified by client code except when plugging such code
into the amalgamation process.
Each component of the amalgamation is expected to append a function
to this array. When sqlite3ApiBootstrap() is called for the first
time, each such function will be called (in their appended order)
and passed the sqlite3 namespace object, into which they can install
their features (noting that most will also require that certain
features alread have been installed). At the end of that process,
this array is deleted.
Note that the order of insertion into this array is significant for
some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully
utilized until the whwasmutil.js part is plugged in via
sqlite3-api-glue.js.
*/
globalThis.sqlite3ApiBootstrap.initializers = [];
/**
globalThis.sqlite3ApiBootstrap.initializersAsync is an internal detail
used by the sqlite3 API's amalgamation process. It must not be
modified by client code except when plugging such code into the
amalgamation process.
The counterpart of globalThis.sqlite3ApiBootstrap.initializers,
specifically for initializers which are asynchronous. All entries in
this list must be either async functions, non-async functions which
return a Promise, or a Promise. Each function in the list is called
with the sqlite3 object as its only argument.
The resolved value of any Promise is ignored and rejection will kill
the asyncPostInit() process (at an indeterminate point because all
of them are run asynchronously in parallel).
This list is not processed until the client calls
sqlite3.asyncPostInit(). This means, for example, that intializers
added to globalThis.sqlite3ApiBootstrap.initializers may push entries to
this list.
*/
globalThis.sqlite3ApiBootstrap.initializersAsync = [];
/**
Client code may assign sqlite3ApiBootstrap.defaultConfig an
object-type value before calling sqlite3ApiBootstrap() (without
arguments) in order to tell that call to use this object as its
default config value. The intention of this is to provide
downstream clients with a reasonably flexible approach for plugging in
an environment-suitable configuration without having to define a new
global-scope symbol.
*/
globalThis.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
/**
Placeholder: gets installed by the first call to
globalThis.sqlite3ApiBootstrap(). However, it is recommended that the
caller of sqlite3ApiBootstrap() capture its return value and delete
globalThis.sqlite3ApiBootstrap after calling it. It returns the same
value which will be stored here.
*/
globalThis.sqlite3ApiBootstrap.sqlite3 = undefined;
|