]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change DB.exec() rowMode default from 'stmt' to 'array', per /chat discussion. Add...
authorstephan <stephan@noemail.net>
Wed, 24 Aug 2022 20:57:37 +0000 (20:57 +0000)
committerstephan <stephan@noemail.net>
Wed, 24 Aug 2022 20:57:37 +0000 (20:57 +0000)
FossilOrigin-Name: 1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f

ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-worker1.js
ext/wasm/testing-worker1-promiser.js
ext/wasm/testing2.js
manifest
manifest.uuid

index e16b45bb5eb775fa9f0598369f057b6ead8b3bfd..ea42e6bf8dfb4c8887155198f8a2a64bf1def487 100644 (file)
@@ -236,14 +236,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }
     if(out.opt.callback || out.opt.resultRows){
       switch((undefined===out.opt.rowMode)
-             ? 'stmt' : out.opt.rowMode) {
+             ? 'array' : out.opt.rowMode) {
           case 'object': out.cbArg = (stmt)=>stmt.get({}); break;
           case 'array': out.cbArg = (stmt)=>stmt.get([]); break;
           case 'stmt':
             if(Array.isArray(out.opt.resultRows)){
-              toss3("Invalid rowMode for resultRows array: must",
+              toss3("Invalid rowMode for resultRows array: must",
                     "be one of 'array', 'object',",
-                    "or a result column number.");
+                    "a result column number, or column name reference.");
             }
             out.cbArg = (stmt)=>stmt;
             break;
@@ -251,22 +251,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
             if(util.isInt32(out.opt.rowMode)){
               out.cbArg = (stmt)=>stmt.get(out.opt.rowMode);
               break;
-            }
-            /*
-              TODO?: how can we define rowMode such that it uses
-              rowMode of 'object' and returns a given named field from
-              the object. Something like:
-
-              if(?what goes here?){
-                out.cbArg = function f(stmt){return stmt.get(this.obj)[this.colName]}
-                  .bind({obj:{}, colName: ???what goes here???}});
+            }else if('string'===typeof out.opt.rowMode && out.opt.rowMode.length>1){
+              /* "$X", ":X", and "@X" fetch column named "X" (case-sensitive!) */
+              const prefix = out.opt.rowMode[0];
+              if(':'===prefix || '@'===prefix || '$'===prefix){
+                out.cbArg = function(stmt){
+                  return stmt.get(this.obj)[this.colName];
+                }.bind({obj:{}, colName: out.opt.rowMode.substr(1)})
                 break;
               }
-
-              Maybe rowMode:['colName1',... 'colNameN']? That could be
-              ambiguous: might mean "return an object with just these
-              columns".
-            */
+            }
             toss3("Invalid rowMode:",out.opt.rowMode);
       }
     }
@@ -449,7 +443,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
             if(rowTarget) rowTarget.push(row);
             if(opt.callback){
               stmt._isLocked = true;
-              opt.callback(row, stmt);
+              opt.callback(row,stmt);
               stmt._isLocked = false;
             }
           }
@@ -494,23 +488,40 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        if that statement has any result _rows_. The callback's "this"
        is the options object. The second argument passed to the
        callback is always the current Stmt object (so that the caller
-       may collect column names, or similar). The first argument
-       passed to the callback defaults to the current Stmt object but
-       may be changed with ...
-
-       - .rowMode = either a string describing what type of argument
-       should be passed as the first argument to the callback or an
-       integer representing a result column index. A `rowMode` of
-       'object' causes the results of `stmt.get({})` to be passed to
-       the `callback` and/or appended to `resultRows`. A value of
-       'array' causes the results of `stmt.get([])` to be passed to
-       passed on.  A value of 'stmt' is equivalent to the default,
-       passing the current Stmt to the callback (noting that it's
-       always passed as the 2nd argument), but this mode will trigger
-       an exception if `resultRows` is an array. If `rowMode` is an
-       integer, only the single value from that result column will be
-       passed on. Any other value for the option triggers an
-       exception.
+       may collect column names, or similar). The 2nd argument to the
+       callback is always the Stmt instance, as it's needed if the
+       caller wants to fetch the column names or some such. The first
+       argument passed to the callback defaults to the current Stmt
+       object but may be changed with ...
+
+       - .rowMode = may take one of several forms:
+
+       A) If `rowMode` is an integer, only the single value from that
+       result column (0-based) will be passed on.
+
+       B) A string describing what type of argument should be passed
+       as the first argument to the callback:
+
+         B.1) 'array' (the default) causes the results of
+         `stmt.get([])` to be passed to passed on and/or appended to
+         `resultRows`.
+
+         B.2) 'object' causes the results of `stmt.get({})` to be
+         passed to the `callback` and/or appended to `resultRows`.
+
+         B.3) 'stmt' causes the current Stmt to be passed to the
+         callback, but this mode will trigger an exception if
+         `resultRows` is an array because appending the statement to
+         the array would be unhelpful.
+
+       C) A string with a minimum length of 2 and leading character of
+       ':', '$', or '@' will fetch the row as an object, extract that
+       one field, and pass that field's value to the callback. Note
+       that these keys are case-sensitive so must match the case used
+       in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A'
+       would work but '$a' would not (it would result in `undefined`).
+
+       Any other `rowMode` value triggers an exception.
 
        - .resultRows: if this is an array, it functions similarly to
        the `callback` option: each row of the result set (if any) of
