]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Further JNI cleanups.
authorstephan <stephan@noemail.net>
Sat, 2 Sep 2023 08:51:14 +0000 (08:51 +0000)
committerstephan <stephan@noemail.net>
Sat, 2 Sep 2023 08:51:14 +0000 (08:51 +0000)
FossilOrigin-Name: 30e38173c3ece0c9f8e7a9710f46cb5e8e8ef101c04531318a7adb070242f5dd

ext/jni/GNUmakefile
ext/jni/src/c/sqlite3-jni.c
ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java
ext/jni/src/org/sqlite/jni/Fts5Function.java [deleted file]
ext/jni/src/org/sqlite/jni/Tester1.java
ext/jni/src/org/sqlite/jni/TesterFts5.java
ext/jni/src/org/sqlite/jni/fts5_api.java
ext/jni/src/org/sqlite/jni/fts5_extension_function.java
manifest
manifest.uuid

index 346351b06b24e44d4d1868a02b16b11cb00e5a50..92819688d847ff9ef14b4343231b897b1ceefeeb 100644 (file)
@@ -110,7 +110,6 @@ ifeq (1,$(enable.fts5))
     Fts5.java \
     Fts5Context.java \
     Fts5ExtensionApi.java \
-    Fts5Function.java \
     Fts5PhraseIter.java \
     Fts5Tokenizer.java \
     TesterFts5.java \
