]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a basic batch-mode SQL runner for the SAH Pool VFS, for use in comparing it again...
authorstephan <stephan@noemail.net>
Thu, 30 Nov 2023 20:34:24 +0000 (20:34 +0000)
committerstephan <stephan@noemail.net>
Thu, 30 Nov 2023 20:34:24 +0000 (20:34 +0000)
FossilOrigin-Name: 883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62

ext/wasm/GNUmakefile
ext/wasm/batch-runner-sahpool.html [new file with mode: 0644]
ext/wasm/batch-runner-sahpool.js [new file with mode: 0644]
ext/wasm/batch-runner.js
ext/wasm/demo-123.js
ext/wasm/speedtest1-worker.html
manifest
manifest.uuid

index c0cab212d8b9446ee398d8b757f74ebd50a04375..3ef478612cbe40fd2f19b4d7d6fbea3f529c5b2d 100644 (file)
@@ -854,7 +854,7 @@ dir.sql := sql
 speedtest1 := ../../speedtest1
 speedtest1.c := ../../test/speedtest1.c
 speedtest1.sql := $(dir.sql)/speedtest1.sql
-speedtest1.cliflags := --size 25 --big-transactions
+speedtest1.cliflags := --size 10 --big-transactions
 $(speedtest1):
        $(MAKE) -C ../.. speedtest1
 $(speedtest1.sql): $(speedtest1) $(MAKEFILE)
