]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Numerous cleanups in the JS bits. Removed some now-defunct wasm test files. Expose...
authorstephan <stephan@noemail.net>
Sun, 18 Sep 2022 17:32:35 +0000 (17:32 +0000)
committerstephan <stephan@noemail.net>
Sun, 18 Sep 2022 17:32:35 +0000 (17:32 +0000)
FossilOrigin-Name: 26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8

15 files changed:
ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-opfs.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/api/sqlite3-api-worker1.js
ext/wasm/index.html
ext/wasm/scratchpad-opfs-worker.html [deleted file]
ext/wasm/scratchpad-opfs-worker.js [deleted file]
ext/wasm/scratchpad-opfs-worker2.js [deleted file]
ext/wasm/scratchpad-wasmfs-main.js
ext/wasm/sqlite3-opfs-async-proxy.js
ext/wasm/test-opfs-vfs.html
ext/wasm/test-opfs-vfs.js
ext/wasm/testing1.js
manifest
manifest.uuid

index 368986933f36e4820cfcbb020fed15617230f916..fb01e987138dc6a58c9ed9c18d67c9c8abce2d20 100644 (file)
@@ -85,9 +85,24 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
 
   /**
      A proxy for DB class constructors. It must be called with the
-     being-construct DB object as its "this".
+     being-construct DB object as its "this". See the DB constructor
+     for the argument docs. This is split into a separate function
+     in order to enable simple creation of special-case DB constructors,
+     e.g. a hypothetical LocalStorageDB or OpfsDB.
+
+     Expects to be passed a configuration object with the following
+     properties:
+
+     - `.filename`: the db filename. It may be a special name like ":memory:"
+       or "".
+
+     - `.flags`: as documented in the DB constructor.
+
+     - `.vfs`: as documented in the DB constructor.
+
+     It also accepts those as the first 3 arguments.
   */
-  const dbCtorHelper = function ctor(fn=':memory:', flags='c', vfsName){
+  const dbCtorHelper = function ctor(...args){
     if(!ctor._name2vfs){
       // Map special filenames which we handle here (instead of in C)
       // to some helpful metadata...
@@ -104,25 +119,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         filename: isWorkerThread || (()=>'session')
       };
     }
-    if('string'!==typeof fn){
-      toss3("Invalid filename for DB constructor.");
+    const opt = ctor.normalizeArgs(...args);
+    let fn = opt.filename, vfsName = opt.vfs, flagsStr = opt.flags;
+    if(('string'!==typeof fn && 'number'!==typeof fn)
+       || 'string'!==typeof flagsStr
+       || (vfsName && ('string'!==typeof vfsName && 'number'!==typeof vfsName))){
+      console.error("Invalid DB ctor args",opt,arguments);
+      toss3("Invalid arguments for DB constructor.");
     }
-    const vfsCheck = ctor._name2vfs[fn];
+    let fnJs = ('number'===typeof fn) ? capi.wasm.cstringToJs(fn) : fn;
+    const vfsCheck = ctor._name2vfs[fnJs];
     if(vfsCheck){
       vfsName = vfsCheck.vfs;
-      fn = vfsCheck.filename(fn);
+      fn = fnJs = vfsCheck.filename(fnJs);
     }
     let ptr, oflags = 0;
-    if( flags.indexOf('c')>=0 ){
+    if( flagsStr.indexOf('c')>=0 ){
       oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
     }
-    if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
+    if( flagsStr.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE;
     if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY;
     oflags |= capi.SQLITE_OPEN_EXRESCODE;
     const stack = capi.wasm.scopedAllocPush();
     try {
       const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */;
-      const pVfsName = vfsName ? capi.wasm.scopedAllocCString(vfsName) : 0;
+      const pVfsName = vfsName ? (
+        ('number'===typeof vfsName ? vfsName : capi.wasm.scopedAllocCString(vfsName))
+      ): 0;
       const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName);
       ptr = capi.wasm.getPtrValue(ppDb);
       checkSqlite3Rc(ptr, rc);
@@ -132,11 +155,36 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }finally{
       capi.wasm.scopedAllocPop(stack);
     }
-    this.filename = fn;
+    this.filename = fnJs;
     __ptrMap.set(this, ptr);
     __stmtMap.set(this, Object.create(null));
     __udfMap.set(this, Object.create(null));
   };
+
+  /**
+     A helper for DB constructors. It accepts either a single
+     config-style object or up to 3 arguments (filename, dbOpenFlags,
+     dbVfsName). It returns a new object containing:
+
+     { filename: ..., flags: ..., vfs: ... }
+
+     If passed an object, any additional properties it has are copied
+     as-is into the new object.
+  */
+  dbCtorHelper.normalizeArgs = function(filename,flags = 'c',vfs = null){
+    const arg = {};
+    if(1===arguments.length && 'object'===typeof arguments[0]){
+      const x = arguments[0];
+      Object.keys(x).forEach((k)=>arg[k] = x[k]);
+      if(undefined===arg.flags) arg.flags = 'c';
+      if(undefined===arg.vfs) arg.vfs = null;
+    }else{
+      arg.filename = filename;
+      arg.flags = flags;
+      arg.vfs = vfs;
+    }
+    return arg;
+  };
   
   /**
      The DB class provides a high-level OO wrapper around an sqlite3
@@ -175,6 +223,17 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      or not at all, to use the default. If passed a value, it must
      be the string name of a VFS
 
+     The constructor optionally (and preferably) takes its arguments
+     in the form of a single configuration object with the following
+     properties:
+
+     - `.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.
+
      For purposes of passing a DB instance to C-style sqlite3
      functions, the DB object's read-only `pointer` property holds its
      `sqlite3*` pointer value. That property can also be used to check
@@ -187,12 +246,12 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      the database. In this mode, only a single database is permitted
      in each storage object. This feature is experimental and subject
      to any number of changes (including outright removal). This
-     support requires a specific build of sqlite3, the existence of
-     which can be determined at runtime by checking for a non-0 return
-     value from sqlite3.capi.sqlite3_vfs_find("kvvfs").
+     support requires the kvvfs sqlite3 VFS, the existence of which
+     can be determined at runtime by checking for a non-0 return value
+     from sqlite3.capi.sqlite3_vfs_find("kvvfs").
   */
-  const DB = function ctor(fn=':memory:', flags='c', vfsName){
-    dbCtorHelper.apply(this, Array.prototype.slice.call(arguments));
+  const DB = function(...args){
+    dbCtorHelper.apply(this, args);
   };
 
   /**
@@ -361,12 +420,31 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        closed. After calling close(), `this.pointer` will resolve to
        `undefined`, so that can be used to check whether the db
        instance is still opened.
+
+       If this.onclose.before is a function then it is called before
+       any close-related cleanup.
+
+       If this.onclose.after is a function then it is called after the
+       db is closed but before auxiliary state like this.filename is
+       cleared.
+
+       Both onclose handlers are passed this object. If this db is not
+       opened, neither of the handlers are called. Any exceptions the
+       handlers throw are ignored because "destructors must not
+       throw."
+
+       Note that garbage collection of a db handle, if it happens at
+       all, will never trigger close(), so onclose handlers are not a
+       reliable way to implement close-time cleanup or maintenance of
+       a db.
     */
     close: function(){
       if(this.pointer){
+        if(this.onclose && (this.onclose.before instanceof Function)){
+          try{this.onclose.before(this)}
+          catch(e){/*ignore*/}
+        }
         const pDb = this.pointer;
-        let s;
-        const that = this;
         Object.keys(__stmtMap.get(this)).forEach((k,s)=>{
           if(s && s.pointer) s.finalize();
         });
@@ -377,6 +455,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         __stmtMap.delete(this);
         __udfMap.delete(this);
         capi.sqlite3_close_v2(pDb);
+        if(this.onclose && (this.onclose.after instanceof Function)){
+          try{this.onclose.after(this)}
+          catch(e){/*ignore*/}
+        }
         delete this.filename;
       }
     },
@@ -401,13 +483,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       }
     },
     /**
-       Similar to this.filename but will return NULL for special names
-       like ":memory:". Not of much use until we have filesystem
-       support. Throws if the DB has been closed. If passed an
-       argument it then it will return the filename of the ATTACHEd db
-       with that name, else it assumes a name of `main`.
+       Similar to this.filename but will return a falsy value for
+       special names like ":memory:". Throws if the DB has been
+       closed. If passed an argument it then it will return the
+       filename of the ATTACHEd db with that name, else it assumes a
+       name of `main`.
     */
-    fileName: function(dbName='main'){
+    getFilename: function(dbName='main'){
       return capi.sqlite3_db_filename(affirmDbOpen(this).pointer, dbName);
     },
     /**
@@ -1591,7 +1673,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       ooApi: "0.1"
     },
     DB,
-    Stmt
+    Stmt,
+    dbCtorHelper
   }/*oo1 object*/;
 
 });
