]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add/apply various kvvfs-specific utility APIs to the JS layer to assist in testing...
authorstephan <stephan@noemail.net>
Tue, 13 Sep 2022 19:27:03 +0000 (19:27 +0000)
committerstephan <stephan@noemail.net>
Tue, 13 Sep 2022 19:27:03 +0000 (19:27 +0000)
FossilOrigin-Name: 0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2

13 files changed:
ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api
ext/wasm/api/sqlite3-api-cleanup.js
ext/wasm/api/sqlite3-api-glue.js
ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/api/sqlite3-wasm.c
ext/wasm/batch-runner.html
ext/wasm/batch-runner.js
ext/wasm/kvvfs1.html
ext/wasm/kvvfs1.js
ext/wasm/testing1.js
manifest
manifest.uuid

index aead79e50d8380b33a09ba9e0f26490e3ea25435..f03478b17cb47c95242278bfb1554328628c928b 100644 (file)
@@ -25,6 +25,7 @@ _sqlite3_compileoption_used
 _sqlite3_create_function_v2
 _sqlite3_data_count
 _sqlite3_db_filename
+_sqlite3_db_handle
 _sqlite3_db_name
 _sqlite3_errmsg
 _sqlite3_error_offset
@@ -33,6 +34,7 @@ _sqlite3_exec
 _sqlite3_expanded_sql
 _sqlite3_extended_errcode
 _sqlite3_extended_result_codes
+_sqlite3_file_control
 _sqlite3_finalize
 _sqlite3_initialize
 _sqlite3_interrupt
index 1b57cdc5de829afcb26d841d7dd75d64181d67cc..0e99edf508836fe9c4a2b1027360668f560b0b5c 100644 (file)
@@ -20,11 +20,17 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
   /**
      Install a suitable default configuration for sqlite3ApiBootstrap().
   */
-  const SABC = self.sqlite3ApiBootstrap.defaultConfig;
-  SABC.Module = Module /* ==>  Currently needs to be exposed here for test code. NOT part
-                          of the public API. */;
-  SABC.exports = Module['asm'];
-  SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
+  const SABC = self.sqlite3ApiConfig || Object.create(null);
+  if(undefined===SABC.Module){
+    SABC.Module = Module /* ==> Currently needs to be exposed here for
+                            test code. NOT part of the public API. */;
+  }
+  if(undefined===SABC.exports){
+    SABC.exports = Module['asm'];
+  }
+  if(undefined===SABC.memory){
+    SABC.memory = Module.wasmMemory /* gets set if built with -sIMPORT_MEMORY */;
+  }
 
   /**
      For current (2022-08-22) purposes, automatically call
@@ -35,8 +41,15 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
      configuration used by a no-args call to sqlite3ApiBootstrap().
   */
   //console.warn("self.sqlite3ApiConfig = ",self.sqlite3ApiConfig);