@@ -584,7 +595,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         else wasm.jstrcpy(arg.sql, wasm.heap8(), pSql, sqlByteLen, false);
         wasm.setMemValue(pSql + sqlByteLen, 0/*NUL terminator*/);
         while(wasm.getMemValue(pSql, 'i8')
-              /* Maintenance reminder:   ^^^^ _must_ be i8 or else we
+              /* Maintenance reminder:^^^ _must_ be 'i8' or else we
                  will very likely cause an endless loop. What that's
                  doing is checking for a terminating NUL byte. If we
                  use i32 or similar then we read 4 bytes, read stuff
@@ -615,7 +626,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
               stmt._isLocked = true;
               const row = arg.cbArg(stmt);
               if(resultRows) resultRows.push(row);
-              if(callback) callback(row, stmt);
+              if(callback) callback(row,stmt);
               stmt._isLocked = false;
             }
             rowMode = undefined;
index afb2e7812472fe69112564886180aef32d982dfa..90c8f0de19637e0b2438bb7322331fdd84fff4f1 100644 (file)
@@ -249,7 +249,11 @@ sqlite3.initWorker1API = function(){
        message type key, in which case a callback function will be
        applied which posts each row result via:
 
-       postMessage({type: thatKeyType, rowNumber: 1-based-#, row: theRow})
+       postMessage({type: thatKeyType,
+                    rowNumber: 1-based-#,
+                    row: theRow,
+                    columnNames: anArray
+                    })
 
        And, at the end of the result set (whether or not any result
        rows were produced), it will post an identical message with
@@ -280,12 +284,7 @@ sqlite3.initWorker1API = function(){
       const rc = (
         'string'===typeof ev.args
       ) ? {sql: ev.args} : (ev.args || Object.create(null));
-      if(undefined===rc.rowMode){
-        /* Since the default rowMode of 'stmt' is not useful
-           for the Worker interface, we'll default to
-           something else. */
-        rc.rowMode = 'array';
-      }else if('stmt'===rc.rowMode){
+      if('stmt'===rc.rowMode){
         toss("Invalid rowMode for 'exec': stmt mode",
              "does not work in the Worker API.");
       }
@@ -294,25 +293,40 @@ sqlite3.initWorker1API = function(){
         // Part of a copy-avoidance optimization for blobs
         db._blobXfer = wState.xfer;
       }
-      const callbackMsgType = rc.callback;
+      const theCallback = rc.callback;
       let rowNumber = 0;
-      if('string' === typeof callbackMsgType){
+      const hadColNames = !!rc.columnNames;
+      if('string' === typeof theCallback){
+        if(!hadColNames) rc.columnNames = [];
         /* Treat this as a worker message type and post each
            row as a message of that type. */
-        rc.callback =
-          (row)=>wState.post({type: callbackMsgType, rowNumber:++rowNumber, row:row}, wState.xfer);
+        rc.callback = function(row,stmt){
+          wState.post({
+            type: theCallback,
+            columnNames: rc.columnNames,
+            rowNumber: ++rowNumber,
+            row: row
+          }, wState.xfer);
+        }
       }
       try {
         db.exec(rc);
         if(rc.callback instanceof Function){
-          rc.callback = callbackMsgType;
-          wState.post({type: callbackMsgType, rowNumber: null, row: undefined});
+          rc.callback = theCallback;
+          /* Post a sentinel message to tell the client that the end
+             of the result set has been reached (possibly with zero
+             rows). */
+          wState.post({
+            type: theCallback,
+            columnNames: rc.columnNames,
+            rowNumber: null /*null to distinguish from "property not set"*/,
+            row: undefined /*undefined because null is a legal row value
+                             for some rowType values, but undefined is not*/
+          });
         }
       }finally{
         delete db._blobXfer;
-        if(rc.callback){
-          rc.callback = callbackMsgType;
-        }
+        if(rc.callback) rc.callback = theCallback;
       }
       return rc;
     }/*exec()*/,
index a2f3a2523d770a6a9af98c585142a3d8ac1173c8..63401033c07242696ca37223aac7b037e57fcb6c 100644 (file)
       if(undefined === f.counter) f.counter = 0;
       if(null === ev.rowNumber){
         /* End of result set. */
-        T.assert(undefined === ev.row);
+        T.assert(undefined === ev.row)
+          .assert(2===ev.columnNames.length)
+          .assert('a'===ev.columnNames[0])
+          .assert('B'===ev.columnNames[1]);
       }else{
         T.assert(ev.rowNumber > 0);
         ++f.counter;
       }
       log("exec() result row:",ev);
-      T.assert(null === ev.rowNumber || 'number' === typeof ev.row.b);
+      T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B);
     };
     await wtest('exec',{
-      sql: 'select a a, b b from t order by a',
+      sql: 'select a a, b B from t order by a limit 3',
       callback: resultRowTest1,
       rowMode: 'object'
     }, function(ev){
       resultRowTest1.counter = 0;
     });
 
