]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
JNI-internal cleanups and API renaming. Add a C-side java-string-to-utf8 conversion.
authorstephan <stephan@noemail.net>
Sun, 6 Aug 2023 13:02:43 +0000 (13:02 +0000)
committerstephan <stephan@noemail.net>
Sun, 6 Aug 2023 13:02:43 +0000 (13:02 +0000)
FossilOrigin-Name: 672d85795d04131135b1dc6a02d64eceb8b5084217c17766afeca4af23c07ec4

ext/jni/GNUmakefile
ext/jni/src/c/sqlite3-jni.c
ext/jni/src/c/sqlite3-jni.h
ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
ext/jni/src/org/sqlite/jni/Tester1.java
ext/jni/src/org/sqlite/jni/fts5_api.java
manifest
manifest.uuid

index d3b376ba0e2d4349b5cd7a2e01774f57dd6df44a..98ea80bb8da6006065bb66d2df9391242d27d299 100644 (file)
@@ -128,7 +128,6 @@ $(sqlite3.c): $(sqlite3.h)
 SQLITE_OPT := \
   -DSQLITE_ENABLE_RTREE \
   -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-  -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
   -DSQLITE_ENABLE_STMTVTAB \
   -DSQLITE_ENABLE_DBPAGE_VTAB \
   -DSQLITE_ENABLE_DBSTAT_VTAB \
@@ -137,7 +136,6 @@ SQLITE_OPT := \
   -DSQLITE_OMIT_LOAD_EXTENSION \
   -DSQLITE_OMIT_DEPRECATED \
   -DSQLITE_OMIT_SHARED_CACHE \
-  -DSQLITE_OMIT_WAL \
   -DSQLITE_THREADSAFE=0 \
   -DSQLITE_TEMP_STORE=2 \
   -DSQLITE_USE_URI=1 \
@@ -147,7 +145,7 @@ SQLITE_OPT := \
 # for a var which gets set in all builds but only read
 # via assert().
 
-SQLITE_OPFS += -g -DDEBUG -UNDEBUG
+SQLITE_OPT += -g -DDEBUG -UNDEBUG
 
 ifeq (1,$(enable.fts5))
   SQLITE_OPT += -DSQLITE_ENABLE_FTS5
index 0e50e20f227ca0d2d79f3bb076c398e350ffdfcd..c2c99d88904772e522ca4f558780468bd1008fa4 100644 (file)
 #if !defined(SQLITE_DEFAULT_PAGE_SIZE)
 # define SQLITE_DEFAULT_PAGE_SIZE 8192
 #endif
-#ifndef SQLITE_DEFAULT_UNIX_VFS
-# define SQLITE_DEFAULT_UNIX_VFS "unix"
+#ifndef SQLITE_DQS
+#  define SQLITE_DQS 0
 #endif
-#undef SQLITE_DQS
-#define SQLITE_DQS 0
 
 /**********************************************************************/
 /* SQLITE_ENABLE_... */
 #ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
 #  define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
 #endif
-#ifdef SQLITE_ENABLE_FTS5
-#  ifndef SQLITE_ENABLE_FTS4
-#    define SQLITE_ENABLE_FTS4 1
-#  endif
-#endif
 #ifndef SQLITE_ENABLE_MATH_FUNCTIONS
 #  define SQLITE_ENABLE_MATH_FUNCTIONS 1
 #endif
 #ifndef SQLITE_ENABLE_RTREE
 #  define SQLITE_ENABLE_RTREE 1
 #endif
-#ifndef SQLITE_ENABLE_SESSION
-#  define SQLITE_ENABLE_SESSION 1
-#endif
+//#ifndef SQLITE_ENABLE_SESSION
+//#  define SQLITE_ENABLE_SESSION 1
+//#endif
 #ifndef SQLITE_ENABLE_STMTVTAB
 #  define SQLITE_ENABLE_STMTVTAB 1
 #endif
-#ifndef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
-#  define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
-#endif
+//#ifndef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+//#  define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+//#endif
 
 /**********************************************************************/
 /* SQLITE_M... */
   JFuncName(Suffix)
 /* First 2 parameters to all JNI bindings. */
 #define JENV_JSELF JNIEnv * const env, jobject jSelf
-/* Helpers to squelch -Xcheck:jni warnings about
-   not having checked for exceptions. */
+/* Helpers to account for -Xcheck:jni warnings about not having
+   checked for exceptions. */
 #define IFTHREW if((*env)->ExceptionCheck(env))
 #define EXCEPTION_IGNORE (void)((*env)->ExceptionCheck(env))
 #define EXCEPTION_CLEAR (*env)->ExceptionClear(env)