-  const sqlite3 = self.sqlite3ApiBootstrap();
-  delete self.sqlite3ApiBootstrap;
+  const rmApiConfig = (SABC !== self.sqlite3ApiConfig);
+  self.sqlite3ApiConfig = SABC;
+  let sqlite3;
+  try{
+    sqlite3 = self.sqlite3ApiBootstrap();
+  }finally{
+    delete self.sqlite3ApiBootstrap;
+    if(rmApiConfig) delete self.sqlite3ApiConfig;
+  }
 
   if(self.location && +self.location.port > 1024){
     console.warn("Installing sqlite3 bits as global S for local dev/test purposes.");
index 3a9e8803cbc80ed0bd9f186481f28ee830c8e455..67f9403548f72c5dbbe4c4a515af2320e9be6037 100644 (file)
@@ -55,6 +55,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     */
     const aPtr = wasm.xWrap.argAdapter('*');
     wasm.xWrap.argAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
+    wasm.xWrap.resultAdapter('sqlite3*', aPtr)('sqlite3_stmt*', aPtr);
 
     /**
        Populate api object with sqlite3_...() by binding the "raw" wasm
@@ -174,7 +175,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     wasm.ctype = JSON.parse(wasm.cstringToJs(cJson));
     //console.debug('wasm.ctype length =',wasm.cstrlen(cJson));
     for(const t of ['access', 'blobFinalizers', 'dataTypes',
-                    'encodings', 'flock', 'ioCap',
+                    'encodings', 'fcntl', 'flock', 'ioCap',
                     'openFlags', 'prepareFlags', 'resultCodes',
                     'syncFlags', 'udfFlags', 'version'
                    ]){
index 3dfe5bfb0528491c4101faec3814ea0105768c4a..8280204d62150a73dcb14703dc8a05d029d1f2de 100644 (file)
@@ -496,6 +496,13 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        the statement actually produces any result rows.
        ==================================================================
 
+       - `.columnNames`: if this is an array, the column names of the
+       result set are stored in this array before the callback (if
+       any) is triggered (regardless of whether the query produces any
+       result rows). If no statement has result columns, this value is
+       unchanged. Achtung: an SQL result may have multiple columns
+       with identical names.
+
        - `.callback` = a function which gets called for each row of
        the result set, but only if that statement has any result
        _rows_. The callback's "this" is the options object. The second
@@ -523,8 +530,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        as the first argument to the callback:
 
          A.1) `'array'` (the default) causes the results of
-         `stmt.get([])` to be passed to passed on and/or appended to
-         `resultRows`.
+         `stmt.get([])` to be passed to the `callback` and/or appended
+         to `resultRows`.
 
          A.2) `'object'` causes the results of
          `stmt.get(Object.create(null))` to be passed to the
@@ -536,7 +543,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
          A.3) `'stmt'` causes the current Stmt to be passed to the
          callback, but this mode will trigger an exception if
          `resultRows` is an array because appending the statement to
-         the array would be unhelpful.
+         the array would be downright unhelpful.
 
        B) An integer, indicating a zero-based column in the result
        row. Only that one single value will be passed on.
@@ -545,10 +552,14 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        ':', '$', or '@' will fetch the row as an object, extract that
        one field, and pass that field's value to the callback. Note
        that these keys are case-sensitive so must match the case used
-       in the SQL. e.g. `"select a A from t"` with a `rowMode` of '$A'
-       would work but '$a' would not. A reference to a column not in
-       the result set will trigger an exception on the first row (as
-       the check is not performed until rows are fetched).
+       in the SQL. e.g. `"select a A from t"` with a `rowMode` of
+       `'$A'` would work but `'$a'` would not. A reference to a column
+       not in the result set will trigger an exception on the first
+       row (as the check is not performed until rows are fetched).
+       Note also that `$` is a legal identifier character in JS so
+       need not be quoted. (Design note: those 3 characters were
+       chosen because they are the characters support for naming bound
+       parameters.)
 
        Any other `rowMode` value triggers an exception.
 
@@ -560,12 +571,17 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        and can be used over a WebWorker-style message interface.
        exec() throws if `resultRows` is set and `rowMode` is 'stmt'.
 
-       - `.columnNames`: if this is an array, the column names of the
-       result set are stored in this array before the callback (if
-       any) is triggered (regardless of whether the query produces any
-       result rows). If no statement has result columns, this value is
-       unchanged. Achtung: an SQL result may have multiple columns
-       with identical names.
+
+       Potential TODOs:
+
+       - `.bind`: permit an array of arrays/objects to bind. The first
+       sub-array would act on the first statement which has bindable
+       parameters (as it does now). The 2nd would act on the next such
+       statement, etc.
+
+       - `.callback` and `.resultRows`: permit an array entries with
+       semantics similar to those described for `.bind` above.
+
     */
     exec: function(/*(sql [,obj]) || (obj)*/){
       affirmDbOpen(this);
@@ -1580,31 +1596,5 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     Stmt
   }/*oo1 object*/;
 
-  if( self.window===self && 0!==capi.sqlite3_vfs_find('kvvfs') ){
-    /* Features specific to kvvfs... */
-    /**
-       Clears all storage used by the kvvfs DB backend, deleting any
-       DB(s) stored there. Its argument must be either 'session',
-       'local', or ''. In the first two cases, only sessionStorage
-       resp. localStorage is cleared. If it's an empty string (the
-       default) then both are cleared. Only storage keys which match
-       the pattern used by kvvfs are cleared: any other client-side
-       data are retained.
-    */
-    DB.clearKvvfsStorage = function(which=''){
-      const prefix = 'kvvfs-'+which;
-      const stores = [];
-      if('session'===which || ''===which) stores.push(sessionStorage);
-      if('local'===which || ''===which) stores.push(localStorage);
-      stores.forEach(function(s){
-        const toRm = [];
-        let i = 0, k;
-        for( i = 0; (k = s.key(i)); ++i ){
-          if(k.startsWith(prefix)) toRm.push(k);
-        }
-        toRm.forEach((kk)=>s.removeItem(kk));
-      });
-    };
-  }/* main-window-only bits */
 });
 
index 17dcd4228986e0fdea549e0bdda1125a797e72f2..1c22e9ea21ac177874e2dd681d60c1c37b059478 100644 (file)
 
   - Insofar as possible, support client-side storage using JS
     filesystem APIs. As of this writing, such things are still very
-    much TODO. Initial testing with using IndexedDB as backing storage
-    showed it to work reasonably well, but it's also too easy to
-    corrupt by using a web page in two browser tabs because IndexedDB
-    lacks the locking features needed to support that.
+    much under development.
 
   Specific non-goals of this project:
 
     Encodings in that realm, there are no currently plans to support
     the UTF16-related sqlite3 APIs. They would add a complication to
     the bindings for no appreciable benefit. Though web-related
