1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

JNI-internal cleanups and API renaming. Add a C-side java-string-to-utf8 conversion.

FossilOrigin-Name: 672d85795d04131135b1dc6a02d64eceb8b5084217c17766afeca4af23c07ec4
This commit is contained in:
stephan
2023-08-06 13:02:43 +00:00
parent 6a9364a518
commit 9c4c16c80d
9 changed files with 155 additions and 149 deletions

View File

@ -37,11 +37,9 @@
#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_... */
@ -57,11 +55,6 @@
#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
@ -74,15 +67,15 @@
#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... */
@ -160,8 +153,8 @@
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;
JNIEnvCache * const jc = S3Global_env_cache(env);
char *zDbName;
jstring jRv = 0;
int nStr = 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);
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;

View 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

View 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,

View 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);

View 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);

View 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,