index 906bca2a24a10b78d21d01a78587132fea6dbda0..d54cc1ca9c19df70dc56b76f468ecd3bb6526926 100644 (file)
@@ -269,6 +269,8 @@ static void * s3jni_realloc_or_die(JNIEnv * const env, void * p, size_t n){
 #endif
 //#define S3JniDb_oom(pDb,EXPR) ((EXPR) ? sqlite3OomFault(pDb) : 0)
 
+#define s3jni_db_oom(pDb) (void)((pDb) ? ((pDb)->mallocFailed=1) : 0)
+
 /* Helpers for Java value reference management. */
 static jobject s3jni_ref_global(JNIEnv * const env, jobject const v){
   jobject const rv = v ? (*env)->NewGlobalRef(env, v) : NULL;
@@ -402,7 +404,7 @@ struct S3JniNphClass {
   */
   jclass klazz;
   volatile jmethodID midCtor  /* klazz's no-arg constructor. Used by
-                              ** new_NativePointerHolder_object(). */;
+                              ** NativePointerHolder_new(). */;
   volatile jfieldID fidValue  /* NativePointerHolder.nativePointer or
                               ** OutputPointer.T.value */;
   volatile jfieldID fidAggCtx /* sqlite3_context.aggregateContext, used only
@@ -464,7 +466,10 @@ struct S3JniDb {
 #endif
   } hooks;
 #ifdef SQLITE_ENABLE_FTS5
-  jobject jFtsApi  /* global ref to s3jni_fts5_api_from_db() */;
+  /* FTS5-specific state */
+  struct {
+    jobject jApi  /* global ref to s3jni_fts5_api_from_db() */;
+  } fts;
 #endif
   S3JniDb * pNext /* Next entry in SJG.perDb.aFree */;
 };
@@ -581,18 +586,22 @@ struct S3JniGlobalType {
   /* Global mutex. */
   sqlite3_mutex * mutex;
   /*
-  ** Cache of Java refs and method IDs for NativePointerHolder
-  ** subclasses and OutputPointer.T types.
+  ** Cache of references to Java classes and method IDs for
+  ** NativePointerHolder subclasses and OutputPointer.T types.
   */
-  S3JniNphClass nph[S3Jni_NphCache_size];
+  struct {
+    S3JniNphClass list[S3Jni_NphCache_size];
+    sqlite3_mutex * mutex;    /* mutex for this->list */
+    void const * locker;      /* sanity-checking-only context object
+                                 for this->mutex */
+  } nph;
   /*
   ** Cache of per-thread state.
   */
   struct {
     S3JniEnv * aHead      /* Linked list of in-use instances */;
     S3JniEnv * aFree      /* Linked list of free instances */;
-    sqlite3_mutex * mutex /* mutex for aHead and aFree, and first-time
-                             inits of nph[] entries. */;
+    sqlite3_mutex * mutex /* mutex for aHead and aFree. */;
     void const * locker   /* env mutex is held on this object's behalf.
                              Used only for sanity checking. */;
   } envCache;
@@ -642,7 +651,7 @@ struct S3JniGlobalType {
   } autoExt;
 #ifdef SQLITE_ENABLE_FTS5
   struct {
-    volatile jobject jFtsExt /* Global ref to Java singleton for the
+    volatile jobject jExt /* Global ref to Java singleton for the
                                 Fts5ExtensionApi instance. */;
     struct {
       jfieldID fidA         /* Fts5Phrase::a member */;
@@ -665,7 +674,7 @@ struct S3JniGlobalType {
     volatile unsigned nEnvAlloc;
     volatile unsigned nMutexEnv       /* number of times envCache.mutex was entered for
                                          a S3JniEnv operation. */;
-    volatile unsigned nMutexEnv2      /* number of times envCache.mutex was entered */;
+    volatile unsigned nMutexNph       /* number of times SJG.mutex was entered for NPH init */;
     volatile unsigned nMutexPerDb     /* number of times perDb.mutex was entered */;
     volatile unsigned nMutexAutoExt   /* number of times autoExt.mutex was entered */;
     volatile unsigned nMutexGlobal    /* number of times global mutex was entered. */;
@@ -730,11 +739,11 @@ static void s3jni_incr( volatile unsigned int * const p ){
   SJG.envCache.locker = 0;                           \
   sqlite3_mutex_leave( SJG.envCache.mutex )
 
-#define S3JniAutoExt_mutex_enter                            \
+#define S3JniAutoExt_mutex_enter                        \
   sqlite3_mutex_enter( SJG.autoExt.mutex );             \
   SJG.autoExt.locker = env;                             \
   s3jni_incr( &SJG.metrics.nMutexAutoExt )
-#define S3JniAutoExt_mutex_leave                            \
+#define S3JniAutoExt_mutex_leave                                                \
   assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ); \
   sqlite3_mutex_leave( SJG.autoExt.mutex )
 #define S3JniAutoExt_mutex_assertLocker                     \
@@ -743,29 +752,28 @@ static void s3jni_incr( volatile unsigned int * const p ){
 #define S3JniGlobal_mutex_enter                        \
   sqlite3_mutex_enter( SJG.mutex );                    \
   s3jni_incr(&SJG.metrics.nMutexGlobal);
-#define S3JniGlobal_mutex_leave                         \
-  sqlite3_mutex_leave( SJG.mutex )
-
-#define S3JniNph_mutex_enter                        \
-  S3JniEnv_mutex_assertNotLocker;                   \
-  sqlite3_mutex_enter( SJG.envCache.mutex );        \
-  s3jni_incr( &SJG.metrics.nMutexEnv2 );            \
-  SJG.envCache.locker = env
-#define S3JniNph_mutex_leave                         \
-  S3JniEnv_mutex_assertLocker;                       \
-  SJG.envCache.locker = 0;                           \
-  sqlite3_mutex_leave( SJG.envCache.mutex )
+#define S3JniGlobal_mutex_leave sqlite3_mutex_leave( SJG.mutex )
+
+#define S3JniNph_mutex_enter                                      \
+  sqlite3_mutex_enter( SJG.nph.mutex );                           \
+  assert( !SJG.nph.locker && "Misuse of S3JniGlobal.nph.mutex" ); \
+  s3jni_incr( &SJG.metrics.nMutexNph );                           \
+  SJG.nph.locker = env
+#define S3JniNph_mutex_leave                                              \
+  assert( (env) == SJG.nph.locker && "Misuse of S3JniGlobal.nph.mutex" ); \
+  SJG.nph.locker = 0;                                                     \
+  sqlite3_mutex_leave( SJG.nph.mutex )
 
 #define S3JniDb_mutex_assertLocker \
   assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
-#define S3JniDb_mutex_enter                      \
-  sqlite3_mutex_enter( SJG.perDb.mutex );             \
+#define S3JniDb_mutex_enter                                             \
+  sqlite3_mutex_enter( SJG.perDb.mutex );                               \
   assert( 0==SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
-  s3jni_incr( &SJG.metrics.nMutexPerDb );             \
+  s3jni_incr( &SJG.metrics.nMutexPerDb );                               \
   SJG.perDb.locker = env;
-#define S3JniDb_mutex_leave                      \
+#define S3JniDb_mutex_leave                                                 \
   assert( env == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
-  SJG.perDb.locker = 0;                                                 \
+  SJG.perDb.locker = 0;                                                     \
   sqlite3_mutex_leave( SJG.perDb.mutex )
 
 #else /* SQLITE_THREADSAFE==0 */
@@ -1120,12 +1128,12 @@ static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){
 */
 static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src,
                                  S3JniHook * const dest ){
-  S3JniDb_mutex_enter;
+  S3JniGlobal_mutex_enter;
   *dest = *src;
   if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj);
   if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra);
   dest->doXDestroy = 0;
-  S3JniDb_mutex_leave;
+  S3JniGlobal_mutex_leave;
 }
 #define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest)
 
@@ -1309,7 +1317,7 @@ static int S3JniEnv_uncache(JNIEnv * const env){
 ** (2023-07-31) tests.
 */
 static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){
-  S3JniNphClass * const pNC = &SJG.nph[pRef->index];
+  S3JniNphClass * const pNC = &SJG.nph.list[pRef->index];
 
   assert( (void*)pRef>=(void*)&S3JniNphRefs && (void*)pRef<(void*)(&S3JniNphRefs + 1)
           && "pRef is out of range" );
@@ -1341,7 +1349,7 @@ static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){
 ** that requirement). If necessary, this fetches the jfieldID for
 ** jOut's pRef->zMember, which must be of the type represented by the
 ** JNI type signature pRef->zTypeSig, and stores it in
-** S3JniGlobal.nph[pRef->index].  Fails fatally if the pRef->zMember
+** S3JniGlobal.nph.list[pRef->index].  Fails fatally if the pRef->zMember
 ** property is not found, as that presents a serious internal misuse.
 **
 ** Property lookups are cached on a per-pRef basis.
@@ -1661,8 +1669,9 @@ static void ResultJavaValue_finalizer(void *v){
 ** Always use a static pointer from the S3JniNphRefs struct for the
 ** 2nd argument.
 */
-static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef const * pRef,
-                                              const void * pNative){
+static jobject NativePointerHolder_new(JNIEnv * const env,
+                                       S3JniNphRef const * pRef,
+                                       const void * pNative){
   jobject rv = 0;
   S3JniNphClass * const pNC = s3jni_nph(pRef);
   if( !pNC->midCtor ){
@@ -1680,17 +1689,17 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef co
   return rv;
 }
 
-static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(sqlite3), sv);
+static inline jobject new_java_sqlite3(JNIEnv * const env, sqlite3 *sv){
+  return NativePointerHolder_new(env, S3JniNph(sqlite3), sv);
 }
-static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(sqlite3_context), sv);
+static inline jobject new_java_sqlite3_context(JNIEnv * const env, sqlite3_context *sv){
+  return NativePointerHolder_new(env, S3JniNph(sqlite3_context), sv);
 }
-static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(sqlite3_stmt), sv);
+static inline jobject new_java_sqlite3_stmt(JNIEnv * const env, sqlite3_stmt *sv){
+  return NativePointerHolder_new(env, S3JniNph(sqlite3_stmt), sv);
 }
-static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(sqlite3_value), sv);
+static inline jobject new_java_sqlite3_value(JNIEnv * const env, sqlite3_value *sv){
+  return NativePointerHolder_new(env, S3JniNph(sqlite3_value), sv);
 }
 
 /* Helper typedefs for UDF callback types. */
@@ -1811,7 +1820,7 @@ static int udf_args(JNIEnv *env,
                     int argc, sqlite3_value**argv,
                     jobject * jCx, jobjectArray *jArgv){
   jobjectArray ja = 0;
-  jobject jcx = new_sqlite3_context_wrapper(env, cx);
+  jobject jcx = new_java_sqlite3_context(env, cx);
   jint i;
   *jCx = 0;
   *jArgv = 0;
@@ -1822,7 +1831,7 @@ static int udf_args(JNIEnv *env,
   s3jni_oom_check( ja );
   if( !ja ) goto error_oom;
   for(i = 0; i < argc; ++i){
-    jobject jsv = new_sqlite3_value_wrapper(env, argv[i]);
+    jobject jsv = new_java_sqlite3_value(env, argv[i]);
     if( !jsv ) goto error_oom;
     (*env)->SetObjectArrayElement(env, ja, i, jsv);
     S3JniUnrefLocal(jsv)/*ja has a ref*/;
@@ -1909,7 +1918,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s,
                    jmethodID xMethodID,
                    const char *zFuncType){
   S3JniDeclLocal_env;
-  jobject jcx = new_sqlite3_context_wrapper(env, cx);
+  jobject jcx = new_java_sqlite3_context(env, cx);
   int rc = 0;
   int const isFinal = 'F'==zFuncType[1]/*xFinal*/;
 
@@ -2104,21 +2113,30 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
   if( 0==SJG.autoExt.nExt ) return 0;
   env = s3jni_env();
   jc = S3JniEnv_get();
-  ps = jc->pdbOpening;
+  S3JniAutoExt_mutex_enter;
+  ps = jc->pdbOpening ? jc->pdbOpening : S3JniDb_from_c(pDb);
   if( !ps ){
     *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in "
                              "auto-extension runner.");
+    S3JniAutoExt_mutex_leave;
     return SQLITE_ERROR;
   }
-  jc->pdbOpening = 0;
-  assert( !ps->pDb && "it's still being opened" );
   assert( ps->jDb );
-  rc = sqlite3_set_clientdata(pDb, S3JniDb_clientdata_key,
-                              ps, 0/* we'll re-set this after open()
-                                      completes. */);
-  if( rc ){
-    return rc;
+  if( !ps->pDb ){
+    assert( jc->pdbOpening == ps );
+    rc = sqlite3_set_clientdata(pDb, S3JniDb_clientdata_key,
+                                ps, 0/* we'll re-set this after open()
+                                        completes. */);
+    if( rc ){
+      S3JniAutoExt_mutex_leave;
+      return rc;
+    }
+  }
+  else{
+    assert( ps == jc->pdbOpening );
+    jc->pdbOpening = 0;
   }
+  S3JniAutoExt_mutex_leave;
   NativePointerHolder_set(S3JniNph(sqlite3), ps->jDb, pDb)
     /* As of here, the Java/C connection is complete except for the
        (temporary) lack of finalizer for the ps object. */;
@@ -2534,7 +2552,8 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
                               "Cannot not find matching call() in "
                               "CollationNeededCallback object.");
     }else{
-      rc = sqlite3_collation_needed16(ps->pDb, pHook, s3jni_collation_needed_impl16);
+      rc = sqlite3_collation_needed16(ps->pDb, pHook,
+                                      s3jni_collation_needed_impl16);
       if( 0==rc ){
         S3JniHook_unref(pHook);
         pHook->midCallback = xCallback;
@@ -2610,7 +2629,7 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)(
 ){
   sqlite3_value * const sv =
     sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
-  return new_sqlite3_value_wrapper(env, sv);
+  return new_java_sqlite3_value(env, sv);
 }
 
 /*
@@ -2896,7 +2915,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
                           "Could not get call() method from "
                           "CollationCallback object.");
     }else{
-      char * const zName = s3jni_jstring_to_utf8( name, 0);
+      char * const zName = s3jni_jstring_to_utf8(name, 0);
       S3JniCollationCallback * const pCC =
         zName ? S3JniHook_alloc() : 0;
       if( pCC ){
@@ -3253,7 +3272,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc,
     rc = SQLITE_NOMEM;
     goto end;
   }
-  jDb = new_sqlite3_wrapper(env, 0);
+  jDb = new_java_sqlite3(env, 0);
   if( !jDb ){
     sqlite3_free(*zDbName);
     *zDbName = 0;
@@ -3371,7 +3390,7 @@ jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self,
     rc = baSql ? SQLITE_NOMEM : SQLITE_MISUSE;
     goto end;
   }
-  jStmt = new_sqlite3_stmt_wrapper(env, 0);
+  jStmt = new_java_sqlite3_stmt(env, 0);
   if( !jStmt ){
     rc = SQLITE_NOMEM;
     goto end;
@@ -3616,7 +3635,7 @@ static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb,
       isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
     rc = fOrig(pDb, (int)iCol, &pOut);
     if( 0==rc ){
-      jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
+      jobject pWrap = new_java_sqlite3_value(env, pOut);
       if( pWrap ){
         OutputPointer_set_sqlite3_value(env, jOut, pWrap);
         S3JniUnrefLocal(pWrap);
@@ -4235,7 +4254,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
   if( 0==rc ){
     if( !jP ){
       /* Create a new temporary sqlite3_stmt wrapper */
-      jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
+      jP = jPUnref = new_java_sqlite3_stmt(env, pP);
       if( !jP ){
         rc = SQLITE_NOMEM;
       }
@@ -4347,7 +4366,7 @@ S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)(
   JniArgsEnvClass, jobject jpSVal
 ){
   sqlite3_value * const sv = sqlite3_value_dup(PtrGet_sqlite3_value(jpSVal));
-  return sv ? new_sqlite3_value_wrapper(env, sv) : 0;
+  return sv ? new_java_sqlite3_value(env, sv) : 0;
 }
 
 S3JniApi(sqlite3_value_free(),void,1value_1free)(
@@ -4444,7 +4463,7 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
          "\n\tS3JniUdf     = %u (free-list)"
          "\n\tmetrics      = %u\n",
          SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv,
-         SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb,
+         SJG.metrics.nMutexNph, SJG.metrics.nMutexPerDb,
          SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf,
          SJG.metrics.nMetrics);
   puts("Allocs:");
@@ -4501,7 +4520,8 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
 #define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,S3JniNph(fts5_tokenizer))
 #define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,S3JniNph(Fts5Context))
 #define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,S3JniNph(Fts5Tokenizer))
-#define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
+#define s3jni_ftsext() &sFts5Api/*singleton from sqlite3.c*/
+#define Fts5ExtDecl Fts5ExtensionApi const * const ext = s3jni_ftsext()
 
 /**
    State for binding Java-side FTS5 auxiliary functions.
@@ -4541,7 +4561,7 @@ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){
     memset(s, 0, sizeof(Fts5JniAux));
     s->jObj = S3JniRefGlobal(jObj);
     klazz = (*env)->GetObjectClass(env, jObj);
-    s->jmid = (*env)->GetMethodID(env, klazz, "xFunction",
+    s->jmid = (*env)->GetMethodID(env, klazz, "call",
                                   "(Lorg/sqlite/jni/Fts5ExtensionApi;"
                                   "Lorg/sqlite/jni/Fts5Context;"
                                   "Lorg/sqlite/jni/sqlite3_context;"
@@ -4557,15 +4577,11 @@ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){
   return s;
 }
 
-static inline Fts5ExtensionApi const * s3jni_ftsext(void){
-  return &sFts5Api/*singleton from sqlite3.c*/;
+static inline jobject new_java_Fts5Context(JNIEnv * const env, Fts5Context *sv){
+  return NativePointerHolder_new(env, S3JniNph(Fts5Context), sv);
 }
-
-static inline jobject new_Fts5Context_wrapper(JNIEnv * const env, Fts5Context *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(Fts5Context), sv);
-}
-static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){
-  return new_NativePointerHolder_object(env, S3JniNph(fts5_api), sv);
+static inline jobject new_java_fts5_api(JNIEnv * const env, fts5_api *sv){
+  return NativePointerHolder_new(env, S3JniNph(fts5_api), sv);
 }
 
 /*
@@ -4573,20 +4589,20 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){
 ** instance, or NULL on OOM.
 */
 static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
-  if( !SJG.fts5.jFtsExt ){
-    jobject pNPH = new_NativePointerHolder_object(
-      env, S3JniNph(Fts5ExtensionApi), s3jni_ftsext()
-    );
-    S3JniEnv_mutex_enter;
-    if( pNPH ){
-      if( !SJG.fts5.jFtsExt ){
-        SJG.fts5.jFtsExt = S3JniRefGlobal(pNPH);
+  if( !SJG.fts5.jExt ){
+    S3JniGlobal_mutex_enter;
+    if( !SJG.fts5.jExt ){
+      jobject const pNPH = NativePointerHolder_new(
+        env, S3JniNph(Fts5ExtensionApi), s3jni_ftsext()
+      );
+      if( pNPH ){
+        SJG.fts5.jExt = S3JniRefGlobal(pNPH);
+        S3JniUnrefLocal(pNPH);
       }
-      S3JniUnrefLocal(pNPH);
     }
-    S3JniEnv_mutex_leave;
+    S3JniGlobal_mutex_leave;
   }
-  return SJG.fts5.jFtsExt;
+  return SJG.fts5.jExt;
 }
 
 /*
@@ -4607,18 +4623,33 @@ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){
 
 JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){
   S3JniDb * const ps = S3JniDb_from_java(jDb);
+#if 0
   jobject rv = 0;
   if( !ps ) return 0;
-  else if( ps->jFtsApi ){
-    rv = ps->jFtsApi;
+  else if( ps->fts.jApi ){
+    rv = ps->fts.jApi;
   }else{
     fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb);
     if( pApi ){
-      rv = new_fts5_api_wrapper(env, pApi);
-      ps->jFtsApi = rv ? S3JniRefGlobal(rv) : 0;
+      rv = new_java_fts5_api(env, pApi);
+      ps->fts.jApi = rv ? S3JniRefGlobal(rv) : 0;
     }
   }
   return rv;
+#else
+  if( ps && !ps->fts.jApi ){
+    S3JniDb_mutex_enter;
+    if( !ps->fts.jApi ){
+      fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb);
+      if( pApi ){
+        jobject const rv = new_java_fts5_api(env, pApi);
+        ps->fts.jApi = rv ? S3JniRefGlobal(rv) : 0;
+      }
+    }
+    S3JniDb_mutex_leave;
+  }
+  return ps ? ps->fts.jApi : 0;
+#endif
 }
 
 
@@ -4628,13 +4659,13 @@ JniDeclFtsXA(jobject,getInstance)(JniArgsEnvClass){
 
 JniDeclFtsXA(jint,xColumnCount)(JniArgsEnvObj,jobject jCtx){
   Fts5ExtDecl;
-  return (jint)fext->xColumnCount(PtrGet_Fts5Context(jCtx));
+  return (jint)ext->xColumnCount(PtrGet_Fts5Context(jCtx));
 }
 
 JniDeclFtsXA(jint,xColumnSize)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOut32){
   Fts5ExtDecl;
   int n1 = 0;
-  int const rc = fext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1);
+  int const rc = ext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1);
   if( 0==rc ) OutputPointer_set_Int32(env, jOut32, n1);
   return rc;
 }
@@ -4644,7 +4675,7 @@ JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol,
   Fts5ExtDecl;
   const char *pz = 0;
   int pn = 0;
-  int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol,
+  int rc = ext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol,
                              &pz, &pn);
   if( 0==rc ){
     jstring jstr = pz ? s3jni_utf8_to_jstring( pz, pn) : 0;
@@ -4663,7 +4694,7 @@ JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol,
 JniDeclFtsXA(jint,xColumnTotalSize)(JniArgsEnvObj,jobject jCtx, jint iCol, jobject jOut64){
   Fts5ExtDecl;
   sqlite3_int64 nOut = 0;
-  int const rc = fext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut);
+  int const rc = ext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut);
   if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
   return (jint)rc;
 }
@@ -4688,20 +4719,21 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
   assert(pAux);
   jFXA = s3jni_getFts5ExensionApi(env);
   if( !jFXA ) goto error_oom;
-  jpFts = new_Fts5Context_wrapper(env, pFts);
+  jpFts = new_java_Fts5Context(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);
   S3JniIfThrew{
-    udf_report_exception(env, 1, pCx, pAux->zFuncName, "xFunction");
+    udf_report_exception(env, 1, pCx, pAux->zFuncName, "call");
   }
   S3JniUnrefLocal(jpFts);
   S3JniUnrefLocal(jpCx);
   S3JniUnrefLocal(jArgv);
   return;
 error_oom:
+  s3jni_db_oom( sqlite3_context_db_handle(pCx) );
   assert( !jArgv );
   assert( !jpCx );
   S3JniUnrefLocal(jpFts);
@@ -4761,7 +4793,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){
 JniDeclFtsXA(jobject,xGetAuxdata)(JniArgsEnvObj,jobject jCtx, jboolean bClear){
   Fts5ExtDecl;
   jobject rv = 0;
-  S3JniFts5AuxData * const pAux = fext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear);
+  S3JniFts5AuxData * const pAux = ext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear);
   if( pAux ){
     if( bClear ){
       if( pAux->jObj ){
@@ -4781,7 +4813,7 @@ JniDeclFtsXA(jint,xInst)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOutPhra
                     jobject jOutCol, jobject jOutOff){
   Fts5ExtDecl;
   int n1 = 0, n2 = 2, n3 = 0;
-  int const rc = fext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3);
+  int const rc = ext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3);
   if( 0==rc ){
     OutputPointer_set_Int32(env, jOutPhrase, n1);
     OutputPointer_set_Int32(env, jOutCol, n2);
@@ -4793,14 +4825,14 @@ JniDeclFtsXA(jint,xInst)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOutPhra
 JniDeclFtsXA(jint,xInstCount)(JniArgsEnvObj,jobject jCtx, jobject jOut32){
   Fts5ExtDecl;
   int nOut = 0;
-  int const rc = fext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut);
+  int const rc = ext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut);
   if( 0==rc && jOut32 ) OutputPointer_set_Int32(env, jOut32, nOut);
   return (jint)rc;
 }
 
 JniDeclFtsXA(jint,xPhraseCount)(JniArgsEnvObj,jobject jCtx){
   Fts5ExtDecl;
-  return (jint)fext->xPhraseCount(PtrGet_Fts5Context(jCtx));
+  return (jint)ext->xPhraseCount(PtrGet_Fts5Context(jCtx));
 }
 
 /* Copy the 'a' and 'b' fields from pSrc to Fts5PhraseIter object jIter. */
@@ -4834,7 +4866,7 @@ JniDeclFtsXA(jint,xPhraseFirst)(JniArgsEnvObj,jobject jCtx, jint iPhrase,
   Fts5ExtDecl;
   Fts5PhraseIter iter;
   int rc, iCol = 0, iOff = 0;
-  rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase,
+  rc = ext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase,
                          &iter, &iCol, &iOff);
   if( 0==rc ){
     OutputPointer_set_Int32(env, jOutCol, iCol);
@@ -4849,7 +4881,7 @@ JniDeclFtsXA(jint,xPhraseFirstColumn)(JniArgsEnvObj,jobject jCtx, jint iPhrase,
   Fts5ExtDecl;
   Fts5PhraseIter iter;
   int rc, iCol = 0;
-  rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase,
+  rc = ext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase,
                                 &iter, &iCol);
   if( 0==rc ){
     OutputPointer_set_Int32(env, jOutCol, iCol);
@@ -4864,7 +4896,7 @@ JniDeclFtsXA(void,xPhraseNext)(JniArgsEnvObj,jobject jCtx, jobject jIter,
   Fts5PhraseIter iter;
   int iCol = 0, iOff = 0;
   s3jni_phraseIter_JToN(env, jIter, &iter);
-  fext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff);
+  ext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff);
   OutputPointer_set_Int32(env, jOutCol, iCol);
   OutputPointer_set_Int32(env, jOutOff, iOff);
   s3jni_phraseIter_NToJ(env, &iter, jIter);
@@ -4876,7 +4908,7 @@ JniDeclFtsXA(void,xPhraseNextColumn)(JniArgsEnvObj,jobject jCtx, jobject jIter,
   Fts5PhraseIter iter;
   int iCol = 0;
   s3jni_phraseIter_JToN(env, jIter, &iter);
-  fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol);
+  ext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol);
   OutputPointer_set_Int32(env, jOutCol, iCol);
   s3jni_phraseIter_NToJ(env, &iter, jIter);
 }
@@ -4884,12 +4916,12 @@ JniDeclFtsXA(void,xPhraseNextColumn)(JniArgsEnvObj,jobject jCtx, jobject jIter,
 
 JniDeclFtsXA(jint,xPhraseSize)(JniArgsEnvObj,jobject jCtx, jint iPhrase){
   Fts5ExtDecl;
-  return (jint)fext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase);
+  return (jint)ext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase);
 }
 
 /* State for use with xQueryPhrase() and xTokenize(). */
 struct s3jni_xQueryPhraseState {
-  Fts5ExtensionApi const * fext;
+  Fts5ExtensionApi const * ext;
   S3JniEnv const * jc;
   jmethodID midCallback;
   jobject jCallback;
@@ -4910,7 +4942,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
   struct s3jni_xQueryPhraseState const * s = pData;
   S3JniDeclLocal_env;
   int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback,
-                                      SJG.fts5.jFtsExt, s->jFcx);
+                                      SJG.fts5.jExt, s->jFcx);
   S3JniIfThrew{
     S3JniExceptionWarnCallbackThrew("xQueryPhrase() callback");
     S3JniExceptionClear;
@@ -4930,13 +4962,13 @@ JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase,
   s.jc = jc;
   s.jCallback = jCallback;
   s.jFcx = jFcx;
-  s.fext = fext;
-  s.midCallback = (*env)->GetMethodID(env, klazz, "xCallback",
+  s.ext = ext;
+  s.midCallback = (*env)->GetMethodID(env, klazz, "call",
                                       "(Lorg.sqlite.jni.Fts5ExtensionApi;"
                                       "Lorg.sqlite.jni.Fts5Context;)I");
   S3JniUnrefLocal(klazz);
-  S3JniExceptionIsFatal("Could not extract xQueryPhraseCallback.xCallback method.");
-  return (jint)fext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s,
+  S3JniExceptionIsFatal("Could not extract xQueryPhraseCallback.call() method.");
+  return (jint)ext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s,
                                   s3jni_xQueryPhrase);
 }
 
@@ -4944,14 +4976,14 @@ JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase,
 JniDeclFtsXA(jint,xRowCount)(JniArgsEnvObj,jobject jCtx, jobject jOut64){
   Fts5ExtDecl;
   sqlite3_int64 nOut = 0;
-  int const rc = fext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut);
+  int const rc = ext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut);
   if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
   return (jint)rc;
 }
 
 JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){
   Fts5ExtDecl;
-  return (jlong)fext->xRowid(PtrGet_Fts5Context(jCtx));
+  return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx));
 }
 
 JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
