diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index e318b72001..aaaeaa3d5a 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -258,9 +258,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ in the form of a single configuration object with the following properties: - - `.filename`: database file name - - `.flags`: open-mode flags - - `.vfs`: the VFS fname + - `filename`: database file name + - `flags`: open-mode flags + - `vfs`: the VFS fname The `filename` and `vfs` arguments may be either JS strings or C-strings allocated via WASM. `flags` is required to be a JS @@ -560,6 +560,25 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ dbName: function(dbNumber=0){ return capi.sqlite3_db_name(affirmDbOpen(this).pointer, dbNumber); }, + /** + Returns the name of the sqlite3_vfs used by the given database + of this connection (defaulting to 'main'). The argument may be + either a JS string or a WASM C-string. Returns undefined if the + given db name is invalid. Throws if this object has been + close()d. + */ + dbVfsName: function(dbName=0){ + let rc; + const pVfs = capi.sqlite3_js_db_vfs( + affirmDbOpen(this).pointer, dbName + ); + if(pVfs){ + const v = new capi.sqlite3_vfs(pVfs); + try{ rc = wasm.cstringToJs(v.$zName) } + finally { v.dispose() } + } + return rc; + }, /** Compiles the given SQL and returns a prepared Stmt. This is the only way to create new Stmt objects. Throws on error. diff --git a/ext/wasm/api/sqlite3-api-opfs.js b/ext/wasm/api/sqlite3-api-opfs.js index 1a89432c5f..90337d5279 100644 --- a/ext/wasm/api/sqlite3-api-opfs.js +++ b/ext/wasm/api/sqlite3-api-opfs.js @@ -371,6 +371,20 @@ const installOpfsVfs = function callee(options){ return rc; }; + /** + Not part of the public API. Only for test/development use. + */ + opfsUtil.debug = { + asyncShutdown: ()=>{ + warn("Shutting down OPFS async listener. The OPFS VFS will no longer work."); + opRun('opfs-async-shutdown'); + }, + asyncRestart: ()=>{ + warn("Attempting to restart OPFS VFS async listener. Might work, might not."); + W.postMessage({type: 'opfs-async-restart'}); + } + }; + const initS11n = ()=>{ /** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -876,37 +890,61 @@ const installOpfsVfs = function callee(options){ } /** - Syncronously deletes the given OPFS filesystem entry, ignoring - any errors. As this environment has no notion of "current - directory", the given name must be an absolute path. If the 2nd - argument is truthy, deletion is recursive (use with caution!). - - Returns true if the deletion succeeded and false if it fails, - but cannot report the nature of the failure. + Expects an OPFS file path. It gets resolved, such that ".." + components are properly expanded, and returned. If the 2nd arg + is true, the result is returned as an array of path elements, + else an absolute path string is returned. */ - opfsUtil.deleteEntry = function(fsEntryName,recursive=false){ - mTimeStart('xDelete'); - const rc = opRun('xDelete', fsEntryName, 0, recursive); - mTimeEnd(); - return 0===rc; + opfsUtil.getResolvedPath = function(filename,splitIt){ + const p = new URL(filename, "file://irrelevant").pathname; + return splitIt ? p.split('/').filter((v)=>!!v) : p; }; + /** - Synchronously creates the given directory name, recursively, in + Takes the absolute path to a filesystem element. Returns an + array of [handleOfContainingDir, filename]. If the 2nd argument + is truthy then each directory element leading to the file is + created along the way. Throws if any creation or resolution + fails. + */ + opfsUtil.getDirForFilename = async function f(absFilename, createDirs = false){ + const path = opfsUtil.getResolvedPath(absFilename, true); + const filename = path.pop(); + let dh = opfsUtil.rootDirectory; + for(const dirName of path){ + if(dirName){ + dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs}); + } + } + return [dh, filename]; + }; + + /** + Creates the given directory name, recursively, in the OPFS filesystem. Returns true if it succeeds or the directory already exists, else false. */ - opfsUtil.mkdir = function(absDirName){ - mTimeStart('mkdir'); - const rc = opRun('mkdir', absDirName); - mTimeEnd(); - return 0===rc; + opfsUtil.mkdir = async function(absDirName){ + try { + await opfsUtil.getDirForFilename(absDirName+"/filepart", true); + return true; + }catch(e){ + //console.warn("mkdir(",absDirName,") failed:",e); + return false; + } }; /** - Synchronously checks whether the given OPFS filesystem exists, + Checks whether the given OPFS filesystem entry exists, returning true if it does, false if it doesn't. */ - opfsUtil.entryExists = function(fsEntryName){ - return 0===opRun('xAccess', fsEntryName); + opfsUtil.entryExists = async function(fsEntryName){ + try { + const [dh, fn] = await opfsUtil.getDirForFilename(filename); + await dh.getFileHandle(fn); + return true; + }catch(e){ + return false; + } }; /** @@ -938,20 +976,160 @@ const installOpfsVfs = function callee(options){ }; /** - Only for test/development use. + Returns a promise which resolves to an object which represents + all files and directories in the OPFS tree. The top-most object + has two properties: `dirs` is an array of directory entries + (described below) and `files` is a list of file names for all + files in that directory. + + Traversal starts at sqlite3.opfs.rootDirectory. + + Each `dirs` entry is an object in this form: + + ``` + { name: directoryName, + dirs: [...subdirs], + files: [...file names] + } + ``` + + The `files` and `subdirs` entries are always set but may be + empty arrays. + + The returned object has the same structure but its `name` is + an empty string. All returned objects are created with + Object.create(null), so have no prototype. + + Design note: the entries do not contain more information, + e.g. file sizes, because getting such info is not only + expensive but is subject to locking-related errors. */ - opfsUtil.debug = { - asyncShutdown: ()=>{ - warn("Shutting down OPFS async listener. OPFS will no longer work."); - opRun('opfs-async-shutdown'); - }, - asyncRestart: ()=>{ - warn("Attempting to restart OPFS async listener. Might work, might not."); - W.postMessage({type: 'opfs-async-restart'}); + opfsUtil.treeList = async function(){ + const doDir = async function callee(dirHandle,tgt){ + tgt.name = dirHandle.name; + tgt.dirs = []; + tgt.files = []; + for await (const handle of dirHandle.values()){ + if('directory' === handle.kind){ + const subDir = Object.create(null); + tgt.dirs.push(subDir); + await callee(handle, subDir); + }else{ + tgt.files.push(handle.name); + } + } + }; + const root = Object.create(null); + await doDir(opfsUtil.rootDirectory, root); + return root; + }; + + /** + Irrevocably deletes _all_ files in the current origin's OPFS. + Obviously, this must be used with great caution. It may throw + an exception if removal of anything fails (e.g. a file is + locked), but the precise conditions under which it will throw + are not documented (so we cannot tell you what they are). + */ + opfsUtil.rmfr = async function(){ + const dir = opfsUtil.rootDirectory, opt = {recurse: true}; + for await (const handle of dir.values()){ + dir.removeEntry(handle.name, opt); } }; - //TODO to support fiddle db upload: + /** + Deletes the given OPFS filesystem entry. As this environment + has no notion of "current directory", the given name must be an + absolute path. If the 2nd argument is truthy, deletion is + recursive (use with caution!). + + The returned Promise resolves to true if the deletion was + successful, else false (but...). The OPFS API reports the + reason for the failure only in human-readable form, not + exceptions which can be type-checked to determine the + failure. Because of that... + + If the final argument is truthy then this function will + propagate any exception on error, rather than returning false. + */ + opfsUtil.unlink = async function(fsEntryName, recursive = false, + throwOnError = false){ + try { + const [hDir, filenamePart] = + await opfsUtil.getDirForFilename(fsEntryName, false); + await hDir.removeEntry(filenamePart, {recursive}); + return true; + }catch(e){ + if(throwOnError){ + throw new Error("unlink(",arguments[0],") failed: "+e.message,{ + cause: e + }); + } + return false; + } + }; + + /** + Traverses the OPFS filesystem, calling a callback for each one. + The argument may be either a callback function or an options object + with any of the following properties: + + - `callback`: function which gets called for each filesystem + entry. It gets passed 3 arguments: 1) the + FileSystemFileHandle or FileSystemDirectoryHandle of each + entry (noting that both are instanceof FileSystemHandle). 2) + the FileSystemDirectoryHandle of the parent directory. 3) the + current depth level, with 0 being at the top of the tree + relative to the starting directory. If the callback returns a + literal false, as opposed to any other falsy value, traversal + stops without an error. Any exceptions it throws are + propagated. Results are undefined if the callback manipulate + the filesystem (e.g. removing or adding entries) because the + how OPFS iterators behave in the face of such changes is + undocumented. + + - `recursive` [bool=true]: specifies whether to recurse into + subdirectories or not. Whether recursion is depth-first or + breadth-first is unspecified! + + - `directory` [FileSystemDirectoryEntry=sqlite3.opfs.rootDirectory] + specifies the starting directory. + + If this function is passed a function, it is assumed to be the + callback. + + Returns a promise because it has to (by virtue of being async) + but that promise has no specific meaning: the traversal it + performs is synchronous. The promise must be used to catch any + exceptions propagated by the callback, however. + + TODO: add an option which specifies whether to traverse + depth-first or breadth-first. We currently do depth-first but + an incremental file browsing widget would benefit more from + breadth-first. + */ + opfsUtil.traverse = async function(opt){ + const defaultOpt = { + recursive: true, + directory: opfsUtil.rootDirectory + }; + if('function'===typeof opt){ + opt = {callback:opt}; + } + opt = Object.assign(defaultOpt, opt||{}); + const doDir = async function callee(dirHandle, depth){ + for await (const handle of dirHandle.values()){ + if(false === opt.callback(handle, dirHandle, depth)) return false; + else if(opt.recursive && 'directory' === handle.kind){ + if(false === await callee(handle, depth + 1)) break; + } + } + }; + doDir(opt.directory, 0); + }; + + //TODO to support fiddle and worker1 db upload: //opfsUtil.createFile = function(absName, content=undefined){...} if(sqlite3.oo1){ diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index a5ff0c40ad..ca6fad8f84 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1187,14 +1187,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } /** - Given an `sqlite3*`, an sqlite3_vfs name, and an optional db - name, returns a truthy value (see below) if that db handle uses - that VFS, else returns false. If pDb is falsy then the 3rd - argument is ignored and this function returns a truthy value if - the default VFS name matches that of the 2nd argument. Results - are undefined if pDb is truthy but refers to an invalid - pointer. The 3rd argument specifies the database name of the - given database connection to check, defaulting to the main db. + Given an `sqlite3*`, an sqlite3_vfs name, and an optional db name + (defaulting to "main"), returns a truthy value (see below) if + that db uses that VFS, else returns false. If pDb is falsy then + the 3rd argument is ignored and this function returns a truthy + value if the default VFS name matches that of the 2nd + argument. Results are undefined if pDb is truthy but refers to an + invalid pointer. The 3rd argument specifies the database name of + the given database connection to check, defaulting to the main + db. The 2nd and 3rd arguments may either be a JS string or a WASM C-string. If the 2nd argument is a NULL WASM pointer, the default @@ -1209,14 +1210,14 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( bad arguments cause a conversion error when passing into wasm-space, false is returned. */ - capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName="main"){ + capi.sqlite3_js_db_uses_vfs = function(pDb,vfsName,dbName=0){ try{ const pK = capi.sqlite3_vfs_find(vfsName); if(!pK) return false; else if(!pDb){ return pK===capi.sqlite3_vfs_find(0) ? pK : false; }else{ - return pK===capi.sqlite3_js_db_vfs(pDb) ? pK : false; + return pK===capi.sqlite3_js_db_vfs(pDb,dbName) ? pK : false; } }catch(e){ /* Ignore - probably bad args to a wasm-bound function. */ diff --git a/ext/wasm/api/sqlite3-api-worker1.js b/ext/wasm/api/sqlite3-api-worker1.js index aa438a52dc..532d61b67e 100644 --- a/ext/wasm/api/sqlite3-api-worker1.js +++ b/ext/wasm/api/sqlite3-api-worker1.js @@ -188,6 +188,8 @@ See the sqlite3.oo1.DB constructor for peculiarities and transformations, + vfs: sqlite3_vfs name. Ignored if filename is ":memory:" or "". + This may change how the given filename is resolved. } } ``` @@ -212,6 +214,7 @@ persistent: true if the given filename resides in the known-persistent storage, else false. + vfs: name of the VFS the "main" db is using. } } ``` @@ -362,7 +365,7 @@ sqlite3.initWorker1API = function(){ /** Temp holder for "transferable" postMessage() state. */ xfer: [], open: function(opt){ - const db = new DB(opt.filename); + const db = new DB(opt); this.dbs[getDbId(db)] = db; if(this.dbList.indexOf(db)<0) this.dbList.push(db); return db; @@ -442,12 +445,14 @@ sqlite3.initWorker1API = function(){ oargs.filename = args.filename || ''; }else{ oargs.filename = args.filename; + oargs.vfs = args.vfs; } const db = wState.open(oargs); rc.filename = db.filename; rc.persistent = (!!pDir && db.filename.startsWith(pDir+'/')) || !!sqlite3.capi.sqlite3_js_db_uses_vfs(db.pointer, "opfs"); rc.dbId = getDbId(db); + rc.vfs = db.dbVfsName(); return rc; }, @@ -528,6 +533,7 @@ sqlite3.initWorker1API = function(){ rc.wasmfsOpfsEnabled = !!sqlite3.capi.sqlite3_wasmfs_opfs_dir(); rc.version = sqlite3.version; rc.vfsList = sqlite3.capi.sqlite3_js_vfs_list(); + rc.opfsEnabled = !!sqlite3.opfs; return rc; }, @@ -542,11 +548,6 @@ sqlite3.initWorker1API = function(){ } */ export: function(ev){ - /** - We need to reimplement this to use the Emscripten FS - interface. That part used to be in the OO#1 API but that - dependency was removed from that level of the API. - */ const db = getMsgDb(ev); const response = { bytearray: sqlite3.capi.sqlite3_js_db_export(db.pointer), @@ -559,17 +560,23 @@ sqlite3.initWorker1API = function(){ toss: function(ev){ toss("Testing worker exception"); + }, + + 'opfs-tree': async function(ev){ + if(!sqlite3.opfs) toss("OPFS support is unavailable."); + const response = await sqlite3.opfs.treeList(); + return response; } }/*wMsgHandler*/; - self.onmessage = function(ev){ + self.onmessage = async function(ev){ ev = ev.data; let result, dbId = ev.dbId, evType = ev.type; const arrivalTime = performance.now(); try { if(wMsgHandler.hasOwnProperty(evType) && wMsgHandler[evType] instanceof Function){ - result = wMsgHandler[evType](ev); + result = await wMsgHandler[evType](ev); }else{ toss("Unknown db worker message type:",ev.type); } diff --git a/ext/wasm/api/sqlite3-opfs-async-proxy.js b/ext/wasm/api/sqlite3-opfs-async-proxy.js index 71c822c1f2..3aa20fa634 100644 --- a/ext/wasm/api/sqlite3-opfs-async-proxy.js +++ b/ext/wasm/api/sqlite3-opfs-async-proxy.js @@ -377,6 +377,7 @@ const vfsAsyncImpls = { if(!filenamePart) break; await hDir.removeEntry(filenamePart, {recursive}); if(0x1234 !== syncDir) break; + recursive = false; filename = getResolvedPath(filename, true); filename.pop(); filename = filename.join('/'); diff --git a/ext/wasm/common/testing.css b/ext/wasm/common/testing.css index a4433af8c6..9438b330c9 100644 --- a/ext/wasm/common/testing.css +++ b/ext/wasm/common/testing.css @@ -37,6 +37,8 @@ span.labeled-input { .strong { font-weight: 700 } .warning { color: firebrick; } .green { color: darkgreen; } +.tests-pass { background-color: green; color: white } +.tests-fail { background-color: red; color: yellow } .faded { opacity: 0.5; } .group-start { color: blue; } .group-end { color: blue; } diff --git a/ext/wasm/demo-worker1-promiser.js b/ext/wasm/demo-worker1-promiser.js index 855643b2d2..3f50ef9e08 100644 --- a/ext/wasm/demo-worker1-promiser.js +++ b/ext/wasm/demo-worker1-promiser.js @@ -91,7 +91,8 @@ const r = ev.result; log("then open result",r); T.assert(ev.dbId === r.dbId) - .assert(ev.messageId); + .assert(ev.messageId) + .assert('string' === typeof r.vfs); promiserConfig.dbId = ev.dbId; }).then(runTests2); }; diff --git a/ext/wasm/demo-worker1.js b/ext/wasm/demo-worker1.js index 2da80bc239..ac4e9f4234 100644 --- a/ext/wasm/demo-worker1.js +++ b/ext/wasm/demo-worker1.js @@ -291,7 +291,8 @@ log("open result",ev); T.assert('testing2.sqlite3'===ev.result.filename) .assert(ev.dbId) - .assert(ev.messageId); + .assert(ev.messageId) + .assert('string' === typeof ev.result.vfs); DbState.id = ev.dbId; if(waitForOpen) setTimeout(runTests2, 0); }); diff --git a/ext/wasm/test-opfs-vfs.js b/ext/wasm/test-opfs-vfs.js index e22acc0e3d..bba31bb9c9 100644 --- a/ext/wasm/test-opfs-vfs.js +++ b/ext/wasm/test-opfs-vfs.js @@ -37,7 +37,7 @@ const tryOpfsVfs = async function(sqlite3){ const urlArgs = new URL(self.location.href).searchParams; const dbFile = "my-persistent.db"; - if(urlArgs.has('delete')) sqlite3.opfs.deleteEntry(dbFile); + if(urlArgs.has('delete')) sqlite3.opfs.unlink(dbFile); const db = new opfs.OpfsDb(dbFile,'ct'); log("db file:",db.filename); @@ -62,13 +62,14 @@ const tryOpfsVfs = async function(sqlite3){ // Some sanity checks of the opfs utility functions... const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12); const aDir = testDir+'/test/dir'; - opfs.mkdir(aDir) || toss("mkdir failed"); - opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists"); - opfs.deleteEntry(testDir+'/test') && toss("delete 1 should have failed (dir not empty)"); - opfs.deleteEntry(testDir+'/test/dir') || toss("delete 2 failed"); - opfs.deleteEntry(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)"); - opfs.deleteEntry(testDir,true) || toss("delete 3 failed"); - opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed"); + await opfs.mkdir(aDir) || toss("mkdir failed"); + await opfs.mkdir(aDir) || toss("mkdir must pass if the dir exists"); + await opfs.unlink(testDir+'/test') && toss("delete 1 should have failed (dir not empty)"); + //await opfs.entryExists(testDir) + await opfs.unlink(testDir+'/test/dir') || toss("delete 2 failed"); + await opfs.unlink(testDir+'/test/dir') && toss("delete 2b should have failed (dir already deleted)"); + await opfs.unlink(testDir, true) || toss("delete 3 failed"); + await opfs.entryExists(testDir) && toss("entryExists(",testDir,") should have failed"); }finally{ db.close(); } diff --git a/ext/wasm/tester1-worker.html b/ext/wasm/tester1-worker.html index e1a7247e3e..4d2df0c8d1 100644 --- a/ext/wasm/tester1-worker.html +++ b/ext/wasm/tester1-worker.html @@ -14,7 +14,7 @@ -

sqlite3 WASM/JS tester #1 (Worker thread)

+

sqlite3 WASM/JS tester #1 (Worker thread)

See tester1.html for the UI-thread variant.
@@ -46,6 +46,14 @@ case 'log': logHtml(data.payload.cssClass, ...data.payload.args); break; + case 'error': + logHtml('error', ...data.payload.args); + break; + case 'test-result': + document.querySelector('#color-target').classList.add( + data.payload.pass ? 'tests-pass' : 'tests-fail' + ); + break; default: logHtml('error',"Unhandled message:",data.type); }; diff --git a/ext/wasm/tester1.html b/ext/wasm/tester1.html index 0ceacc5c36..f7a2fba4af 100644 --- a/ext/wasm/tester1.html +++ b/ext/wasm/tester1.html @@ -14,7 +14,7 @@ -

sqlite3 WASM/JS tester #1 (UI thread)

+

sqlite3 WASM/JS tester #1 (UI thread)

See tester1-worker.html for the Worker-thread variant.
diff --git a/ext/wasm/tester1.js b/ext/wasm/tester1.js index 016886a30a..4f32368701 100644 --- a/ext/wasm/tester1.js +++ b/ext/wasm/tester1.js @@ -99,6 +99,14 @@ }; } } + const reportFinalTestStatus = function(pass){ + if(isUIThread()){ + const e = document.querySelector('#color-target'); + e.classList.add(pass ? 'tests-pass' : 'tests-fail'); + }else{ + postMessage({type:'test-result', payload:{pass}}); + } + }; const log = (...args)=>{ //console.log(...args); logClass('',...args); @@ -139,28 +147,19 @@ toBool: function(expr){ return (expr instanceof Function) ? !!expr() : !!expr; }, - /** abort() if expr is false. If expr is a function, it - is called and its result is evaluated. + /** Throws if expr is false. If expr is a function, it is called + and its result is evaluated. If passed multiple arguments, + those after the first are a message string which get applied + as an exception message if the assertion fails. The message + arguments are concatenated together with a space between each. */ - assert: function f(expr, msg){ - if(!f._){ - f._ = ('undefined'===typeof abort - ? (msg)=>{throw new Error(msg)} - : abort); - } + assert: function f(expr, ...msg){ ++this.counter; if(!this.toBool(expr)){ - f._(msg || "Assertion failed."); + throw new Error(msg.length ? msg.join(' ') : "Assertion failed."); } return this; }, - /** Identical to assert() but throws instead of calling - abort(). */ - affirm: function(expr, msg){ - ++this.counter; - if(!this.toBool(expr)) throw new Error(msg || "Affirmation failed."); - return this; - }, /** Calls f() and squelches any exception it throws. If it does not throw, this function throws. */ mustThrow: function(f, msg){ @@ -301,9 +300,11 @@ "Done running tests.",TestUtil.counter,"assertions in", roundMs(runtime),'ms'); pok(); + reportFinalTestStatus(true); }catch(e){ error(e); pnok(e); + reportFinalTestStatus(false); } }.bind(this)); } @@ -1117,10 +1118,11 @@ const dbFile = '/tester1.db'; wasm.sqlite3_wasm_vfs_unlink(0, dbFile); const db = this.db = new sqlite3.oo1.DB(dbFile); - T.assert(Number.isInteger(db.pointer)). - mustThrowMatching(()=>db.pointer=1, /read-only/). - assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)). - assert('main'===db.dbName(0)); + T.assert(Number.isInteger(db.pointer)) + .mustThrowMatching(()=>db.pointer=1, /read-only/) + .assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1)) + .assert('main'===db.dbName(0)) + .assert('string' === typeof db.dbVfsName()); // Custom db error message handling via sqlite3_prepare_v2/v3() let rc = capi.sqlite3_prepare_v3(db.pointer, {/*invalid*/}, -1, 0, null, null); T.assert(capi.SQLITE_MISUSE === rc) @@ -1737,13 +1739,14 @@ (sqlite3)=>{return !!sqlite3.opfs}) .t({ name: 'OPFS sanity checks', - test: function(sqlite3){ + test: async function(sqlite3){ + const opfs = sqlite3.opfs; const filename = 'sqlite3-tester1.db'; const pVfs = capi.sqlite3_vfs_find('opfs'); T.assert(pVfs); const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn); unlink(); - let db = new sqlite3.opfs.OpfsDb(filename); + let db = new opfs.OpfsDb(filename); try { db.exec([ 'create table p(a);', @@ -1751,13 +1754,26 @@ ]); T.assert(3 === db.selectValue('select count(*) from p')); db.close(); - db = new sqlite3.opfs.OpfsDb(filename); + db = new opfs.OpfsDb(filename); db.exec('insert into p(a) values(4),(5),(6)'); T.assert(6 === db.selectValue('select count(*) from p')); }finally{ db.close(); unlink(); } + + // Some sanity checks of the opfs utility functions... + const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12); + const aDir = testDir+'/test/dir'; + T.assert(await opfs.mkdir(aDir), "mkdir failed") + .assert(await opfs.mkdir(aDir), "mkdir must pass if the dir exists") + .assert(!(await opfs.unlink(testDir+'/test')), "delete 1 should have failed (dir not empty)") + .assert((await opfs.unlink(testDir+'/test/dir')), "delete 2 failed") + .assert(!(await opfs.unlink(testDir+'/test/dir')), + "delete 2b should have failed (dir already deleted)") + .assert((await opfs.unlink(testDir, true)), "delete 3 failed") + .assert(!(await opfs.entryExists(testDir)), + "entryExists(",testDir,") should have failed"); } }/*OPFS sanity checks*/) ;/* end OPFS tests */ diff --git a/manifest b/manifest index 2d888a58ab..f644d6bc06 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sthe\sUPDATE\smethod\sof\sthe\ssqlite_dbpage\svirtual\stable\srebust\sagainst\nOOM\serrors.\s\s[forum:/forumpost/bbcf0dd6ca|Forum\spost\sbbcf0dd6ca].\s\sTest\scase\nin\sTH3. -D 2022-10-31T18:01:05.280 +C Significant\scleanups\sand\sexpansion\sof\sthe\ssqlite3.opfs\sutilities.\sAdd\soo1.DB.dbVfsName().\sAdd\sVFS\sname\sto\sworker1:open's\sarguments\sand\sresult. +D 2022-11-01T07:49:49.018 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -500,12 +500,12 @@ F ext/wasm/api/post-js-header.js d6ab3dfef4a06960d28a7eaa338d4e2a1a5981e9b387181 F ext/wasm/api/pre-js.js 287e462f969342b032c03900e668099fa1471d852df7a472de5bc349161d9c04 F ext/wasm/api/sqlite3-api-cleanup.js ecdc69dbfccfe26146f04799fcfd4a6f5790d46e7e3b9b6e9b0491f92ed8ae34 F ext/wasm/api/sqlite3-api-glue.js ebfd1d01b249c705482bfe3bdef3b1feaecf55821c46abf79b8105ec9b1c2b1a -F ext/wasm/api/sqlite3-api-oo1.js 4028bc2bac7e3ae2d23b7c99828155b4a06da006b51dc2a929bc0db26337370d -F ext/wasm/api/sqlite3-api-opfs.js 6880cc79a4d1b6075942298d9d1ab07e24d81fbd9e5fe6b7c797b86e4b2af596 -F ext/wasm/api/sqlite3-api-prologue.js 873986ca150c79510f647b910f8349bc71b14db21e444cab3b9fad9c4f39ffc7 -F ext/wasm/api/sqlite3-api-worker1.js 7de5160ba86ed216e8b99ba1ae0c39c1ff85c11a4818252bfc8dd0dc21109b84 +F ext/wasm/api/sqlite3-api-oo1.js ce8db98ab7c394fd8fb99ae135c46269a1a0162250a70e39e7975b4317a03640 +F ext/wasm/api/sqlite3-api-opfs.js 59b278ed00764fc47ba88be0582ab3fc3ce725e02b6d86459464cc029b9ac356 +F ext/wasm/api/sqlite3-api-prologue.js 850581aa0c759db25c04d64b340b0c5272f253fb195377acc8d851e376710ad3 +F ext/wasm/api/sqlite3-api-worker1.js 4f920a54fb97d4ca50632d45bd7d011a55016eb5a5883725033abb450903bc6f F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 -F ext/wasm/api/sqlite3-opfs-async-proxy.js b2264efef84c4a0af5dab426d7573d8b4ee5af3a25ba108b709a835a613c5e36 +F ext/wasm/api/sqlite3-opfs-async-proxy.js 2627bf8dc04c24783a55de40d8a6d18da31e81a9bb926a5b672b402f582099b5 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 F ext/wasm/api/sqlite3-wasm.c 14ac9c03f6585332f882703f3427f11ffe8ffe8b6c0e252be2c518f7aac6ab6a F ext/wasm/api/sqlite3-worker1-promiser.js 0c7a9826dbf82a5ed4e4f7bf7816e825a52aff253afbf3350431f5773faf0e4b @@ -514,7 +514,7 @@ F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d F ext/wasm/batch-runner.js 49609e89aaac9989d6c1ad3fae268e4878e1ad7bc5fd3e5c2f44959660780b2e F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f -F ext/wasm/common/testing.css 739b58c44511f642f16f57b701c84dc9ee412d8bc47b3d8a99d947babfa69d9d +F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06 F ext/wasm/common/whwasmutil.js 77930367c2a65cf6fd6f99ad3644ede33e4d20466f5e506eb87b8d101a0a7655 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508 @@ -522,9 +522,9 @@ F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb0 F ext/wasm/demo-jsstorage.html 409c4be4af5f207fb2877160724b91b33ea36a3cd8c204e8da1acb828ffe588e F ext/wasm/demo-jsstorage.js 44e3ae7ec2483b6c511384c3c290beb6f305c721186bcf5398ca4e00004a06b8 F ext/wasm/demo-worker1-promiser.html 1de7c248c7c2cfd4a5783d2aa154bce62d74c6de98ab22f5786620b3354ed15f -F ext/wasm/demo-worker1-promiser.js f68ffbbe1c6086e18ce7961b8fc2b40dd88db174f59052e228c06b07484945ca +F ext/wasm/demo-worker1-promiser.js 988ce92220c1cf1dd95fcb6b59734f0ae677942469390ddd8a64f4bbb5f99821 F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2eb1ab2c68ef5d -F ext/wasm/demo-worker1.js 8ba51d94c4601fa5c313d9e59b63b238f5305b5d5739ad21f4782a0161e6682e +F ext/wasm/demo-worker1.js 117e4eedc62e103e287f0e4a694add7e13a200a4d7056e718645032288c4a8ab F ext/wasm/dist.make 481289899a07958439d07ee4302ff86235fa0fbb72f17ea05db2be90a94abf90 F ext/wasm/fiddle.make e570ec1bfc7d803507a2e514fe32f673fe001b2114b85c73c3964a462ba8bcfc F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f @@ -546,10 +546,10 @@ F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f129 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f -F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548 -F ext/wasm/tester1-worker.html d02b9d38876b023854cf8955e77a40912f7e516956b4dbe1ec7f215faac273ee -F ext/wasm/tester1.html c6c47e5a8071eb09cb1301104435c8e44fbb5719c92411f5b2384a461f9793c5 -F ext/wasm/tester1.js 2427ac48e255f658ad81163b5dc6372a8609ab6ab60e295e371d1e5fe9a495ab +F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac +F ext/wasm/tester1-worker.html 51bf39e2b87f974ae3d5bc3086e2fb36d258f3698c54f6e21ba4b3b99636fa27 +F ext/wasm/tester1.html 624ec41cd9f78a1f2b6d7df70aaa7a6394396b1f2455ecbd6de5775c1275b121 +F ext/wasm/tester1.js 157eb499aad3365e33a7d95d6847c1d58d533335bc19d79bd3bc700b6350d1a5 F ext/wasm/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd72273503ae7d5 F ext/wasm/wasmfs.make fb2d3c4a298b12cf1ec994ad1d0f1d027ae297449b364cde43d2eb807d68048f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x @@ -2054,8 +2054,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 c7750c101d9b7baa31496731bc34ea0a9b2bad0c11e2b3e92a8c7b327135c1bb -R 196f3951f65687c9f90c3fbde15932cd -U drh -Z 5dce30cfcdbfc3a472a813e27f5eb193 +P d15c9a4a323b825eb80e706e12e9df95e5db458024f51e6e537940efc8234d8b +R 095ddbe256b2c65febf55a9bc126a8d9 +U stephan +Z 25b125153b5de3e07d69ba6b59e3eede # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0771c2adac..f229779db4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d15c9a4a323b825eb80e706e12e9df95e5db458024f51e6e537940efc8234d8b \ No newline at end of file +86a341d7e061f946b39e8647ddd4743013b851b33ae9e6e755d8dbc53fba5286 \ No newline at end of file