Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e4f58bf9c | |||
| a31a31715d | |||
| 75c60b4656 | |||
| 2c88b4aa24 | |||
| 97f052710d | |||
| e5af3c1cc5 | |||
| 79b4153e09 | |||
| 96f6cd5cab | |||
| dd12783730 |
@@ -349,6 +349,12 @@ EXPORTED_FUNCTIONS.api.in := $(EXPORTED_FUNCTIONS.api.main)
|
||||
ifeq (1,$(SQLITE_C_IS_SEE))
|
||||
EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-see)
|
||||
endif
|
||||
ifeq (1,$(emcc.WASM_BIGINT))
|
||||
ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
|
||||
EXPORTED_FUNCTIONS.api.in += $(abspath $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-fts5)
|
||||
endif
|
||||
endif
|
||||
|
||||
EXPORTED_FUNCTIONS.api := $(dir.tmp)/EXPORTED_FUNCTIONS.api
|
||||
$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE)
|
||||
cat $(EXPORTED_FUNCTIONS.api.in) > $@
|
||||
@@ -375,6 +381,9 @@ sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.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-sahpool.c-pp.js
|
||||
ifneq (,$(filter -DSQLITE_ENABLE_FTS5,$(SQLITE_OPT)))
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-fts5-helper.js
|
||||
endif
|
||||
sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
|
||||
|
||||
# SOAP.js is an external API file which is part of our distribution
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
_fts5_api_from_db
|
||||
@@ -52,9 +52,10 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build
|
||||
delete globalThis.sqlite3ApiBootstrap;
|
||||
delete globalThis.sqlite3ApiConfig;
|
||||
}
|
||||
|
||||
delete sqlite3.__dbCleanupMap;
|
||||
Module.sqlite3 = sqlite3 /* Needed for customized sqlite3InitModule() to be able to
|
||||
pass the sqlite3 object off to the client. */;
|
||||
sqlite3.wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
|
||||
}else{
|
||||
console.warn("This is not running in an Emscripten module context, so",
|
||||
"globalThis.sqlite3ApiBootstrap() is _not_ being called due to lack",
|
||||
|
||||
@@ -261,6 +261,18 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}),
|
||||
"*"/*pUserData*/
|
||||
]],
|
||||
/**
|
||||
Achtung: when specifying an xDestroy() method via
|
||||
sqlite3_set_auxdata(), it is up to the client to re-set it to
|
||||
0/NULL at the end of its lifetime (e.g. in the associated UDF's
|
||||
xFinal() impl), The C library will be able to call the
|
||||
destructor but _not_ uninstall the temporary WASM-bound proxy
|
||||
function because it does not have enough information to do
|
||||
so. Alternately, clients may create the function pointer
|
||||
themselves using wasm.createFunction() and pass that pointer
|
||||
here, in which case they avoid creating a stranded "temporary"
|
||||
function binding.
|
||||
*/
|
||||
["sqlite3_set_auxdata", undefined, [
|
||||
"sqlite3_context*", "int", "*",
|
||||
new wasm.xWrap.FuncPtrAdapter({
|
||||
@@ -421,8 +433,6 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
|
||||
// Add session/changeset APIs...
|
||||
if(wasm.bigIntEnabled && !!wasm.exports.sqlite3changegroup_add){
|
||||
/* ACHTUNG: 2022-12-23: the session/changeset API bindings are
|
||||
COMPLETELY UNTESTED. */
|
||||
/**
|
||||
FuncPtrAdapter options for session-related callbacks with the
|
||||
native signature "i(ps)". This proxy converts the 2nd argument
|
||||
@@ -597,6 +607,12 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
]);
|
||||
}/*session/changeset APIs*/
|
||||
|
||||
if(wasm.bigIntEnabled && !!wasm.exports.fts5_api_from_db){
|
||||
wasm.bindingSignatures.int64.push(
|
||||
['fts5_api_from_db', 'fts5_api*', 'sqlite3*']
|
||||
);
|
||||
}/* fts5 APIs */
|
||||
|
||||
/**
|
||||
Functions which are intended solely for API-internal use by the
|
||||
WASM components, not client code. These get installed into
|
||||
@@ -659,8 +675,8 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
function(v){
|
||||
if(wasm.isPtr(v)) return v;
|
||||
v = ''+v;
|
||||
let rc = this[v];
|
||||
return rc || (this[v] = wasm.allocCString(v));
|
||||
const rc = this[v];
|
||||
return (undefined===rc) ? (this[v] = wasm.allocCString(v)) : rc;
|
||||
}.bind(Object.create(null))
|
||||
);
|
||||
|
||||
@@ -717,7 +733,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
});
|
||||
|
||||
const __xRcPtr = wasm.xWrap.resultAdapter('*');
|
||||
wasm.xWrap.resultAdapter('sqlite3*', __xRcPtr)
|
||||
wasm.xWrap.resultAdapter
|
||||
('fts5_api*', __xRcPtr)
|
||||
('sqlite3*', __xRcPtr)
|
||||
('sqlite3_context*', __xRcPtr)
|
||||
('sqlite3_stmt*', __xRcPtr)
|
||||
('sqlite3_value*', __xRcPtr)
|
||||
@@ -823,6 +841,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
'version' ];
|
||||
if(wasm.bigIntEnabled){
|
||||
defineGroups.push('serialize', 'session', 'vtab');
|
||||
if(!!wasm.ctype.fts5){
|
||||
defineGroups.push('fts5');
|
||||
}
|
||||
}
|
||||
for(const t of defineGroups){
|
||||
for(const e of Object.entries(wasm.ctype[t])){
|
||||
@@ -906,9 +927,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
UDFs and collations so that sqlite3_close_v2() can clean up any
|
||||
automated JS-to-WASM function conversions installed by those.
|
||||
*/
|
||||
const __argPDb = (pDb)=>wasm.xWrap.argAdapter('sqlite3*')(pDb);
|
||||
const __argPDb = wasm.xWrap.argAdapter('sqlite3*');
|
||||
const __argStr = (str)=>wasm.isPtr(str) ? wasm.cstrToJs(str) : str;
|
||||
const __dbCleanupMap = function(
|
||||
const __dbCleanupMap = sqlite3.__dbCleanupMap = function(
|
||||
pDb, mode/*0=remove, >0=create if needed, <0=do not create if missing*/
|
||||
){
|
||||
pDb = __argPDb(pDb);
|
||||
@@ -973,6 +994,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
*/
|
||||
__dbCleanupMap.cleanup = function(pDb){
|
||||
pDb = __argPDb(pDb);
|
||||
//console.warn("db cleanup",pDb);
|
||||
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
|
||||
/**
|
||||
Installing NULL functions in the C API will remove those
|
||||
@@ -1001,6 +1023,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
console.warn("close-time call of",name+"(",closeArgs,") threw:",e);
|
||||
}
|
||||
}
|
||||
for(const callback of __dbCleanupMap.extraCallbacks){
|
||||
callback(pDb);
|
||||
}
|
||||
const m = __dbCleanupMap(pDb, 0);
|
||||
if(!m) return;
|
||||
if(m.collation){
|
||||
@@ -1037,15 +1062,39 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
delete m.udf;
|
||||
delete m.wudf;
|
||||
}/*__dbCleanupMap.cleanup()*/;
|
||||
/**
|
||||
Downstream code, namely sqlite3-fts5-helper.js, should add any custom
|
||||
cleanup handlers to __dbCleanupMap.extraCallbacks. Each function in this
|
||||
array will be called during sqlite3_close_v2() and passed a pointer to
|
||||
the being-destroyed (sqlite3*) object.
|
||||
*/
|
||||
__dbCleanupMap.extraCallbacks = [];
|
||||
/**
|
||||
Downstream code, namely sqlite3-fts5-helper.js, should add any
|
||||
custom cleanup handlers to __dbCleanupMap.postCloseCallbacks.
|
||||
Each function in this array will be called during
|
||||
sqlite3_close_v2(), AFTER the db is closed, and passed a pointer
|
||||
to the being-destroyed (sqlite3*) object. The memory is NOT A
|
||||
VALID OBJECT but its address is still valid as a lookup key.
|
||||
*/
|
||||
__dbCleanupMap.postCloseCallbacks = [];
|
||||
|
||||
{/* Binding of sqlite3_close_v2() */
|
||||
const __sqlite3CloseV2 = wasm.xWrap("sqlite3_close_v2", "int", "sqlite3*");
|
||||
const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');
|
||||
capi.sqlite3_close_v2 = function(pDb){
|
||||
if(1!==arguments.length) return __dbArgcMismatch(pDb, 'sqlite3_close_v2', 1);
|
||||
pDb = __xArgDb(pDb);
|
||||
if(pDb){
|
||||
try{__dbCleanupMap.cleanup(pDb)} catch(e){/*ignored*/}
|
||||
}
|
||||
return __sqlite3CloseV2(pDb);
|
||||
const rc = __sqlite3CloseV2(pDb);
|
||||
if(pDb/*noting that it's not valid anymore*/){
|
||||
for(const f of __dbCleanupMap.postCloseCallbacks){
|
||||
try{f(pDb)}catch(e){/*ingored*/}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
};
|
||||
}/*sqlite3_close_v2()*/
|
||||
|
||||
@@ -1154,6 +1203,9 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
/**
|
||||
JS proxies for the various sqlite3_create[_window]_function()
|
||||
callbacks, structured in a form usable by wasm.xWrap.FuncPtrAdapter.
|
||||
|
||||
TODO: explore an API option which more closely resembles the
|
||||
/ext/jni mapping, which is much friendlier at the client level.
|
||||
*/
|
||||
const __cfProxy = Object.assign(Object.create(null), {
|
||||
xInverseAndStep: {
|
||||
@@ -1665,5 +1717,4 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
}
|
||||
}/*pKvvfs*/
|
||||
|
||||
wasm.xWrap.FuncPtrAdapter.warnOnUse = true;
|
||||
});
|
||||
|
||||
@@ -1645,7 +1645,8 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
|
||||
do not.
|
||||
*/
|
||||
tgt.push(capi.sqlite3_value_to_js(
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i))
|
||||
wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)),
|
||||
throwIfCannotConvert
|
||||
));
|
||||
}
|
||||
return tgt;
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
** 2023-08-03
|
||||
**
|
||||
** 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.fts5, a namespace which exists to assist
|
||||
in JavaScript-side extension of FTS5.
|
||||
*/
|
||||
'use strict';
|
||||
globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
|
||||
const wasm = sqlite3.wasm, capi = sqlite3.capi, toss = sqlite3.util.toss3;
|
||||
if(!capi.fts5_api_from_db){
|
||||
return /*this build does not have FTS5*/;
|
||||
}
|
||||
const fts = sqlite3.fts5 = Object.create(null);
|
||||
const __xArgDb = wasm.xWrap.argAdapter('sqlite3*');
|
||||
|
||||
/**
|
||||
Move FTS-specific APIs (installed via automation) from
|
||||
sqlite3.capi to sqlite3.fts.
|
||||
*/
|
||||
for(const c of [
|
||||
'Fts5ExtensionApi', 'Fts5PhraseIter', 'fts5_api',
|
||||
'fts5_api_from_db', 'fts5_tokenizer'
|
||||
]){
|
||||
fts[c] = capi[c] || toss("Cannot find capi."+c);
|
||||
delete capi[c];
|
||||
}
|
||||
|
||||
/**
|
||||
Requires a JS Function intended to be used as an xFunction()
|
||||
implementation. This function returns a proxy xFunction
|
||||
wrapper which:
|
||||
|
||||
- Converts all of its sqlite3_value arguments to an array
|
||||
of JS values using sqlite3_values_to_js().
|
||||
|
||||
- Calls the given callback, passing it:
|
||||
|
||||
(pFtsXApi, pFtsCx, pCtx, array-of-values)
|
||||
|
||||
where the first 3 arguments are the first 3 pointers
|
||||
in the xFunction interface.
|
||||
|
||||
|
||||
The call is intended to set a result value into the db, and may
|
||||
do so be either (A) explicitly returning non-undefined or (B)
|
||||
using one of the sqlite3_result_XYZ() functions and returning
|
||||
undefined. If the callback throws, its exception will be passed
|
||||
to sqlite3_result_error_js().
|
||||
*/
|
||||
fts.xFunctionProxy1 = function(callback){
|
||||
return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
|
||||
try{
|
||||
capi.sqlite3_result_js(pCtx, callback(
|
||||
pFtsXApi, pFtsCx, pCtx,
|
||||
capi.sqlite3_values_to_js(argc, pArgv)
|
||||
));
|
||||
}catch(e){
|
||||
capi.sqlite3_result_error_js(pCtx, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Identical to xFunctionProxy1 except that the callback wrapper it
|
||||
creates does _not_ perform sqlite3_value-to-JS conversion in
|
||||
advance and calls the callback with:
|
||||
|
||||
(pFtsXApi, pFtsCx, pCtx, array-of-ptr-to-sqlite3_value)
|
||||
|
||||
It is up to the callback to use the sqlite3_value_XYZ() family of
|
||||
functions to inspect or convert the values.
|
||||
*/
|
||||
fts.xFunctionProxy2 = function(callback){
|
||||
return (pFtsXApi, pFtsCx, pCtx, argc, pArgv)=>{
|
||||
try{
|
||||
const list = [];
|
||||
let i;
|
||||
for(i = 0; i < argc; ++i){
|
||||
list.push( wasm.peekPtr(pArgv + (wasm.ptrSizeof * i)) );
|
||||
}
|
||||
capi.sqlite3_result_js(pCtx, callback(
|
||||
pFtsXApi, pFtsCx, pCtx, list
|
||||
));
|
||||
}catch(e){
|
||||
capi.sqlite3_result_error_js(pCtx, e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
JS-to-WASM arg adapter for xCreateFunction()'s xFunction arg.
|
||||
This binds JS impls of xFunction to WASM so that they can be
|
||||
called from native code. Its context is limited to the
|
||||
combination of ((fts5_api*) + functionNameCaseInsensitive), and
|
||||
will replace any existing impl for subsequent invocations for the
|
||||
same combination.
|
||||
|
||||
The functions is creates are intended to set a result value into
|
||||
the db, and may do so be either (A) explicitly returning
|
||||
non-undefined or (B) using one of the sqlite3_result_XYZ()
|
||||
functions and returning undefined. If the callback throws, its
|
||||
exception will be passed to sqlite3_result_error_js().
|
||||
|
||||
PENDING DESIGN DECISION: this framework currently converts each
|
||||
argument in its JS equivalent before passing them on to the
|
||||
xFunction impl. We could, and possibly should, instead pass a JS
|
||||
array of sqlite3_value pointers. The advantages would be:
|
||||
|
||||
- No in-advance to-JS overhead which xFunction might not use.
|
||||
|
||||
Disadvantages include:
|
||||
|
||||
- xFunction would be required to call sqlite3_value_to_js(),
|
||||
or one of the many sqlite3_value_XYZ() functions on their own.
|
||||
This would be more cumbersome for most users.
|
||||
|
||||
Regardless of which approach is chosen here, clients could
|
||||
provide a function of their own which takes the _other_ approach,
|
||||
install it with wasm.installFunction(), and then pass that
|
||||
generated pointer to createFunction(), in which case this layer
|
||||
does not proxying and passes all native-level arguments as-is to
|
||||
the client-defined function.
|
||||
*/
|
||||
const xFunctionArgAdapter = new wasm.xWrap.FuncPtrAdapter({
|
||||
name: 'fts5_api::xCreateFunction(xFunction)',
|
||||
signature: 'v(pppip)',
|
||||
contextKey: (argv,argIndex)=>{
|
||||
return (argv[0]/*(fts5_api*)*/
|
||||
+ wasm.cstrToJs(argv[1]).toLowerCase()/*name*/)
|
||||
},
|
||||
callProxy: fts.xFunctionProxy1
|
||||
});
|
||||
|
||||
/** Map of (sqlite3*) to fts.fts5_api. */
|
||||
const __ftsApiToStruct = Object.create(null);
|
||||
const __fts5_api_from_db = function(pDb, createIfNeeded){
|
||||
let rc = __ftsApiToStruct[pDb];
|
||||
if(!rc && createIfNeeded){
|
||||
const fapi = fts.fts5_api_from_db(pDb)
|
||||
|| toss("Internal error - cannot get FTS5 API object for db.");
|
||||
rc = new fts.fts5_api(fapi);
|
||||
__ftsApiToStruct[pDb] = rc;
|
||||
}
|
||||
return rc;
|
||||
};
|
||||
|
||||
/**
|
||||
Arrange for WASM functions dynamically created via this API to be
|
||||
uninstalled when the db they were installed for is closed... */
|
||||
const __addCleanupForFunc = function(sfapi, name, pDestroy){
|
||||
if(!sfapi.$$cleanup){
|
||||
sfapi.$$cleanup = [];
|
||||
}
|
||||
sfapi.$$cleanup.push([name.toLowerCase(), pDestroy]);
|
||||
};
|
||||
|
||||
/**
|
||||
Callback to be invoked via the JS binding of sqlite3_close_v2(),
|
||||
after the db has been closed, meaning that the argument to this
|
||||
function is not a valid object. We use its address only as a
|
||||
lookup key.
|
||||
*/
|
||||
sqlite3.__dbCleanupMap.postCloseCallbacks.push(function(pDb){
|
||||
const sfapi = __fts5_api_from_db(pDb, false);
|
||||
if(sfapi){
|
||||
delete __ftsApiToStruct[pDb];
|
||||
if(sfapi.$$cleanup){
|
||||
const fapi = sfapi.pointer;
|
||||
const scope = wasm.scopedAllocPush();
|
||||
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = true;
|
||||
try{
|
||||
for(const [name, pDestroy] of sfapi.$$cleanup){
|
||||
try{
|
||||
/* Uninstall xFunctionArgAdapter's bindings via a
|
||||
roundabout approach: its scoping rules uninstall each
|
||||
new installation at the earliest opportunity, so we
|
||||
simply need to fake a call with a 0-pointer for the
|
||||
xFunction callback to uninstall the most recent
|
||||
one. */
|
||||
const zName = wasm.scopedAllocCString(name);
|
||||
const argv = [fapi, zName, 0, 0, 0];
|
||||
xFunctionArgAdapter.convertArg(argv[3], argv, 3);
|
||||
/* xDestroy, on the other hand, requires some
|
||||
hand-holding to ensure we don't prematurely
|
||||
uninstall these when a function is replaced
|
||||
(shadowed). */
|
||||
if(pDestroy) wasm.uninstallFunction(pDestroy);
|
||||
}catch(e){
|
||||
sqlite3.config.warn("Could not remove FTS func",name,e);
|
||||
}
|
||||
}
|
||||
}finally{
|
||||
wasm.scopedAllocPop(scope);
|
||||
}
|
||||
//wasm.xWrap.FuncPtrAdapter.debugFuncInstall = false;
|
||||
}
|
||||
sfapi.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
const __affirmDbArg = (arg)=>{
|
||||
arg = __xArgDb(arg);
|
||||
if(!arg || !wasm.isPtr(arg)) toss("Invalid db argument.");
|
||||
return arg;
|
||||
};
|
||||
|
||||
/**
|
||||
Convenience wrapper to fts5_api::xCreateFunction.
|
||||
|
||||
Creates a new FTS5 function for the given database. The arguments are:
|
||||
|
||||
- db must be either an sqlite3.oo1.DB instance or a WASM pointer
|
||||
to (sqlite3*).
|
||||
|
||||
- name: name (JS string) of the function
|
||||
|
||||
- xFunction either a Function or a pointer to a WASM function. In
|
||||
the former case a WASM-bound wrapper, behaving as documented
|
||||
for fts5.xFunctionProxy1(), gets installed for the life of the
|
||||
given db handle. In the latter case the function is
|
||||
passed-through as-is, with no argument conversion or lifetime
|
||||
tracking. In the former case the function is called as
|
||||
documented for xFunctionProxy1() and in the latter it must
|
||||
return void and is called with args (ptrToFts5ExtensionApi,
|
||||
ptrToFts5Context, ptrToSqlite3Context, int argc,
|
||||
C-array-of-sqlite3_value-pointers).
|
||||
|
||||
- xDestroy optional Function or pointer to WASM function to call
|
||||
when the binding is destroyed (when the db handle is
|
||||
closed). The function will, in this context, always be passed 0
|
||||
as its only argument. A passed-in function must, however,
|
||||
have one parameter so that type signature checks will pass.
|
||||
It must return void and must not throw.
|
||||
|
||||
The 2nd and subsequent aruguments may optionally be packed into
|
||||
a single Object with like-named properties.
|
||||
|
||||
This function throws on error, of which there are many potential
|
||||
candidates. It returns `undefined`.
|
||||
*/
|
||||
fts.createFunction = function(db, name, xFunction, xDestroy = 0){
|
||||
db = __affirmDbArg(db);
|
||||
if( 2 === arguments.length && 'string' !== typeof name){
|
||||
xDestroy = name.xDestroy || null;
|
||||
xFunction = name.xFunction || null;
|
||||
name = name.name;
|
||||
}
|
||||
if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
|
||||
const sfapi = __fts5_api_from_db(db, true);
|
||||
let pDestroy = 0;
|
||||
try{
|
||||
/** Because of how fts5_api::xCreateFunction() replaces
|
||||
functions (by prepending new ones to a linked list but
|
||||
retaining old ones), we cannot use a FuncPtrAdapter to
|
||||
automatically convert xDestroy, lest we end up uninstalling
|
||||
a bound-to-wasm JS function's wasm pointer before fts5
|
||||
cleans it up when the db is closed. */
|
||||
if(xDestroy instanceof Function){
|
||||
pDestroy = wasm.installFunction(xDestroy, 'v(p)');
|
||||
}
|
||||
const xcf = sfapi.$$xCreateFunction || (
|
||||
sfapi.$$xCreateFunction = wasm.xWrap(sfapi.$xCreateFunction, 'int', [
|
||||
'*', 'string', '*', xFunctionArgAdapter, '*'
|
||||
])
|
||||
);
|
||||
const rc = xcf(sfapi.pointer, name, 0, xFunction || 0, pDestroy || xDestroy || 0 );
|
||||
if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
|
||||
__addCleanupForFunc(sfapi, name, pDestroy);
|
||||
}catch(e){
|
||||
if(pDestroy) wasm.uninstallFunction(pDestroy);
|
||||
sfapi.dispose();
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
! UNTESTED
|
||||
|
||||
Convenience wrapper for fts5_api::xCreateTokenizer().
|
||||
|
||||
- db = the db to install the tokenizer into.
|
||||
|
||||
- name = the JS string name of the tokenizer.
|
||||
|
||||
- pTokenizer = the tokenizer instance, which must be a
|
||||
fts5.fts5_tokenizer instance or a valid WASM pointer to one.
|
||||
|
||||
- xDestroy = as documented for createFunction().
|
||||
|
||||
The C layer makes a bitwise copy of the tokenizer, so any
|
||||
changes made to it after installation will have no effect.
|
||||
|
||||
Throws on error.
|
||||
*/
|
||||
const createTokenizer = function(db, name, pTokenizer, xDestroy = 0){
|
||||
db = __affirmDbArg(db);
|
||||
if( 2 === arguments.length && 'string' !== typeof name){
|
||||
pTokenizer = name.pTokenizer;
|
||||
xDestroy = name.xDestroy || null;
|
||||
name = name.name;
|
||||
}
|
||||
if( !name || 'string' !== typeof name ) toss("Invalid name argument.");
|
||||
if(pTokenizer instanceof fts.fts5_tokenizer){
|
||||
pTokenizer = pTokenizer.pointer;
|
||||
}
|
||||
if(!pTokenizer || !wasm.isPtr(pTokenizer)){
|
||||
toss("Invalid pTokenizer argument - must be a valid fts5.fts5_tokenizer",
|
||||
"instance or a WASM pointer to one.");
|
||||
}
|
||||
const sfapi = __fts5_api_from_db(db, true);
|
||||
let pDestroy = 0;
|
||||
const stackPos = wasm.pstack.pointer;
|
||||
try{
|
||||
if(xDestroy instanceof Function){
|
||||
pDestroy = wasm.installFunction(xDestroy, 'v(p)');
|
||||
}
|
||||
const xct = sfapi.$$xCreateTokenizer || (
|
||||
sfapi.$$xCreateTokenizer = wasm.xWrap(sfapi.$xCreateTokenizer, 'int', [
|
||||
'*', 'string', '*', '*', '*'
|
||||
/* fts5_api*, const char *zName, void *pContext,
|
||||
fts5_tokenizer *pTokenizer, void(*xDestroy)(void*) */
|
||||
])
|
||||
);
|
||||
const outPtr = wasm.pstack.allocPtr();
|
||||
const rc = xct(fapi.pointer, name, 0, pTokenizer, pDestroy || xDestroy || 0 );
|
||||
if(rc) toss(rc,"FTS5::xCreateFunction() failed.");
|
||||
if(pDestroy) __addCleanupForFunc(sfapi, name, pDestroy);
|
||||
}catch(e){
|
||||
if(pDestroy) wasm.uninstallFunction(pDestroy);
|
||||
sfapi.dispose();
|
||||
throw e;
|
||||
}finally{
|
||||
wasm.pstack.restore(stackPost);
|
||||
}
|
||||
};
|
||||
//fts.createTokenizer = createTokenizer;
|
||||
|
||||
}/*sqlite3ApiBootstrap.initializers.push()*/);
|
||||
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
This file installs sqlite3.vfs, and object which exists to assist
|
||||
This file installs sqlite3.vfs, an object which exists to assist
|
||||
in the creation of JavaScript implementations of sqlite3_vfs, along
|
||||
with its virtual table counterpart, sqlite3.vtab.
|
||||
*/
|
||||
|
||||
+145
-5
@@ -96,8 +96,8 @@
|
||||
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
|
||||
# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
|
||||
#endif
|
||||
#ifndef SQLITE_ENABLE_FTS4
|
||||
# define SQLITE_ENABLE_FTS4 1
|
||||
#ifndef SQLITE_ENABLE_FTS5
|
||||
# define SQLITE_ENABLE_FTS5 1
|
||||
#endif
|
||||
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS
|
||||
# define SQLITE_ENABLE_MATH_FUNCTIONS 1
|
||||
@@ -363,6 +363,27 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){
|
||||
return err_code;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
/*
|
||||
** Return a pointer to the fts5_api pointer for database connection db.
|
||||
** If an error occurs, return NULL and leave an error in the database
|
||||
** handle (accessible using sqlite3_errcode()/errmsg()).
|
||||
**
|
||||
** This function was taken verbatim from the /fts5.html docs.
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
fts5_api *fts5_api_from_db(sqlite3 *db){
|
||||
fts5_api *pRet = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
|
||||
sqlite3_bind_pointer(pStmt, 1, (void*)&pRet, "fts5_api_ptr", NULL);
|
||||
sqlite3_step(pStmt);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
return pRet;
|
||||
}
|
||||
#endif /*SQLITE_ENABLE_FTS5*/
|
||||
|
||||
#if SQLITE_WASM_TESTS
|
||||
struct WasmTestStruct {
|
||||
int v4;
|
||||
@@ -402,7 +423,10 @@ void sqlite3_wasm_test_struct(WasmTestStruct * s){
|
||||
*/
|
||||
SQLITE_WASM_EXPORT
|
||||
const char * sqlite3_wasm_enum_json(void){
|
||||
static char aBuffer[1024 * 20] = {0} /* where the JSON goes */;
|
||||
static char aBuffer[1024 * 22] = {0}
|
||||
/* where the JSON goes. If this buffer is not large enough, this
|
||||
function will assert (in debug builds) and return 0. When it does
|
||||
so, this value needs to be increased. */;
|
||||
int n = 0, nChildren = 0, nStruct = 0
|
||||
/* output counters for figuring out where commas go */;
|
||||
char * zPos = &aBuffer[1] /* skip first byte for now to help protect
|
||||
@@ -651,6 +675,16 @@ const char * sqlite3_wasm_enum_json(void){
|
||||
DefInt(SQLITE_LOCK_EXCLUSIVE);
|
||||
} _DefGroup;
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
DefGroup(fts5) {
|
||||
DefInt(FTS5_TOKENIZE_QUERY);
|
||||
DefInt(FTS5_TOKENIZE_PREFIX);
|
||||
DefInt(FTS5_TOKENIZE_DOCUMENT);
|
||||
DefInt(FTS5_TOKENIZE_AUX);
|
||||
DefInt(FTS5_TOKEN_COLOCATED);
|
||||
} _DefGroup;
|
||||
#endif
|
||||
|
||||
DefGroup(ioCap) {
|
||||
DefInt(SQLITE_IOCAP_ATOMIC);
|
||||
DefInt(SQLITE_IOCAP_ATOMIC512);
|
||||
@@ -876,7 +910,7 @@ const char * sqlite3_wasm_enum_json(void){
|
||||
DefInt(SQLITE_STMTSTATUS_FILTER_HIT);
|
||||
DefInt(SQLITE_STMTSTATUS_MEMUSED);
|
||||
} _DefGroup;
|
||||
|
||||
|
||||
DefGroup(syncFlags) {
|
||||
DefInt(SQLITE_SYNC_NORMAL);
|
||||
DefInt(SQLITE_SYNC_FULL);
|
||||
@@ -1094,7 +1128,7 @@ const char * sqlite3_wasm_enum_json(void){
|
||||
M(xShadowName, "i(s)");
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
|
||||
/**
|
||||
** Workaround: in order to map the various inner structs from
|
||||
** sqlite3_index_info, we have to uplift those into constructs we
|
||||
@@ -1171,6 +1205,112 @@ const char * sqlite3_wasm_enum_json(void){
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
#define CurrentStruct Fts5PhraseIter
|
||||
StructBinder {
|
||||
M(a, "p");
|
||||
M(b, "p");
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#define CurrentStruct Fts5ExtensionApi
|
||||
StructBinder {
|
||||
M(iVersion, "i");
|
||||
M(xUserData, "p(p)");// void *(*)(Fts5Context*);
|
||||
M(xColumnCount, "i(p)");// int (*)(Fts5Context*);
|
||||
M(xRowCount, "i(pp)");
|
||||
//^^^ int (*)(Fts5Context*, sqlite3_int64 *pnRow);
|
||||
M(xColumnTotalSize, "i(pip)");
|
||||
//^^^ int (*)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
|
||||
M(xTokenize, "i(ppipp)");
|
||||
//^^^ int (*)(Fts5Context*,
|
||||
// const char *pText, int nText, /* Text to tokenize */
|
||||
// void *pCtx, /* Context passed to xToken() */
|
||||
// int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
|
||||
//);
|
||||
M(xPhraseCount, "i(p)"); // int (*)(Fts5Context*);
|
||||
M(xPhraseSize, "i(pi)"); // int (*)(Fts5Context*, int iPhrase);
|
||||
M(xInstCount, "i(pp)"); // int (*)(Fts5Context*, int *pnInst);
|
||||
M(xInst, "i(pippp)");
|
||||
//^^^ int (*)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
|
||||
M(xRowid, "j(p)"); // sqlite3_int64 (*)(Fts5Context*);
|
||||
M(xColumnText, "i(pipp)");
|
||||
//^^^ int (*)(Fts5Context*, int iCol, const char **pz, int *pn);
|
||||
M(xColumnSize, "i(pip)");
|
||||
//^^^ int (*)(Fts5Context*, int iCol, int *pnToken);
|
||||
M(xQueryPhrase, "i(pipp)");
|
||||
//^^^ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
|
||||
// int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
|
||||
// );
|
||||
M(xSetAuxdata, "i(ppp)");
|
||||
//^^^ int (*)(Fts5Context*, void *pAux, void(*xDelete)(void*));
|
||||
M(xGetAuxdata, "p(pi)"); // void *(*)(Fts5Context*, int bClear);
|
||||
M(xPhraseFirst, "i(pippp)");
|
||||
//^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
|
||||
M(xPhraseNext, "v(pppp)");
|
||||
//^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
|
||||
M(xPhraseFirstColumn, "i(pipp)");
|
||||
//^^^ int (*)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
|
||||
M(xPhraseNextColumn, "v(ppp)");
|
||||
//^^^ void (*)(Fts5Context*, Fts5PhraseIter*, int *piCol);
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#define CurrentStruct fts5_api
|
||||
StructBinder {
|
||||
M(iVersion, "i");/* Currently always 2 */
|
||||
M(xCreateTokenizer, "i(ppppp)");
|
||||
//^^^ int (*)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_tokenizer *pTokenizer,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
M(xFindTokenizer, "i(pppp)");
|
||||
//^^^ int (*)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void **ppContext,
|
||||
// fts5_tokenizer *pTokenizer
|
||||
// );
|
||||
M(xCreateFunction, "i(ppppp)");
|
||||
//^^^ int (*)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_extension_function xFunction,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#define CurrentStruct fts5_tokenizer
|
||||
StructBinder {
|
||||
M(xCreate, "i(ppip)");
|
||||
//^^^ int (*)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
M(xDelete, "v(p)");
|
||||
//^^^ void(Fts5Tokenizer*)
|
||||
M(xTokenize, "i(ppipip)");
|
||||
/**^^^ int (*xTokenize)(Fts5Tokenizer*,
|
||||
void *pCtx,
|
||||
int flags,
|
||||
const char *pText,
|
||||
int nText,
|
||||
int (*xToken)(
|
||||
void *pCtx,
|
||||
int tflags,
|
||||
const char *pToken,
|
||||
int nToken,
|
||||
int iStart,
|
||||
int iEnd
|
||||
)
|
||||
); */
|
||||
} _StructBinder;
|
||||
#undef CurrentStruct
|
||||
|
||||
#endif /* SQLITE_ENABLE_FTS5 */
|
||||
|
||||
#if SQLITE_WASM_TESTS
|
||||
#define CurrentStruct WasmTestStruct
|
||||
StructBinder {
|
||||
|
||||
@@ -613,8 +613,6 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
target.installFunction = (func, sig)=>__installFunction(func, sig, false);
|
||||
|
||||
/**
|
||||
EXPERIMENTAL! DO NOT USE IN CLIENT CODE!
|
||||
|
||||
Works exactly like installFunction() but requires that a
|
||||
scopedAllocPush() is active and uninstalls the given function
|
||||
when that alloc scope is popped via scopedAllocPop().
|
||||
@@ -1180,7 +1178,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
cache.scopedAlloc.splice(n,1);
|
||||
for(let p; (p = state.pop()); ){
|
||||
if(target.functionEntry(p)){
|
||||
//console.warn("scopedAllocPop() uninstalling transient function",p);
|
||||
//console.warn("scopedAllocPop() uninstalling function",p);
|
||||
target.uninstallFunction(p);
|
||||
}
|
||||
else target.dealloc(p);
|
||||
@@ -1384,15 +1382,19 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
conversion of argument or return types, but see xWrap() and
|
||||
xCallWrapped() for variants which do.
|
||||
|
||||
If the first argument is a function is is assumed to be
|
||||
a WASM-bound function and is used as-is instead of looking up
|
||||
the function via xGet().
|
||||
|
||||
As a special case, if passed only 1 argument after the name and
|
||||
that argument in an Array, that array's entries become the
|
||||
function arguments. (This is not an ambiguous case because it's
|
||||
not legal to pass an Array object to a WASM function.)
|
||||
*/
|
||||
target.xCall = function(fname, ...args){
|
||||
const f = target.xGet(fname);
|
||||
const f = (fname instanceof Function) ? fname : target.xGet(fname);
|
||||
if(!(f instanceof Function)) toss("Exported symbol",fname,"is not a function.");
|
||||
if(f.length!==args.length) __argcMismatch(fname,f.length)
|
||||
if(f.length!==args.length) __argcMismatch(((f===fname) ? f.name : fname),f.length)
|
||||
/* This is arguably over-pedantic but we want to help clients keep
|
||||
from shooting themselves in the foot when calling C APIs. */;
|
||||
return (2===arguments.length && Array.isArray(arguments[1]))
|
||||
@@ -1539,7 +1541,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
jsFuncToWasm().
|
||||
|
||||
- bindScope (string): one of ('transient', 'context',
|
||||
'singleton'). Bind scopes are:
|
||||
'singleton', 'permanent'). Bind scopes are:
|
||||
|
||||
- 'transient': it will convert JS functions to WASM only for
|
||||
the duration of the xWrap()'d function call, using
|
||||
@@ -1637,6 +1639,7 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
'and is not intended to be invoked from',
|
||||
'client-level code. Invoked with:',opt);
|
||||
}
|
||||
this.name = opt.name || "unnamed";
|
||||
this.signature = opt.signature;
|
||||
if(opt.contextKey instanceof Function){
|
||||
this.contextKey = opt.contextKey;
|
||||
@@ -1698,14 +1701,16 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
exactly the 2nd and 3rd arguments are.
|
||||
*/
|
||||
convertArg(v,argv,argIndex){
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.signature,this.transient,v);
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v);
|
||||
let pair = this.singleton;
|
||||
if(!pair && this.isContext){
|
||||
pair = this.contextMap(this.contextKey(argv,argIndex));
|
||||
//FuncPtrAdapter.debugOut(this.name, this.signature, "contextKey() =",this.contextKey(argv,argIndex), pair);
|
||||
}
|
||||
if(pair && pair[0]===v) return pair[1];
|
||||
if(v instanceof Function){
|
||||
/* Install a WASM binding and return its pointer. */
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
|
||||
if(this.callProxy) v = this.callProxy(v);
|
||||
const fp = __installFunction(v, this.signature, this.isTransient);
|
||||
if(FuncPtrAdapter.debugFuncInstall){
|
||||
@@ -1719,7 +1724,18 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
|
||||
this.contextKey(argv,argIndex), '@'+pair[1], v);
|
||||
}
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
try{
|
||||
/* Because the pending native call might rely on the
|
||||
pointer we're replacing, e.g. as is normally the case
|
||||
with sqlite3's xDestroy() methods, we don't
|
||||
immediately uninstall but instead add its pointer to
|
||||
the scopedAlloc stack, which will be cleared when the
|
||||
xWrap() mechanism is done calling the native
|
||||
function. We're relying very much here on xWrap()
|
||||
having pushed an alloc scope.
|
||||
*/
|
||||
cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]);
|
||||
}
|
||||
catch(e){/*ignored*/}
|
||||
}
|
||||
pair[0] = v;
|
||||
@@ -1727,13 +1743,14 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
}
|
||||
return fp;
|
||||
}else if(target.isPtr(v) || null===v || undefined===v){
|
||||
//FuncPtrAdapter.debugOut("FuncPtrAdapter.convertArg()",this.name,this.signature,this.transient,v,pair);
|
||||
if(pair && pair[1] && pair[1]!==v){
|
||||
/* uninstall stashed mapping and replace stashed mapping with v. */
|
||||
if(FuncPtrAdapter.debugFuncInstall){
|
||||
FuncPtrAdapter.debugOut("FuncPtrAdapter uninstalling", this,
|
||||
this.contextKey(argv,argIndex), '@'+pair[1], v);
|
||||
}
|
||||
try{target.uninstallFunction(pair[1])}
|
||||
try{ cache.scopedAlloc[cache.scopedAlloc.length-1].push(pair[1]) }
|
||||
catch(e){/*ignored*/}
|
||||
pair[0] = pair[1] = (v | 0);
|
||||
}
|
||||
@@ -1774,11 +1791,29 @@ globalThis.WhWasmUtilInstaller = function(target){
|
||||
const __xResultAdapterCheck =
|
||||
(t)=>xResult.get(t) || toss("Result adapter not found:",t);
|
||||
|
||||
/**
|
||||
Fetches the xWrap() argument adapter mapped to t, calls it,
|
||||
passing in all remaining arguments, and returns the result.
|
||||
Throws if t is not mapped to an argument converter.
|
||||
*/
|
||||
cache.xWrap.convertArg = (t,...args)=>__xArgAdapterCheck(t)(...args);
|
||||
/**
|
||||
Identical to convertArg() except that it does not perform
|
||||
an is-defined check on the mapping to t before invoking it.
|
||||
*/
|
||||
cache.xWrap.convertArgNoCheck = (t,...args)=>xArg.get(t)(...args);
|
||||
|
||||
/**
|
||||
Fetches the xWrap() result adapter mapped to t, calls it, passing
|
||||
it v, and returns the result. Throws if t is not mapped to an
|
||||
argument converter.
|
||||
*/
|
||||
cache.xWrap.convertResult =
|
||||
(t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
|
||||
/**
|
||||
Identical to convertResult() except that it does not perform an
|
||||
is-defined check on the mapping to t before invoking it.
|
||||
*/
|
||||
cache.xWrap.convertResultNoCheck =
|
||||
(t,v)=>(null===t ? v : (t ? xResult.get(t)(v) : undefined));
|
||||
|
||||
|
||||
@@ -2899,6 +2899,58 @@ globalThis.sqlite3InitModule = sqlite3InitModule;
|
||||
}
|
||||
})/*session API sanity tests*/
|
||||
;/*end of session API group*/;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('FTS5')
|
||||
.t({
|
||||
name: "Sanity checks",
|
||||
predicate: (sqlite3)=>sqlite3.fts5 || "Missing sqlite3.fts5 namespace.",
|
||||
test: function(sqlite3){
|
||||
const db = new sqlite3.oo1.DB();
|
||||
db.exec([
|
||||
"create virtual table ft using fts5(a,b);",
|
||||
"insert into ft(a,b) values",
|
||||
"('a1','b1'),",
|
||||
"('a2','b2'),",
|
||||
"('a3','b3');"
|
||||
]);
|
||||
const fts = sqlite3.fts5;
|
||||
let pApi = fts.fts5_api_from_db(db);
|
||||
T.assert( !!pApi );
|
||||
let fApi = new fts.fts5_api(pApi);
|
||||
T.assert( fApi.$iVersion >= 2 );
|
||||
fApi.dispose();
|
||||
fApi = undefined;
|
||||
let destroyCalled = false;
|
||||
fts.createFunction(db, {
|
||||
name: 'mymatch',
|
||||
xFunction: function(pFtsX, pFtsCx, pCtx, argv){
|
||||
// Both of these return-value approaches are equivalent:
|
||||
const rv = "MY+"+argv.join(':');
|
||||
if(0){
|
||||
return rv;
|
||||
}else{
|
||||
capi.sqlite3_result_text(pCtx, rv, -1, capi.SQLITE_TRANSIENT);
|
||||
// implicit return of undefined
|
||||
}
|
||||
},
|
||||
xDestroy: function(){
|
||||
destroyCalled = true;
|
||||
}
|
||||
});
|
||||
let list = db.selectValues(
|
||||
"select mymatch(ft,a,b) from ft where b match 'b2'"
|
||||
);
|
||||
T.assert( 1 === list.length )
|
||||
.assert( 'MY+a2:b2' === list[0] );
|
||||
|
||||
//const fTok = new fts.fts5_tokenizer();
|
||||
//fTok.installMethods({});
|
||||
|
||||
db.close();
|
||||
T.assert( destroyCalled );
|
||||
//toss("Testing");
|
||||
}
|
||||
})/*FTS5*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
T.g('OPFS: Origin-Private File System',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C Performance\soptimization\sfor\sJSON\srendering\slogic.
|
||||
D 2023-08-02T16:06:02.660
|
||||
C More\swork\stowards\sfts5\scustomzation\sin\sJS.
|
||||
D 2023-08-04T08:39:21.730
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -487,10 +487,11 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c
|
||||
F ext/wasm/GNUmakefile ddf1aede4275e404c7eda782462c33b6406fcd2dd327241f6b22c0f7b80938e4
|
||||
F ext/wasm/GNUmakefile a33d4acc4fab03bd4a1c76243ec1e43abaeb6e6e29f0971444bcb008ec6420ec
|
||||
F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576
|
||||
F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-fts5 abdb0216e3035d48f797302f8712dd31d917b4dd7dfdb7957428f8046ebaf1b1
|
||||
F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b
|
||||
F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287
|
||||
F ext/wasm/api/README.md 5eb44fa02e9c693a1884a3692428647894b0380b24bca120866b7a24c8786134
|
||||
@@ -499,17 +500,18 @@ F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e
|
||||
F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08902f15c34720ee4a1
|
||||
F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
|
||||
F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e
|
||||
F ext/wasm/api/sqlite3-api-glue.js cc6b0bb093bdb6279d4af259200b7b9e150e3796a8a3a4cd09a4928c43d25e56
|
||||
F ext/wasm/api/sqlite3-api-cleanup.js 8489ac9933783f36807cfa5d79bfa366008c0443b42bf47bdef41bbfce4aa367
|
||||
F ext/wasm/api/sqlite3-api-glue.js 861ff5ccfbaa50579c46618be53ac374202ad73cc27eb5077967c35edbb1869d
|
||||
F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
|
||||
F ext/wasm/api/sqlite3-api-prologue.js cbd7d6ba185f3a844a8b0020e954b49bbc2ca78b305d117bec2ceca21431795a
|
||||
F ext/wasm/api/sqlite3-api-prologue.js 76258e160bf6a89cc75a7d3c05646a054c8cab7219cd1e10bc20cacaad022131
|
||||
F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
|
||||
F ext/wasm/api/sqlite3-fts5-helper.js b7716c46acfc591421aa6f92c3962764666786a92b5bc116e85523c2d15f3f09
|
||||
F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
|
||||
F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379
|
||||
F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25
|
||||
F ext/wasm/api/sqlite3-v-helper.js 8dc3da6e69d51f455b64cc88fec7977f528d79ec267cfcdd97b606c8cc4cd156
|
||||
F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 54cee22aacadb9dfaea438d72ac0882249d028c37903208d48c52871290ceff7
|
||||
F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e7a690e0e78ff4d563f2eca468f91db69f001ff4b79c6d2304cbb6f62dca437d
|
||||
F ext/wasm/api/sqlite3-wasm.c 8867f1d41c112fb4a2cfe22ff224eccaf309fcdea266cee0ec554f85db72ef0f
|
||||
F ext/wasm/api/sqlite3-wasm.c 4f0ff557fa67a4b8da8a68a6736e71265378b9d1a24ac33165a7d58fcad6cda4
|
||||
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
|
||||
F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
|
||||
F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
|
||||
@@ -518,7 +520,7 @@ F ext/wasm/c-pp.c 6d80d8569d85713effe8b0818a3cf51dc779e3f0bf8dc88771b8998552ee25
|
||||
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
|
||||
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
|
||||
F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f
|
||||
F ext/wasm/common/whwasmutil.js db6368ee57af90ee6691b6fb3ca97ee8064d12482e06a29113127c67b08f956e
|
||||
F ext/wasm/common/whwasmutil.js fc923692f51b9a47cb6c3d0ab350db542cf6774c39d9e4ed9e894b55180d6989
|
||||
F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
|
||||
F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
|
||||
F ext/wasm/demo-123.js 38aa8faec4d0ace1c973bc8a7a1533584463ebeecd4c420daa7d9687beeb9cb5
|
||||
@@ -554,7 +556,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
|
||||
F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b
|
||||
F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c
|
||||
F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2
|
||||
F ext/wasm/tester1.c-pp.js b88dcad5424a652e8204c44a71bbc3deb22a4922c97ba792aedbabb7a6827b91
|
||||
F ext/wasm/tester1.c-pp.js 5275b89d3657e1ca1b868f227539136a4642d42474e0202191fff9d7f4f91fff
|
||||
F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1
|
||||
F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d
|
||||
F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
|
||||
@@ -2049,8 +2051,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P c4347e4400e96f932ac12f8f22484a2ebce2a578d1b2181977954c432f117bfd
|
||||
R a3168d15b44149a629121fdff1d59acd
|
||||
U drh
|
||||
Z 37bd268b83df81194e3e4e7be36a0655
|
||||
P 2666f52e88691201a30dc0f27c294c498bba3f16d5d2dddcadcfac115a3cba20
|
||||
R 643c955846e19c824f4b95b5e770827f
|
||||
U stephan
|
||||
Z 00d6d6bd94d004d3b32085de27313db7
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ea0b9aecbaca9a8e784fd2bcb50f78cbdcf4c5cfb45a7700bb222e4cc104c644
|
||||
ce2a65d80f7793ae8183e311fc7284b37caa78d747436f22496284dfa57e3533
|
||||
Reference in New Issue
Block a user