+    const resultRowTest2 = function f(ev){
+      if(null === ev.rowNumber){
+        /* End of result set. */
+        T.assert(undefined === ev.row)
+          .assert(1===ev.columnNames.length)
+          .assert('a'===ev.columnNames[0])
+      }else{
+        T.assert(ev.rowNumber > 0);
+        f.counter = ev.rowNumber;
+      }
+      log("exec() result row:",ev);
+      T.assert(null === ev.rowNumber || 'number' === typeof ev.row);
+    };
+    await wtest('exec',{
+      sql: 'select a a from t limit 3',
+      callback: resultRowTest2,
+      rowMode: 0
+    }, function(ev){
+      T.assert(3===resultRowTest2.counter);
+    });
+
+    const resultRowTest3 = function f(ev){
+      if(null === ev.rowNumber){
+        T.assert(3===ev.columnNames.length)
+          .assert('foo'===ev.columnNames[0])
+          .assert('bar'===ev.columnNames[1])
+          .assert('baz'===ev.columnNames[2]);
+      }else{
+        f.counter = ev.rowNumber;
+        T.assert('number' === typeof ev.row);
+      }
+    };
+    await wtest('exec',{
+      sql: "select 'foo' foo, a bar, 'baz' baz  from t limit 2",
+      callback: resultRowTest3,
+      columnNames: [],
+      rowMode: ':bar'
+    }, function(ev){
+      log("exec() result row:",ev);
+      T.assert(2===resultRowTest3.counter);
+    });
+
     await wtest('exec',{
       multi: true,
       sql:[
 
     await wtest('close', (ev)=>{
       T.assert(undefined === ev.result.filename);
-    }).finally(()=>log("That's all, folks!"));
+    }).finally(()=>logHtml('',"That's all, folks!"));
   }/*runTests2()*/;
 
   
