1
0
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:
stephan
2022-12-07 07:22:34 +00:00
parent 30da58c5d6
commit 1eb1b59b89
6 changed files with 659 additions and 301 deletions

View File

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

View File

@ -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()*/);

View File

@ -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:",

View File

@ -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);

View File

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

View File

@ -1 +1 @@
6a2723fe3f28dd94328d901e64e1e9ee9a1b2e9eeaed6c54038a5b83c914db78 6a0fefb93bcccd950df211cf5c2f49660c7b92115dd01b2b508a4ab9e3ab3d23