mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Add a demonstration sqlite3_vtab/module implemented in JS, based on ext/misc/templatevtab.c. Add oo1.selectArrays() and selectObjects().
FossilOrigin-Name: 60482c97e02bc4cafefef281be0cf0bc8c5c53232162829c137f3f7a80cdc534
This commit is contained in:
@ -289,7 +289,7 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js
|
|||||||
sqlite3-api.jses += $(sqlite3-api-build-version.js)
|
sqlite3-api.jses += $(sqlite3-api-build-version.js)
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-v-helper.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js
|
||||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||||
|
|
||||||
|
@ -78,11 +78,10 @@ browser client:
|
|||||||
a Promise-based interface into the Worker #1 API. This is
|
a Promise-based interface into the Worker #1 API. This is
|
||||||
a far user-friendlier way to interface with databases running
|
a far user-friendlier way to interface with databases running
|
||||||
in a Worker thread.
|
in a Worker thread.
|
||||||
- **`sqlite3-vfs-helper.js`**\
|
- **`sqlite3-v-helper.js`**\
|
||||||
This internal-use-only file installs `sqlite3.VfsHelper` for use by
|
Installs `sqlite3.VfsHelper` and `sqlite3.VtabHelper` for use by
|
||||||
`sqlite3-*.js` files which create `sqlite3_vfs` implementations.
|
downstream code which creates `sqlite3_vfs` and `sqlite3_module`
|
||||||
`sqlite3.VfsHelper` gets removed from the the `sqlite3` object after
|
implementations.
|
||||||
the library is finished initializing.
|
|
||||||
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
- **`sqlite3-vfs-opfs.c-pp.js`**\
|
||||||
is an sqlite3 VFS implementation which supports Google Chrome's
|
is an sqlite3 VFS implementation which supports Google Chrome's
|
||||||
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
Origin-Private FileSystem (OPFS) as a storage layer to provide
|
||||||
|
@ -473,6 +473,15 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Internal impl of the DB.selectArrays() and
|
||||||
|
selectObjects() methods.
|
||||||
|
*/
|
||||||
|
const __selectAll =
|
||||||
|
(db, sql, bind, rowMode)=>db.exec({
|
||||||
|
sql, bind, rowMode, returnValue: 'resultRows'
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Expects to be given a DB instance or an `sqlite3*` pointer (may
|
Expects to be given a DB instance or an `sqlite3*` pointer (may
|
||||||
be null) and an sqlite3 API result code. If the result code is
|
be null) and an sqlite3 API result code. If the result code is
|
||||||
@ -1098,6 +1107,26 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|||||||
return __selectFirstRow(this, sql, bind, {});
|
return __selectFirstRow(this, sql, bind, {});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Runs the given SQL and returns an array of all results, with
|
||||||
|
each row represented as an array, as per the 'array' `rowMode`
|
||||||
|
option to `exec()`. An empty result set resolves
|
||||||
|
to an empty array. The second argument, if any, is treated as
|
||||||
|
the 'bind' option to a call to exec().
|
||||||
|
*/
|
||||||
|
selectArrays: function(sql,bind){
|
||||||
|
return __selectAll(this, sql, bind, 'array');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Works identically to selectArrays() except that each value
|
||||||
|
in the returned array is an object, as per the 'object' `rowMode`
|
||||||
|
option to `exec()`.
|
||||||
|
*/
|
||||||
|
selectObjects: function(sql,bind){
|
||||||
|
return __selectAll(this, sql, bind, 'object');
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the number of currently-opened Stmt handles for this db
|
Returns the number of currently-opened Stmt handles for this db
|
||||||
handle, or 0 if this DB instance is closed.
|
handle, or 0 if this DB instance is closed.
|
||||||
|
@ -1642,7 +1642,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
|||||||
some initializers. Retain them when running in test mode
|
some initializers. Retain them when running in test mode
|
||||||
so that we can add tests for them. */
|
so that we can add tests for them. */
|
||||||
delete sqlite3.util;
|
delete sqlite3.util;
|
||||||
delete sqlite3.VfsHelper;
|
|
||||||
delete sqlite3.StructBinder;
|
delete sqlite3.StructBinder;
|
||||||
}
|
}
|
||||||
return sqlite3;
|
return sqlite3;
|
||||||
|
392
ext/wasm/api/sqlite3-v-helper.js
Normal file
392
ext/wasm/api/sqlite3-v-helper.js
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
/*
|
||||||
|
** 2022-11-30
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of a
|
||||||
|
** legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** * May you do good and not evil.
|
||||||
|
** * May you find forgiveness for yourself and forgive others.
|
||||||
|
** * May you share freely, never taking more than you give.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
This file installs sqlite3.VfsHelper, and object which exists to
|
||||||
|
assist in the creation of JavaScript implementations of sqlite3_vfs,
|
||||||
|
along with its virtual table counterpart, sqlite3.VtabHelper.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||||
|
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss;
|
||||||
|
const vh = Object.create(null), vt = Object.create(null);
|
||||||
|
|
||||||
|
sqlite3.VfsHelper = vh;
|
||||||
|
sqlite3.VtabHelper = vt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Installs a StructBinder-bound function pointer member of the
|
||||||
|
given name and function in the given StructType target object.
|
||||||
|
It creates a WASM proxy for the given function and arranges for
|
||||||
|
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,
|
||||||
|
name does not map to a struct-bound member, etc.
|
||||||
|
|
||||||
|
Returns a proxy for this function which is bound to tgt and takes
|
||||||
|
2 args (name,func). That function returns the same thing,
|
||||||
|
permitting calls to be chained.
|
||||||
|
|
||||||
|
If called with only 1 arg, it has no side effects but returns a
|
||||||
|
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
|
||||||
|
exceptions into result codes, the installed functions do no
|
||||||
|
automatic catching of exceptions. It is critical, to avoid
|
||||||
|
undefined behavior in the C layer, that methods mapped via
|
||||||
|
this function do not throw. The exception, as it were, to that
|
||||||
|
rule is...
|
||||||
|
|
||||||
|
If applyArgcCheck is true then each method gets wrapped in a
|
||||||
|
proxy which asserts that it is passed the expected number of
|
||||||
|
arguments, throwing if the argument count does not match
|
||||||
|
expectations. That is only intended for dev-time usage for sanity
|
||||||
|
checking, and will leave the C environment in an undefined
|
||||||
|
state. For non-dev-time use, it is a given that the C API will
|
||||||
|
never call one of the generated function wrappers with the wrong
|
||||||
|
argument count.
|
||||||
|
*/
|
||||||
|
vh.installMethod = vt.installMethod = function callee(
|
||||||
|
tgt, name, func, applyArgcCheck = callee.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
||||||
|
toss("Usage error: target object is-not-a StructType.");
|
||||||
|
}
|
||||||
|
if(1===arguments.length){
|
||||||
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
|
}
|
||||||
|
if(!callee.argcProxy){
|
||||||
|
callee.argcProxy = function(tgt, funcName, func,sig){
|
||||||
|
return function(...args){
|
||||||
|
if(func.length!==arguments.length){
|
||||||
|
toss("Argument mismatch for",
|
||||||
|
tgt.structInfo.name+"::"+funcName
|
||||||
|
+": Native signature is:",sig);
|
||||||
|
}
|
||||||
|
return func.apply(this, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* An ondispose() callback for use with
|
||||||
|
sqlite3.StructBinder-created types. */
|
||||||
|
callee.removeFuncList = function(){
|
||||||
|
if(this.ondispose.__removeFuncList){
|
||||||
|
this.ondispose.__removeFuncList.forEach(
|
||||||
|
(v,ndx)=>{
|
||||||
|
if('number'===typeof v){
|
||||||
|
try{wasm.uninstallFunction(v)}
|
||||||
|
catch(e){/*ignore*/}
|
||||||
|
}
|
||||||
|
/* else it's a descriptive label for the next number in
|
||||||
|
the list. */
|
||||||
|
}
|
||||||
|
);
|
||||||
|
delete this.ondispose.__removeFuncList;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}/*static init*/
|
||||||
|
const sigN = tgt.memberSignature(name);
|
||||||
|
if(sigN.length<2){
|
||||||
|
toss("Member",name," is not a function pointer. Signature =",sigN);
|
||||||
|
}
|
||||||
|
const memKey = tgt.memberKey(name);
|
||||||
|
const fProxy = applyArgcCheck
|
||||||
|
/** This middle-man proxy is only for use during development, to
|
||||||
|
confirm that we always pass the proper number of
|
||||||
|
arguments. We know that the C-level code will always use the
|
||||||
|
correct argument count. */
|
||||||
|
? callee.argcProxy(tgt, memKey, func, sigN)
|
||||||
|
: func;
|
||||||
|
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
||||||
|
tgt[memKey] = pFunc;
|
||||||
|
if(!tgt.ondispose) tgt.ondispose = [];
|
||||||
|
if(!tgt.ondispose.__removeFuncList){
|
||||||
|
tgt.ondispose.push('ondispose.__removeFuncList handler',
|
||||||
|
callee.removeFuncList);
|
||||||
|
tgt.ondispose.__removeFuncList = [];
|
||||||
|
}
|
||||||
|
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
||||||
|
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
||||||
|
}/*installMethod*/;
|
||||||
|
vh.installMethod.installMethodArgcCheck = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Installs methods into the given StructType-type instance. Each
|
||||||
|
entry in the given methods object must map to a known member of
|
||||||
|
the given StructType, else an exception will be triggered. See
|
||||||
|
installMethod() for more details, including the semantics of the
|
||||||
|
3rd argument.
|
||||||
|
|
||||||
|
As an exception to the above, if any two or more methods in the
|
||||||
|
2nd argument are the exact same function, installMethod() is
|
||||||
|
_not_ called for the 2nd and subsequent instances, and instead
|
||||||
|
those instances get assigned the same method pointer which is
|
||||||
|
created for the first instance. This optimization is primarily to
|
||||||
|
accommodate special handling of sqlite3_module::xConnect and
|
||||||
|
xCreate methods.
|
||||||
|
|
||||||
|
On success, returns this object. Throws on error.
|
||||||
|
*/
|
||||||
|
vh.installMethods = vt.installMethods = function(
|
||||||
|
structType, methods, applyArgcCheck = vh.installMethod.installMethodArgcCheck
|
||||||
|
){
|
||||||
|
const seen = new Map /* map of <Function, memberName> */;
|
||||||
|
for(const k of Object.keys(methods)){
|
||||||
|
const m = methods[k];
|
||||||
|
const prior = seen.get(m);
|
||||||
|
if(prior){
|
||||||
|
const mkey = structType.memberKey(k);
|
||||||
|
structType[mkey] = structType[structType.memberKey(prior)];
|
||||||
|
}else{
|
||||||
|
vh.installMethod(structType, k, m, applyArgcCheck);
|
||||||
|
seen.set(m, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Uses sqlite3_vfs_register() to register the
|
||||||
|
sqlite3.capi.sqlite3_vfs-type vfs, which must have already been
|
||||||
|
filled out properly. If the 2nd argument is truthy, the VFS is
|
||||||
|
registered as the default VFS, else it is not.
|
||||||
|
|
||||||
|
On success, returns this object. Throws on error.
|
||||||
|
*/
|
||||||
|
vh.registerVfs = function(vfs, asDefault=false){
|
||||||
|
if(!(vfs instanceof sqlite3.capi.sqlite3_vfs)){
|
||||||
|
toss("Expecting a sqlite3_vfs-type argument.");
|
||||||
|
}
|
||||||
|
const rc = capi.sqlite3_vfs_register(vfs.pointer, asDefault ? 1 : 0);
|
||||||
|
if(rc){
|
||||||
|
toss("sqlite3_vfs_register(",vfs,") failed with rc",rc);
|
||||||
|
}
|
||||||
|
if(vfs.pointer !== capi.sqlite3_vfs_find(vfs.$zName)){
|
||||||
|
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
||||||
|
vfs);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
A wrapper for installMethods() or registerVfs() to reduce
|
||||||
|
installation of a VFS and/or its I/O methods to a single
|
||||||
|
call.
|
||||||
|
|
||||||
|
Accepts an object which contains the properties "io" and/or
|
||||||
|
"vfs", each of which is itself an object with following properties:
|
||||||
|
|
||||||
|
- `struct`: an sqlite3.StructType-type struct. This must be a
|
||||||
|
populated (except for the methods) object of type
|
||||||
|
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
||||||
|
"vfs" entry).
|
||||||
|
|
||||||
|
- `methods`: an object mapping sqlite3_io_methods method names
|
||||||
|
(e.g. 'xClose') to JS implementations of those methods. The JS
|
||||||
|
implementations must be call-compatible with their native
|
||||||
|
counterparts.
|
||||||
|
|
||||||
|
For each of those object, this function passes its (`struct`,
|
||||||
|
`methods`, (optional) `applyArgcCheck`) properties to
|
||||||
|
this.installMethods().
|
||||||
|
|
||||||
|
If the `vfs` entry is set then:
|
||||||
|
|
||||||
|
- Its `struct` property is passed to this.registerVfs(). The
|
||||||
|
`vfs` entry may optionally have an `asDefault` property, which
|
||||||
|
gets passed as the 2nd argument to registerVfs().
|
||||||
|
|
||||||
|
- If `struct.$zName` is falsy and the entry has a string-type
|
||||||
|
`name` property, `struct.$zName` is set to the C-string form of
|
||||||
|
that `name` value before registerVfs() is called.
|
||||||
|
|
||||||
|
On success returns this object. Throws on error.
|
||||||
|
*/
|
||||||
|
vh.installVfs = function(opt){
|
||||||
|
let count = 0;
|
||||||
|
const propList = ['io','vfs'];
|
||||||
|
for(const key of propList){
|
||||||
|
const o = opt[key];
|
||||||
|
if(o){
|
||||||
|
++count;
|
||||||
|
this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
||||||
|
if('vfs'===key){
|
||||||
|
if(!o.struct.$zName && 'string'===typeof o.name){
|
||||||
|
o.struct.$zName = wasm.allocCString(o.name);
|
||||||
|
/* Note that we leak that C-string. */
|
||||||
|
}
|
||||||
|
this.registerVfs(o.struct, !!o.asDefault);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!count) toss("Misuse: installVfs() options object requires at least",
|
||||||
|
"one of:", propList);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Expects to be passed the (argc,argv) arguments of
|
||||||
|
sqlite3_module::xFilter(), or an equivalent API. This function
|
||||||
|
transforms the arguments (an array of (sqlite3_value*)) into a JS
|
||||||
|
array of equivalent JS values. It uses the same type conversions
|
||||||
|
as sqlite3_create_function_v2() and friends. Throws on error,
|
||||||
|
e.g. if it cannot figure out a sensible data conversion.
|
||||||
|
*/
|
||||||
|
vt.sqlite3ValuesToJs = capi.sqlite3_create_function_v2.udfConvertArgs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Factory function for xyz2js() impls.
|
||||||
|
*/
|
||||||
|
const __v2jsFactory = function(structType){
|
||||||
|
return function(ptr,remove=false){
|
||||||
|
if(0===arguments.length) ptr = new structType;
|
||||||
|
if(ptr instanceof structType){
|
||||||
|
//T.assert(!this.has(ptr.pointer));
|
||||||
|
this.set(ptr.pointer, ptr);
|
||||||
|
return ptr;
|
||||||
|
}else if(!wasm.isPtr(ptr)){
|
||||||
|
sqlite3.SQLite3Error.toss("Invalid argument to v2jsFactory");
|
||||||
|
}
|
||||||
|
let rc = this.get(ptr);
|
||||||
|
if(remove) this.delete(ptr);
|
||||||
|
/*arguable else if(!rc){
|
||||||
|
rc = new structType(ptr);
|
||||||
|
this.set(ptr, rc);
|
||||||
|
}*/
|
||||||
|
return rc;
|
||||||
|
}.bind(new Map);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
EXPERIMENTAL. DO NOT USE IN CLIENT CODE.
|
||||||
|
|
||||||
|
Has 3 distinct uses:
|
||||||
|
|
||||||
|
- vtab2js() instantiates a new capi.sqlite3_vtab instance, maps
|
||||||
|
its pointer for later by-pointer lookup, and returns that
|
||||||
|
object. This is intended to be called from
|
||||||
|
sqlite3_module::xConnect() or xCreate() implementations.
|
||||||
|
|
||||||
|
- vtab2js(pVtab) accepts a WASM pointer to a C-level
|
||||||
|
(sqlite3_vtab*) instance and returns the capi.sqlite3_vtab
|
||||||
|
object created by the first form of this function, or undefined
|
||||||
|
if that form has not been used. This is intended to be called
|
||||||
|
from sqlite3_module methods which take a (sqlite3_vtab*) pointer
|
||||||
|
_except_ for xDisconnect(), in which case use...
|
||||||
|
|
||||||
|
- vtab2js(pVtab,true) as for the previous form, but removes the
|
||||||
|
pointer-to-object mapping before returning. The caller must
|
||||||
|
call dispose() on the returned object. This is intended to be
|
||||||
|
called from sqlite3_module::xDisconnect() implementations or
|
||||||
|
in error handling of a failed xCreate() or xConnect().
|
||||||
|
*/
|
||||||
|
vt.vtab2js = __v2jsFactory(capi.sqlite3_vtab);
|
||||||
|
|
||||||
|
/**
|
||||||
|
EXPERIMENTAL. DO NOT USE IN CLIENT CODE.
|
||||||
|
|
||||||
|
Works identically to vtab2js() except that it deals with
|
||||||
|
sqlite3_cursor objects and pointers instead of sqlite3_vtab.
|
||||||
|
|
||||||
|
- vcur2js() is intended to be called from sqlite3_module::xOpen()
|
||||||
|
|
||||||
|
- vcur2js(pCursor) is intended to be called from all sqlite3_module
|
||||||
|
methods which take a (sqlite3_vtab_cursor*) _except_ for
|
||||||
|
xClose(), in which case use...
|
||||||
|
|
||||||
|
- vcur2js(pCursor, true) will remove the m apping of pCursor to a
|
||||||
|
capi.sqlite3_vtab_cursor object and return that object. The
|
||||||
|
caller must call dispose() on the returned object. This is
|
||||||
|
intended to be called form xClose() or in error handling of a
|
||||||
|
failed xOpen().
|
||||||
|
*/
|
||||||
|
vt.vcur2js = __v2jsFactory(capi.sqlite3_vtab_cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given an error object, this function returns
|
||||||
|
sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||||
|
sqlite3.WasmAllocError), else it returns its
|
||||||
|
second argument. Its intended usage is in the methods
|
||||||
|
of a sqlite3_vfs or sqlite3_module:
|
||||||
|
|
||||||
|
```
|
||||||
|
try{
|
||||||
|
let rc = ...
|
||||||
|
return rc;
|
||||||
|
}catch(e){
|
||||||
|
return sqlite3.VtabHelper.exceptionToRc(e, sqlite3.capi.SQLITE_XYZ);
|
||||||
|
// where SQLITE_XYZ is some call-appropriate result code.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
/**vh.exceptionToRc = vt.exceptionToRc =
|
||||||
|
(e, defaultRc=capi.SQLITE_ERROR)=>(
|
||||||
|
(e instanceof sqlite3.WasmAllocError)
|
||||||
|
? capi.SQLITE_NOMEM
|
||||||
|
: defaultRc
|
||||||
|
);*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Given an sqlite3_module method name and error object, this
|
||||||
|
function returns sqlite3.capi.SQLITE_NOMEM if (e instanceof
|
||||||
|
sqlite3.WasmAllocError), else it returns its second argument. Its
|
||||||
|
intended usage is in the methods of a sqlite3_vfs or
|
||||||
|
sqlite3_module:
|
||||||
|
|
||||||
|
```
|
||||||
|
try{
|
||||||
|
let rc = ...
|
||||||
|
return rc;
|
||||||
|
}catch(e){
|
||||||
|
return sqlite3.VtabHelper.xMethodError(
|
||||||
|
'xColumn', e, sqlite3.capi.SQLITE_XYZ);
|
||||||
|
// where SQLITE_XYZ is some call-appropriate result code
|
||||||
|
// defaulting to SQLITE_ERROR.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If xMethodError.errorReporter is a function, it is called in
|
||||||
|
order to report the error, else the error is not reported.
|
||||||
|
If that function throws, that exception is ignored.
|
||||||
|
*/
|
||||||
|
vt.xMethodError = function f(methodName, err, defaultRc=capi.SQLITE_ERROR){
|
||||||
|
if(f.errorReporter instanceof Function){
|
||||||
|
try{f.errorReporter("sqlite3_module::"+methodName+"(): "+err.message);}
|
||||||
|
catch(e){/*ignored*/}
|
||||||
|
}
|
||||||
|
return (err instanceof sqlite3.WasmAllocError)
|
||||||
|
? capi.SQLITE_NOMEM
|
||||||
|
: defaultRc;
|
||||||
|
};
|
||||||
|
vt.xMethodError.errorReporter = 1 ? console.error.bind(console) : false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
"The problem" with this is that it introduces an outer function with
|
||||||
|
a different arity than the passed-in method callback. That means we
|
||||||
|
cannot do argc validation on these. Additionally, some methods (namely
|
||||||
|
xConnect) may have call-specific error handling. It would be a shame to
|
||||||
|
hard-coded that per-method support in this function.
|
||||||
|
*/
|
||||||
|
/** vt.methodCatcher = function(methodName, method, defaultErrRc=capi.SQLITE_ERROR){
|
||||||
|
return function(...args){
|
||||||
|
try { method(...args); }
|
||||||
|
}catch(e){ return vt.xMethodError(methodName, e, defaultRc) }
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
A helper for sqlite3_vtab::xRow() implementations. It must be
|
||||||
|
passed that function's 2nd argument and the value for that
|
||||||
|
pointer. Returns the same as wasm.setMemValue() and will throw
|
||||||
|
if the 1st or 2nd arguments are invalid for that function.
|
||||||
|
*/
|
||||||
|
vt.setRowId = (ppRowid64, value)=>wasm.setMemValue(ppRowid64, value, 'i64');
|
||||||
|
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
@ -1,221 +0,0 @@
|
|||||||
/*
|
|
||||||
** 2022-11-30
|
|
||||||
**
|
|
||||||
** The author disclaims copyright to this source code. In place of a
|
|
||||||
** legal notice, here is a blessing:
|
|
||||||
**
|
|
||||||
** * May you do good and not evil.
|
|
||||||
** * May you find forgiveness for yourself and forgive others.
|
|
||||||
** * May you share freely, never taking more than you give.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
This file installs sqlite.VfsHelper, an object which exists
|
|
||||||
to assist in the creation of JavaScript implementations of
|
|
||||||
sqlite3_vfs. It is NOT part of the public API, and is an
|
|
||||||
internal implemenation detail for use in this project's
|
|
||||||
own development of VFSes. It may be exposed to clients
|
|
||||||
at some point, provided there is value in doing so.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
|
||||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss;
|
|
||||||
const vh = Object.create(null);
|
|
||||||
|
|
||||||
/**
|
|
||||||
Does nothing more than holds a permanent reference to each
|
|
||||||
argument. This is useful in some cases to ensure that, e.g., a
|
|
||||||
custom sqlite3_io_methods instance does not get
|
|
||||||
garbage-collected.
|
|
||||||
|
|
||||||
Returns this object.
|
|
||||||
*/
|
|
||||||
vh.holdReference = function(...args){
|
|
||||||
for(const v of args) this.refs.add(v);
|
|
||||||
return vh;
|
|
||||||
}.bind({refs: new Set});
|
|
||||||
|
|
||||||
/**
|
|
||||||
Installs a StructBinder-bound function pointer member of the
|
|
||||||
given name and function in the given StructType target object.
|
|
||||||
It creates a WASM proxy for the given function and arranges for
|
|
||||||
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,
|
|
||||||
name does not map to a struct-bound member, etc.
|
|
||||||
|
|
||||||
If applyArgcCheck is true then each method gets wrapped in a
|
|
||||||
proxy which asserts that it is passed the expected number of
|
|
||||||
arguments, throwing if the argument count does not match
|
|
||||||
expectations. That is only recommended for dev-time usage for
|
|
||||||
sanity checking. Once a VFS implementation is known to be
|
|
||||||
working, it is a given that the C API will never call it with the
|
|
||||||
wrong argument count.
|
|
||||||
|
|
||||||
Returns a proxy for this function which is bound to tgt and takes
|
|
||||||
2 args (name,func). That function returns the same thing,
|
|
||||||
permitting calls to be chained.
|
|
||||||
|
|
||||||
If called with only 1 arg, it has no side effects but returns a
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
vh.installMethod = function callee(tgt, name, func,
|
|
||||||
applyArgcCheck=callee.installMethodArgcCheck){
|
|
||||||
if(!(tgt instanceof sqlite3.StructBinder.StructType)){
|
|
||||||
toss("Usage error: target object is-not-a StructType.");
|
|
||||||
}
|
|
||||||
if(1===arguments.length){
|
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
|
||||||
}
|
|
||||||
if(!callee.argcProxy){
|
|
||||||
callee.argcProxy = function(func,sig){
|
|
||||||
return function(...args){
|
|
||||||
if(func.length!==arguments.length){
|
|
||||||
toss("Argument mismatch. Native signature is:",sig);
|
|
||||||
}
|
|
||||||
return func.apply(this, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/* An ondispose() callback for use with
|
|
||||||
sqlite3.StructBinder-created types. */
|
|
||||||
callee.removeFuncList = function(){
|
|
||||||
if(this.ondispose.__removeFuncList){
|
|
||||||
this.ondispose.__removeFuncList.forEach(
|
|
||||||
(v,ndx)=>{
|
|
||||||
if('number'===typeof v){
|
|
||||||
try{wasm.uninstallFunction(v)}
|
|
||||||
catch(e){/*ignore*/}
|
|
||||||
}
|
|
||||||
/* else it's a descriptive label for the next number in
|
|
||||||
the list. */
|
|
||||||
}
|
|
||||||
);
|
|
||||||
delete this.ondispose.__removeFuncList;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}/*static init*/
|
|
||||||
const sigN = tgt.memberSignature(name);
|
|
||||||
if(sigN.length<2){
|
|
||||||
toss("Member",name," is not a function pointer. Signature =",sigN);
|
|
||||||
}
|
|
||||||
const memKey = tgt.memberKey(name);
|
|
||||||
const fProxy = applyArgcCheck
|
|
||||||
/** This middle-man proxy is only for use during development, to
|
|
||||||
confirm that we always pass the proper number of
|
|
||||||
arguments. We know that the C-level code will always use the
|
|
||||||
correct argument count. */
|
|
||||||
? callee.argcProxy(func, sigN)
|
|
||||||
: func;
|
|
||||||
const pFunc = wasm.installFunction(fProxy, tgt.memberSignature(name, true));
|
|
||||||
tgt[memKey] = pFunc;
|
|
||||||
if(!tgt.ondispose) tgt.ondispose = [];
|
|
||||||
if(!tgt.ondispose.__removeFuncList){
|
|
||||||
tgt.ondispose.push('ondispose.__removeFuncList handler',
|
|
||||||
callee.removeFuncList);
|
|
||||||
tgt.ondispose.__removeFuncList = [];
|
|
||||||
}
|
|
||||||
tgt.ondispose.__removeFuncList.push(memKey, pFunc);
|
|
||||||
return (n,f)=>callee(tgt, n, f, applyArgcCheck);
|
|
||||||
}/*installMethod*/;
|
|
||||||
vh.installMethod.installMethodArgcCheck = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Installs methods into the given StructType-type object. Each
|
|
||||||
entry in the given methods object must map to a known member of
|
|
||||||
the given StructType, else an exception will be triggered.
|
|
||||||
See installMethod() for more details, including the semantics
|
|
||||||
of the 3rd argument.
|
|
||||||
|
|
||||||
On success, passes its first argument to holdRefence() and
|
|
||||||
returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.installMethods = function(structType, methods,
|
|
||||||
applyArgcCheck=vh.installMethod.installMethodArgcCheck){
|
|
||||||
for(const k of Object.keys(methods)){
|
|
||||||
vh.installMethod(structType, k, methods[k], applyArgcCheck);
|
|
||||||
}
|
|
||||||
return vh.holdReference(structType);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Uses sqlite3_vfs_register() to register the
|
|
||||||
sqlite3.capi.sqlite3_vfs-type vfs, which must have already been
|
|
||||||
filled out properly. If the 2nd argument is truthy, the VFS is
|
|
||||||
registered as the default VFS, else it is not.
|
|
||||||
|
|
||||||
On success, passes its first argument to this.holdReference() and
|
|
||||||
returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.registerVfs = function(vfs, asDefault=false){
|
|
||||||
if(!(vfs instanceof sqlite3.capi.sqlite3_vfs)){
|
|
||||||
toss("Expecting a sqlite3_vfs-type argument.");
|
|
||||||
}
|
|
||||||
const rc = capi.sqlite3_vfs_register(vfs.pointer, asDefault ? 1 : 0);
|
|
||||||
if(rc){
|
|
||||||
toss("sqlite3_vfs_register(",vfs,") failed with rc",rc);
|
|
||||||
}
|
|
||||||
if(vfs.pointer !== capi.sqlite3_vfs_find(vfs.$zName)){
|
|
||||||
toss("BUG: sqlite3_vfs_find(vfs.$zName) failed for just-installed VFS",
|
|
||||||
vfs);
|
|
||||||
}
|
|
||||||
return vh.holdReference(vfs);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
A wrapper for installMethods() or registerVfs() to reduce
|
|
||||||
installation of a VFS and/or its I/O methods to a single
|
|
||||||
call.
|
|
||||||
|
|
||||||
Accepts an object which contains the properties "io" and/or
|
|
||||||
"vfs", each of which is itself an object with following properties:
|
|
||||||
|
|
||||||
- `struct`: an sqlite3.StructType-type struct. This must be a
|
|
||||||
populated (except for the methods) object of type
|
|
||||||
sqlite3_io_methods (for the "io" entry) or sqlite3_vfs (for the
|
|
||||||
"vfs" entry).
|
|
||||||
|
|
||||||
- `methods`: an object mapping sqlite3_io_methods method names
|
|
||||||
(e.g. 'xClose') to JS implementations of those methods.
|
|
||||||
|
|
||||||
For each of those object, this function passes its (`struct`,
|
|
||||||
`methods`, (optional) `applyArgcCheck`) properties to
|
|
||||||
this.installMethods().
|
|
||||||
|
|
||||||
If the `vfs` entry is set then:
|
|
||||||
|
|
||||||
- Its `struct` property is passed to this.registerVfs(). The
|
|
||||||
`vfs` entry may optionally have an `asDefault` property, which
|
|
||||||
gets passed as the 2nd argument to registerVfs().
|
|
||||||
|
|
||||||
- If `struct.$zName` is falsy and the entry has a string-type
|
|
||||||
`name` property, `struct.$zName` is set to the C-string form of
|
|
||||||
that `name` value before registerVfs() is called.
|
|
||||||
|
|
||||||
On success returns this object. Throws on error.
|
|
||||||
*/
|
|
||||||
vh.installVfs = function(opt){
|
|
||||||
let count = 0;
|
|
||||||
const propList = ['io','vfs'];
|
|
||||||
for(const key of propList){
|
|
||||||
const o = opt[key];
|
|
||||||
if(o){
|
|
||||||
++count;
|
|
||||||
this.installMethods(o.struct, o.methods, !!o.applyArgcCheck);
|
|
||||||
if('vfs'===key){
|
|
||||||
if(!o.struct.$zName && 'string'===typeof o.name){
|
|
||||||
o.struct.$zName = wasm.allocCString(o.name);
|
|
||||||
/* Note that we leak that C-string. */
|
|
||||||
}
|
|
||||||
this.registerVfs(o.struct, !!o.asDefault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!count) toss("Misuse: installVfs() options object requires at least",
|
|
||||||
"one of:", propList);
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
sqlite3.VfsHelper = vh;
|
|
||||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
|
@ -231,6 +231,7 @@ self.Jaccwabyt = function StructBinderFactory(config){
|
|||||||
obj.ondispose.forEach(function(x){
|
obj.ondispose.forEach(function(x){
|
||||||
try{
|
try{
|
||||||
if(x instanceof Function) x.call(obj);
|
if(x instanceof Function) x.call(obj);
|
||||||
|
else if(x instanceof StructType) x.dispose();
|
||||||
else if('number' === typeof x) dealloc(x);
|
else if('number' === typeof x) dealloc(x);
|
||||||
// else ignore. Strings are permitted to annotate entries
|
// else ignore. Strings are permitted to annotate entries
|
||||||
// to assist in debugging.
|
// to assist in debugging.
|
||||||
|
@ -426,7 +426,8 @@ simply passing a pointer to the constructor. For example:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
const m = new MyStruct( functionReturningASharedPtr() );
|
const m = new MyStruct( functionReturningASharedPtr() );
|
||||||
// calling m.dispose() will _not_ free the wrapped C-side instance.
|
// calling m.dispose() will _not_ free the wrapped C-side instance
|
||||||
|
// but will trigger any ondispose handler.
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that we have struct instances, there are a number of things we
|
Now that we have struct instances, there are a number of things we
|
||||||
|
@ -419,6 +419,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
T.g('C/WASM Utilities')
|
T.g('C/WASM Utilities')
|
||||||
.t('sqlite3.wasm namespace', function(sqlite3){
|
.t('sqlite3.wasm namespace', function(sqlite3){
|
||||||
|
// TODO: break this into smaller individual test functions.
|
||||||
const w = wasm;
|
const w = wasm;
|
||||||
const chr = (x)=>x.charCodeAt(0);
|
const chr = (x)=>x.charCodeAt(0);
|
||||||
//log("heap getters...");
|
//log("heap getters...");
|
||||||
@ -974,7 +975,7 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
.t('Create db', function(sqlite3){
|
.t('Create db', function(sqlite3){
|
||||||
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);
|
const db = this.db = new sqlite3.oo1.DB(dbFile, 0 ? 'ct' : 'c');
|
||||||
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))
|
||||||
@ -1197,6 +1198,29 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
rc = db.selectArray('select a, b from t where b=-1');
|
rc = db.selectArray('select a, b from t where b=-1');
|
||||||
T.assert(undefined === rc);
|
T.assert(undefined === rc);
|
||||||
})
|
})
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
.t('selectArrays/Objects()', function(sqlite3){
|
||||||
|
const db = this.db;
|
||||||
|
const sql = 'select a, b from t where a=? or b=? order by a';
|
||||||
|
let rc = db.selectArrays(sql, [1, 4]);
|
||||||
|
T.assert(Array.isArray(rc))
|
||||||
|
.assert(2===rc.length)
|
||||||
|
.assert(2===rc[0].length)
|
||||||
|
.assert(1===rc[0][0])
|
||||||
|
.assert(2===rc[0][1])
|
||||||
|
.assert(3===rc[1][0])
|
||||||
|
.assert(4===rc[1][1])
|
||||||
|
rc = db.selectArrays(sql, [99,99]);
|
||||||
|
T.assert(Array.isArray(rc)).assert(0===rc.length);
|
||||||
|
rc = db.selectObjects(sql, [1,4]);
|
||||||
|
T.assert(Array.isArray(rc))
|
||||||
|
.assert(2===rc.length)
|
||||||
|
.assert('object' === typeof rc[1])
|
||||||
|
.assert(1===rc[0].a)
|
||||||
|
.assert(2===rc[0].b)
|
||||||
|
.assert(3===rc[1].a)
|
||||||
|
.assert(4===rc[1].b);
|
||||||
|
})
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
.t({
|
.t({
|
||||||
@ -1503,6 +1527,173 @@ 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 rc = capi.sqlite3_declare_vtab(
|
||||||
|
pDb, "CREATE TABLE ignored(a,b)"
|
||||||
|
);
|
||||||
|
if(0===rc){
|
||||||
|
const t = vth.vtab2js();
|
||||||
|
wasm.setPtrValue(ppVtab, t.pointer);
|
||||||
|
T.assert(t === vth.vtab2js(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.vtab2js(pVtab, true);
|
||||||
|
t.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xDisconnect',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xOpen: function(pVtab, ppCursor){
|
||||||
|
try{
|
||||||
|
const t = vth.vtab2js(pVtab), c = vth.vcur2js();
|
||||||
|
T.assert(t instanceof capi.sqlite3_vtab);
|
||||||
|
T.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.vcur2js(pCursor,true);
|
||||||
|
T.assert(c instanceof capi.sqlite3_vtab_cursor)
|
||||||
|
.assert(!vth.vcur2js(pCursor));
|
||||||
|
c.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xClose',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xNext: function(pCursor){
|
||||||
|
try{
|
||||||
|
const c = vth.vcur2js(pCursor);
|
||||||
|
++c._rowId;
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xNext',e);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
xColumn: function(pCursor, pCtx, iCol){
|
||||||
|
try{
|
||||||
|
const c = vth.vcur2js(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.vcur2js(pCursor);
|
||||||
|
vth.setRowId(ppRowid64, c._rowId);
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xRowid',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xEof: function(pCursor){
|
||||||
|
const c = vth.vcur2js(pCursor);
|
||||||
|
return c._rowId>=10;
|
||||||
|
},
|
||||||
|
xFilter: function(pCursor, idxNum, idxCStr,
|
||||||
|
argc, argv/* [sqlite3_value* ...] */){
|
||||||
|
try{
|
||||||
|
const c = vth.vcur2js(pCursor);
|
||||||
|
c._rowId = 0;
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xFilter',e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xBestIndex: function(pVtab, pIdxInfo){
|
||||||
|
try{
|
||||||
|
const t = vth.vtab2js(pVtab);
|
||||||
|
const pii = new capi.sqlite3_index_info(pIdxInfo);
|
||||||
|
pii.$estimatedRows = 10;
|
||||||
|
pii.$estimatedCost = 10.0;
|
||||||
|
pii.dispose();
|
||||||
|
return 0;
|
||||||
|
}catch(e){
|
||||||
|
return vth.xMethodError('xBestIndex',e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
Problem to resolve: 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 === 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 order by a"
|
||||||
|
);
|
||||||
|
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 (if compiled in)',
|
name: 'C-side WASM tests (if compiled in)',
|
||||||
@ -1590,8 +1781,8 @@ self.sqlite3InitModule = sqlite3InitModule;
|
|||||||
}/* jaccwabyt-specific tests */)
|
}/* jaccwabyt-specific tests */)
|
||||||
|
|
||||||
.t('Close db', function(){
|
.t('Close db', function(){
|
||||||
T.assert(this.db).assert(Number.isInteger(this.db.pointer));
|
T.assert(this.db).assert(wasm.isPtr(this.db.pointer));
|
||||||
wasm.exports.sqlite3_wasm_db_reset(this.db.pointer);
|
wasm.sqlite3_wasm_db_reset(this.db);
|
||||||
this.db.close();
|
this.db.close();
|
||||||
T.assert(!this.db.pointer);
|
T.assert(!this.db.pointer);
|
||||||
})
|
})
|
||||||
|
26
manifest
26
manifest
@ -1,5 +1,5 @@
|
|||||||
C Remove\stwo\sfeatures\sof\sjaccwabyt\swhich\swere\sfundamentally\sflawed,\salong\swith\sapprox.\s250\slines\sof\sunit\stests\swhich\sheavily\srelied\son\sthem.\sThankfully,\snone\sof\sthe\ssqlite3.js-level\scode\sused\sthose\sbits.
|
C Add\sa\sdemonstration\ssqlite3_vtab/module\simplemented\sin\sJS,\sbased\son\sext/misc/templatevtab.c.\sAdd\soo1.selectArrays()\sand\sselectObjects().
|
||||||
D 2022-12-05T15:05:46.306
|
D 2022-12-06T06:09:03.466
|
||||||
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
|
||||||
@ -491,12 +491,12 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
|||||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||||
F ext/wasm/GNUmakefile bfa47f169468ca9db031105b0e336db29a88e93c3abd217d0bbb2b8731fa5413
|
F ext/wasm/GNUmakefile 54c0db93a5493f625c0a993c12aee5d83951440eee03b2aecfc8aeb998182998
|
||||||
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
|
F ext/wasm/README-dist.txt 2d670b426fc7c613b90a7d2f2b05b433088fe65181abead970980f0a4a75ea20
|
||||||
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
F ext/wasm/README.md ef39861aa21632fdbca0bdd469f78f0096f6449a720f3f39642594af503030e9
|
||||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api ffa70413409e922ce0f761779787a1d9100b34b43c8e3106bb7ccf2786a41326
|
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api ffa70413409e922ce0f761779787a1d9100b34b43c8e3106bb7ccf2786a41326
|
||||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||||
F ext/wasm/api/README.md 20a256f4aaae80035d2bb1c9e3e0a125570313a8d137d427471d7be10edde87a
|
F ext/wasm/api/README.md 17fb1e10335cc87e366dec496c5b17b061f3f75cdf216e825258de34d97a3e53
|
||||||
F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d
|
F ext/wasm/api/extern-post-js.c-pp.js 8923f76c3d2213159e12d641dc750523ead5c848185dc4996fae5cc12397f88d
|
||||||
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41
|
||||||
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
||||||
@ -504,12 +504,12 @@ F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b
|
|||||||
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
|
||||||
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 c3a11e1d0e6fd381f68f9e76ad01f3616a6b809fbf9f5aa8e323955c128a6811
|
F ext/wasm/api/sqlite3-api-glue.js c3a11e1d0e6fd381f68f9e76ad01f3616a6b809fbf9f5aa8e323955c128a6811
|
||||||
F ext/wasm/api/sqlite3-api-oo1.js 793883953d4024e7b8c5ee1c7a6cb49c18ca53a1d235a203f93746f8907d32ba
|
F ext/wasm/api/sqlite3-api-oo1.js b970787aaf0bdd3a3df59cf66aeb84d0decaaa0529aed7eaf45121087181245f
|
||||||
F ext/wasm/api/sqlite3-api-prologue.js 815fef5ee93e1bb11ebec5a1d6a1b8ae2e47cfeb66dc5f6e93380ccce045f194
|
F ext/wasm/api/sqlite3-api-prologue.js 31cffd8ce212fad8d316a08decd864b0f614c5fce3686153707dffe40f8279e8
|
||||||
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-vfs-helper.js 4ad4faf02e1524bf0296be8452c00b5708dce6faf649468d0377e26a0b299263
|
F ext/wasm/api/sqlite3-v-helper.js 4451763a0cd85734f0afe18b48918cb3c88ca99cef399b7c5f12119281e7b6a8 w ext/wasm/api/sqlite3-vfs-helper.js
|
||||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 29d6487a26b2fb6a471cde52c37ffee7c27ed6a91914b308c247e0706f454ffb
|
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 29d6487a26b2fb6a471cde52c37ffee7c27ed6a91914b308c247e0706f454ffb
|
||||||
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,8 +539,8 @@ 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 b7261221133cda8d363f16ddbac8e5b671fd51ce962fc34dc10e738a293b696d
|
F ext/wasm/jaccwabyt/jaccwabyt.js f4fc93375e9c40ef60f56cbecca1b4dc8bf4f53fab2c3abc860ed34890c5d32d
|
||||||
F ext/wasm/jaccwabyt/jaccwabyt.md 72742a3205f1477de68086e7e4a854ed5f7d08dfcd6db54cdbea4dba4866f159
|
F ext/wasm/jaccwabyt/jaccwabyt.md 4bf62f7519857cdd674594aba7436cc4fae177eefbfaabc00740e16d9a828bee
|
||||||
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
|
||||||
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
F ext/wasm/scratchpad-wasmfs-main.js 4c140457f4d6da9d646a49addd91edb6e9ad1643c6c48e3258b5bce24725dc18
|
||||||
@ -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 8ed17c0e1f271e536cb7ccd86d3992785fc8bf2f94f9c2e0088ca670601ee087
|
F ext/wasm/tester1.c-pp.js d96a77dbf0d8af11e3f3adb013bee2bfb5bf9410e3f5eade528d70104451dd10
|
||||||
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
|
||||||
@ -2065,8 +2065,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 a329a809b5da135a9c251e4d5f637d45d01d0248110ac05f2ad8f01d9df38c64
|
P a190abc307847174f36421eaa3f47ef349c6f84a2bb35857fa64f64bbe722708
|
||||||
R 3d4ed37777b015bac598ce3a6734f5aa
|
R 5c37541d673cc882e16d46aec14f6ca8
|
||||||
U stephan
|
U stephan
|
||||||
Z 002b2dd6406bdc41efd7e7f9f5d7918f
|
Z d89d1c25adf51ede4f60b7c8a7ea4184
|
||||||
# Remove this line to create a well-formed Fossil manifest.
|
# Remove this line to create a well-formed Fossil manifest.
|
||||||
|
@ -1 +1 @@
|
|||||||
a190abc307847174f36421eaa3f47ef349c6f84a2bb35857fa64f64bbe722708
|
60482c97e02bc4cafefef281be0cf0bc8c5c53232162829c137f3f7a80cdc534
|
Reference in New Issue
Block a user