diff --git a/ext/wasm/batch-runner-sahpool.html b/ext/wasm/batch-runner-sahpool.html
new file mode 100644 (file)
index 0000000..ad7e7b5
--- /dev/null
@@ -0,0 +1,86 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
+    <link rel="stylesheet" href="common/testing.css"/>
+    <title>sqlite3-api batch SQL runner for the SAHPool VFS</title>
+  </head>
+  <body>
+    <header id='titlebar'><span>sqlite3-api batch SQL runner for the SAHPool VFS</span></header>
+    <div>
+      <span class='input-wrapper'>
+        <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
+        <label for='cb-reverse-log-order' id='lbl-reverse-log-order'>Reverse log order</label>
+      </span>
+    </div>
+    <div id='test-output' class='reverse'></div>
+    <script>
+      (function(){
+        const E = (sel)=>document.querySelector(sel);
+
+        const eOut = E('#test-output');
+        const log2 = function(cssClass,...args){
+          let ln;
+          if(1 || cssClass){
+            ln = document.createElement('div');
+            if(cssClass) ln.classList.add(cssClass);
+            ln.append(document.createTextNode(args.join(' ')));
+          }else{
+            // This doesn't work with the "reverse order" option!
+            ln = document.createTextNode(args.join(' ')+'\n');
+          }
+          eOut.append(ln);
+        };
+        const log = (...args)=>{
+          //console.log(...args);
+          log2('', ...args);
+        };
+        const logErr = function(...args){
+          console.error(...args);
+          log2('error', ...args);
+        };
+        const logWarn = function(...args){
+          console.warn(...args);
+          log2('warning', ...args);
+        };
+
+        const cbReverseLog = E('#cb-reverse-log-order');
+        const lblReverseLog = E('#lbl-reverse-log-order');
+        if(cbReverseLog.checked){
+            lblReverseLog.classList.add('warning');
+            eOut.classList.add('reverse');
+        }
+        cbReverseLog.addEventListener('change', function(){
+            if(this.checked){
+                eOut.classList.add('reverse');
+                lblReverseLog.classList.add('warning');
+            }else{
+                eOut.classList.remove('reverse');
+                lblReverseLog.classList.remove('warning');
+            }
+        }, false);
+
+        const w = new Worker('batch-runner-sahpool.js?sqlite3.dir=jswasm');
+        w.onmessage = function(msg){
+          msg = msg.data;
+          switch(msg.type){
+            case 'stdout': log(...msg.data); break;
+            case 'warn': logWarn(...msg.data); break;
+            case 'error': logErr(...msg.data); break;
+            default:
+              logErr("Unhandled worker message type:",msg);
+              break;
+          }
+        };
+      })();
+    </script>
+    <style>
+      #test-output {
+          white-space: break-spaces;
+          overflow: auto;
+      }
+    </style>
+  </body>
+</html>
diff --git a/ext/wasm/batch-runner-sahpool.js b/ext/wasm/batch-runner-sahpool.js
new file mode 100644 (file)
index 0000000..dfa5044
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+  2023-11-30
+
+  The author disclaims copyright to this source code.  In place of a
+  legal notice, here is a blessing:
+
+  *   May you do good and not evil.
+  *   May you find forgiveness for yourself and forgive others.
+  *   May you share freely, never taking more than you give.
+
+  ***********************************************************************
+
+  A basic batch SQL runner for the SAHPool VFS. This file must be run in
+  a worker thread. This is not a full-featured app, just a way to get some
+  measurements for batch execution of SQL for the OPFS SAH Pool VFS.
+*/
+'use strict';
+
+const wMsg = function(msgType,...args){
+  postMessage({
+    type: msgType,
+    data: args
+  });
+};
+const toss = function(...args){throw new Error(args.join(' '))};
+const warn = (...args)=>{ wMsg('warn',...args); };
+const error = (...args)=>{ wMsg('error',...args); };
+const log = (...args)=>{ wMsg('stdout',...args); }
+let sqlite3;
+const urlParams = new URL(globalThis.location.href).searchParams;
+const cacheSize = (()=>{
+  if(urlParams.has('cachesize')) return +urlParams.get('cachesize');
+  return 200;
+})();
+
+
+/** Throws if the given sqlite3 result code is not 0. */
+const checkSqliteRc = (dbh,rc)=>{
+  if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh));
+};
+
+const sqlToDrop = [
+  "SELECT type,name FROM sqlite_schema ",
+  "WHERE name NOT LIKE 'sqlite\\_%' escape '\\' ",
+  "AND name NOT LIKE '\\_%' escape '\\'"
+].join('');
+
+const clearDbSqlite = function(db){
+  // This would be SO much easier with the oo1 API, but we specifically want to
+  // inject metrics we can't get via that API, and we cannot reliably (OPFS)
+  // open the same DB twice to clear it using that API, so...
+  const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle);
+  log("reset db rc =",rc,db.id, db.filename);
+};
+
+const App = {
+  db: undefined,
+  cache:Object.create(null),
+  log: log,
+  warn: warn,
+  error: error,
+  metrics: {
+    fileCount: 0,
+    runTimeMs: 0,
+    prepareTimeMs: 0,
+    stepTimeMs: 0,
+    stmtCount: 0,
+    strcpyMs: 0,
+    sqlBytes: 0
+  },
+  fileList: undefined,
+  execSql: async function(name,sql){
+    const db = this.db;
+    const banner = "========================================";
+    this.log(banner,
+             "Running",name,'('+sql.length,'bytes)');
+    const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm;
+    let pStmt = 0, pSqlBegin;
+    const metrics = db.metrics = Object.create(null);
+    metrics.prepTotal = metrics.stepTotal = 0;
+    metrics.stmtCount = 0;
+    metrics.malloc = 0;
+    metrics.strcpy = 0;
+    if(this.gotErr){
+      this.error("Cannot run SQL: error cleanup is pending.");
+      return;
+    }
+    // Run this async so that the UI can be updated for the above header...
+    const endRun = ()=>{
+      metrics.evalSqlEnd = performance.now();
+      metrics.evalTimeTotal = (metrics.evalSqlEnd - metrics.evalSqlStart);
+      this.log("metrics:",JSON.stringify(metrics, undefined, ' '));
+      this.log("prepare() count:",metrics.stmtCount);
+      this.log("Time in prepare_v2():",metrics.prepTotal,"ms",
+               "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())");
+      this.log("Time in step():",metrics.stepTotal,"ms",
+               "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())");
+      this.log("Total runtime:",metrics.evalTimeTotal,"ms");
+      this.log("Overhead (time - prep - step):",
+               (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms");
+      this.log(banner,"End of",name);
+      this.metrics.prepareTimeMs += metrics.prepTotal;
+      this.metrics.stepTimeMs += metrics.stepTotal;
+      this.metrics.stmtCount += metrics.stmtCount;
+      this.metrics.strcpyMs += metrics.strcpy;
+      this.metrics.sqlBytes += sql.length;
+    };
+
+    const runner = function(resolve, reject){
+      ++this.metrics.fileCount;
+      metrics.evalSqlStart = performance.now();
+      const stack = wasm.scopedAllocPush();
+      try {
+        let t, rc;
+        let sqlByteLen = sql.byteLength;
+        const [ppStmt, pzTail] = wasm.scopedAllocPtr(2);
+        t = performance.now();
+        pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed");
+        metrics.malloc = performance.now() - t;
+        metrics.byteLength = sqlByteLen;
+        let pSql = pSqlBegin;
+        const pSqlEnd = pSqlBegin + sqlByteLen;
+        t = performance.now();
+        wasm.heap8().set(sql, pSql);
+        wasm.poke(pSql + sqlByteLen, 0);
+        //log("SQL:",wasm.cstrToJs(pSql));
+        metrics.strcpy = performance.now() - t;
+        let breaker = 0;
+        while(pSql && wasm.peek8(pSql)){
+          wasm.pokePtr(ppStmt, 0);
+          wasm.pokePtr(pzTail, 0);
+          t = performance.now();
+          rc = capi.sqlite3_prepare_v2(
+            db.handle, pSql, sqlByteLen, ppStmt, pzTail
+          );
+          metrics.prepTotal += performance.now() - t;
+          checkSqliteRc(db.handle, rc);
+          pStmt = wasm.peekPtr(ppStmt);
+          pSql = wasm.peekPtr(pzTail);
+          sqlByteLen = pSqlEnd - pSql;
+          if(!pStmt) continue/*empty statement*/;
+          ++metrics.stmtCount;
+          t = performance.now();
+          rc = capi.sqlite3_step(pStmt);
+          capi.sqlite3_finalize(pStmt);
+          pStmt = 0;
+          metrics.stepTotal += performance.now() - t;
+          switch(rc){
+            case capi.SQLITE_ROW:
+            case capi.SQLITE_DONE: break;
+            default: checkSqliteRc(db.handle, rc); toss("Not reached.");
+          }
+        }
+        resolve(this);
+      }catch(e){
+        if(pStmt) capi.sqlite3_finalize(pStmt);
+        this.gotErr = e;
+        reject(e);
+      }finally{
+        capi.sqlite3_exec(db.handle,"rollback;",0,0,0);
+        wasm.scopedAllocPop(stack);
+      }
+    }.bind(this);
+    const p = new Promise(runner);
+    return p.catch(
+      (e)=>this.error("Error via execSql("+name+",...):",e.message)
+    ).finally(()=>{
+      endRun();
+    });
+  },
+
+  /**
+     Loads batch-runner.list and populates the selection list from
+     it. Returns a promise which resolves to nothing in particular
+     when it completes. Only intended to be run once at the start
+     of the app.
+  */
+  loadSqlList: async function(){
+    const infile = 'batch-runner.list';
+    this.log("Loading list of SQL files:", infile);
+    let txt;
+    try{
+      const r = await fetch(infile);
+      if(404 === r.status){
+        toss("Missing file '"+infile+"'.");
+      }
+      if(!r.ok) toss("Loading",infile,"failed:",r.statusText);
+      txt = await r.text();
+    }catch(e){
+      this.error(e.message);
+      throw e;
+    }
+    App.fileList = txt.split(/\n+/).filter(x=>!!x);
+    this.log("Loaded",infile);
+  },
+
+  /** Fetch ./fn and return its contents as a Uint8Array. */
+  fetchFile: async function(fn, cacheIt=false){
+    if(cacheIt && this.cache[fn]) return this.cache[fn];
+    this.log("Fetching",fn,"...");
+    let sql;
+    try {
+      const r = await fetch(fn);
+      if(!r.ok) toss("Fetch failed:",r.statusText);
+      sql = new Uint8Array(await r.arrayBuffer());
+    }catch(e){
+      this.error(e.message);
+      throw e;
+    }
+    this.log("Fetched",sql.length,"bytes from",fn);
+    if(cacheIt) this.cache[fn] = sql;
+    return sql;
+  }/*fetchFile()*/,
+
+  /**
+     Converts this.metrics() to a form which is suitable for easy conversion to
+     CSV. It returns an array of arrays. The first sub-array is the column names.
+     The 2nd and subsequent are the values, one per test file (only the most recent
+     metrics are kept for any given file).
+  */
+  metricsToArrays: function(){
+    const rc = [];
+    Object.keys(this.dbs).sort().forEach((k)=>{
+      const d = this.dbs[k];
+      const m = d.metrics;
+      delete m.evalSqlStart;
+      delete m.evalSqlEnd;
+      const mk = Object.keys(m).sort();
+      if(!rc.length){
+        rc.push(['db', ...mk]);
+      }
+      const row = [k.split('/').pop()/*remove dir prefix from filename*/];
+      rc.push(row);
+      row.push(...mk.map((kk)=>m[kk]));
+    });
+    return rc;
+  },
+
+  metricsToBlob: function(colSeparator='\t'){
+    const ar = [], ma = this.metricsToArrays();
+    if(!ma.length){
+      this.error("Metrics are empty. Run something.");
+      return;
+    }
+    ma.forEach(function(row){
+      ar.push(row.join(colSeparator),'\n');
+    });
+    return new Blob(ar);
+  },
+
+  /**
+     Fetch file fn and eval it as an SQL blob. This is an async
+     operation and returns a Promise which resolves to this
+     object on success.
+  */
+  evalFile: async function(fn){
+    const sql = await this.fetchFile(fn);
+    return this.execSql(fn,sql);
+  }/*evalFile()*/,
+
+  /**
+     Fetches the handle of the db associated with
+     this.e.selImpl.value, opening it if needed.
+  */
+  initDb: function(){
+    const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm;
+    const stack = wasm.scopedAllocPush();
+    let pDb = 0;
+    const d = Object.create(null);
+    d.filename = "/batch.db";
+    try{
+      const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
+      const ppDb = wasm.scopedAllocPtr();
+      const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, this.PoolUtil.vfsName);
+      pDb = wasm.peekPtr(ppDb)
+      if(rc) toss("sqlite3_open_v2() failed with code",rc);
+      capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0);
+      this.log("cache_size =",cacheSize);
+    }catch(e){
+      if(pDb) capi.sqlite3_close_v2(pDb);
+      throw e;
+    }finally{
+      wasm.scopedAllocPop(stack);
+    }
+    d.handle = pDb;
+    this.log("Opened db:",d.filename,'@',d.handle);
+    return d;
+  },
+
+  closeDb: function(){
+    if(this.db.handle){
+      this.sqlite3.capi.sqlite3_close_v2(this.db.handle);
+      this.db.handle = undefined;
+    }
+  },
+
+  run: async function(sqlite3){
+    delete this.run;
+    this.sqlite3 = sqlite3;
+    const capi = sqlite3.capi, wasm = sqlite3.wasm;
+    this.log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
+    this.log("WASM heap size =",wasm.heap8().length);
+    let timeStart;
+    sqlite3.installOpfsSAHPoolVfs({
+      clearOnInit: true, initialCapacity: 4,
+      name: 'batch-sahpool',
+      verbosity: 2
+    }).then(PoolUtil=>{
+      App.PoolUtil = PoolUtil;
+      App.db = App.initDb();
+    })
+      .then(async ()=>this.loadSqlList())
+      .then(async ()=>{
+        timeStart = performance.now();
+        for(let i = 0; i < App.fileList.length; ++i){
+          const fn = App.fileList[i];
+          await App.evalFile(fn);
+          if(App.gotErr) throw App.gotErr;
+        }
+      })
+      .then(()=>{
+        App.metrics.runTimeMs = performance.now() - timeStart;
+        App.log("total metrics:",JSON.stringify(App.metrics, undefined, ' '));
+        App.log("Reload the page to run this again.");
+        App.closeDb();
+        App.PoolUtil.removeVfs();
+      })
+      .catch(e=>this.error("ERROR:",e));
+  }/*run()*/
+}/*App*/;
+
+let sqlite3Js = 'sqlite3.js';
+if(urlParams.has('sqlite3.dir')){
+  sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
+}
+importScripts(sqlite3Js);
+globalThis.sqlite3InitModule().then(async function(sqlite3_){
+  log("Done initializing. Running batch runner...");
+  sqlite3 = sqlite3_;
+  App.run(sqlite3_);
+});
index ff287a66e7b208878d0694b38dd572c4c82c6850..e7a322b7f01a80e75f02a9e26247e786a054f1fb 100644 (file)
@@ -72,7 +72,6 @@
     App.logHtml("reset db rc =",rc,db.id, db.filename);
   };
 
