]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Further JNI cleanups and javadoc additions.
authorstephan <stephan@noemail.net>
Mon, 28 Aug 2023 11:10:13 +0000 (11:10 +0000)
committerstephan <stephan@noemail.net>
Mon, 28 Aug 2023 11:10:13 +0000 (11:10 +0000)
FossilOrigin-Name: c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10

ext/jni/GNUmakefile
ext/jni/src/c/sqlite3-jni.c
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
ext/jni/src/org/sqlite/jni/package-info.java
manifest
manifest.uuid

index 3518518520193835a381d107365836f9d6d5eb10..44a0d740112d38cf20d5d5ff5538e80b7a90f671 100644 (file)
@@ -329,7 +329,8 @@ dir.doc   := $(dir.jni)/javadoc
 doc.index := $(dir.doc)/index.html
 $(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
        @if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
-       $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet -subpackages org.sqlite.jni
+       $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
+               -subpackages org.sqlite.jni -exclude org.sqlite.jni.tester
        @echo "javadoc output is in $@"
 
 .PHONY: doc javadoc docserve
index 471cc1503d656856c135083e909fd363ab1688a2..00138ba40031dd934bf58bef07ecec0a537ded9c 100644 (file)
@@ -565,8 +565,9 @@ struct S3JniGlobalType {
   struct {
     S3JniEnv * aHead      /* Linked list of in-use instances */;
     S3JniEnv * aFree      /* Linked list of free instances */;
-    sqlite3_mutex * mutex /* mutex for aHead and aFree as well for
-                             first-time inits of nph[] entries. */;
+    sqlite3_mutex * mutex /* mutex for aHead and aFree, first-time
+                             inits of nph[] entries, and
+                             NativePointerHolder_get/set(). */;
     void const * locker   /* env mutex is held on this object's behalf.
                              Used only for sanity checking. */;
   } envCache;
@@ -686,7 +687,7 @@ static S3JniGlobalType S3JniGlobal = {};
 ** argument is a Java sqlite3 object, as this operation only has void
 ** pointers to work with.
 */
-#define PtrGet_T(T,OBJ) NativePointerHolder_get(env, OBJ, &S3NphRefs.T)
+#define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3NphRefs.T)
 #define PtrGet_sqlite3(OBJ) PtrGet_T(sqlite3, OBJ)
 #define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ)
 #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ)
@@ -821,7 +822,7 @@ static JNIEnv * s3jni_env(void){
 ** insofar as possible. Calls (*env)->FatalError() if allocation of an
 ** entry fails. That's hypothetically possible but "shouldn't happen."
 */
-static S3JniEnv * S3JniEnv_get(JNIEnv * const env){
+static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
   struct S3JniEnv * row;
   S3JniMutex_Env_enter;
   row = SJG.envCache.aHead;
@@ -852,6 +853,8 @@ static S3JniEnv * S3JniEnv_get(JNIEnv * const env){
   return row;
 }
 
+#define S3JniEnv_get() S3JniEnv__get(env)
+
 /*
 ** This function is NOT part of the sqlite3 public API. It is strictly
 ** for use by the sqlite project's own Java/JNI bindings.
@@ -1162,12 +1165,13 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
 }
 
 /*
-** Clears s's state and moves it to the free-list. Requires that that the
-** caller has locked S3JniGlobal.perDb.mutex.
+** Clears s's state and moves it to the free-list. Requires that
+** S3JniGlobal.perDb.mutex is locked.
 */
-static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){
+static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
+  assert( s );
   if( s ){
-    S3JniMutex_S3JniDb_enter;
+    S3JniMutex_S3JniDb_assertLocker;
     assert(s->pPrev != s);
     assert(s->pNext != s);
     assert(s->pPrev ? (s->pPrev!=s->pNext) : 1);
@@ -1181,10 +1185,16 @@ static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){
     s->pNext = SJG.perDb.aFree;
     if(s->pNext) s->pNext->pPrev = s;
     SJG.perDb.aFree = s;
-    S3JniMutex_S3JniDb_leave;
   }
 }
 
+static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){
+  S3JniMutex_S3JniDb_enter;
+  S3JniDb__set_aside_unlocked(env, s);
+  S3JniMutex_S3JniDb_leave;
+}
+#define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
+
 /*
 ** Uncache any state for the given JNIEnv, clearing all Java
 ** references the cache owns. Returns true if env was cached and false
@@ -1220,16 +1230,20 @@ static int S3JniEnv_uncache(JNIEnv * const env){
 
 /*
 ** Searches the NativePointerHolder cache for the given combination of
-** args.  It returns a cache entry with its klazz member set.
+** args.  It returns a cache entry with its klazz member set. This is
+** an O(1) operation except on the first call for a given pRef, during
+** which pRef->klazz and pRef->pRef are initialized thread-safely. In
+** the latter case it's still effectively O(1), but with a much longer
+** 1.
 **
 ** It is up to the caller to populate the other members of the
-** returned object if needed, taking care to lock the population with
-** S3JniMutex_Nph_enter/LEAVE.
+** returned object if needed, taking care to lock the modification
+** with S3JniMutex_Nph_enter/leave.
 **
 ** This simple cache catches >99% of searches in the current
 ** (2023-07-31) tests.
 */
-static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef){
+static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3NphRef const* pRef){
   /**
    According to:
 
@@ -1261,13 +1275,15 @@ static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef)
   return pNC;
 }
 
+#define S3JniGlobal_nph(PREF) S3JniGlobal__nph(env, PREF)
+
 /*
 ** Returns the ID of the "nativePointer" field from the given
 ** NativePointerHolder<T> class.
 */
 static jfieldID NativePointerHolder_field(JNIEnv * const env,
                                           S3NphRef const* pRef){
-  S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+  S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
   if( !pNC->fidValue ){
     S3JniMutex_Nph_enter;
     if( !pNC->fidValue ){
@@ -1286,24 +1302,31 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env,
 ** zClassName must be a static string so we can use its address
 ** as a cache key.
 */
-static void NativePointerHolder_set(JNIEnv * env, S3NphRef const* pRef,
+static void NativePointerHolder__set(JNIEnv * env, S3NphRef const* pRef,
                                     jobject ppOut, const void * p){
-  (*env)->SetLongField(env, ppOut, NativePointerHolder_field(env, pRef),
-                       (jlong)p);
+  jfieldID const fid = NativePointerHolder_field(env, pRef);
+  S3JniMutex_Nph_enter;
+  (*env)->SetLongField(env, ppOut, fid, (jlong)p);
+  S3JniMutex_Nph_leave;
   S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer.");
 }
 
+#define NativePointerHolder_set(PREF,PPOUT,P) \
+  NativePointerHolder__set(env, PREF, PPOUT, P)
+
 /*
 ** Fetches a native ptr value from NativePointerHolder object ppOut.
 ** zClassName must be a static string so we can use its address as a
 ** cache key. This is a no-op if pObj is NULL.
 */
-static void * NativePointerHolder_get(JNIEnv * env, jobject pObj,
-                                      S3NphRef const* pRef){
+static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
+                                       S3NphRef const* pRef){
   if( pObj ){
-    void * const rv = (void*)(*env)->GetLongField(
-      env, pObj, NativePointerHolder_field(env, pRef)
-    );
+    jfieldID const fid = NativePointerHolder_field(env, pRef);
+    void * rv;
+    S3JniMutex_Nph_enter;
+    rv = (void*)(*env)->GetLongField(env, pObj, fid);
+    S3JniMutex_Nph_leave;
     S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer.");
     return rv;
   }else{
@@ -1311,6 +1334,9 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj,
   }
 }
 
+#define NativePointerHolder_get(JOBJ,NPHREF) \
+  NativePointerHolder__get(env, (JOBJ), (NPHREF))
+
 /*
 ** Extracts the new S3JniDb instance from the free-list, or allocates
 ** one if needed, associats it with pDb, and returns.  Returns NULL on
@@ -1351,6 +1377,13 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
   return rv;
 }
 
+/* Short-lived code consolidator. */
+#define S3JniDb_search            \
+  s = SJG.perDb.aHead;            \
+  for( ; pDb && s; s = s->pNext){ \
+    if( s->pDb == pDb ) break;    \
+  }
+
 /*
 ** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3
 ** object, or NULL if jDb is NULL, no pointer can be extracted
@@ -1361,20 +1394,28 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
 static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
   S3JniDb * s = 0;
   sqlite3 * pDb = 0;
+
   S3JniMutex_S3JniDb_enter;
-  if( jDb ){
-    pDb = PtrGet_sqlite3(jDb);
-  }
-  s = SJG.perDb.aHead;
-  for( ; pDb && s; s = s->pNext){
-    if( s->pDb == pDb ){
-      break;
-    }
-  }
+  if( jDb ) pDb = PtrGet_sqlite3(jDb);
+  S3JniDb_search;
   S3JniMutex_S3JniDb_leave;
   return s;
 }
 
+/* An experiment */
+//#define CLOSE_DB_LOCKED
+#if defined(CLOSE_DB_LOCKED)
+static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
+  S3JniDb * s = 0;
+  sqlite3 * pDb = 0;
+
+  S3JniMutex_S3JniDb_assertLocker;
+  if( jDb ) pDb = PtrGet_sqlite3(jDb);
+  S3JniDb_search;
+  return s;
+}
+#endif
+
 /*
 ** Returns the S3JniDb object for the sqlite3 object, or NULL if pDb
 ** is NULL, or no matching entry
@@ -1384,13 +1425,9 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
 */
 static S3JniDb * S3JniDb__from_c(JNIEnv * const env, sqlite3 *pDb){
   S3JniDb * s = 0;
+
   S3JniMutex_S3JniDb_enter;
-  s = SJG.perDb.aHead;
-  for( ; pDb && s; s = s->pNext){
-    if( s->pDb == pDb ){
-      break;
-    }
-  }
+  S3JniDb_search;
   S3JniMutex_S3JniDb_leave;
   return s;
 }
@@ -1442,7 +1479,9 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
 ** misbehave.
 */
 static jfieldID OutputPointer_field(JNIEnv * const env, S3NphRef const * pRef){
-  S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+  S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
+
+  assert( pNC->klazz );
   if( !pNC->fidValue ){
     S3JniMutex_Nph_enter;
     if( !pNC->fidValue ){
@@ -1628,7 +1667,7 @@ static void ResultJavaValue_finalizer(void *v){
 static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef,
                                               const void * pNative){
   jobject rv = 0;
-  S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+  S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
   if( !pNC->midCtor ){
     S3JniMutex_Nph_enter;
     if( !pNC->midCtor ){
@@ -1640,7 +1679,7 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const
   rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor);
   S3JniExceptionIsFatal("No-arg constructor threw.");
   s3jni_oom_check(rv);
-  if( rv ) NativePointerHolder_set(env, pRef, rv, pNative);
+  if( rv ) NativePointerHolder_set(pRef, rv, pNative);
   return rv;
 }
 
@@ -1778,7 +1817,7 @@ static int udf_args(JNIEnv *env,
   if( !jcx ) goto error_oom;
   ja = (*env)->NewObjectArray(
     env, argc, SJG.g.cObj
-    /* S3JniGlobal_nph(env,&S3NphRefs.sqlite3_value)->klazz would be
+    /* S3JniGlobal_nph(&S3NphRefs.sqlite3_value)->klazz would be
        more correct, but it unpredictably triggers an assert in the
        JVM. */, NULL);
   s3jni_oom_check( ja );
@@ -2056,7 +2095,7 @@ 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(env);
+  jc = S3JniEnv_get();
   ps = jc->pdbOpening;
   if( !ps ){
     *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in "
@@ -2067,7 +2106,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
   assert( !ps->pDb && "it's still being opened" );
   assert( ps->jDb );
   ps->pDb = pDb;
-  NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb)
+  NativePointerHolder_set(&S3NphRefs.sqlite3, ps->jDb, pDb)
     /* As of here, the Java/C connection is complete */;
   for( i = 0; go && 0==rc; ++i ){
     S3JniAutoExtension ax = {0,0}
@@ -2373,6 +2412,8 @@ 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 ){
@@ -2380,11 +2421,39 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
       ? (jint)sqlite3_close(ps->pDb)
       : (jint)sqlite3_close_v2(ps->pDb);
     if( 0==rc ){
-      S3JniDb_set_aside(env, ps)
+      S3JniDb_set_aside(ps)
         /* MUST come after close() because of ps->trace. */;
-      NativePointerHolder_set(env, &S3NphRefs.sqlite3, jDb, 0);
+      NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0);
     }
   }
+#else
+  /* This impl leads to an assertion in sqlite3_close[_v2]()
+
+     pthreadMutexEnter: Assertion `p->id==SQLITE_MUTEX_RECURSIVE
+                        || pthreadMutexNotheld(p)' failed.
+
+     For reasons not yet fully understood.
+  */
+  assert(version == 1 || version == 2);
+  if( 0!=jDb ){
+    S3JniDb * ps;
+    S3JniMutex_S3JniDb_enter;
+    ps = S3JniDb__from_java_unlocked(env, 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. */;
+        NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0);
+      }
+    }else{
+      /* ps is from S3Global.perDb.aFree. */
+    }
+    S3JniMutex_S3JniDb_leave;
+  }
+#endif
   return (jint)rc;
 }
 
@@ -3031,7 +3100,7 @@ S3JniApi(sqlite3_finalize(),jint,1finalize)(
   sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
   if( pStmt ){
     rc = sqlite3_finalize(pStmt);
-    NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jpStmt, 0);
+    NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jpStmt, 0);
   }
   return rc;
 }
@@ -3086,7 +3155,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc,
                           S3JniDb ** ps){
   int rc = 0;
   jobject jDb = 0;
-  *jc = S3JniEnv_get(env);
+  *jc = S3JniEnv_get();
   if( !*jc ){
     rc = SQLITE_NOMEM;
     goto end;
@@ -3136,14 +3205,14 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc,
     assert(ps->jDb);
     if( 0==ps->pDb ){
       ps->pDb = *ppDb;
-      NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, *ppDb)
+      NativePointerHolder_set(&S3NphRefs.sqlite3, ps->jDb, *ppDb)
         /* As of here, the Java/C connection is complete */;
     }else{
       assert( ps->pDb==*ppDb
               && "Set up via s3jni_run_java_auto_extensions()" );
     }
   }else{
-    S3JniDb_set_aside(env, ps);
+    S3JniDb_set_aside(ps);
     ps = 0;
   }
   OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0);
@@ -3241,7 +3310,7 @@ end:
       OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0));
     }
     if( pStmt ){
-      NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jStmt, pStmt);
+      NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jStmt, pStmt);
     }else{
       /* Happens for comments and whitespace. */
       S3JniUnrefLocal(jStmt);
@@ -4203,7 +4272,7 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
   printf("Mutex entry:"
          "\n\tglobal       = %u"
          "\n\tenv          = %u"
-         "\n\tnph inits    = %u"
+         "\n\tnph          = %u"
          "\n\tperDb        = %u"
          "\n\tautoExt list = %u"
          "\n\tS3JniUdf free-list = %u"
@@ -4212,11 +4281,12 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
          SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb,
          SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf,
          SJG.metrics.nMetrics);
-  printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n",
+  puts("Allocs:");
+  printf("\tS3JniDb:  %u alloced (*%u = %u bytes), %u recycled\n",
          SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb),
          (unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)),
          SJG.metrics.nPdbRecycled);
