]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Correct JNI mapping of collations to be 1-db-to-many-collations.
authorstephan <stephan@noemail.net>
Mon, 28 Aug 2023 20:21:56 +0000 (20:21 +0000)
committerstephan <stephan@noemail.net>
Mon, 28 Aug 2023 20:21:56 +0000 (20:21 +0000)
FossilOrigin-Name: b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f

ext/jni/src/c/sqlite3-jni.c
ext/jni/src/org/sqlite/jni/Tester1.java
ext/jni/src/org/sqlite/jni/XDestroyCallback.java
manifest
manifest.uuid

index a78b07166b1c46ccddff593d93de700abc0f8967..dfa929ce901ecafe9d51e55e4f4a86319bc3c480 100644 (file)
@@ -429,7 +429,6 @@ struct S3JniDb {
                          SQLITE_DBCONFIG_MAINDBNAME. */;
   struct {
     S3JniHook busyHandler;
-    S3JniHook collation;
     S3JniHook collationNeeded;
     S3JniHook commit;
     S3JniHook progress;
@@ -1021,12 +1020,14 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){
 ** problem extracting the exception's message, it's treated as
 ** non-fatal and zDfltMsg is used in its place.
 **
+** Locks the global S3JniDb mutex.
+**
 ** This must only be called if a JNI exception is pending.
 **
 ** Returns errCode unless it is 0, in which case SQLITE_ERROR is
 ** returned.
 */
-static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
+static int s3jni__db_exception(JNIEnv * const env, S3JniDb * const ps,
                               int errCode, const char *zDfltMsg){
   jthrowable const ex = (*env)->ExceptionOccurred(env);
 
@@ -1034,13 +1035,17 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
   if( ex ){
     char * zMsg;
     S3JniExceptionClear;
+    S3JniMutex_S3JniDb_enter;
     zMsg = s3jni_exception_error_msg(env, ex);
     s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg);
     sqlite3_free(zMsg);
     S3JniUnrefLocal(ex);
+    S3JniMutex_S3JniDb_leave;
   }
   return errCode;
 }
+#define s3jni_db_exception(JniDb,ERRCODE,DFLTMSG) \
+  s3jni__db_exception(env, (JniDb), (ERRCODE), (DFLTMSG) )
 
 /*
 ** Extracts the (void xDestroy()) method from jObj and applies it to
@@ -1049,7 +1054,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
 ** trigger a warning to stdout or stderr and then the exception is
 ** suppressed.
 */
-static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){
+static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){
   if( jObj ){
     jclass const klazz = (*env)->GetObjectClass(env, jObj);
     jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V");
@@ -1068,25 +1073,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){
     }
   }
 }
-
-/*
-** Removes any Java references from s and clears its state. If
-** doXDestroy is true and s->jObj is not NULL, s->jObj's
-** s is passed to s3jni_call_xDestroy() before any references are
-** cleared. It is legal to call this when the object has no Java
-** references.
-*/
-static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s,
-                                 int doXDestroy){
-  if( s->jObj ){
-    if( doXDestroy ){
-      s3jni_call_xDestroy(env, s->jObj);
-    }
-    S3JniUnrefGlobal(s->jObj);
-  }
-  memset(s, 0, sizeof(*s));
-}
-#define S3JniHook_unref(H,X) S3JniHook_unref_impl(env, (H), (X))
+#define s3jni_call_xDestroy(JOBJ) s3jni__call_xDestroy(env, (JOBJ))
 
 /*
 ** Internal helper for many hook callback impls. Locks the S3JniDb
@@ -1105,15 +1092,36 @@ static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s,
 ** another thread modify the hook while we're running it. That copy
 ** has to have its own Java reference, but it need only be call-local.
 */
-static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src,
-                                S3JniHook * const dest ){
+static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src,
+                                 S3JniHook * const dest ){
   S3JniMutex_S3JniDb_enter;
   *dest = *src;
   if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj);
   S3JniMutex_S3JniDb_leave;
 }
+#define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest)
 #define S3JniHook_localundup(HOOK) S3JniUnrefLocal(HOOK.jObj)
 
