]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add oo1.DB.exec() 'returnValue' option, which specifies what exec() should return...
authorstephan <stephan@noemail.net>
Mon, 31 Oct 2022 11:09:14 +0000 (11:09 +0000)
committerstephan <stephan@noemail.net>
Mon, 31 Oct 2022 11:09:14 +0000 (11:09 +0000)
FossilOrigin-Name: 69d36a6aa5e2cd79d26c0fd3e0d20fe8838fd1be97db07725233bfff1dfe6643

ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/tester1.js
manifest
manifest.uuid

index b44dce690fec6f6cba62ad5e34cdd878b99e4860..e318b7200116363d4e7e3fd05f4126deee5efa89 100644 (file)
@@ -365,7 +365,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      current Stmt and returns the callback argument of the type
      indicated by the input arguments.
   */
-  const parseExecArgs = function(args){
+  const parseExecArgs = function(db, args){
     const out = Object.create(null);
     out.opt = Object.create(null);
     switch(args.length){
@@ -385,20 +385,34 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
           break;
         default: toss3("Invalid argument count for exec().");
     };
-    if(util.isSQLableTypedArray(out.sql)){
-      out.sql = util.typedArrayToString(out.sql);
-    }else if(Array.isArray(out.sql)){
-      out.sql = out.sql.join('');
-    }else if('string'!==typeof out.sql){
+    out.sql = util.flexibleString(out.sql);
+    if('string'!==typeof out.sql){
       toss3("Missing SQL argument or unsupported SQL value type.");
     }
-    if(out.opt.callback || out.opt.resultRows){
-      switch((undefined===out.opt.rowMode)
-             ? 'array' : out.opt.rowMode) {
+    const opt = out.opt;
+    switch(opt.returnValue){
+        case 'resultRows':
+          if(!opt.resultRows) opt.resultRows = [];
+          out.returnVal = ()=>opt.resultRows;
+          break;
+        case 'saveSql':
+          if(!opt.saveSql) opt.saveSql = [];
+          out.returnVal = ()=>opt.saveSql;
+          break;
+        case undefined:
+        case 'this':
+          break;
+        default:
+          toss3("Invalid returnValue value:",opt.returnValue);
+    }
+    if(!out.returnVal) out.returnVal = ()=>db;
+    if(opt.callback || opt.resultRows){
+      switch((undefined===opt.rowMode)
+             ? 'array' : opt.rowMode) {
           case 'object': out.cbArg = (stmt)=>stmt.get(Object.create(null)); break;
           case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
           case 'stmt':
-            if(Array.isArray(out.opt.resultRows)){
+            if(Array.isArray(opt.resultRows)){
               toss3("exec(): invalid rowMode for a resultRows array: must",
                     "be one of 'array', 'object',",
                     "a result column number, or column name reference.");
@@ -406,32 +420,32 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
             out.cbArg = (stmt)=>stmt;
             break;
           default:
-            if(util.isInt32(out.opt.rowMode)){
-              out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
+            if(util.isInt32(opt.rowMode)){
+              out.cbArg = (stmt)=>stmt.get(opt.rowMode);
               break;
-            }else if('string'===typeof out.opt.rowMode && out.opt.rowMode.length>1){
+            }else if('string'===typeof opt.rowMode && opt.rowMode.length>1){
               /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */
-              const prefix = out.opt.rowMode[0];
+              const prefix = opt.rowMode[0];
               if(':'===prefix || '@'===prefix || '$'===prefix){
                 out.cbArg = function(stmt){
                   const rc = stmt.get(this.obj)[this.colName];
                   return (undefined===rc) ? toss3("exec(): unknown result column:",this.colName) : rc;
                 }.bind({
                   obj:Object.create(null),
-                  colName: out.opt.rowMode.substr(1)
+                  colName: opt.rowMode.substr(1)
                 });
                 break;
               }
             }
-            toss3("Invalid rowMode:",out.opt.rowMode);
+            toss3("Invalid rowMode:",opt.rowMode);
       }
     }
     return out;
   };
 
   /**
-     Internal impl of the DB.selectRowArray() and
-     selectRowObject() methods.
+     Internal impl of the DB.selectArray() and
+     selectObject() methods.
   */
   const __selectFirstRow = (db, sql, bind, getArg)=>{
     let stmt, rc;
@@ -588,9 +602,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     /**
        Executes one or more SQL statements in the form of a single
        string. Its arguments must be either (sql,optionsObject) or
-       (optionsObject). In the latter case, optionsObject.sql
-       must contain the SQL to execute. Returns this
-       object. Throws on error.
+       (optionsObject). In the latter case, optionsObject.sql must
+       contain the SQL to execute. By default it returns this object
+       but that can be changed via the `returnValue` option as
+       described below. Throws on error.
 
        If no SQL is provided, or a non-string is provided, an
        exception is triggered. Empty SQL, on the other hand, is
@@ -599,21 +614,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        The optional options object may contain any of the following
        properties:
 
-       - `.sql` = the SQL to run (unless it's provided as the first
+       - `sql` = the SQL to run (unless it's provided as the first
        argument). This must be of type string, Uint8Array, or an array
        of strings. In the latter case they're concatenated together
        as-is, _with no separator_ between elements, before evaluation.
        The array form is often simpler for long hand-written queries.
 
-       - `.bind` = a single value valid as an argument for
+       - `bind` = a single value valid as an argument for
        Stmt.bind(). This is _only_ applied to the _first_ non-empty
        statement in the SQL which has any bindable parameters. (Empty
        statements are skipped entirely.)
 
-       - `.saveSql` = an optional array. If set, the SQL of each
+       - `saveSql` = an optional array. If set, the SQL of each
        executed statement is appended to this array before the
        statement is executed (but after it is prepared - we don't have
-       the string until after that). Empty SQL statements are elided.
+       the string until after that). Empty SQL statements are elided
+       but can have odd effects in the output. e.g. SQL of: `"select
+       1; -- empty\n; select 2"` will result in an array containing
+       `["select 1;", "--empty \n; select 2"]`. That's simply how
+       sqlite3 records the SQL for the 2nd statement.
 
        ==================================================================
        The following options apply _only_ to the _first_ statement
@@ -621,14 +640,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        the statement actually produces any result rows.
        ==================================================================
 
-       - `.columnNames`: if this is an array, the column names of the
+       - `columnNames`: if this is an array, the column names of the
        result set are stored in this array before the callback (if
        any) is triggered (regardless of whether the query produces any
        result rows). If no statement has result columns, this value is
        unchanged. Achtung: an SQL result may have multiple columns
        with identical names.
 
-       - `.callback` = a function which gets called for each row of
+       - `callback` = a function which gets called for each row of
        the result set, but only if that statement has any result
        _rows_. The callback's "this" is the options object, noting
        that this function synthesizes one if the caller does not pass
@@ -647,7 +666,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        The first argument passed to the callback defaults to an array of
        values from the current result row but may be changed with ...
 
-       - `.rowMode` = specifies the type of he callback's first argument.
+       - `rowMode` = specifies the type of he callback's first argument.
        It may be any of...
 
        A) A string describing what type of argument should be passed
@@ -655,7 +674,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
 
          A.1) `'array'` (the default) causes the results of
          `stmt.get([])` to be passed to the `callback` and/or appended
-         to `resultRows`.
+         to `resultRows`
 
          A.2) `'object'` causes the results of
          `stmt.get(Object.create(null))` to be passed to the
@@ -687,7 +706,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
 
        Any other `rowMode` value triggers an exception.
 
-       - `.resultRows`: if this is an array, it functions similarly to
+       - `resultRows`: if this is an array, it functions similarly to
        the `callback` option: each row of the result set (if any),
        with the exception that the `rowMode` 'stmt' is not legal. It
        is legal to use both `resultRows` and `callback`, but
@@ -695,28 +714,44 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        and can be used over a WebWorker-style message interface.
        exec() throws if `resultRows` is set and `rowMode` is 'stmt'.
 
+       - `returnValue`: is a string specifying what this function
+       should return:
+
+         A) The default value is `"this"`, meaning that the
+            DB object itself should be returned.
+
+         B) `"resultRows"` means to return the value of the
+            `resultRows` option. If `resultRows` is not set, this
+            function behaves as if it were set to an empty array.
+
+         C) `"saveSql"` means to return the value of the
+            `saveSql` option. If `saveSql` is not set, this
+            function behaves as if it were set to an empty array.
 
        Potential TODOs:
 
-       - `.bind`: permit an array of arrays/objects to bind. The first
+       - `bind`: permit an array of arrays/objects to bind. The first
        sub-array would act on the first statement which has bindable
        parameters (as it does now). The 2nd would act on the next such
        statement, etc.
 
-       - `.callback` and `.resultRows`: permit an array entries with
-       semantics similar to those described for `.bind` above.
+       - `callback` and `resultRows`: permit an array entries with
+       semantics similar to those described for `bind` above.
 
     */
     exec: function(/*(sql [,obj]) || (obj)*/){
       affirmDbOpen(this);
-      const arg = parseExecArgs(arguments);
+      const arg = parseExecArgs(this, arguments);
       if(!arg.sql){
         return (''===arg.sql) ? this : toss3("exec() requires an SQL string.");
       }
       const opt = arg.opt;
       const callback = opt.callback;
-      let resultRows = (Array.isArray(opt.resultRows)
-                          ? opt.resultRows : undefined);
+      const returnValue = opt.returnValue || 'this';
+      const resultRows = (Array.isArray(opt.resultRows)
+                          ? opt.resultRows : (
+                            'resultRows'===returnValue ? [] : undefined
+                          ));
       let stmt;
       let bind = opt.bind;
       let evalFirstResult = !!(arg.cbArg || opt.columnNames) /* true to evaluate the first result-returning query */;
@@ -774,7 +809,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
               stmt._isLocked = true;
               const row = arg.cbArg(stmt);
               if(resultRows) resultRows.push(row);
-              if(callback) callback.apply(opt,[row,stmt]);
+              if(callback) callback.call(opt, row, stmt);
               stmt._isLocked = false;
             }
           }else{
@@ -793,7 +828,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         }
         wasm.scopedAllocPop(stack);
       }
-      return this;
+      return arg.returnVal();
     }/*exec()*/,
     /**
        Creates a new scalar UDF (User-Defined Function) which is
index 6dae0b60601d995fe21f3a88a9f32a3a971fd140..a5ff0c40ad33832ae353b29869c781f848056b2e 100644 (file)
@@ -162,12 +162,64 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     }
   });
 
-  /** Throws a new Error, the message of which is the concatenation
-      all args with a space between each. */
-  const toss = (...args)=>{throw new Error(args.join(' '))};
+  /**
+     An Error subclass specifically for reporting DB-level errors and
+     enabling clients to unambiguously identify such exceptions.
+     The C-level APIs never throw, but some of the higher-level
+     C-style APIs do and the object-oriented APIs use exceptions
+     exclusively to report errors.
+  */
+  class SQLite3Error extends Error {
+    /**
+       Constructs this object with a message equal to all arguments
+       concatenated with a space between each one. As a special case,
+       if it's passed only a single integer argument, the string form
+       of that argument is the result of
+       sqlite3.capi.sqlite3_js_rc_str() or (if that returns falsy), a
+       synthesized string which contains that integer.
+    */
+    constructor(...args){
+      if(1===args.length && 'number'===typeof args[0] && args[0]===(args[0] | 0)){
+        super((capi.sqlite3_js_rc_str && capi.sqlite3_js_rc_str(args[0]))
+              || ("Unknown result code #"+args[0]));
+      }else{
+        super(args.join(' '));
+      }
+      this.name = 'SQLite3Error';
+    }
+  };
+
+  /** 
+      The main sqlite3 binding API gets installed into this object,
+      mimicking the C API as closely as we can. The numerous members
+      names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as
+      possible, identically to the C-native counterparts, as documented at:
+
+      https://www.sqlite.org/c3ref/intro.html
+
+      A very few exceptions require an additional level of proxy
+      function or may otherwise require special attention in the WASM
+      environment, and all such cases are document here. Those not
+      documented otherwise are installed as 1-to-1 proxies for their
+      C-side counterparts.
+  */
+  const capi = Object.create(null);
+
+  /**
+     Functionally equivalent to the SQLite3Error constructor but may
+     be used as part of an expression, e.g.:
+
+     ```
+     return someFunction(x) || SQLite3Error.toss(...);
+     ```
+  */
+  SQLite3Error.toss = (...args)=>{
+    throw new SQLite3Error(...args);
+  };
+  const toss3 = SQLite3Error.toss;
 
   if(config.wasmfsOpfsDir && !/^\/[^/]+$/.test(config.wasmfsOpfsDir)){
-    toss("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'.");
+    toss3("config.wasmfsOpfsDir must be falsy or in the form '/dir-name'.");
   }
 
   /**
@@ -267,7 +319,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
       that v is not a supported TypedArray value. */
   const affirmBindableTypedArray = (v)=>{
     return isBindableTypedArray(v)
-      || toss("Value is not of a supported TypedArray type.");
+      || toss3("Value is not of a supported TypedArray type.");
   };
 
   const utf8Decoder = new TextDecoder('utf-8');
@@ -318,21 +370,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     throw new WasmAllocError(...args);
   };
 
-  /** 
-      The main sqlite3 binding API gets installed into this object,
-      mimicking the C API as closely as we can. The numerous members
-      names with prefixes 'sqlite3_' and 'SQLITE_' behave, insofar as
-      possible, identically to the C-native counterparts, as documented at:
-
-      https://www.sqlite.org/c3ref/intro.html
-
-      A very few exceptions require an additional level of proxy
-      function or may otherwise require special attention in the WASM
-      environment, and all such cases are document here. Those not
-      documented here are installed as 1-to-1 proxies for their C-side
-      counterparts.
-  */
-  const capi = {
+  Object.assign(capi, {
     /**
        sqlite3_create_function_v2() differs from its native
        counterpart only in the following ways:
@@ -557,7 +595,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
        values.
      */
     sqlite3_randomness: (n, outPtr)=>{/*installed later*/},
-  }/*capi*/;
+  }/*capi*/);
 
   /**
      Various internal-use utilities are added here as needed. They
@@ -617,7 +655,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
        The symbols exported by the WASM environment.
     */
     exports: config.exports
-      || toss("Missing API config.exports (WASM module exports)."),
+      || toss3("Missing API config.exports (WASM module exports)."),
 
     /**
        When Emscripten compiles with `-sIMPORT_MEMORY`, it
@@ -626,7 +664,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
        available via this.exports.memory.
     */
     memory: config.memory || config.exports['memory']
-      || toss("API config object requires a WebAssembly.Memory object",
+      || toss3("API config object requires a WebAssembly.Memory object",
               "in either config.exports.memory (exported)",
               "or config.memory (imported)."),
 
@@ -688,7 +726,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
         keyDealloc =  config.deallocExportName || 'free';
   for(const key of [keyAlloc, keyDealloc]){
     const f = wasm.exports[key];
-    if(!(f instanceof Function)) toss("Missing required exports[",key,"] function.");
+    if(!(f instanceof Function)) toss3("Missing required exports[",key,"] function.");
   }
 
   wasm.alloc = function(n){
@@ -1057,43 +1095,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     }
   })/*wasm.pstack properties*/;
 
-  /**
-     An Error subclass specifically for reporting DB-level errors and
-     enabling clients to unambiguously identify such exceptions.
-     The C-level APIs never throw, but some of the higher-level
-     C-style APIs do and the object-oriented APIs use exceptions
-     exclusively to report errors.
-  */
-  class SQLite3Error extends Error {
-    /**
-       Constructs this object with a message equal to all arguments
-       concatenated with a space between each one. As a special case,
-       if it's passed only a single integer argument, the string form
-       of that argument is the result of
-       sqlite3.capi.sqlite3_js_rc_str() or (if that returns falsy), a
-       synthesized string which contains that integer.
-    */
-    constructor(...args){
-      if(1===args.length && 'number'===typeof args[0] && args[0]===(args[0] | 0)){
-        super(capi.sqlite3_js_rc_str(args[0]) || ("Unknown result code #"+args[0]));
-      }else{
-        super(args.join(' '));
-      }
-      this.name = 'SQLite3Error';
-    }
-  };
-  /**
-     Functionally equivalent to the SQLite3Error constructor but may
-     be used as part of an expression, e.g.:
-
-     ```
-     return someFunction(x) || SQLite3Error.toss(...);
-     ```
-  */
-  SQLite3Error.toss = (...args)=>{
-    throw new SQLite3Error(...args);
-  };
-
   capi.sqlite3_randomness = (...args)=>{
     if(1===args.length && util.isTypedArray(args[0])
       && 1===args[0].BYTES_PER_ELEMENT){
@@ -1245,8 +1246,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      error it throws with a description of the problem.
   */
   capi.sqlite3_js_db_export = function(pDb){
-    if(!pDb) toss('Invalid sqlite3* argument.');
-    if(!wasm.bigIntEnabled) toss('BigInt64 support is not enabled.');
+    if(!pDb) toss3('Invalid sqlite3* argument.');
+    if(!wasm.bigIntEnabled) toss3('BigInt64 support is not enabled.');
     const stack = wasm.pstack.pointer;
     let pOut;
     try{
@@ -1263,7 +1264,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
         pDb, ppOut, pSize, 0
       );
       if(rc){
-        toss("Database serialization failed with code",
+        toss3("Database serialization failed with code",
              sqlite3.capi.sqlite3_js_rc_str(rc));
       }
       pOut = wasm.getPtrValue(ppOut);
index 3c6136b907c496d6bcc64ac2f3a687e892baa6dc..016886a30add973e3ff4d1bf958f3a23d834dba4 100644 (file)
     .t('Table t', function(sqlite3){
       const db = this.db;
       let list = [];
-      db.exec({
+      let rc = db.exec({
         sql:['CREATE TABLE t(a,b);',
+             // ^^^ using TEMP TABLE breaks the db export test
              "INSERT INTO t(a,b) VALUES(1,2),(3,4),",
              "(?,?),('blob',X'6869')"/*intentionally missing semicolon to test for
                                        off-by-one bug in string-to-WASM conversion*/],
         bind: [5,6]
       });
       //debug("Exec'd SQL:", list);
-      T.assert(2 === list.length)
+      T.assert(rc === db)
+        .assert(2 === list.length)
         .assert('string'===typeof list[1])
         .assert(4===db.changes());
       if(wasm.bigIntEnabled){
           xValue: xValueFinal
         });
         db.exec([
-          "CREATE TABLE twin(x, y); INSERT INTO twin VALUES",
+          "CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES",
           "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
         ]);
-        let count = 0;
-        db.exec({
+        let rc = db.exec({
+          returnValue: 'resultRows',
           sql:[
             "SELECT x, winsumint(y) OVER (",
             "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING",
             ") AS sum_y ",
-            "FROM twin ORDER BY x;",
-            "DROP TABLE twin;"
-          ],
-          callback: function(row){
-            switch(++count){
-                case 1: T.assert('a'===row[0] && 9===row[1]); break;
-                case 2: T.assert('b'===row[0] && 12===row[1]); break;
-                case 3: T.assert('c'===row[0] && 16===row[1]); break;
-                case 4: T.assert('d'===row[0] && 12===row[1]); break;
-                case 5: T.assert('e'===row[0] && 9===row[1]); break;
-                default: toss("Too many rows to window function.");
-            }
+            "FROM twin ORDER BY x;"
+          ]
+        });
+        T.assert(Array.isArray(rc))
+          .assert(5 === rc.length);
+        let count = 0;
+        for(const row of rc){
+          switch(++count){
+              case 1: T.assert('a'===row[0] && 9===row[1]); break;
+              case 2: T.assert('b'===row[0] && 12===row[1]); break;
+              case 3: T.assert('c'===row[0] && 16===row[1]); break;
+              case 4: T.assert('d'===row[0] && 12===row[1]); break;
+              case 5: T.assert('e'===row[0] && 9===row[1]); break;
+              default: toss("Too many rows to window function.");
           }
+        }
+        const resultRows = [];
+        rc = db.exec({
+          resultRows,
+          returnValue: 'resultRows',
+          sql:[
+            "SELECT x, winsumint(y) OVER (",
+            "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING",
+            ") AS sum_y ",
+            "FROM twin ORDER BY x;"
+          ]
+        });
+        T.assert(rc === resultRows)
+          .assert(5 === rc.length);
+
+        rc = db.exec({
+          returnValue: 'saveSql',
+          sql: "select 1; select 2; -- empty\n; select 3"
         });
-        T.assert(5 === count);
+        T.assert(Array.isArray(rc))
+          .assert(3===rc.length)
+          .assert('select 1;' === rc[0])
+          .assert('select 2;' === rc[1])
+          .assert('-- empty\n; select 3' === rc[2]
+                  /* Strange but true. */);
+        
+        T.mustThrowMatching(()=>{
+          db.exec({sql:'', returnValue: 'nope'});
+        }, /^Invalid returnValue/);
+
+        db.exec("DROP TABLE twin");
       }
     }/*window UDFs*/)
 
       db.exec({
         sql:new TextEncoder('utf-8').encode([
           // ^^^ testing string-vs-typedarray handling in exec()
-          "attach 'session' as foo;" /* name 'session' is magic for kvvfs! */,
+          "attach 'session' as foo;",
           "create table foo.bar(a);",
           "insert into foo.bar(a) values(1),(2),(3);",
           "select a from foo.bar order by a;"
   ;/* end of oo1 checks */
 
   ////////////////////////////////////////////////////////////////////////
-  T.g('kvvfs (Worker thread only)', isWorker)
-    .t({
-      name: 'kvvfs is disabled',
-      test: ()=>{
+  T.g('kvvfs')
+    .t('kvvfs sanity checks', function(sqlite3){
+      if(isWorker()){
         T.assert(
           !capi.sqlite3_vfs_find('kvvfs'),
           "Expecting kvvfs to be unregistered."
         );
+        log("kvvfs is (correctly) unavailable in a Worker.");
+        return;
       }
-    });
-  T.g('kvvfs (UI thread only)', isUIThread)
-    .t({
-      name: 'kvvfs sanity checks',
-      test: function(sqlite3){
-        const filename = 'session';
-        const pVfs = capi.sqlite3_vfs_find('kvvfs');
-        T.assert(pVfs);
-        const JDb = sqlite3.oo1.JsStorageDb;
-        const unlink = ()=>JDb.clearStorage(filename);
-        unlink();
-        let db = new JDb(filename);
+      const filename = 'session';
+      const pVfs = capi.sqlite3_vfs_find('kvvfs');
+      T.assert(pVfs);
+      const JDb = sqlite3.oo1.JsStorageDb;
+      const unlink = ()=>JDb.clearStorage(filename);
+      unlink();
+      let db = new JDb(filename);
+      try {
         db.exec([
           'create table kvvfs(a);',
           'insert into kvvfs(a) values(1),(2),(3)'
         db = new JDb(filename);
         db.exec('insert into kvvfs(a) values(4),(5),(6)');
         T.assert(6 === db.selectValue('select count(*) from kvvfs'));
+      }finally{
         db.close();
         unlink();
       }
         const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
         unlink();
         let db = new sqlite3.opfs.OpfsDb(filename);
-        db.exec([
-          'create table p(a);',
-          'insert into p(a) values(1),(2),(3)'
-        ]);
-        T.assert(3 === db.selectValue('select count(*) from p'));
-        db.close();
-        db = new sqlite3.opfs.OpfsDb(filename);
-        db.exec('insert into p(a) values(4),(5),(6)');
-        T.assert(6 === db.selectValue('select count(*) from p'));
-        db.close();
-        unlink();
+        try {
+          db.exec([
+            'create table p(a);',
+            'insert into p(a) values(1),(2),(3)'
+          ]);
+          T.assert(3 === db.selectValue('select count(*) from p'));
+          db.close();
+          db = new sqlite3.opfs.OpfsDb(filename);
+          db.exec('insert into p(a) values(4),(5),(6)');
+          T.assert(6 === db.selectValue('select count(*) from p'));
+        }finally{
+          db.close();
+          unlink();
+        }
       }
     }/*OPFS sanity checks*/)
   ;/* end OPFS tests */
index 40e385cfd62f687b83beeefec33d2478382f1d49..24403f2ca29eea8e899a6c6942a10775b341eced 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Avoid\sa\ssegfault\sthat\scould\soccur\swhen\srunning\sthe\srecover\sAPI\son\sa\sdatabase\shandle\swith\smemory-mapping\senabled.
-D 2022-10-31T10:53:23.735
+C Add\soo1.DB.exec()\s'returnValue'\soption,\swhich\sspecifies\swhat\sexec()\sshould\sreturn.\sDefaults\sto\sthe\sdb\sobject\sand\senables\sdirect\sreturn\sof\sthe\sresult\srows\sarray\sor\sa\slist\sof\sthe\sindividual\sSQL\sstatements.\sOther\scode-adjacent\sinternal\scleanups.
+D 2022-10-31T11:09:14.111
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -500,9 +500,9 @@ F ext/wasm/api/post-js-header.js d6ab3dfef4a06960d28a7eaa338d4e2a1a5981e9b387181
 F ext/wasm/api/pre-js.js 287e462f969342b032c03900e668099fa1471d852df7a472de5bc349161d9c04
 F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34
 F ext/wasm/api/sqlite3-api-glue.js b87543534821ecfa56fc0d0cd153a115fa974e70d6217964baf6e93ef8d25fb1
-F ext/wasm/api/sqlite3-api-oo1.js a17e2624967073f86cc50f4c1e30f8822ef631dc20dfc73b1143847b9e9723fe
+F ext/wasm/api/sqlite3-api-oo1.js 4028bc2bac7e3ae2d23b7c99828155b4a06da006b51dc2a929bc0db26337370d
 F ext/wasm/api/sqlite3-api-opfs.js c67cbe0b1451ec43bc6b3199e13453e1ca56d718a75c0498253b0d479c336256
-F ext/wasm/api/sqlite3-api-prologue.js a218dda5e5ced8894f65760131371e4cabd31062af58803af8952cc00ea778d2
+F ext/wasm/api/sqlite3-api-prologue.js 873986ca150c79510f647b910f8349bc71b14db21e444cab3b9fad9c4f39ffc7
 F ext/wasm/api/sqlite3-api-worker1.js efdca1b42299d80b54f366d15a8fc5343f3b3e9e3647e5c1fd6f3ee1015e501b
 F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
 F ext/wasm/api/sqlite3-opfs-async-proxy.js 29f6f5c314c2956e77573c6ab975c2455a0839721ed44f38004382c2a1463ab5
@@ -549,7 +549,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
 F ext/wasm/tester1-worker.html d02b9d38876b023854cf8955e77a40912f7e516956b4dbe1ec7f215faac273ee
 F ext/wasm/tester1.html c6c47e5a8071eb09cb1301104435c8e44fbb5719c92411f5b2384a461f9793c5
-F ext/wasm/tester1.js 08ccc16972562ff604b7bda387b56d6eea380aaf968697fc8e2a9bb3c6ba2dda
+F ext/wasm/tester1.js 2427ac48e255f658ad81163b5dc6372a8609ab6ab60e295e371d1e5fe9a495ab
 F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5
 F ext/wasm/wasmfs.make fb2d3c4a298b12cf1ec994ad1d0f1d027ae297449b364cde43d2eb807d68048f
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@@ -2054,8 +2054,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P cb12ac5de17e677f089d7b0b46803efbd9a9178972ffb0454a8b557b06633658
-R 46019e96ebee7f43e27063daa1a45bd1
-U dan
-Z 31c01b86489219dc9c143f294c0c509f
+P e02c697281a777c33070168af784b2d291397e488244a217620897f40aed7158
+R 26339704ab975fcc1e6818338a87c2a1
+U stephan
+Z 2aa3561d4216c084633673f53cc5adc1
 # Remove this line to create a well-formed Fossil manifest.
index 37b316cefb69311046055de943467ac6776550b0..52bd63380eb9f0f439c215916bd11baaca86a17a 100644 (file)
@@ -1 +1 @@
-e02c697281a777c33070168af784b2d291397e488244a217620897f40aed7158
\ No newline at end of file
+69d36a6aa5e2cd79d26c0fd3e0d20fe8838fd1be97db07725233bfff1dfe6643
\ No newline at end of file