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   This global symbol is is only a temporary measure: the JS-side
82   post-processing will remove that object from the global scope when
83   setup is complete. We require it there temporarily in order to glue
84   disparate parts together during the loading of the API (which spans
85   several components).
86
87   This function requires 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 Emscripten.
90   (That said, OO API #1 requires, as of this writing, Emscripten's
91   virtual filesystem API. Baby steps.)
92*/
93self.sqlite3ApiBootstrap = function(config){
94  'use strict';
95
96  /** Throws a new Error, the message of which is the concatenation
97      all args with a space between each. */
98  const toss = (...args)=>{throw new Error(args.join(' '))};
99
100  /**
101     Returns true if n is a 32-bit (signed) integer, else
102     false. This is used for determining when we need to switch to
103     double-type DB operations for integer values in order to keep
104     more precision.
105  */
106  const isInt32 = function(n){
107    return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/)
108      && !!(n===(n|0) && n<=2147483647 && n>=-2147483648);
109  };
110
111  /** Returns v if v appears to be a TypedArray, else false. */
112  const isTypedArray = (v)=>{
113    return (v && v.constructor && isInt32(v.constructor.BYTES_PER_ELEMENT)) ? v : false;
114  };
115
116  /**
117     Returns true if v appears to be one of our bind()-able
118     TypedArray types: Uint8Array or Int8Array. Support for
119     TypedArrays with element sizes >1 is TODO.
120  */
121  const isBindableTypedArray = (v)=>{
122    return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT);
123  };
124
125  /**
126     Returns true if v appears to be one of the TypedArray types
127     which is legal for holding SQL code (as opposed to binary blobs).
128
129     Currently this is the same as isBindableTypedArray() but it
130     seems likely that we'll eventually want to add Uint32Array
131     and friends to the isBindableTypedArray() list but not to the
132     isSQLableTypedArray() list.
133  */
134  const isSQLableTypedArray = (v)=>{
135    return v && v.constructor && (1===v.constructor.BYTES_PER_ELEMENT);
136  };
137
138  /** Returns true if isBindableTypedArray(v) does, else throws with a message
139      that v is not a supported TypedArray value. */
140  const affirmBindableTypedArray = (v)=>{
141    return isBindableTypedArray(v)
142      || toss("Value is not of a supported TypedArray type.");
143  };
144
145  const utf8Decoder = new TextDecoder('utf-8');
146  const typedArrayToString = (str)=>utf8Decoder.decode(str);
147
148  /**
149     An Error subclass specifically for reporting Wasm-level malloc()
150     failure and enabling clients to unambiguously identify such
151     exceptions.
152  */
153  class WasmAllocError extends Error {
154    constructor(...args){
155      super(...args);
156      this.name = 'WasmAllocError';
157    }
158  };
159
160  /**
161      The main sqlite3 binding API gets installed into this object,
162      mimicking the C API as closely as we can. The numerous members
163      names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as
164      possible, identically to the C-native counterparts, as documented at:
165
166      https://www.sqlite.org/c3ref/intro.html
167
168      A very few exceptions require an additional level of proxy
169      function or may otherwise require special attention in the WASM
170      environment, and all such cases are document here. Those not
171      documented here are installed as 1-to-1 proxies for their C-side
172      counterparts.
173  */
174  const capi = {
175    /**
176       An Error subclass which is thrown by this object's alloc() method
177       on OOM.
178    */
179    WasmAllocError: WasmAllocError,
180    /**
181       The API's one single point of access to the WASM-side memory
182       allocator. Works like malloc(3) (and is likely bound to
183       malloc()) but throws an WasmAllocError if allocation fails. It is
184       important that any code which might pass through the sqlite3 C
185       API NOT throw and must instead return SQLITE_NOMEM (or
186       equivalent, depending on the context).
187
188       That said, very few cases in the API can result in
189       client-defined functions propagating exceptions via the C-style
190       API. Most notably, this applies ot User-defined SQL Functions
191       (UDFs) registered via sqlite3_create_function_v2(). For that
192       specific case it is recommended that all UDF creation be
193       funneled through a utility function and that a wrapper function
194       be added around the UDF which catches any exception and sets
195       the error state to OOM. (The overall complexity of registering
196       UDFs essentially requires a helper for doing so!)
197    */
198    alloc: undefined/*installed later*/,
199    /**
200       The API's one single point of access to the WASM-side memory
201       deallocator. Works like free(3) (and is likely bound to
202       free()).
203    */
204    dealloc: undefined/*installed later*/,
205    /**
206       When using sqlite3_open_v2() it is important to keep the following
207       in mind:
208
209       https://www.sqlite.org/c3ref/open.html
210
211       - The flags for use with its 3rd argument are installed in this
212       object using the C-cide names, e.g. SQLITE_OPEN_CREATE.
213
214       - If the combination of flags passed to it are invalid,
215       behavior is undefined. Thus is is never okay to call this
216       with fewer than 3 arguments, as JS will default the
217       missing arguments to `undefined`, which will result in a
218       flag value of 0. Most of the available SQLITE_OPEN_xxx
219       flags are meaningless in the WASM build, e.g. the mutext-
220       and cache-related flags, but they are retained in this
221       API for consistency's sake.
222
223       - The final argument to this function specifies the VFS to
224       use, which is largely (but not entirely!) meaningless in
225       the WASM environment. It should always be null or
226       undefined, and it is safe to elide that argument when
227       calling this function.
228    */
229    sqlite3_open_v2: function(filename,dbPtrPtr,flags,vfsStr){}/*installed later*/,
230    /**
231       The sqlite3_prepare_v3() binding handles two different uses
232       with differing JS/WASM semantics:
233
234       1) sqlite3_prepare_v3(pDb, sqlString, -1, prepFlags, ppStmt [, null])
235
236       2) sqlite3_prepare_v3(pDb, sqlPointer, sqlByteLen, prepFlags, ppStmt, sqlPointerToPointer)
237
238       Note that the SQL length argument (the 3rd argument) must, for
239       usage (1), always be negative because it must be a byte length
240       and that value is expensive to calculate from JS (where only
241       the character length of strings is readily available). It is
242       retained in this API's interface for code/documentation
243       compatibility reasons but is currently _always_ ignored. With
244       usage (2), the 3rd argument is used as-is but is is still
245       critical that the C-style input string (2nd argument) be
246       terminated with a 0 byte.
247
248       In usage (1), the 2nd argument must be of type string,
249       Uint8Array, or Int8Array (either of which is assumed to
250       hold SQL). If it is, this function assumes case (1) and
251       calls the underyling C function with the equivalent of:
252
253       (pDb, sqlAsString, -1, prepFlags, ppStmt, null)
254
255       The pzTail argument is ignored in this case because its result
256       is meaningless when a string-type value is passed through
257       (because the string goes through another level of internal
258       conversion for WASM's sake and the result pointer would refer
259       to that transient conversion's memory, not the passed-in
260       string).
261
262       If the sql argument is not a string, it must be a _pointer_ to
263       a NUL-terminated string which was allocated in the WASM memory
264       (e.g. using cwapi.wasm.alloc() or equivalent). In that case,
265       the final argument may be 0/null/undefined or must be a pointer
266       to which the "tail" of the compiled SQL is written, as
267       documented for the C-side sqlite3_prepare_v3(). In case (2),
268       the underlying C function is called with the equivalent of:
269
270       (pDb, sqlAsPointer, (sqlByteLen||-1), prepFlags, ppStmt, pzTail)
271
272       It returns its result and compiled statement as documented in
273       the C API. Fetching the output pointers (5th and 6th
274       parameters) requires using capi.wasm.getMemValue() (or
275       equivalent) and the pzTail will point to an address relative to
276       the sqlAsPointer value.
277
278       If passed an invalid 2nd argument type, this function will
279       return SQLITE_MISUSE but will unfortunately be able to return
280       any additional error information because we have no way to set
281       the db's error state such that this function could return a
282       non-0 integer and the client could call sqlite3_errcode() or
283       sqlite3_errmsg() to fetch it. See the RFE at:
284
285       https://sqlite.org/forum/forumpost/f9eb79b11aefd4fc81d
286
287       The alternative would be to throw an exception for that case,
288       but that would be in strong constrast to the rest of the
289       C-level API and seems likely to cause more confusion.
290
291       Side-note: in the C API the function does not fail if provided
292       an empty string but its result output pointer will be NULL.
293    */
294    sqlite3_prepare_v3: function(dbPtr, sql, sqlByteLen, prepFlags,
295                                 stmtPtrPtr, strPtrPtr){}/*installed later*/,
296
297    /**
298       Equivalent to calling sqlite3_prapare_v3() with 0 as its 4th argument.
299    */
300    sqlite3_prepare_v2: function(dbPtr, sql, sqlByteLen, stmtPtrPtr,
301                                 strPtrPtr){}/*installed later*/,
302
303    /**
304       Various internal-use utilities are added here as needed. They
305       are bound to an object only so that we have access to them in
306       the differently-scoped steps of the API bootstrapping
307       process. At the end of the API setup process, this object gets
308       removed.
309    */
310    util:{
311      isInt32, isTypedArray, isBindableTypedArray, isSQLableTypedArray,
312      affirmBindableTypedArray, typedArrayToString
313    },
314
315    /**
316       Holds state which are specific to the WASM-related
317       infrastructure and glue code. It is not expected that client
318       code will normally need these, but they're exposed here in case
319       it does. These APIs are _not_ to be considered an
320       official/stable part of the sqlite3 WASM API. They may change
321       as the developers' experience suggests appropriate changes.
322
323       Note that a number of members of this object are injected
324       dynamically after the api object is fully constructed, so
325       not all are documented inline here.
326    */
327    wasm: {
328    //^^^ TODO?: move wasm from sqlite3.capi.wasm to sqlite3.wasm
329      /**
330         Emscripten APIs have a deep-seated assumption that all pointers
331         are 32 bits. We'll remain optimistic that that won't always be
332         the case and will use this constant in places where we might
333         otherwise use a hard-coded 4.
334      */
335      ptrSizeof: config.wasmPtrSizeof || 4,
336      /**
337         The WASM IR (Intermediate Representation) value for
338         pointer-type values. It MUST refer to a value type of the
339         size described by this.ptrSizeof _or_ it may be any value
340         which ends in '*', which Emscripten's glue code internally
341         translates to i32.
342      */
343      ptrIR: config.wasmPtrIR || "i32",
344      /**
345         True if BigInt support was enabled via (e.g.) the
346         Emscripten -sWASM_BIGINT flag, else false. When
347         enabled, certain 64-bit sqlite3 APIs are enabled which
348         are not otherwise enabled due to JS/WASM int64
349         impedence mismatches.
350      */
351      bigIntEnabled: !!config.bigIntEnabled,
352      /**
353         The symbols exported by the WASM environment.
354      */
355      exports: config.exports
356        || toss("Missing API config.exports (WASM module exports)."),
357
358      /**
359         When Emscripten compiles with `-sIMPORT_MEMORY`, it
360         initalizes the heap and imports it into wasm, as opposed to
361         the other way around. In this case, the memory is not
362         available via this.exports.memory.
363      */
364      memory: config.memory || config.exports['memory']
365        || toss("API config object requires a WebAssembly.Memory object",
366                "in either config.exports.memory (exported)",
367                "or config.memory (imported)."),
368      /* Many more wasm-related APIs get installed later on. */
369    }/*wasm*/
370  }/*capi*/;
371
372  /**
373     capi.wasm.alloc()'s srcTypedArray.byteLength bytes,
374     populates them with the values from the source
375     TypedArray, and returns the pointer to that memory. The
376     returned pointer must eventually be passed to
377     capi.wasm.dealloc() to clean it up.
378
379     As a special case, to avoid further special cases where
380     this is used, if srcTypedArray.byteLength is 0, it
381     allocates a single byte and sets it to the value
382     0. Even in such cases, calls must behave as if the
383     allocated memory has exactly srcTypedArray.byteLength
384     bytes.
385
386     ACHTUNG: this currently only works for Uint8Array and
387     Int8Array types and will throw if srcTypedArray is of
388     any other type.
389  */
390  capi.wasm.mallocFromTypedArray = function(srcTypedArray){
391    affirmBindableTypedArray(srcTypedArray);
392    const pRet = this.alloc(srcTypedArray.byteLength || 1);
393    this.heapForSize(srcTypedArray.constructor).set(srcTypedArray.byteLength ? srcTypedArray : [0], pRet);
394    return pRet;
395  }.bind(capi.wasm);
396
397  const keyAlloc = config.allocExportName || 'malloc',
398        keyDealloc =  config.deallocExportName || 'free';
399  for(const key of [keyAlloc, keyDealloc]){
400    const f = capi.wasm.exports[key];
401    if(!(f instanceof Function)) toss("Missing required exports[",key,"] function.");
402  }
403  capi.wasm.alloc = function(n){
404    const m = this.exports[keyAlloc](n);
405    if(!m) throw new WasmAllocError("Failed to allocate "+n+" bytes.");
406    return m;
407  }.bind(capi.wasm)
408  capi.wasm.dealloc = (m)=>capi.wasm.exports[keyDealloc](m);
409
410  /**
411     Reports info about compile-time options using
412     sqlite_compileoption_get() and sqlite3_compileoption_used(). It
413     has several distinct uses:
414
415     If optName is an array then it is expected to be a list of
416     compilation options and this function returns an object
417     which maps each such option to true or false, indicating
418     whether or not the given option was included in this
419     build. That object is returned.
420
421     If optName is an object, its keys are expected to be compilation
422     options and this function sets each entry to true or false,
423     indicating whether the compilation option was used or not. That
424     object is returned.
425
426     If passed no arguments then it returns an object mapping
427     all known compilation options to their compile-time values,
428     or boolean true if they are defined with no value. This
429     result, which is relatively expensive to compute, is cached
430     and returned for future no-argument calls.
431
432     In all other cases it returns true if the given option was
433     active when when compiling the sqlite3 module, else false.
434
435     Compile-time option names may optionally include their
436     "SQLITE_" prefix. When it returns an object of all options,
437     the prefix is elided.
438  */
439  capi.wasm.compileOptionUsed = function f(optName){
440    if(!arguments.length){
441      if(f._result) return f._result;
442      else if(!f._opt){
443        f._rx = /^([^=]+)=(.+)/;
444        f._rxInt = /^-?\d+$/;
445        f._opt = function(opt, rv){
446          const m = f._rx.exec(opt);
447          rv[0] = (m ? m[1] : opt);
448          rv[1] = m ? (f._rxInt.test(m[2]) ? +m[2] : m[2]) : true;
449        };
450      }
451      const rc = {}, ov = [0,0];
452      let i = 0, k;
453      while((k = capi.sqlite3_compileoption_get(i++))){
454        f._opt(k,ov);
455        rc[ov[0]] = ov[1];
456      }
457      return f._result = rc;
458    }else if(Array.isArray(optName)){
459      const rc = {};
460      optName.forEach((v)=>{
461        rc[v] = capi.sqlite3_compileoption_used(v);
462      });
463      return rc;
464    }else if('object' === typeof optName){
465      Object.keys(optName).forEach((k)=> {
466        optName[k] = capi.sqlite3_compileoption_used(k);
467      });
468      return optName;
469    }
470    return (
471      'string'===typeof optName
472    ) ? !!capi.sqlite3_compileoption_used(optName) : false;
473  }/*compileOptionUsed()*/;
474
475  capi.wasm.bindingSignatures = [
476    /**
477       Signatures for the WASM-exported C-side functions. Each entry
478       is an array with 2+ elements:
479
480       ["c-side name",
481        "result type" (capi.wasm.xWrap() syntax),
482         [arg types in xWrap() syntax]
483         // ^^^ this needn't strictly be an array: it can be subsequent
484         // elements instead: [x,y,z] is equivalent to x,y,z
485       ]
486    */
487    // Please keep these sorted by function name!
488    ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"],
489    ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
490    ["sqlite3_bind_int","int", "sqlite3_stmt*", "int", "int"],
491    ["sqlite3_bind_null",undefined, "sqlite3_stmt*", "int"],
492    ["sqlite3_bind_parameter_count", "int", "sqlite3_stmt*"],
493    ["sqlite3_bind_parameter_index","int", "sqlite3_stmt*", "string"],
494    ["sqlite3_bind_text","int", "sqlite3_stmt*", "int", "string", "int", "int"],
495    ["sqlite3_close_v2", "int", "sqlite3*"],
496    ["sqlite3_changes", "int", "sqlite3*"],
497    ["sqlite3_clear_bindings","int", "sqlite3_stmt*"],
498    ["sqlite3_column_blob","*", "sqlite3_stmt*", "int"],
499    ["sqlite3_column_bytes","int", "sqlite3_stmt*", "int"],
500    ["sqlite3_column_count", "int", "sqlite3_stmt*"],
501    ["sqlite3_column_double","f64", "sqlite3_stmt*", "int"],
502    ["sqlite3_column_int","int", "sqlite3_stmt*", "int"],
503    ["sqlite3_column_name","string", "sqlite3_stmt*", "int"],
504    ["sqlite3_column_text","string", "sqlite3_stmt*", "int"],
505    ["sqlite3_column_type","int", "sqlite3_stmt*", "int"],
506    ["sqlite3_compileoption_get", "string", "int"],
507    ["sqlite3_compileoption_used", "int", "string"],
508    ["sqlite3_create_function_v2", "int",
509     "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"],
510    ["sqlite3_data_count", "int", "sqlite3_stmt*"],
511    ["sqlite3_db_filename", "string", "sqlite3*", "string"],
512    ["sqlite3_db_name", "string", "sqlite3*", "int"],
513    ["sqlite3_errmsg", "string", "sqlite3*"],
514    ["sqlite3_error_offset", "int", "sqlite3*"],
515    ["sqlite3_errstr", "string", "int"],
516    //["sqlite3_exec", "int", "sqlite3*", "string", "*", "*", "**"],
517    // ^^^ TODO: we need a wrapper to support passing a function pointer or a function
518    // for the callback.
519    ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
520    ["sqlite3_extended_errcode", "int", "sqlite3*"],
521    ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
522    ["sqlite3_finalize", "int", "sqlite3_stmt*"],
523    ["sqlite3_initialize", undefined],
524    ["sqlite3_interrupt", undefined, "sqlite3*"
525     /* ^^^ we cannot actually currently support this because JS is
526        single-threaded and we don't have a portable way to access a DB
527        from 2 SharedWorkers concurrently. */],
528    ["sqlite3_libversion", "string"],
529    ["sqlite3_libversion_number", "int"],
530    ["sqlite3_open", "int", "string", "*"],
531    ["sqlite3_open_v2", "int", "string", "*", "int", "string"],
532    /* sqlite3_prepare_v2() and sqlite3_prepare_v3() are handled
533       separately due to us requiring two different sets of semantics
534       for those, depending on how their SQL argument is provided. */
535    ["sqlite3_reset", "int", "sqlite3_stmt*"],
536    ["sqlite3_result_blob",undefined, "*", "*", "int", "*"],
537    ["sqlite3_result_double",undefined, "*", "f64"],
538    ["sqlite3_result_error",undefined, "*", "string", "int"],
539    ["sqlite3_result_error_code", undefined, "*", "int"],
540    ["sqlite3_result_error_nomem", undefined, "*"],
541    ["sqlite3_result_error_toobig", undefined, "*"],
542    ["sqlite3_result_int",undefined, "*", "int"],
543    ["sqlite3_result_null",undefined, "*"],
544    ["sqlite3_result_text",undefined, "*", "string", "int", "*"],
545    ["sqlite3_sourceid", "string"],
546    ["sqlite3_sql", "string", "sqlite3_stmt*"],
547    ["sqlite3_step", "int", "sqlite3_stmt*"],
548    ["sqlite3_strglob", "int", "string","string"],
549    ["sqlite3_strlike", "int", "string","string","int"],
550    ["sqlite3_total_changes", "int", "sqlite3*"],
551    ["sqlite3_value_blob", "*", "*"],
552    ["sqlite3_value_bytes","int", "*"],
553    ["sqlite3_value_double","f64", "*"],
554    ["sqlite3_value_text", "string", "*"],
555    ["sqlite3_value_type", "int", "*"],
556    ["sqlite3_vfs_find", "*", "string"],
557    ["sqlite3_vfs_register", "int", "*", "int"]
558  ]/*capi.wasm.bindingSignatures*/;
559
560  if(false && capi.wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
561    /* ^^^ "the problem" is that this is an option feature and the
562       build-time function-export list does not currently take
563       optional features into account. */
564    capi.wasm.bindingSignatures.push(["sqlite3_normalized_sql", "string", "sqlite3_stmt*"]);
565  }
566
567  /**
568     Functions which require BigInt (int64) support are separated from
569     the others because we need to conditionally bind them or apply
570     dummy impls, depending on the capabilities of the environment.
571  */
572  capi.wasm.bindingSignatures.int64 = [
573      ["sqlite3_bind_int64","int", ["sqlite3_stmt*", "int", "i64"]],
574      ["sqlite3_changes64","i64", ["sqlite3*"]],
575      ["sqlite3_column_int64","i64", ["sqlite3_stmt*", "int"]],
576      ["sqlite3_total_changes64", "i64", ["sqlite3*"]]
577  ];
578
579  /* The remainder of the API will be set up in later steps. */
580  return {
581    capi,
582    postInit: [
583      /* some pieces of the API may install functions into this array,
584         and each such function will be called, passed (self,sqlite3),
585         at the very end of the API load/init process, where self is
586         the current global object and sqlite3 is the object returned
587         from sqlite3ApiBootstrap(). This array will be removed at the
588         end of the API setup process. */],
589    /** Config is needed downstream for gluing pieces together. It
590        will be removed at the end of the API setup process. */
591    config
592  };
593}/*sqlite3ApiBootstrap()*/;
594