+/*
+** Removes any Java references from s and clears its state. If
+** doXDestroy is true and s->jObj is not NULL, s->jObj's
+** s is passed to s3jni_call_xDestroy() before any references are
+** cleared. It is legal to call this when the object has no Java
+** references.
+*/
+static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s,
+                                 int doXDestroy){
+  if( s->jObj ){
+    if( doXDestroy ){
+      s3jni_call_xDestroy(s->jObj);
+    }
+    S3JniUnrefGlobal(s->jObj);
+  }
+  memset(s, 0, sizeof(*s));
+}
+#define S3JniHook_unref(hook,doDestroy) \
+  S3JniHook__unref(env, (hook), (doDestroy))
+
 /*
 ** Clears all of s's state. Requires that that the caller has locked
 ** S3JniGlobal.perDb.mutex. Make sure to do anything needed with
@@ -1125,7 +1133,6 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
 #define UNHOOK(MEMBER) S3JniHook_unref(&s->hooks.MEMBER, 0)
   UNHOOK(auth);
   UNHOOK(busyHandler);
-  UNHOOK(collation);
   UNHOOK(collationNeeded);
   UNHOOK(commit);
   UNHOOK(progress);
@@ -1163,10 +1170,11 @@ static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
     SJG.perDb.aFree = s;
   }
 }
+#define S3JniDb_set_aside_unlocked(JniDb) S3JniDb__set_aside_unlocked(env, JniDb)
 
 static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){
   S3JniMutex_S3JniDb_enter;
-  S3JniDb__set_aside_unlocked(env, s);
+  S3JniDb_set_aside_unlocked(s);
   S3JniMutex_S3JniDb_leave;
 }
 #define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
@@ -1294,10 +1302,8 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){
 */
 static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef,
                                      jobject ppOut, const void * p){
-  jfieldID const fid = s3jni_nphop_field(env, pRef);
-
   assert( ppOut );
-  (*env)->SetLongField(env, ppOut, fid, (jlong)p);
+  (*env)->SetLongField(env, ppOut, s3jni_nphop_field(env, pRef), (jlong)p);
   S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer.");
 }
 
@@ -1313,8 +1319,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
                                        S3JniNphRef const* pRef){
   void * rv = 0;
   if( pObj ){
-    jfieldID const fid = s3jni_nphop_field(env, pRef);
-    rv = (void*)(*env)->GetLongField(env, pObj, fid);
+    rv = (void*)(*env)->GetLongField(env, pObj, s3jni_nphop_field(env, pRef));
     S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer.");
   }
   return rv;
@@ -1427,7 +1432,7 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
 
 /* An experiment */
 //#define CLOSE_DB_LOCKED
-#if defined(CLOSE_DB_LOCKED)
+#if 1 || defined(CLOSE_DB_LOCKED)
 static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
   S3JniDb * s = 0;
   sqlite3 * pDb = 0;
@@ -1436,7 +1441,9 @@ static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
   if( jDb ) pDb = PtrGet_sqlite3(jDb);
   S3JniDb_search;
   return s;
+
 }
+#define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB))
 #endif
 
 /*
@@ -1589,45 +1596,44 @@ static int encodingTypeIsValid(int eTextRep){
   }
 }
 
+/**
+   State for CollationCallbacks.
+*/
+typedef S3JniHook S3JniCollationCallback;
+
 /*
 ** Proxy for Java-side CollationCallback.xCompare() callbacks.
 */
 static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs,
                                    int nRhs, const void *rhs){
-  S3JniDb * const ps = pArg;
+  S3JniCollationCallback * const pCC = pArg;
   S3JniDeclLocal_env;
   jint rc = 0;
-  S3JniHook hook;
-
-  S3JniHook_localdup(env, &ps->hooks.collation, &hook );
-  if( hook.jObj ){
+  if( pCC->jObj ){
     jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs);
     jbyteArray jbaRhs = jbaLhs
       ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0;
     if( !jbaRhs ){
       S3JniUnrefLocal(jbaLhs);
-      s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
+      /* We have no recovery strategy here. */
+      s3jni_oom_check( jbaRhs );
       return 0;
     }
-    rc = (*env)->CallIntMethod(env, ps->hooks.collation.jObj,
-                               ps->hooks.collation.midCallback,
+    rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback,
                                jbaLhs, jbaRhs);
     S3JniExceptionIgnore;
     S3JniUnrefLocal(jbaLhs);
     S3JniUnrefLocal(jbaRhs);
-    S3JniHook_localundup(hook);
   }
   return (int)rc;
 }
 
 /* CollationCallback finalizer for use by the sqlite3 internals. */
 static void CollationCallback_xDestroy(void *pArg){
-  S3JniDb * const ps = pArg;
+  S3JniCollationCallback * const pCC = pArg;
   S3JniDeclLocal_env;
-
-  S3JniMutex_S3JniDb_enter;
-  S3JniHook_unref(&ps->hooks.collation, 1);
-  S3JniMutex_S3JniDb_leave;
+  S3JniHook_unref(pCC, 1);
+  sqlite3_free(pCC);
 }
 
 /* For use with sqlite3_result/value_pointer() */