index d64fd6e89ad055531370db89702f09d961a42357..8d9e66dc1f088e39158760d93db8b18d083f8cc5 100644 (file)
       if(undefined === f.counter) f.counter = 0;
       if(null === ev.rowNumber){
         /* End of result set. */
-        T.assert(undefined === ev.row);
+        T.assert(undefined === ev.row)
+          .assert(Array.isArray(ev.columnNames))
+          .assert(ev.columnNames.length);
       }else{
         T.assert(ev.rowNumber > 0);
         ++f.counter;
index 2b3fc24b379677ce582dd9278d4d2c400cfd7150..dd1b3968985bde76c811625ae70ec0da95514b02 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Expand\sthe\sworker1\s'exec'\sop\shandling\sfor\sper-row\scallbacks\sfor\sAPI-level\sconsistency\sand\ssmooth\ssome\sedges\sbetween\sworker1\score\sand\sworker1-promiser.\sAdd\sworker1\s'config-get'\smessage\sto\sfetch\sthe\sserializable\sparts\sof\sthe\ssqlite3.config\sstate.\sImprove\sthe\s'open'\sop's\shandling\sof\sthe\s'persistent'\soption\s(noting\sthat\swe\scannot\syet\stest\sthat\scase\sfrom\sa\sworker).
-D 2022-08-24T18:39:46.246
+C Change\sDB.exec()\srowMode\sdefault\sfrom\s'stmt'\sto\s'array',\sper\s/chat\sdiscussion.\sAdd\sDB.exec()\srowMode\soption\sfor\sfetching\sa\sspecific\scolumn\sby\sname.\sAdd\sresult\scolumn\snames\sto\sworker1\sexec()\scallback\sinterface,\sas\sthere's\sotherwise\sno\sway\sto\sget\sthat\sinfo\sfrom\sa\sworker.
+D 2022-08-24T20:57:37.430
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
 F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de
 F ext/wasm/api/sqlite3-api-glue.js 67ca83974410961953eeaa1dfed3518530d68381729ed1d27f95122f5baeabd3
-F ext/wasm/api/sqlite3-api-oo1.js 5ce93b89165e1eb6ef26ba67ae9d3c25379df74eea82edb7b46255f86db21cfc
+F ext/wasm/api/sqlite3-api-oo1.js 324b2f6817ff3711b59bd9505157f7a91fe319249d3b8b525c8254427c10504a
 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
 F ext/wasm/api/sqlite3-api-prologue.js 2d5c5d3355f55eefe51922cec5bfedbec0f8300db98a17685ab7a34a03953c7a
-F ext/wasm/api/sqlite3-api-worker1.js f7372b84b6d71ebdc0d2a9e7944ce571b4f18e0dd4c1be78282c68b4582558ca
+F ext/wasm/api/sqlite3-api-worker1.js 11a9e8b22147d948e338b25d21697178b4414dc0578fc9613aa5fc4bfe62f208
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
 F ext/wasm/api/sqlite3-wasm.c 0d81282eaeff2a6e9fc5c28a388c5c5b45cf25a9393992fa511ac009b27df982
 F ext/wasm/common/SqliteTestUtil.js eb96275bed43fdb364b7d65bcded0ca5e22aaacff120d593d1385f852f486247
@@ -511,11 +511,11 @@ F ext/wasm/scratchpad-opfs-worker2.js 5f2237427ac537b8580b1c659ff14ad2621d169404
 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
-F ext/wasm/testing-worker1-promiser.js 81d81eda77c9d4a3e43cfeee91df6c3b039782cc998020d72fe1fdf91790242d
+F ext/wasm/testing-worker1-promiser.js f4b0895b612606d04ae371d03a9ffe9ffa94a2a840da6e92742b2adf86f0783c
 F ext/wasm/testing1.html 528001c7e32ee567abc195aa071fd9820cc3c8ffc9c8a39a75e680db05f0c409
 F ext/wasm/testing1.js 2def7a86c52ff28b145cb86188d5c7a49d5993f9b78c50d140e1c31551220955
 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
-F ext/wasm/testing2.js 04a4194188d54856027eb4cad7239223a8f7a60e64b0aac81fc1a5a70363b98e
+F ext/wasm/testing2.js ab4ae24cd3ffe814370b35515aea426647a6f9d271c6542cf18e580470540615
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
@@ -2009,8 +2009,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 7467ac88801224089b51c6ba7924f93283dd87beca602a186c83632df26cfc85
-R 4d53a1266e528a954f73a792c3648256
+P 509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
+R 4d9ca8433788ec1804950abdc80ba467
 U stephan
-Z cffa6daad399e2d401f0fd69f4f04b4f
+Z aac659391ac05e725777e807ed0e05f4
 # Remove this line to create a well-formed Fossil manifest.
index 7400cbe1778ab9332806d7991326e2304d9eb303..c547adbe4a4e08bf14af2824cc1625db9b5278a0 100644 (file)
@@ -1 +1 @@
-509f8839201ec1ea4863bd31493e6c29a0721ca6340755bb96656b828758fea7
\ No newline at end of file
+1bb37e5c477b9eb098362f74a45a55be23d450fe45cdff58c1cbff08b5b3998f
\ No newline at end of file