]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Get kvvfs v2 storage import working by disallowing it when there are opened db/journa...
authorstephan <stephan@noemail.net>
Mon, 24 Nov 2025 15:17:52 +0000 (15:17 +0000)
committerstephan <stephan@noemail.net>
Mon, 24 Nov 2025 15:17:52 +0000 (15:17 +0000)
FossilOrigin-Name: c9e0b32278290baf987b5a46bd47358439e3d0f190b2879a965d6e4262ea7baf

ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js
ext/wasm/tester1.c-pp.js
manifest
manifest.uuid

index e0334a6ec3b56e909060670c12d7ddbda9bb0d8b..376dcf6ac2cb2fea3e0e7bc482608658cfaf3a39 100644 (file)
@@ -144,7 +144,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       if( !hop(this.#map, k) ){
         this.#keys = null;
       }
-      this.#map[k] = v;
+      this.#map[k] = ''+v;
     }
 
     removeItem(k){
@@ -163,6 +163,40 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }
   }/*KVVfsStorage*/;
 
+  /** True if v is one of the special persistant Storage objects. */
+  const kvvfsIsPersistentName = (v)=>'local'===v || 'session'===v;
+
+  /**
+     Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the
+     db name. This key is redundant in JS but it's how kvvfs works (it
+     saves each key to a separate file, so needs a distinct namespace
+     per data source name). We retain this prefix in 'local' and
+     'session' storage for backwards compatibility but elide them from
+     "v2" storage, where they're superfluous.
+  */
+  const kvvfsKeyPrefix = (v)=>kvvfsIsPersistentName(v) ? 'kvvfs-'+v+'-' : '';
+
+  /**
+     Create a new instance of the objects which go into
+     cache.storagePool.
+  */
+  const createStorageObj = (name,storage)=>Object.assign(Object.create(null),{
+    jzClass: name,
+    refc: 1,
+    storage: storage || new KVVfsStorage,
+    /* This is the storage prefix used for kvvfs keys.  It is
+       "kvvfs-STORAGENAME-" for local/session storage and an empty
+       string for transient storage. local/session storage must
+       use the long form for backwards compatibility.
+
+       This prefix mirrors the one generated by os_kv.c's
+       kvrecordMakeKey() and must stay in sync with that one.
+    */
+    keyPrefix: kvvfsKeyPrefix(name),
+    files: [/*KVVfsFile instances currently using this storage*/]
+  });
+
+
   /**
      Map of JS-stringified KVVfsFile::zClass names to
      reference-counted Storage objects. These objects are created in
@@ -171,47 +205,27 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      that concurrent active xOpen()s on a given name, and within a
      given thread, use the same storage object.
   */
+  /* Start off with mappings for well-known names. */
   cache.storagePool = Object.assign(Object.create(null),{
-    /* Start off with mappings for well-known names. */
-    localThread: {
-      refc: 3/*never reaches 0*/,
-      storage: new KVVfsStorage,
-      files: [/*KVVfsFile instances currently using this storage*/]
-    }
+    localThread: createStorageObj('localThread')
   });
+
   if( globalThis.localStorage instanceof globalThis.Storage ){
-    cache.storagePool.local = {
-      refc: 3/*never reaches 0*/,
-      storage: globalThis.localStorage,
-      /* This is the storage prefix used for kvvfs keys.  It is
-         "kvvfs-STORAGENAME-" for local/session storage and an empty
-         string for transient storage. local/session storage must
-         use the long form for backwards compatibility.
-
-         This prefix mirrors the one generated by os_kv.c's
-         kvrecordMakeKey() and must stay in sync with that one.
-      */
-      keyPrefix: "kvvfs-local-",
-      files: []
-    };
+    cache.storagePool.local = createStorageObj('local');
   }
   if( globalThis.sessionStorage instanceof globalThis.Storage ){
-    cache.storagePool.session = {
-      refc: 3/*never reaches 0*/,
-      storage: globalThis.sessionStorage,
-      keyPrefix: "kvvfs-session-",
-      files: []
-    }
+    cache.storagePool.session = createStorageObj('session');
   }
