]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Expose sqlite3_vfs_unregister() to WASM and unregister kvvfs in Worker threads to...
authorstephan <stephan@noemail.net>
Fri, 21 Oct 2022 05:27:40 +0000 (05:27 +0000)
committerstephan <stephan@noemail.net>
Fri, 21 Oct 2022 05:27:40 +0000 (05:27 +0000)
FossilOrigin-Name: f07ce15479b7224b0d1ba9f147a433136e70c1461aa667d2737d4a918f778f55

ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api
ext/wasm/api/sqlite3-api-glue.js
ext/wasm/api/sqlite3-api-oo1.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/tester1.js
manifest
manifest.uuid

index 506775d2fe462d831accdd6a28c208de6755b770..b31f613576975cef13d62543ecaf4fb6aa54db17 100644 (file)
@@ -89,5 +89,6 @@ _sqlite3_value_text
 _sqlite3_value_type
 _sqlite3_vfs_find
 _sqlite3_vfs_register
+_sqlite3_vfs_unregister
 _malloc
 _free
index c9a3db15d9a8759f26beb9a1aaf03cdd97fb43e4..639e670792466e8d112fa7ce176b81b15a35a529 100644 (file)
@@ -443,10 +443,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       try{
         const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
                                       uninstall);
-        rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep,
-                                   pApp, ...funcArgs);
+        rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
+                                         pApp, ...funcArgs);
       }catch(e){
-        console.error("sqlite3_create_function_v2() setup threw:",e);
+        console.error("sqlite3_create_window_function() setup threw:",e);
         for(let v of uninstall){
           wasm.uninstallFunction(v);
         }
@@ -607,6 +607,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       // Structs NOT to register
       WasmTestStruct: true
     });
+    if(!util.isUIThread()){
+      /* We remove the kvvfs VFS from Worker threads below. */
+      notThese.sqlite3_kvvfs_methods = true;
+    }
     for(const s of wasm.ctype.structs){
       if(!notThese[s.name]){
         capi[s.name] = sqlite3.StructBinder(s);
@@ -614,89 +618,96 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }
   }/*end C constant imports*/
 
