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