mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Bind fts5_api::xCreateFunction() to JNI and demonstrate it with a test.
FossilOrigin-Name: c653bf16cbdccae05ab14059b140191afd5c17740fb78d756d8822986e54b17c
This commit is contained in:
@ -209,6 +209,8 @@ static const struct {
|
||||
const char * const Fts5Context;
|
||||
const char * const Fts5ExtensionApi;
|
||||
const char * const fts5_api;
|
||||
const char * const fts5_tokenizer;
|
||||
const char * const Fts5Tokenizer;
|
||||
#endif
|
||||
} S3ClassNames = {
|
||||
"org/sqlite/jni/sqlite3",
|
||||
@ -222,7 +224,9 @@ static const struct {
|
||||
#ifdef SQLITE_ENABLE_FTS5
|
||||
"org/sqlite/jni/Fts5Context",
|
||||
"org/sqlite/jni/Fts5ExtensionApi",
|
||||
"org/sqlite/jni/fts5_api"
|
||||
"org/sqlite/jni/fts5_api",
|
||||
"org/sqlite/jni/fts5_tokenizer",
|
||||
"org/sqlite/jni/Fts5Tokenizer"
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -304,9 +308,9 @@ enum {
|
||||
Need enough space for (only) the library's NativePointerHolder
|
||||
types, a fixed count known at build-time. If we add more than this
|
||||
a fatal error will be triggered with a reminder to increase this.
|
||||
This value needs to be, at most, the number of entries in the
|
||||
S3ClassNames object, as that value is our upper limit. The
|
||||
S3ClassNames entries are the keys for this particular cache.
|
||||
This value needs to be exactly the number of entries in the
|
||||
S3ClassNames object. The S3ClassNames entries are the keys for
|
||||
this particular cache.
|
||||
*/
|
||||
NphCache_SIZE = sizeof(S3ClassNames) / sizeof(char const *)
|
||||
};
|
||||
@ -358,6 +362,12 @@ struct JNIEnvCacheLine {
|
||||
JNIEnvCacheLine * pPrev /* Previous entry in the linked list */;
|
||||
JNIEnvCacheLine * pNext /* Next entry in the linked list */;
|
||||
#endif
|
||||
/** TODO: NphCacheLine *pNphHit;
|
||||
|
||||
to help fast-track cache lookups, update this to point to the
|
||||
most recent hit. That will speed up, e.g. the
|
||||
sqlite3_value-to-Java-array loop.
|
||||
*/
|
||||
struct NphCacheLine nph[NphCache_SIZE];
|
||||
};
|
||||
typedef struct JNIEnvCache JNIEnvCache;
|
||||
@ -1268,49 +1278,55 @@ typedef struct {
|
||||
Converts the given (cx, argc, argv) into arguments for the given
|
||||
UDF, placing the result in the final argument. Returns 0 on
|
||||
success, SQLITE_NOMEM on allocation error.
|
||||
|
||||
TODO: see what we can do to optimize the
|
||||
new_sqlite3_value_wrapper() call. e.g. find the ctor a single time
|
||||
and call it here, rather than looking it up repeatedly.
|
||||
*/
|
||||
static int udf_args(sqlite3_context * const cx,
|
||||
static int udf_args(JNIEnv *env,
|
||||
sqlite3_context * const cx,
|
||||
int argc, sqlite3_value**argv,
|
||||
UDFState * const s,
|
||||
udf_jargs * const args){
|
||||
jobject * jCx, jobjectArray *jArgv){
|
||||
jobjectArray ja = 0;
|
||||
JNIEnv * const env = s->env;
|
||||
jobject jcx = new_sqlite3_context_wrapper(s->env, cx);
|
||||
jobject jcx = new_sqlite3_context_wrapper(env, cx);
|
||||
jint i;
|
||||
args->jcx = 0;
|
||||
args->jargv = 0;
|
||||
*jCx = 0;
|
||||
*jArgv = 0;
|
||||
if(!jcx) goto error_oom;
|
||||
ja = (*(s->env))->NewObjectArray(s->env, argc,
|
||||
S3Global_env_cache(env)->globalClassObj,
|
||||
NULL);
|
||||
ja = (*env)->NewObjectArray(env, argc,
|
||||
S3Global_env_cache(env)->globalClassObj,
|
||||
NULL);
|
||||
if(!ja) goto error_oom;
|
||||
for(i = 0; i < argc; ++i){
|
||||
jobject jsv = new_sqlite3_value_wrapper(s->env, argv[i]);
|
||||
jobject jsv = new_sqlite3_value_wrapper(env, argv[i]);
|
||||
if(!jsv) goto error_oom;
|
||||
(*env)->SetObjectArrayElement(env, ja, i, jsv);
|
||||
UNREF_L(jsv)/*array has a ref*/;
|
||||
}
|
||||
args->jcx = jcx;
|
||||
args->jargv = ja;
|
||||
*jCx = jcx;
|
||||
*jArgv = ja;
|
||||
return 0;
|
||||
error_oom:
|
||||
sqlite3_result_error_nomem(cx);
|
||||
UNREF_L(jcx);
|
||||
UNREF_L(ja);
|
||||
return 1;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
static int udf_report_exception(sqlite3_context * cx, UDFState *s,
|
||||
static int udf_report_exception(sqlite3_context * cx,
|
||||
const char *zFuncName,
|
||||
const char *zFuncType){
|
||||
int rc;
|
||||
char * z =
|
||||
sqlite3_mprintf("UDF %s.%s() threw. It should not do that.",
|
||||
s->zFuncName, zFuncType);
|
||||
sqlite3_mprintf("Client-defined function %s.%s() threw. It should "
|
||||
"not do that.",
|
||||
zFuncName ? zFuncName : "<unnamed>", zFuncType);
|
||||
if(z){
|
||||
sqlite3_result_error(cx, z, -1);
|
||||
sqlite3_free(z);
|
||||
rc = SQLITE_ERROR;
|
||||
}else{
|
||||
sqlite3_result_error_nomem(cx);
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
return rc;
|
||||
@ -1325,9 +1341,9 @@ static int udf_xFSI(sqlite3_context* pCx, int argc,
|
||||
UDFState * s,
|
||||
jmethodID xMethodID,
|
||||
const char * zFuncType){
|
||||
udf_jargs args;
|
||||
JNIEnv * const env = s->env;
|
||||
int rc = udf_args(pCx, argc, argv, s, &args);
|
||||
udf_jargs args = {0,0};
|
||||
int rc = udf_args(s->env, pCx, argc, argv, &args.jcx, &args.jargv);
|
||||
//MARKER(("%s.%s() pCx = %p\n", s->zFuncName, zFuncType, pCx));
|
||||
if(rc) return rc;
|
||||
//MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType));
|
||||
@ -1337,7 +1353,7 @@ static int udf_xFSI(sqlite3_context* pCx, int argc,
|
||||
if( 0 == rc ){
|
||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
|
||||
IFTHREW{
|
||||
rc = udf_report_exception(pCx,s, zFuncType);
|
||||
rc = udf_report_exception(pCx, s->zFuncName, zFuncType);
|
||||
}
|
||||
}
|
||||
UNREF_L(args.jcx);
|
||||
@ -1367,7 +1383,7 @@ static int udf_xFV(sqlite3_context* cx, UDFState * s,
|
||||
if( 0 == rc ){
|
||||
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx);
|
||||
IFTHREW{
|
||||
rc = udf_report_exception(cx,s, zFuncType);
|
||||
rc = udf_report_exception(cx,s->zFuncName, zFuncType);
|
||||
}
|
||||
}
|
||||
UNREF_L(jcx);
|
||||
@ -2636,6 +2652,8 @@ JDECL(void,1do_1something_1for_1developer)(JENV_JSELF){
|
||||
Java_org_sqlite_jni_Fts5ExtensionApi_ ## Suffix
|
||||
#define JFuncNameFtsApi(Suffix) \
|
||||
Java_org_sqlite_jni_fts5_1api_ ## Suffix
|
||||
#define JFuncNameFtsTok(Suffix) \
|
||||
Java_org_sqlite_jni_fts5_tokenizer_ ## Suffix
|
||||
|
||||
#define JDECLFtsXA(ReturnType,Suffix) \
|
||||
JNIEXPORT ReturnType JNICALL \
|
||||
@ -2643,22 +2661,82 @@ JDECL(void,1do_1something_1for_1developer)(JENV_JSELF){
|
||||
#define JDECLFtsApi(ReturnType,Suffix) \
|
||||
JNIEXPORT ReturnType JNICALL \
|
||||
JFuncNameFtsApi(Suffix)
|
||||
#define JDECLFtsTok(ReturnType,Suffix) \
|
||||
JNIEXPORT ReturnType JNICALL \
|
||||
JFuncNameFtsTok(Suffix)
|
||||
|
||||
#define PtrGet_fts5_api(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_api)
|
||||
#define PtrGet_fts5_tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_tokenizer)
|
||||
#define PtrGet_Fts5Context(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Context)
|
||||
#define PtrGet_Fts5Tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Tokenizer)
|
||||
|
||||
/**
|
||||
State for binding Java-side FTS5 auxiliary functions.
|
||||
*/
|
||||
typedef struct {
|
||||
JNIEnv * env; /* env registered from */;
|
||||
jobject jObj /* functor instance */;
|
||||
jclass klazz /* jObj's class */;
|
||||
char * zFuncName /* Only for error reporting and debug logging */;
|
||||
jmethodID jmid /* callback member's method ID */;
|
||||
} Fts5JniAux;
|
||||
|
||||
static void Fts5JniAux_free(Fts5JniAux * const s){
|
||||
JNIEnv * const env = s->env;
|
||||
if(env){
|
||||
/*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/
|
||||
s3jni_call_xDestroy(env, s->jObj, s->klazz);
|
||||
UNREF_G(s->jObj);
|
||||
UNREF_G(s->klazz);
|
||||
}
|
||||
sqlite3_free(s->zFuncName);
|
||||
sqlite3_free(s);
|
||||
}
|
||||
|
||||
static void Fts5JniAux_xDestroy(void *p){
|
||||
if(p) Fts5JniAux_free(p);
|
||||
}
|
||||
|
||||
static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){
|
||||
Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux));
|
||||
if(s){
|
||||
const char * zSig =
|
||||
"(Lorg/sqlite/jni/Fts5ExtensionApi;"
|
||||
"Lorg/sqlite/jni/Fts5Context;"
|
||||
"Lorg/sqlite/jni/sqlite3_context;"
|
||||
"[Lorg/sqlite/jni/sqlite3_value;)V";
|
||||
memset(s, 0, sizeof(Fts5JniAux));
|
||||
s->env = env;
|
||||
s->jObj = REF_G(jObj);
|
||||
s->klazz = REF_G((*env)->GetObjectClass(env, jObj));
|
||||
EXCEPTION_IS_FATAL("Cannot get class for FTS5 aux function object.");
|
||||
s->jmid = (*env)->GetMethodID(env, s->klazz, "xFunction", zSig);
|
||||
IFTHREW{
|
||||
EXCEPTION_REPORT;
|
||||
EXCEPTION_CLEAR;
|
||||
Fts5JniAux_free(s);
|
||||
s = 0;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline Fts5ExtensionApi const * s3jni_ftsext(void){
|
||||
return &sFts5Api/*singleton from sqlite3.c*/;
|
||||
}
|
||||
#define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
|
||||
|
||||
#if 0
|
||||
static jobject new_Fts5Context_wrapper(JNIEnv * const env, Fts5Context *sv){
|
||||
return new_NativePointerHolder_object(env, S3ClassNames.Fts5Context, sv);
|
||||
}
|
||||
#endif
|
||||
static jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){
|
||||
return new_NativePointerHolder_object(env, S3ClassNames.fts5_api, sv);
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton
|
||||
instance, or NULL on OOM.
|
||||
*/
|
||||
static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
|
||||
JNIEnvCacheLine * const row = S3Global_env_cache(env);
|
||||
if( !row->jFtsExt ){
|
||||
@ -2670,9 +2748,9 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
|
||||
}
|
||||
|
||||
/*
|
||||
** 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()).
|
||||
** Return a pointer to the fts5_api instance for database connection
|
||||
** db. If an error occurs, return NULL and leave an error in the
|
||||
** database handle (accessible using sqlite3_errcode()/errmsg()).
|
||||
*/
|
||||
static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){
|
||||
fts5_api *pRet = 0;
|
||||
@ -2759,6 +2837,73 @@ JDECLFtsXA(jint,xColumnTotalSize)(JENV_JSELF,jobject jCtx, jint iCol, jobject jO
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
/**
|
||||
Proxy for fts5_extension_function instances plugged in via
|
||||
fts5_api::xCreateFunction().
|
||||
*/
|
||||
static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
|
||||
Fts5Context *pFts,
|
||||
sqlite3_context *pCx,
|
||||
int argc,
|
||||
sqlite3_value **argv){
|
||||
Fts5JniAux * const pAux = pApi->xUserData(pFts);
|
||||
JNIEnv *env;
|
||||
jobject jpCx = 0;
|
||||
jobjectArray jArgv = 0;
|
||||
jobject jpFts = 0;
|
||||
jobject jFXA;
|
||||
int rc;
|
||||
assert(pAux);
|
||||
env = pAux->env;
|
||||
jFXA = s3jni_getFts5ExensionApi(env);
|
||||
if( !jFXA ) goto error_oom;
|
||||
jpFts = new_Fts5Context_wrapper(env, pFts);
|
||||
if(!jpFts) goto error_oom;
|
||||
rc = udf_args(env, pCx, argc, argv, &jpCx, &jArgv);
|
||||
if(rc) goto error_oom;
|
||||
(*env)->CallVoidMethod(env, pAux->jObj, pAux->jmid,
|
||||
jFXA, jpFts, jpCx, jArgv);
|
||||
IFTHREW{
|
||||
EXCEPTION_CLEAR;
|
||||
udf_report_exception(pCx, pAux->zFuncName, "xFunction");
|
||||
}
|
||||
UNREF_L(jpFts);
|
||||
UNREF_L(jpCx);
|
||||
UNREF_L(jArgv);
|
||||
return;
|
||||
error_oom:
|
||||
assert( !jArgv );
|
||||
assert( !jpCx );
|
||||
UNREF_L(jpFts);
|
||||
sqlite3_result_error_nomem(pCx);
|
||||
return;
|
||||
}
|
||||
|
||||
JDECLFtsApi(jint,xCreateFunction)(JENV_JSELF, jstring jName, jobject jFunc){
|
||||
fts5_api * const pApi = PtrGet_fts5_api(jSelf);
|
||||
int rc;
|
||||
char const * zName;
|
||||
Fts5JniAux * pAux;
|
||||
assert(pApi);
|
||||
zName = JSTR_TOC(jName);
|
||||
if(!zName) return SQLITE_NOMEM;
|
||||
pAux = Fts5JniAux_alloc(env, jFunc);
|
||||
if( pAux ){
|
||||
rc = pApi->xCreateFunction(pApi, zName, pAux,
|
||||
s3jni_fts5_extension_function,
|
||||
Fts5JniAux_xDestroy);
|
||||
}else{
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( 0==rc ){
|
||||
pAux->zFuncName = sqlite3_mprintf("%s", zName);
|
||||
/* OOM here is non-fatal. Ignore it. */
|
||||
}
|
||||
JSTR_RELEASE(jName, zName);
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
|
||||
typedef struct s3jni_fts5AuxData s3jni_fts5AuxData;
|
||||
struct s3jni_fts5AuxData {
|
||||
JNIEnv *env;
|
||||
@ -3022,10 +3167,7 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_JSELF,jobject jCtx, jobject jAux){
|
||||
}
|
||||
|
||||
/**
|
||||
xToken() imp for xTokenize().
|
||||
|
||||
TODO: hold on to the byte array and avoid initializing
|
||||
it if passed the same (z,nZ) as a previous call.
|
||||
xToken() impl for xTokenize().
|
||||
*/
|
||||
static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z,
|
||||
int nZ, int iStart, int iEnd){
|
||||
@ -3052,12 +3194,16 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z,
|
||||
return rc;
|
||||
}
|
||||
|
||||
JDECLFtsXA(jint,xTokenize)(JENV_JSELF,jobject jFcx, jbyteArray jbaText,
|
||||
jobject jCallback){
|
||||
/**
|
||||
Proxy for Fts5ExtensionApi.xTokenize() and fts5_tokenizer.xTokenize()
|
||||
*/
|
||||
static jint s3jni_fts5_xTokenize(JENV_JSELF, const char *zClassName,
|
||||
jint tokFlags, jobject jFcx,
|
||||
jbyteArray jbaText, jobject jCallback){
|
||||
Fts5ExtDecl;
|
||||
JNIEnvCacheLine * const jc = S3Global_env_cache(env);
|
||||
struct s3jni_xQueryPhraseState s;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
jbyte * const pText = JBA_TOC(jbaText);
|
||||
jsize nText = (*env)->GetArrayLength(env, jbaText);
|
||||
jclass const klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
|
||||
@ -3082,9 +3228,18 @@ JDECLFtsXA(jint,xTokenize)(JENV_JSELF,jobject jFcx, jbyteArray jbaText,
|
||||
s.tok.jba = REF_L(jbaText);
|
||||
s.tok.zPrev = (const char *)pText;
|
||||
s.tok.nPrev = (int)nText;
|
||||
rc = fext->xTokenize(PtrGet_Fts5Context(jFcx),
|
||||
(const char *)pText, (int)nText,
|
||||
&s, s3jni_xTokenize_xToken);
|
||||
if( zClassName == S3ClassNames.Fts5ExtensionApi ){
|
||||
rc = fext->xTokenize(PtrGet_Fts5Context(jFcx),
|
||||
(const char *)pText, (int)nText,
|
||||
&s, s3jni_xTokenize_xToken);
|
||||
}else if( zClassName == S3ClassNames.fts5_tokenizer ){
|
||||
fts5_tokenizer * const pTok = PtrGet_fts5_tokenizer(jSelf);
|
||||
rc = pTok->xTokenize(PtrGet_Fts5Tokenizer(jFcx), &s, tokFlags,
|
||||
(const char *)pText, (int)nText,
|
||||
s3jni_xTokenize_xToken);
|
||||
}else{
|
||||
(*env)->FatalError(env, "This cannot happen. Maintenance required.");
|
||||
}
|
||||
if(s.tok.jba){
|
||||
assert( s.tok.zPrev );
|
||||
UNREF_L(s.tok.jba);
|
||||
@ -3093,6 +3248,18 @@ JDECLFtsXA(jint,xTokenize)(JENV_JSELF,jobject jFcx, jbyteArray jbaText,
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
JDECLFtsXA(jint,xTokenize)(JENV_JSELF,jobject jFcx, jbyteArray jbaText,
|
||||
jobject jCallback){
|
||||
return s3jni_fts5_xTokenize(env, jSelf, S3ClassNames.Fts5ExtensionApi,
|
||||
0, jFcx, jbaText, jCallback);
|
||||
}
|
||||
|
||||
JDECLFtsTok(jint,xTokenize)(JENV_JSELF,jobject jFcx, jint tokFlags,
|
||||
jbyteArray jbaText, jobject jCallback){
|
||||
return s3jni_fts5_xTokenize(env, jSelf, S3ClassNames.Fts5Tokenizer,
|
||||
tokFlags, jFcx, jbaText, jCallback);
|
||||
}
|
||||
|
||||
|
||||
#endif /* SQLITE_ENABLE_FTS5 */
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1771,7 +1771,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_Fts5ExtensionApi_xSetAuxdata
|
||||
/*
|
||||
* Class: org_sqlite_jni_Fts5ExtensionApi
|
||||
* Method: xTokenize
|
||||
* Signature: (Lorg/sqlite/jni/Fts5Context;[BLorg/sqlite/jni/Fts5ExtensionApi/xTokenizeCallback;)I
|
||||
* Signature: (Lorg/sqlite/jni/Fts5Context;[BLorg/sqlite/jni/Fts5/xTokenizeCallback;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_Fts5ExtensionApi_xTokenize
|
||||
(JNIEnv *, jobject, jobject, jbyteArray, jobject);
|
||||
@ -1797,6 +1797,35 @@ extern "C" {
|
||||
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_fts5_1api_getInstanceForDb
|
||||
(JNIEnv *, jclass, jobject);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_fts5_api
|
||||
* Method: xCreateFunction
|
||||
* Signature: (Ljava/lang/String;Lorg/sqlite/jni/fts5_api/fts5_extension_function;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_fts5_1api_xCreateFunction
|
||||
(JNIEnv *, jobject, jstring, jobject);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class org_sqlite_jni_fts5_tokenizer */
|
||||
|
||||
#ifndef _Included_org_sqlite_jni_fts5_tokenizer
|
||||
#define _Included_org_sqlite_jni_fts5_tokenizer
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: org_sqlite_jni_fts5_tokenizer
|
||||
* Method: xTokenize
|
||||
* Signature: (Lorg/sqlite/jni/Fts5Tokenizer;I[BLorg/sqlite/jni/Fts5/xTokenizeCallback;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_fts5_1tokenizer_xTokenize
|
||||
(JNIEnv *, jobject, jobject, jint, jbyteArray, jobject);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
38
ext/jni/src/org/sqlite/jni/Fts5.java
Normal file
38
ext/jni/src/org/sqlite/jni/Fts5.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** 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 is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (fts5_api*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class Fts5 {
|
||||
/* Not used */
|
||||
private Fts5(){}
|
||||
|
||||
//! Callback type for use with xTokenize() variants
|
||||
public static interface xTokenizeCallback {
|
||||
int xToken(int tFlags, byte txt[], int iStart, int iEnd);
|
||||
}
|
||||
|
||||
public static final int FTS5_TOKENIZE_QUERY = 0x0001;
|
||||
public static final int FTS5_TOKENIZE_PREFIX = 0x0002;
|
||||
public static final int FTS5_TOKENIZE_DOCUMENT = 0x0004;
|
||||
public static final int FTS5_TOKENIZE_AUX = 0x0008;
|
||||
public static final int FTS5_TOKEN_COLOCATED = 0x0001;
|
||||
}
|
@ -27,10 +27,6 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
private Fts5ExtensionApi(){}
|
||||
private int iVersion = 2;
|
||||
|
||||
//! Callback type for use with xTokenize().
|
||||
public static interface xTokenizeCallback {
|
||||
int xToken(int tFlags, byte txt[], int iStart, int iEnd);
|
||||
}
|
||||
public static interface xQueryPhraseCallback {
|
||||
int xCallback(Fts5ExtensionApi fapi, Fts5Context cx);
|
||||
}
|
||||
@ -68,7 +64,6 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol,
|
||||
@NotNull OutputPointer.Int32 iOff);
|
||||
|
||||
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
@ -80,11 +75,8 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
@NotNull Fts5PhraseIter iter,
|
||||
@NotNull OutputPointer.Int32 iCol);
|
||||
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
|
||||
|
||||
|
||||
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
|
||||
@NotNull xQueryPhraseCallback callback);
|
||||
|
||||
public native int xRowCount(@NotNull Fts5Context fcx,
|
||||
@NotNull OutputPointer.Int64 nRow);
|
||||
public native long xRowid(@NotNull Fts5Context cx);
|
||||
@ -95,11 +87,12 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
|
||||
the JNI layer will be relinquished regardless of whther pAux has
|
||||
an xDestroy() method. */
|
||||
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
|
||||
|
||||
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte pText[],
|
||||
@NotNull xTokenizeCallback callback);
|
||||
@NotNull Fts5.xTokenizeCallback callback);
|
||||
|
||||
/**************************************************************
|
||||
void *(*xUserData)(Fts5Context*);
|
||||
^^^ returns the pointer passed as the 3rd arg to
|
||||
fts5_api::xCreateFunction.
|
||||
**************************************************************/
|
||||
}
|
||||
|
30
ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java
Normal file
30
ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
** 2023-08-05x
|
||||
**
|
||||
** 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 is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (Fts5Tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
|
||||
At the C level, the Fts5Tokenizer type is essentially a void
|
||||
pointer used specifically for tokenizers.
|
||||
*/
|
||||
public final class Fts5Tokenizer extends NativePointerHolder<Fts5Tokenizer> {
|
||||
//! Only called from JNI.
|
||||
private Fts5Tokenizer(){}
|
||||
}
|
@ -288,11 +288,6 @@ public final class SQLite3Jni {
|
||||
int eTextRep,
|
||||
@NotNull Collation col);
|
||||
|
||||
//Potential TODO, if we can sensibly map the lower-level bits to Java:
|
||||
//public static native int sqlite3_create_fts5_function(@NotNull sqlite3 db,
|
||||
// @NotNull String functionName,
|
||||
// @NotNull Fts5Function func);
|
||||
|
||||
/**
|
||||
The Java counterpart to the C-native sqlite3_create_function(),
|
||||
sqlite3_create_function_v2(), and
|
||||
|
@ -83,7 +83,8 @@ public class Tester1 {
|
||||
affirm(0 == rc);
|
||||
pos = oTail.getValue();
|
||||
affirm(0 != stmt.getNativePointer());
|
||||
rc = sqlite3_step(stmt);
|
||||
while( SQLITE_ROW == (rc = sqlite3_step(stmt)) ){
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
affirm(0 == stmt.getNativePointer());
|
||||
if(0!=rc && SQLITE_ROW!=rc && SQLITE_DONE!=rc){
|
||||
|
@ -27,7 +27,45 @@ public class TesterFts5 {
|
||||
fts5_api fApi = fts5_api.getInstanceForDb(db);
|
||||
affirm( fApi != null );
|
||||
affirm( fApi == fts5_api.getInstanceForDb(db) /* singleton per db */ );
|
||||
|
||||
execSql(db, new String[] {
|
||||
"CREATE VIRTUAL TABLE ft USING fts5(a, b);",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(1, 'X Y', 'Y Z');",
|
||||
"INSERT INTO ft(rowid, a, b) VALUES(2, 'A Z', 'Y Y');"
|
||||
});
|
||||
|
||||
ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
|
||||
ValueHolder<Integer> xFuncCount = new ValueHolder<>(0);
|
||||
fts5_api.fts5_extension_function func = new fts5_api.fts5_extension_function(){
|
||||
|
||||
public void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]){
|
||||
int nCols = ext.xColumnCount(fCx);
|
||||
affirm( 2 == nCols );
|
||||
if(false){
|
||||
OutputPointer.String op = new OutputPointer.String();
|
||||
for(int i = 0; i < nCols; ++i ){
|
||||
int rc = ext.xColumnText(fCx, i, op);
|
||||
affirm( 0 == rc );
|
||||
outln("xFunction col "+i+": "+op.getValue());
|
||||
}
|
||||
}
|
||||
++xFuncCount.value;
|
||||
}
|
||||
public void xDestroy(){
|
||||
xDestroyCalled.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
int rc = fApi.xCreateFunction("myaux", func);
|
||||
affirm( 0==rc );
|
||||
|
||||
affirm( 0==xFuncCount.value );
|
||||
execSql(db, "select myaux(ft,a,b) from ft;");
|
||||
affirm( 2==xFuncCount.value );
|
||||
affirm( !xDestroyCalled.value );
|
||||
sqlite3_close_v2(db);
|
||||
affirm( xDestroyCalled.value );
|
||||
}
|
||||
|
||||
public TesterFts5(){
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
** 2023-08-04
|
||||
** 2023-08-05
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
@ -22,12 +22,57 @@ package org.sqlite.jni;
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_api extends NativePointerHolder<fts5_api> {
|
||||
/* Only invoked by JNI */
|
||||
/* Only invoked from JNI */
|
||||
private fts5_api(){}
|
||||
public final int iVersion = 2;
|
||||
|
||||
/**
|
||||
Returns the fts5_api instance associated with the given db, or
|
||||
null if something goes horribly wrong.
|
||||
*/
|
||||
public static native fts5_api getInstanceForDb(@NotNull sqlite3 db);
|
||||
|
||||
public static abstract class fts5_extension_function {
|
||||
public abstract void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
|
||||
sqlite3_context pCx, sqlite3_value argv[]);
|
||||
//! Optionally override
|
||||
public void xDestroy(){}
|
||||
}
|
||||
|
||||
// int (*xCreateTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_tokenizer *pTokenizer,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
// /* Find an existing tokenizer */
|
||||
// int (*xFindTokenizer)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void **ppContext,
|
||||
// fts5_tokenizer *pTokenizer
|
||||
// );
|
||||
|
||||
// /* Create a new auxiliary function */
|
||||
// int (*xCreateFunction)(
|
||||
// fts5_api *pApi,
|
||||
// const char *zName,
|
||||
// void *pContext,
|
||||
// fts5_extension_function xFunction,
|
||||
// void (*xDestroy)(void*)
|
||||
// );
|
||||
|
||||
public native int xCreateFunction(@NotNull String name,
|
||||
@NotNull fts5_extension_function xFunction);
|
||||
|
||||
// typedef void (*fts5_extension_function)(
|
||||
// const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
|
||||
// Fts5Context *pFts, /* First arg to pass to pApi functions */
|
||||
// sqlite3_context *pCtx, /* Context for returning result/error */
|
||||
// int nVal, /* Number of values in apVal[] array */
|
||||
// sqlite3_value **apVal /* Array of trailing arguments */
|
||||
// );
|
||||
|
||||
}
|
||||
|
49
ext/jni/src/org/sqlite/jni/fts5_tokenizer.java
Normal file
49
ext/jni/src/org/sqlite/jni/fts5_tokenizer.java
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
** 2023-08-05
|
||||
**
|
||||
** 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 is part of the JNI bindings for the sqlite3 C API.
|
||||
*/
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
INCOMPLETE AND COMPLETELY UNTESTED.
|
||||
|
||||
A wrapper for communicating C-level (fts5_tokenizer*) instances with
|
||||
Java. These wrappers do not own their associated pointer, they
|
||||
simply provide a type-safe way to communicate it between Java and C
|
||||
via JNI.
|
||||
*/
|
||||
public final class fts5_tokenizer extends NativePointerHolder<fts5_tokenizer> {
|
||||
/* Only invoked by JNI */
|
||||
private fts5_tokenizer(){}
|
||||
|
||||
// int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
|
||||
// void (*xDelete)(Fts5Tokenizer*);
|
||||
|
||||
public native int xTokenize(@NotNull Fts5Tokenizer t, int tokFlags,
|
||||
@NotNull byte pText[],
|
||||
@NotNull Fts5.xTokenizeCallback callback);
|
||||
|
||||
|
||||
// int (*xTokenize)(Fts5Tokenizer*,
|
||||
// void *pCtx,
|
||||
// int flags, /* Mask of FTS5_TOKENIZE_* flags */
|
||||
// const char *pText, int nText,
|
||||
// int (*xToken)(
|
||||
// void *pCtx, /* Copy of 2nd argument to xTokenize() */
|
||||
// int tflags, /* Mask of FTS5_TOKEN_* flags */
|
||||
// const char *pToken, /* Pointer to buffer containing token */
|
||||
// int nToken, /* Size of token in bytes */
|
||||
// int iStart, /* Byte offset of token within input text */
|
||||
// int iEnd /* Byte offset of end of token within input text */
|
||||
// )
|
||||
// );
|
||||
}
|
Reference in New Issue
Block a user