@@ -1763,11 +1769,13 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
 static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s,
                           int cacheIt){
   assert( !s->pNext );
-  s3jni_call_xDestroy(env, s->jObj);
-  S3JniUnrefGlobal(s->jObj);
-  sqlite3_free(s->zFuncName);
-  assert( !s->pNext );
-  memset(s, 0, sizeof(*s));
+  if( s->jObj ){
+    s3jni_call_xDestroy(s->jObj);
+    S3JniUnrefGlobal(s->jObj);
+    sqlite3_free(s->zFuncName);
+    assert( !s->pNext );
+    memset(s, 0, sizeof(*s));
+  }
   if( cacheIt ){
     S3JniMutex_Global_enter;
     s->pNext = S3JniGlobal.udf.aFree;
@@ -2309,13 +2317,13 @@ static int s3jni_busy_handler(void* pState, int n){
   S3JniDeclLocal_env;
   S3JniHook hook;
 
-  S3JniHook_localdup(env, &ps->hooks.busyHandler, &hook );
+  S3JniHook_localdup(&ps->hooks.busyHandler, &hook );
   if( hook.jObj ){
     rc = (*env)->CallIntMethod(env, hook.jObj,
                                hook.midCallback, (jint)n);
     S3JniIfThrew{
       S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback");
-      rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
+      rc = s3jni_db_exception(ps, SQLITE_ERROR,
                               "sqlite3_busy_handler() callback threw.");
     }
     S3JniHook_localundup(hook);
@@ -2328,33 +2336,42 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
 ){
   S3JniDb * const ps = S3JniDb_from_java(jDb);
   S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0;
+  S3JniHook hook = S3JniHook_empty;
   int rc = 0;
+
   if( !ps ) return (jint)SQLITE_MISUSE;
   S3JniMutex_S3JniDb_enter;
   if( jBusy ){
     if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){
       /* Same object - this is a no-op. */
-      S3JniMutex_S3JniDb_leave;
-      return 0;
-    }
-    jclass klazz;
-    S3JniHook_unref(pHook, 0);
-    pHook->jObj = S3JniRefGlobal(jBusy);
-    klazz = (*env)->GetObjectClass(env, jBusy);
-    pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I");
-    S3JniUnrefLocal(klazz);
-    S3JniIfThrew {
-      S3JniHook_unref(pHook, 0);
-      rc = SQLITE_ERROR;
+    }else{
+      jclass const klazz = (*env)->GetObjectClass(env, jBusy);
+      hook.jObj = S3JniRefGlobal(jBusy);
+      hook.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I");
+      S3JniUnrefLocal(klazz);
+      S3JniIfThrew {
+        rc = SQLITE_ERROR;
+      }
     }
-  }else{
-    S3JniHook_unref(pHook, 0);
   }
   if( 0==rc ){
-    rc = jBusy
-      ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps)
-      : sqlite3_busy_handler(ps->pDb, 0, 0);
+    if( jBusy ){
+      if( hook.jObj ){ /* Replace handler */
+        rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps);
+        if( 0==rc ){
+          S3JniHook_unref(pHook, 0);
+          *pHook = hook;
+          hook = S3JniHook_empty;
+        }
+      }/* else no-op */
+    }else{ /* Clear handler */
+      rc = sqlite3_busy_handler(ps->pDb, 0, 0);
+      if( 0==rc ){
+        S3JniHook_unref(pHook, 0);
+      }
+    }
   }
+  S3JniHook_unref(&hook, 0);
   S3JniMutex_S3JniDb_leave;
   return rc;
 }
@@ -2388,9 +2405,8 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
       /* Move final entry into this slot. */
       --SJG.autoExt.nExt;
       *ax = SJG.autoExt.aExt[SJG.autoExt.nExt];
-      memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0,
-             sizeof(S3JniAutoExtension));
-      assert(! SJG.autoExt.aExt[SJG.autoExt.nExt].jObj );
+      memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, sizeof(*ax));
+      assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj );
       rc = JNI_TRUE;
       break;
     }
@@ -2403,9 +2419,9 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
 /* Wrapper for sqlite3_close(_v2)(). */
 static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
   int rc = 0;
-
 #ifndef CLOSE_DB_LOCKED
   S3JniDb * const ps = S3JniDb_from_java(jDb);