-  printf("S3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n",
+  printf("\tS3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n",
          SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf),
          (unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)),
          SJG.metrics.nUdfRecycled);
@@ -4254,10 +4324,10 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
   JNIEXPORT ReturnType JNICALL                  \
   JniFuncNameFtsTok(Suffix)
 
-#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.fts5_api)
-#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.fts5_tokenizer)
-#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.Fts5Context)
-#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.Fts5Tokenizer)
+#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.fts5_api)
+#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.fts5_tokenizer)
+#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.Fts5Context)
+#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.Fts5Tokenizer)
 #define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
 
 /**
@@ -4679,7 +4749,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
 JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase,
                             jobject jCallback){
   Fts5ExtDecl;
-  S3JniEnv * const jc = S3JniEnv_get(env);
+  S3JniEnv * const jc = S3JniEnv_get();
   struct s3jni_xQueryPhraseState s;
   jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
 
@@ -4763,7 +4833,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef,
                                  jint tokFlags, jobject jFcx,
                                  jbyteArray jbaText, jobject jCallback){
   Fts5ExtDecl;
-  S3JniEnv * const jc = S3JniEnv_get(env);
+  S3JniEnv * const jc = S3JniEnv_get();
   struct s3jni_xQueryPhraseState s;
   int rc = 0;
   jbyte * const pText = jCallback ? s3jni_jbytearray_bytes(jbaText) : 0;
index 6cad93d74be15c377d9acac02012dcfffdea4356..d32320b26fa6c2f1ea45596635f2cae0e67d998a 100644 (file)
@@ -39,8 +39,13 @@ import org.sqlite.jni.annotation.*;
 
   <p><a href="https://sqlite.org/c3ref/intro.html">https://sqlite.org/c3ref/intro.html</a>
 
-  <p>A handful of Java-specific APIs have been added which are documented
-  here.
+  <p>A handful of Java-specific APIs have been added which are
+  documented here. A number of convenience overloads are provided
+  which are not documented but whose semantics map 1-to-1 in an
+  intuitive manner. e.g. {@link
+  #sqlite3_result_set(sqlite3_context,int)} is equivalent to {@link
+  #sqlite3_result_int}, and sqlite3_result_set() has many
+  type-specific overloads.
 
   <p>Though most of the {@code SQLITE_abc...} C macros represented by
   this class are defined as final, a few are necessarily non-final
@@ -228,15 +233,16 @@ public final class SQLite3Jni {
 
   /**
      Works like the C-level sqlite3_bind_text() but assumes
-     SQLITE_TRANSIENT for the final C API parameter.
+     SQLITE_TRANSIENT for the final C API parameter. The byte array is
+     assumed to be in UTF-8 encoding.
 
      <p>Results are undefined if data is not null and
-     maxBytes>=data.length. If maxBytes is negative then results are
+     maxBytes>=utf8.length. If maxBytes is negative then results are
      undefined if data is not null and does not contain a NUL byte.
   */
   @Canonical
