const kvvfsEncode = wasm.exports.sqlite3__wasm_kvvfs_encode;
/**
- Listener events and their argument(s):
+ Listener events and their argument(s) (via the callback(ev)
+ ev.data member):
'open': number of opened handles on this storage.
'write': key, value
'delete': key
+
+ 'sync': true if it's from xSync(), false if it's from
+ xFileControl().
*/
const notifyListeners = async function(eventName,store,...args){
if( store.listeners ){
//cache.rxPageNoSuffix ??= /(\d+)$/;
- if( store.keyPrefix ){
+ if( store.keyPrefix && args[0] ){
args[0] = args[0].replace(store.keyPrefix,'');
}
let u8enc, z0, z1, wcache;
wcache[args[0]]
= ev.data[1]
= heap.slice(z1, wasm.ptr.add(z1,rc));
+ }else{
+ continue;
}
}else{
- ev.data = ((args.length===1) ? args[0] : args);
+ ev.data = args.length
+ ? ((args.length===1) ? args[0] : args)
+ : undefined;
}
try{f(ev)?.catch?.(catchForNotify)}
catch(e){
//no if( true===deleteAt0 ) s.deleteAtRefc0 = true;
s.files.push(f);
}else{
- /* TODO: a url flag which tells it to keep the storage
- around forever so that future xOpen()s get the same
- Storage-ish objects. We can accomplish that by
- simply increasing the refcount once more. */
wasm.poke32(pOutFlags, flags | sqlite3.SQLITE_OPEN_CREATE);
util.assert( !f.$isJournal, "Opening a journal before its db? "+jzClass );
/* Map both zName and zName-journal to the same storage. */
s.deleteAtRefc0 = deleteAt0 || !!(capi.SQLITE_OPEN_DELETEONCLOSE & flags);
//debug("xOpen installed storage handle [",nm, nm+"-journal","]", s);
}
- pFileHandles.set(pProtoFile, {storage: s, file: f, jzClass});
+ pFileHandles.set(pProtoFile, {store: s, file: f, jzClass});
notifyListeners('open', s, s.files.length);
return 0;
}catch(e){
cache.popError();
try{
const jzName = wasm.cstrToJs(zName);
- //debug("xDelete", jzName, storageForZClass(jzName));
if( cache.rxJournalSuffix.test(jzName) ){
recordHandler.xRcrdDelete(zName, cache.zKeyJrnl);
- }
+ }/*
+ else: historically not done, but maybe otherwise delete
+ all db pages from storageForZClass(zName)?
+ */
return 0;
}catch(e){
warn("xDelete",e);
ioDb:{
/* sqlite3_kvvfs_methods::pIoDb's methods */
xClose: function(pFile){
+ cache.popError();
try{
const h = pFileHandles.get(pFile);
//debug("xClose", pFile, h);
if( h ){
pFileHandles.delete(pFile);
- const s = storageForZClass(h.jzClass);
+ const s = h.store;//storageForZClass(h.jzClass);
s.files = s.files.filter((v)=>v!==h.file);
if( --s.refc<=0 && s.deleteAtRefc0 ){
deleteStorage(s);
},
xFileControl: function(pFile, opId, pArg){
+ cache.popError();
try{
const h = pFileHandles.get(pFile);
util.assert(h, "Missing KVVfsFile handle");
+ //debug("xFileControl",h,opId);
if( opId===capi.SQLITE_FCNTL_PRAGMA ){
/* pArg== length-3 (char**) */
const zName = wasm.peekPtr(wasm.ptr.add(pArg, wasm.ptr.size));
}
}
}
- return originalIoMethods(h.file).xFileControl(pFile, opId, pArg);
+ const rc = originalIoMethods(h.file).xFileControl(pFile, opId, pArg);
+ if( 0==rc && capi.SQLITE_FCNTL_SYNC===opId ){
+ notifyListeners('sync', h.store, false);
+ }
+ return rc;
}catch(e){
error("xFileControl",e);
return cache.setError(e);
}
},
+ xSync: function(pFile,flags){
+ cache.popError();
+ try{
+ const h = pFileHandles.get(pFile);
+ //debug("xSync",h);
+ util.assert(h, "Missing KVVfsFile handle");
+ const rc = originalIoMethods(h.file).xSync(pFile, flags);
+ if( 0==rc ) notifyListeners('sync', h.store, true);
+ return rc;
+ }catch(e){
+ error("xSync",e);
+ return cache.setError(e);
+ }
+ }
+
//#if nope
xRead: function(pFile,pTgt,n,iOff64){},
xWrite: function(pFile,pSrc,n,iOff64){},
xTruncate: function(pFile,i64){},
- xSync: function(pFile,flags){},
xFileSize: function(pFile,pi64Out){},
xLock: function(pFile,iLock){},
xUnlock: function(pFile,iLock){},
- 'delete' gets the string-type key of the deleted record.
+ - 'sync' receives
+ one argument: true if it was triggered by db file's
+ xSync(), false if it was triggered by xFileControl() (which
+ triggers before the xSync() and also triggers is the DB has
+ PRAGMA SYNCHRONOUS=OFF.
+
The arguments to 'write', and keys to 'delete', are in one of the
following forms:
];
const sqlCount = "select count(*) from kvvfs";
const sqlSelectSchema = "select * from sqlite_schema";
- const counts = Object.assign(Object.create(null),{
- open: 0, close: 0, delete: 0, write: 0
- });
+ const counts = Object.create(null);
+ const incr = (key)=>counts[key] = 1 + (counts[key] ?? 0);
const pglog = Object.create(null);
pglog.pages = [];
pglog.jrnl = undefined;
throw e;
}
};
+
const listener = {
storage: filename,
reserve: true,
events: {
'open': (ev)=>{
//console.warn('open',ev);
- ++counts[ev.type];
+ incr(ev.type);
T.assert(filename===ev.storageName)
.assert('number'===typeof ev.data);
},
//^^^ if this is async, we can't time the test for
// pglog.exception without far more hoop-jumping.
//console.warn('close',ev);
- ++counts[ev.type];
+ incr(ev.type);
T.assert('number'===typeof ev.data);
toss();
},
'delete': (ev)=>{
//console.warn('delete',ev);
- ++counts[ev.type];
+ incr(ev.type);
T.assert('string'===typeof ev.data);
switch(ev.data){
case 'jrnl':
}
}
},
+ 'sync': (ev)=>{
+ incr(ev.data ? 'xSync' : 'xFileControlSync');
+ },
'write': (ev)=>{
//console.warn('write',ev);
- ++counts[ev.type];
+ incr(ev.type);
const key = ev.data[0], val = ev.data[1];
T.assert(Array.isArray(ev.data))
.assert('string'===typeof key);
}
}
};
+
kvvfs.listen(listener);
const dbFileRaw = 'file:'+filename+'?vfs=kvvfs&delete-on-close=1';
const expOpt = {
"Unexpected empty schema");
db.close();
debug("kvvfs listener counts:",counts);
- T.assert( counts.open )
- .assert( counts.close )
- .assert( listener.elideJournal ? !counts.delete : counts.delete )
- .assert( counts.write )
- .assert( counts.open===counts.close )
- .assert( pglog.elideJournal
+ T.assert( counts.open );
+ T.assert( counts.close );
+ T.assert( listener.elideJournal ? !counts.delete : counts.delete );
+ T.assert( counts.write );
+ T.assert( counts.xSync );
+ T.assert( counts.xFileControlSync>=counts.xSync );
+ T.assert( counts.open===counts.close );
+ T.assert( pglog.elideJournal
? (undefined===pglog.jrnl)
: (null===pglog.jrnl),
"Unexpected pglog.jrnl value: "+pglog.jrnl );
//debug("kvvfs listener pageLog", pglog);
}
const before = JSON.stringify(counts);
- T.assert( kvvfs.unlisten(listener) )
- .assert( !kvvfs.unlisten(listener) );
+ T.assert( kvvfs.unlisten(listener) );
+ T.assert( !kvvfs.unlisten(listener) );
db = new DB(dbFileRaw);
T.assert( db.selectObjects(sqlSelectSchema)?.length>0 );
const exp = kvvfs.export(expOpt);
-C More\swork\son\sthe\skvvfs\sevents.\sDemonstrate\sre-importing\sa\sstreamed-out\sset\sof\sdb\spages.
-D 2025-11-30T06:20:28.183
+C Add\ssync\sevents\sto\skvvfs.listen()\sso\sthat\sstreaming\scan\smaybe\sget\sa\sbetter\ssense\sof\swhen\sit's\sokay\sto\sprocess\sthe\sstream.
+D 2025-11-30T07:08:45.236
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
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 4785535033eff734b4a9115edcf4d09b58b51a5830cc3a54f2ded9fff51a8c75
+F ext/wasm/api/sqlite3-vfs-kvvfs.c-pp.js 92e838b2b1b3039d14ab43f23a99c553df5bd967525457335e2d498f4b72f632
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js a2eea6442556867b589e04107796c6e1d04a472219529eeb45b7cd221d7d048b
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 88ce2078267a2d1af57525a32d896295f4a8db7664de0e17e82dc9ff006ed8d3
F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 366596d8ff73d4cefb938bbe95bc839d503c3fab6c8335ce4bf52f0d8a7dee81
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 dfffa59662eb6ae04be40b65c34becb864edb74d1439f3f8a850c0102546ebe6
+F ext/wasm/tester1.c-pp.js cb0b1a07bd19af014e62343b973c61be312ef91f612b83db074b1542969d64e3
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 tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P bbd08b67ceeaae2a3a82655da5a3983f4e9057424de4223f1505ba74c36238af
-R 67172ddc08db9584ebb1618c06504a62
+P b8fee839b0199d1d2714c99612ea4b4d943d8b0033d742eaa66e1575ce46f0d2
+R fb9f8ede3e2ffe33cbb590ee3d7d0cdc
U stephan
-Z f1288e031bc5ab35fe9740e0ceeda37e
+Z a9c442087c9256ab328a4323b6416791
# Remove this line to create a well-formed Fossil manifest.