+
   assert(version == 1 || version == 2);
   if( ps ){
     rc = 1==version
@@ -2429,14 +2445,14 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
   if( 0!=jDb ){
     S3JniDb * ps;
     S3JniMutex_S3JniDb_enter;
-    ps = S3JniDb__from_java_unlocked(env, jDb);
+    ps = S3JniDb_from_java_unlocked(jDb);
     if( ps && ps->pDb ){
       rc = 1==version
         ? (jint)sqlite3_close(ps->pDb)
         : (jint)sqlite3_close_v2(ps->pDb);
       if( 0==rc ){
-        S3JniDb__set_aside_unlocked(env,ps)
-          /* MUST come after close() because of ps->trace. */;
+        S3JniDb_set_aside_unlocked(ps)
+          /* MUST come after close() because of ps->hooks.trace. */;
         NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0);
       }
     }else{
@@ -2478,25 +2494,29 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
   S3JniDeclLocal_env;
   S3JniHook hook;
 
-  S3JniHook_localdup(env, &ps->hooks.collationNeeded, &hook );
+  S3JniHook_localdup(&ps->hooks.collationNeeded, &hook );
   if( hook.jObj ){
     unsigned int const nName = s3jni_utf16_strlen(z16Name);
     jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName);
-
     s3jni_oom_check( jName );
     S3JniIfThrew{
-      s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
       S3JniExceptionClear;
     }else{
-      (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj,
-                             ps->hooks.collationNeeded.midCallback,
-                             ps->jDb, (jint)eTextRep, jName);
-      S3JniIfThrew{
-        S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback");
-        s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw");
+      jobject jDb;
+      S3JniMutex_S3JniDb_enter;
+      jDb = ps->jDb ? S3JniRefLocal(ps->jDb) : 0;
+      S3JniMutex_S3JniDb_leave;
+      if( jDb ){
+        (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
+                               jDb, (jint)eTextRep, jName);
+        S3JniIfThrew{
+          S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback");
+          s3jni_db_exception(ps, 0, "sqlite3_collation_needed() callback threw");
+        }
+        S3JniUnrefLocal(jDb);
       }
-      S3JniUnrefLocal(jName);
     }
+    S3JniUnrefLocal(jName);
     S3JniHook_localundup(hook);
   }
 }
@@ -2504,18 +2524,25 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
 S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
   JniArgsEnvClass, jobject jDb, jobject jHook
 ){
-  S3JniDb * const ps = S3JniDb_from_java(jDb);
-  S3JniHook * const pHook = &ps->hooks.collationNeeded;
+  S3JniDb * ps;
+  S3JniHook * pHook;
   int rc = 0;
 
-  if( !ps ) return SQLITE_MISUSE;
   S3JniMutex_S3JniDb_enter;
+  ps = S3JniDb_from_java_unlocked(jDb);
+  if( !ps ){
+    S3JniMutex_S3JniDb_leave;
+    return SQLITE_MISUSE;
+  }
+  pHook = &ps->hooks.collationNeeded;
   if( pHook->jObj && jHook &&
      (*env)->IsSameObject(env, pHook->jObj, jHook) ){
     /* no-op */
   }else if( !jHook ){
-    S3JniHook_unref(pHook, 0);
-    sqlite3_collation_needed(ps->pDb, 0, 0);
+    rc = sqlite3_collation_needed(ps->pDb, 0, 0);
+    if( 0==rc ){
+      S3JniHook_unref(pHook, 0);
+    }
   }else{
     jclass const klazz = (*env)->GetObjectClass(env, jHook);
     jmethodID const xCallback = (*env)->GetMethodID(
@@ -2523,13 +2550,12 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
     );
     S3JniUnrefLocal(klazz);
     S3JniIfThrew {
-      rc = s3jni_db_exception(env, ps, SQLITE_MISUSE,
-                              "Cannot not find matching callback on "
-                              "collation-needed hook object.");
+      rc = s3jni_db_exception(ps, SQLITE_MISUSE,
+                              "Cannot not find matching call() in "
+                              "CollationNeededCallback object.");
     }else{
       rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16);
-      if( rc ){
-      }else{
+      if( 0==rc ){
         S3JniHook_unref(pHook, 0);
         pHook->midCallback = xCallback;
         pHook->jObj = S3JniRefGlobal(jHook);
@@ -2603,15 +2629,17 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)(
   return new_sqlite3_value_wrapper(env, sv);
 }
 
-static int s3jni_commit_rollback_hook_impl(int isCommit,
-                                           S3JniDb * const ps){
+/*
+** Impl for both commit hooks (if isCommit is true) or rollback hooks.
+*/
+static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){
   S3JniDeclLocal_env;
   int rc = 0;
   S3JniHook hook;
 
-  S3JniHook_localdup( env, isCommit
-                      ? &ps->hooks.commit : &ps->hooks.rollback,
-                      &hook);
+  S3JniHook_localdup(isCommit
+                     ? &ps->hooks.commit : &ps->hooks.rollback,
+                     &hook);
   if( hook.jObj ){
     rc = isCommit
       ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback)
@@ -2625,32 +2653,35 @@ static int s3jni_commit_rollback_hook_impl(int isCommit,
   return rc;
 }
 
+/* C-to-Java commit hook wrapper. */
 static int s3jni_commit_hook_impl(void *pP){
   return s3jni_commit_rollback_hook_impl(1, pP);
 }
 
+/* C-to-Java rollback hook wrapper. */
 static void s3jni_rollback_hook_impl(void *pP){
   (void)s3jni_commit_rollback_hook_impl(0, pP);
 }
 
 static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
                                           jobject jDb, jobject jHook){
-  S3JniDb * const ps = S3JniDb_from_java(jDb);
-  jclass klazz;
+  S3JniDb * ps;
   jobject pOld = 0;
-  jmethodID xCallback;
-  S3JniHook * const pHook =
-    isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
+  S3JniHook * pHook;
+
+  S3JniMutex_S3JniDb_enter;
+  ps = S3JniDb_from_java_unlocked(jDb);
   if( !ps ){
     s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
+    S3JniMutex_S3JniDb_leave;
     return 0;
   }
+  pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
   pOld = pHook->jObj;
   if( pOld && jHook &&
       (*env)->IsSameObject(env, pOld, jHook) ){
-    return pOld;
-  }
-  if( !jHook ){
+    /* No-op. */
+  }else if( !jHook ){
     if( pOld ){
       jobject tmp = S3JniRefLocal(pOld);
       S3JniUnrefGlobal(pOld);
@@ -2659,29 +2690,30 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
     memset(pHook, 0, sizeof(S3JniHook));
     if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0);
     else sqlite3_rollback_hook(ps->pDb, 0, 0);
-    return pOld;
-  }
-  klazz = (*env)->GetObjectClass(env, jHook);
-  xCallback = (*env)->GetMethodID(env, klazz, "call",
-                                  isCommit ? "()I" : "()V");
-  S3JniUnrefLocal(klazz);
-  S3JniIfThrew {
-    S3JniExceptionReport;
-    S3JniExceptionClear;
-    s3jni_db_error(ps->pDb, SQLITE_ERROR,
-                   "Cannot not find matching callback on "
-                   "hook object.");
   }else{
-    pHook->midCallback = xCallback;
-    pHook->jObj = S3JniRefGlobal(jHook);
-    if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps);
-    else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps);
-    if( pOld ){
-      jobject tmp = S3JniRefLocal(pOld);
-      S3JniUnrefGlobal(pOld);
-      pOld = tmp;
+    jclass const klazz = (*env)->GetObjectClass(env, jHook);
+    jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call",
+                                                    isCommit ? "()I" : "()V");
+    S3JniUnrefLocal(klazz);
+    S3JniIfThrew {
+      S3JniExceptionReport;
+      S3JniExceptionClear;
+      s3jni_db_error(ps->pDb, SQLITE_ERROR,
+                     "Cannot not find matching call() in"
+                     "hook object.");
+    }else{
+      pHook->midCallback = xCallback;
+      pHook->jObj = S3JniRefGlobal(jHook);
+      if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps);
+      else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps);
+      if( pOld ){
+        jobject tmp = S3JniRefLocal(pOld);
+        S3JniUnrefGlobal(pOld);
+        pOld = tmp;
+      }
     }
   }
