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 TODO. Initial testing with using IndexedDB as backing storage 47 showed it to work reasonably well, but it's also too easy to 48 corrupt by using a web page in two browser tabs because IndexedDB 49 lacks the locking features needed to support that. 50 51 Specific non-goals of this project: 52 53 - As WASM is a web-centric technology and UTF-8 is the King of 54 Encodings in that realm, there are no currently plans to support 55 the UTF16-related sqlite3 APIs. They would add a complication to 56 the bindings for no appreciable benefit. Though web-related 57 implementation details take priority, the lower-level WASM module 58 "should" work in non-web WASM environments. 59 60 - Supporting old or niche-market platforms. WASM is built for a 61 modern web and requires modern platforms. 62 63 - Though scalar User-Defined Functions (UDFs) may be created in 64 JavaScript, there are currently no plans to add support for 65 aggregate and window functions. 66 67 Attribution: 68 69 This project is endebted to the work of sql.js: 70 71 https://github.com/sql-js/sql.js 72 73 sql.js was an essential stepping stone in this code's development as 74 it demonstrated how to handle some of the WASM-related voodoo (like 75 handling pointers-to-pointers and adding JS implementations of 76 C-bound callback functions). These APIs have a considerably 77 different shape than sql.js's, however. 78*/ 79 80/** 81 sqlite3ApiBootstrap() is the only global symbol exposed by this 82 API. It is intended to be called one time at the end of the API 83 amalgamation process, passed configuration details for the current 84 environment, and then optionally be removed from the global object 85 using `delete self.sqlite3ApiBootstrap`. 86 87 This function expects a configuration object, intended to abstract 88 away details specific to any given WASM environment, primarily so 89 that it can be used without any _direct_ dependency on 90 Emscripten. The config object is only honored the first time this 91 is 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 = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig) 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: !!self.BigInt64Array, 151 allocExportName: 'malloc', 152 deallocExportName: 'free', 153 persistentDirName: '/persistent' 154 }; 155 Object.keys(configDefaults).forEach(function(k){ 156 config[k] = Object.getOwnPropertyDescriptor(apiConfig, k) 157 ? apiConfig[k] : configDefaults[k]; 158 }); 159 // Copy over any properties apiConfig defines but configDefaults does not... 160 Object.keys(apiConfig).forEach(function(k){ 161 if(!Object.getOwnPropertyDescriptor(config, k)){ 162 config[k] = apiConfig[k]; 163 } 164 }); 165 } 166 167 [ 168 // If any of these config options are functions, replace them with 169 // the result of calling that function... 170 'Module', 'exports', 'memory', 'persistentDirName' 171 ].forEach((k)=>{ 172 if('function' === typeof config[k]){ 173 config[k] = config[k](); 174 } 175 }); 176 177 /** Throws a new Error, the message of which is the concatenation 178 all args with a space between each. */ 179 const toss = (...args)=>{throw new Error(args.join(' '))}; 180 181 if(config.persistentDirName && !/^\/[^/]+$/.test(config.persistentDirName)){ 182 toss("config.persistentDirName must be falsy or in the form '/dir-name'."); 183 } 184 185 /** 186 Returns true if n is a 32-bit (signed) integer, else 187 false. This is used for determining when we need to switch to 188 double-type DB operations for integer values in order to keep 189 more precision. 190 */ 191 const isInt32 = function(n){ 192 return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/) 193 && !!(n===(n|0) && n<=2147483647 && n>=-2147483648); 194 }; 195 196 /** Returns v if v appears to be a TypedArray, else false. */ 197 const isTypedArray = (v)=>{ 198 return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false; 199 }; 200 201 /** 202 Returns true if v appears to be one of our bind()-able 203 TypedArray types: Uint8Array or Int8Array. Support for 204 TypedArrays with element sizes >1 is TODO. 205 */ 206 const isBindableTypedArray = (v)=>{ 207 return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 208 }; 209 210 /** 211 Returns true if v appears to be one of the TypedArray types 212 which is legal for holding SQL code (as opposed to binary blobs). 213 214 Currently this is the same as isBindableTypedArray() but it 215 seems likely that we'll eventually want to add Uint32Array 216 and friends to the isBindableTypedArray() list but not to the 217 isSQLableTypedArray() list. 218 */ 219 const isSQLableTypedArray = (v)=>{ 220 return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT); 221 }; 222 223 /** Returns true if isBindableTypedArray(v) does, else throws with a message 224 that v is not a supported TypedArray value. */ 225 const affirmBindableTypedArray = (v)=>{ 226 return isBindableTypedArray(v) 227 || toss("Value is not of a supported TypedArray type."); 228 }; 229 230 const utf8Decoder = new TextDecoder('utf-8'); 231 232 /** Internal helper to use in operations which need to distinguish 233 between SharedArrayBuffer heap memory and non-shared heap. */ 234 const __SAB = ('undefined'===typeof SharedArrayBuffer) 235 ? function(){} : SharedArrayBuffer; 236 const typedArrayToString = function(arrayBuffer, begin, end){ 237 return utf8Decoder.decode( 238 (arrayBuffer.buffer instanceof __SAB) 239 ? arrayBuffer.slice(begin, end) 240 : arrayBuffer.subarray(begin, end) 241 ); 242 }; 243 244 /** 245 An Error subclass specifically for reporting Wasm-level malloc() 246 failure and enabling clients to unambiguously identify such 247 exceptions. 248 */ 249 class WasmAllocError extends Error { 250 constructor(...args){ 251 super(...args); 252 this.name = 'WasmAllocError'; 253 } 254 }; 255 256 /** 257 The main sqlite3 binding API gets installed into this object, 258 mimicking the C API as closely as we can. The numerous members 259 names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as 260 possible, identically to the C-native counterparts, as documented at: 261 262 https://www.sqlite.org/c3ref/intro.html 263 264 A very few exceptions require an additional level of proxy 265 function or may otherwise require special attention in the WASM 266 environment, and all such cases are document here. Those not 267 documented here are installed as 1-to-1 proxies for their C-side 268 counterparts. 269 */ 270 const capi = { 271 /** 272 When using sqlite3_open_v2() it is important to keep the following 273 in mind: 274 275 https://www.sqlite.org/c3ref/open.html 276 277 - The flags for use with its 3rd argument are installed in this 278 object using the C-cide names, e.g. SQLITE_OPEN_CREATE. 279 280 - If the combination of flags passed to it are invalid, 281 behavior is undefined. Thus is is never okay to call this 282 with fewer than 3 arguments, as JS will default the 283 missing arguments to `undefined`, which will result in a 284 flag value of 0. Most of the available SQLITE_OPEN_xxx 285 flags are meaningless in the WASM build, e.g. the mutext- 286 and cache-related flags, but they are retained in this 287 API for consistency's sake. 288 289 - The final argument to this function specifies the VFS to 290 use, which is largely (but not entirely!) meaningless in 291 the WASM environment. It should always be null or 292 undefined, and it is safe to elide that argument when 293 calling this function. 294 */ 295 sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/, 296 /** 297 The sqlite3_prepare_v3() binding handles two different uses 298 with differing JS/WASM semantics: 299 300 1) sqlite3_prepare_v3(pDb, sqlString, -1, prepFlags, ppStmt [, null]) 301 302 2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer) 303 304 Note that the SQL length argument (the 3rd argument) must, for 305 usage (1), always be negative because it must be a byte length 306 and that value is expensive to calculate from JS (where only 307 the character length of strings is readily available). It is 308 retained in this API's interface for code/documentation 309 compatibility reasons but is currently _always_ ignored. With 310 usage (2), the 3rd argument is used as-is but is is still 311 critical that the C-style input string (2nd argument) be 312 terminated with a 0 byte. 313 314 In usage (1), the 2nd argument must be of type string, 315 Uint8Array, or Int8Array (either of which is assumed to 316 hold SQL). If it is, this function assumes case (1) and 317 calls the underyling C function with the equivalent of: 318 319 (pDb, sqlAsString, -1, prepFlags, ppStmt, null) 320 321 The pzTail argument is ignored in this case because its result 322 is meaningless when a string-type value is passed through 323 (because the string goes through another level of internal 324 conversion for WASM's sake and the result pointer would refer 325 to that transient conversion's memory, not the passed-in 326 string). 327 328 If the sql argument is not a string, it must be a _pointer_ to 329 a NUL-terminated string which was allocated in the WASM memory 330 (e.g. using cwapi.wasm.alloc() or equivalent). In that case, 331 the final argument may be 0/null/undefined or must be a pointer 332 to which the "tail" of the compiled SQL is written, as 333 documented for the C-side sqlite3_prepare_v3(). In case (2), 334 the underlying C function is called with the equivalent of: 335 336 (pDb, sqlAsPointer, (sqlByteLen||-1), prepFlags, ppStmt, pzTail) 337 338 It returns its result and compiled statement as documented in 339 the C API. Fetching the output pointers (5th and 6th 340 parameters) requires using capi.wasm.getMemValue() (or 341 equivalent) and the pzTail will point to an address relative to 342 the sqlAsPointer value. 343 344 If passed an invalid 2nd argument type, this function will 345 return SQLITE_MISUSE but will unfortunately be able to return 346 any additional error information because we have no way to set 347 the db's error state such that this function could return a 348 non-0 integer and the client could call sqlite3_errcode() or 349 sqlite3_errmsg() to fetch it. See the RFE at: 350 351 https://sqlite.org/forum/forumpost/f9eb79b11aefd4fc81d 352 353 The alternative would be to throw an exception for that case, 354 but that would be in strong constrast to the rest of the 355 C-level API and seems likely to cause more confusion. 356 357 Side-note: in the C API the function does not fail if provided 358 an empty string but its result output pointer will be NULL. 359 */ 360 sqlite3_prepare_v3: function(dbPtr, sql, sqlByteLen, prepFlags, 361 stmtPtrPtr, strPtrPtr){}/*installed later*/, 362 363 /** 364 Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument. 365 */ 366 sqlite3_prepare_v2: function(dbPtr, sql, sqlByteLen, stmtPtrPtr, 367 strPtrPtr){}/*installed later*/, 368 369 /** 370 Various internal-use utilities are added here as needed. They 371 are bound to an object only so that we have access to them in 372 the differently-scoped steps of the API bootstrapping 373 process. At the end of the API setup process, this object gets 374 removed. 375 */ 376 util:{ 377 isInt32, isTypedArray, isBindableTypedArray, isSQLableTypedArray, 378 affirmBindableTypedArray, typedArrayToString 379 }, 380 381 /** 382 Holds state which are specific to the WASM-related 383 infrastructure and glue code. It is not expected that client 384 code will normally need these, but they're exposed here in case 385 it does. These APIs are _not_ to be considered an 386 official/stable part of the sqlite3 WASM API. They may change 387 as the developers' experience suggests appropriate changes. 388 389 Note that a number of members of this object are injected 390 dynamically after the api object is fully constructed, so 391 not all are documented inline here. 392 */ 393 wasm: { 394 //^^^ TODO?: move wasm from sqlite3.capi.wasm to sqlite3.wasm 395 /** 396 Emscripten APIs have a deep-seated assumption that all pointers 397 are 32 bits. We'll remain optimistic that that won't always be 398 the case and will use this constant in places where we might 399 otherwise use a hard-coded 4. 400 */ 401 ptrSizeof: config.wasmPtrSizeof || 4, 402 /** 403 The WASM IR (Intermediate Representation) value for 404 pointer-type values. It MUST refer to a value type of the 405 size described by this.ptrSizeof _or_ it may be any value 406 which ends in '*', which Emscripten's glue code internally 407 translates to i32. 408 */ 409 ptrIR: config.wasmPtrIR || "i32", 410 /** 411 True if BigInt support was enabled via (e.g.) the 412 Emscripten -sWASM_BIGINT flag, else false. When 413 enabled, certain 64-bit sqlite3 APIs are enabled which 414 are not otherwise enabled due to JS/WASM int64 415 impedence mismatches. 416 */ 417 bigIntEnabled: !!config.bigIntEnabled, 418 /** 419 The symbols exported by the WASM environment. 420 */ 421 exports: config.exports 422 || toss("Missing API config.exports (WASM module exports)."), 423 424 /** 425 When Emscripten compiles with `-sIMPORT_MEMORY`, it 426 initalizes the heap and imports it into wasm, as opposed to 427 the other way around. In this case, the memory is not 428 available via this.exports.memory. 429 */ 430 memory: config.memory || config.exports['memory'] 431 || toss("API config object requires a WebAssembly.Memory object", 432 "in either config.exports.memory (exported)", 433 "or config.memory (imported)."), 434 435 /** 436 The API's one single point of access to the WASM-side memory 437 allocator. Works like malloc(3) (and is likely bound to 438 malloc()) but throws an WasmAllocError if allocation fails. It is 439 important that any code which might pass through the sqlite3 C 440 API NOT throw and must instead return SQLITE_NOMEM (or 441 equivalent, depending on the context). 442 443 That said, very few cases in the API can result in 444 client-defined functions propagating exceptions via the C-style 445 API. Most notably, this applies ot User-defined SQL Functions 446 (UDFs) registered via sqlite3_create_function_v2(). For that 447 specific case it is recommended that all UDF creation be 448 funneled through a utility function and that a wrapper function 449 be added around the UDF which catches any exception and sets 450 the error state to OOM. (The overall complexity of registering 451 UDFs essentially requires a helper for doing so!) 452 */ 453 alloc: undefined/*installed later*/, 454 /** 455 The API's one single point of access to the WASM-side memory 456 deallocator. Works like free(3) (and is likely bound to 457 free()). 458 */ 459 dealloc: undefined/*installed later*/ 460 461 /* Many more wasm-related APIs get installed later on. */ 462 }/*wasm*/ 463 }/*capi*/; 464 465 /** 466 capi.wasm.alloc()'s srcTypedArray.byteLength bytes, 467 populates them with the values from the source 468 TypedArray, and returns the pointer to that memory. The 469 returned pointer must eventually be passed to 470 capi.wasm.dealloc() to clean it up. 471 472 As a special case, to avoid further special cases where 473 this is used, if srcTypedArray.byteLength is 0, it 474 allocates a single byte and sets it to the value 475 0. Even in such cases, calls must behave as if the 476 allocated memory has exactly srcTypedArray.byteLength 477 bytes. 478 479 ACHTUNG: this currently only works for Uint8Array and 480 Int8Array types and will throw if srcTypedArray is of 481 any other type. 482 */ 483 capi.wasm.allocFromTypedArray = function(srcTypedArray){ 484 affirmBindableTypedArray(srcTypedArray); 485 const pRet = this.alloc(srcTypedArray.byteLength || 1); 486 this.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet); 487 return pRet; 488 }.bind(capi.wasm); 489 490 const keyAlloc = config.allocExportName || 'malloc', 491 keyDealloc = config.deallocExportName || 'free'; 492 for(const key of [keyAlloc, keyDealloc]){ 493 const f = capi.wasm.exports[key]; 494 if(!(f instanceof Function)) toss("Missing required exports[",key,"] function."); 495 } 496 497 capi.wasm.alloc = function(n){ 498 const m = this.exports[keyAlloc](n); 499 if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes."); 500 return m; 501 }.bind(capi.wasm) 502 503 capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m); 504 505 /** 506 Reports info about compile-time options using 507 sqlite_compileoption_get() and sqlite3_compileoption_used(). It 508 has several distinct uses: 509 510 If optName is an array then it is expected to be a list of 511 compilation options and this function returns an object 512 which maps each such option to true or false, indicating 513 whether or not the given option was included in this 514 build. That object is returned. 515 516 If optName is an object, its keys are expected to be compilation 517 options and this function sets each entry to true or false, 518 indicating whether the compilation option was used or not. That 519 object is returned. 520 521 If passed no arguments then it returns an object mapping 522 all known compilation options to their compile-time values, 523 or boolean true if they are defined with no value. This 524 result, which is relatively expensive to compute, is cached 525 and returned for future no-argument calls. 526 527 In all other cases it returns true if the given option was 528 active when when compiling the sqlite3 module, else false. 529 530 Compile-time option names may optionally include their 531 "SQLITE_" prefix. When it returns an object of all options, 532 the prefix is elided. 533 */ 534 capi.wasm.compileOptionUsed = function f(optName){ 535 if(!arguments.length){ 536 if(f._result) return f._result; 537 else if(!f._opt){ 538 f._rx = /^([^=]+)=(.+)/; 539 f._rxInt = /^-?\d+$/; 540 f._opt = function(opt, rv){ 541 const m = f._rx.exec(opt); 542 rv[0] = (m ? m[1] : opt); 543 rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true; 544 }; 545 } 546 const rc = {}, ov = [0,0]; 547 let i = 0, k; 548 while((k = capi.sqlite3_compileoption_get(i++))){ 549 f._opt(k,ov); 550 rc[ov[0]] = ov[1]; 551 } 552 return f._result = rc; 553 }else if(Array.isArray(optName)){ 554 const rc = {}; 555 optName.forEach((v)=>{ 556 rc[v] = capi.sqlite3_compileoption_used(v); 557 }); 558 return rc; 559 }else if('object' === typeof optName){ 560 Object.keys(optName).forEach((k)=> { 561 optName[k] = capi.sqlite3_compileoption_used(k); 562 }); 563 return optName; 564 } 565 return ( 566 'string'===typeof optName 567 ) ? !!capi.sqlite3_compileoption_used(optName) : false; 568 }/*compileOptionUsed()*/; 569 570 capi.wasm.bindingSignatures = [ 571 /** 572 Signatures for the WASM-exported C-side functions. Each entry 573 is an array with 2+ elements: 574 575 ["c-side name", 576 "result type" (capi.wasm.xWrap() syntax), 577 [arg types in xWrap() syntax] 578 // ^^^ this needn't strictly be an array: it can be subsequent 579 // elements instead: [x,y,z] is equivalent to x,y,z 580 ] 581 */ 582 // Please keep these sorted by function name! 583 ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"], 584 ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"], 585 ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"], 586 ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"], 587 ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"], 588 ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"], 589 ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"], 590 ["sqlite3_close_v2", "int", "sqlite3*"], 591 ["sqlite3_changes", "int", "sqlite3*"], 592 ["sqlite3_clear_bindings","int", "sqlite3_stmt*"], 593 ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"], 594 ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"], 595 ["sqlite3_column_count", "int", "sqlite3_stmt*"], 596 ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"], 597 ["sqlite3_column_int","int", "sqlite3_stmt*", "int"], 598 ["sqlite3_column_name","string", "sqlite3_stmt*", "int"], 599 ["sqlite3_column_text","string", "sqlite3_stmt*", "int"], 600 ["sqlite3_column_type","int", "sqlite3_stmt*", "int"], 601 ["sqlite3_compileoption_get", "string", "int"], 602 ["sqlite3_compileoption_used", "int", "string"], 603 ["sqlite3_create_function_v2", "int", 604 "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"], 605 ["sqlite3_data_count", "int", "sqlite3_stmt*"], 606 ["sqlite3_db_filename", "string", "sqlite3*", "string"], 607 ["sqlite3_db_name", "string", "sqlite3*", "int"], 608 ["sqlite3_errmsg", "string", "sqlite3*"], 609 ["sqlite3_error_offset", "int", "sqlite3*"], 610 ["sqlite3_errstr", "string", "int"], 611 //["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"], 612 // ^^^ TODO: we need a wrapper to support passing a function pointer or a function 613 // for the callback. 614 ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"], 615 ["sqlite3_extended_errcode", "int", "sqlite3*"], 616 ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"], 617 ["sqlite3_finalize", "int", "sqlite3_stmt*"], 618 ["sqlite3_initialize", undefined], 619 ["sqlite3_interrupt", undefined, "sqlite3*" 620 /* ^^^ we cannot actually currently support this because JS is 621 single-threaded and we don't have a portable way to access a DB 622 from 2 SharedWorkers concurrently. */], 623 ["sqlite3_libversion", "string"], 624 ["sqlite3_libversion_number", "int"], 625 ["sqlite3_open", "int", "string", "*"], 626 ["sqlite3_open_v2", "int", "string", "*", "int", "string"], 627 /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled 628 separately due to us requiring two different sets of semantics 629 for those, depending on how their SQL argument is provided. */ 630 ["sqlite3_reset", "int", "sqlite3_stmt*"], 631 ["sqlite3_result_blob",undefined, "*", "*", "int", "*"], 632 ["sqlite3_result_double",undefined, "*", "f64"], 633 ["sqlite3_result_error",undefined, "*", "string", "int"], 634 ["sqlite3_result_error_code", undefined, "*", "int"], 635 ["sqlite3_result_error_nomem", undefined, "*"], 636 ["sqlite3_result_error_toobig", undefined, "*"], 637 ["sqlite3_result_int",undefined, "*", "int"], 638 ["sqlite3_result_null",undefined, "*"], 639 ["sqlite3_result_text",undefined, "*", "string", "int", "*"], 640 ["sqlite3_sourceid", "string"], 641 ["sqlite3_sql", "string", "sqlite3_stmt*"], 642 ["sqlite3_step", "int", "sqlite3_stmt*"], 643 ["sqlite3_strglob", "int", "string","string"], 644 ["sqlite3_strlike", "int", "string","string","int"], 645 ["sqlite3_total_changes", "int", "sqlite3*"], 646 ["sqlite3_value_blob", "*", "*"], 647 ["sqlite3_value_bytes","int", "*"], 648 ["sqlite3_value_double","f64", "*"], 649 ["sqlite3_value_text", "string", "*"], 650 ["sqlite3_value_type", "int", "*"], 651 ["sqlite3_vfs_find", "*", "string"], 652 ["sqlite3_vfs_register", "int", "*", "int"], 653 ["sqlite3_wasm_vfs_unlink", "int", "string"] 654 ]/*capi.wasm.bindingSignatures*/; 655 656 if(false && capi.wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ 657 /* ^^^ "the problem" is that this is an option feature and the 658 build-time function-export list does not currently take 659 optional features into account. */ 660 capi.wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]); 661 } 662 663 /** 664 Functions which require BigInt (int64) support are separated from 665 the others because we need to conditionally bind them or apply 666 dummy impls, depending on the capabilities of the environment. 667 */ 668 capi.wasm.bindingSignatures.int64 = [ 669 ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]], 670 ["sqlite3_changes64","i64", ["sqlite3*"]], 671 ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]], 672 ["sqlite3_total_changes64", "i64", ["sqlite3*"]] 673 ]; 674 675 /** State for sqlite3_web_persistent_dir(). */ 676 let __persistentDir; 677 /** 678 An experiment. Do not use. 679 680 If the wasm environment has a persistent storage directory, 681 its path is returned by this function. If it does not then 682 it returns "" (noting that "" is a falsy value). 683 684 The first time this is called, this function inspects the current 685 environment to determine whether persistence filesystem support 686 is available and, if it is, enables it (if needed). 687 688 TODOs and caveats: 689 690 - If persistent storage is available at the root of the virtual 691 filesystem, this interface cannot currently distinguish that 692 from the lack of persistence. That case cannot currently (with 693 WASMFS/OPFS) happen, but it is conceivably possible in future 694 environments or non-browser runtimes (none of which are yet 695 supported targets). 696 */ 697 capi.sqlite3_web_persistent_dir = function(){ 698 if(undefined !== __persistentDir) return __persistentDir; 699 // If we have no OPFS, there is no persistent dir 700 const pdir = config.persistentDirName; 701 if(!pdir 702 || !self.FileSystemHandle 703 || !self.FileSystemDirectoryHandle 704 || !self.FileSystemFileHandle){ 705 return __persistentDir = ""; 706 } 707 try{ 708 if(pdir && 0===this.wasm.xCallWrapped( 709 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir 710 )){ 711 /** OPFS does not support locking and will trigger errors if 712 we try to lock. We don't _really_ want to 713 _unconditionally_ install a non-locking sqlite3 VFS as the 714 default, but we do so here for simplicy's sake for the 715 time being. That said: locking is a no-op on all of the 716 current WASM storage, so this isn't (currently) as bad as 717 it may initially seem. */ 718 const pVfs = sqlite3.capi.sqlite3_vfs_find("unix-none"); 719 if(pVfs){ 720 capi.sqlite3_vfs_register(pVfs,1); 721 console.warn("Installed 'unix-none' as the default sqlite3 VFS."); 722 } 723 return __persistentDir = pdir; 724 }else{ 725 return __persistentDir = ""; 726 } 727 }catch(e){ 728 // sqlite3_wasm_init_opfs() is not available 729 return __persistentDir = ""; 730 } 731 }.bind(capi); 732 733 /** 734 Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a 735 non-empty string and the given name has that string as its 736 prefix, else returns false. 737 */ 738 capi.sqlite3_web_filename_is_persistent = function(name){ 739 const p = this.sqlite3_web_persistent_dir(); 740 return (p && name) ? name.startsWith(p) : false; 741 }.bind(capi); 742 743 /* The remainder of the API will be set up in later steps. */ 744 const sqlite3 = { 745 WasmAllocError: WasmAllocError, 746 capi, 747 config 748 }; 749 sqlite3ApiBootstrap.initializers.forEach((f)=>f(sqlite3)); 750 delete sqlite3ApiBootstrap.initializers; 751 sqlite3ApiBootstrap.sqlite3 = sqlite3; 752 return sqlite3; 753}/*sqlite3ApiBootstrap()*/; 754/** 755 self.sqlite3ApiBootstrap.initializers is an internal detail used by 756 the various pieces of the sqlite3 API's amalgamation process. It 757 must not be modified by client code except when plugging such code 758 into the amalgamation process. 759 760 Each component of the amalgamation is expected to append a function 761 to this array. When sqlite3ApiBootstrap() is called for the first 762 time, each such function will be called (in their appended order) 763 and passed the sqlite3 namespace object, into which they can install 764 their features (noting that most will also require that certain 765 features alread have been installed). At the end of that process, 766 this array is deleted. 767*/ 768self.sqlite3ApiBootstrap.initializers = []; 769/** 770 Client code may assign sqlite3ApiBootstrap.defaultConfig an 771 object-type value before calling sqlite3ApiBootstrap() (without 772 arguments) in order to tell that call to use this object as its 773 default config value. The intention of this is to provide 774 downstream clients with a reasonably flexible approach for plugging in 775 an environment-suitable configuration without having to define a new 776 global-scope symbol. 777*/ 778self.sqlite3ApiBootstrap.defaultConfig = Object.create(null); 779/** Placeholder: gets installed by the first call to 780 self.sqlite3ApiBootstrap(). */ 781self.sqlite3ApiBootstrap.sqlite3 = undefined; 782