13961b263Sstephan/* 23961b263Sstephan 2022-05-22 33961b263Sstephan 43961b263Sstephan The author disclaims copyright to this source code. In place of a 53961b263Sstephan legal notice, here is a blessing: 63961b263Sstephan 73961b263Sstephan * May you do good and not evil. 83961b263Sstephan * May you find forgiveness for yourself and forgive others. 93961b263Sstephan * May you share freely, never taking more than you give. 103961b263Sstephan 113961b263Sstephan *********************************************************************** 123961b263Sstephan 133961b263Sstephan This file is intended to be combined at build-time with other 143961b263Sstephan related code, most notably a header and footer which wraps this whole 153961b263Sstephan file into an Emscripten Module.postRun() handler which has a parameter 163961b263Sstephan named "Module" (the Emscripten Module object). The exact requirements, 173961b263Sstephan conventions, and build process are very much under construction and 183961b263Sstephan will be (re)documented once they've stopped fluctuating so much. 193961b263Sstephan 20c0a18d6aSstephan Project home page: https://sqlite.org 21c0a18d6aSstephan 22c0a18d6aSstephan Documentation home page: https://sqlite.org/wasm 23c0a18d6aSstephan 24c0a18d6aSstephan Specific goals of this subproject: 253961b263Sstephan 263961b263Sstephan - Except where noted in the non-goals, provide a more-or-less 273961b263Sstephan feature-complete wrapper to the sqlite3 C API, insofar as WASM 28c0a18d6aSstephan feature parity with C allows for. In fact, provide at least 4 293961b263Sstephan APIs... 303961b263Sstephan 31c0a18d6aSstephan 1) 1-to-1 bindings as exported from WASM, with no automatic 32c0a18d6aSstephan type conversions between JS and C. 333961b263Sstephan 34c0a18d6aSstephan 2) A binding of (1) which provides certain JS/C type conversions 35c0a18d6aSstephan to greatly simplify its use. 36c0a18d6aSstephan 37c0a18d6aSstephan 3) A higher-level API, more akin to sql.js and node.js-style 383961b263Sstephan implementations. This one speaks directly to the low-level 393961b263Sstephan API. This API must be used from the same thread as the 403961b263Sstephan low-level API. 413961b263Sstephan 42c0a18d6aSstephan 4) A second higher-level API which speaks to the previous APIs via 433961b263Sstephan worker messages. This one is intended for use in the main 443961b263Sstephan thread, with the lower-level APIs installed in a Worker thread, 453961b263Sstephan and talking to them via Worker messages. Because Workers are 463961b263Sstephan asynchronouns and have only a single message channel, some 473961b263Sstephan acrobatics are needed here to feed async work results back to 483961b263Sstephan the client (as we cannot simply pass around callbacks between 493961b263Sstephan the main and Worker threads). 503961b263Sstephan 513961b263Sstephan - Insofar as possible, support client-side storage using JS 523961b263Sstephan filesystem APIs. As of this writing, such things are still very 535b915007Sstephan much under development. 543961b263Sstephan 553961b263Sstephan Specific non-goals of this project: 563961b263Sstephan 573961b263Sstephan - As WASM is a web-centric technology and UTF-8 is the King of 583961b263Sstephan Encodings in that realm, there are no currently plans to support 593961b263Sstephan the UTF16-related sqlite3 APIs. They would add a complication to 603961b263Sstephan the bindings for no appreciable benefit. Though web-related 615b915007Sstephan implementation details take priority, and the JavaScript 625b915007Sstephan components of the API specifically focus on browser clients, the 635b915007Sstephan lower-level WASM module "should" work in non-web WASM 645b915007Sstephan environments. 653961b263Sstephan 663961b263Sstephan - Supporting old or niche-market platforms. WASM is built for a 673961b263Sstephan modern web and requires modern platforms. 683961b263Sstephan 693961b263Sstephan - Though scalar User-Defined Functions (UDFs) may be created in 703961b263Sstephan JavaScript, there are currently no plans to add support for 713961b263Sstephan aggregate and window functions. 723961b263Sstephan 733961b263Sstephan Attribution: 743961b263Sstephan 753961b263Sstephan This project is endebted to the work of sql.js: 763961b263Sstephan 773961b263Sstephan https://github.com/sql-js/sql.js 783961b263Sstephan 793961b263Sstephan sql.js was an essential stepping stone in this code's development as 803961b263Sstephan it demonstrated how to handle some of the WASM-related voodoo (like 813961b263Sstephan handling pointers-to-pointers and adding JS implementations of 823961b263Sstephan C-bound callback functions). These APIs have a considerably 833961b263Sstephan different shape than sql.js's, however. 843961b263Sstephan*/ 853961b263Sstephan 863961b263Sstephan/** 875b915007Sstephan sqlite3ApiBootstrap() is the only global symbol persistently 885b915007Sstephan exposed by this API. It is intended to be called one time at the 895b915007Sstephan end of the API amalgamation process, passed configuration details 905b915007Sstephan for the current environment, and then optionally be removed from 915b915007Sstephan the global object using `delete self.sqlite3ApiBootstrap`. 923961b263Sstephan 93e3cd6760Sstephan This function expects a configuration object, intended to abstract 943961b263Sstephan away details specific to any given WASM environment, primarily so 95e3cd6760Sstephan that it can be used without any _direct_ dependency on 965b915007Sstephan Emscripten. (Note the default values for the config object!) The 975b915007Sstephan config object is only honored the first time this is 985b915007Sstephan called. Subsequent calls ignore the argument and return the same 99e3cd6760Sstephan (configured) object which gets initialized by the first call. 100c0a18d6aSstephan This function will throw if any of the required config options are 101c0a18d6aSstephan missing. 102e3cd6760Sstephan 103e3cd6760Sstephan The config object properties include: 104e3cd6760Sstephan 1059a34509aSstephan - `exports`[^1]: the "exports" object for the current WASM 106c0a18d6aSstephan environment. In an Emscripten-based build, this should be set to 107e3cd6760Sstephan `Module['asm']`. 108e3cd6760Sstephan 1099a34509aSstephan - `memory`[^1]: optional WebAssembly.Memory object, defaulting to 110e3cd6760Sstephan `exports.memory`. In Emscripten environments this should be set 111e3cd6760Sstephan to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be 112e3cd6760Sstephan left undefined/falsy to default to `exports.memory` when using 113e3cd6760Sstephan WASM-exported memory. 114e3cd6760Sstephan 115e3cd6760Sstephan - `bigIntEnabled`: true if BigInt support is enabled. Defaults to 116c0a18d6aSstephan true if `self.BigInt64Array` is available, else false. Some APIs 117e3cd6760Sstephan will throw exceptions if called without BigInt support, as BigInt 118e3cd6760Sstephan is required for marshalling C-side int64 into and out of JS. 119e3cd6760Sstephan 120e3cd6760Sstephan - `allocExportName`: the name of the function, in `exports`, of the 121e3cd6760Sstephan `malloc(3)`-compatible routine for the WASM environment. Defaults 122e3cd6760Sstephan to `"malloc"`. 123e3cd6760Sstephan 124e3cd6760Sstephan - `deallocExportName`: the name of the function, in `exports`, of 125e3cd6760Sstephan the `free(3)`-compatible routine for the WASM 126e3cd6760Sstephan environment. Defaults to `"free"`. 127e3cd6760Sstephan 128c0a18d6aSstephan - `wasmfsOpfsDir`[^1]: if the environment supports persistent 129c0a18d6aSstephan storage, this directory names the "mount point" for that 130c0a18d6aSstephan directory. It must be prefixed by `/` and may contain only a 131c0a18d6aSstephan single directory-name part. Using the root directory name is not 132c0a18d6aSstephan supported by any current persistent backend. This setting is 133c0a18d6aSstephan only used in WASMFS-enabled builds. 1343961b263Sstephan 1359a34509aSstephan 1369a34509aSstephan [^1] = This property may optionally be a function, in which case this 1379a34509aSstephan function re-assigns it to the value returned from that function, 1389a34509aSstephan enabling delayed evaluation. 1399a34509aSstephan 1409a34509aSstephan*/ 1419a34509aSstephan'use strict'; 1429a34509aSstephanself.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( 1435b915007Sstephan apiConfig = (self.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) 1449a34509aSstephan){ 145e3cd6760Sstephan if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ 146e3cd6760Sstephan console.warn("sqlite3ApiBootstrap() called multiple times.", 147e3cd6760Sstephan "Config and external initializers are ignored on calls after the first."); 148e3cd6760Sstephan return sqlite3ApiBootstrap.sqlite3; 149e3cd6760Sstephan } 15092ede964Sstephan const config = Object.assign(Object.create(null),{ 151e3cd6760Sstephan exports: undefined, 152e3cd6760Sstephan memory: undefined, 1536110a5d0Sstephan bigIntEnabled: (()=>{ 15460d9aa7cSstephan if('undefined'!==typeof Module){ 155eb97743cSstephan /* Emscripten module will contain HEAPU64 when built with 15660d9aa7cSstephan -sWASM_BIGINT=1, else it will not. */ 15760d9aa7cSstephan return !!Module.HEAPU64; 15860d9aa7cSstephan } 1596110a5d0Sstephan return !!self.BigInt64Array; 1606110a5d0Sstephan })(), 161e3cd6760Sstephan allocExportName: 'malloc', 162e3cd6760Sstephan deallocExportName: 'free', 1633d645484Sstephan wasmfsOpfsDir: '/opfs' 16492ede964Sstephan }, apiConfig || {}); 165e3cd6760Sstephan 1669a34509aSstephan [ 1679a34509aSstephan // If any of these config options are functions, replace them with 1689a34509aSstephan // the result of calling that function... 169eb97743cSstephan 'exports', 'memory', 'wasmfsOpfsDir' 1709a34509aSstephan ].forEach((k)=>{ 1719a34509aSstephan if('function' === typeof config[k]){ 1729a34509aSstephan config[k] = config[k](); 1739a34509aSstephan } 1749a34509aSstephan }); 1759a34509aSstephan 1761acfe915Sstephan /** 1771acfe915Sstephan The main sqlite3 binding API gets installed into this object, 1781acfe915Sstephan mimicking the C API as closely as we can. The numerous members 1791acfe915Sstephan names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as 1801acfe915Sstephan possible, identically to the C-native counterparts, as documented at: 1811acfe915Sstephan 1821acfe915Sstephan https://www.sqlite.org/c3ref/intro.html 1831acfe915Sstephan 1841acfe915Sstephan A very few exceptions require an additional level of proxy 1851acfe915Sstephan function or may otherwise require special attention in the WASM 1860f32760eSstephan environment, and all such cases are documented somewhere below 1870f32760eSstephan in this file or in sqlite3-api-glue.js. capi members which are 1880f32760eSstephan not documented are installed as 1-to-1 proxies for their 1891acfe915Sstephan C-side counterparts. 1901acfe915Sstephan */ 1911acfe915Sstephan const capi = Object.create(null); 1920f32760eSstephan /** 1930f32760eSstephan Holds state which are specific to the WASM-related 1940f32760eSstephan infrastructure and glue code. It is not expected that client 1950f32760eSstephan code will normally need these, but they're exposed here in case 1960f32760eSstephan it does. These APIs are _not_ to be considered an 1970f32760eSstephan official/stable part of the sqlite3 WASM API. They may change 1980f32760eSstephan as the developers' experience suggests appropriate changes. 1990f32760eSstephan 2000f32760eSstephan Note that a number of members of this object are injected 2010f32760eSstephan dynamically after the api object is fully constructed, so 2020f32760eSstephan not all are documented in this file. 2030f32760eSstephan */ 2040f32760eSstephan const wasm = Object.create(null); 2051acfe915Sstephan 206f45c3370Sstephan /** Internal helper for SQLite3Error ctor. */ 207f45c3370Sstephan const __rcStr = (rc)=>{ 208f45c3370Sstephan return (capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(rc)) 209f45c3370Sstephan || ("Unknown result code #"+rc); 210f45c3370Sstephan }; 211f45c3370Sstephan 212f45c3370Sstephan /** Internal helper for SQLite3Error ctor. */ 213f45c3370Sstephan const __isInt = (n)=>'number'===typeof n && n===(n | 0); 214f45c3370Sstephan 215f45c3370Sstephan /** 216f45c3370Sstephan An Error subclass specifically for reporting DB-level errors and 217f45c3370Sstephan enabling clients to unambiguously identify such exceptions. 218f45c3370Sstephan The C-level APIs never throw, but some of the higher-level 219f45c3370Sstephan C-style APIs do and the object-oriented APIs use exceptions 220f45c3370Sstephan exclusively to report errors. 221f45c3370Sstephan */ 222f45c3370Sstephan class SQLite3Error extends Error { 223f45c3370Sstephan /** 224f45c3370Sstephan Constructs this object with a message depending on its arguments: 225f45c3370Sstephan 226f45c3370Sstephan - If it's passed only a single integer argument, it is assumed 227f45c3370Sstephan to be an sqlite3 C API result code. The message becomes the 228f45c3370Sstephan result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns 229f45c3370Sstephan falsy) a synthesized string which contains that integer. 230f45c3370Sstephan 231f45c3370Sstephan - If passed 2 arguments and the 2nd is a object, it bevaves 232f45c3370Sstephan like the Error(string,object) constructor except that the first 233f45c3370Sstephan argument is subject to the is-integer semantics from the 234f45c3370Sstephan previous point. 235f45c3370Sstephan 236f45c3370Sstephan - Else all arguments are concatenated with a space between each 237f45c3370Sstephan one, using args.join(' '), to create the error message. 238f45c3370Sstephan */ 239f45c3370Sstephan constructor(...args){ 240f45c3370Sstephan if(1===args.length && __isInt(args[0])){ 241f45c3370Sstephan super(__rcStr(args[0])); 242f45c3370Sstephan }else if(2===args.length && 'object'===typeof args){ 243f45c3370Sstephan if(__isInt(args[0])) super(__rcStr(args[0]), args[1]); 244f45c3370Sstephan else super(...args); 245f45c3370Sstephan }else{ 246f45c3370Sstephan super(args.join(' ')); 247f45c3370Sstephan } 248f45c3370Sstephan this.name = 'SQLite3Error'; 249f45c3370Sstephan } 250f45c3370Sstephan }; 251f45c3370Sstephan 2521acfe915Sstephan /** 2531acfe915Sstephan Functionally equivalent to the SQLite3Error constructor but may 2541acfe915Sstephan be used as part of an expression, e.g.: 2551acfe915Sstephan 2561acfe915Sstephan ``` 2571acfe915Sstephan return someFunction(x) || SQLite3Error.toss(...); 2581acfe915Sstephan ``` 2591acfe915Sstephan */ 2601acfe915Sstephan SQLite3Error.toss = (...args)=>{ 2611acfe915Sstephan throw new SQLite3Error(...args); 2621acfe915Sstephan }; 2631acfe915Sstephan const toss3 = SQLite3Error.toss; 2643961b263Sstephan 2653d645484Sstephan if(config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)){ 2661acfe915Sstephan toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'."); 267e3cd6760Sstephan } 268e3cd6760Sstephan 2693961b263Sstephan /** 2703961b263Sstephan Returns true if n is a 32-bit (signed) integer, else 2713961b263Sstephan false. This is used for determining when we need to switch to 2723961b263Sstephan double-type DB operations for integer values in order to keep 2733961b263Sstephan more precision. 2743961b263Sstephan */ 275510a9d1cSstephan const isInt32 = (n)=>{ 2763961b263Sstephan return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) 2773961b263Sstephan && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); 2783961b263Sstephan }; 279510a9d1cSstephan /** 280510a9d1cSstephan Returns true if the given BigInt value is small enough to fit 281510a9d1cSstephan into an int64 value, else false. 282510a9d1cSstephan */ 283510a9d1cSstephan const bigIntFits64 = function f(b){ 284510a9d1cSstephan if(!f._max){ 285510a9d1cSstephan f._max = BigInt("0x7fffffffffffffff"); 286510a9d1cSstephan f._min = ~f._max; 287510a9d1cSstephan } 288510a9d1cSstephan return b >= f._min && b <= f._max; 289510a9d1cSstephan }; 290510a9d1cSstephan 291510a9d1cSstephan /** 292510a9d1cSstephan Returns true if the given BigInt value is small enough to fit 293510a9d1cSstephan into an int32, else false. 294510a9d1cSstephan */ 295510a9d1cSstephan const bigIntFits32 = (b)=>(b >= (-0x7fffffffn - 1n) && b <= 0x7fffffffn); 296510a9d1cSstephan 297510a9d1cSstephan /** 298510a9d1cSstephan Returns true if the given BigInt value is small enough to fit 299510a9d1cSstephan into a double value without loss of precision, else false. 300510a9d1cSstephan */ 301510a9d1cSstephan const bigIntFitsDouble = function f(b){ 302510a9d1cSstephan if(!f._min){ 303510a9d1cSstephan f._min = Number.MIN_SAFE_INTEGER; 304510a9d1cSstephan f._max = Number.MAX_SAFE_INTEGER; 305510a9d1cSstephan } 306510a9d1cSstephan return b >= f._min && b <= f._max; 307510a9d1cSstephan }; 3083961b263Sstephan 3093961b263Sstephan /** Returns v if v appears to be a TypedArray, else false. */ 3103961b263Sstephan const isTypedArray = (v)=>{ 3113961b263Sstephan return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; 3123961b263Sstephan }; 3133961b263Sstephan 314de868175Sstephan 315de868175Sstephan /** Internal helper to use in operations which need to distinguish 316de868175Sstephan between TypedArrays which are backed by a SharedArrayBuffer 317de868175Sstephan from those which are not. */ 318de868175Sstephan const __SAB = ('undefined'===typeof SharedArrayBuffer) 319de868175Sstephan ? function(){} : SharedArrayBuffer; 320de868175Sstephan /** Returns true if the given TypedArray object is backed by a 321de868175Sstephan SharedArrayBuffer, else false. */ 322de868175Sstephan const isSharedTypedArray = (aTypedArray)=>(aTypedArray.buffer instanceof __SAB); 323de868175Sstephan 324de868175Sstephan /** 325de868175Sstephan Returns either aTypedArray.slice(begin,end) (if 326de868175Sstephan aTypedArray.buffer is a SharedArrayBuffer) or 327de868175Sstephan aTypedArray.subarray(begin,end) (if it's not). 328de868175Sstephan 329de868175Sstephan This distinction is important for APIs which don't like to 330de868175Sstephan work on SABs, e.g. TextDecoder, and possibly for our 331de868175Sstephan own APIs which work on memory ranges which "might" be 3324f975c33Sstephan modified by other threads while they're working. 333de868175Sstephan */ 334de868175Sstephan const typedArrayPart = (aTypedArray, begin, end)=>{ 335de868175Sstephan return isSharedTypedArray(aTypedArray) 336de868175Sstephan ? aTypedArray.slice(begin, end) 337de868175Sstephan : aTypedArray.subarray(begin, end); 338de868175Sstephan }; 339de868175Sstephan 3403961b263Sstephan /** 3413961b263Sstephan Returns true if v appears to be one of our bind()-able 3423961b263Sstephan TypedArray types: Uint8Array or Int8Array. Support for 3433961b263Sstephan TypedArrays with element sizes >1 is TODO. 3443961b263Sstephan */ 3453961b263Sstephan const isBindableTypedArray = (v)=>{ 3463961b263Sstephan return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 3473961b263Sstephan }; 3483961b263Sstephan 3493961b263Sstephan /** 3503961b263Sstephan Returns true if v appears to be one of the TypedArray types 3513961b263Sstephan which is legal for holding SQL code (as opposed to binary blobs). 3523961b263Sstephan 3533961b263Sstephan Currently this is the same as isBindableTypedArray() but it 3543961b263Sstephan seems likely that we'll eventually want to add Uint32Array 3553961b263Sstephan and friends to the isBindableTypedArray() list but not to the 3563961b263Sstephan isSQLableTypedArray() list. 3573961b263Sstephan */ 3583961b263Sstephan const isSQLableTypedArray = (v)=>{ 3593961b263Sstephan return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 3603961b263Sstephan }; 3613961b263Sstephan 3623961b263Sstephan /** Returns true if isBindableTypedArray(v) does, else throws with a message 3633961b263Sstephan that v is not a supported TypedArray value. */ 3643961b263Sstephan const affirmBindableTypedArray = (v)=>{ 3653961b263Sstephan return isBindableTypedArray(v) 3661acfe915Sstephan || toss3("Value is not of a supported TypedArray type."); 3673961b263Sstephan }; 3683961b263Sstephan 3693961b263Sstephan const utf8Decoder = new TextDecoder('utf-8'); 370e3cd6760Sstephan 371de868175Sstephan /** 372de868175Sstephan Uses TextDecoder to decode the given half-open range of the 373de868175Sstephan given TypedArray to a string. This differs from a simple 374de868175Sstephan call to TextDecoder in that it accounts for whether the 3754f975c33Sstephan first argument is backed by a SharedArrayBuffer or not, 376de868175Sstephan and can work more efficiently if it's not (TextDecoder 377de868175Sstephan refuses to act upon an SAB). 378de868175Sstephan */ 379de868175Sstephan const typedArrayToString = function(typedArray, begin, end){ 380de868175Sstephan return utf8Decoder.decode(typedArrayPart(typedArray, begin,end)); 381e3cd6760Sstephan }; 3823961b263Sstephan 3833961b263Sstephan /** 3844df2ab57Sstephan If v is-a Array, its join("") result is returned. If 385ef9cd12eSstephan isSQLableTypedArray(v) is true then typedArrayToString(v) is 3860f32760eSstephan returned. If it looks like a WASM pointer, wasm.cstringToJs(v) is 387e67a0f40Sstephan returned. Else v is returned as-is. 388e67a0f40Sstephan */ 389ef9cd12eSstephan const flexibleString = function(v){ 390e67a0f40Sstephan if(isSQLableTypedArray(v)) return typedArrayToString(v); 3914df2ab57Sstephan else if(Array.isArray(v)) return v.join(""); 3920f32760eSstephan else if(wasm.isPtr(v)) v = wasm.cstringToJs(v); 393e67a0f40Sstephan return v; 394e67a0f40Sstephan }; 395e67a0f40Sstephan 396e67a0f40Sstephan /** 3973961b263Sstephan An Error subclass specifically for reporting Wasm-level malloc() 3983961b263Sstephan failure and enabling clients to unambiguously identify such 3993961b263Sstephan exceptions. 4003961b263Sstephan */ 4013961b263Sstephan class WasmAllocError extends Error { 402*690d4c54Sstephan /** 403*690d4c54Sstephan If called with 2 arguments and the 2nd one is an object, it 404*690d4c54Sstephan behaves like the Error constructor, else it concatenates all 405*690d4c54Sstephan arguments together with a single space between each to 406*690d4c54Sstephan construct an error message string. As a special case, if 407*690d4c54Sstephan called with no arguments then it uses a default error 408*690d4c54Sstephan message. 409*690d4c54Sstephan */ 4103961b263Sstephan constructor(...args){ 411*690d4c54Sstephan if(2===args.length && 'object'===typeof args){ 4123961b263Sstephan super(...args); 413*690d4c54Sstephan }else if(args.length){ 414*690d4c54Sstephan super(args.join(' ')); 415*690d4c54Sstephan }else{ 416*690d4c54Sstephan super("Allocation failed."); 417*690d4c54Sstephan } 4183961b263Sstephan this.name = 'WasmAllocError'; 4193961b263Sstephan } 4203961b263Sstephan }; 42196b6371dSstephan /** 42296b6371dSstephan Functionally equivalent to the WasmAllocError constructor but may 42396b6371dSstephan be used as part of an expression, e.g.: 42496b6371dSstephan 42596b6371dSstephan ``` 42696b6371dSstephan return someAllocatingFunction(x) || WasmAllocError.toss(...); 42796b6371dSstephan ``` 42896b6371dSstephan */ 42963e9ec2fSstephan WasmAllocError.toss = (...args)=>{ 430a6ca996eSstephan throw new WasmAllocError(...args); 43163e9ec2fSstephan }; 4323961b263Sstephan 4331acfe915Sstephan Object.assign(capi, { 4343961b263Sstephan /** 4359892883eSstephan sqlite3_create_function_v2() differs from its native 4369892883eSstephan counterpart only in the following ways: 4373961b263Sstephan 4389892883eSstephan 1) The fourth argument (`eTextRep`) argument must not specify 4398d9e5955Sstephan any encoding other than sqlite3.SQLITE_UTF8. The JS API does not 4409892883eSstephan currently support any other encoding and likely never 4419892883eSstephan will. This function does not replace that argument on its own 4429892883eSstephan because it may contain other flags. 4433961b263Sstephan 4449892883eSstephan 2) Any of the four final arguments may be either WASM pointers 4459892883eSstephan (assumed to be function pointers) or JS Functions. In the 4469892883eSstephan latter case, each gets bound to WASM using 4479892883eSstephan sqlite3.capi.wasm.installFunction() and that wrapper is passed 4489892883eSstephan on to the native implementation. 4493961b263Sstephan 4509892883eSstephan The semantics of JS functions are: 4513961b263Sstephan 452510a9d1cSstephan xFunc: is passed `(pCtx, ...values)`. Its return value becomes 4539892883eSstephan the new SQL function's result. 4549892883eSstephan 455510a9d1cSstephan xStep: is passed `(pCtx, ...values)`. Its return value is 4569892883eSstephan ignored. 4579892883eSstephan 458510a9d1cSstephan xFinal: is passed `(pCtx)`. Its return value becomes the new 459510a9d1cSstephan aggregate SQL function's result. 4609892883eSstephan 4619892883eSstephan xDestroy: is passed `(void*)`. Its return value is ignored. The 4629892883eSstephan pointer passed to it is the one from the 5th argument to 4639892883eSstephan sqlite3_create_function_v2(). 4649892883eSstephan 465510a9d1cSstephan Note that: 466510a9d1cSstephan 4678d9e5955Sstephan - `pCtx` in the above descriptions is a `sqlite3_context*`. At 4688d9e5955Sstephan least 99 times out of a hundred, that initial argument will 4698d9e5955Sstephan be irrelevant for JS UDF bindings, but it needs to be there 4708d9e5955Sstephan so that the cases where it _is_ relevant, in particular with 4718d9e5955Sstephan window and aggregate functions, have full access to the 4728d9e5955Sstephan lower-level sqlite3 APIs. 473510a9d1cSstephan 4748d9e5955Sstephan - When wrapping JS functions, the remaining arguments are passd 4758d9e5955Sstephan to them as positional arguments, not as an array of 4768d9e5955Sstephan arguments, because that allows callback definitions to be 4778d9e5955Sstephan more JS-idiomatic than C-like. For example `(pCtx,a,b)=>a+b` 4788d9e5955Sstephan is more intuitive and legible than 4798d9e5955Sstephan `(pCtx,args)=>args[0]+args[1]`. For cases where an array of 4808d9e5955Sstephan arguments would be more convenient, the callbacks simply need 4818d9e5955Sstephan to be declared like `(pCtx,...args)=>{...}`, in which case 4828d9e5955Sstephan `args` will be an array. 483510a9d1cSstephan 484510a9d1cSstephan - If a JS wrapper throws, it gets translated to 485510a9d1cSstephan sqlite3_result_error() or sqlite3_result_error_nomem(), 486510a9d1cSstephan depending on whether the exception is an 487510a9d1cSstephan sqlite3.WasmAllocError object or not. 488510a9d1cSstephan 489510a9d1cSstephan - When passing on WASM function pointers, arguments are _not_ 490510a9d1cSstephan converted or reformulated. They are passed on as-is in raw 491510a9d1cSstephan pointer form using their native C signatures. Only JS 492510a9d1cSstephan functions passed in to this routine, and thus wrapped by this 493510a9d1cSstephan routine, get automatic conversions of arguments and result 494510a9d1cSstephan values. The routines which perform those conversions are 495510a9d1cSstephan exposed for client-side use as 496510a9d1cSstephan sqlite3_create_function_v2.convertUdfArgs() and 497510a9d1cSstephan sqlite3_create_function_v2.setUdfResult(). sqlite3_create_function() 498510a9d1cSstephan and sqlite3_create_window_function() have those same methods. 4999892883eSstephan 5009892883eSstephan For xFunc(), xStep(), and xFinal(): 5019892883eSstephan 5029892883eSstephan - When called from SQL, arguments to the UDF, and its result, 5039892883eSstephan will be converted between JS and SQL with as much fidelity as 5049892883eSstephan is feasible, triggering an exception if a type conversion 5059892883eSstephan cannot be determined. Some freedom is afforded to numeric 5069892883eSstephan conversions due to friction between the JS and C worlds: 5078d9e5955Sstephan integers which are larger than 32 bits may be treated as 5088d9e5955Sstephan doubles or BigInts. 5099892883eSstephan 510510a9d1cSstephan If any JS-side bound functions throw, those exceptions are 511510a9d1cSstephan intercepted and converted to database-side errors with the 512510a9d1cSstephan exception of xDestroy(): any exception from it is ignored, 513510a9d1cSstephan possibly generating a console.error() message. Destructors 514510a9d1cSstephan must not throw. 5159892883eSstephan 5169892883eSstephan Once installed, there is currently no way to uninstall the 517510a9d1cSstephan automatically-converted WASM-bound JS functions from WASM. They 518510a9d1cSstephan can be uninstalled from the database as documented in the C 519510a9d1cSstephan API, but this wrapper currently has no infrastructure in place 520510a9d1cSstephan to also free the WASM-bound JS wrappers, effectively resulting 521510a9d1cSstephan in a memory leak if the client uninstalls the UDF. Improving that 522510a9d1cSstephan is a potential TODO, but removing client-installed UDFs is rare 523510a9d1cSstephan in practice. If this factor is relevant for a given client, 524510a9d1cSstephan they can create WASM-bound JS functions themselves, hold on to their 525510a9d1cSstephan pointers, and pass the pointers in to here. Later on, they can 526510a9d1cSstephan free those pointers (using `wasm.uninstallFunction()` or 527510a9d1cSstephan equivalent). 528510a9d1cSstephan 529510a9d1cSstephan C reference: https://www.sqlite.org/c3ref/create_function.html 5309892883eSstephan 5319892883eSstephan Maintenance reminder: the ability to add new 5329892883eSstephan WASM-accessible functions to the runtime requires that the 5339892883eSstephan WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH` 5349892883eSstephan flag. 5353961b263Sstephan */ 5369892883eSstephan sqlite3_create_function_v2: function( 5379892883eSstephan pDb, funcName, nArg, eTextRep, pApp, 538510a9d1cSstephan xFunc, xStep, xFinal, xDestroy 5399892883eSstephan ){/*installed later*/}, 5409892883eSstephan /** 5419892883eSstephan Equivalent to passing the same arguments to 5429892883eSstephan sqlite3_create_function_v2(), with 0 as the final argument. 5439892883eSstephan */ 5449892883eSstephan sqlite3_create_function:function( 5459892883eSstephan pDb, funcName, nArg, eTextRep, pApp, 546510a9d1cSstephan xFunc, xStep, xFinal 5479892883eSstephan ){/*installed later*/}, 5483961b263Sstephan /** 54992ede964Sstephan The sqlite3_create_window_function() JS wrapper differs from 55092ede964Sstephan its native implementation in the exact same way that 55192ede964Sstephan sqlite3_create_function_v2() does. The additional function, 55292ede964Sstephan xInverse(), is treated identically to xStep() by the wrapping 55392ede964Sstephan layer. 55492ede964Sstephan */ 55592ede964Sstephan sqlite3_create_window_function: function( 55692ede964Sstephan pDb, funcName, nArg, eTextRep, pApp, 557510a9d1cSstephan xStep, xFinal, xValue, xInverse, xDestroy 55892ede964Sstephan ){/*installed later*/}, 55992ede964Sstephan /** 5603961b263Sstephan The sqlite3_prepare_v3() binding handles two different uses 5613961b263Sstephan with differing JS/WASM semantics: 5623961b263Sstephan 563d9801185Sstephan 1) sqlite3_prepare_v3(pDb, sqlString, -1, prepFlags, ppStmt , null) 5643961b263Sstephan 5653961b263Sstephan 2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer) 5663961b263Sstephan 5673961b263Sstephan Note that the SQL length argument (the 3rd argument) must, for 5683961b263Sstephan usage (1), always be negative because it must be a byte length 5693961b263Sstephan and that value is expensive to calculate from JS (where only 5703961b263Sstephan the character length of strings is readily available). It is 5713961b263Sstephan retained in this API's interface for code/documentation 5723961b263Sstephan compatibility reasons but is currently _always_ ignored. With 5733961b263Sstephan usage (2), the 3rd argument is used as-is but is is still 5743961b263Sstephan critical that the C-style input string (2nd argument) be 5753961b263Sstephan terminated with a 0 byte. 5763961b263Sstephan 5773961b263Sstephan In usage (1), the 2nd argument must be of type string, 5783961b263Sstephan Uint8Array, or Int8Array (either of which is assumed to 5793961b263Sstephan hold SQL). If it is, this function assumes case (1) and 5803961b263Sstephan calls the underyling C function with the equivalent of: 5813961b263Sstephan 5823961b263Sstephan (pDb, sqlAsString, -1, prepFlags, ppStmt, null) 5833961b263Sstephan 584d9801185Sstephan The `pzTail` argument is ignored in this case because its 585d9801185Sstephan result is meaningless when a string-type value is passed 586d9801185Sstephan through: the string goes through another level of internal 5873961b263Sstephan conversion for WASM's sake and the result pointer would refer 5883961b263Sstephan to that transient conversion's memory, not the passed-in 589d9801185Sstephan string. 5903961b263Sstephan 5913961b263Sstephan If the sql argument is not a string, it must be a _pointer_ to 5923961b263Sstephan a NUL-terminated string which was allocated in the WASM memory 593d9801185Sstephan (e.g. using capi.wasm.alloc() or equivalent). In that case, 5943961b263Sstephan the final argument may be 0/null/undefined or must be a pointer 5953961b263Sstephan to which the "tail" of the compiled SQL is written, as 5963961b263Sstephan documented for the C-side sqlite3_prepare_v3(). In case (2), 5973961b263Sstephan the underlying C function is called with the equivalent of: 5983961b263Sstephan 599d9801185Sstephan (pDb, sqlAsPointer, sqlByteLen, prepFlags, ppStmt, pzTail) 6003961b263Sstephan 6013961b263Sstephan It returns its result and compiled statement as documented in 6023961b263Sstephan the C API. Fetching the output pointers (5th and 6th 603d9801185Sstephan parameters) requires using `capi.wasm.getMemValue()` (or 604d9801185Sstephan equivalent) and the `pzTail` will point to an address relative to 605d9801185Sstephan the `sqlAsPointer` value. 6063961b263Sstephan 6073961b263Sstephan If passed an invalid 2nd argument type, this function will 608d9801185Sstephan return SQLITE_MISUSE and sqlite3_errmsg() will contain a string 609d9801185Sstephan describing the problem. 6103961b263Sstephan 611d9801185Sstephan Side-note: if given an empty string, or one which contains only 612d9801185Sstephan comments or an empty SQL expression, 0 is returned but the result 613d9801185Sstephan output pointer will be NULL. 6143961b263Sstephan */ 615e67a0f40Sstephan sqlite3_prepare_v3: (dbPtr, sql, sqlByteLen, prepFlags, 616e67a0f40Sstephan stmtPtrPtr, strPtrPtr)=>{}/*installed later*/, 6173961b263Sstephan 6183961b263Sstephan /** 6193961b263Sstephan Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument. 6203961b263Sstephan */ 621e67a0f40Sstephan sqlite3_prepare_v2: (dbPtr, sql, sqlByteLen, 622e67a0f40Sstephan stmtPtrPtr,strPtrPtr)=>{}/*installed later*/, 6233961b263Sstephan 6243961b263Sstephan /** 625e67a0f40Sstephan This binding enables the callback argument to be a JavaScript. 626e67a0f40Sstephan 627e67a0f40Sstephan If the callback is a function, then for the duration of the 628e67a0f40Sstephan sqlite3_exec() call, it installs a WASM-bound function which 629de868175Sstephan acts as a proxy for the given callback. That proxy will also 630de868175Sstephan perform a conversion of the callback's arguments from 631e67a0f40Sstephan `(char**)` to JS arrays of strings. However, for API 632de868175Sstephan consistency's sake it will still honor the C-level callback 633de868175Sstephan parameter order and will call it like: 634e67a0f40Sstephan 635e67a0f40Sstephan `callback(pVoid, colCount, listOfValues, listOfColNames)` 636e67a0f40Sstephan 637e67a0f40Sstephan If the callback is not a JS function then this binding performs 638e67a0f40Sstephan no translation of the callback, but the sql argument is still 639e67a0f40Sstephan converted to a WASM string for the call using the 640e67a0f40Sstephan "flexible-string" argument converter. 641e67a0f40Sstephan */ 642e67a0f40Sstephan sqlite3_exec: (pDb, sql, callback, pVoid, pErrMsg)=>{}/*installed later*/, 64396b6371dSstephan 644e67a0f40Sstephan /** 645de868175Sstephan If passed a single argument which appears to be a byte-oriented 646de868175Sstephan TypedArray (Int8Array or Uint8Array), this function treats that 647de868175Sstephan TypedArray as an output target, fetches `theArray.byteLength` 648de868175Sstephan bytes of randomness, and populates the whole array with it. As 649de868175Sstephan a special case, if the array's length is 0, this function 650de868175Sstephan behaves as if it were passed (0,0). When called this way, it 651de868175Sstephan returns its argument, else it returns the `undefined` value. 652de868175Sstephan 653de868175Sstephan If called with any other arguments, they are passed on as-is 654de868175Sstephan to the C API. Results are undefined if passed any incompatible 655de868175Sstephan values. 656de868175Sstephan */ 657de868175Sstephan sqlite3_randomness: (n, outPtr)=>{/*installed later*/}, 6581acfe915Sstephan }/*capi*/); 659de868175Sstephan 660de868175Sstephan /** 6613961b263Sstephan Various internal-use utilities are added here as needed. They 6623961b263Sstephan are bound to an object only so that we have access to them in 6633961b263Sstephan the differently-scoped steps of the API bootstrapping 6643961b263Sstephan process. At the end of the API setup process, this object gets 665de868175Sstephan removed. These are NOT part of the public API. 6663961b263Sstephan */ 6678948fbeeSstephan const util = { 668ef9cd12eSstephan affirmBindableTypedArray, flexibleString, 669510a9d1cSstephan bigIntFits32, bigIntFits64, bigIntFitsDouble, 670510a9d1cSstephan isBindableTypedArray, 671e67a0f40Sstephan isInt32, isSQLableTypedArray, isTypedArray, 672e67a0f40Sstephan typedArrayToString, 673de868175Sstephan isUIThread: ()=>'undefined'===typeof WorkerGlobalScope, 674de868175Sstephan isSharedTypedArray, 675de868175Sstephan typedArrayPart 6768948fbeeSstephan }; 6773961b263Sstephan 6780f32760eSstephan Object.assign(wasm, { 6793961b263Sstephan /** 6803961b263Sstephan Emscripten APIs have a deep-seated assumption that all pointers 6813961b263Sstephan are 32 bits. We'll remain optimistic that that won't always be 6823961b263Sstephan the case and will use this constant in places where we might 6833961b263Sstephan otherwise use a hard-coded 4. 6843961b263Sstephan */ 6853961b263Sstephan ptrSizeof: config.wasmPtrSizeof || 4, 6863961b263Sstephan /** 6873961b263Sstephan The WASM IR (Intermediate Representation) value for 6883961b263Sstephan pointer-type values. It MUST refer to a value type of the 6893961b263Sstephan size described by this.ptrSizeof _or_ it may be any value 6903961b263Sstephan which ends in '*', which Emscripten's glue code internally 6913961b263Sstephan translates to i32. 6923961b263Sstephan */ 6933961b263Sstephan ptrIR: config.wasmPtrIR || "i32", 6943961b263Sstephan /** 6953961b263Sstephan True if BigInt support was enabled via (e.g.) the 6963961b263Sstephan Emscripten -sWASM_BIGINT flag, else false. When 6973961b263Sstephan enabled, certain 64-bit sqlite3 APIs are enabled which 6983961b263Sstephan are not otherwise enabled due to JS/WASM int64 6993961b263Sstephan impedence mismatches. 7003961b263Sstephan */ 7013961b263Sstephan bigIntEnabled: !!config.bigIntEnabled, 7023961b263Sstephan /** 7033961b263Sstephan The symbols exported by the WASM environment. 7043961b263Sstephan */ 7053961b263Sstephan exports: config.exports 7061acfe915Sstephan || toss3("Missing API config.exports (WASM module exports)."), 7073961b263Sstephan 7083961b263Sstephan /** 7093961b263Sstephan When Emscripten compiles with `-sIMPORT_MEMORY`, it 7103961b263Sstephan initalizes the heap and imports it into wasm, as opposed to 7113961b263Sstephan the other way around. In this case, the memory is not 7123961b263Sstephan available via this.exports.memory. 7133961b263Sstephan */ 7143961b263Sstephan memory: config.memory || config.exports['memory'] 7151acfe915Sstephan || toss3("API config object requires a WebAssembly.Memory object", 7163961b263Sstephan "in either config.exports.memory (exported)", 7173961b263Sstephan "or config.memory (imported)."), 718453af2f6Sstephan 719453af2f6Sstephan /** 720453af2f6Sstephan The API's one single point of access to the WASM-side memory 721453af2f6Sstephan allocator. Works like malloc(3) (and is likely bound to 722453af2f6Sstephan malloc()) but throws an WasmAllocError if allocation fails. It is 723453af2f6Sstephan important that any code which might pass through the sqlite3 C 724453af2f6Sstephan API NOT throw and must instead return SQLITE_NOMEM (or 725453af2f6Sstephan equivalent, depending on the context). 726453af2f6Sstephan 727*690d4c54Sstephan Very few cases in the sqlite3 JS APIs can result in 728453af2f6Sstephan client-defined functions propagating exceptions via the C-style 729*690d4c54Sstephan API. Most notably, this applies to WASM-bound JS functions 730*690d4c54Sstephan which are created directly by clients and passed on _as WASM 731*690d4c54Sstephan function pointers_ to functions such as 732*690d4c54Sstephan sqlite3_create_function_v2(). Such bindings created 733*690d4c54Sstephan transparently by this API will automatically use wrappers which 734*690d4c54Sstephan catch exceptions and convert them to appropriate error codes. 735*690d4c54Sstephan 736*690d4c54Sstephan For cases where non-throwing allocation is required, use 737*690d4c54Sstephan sqlite3.wasm.alloc.impl(), which is direct binding of the 738*690d4c54Sstephan underlying C-level allocator. 739*690d4c54Sstephan 740*690d4c54Sstephan Design note: this function is not named "malloc" primarily 741*690d4c54Sstephan because Emscripten uses that name and we wanted to avoid any 742*690d4c54Sstephan confusion early on in this code's development, when it still 743*690d4c54Sstephan had close ties to Emscripten's glue code. 744453af2f6Sstephan */ 745453af2f6Sstephan alloc: undefined/*installed later*/, 746*690d4c54Sstephan 747453af2f6Sstephan /** 748453af2f6Sstephan The API's one single point of access to the WASM-side memory 749453af2f6Sstephan deallocator. Works like free(3) (and is likely bound to 750453af2f6Sstephan free()). 751*690d4c54Sstephan 752*690d4c54Sstephan Design note: this function is not named "free" for the same 753*690d4c54Sstephan reason that this.alloc() is not called this.malloc(). 754453af2f6Sstephan */ 755453af2f6Sstephan dealloc: undefined/*installed later*/ 756453af2f6Sstephan 7573961b263Sstephan /* Many more wasm-related APIs get installed later on. */ 7580f32760eSstephan }/*wasm*/); 7592b776ee2Sstephan 7603961b263Sstephan /** 7612b776ee2Sstephan wasm.alloc()'s srcTypedArray.byteLength bytes, 7623961b263Sstephan populates them with the values from the source 7633961b263Sstephan TypedArray, and returns the pointer to that memory. The 7643961b263Sstephan returned pointer must eventually be passed to 7652b776ee2Sstephan wasm.dealloc() to clean it up. 7663961b263Sstephan 7673961b263Sstephan As a special case, to avoid further special cases where 7683961b263Sstephan this is used, if srcTypedArray.byteLength is 0, it 7693961b263Sstephan allocates a single byte and sets it to the value 7703961b263Sstephan 0. Even in such cases, calls must behave as if the 7713961b263Sstephan allocated memory has exactly srcTypedArray.byteLength 7723961b263Sstephan bytes. 7733961b263Sstephan 7743961b263Sstephan ACHTUNG: this currently only works for Uint8Array and 7753961b263Sstephan Int8Array types and will throw if srcTypedArray is of 7763961b263Sstephan any other type. 7773961b263Sstephan */ 7782b776ee2Sstephan wasm.allocFromTypedArray = function(srcTypedArray){ 7793961b263Sstephan affirmBindableTypedArray(srcTypedArray); 7802b776ee2Sstephan const pRet = wasm.alloc(srcTypedArray.byteLength || 1); 781*690d4c54Sstephan wasm.heapForSize(srcTypedArray.constructor).set( 782*690d4c54Sstephan srcTypedArray.byteLength ? srcTypedArray : [0], pRet 783*690d4c54Sstephan ); 7843961b263Sstephan return pRet; 78592ede964Sstephan }; 7863961b263Sstephan 7873961b263Sstephan const keyAlloc = config.allocExportName || 'malloc', 7883961b263Sstephan keyDealloc = config.deallocExportName || 'free'; 7893961b263Sstephan for(const key of [keyAlloc, keyDealloc]){ 7902b776ee2Sstephan const f = wasm.exports[key]; 7911acfe915Sstephan if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function."); 7923961b263Sstephan } 793453af2f6Sstephan 794*690d4c54Sstephan wasm.alloc = function f(n){ 795*690d4c54Sstephan const m = f.impl(n); 796*690d4c54Sstephan if(!m) throw new WasmAllocError("Failed to allocate",n," bytes."); 7973961b263Sstephan return m; 79892ede964Sstephan }; 799*690d4c54Sstephan wasm.alloc.impl = wasm.exports[keyAlloc]; 800*690d4c54Sstephan wasm.dealloc = wasm.exports[keyDealloc]; 8013961b263Sstephan 8023961b263Sstephan /** 8033961b263Sstephan Reports info about compile-time options using 8043961b263Sstephan sqlite_compileoption_get() and sqlite3_compileoption_used(). It 8053961b263Sstephan has several distinct uses: 8063961b263Sstephan 8073961b263Sstephan If optName is an array then it is expected to be a list of 8083961b263Sstephan compilation options and this function returns an object 8093961b263Sstephan which maps each such option to true or false, indicating 8103961b263Sstephan whether or not the given option was included in this 8113961b263Sstephan build. That object is returned. 8123961b263Sstephan 8133961b263Sstephan If optName is an object, its keys are expected to be compilation 8143961b263Sstephan options and this function sets each entry to true or false, 8153961b263Sstephan indicating whether the compilation option was used or not. That 8163961b263Sstephan object is returned. 8173961b263Sstephan 8183961b263Sstephan If passed no arguments then it returns an object mapping 8193961b263Sstephan all known compilation options to their compile-time values, 8203961b263Sstephan or boolean true if they are defined with no value. This 8213961b263Sstephan result, which is relatively expensive to compute, is cached 8223961b263Sstephan and returned for future no-argument calls. 8233961b263Sstephan 8243961b263Sstephan In all other cases it returns true if the given option was 8253961b263Sstephan active when when compiling the sqlite3 module, else false. 8263961b263Sstephan 8273961b263Sstephan Compile-time option names may optionally include their 8283961b263Sstephan "SQLITE_" prefix. When it returns an object of all options, 8293961b263Sstephan the prefix is elided. 8303961b263Sstephan */ 8312b776ee2Sstephan wasm.compileOptionUsed = function f(optName){ 8323961b263Sstephan if(!arguments.length){ 8333961b263Sstephan if(f._result) return f._result; 8343961b263Sstephan else if(!f._opt){ 8353961b263Sstephan f._rx = /^([^=]+)=(.+)/; 8363961b263Sstephan f._rxInt = /^-?\d+$/; 8373961b263Sstephan f._opt = function(opt, rv){ 8383961b263Sstephan const m = f._rx.exec(opt); 8393961b263Sstephan rv[0] = (m ? m[1] : opt); 8403961b263Sstephan rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; 8413961b263Sstephan }; 8423961b263Sstephan } 8433961b263Sstephan const rc = {}, ov = [0,0]; 8443961b263Sstephan let i = 0, k; 8453961b263Sstephan while((k = capi.sqlite3_compileoption_get(i++))){ 8463961b263Sstephan f._opt(k,ov); 8473961b263Sstephan rc[ov[0]] = ov[1]; 8483961b263Sstephan } 8493961b263Sstephan return f._result = rc; 8503961b263Sstephan }else if(Array.isArray(optName)){ 8513961b263Sstephan const rc = {}; 8523961b263Sstephan optName.forEach((v)=>{ 8533961b263Sstephan rc[v] = capi.sqlite3_compileoption_used(v); 8543961b263Sstephan }); 8553961b263Sstephan return rc; 8563961b263Sstephan }else if('object' === typeof optName){ 8573961b263Sstephan Object.keys(optName).forEach((k)=> { 8583961b263Sstephan optName[k] = capi.sqlite3_compileoption_used(k); 8593961b263Sstephan }); 8603961b263Sstephan return optName; 8613961b263Sstephan } 8623961b263Sstephan return ( 8633961b263Sstephan 'string'===typeof optName 8643961b263Sstephan ) ? !!capi.sqlite3_compileoption_used(optName) : false; 8653961b263Sstephan }/*compileOptionUsed()*/; 8663961b263Sstephan 8673961b263Sstephan /** 8683961b263Sstephan Signatures for the WASM-exported C-side functions. Each entry 8693961b263Sstephan is an array with 2+ elements: 8703961b263Sstephan 8713961b263Sstephan [ "c-side name", 8722b776ee2Sstephan "result type" (wasm.xWrap() syntax), 8733961b263Sstephan [arg types in xWrap() syntax] 8743961b263Sstephan // ^^^ this needn't strictly be an array: it can be subsequent 8753961b263Sstephan // elements instead: [x,y,z] is equivalent to x,y,z 8763961b263Sstephan ] 8775b915007Sstephan 8785b915007Sstephan Note that support for the API-specific data types in the 8795b915007Sstephan result/argument type strings gets plugged in at a later phase in 8805b915007Sstephan the API initialization process. 8813961b263Sstephan */ 8822b776ee2Sstephan wasm.bindingSignatures = [ 8833961b263Sstephan // Please keep these sorted by function name! 884510a9d1cSstephan ["sqlite3_aggregate_context","void*", "sqlite3_context*", "int"], 8856167d5cfSstephan ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*" 886de868175Sstephan /* TODO: we should arguably write a custom wrapper which knows 887de868175Sstephan how to handle Blob, TypedArrays, and JS strings. */ 8886167d5cfSstephan ], 8893961b263Sstephan ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], 8903961b263Sstephan ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], 8913961b263Sstephan ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], 8923961b263Sstephan ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"], 8933961b263Sstephan ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], 8946167d5cfSstephan ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int" 895de868175Sstephan /* We should arguably create a hand-written binding of 896de868175Sstephan bind_text() which does more flexible text conversion, along 897de868175Sstephan the lines of sqlite3_prepare_v3(). The slightly problematic 898de868175Sstephan part is the final argument (text destructor). */ 8996167d5cfSstephan ], 9003961b263Sstephan ["sqlite3_close_v2", "int", "sqlite3*"], 9013961b263Sstephan ["sqlite3_changes", "int", "sqlite3*"], 9023961b263Sstephan ["sqlite3_clear_bindings","int", "sqlite3_stmt*"], 9033961b263Sstephan ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"], 9043961b263Sstephan ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"], 9053961b263Sstephan ["sqlite3_column_count", "int", "sqlite3_stmt*"], 9063961b263Sstephan ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"], 9073961b263Sstephan ["sqlite3_column_int","int", "sqlite3_stmt*", "int"], 9083961b263Sstephan ["sqlite3_column_name","string", "sqlite3_stmt*", "int"], 9093961b263Sstephan ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], 9103961b263Sstephan ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], 9113961b263Sstephan ["sqlite3_compileoption_get", "string", "int"], 9123961b263Sstephan ["sqlite3_compileoption_used", "int", "string"], 913de868175Sstephan /* sqlite3_create_function(), sqlite3_create_function_v2(), and 914de868175Sstephan sqlite3_create_window_function() use hand-written bindings to 915de868175Sstephan simplify handling of their function-type arguments. */ 9163961b263Sstephan ["sqlite3_data_count", "int", "sqlite3_stmt*"], 9173961b263Sstephan ["sqlite3_db_filename", "string", "sqlite3*", "string"], 9185b915007Sstephan ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], 9193961b263Sstephan ["sqlite3_db_name", "string", "sqlite3*", "int"], 92053d4e01dSstephan ["sqlite3_deserialize", "int", "sqlite3*", "string", "*", "i64", "i64", "int"] 92153d4e01dSstephan /* Careful! Short version: de/serialize() are problematic because they 922de868175Sstephan might use a different allocator than the user for managing the 92353d4e01dSstephan deserialized block. de/serialize() are ONLY safe to use with 92453d4e01dSstephan sqlite3_malloc(), sqlite3_free(), and its 64-bit variants. */, 9253961b263Sstephan ["sqlite3_errmsg", "string", "sqlite3*"], 9263961b263Sstephan ["sqlite3_error_offset", "int", "sqlite3*"], 9273961b263Sstephan ["sqlite3_errstr", "string", "int"], 928e67a0f40Sstephan /*["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**" 929e67a0f40Sstephan Handled seperately to perform translation of the callback 930e67a0f40Sstephan into a WASM-usable one. ],*/ 9313961b263Sstephan ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], 9323961b263Sstephan ["sqlite3_extended_errcode", "int", "sqlite3*"], 9333961b263Sstephan ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], 9345b915007Sstephan ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], 9353961b263Sstephan ["sqlite3_finalize", "int", "sqlite3_stmt*"], 93653d4e01dSstephan ["sqlite3_free", undefined,"*"], 9373961b263Sstephan ["sqlite3_initialize", undefined], 938ffbc653dSstephan /*["sqlite3_interrupt", undefined, "sqlite3*" 939ffbc653dSstephan ^^^ we cannot actually currently support this because JS is 9403961b263Sstephan single-threaded and we don't have a portable way to access a DB 941ffbc653dSstephan from 2 SharedWorkers concurrently. ],*/ 9423961b263Sstephan ["sqlite3_libversion", "string"], 9433961b263Sstephan ["sqlite3_libversion_number", "int"], 94453d4e01dSstephan ["sqlite3_malloc", "*","int"], 9453961b263Sstephan ["sqlite3_open", "int", "string", "*"], 9463961b263Sstephan ["sqlite3_open_v2", "int", "string", "*", "int", "string"], 9473961b263Sstephan /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled 9483961b263Sstephan separately due to us requiring two different sets of semantics 9493961b263Sstephan for those, depending on how their SQL argument is provided. */ 950de868175Sstephan /* sqlite3_randomness() uses a hand-written wrapper to extend 951de868175Sstephan the range of supported argument types. */ 95253d4e01dSstephan ["sqlite3_realloc", "*","*","int"], 9533961b263Sstephan ["sqlite3_reset", "int", "sqlite3_stmt*"], 9543961b263Sstephan ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], 9553961b263Sstephan ["sqlite3_result_double",undefined, "*", "f64"], 9563961b263Sstephan ["sqlite3_result_error",undefined, "*", "string", "int"], 9573961b263Sstephan ["sqlite3_result_error_code", undefined, "*", "int"], 9583961b263Sstephan ["sqlite3_result_error_nomem", undefined, "*"], 9593961b263Sstephan ["sqlite3_result_error_toobig", undefined, "*"], 9603961b263Sstephan ["sqlite3_result_int",undefined, "*", "int"], 9613961b263Sstephan ["sqlite3_result_null",undefined, "*"], 9623961b263Sstephan ["sqlite3_result_text",undefined, "*", "string", "int", "*"], 96353d4e01dSstephan ["sqlite3_serialize","*", "sqlite3*", "string", "*", "int"], 964ef11fb91Sstephan ["sqlite3_shutdown", undefined], 9653961b263Sstephan ["sqlite3_sourceid", "string"], 9663961b263Sstephan ["sqlite3_sql", "string", "sqlite3_stmt*"], 9673961b263Sstephan ["sqlite3_step", "int", "sqlite3_stmt*"], 9683961b263Sstephan ["sqlite3_strglob", "int", "string","string"], 9693961b263Sstephan ["sqlite3_strlike", "int", "string","string","int"], 9704f5bbedbSstephan ["sqlite3_trace_v2", "int", "sqlite3*", "int", "*", "*"], 9713961b263Sstephan ["sqlite3_total_changes", "int", "sqlite3*"], 97289071030Sstephan ["sqlite3_uri_boolean", "int", "string", "string", "int"], 97389071030Sstephan ["sqlite3_uri_key", "string", "string", "int"], 97489071030Sstephan ["sqlite3_uri_parameter", "string", "string", "string"], 975510a9d1cSstephan ["sqlite3_user_data","void*", "sqlite3_context*"], 976510a9d1cSstephan ["sqlite3_value_blob", "*", "sqlite3_value*"], 977510a9d1cSstephan ["sqlite3_value_bytes","int", "sqlite3_value*"], 978510a9d1cSstephan ["sqlite3_value_double","f64", "sqlite3_value*"], 979510a9d1cSstephan ["sqlite3_value_int","int", "sqlite3_value*"], 980510a9d1cSstephan ["sqlite3_value_text", "string", "sqlite3_value*"], 981510a9d1cSstephan ["sqlite3_value_type", "int", "sqlite3_value*"], 9823961b263Sstephan ["sqlite3_vfs_find", "*", "string"], 983a6ca996eSstephan ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"], 984a6ca996eSstephan ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"] 9852b776ee2Sstephan ]/*wasm.bindingSignatures*/; 9863961b263Sstephan 9872b776ee2Sstephan if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ 9883961b263Sstephan /* ^^^ "the problem" is that this is an option feature and the 9893961b263Sstephan build-time function-export list does not currently take 9903961b263Sstephan optional features into account. */ 9912b776ee2Sstephan wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); 9923961b263Sstephan } 9933961b263Sstephan 9943961b263Sstephan /** 9953961b263Sstephan Functions which require BigInt (int64) support are separated from 9963961b263Sstephan the others because we need to conditionally bind them or apply 9973961b263Sstephan dummy impls, depending on the capabilities of the environment. 9983961b263Sstephan */ 9992b776ee2Sstephan wasm.bindingSignatures.int64 = [ 10003961b263Sstephan ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], 10013961b263Sstephan ["sqlite3_changes64","i64", ["sqlite3*"]], 10023961b263Sstephan ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], 100353d4e01dSstephan ["sqlite3_malloc64", "*","i64"], 1004dc6ae602Sstephan ["sqlite3_msize", "i64", "*"], 100553d4e01dSstephan ["sqlite3_realloc64", "*","*", "i64"], 1006510a9d1cSstephan ["sqlite3_result_int64",undefined, "*", "i64"], 100789071030Sstephan ["sqlite3_total_changes64", "i64", ["sqlite3*"]], 1008510a9d1cSstephan ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]], 1009510a9d1cSstephan ["sqlite3_value_int64","i64", "sqlite3_value*"], 10103961b263Sstephan ]; 10113961b263Sstephan 101273079dbaSstephan /** 101373079dbaSstephan Functions which are intended solely for API-internal use by the 101473079dbaSstephan WASM components, not client code. These get installed into 10158948fbeeSstephan sqlite3.wasm. 101673079dbaSstephan */ 10172b776ee2Sstephan wasm.bindingSignatures.wasm = [ 1018842c5ee8Sstephan ["sqlite3_wasm_db_reset", "int", "sqlite3*"], 1019842c5ee8Sstephan ["sqlite3_wasm_db_vfs", "sqlite3_vfs*", "sqlite3*","string"], 1020f45c3370Sstephan ["sqlite3_wasm_vfs_create_file", "int", 1021f45c3370Sstephan "sqlite3_vfs*","string","*", "int"], 1022842c5ee8Sstephan ["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"] 102373079dbaSstephan ]; 102473079dbaSstephan 10253afad4d4Sstephan 10263afad4d4Sstephan /** 10272b776ee2Sstephan sqlite3.wasm.pstack (pseudo-stack) holds a special-case 10283afad4d4Sstephan stack-style allocator intended only for use with _small_ data of 10293afad4d4Sstephan not more than (in total) a few kb in size, managed as if it were 10303afad4d4Sstephan stack-based. 10313afad4d4Sstephan 10323afad4d4Sstephan It has only a single intended usage: 10333afad4d4Sstephan 10343afad4d4Sstephan ``` 10353afad4d4Sstephan const stackPos = pstack.pointer; 10363afad4d4Sstephan try{ 10373afad4d4Sstephan const ptr = pstack.alloc(8); 10383afad4d4Sstephan // ==> pstack.pointer === ptr 10393afad4d4Sstephan const otherPtr = pstack.alloc(8); 10403afad4d4Sstephan // ==> pstack.pointer === otherPtr 10413afad4d4Sstephan ... 10423afad4d4Sstephan }finally{ 10433afad4d4Sstephan pstack.restore(stackPos); 10443afad4d4Sstephan // ==> pstack.pointer === stackPos 10453afad4d4Sstephan } 10463afad4d4Sstephan ``` 10473afad4d4Sstephan 10483afad4d4Sstephan This allocator is much faster than a general-purpose one but is 10493afad4d4Sstephan limited to usage patterns like the one shown above. 10503afad4d4Sstephan 10513afad4d4Sstephan It operates from a static range of memory which lives outside of 10523afad4d4Sstephan space managed by Emscripten's stack-management, so does not 10533afad4d4Sstephan collide with Emscripten-provided stack allocation APIs. The 10543afad4d4Sstephan memory lives in the WASM heap and can be used with routines such 10553afad4d4Sstephan as wasm.setMemValue() and any wasm.heap8u().slice(). 10563afad4d4Sstephan */ 10572b776ee2Sstephan wasm.pstack = Object.assign(Object.create(null),{ 10583afad4d4Sstephan /** 1059d89a66ecSstephan Sets the current pstack position to the given pointer. Results 1060d89a66ecSstephan are undefined if the passed-in value did not come from 10613afad4d4Sstephan this.pointer. 10623afad4d4Sstephan */ 10632b776ee2Sstephan restore: wasm.exports.sqlite3_wasm_pstack_restore, 10643afad4d4Sstephan /** 10653afad4d4Sstephan Attempts to allocate the given number of bytes from the 10663afad4d4Sstephan pstack. On success, it zeroes out a block of memory of the 10673afad4d4Sstephan given size, adjusts the pstack pointer, and returns a pointer 106863e9ec2fSstephan to the memory. On error, returns throws a WasmAllocError. The 106963e9ec2fSstephan memory must eventually be released using restore(). 10703afad4d4Sstephan 10713afad4d4Sstephan This method always adjusts the given value to be a multiple 1072193ee11fSstephan of 8 bytes because failing to do so can lead to incorrect 1073193ee11fSstephan results when reading and writing 64-bit values from/to the WASM 1074842c5ee8Sstephan heap. Similarly, the returned address is always 8-byte aligned. 10753afad4d4Sstephan */ 107663e9ec2fSstephan alloc: (n)=>{ 10772b776ee2Sstephan return wasm.exports.sqlite3_wasm_pstack_alloc(n) 107863e9ec2fSstephan || WasmAllocError.toss("Could not allocate",n, 107963e9ec2fSstephan "bytes from the pstack."); 10802b776ee2Sstephan }, 10813afad4d4Sstephan /** 1082d89a66ecSstephan alloc()'s n chunks, each sz bytes, as a single memory block and 10832b776ee2Sstephan returns the addresses as an array of n element, each holding 10842b776ee2Sstephan the address of one chunk. 10852b776ee2Sstephan 10862b776ee2Sstephan Throws a WasmAllocError if allocation fails. 10872b776ee2Sstephan 10882b776ee2Sstephan Example: 10892b776ee2Sstephan 10902b776ee2Sstephan ``` 10912b776ee2Sstephan const [p1, p2, p3] = wasm.pstack.allocChunks(3,4); 10922b776ee2Sstephan ``` 10933afad4d4Sstephan */ 10942b776ee2Sstephan allocChunks: (n,sz)=>{ 10952b776ee2Sstephan const mem = wasm.pstack.alloc(n * sz); 1096d85d0839Sstephan const rc = []; 10972b776ee2Sstephan let i = 0, offset = 0; 10982b776ee2Sstephan for(; i < n; offset = (sz * ++i)){ 10992b776ee2Sstephan rc.push(mem + offset); 11002b776ee2Sstephan } 11012b776ee2Sstephan return rc; 11022b776ee2Sstephan }, 11032b776ee2Sstephan /** 1104d92c652aSstephan A convenience wrapper for allocChunks() which sizes each chunk 11052b776ee2Sstephan as either 8 bytes (safePtrSize is truthy) or wasm.ptrSizeof (if 11062b776ee2Sstephan safePtrSize is falsy). 11072b776ee2Sstephan 11082b776ee2Sstephan How it returns its result differs depending on its first 11092b776ee2Sstephan argument: if it's 1, it returns a single pointer value. If it's 11102b776ee2Sstephan more than 1, it returns the same as allocChunks(). 11112b776ee2Sstephan 11122b776ee2Sstephan When a returned pointers will refer to a 64-bit value, e.g. a 11132b776ee2Sstephan double or int64, and that value must be written or fetched, 11142b776ee2Sstephan e.g. using wasm.setMemValue() or wasm.getMemValue(), it is 11152b776ee2Sstephan important that the pointer in question be aligned to an 8-byte 11162b776ee2Sstephan boundary or else it will not be fetched or written properly and 11172b776ee2Sstephan will corrupt or read neighboring memory. 11182b776ee2Sstephan 11192b776ee2Sstephan However, when all pointers involved point to "small" data, it 1120d89a66ecSstephan is safe to pass a falsy value to save a tiny bit of memory. 11212b776ee2Sstephan */ 11222b776ee2Sstephan allocPtr: (n=1,safePtrSize=true)=>{ 11232b776ee2Sstephan return 1===n 11242b776ee2Sstephan ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) 11252b776ee2Sstephan : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); 11262b776ee2Sstephan } 11272b776ee2Sstephan })/*wasm.pstack*/; 11282b776ee2Sstephan Object.defineProperties(wasm.pstack, { 11292b776ee2Sstephan /** 11302b776ee2Sstephan sqlite3.wasm.pstack.pointer resolves to the current pstack 11312716cd25Sstephan position pointer. This value is intended _only_ to be saved 11322716cd25Sstephan for passing to restore(). Writing to this memory, without 11332716cd25Sstephan first reserving it via wasm.pstack.alloc() and friends, leads 11342716cd25Sstephan to undefined results. 11352b776ee2Sstephan */ 11362b776ee2Sstephan pointer: { 11373afad4d4Sstephan configurable: false, iterable: true, writeable: false, 11382b776ee2Sstephan get: wasm.exports.sqlite3_wasm_pstack_ptr 11393afad4d4Sstephan //Whether or not a setter as an alternative to restore() is 11403afad4d4Sstephan //clearer or would just lead to confusion is unclear. 11412b776ee2Sstephan //set: wasm.exports.sqlite3_wasm_pstack_restore 11422b776ee2Sstephan }, 11433afad4d4Sstephan /** 1144f03ddccaSstephan sqlite3.wasm.pstack.quota to the total number of bytes 1145f03ddccaSstephan available in the pstack, including any space which is currently 1146f03ddccaSstephan allocated. This value is a compile-time constant. 11472b776ee2Sstephan */ 11482b776ee2Sstephan quota: { 11492b776ee2Sstephan configurable: false, iterable: true, writeable: false, 11502b776ee2Sstephan get: wasm.exports.sqlite3_wasm_pstack_quota 1151f03ddccaSstephan }, 11522b776ee2Sstephan /** 1153f03ddccaSstephan sqlite3.wasm.pstack.remaining resolves to the amount of space 1154f03ddccaSstephan remaining in the pstack. 11553afad4d4Sstephan */ 1156f03ddccaSstephan remaining: { 11573afad4d4Sstephan configurable: false, iterable: true, writeable: false, 11582b776ee2Sstephan get: wasm.exports.sqlite3_wasm_pstack_remaining 1159f03ddccaSstephan } 1160f03ddccaSstephan })/*wasm.pstack properties*/; 11613afad4d4Sstephan 1162de868175Sstephan capi.sqlite3_randomness = (...args)=>{ 1163de868175Sstephan if(1===args.length && util.isTypedArray(args[0]) 1164de868175Sstephan && 1===args[0].BYTES_PER_ELEMENT){ 1165de868175Sstephan const ta = args[0]; 1166de868175Sstephan if(0===ta.byteLength){ 1167de868175Sstephan wasm.exports.sqlite3_randomness(0,0); 1168de868175Sstephan return ta; 1169de868175Sstephan } 1170de868175Sstephan const stack = wasm.pstack.pointer; 1171de868175Sstephan try { 1172de868175Sstephan let n = ta.byteLength, offset = 0; 1173de868175Sstephan const r = wasm.exports.sqlite3_randomness; 1174de868175Sstephan const heap = wasm.heap8u(); 1175de868175Sstephan const nAlloc = n < 512 ? n : 512; 1176de868175Sstephan const ptr = wasm.pstack.alloc(nAlloc); 1177de868175Sstephan do{ 1178de868175Sstephan const j = (n>nAlloc ? nAlloc : n); 1179de868175Sstephan r(j, ptr); 1180de868175Sstephan ta.set(typedArrayPart(heap, ptr, ptr+j), offset); 1181de868175Sstephan n -= j; 1182de868175Sstephan offset += j; 1183de868175Sstephan } while(n > 0); 1184de868175Sstephan }catch(e){ 1185de868175Sstephan console.error("Highly unexpected (and ignored!) "+ 1186de868175Sstephan "exception in sqlite3_randomness():",e); 1187de868175Sstephan }finally{ 1188de868175Sstephan wasm.pstack.restore(stack); 1189de868175Sstephan } 1190de868175Sstephan return ta; 1191de868175Sstephan } 11928948fbeeSstephan wasm.exports.sqlite3_randomness(...args); 1193de868175Sstephan }; 1194de868175Sstephan 11955b9973d8Sstephan /** State for sqlite3_wasmfs_opfs_dir(). */ 1196de868175Sstephan let __wasmfsOpfsDir = undefined; 11979a4c63b0Sstephan /** 11983afad4d4Sstephan If the wasm environment has a WASMFS/OPFS-backed persistent 11993afad4d4Sstephan storage directory, its path is returned by this function. If it 12003afad4d4Sstephan does not then it returns "" (noting that "" is a falsy value). 12019a4c63b0Sstephan 1202453af2f6Sstephan The first time this is called, this function inspects the current 12033afad4d4Sstephan environment to determine whether persistence support is available 12043afad4d4Sstephan and, if it is, enables it (if needed). 12059a4c63b0Sstephan 1206f3860120Sstephan This function currently only recognizes the WASMFS/OPFS storage 12073afad4d4Sstephan combination and its path refers to storage rooted in the 12083afad4d4Sstephan Emscripten-managed virtual filesystem. 12099a4c63b0Sstephan */ 12105b9973d8Sstephan capi.sqlite3_wasmfs_opfs_dir = function(){ 1211de868175Sstephan if(undefined !== __wasmfsOpfsDir) return __wasmfsOpfsDir; 12129a4c63b0Sstephan // If we have no OPFS, there is no persistent dir 12133d645484Sstephan const pdir = config.wasmfsOpfsDir; 1214e3cd6760Sstephan if(!pdir 1215e3cd6760Sstephan || !self.FileSystemHandle 1216e3cd6760Sstephan || !self.FileSystemDirectoryHandle 12179a4c63b0Sstephan || !self.FileSystemFileHandle){ 1218de868175Sstephan return __wasmfsOpfsDir = ""; 12199a4c63b0Sstephan } 12209a4c63b0Sstephan try{ 12212b776ee2Sstephan if(pdir && 0===wasm.xCallWrapped( 122228ef9bddSstephan 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir 1223e3cd6760Sstephan )){ 1224de868175Sstephan return __wasmfsOpfsDir = pdir; 12259a4c63b0Sstephan }else{ 1226de868175Sstephan return __wasmfsOpfsDir = ""; 12279a4c63b0Sstephan } 12289a4c63b0Sstephan }catch(e){ 122928ef9bddSstephan // sqlite3_wasm_init_wasmfs() is not available 1230de868175Sstephan return __wasmfsOpfsDir = ""; 12319a4c63b0Sstephan } 12325360f5fcSstephan }; 12339a4c63b0Sstephan 1234453af2f6Sstephan /** 1235b5ae85ecSstephan Experimental and subject to change or removal. 1236b5ae85ecSstephan 12375b9973d8Sstephan Returns true if sqlite3.capi.sqlite3_wasmfs_opfs_dir() is a 12385b915007Sstephan non-empty string and the given name starts with (that string + 12395b915007Sstephan '/'), else returns false. 1240453af2f6Sstephan */ 1241842c5ee8Sstephan capi.sqlite3_wasmfs_filename_is_persistent = function(name){ 12425b9973d8Sstephan const p = capi.sqlite3_wasmfs_opfs_dir(); 12435b915007Sstephan return (p && name) ? name.startsWith(p+'/') : false; 12445360f5fcSstephan }; 12455360f5fcSstephan 124660d9aa7cSstephan // This bit is highly arguable and is incompatible with the fiddle shell. 12472b776ee2Sstephan if(false && 0===wasm.exports.sqlite3_vfs_find(0)){ 12485360f5fcSstephan /* Assume that sqlite3_initialize() has not yet been called. 12495360f5fcSstephan This will be the case in an SQLITE_OS_KV build. */ 12502b776ee2Sstephan wasm.exports.sqlite3_initialize(); 12515360f5fcSstephan } 1252453af2f6Sstephan 1253f3860120Sstephan /** 125449048b14Sstephan Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name 125549048b14Sstephan (defaulting to "main"), returns a truthy value (see below) if 125649048b14Sstephan that db uses that VFS, else returns false. If pDb is falsy then 125749048b14Sstephan the 3rd argument is ignored and this function returns a truthy 125849048b14Sstephan value if the default VFS name matches that of the 2nd 125949048b14Sstephan argument. Results are undefined if pDb is truthy but refers to an 126049048b14Sstephan invalid pointer. The 3rd argument specifies the database name of 126149048b14Sstephan the given database connection to check, defaulting to the main 126249048b14Sstephan db. 1263f3860120Sstephan 126496b6371dSstephan The 2nd and 3rd arguments may either be a JS string or a WASM 126596b6371dSstephan C-string. If the 2nd argument is a NULL WASM pointer, the default 126696b6371dSstephan VFS is assumed. If the 3rd is a NULL WASM pointer, "main" is 126796b6371dSstephan assumed. 1268f3860120Sstephan 1269f3860120Sstephan The truthy value it returns is a pointer to the `sqlite3_vfs` 1270f3860120Sstephan object. 1271f3860120Sstephan 1272f3860120Sstephan To permit safe use of this function from APIs which may be called 1273f3860120Sstephan via the C stack (like SQL UDFs), this function does not throw: if 1274f3860120Sstephan bad arguments cause a conversion error when passing into 1275f3860120Sstephan wasm-space, false is returned. 1276f3860120Sstephan */ 127749048b14Sstephan capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName=0){ 1278f3860120Sstephan try{ 1279278d3fafSstephan const pK = capi.sqlite3_vfs_find(vfsName); 1280f3860120Sstephan if(!pK) return false; 1281f3860120Sstephan else if(!pDb){ 128296b6371dSstephan return pK===capi.sqlite3_vfs_find(0) ? pK : false; 128396b6371dSstephan }else{ 128449048b14Sstephan return pK===capi.sqlite3_js_db_vfs(pDb,dbName) ? pK : false; 1285f3860120Sstephan } 1286f3860120Sstephan }catch(e){ 1287f3860120Sstephan /* Ignore - probably bad args to a wasm-bound function. */ 1288f3860120Sstephan return false; 1289f3860120Sstephan } 1290f3860120Sstephan }; 1291f3860120Sstephan 12920e0687ccSstephan /** 12930e0687ccSstephan Returns an array of the names of all currently-registered sqlite3 12940e0687ccSstephan VFSes. 12950e0687ccSstephan */ 12968a8244b5Sstephan capi.sqlite3_js_vfs_list = function(){ 12970e0687ccSstephan const rc = []; 12980e0687ccSstephan let pVfs = capi.sqlite3_vfs_find(0); 12990e0687ccSstephan while(pVfs){ 13000e0687ccSstephan const oVfs = new capi.sqlite3_vfs(pVfs); 13012b776ee2Sstephan rc.push(wasm.cstringToJs(oVfs.$zName)); 13020e0687ccSstephan pVfs = oVfs.$pNext; 13030e0687ccSstephan oVfs.dispose(); 13040e0687ccSstephan } 13050e0687ccSstephan return rc; 13060e0687ccSstephan }; 13070e0687ccSstephan 130832781427Sstephan /** 130932781427Sstephan Serializes the given `sqlite3*` pointer to a Uint8Array, as per 131032781427Sstephan sqlite3_serialize(). On success it returns a Uint8Array. On 131132781427Sstephan error it throws with a description of the problem. 131232781427Sstephan */ 13138a8244b5Sstephan capi.sqlite3_js_db_export = function(pDb){ 13141acfe915Sstephan if(!pDb) toss3('Invalid sqlite3* argument.'); 13151acfe915Sstephan if(!wasm.bigIntEnabled) toss3('BigInt64 support is not enabled.'); 1316f064a3bbSstephan const stack = wasm.pstack.pointer; 131732781427Sstephan let pOut; 131832781427Sstephan try{ 13193afad4d4Sstephan const pSize = wasm.pstack.alloc(8/*i64*/ + wasm.ptrSizeof); 132032781427Sstephan const ppOut = pSize + 8; 132132781427Sstephan /** 132232781427Sstephan Maintenance reminder, since this cost a full hour of grief 132332781427Sstephan and confusion: if the order of pSize/ppOut are reversed in 132432781427Sstephan that memory block, fetching the value of pSize after the 132532781427Sstephan export reads a garbage size because it's not on an 8-byte 132632781427Sstephan memory boundary! 132732781427Sstephan */ 132832781427Sstephan let rc = wasm.exports.sqlite3_wasm_db_serialize( 132932781427Sstephan pDb, ppOut, pSize, 0 133032781427Sstephan ); 133132781427Sstephan if(rc){ 13321acfe915Sstephan toss3("Database serialization failed with code", 13338a8244b5Sstephan sqlite3.capi.sqlite3_js_rc_str(rc)); 133432781427Sstephan } 13353afad4d4Sstephan pOut = wasm.getPtrValue(ppOut); 133632781427Sstephan const nOut = wasm.getMemValue(pSize, 'i64'); 133732781427Sstephan rc = nOut 133832781427Sstephan ? wasm.heap8u().slice(pOut, pOut + Number(nOut)) 133932781427Sstephan : new Uint8Array(); 134032781427Sstephan return rc; 134132781427Sstephan }finally{ 134232781427Sstephan if(pOut) wasm.exports.sqlite3_free(pOut); 13433afad4d4Sstephan wasm.pstack.restore(stack); 134432781427Sstephan } 134532781427Sstephan }; 134632781427Sstephan 134796b6371dSstephan /** 134896b6371dSstephan Given a `sqlite3*` and a database name (JS string or WASM 134996b6371dSstephan C-string pointer, which may be 0), returns a pointer to the 135096b6371dSstephan sqlite3_vfs responsible for it. If the given db name is null/0, 135196b6371dSstephan or not provided, then "main" is assumed. 135296b6371dSstephan */ 135396b6371dSstephan capi.sqlite3_js_db_vfs = 135496b6371dSstephan (dbPointer, dbName=0)=>wasm.sqlite3_wasm_db_vfs(dbPointer, dbName); 135596b6371dSstephan 135696b6371dSstephan /** 135796b6371dSstephan A thin wrapper around capi.sqlite3_aggregate_context() which 135896b6371dSstephan behaves the same except that it throws a WasmAllocError if that 13596f3286caSstephan function returns 0. As a special case, if n is falsy it does 13606f3286caSstephan _not_ throw if that function returns 0. That special case is 13616f3286caSstephan intended for use with xFinal() implementations. 136296b6371dSstephan */ 136396b6371dSstephan capi.sqlite3_js_aggregate_context = (pCtx, n)=>{ 136496b6371dSstephan return capi.sqlite3_aggregate_context(pCtx, n) 13656f3286caSstephan || (n ? WasmAllocError.toss("Cannot allocate",n, 13666f3286caSstephan "bytes for sqlite3_aggregate_context()") 13676f3286caSstephan : 0); 136896b6371dSstephan }; 136996b6371dSstephan 13708948fbeeSstephan if( util.isUIThread() ){ 13715b915007Sstephan /* Features specific to the main window thread... */ 13725b915007Sstephan 13735b915007Sstephan /** 13748a8244b5Sstephan Internal helper for sqlite3_js_kvvfs_clear() and friends. 13754df2ab57Sstephan Its argument should be one of ('local','session',""). 13765b915007Sstephan */ 13775b915007Sstephan const __kvvfsInfo = function(which){ 13785b915007Sstephan const rc = Object.create(null); 13795b915007Sstephan rc.prefix = 'kvvfs-'+which; 13805b915007Sstephan rc.stores = []; 13814df2ab57Sstephan if('session'===which || ""===which) rc.stores.push(self.sessionStorage); 13824df2ab57Sstephan if('local'===which || ""===which) rc.stores.push(self.localStorage); 13835b915007Sstephan return rc; 13845b915007Sstephan }; 13855b915007Sstephan 13865b915007Sstephan /** 13875b915007Sstephan Clears all storage used by the kvvfs DB backend, deleting any 13885b915007Sstephan DB(s) stored there. Its argument must be either 'session', 13894df2ab57Sstephan 'local', or "". In the first two cases, only sessionStorage 13905b915007Sstephan resp. localStorage is cleared. If it's an empty string (the 13915b915007Sstephan default) then both are cleared. Only storage keys which match 13925b915007Sstephan the pattern used by kvvfs are cleared: any other client-side 13935b915007Sstephan data are retained. 13945b915007Sstephan 13955b915007Sstephan This function is only available in the main window thread. 13965b915007Sstephan 13975b915007Sstephan Returns the number of entries cleared. 13985b915007Sstephan */ 13994df2ab57Sstephan capi.sqlite3_js_kvvfs_clear = function(which=""){ 14005b915007Sstephan let rc = 0; 14015b915007Sstephan const kvinfo = __kvvfsInfo(which); 14025b915007Sstephan kvinfo.stores.forEach((s)=>{ 14035b915007Sstephan const toRm = [] /* keys to remove */; 14045b915007Sstephan let i; 14055b915007Sstephan for( i = 0; i < s.length; ++i ){ 14065b915007Sstephan const k = s.key(i); 14075b915007Sstephan if(k.startsWith(kvinfo.prefix)) toRm.push(k); 14085b915007Sstephan } 14095b915007Sstephan toRm.forEach((kk)=>s.removeItem(kk)); 14105b915007Sstephan rc += toRm.length; 14115b915007Sstephan }); 14125b915007Sstephan return rc; 14135b915007Sstephan }; 14145b915007Sstephan 14155b915007Sstephan /** 14165b915007Sstephan This routine guesses the approximate amount of 14175b915007Sstephan window.localStorage and/or window.sessionStorage in use by the 14185b915007Sstephan kvvfs database backend. Its argument must be one of 14194df2ab57Sstephan ('session', 'local', ""). In the first two cases, only 14205b915007Sstephan sessionStorage resp. localStorage is counted. If it's an empty 14215b915007Sstephan string (the default) then both are counted. Only storage keys 14225b915007Sstephan which match the pattern used by kvvfs are counted. The returned 14235b915007Sstephan value is the "length" value of every matching key and value, 142481439a07Sstephan noting that JavaScript stores each character in 2 bytes. 14255b915007Sstephan 14265b915007Sstephan Note that the returned size is not authoritative from the 14275b915007Sstephan perspective of how much data can fit into localStorage and 14285b915007Sstephan sessionStorage, as the precise algorithms for determining 14295b915007Sstephan those limits are unspecified and may include per-entry 14305b915007Sstephan overhead invisible to clients. 14315b915007Sstephan */ 14324df2ab57Sstephan capi.sqlite3_js_kvvfs_size = function(which=""){ 14335b915007Sstephan let sz = 0; 14345b915007Sstephan const kvinfo = __kvvfsInfo(which); 14355b915007Sstephan kvinfo.stores.forEach((s)=>{ 14365b915007Sstephan let i; 14375b915007Sstephan for(i = 0; i < s.length; ++i){ 14385b915007Sstephan const k = s.key(i); 14395b915007Sstephan if(k.startsWith(kvinfo.prefix)){ 14405b915007Sstephan sz += k.length; 14415b915007Sstephan sz += s.getItem(k).length; 14425b915007Sstephan } 14435b915007Sstephan } 14445b915007Sstephan }); 144532781427Sstephan return sz * 2 /* because JS uses 2-byte char encoding */; 14465b915007Sstephan }; 14475b915007Sstephan 14485b915007Sstephan }/* main-window-only bits */ 14495b915007Sstephan 14505b9973d8Sstephan 14513961b263Sstephan /* The remainder of the API will be set up in later steps. */ 1452e3cd6760Sstephan const sqlite3 = { 1453453af2f6Sstephan WasmAllocError: WasmAllocError, 1454193ee11fSstephan SQLite3Error: SQLite3Error, 14553961b263Sstephan capi, 14568948fbeeSstephan util, 14578948fbeeSstephan wasm, 14585b9973d8Sstephan config, 14595b9973d8Sstephan /** 14608ffc9899Sstephan Holds the version info of the sqlite3 source tree from which 14618ffc9899Sstephan the generated sqlite3-api.js gets built. Note that its version 14628ffc9899Sstephan may well differ from that reported by sqlite3_libversion(), but 14638ffc9899Sstephan that should be considered a source file mismatch, as the JS and 14648ffc9899Sstephan WASM files are intended to be built and distributed together. 14658ffc9899Sstephan 14668ffc9899Sstephan This object is initially a placeholder which gets replaced by a 14678ffc9899Sstephan build-generated object. 14688ffc9899Sstephan */ 14698ffc9899Sstephan version: Object.create(null), 14708ffc9899Sstephan /** 14715b9973d8Sstephan Performs any optional asynchronous library-level initialization 14725b9973d8Sstephan which might be required. This function returns a Promise which 1473ff891b4eSstephan resolves to the sqlite3 namespace object. Any error in the 1474ff891b4eSstephan async init will be fatal to the init as a whole, but init 1475ff891b4eSstephan routines are themselves welcome to install dummy catch() 1476ff891b4eSstephan handlers which are not fatal if their failure should be 1477ff891b4eSstephan considered non-fatal. If called more than once, the second and 14785b9973d8Sstephan subsequent calls are no-ops which return a pre-resolved 14795b9973d8Sstephan Promise. 14805b9973d8Sstephan 1481d18f1bbfSstephan Ideally this function is called as part of the Promise chain 1482d18f1bbfSstephan which handles the loading and bootstrapping of the API. If not 1483d18f1bbfSstephan then it must be called by client-level code, which must not use 1484d18f1bbfSstephan the library until the returned promise resolves. 14855b9973d8Sstephan 14865b9973d8Sstephan Bug: if called while a prior call is still resolving, the 2nd 14875b9973d8Sstephan call will resolve prematurely, before the 1st call has finished 1488d18f1bbfSstephan resolving. The current build setup precludes that possibility, 1489d18f1bbfSstephan so it's only a hypothetical problem if/when this function 1490d18f1bbfSstephan ever needs to be invoked by clients. 1491b94a9860Sstephan 1492b94a9860Sstephan In Emscripten-based builds, this function is called 1493b94a9860Sstephan automatically and deleted from this object. 14945b9973d8Sstephan */ 14955b9973d8Sstephan asyncPostInit: async function(){ 14965b9973d8Sstephan let lip = sqlite3ApiBootstrap.initializersAsync; 14975b9973d8Sstephan delete sqlite3ApiBootstrap.initializersAsync; 14985b9973d8Sstephan if(!lip || !lip.length) return Promise.resolve(sqlite3); 14995b9973d8Sstephan // Is it okay to resolve these in parallel or do we need them 15005b9973d8Sstephan // to resolve in order? We currently only have 1, so it 15015b9973d8Sstephan // makes no difference. 1502ff891b4eSstephan lip = lip.map((f)=>{ 1503ff891b4eSstephan const p = (f instanceof Promise) ? f : f(sqlite3); 1504ff891b4eSstephan return p.catch((e)=>{ 1505ff891b4eSstephan console.error("an async sqlite3 initializer failed:",e); 1506ff891b4eSstephan throw e; 1507ff891b4eSstephan }); 1508ff891b4eSstephan }); 15095b9973d8Sstephan //let p = lip.shift(); 15105b9973d8Sstephan //while(lip.length) p = p.then(lip.shift()); 15115b9973d8Sstephan //return p.then(()=>sqlite3); 15125b9973d8Sstephan return Promise.all(lip).then(()=>sqlite3); 1513cd0df83cSstephan }, 1514cd0df83cSstephan /** 1515cd0df83cSstephan scriptInfo ideally gets injected into this object by the 1516cd0df83cSstephan infrastructure which assembles the JS/WASM module. It contains 1517cd0df83cSstephan state which must be collected before sqlite3ApiBootstrap() can 1518cd0df83cSstephan be declared. It is not necessarily available to any 1519cd0df83cSstephan sqlite3ApiBootstrap.initializers but "should" be in place (if 1520cd0df83cSstephan it's added at all) by the time that 1521cd0df83cSstephan sqlite3ApiBootstrap.initializersAsync is processed. 1522cd0df83cSstephan 1523cd0df83cSstephan This state is not part of the public API, only intended for use 1524cd0df83cSstephan with the sqlite3 API bootstrapping and wasm-loading process. 1525cd0df83cSstephan */ 1526cd0df83cSstephan scriptInfo: undefined 15273961b263Sstephan }; 15289a55773bSstephan try{ 15299a55773bSstephan sqlite3ApiBootstrap.initializers.forEach((f)=>{ 15309a55773bSstephan f(sqlite3); 15319a55773bSstephan }); 15329a55773bSstephan }catch(e){ 15339a55773bSstephan /* If we don't report this here, it can get completely swallowed 15349a55773bSstephan up and disappear into the abyss of Promises and Workers. */ 15359a55773bSstephan console.error("sqlite3 bootstrap initializer threw:",e); 15369a55773bSstephan throw e; 15379a55773bSstephan } 1538e3cd6760Sstephan delete sqlite3ApiBootstrap.initializers; 1539e3cd6760Sstephan sqlite3ApiBootstrap.sqlite3 = sqlite3; 1540e3cd6760Sstephan return sqlite3; 15413961b263Sstephan}/*sqlite3ApiBootstrap()*/; 1542e3cd6760Sstephan/** 1543e3cd6760Sstephan self.sqlite3ApiBootstrap.initializers is an internal detail used by 1544e3cd6760Sstephan the various pieces of the sqlite3 API's amalgamation process. It 1545e3cd6760Sstephan must not be modified by client code except when plugging such code 1546e3cd6760Sstephan into the amalgamation process. 1547e3cd6760Sstephan 1548e3cd6760Sstephan Each component of the amalgamation is expected to append a function 1549e3cd6760Sstephan to this array. When sqlite3ApiBootstrap() is called for the first 1550e3cd6760Sstephan time, each such function will be called (in their appended order) 1551e3cd6760Sstephan and passed the sqlite3 namespace object, into which they can install 1552e3cd6760Sstephan their features (noting that most will also require that certain 1553e3cd6760Sstephan features alread have been installed). At the end of that process, 1554e3cd6760Sstephan this array is deleted. 15555b9973d8Sstephan 15565b9973d8Sstephan Note that the order of insertion into this array is significant for 15578948fbeeSstephan some pieces. e.g. sqlite3.capi and sqlite3.wasm cannot be fully 1558ff891b4eSstephan utilized until the whwasmutil.js part is plugged in via 1559ff891b4eSstephan sqlite3-api-glue.js. 1560e3cd6760Sstephan*/ 1561e3cd6760Sstephanself.sqlite3ApiBootstrap.initializers = []; 15629a34509aSstephan/** 15635b9973d8Sstephan self.sqlite3ApiBootstrap.initializersAsync is an internal detail 15645b9973d8Sstephan used by the sqlite3 API's amalgamation process. It must not be 15655b9973d8Sstephan modified by client code except when plugging such code into the 15665b9973d8Sstephan amalgamation process. 15675b9973d8Sstephan 1568ff891b4eSstephan The counterpart of self.sqlite3ApiBootstrap.initializers, 1569ff891b4eSstephan specifically for initializers which are asynchronous. All entries in 1570ff891b4eSstephan this list must be either async functions, non-async functions which 1571ff891b4eSstephan return a Promise, or a Promise. Each function in the list is called 1572ff891b4eSstephan with the sqlite3 ojbect as its only argument. 1573ff891b4eSstephan 1574ff891b4eSstephan The resolved value of any Promise is ignored and rejection will kill 1575ff891b4eSstephan the asyncPostInit() process (at an indeterminate point because all 1576ff891b4eSstephan of them are run asynchronously in parallel). 15775b9973d8Sstephan 15785b9973d8Sstephan This list is not processed until the client calls 15795b9973d8Sstephan sqlite3.asyncPostInit(). This means, for example, that intializers 15805b9973d8Sstephan added to self.sqlite3ApiBootstrap.initializers may push entries to 15815b9973d8Sstephan this list. 15825b9973d8Sstephan*/ 15835b9973d8Sstephanself.sqlite3ApiBootstrap.initializersAsync = []; 15845b9973d8Sstephan/** 15859a34509aSstephan Client code may assign sqlite3ApiBootstrap.defaultConfig an 15869a34509aSstephan object-type value before calling sqlite3ApiBootstrap() (without 15879a34509aSstephan arguments) in order to tell that call to use this object as its 15889a34509aSstephan default config value. The intention of this is to provide 15899a34509aSstephan downstream clients with a reasonably flexible approach for plugging in 15909a34509aSstephan an environment-suitable configuration without having to define a new 15919a34509aSstephan global-scope symbol. 15929a34509aSstephan*/ 15939a34509aSstephanself.sqlite3ApiBootstrap.defaultConfig = Object.create(null); 15945b915007Sstephan/** 15955b915007Sstephan Placeholder: gets installed by the first call to 15965b915007Sstephan self.sqlite3ApiBootstrap(). However, it is recommended that the 15975b915007Sstephan caller of sqlite3ApiBootstrap() capture its return value and delete 15985b915007Sstephan self.sqlite3ApiBootstrap after calling it. It returns the same 15995b915007Sstephan value which will be stored here. 16005b915007Sstephan*/ 16019a34509aSstephanself.sqlite3ApiBootstrap.sqlite3 = undefined; 1602cd0df83cSstephan 1603