+  S3JniMutex_S3JniDb_leave;
   return pOld;
 }
 
@@ -2733,7 +2765,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int
   S3JniHook hook = S3JniHook_empty;
 
   if( ps ){
-    S3JniHook_localdup(env, &SJG.hooks.sqllog, &hook);
+    S3JniHook_localdup(&SJG.hooks.sqllog, &hook);
   }
   if( !hook.jObj ) return;
   jArg0 = S3JniRefLocal(ps->jDb);
@@ -2775,7 +2807,10 @@ S3JniApi(sqlite3_config() /* for SQLLOG */,
 
   S3JniMutex_Global_enter;
   if( !jLog ){
-    S3JniHook_unref(pHook, 0);
+    rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
+    if( 0==rc ){
+      S3JniHook_unref(pHook, 0);
+    }
   }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){
     /* No-op */
   }else {
@@ -2825,26 +2860,28 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
   midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I");
   S3JniUnrefLocal(klazz);
   S3JniIfThrew{
-    S3JniUnrefLocal(klazz);
     rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
-                        "Could not get xCompare() method for object.");
+                        "Could not get call() method from "
+                        "CollationCallback object.");
   }else{
     char * const zName = s3jni_jstring_to_utf8( name, 0);
-    if( zName ){
-      S3JniMutex_S3JniDb_enter;
+    S3JniCollationCallback * const pCC =
+      zName ? s3jni_malloc(sizeof(S3JniCollationCallback)) : 0;
+    if( pCC ){
+      memset( pCC, 0, sizeof(*pCC) );
       rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep,
-                                       ps, CollationCallback_xCompare,
+                                       pCC, CollationCallback_xCompare,
                                        CollationCallback_xDestroy);
-      sqlite3_free(zName);
       if( 0==rc ){
-        S3JniHook_unref( &ps->hooks.collation, 1 );
-        ps->hooks.collation.midCallback = midCallback;
-        ps->hooks.collation.jObj = S3JniRefGlobal(oCollation);
+        pCC->midCallback = midCallback;
+        pCC->jObj = S3JniRefGlobal(oCollation);
+      }else{
+        CollationCallback_xDestroy(pCC);
       }
-      S3JniMutex_S3JniDb_leave;
     }else{
       rc = SQLITE_NOMEM;
     }
+    sqlite3_free(zName);
   }
   return (jint)rc;
 }
@@ -3363,7 +3400,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
   const int isPre = 0!=pDb;
   S3JniHook hook;
 
