1/*
2  2022-07-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 contains the so-called OO #1 API wrapper for the sqlite3
14  WASM build. It requires that sqlite3-api-glue.js has already run
15  and it installs its deliverable as self.sqlite3.oo1.
16*/
17self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
18  const toss = (...args)=>{throw new Error(args.join(' '))};
19  const toss3 = (...args)=>{throw new sqlite3.SQLite3Error(...args)};
20
21  const capi = sqlite3.capi, wasm = capi.wasm, util = capi.util;
22  /* What follows is colloquially known as "OO API #1". It is a
23     binding of the sqlite3 API which is designed to be run within
24     the same thread (main or worker) as the one in which the
25     sqlite3 WASM binding was initialized. This wrapper cannot use
26     the sqlite3 binding if, e.g., the wrapper is in the main thread
27     and the sqlite3 API is in a worker. */
28
29  /**
30     In order to keep clients from manipulating, perhaps
31     inadvertently, the underlying pointer values of DB and Stmt
32     instances, we'll gate access to them via the `pointer` property
33     accessor and store their real values in this map. Keys = DB/Stmt
34     objects, values = pointer values. This also unifies how those are
35     accessed, for potential use downstream via custom
36     wasm.xWrap() function signatures which know how to extract
37     it.
38  */
39  const __ptrMap = new WeakMap();
40  /**
41     Map of DB instances to objects, each object being a map of UDF
42     names to wasm function _pointers_ added to that DB handle via
43     createFunction().
44  */
45  const __udfMap = new WeakMap();
46  /**
47     Map of DB instances to objects, each object being a map of Stmt
48     wasm pointers to Stmt objects.
49  */
50  const __stmtMap = new WeakMap();
51
52  /** If object opts has _its own_ property named p then that
53      property's value is returned, else dflt is returned. */
54  const getOwnOption = (opts, p, dflt)=>
55        opts.hasOwnProperty(p) ? opts[p] : dflt;
56
57  // Documented in DB.checkRc()
58  const checkSqlite3Rc = function(dbPtr, sqliteResultCode){
59    if(sqliteResultCode){
60      if(dbPtr instanceof DB) dbPtr = dbPtr.pointer;
61      toss3(
62        "sqlite result code",sqliteResultCode+":",
63        (dbPtr
64         ? capi.sqlite3_errmsg(dbPtr)
65         : capi.sqlite3_errstr(sqliteResultCode))
66      );
67    }
68  };
69
70  /**
71     A proxy for DB class constructors. It must be called with the
72     being-construct DB object as its "this". See the DB constructor
73     for the argument docs. This is split into a separate function
74     in order to enable simple creation of special-case DB constructors,
75     e.g. JsStorageDB and OpfsDB.
76
77     Expects to be passed a configuration object with the following
78     properties:
79
80     - `.filename`: the db filename. It may be a special name like ":memory:"
81       or "".
82
83     - `.flags`: as documented in the DB constructor.
84
85     - `.vfs`: as documented in the DB constructor.
86
87     It also accepts those as the first 3 arguments.
88  */
89  const dbCtorHelper = function ctor(...args){
90    if(!ctor._name2vfs){
91      /**
92         Map special filenames which we handle here (instead of in C)
93         to some helpful metadata...
94
95         As of 2022-09-20, the C API supports the names :localStorage:
96         and :sessionStorage: for kvvfs. However, C code cannot
97         determine (without embedded JS code, e.g. via Emscripten's
98         EM_JS()) whether the kvvfs is legal in the current browser
99         context (namely the main UI thread). In order to help client
100         code fail early on, instead of it being delayed until they
101         try to read or write a kvvfs-backed db, we'll check for those
102         names here and throw if they're not legal in the current
103         context.
104      */
105      ctor._name2vfs = Object.create(null);
106      const isWorkerThread = ('function'===typeof importScripts/*===running in worker thread*/)
107            ? (n)=>toss3("The VFS for",n,"is only available in the main window thread.")
108            : false;
109      ctor._name2vfs[':localStorage:'] = {
110        vfs: 'kvvfs',
111        filename: isWorkerThread || (()=>'local')
112      };
113      ctor._name2vfs[':sessionStorage:'] = {
114        vfs: 'kvvfs',
115        filename: isWorkerThread || (()=>'session')
116      };
117    }
118    const opt = ctor.normalizeArgs(...args);
119    let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
120    if(('string'!==typeof fn && 'number'!==typeof fn)
121       || 'string'!==typeof flagsStr
122       || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){
123      console.error("Invalid DB ctor args",opt,arguments);
124      toss3("Invalid arguments for DB constructor.");
125    }
126    let fnJs = ('number'===typeof fn) ? wasm.cstringToJs(fn) : fn;
127    const vfsCheck = ctor._name2vfs[fnJs];
128    if(vfsCheck){
129      vfsName = vfsCheck.vfs;
130      fn = fnJs = vfsCheck.filename(fnJs);
131    }
132    let ptr, oflags = 0;
133    if( flagsStr.indexOf('c')>=0 ){
134      oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
135    }
136    if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
137    if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
138    oflags |= capi.SQLITE_OPEN_EXRESCODE;
139    const scope = wasm.scopedAllocPush();
140    try {
141      const ppDb = wasm.allocPtr() /* output (sqlite3**) arg */;
142      const pVfsName = vfsName ? (
143        ('number'===typeof vfsName ? vfsName : wasm.scopedAllocCString(vfsName))
144      ): 0;
145      const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
146      ptr = wasm.getPtrValue(ppDb);
147      checkSqlite3Rc(ptr, rc);
148    }catch( e ){
149      if( ptr ) capi.sqlite3_close_v2(ptr);
150      throw e;
151    }finally{
152      wasm.scopedAllocPop(scope);
153    }
154    this.filename = fnJs;
155    __ptrMap.set(this, ptr);
156    __stmtMap.set(this, Object.create(null));
157    __udfMap.set(this, Object.create(null));
158  };
159
160  /**
161     A helper for DB constructors. It accepts either a single
162     config-style object or up to 3 arguments (filename, dbOpenFlags,
163     dbVfsName). It returns a new object containing:
164
165     { filename: ..., flags: ..., vfs: ... }
166
167     If passed an object, any additional properties it has are copied
168     as-is into the new object.
169  */
170  dbCtorHelper.normalizeArgs = function(filename=':memory:',flags = 'c',vfs = null){
171    const arg = {};
172    if(1===arguments.length && 'object'===typeof arguments[0]){
173      const x = arguments[0];
174      Object.keys(x).forEach((k)=>arg[k] = x[k]);
175      if(undefined===arg.flags) arg.flags = 'c';
176      if(undefined===arg.vfs) arg.vfs = null;
177      if(undefined===arg.filename) arg.filename = ':memory:';
178    }else{
179      arg.filename = filename;
180      arg.flags = flags;
181      arg.vfs = vfs;
182    }
183    return arg;
184  };
185
186  /**
187     The DB class provides a high-level OO wrapper around an sqlite3
188     db handle.
189
190     The given db filename must be resolvable using whatever
191     filesystem layer (virtual or otherwise) is set up for the default
192     sqlite3 VFS.
193
194     Note that the special sqlite3 db names ":memory:" and ""
195     (temporary db) have their normal special meanings here and need
196     not resolve to real filenames, but "" uses an on-storage
197     temporary database and requires that the VFS support that.
198
199     The second argument specifies the open/create mode for the
200     database. It must be string containing a sequence of letters (in
201     any order, but case sensitive) specifying the mode:
202
203     - "c" => create if it does not exist, else fail if it does not
204       exist. Implies the "w" flag.
205
206     - "w" => write. Implies "r": a db cannot be write-only.
207
208     - "r" => read-only if neither "w" nor "c" are provided, else it
209       is ignored.
210
211     If "w" is not provided, the db is implicitly read-only, noting that
212     "rc" is meaningless
213
214     Any other letters are currently ignored. The default is
215     "c". These modes are ignored for the special ":memory:" and ""
216     names.
217
218     The final argument is analogous to the final argument of
219     sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value,
220     or none at all, to use the default. If passed a value, it must
221     be the string name of a VFS
222
223     The constructor optionally (and preferably) takes its arguments
224     in the form of a single configuration object with the following
225     properties:
226
227     - `.filename`: database file name
228     - `.flags`: open-mode flags
229     - `.vfs`: the VFS fname
230
231     The `filename` and `vfs` arguments may be either JS strings or
232     C-strings allocated via WASM.
233
234     For purposes of passing a DB instance to C-style sqlite3
235     functions, the DB object's read-only `pointer` property holds its
236     `sqlite3*` pointer value. That property can also be used to check
237     whether this DB instance is still open.
238
239
240     EXPERIMENTAL: in the main window thread, the filenames
241     ":localStorage:" and ":sessionStorage:" are special: they cause
242     the db to use either localStorage or sessionStorage for storing
243     the database. In this mode, only a single database is permitted
244     in each storage object. This feature is experimental and subject
245     to any number of changes (including outright removal). This
246     support requires the kvvfs sqlite3 VFS, the existence of which
247     can be determined at runtime by checking for a non-0 return value
248     from sqlite3.capi.sqlite3_vfs_find("kvvfs").
249  */
250  const DB = function(...args){
251    dbCtorHelper.apply(this, args);
252  };
253
254  /**
255     Internal-use enum for mapping JS types to DB-bindable types.
256     These do not (and need not) line up with the SQLITE_type
257     values. All values in this enum must be truthy and distinct
258     but they need not be numbers.
259  */
260  const BindTypes = {
261    null: 1,
262    number: 2,
263    string: 3,
264    boolean: 4,
265    blob: 5
266  };
267  BindTypes['undefined'] == BindTypes.null;
268  if(wasm.bigIntEnabled){
269    BindTypes.bigint = BindTypes.number;
270  }
271
272  /**
273     This class wraps sqlite3_stmt. Calling this constructor
274     directly will trigger an exception. Use DB.prepare() to create
275     new instances.
276
277     For purposes of passing a Stmt instance to C-style sqlite3
278     functions, its read-only `pointer` property holds its `sqlite3_stmt*`
279     pointer value.
280
281     Other non-function properties include:
282
283     - `db`: the DB object which created the statement.
284
285     - `columnCount`: the number of result columns in the query, or 0 for
286     queries which cannot return results.
287
288     - `parameterCount`: the number of bindable paramters in the query.
289  */
290  const Stmt = function(){
291    if(BindTypes!==arguments[2]){
292      toss3("Do not call the Stmt constructor directly. Use DB.prepare().");
293    }
294    this.db = arguments[0];
295    __ptrMap.set(this, arguments[1]);
296    this.columnCount = capi.sqlite3_column_count(this.pointer);
297    this.parameterCount = capi.sqlite3_bind_parameter_count(this.pointer);
298  };
299
300  /** Throws if the given DB has been closed, else it is returned. */
301  const affirmDbOpen = function(db){
302    if(!db.pointer) toss3("DB has been closed.");
303    return db;
304  };
305
306  /** Throws if ndx is not an integer or if it is out of range
307      for stmt.columnCount, else returns stmt.
308
309      Reminder: this will also fail after the statement is finalized
310      but the resulting error will be about an out-of-bounds column
311      index rather than a statement-is-finalized error.
312  */
313  const affirmColIndex = function(stmt,ndx){
314    if((ndx !== (ndx|0)) || ndx<0 || ndx>=stmt.columnCount){
315      toss3("Column index",ndx,"is out of range.");
316    }
317    return stmt;
318  };
319
320  /**
321     Expects to be passed the `arguments` object from DB.exec(). Does
322     the argument processing/validation, throws on error, and returns
323     a new object on success:
324
325     { sql: the SQL, opt: optionsObj, cbArg: function}
326
327     The opt object is a normalized copy of any passed to this
328     function. The sql will be converted to a string if it is provided
329     in one of the supported non-string formats.
330
331     cbArg is only set if the opt.callback or opt.resultRows are set,
332     in which case it's a function which expects to be passed the
333     current Stmt and returns the callback argument of the type
334     indicated by the input arguments.
335  */
336  const parseExecArgs = function(args){
337    const out = Object.create(null);
338    out.opt = Object.create(null);
339    switch(args.length){
340        case 1:
341          if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){
342            out.sql = args[0];
343          }else if(args[0] && 'object'===typeof args[0]){
344            out.opt = args[0];
345            out.sql = out.opt.sql;
346          }else if(Array.isArray(args[0])){
347            out.sql = args[0];
348          }
349          break;
350        case 2:
351          out.sql = args[0];
352          out.opt = args[1];
353          break;
354        default: toss3("Invalid argument count for exec().");
355    };
356    if(util.isSQLableTypedArray(out.sql)){
357      out.sql = util.typedArrayToString(out.sql);
358    }else if(Array.isArray(out.sql)){
359      out.sql = out.sql.join('');
360    }else if('string'!==typeof out.sql){
361      toss3("Missing SQL argument.");
362    }
363    if(out.opt.callback || out.opt.resultRows){
364      switch((undefined===out.opt.rowMode)
365             ? 'array' : out.opt.rowMode) {
366          case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break;
367          case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
368          case 'stmt':
369            if(Array.isArray(out.opt.resultRows)){
370              toss3("exec(): invalid rowMode for a resultRows array: must",
371                    "be one of 'array', 'object',",
372                    "a result column number, or column name reference.");
373            }
374            out.cbArg = (stmt)=>stmt;
375            break;
376          default:
377            if(util.isInt32(out.opt.rowMode)){
378              out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
379              break;
380            }else if('string'===typeof out.opt.rowMode && out.opt.rowMode.length>1){
381              /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */
382              const prefix = out.opt.rowMode[0];
383              if(':'===prefix || '@'===prefix || '$'===prefix){
384                out.cbArg = function(stmt){
385                  const rc = stmt.get(this.obj)[this.colName];
386                  return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc;
387                }.bind({
388                  obj:Object.create(null),
389                  colName: out.opt.rowMode.substr(1)
390                });
391                break;
392              }
393            }
394            toss3("Invalid rowMode:",out.opt.rowMode);
395      }
396    }
397    return out;
398  };
399
400  /**
401     Expects to be given a DB instance or an `sqlite3*` pointer (may
402     be null) and an sqlite3 API result code. If the result code is
403     not falsy, this function throws an SQLite3Error with an error
404     message from sqlite3_errmsg(), using dbPtr as the db handle, or
405     sqlite3_errstr() if dbPtr is falsy. Note that if it's passed a
406     non-error code like SQLITE_ROW or SQLITE_DONE, it will still
407     throw but the error string might be "Not an error."  The various
408     non-0 non-error codes need to be checked for in
409     client code where they are expected.
410  */
411  DB.checkRc = checkSqlite3Rc;
412
413  DB.prototype = {
414    /** Returns true if this db handle is open, else false. */
415    isOpen: function(){
416      return !!this.pointer;
417    },
418    /** Throws if this given DB has been closed, else returns `this`. */
419    affirmOpen: function(){
420      return affirmDbOpen(this);
421    },
422    /**
423       Finalizes all open statements and closes this database
424       connection. This is a no-op if the db has already been
425       closed. After calling close(), `this.pointer` will resolve to
426       `undefined`, so that can be used to check whether the db
427       instance is still opened.
428
429       If this.onclose.before is a function then it is called before
430       any close-related cleanup.
431
432       If this.onclose.after is a function then it is called after the
433       db is closed but before auxiliary state like this.filename is
434       cleared.
435
436       Both onclose handlers are passed this object. If this db is not
437       opened, neither of the handlers are called. Any exceptions the
438       handlers throw are ignored because "destructors must not
439       throw."
440
441       Note that garbage collection of a db handle, if it happens at
442       all, will never trigger close(), so onclose handlers are not a
443       reliable way to implement close-time cleanup or maintenance of
444       a db.
445    */
446    close: function(){
447      if(this.pointer){
448        if(this.onclose && (this.onclose.before instanceof Function)){
449          try{this.onclose.before(this)}
450          catch(e){/*ignore*/}
451        }
452        const pDb = this.pointer;
453        Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
454          if(s && s.pointer) s.finalize();
455        });
456        Object.values(__udfMap.get(this)).forEach(
457          wasm.uninstallFunction.bind(capi.wasm)
458        );
459        __ptrMap.delete(this);
460        __stmtMap.delete(this);
461        __udfMap.delete(this);
462        capi.sqlite3_close_v2(pDb);
463        if(this.onclose && (this.onclose.after instanceof Function)){
464          try{this.onclose.after(this)}
465          catch(e){/*ignore*/}
466        }
467        delete this.filename;
468      }
469    },
470    /**
471       Returns the number of changes, as per sqlite3_changes()
472       (if the first argument is false) or sqlite3_total_changes()
473       (if it's true). If the 2nd argument is true, it uses
474       sqlite3_changes64() or sqlite3_total_changes64(), which
475       will trigger an exception if this build does not have
476       BigInt support enabled.
477    */
478    changes: function(total=false,sixtyFour=false){
479      const p = affirmDbOpen(this).pointer;
480      if(total){
481        return sixtyFour
482          ? capi.sqlite3_total_changes64(p)
483          : capi.sqlite3_total_changes(p);
484      }else{
485        return sixtyFour
486          ? capi.sqlite3_changes64(p)
487          : capi.sqlite3_changes(p);
488      }
489    },
490    /**
491       Similar to the this.filename property but will return a falsy
492       value for special names like ":memory:". Throws if the DB has
493       been closed. If passed an argument it then it will return the
494       filename of the ATTACHEd db with that name, else it assumes a
495       name of `main`. The argument may be either a JS string or
496       a pointer to a WASM-allocated C-string.
497    */
498    getFilename: function(dbName='main'){
499      return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
500    },
501    /**
502       Returns true if this db instance has a name which resolves to a
503       file. If the name is "" or starts with ":", it resolves to false.
504       Note that it is not aware of the peculiarities of URI-style
505       names and a URI-style name for a ":memory:" db will fool it.
506       Returns false if this db is closed.
507    */
508    hasFilename: function(){
509      return this.filename && ':'!==this.filename[0];
510    },
511    /**
512       Returns the name of the given 0-based db number, as documented
513       for sqlite3_db_name().
514    */
515    dbName: function(dbNumber=0){
516      return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber);
517    },
518    /**
519       Compiles the given SQL and returns a prepared Stmt. This is
520       the only way to create new Stmt objects. Throws on error.
521
522       The given SQL must be a string, a Uint8Array holding SQL, a
523       WASM pointer to memory holding the NUL-terminated SQL string,
524       or an array of strings. In the latter case, the array is
525       concatenated together, with no separators, to form the SQL
526       string (arrays are often a convenient way to formulate long
527       statements).  If the SQL contains no statements, an
528       SQLite3Error is thrown.
529
530       Design note: the C API permits empty SQL, reporting it as a 0
531       result code and a NULL stmt pointer. Supporting that case here
532       would cause extra work for all clients: any use of the Stmt API
533       on such a statement will necessarily throw, so clients would be
534       required to check `stmt.pointer` after calling `prepare()` in
535       order to determine whether the Stmt instance is empty or not.
536       Long-time practice (with other sqlite3 script bindings)
537       suggests that the empty-prepare case is sufficiently rare that
538       supporting it here would simply hurt overall usability.
539    */
540    prepare: function(sql){
541      affirmDbOpen(this);
542      const stack = wasm.pstack.pointer;
543      let ppStmt, pStmt;
544      try{
545        ppStmt = wasm.pstack.alloc(8)/* output (sqlite3_stmt**) arg */;
546        DB.checkRc(this, capi.sqlite3_prepare_v2(this.pointer, sql, -1, ppStmt, null));
547        pStmt = wasm.getPtrValue(ppStmt);
548      }
549      finally {
550        wasm.pstack.restore(stack);
551      }
552      if(!pStmt) toss3("Cannot prepare empty SQL.");
553      const stmt = new Stmt(this, pStmt, BindTypes);
554      __stmtMap.get(this)[pStmt] = stmt;
555      return stmt;
556    },
557    /**
558       Executes one or more SQL statements in the form of a single
559       string. Its arguments must be either (sql,optionsObject) or
560       (optionsObject). In the latter case, optionsObject.sql
561       must contain the SQL to execute. Returns this
562       object. Throws on error.
563
564       If no SQL is provided, or a non-string is provided, an
565       exception is triggered. Empty SQL, on the other hand, is
566       simply a no-op.
567
568       The optional options object may contain any of the following
569       properties:
570
571       - `.sql` = the SQL to run (unless it's provided as the first
572       argument). This must be of type string, Uint8Array, or an array
573       of strings. In the latter case they're concatenated together
574       as-is, _with no separator_ between elements, before evaluation.
575       The array form is often simpler for long hand-written queries.
576
577       - `.bind` = a single value valid as an argument for
578       Stmt.bind(). This is _only_ applied to the _first_ non-empty
579       statement in the SQL which has any bindable parameters. (Empty
580       statements are skipped entirely.)
581
582       - `.saveSql` = an optional array. If set, the SQL of each
583       executed statement is appended to this array before the
584       statement is executed (but after it is prepared - we don't have
585       the string until after that). Empty SQL statements are elided.
586
587       ==================================================================
588       The following options apply _only_ to the _first_ statement
589       which has a non-zero result column count, regardless of whether
590       the statement actually produces any result rows.
591       ==================================================================
592
593       - `.columnNames`: if this is an array, the column names of the
594       result set are stored in this array before the callback (if
595       any) is triggered (regardless of whether the query produces any
596       result rows). If no statement has result columns, this value is
597       unchanged. Achtung: an SQL result may have multiple columns
598       with identical names.
599
600       - `.callback` = a function which gets called for each row of
601       the result set, but only if that statement has any result
602       _rows_. The callback's "this" is the options object, noting
603       that this function synthesizes one if the caller does not pass
604       one to exec(). The second argument passed to the callback is
605       always the current Stmt object, as it's needed if the caller
606       wants to fetch the column names or some such (noting that they
607       could also be fetched via `this.columnNames`, if the client
608       provides the `columnNames` option).
609
610       ACHTUNG: The callback MUST NOT modify the Stmt object. Calling
611       any of the Stmt.get() variants, Stmt.getColumnName(), or
612       similar, is legal, but calling step() or finalize() is
613       not. Member methods which are illegal in this context will
614       trigger an exception.
615
616       The first argument passed to the callback defaults to an array of
617       values from the current result row but may be changed with ...
618
619       - `.rowMode` = specifies the type of he callback's first argument.
620       It may be any of...
621
622       A) A string describing what type of argument should be passed
623       as the first argument to the callback:
624
625         A.1) `'array'` (the default) causes the results of
626         `stmt.get([])` to be passed to the `callback` and/or appended
627         to `resultRows`.
628
629         A.2) `'object'` causes the results of
630         `stmt.get(Object.create(null))` to be passed to the
631         `callback` and/or appended to `resultRows`.  Achtung: an SQL
632         result may have multiple columns with identical names. In
633         that case, the right-most column will be the one set in this
634         object!
635
636         A.3) `'stmt'` causes the current Stmt to be passed to the
637         callback, but this mode will trigger an exception if
638         `resultRows` is an array because appending the statement to
639         the array would be downright unhelpful.
640
641       B) An integer, indicating a zero-based column in the result
642       row. Only that one single value will be passed on.
643
644       C) A string with a minimum length of 2 and leading character of
645       ':', '$', or '@' will fetch the row as an object, extract that
646       one field, and pass that field's value to the callback. Note
647       that these keys are case-sensitive so must match the case used
648       in the SQL. e.g. `"select a A from t"` with a `rowMode` of
649       `'$A'` would work but `'$a'` would not. A reference to a column
650       not in the result set will trigger an exception on the first
651       row (as the check is not performed until rows are fetched).
652       Note also that `$` is a legal identifier character in JS so
653       need not be quoted. (Design note: those 3 characters were
654       chosen because they are the characters support for naming bound
655       parameters.)
656
657       Any other `rowMode` value triggers an exception.
658
659       - `.resultRows`: if this is an array, it functions similarly to
660       the `callback` option: each row of the result set (if any),
661       with the exception that the `rowMode` 'stmt' is not legal. It
662       is legal to use both `resultRows` and `callback`, but
663       `resultRows` is likely much simpler to use for small data sets
664       and can be used over a WebWorker-style message interface.
665       exec() throws if `resultRows` is set and `rowMode` is 'stmt'.
666
667
668       Potential TODOs:
669
670       - `.bind`: permit an array of arrays/objects to bind. The first
671       sub-array would act on the first statement which has bindable
672       parameters (as it does now). The 2nd would act on the next such
673       statement, etc.
674
675       - `.callback` and `.resultRows`: permit an array entries with
676       semantics similar to those described for `.bind` above.
677
678    */
679    exec: function(/*(sql [,obj]) || (obj)*/){
680      affirmDbOpen(this);
681      const wasm = capi.wasm;
682      const arg = parseExecArgs(arguments);
683      if(!arg.sql){
684        return (''===arg.sql) ? this : toss3("exec() requires an SQL string.");
685      }
686      const opt = arg.opt;
687      const callback = opt.callback;
688      let resultRows = (Array.isArray(opt.resultRows)
689                          ? opt.resultRows : undefined);
690      let stmt;
691      let bind = opt.bind;
692      let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */;
693      const stack = wasm.scopedAllocPush();
694      try{
695        const isTA = util.isSQLableTypedArray(arg.sql)
696        /* Optimization: if the SQL is a TypedArray we can save some string
697           conversion costs. */;
698        /* Allocate the two output pointers (ppStmt, pzTail) and heap
699           space for the SQL (pSql). When prepare_v2() returns, pzTail
700           will point to somewhere in pSql. */
701        let sqlByteLen = isTA ? arg.sql.byteLength : wasm.jstrlen(arg.sql);
702        const ppStmt  = wasm.scopedAlloc(/* output (sqlite3_stmt**) arg and pzTail */
703          (2 * wasm.ptrSizeof)
704          + (sqlByteLen + 1/* SQL + NUL */));
705        const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */;
706        let pSql = pzTail + wasm.ptrSizeof;
707        const pSqlEnd = pSql + sqlByteLen;
708        if(isTA) wasm.heap8().set(arg.sql, pSql);
709        else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
710        wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/);
711        while(pSql && wasm.getMemValue(pSql, 'i8')
712              /* Maintenance reminder:^^^ _must_ be 'i8' or else we
713                 will very likely cause an endless loop. What that's
714                 doing is checking for a terminating NUL byte. If we
715                 use i32 or similar then we read 4 bytes, read stuff
716                 around the NUL terminator, and get stuck in and
717                 endless loop at the end of the SQL, endlessly
718                 re-preparing an empty statement. */ ){
719          wasm.setPtrValue(ppStmt, 0);
720          wasm.setPtrValue(pzTail, 0);
721          DB.checkRc(this, capi.sqlite3_prepare_v3(
722            this.pointer, pSql, sqlByteLen, 0, ppStmt, pzTail
723          ));
724          const pStmt = wasm.getPtrValue(ppStmt);
725          pSql = wasm.getPtrValue(pzTail);
726          sqlByteLen = pSqlEnd - pSql;
727          if(!pStmt) continue;
728          if(Array.isArray(opt.saveSql)){
729            opt.saveSql.push(capi.sqlite3_sql(pStmt).trim());
730          }
731          stmt = new Stmt(this, pStmt, BindTypes);
732          if(bind && stmt.parameterCount){
733            stmt.bind(bind);
734            bind = null;
735          }
736          if(evalFirstResult && stmt.columnCount){
737            /* Only forward SELECT results for the FIRST query
738               in the SQL which potentially has them. */
739            evalFirstResult = false;
740            if(Array.isArray(opt.columnNames)){
741              stmt.getColumnNames(opt.columnNames);
742            }
743            while(!!arg.cbArg && stmt.step()){
744              stmt._isLocked = true;
745              const row = arg.cbArg(stmt);
746              if(resultRows) resultRows.push(row);
747              if(callback) callback.apply(opt,[row,stmt]);
748              stmt._isLocked = false;
749            }
750          }else{
751            stmt.step();
752          }
753          stmt.finalize();
754          stmt = null;
755        }
756      }/*catch(e){
757        console.warn("DB.exec() is propagating exception",opt,e);
758        throw e;
759      }*/finally{
760        if(stmt){
761          delete stmt._isLocked;
762          stmt.finalize();
763        }
764        wasm.scopedAllocPop(stack);
765      }
766      return this;
767    }/*exec()*/,
768    /**
769       Creates a new scalar UDF (User-Defined Function) which is
770       accessible via SQL code. This function may be called in any
771       of the following forms:
772
773       - (name, function)
774       - (name, function, optionsObject)
775       - (name, optionsObject)
776       - (optionsObject)
777
778       In the final two cases, the function must be defined as the
779       'callback' property of the options object. In the final
780       case, the function's name must be the 'name' property.
781
782       This can only be used to create scalar functions, not
783       aggregate or window functions. UDFs cannot be removed from
784       a DB handle after they're added.
785
786       On success, returns this object. Throws on error.
787
788       When called from SQL, arguments to the UDF, and its result,
789       will be converted between JS and SQL with as much fidelity
790       as is feasible, triggering an exception if a type
791       conversion cannot be determined. Some freedom is afforded
792       to numeric conversions due to friction between the JS and C
793       worlds: integers which are larger than 32 bits will be
794       treated as doubles, as JS does not support 64-bit integers
795       and it is (as of this writing) illegal to use WASM
796       functions which take or return 64-bit integers from JS.
797
798       The optional options object may contain flags to modify how
799       the function is defined:
800
801       - .arity: the number of arguments which SQL calls to this
802       function expect or require. The default value is the
803       callback's length property (i.e. the number of declared
804       parameters it has). A value of -1 means that the function
805       is variadic and may accept any number of arguments, up to
806       sqlite3's compile-time limits. sqlite3 will enforce the
807       argument count if is zero or greater.
808
809       The following properties correspond to flags documented at:
810
811       https://sqlite.org/c3ref/create_function.html
812
813       - .deterministic = SQLITE_DETERMINISTIC
814       - .directOnly = SQLITE_DIRECTONLY
815       - .innocuous = SQLITE_INNOCUOUS
816
817       Maintenance reminder: the ability to add new
818       WASM-accessible functions to the runtime requires that the
819       WASM build is compiled with emcc's `-sALLOW_TABLE_GROWTH`
820       flag.
821    */
822    createFunction: function f(name, callback,opt){
823      switch(arguments.length){
824          case 1: /* (optionsObject) */
825            opt = name;
826            name = opt.name;
827            callback = opt.callback;
828            break;
829          case 2: /* (name, callback|optionsObject) */
830            if(!(callback instanceof Function)){
831              opt = callback;
832              callback = opt.callback;
833            }
834            break;
835          default: break;
836      }
837      if(!opt) opt = {};
838      if(!(callback instanceof Function)){
839        toss3("Invalid arguments: expecting a callback function.");
840      }else if('string' !== typeof name){
841        toss3("Invalid arguments: missing function name.");
842      }
843      if(!f._extractArgs){
844        /* Static init */
845        f._extractArgs = function(argc, pArgv){
846          let i, pVal, valType, arg;
847          const tgt = [];
848          for(i = 0; i < argc; ++i){
849            pVal = wasm.getPtrValue(pArgv + (wasm.ptrSizeof * i));
850            /**
851               Curiously: despite ostensibly requiring 8-byte
852               alignment, the pArgv array is parcelled into chunks of
853               4 bytes (1 pointer each). The values those point to
854               have 8-byte alignment but the individual argv entries
855               do not.
856            */
857            valType = capi.sqlite3_value_type(pVal);
858            switch(valType){
859                case capi.SQLITE_INTEGER:
860                case capi.SQLITE_FLOAT:
861                  arg = capi.sqlite3_value_double(pVal);
862                  break;
863                case capi.SQLITE_TEXT:
864                  arg = capi.sqlite3_value_text(pVal);
865                  break;
866                case capi.SQLITE_BLOB:{
867                  const n = capi.sqlite3_value_bytes(pVal);
868                  const pBlob = capi.sqlite3_value_blob(pVal);
869                  arg = new Uint8Array(n);
870                  let i;
871                  const heap = n ? wasm.heap8() : false;
872                  for(i = 0; i < n; ++i) arg[i] = heap[pBlob+i];
873                  break;
874                }
875                case capi.SQLITE_NULL:
876                  arg = null; break;
877                default:
878                  toss3("Unhandled sqlite3_value_type()",valType,
879                        "is possibly indicative of incorrect",
880                        "pointer size assumption.");
881            }
882            tgt.push(arg);
883          }
884          return tgt;
885        }/*_extractArgs()*/;
886        f._setResult = function(pCx, val){
887          switch(typeof val) {
888              case 'boolean':
889                capi.sqlite3_result_int(pCx, val ? 1 : 0);
890                break;
891              case 'number': {
892                (util.isInt32(val)
893                 ? capi.sqlite3_result_int
894                 : capi.sqlite3_result_double)(pCx, val);
895                break;
896              }
897              case 'string':
898                capi.sqlite3_result_text(pCx, val, -1, capi.SQLITE_TRANSIENT);
899                break;
900              case 'object':
901                if(null===val) {
902                  capi.sqlite3_result_null(pCx);
903                  break;
904                }else if(util.isBindableTypedArray(val)){
905                  const pBlob = wasm.allocFromTypedArray(val);
906                  capi.sqlite3_result_blob(pCx, pBlob, val.byteLength,
907                                          capi.SQLITE_TRANSIENT);
908                  wasm.dealloc(pBlob);
909                  break;
910                }
911                // else fall through
912              default:
913                toss3("Don't not how to handle this UDF result value:",val);
914          };
915        }/*_setResult()*/;
916      }/*static init*/
917      const wrapper = function(pCx, argc, pArgv){
918        try{
919          f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv)));
920        }catch(e){
921          if(e instanceof capi.WasmAllocError){
922            capi.sqlite3_result_error_nomem(pCx);
923          }else{
924            capi.sqlite3_result_error(pCx, e.message, -1);
925          }
926        }
927      };
928      const pUdf = wasm.installFunction(wrapper, "v(iii)");
929      let fFlags = 0 /*flags for sqlite3_create_function_v2()*/;
930      if(getOwnOption(opt, 'deterministic')) fFlags |= capi.SQLITE_DETERMINISTIC;
931      if(getOwnOption(opt, 'directOnly')) fFlags |= capi.SQLITE_DIRECTONLY;
932      if(getOwnOption(opt, 'innocuous')) fFlags |= capi.SQLITE_INNOCUOUS;
933      name = name.toLowerCase();
934      try {
935        DB.checkRc(this, capi.sqlite3_create_function_v2(
936          this.pointer, name,
937          (opt.hasOwnProperty('arity') ? +opt.arity : callback.length),
938          capi.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf,
939          null/*xStep*/, null/*xFinal*/, null/*xDestroy*/));
940      }catch(e){
941        wasm.uninstallFunction(pUdf);
942        throw e;
943      }
944      const udfMap = __udfMap.get(this);
945      if(udfMap[name]){
946        try{wasm.uninstallFunction(udfMap[name])}
947        catch(e){/*ignore*/}
948      }
949      udfMap[name] = pUdf;
950      return this;
951    }/*createFunction()*/,
952    /**
953       Prepares the given SQL, step()s it one time, and returns
954       the value of the first result column. If it has no results,
955       undefined is returned.
956
957       If passed a second argument, it is treated like an argument
958       to Stmt.bind(), so may be any type supported by that
959       function. Passing the undefined value is the same as passing
960       no value, which is useful when...
961
962       If passed a 3rd argument, it is expected to be one of the
963       SQLITE_{typename} constants. Passing the undefined value is
964       the same as not passing a value.
965
966       Throws on error (e.g. malformed SQL).
967    */
968    selectValue: function(sql,bind,asType){
969      let stmt, rc;
970      try {
971        stmt = this.prepare(sql).bind(bind);
972        if(stmt.step()) rc = stmt.get(0,asType);
973      }finally{
974        if(stmt) stmt.finalize();
975      }
976      return rc;
977    },
978
979    /**
980       Returns the number of currently-opened Stmt handles for this db
981       handle, or 0 if this DB instance is closed.
982    */
983    openStatementCount: function(){
984      return this.pointer ? Object.keys(__stmtMap.get(this)).length : 0;
985    },
986
987    /**
988       Starts a transaction, calls the given callback, and then either
989       rolls back or commits the savepoint, depending on whether the
990       callback throws. The callback is passed this db object as its
991       only argument. On success, returns the result of the
992       callback. Throws on error.
993
994       Note that transactions may not be nested, so this will throw if
995       it is called recursively. For nested transactions, use the
996       savepoint() method or manually manage SAVEPOINTs using exec().
997     */
998    transaction: function(callback){
999      affirmDbOpen(this).exec("BEGIN");
1000      try {
1001        const rc = callback(this);
1002        this.exec("COMMIT");
1003        return rc;
1004      }catch(e){
1005        this.exec("ROLLBACK");
1006        throw e;
1007      }
1008    },
1009
1010    /**
1011       This works similarly to transaction() but uses sqlite3's SAVEPOINT
1012       feature. This function starts a savepoint (with an unspecified name)
1013       and calls the given callback function, passing it this db object.
1014       If the callback returns, the savepoint is released (committed). If
1015       the callback throws, the savepoint is rolled back. If it does not
1016       throw, it returns the result of the callback.
1017    */
1018    savepoint: function(callback){
1019      affirmDbOpen(this).exec("SAVEPOINT oo1");
1020      try {
1021        const rc = callback(this);
1022        this.exec("RELEASE oo1");
1023        return rc;
1024      }catch(e){
1025        this.exec("ROLLBACK to SAVEPOINT oo1; RELEASE SAVEPOINT oo1");
1026        throw e;
1027      }
1028    }
1029  }/*DB.prototype*/;
1030
1031
1032  /** Throws if the given Stmt has been finalized, else stmt is
1033      returned. */
1034  const affirmStmtOpen = function(stmt){
1035    if(!stmt.pointer) toss3("Stmt has been closed.");
1036    return stmt;
1037  };
1038
1039  /** Returns an opaque truthy value from the BindTypes
1040      enum if v's type is a valid bindable type, else
1041      returns a falsy value. As a special case, a value of
1042      undefined is treated as a bind type of null. */
1043  const isSupportedBindType = function(v){
1044    let t = BindTypes[(null===v||undefined===v) ? 'null' : typeof v];
1045    switch(t){
1046        case BindTypes.boolean:
1047        case BindTypes.null:
1048        case BindTypes.number:
1049        case BindTypes.string:
1050          return t;
1051        case BindTypes.bigint:
1052          if(wasm.bigIntEnabled) return t;
1053          /* else fall through */
1054        default:
1055          //console.log("isSupportedBindType",t,v);
1056          return util.isBindableTypedArray(v) ? BindTypes.blob : undefined;
1057    }
1058  };
1059
1060  /**
1061     If isSupportedBindType(v) returns a truthy value, this
1062     function returns that value, else it throws.
1063  */
1064  const affirmSupportedBindType = function(v){
1065    //console.log('affirmSupportedBindType',v);
1066    return isSupportedBindType(v) || toss3("Unsupported bind() argument type:",typeof v);
1067  };
1068
1069  /**
1070     If key is a number and within range of stmt's bound parameter
1071     count, key is returned.
1072
1073     If key is not a number then it is checked against named
1074     parameters. If a match is found, its index is returned.
1075
1076     Else it throws.
1077  */
1078  const affirmParamIndex = function(stmt,key){
1079    const n = ('number'===typeof key)
1080          ? key : capi.sqlite3_bind_parameter_index(stmt.pointer, key);
1081    if(0===n || !util.isInt32(n)){
1082      toss3("Invalid bind() parameter name: "+key);
1083    }
1084    else if(n<1 || n>stmt.parameterCount) toss3("Bind index",key,"is out of range.");
1085    return n;
1086  };
1087
1088  /**
1089     If stmt._isLocked is truthy, this throws an exception
1090     complaining that the 2nd argument (an operation name,
1091     e.g. "bind()") is not legal while the statement is "locked".
1092     Locking happens before an exec()-like callback is passed a
1093     statement, to ensure that the callback does not mutate or
1094     finalize the statement. If it does not throw, it returns stmt.
1095  */
1096  const affirmUnlocked = function(stmt,currentOpName){
1097    if(stmt._isLocked){
1098      toss3("Operation is illegal when statement is locked:",currentOpName);
1099    }
1100    return stmt;
1101  };
1102
1103  /**
1104     Binds a single bound parameter value on the given stmt at the
1105     given index (numeric or named) using the given bindType (see
1106     the BindTypes enum) and value. Throws on error. Returns stmt on
1107     success.
1108  */
1109  const bindOne = function f(stmt,ndx,bindType,val){
1110    affirmUnlocked(stmt, 'bind()');
1111    if(!f._){
1112      if(wasm.bigIntEnabled){
1113        f._maxInt = BigInt("0x7fffffffffffffff");
1114        f._minInt = ~f._maxInt;
1115      }
1116      /* Reminder: when not in BigInt mode, it's impossible for
1117         JS to represent a number out of the range we can bind,
1118         so we have no range checking. */
1119      f._ = {
1120        string: function(stmt, ndx, val, asBlob){
1121          if(1){
1122            /* _Hypothetically_ more efficient than the impl in the 'else' block. */
1123            const stack = wasm.scopedAllocPush();
1124            try{
1125              const n = wasm.jstrlen(val);
1126              const pStr = wasm.scopedAlloc(n);
1127              wasm.jstrcpy(val, wasm.heap8u(), pStr, n, false);
1128              const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
1129              return f(stmt.pointer, ndx, pStr, n, capi.SQLITE_TRANSIENT);
1130            }finally{
1131              wasm.scopedAllocPop(stack);
1132            }
1133          }else{
1134            const bytes = wasm.jstrToUintArray(val,false);
1135            const pStr = wasm.alloc(bytes.length || 1);
1136            wasm.heap8u().set(bytes.length ? bytes : [0], pStr);
1137            try{
1138              const f = asBlob ? capi.sqlite3_bind_blob : capi.sqlite3_bind_text;
1139              return f(stmt.pointer, ndx, pStr, bytes.length, capi.SQLITE_TRANSIENT);
1140            }finally{
1141              wasm.dealloc(pStr);
1142            }
1143          }
1144        }
1145      };
1146    }/* static init */
1147    affirmSupportedBindType(val);
1148    ndx = affirmParamIndex(stmt,ndx);
1149    let rc = 0;
1150    switch((null===val || undefined===val) ? BindTypes.null : bindType){
1151        case BindTypes.null:
1152          rc = capi.sqlite3_bind_null(stmt.pointer, ndx);
1153          break;
1154        case BindTypes.string:
1155          rc = f._.string(stmt, ndx, val, false);
1156          break;
1157        case BindTypes.number: {
1158          let m;
1159          if(util.isInt32(val)) m = capi.sqlite3_bind_int;
1160          else if('bigint'===typeof val){
1161            if(val<f._minInt || val>f._maxInt){
1162              toss3("BigInt value is out of range for storing as int64: "+val);
1163            }else if(wasm.bigIntEnabled){
1164              m = capi.sqlite3_bind_int64;
1165            }else if(val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER){
1166              val = Number(val);
1167              m = capi.sqlite3_bind_double;
1168            }else{
1169              toss3("BigInt value is out of range for storing as double: "+val);
1170            }
1171          }else{ // !int32, !bigint
1172            val = Number(val);
1173            if(wasm.bigIntEnabled && Number.isInteger(val)){
1174              m = capi.sqlite3_bind_int64;
1175            }else{
1176              m = capi.sqlite3_bind_double;
1177            }
1178          }
1179          rc = m(stmt.pointer, ndx, val);
1180          break;
1181        }
1182        case BindTypes.boolean:
1183          rc = capi.sqlite3_bind_int(stmt.pointer, ndx, val ? 1 : 0);
1184          break;
1185        case BindTypes.blob: {
1186          if('string'===typeof val){
1187            rc = f._.string(stmt, ndx, val, true);
1188          }else if(!util.isBindableTypedArray(val)){
1189            toss3("Binding a value as a blob requires",
1190                  "that it be a string, Uint8Array, or Int8Array.");
1191          }else if(1){
1192            /* _Hypothetically_ more efficient than the impl in the 'else' block. */
1193            const stack = wasm.scopedAllocPush();
1194            try{
1195              const pBlob = wasm.scopedAlloc(val.byteLength || 1);
1196              wasm.heap8().set(val.byteLength ? val : [0], pBlob)
1197              rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
1198                                         capi.SQLITE_TRANSIENT);
1199            }finally{
1200              wasm.scopedAllocPop(stack);
1201            }
1202          }else{
1203            const pBlob = wasm.allocFromTypedArray(val);
1204            try{
1205              rc = capi.sqlite3_bind_blob(stmt.pointer, ndx, pBlob, val.byteLength,
1206                                         capi.SQLITE_TRANSIENT);
1207            }finally{
1208              wasm.dealloc(pBlob);
1209            }
1210          }
1211          break;
1212        }
1213        default:
1214          console.warn("Unsupported bind() argument type:",val);
1215          toss3("Unsupported bind() argument type: "+(typeof val));
1216    }
1217    if(rc) DB.checkRc(stmt.db.pointer, rc);
1218    return stmt;
1219  };
1220
1221  Stmt.prototype = {
1222    /**
1223       "Finalizes" this statement. This is a no-op if the
1224       statement has already been finalizes. Returns
1225       undefined. Most methods in this class will throw if called
1226       after this is.
1227    */
1228    finalize: function(){
1229      if(this.pointer){
1230        affirmUnlocked(this,'finalize()');
1231        delete __stmtMap.get(this.db)[this.pointer];
1232        capi.sqlite3_finalize(this.pointer);
1233        __ptrMap.delete(this);
1234        delete this._mayGet;
1235        delete this.columnCount;
1236        delete this.parameterCount;
1237        delete this.db;
1238        delete this._isLocked;
1239      }
1240    },
1241    /** Clears all bound values. Returns this object.
1242        Throws if this statement has been finalized. */
1243    clearBindings: function(){
1244      affirmUnlocked(affirmStmtOpen(this), 'clearBindings()')
1245      capi.sqlite3_clear_bindings(this.pointer);
1246      this._mayGet = false;
1247      return this;
1248    },
1249    /**
1250       Resets this statement so that it may be step()ed again
1251       from the beginning. Returns this object. Throws if this
1252       statement has been finalized.
1253
1254       If passed a truthy argument then this.clearBindings() is
1255       also called, otherwise any existing bindings, along with
1256       any memory allocated for them, are retained.
1257    */
1258    reset: function(alsoClearBinds){
1259      affirmUnlocked(this,'reset()');
1260      if(alsoClearBinds) this.clearBindings();
1261      capi.sqlite3_reset(affirmStmtOpen(this).pointer);
1262      this._mayGet = false;
1263      return this;
1264    },
1265    /**
1266       Binds one or more values to its bindable parameters. It
1267       accepts 1 or 2 arguments:
1268
1269       If passed a single argument, it must be either an array, an
1270       object, or a value of a bindable type (see below).
1271
1272       If passed 2 arguments, the first one is the 1-based bind
1273       index or bindable parameter name and the second one must be
1274       a value of a bindable type.
1275
1276       Bindable value types:
1277
1278       - null is bound as NULL.
1279
1280       - undefined as a standalone value is a no-op intended to
1281         simplify certain client-side use cases: passing undefined as
1282         a value to this function will not actually bind anything and
1283         this function will skip confirmation that binding is even
1284         legal. (Those semantics simplify certain client-side uses.)
1285         Conversely, a value of undefined as an array or object
1286         property when binding an array/object (see below) is treated
1287         the same as null.
1288
1289       - Numbers are bound as either doubles or integers: doubles if
1290         they are larger than 32 bits, else double or int32, depending
1291         on whether they have a fractional part. Booleans are bound as
1292         integer 0 or 1. It is not expected the distinction of binding
1293         doubles which have no fractional parts is integers is
1294         significant for the majority of clients due to sqlite3's data
1295         typing model. If [BigInt] support is enabled then this
1296         routine will bind BigInt values as 64-bit integers if they'll
1297         fit in 64 bits. If that support disabled, it will store the
1298         BigInt as an int32 or a double if it can do so without loss
1299         of precision. If the BigInt is _too BigInt_ then it will
1300         throw.
1301
1302       - Strings are bound as strings (use bindAsBlob() to force
1303         blob binding).
1304
1305       - Uint8Array and Int8Array instances are bound as blobs.
1306         (TODO: binding the other TypedArray types.)
1307
1308       If passed an array, each element of the array is bound at
1309       the parameter index equal to the array index plus 1
1310       (because arrays are 0-based but binding is 1-based).
1311
1312       If passed an object, each object key is treated as a
1313       bindable parameter name. The object keys _must_ match any
1314       bindable parameter names, including any `$`, `@`, or `:`
1315       prefix. Because `$` is a legal identifier chararacter in
1316       JavaScript, that is the suggested prefix for bindable
1317       parameters: `stmt.bind({$a: 1, $b: 2})`.
1318
1319       It returns this object on success and throws on
1320       error. Errors include:
1321
1322       - Any bind index is out of range, a named bind parameter
1323       does not match, or this statement has no bindable
1324       parameters.
1325
1326       - Any value to bind is of an unsupported type.
1327
1328       - Passed no arguments or more than two.
1329
1330       - The statement has been finalized.
1331    */
1332    bind: function(/*[ndx,] arg*/){
1333      affirmStmtOpen(this);
1334      let ndx, arg;
1335      switch(arguments.length){
1336          case 1: ndx = 1; arg = arguments[0]; break;
1337          case 2: ndx = arguments[0]; arg = arguments[1]; break;
1338          default: toss3("Invalid bind() arguments.");
1339      }
1340      if(undefined===arg){
1341        /* It might seem intuitive to bind undefined as NULL
1342           but this approach simplifies certain client-side
1343           uses when passing on arguments between 2+ levels of
1344           functions. */
1345        return this;
1346      }else if(!this.parameterCount){
1347        toss3("This statement has no bindable parameters.");
1348      }
1349      this._mayGet = false;
1350      if(null===arg){
1351        /* bind NULL */
1352        return bindOne(this, ndx, BindTypes.null, arg);
1353      }
1354      else if(Array.isArray(arg)){
1355        /* bind each entry by index */
1356        if(1!==arguments.length){
1357          toss3("When binding an array, an index argument is not permitted.");
1358        }
1359        arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v));
1360        return this;
1361      }
1362      else if('object'===typeof arg/*null was checked above*/
1363              && !util.isBindableTypedArray(arg)){
1364        /* Treat each property of arg as a named bound parameter. */
1365        if(1!==arguments.length){
1366          toss3("When binding an object, an index argument is not permitted.");
1367        }
1368        Object.keys(arg)
1369          .forEach(k=>bindOne(this, k,
1370                              affirmSupportedBindType(arg[k]),
1371                              arg[k]));
1372        return this;
1373      }else{
1374        return bindOne(this, ndx, affirmSupportedBindType(arg), arg);
1375      }
1376      toss3("Should not reach this point.");
1377    },
1378    /**
1379       Special case of bind() which binds the given value using the
1380       BLOB binding mechanism instead of the default selected one for
1381       the value. The ndx may be a numbered or named bind index. The
1382       value must be of type string, null/undefined (both get treated
1383       as null), or a TypedArray of a type supported by the bind()
1384       API.
1385
1386       If passed a single argument, a bind index of 1 is assumed and
1387       the first argument is the value.
1388    */
1389    bindAsBlob: function(ndx,arg){
1390      affirmStmtOpen(this);
1391      if(1===arguments.length){
1392        arg = ndx;
1393        ndx = 1;
1394      }
1395      const t = affirmSupportedBindType(arg);
1396      if(BindTypes.string !== t && BindTypes.blob !== t
1397         && BindTypes.null !== t){
1398        toss3("Invalid value type for bindAsBlob()");
1399      }
1400      bindOne(this, ndx, BindTypes.blob, arg);
1401      this._mayGet = false;
1402      return this;
1403    },
1404    /**
1405       Steps the statement one time. If the result indicates that a
1406       row of data is available, a truthy value is returned.
1407       If no row of data is available, a falsy
1408       value is returned.  Throws on error.
1409    */
1410    step: function(){
1411      affirmUnlocked(this, 'step()');
1412      const rc = capi.sqlite3_step(affirmStmtOpen(this).pointer);
1413      switch(rc){
1414          case capi.SQLITE_DONE: return this._mayGet = false;
1415          case capi.SQLITE_ROW: return this._mayGet = true;
1416          default:
1417            this._mayGet = false;
1418            console.warn("sqlite3_step() rc=",rc,"SQL =",
1419                         capi.sqlite3_sql(this.pointer));
1420            DB.checkRc(this.db.pointer, rc);
1421      }
1422    },
1423    /**
1424       Functions exactly like step() except that...
1425
1426       1) On success, it calls this.reset() and returns this object.
1427       2) On error, it throws and does not call reset().
1428
1429       This is intended to simplify constructs like:
1430
1431       ```
1432       for(...) {
1433         stmt.bind(...).stepReset();
1434       }
1435       ```
1436
1437       Note that the reset() call makes it illegal to call this.get()
1438       after the step.
1439    */
1440    stepReset: function(){
1441      this.step();
1442      return this.reset();
1443    },
1444    /**
1445       Functions like step() except that it finalizes this statement
1446       immediately after stepping unless the step cannot be performed
1447       because the statement is locked. Throws on error, but any error
1448       other than the statement-is-locked case will also trigger
1449       finalization of this statement.
1450
1451       On success, it returns true if the step indicated that a row of
1452       data was available, else it returns false.
1453
1454       This is intended to simplify use cases such as:
1455
1456       ```
1457       aDb.prepare("insert into foo(a) values(?)").bind(123).stepFinalize();
1458       ```
1459    */
1460    stepFinalize: function(){
1461      const rc = this.step();
1462      this.finalize();
1463      return rc;
1464    },
1465    /**
1466       Fetches the value from the given 0-based column index of
1467       the current data row, throwing if index is out of range.
1468
1469       Requires that step() has just returned a truthy value, else
1470       an exception is thrown.
1471
1472       By default it will determine the data type of the result
1473       automatically. If passed a second arugment, it must be one
1474       of the enumeration values for sqlite3 types, which are
1475       defined as members of the sqlite3 module: SQLITE_INTEGER,
1476       SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB. Any other value,
1477       except for undefined, will trigger an exception. Passing
1478       undefined is the same as not passing a value. It is legal
1479       to, e.g., fetch an integer value as a string, in which case
1480       sqlite3 will convert the value to a string.
1481
1482       If ndx is an array, this function behaves a differently: it
1483       assigns the indexes of the array, from 0 to the number of
1484       result columns, to the values of the corresponding column,
1485       and returns that array.
1486
1487       If ndx is a plain object, this function behaves even
1488       differentlier: it assigns the properties of the object to
1489       the values of their corresponding result columns.
1490
1491       Blobs are returned as Uint8Array instances.
1492
1493       Potential TODO: add type ID SQLITE_JSON, which fetches the
1494       result as a string and passes it (if it's not null) to
1495       JSON.parse(), returning the result of that. Until then,
1496       getJSON() can be used for that.
1497    */
1498    get: function(ndx,asType){
1499      if(!affirmStmtOpen(this)._mayGet){
1500        toss3("Stmt.step() has not (recently) returned true.");
1501      }
1502      if(Array.isArray(ndx)){
1503        let i = 0;
1504        while(i<this.columnCount){
1505          ndx[i] = this.get(i++);
1506        }
1507        return ndx;
1508      }else if(ndx && 'object'===typeof ndx){
1509        let i = 0;
1510        while(i<this.columnCount){
1511          ndx[capi.sqlite3_column_name(this.pointer,i)] = this.get(i++);
1512        }
1513        return ndx;
1514      }
1515      affirmColIndex(this, ndx);
1516      switch(undefined===asType
1517             ? capi.sqlite3_column_type(this.pointer, ndx)
1518             : asType){
1519          case capi.SQLITE_NULL: return null;
1520          case capi.SQLITE_INTEGER:{
1521            if(wasm.bigIntEnabled){
1522              const rc = capi.sqlite3_column_int64(this.pointer, ndx);
1523              if(rc>=Number.MIN_SAFE_INTEGER && rc<=Number.MAX_SAFE_INTEGER){
1524                /* Coerce "normal" number ranges to normal number values,
1525                   and only return BigInt-type values for numbers out of this
1526                   range. */
1527                return Number(rc).valueOf();
1528              }
1529              return rc;
1530            }else{
1531              const rc = capi.sqlite3_column_double(this.pointer, ndx);
1532              if(rc>Number.MAX_SAFE_INTEGER || rc<Number.MIN_SAFE_INTEGER){
1533                /* Throwing here is arguable but, since we're explicitly
1534                   extracting an SQLITE_INTEGER-type value, it seems fair to throw
1535                   if the extracted number is out of range for that type.
1536                   This policy may be laxened to simply pass on the number and
1537                   hope for the best, as the C API would do. */
1538                toss3("Integer is out of range for JS integer range: "+rc);
1539              }
1540              //console.log("get integer rc=",rc,isInt32(rc));
1541              return util.isInt32(rc) ? (rc | 0) : rc;
1542            }
1543          }
1544          case capi.SQLITE_FLOAT:
1545            return capi.sqlite3_column_double(this.pointer, ndx);
1546          case capi.SQLITE_TEXT:
1547            return capi.sqlite3_column_text(this.pointer, ndx);
1548          case capi.SQLITE_BLOB: {
1549            const n = capi.sqlite3_column_bytes(this.pointer, ndx),
1550                  ptr = capi.sqlite3_column_blob(this.pointer, ndx),
1551                  rc = new Uint8Array(n);
1552            //heap = n ? wasm.heap8() : false;
1553            if(n) rc.set(wasm.heap8u().slice(ptr, ptr+n), 0);
1554            //for(let i = 0; i < n; ++i) rc[i] = heap[ptr + i];
1555            if(n && this.db._blobXfer instanceof Array){
1556              /* This is an optimization soley for the
1557                 Worker-based API. These values will be
1558                 transfered to the main thread directly
1559                 instead of being copied. */
1560              this.db._blobXfer.push(rc.buffer);
1561            }
1562            return rc;
1563          }
1564          default: toss3("Don't know how to translate",
1565                         "type of result column #"+ndx+".");
1566      }
1567      toss3("Not reached.");
1568    },
1569    /** Equivalent to get(ndx) but coerces the result to an
1570        integer. */
1571    getInt: function(ndx){return this.get(ndx,capi.SQLITE_INTEGER)},
1572    /** Equivalent to get(ndx) but coerces the result to a
1573        float. */
1574    getFloat: function(ndx){return this.get(ndx,capi.SQLITE_FLOAT)},
1575    /** Equivalent to get(ndx) but coerces the result to a
1576        string. */
1577    getString: function(ndx){return this.get(ndx,capi.SQLITE_TEXT)},
1578    /** Equivalent to get(ndx) but coerces the result to a
1579        Uint8Array. */
1580    getBlob: function(ndx){return this.get(ndx,capi.SQLITE_BLOB)},
1581    /**
1582       A convenience wrapper around get() which fetches the value
1583       as a string and then, if it is not null, passes it to
1584       JSON.parse(), returning that result. Throws if parsing
1585       fails. If the result is null, null is returned. An empty
1586       string, on the other hand, will trigger an exception.
1587    */
1588    getJSON: function(ndx){
1589      const s = this.get(ndx, capi.SQLITE_STRING);
1590      return null===s ? s : JSON.parse(s);
1591    },
1592    // Design note: the only reason most of these getters have a 'get'
1593    // prefix is for consistency with getVALUE_TYPE().  The latter
1594    // arguably really need that prefix for API readability and the
1595    // rest arguably don't, but consistency is a powerful thing.
1596    /**
1597       Returns the result column name of the given index, or
1598       throws if index is out of bounds or this statement has been
1599       finalized. This can be used without having run step()
1600       first.
1601    */
1602    getColumnName: function(ndx){
1603      return capi.sqlite3_column_name(
1604        affirmColIndex(affirmStmtOpen(this),ndx).pointer, ndx
1605      );
1606    },
1607    /**
1608       If this statement potentially has result columns, this
1609       function returns an array of all such names. If passed an
1610       array, it is used as the target and all names are appended
1611       to it. Returns the target array. Throws if this statement
1612       cannot have result columns. This object's columnCount member
1613       holds the number of columns.
1614    */
1615    getColumnNames: function(tgt=[]){
1616      affirmColIndex(affirmStmtOpen(this),0);
1617      for(let i = 0; i < this.columnCount; ++i){
1618        tgt.push(capi.sqlite3_column_name(this.pointer, i));
1619      }
1620      return tgt;
1621    },
1622    /**
1623       If this statement has named bindable parameters and the
1624       given name matches one, its 1-based bind index is
1625       returned. If no match is found, 0 is returned. If it has no
1626       bindable parameters, the undefined value is returned.
1627    */
1628    getParamIndex: function(name){
1629      return (affirmStmtOpen(this).parameterCount
1630              ? capi.sqlite3_bind_parameter_index(this.pointer, name)
1631              : undefined);
1632    }
1633  }/*Stmt.prototype*/;
1634
1635  {/* Add the `pointer` property to DB and Stmt. */
1636    const prop = {
1637      enumerable: true,
1638      get: function(){return __ptrMap.get(this)},
1639      set: ()=>toss3("The pointer property is read-only.")
1640    }
1641    Object.defineProperty(Stmt.prototype, 'pointer', prop);
1642    Object.defineProperty(DB.prototype, 'pointer', prop);
1643  }
1644
1645  /** The OO API's public namespace. */
1646  sqlite3.oo1 = {
1647    version: {
1648      lib: capi.sqlite3_libversion(),
1649      ooApi: "0.1"
1650    },
1651    DB,
1652    Stmt,
1653    dbCtorHelper
1654  }/*oo1 object*/;
1655
1656  if(util.isMainWindow()){
1657    /**
1658       Functionally equivalent to DB(storageName,'c','kvvfs') except
1659       that it throws if the given storage name is not one of 'local'
1660       or 'session'.
1661    */
1662    sqlite3.oo1.JsStorageDb = function(storageName='session'){
1663      if('session'!==storageName && 'local'!==storageName){
1664        toss3("JsStorageDb db name must be one of 'session' or 'local'.");
1665      }
1666      dbCtorHelper.call(this, {
1667        filename: storageName,
1668        flags: 'c',
1669        vfs: "kvvfs"
1670      });
1671    };
1672    const jdb = sqlite3.oo1.JsStorageDb;
1673    jdb.prototype = Object.create(DB.prototype);
1674    /** Equivalent to sqlite3_web_kvvfs_clear(). */
1675    jdb.clearStorage = capi.sqlite3_web_kvvfs_clear;
1676    /**
1677       Clears this database instance's storage or throws if this
1678       instance has been closed. Returns the number of
1679       database blocks which were cleaned up.
1680    */
1681    jdb.prototype.clearStorage = function(){
1682      return jdb.clearStorage(affirmDbOpen(this).filename);
1683    };
1684    /** Equivalent to sqlite3_web_kvvfs_size(). */
1685    jdb.storageSize = capi.sqlite3_web_kvvfs_size;
1686    /**
1687       Returns the _approximate_ number of bytes this database takes
1688       up in its storage or throws if this instance has been closed.
1689    */
1690    jdb.prototype.storageSize = function(){
1691      return jdb.storageSize(affirmDbOpen(this).filename);
1692    };
1693  }/*main-window-only bits*/
1694
1695});
1696
1697