hop = (o,k)=>Object.prototype.hasOwnProperty.call(o,k);
const cache = Object.assign(Object.create(null),{
- rxJournalSuffix: /^-journal$/ // TOOD: lazily init once we figure out where
+ rxJournalSuffix: /^-journal$/, // TOOD: lazily init once we figure out where
+ zKeyJrnl: wasm.allocCString("jrnl"),
+ zKeySz: wasm.allocCString("sz")
});
const debug = function(){
*/
cache.jzClassToStorage = Object.assign(Object.create(null),{
/* Start off with mappings for well-known names. */
- global: {refc: 3/*never reaches 0*/, s: new TransientStorage}
+ global: {
+ refc: 3/*never reaches 0*/,
+ s: new TransientStorage
+ }
});
if( globalThis.localStorage ){
cache.jzClassToStorage.local =
- {refc: 3/*never reaches 0*/, s: globalThis.localStorage};
+ {
+ refc: 3/*never reaches 0*/,
+ s: globalThis.localStorage,
+ /* If useFullZClass is true, kvvfs storage keys are in
+ the form kvvfs-{zClass}-*, else they lack the "-{zClass}"
+ part. local/session storage must use the long form for
+ backwards compatibility. */
+ useFullZClass: true
+ };
}
if( globalThis.sessionStorage ){
cache.jzClassToStorage.session =
- {refc: 3/*never reaches 0*/, s: globalThis.sessionStorage}
+ {
+ refc: 3/*never reaches 0*/,
+ s: globalThis.sessionStorage,
+ useFullZClass: true
+ }
}
for(const k of Object.keys(cache.jzClassToStorage)){
/* Journals in kvvfs are are stored as individual records within
their Storage-ish object, named "kvvfs-${zClass}-jrnl". We
always create mappings for both the db file's name and the
journal's name referring to the same Storage object. */
- cache.jzClassToStorage[k+'-journal'] = cache.jzClassToStorage[k];
+ const orig = cache.jzClassToStorage[k];
+ orig.jzClass = k;
+ cache.jzClassToStorage[k+'-journal'] = orig;
}
/**
from the main thread) localStorage and sessionStorage. It will
be empty if no mapping is found.
*/
- const kvfsWhich = function callee(which){
+ const kvvfsWhich = function callee(which){
const rc = Object.assign(Object.create(null),{
prefix: 'kvvfs-' + which,
stores: []
});
if( which ){
const s = cache.jzClassToStorage[which];
- if( s ) rc.stores.push(s.s);
+ if( s ){
+ //debug("kvvfsWhich",s.jzClass,rc.prefix, s.s);
+ rc.prefix = 'kvvfs-'+s.jzClass;
+ rc.stores.push(s.s);
+ }
}else{
if( globalThis.sessionStorage ) rc.stores.push(globalThis.sessionStorage);
if( globalThis.localStorage ) rc.stores.push(globalThis.localStorage);
*/
capi.sqlite3_js_kvvfs_clear = function(which=""){
let rc = 0;
- const store = kvfsWhich(which);
+ const store = kvvfsWhich(which);
store.stores.forEach((s)=>{
const toRm = [] /* keys to remove */;
let i, n = s.length;
*/
capi.sqlite3_js_kvvfs_size = function(which=""){
let sz = 0;
- const store = kvfsWhich(which);
+ const store = kvvfsWhich(which);
store?.stores?.forEach?.((s)=>{
let i;
for(i = 0; i < s.length; ++i){
return sz * 2 /* because JS uses 2-byte char encoding */;
};
- const kvvfsMakeKey = wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack;
+ /** pstack-allocates a key. Caller must eventually restore
+ the pstack to free the memory. */
+ const keyForStorage = (store, zClass, zKey)=>{
+ //debug("keyForStorage(",store, wasm.cstrToJs(zClass), wasm.cstrToJs(zKey));
+ return wasm.exports.sqlite3__wasm_kvvfsMakeKeyOnPstack(
+ store.useFullZClass ? zClass : null, zKey
+ );
+ };
const pstack = wasm.pstack;
const storageForZClass =
(zClass)=>cache.jzClassToStorage[wasm.cstrToJs(zClass)];
/* sqlite3_file pointers => objects, each of which has:
.s = Storage object
.f = KVVfsFile instance
- .n = JS-string form of f.$zName
+ .n = JS-string form of f.$zClass
*/
);
+ if( sqlite3.__isUnderTest ){
+ sqlite3.kvvfsStuff = {
+ pFileHandles,
+ cache
+ };
+ }
+
{
/**
Original WASM functions for methods we partially override.
const stack = pstack.pointer,
astack = wasm.scopedAllocPush();
try{
- const zXKey = kvvfsMakeKey(zClass,zKey);
+ const store = storageForZClass(zClass);
+ const zXKey = keyForStorage(store, zClass, zKey);
if(!zXKey) return -3/*OOM*/;
- const jV = storageForZClass(zClass)
- .s.getItem(wasm.cstrToJs(zXKey));
+ const jV = store.s.getItem(wasm.cstrToJs(zXKey));
if(!jV) return -1;
const nV = jV.length /* We are relying 100% on v being
** ASCII so that jV.length is equal
xRcrdWrite: (zClass, zKey, zData)=>{
const stack = pstack.pointer;
try {
- const zXKey = kvvfsMakeKey(zClass,zKey);
+ const store = storageForZClass(zClass);
+ const zXKey = keyForStorage(store, zClass, zKey);
if(!zXKey) return SQLITE_NOMEM;
- storageForZClass(zClass).s.setItem(
+ store.s.setItem(
wasm.cstrToJs(zXKey),
wasm.cstrToJs(zData)
);
xRcrdDelete: (zClass, zKey)=>{
const stack = pstack.pointer;
try {
- const zXKey = kvvfsMakeKey(zClass,zKey);
+ const store = storageForZClass(zClass);
+ const zXKey = keyForStorage(store, zClass, zKey);
if(!zXKey) return capi.SQLITE_NOMEM;
- storageForZClass(zClass).s.removeItem(wasm.cstrToJs(zXKey));
+ store.s.removeItem(wasm.cstrToJs(zXKey));
return 0;
}catch(e){
sqlite3.config.error("kvrecordDelete()",e);
const rc = originalMethods.vfs.xOpen(pProtoVfs, zName, pProtoFile,
flags, pOutFlags);
if( 0==rc ){
- const jzName = wasm.cstrToJs(zName);
const f = new KVVfsFile(pProtoFile);
- let s = cache.jzClassToStorage[jzName];
- debug("xOpen", jzName, s);
+ util.assert(f.$zClass, "Missing f.$zClass");
+ const jzName = wasm.cstrToJs(zName);
+ const jzClass = wasm.cstrToJs(f.$zClass);
+ let s = cache.jzClassToStorage[jzClass];
+ //debug("xOpen", jzName, jzClass, s);
if( s ){
++s.refc;
}else{
const other = f.$isJournal
? jzName.replace(cache.rxJournalSuffix,'')
: jzName + '-journal';
- s = cache.jzClassToStorage[jzName]
+ s = cache.jzClassToStorage[jzClass]
= cache.jzClassToStorage[other]
= Object.assign(Object.create(null),{
+ jzClass: 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
}
}/*xOpen()*/,
//#if nope
- xDelete: function(pVfs, zName, iSyncFlag){},
- xAccess:function(pProtoVfs, zPath, flags, pResOut){},
+ xAccess:function(pProtoVfs, zPath, flags, pResOut){
+ /* Triggering an abort() via xClose(), but we'll need a working
+ xAccess() if we want to use kvvfs as the default vfs. */
+ try{
+ let drc = 0;
+ const jzName = wasm.cstrToJs(zPath);
+ const s = cache.jzClassToStorage[jzName];
+ debug("xAccess", jzName, s);
+ if( s ){
+ if( 0 && !jzName.endsWith("-journal") ){
+ drc = 1;
+ }else{
+ const zKey = jzName.endsWith("-journal")
+ ? cache.zKeyJrnl
+ : cache.zKeySz;
+ drc = sqlite3_kvvfs_methods.override.recordHandler
+ .xRcrdRead(zPath, zKey, wasm.ptr.null, 0);
+ }
+ }
+ wasm.poke32(pResOut, drc>0 ? 1 : 0);
+ return 0;
+ }catch(e){
+ warn("xAccess",e);
+ return capi.SQLITE_ERROR;
+ }
+ }/*xAccess*/,
+ xDelete: function(pVfs, zName, iSyncFlag){
+ try{
+ // Triggering an abort() via xClose()
+ util.assert(zName, "null zName?");
+ const jzName = wasm.cstrToJs(zName);
+ const s = cache.jzClassToStorage[jzName];
+ debug("xDelete", jzName, s);
+ if( s ){
+ if( jzName.endsWith("-journal") ){
+ const zKey = cache.zKeyJrnl;
+ util.assert(zKey, "Missing cache.zKeyJrnl");
+ sqlite3_kvvfs_methods.override.recordHandler
+ .xRcrdDelete(zName, zKey);
+ }else{
+ drc = 1;
+ delete cache.jzClassToStorage[jzName];
+ }
+ return 0;
+ }else{
+ return originalMethods.vfs.xDelete(pVfs, zName, iSyncFlag);
+ }
+ }catch(e){
+ warn("xDelete",e);
+ return capi.SQLITE_ERROR;
+ }
+ },
xFullPathname: function(pVfs, zPath, nOut, zOut){},
xDlOpen: function(pVfs, zFilename){},
xSleep: function(pVfs,usec){},
xClose: function(pFile){
try{
const h = pFileHandles.get(pFile);
- debug("xClose", pFile, h);
+ //debug("xClose", pFile, h);
if( h ){
pFileHandles.delete(pFile);
const s = cache.jzClassToStorage[h.n];
delete s.s;
delete s.refc;
}
- originalIoMethods(h.f).xClose(pFile);
h.f.dispose();
+ originalIoMethods(h.f).xClose(pFile);
}else{
/* Can happen if xOpen fails */
}
noting that the 'vfs' option supported by main DB
constructor is ignored here: the vfs is always 'kvvfs'.
*/
+ const DB = sqlite3.oo1.DB;
sqlite3.oo1.JsStorageDb = function(
storageName = sqlite3.oo1.JsStorageDb.defaultStorageName
){
- const opt = sqlite3.oo1.DB.dbCtorHelper.normalizeArgs(...arguments);
+ const opt = DB.dbCtorHelper.normalizeArgs(...arguments);
opt.vfs = 'kvvfs';
- sqlite3.oo1.DB.dbCtorHelper.call(this, opt);
+ DB.dbCtorHelper.call(this, opt);
};
sqlite3.oo1.JsStorageDb.defaultStorageName = 'session';
const jdb = sqlite3.oo1.JsStorageDb;
- jdb.prototype = Object.create(sqlite3.oo1.DB.prototype);
+ jdb.prototype = Object.create(DB.prototype);
/** Equivalent to sqlite3_js_kvvfs_clear(). */
jdb.clearStorage = capi.sqlite3_js_kvvfs_clear;
/**
database blocks which were cleaned up.
*/
jdb.prototype.clearStorage = function(){
- return jdb.clearStorage(this.affirmDbOpen().filename);
+ return jdb.clearStorage(this.affirmOpen().filename);
};
/** Equivalent to sqlite3_js_kvvfs_size(). */
jdb.storageSize = capi.sqlite3_js_kvvfs_size;
up in its storage or throws if this instance has been closed.
*/
jdb.prototype.storageSize = function(){
- return jdb.storageSize(this.affirmDbOpen().filename);
+ return jdb.storageSize(this.affirmOpen().filename);
};
+
+ if( sqlite3.__isUnderTest ){
+ jdb.prototype.testDbToObject = function(){
+ const store = cache.jzClassToStorage[this.affirmOpen().filename];
+ if( store ){
+ const s = store.s;
+ const rc = Object.create(null);
+ let i = 0, n = s.length;
+ for( ; i < n; ++i ){
+ const k = s.key(i), v = s.getItem(k);
+ rc[k] = v;
+ }
+ return rc;
+ }
+ }
+ }
}/*sqlite3.oo1.JsStorageDb*/
})/*globalThis.sqlite3ApiBootstrap.initializers*/;
-C Demonstrate\scompletely\stransient\sand\sa\ssemi-transient\s(until\spage\sreload)\skvvfs\sinstances.
-D 2025-11-22T02:43:56.155
+C Factor\sKVVfsFile::zName\sback\sout.\sRemove\sthe\sextraneous\spart\sof\sthe\sstorage\skeys\sfor\snon-local/non-session\sstorage.
+D 2025-11-22T05:37:21.474
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F ext/wasm/api/pre-js.c-pp.js ad2546290e0c8ce5ca2081bff6e85cc25eeb904a3303921f1184290a7ff1b32f
F ext/wasm/api/sqlite3-api-glue.c-pp.js 9eaed1801be392f6687aa7da8e3a5a41d03de19993d8fe62ee6c52617eab4985
F ext/wasm/api/sqlite3-api-oo1.c-pp.js 7d8850f754b4a9aecd5a4a92a357e05f720cd7f5cf962909343b40c914237256
-F ext/wasm/api/sqlite3-api-prologue.js d78114039f77ab79bdffb682a58db34000ba2a263efbb210e3c51aa74d206e1c
+F ext/wasm/api/sqlite3-api-prologue.js 29bc41d0f9fbca946fc60681fb79901b3224988b5b978a47620595bbe0e4cf84
F ext/wasm/api/sqlite3-api-worker1.c-pp.js 1041dd645e8e821c082b628cd8d9acf70c667430f9d45167569633ffc7567938
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 1df90559ff3e01175fcfa5b653088a331c23474ec019a156e10d468dbbfacfac
+F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js a0835218719d0ec6bfc79ec29d44d2028ef98a7c1b1aeee75f4338f5d3eb337c
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 26cb41d5a62f46a106b6371eb00fef02de3cdbfaa51338ba087a45f53028e0d0
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js aa330fa0e8ef35cbd92eb0d52e05fbaa07e61540c5cb164e693c82428ce1d763
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86
-F ext/wasm/api/sqlite3-wasm.c 1b49686314999e267de6f243a1ebacb86c6bebc528646b26096c44c26e50ae42
+F ext/wasm/api/sqlite3-wasm.c 1837eac45775ca92f28bf94921a2cf7f6f9d7a77955f95005399c57975e7d080
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bda1c75bd674a92a0e27cc2f3d46dbbf21e422413f8046814515a0bd7409328a
F ext/wasm/api/sqlite3-worker1.c-pp.js 802d69ead8c38dc1be52c83afbfc77e757da8a91a2e159e7ed3ecda8b8dba2e7
F ext/wasm/c-pp-lite.c 943be1a36774d58385dca32de36fc18d4f432fe79f7aa35e6c85dd6a6b825bd0
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 cbfee01ad8ca3d981c24a44807aa1c686bc0e01c86fc8532044d7da17e584de7
+F ext/wasm/tester1.c-pp.js 4bb05703aa335a14ce1c177c05d93a2d33031ad6b8db2f59cd6d5b26e93cc1fe
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
F src/os.c 509452169d5ea739723e213b8e2481cf0e587f0e88579a912d200db5269f5f6d
F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63
F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
-F src/os_kv.c 494f977e4977ec58d754a7ad6e0f29e3bf63ac155f3a8bb78bdcda50d0af92c5
+F src/os_kv.c 59f41c1ac47f9d1f89d3c04eb08b7f91e14e903d2b44e10a29e803ee353553a1
F src/os_setup.h 8efc64eda6a6c2f221387eefc2e7e45fd5a3d5c8337a7a83519ba4fbd2957ae2
F src/os_unix.c 7945ede1e85b2d1b910e1b4af9ba342e964b1e30e79f4176480a60736445cb36
F src/os_win.c a89b501fc195085c7d6c9eec7f5bd782625e94bb2a96b000f4d009703df1083f
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P e0b33b51229a977cc3fa8a5a6c8ea59669f8bf566b2a6330fd24da1ad886a716
-R c905cc3949f1a87b4595864b9a20953b
+P 3f9ff9873303c3900dd3cba6e922bfb8cdb1f595353b692796b62e3025013517
+R 3850965b51b022600461307bc9bfa402
U stephan
-Z 95fff1b5f36e3d97fba51de647f25d12
+Z 2898a867b93493236c621da268e3979f
# Remove this line to create a well-formed Fossil manifest.