-  
   const E = (s)=>document.querySelector(s);
   const App = {
     e: {
     db: Object.create(null),
     dbs: Object.create(null),
     cache:{},
+    metrics: {
+      fileCount: 0,
+      runTimeMs: 0,
+      prepareTimeMs: 0,
+      stepTimeMs: 0,
+      stmtCount: 0,
+      strcpyMs: 0,
+      sqlBytes: 0
+    },
     log: console.log.bind(console),
     warn: console.warn.bind(console),
     cls: function(){this.e.output.innerHTML = ''},
                    "Running",name,'('+sql.length,'bytes) using',db.id);
       const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm;
       let pStmt = 0, pSqlBegin;
-      const stack = wasm.scopedAllocPush();
       const metrics = db.metrics = Object.create(null);
       metrics.prepTotal = metrics.stepTotal = 0;
       metrics.stmtCount = 0;
         this.logHtml("Overhead (time - prep - step):",
                      (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms");
         this.logHtml(banner,"End of",name);
+        this.metrics.prepareTimeMs += metrics.prepTotal;
+        this.metrics.stepTimeMs += metrics.stepTotal;
+        this.metrics.stmtCount += metrics.stmtCount;
+        this.metrics.strcpyMs += metrics.strcpy;
+        this.metrics.sqlBytes += sql.length;
       };
 
       let runner;
         }.bind(this);
       }else{/*sqlite3 db...*/
         runner = function(resolve, reject){
+          ++this.metrics.fileCount;
           metrics.evalSqlStart = performance.now();
+          const stack = wasm.scopedAllocPush();
           try {
             let t;
             let sqlByteLen = sql.byteLength;
       let p;
       if(1){
         p = new Promise(function(res,rej){
-          setTimeout(()=>runner(res, rej), 50)/*give UI a chance to output the "running" banner*/;
+          setTimeout(()=>runner(res, rej), 0)/*give UI a chance to output the "running" banner*/;
         });
       }else{
         p = new Promise(runner);
       });
       return new Blob(ar);
     },
-    
+
     downloadMetrics: function(){
       const b = this.metricsToBlob();
       if(!b) return;
         const timeTotal = performance.now() - timeStart;
         who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))");
         who.clearStorage();
