]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Implement OPFS xAccess(), albeit with more limited semantics than the VFS API calls...
authorstephan <stephan@noemail.net>
Sat, 17 Sep 2022 23:29:27 +0000 (23:29 +0000)
committerstephan <stephan@noemail.net>
Sat, 17 Sep 2022 23:29:27 +0000 (23:29 +0000)
FossilOrigin-Name: c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f

ext/wasm/sqlite3-opfs-async-proxy.js
ext/wasm/x-sync-async.js
manifest
manifest.uuid

index 1ff9397027aa1da17ca193d95673e043aa79f7c1..4a60e8aa1a5b0a586106e7da344e27f562864c66 100644 (file)
@@ -72,9 +72,17 @@ warn("This file is very much experimental and under construction.",self.location
 const __openFiles = Object.create(null);
 
 /**
-   Map of dir names to FileSystemDirectoryHandle objects.
+   Expects an OPFS file path. It gets resolved, such that ".."
+   components are properly expanded, and returned. If the 2nd
+   are is true, it's returned as an array of path elements,
+   else it's returned as an absolute path string.
 */
-const __dirCache = new Map;
+const getResolvedPath = function(filename,splitIt){
+  const p = new URL(
+    filename, 'file://irrelevant'
+  ).pathname;
+  return splitIt ? p.split('/').filter((v)=>!!v) : p;
+}
 
 /**
    Takes the absolute path to a filesystem element. Returns an array
@@ -83,21 +91,13 @@ const __dirCache = new Map;
    along the way. Throws if any creation or resolution fails.
 */
 const getDirForPath = async function f(absFilename, createDirs = false){
-  const url = new URL(
-    absFilename, 'file://xyz'
-  ) /* use URL to resolve path pieces such as a/../b */;
-  const path = url.pathname.split('/').filter((v)=>!!v);
+  const path = getResolvedPath(absFilename, true);
   const filename = path.pop();
-  const allDirs = '/'+path.join('/');
-  let dh = __dirCache.get(allDirs);
-  if(!dh){
-    dh = state.rootDir;
-    for(const dirName of path){
-      if(dirName){
-        dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs});
-      }
+  let dh = state.rootDir;
+  for(const dirName of path){
+    if(dirName){
+      dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs});
     }
-    __dirCache.set(allDirs, dh);
   }
   return [dh, filename];
 };
@@ -113,14 +113,6 @@ const storeAndNotify = (opName, value)=>{
   Atomics.notify(state.opSABView, state.opIds[opName]);
 };
 
-const isInt32 = function(n){
-  return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/)
-    && !!(n===(n|0) && n<=2147483647 && n>=-2147483648);
-};
-const affirm32Bits = function(n){
-  return isInt32(n) || toss("Number is too large (>31 bits) (FIXME!):",n);
-};
-
 /**
    Throws if fh is a file-holding object which is flagged as read-only.
 */