-  private static native int sqlite3_bind_text(
-    @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
+  public static native int sqlite3_bind_text(
+    @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8, int maxBytes
   );
 
   /**
@@ -253,14 +259,14 @@ public final class SQLite3Jni {
   }
 
   /**
-     Requires that data be null or in UTF-8 encoding.
+     Requires that utf8 be null or in UTF-8 encoding.
   */
   public static int sqlite3_bind_text(
-    @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
+    @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8
   ){
-    return (null == data)
+    return (null == utf8)
       ? sqlite3_bind_null(stmt, ndx)
-      : sqlite3_bind_text(stmt, ndx, data, data.length);
+      : sqlite3_bind_text(stmt, ndx, utf8, utf8.length);
   }
 
   /**
@@ -269,7 +275,7 @@ public final class SQLite3Jni {
      platform byte order.
   */
   @Canonical
-  private static native int sqlite3_bind_text16(
+  public static native int sqlite3_bind_text16(
     @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
   );
 
@@ -728,6 +734,14 @@ public final class SQLite3Jni {
      class.) For that vast majority of uses, that capability is not
      necessary, however, and overloads are provided which gloss over
      that.
+
+     <p>Results are undefined if maxBytes>=sqlUtf8.length.
+
+     <p>This routine is private because its maxBytes value is not
+     strictly necessary in the Java interface, as sqlUtf8.length tells
+     us the information we need. Making this public would give clients
+     more ways to shoot themselves in the foot without providing any
+     real utility.
   */
   @Canonical
   private static native int sqlite3_prepare(
@@ -736,6 +750,14 @@ public final class SQLite3Jni {
     @Nullable OutputPointer.Int32 pTailOffset
   );
 
+  /**
+     Works like the canonical sqlite3_prepare() but its "tail" output
+     argument is returned as the index offset into the given
+     UTF-8-encoded byte array at which SQL parsing stopped. The
+     semantics are otherwise identical to the C API counterpart.
+
+     <p>Several overloads provided simplified call signatures.
+  */
   public static int sqlite3_prepare(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
     @NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -776,8 +798,7 @@ public final class SQLite3Jni {
   }
 
   /**
-     See sqlite3_prepare() for details about the slight API differences
-     from the C API.
+     @see #sqlite3_prepare
   */
   @Canonical
   private static native int sqlite3_prepare_v2(
@@ -786,6 +807,11 @@ public final class SQLite3Jni {
     @Nullable OutputPointer.Int32 pTailOffset
   );
 
+  /**
+     Works like the canonical sqlite3_prepare_v2() but its "tail"
+     output paramter is returned as the index offset into the given
+     byte array at which SQL parsing stopped.
+  */
   public static int sqlite3_prepare_v2(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
     @NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -821,6 +847,9 @@ public final class SQLite3Jni {
     return out.take();
   }
 
+  /**
+     @see #sqlite3_prepare
+  */
   @Canonical
   private static native int sqlite3_prepare_v3(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
@@ -828,6 +857,11 @@ public final class SQLite3Jni {
     @Nullable OutputPointer.Int32 pTailOffset
   );
 
+  /**
+     Works like the canonical sqlite3_prepare_v2() but its "tail"
+     output paramter is returned as the index offset into the given
+     byte array at which SQL parsing stopped.
+  */
   public static int sqlite3_prepare_v3(
     @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int prepFlags,
     @NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -993,13 +1027,15 @@ public final class SQLite3Jni {
   }
 
   /**
-     Equivalent to passing e.getMessage() to
-     sqlite3_result_error(db,String).
+     Equivalent to passing e.toString() to {@link
+     #sqlite3_result_error(sqlite3_context,String)}.  Note that
+     toString() is used instead of getMessage() because the former
+     prepends the exception type name to the message.
   */
   public static void sqlite3_result_error(
     @NotNull sqlite3_context cx, @NotNull Exception e
   ){
-    sqlite3_result_error(cx, e.getMessage());
+    sqlite3_result_error(cx, e.toString());
   }
 
   @Canonical
@@ -1033,18 +1069,14 @@ public final class SQLite3Jni {
   );
 
   /**
-     Binds the SQL result to the given object, or
-     {@link #sqlite3_result_null} if {@code o} is null. Use
-     {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} to
-     fetch it.
+     Binds the SQL result to the given object, or {@link
+     #sqlite3_result_null} if {@code o} is null. Use {@link
+     #sqlite3_value_java_object} to fetch it.
 
      <p>This is implemented in terms of C's sqlite3_result_pointer(),
-     but that function is not exposed to JNI because its 3rd argument
-     must be a constant string (the library does not copy it), and
-     those semantics are cumbersome to bridge cross-language. Java
-     doesn't need that argument for type safety, in any case: the
-     object can, after extraction on the other end of the API, be
-     inspected with {@code instanceof}.
+     but that function is not exposed to JNI because (A)
+     cross-language semantic mismatch and (B) Java doesn't need that
+     argument for its intended purpose (type safety).
 
      <p>Note that there is no sqlite3_column_java_object(), as the
      C-level API has no sqlite3_column_pointer() to proxy.
@@ -1057,57 +1089,63 @@ public final class SQLite3Jni {
   );
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, @NotNull Integer v
+    @NotNull sqlite3_context cx, @NotNull Boolean v
   ){
-    sqlite3_result_int(cx, v);
+    sqlite3_result_int(cx, v ? 1 : 0);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, int v
+    @NotNull sqlite3_context cx, boolean v
   ){
-    sqlite3_result_int(cx, v);
+    sqlite3_result_int(cx, v ? 1 : 0);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, @NotNull Boolean v
+    @NotNull sqlite3_context cx, @NotNull Double v
   ){
-    sqlite3_result_int(cx, v ? 1 : 0);
+    sqlite3_result_double(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, boolean v
+    @NotNull sqlite3_context cx, double v
   ){
-    sqlite3_result_int(cx, v ? 1 : 0);
+    sqlite3_result_double(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, @NotNull Long v
+    @NotNull sqlite3_context cx, @NotNull Integer v
   ){
-    sqlite3_result_int64(cx, v);
+    sqlite3_result_int(cx, v);
+  }
+
+  public static void sqlite3_result_set(@NotNull sqlite3_context cx, int v){
+    sqlite3_result_int(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, long v
+    @NotNull sqlite3_context cx, @NotNull Long v
   ){
     sqlite3_result_int64(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, @NotNull Double v
+    @NotNull sqlite3_context cx, long v
   ){
-    sqlite3_result_double(cx, v);
+    sqlite3_result_int64(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, double v
+    @NotNull sqlite3_context cx, @Nullable String v
   ){
-    sqlite3_result_double(cx, v);
+    if( null==v ) sqlite3_result_null(cx);
+    else sqlite3_result_text(cx, v);
   }
 
   public static void sqlite3_result_set(
-    @NotNull sqlite3_context cx, @Nullable String v
+    @NotNull sqlite3_context cx, @Nullable byte[] blob
   ){
-    sqlite3_result_text(cx, v);
+    if( null==blob ) sqlite3_result_null(cx);
+    else sqlite3_result_blob(cx, blob, blob.length);
   }
 
   @Canonical
index ed9086c4c2085aaa25308da5c20a7b8c10b05e3a..2ca997955acd65d0f62fed09bf059b07c55fc01f 100644 (file)
@@ -5,6 +5,11 @@
 
    <p>The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}.
 
+   <h1>State of this API</h1>
+
+   <p>As of version 3.43, this software is in "tech preview" form. We
+   tentatively plan to stamp it as stable with the 3.44 release.
+
    <h1>Threading Considerations</h1>
 
    <p>This API is, if built with SQLITE_THREADSAFE set to 1 or 2,
@@ -39,5 +44,6 @@
    <p>When built with SQLITE_THREADSAFE=0 then no threading guarantees
    are provided and multi-threaded use of the library will provoke
    undefined behavior.
+
 */
 package org.sqlite.jni;
index 3b290f812b8b6b694887fb6d2d6280b8be19dfed..48f883fc4840a0874d3367fa3dd83a22361ba6b7 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Lots\sof\sjavadoc-related\stweaks.
-D 2023-08-28T07:28:36.868
+C Further\sJNI\scleanups\sand\sjavadoc\sadditions.
+D 2023-08-28T11:10:13.491
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -233,10 +233,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 8bb7f82029eb7d6182f4af9c42f99abaf4cf476984f5aebd6dcf05d85da340c5
+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 064d3a14650221582a1d1621bd1109efb2a455f556387613cd583731eac28674
+F ext/jni/src/c/sqlite3-jni.c c8f329d225c87c9af2c74508e6be48424a380502da7bca82fc7486dd91a7af9c
 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
@@ -261,7 +261,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c
 F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java 16042be9d072a26dbb2f1b1b63e7639989b747bb80d2bd667ba4f7555f56a825
 F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c
 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 54b39846ee0540f8d8fc700739cd6701ba1231e12c3964c1d367b51e9315ed3b
+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 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b
 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
@@ -277,7 +277,7 @@ F ext/jni/src/org/sqlite/jni/annotation/package-info.java f66bfb621c6494e67c03ed
 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_tokenizer.java a92c2e55bda492e4c76d48ddc73369bcc0d5e8727940840f9339e3292ea58fa7
-F ext/jni/src/org/sqlite/jni/package-info.java c8f1c858ebcadd16ff047a74cf7a0556d4235386c47bc0a4d78c4a564bba03fe
+F ext/jni/src/org/sqlite/jni/package-info.java 73f7821c240e4d116f164e87b613c5836b8a33ce2666967a29d9acb1ced7ca92
 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad
 F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
@@ -2106,8 +2106,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 8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf
-R 9e09f148c76497a018ec611f2deafbbd
+P cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd
+R d8657bd6950439669ea385ca4259469a
 U stephan
-Z cceffc0e8c2aa755651a9db7f05913c5
+Z 33c0cfaa6b6aa55ee1261a4aa78cec86
 # Remove this line to create a well-formed Fossil manifest.
index ce51adc40634d279c8f9d54820b95f8d06973280..efd91396c69c8ef1508c4424476cda4dd2247bdb 100644 (file)
@@ -1 +1 @@
-cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd
\ No newline at end of file
+c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10
\ No newline at end of file