diff --git a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api index 506775d2fe..b31f613576 100644 --- a/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api +++ b/ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api @@ -89,5 +89,6 @@ _sqlite3_value_text _sqlite3_value_type _sqlite3_vfs_find _sqlite3_vfs_register +_sqlite3_vfs_unregister _malloc _free diff --git a/ext/wasm/api/sqlite3-api-glue.js b/ext/wasm/api/sqlite3-api-glue.js index c9a3db15d9..639e670792 100644 --- a/ext/wasm/api/sqlite3-api-glue.js +++ b/ext/wasm/api/sqlite3-api-glue.js @@ -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 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 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 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*/ }); diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index d101bc17f3..5b934818fd 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -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' diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 73d189c4f7..1fcc9a64ab 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -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... */ /** diff --git a/ext/wasm/tester1.js b/ext/wasm/tester1.js index e51d0aeb23..642ca1024e 100644 --- a/ext/wasm/tester1.js +++ b/ext/wasm/tester1.js @@ -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; } @@ -216,7 +216,7 @@ } 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 = []; @@ -230,7 +230,7 @@ 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', @@ -312,6 +312,8 @@ 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){ @@ -1092,7 +1094,7 @@ 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); @@ -1282,7 +1284,7 @@ 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'); }, @@ -1305,11 +1307,11 @@ 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); @@ -1352,9 +1354,9 @@ 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); @@ -1362,19 +1364,64 @@ } }); 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(){ diff --git a/manifest b/manifest index 730ee02808..e45eccf68a 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 61a8d1fbb7..9dfbdf93ac 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -89f3e1982ec32c010af67d15ef780847df20de568669e5c9d02f3cf084f51330 \ No newline at end of file +f07ce15479b7224b0d1ba9f147a433136e70c1461aa667d2737d4a918f778f55 \ No newline at end of file