1/* 2 2022-05-22 3 4 The author disclaims copyright to this source code. In place of a 5 legal notice, here is a blessing: 6 7 * May you do good and not evil. 8 * May you find forgiveness for yourself and forgive others. 9 * May you share freely, never taking more than you give. 10 11 *********************************************************************** 12 13 This file is intended to be combined at build-time with other 14 related code, most notably a header and footer which wraps this whole 15 file into an Emscripten Module.postRun() handler which has a parameter 16 named "Module" (the Emscripten Module object). The exact requirements, 17 conventions, and build process are very much under construction and 18 will be (re)documented once they've stopped fluctuating so much. 19 20 Specific goals of this project: 21 22 - Except where noted in the non-goals, provide a more-or-less 23 feature-complete wrapper to the sqlite3 C API, insofar as WASM 24 feature parity with C allows for. In fact, provide at least 3 25 APIs... 26 27 1) Bind a low-level sqlite3 API which is as close to the native 28 one as feasible in terms of usage. 29 30 2) A higher-level API, more akin to sql.js and node.js-style 31 implementations. This one speaks directly to the low-level 32 API. This API must be used from the same thread as the 33 low-level API. 34 35 3) A second higher-level API which speaks to the previous APIs via 36 worker messages. This one is intended for use in the main 37 thread, with the lower-level APIs installed in a Worker thread, 38 and talking to them via Worker messages. Because Workers are 39 asynchronouns and have only a single message channel, some 40 acrobatics are needed here to feed async work results back to 41 the client (as we cannot simply pass around callbacks between 42 the main and Worker threads). 43 44 - Insofar as possible, support client-side storage using JS 45 filesystem APIs. As of this writing, such things are still very 46 much under development. 47 48 Specific non-goals of this project: 49 50 - As WASM is a web-centric technology and UTF-8 is the King of 51 Encodings in that realm, there are no currently plans to support 52 the UTF16-related sqlite3 APIs. They would add a complication to 53 the bindings for no appreciable benefit. Though web-related 54 implementation details take priority, and the JavaScript 55 components of the API specifically focus on browser clients, the 56 lower-level WASM module "should" work in non-web WASM 57 environments. 58 59 - Supporting old or niche-market platforms. WASM is built for a 60 modern web and requires modern platforms. 61 62 - Though scalar User-Defined Functions (UDFs) may be created in 63 JavaScript, there are currently no plans to add support for 64 aggregate and window functions. 65 66 Attribution: 67 68 This project is endebted to the work of sql.js: 69 70 https://github.com/sql-js/sql.js 71 72 sql.js was an essential stepping stone in this code's development as 73 it demonstrated how to handle some of the WASM-related voodoo (like 74 handling pointers-to-pointers and adding JS implementations of 75 C-bound callback functions). These APIs have a considerably 76 different shape than sql.js's, however. 77*/ 78 79/** 80 sqlite3ApiBootstrap() is the only global symbol persistently 81 exposed by this API. It is intended to be called one time at the 82 end of the API amalgamation process, passed configuration details 83 for the current environment, and then optionally be removed from 84 the global object using `delete self.sqlite3ApiBootstrap`. 85 86 This function expects a configuration object, intended to abstract 87 away details specific to any given WASM environment, primarily so 88 that it can be used without any _direct_ dependency on 89 Emscripten. (Note the default values for the config object!) The 90 config object is only honored the first time this is 91 called. Subsequent calls ignore the argument and return the same 92 (configured) object which gets initialized by the first call. 93 94 The config object properties include: 95 96 - `Module`[^1]: Emscripten-style module object. Currently only required 97 by certain test code and is _not_ part of the public interface. 98 (TODO: rename this to EmscriptenModule to be more explicit.) 99 100 - `exports`[^1]: the "exports" object for the current WASM 101 environment. In an Emscripten build, this should be set to 102 `Module['asm']`. 103 104 - `memory`[^1]: optional WebAssembly.Memory object, defaulting to 105 `exports.memory`. In Emscripten environments this should be set 106 to `Module.wasmMemory` if the build uses `-sIMPORT_MEMORY`, or be 107 left undefined/falsy to default to `exports.memory` when using 108 WASM-exported memory. 109 110 - `bigIntEnabled`: true if BigInt support is enabled. Defaults to 111 true if self.BigInt64Array is available, else false. Some APIs 112 will throw exceptions if called without BigInt support, as BigInt 113 is required for marshalling C-side int64 into and out of JS. 114 115 - `allocExportName`: the name of the function, in `exports`, of the 116 `malloc(3)`-compatible routine for the WASM environment. Defaults 117 to `"malloc"`. 118 119 - `deallocExportName`: the name of the function, in `exports`, of 120 the `free(3)`-compatible routine for the WASM 121 environment. Defaults to `"free"`. 122 123 - `persistentDirName`[^1]: if the environment supports persistent storage, this 124 directory names the "mount point" for that directory. It must be prefixed 125 by `/` and may currently contain only a single directory-name part. Using 126 the root directory name is not supported by any current persistent backend. 127 128 129 [^1] = This property may optionally be a function, in which case this 130 function re-assigns it to the value returned from that function, 131 enabling delayed evaluation. 132 133*/ 134'use strict'; 135self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( 136 apiConfig = (self.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig) 137){ 138 if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */ 139 console.warn("sqlite3ApiBootstrap() called multiple times.", 140 "Config and external initializers are ignored on calls after the first."); 141 return sqlite3ApiBootstrap.sqlite3; 142 } 143 apiConfig = apiConfig || {}; 144 const config = Object.create(null); 145 { 146 const configDefaults = { 147 Module: undefined/*needed for some test code, not part of the public API*/, 148 exports: undefined, 149 memory: undefined, 150 bigIntEnabled: (()=>{ 151 if('undefined'!==typeof Module){ 152 /* Emscripten module will contain HEAPU64 when build with 153 -sWASM_BIGINT=1, else it will not. */ 154 return !!Module.HEAPU64; 155 } 156 return !!self.BigInt64Array; 157 })(), 158 allocExportName: 'malloc', 159 deallocExportName: 'free', 160 persistentDirName: '/persistent' 161 }; 162 Object.keys(configDefaults).forEach(function(k){ 163 config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) 164 ? apiConfig[k] : configDefaults[k]; 165 }); 166 // Copy over any properties apiConfig defines but configDefaults does not... 167 Object.keys(apiConfig).forEach(function(k){ 168 if(!Object.getOwnPropertyDescriptor(config, k)){ 169 config[k] = apiConfig[k]; 170 } 171 }); 172 } 173 174 [ 175 // If any of these config options are functions, replace them with 176 // the result of calling that function... 177 'Module', 'exports', 'memory', 'persistentDirName' 178 ].forEach((k)=>{ 179 if('function' === typeof config[k]){ 180 config[k] = config[k](); 181 } 182 }); 183 184 /** Throws a new Error, the message of which is the concatenation 185 all args with a space between each. */ 186 const toss = (...args)=>{throw new Error(args.join(' '))}; 187 188 if(config.persistentDirName && !/^\/[^/]+$/.test(config.persistentDirName)){ 189 toss("config.persistentDirName must be falsy or in the form '/dir-name'."); 190 } 191 192 /** 193 Returns true if n is a 32-bit (signed) integer, else 194 false. This is used for determining when we need to switch to 195 double-type DB operations for integer values in order to keep 196 more precision. 197 */ 198 const isInt32 = function(n){ 199 return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) 200 && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); 201 }; 202 203 /** Returns v if v appears to be a TypedArray, else false. */ 204 const isTypedArray = (v)=>{ 205 return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; 206 }; 207 208 /** 209 Returns true if v appears to be one of our bind()-able 210 TypedArray types: Uint8Array or Int8Array. Support for 211 TypedArrays with element sizes >1 is TODO. 212 */ 213 const isBindableTypedArray = (v)=>{ 214 return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 215 }; 216 217 /** 218 Returns true if v appears to be one of the TypedArray types 219 which is legal for holding SQL code (as opposed to binary blobs). 220 221 Currently this is the same as isBindableTypedArray() but it 222 seems likely that we'll eventually want to add Uint32Array 223 and friends to the isBindableTypedArray() list but not to the 224 isSQLableTypedArray() list. 225 */ 226 const isSQLableTypedArray = (v)=>{ 227 return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 228 }; 229 230 /** Returns true if isBindableTypedArray(v) does, else throws with a message 231 that v is not a supported TypedArray value. */ 232 const affirmBindableTypedArray = (v)=>{ 233 return isBindableTypedArray(v) 234 || toss("Value is not of a supported TypedArray type."); 235 }; 236 237 const utf8Decoder = new TextDecoder('utf-8'); 238 239 /** Internal helper to use in operations which need to distinguish 240 between SharedArrayBuffer heap memory and non-shared heap. */ 241 const __SAB = ('undefined'===typeof SharedArrayBuffer) 242 ? function(){} : SharedArrayBuffer; 243 const typedArrayToString = function(arrayBuffer, begin, end){ 244 return utf8Decoder.decode( 245 (arrayBuffer.buffer instanceof __SAB) 246 ? arrayBuffer.slice(begin, end) 247 : arrayBuffer.subarray(begin, end) 248 ); 249 }; 250 251 /** 252 An Error subclass specifically for reporting Wasm-level malloc() 253 failure and enabling clients to unambiguously identify such 254 exceptions. 255 */ 256 class WasmAllocError extends Error { 257 constructor(...args){ 258 super(...args); 259 this.name = 'WasmAllocError'; 260 } 261 }; 262 263 /** 264 The main sqlite3 binding API gets installed into this object, 265 mimicking the C API as closely as we can. The numerous members 266 names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as 267 possible, identically to the C-native counterparts, as documented at: 268 269 https://www.sqlite.org/c3ref/intro.html 270 271 A very few exceptions require an additional level of proxy 272 function or may otherwise require special attention in the WASM 273 environment, and all such cases are document here. Those not 274 documented here are installed as 1-to-1 proxies for their C-side 275 counterparts. 276 */ 277 const capi = { 278 /** 279 When using sqlite3_open_v2() it is important to keep the following 280 in mind: 281 282 https://www.sqlite.org/c3ref/open.html 283 284 - The flags for use with its 3rd argument are installed in this 285 object using the C-cide names, e.g. SQLITE_OPEN_CREATE. 286 287 - If the combination of flags passed to it are invalid, 288 behavior is undefined. Thus is is never okay to call this 289 with fewer than 3 arguments, as JS will default the 290 missing arguments to `undefined`, which will result in a 291 flag value of 0. Most of the available SQLITE_OPEN_xxx 292 flags are meaningless in the WASM build, e.g. the mutext- 293 and cache-related flags, but they are retained in this 294 API for consistency's sake. 295 296 - The final argument to this function specifies the VFS to 297 use, which is largely (but not entirely!) meaningless in 298 the WASM environment. It should always be null or 299 undefined, and it is safe to elide that argument when 300 calling this function. 301 */ 302 sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/, 303 /** 304 The sqlite3_prepare_v3() binding handles two different uses 305 with differing JS/WASM semantics: 306 307 1) sqlite3_prepare_v3(pDb, sqlString, -1, prepFlags, ppStmt [, null]) 308 309 2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer) 310 311 Note that the SQL length argument (the 3rd argument) must, for 312 usage (1), always be negative because it must be a byte length 313 and that value is expensive to calculate from JS (where only 314 the character length of strings is readily available). It is 315 retained in this API's interface for code/documentation 316 compatibility reasons but is currently _always_ ignored. With 317 usage (2), the 3rd argument is used as-is but is is still 318 critical that the C-style input string (2nd argument) be 319 terminated with a 0 byte. 320 321 In usage (1), the 2nd argument must be of type string, 322 Uint8Array, or Int8Array (either of which is assumed to 323 hold SQL). If it is, this function assumes case (1) and 324 calls the underyling C function with the equivalent of: 325 326 (pDb, sqlAsString, -1, prepFlags, ppStmt, null) 327 328 The pzTail argument is ignored in this case because its result 329 is meaningless when a string-type value is passed through 330 (because the string goes through another level of internal 331 conversion for WASM's sake and the result pointer would refer 332 to that transient conversion's memory, not the passed-in 333 string). 334 335 If the sql argument is not a string, it must be a _pointer_ to 336 a NUL-terminated string which was allocated in the WASM memory 337 (e.g. using cwapi.wasm.alloc() or equivalent). In that case, 338 the final argument may be 0/null/undefined or must be a pointer 339 to which the "tail" of the compiled SQL is written, as 340 documented for the C-side sqlite3_prepare_v3(). In case (2), 341 the underlying C function is called with the equivalent of: 342 343 (pDb, sqlAsPointer, (sqlByteLen||-1), prepFlags, ppStmt, pzTail) 344 345 It returns its result and compiled statement as documented in 346 the C API. Fetching the output pointers (5th and 6th 347 parameters) requires using capi.wasm.getMemValue() (or 348 equivalent) and the pzTail will point to an address relative to 349 the sqlAsPointer value. 350 351 If passed an invalid 2nd argument type, this function will 352 return SQLITE_MISUSE but will unfortunately be able to return 353 any additional error information because we have no way to set 354 the db's error state such that this function could return a 355 non-0 integer and the client could call sqlite3_errcode() or 356 sqlite3_errmsg() to fetch it. See the RFE at: 357 358 https://sqlite.org/forum/forumpost/f9eb79b11aefd4fc81d 359 360 The alternative would be to throw an exception for that case, 361 but that would be in strong constrast to the rest of the 362 C-level API and seems likely to cause more confusion. 363 364 Side-note: in the C API the function does not fail if provided 365 an empty string but its result output pointer will be NULL. 366 */ 367 sqlite3_prepare_v3: function(dbPtr, sql, sqlByteLen, prepFlags, 368 stmtPtrPtr, strPtrPtr){}/*installed later*/, 369 370 /** 371 Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument. 372 */ 373 sqlite3_prepare_v2: function(dbPtr, sql, sqlByteLen, stmtPtrPtr, 374 strPtrPtr){}/*installed later*/, 375 376 /** 377 Various internal-use utilities are added here as needed. They 378 are bound to an object only so that we have access to them in 379 the differently-scoped steps of the API bootstrapping 380 process. At the end of the API setup process, this object gets 381 removed. 382 */ 383 util:{ 384 isInt32, isTypedArray, isBindableTypedArray, isSQLableTypedArray, 385 affirmBindableTypedArray, typedArrayToString 386 }, 387 388 /** 389 Holds state which are specific to the WASM-related 390 infrastructure and glue code. It is not expected that client 391 code will normally need these, but they're exposed here in case 392 it does. These APIs are _not_ to be considered an 393 official/stable part of the sqlite3 WASM API. They may change 394 as the developers' experience suggests appropriate changes. 395 396 Note that a number of members of this object are injected 397 dynamically after the api object is fully constructed, so 398 not all are documented inline here. 399 */ 400 wasm: { 401 //^^^ TODO?: move wasm from sqlite3.capi.wasm to sqlite3.wasm 402 /** 403 Emscripten APIs have a deep-seated assumption that all pointers 404 are 32 bits. We'll remain optimistic that that won't always be 405 the case and will use this constant in places where we might 406 otherwise use a hard-coded 4. 407 */ 408 ptrSizeof: config.wasmPtrSizeof || 4, 409 /** 410 The WASM IR (Intermediate Representation) value for 411 pointer-type values. It MUST refer to a value type of the 412 size described by this.ptrSizeof _or_ it may be any value 413 which ends in '*', which Emscripten's glue code internally 414 translates to i32. 415 */ 416 ptrIR: config.wasmPtrIR || "i32", 417 /** 418 True if BigInt support was enabled via (e.g.) the 419 Emscripten -sWASM_BIGINT flag, else false. When 420 enabled, certain 64-bit sqlite3 APIs are enabled which 421 are not otherwise enabled due to JS/WASM int64 422 impedence mismatches. 423 */ 424 bigIntEnabled: !!config.bigIntEnabled, 425 /** 426 The symbols exported by the WASM environment. 427 */ 428 exports: config.exports 429 || toss("Missing API config.exports (WASM module exports)."), 430 431 /** 432 When Emscripten compiles with `-sIMPORT_MEMORY`, it 433 initalizes the heap and imports it into wasm, as opposed to 434 the other way around. In this case, the memory is not 435 available via this.exports.memory. 436 */ 437 memory: config.memory || config.exports['memory'] 438 || toss("API config object requires a WebAssembly.Memory object", 439 "in either config.exports.memory (exported)", 440 "or config.memory (imported)."), 441 442 /** 443 The API's one single point of access to the WASM-side memory 444 allocator. Works like malloc(3) (and is likely bound to 445 malloc()) but throws an WasmAllocError if allocation fails. It is 446 important that any code which might pass through the sqlite3 C 447 API NOT throw and must instead return SQLITE_NOMEM (or 448 equivalent, depending on the context). 449 450 That said, very few cases in the API can result in 451 client-defined functions propagating exceptions via the C-style 452 API. Most notably, this applies ot User-defined SQL Functions 453 (UDFs) registered via sqlite3_create_function_v2(). For that 454 specific case it is recommended that all UDF creation be 455 funneled through a utility function and that a wrapper function 456 be added around the UDF which catches any exception and sets 457 the error state to OOM. (The overall complexity of registering 458 UDFs essentially requires a helper for doing so!) 459 */ 460 alloc: undefined/*installed later*/, 461 /** 462 The API's one single point of access to the WASM-side memory 463 deallocator. Works like free(3) (and is likely bound to 464 free()). 465 */ 466 dealloc: undefined/*installed later*/ 467 468 /* Many more wasm-related APIs get installed later on. */ 469 }/*wasm*/ 470 }/*capi*/; 471 472 /** 473 capi.wasm.alloc()'s srcTypedArray.byteLength bytes, 474 populates them with the values from the source 475 TypedArray, and returns the pointer to that memory. The 476 returned pointer must eventually be passed to 477 capi.wasm.dealloc() to clean it up. 478 479 As a special case, to avoid further special cases where 480 this is used, if srcTypedArray.byteLength is 0, it 481 allocates a single byte and sets it to the value 482 0. Even in such cases, calls must behave as if the 483 allocated memory has exactly srcTypedArray.byteLength 484 bytes. 485 486 ACHTUNG: this currently only works for Uint8Array and 487 Int8Array types and will throw if srcTypedArray is of 488 any other type. 489 */ 490 capi.wasm.allocFromTypedArray = function(srcTypedArray){ 491 affirmBindableTypedArray(srcTypedArray); 492 const pRet = this.alloc(srcTypedArray.byteLength || 1); 493 this.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); 494 return pRet; 495 }.bind(capi.wasm); 496 497 const keyAlloc = config.allocExportName || 'malloc', 498 keyDealloc = config.deallocExportName || 'free'; 499 for(const key of [keyAlloc, keyDealloc]){ 500 const f = capi.wasm.exports[key]; 501 if(!(f instanceof Function)) toss("Missing required exports[",key,"] function."); 502 } 503 504 capi.wasm.alloc = function(n){ 505 const m = this.exports[keyAlloc](n); 506 if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes."); 507 return m; 508 }.bind(capi.wasm) 509 510 capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m); 511 512 /** 513 Reports info about compile-time options using 514 sqlite_compileoption_get() and sqlite3_compileoption_used(). It 515 has several distinct uses: 516 517 If optName is an array then it is expected to be a list of 518 compilation options and this function returns an object 519 which maps each such option to true or false, indicating 520 whether or not the given option was included in this 521 build. That object is returned. 522 523 If optName is an object, its keys are expected to be compilation 524 options and this function sets each entry to true or false, 525 indicating whether the compilation option was used or not. That 526 object is returned. 527 528 If passed no arguments then it returns an object mapping 529 all known compilation options to their compile-time values, 530 or boolean true if they are defined with no value. This 531 result, which is relatively expensive to compute, is cached 532 and returned for future no-argument calls. 533 534 In all other cases it returns true if the given option was 535 active when when compiling the sqlite3 module, else false. 536 537 Compile-time option names may optionally include their 538 "SQLITE_" prefix. When it returns an object of all options, 539 the prefix is elided. 540 */ 541 capi.wasm.compileOptionUsed = function f(optName){ 542 if(!arguments.length){ 543 if(f._result) return f._result; 544 else if(!f._opt){ 545 f._rx = /^([^=]+)=(.+)/; 546 f._rxInt = /^-?\d+$/; 547 f._opt = function(opt, rv){ 548 const m = f._rx.exec(opt); 549 rv[0] = (m ? m[1] : opt); 550 rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; 551 }; 552 } 553 const rc = {}, ov = [0,0]; 554 let i = 0, k; 555 while((k = capi.sqlite3_compileoption_get(i++))){ 556 f._opt(k,ov); 557 rc[ov[0]] = ov[1]; 558 } 559 return f._result = rc; 560 }else if(Array.isArray(optName)){ 561 const rc = {}; 562 optName.forEach((v)=>{ 563 rc[v] = capi.sqlite3_compileoption_used(v); 564 }); 565 return rc; 566 }else if('object' === typeof optName){ 567 Object.keys(optName).forEach((k)=> { 568 optName[k] = capi.sqlite3_compileoption_used(k); 569 }); 570 return optName; 571 } 572 return ( 573 'string'===typeof optName 574 ) ? !!capi.sqlite3_compileoption_used(optName) : false; 575 }/*compileOptionUsed()*/; 576 577 /** 578 Signatures for the WASM-exported C-side functions. Each entry 579 is an array with 2+ elements: 580 581 [ "c-side name", 582 "result type" (capi.wasm.xWrap() syntax), 583 [arg types in xWrap() syntax] 584 // ^^^ this needn't strictly be an array: it can be subsequent 585 // elements instead: [x,y,z] is equivalent to x,y,z 586 ] 587 588 Note that support for the API-specific data types in the 589 result/argument type strings gets plugged in at a later phase in 590 the API initialization process. 591 */ 592 capi.wasm.bindingSignatures = [ 593 // Please keep these sorted by function name! 594 ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"], 595 ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], 596 ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], 597 ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], 598 ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"], 599 ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], 600 ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"], 601 ["sqlite3_close_v2", "int", "sqlite3*"], 602 ["sqlite3_changes", "int", "sqlite3*"], 603 ["sqlite3_clear_bindings","int", "sqlite3_stmt*"], 604 ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"], 605 ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"], 606 ["sqlite3_column_count", "int", "sqlite3_stmt*"], 607 ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"], 608 ["sqlite3_column_int","int", "sqlite3_stmt*", "int"], 609 ["sqlite3_column_name","string", "sqlite3_stmt*", "int"], 610 ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], 611 ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], 612 ["sqlite3_compileoption_get", "string", "int"], 613 ["sqlite3_compileoption_used", "int", "string"], 614 ["sqlite3_create_function_v2", "int", 615 "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"], 616 ["sqlite3_data_count", "int", "sqlite3_stmt*"], 617 ["sqlite3_db_filename", "string", "sqlite3*", "string"], 618 ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"], 619 ["sqlite3_db_name", "string", "sqlite3*", "int"], 620 ["sqlite3_errmsg", "string", "sqlite3*"], 621 ["sqlite3_error_offset", "int", "sqlite3*"], 622 ["sqlite3_errstr", "string", "int"], 623 //["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"], 624 // ^^^ TODO: we need a wrapper to support passing a function pointer or a function 625 // for the callback. 626 ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], 627 ["sqlite3_extended_errcode", "int", "sqlite3*"], 628 ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], 629 ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"], 630 ["sqlite3_finalize", "int", "sqlite3_stmt*"], 631 ["sqlite3_initialize", undefined], 632 ["sqlite3_interrupt", undefined, "sqlite3*" 633 /* ^^^ we cannot actually currently support this because JS is 634 single-threaded and we don't have a portable way to access a DB 635 from 2 SharedWorkers concurrently. */], 636 ["sqlite3_libversion", "string"], 637 ["sqlite3_libversion_number", "int"], 638 ["sqlite3_open", "int", "string", "*"], 639 ["sqlite3_open_v2", "int", "string", "*", "int", "string"], 640 /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled 641 separately due to us requiring two different sets of semantics 642 for those, depending on how their SQL argument is provided. */ 643 ["sqlite3_reset", "int", "sqlite3_stmt*"], 644 ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], 645 ["sqlite3_result_double",undefined, "*", "f64"], 646 ["sqlite3_result_error",undefined, "*", "string", "int"], 647 ["sqlite3_result_error_code", undefined, "*", "int"], 648 ["sqlite3_result_error_nomem", undefined, "*"], 649 ["sqlite3_result_error_toobig", undefined, "*"], 650 ["sqlite3_result_int",undefined, "*", "int"], 651 ["sqlite3_result_null",undefined, "*"], 652 ["sqlite3_result_text",undefined, "*", "string", "int", "*"], 653 ["sqlite3_shutdown", undefined], 654 ["sqlite3_sourceid", "string"], 655 ["sqlite3_sql", "string", "sqlite3_stmt*"], 656 ["sqlite3_step", "int", "sqlite3_stmt*"], 657 ["sqlite3_strglob", "int", "string","string"], 658 ["sqlite3_strlike", "int", "string","string","int"], 659 ["sqlite3_total_changes", "int", "sqlite3*"], 660 ["sqlite3_uri_boolean", "int", "string", "string", "int"], 661 ["sqlite3_uri_key", "string", "string", "int"], 662 ["sqlite3_uri_parameter", "string", "string", "string"], 663 ["sqlite3_value_blob", "*", "*"], 664 ["sqlite3_value_bytes","int", "*"], 665 ["sqlite3_value_double","f64", "*"], 666 ["sqlite3_value_text", "string", "*"], 667 ["sqlite3_value_type", "int", "*"], 668 ["sqlite3_vfs_find", "*", "string"], 669 ["sqlite3_vfs_register", "int", "*", "int"] 670 ]/*capi.wasm.bindingSignatures*/; 671 672 if(false && capi.wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ 673 /* ^^^ "the problem" is that this is an option feature and the 674 build-time function-export list does not currently take 675 optional features into account. */ 676 capi.wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); 677 } 678 679 /** 680 Functions which require BigInt (int64) support are separated from 681 the others because we need to conditionally bind them or apply 682 dummy impls, depending on the capabilities of the environment. 683 */ 684 capi.wasm.bindingSignatures.int64 = [ 685 ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], 686 ["sqlite3_changes64","i64", ["sqlite3*"]], 687 ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], 688 ["sqlite3_total_changes64", "i64", ["sqlite3*"]], 689 ["sqlite3_uri_int64", "i64", ["string", "string", "i64"]] 690 ]; 691 692 /** 693 Functions which are intended solely for API-internal use by the 694 WASM components, not client code. These get installed into 695 capi.wasm. 696 */ 697 capi.wasm.bindingSignatures.wasm = [ 698 ["sqlite3_wasm_vfs_unlink", "int", "string"] 699 ]; 700 701 /** State for sqlite3_web_persistent_dir(). */ 702 let __persistentDir; 703 /** 704 An experiment. Do not use in client code. 705 706 If the wasm environment has a persistent storage directory, 707 its path is returned by this function. If it does not then 708 it returns "" (noting that "" is a falsy value). 709 710 The first time this is called, this function inspects the current 711 environment to determine whether persistence filesystem support 712 is available and, if it is, enables it (if needed). 713 714 This function currently only recognizes the WASMFS/OPFS storage 715 combination. "Plain" OPFS is provided via a separate VFS which 716 can optionally be installed (if OPFS is available on the system) 717 using sqlite3.installOpfsVfs(). 718 719 TODOs and caveats: 720 721 - If persistent storage is available at the root of the virtual 722 filesystem, this interface cannot currently distinguish that 723 from the lack of persistence. That can (in the mean time) 724 happen when using the JS-native "opfs" VFS, as opposed to the 725 WASMFS/OPFS combination. 726 */ 727 capi.sqlite3_web_persistent_dir = function(){ 728 if(undefined !== __persistentDir) return __persistentDir; 729 // If we have no OPFS, there is no persistent dir 730 const pdir = config.persistentDirName; 731 if(!pdir 732 || !self.FileSystemHandle 733 || !self.FileSystemDirectoryHandle 734 || !self.FileSystemFileHandle){ 735 return __persistentDir = ""; 736 } 737 try{ 738 if(pdir && 0===capi.wasm.xCallWrapped( 739 'sqlite3_wasm_init_wasmfs', 'i32', ['string'], pdir 740 )){ 741 /** OPFS does not support locking and will trigger errors if 742 we try to lock. We don't _really_ want to 743 _unconditionally_ install a non-locking sqlite3 VFS as the 744 default, but we do so here for simplicy's sake for the 745 time being. That said: locking is a no-op on all of the 746 current WASM storage, so this isn't (currently) as bad as 747 it may initially seem. */ 748 const pVfs = sqlite3.capi.sqlite3_vfs_find("unix-none"); 749 if(pVfs){ 750 capi.sqlite3_vfs_register(pVfs,1); 751 console.warn("Installed 'unix-none' as the default sqlite3 VFS."); 752 } 753 return __persistentDir = pdir; 754 }else{ 755 return __persistentDir = ""; 756 } 757 }catch(e){ 758 // sqlite3_wasm_init_wasmfs() is not available 759 return __persistentDir = ""; 760 } 761 }; 762 763 /** 764 Experimental and subject to change or removal. 765 766 Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a 767 non-empty string and the given name starts with (that string + 768 '/'), else returns false. 769 770 Potential (but arguable) TODO: return true if the name is one of 771 (":localStorage:", "local", ":sessionStorage:", "session") and 772 kvvfs is available. 773 */ 774 capi.sqlite3_web_filename_is_persistent = function(name){ 775 const p = capi.sqlite3_web_persistent_dir(); 776 return (p && name) ? name.startsWith(p+'/') : false; 777 }; 778 779 // This bit is highly arguable and is incompatible with the fiddle shell. 780 if(false && 0===capi.wasm.exports.sqlite3_vfs_find(0)){ 781 /* Assume that sqlite3_initialize() has not yet been called. 782 This will be the case in an SQLITE_OS_KV build. */ 783 capi.wasm.exports.sqlite3_initialize(); 784 } 785 786 /** 787 Given an `sqlite3*` and an sqlite3_vfs name, returns a truthy 788 value (see below) if that db handle uses that VFS, else returns 789 false. If pDb is falsy then this function returns a truthy value 790 if the default VFS is that VFS. Results are undefined if pDb is 791 truthy but refers to an invalid pointer. 792 793 The 2nd argument may either be a JS string or a C-string 794 allocated from the wasm environment. 795 796 The truthy value it returns is a pointer to the `sqlite3_vfs` 797 object. 798 799 To permit safe use of this function from APIs which may be called 800 via the C stack (like SQL UDFs), this function does not throw: if 801 bad arguments cause a conversion error when passing into 802 wasm-space, false is returned. 803 */ 804 capi.sqlite3_web_db_uses_vfs = function(pDb,vfsName){ 805 try{ 806 const pK = ('number'===vfsName) 807 ? capi.wasm.exports.sqlite3_vfs_find(vfsName) 808 : capi.sqlite3_vfs_find(vfsName); 809 if(!pK) return false; 810 else if(!pDb){ 811 return capi.sqlite3_vfs_find(0)===pK ? pK : false; 812 } 813 const ppVfs = capi.wasm.allocPtr(); 814 try{ 815 return ( 816 (0===capi.sqlite3_file_control( 817 pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs 818 )) && (capi.wasm.getPtrValue(ppVfs) === pK) 819 ) ? pK : false; 820 }finally{ 821 capi.wasm.dealloc(ppVfs); 822 } 823 }catch(e){ 824 /* Ignore - probably bad args to a wasm-bound function. */ 825 return false; 826 } 827 }; 828 829 /** 830 Returns an array of the names of all currently-registered sqlite3 831 VFSes. 832 */ 833 capi.sqlite3_web_vfs_list = function(){ 834 const rc = []; 835 let pVfs = capi.sqlite3_vfs_find(0); 836 while(pVfs){ 837 const oVfs = new capi.sqlite3_vfs(pVfs); 838 rc.push(capi.wasm.cstringToJs(oVfs.$zName)); 839 pVfs = oVfs.$pNext; 840 oVfs.dispose(); 841 } 842 return rc; 843 }; 844 845 if( self.window===self ){ 846 /* Features specific to the main window thread... */ 847 848 /** 849 Internal helper for sqlite3_web_kvvfs_clear() and friends. 850 Its argument should be one of ('local','session',''). 851 */ 852 const __kvvfsInfo = function(which){ 853 const rc = Object.create(null); 854 rc.prefix = 'kvvfs-'+which; 855 rc.stores = []; 856 if('session'===which || ''===which) rc.stores.push(self.sessionStorage); 857 if('local'===which || ''===which) rc.stores.push(self.localStorage); 858 return rc; 859 }; 860 861 /** 862 Clears all storage used by the kvvfs DB backend, deleting any 863 DB(s) stored there. Its argument must be either 'session', 864 'local', or ''. In the first two cases, only sessionStorage 865 resp. localStorage is cleared. If it's an empty string (the 866 default) then both are cleared. Only storage keys which match 867 the pattern used by kvvfs are cleared: any other client-side 868 data are retained. 869 870 This function is only available in the main window thread. 871 872 Returns the number of entries cleared. 873 */ 874 capi.sqlite3_web_kvvfs_clear = function(which=''){ 875 let rc = 0; 876 const kvinfo = __kvvfsInfo(which); 877 kvinfo.stores.forEach((s)=>{ 878 const toRm = [] /* keys to remove */; 879 let i; 880 for( i = 0; i < s.length; ++i ){ 881 const k = s.key(i); 882 if(k.startsWith(kvinfo.prefix)) toRm.push(k); 883 } 884 toRm.forEach((kk)=>s.removeItem(kk)); 885 rc += toRm.length; 886 }); 887 return rc; 888 }; 889 890 /** 891 This routine guesses the approximate amount of 892 window.localStorage and/or window.sessionStorage in use by the 893 kvvfs database backend. Its argument must be one of 894 ('session', 'local', ''). In the first two cases, only 895 sessionStorage resp. localStorage is counted. If it's an empty 896 string (the default) then both are counted. Only storage keys 897 which match the pattern used by kvvfs are counted. The returned 898 value is the "length" value of every matching key and value, 899 noting that JavaScript stores each character in 2 bytes. 900 901 Note that the returned size is not authoritative from the 902 perspective of how much data can fit into localStorage and 903 sessionStorage, as the precise algorithms for determining 904 those limits are unspecified and may include per-entry 905 overhead invisible to clients. 906 */ 907 capi.sqlite3_web_kvvfs_size = function(which=''){ 908 let sz = 0; 909 const kvinfo = __kvvfsInfo(which); 910 kvinfo.stores.forEach((s)=>{ 911 let i; 912 for(i = 0; i < s.length; ++i){ 913 const k = s.key(i); 914 if(k.startsWith(kvinfo.prefix)){ 915 sz += k.length; 916 sz += s.getItem(k).length; 917 } 918 } 919 }); 920 return sz * 2 /* because JS uses UC16 encoding */; 921 }; 922 923 }/* main-window-only bits */ 924 925 /* The remainder of the API will be set up in later steps. */ 926 const sqlite3 = { 927 WasmAllocError: WasmAllocError, 928 capi, 929 config 930 }; 931 sqlite3ApiBootstrap.initializers.forEach((f)=>f(sqlite3)); 932 delete sqlite3ApiBootstrap.initializers; 933 sqlite3ApiBootstrap.sqlite3 = sqlite3; 934 return sqlite3; 935}/*sqlite3ApiBootstrap()*/; 936/** 937 self.sqlite3ApiBootstrap.initializers is an internal detail used by 938 the various pieces of the sqlite3 API's amalgamation process. It 939 must not be modified by client code except when plugging such code 940 into the amalgamation process. 941 942 Each component of the amalgamation is expected to append a function 943 to this array. When sqlite3ApiBootstrap() is called for the first 944 time, each such function will be called (in their appended order) 945 and passed the sqlite3 namespace object, into which they can install 946 their features (noting that most will also require that certain 947 features alread have been installed). At the end of that process, 948 this array is deleted. 949*/ 950self.sqlite3ApiBootstrap.initializers = []; 951/** 952 Client code may assign sqlite3ApiBootstrap.defaultConfig an 953 object-type value before calling sqlite3ApiBootstrap() (without 954 arguments) in order to tell that call to use this object as its 955 default config value. The intention of this is to provide 956 downstream clients with a reasonably flexible approach for plugging in 957 an environment-suitable configuration without having to define a new 958 global-scope symbol. 959*/ 960self.sqlite3ApiBootstrap.defaultConfig = Object.create(null); 961/** 962 Placeholder: gets installed by the first call to 963 self.sqlite3ApiBootstrap(). However, it is recommended that the 964 caller of sqlite3ApiBootstrap() capture its return value and delete 965 self.sqlite3ApiBootstrap after calling it. It returns the same 966 value which will be stored here. 967*/ 968self.sqlite3ApiBootstrap.sqlite3 = undefined; 969