mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Work on an alternate (slightly simpler) approach to binding JS vtabs. Non-eponymous vtabs are not working, for reasons as yet unknown.
FossilOrigin-Name: 6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23
This commit is contained in:
@ -185,28 +185,49 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
/**
|
/**
|
||||||
Constructs this object with a message depending on its arguments:
|
Constructs this object with a message depending on its arguments:
|
||||||
|
|
||||||
- If it's passed only a single integer argument, it is assumed
|
If its first argument is an integer, it is assumed to be
|
||||||
to be an sqlite3 C API result code. The message becomes the
|
an SQLITE_... result code and it is passed to
|
||||||
result of sqlite3.capi.sqlite3_js_rc_str() or (if that returns
|
sqlite3.capi.sqlite3_js_rc_str() to stringify it.
|
||||||
falsy) a synthesized string which contains that integer.
|
|
||||||
|
|
||||||
- If passed 2 arguments and the 2nd is a object, it behaves
|
If called with exactly 2 arguments and the 2nd is an object,
|
||||||
like the Error(string,object) constructor except that the first
|
that object is treated as the 2nd argument to the parent
|
||||||
argument is subject to the is-integer semantics from the
|
constructor.
|
||||||
previous point.
|
|
||||||
|
|
||||||
- Else all arguments are concatenated with a space between each
|
The exception's message is created by concatenating its
|
||||||
one, using args.join(' '), to create the error message.
|
arguments with a space between each, except for the
|
||||||
|
two-args-with-an-objec form and that the first argument will
|
||||||
|
get coerced to a string, as described above, if it's an
|
||||||
|
integer.
|
||||||
|
|
||||||
|
If passed an integer first argument, the error object's
|
||||||
|
`resultCode` member will be set to the given integer value,
|
||||||
|
else it will be set to capi.SQLITE_ERROR.
|
||||||
*/
|
*/
|
||||||
constructor(...args){
|
constructor(...args){
|
||||||
if(1===args.length && __isInt(args[0])){
|
let rc;
|
||||||
|
if(args.length){
|
||||||
|
if(__isInt(args[0])){
|
||||||
|
rc = args[0];
|
||||||
|
if(1===args.length){
|
||||||
super(__rcStr(args[0]));
|
super(__rcStr(args[0]));
|
||||||
}else if(2===args.length && 'object'===typeof args[1]){
|
}else{
|
||||||
if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
|
const rcStr = __rcStr(rc);
|
||||||
else super(...args);
|
if('object'===typeof args[1]){
|
||||||
|
super(rcStr,args[1]);
|
||||||
|
}else{
|
||||||
|
args[0] = rcStr+':';
|
||||||
|
super(args.join(' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(2===args.length && 'object'===typeof args[1]){
|
||||||
|
super(...args);
|
||||||
}else{
|
}else{
|
||||||
super(args.join(' '));
|
super(args.join(' '));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.resultCode = rc || capi.SQLITE_ERROR;
|
||||||
this.name = 'SQLite3Error';
|
this.name = 'SQLite3Error';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss;
|
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
||||||
const vh = Object.create(null), vt = Object.create(null);
|
const vh = Object.create(null), vt = Object.create(null);
|
||||||
|
|
||||||
sqlite3.VfsHelper = vh;
|
sqlite3.VfsHelper = vh;
|
||||||
@ -72,21 +72,29 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
/**
|
/**
|
||||||
Installs a StructBinder-bound function pointer member of the
|
Installs a StructBinder-bound function pointer member of the
|
||||||
given name and function in the given StructType target object.
|
given name and function in the given StructType target object.
|
||||||
|
|
||||||
It creates a WASM proxy for the given function and arranges for
|
It creates a WASM proxy for the given function and arranges for
|
||||||
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
that proxy to be cleaned up when tgt.dispose() is called. Throws
|
||||||
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
on the slightest hint of error, e.g. tgt is-not-a StructType,
|
||||||
name does not map to a struct-bound member, etc.
|
name does not map to a struct-bound member, etc.
|
||||||
|
|
||||||
Returns a proxy for this function which is bound to tgt and takes
|
As a special case, if the given function is a pointer, it is
|
||||||
2 args (name,func). That function returns the same thing,
|
assumed to be an existing WASM-bound function pointer and is used
|
||||||
permitting calls to be chained.
|
as-is with no extra level of proxying or cleanup. Results are
|
||||||
|
undefined if it's a pointer and it's _not_ a function pointer.
|
||||||
|
It is legal to pass a value of 0, indicating a NULL pointer, with
|
||||||
|
the caveat that 0 _is_ a legal function pointer in WASM but it
|
||||||
|
will not be accepted as such _here_. (Justification: the function
|
||||||
|
at address zero must be one which initially came from the WASM
|
||||||
|
module, not a method we want to bind to a virtual table or VFS.)
|
||||||
|
|
||||||
|
This function returns a proxy for itself which is bound to tgt
|
||||||
|
and takes 2 args (name,func). That function returns the same
|
||||||
|
thing as this one, permitting calls to be chained.
|
||||||
|
|
||||||
If called with only 1 arg, it has no side effects but returns a
|
If called with only 1 arg, it has no side effects but returns a
|
||||||
func with the same signature as described above.
|
func with the same signature as described above.
|
||||||
|
|
||||||
If tgt.ondispose is set before this is called then it _must_
|
|
||||||
be an array, to which this function will append entries.
|
|
||||||
|
|
||||||
ACHTUNG: because we cannot generically know how to transform JS
|
ACHTUNG: because we cannot generically know how to transform JS
|
||||||
exceptions into result codes, the installed functions do no
|
exceptions into result codes, the installed functions do no
|
||||||
automatic catching of exceptions. It is critical, to avoid
|
automatic catching of exceptions. It is critical, to avoid
|
||||||
@ -94,20 +102,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
this function do not throw. The exception, as it were, to that
|
this function do not throw. The exception, as it were, to that
|
||||||
rule is...
|
rule is...
|
||||||
|
|
||||||
If applyArgcCheck is true then each method gets wrapped in a
|
If applyArgcCheck is true then each JS function (as opposed to
|
||||||
proxy which asserts that it is passed the expected number of
|
function pointers) gets wrapped in a proxy which asserts that it
|
||||||
arguments, throwing if the argument count does not match
|
is passed the expected number of arguments, throwing if the
|
||||||
expectations. That is only intended for dev-time usage for sanity
|
argument count does not match expectations. That is only intended
|
||||||
checking, and will leave the C environment in an undefined
|
for dev-time usage for sanity checking, and will leave the C
|
||||||
state. For non-dev-time use, it is a given that the C API will
|
environment in an undefined state.
|
||||||
never call one of the generated function wrappers with the wrong
|
|
||||||
argument count.
|
|
||||||
*/
|
*/
|
||||||
vh.installMethod = vt.installMethod = function callee(
|
vh.installMethod = vt.installMethod = function callee(
|
||||||
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
||||||
){
|
){
|
||||||
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
||||||
toss("Usage error: target object is-not-a StructType.");
|
toss("Usage error: target object is-not-a StructType.");
|
||||||
|
}else if(!(func instanceof Function) && !wasm.isPtr(func)){
|
||||||
|
toss("Usage errror: expecting a Function or WASM pointer to one.");
|
||||||
}
|
}
|
||||||
if(1===arguments.length){
|
if(1===arguments.length){
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
@ -143,26 +151,33 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
}/*static init*/
|
}/*static init*/
|
||||||
const sigN = tgt.memberSignature(name);
|
const sigN = tgt.memberSignature(name);
|
||||||
if(sigN.length<2){
|
if(sigN.length<2){
|
||||||
toss("Member",name," is not a function pointer. Signature =",sigN);
|
toss("Member",name,"does not have a function pointer signature:",sigN);
|
||||||
}
|
}
|
||||||
const memKey = tgt.memberKey(name);
|
const memKey = tgt.memberKey(name);
|
||||||
const fProxy = applyArgcCheck
|
const fProxy = (applyArgcCheck && !wasm.isPtr(func))
|
||||||
/** This middle-man proxy is only for use during development, to
|
/** This middle-man proxy is only for use during development, to
|
||||||
confirm that we always pass the proper number of
|
confirm that we always pass the proper number of
|
||||||
arguments. We know that the C-level code will always use the
|
arguments. We know that the C-level code will always use the
|
||||||
correct argument count. */
|
correct argument count. */
|
||||||
? callee.argcProxy(tgt, memKey, func, sigN)
|
? callee.argcProxy(tgt, memKey, func, sigN)
|
||||||
: func;
|
: func;
|
||||||
|
if(wasm.isPtr(fProxy)){
|
||||||
|
if(fProxy && !wasm.functionEntry(fProxy)){
|
||||||
|
toss("Pointer",fProxy,"is not a WASM function table entry.");
|
||||||
|
}
|
||||||
|
tgt[memKey] = fProxy;
|
||||||
|
}else{
|
||||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
||||||
tgt[memKey] = pFunc;
|
tgt[memKey] = pFunc;
|
||||||
if(!tgt.ondispose) tgt.ondispose = [];
|
if(!tgt.ondispose) tgt.ondispose = [];
|
||||||
else if(!Array.isArray(tgt.ondispose)) tgt.ondispose = [tgt.ondispose];
|
else if(!Array.isArray(tgt.ondispose)) tgt.ondispose = [tgt.ondispose];
|
||||||
if(!tgt.ondispose.__removeFuncList){
|
if(!tgt.ondispose || !tgt.ondispose.__removeFuncList){
|
||||||
tgt.ondispose.push('ondispose.__removeFuncList handler',
|
tgt.addOnDispose('ondispose.__removeFuncList handler',
|
||||||
callee.removeFuncList);
|
callee.removeFuncList);
|
||||||
tgt.ondispose.__removeFuncList = [];
|
tgt.ondispose.__removeFuncList = [];
|
||||||
}
|
}
|
||||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
||||||
|
}
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
}/*installMethod*/;
|
}/*installMethod*/;
|
||||||
vh.installMethod.installMethodArgcCheck = false;
|
vh.installMethod.installMethodArgcCheck = false;
|
||||||
@ -269,8 +284,9 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
||||||
if('vfs'===key){
|
if('vfs'===key){
|
||||||
if(!o.struct.$zName && 'string'===typeof o.name){
|
if(!o.struct.$zName && 'string'===typeof o.name){
|
||||||
o.struct.$zName = wasm.allocCString(o.name);
|
o.struct.addOnDispose(
|
||||||
/* Note that we leak that C-string. */
|
o.struct.$zName = wasm.allocCString(o.name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.registerVfs(o.struct, !!o.asDefault);
|
this.registerVfs(o.struct, !!o.asDefault);
|
||||||
}
|
}
|
||||||
@ -292,24 +308,20 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
vt.sqlite3ValuesToJs = capi.sqlite3_create_function_v2.udfConvertArgs;
|
vt.sqlite3ValuesToJs = capi.sqlite3_create_function_v2.udfConvertArgs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Factory function for wrapXyz() impls.
|
Factory function for xAbc() impls.
|
||||||
*/
|
*/
|
||||||
const __xWrapFactory = function(structType){
|
const __xWrapFactory = function(methodName,structType){
|
||||||
return function(ptr,remove=false){
|
return function(ptr,removeMapping=false){
|
||||||
if(0===arguments.length) ptr = new structType;
|
if(0===arguments.length) ptr = new structType;
|
||||||
if(ptr instanceof structType){
|
if(ptr instanceof structType){
|
||||||
//T.assert(!this.has(ptr.pointer));
|
//T.assert(!this.has(ptr.pointer));
|
||||||
this.set(ptr.pointer, ptr);
|
this.set(ptr.pointer, ptr);
|
||||||
return ptr;
|
return ptr;
|
||||||
}else if(!wasm.isPtr(ptr)){
|
}else if(!wasm.isPtr(ptr)){
|
||||||
sqlite3.SQLite3Error.toss("Invalid argument to xWrapFactory");
|
sqlite3.SQLite3Error.toss("Invalid argument to",methodName+"()");
|
||||||
}
|
}
|
||||||
let rc = this.get(ptr);
|
let rc = this.get(ptr);
|
||||||
if(remove) this.delete(ptr);
|
if(removeMapping) this.delete(ptr);
|
||||||
/*arguable else if(!rc){
|
|
||||||
rc = new structType(ptr);
|
|
||||||
this.set(ptr, rc);
|
|
||||||
}*/
|
|
||||||
return rc;
|
return rc;
|
||||||
}.bind(new Map);
|
}.bind(new Map);
|
||||||
};
|
};
|
||||||
@ -336,7 +348,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
called from sqlite3_module::xDisconnect() implementations or
|
called from sqlite3_module::xDisconnect() implementations or
|
||||||
in error handling of a failed xCreate() or xConnect().
|
in error handling of a failed xCreate() or xConnect().
|
||||||
*/
|
*/
|
||||||
vt.xWrapVtab = __xWrapFactory(capi.sqlite3_vtab);
|
vt.xVtab = __xWrapFactory('xVtab',capi.sqlite3_vtab);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
EXPERIMENTAL. DO NOT USE IN CLIENT CODE.
|
EXPERIMENTAL. DO NOT USE IN CLIENT CODE.
|
||||||
@ -356,7 +368,16 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
intended to be called from xClose() or in error handling of a
|
intended to be called from xClose() or in error handling of a
|
||||||
failed xOpen().
|
failed xOpen().
|
||||||
*/
|
*/
|
||||||
vt.xWrapCursor = __xWrapFactory(capi.sqlite3_vtab_cursor);
|
vt.xCursor = __xWrapFactory('xCursor',capi.sqlite3_vtab_cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convenience form of creating an sqlite3_index_info wrapper,
|
||||||
|
intended for use in xBestIndex implementations. Note that the
|
||||||
|
caller is expected to call dispose() on the returned object
|
||||||
|
before returning. Though not _strictly_ required, as that object
|
||||||
|
does not own the pIdxInfo memory, it is nonetheless good form.
|
||||||
|
*/
|
||||||
|
vt.xIndexInfo = (pIdxInfo)=>new capi.sqlite3_index_info(pIdxInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Given an error object, this function returns
|
Given an error object, this function returns
|
||||||
@ -394,27 +415,38 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
let rc = ...
|
let rc = ...
|
||||||
return rc;
|
return rc;
|
||||||
}catch(e){
|
}catch(e){
|
||||||
return sqlite3.VtabHelper.xMethodError(
|
return sqlite3.VtabHelper.xError(
|
||||||
'xColumn', e, sqlite3.capi.SQLITE_XYZ);
|
'xColumn', e, sqlite3.capi.SQLITE_XYZ);
|
||||||
// where SQLITE_XYZ is some call-appropriate result code
|
// where SQLITE_XYZ is some call-appropriate result code.
|
||||||
// defaulting to SQLITE_ERROR.
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If xMethodError.errorReporter is a function, it is called in
|
If no 3rd argument is provided, its default depends on
|
||||||
|
the error type:
|
||||||
|
|
||||||
|
- An sqlite3.WasmAllocError always resolves to capi.SQLITE_NOMEM.
|
||||||
|
|
||||||
|
- If err is an SQLite3Error then its `resultCode` property
|
||||||
|
is used.
|
||||||
|
|
||||||
|
- If all else fails, capi.SQLITE_ERROR is used.
|
||||||
|
|
||||||
|
If xError.errorReporter is a function, it is called in
|
||||||
order to report the error, else the error is not reported.
|
order to report the error, else the error is not reported.
|
||||||
If that function throws, that exception is ignored.
|
If that function throws, that exception is ignored.
|
||||||
*/
|
*/
|
||||||
vt.xMethodError = function f(methodName, err, defaultRc=capi.SQLITE_ERROR){
|
vt.xError = function f(methodName, err, defaultRc){
|
||||||
if(f.errorReporter instanceof Function){
|
if(f.errorReporter instanceof Function){
|
||||||
try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);}
|
try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);}
|
||||||
catch(e){/*ignored*/}
|
catch(e){/*ignored*/}
|
||||||
}
|
}
|
||||||
return (err instanceof sqlite3.WasmAllocError)
|
let rc;
|
||||||
? capi.SQLITE_NOMEM
|
if(err instanceof sqlite3.WasmAllocError) rc = capi.SQLITE_NOMEM;
|
||||||
: defaultRc;
|
else if(arguments.length>2) rc = defaultRc;
|
||||||
|
else if(err instanceof sqlite3.SQLite3Error) rc = err.resultCode;
|
||||||
|
return rc || capi.SQLITE_ERROR;
|
||||||
};
|
};
|
||||||
vt.xMethodError.errorReporter = 1 ? console.error.bind(console) : false;
|
vt.xError.errorReporter = 1 ? console.error.bind(console) : false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
"The problem" with this is that it introduces an outer function with
|
"The problem" with this is that it introduces an outer function with
|
||||||
@ -426,15 +458,139 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
/** vt.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
|
/** vt.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
|
||||||
return function(...args){
|
return function(...args){
|
||||||
try { method(...args); }
|
try { method(...args); }
|
||||||
}catch(e){ return vt.xMethodError(methodName, e, defaultRc) }
|
}catch(e){ return vt.xError(methodName, e, defaultRc) }
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A helper for sqlite3_vtab::xRow() implementations. It must be
|
A helper for sqlite3_vtab::xRowid() implementations. It must be
|
||||||
passed that function's 2nd argument and the value for that
|
passed that function's 2nd argument and the value for that
|
||||||
pointer. Returns the same as wasm.setMemValue() and will throw
|
pointer. Returns the same as wasm.setMemValue() and will throw
|
||||||
if the 1st or 2nd arguments are invalid for that function.
|
if the 1st or 2nd arguments are invalid for that function.
|
||||||
*/
|
*/
|
||||||
vt.setRowId = (ppRowid64, value)=>wasm.setMemValue(ppRowid64, value, 'i64');
|
vt.xRowid = (ppRowid64, value)=>wasm.setMemValue(ppRowid64, value, 'i64');
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets up an sqlite3_module() object for later installation into
|
||||||
|
individual databases using sqlite3_create_module(). Requires an
|
||||||
|
object with the following properties:
|
||||||
|
|
||||||
|
- `methods`: an object containing a mapping of properties with
|
||||||
|
the C-side names of the sqlite3_module methods, e.g. xCreate,
|
||||||
|
xBestIndex, etc., to JS implementations for those functions.
|
||||||
|
Certain special-case handling is performed, as described below.
|
||||||
|
|
||||||
|
- `catchExceptions` (default=false): if truthy, the given methods
|
||||||
|
are not mapped as-is, but are instead wrapped inside wrappers
|
||||||
|
which translate exceptions into result codes of SQLITE_ERROR or
|
||||||
|
SQLITE_NOMEM, depending on whether the exception is an
|
||||||
|
sqlite3.WasmAllocError. In the case of the xConnect and xCreate
|
||||||
|
methods, the exception handler also sets the output error
|
||||||
|
string to the exception's error string.
|
||||||
|
|
||||||
|
- OPTIONAL `struct`: a sqlite3.capi.sqlite3_module() instance. If
|
||||||
|
not set, one will be created automatically and (on success)
|
||||||
|
added to the object.
|
||||||
|
|
||||||
|
- OPTIONAL `iVersion`: if set, it must be an integer value and it
|
||||||
|
gets assigned to the `$iVersion` member of the struct object.
|
||||||
|
If it's _not_ set, and the passed-in `struct` object's `$iVersion`
|
||||||
|
is 0 (the default) then this function attempts to define a value
|
||||||
|
for that property based on the list of methods it has.
|
||||||
|
|
||||||
|
If `catchExceptions` is false, it is up to the client to ensure
|
||||||
|
that no exceptions escape the methods, as doing so would move
|
||||||
|
them through the C API, leading to undefined
|
||||||
|
behavior. (VtabHelper.xError() is intended to assist in reporting
|
||||||
|
such exceptions.)
|
||||||
|
|
||||||
|
If `methods.xConnect` is `true` then the value of
|
||||||
|
`methods.xCreate` is used in its place, and vice versa. This is
|
||||||
|
to facilitate creation of those methods inline in the passed-in
|
||||||
|
object without requiring the client to explicitly get a reference
|
||||||
|
to one of them in order to assign it to the other one. Note that
|
||||||
|
sqlite treats those two functions specially if they are exactly
|
||||||
|
the same function (same pointer value). The
|
||||||
|
`catchExceptions`-installed handlers will account for identical
|
||||||
|
references to those two functions and will install the same
|
||||||
|
wrapper function for both.
|
||||||
|
|
||||||
|
The given methods are expected to return integer values, as
|
||||||
|
expected by the C API. If `catchExceptions` is truthy, the return
|
||||||
|
value of the wrapped function will be used as-is and will be
|
||||||
|
translated to 0 if the function returns a falsy value (e.g. if it
|
||||||
|
does not have an explicit return). If `catchExceptions` is _not_
|
||||||
|
active, the method implementations must explicitly return integer
|
||||||
|
values.
|
||||||
|
|
||||||
|
Throws on error. Returns the sqlite3_module object on success.
|
||||||
|
*/
|
||||||
|
vt.setupModule = function(opt){
|
||||||
|
const mod = opt.struct || new capi.sqlite3_module();
|
||||||
|
try{
|
||||||
|
const methods = opt.methods || toss("Missing 'methods' object.");
|
||||||
|
if(true===methods.xConnect) methods.xConnect = methods.xCreate;
|
||||||
|
else if(true===methods.xCreate) methods.xCreate = methods.xConnect;
|
||||||
|
if(opt.catchExceptions){
|
||||||
|
const fwrap = function(methodName, func){
|
||||||
|
if(['xConnect','xCreate'].indexOf(methodName) >= 0){
|
||||||
|
return function(pDb, pAux, argc, argv, ppVtab, pzErr){
|
||||||
|
try{return func(...arguments) || 0;}
|
||||||
|
catch(e){
|
||||||
|
if(!(e instanceof sqlite3.WasmAllocError)){
|
||||||
|
wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
|
||||||
|
}
|
||||||
|
return vt.xError(methodName, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}else{
|
||||||
|
return function(...args){
|
||||||
|
try{return func(...args) || 0;}
|
||||||
|
catch(e){
|
||||||
|
return vt.xError(methodName, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const mnames = [
|
||||||
|
'xCreate', 'xConnect', 'xBestIndex', 'xDisconnect',
|
||||||
|
'xDestroy', 'xOpen', 'xClose', 'xFilter', 'xNext',
|
||||||
|
'xEof', 'xColumn', 'xRowid', 'xUpdate',
|
||||||
|
'xBegin', 'xSync', 'xCommit', 'xRollback',
|
||||||
|
'xFindFunction', 'xRename', 'xSavepoint', 'xRelease',
|
||||||
|
'xRollbackTo', 'xShadowName'
|
||||||
|
];
|
||||||
|
const remethods = Object.create(null);
|
||||||
|
for(const k of mnames){
|
||||||
|
const m = methods[k];
|
||||||
|
if(!(m instanceof Function)) continue;
|
||||||
|
else if('xConnect'===k && methods.xCreate===m){
|
||||||
|
remethods[k] = methods.xCreate;
|
||||||
|
}else if('xCreate'===k && methods.xConnect===m){
|
||||||
|
remethods[k] = methods.xConnect;
|
||||||
|
}else{
|
||||||
|
remethods[k] = fwrap(k, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.installMethods(mod, remethods, false);
|
||||||
|
}else{
|
||||||
|
this.installMethods(
|
||||||
|
mod, methods, !!opt.applyArgcCheck/*undocumented option*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if(0===mod.$iVersion){
|
||||||
|
let v;
|
||||||
|
if('number'===typeof opt.iVersion) v = opt.iVersion;
|
||||||
|
else if(mod.$xShadowName) v = 3;
|
||||||
|
else if(mod.$xSavePoint || mod.$xRelease || mod.$xRollbackTo) v = 2;
|
||||||
|
else v = 1;
|
||||||
|
mod.$iVersion = v;
|
||||||
|
}
|
||||||
|
}catch(e){
|
||||||
|
if(!opt.struct) mod.dispose();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
if(!opt.struct) opt.struct = mod;
|
||||||
|
return mod;
|
||||||
|
}/*setupModule()*/;
|
||||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
||||||
|
@ -222,6 +222,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
const __freeStruct = function(ctor, obj, m){
|
const __freeStruct = function(ctor, obj, m){
|
||||||
if(!m) m = __instancePointerMap.get(obj);
|
if(!m) m = __instancePointerMap.get(obj);
|
||||||
if(m) {
|
if(m) {
|
||||||
|
__instancePointerMap.delete(obj);
|
||||||
if(Array.isArray(obj.ondispose)){
|
if(Array.isArray(obj.ondispose)){
|
||||||
let x;
|
let x;
|
||||||
while((x = obj.ondispose.shift())){
|
while((x = obj.ondispose.shift())){
|
||||||
@ -245,7 +246,6 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete obj.ondispose;
|
delete obj.ondispose;
|
||||||
__instancePointerMap.delete(obj);
|
|
||||||
if(ctor.debugFlags.__flags.dealloc){
|
if(ctor.debugFlags.__flags.dealloc){
|
||||||
log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
|
log("debug.dealloc:",(obj[xPtrPropName]?"EXTERNAL":""),
|
||||||
ctor.structName,"instance:",
|
ctor.structName,"instance:",
|
||||||
|
@ -301,14 +301,12 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
addTest: function(name, callback){
|
addTest: function(name, callback){
|
||||||
let predicate;
|
let predicate;
|
||||||
if(1===arguments.length){
|
if(1===arguments.length){
|
||||||
const opt = arguments[0];
|
this.currentTestGroup.addTest(arguments[0]);
|
||||||
predicate = opt.predicate;
|
}else{
|
||||||
name = opt.name;
|
|
||||||
callback = opt.test;
|
|
||||||
}
|
|
||||||
this.currentTestGroup.addTest({
|
this.currentTestGroup.addTest({
|
||||||
name, predicate, test: callback
|
name, predicate, test: callback
|
||||||
});
|
});
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
runTests: async function(sqlite3){
|
runTests: async function(sqlite3){
|
||||||
@ -399,12 +397,21 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
catch(e){T.assert("test ing ." === e.message)}
|
catch(e){T.assert("test ing ." === e.message)}
|
||||||
|
|
||||||
try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
|
try{ throw new sqlite3.SQLite3Error(capi.SQLITE_SCHEMA) }
|
||||||
catch(e){ T.assert('SQLITE_SCHEMA' === e.message) }
|
catch(e){
|
||||||
|
T.assert('SQLITE_SCHEMA' === e.message)
|
||||||
|
.assert(capi.SQLITE_SCHEMA === e.resultCode);
|
||||||
|
}
|
||||||
try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
|
try{ sqlite3.SQLite3Error.toss(capi.SQLITE_CORRUPT,{cause: true}) }
|
||||||
catch(e){
|
catch(e){
|
||||||
T.assert('SQLITE_CORRUPT' === e.message)
|
T.assert('SQLITE_CORRUPT' === e.message)
|
||||||
|
.assert(capi.SQLITE_CORRUPT === e.resultCode)
|
||||||
.assert(true===e.cause);
|
.assert(true===e.cause);
|
||||||
}
|
}
|
||||||
|
try{ sqlite3.SQLite3Error.toss("resultCode check") }
|
||||||
|
catch(e){
|
||||||
|
T.assert(capi.SQLITE_ERROR === e.resultCode)
|
||||||
|
.assert('resultCode check' === e.message);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
.t('strglob/strlike', function(sqlite3){
|
.t('strglob/strlike', function(sqlite3){
|
||||||
@ -988,6 +995,20 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
const dbFile = '/tester1.db';
|
const dbFile = '/tester1.db';
|
||||||
wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
|
wasm.sqlite3_wasm_vfs_unlink(0, dbFile);
|
||||||
const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
|
const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
|
||||||
|
db.onclose = {
|
||||||
|
disposeThese: [],
|
||||||
|
after: function(){
|
||||||
|
while(this.disposeThese.length){
|
||||||
|
const v = this.disposeThese.shift();
|
||||||
|
console.debug("db.onclose cleaning up:",v);
|
||||||
|
if(wasm.isPtr(v)) wasm.dealloc(v);
|
||||||
|
else if(v instanceof sqlite3.StructBinder.StructType){
|
||||||
|
v.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
T.assert(Number.isInteger(db.pointer))
|
T.assert(Number.isInteger(db.pointer))
|
||||||
.mustThrowMatching(()=>db.pointer=1, /read-only/)
|
.mustThrowMatching(()=>db.pointer=1, /read-only/)
|
||||||
.assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1))
|
.assert(0===sqlite3.capi.sqlite3_extended_result_codes(db.pointer,1))
|
||||||
@ -1539,219 +1560,6 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
T.mustThrow(()=>db.exec("select * from foo.bar"));
|
T.mustThrow(()=>db.exec("select * from foo.bar"));
|
||||||
})
|
})
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
|
||||||
.t({
|
|
||||||
name: 'Custom virtual tables',
|
|
||||||
predicate: ()=>wasm.bigIntEnabled,
|
|
||||||
test: function(sqlite3){
|
|
||||||
warn("The vtab/module JS bindings are experimental and subject to change.");
|
|
||||||
const vth = sqlite3.VtabHelper;
|
|
||||||
const tmplCols = Object.assign(Object.create(null),{
|
|
||||||
A: 0, B: 1
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
The vtab demonstrated here is a JS-ification of
|
|
||||||
ext/misc/templatevtab.c.
|
|
||||||
*/
|
|
||||||
const tmplMethods = {
|
|
||||||
xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){
|
|
||||||
try{
|
|
||||||
const args = wasm.cArgvToJs(argc, argv);
|
|
||||||
T.assert(args.length>=3)
|
|
||||||
.assert(args[0] === 'testvtab')
|
|
||||||
.assert(args[1] === 'main')
|
|
||||||
.assert(args[2] === 'testvtab');
|
|
||||||
console.debug("xConnect() args =",args);
|
|
||||||
const rc = capi.sqlite3_declare_vtab(
|
|
||||||
pDb, "CREATE TABLE ignored(a,b)"
|
|
||||||
);
|
|
||||||
if(0===rc){
|
|
||||||
const t = vth.xWrapVtab();
|
|
||||||
wasm.setPtrValue(ppVtab, t.pointer);
|
|
||||||
T.assert(t === vth.xWrapVtab(wasm.getPtrValue(ppVtab)));
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}catch(e){
|
|
||||||
if(!(e instanceof sqlite3.WasmAllocError)){
|
|
||||||
wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
|
|
||||||
}
|
|
||||||
return vth.xMethodError('xConnect',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xDisconnect: function(pVtab){
|
|
||||||
try {
|
|
||||||
const t = vth.xWrapVtab(pVtab, true);
|
|
||||||
t.dispose();
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xDisconnect',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xOpen: function(pVtab, ppCursor){
|
|
||||||
try{
|
|
||||||
const t = vth.xWrapVtab(pVtab), c = vth.xWrapCursor();
|
|
||||||
T.assert(t instanceof capi.sqlite3_vtab)
|
|
||||||
.assert(c instanceof capi.sqlite3_vtab_cursor);
|
|
||||||
wasm.setPtrValue(ppCursor, c.pointer);
|
|
||||||
c._rowId = 0;
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xOpen',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xClose: function(pCursor){
|
|
||||||
try{
|
|
||||||
const c = vth.xWrapCursor(pCursor,true);
|
|
||||||
T.assert(c instanceof capi.sqlite3_vtab_cursor)
|
|
||||||
.assert(!vth.xWrapCursor(pCursor));
|
|
||||||
c.dispose();
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xClose',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xNext: function(pCursor){
|
|
||||||
try{
|
|
||||||
const c = vth.xWrapCursor(pCursor);
|
|
||||||
++c._rowId;
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xNext',e);
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
xColumn: function(pCursor, pCtx, iCol){
|
|
||||||
try{
|
|
||||||
const c = vth.xWrapCursor(pCursor);
|
|
||||||
switch(iCol){
|
|
||||||
case tmplCols.A:
|
|
||||||
capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
|
|
||||||
break;
|
|
||||||
case tmplCols.B:
|
|
||||||
capi.sqlite3_result_int(pCtx, 2000 + c._rowId);
|
|
||||||
break;
|
|
||||||
default: sqlite3.SQLite3Error.toss("Invalid column id",iCol);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xColumn',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xRowid: function(pCursor, ppRowid64){
|
|
||||||
try{
|
|
||||||
const c = vth.xWrapCursor(pCursor);
|
|
||||||
vth.setRowId(ppRowid64, c._rowId);
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xRowid',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xEof: function(pCursor){
|
|
||||||
const c = vth.xWrapCursor(pCursor);
|
|
||||||
return c._rowId>=10;
|
|
||||||
},
|
|
||||||
xFilter: function(pCursor, idxNum, idxCStr,
|
|
||||||
argc, argv/* [sqlite3_value* ...] */){
|
|
||||||
try{
|
|
||||||
const c = vth.xWrapCursor(pCursor);
|
|
||||||
c._rowId = 0;
|
|
||||||
const list = vth.sqlite3ValuesToJs(argc, argv);
|
|
||||||
T.assert(argc === list.length);
|
|
||||||
//log(argc,"xFilter value(s):",list);
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xFilter',e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
xBestIndex: function(pVtab, pIdxInfo){
|
|
||||||
try{
|
|
||||||
//const t = vth.xWrapVtab(pVtab);
|
|
||||||
const sii = capi.sqlite3_index_info;
|
|
||||||
const pii = new sii(pIdxInfo);
|
|
||||||
pii.$estimatedRows = 10;
|
|
||||||
pii.$estimatedCost = 10.0;
|
|
||||||
//log("xBestIndex $nConstraint =",pii.$nConstraint);
|
|
||||||
if(pii.$nConstraint>0){
|
|
||||||
// Validate nthConstraint() and nthConstraintUsage()
|
|
||||||
const max = pii.$nConstraint;
|
|
||||||
for(let i=0; i < max; ++i ){
|
|
||||||
let v = pii.nthConstraint(i,true);
|
|
||||||
T.assert(wasm.isPtr(v));
|
|
||||||
v = pii.nthConstraint(i);
|
|
||||||
T.assert(v instanceof sii.sqlite3_index_constraint)
|
|
||||||
.assert(v.pointer >= pii.$aConstraint);
|
|
||||||
v.dispose();
|
|
||||||
v = pii.nthConstraintUsage(i,true);
|
|
||||||
T.assert(wasm.isPtr(v));
|
|
||||||
v = pii.nthConstraintUsage(i);
|
|
||||||
T.assert(v instanceof sii.sqlite3_index_constraint_usage)
|
|
||||||
.assert(v.pointer >= pii.$aConstraintUsage);
|
|
||||||
v.$argvIndex = i;//just to get some values into xFilter
|
|
||||||
v.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//log("xBestIndex $nOrderBy =",pii.$nOrderBy);
|
|
||||||
if(pii.$nOrderBy>0){
|
|
||||||
// Validate nthOrderBy()
|
|
||||||
const max = pii.$nOrderBy;
|
|
||||||
for(let i=0; i < max; ++i ){
|
|
||||||
let v = pii.nthOrderBy(i,true);
|
|
||||||
T.assert(wasm.isPtr(v));
|
|
||||||
v = pii.nthOrderBy(i);
|
|
||||||
T.assert(v instanceof sii.sqlite3_index_orderby)
|
|
||||||
.assert(v.pointer >= pii.$aOrderBy);
|
|
||||||
v.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pii.dispose();
|
|
||||||
return 0;
|
|
||||||
}catch(e){
|
|
||||||
return vth.xMethodError('xBestIndex',e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
The vtab API places relevance on whether xCreate and
|
|
||||||
xConnect are exactly the same function (same pointer
|
|
||||||
address). Two JS-side references to the same method will
|
|
||||||
end up, without acrobatics to counter it, being compiled as
|
|
||||||
two different WASM-side bindings, i.e. two different
|
|
||||||
pointers.
|
|
||||||
|
|
||||||
In order to account for this, VtabHelper.installMethods()
|
|
||||||
checks for duplicate function entries and maps them to the
|
|
||||||
same WASM-compiled instance.
|
|
||||||
*/
|
|
||||||
if(1){
|
|
||||||
tmplMethods.xCreate = tmplMethods.xConnect;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tmplMod = new sqlite3.capi.sqlite3_module();
|
|
||||||
tmplMod.ondispose = [];
|
|
||||||
tmplMod.$iVersion = 0;
|
|
||||||
vth.installMethods(tmplMod, tmplMethods, true);
|
|
||||||
if(tmplMethods.xCreate){
|
|
||||||
T.assert(tmplMod.$xCreate)
|
|
||||||
.assert(tmplMod.$xCreate === tmplMod.$xConnect,
|
|
||||||
"installMethods() must avoid re-compiling identical functions");
|
|
||||||
tmplMod.$xCreate = 0;
|
|
||||||
}
|
|
||||||
let rc = capi.sqlite3_create_module(
|
|
||||||
this.db, "testvtab", tmplMod, 0
|
|
||||||
);
|
|
||||||
this.db.checkRc(rc);
|
|
||||||
|
|
||||||
const list = this.db.selectArrays(
|
|
||||||
"SELECT a,b FROM testvtab where a<9999 and b>1 order by a, b"
|
|
||||||
/* Query is shaped so that it will ensure that some constraints
|
|
||||||
end up in xBestIndex(). */
|
|
||||||
);
|
|
||||||
T.assert(10===list.length)
|
|
||||||
.assert(1000===list[0][0])
|
|
||||||
.assert(2009===list[list.length-1][1])
|
|
||||||
}
|
|
||||||
})/*vtab sanity checks*/
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
.t({
|
.t({
|
||||||
name: 'C-side WASM tests',
|
name: 'C-side WASM tests',
|
||||||
@ -1838,6 +1646,379 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
}
|
}
|
||||||
}/* jaccwabyt-specific tests */)
|
}/* jaccwabyt-specific tests */)
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
.t({
|
||||||
|
name: 'virtual table #1',
|
||||||
|
predicate: ()=>!!capi.sqlite3_index_info,
|
||||||
|
test: function(sqlite3){
|
||||||
|
warn("The vtab/module JS bindings are experimental and subject to change.");
|
||||||
|
const vth = sqlite3.VtabHelper;
|
||||||
|
const tmplCols = Object.assign(Object.create(null),{
|
||||||
|
A: 0, B: 1
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
The vtab demonstrated here is a JS-ification of
|
||||||
|
ext/misc/templatevtab.c.
|
||||||
|
*/
|
||||||
|
const tmplMethods = {
|
||||||
|
xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){
|
||||||
|
try{
|
||||||
|
const args = wasm.cArgvToJs(argc, argv);
|
||||||
|
T.assert(args.length>=3)
|
||||||
|
.assert(args[0] === 'testvtab')
|
||||||
|
.assert(args[1] === 'main')
|
||||||
|
.assert(args[2] === 'testvtab');
|
||||||
|
console.debug("xConnect() args =",args);
|
||||||
|
const rc = capi.sqlite3_declare_vtab(
|
||||||
|
pDb, "CREATE TABLE ignored(a,b)"
|
||||||
|
);
|
||||||
|
if(0===rc){
|
||||||
|
const t = vth.xVtab();
|
||||||
|
wasm.setPtrValue(ppVtab, t.pointer);
|
||||||
|
T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab)));
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}catch(e){
|
||||||
|
if(!(e instanceof sqlite3.WasmAllocError)){
|
||||||
|
wasm.setPtrValue(pzErr, wasm.allocCString(e.message));
|
||||||
|
}
|
||||||
|
return vth.xError('xConnect',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xDisconnect: function(pVtab){
|
||||||
|
try {
|
||||||
|
const t = vth.xVtab(pVtab, true);
|
||||||
|
t.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xDisconnect',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xOpen: function(pVtab, ppCursor){
|
||||||
|
try{
|
||||||
|
const t = vth.xVtab(pVtab), c = vth.xCursor();
|
||||||
|
T.assert(t instanceof capi.sqlite3_vtab)
|
||||||
|
.assert(c instanceof capi.sqlite3_vtab_cursor);
|
||||||
|
wasm.setPtrValue(ppCursor, c.pointer);
|
||||||
|
c._rowId = 0;
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xOpen',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xClose: function(pCursor){
|
||||||
|
try{
|
||||||
|
const c = vth.xCursor(pCursor,true);
|
||||||
|
T.assert(c instanceof capi.sqlite3_vtab_cursor)
|
||||||
|
.assert(!vth.xCursor(pCursor));
|
||||||
|
c.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xClose',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xNext: function(pCursor){
|
||||||
|
try{
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
++c._rowId;
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xNext',e);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
xColumn: function(pCursor, pCtx, iCol){
|
||||||
|
try{
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
switch(iCol){
|
||||||
|
case tmplCols.A:
|
||||||
|
capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
|
||||||
|
break;
|
||||||
|
case tmplCols.B:
|
||||||
|
capi.sqlite3_result_int(pCtx, 2000 + c._rowId);
|
||||||
|
break;
|
||||||
|
default: sqlite3.SQLite3Error.toss("Invalid column id",iCol);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xColumn',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xRowid: function(pCursor, ppRowid64){
|
||||||
|
try{
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
vth.xRowid(ppRowid64, c._rowId);
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xRowid',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xEof: function(pCursor){
|
||||||
|
const c = vth.xCursor(pCursor),
|
||||||
|
rc = c._rowId>=10;
|
||||||
|
c.dispose();
|
||||||
|
return rc;
|
||||||
|
},
|
||||||
|
xFilter: function(pCursor, idxNum, idxCStr,
|
||||||
|
argc, argv/* [sqlite3_value* ...] */){
|
||||||
|
try{
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
c._rowId = 0;
|
||||||
|
const list = vth.sqlite3ValuesToJs(argc, argv);
|
||||||
|
T.assert(argc === list.length);
|
||||||
|
//log(argc,"xFilter value(s):",list);
|
||||||
|
c.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xFilter',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xBestIndex: function(pVtab, pIdxInfo){
|
||||||
|
try{
|
||||||
|
//const t = vth.xVtab(pVtab);
|
||||||
|
const sii = capi.sqlite3_index_info;
|
||||||
|
const pii = new sii(pIdxInfo);
|
||||||
|
pii.$estimatedRows = 10;
|
||||||
|
pii.$estimatedCost = 10.0;
|
||||||
|
//log("xBestIndex $nConstraint =",pii.$nConstraint);
|
||||||
|
if(pii.$nConstraint>0){
|
||||||
|
// Validate nthConstraint() and nthConstraintUsage()
|
||||||
|
const max = pii.$nConstraint;
|
||||||
|
for(let i=0; i < max; ++i ){
|
||||||
|
let v = pii.nthConstraint(i,true);
|
||||||
|
T.assert(wasm.isPtr(v));
|
||||||
|
v = pii.nthConstraint(i);
|
||||||
|
T.assert(v instanceof sii.sqlite3_index_constraint)
|
||||||
|
.assert(v.pointer >= pii.$aConstraint);
|
||||||
|
v.dispose();
|
||||||
|
v = pii.nthConstraintUsage(i,true);
|
||||||
|
T.assert(wasm.isPtr(v));
|
||||||
|
v = pii.nthConstraintUsage(i);
|
||||||
|
T.assert(v instanceof sii.sqlite3_index_constraint_usage)
|
||||||
|
.assert(v.pointer >= pii.$aConstraintUsage);
|
||||||
|
v.$argvIndex = i;//just to get some values into xFilter
|
||||||
|
v.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//log("xBestIndex $nOrderBy =",pii.$nOrderBy);
|
||||||
|
if(pii.$nOrderBy>0){
|
||||||
|
// Validate nthOrderBy()
|
||||||
|
const max = pii.$nOrderBy;
|
||||||
|
for(let i=0; i < max; ++i ){
|
||||||
|
let v = pii.nthOrderBy(i,true);
|
||||||
|
T.assert(wasm.isPtr(v));
|
||||||
|
v = pii.nthOrderBy(i);
|
||||||
|
T.assert(v instanceof sii.sqlite3_index_orderby)
|
||||||
|
.assert(v.pointer >= pii.$aOrderBy);
|
||||||
|
v.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pii.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xError('xBestIndex',e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
The vtab API places relevance on whether xCreate and
|
||||||
|
xConnect are exactly the same function (same pointer
|
||||||
|
address). Two JS-side references to the same method will
|
||||||
|
end up, without acrobatics to counter it, being compiled as
|
||||||
|
two different WASM-side bindings, i.e. two different
|
||||||
|
pointers.
|
||||||
|
|
||||||
|
In order to account for this, VtabHelper.installMethods()
|
||||||
|
checks for duplicate function entries and maps them to the
|
||||||
|
same WASM-compiled instance.
|
||||||
|
*/
|
||||||
|
if(1){
|
||||||
|
tmplMethods.xCreate = tmplMethods.xConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmplMod = new sqlite3.capi.sqlite3_module();
|
||||||
|
tmplMod.$iVersion = 0;
|
||||||
|
this.db.onclose.disposeThese.push(tmplMod);
|
||||||
|
vth.installMethods(tmplMod, tmplMethods, true);
|
||||||
|
if(tmplMethods.xCreate){
|
||||||
|
T.assert(tmplMod.$xCreate)
|
||||||
|
.assert(tmplMod.$xCreate === tmplMod.$xConnect,
|
||||||
|
"installMethods() must avoid re-compiling identical functions");
|
||||||
|
tmplMod.$xCreate = 0;
|
||||||
|
}
|
||||||
|
let rc = capi.sqlite3_create_module(
|
||||||
|
this.db, "testvtab", tmplMod, 0
|
||||||
|
);
|
||||||
|
this.db.checkRc(rc);
|
||||||
|
|
||||||
|
const list = this.db.selectArrays(
|
||||||
|
"SELECT a,b FROM testvtab where a<9999 and b>1 order by a, b"
|
||||||
|
/* Query is shaped so that it will ensure that some constraints
|
||||||
|
end up in xBestIndex(). */
|
||||||
|
);
|
||||||
|
T.assert(10===list.length)
|
||||||
|
.assert(1000===list[0][0])
|
||||||
|
.assert(2009===list[list.length-1][1])
|
||||||
|
}
|
||||||
|
})/*custom vtab #1*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
.t({
|
||||||
|
name: 'virtual table #2 (w/ automated exception wrapping)',
|
||||||
|
predicate: ()=>!!capi.sqlite3_index_info,
|
||||||
|
test: function(sqlite3){
|
||||||
|
warn("The vtab/module JS bindings are experimental and subject to change.");
|
||||||
|
const vth = sqlite3.VtabHelper;
|
||||||
|
const tmplCols = Object.assign(Object.create(null),{
|
||||||
|
A: 0, B: 1
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
The vtab demonstrated here is a JS-ification of
|
||||||
|
ext/misc/templatevtab.c.
|
||||||
|
*/
|
||||||
|
let throwOnConnect = 1 ? 0 : capi.SQLITE_CANTOPEN
|
||||||
|
/* ^^^ just for testing exception wrapping. Note that sqlite
|
||||||
|
always translates errors from a vtable to a generic
|
||||||
|
SQLITE_ERROR unless it's from xConnect()/xCreate() and that
|
||||||
|
callback sets an error string. */;
|
||||||
|
const modConfig = {
|
||||||
|
/* catchExceptions changes how the methods are wrapped */
|
||||||
|
catchExceptions: false,
|
||||||
|
name: "vtab2test",
|
||||||
|
methods:{
|
||||||
|
xConnect: function(pDb, pAux, argc, argv, ppVtab, pzErr){
|
||||||
|
if(throwOnConnect){
|
||||||
|
sqlite3.SQLite3Error.toss(
|
||||||
|
throwOnConnect,
|
||||||
|
"Throwing a test exception."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const args = wasm.cArgvToJs(argc, argv);
|
||||||
|
console.debug("xCreate/xConnect args:",args);
|
||||||
|
T.assert(args.length>=3);
|
||||||
|
const rc = capi.sqlite3_declare_vtab(
|
||||||
|
pDb, "CREATE TABLE ignored(a,b)"
|
||||||
|
);
|
||||||
|
if(0===rc){
|
||||||
|
const t = vth.xVtab();
|
||||||
|
wasm.setPtrValue(ppVtab, t.pointer);
|
||||||
|
T.assert(t === vth.xVtab(wasm.getPtrValue(ppVtab)));
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
},
|
||||||
|
xDisconnect: function(pVtab){
|
||||||
|
const t = vth.xVtab(pVtab, true);
|
||||||
|
t.dispose();
|
||||||
|
},
|
||||||
|
xOpen: function(pVtab, ppCursor){
|
||||||
|
const t = vth.xVtab(pVtab), c = vth.xCursor();
|
||||||
|
T.assert(t instanceof capi.sqlite3_vtab)
|
||||||
|
.assert(c instanceof capi.sqlite3_vtab_cursor);
|
||||||
|
wasm.setPtrValue(ppCursor, c.pointer);
|
||||||
|
c._rowId = 0;
|
||||||
|
},
|
||||||
|
xClose: function(pCursor){
|
||||||
|
const c = vth.xCursor(pCursor,true);
|
||||||
|
T.assert(c instanceof capi.sqlite3_vtab_cursor)
|
||||||
|
.assert(!vth.xCursor(pCursor));
|
||||||
|
c.dispose();
|
||||||
|
},
|
||||||
|
xNext: function(pCursor){
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
++c._rowId;
|
||||||
|
},
|
||||||
|
xColumn: function(pCursor, pCtx, iCol){
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
switch(iCol){
|
||||||
|
case tmplCols.A:
|
||||||
|
capi.sqlite3_result_int(pCtx, 1000 + c._rowId);
|
||||||
|
break;
|
||||||
|
case tmplCols.B:
|
||||||
|
capi.sqlite3_result_int(pCtx, 2000 + c._rowId);
|
||||||
|
break;
|
||||||
|
default: sqlite3.SQLite3Error.toss("Invalid column id",iCol);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xRowid: function(pCursor, ppRowid64){
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
vth.xRowid(ppRowid64, c._rowId);
|
||||||
|
c.dispose();
|
||||||
|
},
|
||||||
|
xEof: function(pCursor){
|
||||||
|
const c = vth.xCursor(pCursor),
|
||||||
|
rc = c._rowId>=10;
|
||||||
|
c.dispose();
|
||||||
|
return rc;
|
||||||
|
},
|
||||||
|
xFilter: function(pCursor, idxNum, idxCStr,
|
||||||
|
argc, argv/* [sqlite3_value* ...] */){
|
||||||
|
const c = vth.xCursor(pCursor);
|
||||||
|
c._rowId = 0;
|
||||||
|
const list = vth.sqlite3ValuesToJs(argc, argv);
|
||||||
|
T.assert(argc === list.length);
|
||||||
|
c.dispose();
|
||||||
|
},
|
||||||
|
xBestIndex: function(pVtab, pIdxInfo){
|
||||||
|
//const t = vth.xVtab(pVtab);
|
||||||
|
const pii = vth.xIndexInfo(pIdxInfo);
|
||||||
|
pii.$estimatedRows = 10;
|
||||||
|
pii.$estimatedCost = 10.0;
|
||||||
|
pii.dispose();
|
||||||
|
}
|
||||||
|
}/*methods*/
|
||||||
|
};
|
||||||
|
const doEponymous =
|
||||||
|
/* Bug (somewhere): non-eponymous is behaving as is
|
||||||
|
the call to sqlite3_create_module() is missing
|
||||||
|
or failed:
|
||||||
|
|
||||||
|
SQL TRACE #63 create virtual table testvtab2 using vtab2test(arg1, arg2)
|
||||||
|
|
||||||
|
=> sqlite3 result code 1: no such module: vtab2test
|
||||||
|
*/ true;
|
||||||
|
if(doEponymous){
|
||||||
|
warn("Reminder: non-eponymous mode is still not working here.",
|
||||||
|
"Details are in the code comments.");
|
||||||
|
modConfig.methods.xCreate = 0;
|
||||||
|
}else{
|
||||||
|
modConfig.methods.xCreate = (...args)=>0;
|
||||||
|
}
|
||||||
|
const tmplMod = vth.setupModule(modConfig);
|
||||||
|
T.assert(tmplMod instanceof capi.sqlite3_module)
|
||||||
|
.assert(1===tmplMod.$iVersion);
|
||||||
|
if(doEponymous){
|
||||||
|
if(modConfig.methods.xCreate !== 0){
|
||||||
|
T.assert(modConfig.methods.xCreate === modConfig.methods.xConnect)
|
||||||
|
.assert(tmplMod.$xCreate === tmplMod.$xConnect);
|
||||||
|
}else{
|
||||||
|
T.assert(0 === tmplMod.$xCreate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.db.onclose.disposeThese.push(tmplMod);
|
||||||
|
this.db.checkRc(capi.sqlite3_create_module(
|
||||||
|
this.db, modConfig.name, tmplMod, 0
|
||||||
|
));
|
||||||
|
if(!doEponymous){
|
||||||
|
this.db.exec([
|
||||||
|
"create virtual table testvtab2 using ",
|
||||||
|
modConfig.name,
|
||||||
|
"(arg1, arg2)"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
const list = this.db.selectArrays(
|
||||||
|
["SELECT a,b FROM ",
|
||||||
|
(doEponymous ? modConfig.name : "testvtab2"),
|
||||||
|
" where a<9999 and b>1 order by a, b"
|
||||||
|
]/* Query is shaped so that it will ensure that some
|
||||||
|
constraints end up in xBestIndex(). */
|
||||||
|
);
|
||||||
|
T.assert(10===list.length)
|
||||||
|
.assert(1000===list[0][0])
|
||||||
|
.assert(2009===list[list.length-1][1])
|
||||||
|
}
|
||||||
|
})/*custom vtab #2*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
.t('Close db', function(){
|
.t('Close db', function(){
|
||||||
T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
|
T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
|
||||||
wasm.sqlite3_wasm_db_reset(this.db);
|
wasm.sqlite3_wasm_db_reset(this.db);
|
||||||
|
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
|||||||
C Add\saddOnDispose()\smethod\sto\sJaccwabyt\sand\scode-adjacent\sminor\sinternal\scleanups.
|
C Work\son\san\salternate\s(slightly\ssimpler)\sapproach\sto\sbinding\sJS\svtabs.\sNon-eponymous\svtabs\sare\snot\sworking,\sfor\sreasons\sas\syet\sunknown.
|
||||||
D 2022-12-07T03:42:39.134
|
D 2022-12-07T07:22:34.835
|
||||||
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
|
||||||
@ -505,11 +505,11 @@ F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a9578430388
|
|||||||
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
|
F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
|
||||||
F ext/wasm/api/sqlite3-api-glue.js 8fa55af37c9880f94a803f32591dc0304750cc2f750048daf41fe942757bee64
|
F ext/wasm/api/sqlite3-api-glue.js 8fa55af37c9880f94a803f32591dc0304750cc2f750048daf41fe942757bee64
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js 416e6398721a4cbb80ddfa3d7b303216790f1d344efdbbc36239d39abc66aa27
|
F ext/wasm/api/sqlite3-api-oo1.js 416e6398721a4cbb80ddfa3d7b303216790f1d344efdbbc36239d39abc66aa27
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js a7596c30392d9ca8f5b7c14feb4e6788107d1159fd5f90eb26708d653d36c9bc
|
F ext/wasm/api/sqlite3-api-prologue.js 1380e933325c11786b2afc93fc8ff88c2fd1ffeac3e0081da35e5a7317f20e09
|
||||||
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
|
||||||
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 f79dd8d98ef3e0b55c10bb2bee7a3840fa967318e1f577c156aafc34664271d1
|
F ext/wasm/api/sqlite3-opfs-async-proxy.js f79dd8d98ef3e0b55c10bb2bee7a3840fa967318e1f577c156aafc34664271d1
|
||||||
F ext/wasm/api/sqlite3-v-helper.js 2125255f102ab07a2653d74d4931d716b0d27c5a89bebc188ad828de0dd1dcef
|
F ext/wasm/api/sqlite3-v-helper.js c0c56d4fb1272140629ac858297a825e0a8e04005df92c70ef4a4aa75d4d4645
|
||||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 8ec510fee735c646fb18a3b99f0ca5ca461f9e066c43cdc404d7144f12ae6ed6
|
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 8ec510fee735c646fb18a3b99f0ca5ca461f9e066c43cdc404d7144f12ae6ed6
|
||||||
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
|
||||||
F ext/wasm/api/sqlite3-wasm.c 723522a6c2a2463884a83fa1cc7ae5770deaaf0856a1058cc1023b2bfa1c898b
|
F ext/wasm/api/sqlite3-wasm.c 723522a6c2a2463884a83fa1cc7ae5770deaaf0856a1058cc1023b2bfa1c898b
|
||||||
@ -539,7 +539,7 @@ F ext/wasm/fiddle/fiddle.js 974b995119ac443685d7d94d3b3c58c6a36540e9eb3fed7069d5
|
|||||||
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
F ext/wasm/fiddle/index.html 5daf54e8f3d7777cbb1ca4f93affe28858dbfff25841cb4ab81d694efed28ec2
|
||||||
F ext/wasm/index-dist.html c806b6005145b71d64240606e9c6e0bf56878ee8829c66fe7486cebf34b0e6b1
|
F ext/wasm/index-dist.html c806b6005145b71d64240606e9c6e0bf56878ee8829c66fe7486cebf34b0e6b1
|
||||||
F ext/wasm/index.html f151b7c7b5cfdc066567d556acd168e769efd4e982286dc5f849a5ee69ecd0ff
|
F ext/wasm/index.html f151b7c7b5cfdc066567d556acd168e769efd4e982286dc5f849a5ee69ecd0ff
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.js b7efd07ea3927e9d0bc75b3819c6d40bdfb0a03cbc8d93331be16799f35261c3
|
F ext/wasm/jaccwabyt/jaccwabyt.js 06f2ef1ad640c26c593def3d960336e9bb789819b920516480895c38ed5f58fa
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
|
F ext/wasm/jaccwabyt/jaccwabyt.md 37911f00db12cbcca73aa1ed72594430365f30aafae2fa9c886961de74e5e0eb
|
||||||
F ext/wasm/module-symbols.html 980680c8acfa3c8ae6a5aa223512d1b8e78040ced20f8ba2c382129bc73ec028
|
F ext/wasm/module-symbols.html 980680c8acfa3c8ae6a5aa223512d1b8e78040ced20f8ba2c382129bc73ec028
|
||||||
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
F ext/wasm/scratchpad-wasmfs-main.html 20cf6f1a8f368e70d01e8c17200e3eaa90f1c8e1029186d836d14b83845fbe06
|
||||||
@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
|||||||
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
|
||||||
F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
|
F ext/wasm/tester1-worker.html d43f3c131d88f10d00aff3e328fed13c858d674ea2ff1ff90225506137f85aa9
|
||||||
F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
|
F ext/wasm/tester1.c-pp.html d34bef3d48e5cbc1c7c06882ad240fec49bf88f5f65696cc2c72c416933aa406
|
||||||
F ext/wasm/tester1.c-pp.js 8144e0e0f76b14fcd5223fad190bd94a50caf19b2419848b782448cda3ccbc78
|
F ext/wasm/tester1.c-pp.js 661c9461fa104f231ff9e767c89ba1fc4a4af6a61db076772ca634d562afd35d
|
||||||
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
|
F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
|
||||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||||
@ -2067,8 +2067,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 0d77c348039926c24e0fb50a7dc7e4b62895cd201c021f8e29832917e4b8b09f
|
P 6a2723fe3f28dd94328d901e64e1e9ee9a1b2e9eeaed6c54038a5b83c914db78
|
||||||
R c60d510be7a6225cada64eb18dd4b778
|
R 8623f61e63ac3c807b096f8d7028ccc0
|
||||||
U stephan
|
U stephan
|
||||||
Z 9a43ff51fca7fb341935fe3d300c9a6e
|
Z 80cda1da5b829b2851b20f834a26edf8
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
6a2723fe3f28dd94328d901e64e1e9ee9a1b2e9eeaed6c54038a5b83c914db78
|
6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23
|
Reference in New Issue
Block a user