@@ -4969,7 +5001,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
     return SQLITE_NOMEM;
   }
   pAux->jObj = S3JniRefGlobal(jAux);
-  rc = fext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux,
+  rc = ext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux,
                          S3JniFts5AuxData_xDestroy);
   return rc;
 }
@@ -5018,7 +5050,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphRef const *pRef,
   s.jc = jc;
   s.jCallback = jCallback;
   s.jFcx = jFcx;
-  s.fext = fext;
+  s.ext = ext;
   s.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I[BII)I");
   S3JniUnrefLocal(klazz);
   S3JniIfThrew {
@@ -5031,7 +5063,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphRef const *pRef,
   s.tok.zPrev = (const char *)pText;
   s.tok.nPrev = (int)nText;
   if( pRef == S3JniNph(Fts5ExtensionApi) ){
-    rc = fext->xTokenize(PtrGet_Fts5Context(jFcx),
+    rc = ext->xTokenize(PtrGet_Fts5Context(jFcx),
                          (const char *)pText, (int)nText,
                          &s, s3jni_xTokenize_xToken);
   }else if( pRef == S3JniNph(fts5_tokenizer) ){
@@ -5065,7 +5097,7 @@ JniDeclFtsTok(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jint tokFlags,
 
 JniDeclFtsXA(jobject,xUserData)(JniArgsEnvObj,jobject jFcx){
   Fts5ExtDecl;
-  Fts5JniAux * const pAux = fext->xUserData(PtrGet_Fts5Context(jFcx));
+  Fts5JniAux * const pAux = ext->xUserData(PtrGet_Fts5Context(jFcx));
   return pAux ? pAux->jUserData : 0;
 }
 
@@ -5337,6 +5369,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){
 
   SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
   s3jni_oom_fatal( SJG.mutex );
+  SJG.nph.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+  s3jni_oom_fatal( SJG.nph.mutex );
   SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
   s3jni_oom_fatal( SJG.envCache.mutex );
   SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
index ab2995f378534edcb6d91dd9a99a21db5ebf6745..a575ebdb916bc44769ceaf10165eae05a943ec33 100644 (file)
@@ -28,7 +28,7 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
 
   /* Callback type for used by xQueryPhrase(). */
   public static interface xQueryPhraseCallback {
-    int xCallback(Fts5ExtensionApi fapi, Fts5Context cx);
+    int call(Fts5ExtensionApi fapi, Fts5Context cx);
   }
 
   /**
@@ -36,40 +36,67 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
   */
   public static native Fts5ExtensionApi getInstance();
 
+  @Canonical
   public native int xColumnCount(@NotNull Fts5Context fcx);
+
+  @Canonical
   public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
                                 @NotNull OutputPointer.Int32 pnToken);
+
+  @Canonical
   public native int xColumnText(@NotNull Fts5Context cx, int iCol,
                                 @NotNull OutputPointer.String txt);
+
+  @Canonical
   public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
                                      @NotNull OutputPointer.Int64 pnToken);
+
+  @Canonical
   public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
+
+  @Canonical
   public native int xInst(@NotNull Fts5Context cx, int iIdx,
                           @NotNull OutputPointer.Int32 piPhrase,
                           @NotNull OutputPointer.Int32 piCol,
                           @NotNull OutputPointer.Int32 piOff);
+
+  @Canonical
   public native int xInstCount(@NotNull Fts5Context fcx,
                                @NotNull OutputPointer.Int32 pnInst);
+
+  @Canonical
   public native int xPhraseCount(@NotNull Fts5Context fcx);
+
+  @Canonical
   public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
                                  @NotNull Fts5PhraseIter iter,
                                  @NotNull OutputPointer.Int32 iCol,
                                  @NotNull OutputPointer.Int32 iOff);
+
+  @Canonical
   public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
                                        @NotNull Fts5PhraseIter iter,
                                        @NotNull OutputPointer.Int32 iCol);