+        App.metrics.runTimeMs = timeTotal;
+        who.logHtml("Total metrics:",JSON.stringify(App.metrics,undefined,'  '));
       }, false);
     }/*run()*/
   }/*App*/;
index 8e03ee80fa0e46751726727834e7a2aefd9dd47a..9f90ca7568d094bb52f86dafc7a51226bc34d214 100644 (file)
@@ -20,7 +20,7 @@
      the main (UI) thread.
   */
   let logHtml;
-  if(self.window === self /* UI thread */){
+  if(globalThis.window === globalThis /* UI thread */){
     console.log("Running demo from main UI thread.");
     logHtml = function(cssClass,...args){
       const ln = document.createElement('div');
   }/*demo1()*/;
 
   log("Loading and initializing sqlite3 module...");
-  if(self.window!==self) /*worker thread*/{
+  if(globalThis.window!==globalThis) /*worker thread*/{
     /*
       If sqlite3.js is in a directory other than this script, in order
       to get sqlite3.js to resolve sqlite3.wasm properly, we have to
       that's not needed.
 
       URL arguments passed as part of the filename via importScripts()
-      are simply lost, and such scripts see the self.location of
+      are simply lost, and such scripts see the globalThis.location of
       _this_ script.
     */
     let sqlite3Js = 'sqlite3.js';
-    const urlParams = new URL(self.location.href).searchParams;
+    const urlParams = new URL(globalThis.location.href).searchParams;
     if(urlParams.has('sqlite3.dir')){
       sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js;
     }
     importScripts(sqlite3Js);
   }
-  self.sqlite3InitModule({
-    // We can redirect any stdout/stderr from the module
-    // like so...
+  globalThis.sqlite3InitModule({
+    /* We can redirect any stdout/stderr from the module like so, but
+       note that doing so makes use of Emscripten-isms, not
+       well-defined sqlite APIs. */
     print: log,
     printErr: error
   }).then(function(sqlite3){
index 3adf8f2ee9d5b32dcecb091d6339c5d61b828d58..8c9a77dc5e2d6a3f4f13121de5cf608f22c78244 100644 (file)
                 eControls.classList.remove('hidden');
                 break;
             case 'stdout': log(msg.data); break;
-            case 'stdout': logErr(msg.data); break;
+            case 'stderr': logErr(msg.data); break;
             case 'run-start':
                 eControls.disabled = true;
                 log("Running speedtest1 with argv =",msg.data.join(' '));
index 9f44093bfea062465c05ad8e290afa0d622950ae..d6a02fa193c4e7c7e06b9aa77e433f1cd403ccb1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C New\sJSON\stest\scases\sshowing\sinsert\sor\sset\swith\smissing\ssubstructure.
-D 2023-11-30T16:16:10.846
+C Add\sa\sbasic\sbatch-mode\sSQL\srunner\sfor\sthe\sSAH\sPool\sVFS,\sfor\suse\sin\scomparing\sit\sagainst\sWebSQL.\sBring\sthe\sWebSQL\sbatch\srunner\sup\sto\sdate,\snoting\sthat\sit\scannot\srun\swithout\saddition\sof\san\s"origin\strial"\sactivation\skey\sfrom\sGoogle\sbecause\sthat's\snow\sthe\sonly\sway\sto\senable\sWebSQL\sin\sChrome\s(that\spart\sis\snot\schecked\sin\sbecause\sthat\skey\sis\sprivate).\sMinor\scode-adjacent\scleanups.
+D 2023-11-30T20:34:24.440
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -568,7 +568,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
 F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
-F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2
+F ext/wasm/GNUmakefile 57439eec2b8b4d4074e5d861e93aab3f32bb0f44339a2b472c23f4e638b7e8a3
 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
 F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
@@ -598,8 +598,10 @@ F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 46c4afa6c50d7369252c104f274ad977a97e91cc
 F ext/wasm/api/sqlite3-wasm.c d0e09eb5ed3743c00294e30019e591c3aa150572ae7ffe8a8994568a7377589f
 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 569d4e859968e65f55dec5fac0b879828a23c8b179162cc7812fec19f844dd21
 F ext/wasm/api/sqlite3-worker1.c-pp.js a541112aa51e16705f13a99bb943c64efe178aa28c86704a955f8fd9afe4ba37
+F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7
+F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd
 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
-F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e
+F ext/wasm/batch-runner.js 05ec254f5dbfe605146d9640b3db17d6ef8c3fbef6aa8396051ca72bb5884e3f
 F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25b4
 F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
@@ -607,7 +609,7 @@ F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a7
 F ext/wasm/common/whwasmutil.js 4c64594eecc7af4ae64259e95a71ba2a7edf118881aaff0bba86d0c7164e78e4
 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
 F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
-F ext/wasm/demo-123.js 38aa8faec4d0ace1c973bc8a7a1533584463ebeecd4c420daa7d9687beeb9cb5
+F ext/wasm/demo-123.js c7b3cca50c55841c381a9ca4f9396e5bbdc6114273d0b10a43e378e32e7be5bf
 F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e
 F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8
 F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98ab22f5786620b3354ed15f
@@ -630,7 +632,7 @@ F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d
 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
 F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d
 F ext/wasm/speedtest1-wasmfs.mjs ac5cadbf4ffe69e9eaac8b45e8523f030521e02bb67d654c6eb5236d9c456cbe
-F ext/wasm/speedtest1-worker.html e33e2064bda572c0c3ebaec7306c35aa758d9d27e245d67e807f8cc4a9351cc5
+F ext/wasm/speedtest1-worker.html 864b65ed78ce24847a348c180e7f267621a02ca027068a1863ec1c90187c1852
 F ext/wasm/speedtest1-worker.js 4d2ea70a3c24e05bdca78025202841f33d298c4fa9541a0070c3228661f89ecd
 F ext/wasm/speedtest1.html ff048b4a623aa192e83e143e48f1ce2a899846dd42c023fdedc8772b6e3f07da
 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
@@ -2143,8 +2145,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 263f6d3a7784ef7d032dbf7a3265aca8dd70bf50797f28f6b2e8ddb6a301f83a
-R 0e9b3f433035365e3e741a55f5ab0daf
-U drh
-Z 72d2be7125d7371db00777b78c0fb9f8
+P 6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2
+R 0685d189d0b0c5921f2e4eda3787211e
+U stephan
+Z 5692f67161061876d9b3bcabcb3928cc
 # Remove this line to create a well-formed Fossil manifest.
index fcbc7b3b7f22532a099417035fb68e36f3c4c158..98d7aac12d209286b6747d0c8cecf6dd91e64032 100644 (file)
@@ -1 +1 @@
-6802b6459d0d16c961ff41d240a6c88287f197d8f609090f79308707490a49c2
\ No newline at end of file
+883990e7938c1f63906300a6113f0fadce143913b7c384e8aeb5f886f0be7c62
\ No newline at end of file