-  S3JniHook_localdup(env, isPre ?
+  S3JniHook_localdup(isPre ?
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
                  &ps->hooks.preUpdate
 #else
@@ -3392,7 +3429,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
                            (jint)opId, jDbName, jTable, (jlong)iKey1);
     S3JniIfThrew{
       S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback");
-      s3jni_db_exception(env, ps, 0,
+      s3jni_db_exception(ps, 0,
                          "sqlite3_(pre)update_hook() callback threw");
     }
   }
@@ -3516,19 +3553,21 @@ S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)(
 static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb,
                                   jint iCol, jobject jOut){
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
-  sqlite3_value * pOut = 0;
   sqlite3 * const pDb = PtrGet_sqlite3(jDb);
-  int rc;
-  int (*fOrig)(sqlite3*,int,sqlite3_value**) =
-    isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
-  rc = fOrig(pDb, (int)iCol, &pOut);
-  if( 0==rc ){
-    jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
-    if( pWrap ){
-      OutputPointer_set_sqlite3_value(env, jOut, pWrap);
-      S3JniUnrefLocal(pWrap);
-    }else{
-      rc = SQLITE_NOMEM;
+  int rc = SQLITE_MISUSE;
+  if( pDb ){
+    sqlite3_value * pOut = 0;
+    int (*fOrig)(sqlite3*,int,sqlite3_value**) =
+      isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
+    rc = fOrig(pDb, (int)iCol, &pOut);
+    if( 0==rc ){
+      jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
+      if( pWrap ){
+        OutputPointer_set_sqlite3_value(env, jOut, pWrap);
+        S3JniUnrefLocal(pWrap);
+      }else{
+        rc = SQLITE_NOMEM;
+      }
     }
   }
   return rc;
@@ -3557,11 +3596,11 @@ static int s3jni_progress_handler_impl(void *pP){
   S3JniDeclLocal_env;
   S3JniHook hook;
 
-  S3JniHook_localdup( env, &ps->hooks.progress, &hook );
+  S3JniHook_localdup(&ps->hooks.progress, &hook);
   if( hook.jObj ){
     rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback);
     S3JniIfThrew{
-      rc = s3jni_db_exception(env, ps, rc,
+      rc = s3jni_db_exception(ps, rc,
                               "sqlite3_progress_handler() callback threw");
     }
     S3JniHook_localundup(hook);
@@ -3573,8 +3612,6 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
   JniArgsEnvClass,jobject jDb, jint n, jobject jProgress
 ){
   S3JniDb * const ps = S3JniDb_from_java(jDb);
-  jclass klazz;
-  jmethodID xCallback;
   S3JniHook * const pHook = ps ? &ps->hooks.progress : 0;
 
   if( !ps ) return;
@@ -3582,22 +3619,21 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
   if( n<1 || !jProgress ){
     S3JniHook_unref(pHook, 0);
     sqlite3_progress_handler(ps->pDb, 0, 0, 0);
-    S3JniMutex_S3JniDb_leave;
-    return;
-  }
-  klazz = (*env)->GetObjectClass(env, jProgress);
-  xCallback = (*env)->GetMethodID(env, klazz, "call", "()I");
-  S3JniUnrefLocal(klazz);
-  S3JniIfThrew {
-    S3JniExceptionClear;
-    s3jni_db_error(ps->pDb, SQLITE_ERROR,
-                   "Cannot not find matching xCallback() on "
-                   "ProgressHandler object.");
   }else{
-    S3JniUnrefGlobal(pHook->jObj);
-    pHook->midCallback = xCallback;
-    pHook->jObj = S3JniRefGlobal(jProgress);
-    sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
+    jclass const klazz = (*env)->GetObjectClass(env, jProgress);
+    jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", "()I");
+    S3JniUnrefLocal(klazz);
+    S3JniIfThrew {
+      S3JniExceptionClear;
+      s3jni_db_error(ps->pDb, SQLITE_ERROR,
+                     "Cannot not find matching xCallback() on "
+                     "ProgressHandler object.");
+    }else{
+      S3JniUnrefGlobal(pHook->jObj);
+      pHook->midCallback = xCallback;
+      pHook->jObj = S3JniRefGlobal(jProgress);
+      sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
+    }
   }
   S3JniMutex_S3JniDb_leave;
 }
@@ -3752,7 +3788,7 @@ S3JniApi(sqlite3_result_error(),void,1result_1error)(
 S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)(
   JniArgsEnvClass, jobject jpCx, jint v
 ){
-  sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), v ? (int)v : SQLITE_ERROR);
+  sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), (int)v);
 }
 
 S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)(
@@ -3850,7 +3886,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
   S3JniHook hook;
   int rc = 0;
 
-  S3JniHook_localdup(env, &ps->hooks.auth, &hook );
+  S3JniHook_localdup(&ps->hooks.auth, &hook );
   if( hook.jObj ){
     jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0;
     jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0;
@@ -3860,7 +3896,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
     rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op,
                                s0, s1, s3, s3);
     S3JniIfThrew{
-      rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback");
+      rc = s3jni_db_exception(ps, rc, "sqlite3_set_authorizer() callback");
     }
     S3JniUnrefLocal(s0);
     S3JniUnrefLocal(s1);
@@ -3924,50 +3960,6 @@ S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)(
                                 (sqlite3_int64)rowId);
 }
 
-S3JniApi(sqlite3_status(),jint,1status)(
-  JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
-                    jboolean reset
-){
-  int iCur = 0, iHigh = 0;
-  int rc = sqlite3_status( op, &iCur, &iHigh, reset );
-  if( 0==rc ){
-    OutputPointer_set_Int32(env, jOutCurrent, iCur);
-    OutputPointer_set_Int32(env, jOutHigh, iHigh);
-  }
-  return (jint)rc;
-}
-
-S3JniApi(sqlite3_status64(),jint,1status64)(
-  JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
-                      jboolean reset
-){
-  sqlite3_int64 iCur = 0, iHigh = 0;
-  int rc = sqlite3_status64( op, &iCur, &iHigh, reset );
-  if( 0==rc ){
-    OutputPointer_set_Int64(env, jOutCurrent, iCur);
-    OutputPointer_set_Int64(env, jOutHigh, iHigh);
-  }
-  return (jint)rc;
-}
-
-static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
-                              jbyteArray baG, jbyteArray baT, jint escLike){
-  int rc = 0;
-  jbyte * const pG = s3jni_jbytearray_bytes(baG);
-  jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0;
-
-  s3jni_oom_fatal(pT);
-  /* Note that we're relying on the byte arrays having been
-     NUL-terminated on the Java side. */
-  rc = isLike
-    ? sqlite3_strlike((const char *)pG, (const char *)pT,
-                      (unsigned int)escLike)
-    : sqlite3_strglob((const char *)pG, (const char *)pT);
-  s3jni_jbytearray_release(baG, pG);
-  s3jni_jbytearray_release(baT, pT);
-  return rc;
-}
-
 S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
   JniArgsEnvClass
 ){
@@ -3997,7 +3989,6 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
     sqlite3_free(d);
   }
   S3JniMutex_S3JniDb_leave;