+  @Canonical
   public native void xPhraseNext(@NotNull Fts5Context cx,
                                  @NotNull Fts5PhraseIter iter,
                                  @NotNull OutputPointer.Int32 iCol,
                                  @NotNull OutputPointer.Int32 iOff);
+  @Canonical
   public native void xPhraseNextColumn(@NotNull Fts5Context cx,
                                        @NotNull Fts5PhraseIter iter,
                                        @NotNull OutputPointer.Int32 iCol);
+  @Canonical
   public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
+
+  @Canonical
   public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
                                  @NotNull xQueryPhraseCallback callback);
+  @Canonical
   public native int xRowCount(@NotNull Fts5Context fcx,
                               @NotNull OutputPointer.Int64 nRow);
+
+  @Canonical
   public native long xRowid(@NotNull Fts5Context cx);
   /* Note that the JNI binding lacks the C version's xDelete()
      callback argument. Instead, if pAux has an xDestroy() method, it
@@ -77,10 +104,15 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
      allocation of storage for the auxdata fails). Any reference to
      pAux held by the JNI layer will be relinquished regardless of
      whether pAux has an xDestroy() method. */
+
+  @Canonical
   public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
+
+  @Canonical
   public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
                               @NotNull Fts5.xTokenize_callback callback);
 