+
   for(const k of Object.keys(cache.storagePool)){
     /* Journals in kvvfs are are stored as individual records within
-       their Storage-ish object, named "KEYPREFIXjrnl" (see above
-       re. KEYPREFIX). We always map the db and its journal to the
-       same Storage object. */
+       their Storage-ish object, named "{storage.keyPrefix}jrnl".  We
+       always map the db and its journal to the same Storage
+       object. */
     const orig = cache.storagePool[k];
-    orig.jzClass = k;
     cache.storagePool[k+'-journal'] = orig;
   }
+
   /**
      Returns the storage object mapped to the given string zClass
      (C-string pointer or JS string).
@@ -221,19 +235,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         ? cache.storagePool[zClass]
         : cache.storagePool[wasm.cstrToJs(zClass)];
 
-  /** True if v is one of the special persistant Storage objects. */
-  const kvvfsIsPersistentName = (v)=>'local'===v || 'session'===v;
-
-  /**
-     Keys in kvvfs have a prefix of "kvvfs-NAME-", where NAME is the
-     db name. This key is redundant in JS but it's how kvvfs works (it
-     saves each key to a separate file, so needs a distinct namespace
-     per data source name). We retain this prefix in 'local' and
-     'session' storage for backwards compatibility but elide them from
-     "v2" storage, where they're superfluous.
-  */
-  const kvvfsKeyPrefix = (v)=>kvvfsIsPersistentName(v) ? 'kvvfs-'+v+'-' : '';
-
   /**
      Internal helper for sqlite3_js_kvvfs_clear() and friends.  Its
      argument should be one of ('local','session',"") or the name of
@@ -347,6 +348,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     let rc = 0;
     const store = kvvfsWhich(which);
     const keyPrefix = store.prefix;
+    /**
+       Historically this code had no way to check whether the storage
+       was in use before wiping it, so could not error in that
+       case. Whether or not it should, not that it can (as of late
+       2025-11), is TBD.
+    */
     store.stores.forEach((s)=>{
       const toRm = [] /* keys to remove */;
       let i, n = s.length;
@@ -626,16 +633,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
                     : jzClass + '-journal';
               s = cache.storagePool[jzClass]
                 = cache.storagePool[other]
-                = Object.assign(Object.create(null),{
-                  jzClass,
-                  refc: 1/* if this is a db-open, the journal open
-                            will follow soon enough and bump the
-                            refcount. If we start at 2 here, that
-                            pending open will increment it again. */,
-                  storage: new KVVfsStorage,
-                  keyPrefix: '',
-                  files: [f]
-                });
+                = createStorageObj(jzClass);
+              s.files.push(f);
               debug("xOpen installed storage handles [",
                     jzClass, other,"]", s);
             }
@@ -862,6 +861,167 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }
   }/*native method overrides*/
 