-  if( util.isMainWindow()
-      && 0!==capi.sqlite3_vfs_find("kvvfs") ){/* kvvfs-specific glue */
-    const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
-      wasm.exports.sqlite3_wasm_kvvfs_methods()
-    );
-    delete capi.sqlite3_kvvfs_methods;
-
-    const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
-          pstack = wasm.pstack,
-          pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
-
-    const kvvfsStorage = (zClass)=>
-          ((115/*=='s'*/===wasm.getMemValue(zClass))
-           ? sessionStorage : localStorage);
-    
-    const kvvfsImpls = {
-      xRead: (zClass, zKey, zBuf, nBuf)=>{
-        const stack = pstack.pointer,
-              astack = wasm.scopedAllocPush();
-        try {
-          const zXKey = kvvfsMakeKey(zClass,zKey);
-          if(!zXKey) return -3/*OOM*/;
-          const jKey = wasm.cstringToJs(zXKey);
-          const jV = kvvfsStorage(zClass).getItem(jKey);
-          if(!jV) return -1;
-          const nV = jV.length /* Note that we are relying 100% on v being
-                                  ASCII so that jV.length is equal to the
-                                  C-string's byte length. */;
-          if(nBuf<=0) return nV;
-          else if(1===nBuf){
-            wasm.setMemValue(zBuf, 0);
-            return nV;
+  const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
+  if( pKvvfs ){/* kvvfs-specific glue */
+    if(util.isUIThread()){
+      const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
+        wasm.exports.sqlite3_wasm_kvvfs_methods()
+      );
+      delete capi.sqlite3_kvvfs_methods;
+
+      const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
+            pstack = wasm.pstack,
+            pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
+
+      const kvvfsStorage = (zClass)=>
+            ((115/*=='s'*/===wasm.getMemValue(zClass))
+             ? sessionStorage : localStorage);
+      
+      const kvvfsImpls = {
+        xRead: (zClass, zKey, zBuf, nBuf)=>{
+          const stack = pstack.pointer,
+                astack = wasm.scopedAllocPush();
+          try {
+            const zXKey = kvvfsMakeKey(zClass,zKey);
+            if(!zXKey) return -3/*OOM*/;
+            const jKey = wasm.cstringToJs(zXKey);
+            const jV = kvvfsStorage(zClass).getItem(jKey);
+            if(!jV) return -1;
+            const nV = jV.length /* Note that we are relying 100% on v being
+                                    ASCII so that jV.length is equal to the
+                                    C-string's byte length. */;
+            if(nBuf<=0) return nV;
+            else if(1===nBuf){
+              wasm.setMemValue(zBuf, 0);
+              return nV;
+            }
+            const zV = wasm.scopedAllocCString(jV);
+            if(nBuf > nV + 1) nBuf = nV + 1;
+            wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
+            wasm.setMemValue(zBuf + nBuf - 1, 0);
+            return nBuf - 1;
+          }catch(e){
+            console.error("kvstorageRead()",e);
+            return -2;
+          }finally{
+            pstack.restore(stack);
+            wasm.scopedAllocPop(astack);
+          }
+        },
+        xWrite: (zClass, zKey, zData)=>{
+          const stack = pstack.pointer;
+          try {
+            const zXKey = kvvfsMakeKey(zClass,zKey);
+            if(!zXKey) return 1/*OOM*/;
+            const jKey = wasm.cstringToJs(zXKey);
+            kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
+            return 0;
+          }catch(e){
+            console.error("kvstorageWrite()",e);
+            return capi.SQLITE_IOERR;
+          }finally{
+            pstack.restore(stack);
+          }
+        },
+        xDelete: (zClass, zKey)=>{
+          const stack = pstack.pointer;
+          try {
+            const zXKey = kvvfsMakeKey(zClass,zKey);
+            if(!zXKey) return 1/*OOM*/;
+            kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
+            return 0;
+          }catch(e){
+            console.error("kvstorageDelete()",e);
+            return capi.SQLITE_IOERR;
+          }finally{
+            pstack.restore(stack);
           }
-          const zV = wasm.scopedAllocCString(jV);
-          if(nBuf > nV + 1) nBuf = nV + 1;
-          wasm.heap8u().copyWithin(zBuf, zV, zV + nBuf - 1);
-          wasm.setMemValue(zBuf + nBuf - 1, 0);
-          return nBuf - 1;
-        }catch(e){
-          console.error("kvstorageRead()",e);
-          return -2;
-        }finally{
-          pstack.restore(stack);
-          wasm.scopedAllocPop(astack);
-        }
-      },
-      xWrite: (zClass, zKey, zData)=>{
-        const stack = pstack.pointer;
-        try {
-          const zXKey = kvvfsMakeKey(zClass,zKey);
-          if(!zXKey) return 1/*OOM*/;
-          const jKey = wasm.cstringToJs(zXKey);
-          kvvfsStorage(zClass).setItem(jKey, wasm.cstringToJs(zData));
-          return 0;
-        }catch(e){
-          console.error("kvstorageWrite()",e);
-          return capi.SQLITE_IOERR;
-        }finally{
-          pstack.restore(stack);
-        }
-      },
-      xDelete: (zClass, zKey)=>{
-        const stack = pstack.pointer;
-        try {
-          const zXKey = kvvfsMakeKey(zClass,zKey);
-          if(!zXKey) return 1/*OOM*/;
-          kvvfsStorage(zClass).removeItem(wasm.cstringToJs(zXKey));
-          return 0;
-        }catch(e){
-          console.error("kvstorageDelete()",e);
-          return capi.SQLITE_IOERR;
-        }finally{
-          pstack.restore(stack);
         }
+      }/*kvvfsImpls*/;
+      for(let k of Object.keys(kvvfsImpls)){
+        kvvfsMethods[kvvfsMethods.memberKey(k)] =
+          wasm.installFunction(
+            kvvfsMethods.memberSignature(k),
+            kvvfsImpls[k]
+          );
       }
-    }/*kvvfsImpls*/;
-    for(let k of Object.keys(kvvfsImpls)){
-      kvvfsMethods[kvvfsMethods.memberKey(k)] =
-        wasm.installFunction(
-          kvvfsMethods.memberSignature(k),
-          kvvfsImpls[k]
-        );
+    }else{
+      /* Worker thread: unregister kvvfs to avoid it being used
+         for anything other than local/sessionStorage. It "can"
+         be used that way but it's not really intended to be. */
+      capi.sqlite3_vfs_unregister(pKvvfs);
     }
-  }/*kvvfs*/
+  }/*pKvvfs*/
 
 });