@@ -337,41 +330,41 @@ struct NphCacheLine {
 
    Whereas we cache new refs for each thread.
 */
-typedef struct JNIEnvCacheLine JNIEnvCacheLine;
-struct JNIEnvCacheLine {
+typedef struct JNIEnvCache JNIEnvCache;
+struct JNIEnvCache {
   JNIEnv *env            /* env in which this cache entry was created */;
   //! The various refs to global classes might be cacheable a single
   // time globally. Information online seems inconsistent on that
   // point.
   struct {
-    jclass cObj            /* global ref to java.lang.Object */;
-    jclass cLong           /* global ref to java.lang.Long */;
-    jclass cString         /* global ref to java.lang.String */;
-    jobject oCharsetUtf8   /* global ref to StandardCharset.UTF_8 */;
-    jmethodID ctorLong1    /* the Long(long) constructor */;
-    jmethodID ctorStringBA /* the String(byte[],Charset) constructor */;
+    jclass cObj              /* global ref to java.lang.Object */;
+    jclass cLong             /* global ref to java.lang.Long */;
+    jclass cString           /* global ref to java.lang.String */;
+    jobject oCharsetUtf8     /* global ref to StandardCharset.UTF_8 */;
+    jmethodID ctorLong1      /* the Long(long) constructor */;
+    jmethodID ctorStringBA   /* the String(byte[],Charset) constructor */;
+    jmethodID stringGetBytes /* the String.getBytes(Charset) method */;
   } g;
-  jobject currentStmt    /* Current Java sqlite3_stmt object being
-                            prepared, stepped, reset, or
-                            finalized. Needed for tracing, the
-                            alternative being that we create a new
-                            sqlite3_stmt wrapper object for every
-                            tracing call which needs a stmt
-                            object. This approach is rather invasive,
-                            however, requiring code in all stmt
-                            operations which can lead through the
-                            tracing API. */;
+  jobject currentStmt /* Current Java sqlite3_stmt object being
+                         prepared, stepped, reset, or
+                         finalized. Needed for tracing, the
+                         alternative being that we create a new
+                         sqlite3_stmt wrapper object for every tracing
+                         call which needs a stmt object. This approach
+                         is rather invasive, however, requiring code
+                         in all stmt operations which can lead through
+                         the tracing API. */;
 #ifdef SQLITE_ENABLE_FTS5
-  jobject jFtsExt       /* Global ref to Java singleton for the
-                           Fts5ExtensionApi instance. */;
+  jobject jFtsExt     /* Global ref to Java singleton for the
+                         Fts5ExtensionApi instance. */;
   struct {
     jclass klazz;
     jfieldID fidA;
     jfieldID fidB;
   } jPhraseIter;
 #endif
-  JNIEnvCacheLine * pPrev /* Previous entry in the linked list */;
-  JNIEnvCacheLine * pNext /* Next entry in the linked list */;
+  JNIEnvCache * pPrev /* Previous entry in the linked list */;
+  JNIEnvCache * pNext /* Next entry in the linked list */;
   /** TODO: NphCacheLine *pNphHit;
 
       to help fast-track cache lookups, update this to point to the
@@ -445,8 +438,8 @@ static struct {
   */
   JavaVM * jvm;
   struct {
-    JNIEnvCacheLine * aHead /* Linked list of in-use instances */;
-    JNIEnvCacheLine * aFree /* Linked list of free instances */;
+    JNIEnvCache * aHead /* Linked list of in-use instances */;
+    JNIEnvCache * aFree /* Linked list of free instances */;
   } envCache;
   struct {
     PerDbStateJni * aUsed  /* Linked list of in-use instances */;
@@ -550,14 +543,14 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){
    standard UTF-8 to a Java string, but JNI offers only algorithms for
    working with MUTF-8, not UTF-8.
 */
-static jstring s3jni_string_from_utf8(JNIEnvCacheLine * const jc,
-                                      const char * const z, int n){
+static jstring s3jni_utf8_to_jstring(JNIEnvCache * const jc,
+                                     const char * const z, int n){
   jstring rv = NULL;
   JNIEnv * const env = jc->env;
-  if( 0==n || (z && !z[0]) ){
-    /* Fast-track the empty-string case. We could hypothetically do
-       this for any strings where n<4 and z is NUL-terminated and none
-       of z[0..3] are NUL bytes. */
+  if( 0==n || (n<0 && z && !z[0]) ){
+    /* Fast-track the empty-string case via the MUTF-8 API. We could
+       hypothetically do this for any strings where n<4 and z is
+       NUL-terminated and none of z[0..3] are NUL bytes. */
     rv = (*env)->NewStringUTF(env, "");
   }else if( z ){
     jbyteArray jba;
@@ -573,6 +566,45 @@ static jstring s3jni_string_from_utf8(JNIEnvCacheLine * const jc,
   return rv;
 }
 
+/**
+   Converts the given java.lang.String object into a NUL-terminated
+   UTF-8 C-string by calling jstr.getBytes(StandardCharset.UTF_8).
+   Returns NULL if jstr is NULL or on allocation error. If jstr is not
+   NULL and nLen is not NULL then nLen is set to the length of the
+   returned string, not including the terminating NUL. If jstr is not
+   NULL and it returns NULL, this indicates an allocation error. In
+   that case, if nLen is not NULL then it is either set to 0 (if
+   fetching of jstr's bytes fails to allocate) or set to what would
+   have been the length of the string had C-string allocation
+   succeeded.
+*/
+static char * s3jni_jstring_to_utf8(JNIEnvCache * const jc,
+                                    jstring jstr, int *nLen){
+  JNIEnv * const env = jc->env;
+  jbyteArray jba;
+  jsize nBa;
+  char *rv;
+
+  if(!jstr) return 0;
+  jba = (*env)->CallObjectMethod(env, jstr, jc->g.stringGetBytes,
+                                 jc->g.oCharsetUtf8);
+  if( (*env)->ExceptionCheck(env) || !jba
+      /* order of these checks is significant for -Xlint:jni */ ) {
+    EXCEPTION_REPORT;
+    if( nLen ) *nLen = 0;
+    return 0;
+  }
+  nBa = (*env)->GetArrayLength(env, jba);
+  if( nLen ) *nLen = (int)nBa;
+  rv = sqlite3_malloc( nBa + 1 );
+  if( rv ){
+    (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv);
+    rv[nBa] = 0;
+  }
+  UNREF_L(jba);
+  return rv;
+}
+
 /**
    Fetches the S3Global.envCache row for the given env, allocing
    a row if needed. When a row is allocated, its state is initialized
@@ -580,8 +612,8 @@ static jstring s3jni_string_from_utf8(JNIEnvCacheLine * const jc,
    an entry fails. That's hypothetically possible but "shouldn't happen."
 */
 FIXME_THREADING
-static JNIEnvCacheLine * S3Global_JNIEnvCache_cache(JNIEnv * const env){
-  struct JNIEnvCacheLine * row = S3Global.envCache.aHead;
+static JNIEnvCache * S3Global_env_cache(JNIEnv * const env){
+  struct JNIEnvCache * row = S3Global.envCache.aHead;
   for( ; row; row = row->pNext ){
     if( row->env == env ){
       ++S3Global.metrics.envCacheHits;
@@ -595,7 +627,7 @@ static JNIEnvCacheLine * S3Global_JNIEnvCache_cache(JNIEnv * const env){
     S3Global.envCache.aFree = row->pNext;
     if( row->pNext ) row->pNext->pPrev = 0;
   }else{
-    row = sqlite3_malloc(sizeof(JNIEnvCacheLine));
+    row = sqlite3_malloc(sizeof(JNIEnvCache));
     if( !row ){
       (*env)->FatalError(env, "Maintenance required: JNIEnvCache is full.")
         /* Does not return, but cc doesn't know that */;
@@ -624,6 +656,10 @@ static JNIEnvCacheLine * S3Global_JNIEnvCache_cache(JNIEnv * const env){
     (*env)->GetMethodID(env, row->g.cString,
                         "<init>", "([BLjava/nio/charset/Charset;)V");
   EXCEPTION_IS_FATAL("Error getting reference to String(byte[],Charset) ctor.");
+  row->g.stringGetBytes =
+    (*env)->GetMethodID(env, row->g.cString,
+                        "getBytes", "(Ljava/nio/charset/Charset;)[B");
+  EXCEPTION_IS_FATAL("Error getting reference to String.getBytes(Charset).");
 
   { /* StandardCharsets.UTF_8 */
     jfieldID fUtf8;
@@ -702,7 +738,7 @@ static void PerDbStateJni_set_aside(PerDbStateJni * const s){
    Requires that p has been snipped from any linked list it is
    in. Clears all Java refs p holds and zeroes out p.
 */
-static void JNIEnvCacheLine_clear(JNIEnvCacheLine * const p){
+static void JNIEnvCache_clear(JNIEnvCache * const p){
   JNIEnv * const env = p->env;
   if(env){
     int i;
@@ -718,7 +754,7 @@ static void JNIEnvCacheLine_clear(JNIEnvCacheLine * const p){
     for( i = 0; i < NphCache_SIZE; ++i ){
       NphCacheLine_clear(env, &p->nph[i]);
     }
-    memset(p, 0, sizeof(JNIEnvCacheLine));
+    memset(p, 0, sizeof(JNIEnvCache));
   }
 }
 
@@ -750,8 +786,8 @@ static void PerDbStateJni_free_for_env(JNIEnv *env){
    Also passes env to PerDbStateJni_free_for_env() to free up
    what would otherwise be stale references.
 */
-static int S3Global_JNIEnvCache_uncache(JNIEnv * const env){
-  struct JNIEnvCacheLine * row = S3Global.envCache.aHead;
+static int S3Global_env_uncache(JNIEnv * const env){
+  struct JNIEnvCache * row = S3Global.envCache.aHead;
   for( ; row; row = row->pNext ){
     if( row->env == env ){
       break;
@@ -764,7 +800,7 @@ static int S3Global_JNIEnvCache_uncache(JNIEnv * const env){
     assert( !row->pPrev );
     S3Global.envCache.aHead = row->pNext;
   }
-  JNIEnvCacheLine_clear(row);
+  JNIEnvCache_clear(row);
   assert( !row->pNext );
   assert( !row->pPrev );
   row->pNext = S3Global.envCache.aFree;
@@ -776,7 +812,7 @@ static int S3Global_JNIEnvCache_uncache(JNIEnv * const env){
 
 static void S3Global_JNIEnvCache_clear(void){
   while( S3Global.envCache.aHead ){
-    S3Global_JNIEnvCache_uncache( S3Global.envCache.aHead->env );
+    S3Global_env_uncache( S3Global.envCache.aHead->env );
   }
 }
 
@@ -813,7 +849,7 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv * const env, const char *
      looking up class objects can be expensive, so they should be
      cached as well.
   */
-  struct JNIEnvCacheLine * const envRow = S3Global_JNIEnvCache_cache(env);
+  struct JNIEnvCache * const envRow = S3Global_env_cache(env);
   struct NphCacheLine * freeSlot = 0;
   struct NphCacheLine * cacheLine = 0;
   int i;
@@ -1413,7 +1449,7 @@ static int udf_args(JNIEnv *env,
   *jArgv = 0;
   if(!jcx) goto error_oom;
   ja = (*env)->NewObjectArray(env, argc,
-                              S3Global_JNIEnvCache_cache(env)->g.cObj,
+                              S3Global_env_cache(env)->g.cObj,
                               NULL);
   if(!ja) goto error_oom;
   for(i = 0; i < argc; ++i){
@@ -2063,41 +2099,25 @@ JDECL(jint,1create_1function)(JENV_JSELF, jobject jDb, jstring jFuncName,
 }
 
 
-JDECL(jbyteArray,1db_1filename)(JENV_JSELF, jobject jDb, jbyteArray jDbName){
-#if 1
+JDECL(jstring,1db_1filename)(JENV_JSELF, jobject jDb, jstring jDbName){
   PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0);
-  jbyte *zFilename = (ps && jDbName) ? JBA_TOC(jDbName) : 0;
-  const char *zRv;
-  jbyteArray jRv = 0;
-
-  if( !ps || (jDbName && !zFilename) ) return 0;
-  zRv = sqlite3_db_filename(ps->pDb, (const char *)zFilename);
-  if( zRv ){
-    const int n = sqlite3Strlen30(zRv);
-    jRv = (*env)->NewByteArray(env, (jint)n);
-    if( jRv ){
-      (*env)->SetByteArrayRegion(env, jRv, 0, (jint)n, (const jbyte *)zRv);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
+  char *zDbName;
+  jstring jRv = 0;
+  int nStr = 0;
+
+  if( !ps || !jDbName ){
+    return 0;
+  }
+  zDbName = s3jni_jstring_to_utf8(jc, jDbName, &nStr);
+  if( zDbName ){
+    char const * zRv = sqlite3_db_filename(ps->pDb, zDbName);
+    sqlite3_free(zDbName);
+    if( zRv ){
+      jRv = s3jni_utf8_to_jstring(jc, zRv, -1);
     }
   }
-  JBA_RELEASE(jDbName, zFilename);
   return jRv;
-#else
-  /* For comparison, this impl expects a jstring jDbName and returns a
-     jstring for significant code savings but it's not
-     MUTF-8-safe. With this impl, the Java-side byte-array-using
-     sqlite3_db_filename() impl is unnecessary. */
-  JDECL(jstring,1db_1filename)(JENV_JSELF, jobject jDb, jstring jDbName){
-  PerDbStateJni * const ps = PerDbStateJni_for_db(env, jDb, 0, 0);
-  const char *zFilename = (ps && jDbName) ? JSTR_TOC(jDbName) : 0;
-  const char *zRv;
-
-  if( !ps || (jDbName && !zFilename)) return 0;
-  zRv = sqlite3_db_filename(ps->pDb, zFilename ? zFilename : "main");
-  JSTR_RELEASE(jDbName, zFilename);
-  return zRv ? (*env)->NewStringUTF(env, zRv) : 0;
-}
-#endif
-
 }
 
 JDECL(jstring,1errmsg)(JENV_JSELF, jobject jpDb){
@@ -2127,7 +2147,7 @@ JDECL(jint,1initialize)(JENV_JSELF){
    is needed for certain tracing flags. At a minumum those ops are:
    step, reset, finalize, prepare.
 */
-static jobject stmt_set_current(JNIEnvCacheLine * const jc, jobject jStmt){
+static jobject stmt_set_current(JNIEnvCache * const jc, jobject jStmt){
   jobject const old = jc->currentStmt;
   jc->currentStmt = jStmt;
   return old;
@@ -2137,7 +2157,7 @@ JDECL(jint,1finalize)(JENV_JSELF, jobject jpStmt){
   int rc = 0;
   sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
   if( pStmt ){
-    JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+    JNIEnvCache * const jc = S3Global_env_cache(env);
     jobject const pPrev = stmt_set_current(jc, jpStmt);
     rc = sqlite3_finalize(pStmt);
     setNativePointer(env, jpStmt, 0, S3ClassNames.sqlite3_stmt);
@@ -2208,7 +2228,7 @@ static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass
   sqlite3_stmt * pStmt = 0;
   const char * zTail = 0;
   jbyte * const pBuf = JBA_TOC(baSql);
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   jobject const pOldStmt = stmt_set_current(jc, jOutStmt);
   int rc = SQLITE_ERROR;
   assert(prepVersion==1 || prepVersion==2 || prepVersion==3);
@@ -2303,7 +2323,7 @@ JDECL(jint,1reset)(JENV_JSELF, jobject jpStmt){
   int rc = 0;
   sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
   if( pStmt ){
-    JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+    JNIEnvCache * const jc = S3Global_env_cache(env);
     jobject const pPrev = stmt_set_current(jc, jpStmt);
     rc = sqlite3_reset(pStmt);
     (void)stmt_set_current(jc, pPrev);
@@ -2571,7 +2591,7 @@ JDECL(jint,1step)(JENV_JSELF,jobject jStmt){
   int rc = SQLITE_MISUSE;
   sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt);
   if(pStmt){
-    JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+    JNIEnvCache * const jc = S3Global_env_cache(env);
     jobject const jPrevStmt = stmt_set_current(jc, jStmt);
     rc = sqlite3_step(pStmt);
     (void)stmt_set_current(jc, jPrevStmt);
@@ -2585,11 +2605,11 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
   jobject jX = NULL  /* the tracer's X arg */;
   jobject jP = NULL  /* the tracer's P arg */;
   jobject jPUnref = NULL /* potentially a local ref to jP */;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   int rc;
   switch(traceflag){
     case SQLITE_TRACE_STMT:
-      jX = s3jni_string_from_utf8(jc, (const char *)pX, -1);
+      jX = s3jni_utf8_to_jstring(jc, (const char *)pX, -1);
       if(!jX) return SQLITE_NOMEM;
       jP = jc->currentStmt;
       break;
@@ -2841,6 +2861,7 @@ JDECL(void,1do_1something_1for_1developer)(JENV_JSELF){
 #define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T))
   SO(void*);
   SO(JniHookState);
+  SO(JNIEnvCache);
   SO(PerDbStateJni);
   SO(S3Global);
   SO(S3ClassNames);
@@ -2964,7 +2985,7 @@ 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){
-  JNIEnvCacheLine * const row = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const row = S3Global_env_cache(env);
   if( !row->jFtsExt ){
     row->jFtsExt = new_NativePointerHolder_object(env, S3ClassNames.Fts5ExtensionApi,
                                                   s3jni_ftsext());
@@ -3031,8 +3052,8 @@ JDECLFtsXA(jint,xColumnText)(JENV_JSELF,jobject jCtx, jint iCol,
   int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol,
                              &pz, &pn);
   if( 0==rc ){
-    JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
-    jstring jstr = pz ? s3jni_string_from_utf8(jc, pz, pn) : 0;
+    JNIEnvCache * const jc = S3Global_env_cache(env);
+    jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0;
     if( pz ){
       if( jstr ){
         setOutputString(env, jOut, jstr);
@@ -3188,7 +3209,7 @@ JDECLFtsXA(jint,xPhraseCount)(JENV_JSELF,jobject jCtx){
 /**
    Initializes jc->jPhraseIter if it needed it.
 */
-static void s3jni_phraseIter_init(JNIEnv *const env, JNIEnvCacheLine * const jc,
+static void s3jni_phraseIter_init(JNIEnv *const env, JNIEnvCache * const jc,
                                   jobject jIter){
   if(!jc->jPhraseIter.klazz){
     jclass klazz = (*env)->GetObjectClass(env, jIter);
@@ -3202,7 +3223,7 @@ static void s3jni_phraseIter_init(JNIEnv *const env, JNIEnvCacheLine * const jc,
 }
 
 /* Copy the 'a' and 'b' fields from pSrc to Fts5PhraseIter object jIter. */
-static void s3jni_phraseIter_NToJ(JNIEnv *const env, JNIEnvCacheLine const * const jc,
+static void s3jni_phraseIter_NToJ(JNIEnv *const env, JNIEnvCache const * const jc,
                                     Fts5PhraseIter const * const pSrc,
                                     jobject jIter){
   assert(jc->jPhraseIter.klazz);
@@ -3213,7 +3234,7 @@ static void s3jni_phraseIter_NToJ(JNIEnv *const env, JNIEnvCacheLine const * con
 }
 
 /* Copy the 'a' and 'b' fields from Fts5PhraseIter object jIter to pDest. */
-static void s3jni_phraseIter_JToN(JNIEnv *const env, JNIEnvCacheLine const * const jc,
+static void s3jni_phraseIter_JToN(JNIEnv *const env, JNIEnvCache const * const jc,
                                   jobject jIter, Fts5PhraseIter * const pDest){
   assert(jc->jPhraseIter.klazz);
   pDest->a =
@@ -3228,7 +3249,7 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_JSELF,jobject jCtx, jint iPhrase,
                             jobject jIter, jobject jOutCol,
                             jobject jOutOff){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   Fts5PhraseIter iter;
   int rc, iCol = 0, iOff = 0;
   s3jni_phraseIter_init(env, jc, jIter);
@@ -3245,7 +3266,7 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_JSELF,jobject jCtx, jint iPhrase,
 JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_JSELF,jobject jCtx, jint iPhrase,
                                   jobject jIter, jobject jOutCol){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   Fts5PhraseIter iter;
   int rc, iCol = 0;
   s3jni_phraseIter_init(env, jc, jIter);
@@ -3261,7 +3282,7 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_JSELF,jobject jCtx, jint iPhrase,
 JDECLFtsXA(void,xPhraseNext)(JENV_JSELF,jobject jCtx, jobject jIter,
                            jobject jOutCol, jobject jOutOff){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   Fts5PhraseIter iter;
   int iCol = 0, iOff = 0;
   if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/;
@@ -3276,7 +3297,7 @@ JDECLFtsXA(void,xPhraseNext)(JENV_JSELF,jobject jCtx, jobject jIter,
 JDECLFtsXA(void,xPhraseNextColumn)(JENV_JSELF,jobject jCtx, jobject jIter,
                                  jobject jOutCol){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   Fts5PhraseIter iter;
   int iCol = 0;
   if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/;
@@ -3298,7 +3319,7 @@ JDECLFtsXA(jint,xPhraseSize)(JENV_JSELF,jobject jCtx, jint iPhrase){
 struct s3jni_xQueryPhraseState {
   JNIEnv *env;
   Fts5ExtensionApi const * fext;
-  JNIEnvCacheLine const * jc;
+  JNIEnvCache const * jc;
   jmethodID midCallback;
   jobject jCallback;
   jobject jFcx;
@@ -3330,7 +3351,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
 JDECLFtsXA(jint,xQueryPhrase)(JENV_JSELF,jobject jFcx, jint iPhrase,
                             jobject jCallback){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   struct s3jni_xQueryPhraseState s;
   jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
   if( !klazz ){
@@ -3419,7 +3440,7 @@ static jint s3jni_fts5_xTokenize(JENV_JSELF, const char *zClassName,
                                  jint tokFlags, jobject jFcx,
                                  jbyteArray jbaText, jobject jCallback){
   Fts5ExtDecl;
-  JNIEnvCacheLine * const jc = S3Global_JNIEnvCache_cache(env);
+  JNIEnvCache * const jc = S3Global_env_cache(env);
   struct s3jni_xQueryPhraseState s;
   int rc = 0;
   jbyte * const pText = JBA_TOC(jbaText);
@@ -3485,8 +3506,8 @@ JDECLFtsXA(jobject,xUserData)(JENV_JSELF,jobject jFcx){
   return pAux ? pAux->jUserData : 0;
 }
 
-
 #endif /* SQLITE_ENABLE_FTS5 */
+
 ////////////////////////////////////////////////////////////////////////
 // End of the main API bindings. What follows are internal utilities.
 ////////////////////////////////////////////////////////////////////////
@@ -3499,7 +3520,7 @@ JDECLFtsXA(jobject,xUserData)(JENV_JSELF,jobject jFcx){
 */
 JNIEXPORT jboolean JNICALL
 Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JNIEnv * const env, jclass self){
-  return S3Global_JNIEnvCache_uncache(env) ? JNI_TRUE : JNI_FALSE;
+  return S3Global_env_uncache(env) ? JNI_TRUE : JNI_FALSE;
 }
 
 
@@ -3564,7 +3585,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JNIEnv * const env, jclass self, jclass klaz
     (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible.");
     return;
   }
-  (void)S3Global_JNIEnvCache_cache(env);
+  (void)S3Global_env_cache(env);
   if( !S3Global.envCache.aHead ){
     (*env)->FatalError(env, "Could not allocate JNIEnv-specific cache.");
     return;
index ea783afe50db4168d81017dce0cd2964d301faf6..0e5e0813f7badca91cda23daaa1be2962515c1b1 100644 (file)
@@ -1094,10 +1094,10 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1data_1count
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_db_filename
- * Signature: (Lorg/sqlite/jni/sqlite3;[B)[B
+ * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;)Ljava/lang/String;
  */
-JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
-  (JNIEnv *, jclass, jobject, jbyteArray);
+JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
+  (JNIEnv *, jclass, jobject, jstring);
 
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
index 5f2e560daf4a8de49a9607c64e691952a5c9e358..327e5a5d2812e7c3a9f4f446fe0beeb42643a597 100644 (file)
@@ -34,7 +34,7 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
   /**
      Returns a singleton instance of this class.
   */
-  public static native Fts5ExtensionApi getInstance();
+  public static synchronized native Fts5ExtensionApi getInstance();
 
   public native int xColumnCount(@NotNull Fts5Context fcx);
   public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
index 369b87a9d4da4d2eb4a5273bc55878491a601d66..06d168464d07d44c80deff3d6dcc134309d605f9 100644 (file)
@@ -111,6 +111,7 @@ import java.lang.annotation.ElementType;
 
   - https://stackoverflow.com/questions/57419723
   - https://stackoverflow.com/questions/7921016
+  - https://itecnote.com/tecnote/java-getting-true-utf-8-characters-in-java-jni/
   - https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html#unicode
   - https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8
 
@@ -397,22 +398,8 @@ public final class SQLite3Jni {
 
   public static native int sqlite3_data_count(@NotNull sqlite3_stmt stmt);
 
-  /** In order to support the full range of UTF-8 filenames, we
-      require an extra layer of conversion via a byte[]. */
-  private static native byte[] sqlite3_db_filename(@NotNull sqlite3 db,
-                                                   @NotNull byte dbName[]);
-
-  /**
-     As for the C API of the same name except that if dbName is null then
-     "main" is assumed.
-  */
-  public static String sqlite3_db_filename(@NotNull sqlite3 db,
-                                           @Nullable String dbName){
-    final byte[] bName =
-      (((null == dbName) ? "main" : dbName)+"\0").getBytes(StandardCharsets.UTF_8);
-    final byte[] rv = sqlite3_db_filename(db, bName);
-    return (null == rv) ? null : new String(rv, StandardCharsets.UTF_8);
-  }
+  public static native String sqlite3_db_filename(@NotNull sqlite3 db,
+                                                  @NotNull String dbName);
 
   public static native int sqlite3_errcode(@NotNull sqlite3 db);
 
index 2d1dcd4be8dd9cbc01810a9dcd73771801f54dd9..38446e9c70cf41986c1fefd913c9006c382bdf21 100644 (file)
@@ -740,7 +740,7 @@ public class Tester1 {
     rc = sqlite3_open(dbName, db2);
     ++metrics.dbOpen;
     affirm( 0 == rc );
-    affirm( sqlite3_db_filename(db1, null).endsWith(dbName) );
+    affirm( sqlite3_db_filename(db1, "main").endsWith(dbName) );
 
     final ValueHolder<Boolean> xDestroyed = new ValueHolder<>(false);
     final ValueHolder<Integer> xBusyCalled = new ValueHolder<>(0);
index 53afeb93c6cdf193a515b4e81b3143795d28c996..4df8a748e2fb947e594da199cc76a93cb1ed0197 100644 (file)
@@ -30,7 +30,7 @@ public final class fts5_api extends NativePointerHolder<fts5_api> {
      Returns the fts5_api instance associated with the given db, or
      null if something goes horribly wrong.
   */
-  public static native fts5_api getInstanceForDb(@NotNull sqlite3 db);
+  public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
 
   // int (*xCreateTokenizer)(
   //   fts5_api *pApi,
index 749ff0d9023c4c7e4f398828200c57173c03d1af..4be312149d884277e1e85a1460ed546481645ae8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\sdoc\soutdated\swarning\sabout\ssqlite3_trace_v2()\sJNI\sbinding\sbeing\sincompatible\swith\sMUTF-8.\sUse\snew\sto-string\scapability\sto\ssimplify\sFts5ExtensionApi::xColumnText()\sJNI\sbinding.
-D 2023-08-06T11:05:17.309
+C JNI-internal\scleanups\sand\sAPI\srenaming.\sAdd\sa\sC-side\sjava-string-to-utf8\sconversion.
+D 2023-08-06T13:02:43.735
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -230,10 +230,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 bb4cd99bd8da534215cb6d278f05a626283eb5d2e8aebdb4d35e548637d35a9a
+F ext/jni/GNUmakefile 9f0b0172903dfdb46ed3a77d431153238b7b96ec66cf40333c07e6dd23a3fabe
 F ext/jni/README.md 6ff7e1f4100dee980434a6ee37a199b653bceec62e233a6e2ccde6e7ae0c58bf
-F ext/jni/src/c/sqlite3-jni.c 433ac7a2f113cda29ecf89b72711b55eeb97be5305fadd420408d1e538699177
-F ext/jni/src/c/sqlite3-jni.h 1bb138aa39a5ae6cc0b2ab6c72de9afe752123b02f3322a8d5b1ca36b9e5a410
+F ext/jni/src/c/sqlite3-jni.c 38c251d74f78b54b30e84ed97230eb2fa008e7400e9a460066ef6f1c43c06a2b
+F ext/jni/src/c/sqlite3-jni.h 8ddf8a2e044d7880c75c07c9f025f3cdc5d486a42d30d99e0c45d7a8a973a97d
 F ext/jni/src/org/sqlite/jni/Authorizer.java 8dde03bbe50896d2f426240a4af4dcb6d98b655af84fe6ed86e637f5d5ac1fc8
 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
 F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
@@ -241,7 +241,7 @@ F ext/jni/src/org/sqlite/jni/CollationNeeded.java ebc7cd96d46a70daa76016a308e80f
 F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a
 F ext/jni/src/org/sqlite/jni/Fts5.java 13844685231e8b4840a706db3bed84d5dfcf15be0ae7e809eac40420dba24901
 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890
-F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java aaab15f6dd515fa45b99ddb1f2f889657a2939f64f7934114f013c98442032da
+F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 5b92cc034ca403936f11e07838699e6cf28afc5dd84020dfe9a6b64739b65065
 F ext/jni/src/org/sqlite/jni/Fts5Function.java 65cde7151e441fee012250a5e03277de7babcd11a0c308a832b7940574259bcc
 F ext/jni/src/org/sqlite/jni/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab03a7aede43277b2c92859176d6bce3be9
 F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060
@@ -250,13 +250,13 @@ F ext/jni/src/org/sqlite/jni/OutputPointer.java fcece068415b804aa7843534addb3905
 F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc
 F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
 F ext/jni/src/org/sqlite/jni/SQLFunction.java 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 3494bd0eda06b56e137ff71fcdada9c463d40a0bbb02fc0ce96bf3761e6110b7
-F ext/jni/src/org/sqlite/jni/Tester1.java ecc72fcba231f5dfd787fd5d62fac685e8cfc349f74d11245d19325643517bfd
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java eb1a0b741654a1c813de4e3f11f354aae87b50f8656e440d95c0fd0509fad2f5
+F ext/jni/src/org/sqlite/jni/Tester1.java a57a56717104d6ff50932c9e81edf4a8773ce524d497988c8b044c18bbc963ee
 F ext/jni/src/org/sqlite/jni/TesterFts5.java cf2d687baafffdeba219b77cf611fd47a0556248820ea794ae3e8259bfbdc5ee
 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
 F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
-F ext/jni/src/org/sqlite/jni/fts5_api.java ae52ff7f963976fabb7e87b0b8cdb3f9d2ba1838e7d3b79b0b4cb526202d4709
+F ext/jni/src/org/sqlite/jni/fts5_api.java 8c6b32455d7f85ee3f7f3e71c148bb3c2106f1d5484017daddfd560dd69d4f66
 F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c
 F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b
 F ext/jni/src/org/sqlite/jni/sqlite3.java 600c3ddc1ac28ee8f58669fb435fd0d21f2972c652039361fde907d4fe44eb58
@@ -2082,8 +2082,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 07dd082c9e371829a18aeb574f842891e545e1fc125760238ede7e7e2b6a4262
-R 774991bd316a19b5ee8ab2518b7f41ee
+P ebcfc2379be12f76a96f3605b734f406b3354d4c985062cdbfca0cf7e3f31379
+R a7badfa2d93b121b26fb8275a69f64a6
 U stephan
-Z 78a283a26c39161657872bbb379dbda5
+Z afedd572770e60de292afd4c7121fd3f
 # Remove this line to create a well-formed Fossil manifest.
index 972cac53d6e6da0c87665f792dd59b0b6893087d..7a14addf8c69bd52a7540ec55a2b61cd80ad9803 100644 (file)
@@ -1 +1 @@
-ebcfc2379be12f76a96f3605b734f406b3354d4c985062cdbfca0cf7e3f31379
\ No newline at end of file
+672d85795d04131135b1dc6a02d64eceb8b5084217c17766afeca4af23c07ec4
\ No newline at end of file