1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Expose sqlite3_vfs_unregister() to WASM and unregister kvvfs in Worker threads to avoid its unintended use there (in contexts other than local/sessionStorage). Correct registration of window functions, extend oo1.DB.createFunction() to support window functions, and add window function tests to tester1.js. Correct an incorrect 1-arg handling case for DB.exec(). Add per-test assertion counts to tester1.js.

FossilOrigin-Name: f07ce15479b7224b0d1ba9f147a433136e70c1461aa667d2737d4a918f778f55
This commit is contained in:
stephan
2022-10-21 05:27:40 +00:00
parent 6f3286cafd
commit a6ca996e4a
7 changed files with 257 additions and 158 deletions

View File

@ -89,5 +89,6 @@ _sqlite3_value_text
_sqlite3_value_type _sqlite3_value_type
_sqlite3_vfs_find _sqlite3_vfs_find
_sqlite3_vfs_register _sqlite3_vfs_register
_sqlite3_vfs_unregister
_malloc _malloc
_free _free

View File

@ -443,10 +443,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
try{ try{
const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy}, const funcArgs = __xWrapFuncs({xStep, xFinal, xValue, xInverse, xDestroy},
uninstall); uninstall);
rc = sqlite3CreateFunction(pDb, funcName, nArg, eTextRep, rc = sqlite3CreateWindowFunction(pDb, funcName, nArg, eTextRep,
pApp, ...funcArgs); pApp, ...funcArgs);
}catch(e){ }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){ for(let v of uninstall){
wasm.uninstallFunction(v); wasm.uninstallFunction(v);
} }
@ -607,6 +607,10 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
// Structs NOT to register // Structs NOT to register
WasmTestStruct: true 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){ for(const s of wasm.ctype.structs){
if(!notThese[s.name]){ if(!notThese[s.name]){
capi[s.name] = sqlite3.StructBinder(s); capi[s.name] = sqlite3.StructBinder(s);
@ -614,89 +618,96 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
} }
}/*end C constant imports*/ }/*end C constant imports*/
if( util.isMainWindow() const pKvvfs = capi.sqlite3_vfs_find("kvvfs");
&& 0!==capi.sqlite3_vfs_find("kvvfs") ){/* kvvfs-specific glue */ if( pKvvfs ){/* kvvfs-specific glue */
const kvvfsMethods = new capi.sqlite3_kvvfs_methods( if(util.isUIThread()){
wasm.exports.sqlite3_wasm_kvvfs_methods() const kvvfsMethods = new capi.sqlite3_kvvfs_methods(
); wasm.exports.sqlite3_wasm_kvvfs_methods()
delete capi.sqlite3_kvvfs_methods; );
delete capi.sqlite3_kvvfs_methods;
const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack, const kvvfsMakeKey = wasm.exports.sqlite3_wasm_kvvfsMakeKeyOnPstack,
pstack = wasm.pstack, pstack = wasm.pstack,
pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc; pAllocRaw = wasm.exports.sqlite3_wasm_pstack_alloc;
const kvvfsStorage = (zClass)=> const kvvfsStorage = (zClass)=>
((115/*=='s'*/===wasm.getMemValue(zClass)) ((115/*=='s'*/===wasm.getMemValue(zClass))
? sessionStorage : localStorage); ? sessionStorage : localStorage);
const kvvfsImpls = { const kvvfsImpls = {
xRead: (zClass, zKey, zBuf, nBuf)=>{ xRead: (zClass, zKey, zBuf, nBuf)=>{
const stack = pstack.pointer, const stack = pstack.pointer,
astack = wasm.scopedAllocPush(); astack = wasm.scopedAllocPush();
try { try {
const zXKey = kvvfsMakeKey(zClass,zKey); const zXKey = kvvfsMakeKey(zClass,zKey);
if(!zXKey) return -3/*OOM*/; if(!zXKey) return -3/*OOM*/;
const jKey = wasm.cstringToJs(zXKey); const jKey = wasm.cstringToJs(zXKey);
const jV = kvvfsStorage(zClass).getItem(jKey); const jV = kvvfsStorage(zClass).getItem(jKey);
if(!jV) return -1; if(!jV) return -1;
const nV = jV.length /* Note that we are relying 100% on v being const nV = jV.length /* Note that we are relying 100% on v being
ASCII so that jV.length is equal to the ASCII so that jV.length is equal to the
C-string's byte length. */; C-string's byte length. */;
if(nBuf<=0) return nV; if(nBuf<=0) return nV;
else if(1===nBuf){ else if(1===nBuf){
wasm.setMemValue(zBuf, 0); wasm.setMemValue(zBuf, 0);
return nV; 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*/; }else{
for(let k of Object.keys(kvvfsImpls)){ /* Worker thread: unregister kvvfs to avoid it being used
kvvfsMethods[kvvfsMethods.memberKey(k)] = for anything other than local/sessionStorage. It "can"
wasm.installFunction( be used that way but it's not really intended to be. */
kvvfsMethods.memberSignature(k), capi.sqlite3_vfs_unregister(pKvvfs);
kvvfsImpls[k]
);
} }
}/*kvvfs*/ }/*pKvvfs*/
}); });

View File

@ -374,11 +374,11 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
case 1: case 1:
if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){ if('string'===typeof args[0] || util.isSQLableTypedArray(args[0])){
out.sql = args[0]; out.sql = args[0];
}else if(Array.isArray(args[0])){
out.sql = args[0];
}else if(args[0] && 'object'===typeof args[0]){ }else if(args[0] && 'object'===typeof args[0]){
out.opt = args[0]; out.opt = args[0];
out.sql = out.opt.sql; out.sql = out.opt.sql;
}else if(Array.isArray(args[0])){
out.sql = args[0];
} }
break; break;
case 2: case 2:
@ -392,7 +392,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
}else if(Array.isArray(out.sql)){ }else if(Array.isArray(out.sql)){
out.sql = out.sql.join(''); out.sql = out.sql.join('');
}else if('string'!==typeof out.sql){ }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){ if(out.opt.callback || out.opt.resultRows){
switch((undefined===out.opt.rowMode) 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. case, the function's name must be the 'name' property.
The first two call forms can only be used for creating scalar The first two call forms can only be used for creating scalar
functions. Creating an aggregate function requires the functions. Creating an aggregate or window function requires
options-object form (see below for details). the options-object form (see below for details).
UDFs cannot currently be removed from a DB handle after they're UDFs cannot currently be removed from a DB handle after they're
added. More correctly, they can be removed as documented for 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 properties to the "step" and "final" callbacks for the
aggregate. Do not set the `xFunc` property. aggregate. Do not set the `xFunc` property.
The options object may optionally have an `xDestroy` - Window: set the `xStep`, `xFinal`, `xValue`, and `xInverse`
function-type property, as per function-type properties. Do not set the `xFunc` property.
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.
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: the function is defined:
- `arity`: the number of arguments which SQL calls to this - `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 arity value means that the function is variadic and may accept
any number of arguments, up to sqlite3's compile-time any number of arguments, up to sqlite3's compile-time
limits. sqlite3 will enforce the argument count if is zero or limits. sqlite3 will enforce the argument count if is zero or
greater. greater. The callback always receives a pointer to an
`sqlite3_context` object as its first argument. Any arguments
The callback always receives a pointer to an `sqlite3_context` after that are from SQL code. The leading context argument does
object as its first argument. Any arguments after that are from _not_ count towards the function's arity. See the docs for
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 sqlite3.capi.sqlite3_create_function_v2() for why that argument
is needed in the interface. 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 https://sqlite.org/c3ref/create_function.html
- .deterministic = SQLITE_DETERMINISTIC - `deterministic` = sqlite3.capi.SQLITE_DETERMINISTIC
- .directOnly = SQLITE_DIRECTONLY - `directOnly` = sqlite3.capi.SQLITE_DIRECTONLY
- .innocuous = SQLITE_INNOCUOUS - `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){ createFunction: function f(name, xFunc, opt){
let xStep, xFinal;
const isFunc = (f)=>(f instanceof Function); const isFunc = (f)=>(f instanceof Function);
switch(arguments.length){ switch(arguments.length){
case 1: /* (optionsObject) */ case 1: /* (optionsObject) */
opt = name; opt = name;
name = opt.name; name = opt.name;
xFunc = opt.xFunc; xFunc = opt.xFunc || 0;
break; break;
case 2: /* (name, callback|optionsObject) */ case 2: /* (name, callback|optionsObject) */
if(!isFunc(xFunc)){ if(!isFunc(xFunc)){
opt = xFunc; opt = xFunc;
xFunc = opt.xFunc; xFunc = opt.xFunc || 0;
} }
break; break;
case 3: /* name, xFunc, opt */ case 3: /* name, xFunc, opt */
@ -895,30 +903,46 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
if('string' !== typeof name){ if('string' !== typeof name){
toss3("Invalid arguments: missing function name."); toss3("Invalid arguments: missing function name.");
} }
xStep = opt.xStep; let xStep = opt.xStep || 0;
xFinal = opt.xFinal; let xFinal = opt.xFinal || 0;
const xValue = opt.xValue || 0;
const xInverse = opt.xInverse || 0;
let isWindow = undefined;
if(isFunc(xFunc)){ if(isFunc(xFunc)){
isWindow = false;
if(isFunc(xStep) || isFunc(xFinal)){ if(isFunc(xStep) || isFunc(xFinal)){
toss3("Ambiguous arguments: scalar or aggregate?"); toss3("Ambiguous arguments: scalar or aggregate?");
} }
xStep = xFinal = null; xStep = xFinal = null;
}else if(isFunc(xStep)){ }else if(isFunc(xStep)){
if(!isFunc(xFinal)){ if(!isFunc(xFinal)){
toss3("Missing xFinal() callback for aggregate UDF."); toss3("Missing xFinal() callback for aggregate or window UDF.");
} }
xFunc = null; xFunc = null;
}else if(isFunc(xFinal)){ }else if(isFunc(xFinal)){
toss3("Missing xStep() callback for aggregate UDF."); toss3("Missing xStep() callback for aggregate or window UDF.");
}else{ }else{
toss3("Missing function-type properties."); 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; const pApp = opt.pApp;
if(undefined!==pApp && if(undefined!==pApp &&
null!==pApp && null!==pApp &&
(('number'!==typeof pApp) || !capi.util.isInt32(pApp))){ (('number'!==typeof pApp) || !capi.util.isInt32(pApp))){
toss3("Invalid value for pApp property. Must be a legal WASM pointer value."); 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)){ if(xDestroy && !isFunc(xDestroy)){
toss3("xDestroy property must be a function."); toss3("xDestroy property must be a function.");
} }
@ -929,13 +953,22 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
name = name.toLowerCase(); name = name.toLowerCase();
const xArity = xFunc || xStep; const xArity = xFunc || xStep;
const arity = getOwnOption(opt, 'arity'); const arity = getOwnOption(opt, 'arity');
DB.checkRc(this, capi.sqlite3_create_function_v2( const arityArg = ('number'===typeof arity
this.pointer, name, ? arity
('number'===typeof arity : (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0));
? arity let rc;
: (xArity.length ? xArity.length-1/*for pCtx arg*/ : 0)), if( isWindow ){
capi.SQLITE_UTF8 | fFlags, pApp, rc = capi.sqlite3_create_window_function(
xFunc, xStep, xFinal, xDestroy)); 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; return this;
}/*createFunction()*/, }/*createFunction()*/,
/** /**
@ -1642,7 +1675,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
dbCtorHelper dbCtorHelper
}/*oo1 object*/; }/*oo1 object*/;
if(util.isMainWindow()){ if(util.isUIThread()){
/** /**
Functionally equivalent to DB(storageName,'c','kvvfs') except Functionally equivalent to DB(storageName,'c','kvvfs') except
that it throws if the given storage name is not one of 'local' that it throws if the given storage name is not one of 'local'

View File

@ -289,7 +289,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
``` ```
*/ */
WasmAllocError.toss = (...args)=>{ WasmAllocError.toss = (...args)=>{
throw new WasmAllocError(args.join(' ')); throw new WasmAllocError(...args);
}; };
/** /**
@ -530,9 +530,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
isBindableTypedArray, isBindableTypedArray,
isInt32, isSQLableTypedArray, isTypedArray, isInt32, isSQLableTypedArray, isTypedArray,
typedArrayToString, typedArrayToString,
isMainWindow: ()=>{ isUIThread: ()=>'undefined'===typeof WorkerGlobalScope
return 'undefined' === typeof WorkerGlobalScope
}
}, },
/** /**
@ -839,7 +837,8 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
["sqlite3_value_text", "string", "sqlite3_value*"], ["sqlite3_value_text", "string", "sqlite3_value*"],
["sqlite3_value_type", "int", "sqlite3_value*"], ["sqlite3_value_type", "int", "sqlite3_value*"],
["sqlite3_vfs_find", "*", "string"], ["sqlite3_vfs_find", "*", "string"],
["sqlite3_vfs_register", "int", "*", "int"] ["sqlite3_vfs_register", "int", "sqlite3_vfs*", "int"],
["sqlite3_vfs_unregister", "int", "sqlite3_vfs*"]
]/*wasm.bindingSignatures*/; ]/*wasm.bindingSignatures*/;
if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){ if(false && wasm.compileOptionUsed('SQLITE_ENABLE_NORMALIZE')){
@ -1026,10 +1025,18 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
class SQLite3Error extends Error { class SQLite3Error extends Error {
/** /**
Constructs this object with a message equal to all arguments 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){ 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'; this.name = 'SQLite3Error';
} }
}; };
@ -1042,7 +1049,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
``` ```
*/ */
SQLite3Error.toss = (...args)=>{ SQLite3Error.toss = (...args)=>{
throw new SQLite3Error(args.join(' ')); throw new SQLite3Error(...args);
}; };
/** State for sqlite3_wasmfs_opfs_dir(). */ /** State for sqlite3_wasmfs_opfs_dir(). */
@ -1219,7 +1226,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
: 0); : 0);
}; };
if( capi.util.isMainWindow() ){ if( capi.util.isUIThread() ){
/* Features specific to the main window thread... */ /* Features specific to the main window thread... */
/** /**

View File

@ -50,7 +50,7 @@
const mapToString = (v)=>{ const mapToString = (v)=>{
switch(typeof v){ switch(typeof v){
case 'number': case 'string': case 'boolean': case 'number': case 'string': case 'boolean':
case 'undefined': case 'undefined': case 'bigint':
return ''+v; return ''+v;
default: break; default: break;
} }
@ -216,7 +216,7 @@
} }
log(TestUtil.separator); log(TestUtil.separator);
logClass('group-start',"Group #"+this.number+':',this.name); logClass('group-start',"Group #"+this.number+':',this.name);
const indent = '....'; const indent = ' ';
const assertCount = TestUtil.counter; const assertCount = TestUtil.counter;
const groupState = Object.create(null); const groupState = Object.create(null);
const skipped = []; const skipped = [];
@ -230,7 +230,7 @@
const tc = TestUtil.counter const tc = TestUtil.counter
log(indent, n+":", t.name); log(indent, n+":", t.name);
await t.test.call(groupState, sqlite3); await t.test.call(groupState, sqlite3);
//log(indent, indent, 'assertion count:',TestUtil.counter - tc); log(indent, indent, TestUtil.counter - tc, 'assertion(s)');
} }
} }
logClass('green', logClass('green',
@ -312,6 +312,8 @@
T.assert(e instanceof Error) T.assert(e instanceof Error)
.assert(e instanceof sqlite3.WasmAllocError); .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){ .t('strglob/strlike', function(sqlite3){
@ -1092,7 +1094,7 @@
const vfsList = capi.sqlite3_js_vfs_list(); const vfsList = capi.sqlite3_js_vfs_list();
T.assert(vfsList.length>1); T.assert(vfsList.length>1);
T.assert('string'===typeof vfsList[0]); T.assert('string'===typeof vfsList[0]);
//log("vfsList =",vfsList);
for(const v of vfsList){ for(const v of vfsList){
T.assert('string' === typeof v) T.assert('string' === typeof v)
.assert(capi.sqlite3_vfs_find(v) > 0); .assert(capi.sqlite3_vfs_find(v) > 0);
@ -1282,7 +1284,7 @@
const sjac = capi.sqlite3_js_aggregate_context; const sjac = capi.sqlite3_js_aggregate_context;
db.createFunction({ db.createFunction({
name: 'summer', name: 'summer',
xStep: function(pCtx, n){ xStep: (pCtx, n)=>{
const ac = sjac(pCtx, 4); const ac = sjac(pCtx, 4);
wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32'); wasm.setMemValue(ac, wasm.getMemValue(ac,'i32') + Number(n), 'i32');
}, },
@ -1305,11 +1307,11 @@
db.createFunction({ db.createFunction({
name: 'summerN', name: 'summerN',
arity: -1, arity: -1,
xStep: function(pCtx, ...args){ xStep: (pCtx, ...args)=>{
const pAgg = sjac(pCtx, 4); const ac = sjac(pCtx, 4);
let sum = wasm.getMemValue(pAgg, 'i32'); let sum = wasm.getMemValue(ac, 'i32');
for(const v of args) sum += Number(v); for(const v of args) sum += Number(v);
wasm.setMemValue(pAgg, sum, 'i32'); wasm.setMemValue(ac, sum, 'i32');
}, },
xFinal: (pCtx)=>{ xFinal: (pCtx)=>{
const ac = sjac(pCtx, 0); const ac = sjac(pCtx, 0);
@ -1352,9 +1354,9 @@
const sjac = capi.sqlite3_js_aggregate_context; const sjac = capi.sqlite3_js_aggregate_context;
db.createFunction({ db.createFunction({
name: 'summer64', name: 'summer64',
xStep: function(pCtx, n){ xStep: (pCtx, n)=>{
const pAgg = sjac(pCtx, 8); const ac = sjac(pCtx, 8);
wasm.setMemValue(pAgg, wasm.getMemValue(pAgg,'i64') + BigInt(n), 'i64'); wasm.setMemValue(ac, wasm.getMemValue(ac,'i64') + BigInt(n), 'i64');
}, },
xFinal: (pCtx)=>{ xFinal: (pCtx)=>{
const ac = sjac(pCtx, 0); const ac = sjac(pCtx, 0);
@ -1362,19 +1364,64 @@
} }
}); });
let v = db.selectValue([ let v = db.selectValue([
"with cte(v) as (", "with cte(v) as (",
"select 3 union all select 5 union all select 7", "select 9007199254740991 union all select 1 union all select 2",
") select summer64(v*10), summer64(v+1) from cte" ") select summer64(v), summer64(v+1) from cte"
]); ]);
T.assert(150n===BigInt(v)); T.assert(9007199254740994n===v);
} }
}/*aggregate UDFs*/) }/*aggregate UDFs*/)
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
.t({ .t({
name: 'Window UDFs (tests are TODO)', name: 'Window UDFs',
predicate: testIsTodo 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(){ .t("ATTACH", function(){

View File

@ -1,5 +1,5 @@
C Make\ssemantics\sfor\sUDF\sxFinal()\sresult\shandling\sand\serror\sreporting\shandling\smore\sflexible. 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-20T23:48:38.866 D 2022-10-21T05:27:40.995
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -475,7 +475,7 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce
F ext/wasm/GNUmakefile 645bd5670a56acf2c8f1e969c427555e3e8e74dfd6eac8c948858f530617c7d5 F ext/wasm/GNUmakefile 645bd5670a56acf2c8f1e969c427555e3e8e74dfd6eac8c948858f530617c7d5
F ext/wasm/README-dist.txt e78e44a58772d5b5d7a5a179b5bf639cd67b62249aac66138e2c319bd02dd243 F ext/wasm/README-dist.txt e78e44a58772d5b5d7a5a179b5bf639cd67b62249aac66138e2c319bd02dd243
F ext/wasm/README.md 1e5b28158b74ab3ffc9d54fcbc020f0bbeb82c2ff8bbd904214c86c70e8a3066 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/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78 F ext/wasm/api/README.md 1350088aee90e959ad9a94fab1bb6bcb5e99d4d27f976db389050f54f2640c78
F ext/wasm/api/extern-post-js.js efbed835f290b3741259acc5faf68714a60d38e6834e6cfe172d5354c87566d2 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/post-js-header.js 2e5c886398013ba2af88028ecbced1e4b22dc96a86467f1ecc5ba9e64ef90a8b
F ext/wasm/api/pre-js.js 151e0616614a49f3db19ed544fa13b38c87c108959fbcd4029ea8399a562d94f F ext/wasm/api/pre-js.js 151e0616614a49f3db19ed544fa13b38c87c108959fbcd4029ea8399a562d94f
F ext/wasm/api/sqlite3-api-cleanup.js 4d07a7524dc9b7b050acfde57163e839243ad2383bd7ee0de0178b1b3e988588 F ext/wasm/api/sqlite3-api-cleanup.js 4d07a7524dc9b7b050acfde57163e839243ad2383bd7ee0de0178b1b3e988588
F ext/wasm/api/sqlite3-api-glue.js ea760df5991cee48e29aa6ced503027760462d5696d50521f7273a91e0e1430c F ext/wasm/api/sqlite3-api-glue.js 6e4e472eb5afc732a695cd7c5ded6dee6ef8b480e61aa0d648a3fc9033c84745
F ext/wasm/api/sqlite3-api-oo1.js 0e278d131dad72e9eb348a3dda6a4ff734a9e08925b4ed7e6e5a688d2edaf525 F ext/wasm/api/sqlite3-api-oo1.js 5016f6dd4b6b461bb6047fe6a2d3d7cbe85aa6b110c263ebf0347672a0cd897e
F ext/wasm/api/sqlite3-api-opfs.js 22d60ba956e873b65e2e0591e239178082bd53a6d563c3c58db7dc03e562e8f7 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-api-worker1.js a7f38f03275d6c27ab2aef3e83215d3c97ce09c43e6904df47c3764d9d4572b4
F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3 F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
F ext/wasm/api/sqlite3-opfs-async-proxy.js 206ce6bbc3c30ad51a37d9c25e3a2712e70b586e0f9a2cf8cb0b9619017c2671 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/test-opfs-vfs.js 48fc59110e8775bb43c9be25b6d634fc07ebadab7da8fbd44889e8129c6e2548
F ext/wasm/tester1-worker.html 048c341f124fdb61ca14dfd1bd1f78742490f208aa3bb1e84399f83f1e7e6a74 F ext/wasm/tester1-worker.html 048c341f124fdb61ca14dfd1bd1f78742490f208aa3bb1e84399f83f1e7e6a74
F ext/wasm/tester1.html 37ccc958fa0d95074af2d72b7241c8e2d982bbec6dda4dc790241af3d933c3b6 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/version-info.c 5fa356d38859d71a0369b5c37e1935def7413fcc8a4e349a39d9052c1d0479f4
F ext/wasm/wasmfs.make ee0004813e16c283ff633e08b482008d56adf9b7d42f6c5612f7ab002b924f69 F ext/wasm/wasmfs.make ee0004813e16c283ff633e08b482008d56adf9b7d42f6c5612f7ab002b924f69
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
@ -2036,8 +2036,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 9d034ef5e1bab7c9651c2450dc85765fa6365d3f1414c711550de858ff8b3ece P 89f3e1982ec32c010af67d15ef780847df20de568669e5c9d02f3cf084f51330
R dfb3978c0632c8b69ab9fb0976c9a2ee R 8a68d8975e926edd4ad048c9589fdc73
U stephan U stephan
Z c86f3e45c75ca0a38f433f4183ca8f53 Z 78e197950cf91aa30272843b46106df8
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
89f3e1982ec32c010af67d15ef780847df20de568669e5c9d02f3cf084f51330 f07ce15479b7224b0d1ba9f147a433136e70c1461aa667d2737d4a918f778f55