-    implementation details take priority, the lower-level WASM module
-    "should" work in non-web WASM environments.
+    implementation details take priority, and the JavaScript
+    components of the API specifically focus on browser clients, the
+    lower-level WASM module "should" work in non-web WASM
+    environments.
 
   - Supporting old or niche-market platforms. WASM is built for a
     modern web and requires modern platforms.
 */
 
 /**
-   sqlite3ApiBootstrap() is the only global symbol exposed by this
-   API. It is intended to be called one time at the end of the API
-   amalgamation process, passed configuration details for the current
-   environment, and then optionally be removed from the global object
-   using `delete self.sqlite3ApiBootstrap`.
+   sqlite3ApiBootstrap() is the only global symbol persistently
+   exposed by this API. It is intended to be called one time at the
+   end of the API amalgamation process, passed configuration details
+   for the current environment, and then optionally be removed from
+   the global object using `delete self.sqlite3ApiBootstrap`.
 
    This function expects a configuration object, intended to abstract
    away details specific to any given WASM environment, primarily so
    that it can be used without any _direct_ dependency on
-   Emscripten. The config object is only honored the first time this
-   is called. Subsequent calls ignore the argument and return the same
+   Emscripten. (Note the default values for the config object!) The
+   config object is only honored the first time this is
+   called. Subsequent calls ignore the argument and return the same
    (configured) object which gets initialized by the first call.
 
    The config object properties include:
 */
 'use strict';
 self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