index d101bc17f3fe222bac6472345db8c998802213c0..5b934818fdad81eb1031a487a13a46249fa1f192 100644 (file)
@@ -374,11 +374,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
         case 1:
           if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){
             out.sql = args[0];
+          }else if(Array.isArray(args[0])){
+            out.sql = args[0];
           }else if(args[0] && 'object'===typeof args[0]){
             out.opt = args[0];
             out.sql = out.opt.sql;
-          }else if(Array.isArray(args[0])){
-            out.sql = args[0];
           }
           break;
         case 2:
@@ -392,7 +392,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     }else if(Array.isArray(out.sql)){
       out.sql = out.sql.join('');
     }else if('string'!==typeof out.sql){
-      toss3("Missing SQL argument.");
+      toss3("Missing SQL argument or unsupported SQL value type.");
     }
     if(out.opt.callback || out.opt.resultRows){
       switch((undefined===out.opt.rowMode)
@@ -811,8 +811,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        case, the function's name must be the 'name' property.
 
        The first two call forms can only be used for creating scalar
-       functions. Creating an aggregate function requires the
-       options-object form (see below for details).
+       functions. Creating an aggregate or window function requires
+       the options-object form (see below for details).
 
        UDFs cannot currently be removed from a DB handle after they're
        added. More correctly, they can be removed as documented for
@@ -837,14 +837,19 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
          properties to the "step" and "final" callbacks for the
          aggregate. Do not set the `xFunc` property.
 
-       The options object may optionally have an `xDestroy`
-       function-type property, as per
-       sqlite3_create_function_v2(). Its argument will be the
-       WASM-pointer-type value of the `pApp` property, and this
-       function will throw if `pApp` is defined but is not null,
-       undefined, or a numeric (WASM pointer) value.
+       - Window: set the `xStep`, `xFinal`, `xValue`, and `xInverse`
+         function-type properties. Do not set the `xFunc` property.
 
-       The optional options object may contain flags to modify how
+       The options object may optionally have an `xDestroy`
+       function-type property, as per sqlite3_create_function_v2().
+       Its argument will be the WASM-pointer-type value of the `pApp`
+       property, and this function will throw if `pApp` is defined but
+       is not null, undefined, or a numeric (WASM pointer)
+       value. i.e. `pApp`, if set, must be value suitable for use as a
+       WASM pointer argument, noting that `null` or `undefined` will
+       translate to 0 for that purpose.
+
+       The options object may contain flags to modify how
        the function is defined:
 
        - `arity`: the number of arguments which SQL calls to this
@@ -855,36 +860,39 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
        arity value means that the function is variadic and may accept
        any number of arguments, up to sqlite3's compile-time
        limits. sqlite3 will enforce the argument count if is zero or
-       greater.
-
-       The callback always receives a pointer to an `sqlite3_context`
-       object as its first argument. Any arguments after that are from
-       SQL code. The leading context argument does _not_ count towards
-       the function's arity. See the docs for
+       greater. The callback always receives a pointer to an
+       `sqlite3_context` object as its first argument. Any arguments
+       after that are from SQL code. The leading context argument does
+       _not_ count towards the function's arity. See the docs for
        sqlite3.capi.sqlite3_create_function_v2() for why that argument
        is needed in the interface.
 
-       The following properties correspond to flags documented at:
+       The following options-object properties correspond to flags
+       documented at:
 
        https://sqlite.org/c3ref/create_function.html
 
-       - .deterministic = SQLITE_DETERMINISTIC
-       - .directOnly = SQLITE_DIRECTONLY
-       - .innocuous = SQLITE_INNOCUOUS
+       - `deterministic` = sqlite3.capi.SQLITE_DETERMINISTIC
+       - `directOnly` = sqlite3.capi.SQLITE_DIRECTONLY
+       - `innocuous` = sqlite3.capi.SQLITE_INNOCUOUS
+
+       Sidebar: the ability to add new WASM-accessible functions to
+       the runtime requires that the WASM build is compiled with the
+       equivalent functionality as that provided by Emscripten's
+       `-sALLOW_TABLE_GROWTH` flag.
     */
     createFunction: function f(name, xFunc, opt){
-      let xStep, xFinal;
       const isFunc = (f)=>(f instanceof Function);
       switch(arguments.length){
           case 1: /* (optionsObject) */
             opt = name;
             name = opt.name;
-            xFunc = opt.xFunc;
+            xFunc = opt.xFunc || 0;
             break;
           case 2: /* (name, callback|optionsObject) */
             if(!isFunc(xFunc)){
               opt = xFunc;
-              xFunc = opt.xFunc;
+              xFunc = opt.xFunc || 0;
             }
             break;
           case 3: /* name, xFunc, opt */
@@ -895,30 +903,46 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       if('string' !== typeof name){
         toss3("Invalid arguments: missing function name.");
       }
-      xStep = opt.xStep;
-      xFinal = opt.xFinal;
+      let xStep = opt.xStep || 0;
+      let xFinal = opt.xFinal || 0;
+      const xValue = opt.xValue || 0;
+      const xInverse = opt.xInverse || 0;
+      let isWindow = undefined;
       if(isFunc(xFunc)){
+        isWindow = false;
         if(isFunc(xStep) || isFunc(xFinal)){
           toss3("Ambiguous arguments: scalar or aggregate?");
         }
         xStep = xFinal = null;
       }else if(isFunc(xStep)){
         if(!isFunc(xFinal)){
-          toss3("Missing xFinal() callback for aggregate UDF.");
+          toss3("Missing xFinal() callback for aggregate or window UDF.");
         }
         xFunc = null;
       }else if(isFunc(xFinal)){
-        toss3("Missing xStep() callback for aggregate UDF.");
+        toss3("Missing xStep() callback for aggregate or window UDF.");
       }else{
         toss3("Missing function-type properties.");
       }
+      if(false === isWindow){
+        if(isFunc(xValue) || isFunc(xInverse)){
+          toss3("xValue and xInverse are not permitted for non-window UDFs.");
+        }
+      }else if(isFunc(xValue)){
+        if(!isFunc(xInverse)){
+          toss3("xInverse must be provided if xValue is.");
+        }
+        isWindow = true;
+      }else if(isFunc(xInverse)){
+        toss3("xValue must be provided if xInverse is.");
+      }
       const pApp = opt.pApp;
       if(undefined!==pApp &&
          null!==pApp &&
          (('number'!==typeof pApp) || !capi.util.isInt32(pApp))){
         toss3("Invalid value for pApp property. Must be a legal WASM pointer value.");
       }
-      const xDestroy = opt.xDestroy;
+      const xDestroy = opt.xDestroy || 0;
       if(xDestroy && !isFunc(xDestroy)){
         toss3("xDestroy property must be a function.");
       }
@@ -929,13 +953,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
       name = name.toLowerCase();
       const xArity = xFunc || xStep;
       const arity = getOwnOption(opt, 'arity');
-      DB.checkRc(this, capi.sqlite3_create_function_v2(
-        this.pointer, name,
-        ('number'===typeof arity
-         ? arity
-         : (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0)),
-        capi.SQLITE_UTF8 | fFlags, pApp,
-        xFunc, xStep, xFinal, xDestroy));
+      const arityArg = ('number'===typeof arity
+                        ? arity
+                        : (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0));
+      let rc;
+      if( isWindow ){
+        rc = capi.sqlite3_create_window_function(
+          this.pointer, name, arityArg,
+          capi.SQLITE_UTF8 | fFlags, pApp || 0,
+          xStep, xFinal, xValue, xInverse, xDestroy);
+      }else{
+        rc = capi.sqlite3_create_function_v2(
+          this.pointer, name, arityArg,
+          capi.SQLITE_UTF8 | fFlags, pApp || 0,
+          xFunc, xStep, xFinal, xDestroy);
+      }
+      DB.checkRc(this, rc);
       return this;
     }/*createFunction()*/,
     /**
@@ -1642,7 +1675,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     dbCtorHelper
   }/*oo1 object*/;
 
-  if(util.isMainWindow()){
+  if(util.isUIThread()){
     /**
        Functionally equivalent to DB(storageName,'c','kvvfs') except
        that it throws if the given storage name is not one of 'local'
index 73d189c4f7ff448c05711625db2c7336c3da8041..1fcc9a64abce59e7a2fae734ad4754650d615f8c 100644 (file)
@@ -289,7 +289,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      ```
   */
   WasmAllocError.toss = (...args)=>{
-    throw new WasmAllocError(args.join(' '));
+    throw new WasmAllocError(...args);
   };
 
   /** 
@@ -530,9 +530,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
       isBindableTypedArray,
       isInt32, isSQLableTypedArray, isTypedArray, 
       typedArrayToString,
-      isMainWindow: ()=>{
-        return 'undefined' === typeof WorkerGlobalScope
-      }
+      isUIThread: ()=>'undefined'===typeof WorkerGlobalScope
     },
     
     /**
@@ -839,7 +837,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     ["sqlite3_value_text", "string", "sqlite3_value*"],
     ["sqlite3_value_type", "int", "sqlite3_value*"],
     ["sqlite3_vfs_find", "*", "string"],
-    ["sqlite3_vfs_register", "int", "*", "int"]
+    ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
+    ["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
   ]/*wasm.bindingSignatures*/;
 
   if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
@@ -1026,10 +1025,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
   class SQLite3Error extends Error {
     /**
        Constructs this object with a message equal to all arguments
-       concatenated with a space between each one.
+       concatenated with a space between each one. As a special case,
+       if it's passed only a single integer argument, the string form
+       of that argument is the result of
+       sqlite3.capi.sqlite3_js_rc_str() or (if that returns falsy), a
+       synthesized string which contains that integer.
     */
     constructor(...args){
-      super(args.join(' '));
+      if(1===args.length && 'number'===typeof args[0] && args[0]===(args[0] | 0)){
+        super(capi.sqlite3_js_rc_str(args[0]) || ("Unknown result code #"+args[0]));
+      }else{
+        super(args.join(' '));
+      }
       this.name = 'SQLite3Error';
     }
   };
@@ -1042,7 +1049,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
      ```
   */
   SQLite3Error.toss = (...args)=>{
-    throw new SQLite3Error(args.join(' '));
+    throw new SQLite3Error(...args);
   };
 
   /** State for sqlite3_wasmfs_opfs_dir(). */
@@ -1219,7 +1226,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
           : 0);
   };
   
-  if( capi.util.isMainWindow() ){
+  if( capi.util.isUIThread() ){
     /* Features specific to the main window thread... */
 
     /**
index e51d0aeb23830db58e4d463c8ceb6e9c3115cca8..642ca1024e76325a1eb22630bf68688a21799e77 100644 (file)
@@ -50,7 +50,7 @@
     const mapToString = (v)=>{
       switch(typeof v){
           case 'number': case 'string': case 'boolean':
-          case 'undefined':
+          case 'undefined': case 'bigint':
             return ''+v;
           default: break;
       }
           }
           log(TestUtil.separator);
           logClass('group-start',"Group #"+this.number+':',this.name);
-          const indent = '....';
+          const indent = '    ';
           const assertCount = TestUtil.counter;
           const groupState = Object.create(null);
           const skipped = [];
               const tc = TestUtil.counter
               log(indent, n+":", t.name);
               await t.test.call(groupState, sqlite3);
-              //log(indent, indent, 'assertion count:',TestUtil.counter - tc);
+              log(indent, indent, TestUtil.counter - tc, 'assertion(s)');
             }
           }
           logClass('green',
         T.assert(e instanceof Error)
           .assert(e instanceof sqlite3.WasmAllocError);
       }
+      try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
+      catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
     })
   ////////////////////////////////////////////////////////////////////
     .t('strglob/strlike', function(sqlite3){
         const vfsList = capi.sqlite3_js_vfs_list();
         T.assert(vfsList.length>1);
         T.assert('string'===typeof vfsList[0]);
-
+        //log("vfsList =",vfsList);
         for(const v of vfsList){
           T.assert('string' === typeof v)
             .assert(capi.sqlite3_vfs_find(v) > 0);
         const sjac = capi.sqlite3_js_aggregate_context;
         db.createFunction({
           name: 'summer',
-          xStep: function(pCtx, n){
+          xStep: (pCtx, n)=>{
             const ac = sjac(pCtx, 4);
             wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32');
           },
         db.createFunction({
           name: 'summerN',
           arity: -1,
-          xStep: function(pCtx, ...args){
-            const pAgg = sjac(pCtx, 4);
-            let sum = wasm.getMemValue(pAgg, 'i32');
+          xStep: (pCtx, ...args)=>{
+            const ac = sjac(pCtx, 4);
+            let sum = wasm.getMemValue(ac, 'i32');
             for(const v of args) sum += Number(v);
-            wasm.setMemValue(pAgg, sum, 'i32');
+            wasm.setMemValue(ac, sum, 'i32');
           },
           xFinal: (pCtx)=>{
             const ac = sjac(pCtx, 0);
         const sjac = capi.sqlite3_js_aggregate_context;
         db.createFunction({
           name: 'summer64',
-          xStep: function(pCtx, n){
-            const pAgg = sjac(pCtx, 8);
-            wasm.setMemValue(pAgg, wasm.getMemValue(pAgg,'i64') + BigInt(n), 'i64');
+          xStep: (pCtx, n)=>{
+            const ac = sjac(pCtx, 8);
+            wasm.setMemValue(ac, wasm.getMemValue(ac,'i64') + BigInt(n), 'i64');
           },
           xFinal: (pCtx)=>{
             const ac = sjac(pCtx, 0);
           }
         });
         let v = db.selectValue([
-            "with cte(v) as (",
-          "select 3 union all select 5 union all select 7",
-          ") select summer64(v*10), summer64(v+1) from cte"
+          "with cte(v) as (",
+          "select 9007199254740991 union all select 1 union all select 2",
+          ") select summer64(v), summer64(v+1) from cte"
         ]);
-        T.assert(150n===BigInt(v));
+        T.assert(9007199254740994n===v);
      }
     }/*aggregate UDFs*/)
 
   ////////////////////////////////////////////////////////////////////
     .t({
-      name: 'Window UDFs (tests are TODO)',
-      predicate: testIsTodo
-    })
+      name: 'Window UDFs',
+      test: function(){
+        /* Example window function, table, and results taken from:
+           https://sqlite.org/windowfunctions.html#udfwinfunc */
+        const db = this.db;
+        const sjac = (cx,n=4)=>capi.sqlite3_js_aggregate_context(cx,n);
+        const xValueFinal = (pCtx)=>{
+          const ac = sjac(pCtx, 0);
+          return ac ? wasm.getMemValue(ac,'i32') : 0;
+        };
+        const xStepInverse = (pCtx, n)=>{
+          const ac = sjac(pCtx);
+          wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32');
+        };
+        db.createFunction({
+          name: 'winsumint',
+          xStep: (pCtx, n)=>xStepInverse(pCtx, n),
+          xInverse: (pCtx, n)=>xStepInverse(pCtx, -n),
+          xFinal: xValueFinal,
+          xValue: xValueFinal
+        });
+        db.exec([
+          "CREATE TABLE twin(x, y); INSERT INTO twin VALUES",
+          "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
+        ]);
+        let count = 0;
+        db.exec({
+          sql:[
+            "SELECT x, winsumint(y) OVER (",
+            "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING",
+            ") AS sum_y ",
+            "FROM twin ORDER BY x;",
+            "DROP TABLE twin;"
+          ],
+          callback: function(row){
+            switch(++count){
+                case 1: T.assert('a'===row[0] && 9===row[1]); break;
+                case 2: T.assert('b'===row[0] && 12===row[1]); break;
+                case 3: T.assert('c'===row[0] && 16===row[1]); break;
+                case 4: T.assert('d'===row[0] && 12===row[1]); break;
+                case 5: T.assert('e'===row[0] && 9===row[1]); break;
+                default: toss("Too many rows to window function.");
+            }
+          }
+        });
+        T.assert(5 === count);
+      }
+    }/*window UDFs*/)
 
   ////////////////////////////////////////////////////////////////////
     .t("ATTACH", function(){
index 730ee02808fee3757197f3e1b1a0ee0af34ef6e1..e45eccf68a20be68ea8979bb95d0917a5f132bcc 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\ssemantics\sfor\sUDF\sxFinal()\sresult\shandling\sand\serror\sreporting\shandling\smore\sflexible.
-D 2022-10-20T23:48:38.866
+C Expose\ssqlite3_vfs_unregister()\sto\sWASM\sand\sunregister\skvvfs\sin\sWorker\sthreads\sto\savoid\sits\sunintended\suse\sthere\s(in\scontexts\sother\sthan\slocal/sessionStorage).\sCorrect\sregistration\sof\swindow\sfunctions,\sextend\soo1.DB.createFunction()\sto\ssupport\swindow\sfunctions,\sand\sadd\swindow\sfunction\stests\sto\stester1.js.\sCorrect\san\sincorrect\s1-arg\shandling\scase\sfor\sDB.exec().\sAdd\sper-test\sassertion\scounts\sto\stester1.js.
+D 2022-10-21T05:27:40.995
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -475,7 +475,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce
 F ext/wasm/GNUmakefile 645bd5670a56acf2c8f1e969c427555e3e8e74dfd6eac8c948858f530617c7d5
 F ext/wasm/README-dist.txt e78e44a58772d5b5d7a5a179b5bf639cd67b62249aac66138e2c319bd02dd243
 F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066
-F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 89983a8d122c35a90c65ec667844b95a78bcd04f3198a99c1e0c8368c1a0b03a
+F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 36f413ab4dbb057d2dec938fb366ac0a4c5e85ba14660a8d672f0277602c0fc5
 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
 F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78
 F ext/wasm/api/extern-post-js.js efbed835f290b3741259acc5faf68714a60d38e6834e6cfe172d5354c87566d2
@@ -484,10 +484,10 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
 F ext/wasm/api/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
 F ext/wasm/api/pre-js.js 151e0616614a49f3db19ed544fa13b38c87c108959fbcd4029ea8399a562d94f
 F ext/wasm/api/sqlite3-api-cleanup.js 4d07a7524dc9b7b050acfde57163e839243ad2383bd7ee0de0178b1b3e988588
-F ext/wasm/api/sqlite3-api-glue.js ea760df5991cee48e29aa6ced503027760462d5696d50521f7273a91e0e1430c
-F ext/wasm/api/sqlite3-api-oo1.js 0e278d131dad72e9eb348a3dda6a4ff734a9e08925b4ed7e6e5a688d2edaf525
+F ext/wasm/api/sqlite3-api-glue.js 6e4e472eb5afc732a695cd7c5ded6dee6ef8b480e61aa0d648a3fc9033c84745
+F ext/wasm/api/sqlite3-api-oo1.js 5016f6dd4b6b461bb6047fe6a2d3d7cbe85aa6b110c263ebf0347672a0cd897e
 F ext/wasm/api/sqlite3-api-opfs.js 22d60ba956e873b65e2e0591e239178082bd53a6d563c3c58db7dc03e562e8f7
-F ext/wasm/api/sqlite3-api-prologue.js e4fbfa66eb6c5c544c52dea6cb48051eddcdc21c276c39a80801f5e76e6c5b56
+F ext/wasm/api/sqlite3-api-prologue.js fa00d55f927e5a4ec51cf2c80f6f0eaed2f4f5774341ecf3d63a0ea4c738f8f5
 F ext/wasm/api/sqlite3-api-worker1.js a7f38f03275d6c27ab2aef3e83215d3c97ce09c43e6904df47c3764d9d4572b4
 F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
 F ext/wasm/api/sqlite3-opfs-async-proxy.js 206ce6bbc3c30ad51a37d9c25e3a2712e70b586e0f9a2cf8cb0b9619017c2671
@@ -533,7 +533,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
 F ext/wasm/tester1-worker.html 048c341f124fdb61ca14dfd1bd1f78742490f208aa3bb1e84399f83f1e7e6a74
 F ext/wasm/tester1.html 37ccc958fa0d95074af2d72b7241c8e2d982bbec6dda4dc790241af3d933c3b6
-F ext/wasm/tester1.js 5533cd100dc663dbe548fb04aefc490aa4b3d2e0ec2212f79881bb4893266e39
+F ext/wasm/tester1.js 3fc539001b861d6360ea8c4825351157251204781b7a45389527574ad338c7e1
 F ext/wasm/version-info.c 5fa356d38859d71a0369b5c37e1935def7413fcc8a4e349a39d9052c1d0479f4
 F ext/wasm/wasmfs.make ee0004813e16c283ff633e08b482008d56adf9b7d42f6c5612f7ab002b924f69
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@@ -2036,8 +2036,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 9d034ef5e1bab7c9651c2450dc85765fa6365d3f1414c711550de858ff8b3ece
-R dfb3978c0632c8b69ab9fb0976c9a2ee
+P 89f3e1982ec32c010af67d15ef780847df20de568669e5c9d02f3cf084f51330
+R 8a68d8975e926edd4ad048c9589fdc73
 U stephan
-Z c86f3e45c75ca0a38f433f4183ca8f53
+Z 78e197950cf91aa30272843b46106df8
 # Remove this line to create a well-formed Fossil manifest.
index 61a8d1fbb73197c18c83cd54800867b331bc28a1..9dfbdf93aca49fe5bab82157cb4cc9bae3d44895 100644 (file)
@@ -1 +1 @@
-89f3e1982ec32c010af67d15ef780847df20de568669e5c9d02f3cf084f51330
\ No newline at end of file
+f07ce15479b7224b0d1ba9f147a433136e70c1461aa667d2737d4a918f778f55
\ No newline at end of file