+
+  /**
+     Copies the entire contents of the given transient storage object
+     into a JSON-friendly form.  The returned object is structured as
+     follows...
+
+     - "name": the name of the storage. This is 'local' or 'session'
+     for localStorage resp. sessionStorage, and an arbitrary name for
+     transient storage. This propery may be changed before passing
+     this object to sqlite3_js_kvvfs_import_storage() in order to
+     import into a different storage object.
+
+     - "timestamp": the time this function was called, in Unix
+     epoch milliseconds.
+
+     - "size": the unencoded db size.
+
+     - "journal": if includeJournal is true and this db has a
+     journal, it is stored as a string here, otherwise this property
+     is not set.
+
+     - "pages": An array holddig the object holding the raw encoded
+     db pages in their proper order.
+
+     Throws if this db is not opened.
+
+     The encoding of the underlying database is not part of this
+     interface - it is simply passed on as-is. Interested parties
+     are directed to src/os_kv.c in the SQLite source tree.
+
+     Added in version 3.?? (tenatively 3.52).
+  */
+  capi.sqlite3_js_kvvfs_export_storage = function(storageName,includeJournal=true){
+    const store = storageForZClass(storageName);
+    if( !store ){
+      util.toss3(capi.SQLITE_NOTFOUND,
+                 "There is no kvvfs storage named",storageName);
+    }
+    debug("store to export=",store);
+    const s = store.storage;
+    const rc = Object.assign(Object.create(null),{
+      name: store.jzClass,
+      timestamp: Date.now(),
+      pages: []
+    });
+    const pages = Object.create(null);
+    const keyPrefix = kvvfsKeyPrefix(rc.name);
+    const rxTail = keyPrefix
+          ? /^kvvfs-[^-]+-(\w+)/ /* X... part of kvvfs-NAME-X... */
+          : undefined;
+    let i = 0, n = s.length;
+    for( ; i < n; ++i ){
+      const k = s.key(i);
+      if( !keyPrefix || k.startsWith(keyPrefix) ){
+        let kk = (keyPrefix ? rxTail.exec(k) : undefined)?.[1] ?? k;
+        switch( kk ){
+          case 'jrnl':
+            if( includeJournal ) rc.journal = s.getItem(k);
+            break;
+          case 'sz':
+            rc.size = +s.getItem(k);
+            break;
+          default:
+            kk = +kk /* coerce to number */;
+            if( !util.isInt32(kk) || kk<=0 ){
+              util.toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: "+k);
+            }
+            pages[kk] = s.getItem(k);
+            break;
+        }
+      }
+    }
+    /* Now sort the page numbers and move them into an array. In JS
+       property keys are always strings, so we have to coerce them to
+       numbers so we can get them sorted properly for the array. */
+    Object.keys(pages).map((v)=>+v).sort().forEach(
+      (v)=>rc.pages.push(pages[v])
+    );
+    return rc;
+  }/* capi.sqlite3_js_kvvfs_export_storage */;
+
+  /**
+     INCOMPLETE. This interface is subject to change.
+
+     The counterpart of sqlite3_js_kvvfs_export_storage(). Its
+     argument must be the result of that function().
+
+     This either replaces the contents of an existing transient
+     storage object or installs one named exp.name, setting
+     the storage's db contents to that of the exp object.
+
+     Throws on error. Error conditions include:
+
+     - The give storage object is currently opened by any db.
+     Performing this page-by-page import would invoke undefined
+     behavior on them.
+
+     - Malformed input object.
+
+     If it throws after starting the import then it clears the
+     storage before returning, to avoid leaving the db in an
+     undefined state. It may throw for any of the above-listed
+     conditions before reaching that step, in which case the db is
+     not modified.
+  */
+  capi.sqlite3_js_kvvfs_import_storage = function(exp, overwrite=false){
+    if( !exp?.timestamp
+        || !exp.name
+        || undefined===exp.size
+        || exp.size<0 || exp.size>=0x7fffffff
+        || !Array.isArray(exp.pages) ){
+      util.toss3(capi.SQLITE_MISUSE, "Malformed export object.");
+    }
+    //warn("importFromObject() is incomplete");
+    let store = storageForZClass(exp.name);
+    if( store ){
+      if( !overwrite ){
+        //warn("Storage exists:",arguments,store);
+        util.toss3(capi.SQLITE_ACCESS,
+                   "Storage '"+exp.name+"' already exists and",
+                   "overwrite was not specified.");
+      }else if( !store.files || !store.jzClass ){
+        util.toss3(capi.SQLITE_ERROR,
+                   "Internal storage object", exp.name,"seems to be malformed.");
+      }else if( store.files.length ){
+        util.toss3(capi.SQLITE_IOERR_ACCESS,
+                   "Cannot import db storage while it is in use.");
+      }
+      capi.sqlite3_js_kvvfs_clear(exp.name);
+    }else{
+      if( cache.rxJournalSuffix.test(exp.name) ){
+        /* This isn't actually a problem, but the public API does not
+           specifically expose the '-journal' name of the storage so
+           exporting it "shouldn't happen." */
+        util.toss3(capi.SQLITE_MISUSE,
+                   "Cowardly refusing to create storage with a",
+                   "'-journal' suffix.");
+      }
+      store = createStorageObj(exp.name);
+      cache.storagePool[exp.name] =
+        cache.storagePool[exp.name+'-journal'] = store;
+      //warn("Installing new storage:",store);
+    }
+    //debug("Importing store",store.cts.files.length, store);
+    //debug("object to import:",exp);
+    const keyPrefix = kvvfsKeyPrefix(exp.name);
+    try{
+      /* Force the native KVVfsFile instances to re-read the db
+         and page size. */;
+      const s = store.storage;
+      s.setItem(keyPrefix+'sz', exp.size);
+      if( exp.journal ) s.setItem(keyPrefix+'jrnl', exp.journal);
+      exp.pages.forEach((v,ndx)=>s.setItem(keyPrefix+(ndx+1), v));
+      s.getItem("")/*kludge: for KVVfsStorage to reset its keys*/;
+    }catch(e){
+      capi.sqlite3_js_kvvfs_clear(exp.name);
+      throw e;
+    }
+    return this;
+  };
+
   if(sqlite3?.oo1?.DB){
     /**
        Functionally equivalent to DB(storageName,'c','kvvfs') except
@@ -916,176 +1076,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       return jdb.storageSize(this.affirmOpen().filename);
     };
 
-    /**
-       Copies the entire contents of this db into a JSON-friendly
-       form.  The returned object is structured as follows...
-
-       - "name": the name of the db. This is 'local' or 'session' for
-       localStorage resp. sessionStorage, and an arbitrary name for
-       transient storage. This propery may be changed before passing
-       this object to importFromObject() in order to import into a
-       different storage object.
-
-       - "timestamp": the time this function was called, in Unix
-       epoch milliseconds.
-
-       - "size": the unencoded db size.
-
-       - "journal": if includeJournal is true and this db has a
-       journal, it is stored as a string here, otherwise this property
-       is not set.
-
-       - "pages": An array holddig the object holding the raw encoded
-       db pages in their proper order.
-
-       Throws if this db is not opened.
-
-       The encoding of the underlying database is not part of this
-       interface - it is simply passed on as-is. Interested parties
-       are directed to src/os_kv.c in the SQLite source tree.
-
-       Trivia: for non-trivial databases, this object's JSON encoding
-       will be slightly smaller that the full db, as this
-       representation strips out some repetitive parts.
-
-       Added in version 3.?? (tenatively 3.52).
-    */
-    jdb.prototype.exportToObject = function(includeJournal=true){
-      this.affirmOpen();
-      const store = storageForZClass(this.affirmOpen().filename);
-      if( !store ){
-        util.toss3(capi.SQLITE_ERROR,"kvvfs db '",
-                   this.filename,"' has no storage object.");
-      }
-      debug("store=",store);
-      const s = store.storage;
-      const rc = Object.assign(Object.create(null),{
-        name: this.filename,
-        timestamp: Date.now(),
-        pages: []
-      });
-      const pages = Object.create(null);
-      const keyPrefix = kvvfsKeyPrefix(rc.name);
-      const rxTail = keyPrefix
-            ? /^kvvfs-[^-]+-(\w+)/ /* X... part of kvvfs-NAME-X... */
-            : undefined;
-      let i = 0, n = s.length;
-      for( ; i < n; ++i ){
-        const k = s.key(i);
-        if( !keyPrefix || k.startsWith(keyPrefix) ){
-          let kk = (keyPrefix ? rxTail.exec(k) : undefined)?.[1] ?? k;
-          switch( kk ){
-            case 'jrnl':
-              if( includeJournal ) rc.journal = s.getItem(k);
-              break;
-            case 'sz':
-              rc.size = +s.getItem(k);
-              break;
-            default:
-              kk = +kk /* coerce to number */;
-              if( !util.isInt32(kk) || kk<=0 ){
-                util.toss3(capi.SQLITE_RANGE, "Malformed kvvfs key: "+k);
-              }
-              pages[kk] = s.getItem(k);
-              break;
-          }
-        }
-      }
-      /* Now sort the page numbers and move them into an array. In JS
-         property keys are always strings, so we have to coerce them to
-         numbers so we can get them sorted properly for the array. */
-      Object.keys(pages).map((v)=>+v).sort().forEach(
-        (v)=>rc.pages.push(pages[v])
-      );
-      return rc;
-    };
-
-    /**
-       Does not yet work: it imports the db but the handle cannot
-       read from the modified-underneath-it storage yet. We have to
-       figure out how to get the file to re-read the db size.
-
-       The counterpart of exportToObject(). Its argument must be
-       the result of exportToObject().
-
-       This necessarily wipes out the whole database storage, so
-       invoking this while the db is in active use invokes undefined
-       behavior.
-
-       Returns this object on success. Throws on error. Error
-       conditions include:
-
-       - This db is closed.
-
-       - A transaction is active.
-
-       - If any statements are open.
-
-       - Malformed input object.
-
-       - Other handles to the same storage object are opened.
-       Performing this page-by-page import would invoke undefined
-       behavior on them.
-
-       Those are the error case it can easily cover. The room for
-       undefined behavior in wiping a db's storage out from under it
-       is a whole other potential minefield.
-
-       If it throws after starting the input then it clears the
-       storage before returning, to avoid leaving the db in an
-       undefined state. It may throw for any of the above-listed
-       conditions before reaching that step, in which case the db is
-       not modified.
-    */
-    jdb.prototype.importFromObject = function(exp){
-      this.affirmOpen();
-      if( !exp?.timestamp
-          || !exp.name
-          || undefined===exp.size
-          || exp.size<0 || exp.size>=0x7fffffff
-          || !Array.isArray(exp.pages) ){
-        util.toss3(capi.SQLITE_MISUSE, "Malformed export object.");
-      }else if( capi.sqlite3_next_stmt(this.pointer, null) ){
-        util.toss3(capi.SQLITE_MISUSE,
-                   "Cannot import when statements are active.");
-      }else if( capi.sqlite3_txn_state(this.pointer, null)>0 ){
-        util.toss3(capi.SQLITE_MISUSE,
-                   "Cannot import the db while a transaction is active.");
-      }
-      //warn("importFromObject() is incomplete");
-      const store = kvvfsWhich(this.filename);
-      if( !store?.cts ){
-        util.toss3(capi.SQLITE_ERROR,
-                   "Somehow missing a storage object for", this.filename);
-      }else if( store.cts.files.length>1 ){
-        util.toss3(capi.SQLITE_IOERR_ACCESS,
-                   "Cannot import a db when multiple handles to it",
-                   "are opened.");
-      }
-      //debug("Importing store",store.cts.files.length, store);
-      //debug("object to import:",exp);
-      const keyPrefix = kvvfsKeyPrefix(this.filename);
-      this.clearStorage();
-      try{
-        /* Force the native KVVfsFile instances to re-read the db
-           and page size. */;
-        const s = store.cts.storage;
-        s.setItem(keyPrefix+'sz', exp.size);
-        if( exp.journal ) s.setItem(keyPrefix+'jrnl', exp.journal);
-        exp.pages.forEach((v,ndx)=>s.setItem(keyPrefix+(ndx+1), v));
-        //debug("imported:",this.exportToObject());
-        //this.exec("pragma page_size");
-        for( const f of store.cts.files ){
-          f.$szDb = exp.size;
-          f.$szPage = -1;
-        }
-      }catch(e){
-        this.clearStorage();
-        throw e;
-      }
-      return this;
-    };
-
     if( sqlite3.__isUnderTest ){
       jdb.test = {
         kvvfsWhich,
index 4393a35c2f97514aed921b94ab3f4952c3a22e39..3176d0a9dd273f6052f3bde8099f9d4924260c51 100644 (file)
@@ -2941,10 +2941,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
           T.assert(3 === db.selectValue('select count(*) from kvvfs'));
           close();
 
+          const exportDb = capi.sqlite3_js_kvvfs_export_storage;
           db = new JDb(filename);
           db.exec('insert into kvvfs(a) values(4),(5),(6)');
           T.assert(6 === db.selectValue('select count(*) from kvvfs'));
-          const exp = db.exportToObject(true);
+          const exp = exportDb(filename,true);
           T.assert( filename===exp.name, "Broken export filename" )
             .assert( exp?.size > 0, "Missing db size" )
             .assert( exp?.pages?.length > 0, "Missing db pages" );
@@ -2954,7 +2955,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
           db = new JDb('new-storage');
           db.exec(sqlSetup);
           T.assert(3 === db.selectValue('select count(*) from kvvfs'));
-          console.debug("kvvfs to Object:",db.exportToObject(true));
+          console.debug("kvvfs to Object:",exportDb(db.filename));
           const n = db.storageSize();
           T.assert( n>0, "Db size count failed" );
 
@@ -2996,6 +2997,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
         ];
         const sqlCount = 'select count(*) from kvvfs';
         try {
+          const exportDb = capi.sqlite3_js_kvvfs_export_storage;
           db = new JDb(filename);
           db.clearStorage(/*must not throw*/);
           db.exec(sqlSetup);
@@ -3004,7 +3006,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
           duo = new JDb(filename);
           duo.exec('insert into kvvfs(a) values(4),(5),(6)');
           T.assert(6 === db.selectValue(sqlCount));
-          console.debug("duo.exportToObject()",duo.exportToObject(false));
+          let exp = exportDb(filename);
+          console.debug("exported db",exp);
           db.close();
           T.assert(6 === duo.selectValue(sqlCount));
           duo.close();
@@ -3014,18 +3017,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
             finally{ddb.close()}
           }, /.*no such table: kvvfs.*/);
 
-          if( 1 ){
-            // The db does not yet work after an import.
-            duo = new JDb(filename);
-            duo.exec(sqlSetup);
-            const exp = duo.exportToObject();
-            duo.exec('insert into kvvfs(a) values(4),(5),(6)');
-            T.assert(6 === duo.selectValue(sqlCount));
-            duo.importFromObject(exp);
-            console.debug("Before/after exports:",exp, duo.exportToObject());
-            // FIXME: db access does not work beyond that after an import.
-            //T.assert(3 === duo.selectValue(sqlCount));
-          }
+          const importDb = capi.sqlite3_js_kvvfs_import_storage;
+          duo = new JDb(filename);
+          T.mustThrowMatching(()=>importDb(exp,true), /.*in use.*/);
+          duo.close();
+          importDb(exp);
+          duo = new JDb(filename);
+          T.assert(6 === duo.selectValue(sqlCount));
+          duo.close();
 
           /*
             TODO: more advanced concurrent use tests, e.g. looping
index 579039b81b14354797c8ff8c0323be06d78fc47a..dcb77e2730c17841c9ee9ac8437e3dc695e71ca4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C More\swork\son\skvvfs\sv2.\sDb\simports\swork,\sin\sthe\ssense\sthat\sthe\sstorage\sis\sproperly\sreplaced,\sbut\sthe\snative\sside\sis\snot\syet\sable\sto\srecover\sfrom\sthat\s(and\show\sto\smake\sit\sable\sto\sdo\sso\sis\snot\sclear).
-D 2025-11-23T22:13:42.828
+C Get\skvvfs\sv2\sstorage\simport\sworking\sby\sdisallowing\sit\swhen\sthere\sare\sopened\sdb/journal\shandles.\sMove\simport/export\sout\sof\sthe\sJsStorageDb\sclass\sand\sinto\ssqlite3_js_kvvfs_import/export_storage().
+D 2025-11-24T15:17:52.331
 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -600,7 +600,7 @@ F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667
 F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9654b565b346dc609b75d15337f20acfa7af7d9d558da1afeb9b6d8eaa404966
 F ext/wasm/api/sqlite3-vfs-helper.c-pp.js 3f828cc66758acb40e9c5b4dcfd87fd478a14c8fb7f0630264e6c7fa0e57515d
-F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js cd88fa458519bb48f5113430402fe250fcf87bde361f76d1782945c85671207a
+F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js efe13bc4196b02a87d6a472c7c7826c335c82cd5a844381f017813d4f78e1397
 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0
 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3
 F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86
@@ -647,7 +647,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c
 F ext/wasm/tester1-worker.c-pp.html 0e432ec2c0d99cd470484337066e8d27e7aee4641d97115338f7d962bf7b081a
 F ext/wasm/tester1.c-pp.html 52d88fe2c6f21a046030a36410b4839b632f4424028197a45a3d5669ea724ddb
-F ext/wasm/tester1.c-pp.js 420abc664cb6e5077f4822e66a200b95ccdbfd06610a8cbfe1b0cc3da7619562
+F ext/wasm/tester1.c-pp.js eaa1ed5ee1e5e8f12b4cbd9462bc8f3841a1deff3232486f72cf7914c02a3c29
 F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e
 F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88
 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -2178,8 +2178,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 53a99ff4ed5ef5f8620bf324a4f7a1d0812f5c80311228eb820039430ca04bd5
-R 9b53545349401af43f90d2908592ae9f
+P 4857c9d2fe428c19319244bf0589eaf93c124f020a633d6b7d40d35aaf969d24
+R f6b4509896384b2525280dbe96143648
 U stephan
-Z f328a4b235c18864031309f36fc54dd3
+Z 5db1ea489dc4ed416d893c241ae0c76d
 # Remove this line to create a well-formed Fossil manifest.
index dcb805657d535eab13a3e45cdab16a2081806d9a..7030fae5730639df94c6252b6d31add802e9ab7b 100644 (file)
@@ -1 +1 @@
-4857c9d2fe428c19319244bf0589eaf93c124f020a633d6b7d40d35aaf969d24
+c9e0b32278290baf987b5a46bd47358439e3d0f190b2879a965d6e4262ea7baf