-  apiConfig = (sqlite3ApiBootstrap.defaultConfig || self.sqlite3ApiConfig)
+  apiConfig = (self.sqlite3ApiConfig || sqlite3ApiBootstrap.defaultConfig)
 ){
   if(sqlite3ApiBootstrap.sqlite3){ /* already initalized */
     console.warn("sqlite3ApiBootstrap() called multiple times.",
@@ -567,18 +567,22 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     ) ? !!capi.sqlite3_compileoption_used(optName) : false;
   }/*compileOptionUsed()*/;
 
+  /**
+     Signatures for the WASM-exported C-side functions. Each entry
+     is an array with 2+ elements:
+
+     [ "c-side name",
+       "result type" (capi.wasm.xWrap() syntax),
+       [arg types in xWrap() syntax]
+       // ^^^ this needn't strictly be an array: it can be subsequent
+       // elements instead: [x,y,z] is equivalent to x,y,z
+     ]
+
+     Note that support for the API-specific data types in the
+     result/argument type strings gets plugged in at a later phase in
+     the API initialization process.
+  */
   capi.wasm.bindingSignatures = [
-    /**
-       Signatures for the WASM-exported C-side functions. Each entry
-       is an array with 2+ elements:
-
-       ["c-side name",
-        "result type" (capi.wasm.xWrap() syntax),
-         [arg types in xWrap() syntax]
-         // ^^^ this needn't strictly be an array: it can be subsequent
-         // elements instead: [x,y,z] is equivalent to x,y,z
-       ]
-    */
     // Please keep these sorted by function name!
     ["sqlite3_bind_blob","int", "sqlite3_stmt*", "int", "*", "int", "*"],
     ["sqlite3_bind_double","int", "sqlite3_stmt*", "int", "f64"],
@@ -604,6 +608,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      "sqlite3*", "string", "int", "int", "*", "*", "*", "*", "*"],
     ["sqlite3_data_count", "int", "sqlite3_stmt*"],
     ["sqlite3_db_filename", "string", "sqlite3*", "string"],
+    ["sqlite3_db_handle", "sqlite3*", "sqlite3_stmt*"],
     ["sqlite3_db_name", "string", "sqlite3*", "int"],
     ["sqlite3_errmsg", "string", "sqlite3*"],
     ["sqlite3_error_offset", "int", "sqlite3*"],
@@ -614,6 +619,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     ["sqlite3_expanded_sql", "string", "sqlite3_stmt*"],
     ["sqlite3_extended_errcode", "int", "sqlite3*"],
     ["sqlite3_extended_result_codes", "int", "sqlite3*", "int"],
+    ["sqlite3_file_control", "int", "sqlite3*", "string", "int", "*"],
     ["sqlite3_finalize", "int", "sqlite3_stmt*"],
     ["sqlite3_initialize", undefined],
     ["sqlite3_interrupt", undefined, "sqlite3*"
@@ -740,20 +746,132 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
 
   /**
      Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a
-     non-empty string and the given name has that string as its
-     prefix, else returns false.
+     non-empty string and the given name starts with (that string +
+     '/'), else returns false.
+
+     Potential (but arguable) TODO: return true if the name is one of
+     (":localStorage:", "local", ":sessionStorage:", "session") and
+     kvvfs is available.
   */
   capi.sqlite3_web_filename_is_persistent = function(name){
     const p = capi.sqlite3_web_persistent_dir();
-    return (p && name) ? name.startsWith(p) : false;
+    return (p && name) ? name.startsWith(p+'/') : false;
   };
-  
+
   if(0===capi.wasm.exports.sqlite3_vfs_find(0)){
     /* Assume that sqlite3_initialize() has not yet been called.
        This will be the case in an SQLITE_OS_KV build. */
     capi.wasm.exports.sqlite3_initialize();
   }
 
+  if( self.window===self ){
+    /* Features specific to the main window thread... */
+
+    /**
+       Internal helper for sqlite3_web_kvvfs_clear() and friends.
+       Its argument should be one of ('local','session','').
+    */
+    const __kvvfsInfo = function(which){
+      const rc = Object.create(null);
+      rc.prefix = 'kvvfs-'+which;
+      rc.stores = [];
+      if('session'===which || ''===which) rc.stores.push(self.sessionStorage);
+      if('local'===which || ''===which) rc.stores.push(self.localStorage);
+      return rc;
+    };
+
+    /**
+       Clears all storage used by the kvvfs DB backend, deleting any
+       DB(s) stored there. Its argument must be either 'session',
+       'local', or ''. In the first two cases, only sessionStorage
+       resp. localStorage is cleared. If it's an empty string (the
+       default) then both are cleared. Only storage keys which match
+       the pattern used by kvvfs are cleared: any other client-side
+       data are retained.
+
+       This function is only available in the main window thread.
+
+       Returns the number of entries cleared.
+    */
+    capi.sqlite3_web_kvvfs_clear = function(which=''){
+      let rc = 0;
+      const kvinfo = __kvvfsInfo(which);
+      kvinfo.stores.forEach((s)=>{
+        const toRm = [] /* keys to remove */;
+        let i;
+        for( i = 0; i < s.length; ++i ){
+          const k = s.key(i);
+          if(k.startsWith(kvinfo.prefix)) toRm.push(k);
+        }
+        toRm.forEach((kk)=>s.removeItem(kk));
+        rc += toRm.length;
+      });
+      return rc;
+    };
+
+    /**
+       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
+       ('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
+       which match the pattern used by kvvfs are counted. The returned
+       value is the "length" value of every matching key and value,
+       noting that the kvvf uses only ASCII keys and values.
+
+       Note that the returned size is not authoritative from the
+       perspective of how much data can fit into localStorage and
+       sessionStorage, as the precise algorithms for determining
+       those limits are unspecified and may include per-entry
+       overhead invisible to clients.
+    */
+    capi.sqlite3_web_kvvfs_size = function(which=''){
+      let sz = 0;
+      const kvinfo = __kvvfsInfo(which);
+      kvinfo.stores.forEach((s)=>{
+        let i;
+        for(i = 0; i < s.length; ++i){
+          const k = s.key(i);
+          if(k.startsWith(kvinfo.prefix)){
+            sz += k.length;
+            sz += s.getItem(k).length;
+          }
+        }
+      });
+      return sz;
+    };
+
+    /**
+       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. */
   const sqlite3 = {
     WasmAllocError: WasmAllocError,
@@ -790,6 +908,11 @@ self.sqlite3ApiBootstrap.initializers = [];
    global-scope symbol.
 */
 self.sqlite3ApiBootstrap.defaultConfig = Object.create(null);
-/** Placeholder: gets installed by the first call to
-    self.sqlite3ApiBootstrap(). */
+/**
+   Placeholder: gets installed by the first call to
+   self.sqlite3ApiBootstrap(). However, it is recommended that the
+   caller of sqlite3ApiBootstrap() capture its return value and delete
+   self.sqlite3ApiBootstrap after calling it. It returns the same
+   value which will be stored here.
+*/
 self.sqlite3ApiBootstrap.sqlite3 = undefined;
index 2a505f19abfbe79fd87dbcc152b5a1cf79b3ba40..c072e8c9d4b84ff6269bf0f05ecc0e4ad21daaa4 100644 (file)
@@ -76,7 +76,7 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
 */
 WASM_KEEP
 const char * sqlite3_wasm_enum_json(void){
-  static char strBuf[1024 * 8] = {0} /* where the JSON goes */;
+  static char strBuf[1024 * 12] = {0} /* where the JSON goes */;
   int n = 0, childCount = 0, structCount = 0
     /* output counters for figuring out where commas go */;
   char * pos = &strBuf[1] /* skip first byte for now to help protect
@@ -259,7 +259,8 @@ const char * sqlite3_wasm_enum_json(void){
   } _DefGroup;
 
   DefGroup(openFlags) {
-    /* Noting that not all of these will have any effect in WASM-space. */
+    /* Noting that not all of these will have any effect in
+    ** WASM-space. */
     DefInt(SQLITE_OPEN_READONLY);
     DefInt(SQLITE_OPEN_READWRITE);
     DefInt(SQLITE_OPEN_CREATE);
@@ -322,6 +323,49 @@ const char * sqlite3_wasm_enum_json(void){
     DefInt(SQLITE_IOCAP_BATCH_ATOMIC);
   } _DefGroup;
 
+  DefGroup(fcntl) {
+    DefInt(SQLITE_FCNTL_LOCKSTATE);
+    DefInt(SQLITE_FCNTL_GET_LOCKPROXYFILE);
+    DefInt(SQLITE_FCNTL_SET_LOCKPROXYFILE);
+    DefInt(SQLITE_FCNTL_LAST_ERRNO);
+    DefInt(SQLITE_FCNTL_SIZE_HINT);
+    DefInt(SQLITE_FCNTL_CHUNK_SIZE);
+    DefInt(SQLITE_FCNTL_FILE_POINTER);
+    DefInt(SQLITE_FCNTL_SYNC_OMITTED);
+    DefInt(SQLITE_FCNTL_WIN32_AV_RETRY);
+    DefInt(SQLITE_FCNTL_PERSIST_WAL);
+    DefInt(SQLITE_FCNTL_OVERWRITE);
+    DefInt(SQLITE_FCNTL_VFSNAME);
+    DefInt(SQLITE_FCNTL_POWERSAFE_OVERWRITE);
+    DefInt(SQLITE_FCNTL_PRAGMA);
+    DefInt(SQLITE_FCNTL_BUSYHANDLER);
+    DefInt(SQLITE_FCNTL_TEMPFILENAME);
+    DefInt(SQLITE_FCNTL_MMAP_SIZE);
+    DefInt(SQLITE_FCNTL_TRACE);
+    DefInt(SQLITE_FCNTL_HAS_MOVED);
+    DefInt(SQLITE_FCNTL_SYNC);
+    DefInt(SQLITE_FCNTL_COMMIT_PHASETWO);
+    DefInt(SQLITE_FCNTL_WIN32_SET_HANDLE);
+    DefInt(SQLITE_FCNTL_WAL_BLOCK);
+    DefInt(SQLITE_FCNTL_ZIPVFS);
+    DefInt(SQLITE_FCNTL_RBU);
+    DefInt(SQLITE_FCNTL_VFS_POINTER);
+    DefInt(SQLITE_FCNTL_JOURNAL_POINTER);
+    DefInt(SQLITE_FCNTL_WIN32_GET_HANDLE);
+    DefInt(SQLITE_FCNTL_PDB);
+    DefInt(SQLITE_FCNTL_BEGIN_ATOMIC_WRITE);
+    DefInt(SQLITE_FCNTL_COMMIT_ATOMIC_WRITE);
+    DefInt(SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE);
+    DefInt(SQLITE_FCNTL_LOCK_TIMEOUT);
+    DefInt(SQLITE_FCNTL_DATA_VERSION);
+    DefInt(SQLITE_FCNTL_SIZE_LIMIT);
+    DefInt(SQLITE_FCNTL_CKPT_DONE);
+    DefInt(SQLITE_FCNTL_RESERVE_BYTES);
+    DefInt(SQLITE_FCNTL_CKPT_START);
+    DefInt(SQLITE_FCNTL_EXTERNAL_READER);
+    DefInt(SQLITE_FCNTL_CKSM_FILE);
+  } _DefGroup;
+
   DefGroup(access){
     DefInt(SQLITE_ACCESS_EXISTS);
     DefInt(SQLITE_ACCESS_READWRITE);
index 38f38070c03b9c9095afc0103378bfb721274782..2a6c1405cfba563a08c6120a259b18ddace65c4d 100644 (file)
@@ -55,7 +55,9 @@
     <hr>
     <div id='reverse-log-notice' class='hidden'>(Log output is in reverse order, newest first!)</div>
     <div id='test-output'></div>
-    
+    <!-- batch-runner.js "should" work with sqlite3-kvvfs so long as
+         its data sets don't violate the the storage limits. -->
+    <!--script src="sqlite3-kvvfs.js"></script-->
     <script src="sqlite3.js"></script>
     <script src="common/SqliteTestUtil.js"></script>
     <script src="batch-runner.js"></script>
index 437424b48f89bd4a97680e475635b7968c21dbfe..9964d747c78fca3dc29154e335c7ca58805e2dd2 100644 (file)
@@ -17,6 +17,7 @@
 (function(){
   const toss = function(...args){throw new Error(args.join(' '))};
   const warn = console.warn.bind(console);
+  let sqlite3;
 
   const App = {
     e: {
@@ -30,6 +31,7 @@
       btnReset: document.querySelector('#db-reset'),
       cbReverseLog: document.querySelector('#cb-reverse-log-order')
     },
+    db: Object.create(null),
     cache:{},
     metrics:{
       /**
     },
 
     openDb: function(fn, unlinkFirst=true){
-      if(this.db && this.db.ptr){
+      if(this.db.ptr){
         toss("Already have an opened db.");
       }
       const capi = this.sqlite3.capi, wasm = capi.wasm;
       const stack = wasm.scopedAllocPush();
       let pDb = 0;
       try{
-        if(unlinkFirst && fn && ':memory:'!==fn){
-          capi.wasm.sqlite3_wasm_vfs_unlink(fn);
+        if(unlinkFirst && fn){
+          if(':'!==fn[0]) capi.wasm.sqlite3_wasm_vfs_unlink(fn);
+          this.clearStorage();
         }
         const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE;
         const ppDb = wasm.scopedAllocPtr();
@@ -82,7 +85,6 @@
       }finally{
         wasm.scopedAllocPop(stack);
       }
-      this.db = Object.create(null);
       this.db.filename = fn;
       this.db.ptr = pDb;
       this.logHtml("Opened db:",fn);
     },
 
     closeDb: function(unlink=false){
-      if(this.db && this.db.ptr){
+      if(this.db.ptr){
         this.sqlite3.capi.sqlite3_close_v2(this.db.ptr);
         this.logHtml("Closed db",this.db.filename);
-        if(unlink) capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename);
+        if(unlink){
+          capi.wasm.sqlite3_wasm_vfs_unlink(this.db.filename);
+          this.clearStorage();
+        }
         this.db.ptr = this.db.filename = undefined;
       }
     },
       return p.catch((e)=>this.logErr("Error via evalFile("+fn+"):",e.message));
     }/*evalFile()*/,
 
+    clearStorage: function(){
+      const sz = sqlite3.capi.sqlite3_web_kvvfs_size();
+      const n = sqlite3.capi.sqlite3_web_kvvfs_clear(this.db.filename || '');
+      this.logHtml("Cleared kvvfs local/sessionStorage:",
+                   n,"entries totaling approximately",sz,"bytes.");
+    },
+
+    resetDb: function(){
+      if(this.db.ptr){
+        const fn = this.db.filename;
+        this.closeDb(true);
+        this.openDb(fn,false);
+      }
+    },
+
     run: function(sqlite3){
       delete this.run;
       this.sqlite3 = sqlite3;
       this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
       this.logHtml("WASM heap size =",wasm.heap8().length);
       this.loadSqlList();
-      const pDir = capi.sqlite3_web_persistent_dir();
-      const dbFile = pDir ? pDir+"/speedtest.db" : ":memory:";
-      if(!pDir){
+      const pDir = 1 ? '' : capi.sqlite3_web_persistent_dir();
+      const dbFile = pDir ? pDir+"/speedtest.db" : (
+        sqlite3.capi.sqlite3_vfs_find('kvvfs') ? 'local' : ':memory:'
+      );
+      this.clearStorage();
+      if(pDir){
+        logHtml("Using persistent storage:",dbFile);
+      }else{
         document.querySelector('#warn-opfs').remove();
       }
       this.openDb(dbFile, !!pDir);
         who.evalFile(who.e.selSql.value);
       }, false);
       this.e.btnReset.addEventListener('click', function(){
-        const fn = who.db.filename;
-        if(fn){
-          who.closeDb(true);
-          who.openDb(fn,true);
-        }
+        who.resetDb();
       }, false);
       this.e.btnExportMetrics.addEventListener('click', function(){
         who.logHtml2('warning',"Triggering download of metrics CSV. Check your downloads folder.");
         }
         const timeTotal = performance.now() - timeStart;
         who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))");
+        who.clearStorage();
       }, false);
     }/*run()*/
   }/*App*/;
 
   self.sqlite3TestModule.initSqlite3().then(function(theEmccModule){
     self._MODULE = theEmccModule /* this is only to facilitate testing from the console */;
+    sqlite3 = theEmccModule.sqlite3;
     App.run(theEmccModule.sqlite3);
   });
 })();
index 1a5eb62cf141db1055f264fd04373b529b4ecf4a..5d9f0779589ee07c4d879d3efee2b5e2a3a84bfd 100644 (file)
@@ -31,6 +31,7 @@
         <button id='btn-clear-storage'>Clear storage</button>
         <button id='btn-init-db'>(Re)init db</button>
         <button id='btn-select1'>Select db rows</button>
+        <button id='btn-storage-size'>Approx. storage size</button>
       </div>
     </fieldset>
     <div id='test-output'></div>
index c29426fc6301d05f7524b506194cf93a7a5fc2fd..fd57051a2631fd7e7f368adb6abae2a1137457ae 100644 (file)
           wasm = capi.wasm;
     log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
     T.assert( 0 !== capi.sqlite3_vfs_find(null) );
-    if(!oo.DB.clearKvvfsStorage){
+    if(!capi.sqlite3_vfs_find('kvvfs')){
       warn("This build is not kvvfs-capable.");
       return;
     }
     
-    const dbStorage = 1 ? ':sessionStorage:' : ':localStorage:';
-    const theStore = 's'===dbStorage[1] ? sessionStorage : localStorage;
+    const dbStorage = 1 ? 'session' : 'local';
+    const theStore = 's'===dbStorage[0] ? sessionStorage : localStorage;
     /**
        The names ':sessionStorage:' and ':localStorage:' are handled
        via the DB class constructor, not the C level. In the C API,
        the names "local" and "session" are the current (2022-09-12)
        names for those keys, but that is subject to change.
     */
-    const db = new oo.DB( dbStorage );
+    const db = new oo.DB( dbStorage, 'c', 'kvvfs' );
 
     document.querySelector('#btn-clear-storage').addEventListener('click',function(){
-      oo.DB.clearKvvfsStorage();
-      log("kvvfs localStorage and sessionStorage cleared.");
+      const sz = capi.sqlite3_web_kvvfs_clear();
+      log("kvvfs localStorage and sessionStorage cleared:",sz,"entries.");
     });
     document.querySelector('#btn-clear-log').addEventListener('click',function(){
       eOutput.innerText = '';
     });
     document.querySelector('#btn-init-db').addEventListener('click',function(){
-      const saveSql = [];
       try{
+        const saveSql = [];
         db.exec({
-          sql:["drop table if exists t;",
-               "create table if not exists t(a);",
-               "insert into t(a) values(?),(?),(?)"],
+          sql: ["drop table if exists t;",
+                "create table if not exists t(a);",
+                "insert into t(a) values(?),(?),(?)"],
           bind: [performance.now() >> 0,
                  (performance.now() * 2) >> 0,
                  (performance.now() / 2) >> 0],
         error(e.message);
       }
     });
+    document.querySelector('#btn-storage-size').addEventListener('click',function(){
+      log("sqlite3_web_kvvfs_size(",dbStorage,") says", capi.sqlite3_web_kvvfs_size(dbStorage),
+         "bytes");
+    });
     log("Storage backend:",db.filename /* note that the name was internally translated */);
     if(0===db.selectValue('select count(*) from sqlite_master')){
       log("DB is empty. Use the init button to populate it.");
index 2aadc30c3a7bb6a2c7e33a63ec43117e3be2eaff..e45b52bfa06d70894fe5f159c1caa803fb47d99b 100644 (file)
@@ -33,6 +33,8 @@
     return v1>=(v2-factor) && v1<=(v2+factor);
   };
 
+  let sqlite3;
+  
   const testBasicSanity = function(db,sqlite3){
     const capi = sqlite3.capi;
     log("Basic sanity tests...");
     }
   }/*testWasmUtil()*/;
 
+  const clearKvvfs = function(){
+    const sz = sqlite3.capi.sqlite3_web_kvvfs_size();
+    const n = sqlite3.capi.sqlite3_web_kvvfs_clear('');
+    log("Cleared kvvfs local/sessionStorage:",
+        n,"entries totaling approximately",sz,"bytes.");
+  };
+
   const runTests = function(Module){
     //log("Module",Module);
-    const sqlite3 = Module.sqlite3,
-          capi = sqlite3.capi,
+    sqlite3 = Module.sqlite3;
+    const capi = sqlite3.capi,
           oo = sqlite3.oo1,
           wasm = capi.wasm;
     log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid());
 
     let dbName = "/testing1.sqlite3";
     let vfsName = undefined;
-    if(oo.DB.clearKvvfsStorage){
+    if(capi.sqlite3_web_db_is_kvvfs()){
       dbName = "local";
       vfsName = 'kvvfs';
       logHtml("Found kvvfs. Clearing db(s) from sessionStorage and localStorage",
               "and selecting kvvfs-friendly db name:",dbName);
-      oo.DB.clearKvvfsStorage();
+      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));
     try {
       log("db.filename =",db.filename,"db.fileName() =",db.fileName());
       const banner1 = '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>',
       });
     }finally{
       db.close();
+      if('kvvfs'===vfsName) clearKvvfs();
     }
     logHtml("Total Test count:",T.counter,"in",(performance.now() - startTime),"ms");
     log('capi.wasm.exports',capi.wasm.exports);
index b104be806b01e3a6f5e006e330db6504737a69a2..6fa5a9fc8a965e743e1436a6e35c2132e4374355 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Get\stesting1.js\sworking\swith\sa\skvvfs\sbuild.
-D 2022-09-12T22:27:00.037
+C Add/apply\svarious\skvvfs-specific\sutility\sAPIs\sto\sthe\sJS\slayer\sto\sassist\sin\stesting\sand\sanalysis.\sCorrect\sa\sbackwards\sdefault\sarg\scheck\sfor\ssqlite3ApiBootstrap().\sAdd\sexports\sfor\ssqlite3_db_handle(),\ssqlite3_file_control(),\sand\sthe\sSQLITE_FCNTL_xxx\senum.
+D 2022-09-13T19:27:03.599
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -476,21 +476,21 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f057
 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02
 F ext/wasm/GNUmakefile b175a00599c976fe9e7bc02bbc1337b5e3b81d042320c3a0be1621d2c7a21b21
 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52
-F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1dfd067b3cbd9a49cb204097367cf2f8fe71b5a3b245d9d82a24779fd4ac2394
+F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 150a793a47205b8009ac934f3b6d6ebf67b965c072339aaa25ce808a19e116cc
 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b
 F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c
 F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b
-F ext/wasm/api/sqlite3-api-cleanup.js 101919ec261644e2f6f0a59952fd9612127b69ea99b493277b2789ea478f9b6b
-F ext/wasm/api/sqlite3-api-glue.js 2bf536a38cde324cf352bc2c575f8e22c6d204d667c0eda5a254ba45318914bc
-F ext/wasm/api/sqlite3-api-oo1.js b498662748918c132aa59433ea2bd2ebb7e026549fd68506a1ae1ea94736f4f6
+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 4925f4736eb28fd3a6d4dbbd2c078c38be651aa5996041e7f0b81be36506608e
 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0
-F ext/wasm/api/sqlite3-api-prologue.js 9e37ce4dfd74926d0df80dd7e72e33085db4bcee48e2c21236039be416a7dff2
+F ext/wasm/api/sqlite3-api-prologue.js c496adc0cf2ae427ee900aad01c09db97850bd8b6737f2849cab207c8415b839
 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4
 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
-F ext/wasm/api/sqlite3-wasm.c bf4637cf28463cada4b25f09651943c7ece004b253ef39b7ab68eaa60662aa09
-F ext/wasm/batch-runner.html 23209ade7981acce7ecd79d6eff9f4c5a4e8b14ae867ac27cd89b230be640fa6
-F ext/wasm/batch-runner.js 2abd146d3e3a66128ac0a2cc39bfd01e9811c9511fa10ec927d6649795f1ee50
+F ext/wasm/api/sqlite3-wasm.c 7d1760f3a864a9ae16cfb71543829d946a54384bd07af99a3adf9ef32913705f
+F ext/wasm/batch-runner.html 2857a6db7292ac83d1581af865d643fd34235db2df830d10b43b01388c599e04
+F ext/wasm/batch-runner.js fb6a338aeef509f181f499700a8595bc578bbc54ef832e0cda7e7e7c10b90a18
 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b
 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
 F ext/wasm/common/testing.css 3a5143699c2b73a85b962271e1a9b3241b30d90e30d895e4f55665e648572962
@@ -507,8 +507,8 @@ F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356a
 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f
 F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19
 F ext/wasm/kvvfs.make bfa0aaac384d9f200d2c8e31efb3536b40d139667b88e6eba9c0a71e23da6a5c
-F ext/wasm/kvvfs1.html 83bac238d1e93ed102a461672fc58fe74e611e426708e9a5c66002c01eadb942
-F ext/wasm/kvvfs1.js 53721a42e0ec45f6978cc723e5de47f882517867d0fcff4c6ff05f4c422e27c4
+F ext/wasm/kvvfs1.html 13bb24190bfb276a57b228499519badcc1bf39ed07e4b37bc2a425ce6418fed1
+F ext/wasm/kvvfs1.js a23ee98a78b9da379eab7a20d7ddf5283a684dd7a91685a84686c05216f96689
 F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c
 F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e
 F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5
@@ -526,7 +526,7 @@ F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416
 F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
 F ext/wasm/testing-worker1-promiser.js c62b5879339eef0b21aebd9d75bc125c86530edc17470afff18077f931cb704a
 F ext/wasm/testing1.html 50575755e43232dbe4c2f97c9086b3118eb91ec2ee1fae931e6d7669fb17fcae
-F ext/wasm/testing1.js 735120231d6e55b92964924fcb54a66484d8b2e30fe5a18c71081b3590344a5f
+F ext/wasm/testing1.js 7cd8ab255c238b030d928755ae8e91e7d90a12f2ae601b1b8f7827aaa4fb258e
 F ext/wasm/testing2.html a66951c38137ff1d687df79466351f3c734fa9c6d9cce71d3cf97c291b2167e3
 F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@@ -2024,8 +2024,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 8b1608a9a37131121cb3a0cb1e8dbad5e39dc1fc2a341d056f09537f179b2e7a
-R 77095a41dfb9b784dcab0078822e6eff
+P 333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79
+R fff963b52d5c3a76677d96f35303ab67
 U stephan
-Z c624cb9efadffe8972e3df15b8dc9939
+Z e8c548c1774e1a39daa4183bad155966
 # Remove this line to create a well-formed Fossil manifest.
index 69b3e88fd2de86222e962b4e3c13ef729495fcc6..e142fcae1ff032f80631694e55eb480844b81134 100644 (file)
@@ -1 +1 @@
-333a45725d1708e0fefa559c33ce1c7eeb425cdb04f594b1f2b48166c1478c79
\ No newline at end of file
+0d78961870ee9f22f1ba16d423377d28dcc36e04b1e31ffd57f3e2fd51f8f0f2
\ No newline at end of file