@@ -134,9 +126,26 @@ const affirmNotRO = function(opName,fh){
    to simplify finding them.
 */
 const vfsAsyncImpls = {
-  xAccess: async function({filename, exists, readWrite}){
-    warn("xAccess(",arguments[0],") is TODO");
-    const rc = state.sq3Codes.SQLITE_IOERR;
+  xAccess: async function(filename){
+    log("xAccess(",arguments[0],")");
+    /* OPFS cannot support the full range of xAccess() queries sqlite3
+       calls for. We can essentially just tell if the file is
+       accessible, but if it is it's automatically writable (unless
+       it's locked, which we cannot(?) know without trying to open
+       it). OPFS does not have the notion of read-only.
+
+       The return semantics of this function differ from sqlite3's
+       xAccess semantics because we are limited in what we can
+       communicate back to our synchronous communication partner: 0 =
+       accessible, non-0 means not accessible.
+    */
+    let rc = 0;
+    try{
+      const [dh, fn] = await getDirForPath(filename);
+      await dh.getFileHandle(fn);
+    }catch(e){
+      rc = state.sq3Codes.SQLITE_IOERR;
+    }
     storeAndNotify('xAccess', rc);
   },
   xClose: async function(fid){
@@ -156,12 +165,34 @@ const vfsAsyncImpls = {
     }
   },
   xDelete: async function({filename, syncDir/*ignored*/}){
+    /* The syncDir flag is, for purposes of the VFS API's semantics,
+       ignored here. However, if it has the value 0x1234 then: after
+       deleting the given file, recursively try to delete any empty
+       directories left behind in its wake (ignoring any errors and
+       stopping at the first failure).
+
+       That said: we don't know for sure that removeEntry() fails if
+       the dir is not empty because the API is not documented. It has,
+       however, a "recursive" flag which defaults to false, so
+       presumably it will fail if the dir is not empty and that flag
+       is false.
+    */
     log("xDelete(",arguments[0],")");
     try {
-      const [hDir, filenamePart] = await getDirForPath(filename, false);
-      await hDir.removeEntry(filenamePart);
+      while(filename){
+        const [hDir, filenamePart] = await getDirForPath(filename, false);
+        //log("Removing:",hDir, filenamePart);
+        if(!filenamePart) break;
+        await hDir.removeEntry(filenamePart);
+        if(0x1234 !== syncDir) break;
+        filename = getResolvedPath(filename, true);
+        filename.pop();
+        filename = filename.join('/');
+      }
     }catch(e){
-      /* Ignoring: _presumably_ the file can't be found. */
+      /* Ignoring: _presumably_ the file can't be found or a dir is
+         not empty. */
+      //error("Delete failed",filename, e.message);
     }
     storeAndNotify('xDelete', 0);
   },
@@ -268,8 +299,8 @@ const vfsAsyncImpls = {
   xWrite: async function({fid,src,n,offset}){
     log("xWrite(",arguments[0],")");
     let rc;
+    const fh = __openFiles[fid];
     try{
-      const fh = __openFiles[fid];
       affirmNotRO('xWrite', fh);
       const nOut = fh.accessHandle.write(new UInt8Array(fh.sab, 0, n), {at: offset});
       rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE;
@@ -324,4 +355,4 @@ navigator.storage.getDirectory().then(function(d){
     }
   };
   wMsg('loaded');
-});
+}).catch((e)=>error(e));
index 9329e765ac25969f6820b21ab8a4782f2722c437..250fde349fa20a4885a12bb9a78ac1fb97076718 100644 (file)
@@ -47,13 +47,14 @@ const initOpfsVfs = function(sqlite3){
   warn("This file is very much experimental and under construction.",self.location.pathname);
 
   if(self.window===self ||
-     !self.SharedBufferArray ||
+     !self.SharedArrayBuffer ||
      !self.FileSystemHandle ||
      !self.FileSystemDirectoryHandle ||
      !self.FileSystemFileHandle ||
      !self.FileSystemFileHandle.prototype.createSyncAccessHandle ||
      !navigator.storage.getDirectory){
     warn("This environment does not have OPFS support.");
+    return;
   }
 
   const capi = sqlite3.capi;
@@ -282,7 +283,11 @@ const initOpfsVfs = function(sqlite3){
      are in alphabetical order to simplify finding them.
   */
   const vfsSyncWrappers = {
-    // TODO: xAccess
+    xAccess: function(pVfs,zName,flags,pOut){
+      const rc = opRun('xAccess', wasm.cstringToJs(zName));
+      wasm.setMemValue(pOut, rc ? 0 : 1, 'i32');
+      return 0;
+    },
     xCurrentTime: function(pVfs,pOut){
       /* If it turns out that we need to adjust for timezone, see:
          https://stackoverflow.com/a/11760121/1458521 */
@@ -397,17 +402,25 @@ const initOpfsVfs = function(sqlite3){
       const fid = sq3File.pointer;
       const openFlags = capi.SQLITE_OPEN_CREATE
             | capi.SQLITE_OPEN_READWRITE
-            | capi.SQLITE_OPEN_DELETEONCLOSE
+            //| capi.SQLITE_OPEN_DELETEONCLOSE
             | capi.SQLITE_OPEN_MAIN_DB;
       const pOut = wasm.scopedAlloc(8);
       const dbFile = "/sanity/check/file";
-      let rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, dbFile,
-                                     fid, openFlags, pOut);
+      const zDbFile = wasm.scopedAllocCString(dbFile);
+      let rc;
+      vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+      rc = wasm.getMemValue(pOut,'i32');
+      log("xAccess(",dbFile,") exists ?=",rc);
+      rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile,
+                                fid, openFlags, pOut);
       log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]);
       if(isWorkerErrCode(rc)){
         error("open failed with code",rc);
         return;
       }
+      vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+      rc = wasm.getMemValue(pOut,'i32');
+      if(!rc) toss("xAccess() failed to detect file.");
       rc = ioSyncWrappers.xSync(sq3File.pointer, 0);
       if(rc) toss('sync failed w/ rc',rc);
       rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024);
@@ -417,11 +430,14 @@ const initOpfsVfs = function(sqlite3){
       if(rc) toss('xFileSize failed w/ rc',rc);
       log("xFileSize says:",wasm.getMemValue(pOut, 'i64'));
       log("xSleep()ing before close()ing...");
-      opRun('xSleep',1500);
+      opRun('xSleep',1000);
       rc = ioSyncWrappers.xClose(fid);
       log("xClose rc =",rc,"opSABView =",state.opSABView);
       log("Deleting file:",dbFile);
-      opRun('xDelete', dbFile);
+      vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234);
+      vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+      rc = wasm.getMemValue(pOut,'i32');
+      if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete().");
     }finally{
       sq3File.dispose();
       wasm.scopedAllocPop(scope);
index bef8fab86f09ca87ef94e6f90fe1b8b604270380..503b895473680e41407fcb709e7e2b52d2a4e952 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Generic\scleanups\sin\sthe\sOPFS\ssync/async\sproxy.
-D 2022-09-17T21:13:26.228
+C Implement\sOPFS\sxAccess(),\salbeit\swith\smore\slimited\ssemantics\sthan\sthe\sVFS\sAPI\scalls\sfor.\sAdd\sa\sway\sfor\sOPFS\sxDelete()\sto\soptionally\srecursively\sremove\sempty\sdirs\sleft\sover\safter\sdeleting\sa\sfile.
+D 2022-09-17T23:29:27.832
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -523,7 +523,7 @@ F ext/wasm/speedtest1.html fbb8e4d1639028443f3687a683be660beca6927920545cf6b1fdf
 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
 F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
-F ext/wasm/sqlite3-opfs-async-proxy.js ceb5e3a190bfd42d25fc137b3aaf09adf6469b5f1e33528a64ce7ff74e5ef5b1
+F ext/wasm/sqlite3-opfs-async-proxy.js c4be9e614abea5f237564757141e3e128f0cff4b4cd8350f68e17347230fd34e
 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
@@ -534,7 +534,7 @@ F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c2
 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
 F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d
 F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80
-F ext/wasm/x-sync-async.js c99a6f98a8b0ccb43b2883a1bc1da42d0d84b26b5d4fd99d2f053ffe209a0a1f
+F ext/wasm/x-sync-async.js a95e8acb04fd69526317ee63bf25fa478a20e223d5dc16f7526baf5d81e07d1e
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
@@ -2030,8 +2030,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 44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04
-R 75c9aa6717389505aa78f157abdf6d44
+P f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492
+R 5aad77d6a3a0d677e9c84e63e20c4294
 U stephan
-Z 6a0a0bcb470dbb44e9d0c87c09f1fedd
+Z 31fefe04f3f471aad99d738829acf9a8
 # Remove this line to create a well-formed Fossil manifest.
index 4f626ec440a875e8b671bea79aecc75a8182c5ca..6035cd7f00c9884c0ab985878c9edcb4a4dc578a 100644 (file)
@@ -1 +1 @@
-f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492
\ No newline at end of file
+c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f
\ No newline at end of file