+  @Canonical
   public native Object xUserData(Fts5Context cx);
   //^^^ returns the pointer passed as the 3rd arg to the C-level
   // fts5_api::xCreateFunction().
diff --git a/ext/jni/src/org/sqlite/jni/Fts5Function.java b/ext/jni/src/org/sqlite/jni/Fts5Function.java
deleted file mode 100644 (file)
index 463ec03..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-** 2023-08-04
-**
-** 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;
-
-/**
-   Fts5Function is used in conjunction with the
-   sqlite3_create_fts_function() JNI-bound API to give that native code
-   access to the callback functions needed in order to implement
-   FTS5 auxiliary functions in Java.
-*/
-public abstract class Fts5Function {
-
-  public abstract void xFunction(Fts5ExtensionApi pApi, Fts5Context pFts,
-                                 sqlite3_context pCtx, sqlite3_value argv[]);
-  public void xDestroy() {}
-}
index b4ba626222a2b3491e85bb1f56be5dc76f0023b8..a0aaa82bbda08dd3f0b2cdbb4d2664b7c7336cb4 100644 (file)
@@ -1311,7 +1311,7 @@ public class Tester1 implements Runnable {
     final ValueHolder<Integer> val = new ValueHolder<>(0);
     final ValueHolder<String> toss = new ValueHolder<>(null);
     final AutoExtensionCallback ax = new AutoExtensionCallback(){
-        @Override public synchronized int call(sqlite3 db){
+        @Override public int call(sqlite3 db){
           ++val.value;
           if( null!=toss.value ){
             throw new RuntimeException(toss.value);
@@ -1650,7 +1650,7 @@ public class Tester1 implements Runnable {
     {
       // Build list of tests to run from the methods named test*().
       testMethods = new ArrayList<>();
-      out("Skipping tests in multi-thread mode:");
+      int nSkipped = 0;
       for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){
         final String name = m.getName();
         if( name.equals("testFail") ){
@@ -1659,13 +1659,16 @@ public class Tester1 implements Runnable {
           }
         }else if( !m.isAnnotationPresent( ManualTest.class ) ){
           if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
+            if( 0==nSkipped++ ){
+              out("Skipping tests in multi-thread mode:");
+            }
             out(" "+name+"()");
           }else if( name.startsWith("test") ){
             testMethods.add(m);
           }
         }
       }
-      out("\n");
+      if( nSkipped>0 ) out("\n");
     }
 
     final long timeStart = System.currentTimeMillis();
index 70c8e255d2eee18e8e153624f702d4cc33d9e278..5f0be352a5d83ce1817f0fc356de4d7d46f9de0d 100644 (file)
@@ -18,7 +18,7 @@ import static org.sqlite.jni.Tester1.*;
 public class TesterFts5 {
 
   private static void test1(){
-    Fts5ExtensionApi fea = Fts5ExtensionApi.getInstance();
+    final Fts5ExtensionApi fea = Fts5ExtensionApi.getInstance();
     affirm( null != fea );
     affirm( fea.getNativePointer() != 0 );
     affirm( fea == Fts5ExtensionApi.getInstance() )/*singleton*/;
@@ -38,21 +38,18 @@ public class TesterFts5 {
     ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
     ValueHolder<Integer> xFuncCount = new ValueHolder<>(0);
     final fts5_extension_function func = new fts5_extension_function(){
-        public void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
-                              sqlite3_context pCx, sqlite3_value argv[]){
+        @Override public void call(Fts5ExtensionApi ext, Fts5Context fCx,
+                                   sqlite3_context pCx, sqlite3_value argv[]){
           int nCols = ext.xColumnCount(fCx);
           affirm( 2 == nCols );
           affirm( nCols == argv.length );
           affirm( ext.xUserData(fCx) == pUserData );
-          if(true){
-            OutputPointer.String op = new OutputPointer.String();
-            for(int i = 0; i < nCols; ++i ){
-              int rc = ext.xColumnText(fCx, i, op);
-              affirm( 0 == rc );
-              final String val = op.value;
-              affirm( val.equals(sqlite3_value_text16(argv[i])) );
-              //outln("xFunction col "+i+": "+val);
-            }
+          final OutputPointer.String op = new OutputPointer.String();
+          for(int i = 0; i < nCols; ++i ){
+            int rc = ext.xColumnText(fCx, i, op);
+            affirm( 0 == rc );
+            final String val = op.value;
+            affirm( val.equals(sqlite3_value_text16(argv[i])) );
           }
           ++xFuncCount.value;
         }
@@ -72,18 +69,24 @@ public class TesterFts5 {
     affirm( xDestroyCalled.value );
   }
 
+  private void runTests(){
+    test1();
+  }
+
   public TesterFts5(boolean verbose){
     if(verbose){
-      final long timeStart = System.currentTimeMillis();
-      final int oldAffirmCount = Tester1.affirmCount;
-      test1();
-      final int affirmCount = Tester1.affirmCount - oldAffirmCount;
-      final long timeEnd = System.currentTimeMillis();
-      outln("FTS5 Tests done. Assertions checked = ",affirmCount,
-            ", Total time = ",(timeEnd - timeStart),"ms");
+      synchronized(Tester1.class) {
+        final long timeStart = System.currentTimeMillis();
+        final int oldAffirmCount = Tester1.affirmCount;
+        runTests();
+        final int affirmCount = Tester1.affirmCount - oldAffirmCount;
+        final long timeEnd = System.currentTimeMillis();
+        outln("FTS5 Tests done. Assertions checked = ",affirmCount,
+              ", Total time = ",(timeEnd - timeStart),"ms");
+      }
     }else{
-      test1();
+      runTests();
     }
   }
-  public TesterFts5(){ this(false); }
+  public TesterFts5(){ this(true); }
 }
index 92ca7c669a50f354bd5fd056a42ce7c3fe975ad7..a0376dfc8f03d1eb8e261bbcf16fa1d1208be34b 100644 (file)
@@ -33,6 +33,31 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
   */
   public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
 
+  @Canonical
+  public synchronized native int xCreateFunction(@NotNull String name,
+                                                 @Nullable Object userData,
+                                                 @NotNull fts5_extension_function xFunction);
+
+  /**
+     Convenience overload which passes null as the 2nd argument to the
+     3-parameter form.
+  */
+  public int xCreateFunction(@NotNull String name,
+                             @NotNull fts5_extension_function xFunction){
+    return xCreateFunction(name, null, xFunction);
+  }
+
+  // /* Create a new auxiliary function */
+  // int (*xCreateFunction)(
+  //   fts5_api *pApi,
+  //   const char *zName,
+  //   void *pContext,
+  //   fts5_extension_function xFunction,
+  //   void (*xDestroy)(void*)
+  // );
+
+  // Still potentially todo:
+
   // int (*xCreateTokenizer)(
   //   fts5_api *pApi,
   //   const char *zName,
@@ -49,22 +74,4 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
   //   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 synchronized native int xCreateFunction(@NotNull String name,
-                                    @Nullable Object userData,
-                                    @NotNull fts5_extension_function xFunction);
-
-  public int xCreateFunction(@NotNull String name,
-                             @NotNull fts5_extension_function xFunction){
-    return xCreateFunction(name, null, xFunction);
-  }
-
 }
index 0e273119f565ace1dad8e09eeef4f72d9ce2904e..9247003168788422a6ec55cd1fa9436e865589f1 100644 (file)
@@ -15,9 +15,8 @@ package org.sqlite.jni;
 
 /**
    JNI-level wrapper for C's fts5_extension_function type.
-
 */
-public abstract class fts5_extension_function {
+public interface fts5_extension_function {
   // typedef void (*fts5_extension_function)(
   //   const Fts5ExtensionApi *pApi,   /* API offered by current FTS version */
   //   Fts5Context *pFts,              /* First arg to pass to pApi functions */
@@ -30,8 +29,17 @@ public abstract class fts5_extension_function {
      The callback implementation, corresponding to the xFunction
      argument of C's fts5_api::xCreateFunction().
   */
-  public abstract void xFunction(Fts5ExtensionApi ext, Fts5Context fCx,
-                                 sqlite3_context pCx, sqlite3_value argv[]);
-  //! Optionally override
-  public void xDestroy(){}
+  void call(Fts5ExtensionApi ext, Fts5Context fCx,
+            sqlite3_context pCx, sqlite3_value argv[]);
+  /**
+     Is called when this function is destroyed by sqlite3. Typically
+     this function will be empty.
+  */
+  void xDestroy();
+
+  public static abstract class Abstract implements fts5_extension_function {
+    @Override public abstract void call(Fts5ExtensionApi ext, Fts5Context fCx,
+                                        sqlite3_context pCx, sqlite3_value argv[]);
+    @Override public void xDestroy(){}
+  }
 }
index 8128a8355012c6d62066b661bc4bbb581282dea5..4f091a5f465eebd67ab28608a58b66c12809919c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\stimer\sto\sthe\sJS\sSQLTester\sapp\sto\swarn\sif\sit\sappears\sthat\sloading\sthe\smodule\shas\sfailed,\swhich\sis\scommonly\scaused\sby\sthe\stest\sscripts\snot\sbeing\scompiled.
-D 2023-09-01T17:51:09.091
+C Further\sJNI\scleanups.
+D 2023-09-02T08:51:14.022
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -234,10 +234,10 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
-F ext/jni/GNUmakefile 3e0e28c1c451eab31dd4fa9d262951ef67ed3ece3270a2efc8ec66ec57068547
+F ext/jni/GNUmakefile dc6e78f9717470d262b4b3ec17c337834295f9df81717c1539da84106324fd1e
 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
-F ext/jni/src/c/sqlite3-jni.c 00469db3f64a7f53bc1ecb60749260b937f8892ddc56d4c61d5f427f6e6758a7
+F ext/jni/src/c/sqlite3-jni.c d98030bb7f502b42990eeb0f2bd103af0b1f1312ba82892949c5ced85262e036
 F ext/jni/src/c/sqlite3-jni.h c22f0189254abe26fad3ba132b484785b19a1aa96d34d30d7d8c5ffe6a9b25d1
 F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
@@ -251,8 +251,7 @@ F ext/jni/src/org/sqlite/jni/CommitHookCallback.java 77cf8bb4f5548113e9792978f3f
 F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4
 F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8
 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890
-F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 2fd11abb7c5403318181d69bb7b702a79cba7ab460105140f5161bea9bc505d1
-F ext/jni/src/org/sqlite/jni/Fts5Function.java 65cde7151e441fee012250a5e03277de7babcd11a0c308a832b7940574259bcc
+F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 6ac342951c5805b48ba1e46dd41dff3b74c609dc5bf1c26fca5738db36493031
 F ext/jni/src/org/sqlite/jni/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab03a7aede43277b2c92859176d6bce3be9
 F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060
 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d
@@ -265,8 +264,8 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b
 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 98f8d62492e2d6693336dd42c12267ea7f21eefe219aa85b8dd399bd6b0732bd
 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
 F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab
-F ext/jni/src/org/sqlite/jni/Tester1.java 2a6ef03fca523280731a88f697cdc8ece9548cd4cbe7e1cc90823e5bb5998e27
-F ext/jni/src/org/sqlite/jni/TesterFts5.java 02b59ea7d75666fd565d90bb8dc7b0c09441bb9d1af091fad9f6d7377297a474
+F ext/jni/src/org/sqlite/jni/Tester1.java 93e89c4f72065bf28625cf435c47ed2bd3d937fe5c2431e6e7c3407f09f3f9bc
+F ext/jni/src/org/sqlite/jni/TesterFts5.java 2b2d6f3cc9f508358c103b774aee296c0f3d8c2f387d6abae9b8b9055f62c800
 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java beb0b064c1a5f8bfe585a324ed39a4e33edbe379a3fc60f1401661620d3ca7c0
 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 8376f4a931f2d5612b295c003c9515ba933ee76d8f95610e89c339727376e36c
 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
@@ -276,8 +275,8 @@ F ext/jni/src/org/sqlite/jni/annotation/Canonical.java d9ff79f1bf9d063d0c3e3b768
 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java d48ebd7ae6bbb78bd47d54431c85e1521c89b1d3864a2b6eafd9c0e1b2341457
 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 6f962a98c9a5c6e9d21c50ae8716b16bdfdc934a191608cbb7e12ea588ddb6af
 F ext/jni/src/org/sqlite/jni/annotation/package-info.java f66bfb621c6494e67c03ed38a9e26a3bd6af99b9f9f6ef79556bcec30a025a22
-F ext/jni/src/org/sqlite/jni/fts5_api.java ee47f1837d32968f7bb62278c7504c0fb572a68ec107371b714578312e9f734b
-F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c
+F ext/jni/src/org/sqlite/jni/fts5_api.java bf7009a7fd2d11a77280380e6e2c7f98e377494b3f265e50fc44cbb9a24275d9
+F ext/jni/src/org/sqlite/jni/fts5_extension_function.java bec9f17a7e784d57eca99d812a65ecc9dd74c8df62d6d90cc1ecb3e37de14d47
 F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a92c2e55bda492e4c76d48ddc73369bcc0d5e8727940840f9339e3292ea58fa7
 F ext/jni/src/org/sqlite/jni/package-info.java a3946db2504de747a1993c4f6e8ce604bec5a8e5a134b292c3b07527bc321a99
 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
@@ -2116,8 +2115,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 016bbdadbf9f95cef185d2606c23e3b3c724586fcca470e11e6fbdbaefd75c79
-R 4133358901b76bfadefddc6665a81e23
+P f9c1f9cad1ad22f689a4afa13d59bf9969ddaed6624cbc16cb1bf5d1fd0c8a5b
+R 215167a378a1f1105562740ffd0f1842
 U stephan
-Z eab9aeb866f5eecf9b7d34e391979f20
+Z 15dc34b47fe3ed21ed7cc58408f87312
 # Remove this line to create a well-formed Fossil manifest.
index dcede0167e23a77323ed9bdd1222b9cb68fc6d09..bbd378b4fe90e7996371e1b8bb2b67f36c18859f 100644 (file)
@@ -1 +1 @@
-f9c1f9cad1ad22f689a4afa13d59bf9969ddaed6624cbc16cb1bf5d1fd0c8a5b
\ No newline at end of file
+30e38173c3ece0c9f8e7a9710f46cb5e8e8ef101c04531318a7adb070242f5dd
\ No newline at end of file