1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +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

@ -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'