index af1c6f76088fa21b981cdb7ce030ee63bd00808c..ede4fb32b1f9e8d1662352e2aab9ca16cf255807 100644 (file)
@@ -42,8 +42,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     that number will increase as the OPFS API matures).
 
   - The OPFS features used here are only available in dedicated Worker
-    threads. This file tries to detect that case and becomes a no-op
-    if those features do not seem to be available.
+    threads. This file tries to detect that case, resulting in a
+    rejected Promise if those features do not seem to be available.
 
   - It requires the SharedArrayBuffer and Atomics classes, and the
     former is only available if the HTTP server emits the so-called
@@ -72,7 +72,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
      returned Promise resolves.
 
    On success, the Promise resolves to the top-most sqlite3 namespace
-   object.
+   object and that object gets a new object installed in its
+   `opfs` property, containing several OPFS-specific utilities.
 */
 sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri){
   const options = (asyncProxyUri && 'object'===asyncProxyUri) ? asyncProxyUri : {
@@ -89,6 +90,13 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
     options.proxyUri = callee.defaultProxyUri;
   }
   delete sqlite3.installOpfsVfs;
+
+  /**
+     Generic utilities for working with OPFS. This will get filled out
+     by the Promise setup and, on success, installed as sqlite3.opfs.
+  */
+  const opfsUtil = Object.create(null);
+
   const thePromise = new Promise(function(promiseResolve, promiseReject){
     const logPrefix = "OPFS syncer:";
     const warn =  (...args)=>{
@@ -118,9 +126,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
     const sqlite3_vfs = capi.sqlite3_vfs;
     const sqlite3_file = capi.sqlite3_file;
     const sqlite3_io_methods = capi.sqlite3_io_methods;
-    const StructBinder = sqlite3.StructBinder;
     const W = new Worker(options.proxyUri);
-    const workerOrigOnError = W.onrror;
+    W._originalOnError = W.onerror /* will be restored later */;
     W.onerror = function(err){
       promiseReject(new Error("Loading OPFS async Worker failed for unknown reasons."));
     };
@@ -131,17 +138,37 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        This object must initially contain only cloneable or sharable
        objects. After the worker's "inited" message arrives, other types
        of data may be added to it.
+
+       For purposes of Atomics.wait() and Atomics.notify(), we use a
+       SharedArrayBuffer with one slot reserved for each of the API
+       proxy's methods. The sync side of the API uses Atomics.wait()
+       on the corresponding slot and the async side uses
+       Atomics.notify() on that slot.
+
+       The approach of using a single SAB to serialize comms for all
+       instances might(?) lead to deadlock situations in multi-db
+       cases. We should probably have one SAB here with a single slot
+       for locking a per-file initialization step and then allocate a
+       separate SAB like the above one for each file. That will
+       require a bit of acrobatics but should be feasible.
     */
     const state = Object.create(null);
     state.verbose = options.verbose;
-    state.fileBufferSize = 1024 * 64 + 8 /* size of fileHandle.sab. 64k = max sqlite3 page size */;
-    state.fbInt64Offset = state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64*/;
+    state.fileBufferSize =
+      1024 * 64 + 8 /* size of aFileHandle.sab. 64k = max sqlite3 page
+                       size. The additional bytes are space for
+                       holding BigInt results, since we cannot store
+                       those via the Atomics API (which only works on
+                       an Int32Array). */;
+    state.fbInt64Offset =
+      state.fileBufferSize - 8 /*spot in fileHandle.sab to store an int64 result */;
     state.opIds = Object.create(null);
     {
       let i = 0;
       state.opIds.xAccess = i++;
       state.opIds.xClose = i++;
       state.opIds.xDelete = i++;
+      state.opIds.xDeleteNoWait = i++;
       state.opIds.xFileSize = i++;
       state.opIds.xOpen = i++;
       state.opIds.xRead = i++;
@@ -149,14 +176,8 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       state.opIds.xSync = i++;
       state.opIds.xTruncate = i++;
       state.opIds.xWrite = i++;
+      state.opIds.mkdir = i++;
       state.opSAB = new SharedArrayBuffer(i * 4/*sizeof int32*/);
-      /* The approach of using a single SAB to serialize comms for all
-         instances may(?) lead to deadlock situations in multi-db
-         cases. We should probably have one SAB here with a single slot
-         for locking a per-file initialization step and then allocate a
-         separate SAB like the above one for each file. That will
-         require a bit of acrobatics but should be feasible.
-      */
     }
 
     state.sq3Codes = Object.create(null);
@@ -167,15 +188,14 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
       'SQLITE_IOERR_READ', 'SQLITE_IOERR_SHORT_READ',
       'SQLITE_IOERR_WRITE', 'SQLITE_IOERR_FSYNC',
       'SQLITE_IOERR_TRUNCATE', 'SQLITE_IOERR_DELETE',
-      'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE'
+      'SQLITE_IOERR_ACCESS', 'SQLITE_IOERR_CLOSE',
+      'SQLITE_IOERR_DELETE'
     ].forEach(function(k){
       state.sq3Codes[k] = capi[k] || toss("Maintenance required: not found:",k);
       state.sq3Codes._reverse[capi[k]] = k;
     });
 
     const isWorkerErrCode = (n)=>!!state.sq3Codes._reverse[n];
-    const opStore = (op,val=-1)=>Atomics.store(state.opSABView, state.opIds[op], val);
-    const opWait = (op,val=-1)=>Atomics.wait(state.opSABView, state.opIds[op], val);
 
     /**
        Runs the given operation in the async worker counterpart, waits
@@ -185,9 +205,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        given operation's signature in the async API counterpart.
     */
     const opRun = (op,args)=>{
-      opStore(op);
+      Atomics.store(state.opSABView, state.opIds[op], -1);
       wMsg(op, args);
-      opWait(op);
+      Atomics.wait(state.opSABView, state.opIds[op], -1);
       return Atomics.load(state.opSABView, state.opIds[op]);
     };
 
@@ -268,7 +288,7 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
        func with the same signature as described above.
     */
     const installMethod = function callee(tgt, name, func){
-      if(!(tgt instanceof StructBinder.StructType)){
+      if(!(tgt instanceof sqlite3.StructBinder.StructType)){
         toss("Usage error: target object is-not-a StructType.");
       }
       if(1===arguments.length){
@@ -429,7 +449,10 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
         return 0;
       },
       xDelete: function(pVfs, zName, doSyncDir){
-        return opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
+        opRun('xDelete', {filename: wasm.cstringToJs(zName), syncDir: doSyncDir});
+        /* We're ignoring errors because we cannot yet differentiate
+           between harmless and non-harmless failures. */
+        return 0;
       },
       xFullPathname: function(pVfs,zName,nOut,pOut){
         /* Until/unless we have some notion of "current dir"
@@ -521,6 +544,65 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
     for(let k of Object.keys(ioSyncWrappers)) inst(k, ioSyncWrappers[k]);
     inst = installMethod(opfsVfs);
     for(let k of Object.keys(vfsSyncWrappers)) inst(k, vfsSyncWrappers[k]);
+
+
+    /**
+       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 fails if it fails,
+       but cannot report the nature of the failure.
+    */
+    opfsUtil.deleteEntry = function(fsEntryName,recursive){
+      return 0===opRun('xDelete', {filename:fsEntryName, recursive});
+    };
+    /**
+       Exactly like deleteEntry() but runs asynchronously.
+    */
+    opfsUtil.deleteEntryAsync = async function(fsEntryName,recursive){
+      wMsg('xDeleteNoWait', {filename: fsEntryName, recursive});
+    };
+    /**
+       Synchronously creates the given directory name, recursively, in
+       the OPFS filesystem. Returns true if it succeeds or the
+       directory already exists, else false.
+    */
+    opfsUtil.mkdir = async function(absDirName){
+      return 0===opRun('mkdir', absDirName);
+    };
+    /**
+       Synchronously checks whether the given OPFS filesystem exists,
+       returning true if it does, false if it doesn't.
+    */
+    opfsUtil.entryExists = function(fsEntryName){
+      return 0===opRun('xAccess', fsEntryName);
+    };
+
+    /**
+       Generates a random ASCII string, intended for use as a
+       temporary file name. Its argument is the length of the string,
+       defaulting to 16.
+    */
+    opfsUtil.randomFilename = randomFilename;
+    
+    if(sqlite3.oo1){
+      opfsUtil.OpfsDb = function(...args){
+        const opt = sqlite3.oo1.dbCtorHelper.normalizeArgs(...args);
+        opt.vfs = opfsVfs.$zName;
+        sqlite3.oo1.dbCtorHelper.call(this, opt);
+      };
+      opfsUtil.OpfsDb.prototype = Object.create(sqlite3.oo1.DB.prototype);
+    }
+
+    /**
+       Potential TODOs:
+
+       - Expose one or both of the Worker objects via opfsUtil and
+         publish an interface for proxying the higher-level OPFS
+         features like getting a directory listing.
+    */
     
     const sanityCheck = async function(){
       const scope = wasm.scopedAllocPush();
@@ -605,7 +687,9 @@ sqlite3.installOpfsVfs = function callee(asyncProxyUri = callee.defaultProxyUri)
                 warn("Running sanity checks because of opfs-sanity-check URL arg...");
                 sanityCheck();
               }
-              W.onerror = workerOrigOnError;
+              W.onerror = W._originalOnError;
+              delete W._originalOnError;
+              sqlite3.opfs = opfsUtil;
               promiseResolve(sqlite3);
               log("End of OPFS sqlite3_vfs setup.", opfsVfs);
             }catch(e){
index 1fc533da62abe524543cb8c5c844b1fbf7cde639..add8ad658edfa79365593a85fbe43f1f9c9dced1 100644 (file)
@@ -689,7 +689,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
   /** State for sqlite3_web_persistent_dir(). */
   let __persistentDir;
   /**
-     An experiment. Do not use.
+     An experiment. Do not use in client code.
 
      If the wasm environment has a persistent storage directory,
      its path is returned by this function. If it does not then
@@ -699,14 +699,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      environment to determine whether persistence filesystem support
      is available and, if it is, enables it (if needed).
 
+     This function currently only recognizes the WASMFS/OPFS storage
+     combination. "Plain" OPFS is provided via a separate VFS which
+     can optionally be installed (if OPFS is available on the system)
+     using sqlite3.installOpfsVfs().
+
      TODOs and caveats:
 
      - If persistent storage is available at the root of the virtual
        filesystem, this interface cannot currently distinguish that
-       from the lack of persistence. That case cannot currently (with
-       WASMFS/OPFS) happen, but it is conceivably possible in future
-       environments or non-browser runtimes (none of which are yet
-       supported targets).
+       from the lack of persistence. That can (in the mean time)
+       happen when using the JS-native "opfs" VFS, as opposed to the
+       WASMFS/OPFS combination.
   */
   capi.sqlite3_web_persistent_dir = function(){
     if(undefined !== __persistentDir) return __persistentDir;
@@ -764,6 +768,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     capi.wasm.exports.sqlite3_initialize();
   }
 
+  /**
+     Given an `sqlite3*` and an sqlite3_vfs name, returns a truthy
+     value (see below) if that db handle uses that VFS, else returns
+     false. If pDb is falsy then this function returns a truthy value
+     if the default VFS is that VFS. Results are undefined if pDb is
+     truthy but refers to an invalid pointer.
+
+     The 2nd argument may either be a JS string or a C-string
+     allocated from the wasm environment.
+
+     The truthy value it returns is a pointer to the `sqlite3_vfs`
+     object.
+
+     To permit safe use of this function from APIs which may be called
+     via the C stack (like SQL UDFs), this function does not throw: if
+     bad arguments cause a conversion error when passing into
+     wasm-space, false is returned.
+  */
+  capi.sqlite3_web_db_uses_vfs = function(pDb,vfsName){
+    try{
+      const pK = ('number'===vfsName)
+            ? capi.wasm.exports.sqlite3_vfs_find(vfsName)
+            : capi.sqlite3_vfs_find(vfsName);
+      if(!pK) return false;
+      else if(!pDb){
+        return capi.sqlite3_vfs_find(0)===pK ? pK : false;
+      }
+      const ppVfs = capi.wasm.allocPtr();
+      try{
+        return (
+          (0===capi.sqlite3_file_control(
+            pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
+          )) && (capi.wasm.getPtrValue(ppVfs) === pK)
+        ) ? pK : false;
+      }finally{
+        capi.wasm.dealloc(ppVfs);
+      }
+    }catch(e){
+      /* Ignore - probably bad args to a wasm-bound function. */
+      return false;
+    }
+  };
+
   if( self.window===self ){
     /* Features specific to the main window thread... */
 
@@ -812,7 +859,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     /**
        This routine guesses the approximate amount of
        window.localStorage and/or window.sessionStorage in use by the
-       kvvfs database backend.  Its argument must be one of
+       kvvfs database backend. Its argument must be one of
        ('session', 'local', ''). In the first two cases, only
        sessionStorage resp. localStorage is counted. If it's an empty
        string (the default) then both are counted. Only storage keys
@@ -842,34 +889,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
       return sz * 2 /* because JS uses UC16 encoding */;
     };
 
-    /**
-       Given an `sqlite3*`, returns a truthy value (see below) if that
-       db handle uses the "kvvfs" VFS, else returns false. If pDb is
-       NULL then this function returns true if the default VFS is
-       "kvvfs". Results are undefined if pDb is truthy but refers to
-       an invalid pointer.
-
-       The truthy value it returns is a pointer to the kvvfs
-       `sqlite3_vfs` object.
-    */
-    capi.sqlite3_web_db_is_kvvfs = function(pDb){
-      const pK = capi.sqlite3_vfs_find("kvvfs");
-      if(!pK) return false;
-      else if(!pDb){
-        return capi.sqlite3_vfs_find(0) && pK;
-      }
-      const scope = capi.wasm.scopedAllocPush();
-      try{
-        const ppVfs = capi.wasm.scopedAllocPtr();
-        return (
-          (0===capi.sqlite3_file_control(
-            pDb, "main", capi.SQLITE_FCNTL_VFS_POINTER, ppVfs
-          )) && (capi.wasm.getPtrValue(ppVfs) === pK)
-        ) ? pK : false;
-      }finally{
-        capi.wasm.scopedAllocPop(scope);
-      }
-    };
   }/* main-window-only bits */
 
   /* The remainder of the API will be set up in later steps. */
index 00359413b2654da11072de324bcaf27f9d5aa055..97f2677e6c9de34648f56cac9d9870d4df4977cb 100644 (file)
@@ -371,10 +371,14 @@ sqlite3.initWorker1API = function(){
     close: function(db,alsoUnlink){
       if(db){
         delete this.dbs[getDbId(db)];
-        const filename = db.fileName();
+        const filename = db.getFilename();
         db.close();
         if(db===this.defaultDb) this.defaultDb = undefined;
         if(alsoUnlink && filename){
+          /* This isn't necessarily correct: the db might be using a
+             VFS other than the default. How do we best resolve this
+             without having to special-case the kvvfs and opfs
+             VFSes? */
           sqlite3.capi.wasm.sqlite3_wasm_vfs_unlink(filename);
         }
       }
index fc0c33e4325a4ca9837e04bc9801f5f386dfed71..d13f9bb7f3f78131daf3149aa5f6d2b6e401d96a 100644 (file)
@@ -48,8 +48,6 @@
           experimenting with WASMFS/OPFS-based persistence. Maintenance
           reminder: we cannot currently (2022-09-15) load WASMFS in a
           worker due to an Emscripten limitation.</li>
-        <li><a href='scratchpad-opfs-worker.html'>scratchpad-opfs-worker</a>:
-          experimenting with OPFS from a Worker thread (without WASMFS).</li>
         <li><a href='test-opfs-vfs.html'>test-opfs-vfs</a>
           (<a href='test-opfs-vfs.html?opfs-sanity-check&opfs-verbose'>same
           with verbose output and sanity-checking tests</a>) is an
diff --git a/ext/wasm/scratchpad-opfs-worker.html b/ext/wasm/scratchpad-opfs-worker.html
deleted file mode 100644 (file)
index 0ab7822..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<!doctype html>
-<html lang="en-us">
-  <head>
-    <meta charset="utf-8">
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-    <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
-    <link rel="stylesheet" href="common/emscripten.css"/>
-    <link rel="stylesheet" href="common/testing.css"/>
-    <title>sqlite3 OPFS Worker-thread Scratchpad</title>
-  </head>
-  <body>
-    <header id='titlebar'><span>sqlite3 OPFS Worker-thread Scratchpad</span></header>
-    <p>All stuff on this page happens in the dev console.</p>
-    <hr>
-    <div id='test-output'></div>
-    <script src="scratchpad-opfs-worker.js"></script>
-  </body>
-</html>
diff --git a/ext/wasm/scratchpad-opfs-worker.js b/ext/wasm/scratchpad-opfs-worker.js
deleted file mode 100644 (file)
index e271647..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-  2022-05-22
-
-  The author disclaims copyright to this source code.  In place of a
-  legal notice, here is a blessing:
-
-  *   May you do good and not evil.
-  *   May you find forgiveness for yourself and forgive others.
-  *   May you share freely, never taking more than you give.
-
-  ***********************************************************************
-
-  A basic test script for sqlite3-api.js. This file must be run in
-  main JS thread. It will load sqlite3.js in a worker thread.
-*/
-'use strict';
-(function(){
-  const toss = function(...args){throw new Error(args.join(' '))};
-  const eOutput = document.querySelector('#test-output');
-  const logHtml = function(cssClass,...args){
-    if(Array.isArray(args[0])) args = args[0];
-    const ln = document.createElement('div');
-    if(cssClass) ln.classList.add(cssClass);
-    ln.append(document.createTextNode(args.join(' ')));
-    eOutput.append(ln);
-  };
-  const log = function(...args){
-    logHtml('',...args);
-  };
-  const error = function(...args){
-    logHtml('error',...args);
-  };
-  const warn = function(...args){
-    logHtml('warning',...args);
-  };
-
-  const W = new Worker("scratchpad-opfs-worker2.js");
-  W.onmessage = function(ev){
-    ev = ev.data;
-    const d = ev.data;
-    switch(ev.type){
-        case 'stdout': log(d); break;
-        case 'stderr': error(d); break;
-        default: warn("Unhandled message type:",ev); break;
-    }
-  };
-})();
diff --git a/ext/wasm/scratchpad-opfs-worker2.js b/ext/wasm/scratchpad-opfs-worker2.js
deleted file mode 100644 (file)
index 47ace63..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
-  2022-05-22
-
-  The author disclaims copyright to this source code.  In place of a
-  legal notice, here is a blessing:
-
-  *   May you do good and not evil.
-  *   May you find forgiveness for yourself and forgive others.
-  *   May you share freely, never taking more than you give.
-
-  ***********************************************************************
-
-  An experiment for wasmfs/opfs. This file MUST be in the same dir as
-  the sqlite3.js emscripten module or that module won't be able to
-  resolve the relative URIs (importScript()'s relative URI handling
-  is, quite frankly, broken).
-*/
-'use strict';
-
-const toss = function(...args){throw new Error(args.join(' '))};
-/**
-   Posts a message in the form {type,data} unless passed more than 2
-   args, in which case it posts {type, data:[arg1...argN]}.
-*/
-const wMsg = function(type,data){
-  postMessage({
-    type,
-    data: arguments.length<3
-      ? data
-      : Array.prototype.slice.call(arguments,1)
-  });
-};
-
-const stdout = function(...args){
-  wMsg('stdout',args);
-  console.log(...args);
-};
-const stderr = function(...args){
-  wMsg('stderr',args);
-  console.error(...args);
-};
-
-const log = console.log.bind(console);
-const warn = console.warn.bind(console);
-const error = console.error.bind(console);
-
-
-const initOpfsBits = async function(sqlite3){
-  if(!self.importScripts || !self.FileSystemFileHandle){
-    //|| !self.FileSystemFileHandle.prototype.createSyncAccessHandle){
-    // ^^^ sync API is not required with WASMFS/OPFS backend.
-    warn("OPFS is not available in this environment.");
-    return;
-  }else if(!sqlite3.capi.wasm.bigIntEnabled){
-    error("OPFS requires BigInt support but sqlite3.capi.wasm.bigIntEnabled is false.");
-    return;
-  }
-  //warn('self.FileSystemFileHandle =',self.FileSystemFileHandle);
-  //warn('self.FileSystemFileHandle.prototype =',self.FileSystemFileHandle.prototype);
-  const capi = sqlite3.capi,
-        wasm = capi.wasm;
-  const sqlite3_vfs = capi.sqlite3_vfs
-        || toss("Missing sqlite3.capi.sqlite3_vfs object.");
-  const sqlite3_file = capi.sqlite3_file
-        || toss("Missing sqlite3.capi.sqlite3_file object.");
-  const sqlite3_io_methods = capi.sqlite3_io_methods
-        || toss("Missing sqlite3.capi.sqlite3_io_methods object.");
-  const StructBinder = sqlite3.StructBinder || toss("Missing sqlite3.StructBinder.");
-  const debug = console.debug.bind(console),
-        log = console.log.bind(console);
-  warn("UNDER CONSTRUCTION: setting up OPFS VFS...");
-
-  const pDVfs = capi.sqlite3_vfs_find(null)/*pointer to default VFS*/;
-  const dVfs = pDVfs
-        ? new sqlite3_vfs(pDVfs)
-        : null /* dVfs will be null when sqlite3 is built with
-                  SQLITE_OS_OTHER. Though we cannot currently handle
-                  that case, the hope is to eventually be able to. */;
-  const oVfs = new sqlite3_vfs();
-  const oIom = new sqlite3_io_methods();
-  oVfs.$iVersion = 2/*yes, two*/;
-  oVfs.$szOsFile = capi.sqlite3_file.structInfo.sizeof;
-  oVfs.$mxPathname = 1024/*sure, why not?*/;
-  oVfs.$zName = wasm.allocCString("opfs");
-  oVfs.ondispose = [
-    '$zName', oVfs.$zName,
-    'cleanup dVfs', ()=>(dVfs ? dVfs.dispose() : null)
-  ];
-  if(dVfs){
-    oVfs.$xSleep = dVfs.$xSleep;
-    oVfs.$xRandomness = dVfs.$xRandomness;
-  }
-  // All C-side memory of oVfs is zeroed out, but just to be explicit:
-  oVfs.$xDlOpen = oVfs.$xDlError = oVfs.$xDlSym = oVfs.$xDlClose = null;
-
-  /**
-     Pedantic sidebar about oVfs.ondispose: the entries in that array
-     are items to clean up when oVfs.dispose() is called, but in this
-     environment it will never be called. The VFS instance simply
-     hangs around until the WASM module instance is cleaned up. We
-     "could" _hypothetically_ clean it up by "importing" an
-     sqlite3_os_end() impl into the wasm build, but the shutdown order
-     of the wasm engine and the JS one are undefined so there is no
-     guaranty that the oVfs instance would be available in one
-     environment or the other when sqlite3_os_end() is called (_if_ it
-     gets called at all in a wasm build, which is undefined).
-  */
-
-  /**
-     Installs a StructBinder-bound function pointer member of the
-     given name and function in the given StructType target object.
-     It creates a WASM proxy for the given function and arranges for
-     that proxy to be cleaned up when tgt.dispose() is called.  Throws
-     on the slightest hint of error (e.g. tgt is-not-a StructType,
-     name does not map to a struct-bound member, etc.).
-
-     Returns a proxy for this function which is bound to tgt and takes
-     2 args (name,func). That function returns the same thing,
-     permitting calls to be chained.
-
-     If called with only 1 arg, it has no side effects but returns a
-     func with the same signature as described above.
-  */
-  const installMethod = function callee(tgt, name, func){
-    if(!(tgt instanceof StructBinder.StructType)){
-      toss("Usage error: target object is-not-a StructType.");
-    }
-    if(1===arguments.length){
-      return (n,f)=>callee(tgt,n,f);
-    }
-    if(!callee.argcProxy){
-      callee.argcProxy = function(func,sig){
-        return function(...args){
-          if(func.length!==arguments.length){
-            toss("Argument mismatch. Native signature is:",sig);
-          }
-          return func.apply(this, args);
-        }
-      };
-      callee.removeFuncList = function(){
-        if(this.ondispose.__removeFuncList){
-          this.ondispose.__removeFuncList.forEach(
-            (v,ndx)=>{
-              if('number'===typeof v){
-                try{wasm.uninstallFunction(v)}
-                catch(e){/*ignore*/}
-              }
-              /* else it's a descriptive label for the next number in
-                 the list. */
-            }
-          );
-          delete this.ondispose.__removeFuncList;
-        }
-      };
-    }/*static init*/
-    const sigN = tgt.memberSignature(name);
-    if(sigN.length<2){
-      toss("Member",name," is not a function pointer. Signature =",sigN);
-    }
-    const memKey = tgt.memberKey(name);
-    //log("installMethod",tgt, name, sigN);
-    const fProxy = 1
-          // We can remove this proxy middle-man once the VFS is working
-          ? callee.argcProxy(func, sigN)
-          : func;
-    const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
-    tgt[memKey] = pFunc;
-    if(!tgt.ondispose) tgt.ondispose = [];
-    if(!tgt.ondispose.__removeFuncList){
-      tgt.ondispose.push('ondispose.__removeFuncList handler',
-                         callee.removeFuncList);
-      tgt.ondispose.__removeFuncList = [];
-    }
-    tgt.ondispose.__removeFuncList.push(memKey, pFunc);
-    return (n,f)=>callee(tgt, n, f);
-  }/*installMethod*/;
-
-  /**
-     Map of sqlite3_file pointers to OPFS handles.
-  */
-  const __opfsHandles = Object.create(null);
-
-  /**
-     Generates a random ASCII string len characters long, intended for
-     use as a temporary file name.
-  */
-  const randomFilename = function f(len=16){
-    if(!f._chars){
-      f._chars = "abcdefghijklmnopqrstuvwxyz"+
-        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
-        "012346789";
-      f._n = f._chars.length;
-    }
-    const a = [];
-    let i = 0;
-    for( ; i < len; ++i){
-      const ndx = Math.random() * (f._n * 64) % f._n | 0;
-      a[i] = f._chars[ndx];
-    }
-    return a.join('');
-  };
-
-  const rootDir = await navigator.storage.getDirectory();
-  log("rootDir =",rootDir);
-  
-  ////////////////////////////////////////////////////////////////////////
-  // Set up OPFS VFS methods...
-  let inst = installMethod(oVfs);
-  inst('xOpen', function(pVfs, zName, pFile, flags, pOutFlags){
-    const f = new sqlite3_file(pFile);
-    f.$pMethods = oIom.pointer;
-    __opfsHandles[pFile] = f;
-    f.opfsHandle = null /* TODO */;
-    if(flags & capi.SQLITE_OPEN_DELETEONCLOSE){
-      f.deleteOnClose = true;
-    }
-    f.filename = zName ? wasm.cstringToJs(zName) : 'sqlite3-xOpen-'+randomFilename();
-    error("OPFS sqlite3_vfs::xOpen is not yet full implemented.");
-    return capi.SQLITE_IOERR;
-  })
-  ('xFullPathname', function(pVfs,zName,nOut,pOut){
-    /* Until/unless we have some notion of "current dir"
-       in OPFS, simply copy zName to pOut... */
-    const i = wasm.cstrncpy(pOut, zName, nOut);
-    return i<nOut ? 0 : capi.SQLITE_CANTOPEN
-    /*CANTOPEN is required by the docs but SQLITE_RANGE would be a closer match*/;
-  })
-  ('xAccess', function(pVfs,zName,flags,pOut){
-    error("OPFS sqlite3_vfs::xAccess is not yet implemented.");
-    let fileExists = 0;
-    switch(flags){
-        case capi.SQLITE_ACCESS_EXISTS: break;
-        case capi.SQLITE_ACCESS_READWRITE: break;
-        case capi.SQLITE_ACCESS_READ/*docs say this is never used*/:
-        default:
-          error("Unexpected flags value for sqlite3_vfs::xAccess():",flags);
-          return capi.SQLITE_MISUSE;
-    }
-    wasm.setMemValue(pOut, fileExists, 'i32');
-    return 0;
-  })
-  ('xDelete', function(pVfs, zName, doSyncDir){
-    error("OPFS sqlite3_vfs::xDelete is not yet implemented.");
-    // https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
-    // ==> remove()
-    return capi.SQLITE_IOERR;
-  })
-  ('xGetLastError', function(pVfs,nOut,pOut){
-    debug("OPFS sqlite3_vfs::xGetLastError() has nothing sensible to return.");
-    return 0;
-  })
-  ('xCurrentTime', function(pVfs,pOut){
-    /* If it turns out that we need to adjust for timezone, see:
-       https://stackoverflow.com/a/11760121/1458521 */
-    wasm.setMemValue(pOut, 2440587.5 + (new Date().getTime()/86400000),
-                     'double');
-    return 0;
-  })
-  ('xCurrentTimeInt64',function(pVfs,pOut){
-    // TODO: confirm that this calculation is correct
-    wasm.setMemValue(pOut, (2440587.5 * 86400000) + new Date().getTime(),
-                     'i64');
-    return 0;
-  });
-  if(!oVfs.$xSleep){
-    inst('xSleep', function(pVfs,ms){
-      error("sqlite3_vfs::xSleep(",ms,") cannot be implemented from "+
-           "JS and we have no default VFS to copy the impl from.");
-      return 0;
-    });
-  }
-  if(!oVfs.$xRandomness){
-    inst('xRandomness', function(pVfs, nOut, pOut){
-      const heap = wasm.heap8u();
-      let i = 0;
-      for(; i < nOut; ++i) heap[pOut + i] = (Math.random()*255000) & 0xFF;
-      return i;
-    });
-  }
-
-  ////////////////////////////////////////////////////////////////////////
-  // Set up OPFS sqlite3_io_methods...
-  inst = installMethod(oIom);
-  inst('xClose', async function(pFile){
-    warn("xClose(",arguments,") uses await");
-    const f = __opfsHandles[pFile];
-    delete __opfsHandles[pFile];
-    if(f.opfsHandle){
-      await f.opfsHandle.close();
-      if(f.deleteOnClose){
-        // TODO
-      }
-    }
-    f.dispose();
-    return 0;
-  })
-  ('xRead', /*i(ppij)*/function(pFile,pDest,n,offset){
-    /* int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst) */
-    try {
-      const f = __opfsHandles[pFile];
-      const heap = wasm.heap8u();
-      const b = new Uint8Array(heap.buffer, pDest, n);
-      const nRead = f.opfsHandle.read(b, {at: offset});
-      if(nRead<n){
-        // MUST zero-fill short reads (per the docs)
-        heap.fill(0, dest + nRead, n - nRead);
-      }
-      return 0;
-    }catch(e){
-      error("xRead(",arguments,") failed:",e);
-      return capi.SQLITE_IOERR_READ;
-    }
-  })
-  ('xWrite', /*i(ppij)*/function(pFile,pSrc,n,offset){
-    /* int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst) */
-    try {
-      const f = __opfsHandles[pFile];
-      const b = new Uint8Array(wasm.heap8u().buffer, pSrc, n);
-      const nOut = f.opfsHandle.write(b, {at: offset});
-      if(nOut<n){
-        error("xWrite(",arguments,") short write!");
-        return capi.SQLITE_IOERR_WRITE;
-      }
-      return 0;
-    }catch(e){
-      error("xWrite(",arguments,") failed:",e);
-      return capi.SQLITE_IOERR_WRITE;
-    }
-  })
-  ('xTruncate', /*i(pj)*/async function(pFile,sz){
-    /* int (*xTruncate)(sqlite3_file*, sqlite3_int64 size) */
-    try{
-      warn("xTruncate(",arguments,") uses await");
-      const f = __opfsHandles[pFile];
-      await f.opfsHandle.truncate(sz);
-      return 0;
-    }
-    catch(e){
-      error("xTruncate(",arguments,") failed:",e);
-      return capi.SQLITE_IOERR_TRUNCATE;
-    }
-  })
-  ('xSync', /*i(pi)*/async function(pFile,flags){
-    /* int (*xSync)(sqlite3_file*, int flags) */
-    try {
-      warn("xSync(",arguments,") uses await");
-      const f = __opfsHandles[pFile];
-      await f.opfsHandle.flush();
-      return 0;
-    }catch(e){
-      error("xSync(",arguments,") failed:",e);
-      return capi.SQLITE_IOERR_SYNC;
-    }
-  })
-  ('xFileSize', /*i(pp)*/async function(pFile,pSz){
-    /* int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize) */
-    try {
-      warn("xFileSize(",arguments,") uses await");
-      const f = __opfsHandles[pFile];
-      const fsz = await f.opfsHandle.getSize();
-      capi.wasm.setMemValue(pSz, fsz,'i64');
-      return 0;
-    }catch(e){
-      error("xFileSize(",arguments,") failed:",e);
-      return capi.SQLITE_IOERR_SEEK;
-    }
-  })
-  ('xLock', /*i(pi)*/function(pFile,lockType){
-    /* int (*xLock)(sqlite3_file*, int) */
-    // Opening a handle locks it automatically.
-    warn("xLock(",arguments,") is a no-op");
-    return 0;
-  })
-  ('xUnlock', /*i(pi)*/function(pFile,lockType){
-    /* int (*xUnlock)(sqlite3_file*, int) */
-    // Opening a handle locks it automatically.
-    warn("xUnlock(",arguments,") is a no-op");
-    return 0;
-  })
-  ('xCheckReservedLock', /*i(pp)*/function(pFile,pOut){
-    /* int (*xCheckReservedLock)(sqlite3_file*, int *pResOut) */
-    // Exclusive lock is automatically acquired when opened
-    warn("xCheckReservedLock(",arguments,") is a no-op");
-    wasm.setMemValue(pOut,1,'i32');
-    return 0;
-  })
-  ('xFileControl', /*i(pip)*/function(pFile,op,pArg){
-    /* int (*xFileControl)(sqlite3_file*, int op, void *pArg) */
-    debug("xFileControl(",arguments,") is a no-op");
-    return capi.SQLITE_NOTFOUND;
-  })
-  ('xDeviceCharacteristics',/*i(p)*/function(pFile){
-    /* int (*xDeviceCharacteristics)(sqlite3_file*) */
-    debug("xDeviceCharacteristics(",pFile,")");
-    return capi.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
-  });
-  // xSectorSize may be NULL
-  //('xSectorSize', function(pFile){
-  //  /* int (*xSectorSize)(sqlite3_file*) */
-  //  log("xSectorSize(",pFile,")");
-  //  return 4096 /* ==> SQLITE_DEFAULT_SECTOR_SIZE */;
-  //})
-
-  const rc = capi.sqlite3_vfs_register(oVfs.pointer, 0);
-  if(rc){
-    oVfs.dispose();
-    toss("sqlite3_vfs_register(OPFS) failed with rc",rc);
-  }
-  capi.sqlite3_vfs_register.addReference(oVfs, oIom);
-  warn("End of (very incomplete) OPFS setup.", oVfs);
-  //oVfs.dispose()/*only because we can't yet do anything with it*/;
-
-}/*initOpfsBits()*/;
-
-(async function(){
-  importScripts('sqlite3.js');
-
-  const test1 = function(db){
-    db.exec("create table if not exists t(a);")
-      .transaction(function(db){
-        db.prepare("insert into t(a) values(?)")
-          .bind(new Date().getTime())
-          .stepFinalize();
-        stdout("Number of values in table t:",
-            db.selectValue("select count(*) from t"));
-      });
-  };
-
-  const runTests = async function(Module){
-    //stdout("Module",Module);
-    self._MODULE = Module /* this is only to facilitate testing from the console */;
-    const sqlite3 = Module.sqlite3,
-          capi = sqlite3.capi,
-          oo = sqlite3.oo1,
-          wasm = capi.wasm;
-    stdout("Loaded sqlite3:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
-
-    if(1){
-      let errCount = 0;
-      [
-        'FileSystemHandle', 'FileSystemFileHandle', 'FileSystemDirectoryHandle',
-        'FileSystemSyncAccessHandle'
-      ].forEach(function(n){
-        const f = self[n];
-        if(f){
-          warn(n,f);
-          warn(n+'.prototype',f.prototype);
-        }else{
-          stderr("MISSING",n);
-          ++errCount;
-        }
-      });
-      if(errCount) return;
-    }
-    warn('self',self);
-    await initOpfsBits(sqlite3);
-
-    if(1) return;
-    
-    let persistentDir;
-    if(1){
-      persistentDir = '';
-    }else{
-      persistentDir = capi.sqlite3_web_persistent_dir();
-      if(persistentDir){
-        stderr("Persistent storage dir:",persistentDir);
-      }else{
-        stderr("No persistent storage available.");
-        return;
-      }
-    }
-    const startTime = performance.now();
-    let db;
-    try {
-      db = new oo.DB(persistentDir+'/foo.db');
-      stdout("DB filename:",db.filename,db.fileName());
-      const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
-            banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
-      [
-        test1
-      ].forEach((f)=>{
-        const n = performance.now();
-        stdout(banner1,"Running",f.name+"()...");
-        f(db, sqlite3, Module);
-        stdout(banner2,f.name+"() took ",(performance.now() - n),"ms");
-      });
-    }finally{
-      if(db) db.close();
-    }
-    stdout("Total test time:",(performance.now() - startTime),"ms");
-  };
-
-  sqlite3InitModule(self.sqlite3TestModule).then(runTests);
-})();
index 9f7d3fb1d82d9408cbba53fd48b20d0241440bb7..bb0edbcfbc644e2a706864921cbb40e0a71f5ded 100644 (file)
@@ -52,7 +52,7 @@
     let db;
     try {
       db = new oo.DB(persistentDir+'/foo.db');
-      stdout("DB filename:",db.filename,db.fileName());
+      stdout("DB filename:",db.filename,db.getFilename());
       const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
             banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
       [
index 00b4025569626d4573b9b3da774374558ab137dd..5969962d7af1d6b6548905c275215f4962270a6d 100644 (file)
@@ -164,7 +164,7 @@ const vfsAsyncImpls = {
       storeAndNotify(opName, state.sq3Codes.SQLITE_NOFOUND);
     }
   },
-  xDelete: async function({filename, syncDir/*ignored*/}){
+  xDeleteNoWait: async function({filename, syncDir, recursive = false}){
     /* 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
@@ -178,12 +178,13 @@ const vfsAsyncImpls = {
        is false.
     */
     log("xDelete(",arguments[0],")");
+    let rc = 0;
     try {
       while(filename){
         const [hDir, filenamePart] = await getDirForPath(filename, false);
         //log("Removing:",hDir, filenamePart);
         if(!filenamePart) break;
-        await hDir.removeEntry(filenamePart);
+        await hDir.removeEntry(filenamePart, {recursive});
         if(0x1234 !== syncDir) break;
         filename = getResolvedPath(filename, true);
         filename.pop();
@@ -193,8 +194,23 @@ const vfsAsyncImpls = {
       /* Ignoring: _presumably_ the file can't be found or a dir is
          not empty. */
       //error("Delete failed",filename, e.message);
+      rc = state.sq3Codes.SQLITE_IOERR_DELETE;
+    }
+    return rc;
+  },
+  xDelete: async function(...args){
+    const rc = await vfsAsyncImpls.xDeleteNoWait(...args);
+    storeAndNotify('xDelete', rc);
+  },
+  mkdir: async function(dirname){
+    let rc = 0;
+    try {
+        await getDirForPath(dirname+"/filepart", true);
+    }catch(e){
+      //error("mkdir failed",filename, e.message);
+      rc = state.sq3Codes.SQLITE_IOERR;
     }
-    storeAndNotify('xDelete', 0);
+    storeAndNotify('mkdir', rc);
   },
   xFileSize: async function(fid){
     log("xFileSize(",arguments,")");
index 6e470ecc889c907256e0a16aab0929f0346d9518..41b278c2894f9e84db200480475e2f74eab9ac95 100644 (file)
       very much incomplete, under construction, and experimental.
       <strong>See the dev console for all output.</strong>
     </div>
-    <div id='test-output'>
+    <div>
+      <a href='?delete'>Use this link</a> to delete the persistent OPFS-side db (if any).
     </div>
+    <div id='test-output'></div>
     <script>new Worker("test-opfs-vfs.js"+self.location.search);</script>
   </body>
 </html>
index fd71c9cdcfb7ac7b320dcbec95ffe82051dc95ae..04497c1c143210b8b63108ff77df0864ce5c088e 100644 (file)
@@ -34,12 +34,16 @@ const tryOpfsVfs = function(sqlite3){
   const oVfs = capi.sqlite3_vfs.instanceForPointer(pVfs) || toss("Unexpected instanceForPointer() result.");;
   log("OPFS VFS:",pVfs, oVfs);
 
+  const urlArgs = new URL(self.location.href).searchParams;
   const dbFile = "my-persistent.db";
-  const db = new sqlite3.oo1.DB(dbFile, "c", "opfs");
+  if(urlArgs.has('delete')) sqlite3.opfs.deleteEntry(dbFile);
+
+  const opfs = sqlite3.opfs;
+  const db = new opfs.OpfsDb(dbFile);
   log("db file:",db.filename);
   try{
-    let n = db.selectValue("select count(*) from sqlite_schema");
-    if(n){
+    if(opfs.entryExists(dbFile)){
+      let n = db.selectValue("select count(*) from sqlite_schema");
       log("Persistent data found. sqlite_schema entry count =",n);
     }
     db.transaction((db)=>{
@@ -54,6 +58,17 @@ const tryOpfsVfs = function(sqlite3){
       });
     });
     log("count(*) from t =",db.selectValue("select count(*) from t"));
+
+    // 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");
   }finally{
     db.close();
   }
@@ -62,10 +77,9 @@ const tryOpfsVfs = function(sqlite3){
 }/*tryOpfsVfs()*/;
 
 importScripts('sqlite3.js');
-self.sqlite3InitModule().then((EmscriptenModule)=>{
-  EmscriptenModule.sqlite3.installOpfsVfs()
-    .then((sqlite3)=>tryOpfsVfs(sqlite3))
-    .catch((e)=>{
-      console.error("Error initializing OPFS VFS:",e);
-    });
-});
+self.sqlite3InitModule()
+  .then((EmscriptenModule)=>EmscriptenModule.sqlite3.installOpfsVfs())
+  .then((sqlite3)=>tryOpfsVfs(sqlite3))
+  .catch((e)=>{
+    console.error("Error initializing module:",e);
+  });
index e45b52bfa06d70894fe5f159c1caa803fb47d99b..57437190962d3782898b038ac386d8f4d07bcdd8 100644 (file)
 
     let dbName = "/testing1.sqlite3";
     let vfsName = undefined;
-    if(capi.sqlite3_web_db_is_kvvfs()){
+    if(capi.sqlite3_web_db_uses_vfs(0,"kvvfs")){
       dbName = "local";
       vfsName = 'kvvfs';
       logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage",
       clearKvvfs();
     }
     const db = new oo.DB(dbName,'c',vfsName), startTime = performance.now();
-    log("capi.sqlite3_web_db_is_kvvfs() ==",capi.sqlite3_web_db_is_kvvfs(db.pointer));
+    log("db is kvvfs?",capi.sqlite3_web_db_uses_vfs(db.pointer,"kvvfs"));
     try {
-      log("db.filename =",db.filename,"db.fileName() =",db.fileName());
+      log("db.filename =",db.filename,"db.fileName() =",db.getFilename());
       const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
             banner2 = '<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<';
       [
index ab3e8572f14e83a7865b6751abc7b580947ffced..adc88dd13bc35c1268e2f6cc5cd5127f80b72b26 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Move\sthe\sOPFS\sVFS\sbits\sback\sinto\sapi/sqlite3-api-opfs.js.\sRefactor\sthe\sOPFS\sVFS\sinit\sprocess\sto\suse\sa\sPromise-returning\sfunction\swhich\sthe\sclient\smust\scall,\sas\sthat\seliminates\sany\suncertainty\sabout\swhen\sthe\sVFS\s(necessarily\sactivated\sasynchronously)\sactually\sbecomes\savailable\sto\sthe\sclient.\sRename\sx-sync-async.*\sto\stest-opfs-vfs.*\sMilestone:\sfirst\ssuccessful\stest\sof\sOPFS\swithout\sWASMFS.
-D 2022-09-18T03:05:55.278
+C Numerous\scleanups\sin\sthe\sJS\sbits.\sRemoved\ssome\snow-defunct\swasm\stest\sfiles.\sExpose\ssqlite3.opfs\sobject\scontaining\svarious\sOPFS-specific\sutilities.
+D 2022-09-18T17:32:35.336
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -483,10 +483,10 @@ F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba814
 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
 F ext/wasm/api/sqlite3-api-cleanup.js 8564a6077cdcaea9a9f428a019af8a05887f0131e6a2a1e72a7ff1145fadfe77
 F ext/wasm/api/sqlite3-api-glue.js 366d580c8e5bf7fcf4c6dee6f646c31f5549bd417ea03a59a0acca00e8ecce30
-F ext/wasm/api/sqlite3-api-oo1.js d7526517f7ad3f6bda16ad66d373bbb71b43168deef7af60eda5c9fe873d1387
-F ext/wasm/api/sqlite3-api-opfs.js 87d98f2449d5790efd7044e492166e4ed767e3320a03ed5a173b2b9364fc4896
-F ext/wasm/api/sqlite3-api-prologue.js 48ebca4ae340b0242d4f39bbded01bd0588393c8023628be1c454b4db6f7bd6e
-F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4
+F ext/wasm/api/sqlite3-api-oo1.js 2d13dddf0d2b4168a9249f124134d37924331e5b55e05dba18b6d661fbeefe48
+F ext/wasm/api/sqlite3-api-opfs.js 4090abf4e16b460543ff665e96822048e37a2703e0ba46a01fed3a15c024c034
+F ext/wasm/api/sqlite3-api-prologue.js 4e3e26880d444000cca1b4f3ddfa9d49581dfecd1de9426080239ecc208c447d
+F ext/wasm/api/sqlite3-api-worker1.js e8456bd9b93eab297d065b25cb7a253835f606f9349383f2aa5c585e8d3b3aef
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
 F ext/wasm/api/sqlite3-wasm.c 4130e2df9587f4e4c3afc04c3549d682c8a5c0cfe5b22819a0a86edb7f01b9bd
 F ext/wasm/batch-runner-kvvfs.html ef3b2f553abad4f17a2a29ce6526023793a88e8597b7a24b8c7855a030b90a16
@@ -502,7 +502,7 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695
 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8
 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686
-F ext/wasm/index.html 492eb6c9023c9cda391c61702a28bd18e6c986ca2588ec3f384275bb77275285
+F ext/wasm/index.html d698cc021c25ca940f67805c2cc2848c303705d98b4c4f9f55565b9a9c37d2bb
 F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215
 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106
 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
@@ -510,11 +510,8 @@ F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e5
 F ext/wasm/kvvfs.make 4b2ba6d061f3a52da9f5812f86f4faa80fb4d9456a152f6b0585dccd667a4e22
 F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1
 F ext/wasm/kvvfs1.js ec1c1d071bb055711f9151df05616111432cf3e6bf7ac7f8dcbcfb56c9d9ed48
-F ext/wasm/scratchpad-opfs-worker.html 5fdda167571264300f388847d34f00b77dd48984a8dba2ee9c099c3ffa05db66
-F ext/wasm/scratchpad-opfs-worker.js cf6c4554d3b099c1a50013e50d19b3dc60e183511b4b4dbe7fabc2b9d3360567
-F ext/wasm/scratchpad-opfs-worker2.js 8c980370bbd5a262d96af8627c443936e11b87d0263a02123769d5953fc146da
 F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
-F ext/wasm/scratchpad-wasmfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e
+F ext/wasm/scratchpad-wasmfs-main.js f0836e3576df7a89390d777bb53e142e559e8a79becfb2a5a976490b05a1c4fa
 F ext/wasm/speedtest1-kvvfs.html c8b65c20e2b35298dc02d8e0a394d5e1eb857fd22e504468388234aee13aef08
 F ext/wasm/speedtest1-wasmfs.html 6a67a6812f03a2058eb5c6ad0c8dea4bf749d0160ed9d6b826dabe7b766c3cf7
 F ext/wasm/speedtest1-worker.html d8881ae802d15fb8adb94049265173e99f350e07e1d4e6f9e1cbd8969fe63a04
@@ -523,15 +520,15 @@ 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 456bef1253fd4732f133b601a4450b7f8461e67af6e8d30bf8a239ad775c77a2
+F ext/wasm/sqlite3-opfs-async-proxy.js 6e89e1af7c616afdd877cbcf5d0ec3d5f47ba252b9a19e696140b9495dc1e653
 F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
 F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
-F ext/wasm/test-opfs-vfs.html 3e11c875c28f041891deeea6b2375121845ee1269cac6747df957ec0c7d4d37a w ext/wasm/x-sync-async.html
-F ext/wasm/test-opfs-vfs.js bf70cd553a443b4eda63b577787ef73144f879fa062a20a73bb44e3242c81a15 w ext/wasm/x-sync-async.js
+F ext/wasm/test-opfs-vfs.html eb69dda21eb414b8f5e3f7c1cc0f774103cc9c0f87b2d28a33419e778abfbab5
+F ext/wasm/test-opfs-vfs.js 753c6b86dd8ce0813121add44726a038ba1b83acebdc8414189cb163faf23e6d
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
 F ext/wasm/testing-worker1-promiser.js 63448fddfd3b8c89ff667d17c8b31c6c2259dd4647ebbbd28f3a921c48e924da
 F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
-F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e
+F ext/wasm/testing1.js 507001a970fe8a8eb67b6c8d783e1c1daa3db2719f727c4551af29349410e538
 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
 F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d
@@ -2030,8 +2027,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 1c660970d0f62bcfd6e698a72b050d99972a1e39f45a5ac24194a190f8f78ab3
-R 039a2cd7ec4da87358ad6184da91bc68
+P b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5
+R 79bf69f52699f5d039971f9fe79e1ec3
 U stephan
-Z 58e9ce410c47c756bc153984b857c4cf
+Z 77273ebd9a43ebba5449759a950a89f3
 # Remove this line to create a well-formed Fossil manifest.
index d80c68d21c71138a7bfad5c8fa726e0efec289e6..6e29cdca81c5a92c6891db846cdb72b587fe1305 100644 (file)
@@ -1 +1 @@
-b2abf60dbfa6648f671a3932cb65feb28d05a0d5b7f792351d14f9c13d9798c5
\ No newline at end of file
+26e625d05d9820033b23536f18ad3ddc59ed712ad507d4b0c7fe88abd15d2be8
\ No newline at end of file