-
 #if 0
   /*
   ** Is automatically closing any still-open dbs a good idea? We will
@@ -4011,11 +4002,54 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
   }
   S3JniMutex_S3JniDb_leave;
 #endif
-
   /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
   return sqlite3_shutdown();
 }
 
+S3JniApi(sqlite3_status(),jint,1status)(
+  JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
+                    jboolean reset
+){
+  int iCur = 0, iHigh = 0;
+  int rc = sqlite3_status( op, &iCur, &iHigh, reset );
+  if( 0==rc ){
+    OutputPointer_set_Int32(env, jOutCurrent, iCur);
+    OutputPointer_set_Int32(env, jOutHigh, iHigh);
+  }
+  return (jint)rc;
+}
+
+S3JniApi(sqlite3_status64(),jint,1status64)(
+  JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
+                      jboolean reset
+){
+  sqlite3_int64 iCur = 0, iHigh = 0;
+  int rc = sqlite3_status64( op, &iCur, &iHigh, reset );
+  if( 0==rc ){
+    OutputPointer_set_Int64(env, jOutCurrent, iCur);
+    OutputPointer_set_Int64(env, jOutHigh, iHigh);
+  }
+  return (jint)rc;
+}
+
+static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
+                              jbyteArray baG, jbyteArray baT, jint escLike){
+  int rc = 0;
+  jbyte * const pG = s3jni_jbytearray_bytes(baG);
+  jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0;
+
+  s3jni_oom_fatal(pT);
+  /* Note that we're relying on the byte arrays having been
+     NUL-terminated on the Java side. */
+  rc = isLike
+    ? sqlite3_strlike((const char *)pG, (const char *)pT,
+                      (unsigned int)escLike)
+    : sqlite3_strglob((const char *)pG, (const char *)pT);
+  s3jni_jbytearray_release(baG, pG);
+  s3jni_jbytearray_release(baT, pT);
+  return rc;
+}
+
 S3JniApi(sqlite3_strglob(),jint,1strglob)(
   JniArgsEnvClass, jbyteArray baG, jbyteArray baT
 ){
@@ -4061,7 +4095,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
   int rc = 0;
   S3JniHook hook;
 
-  S3JniHook_localdup( env, &ps->hooks.trace, &hook );
+  S3JniHook_localdup(&ps->hooks.trace, &hook );
   if( !hook.jObj ){
     return 0;
   }
@@ -4092,16 +4126,17 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
       /* Create a new temporary sqlite3_stmt wrapper */
       jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
       if( !jP ){
-        S3JniUnrefLocal(jX);
         rc = SQLITE_NOMEM;
       }
     }
-    assert(jP);
-    rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
-                                    (jint)traceflag, jP, jX);
-    S3JniIfThrew{
-      rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
-                              "sqlite3_trace_v2() callback threw.");
+    if( 0==rc ){
+      assert(jP);
+      rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
+                                      (jint)traceflag, jP, jX);
+      S3JniIfThrew{
+        rc = s3jni_db_exception(ps, SQLITE_ERROR,
+                                "sqlite3_trace_v2() callback threw.");
+      }
     }
   }
   S3JniUnrefLocal(jPUnref);
