From: stephan Date: Mon, 24 Nov 2025 15:17:52 +0000 (+0000) Subject: Get kvvfs v2 storage import working by disallowing it when there are opened db/journa... X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=72b4d638a53c147a1120aa56f45b679337f3a742;p=thirdparty%2Fsqlite.git Get kvvfs v2 storage import working by disallowing it when there are opened db/journal handles. Move import/export out of the JsStorageDb class and into sqlite3_js_kvvfs_import/export_storage(). FossilOrigin-Name: c9e0b32278290baf987b5a46bd47358439e3d0f190b2879a965d6e4262ea7baf --- diff --git a/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js index e0334a6ec3..376dcf6ac2 100644 --- a/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js @@ -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, diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 4393a35c2f..3176d0a9dd 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -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 diff --git a/manifest b/manifest index 579039b81b..dcb77e2730 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index dcb805657d..7030fae573 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4857c9d2fe428c19319244bf0589eaf93c124f020a633d6b7d40d35aaf969d24 +c9e0b32278290baf987b5a46bd47358439e3d0f190b2879a965d6e4262ea7baf