@@ -4135,8 +4170,8 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
                           "Cannot not find matching call() on "
                           "TracerCallback object.");
     }else{
-      S3JniMutex_S3JniDb_enter;
       hook.jObj = S3JniRefGlobal(jTracer);
+      S3JniMutex_S3JniDb_enter;
       rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps);
       if( 0==rc ){
         S3JniHook_unref(&ps->hooks.trace, 0);
@@ -4349,7 +4384,7 @@ static void Fts5JniAux_free(Fts5JniAux * const s){
   S3JniDeclLocal_env;
   if( env ){
     /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/
-    s3jni_call_xDestroy(env, s->jObj);
+    s3jni_call_xDestroy(s->jObj);
     S3JniUnrefGlobal(s->jObj);
     S3JniUnrefGlobal(s->jUserData);
   }
@@ -4579,7 +4614,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){
     S3JniFts5AuxData * const p = x;
     if( p->jObj ){
       S3JniDeclLocal_env;
-      s3jni_call_xDestroy(env, p->jObj);
+      s3jni_call_xDestroy(p->jObj);
       S3JniUnrefGlobal(p->jObj);
     }
     sqlite3_free(x);
@@ -4792,7 +4827,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
     if( jAux ){
       /* Emulate how xSetAuxdata() behaves when it cannot alloc
       ** its auxdata wrapper. */
-      s3jni_call_xDestroy(env, jAux);
+      s3jni_call_xDestroy(jAux);
     }
     return SQLITE_NOMEM;
   }
index 3d60362f61a46c511b6e1c5e2477de2ef0578e7f..e418de60966affe5ad95f195e8d1cd2124308ecf 100644 (file)
@@ -1498,7 +1498,7 @@ public class Tester1 implements Runnable {
 
     if( sqlLog ){
       if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){
-        int rc = sqlite3_config( new ConfigSqllogCallback() {
+        final ConfigSqllogCallback log = new ConfigSqllogCallback() {
             @Override public void call(sqlite3 db, String msg, int op){
               switch(op){
                 case 0: outln("Opening db: ",db); break;
@@ -1506,7 +1506,12 @@ public class Tester1 implements Runnable {
                 case 2: outln("Closing db: ",db); break;
               }
             }
-          });
+          };
+        int rc = sqlite3_config( log );
+        affirm( 0==rc );
+        rc = sqlite3_config( null );
+        affirm( 0==rc );
+        rc = sqlite3_config( log );
         affirm( 0==rc );
       }else{
         outln("WARNING: -sqllog is not active because library was built ",
index d1e4b1ee3cdb2c05f71d264d660c86f4289f3958..4b547e6bc971c848ae4349ffe012fe443ec7c21d 100644 (file)
@@ -24,6 +24,14 @@ public interface XDestroyCallback {
      Must perform any cleanup required by this object. Must not
      throw. Must not call back into the sqlite3 API, else it might
      invoke a deadlock.
+
+     WARNING: as a rule, it is never safe to register individual
+     instances with this interface multiple times in the
+     library. e.g., do not register the same CollationCallback with
+     multiple arities or names using sqlite3_create_collation().  If
+     this rule is violated, the library will eventually try to free
+     each individual reference, leading to memory corruption or a
+     crash via duplicate free().
   */
   public void xDestroy();
 }
index 7c36b33c283c2d0b5b0b868f76252da4708afade..ae37df779d4a9699a1d4a58929b456f1ecae7196 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Updates\sto\stestrunner.tcl\sso\sthat\sit\sruns\sfuzztest\susing\smultiple\sjobs.
-D 2023-08-28T20:14:19.097
+C Correct\sJNI\smapping\sof\scollations\sto\sbe\s1-db-to-many-collations.
+D 2023-08-28T20:21:56.713
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
 F ext/jni/GNUmakefile 374873bf6d2cd6ceafb458e28b59140dbb074f01f7adddf7e15a3ee3daf44551
 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
-F ext/jni/src/c/sqlite3-jni.c c92a764a728a3fcd34882defb7517c47af22c9f7fe184113015cdab9fe401c21
+F ext/jni/src/c/sqlite3-jni.c 3c16035f6d604c17e50a6477b7309ca8885c54cd8a9c85e8c36d2f8b918d71e7
 F ext/jni/src/c/sqlite3-jni.h 12e1a5ef5ee1795dc22577c285b4518dfd8aa4af45757f6cb81a555d967bf201
 F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
@@ -264,13 +264,13 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b
 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185
 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 440d64e8c4cff53bd3c0cc676381212489198302d7f1aaa535712c2d7163cc69
 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
-F ext/jni/src/org/sqlite/jni/Tester1.java a9558165dbb085494705525ef28e41d337d8348bf44259ce1f77cad72547bfdf
+F ext/jni/src/org/sqlite/jni/Tester1.java 21d78aa59bfc5ce5ff242d4bb6f6d2255d162fba8be5859ab87c9201d61433f0
 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246
 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d
 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
 F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246
-F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471
+F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 50c5ca124ef6c6b735a7e136e7a23a557be367e61b56d4aab5777a614ab46cc2
 F ext/jni/src/org/sqlite/jni/annotation/Canonical.java e55b82c8259b617ff754ac493fd8b79602631d659b87a858b987540e4c4fdf56
 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java d48ebd7ae6bbb78bd47d54431c85e1521c89b1d3864a2b6eafd9c0e1b2341457
 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 6f962a98c9a5c6e9d21c50ae8716b16bdfdc934a191608cbb7e12ea588ddb6af
@@ -2107,9 +2107,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 ecf07a0144dc6402b1e0924b1775d99dc465b27aa86a2718cac60a9b4c974312 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b
-R 31bc738c1437151db584e22f2c509958
-T +closed 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b
-U dan
-Z 97aabfccc20038ea125aae98b8297d02
+P ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf
+R 68323f68f37601aa48cb5f2c61fd0c0a
+U stephan
+Z 6560ea32a29f03de2be1f2fe7d94526d
 # Remove this line to create a well-formed Fossil manifest.
index d7d1100a9c8285505364be04c9c6d2e0389ae705..3ef7700236975e8329815dbe88c9a50f51477fe0 100644 (file)
@@ -1 +1 @@
-ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf
\ No newline at end of file
+b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f
\ No newline at end of file