1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-08 19:42:06 +03:00

Improve threading support for all types of JNI-side callback hooks, making them safe to invoke if another thread is busy replacing them.

FossilOrigin-Name: f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114
This commit is contained in:
stephan
2023-08-26 10:20:38 +00:00
parent 25033b6b3b
commit 6f8f587d2f
4 changed files with 353 additions and 255 deletions

View File

@ -337,6 +337,9 @@ struct S3JniHook{
/* We lookup the jObj.xDestroy() method as-needed for contexts which /* We lookup the jObj.xDestroy() method as-needed for contexts which
** have custom finalizers. */ ** have custom finalizers. */
}; };
#ifndef SQLITE_ENABLE_PREUPDATE_HOOK
static const S3JniHook S3JniHook_empty = {0,0};
#endif
/* /*
** Per-(sqlite3*) state for various JNI bindings. This state is ** Per-(sqlite3*) state for various JNI bindings. This state is
@ -513,6 +516,8 @@ struct S3JniGlobalType {
int nExt /* number of active entries in pExt, all in the int nExt /* number of active entries in pExt, all in the
first nExt'th array elements. */; first nExt'th array elements. */;
sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */;
const void * locker /* object on whose behalf the mutex is held.
Only for sanity checking in debug builds. */;
} autoExt; } autoExt;
#ifdef SQLITE_ENABLE_FTS5 #ifdef SQLITE_ENABLE_FTS5
struct { struct {
@ -599,10 +604,14 @@ static void s3jni_incr( volatile unsigned int * const p ){
#define S3JniMutex_Ext_enter \ #define S3JniMutex_Ext_enter \
/*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \
sqlite3_mutex_enter( SJG.autoExt.mutex ); \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \
SJG.autoExt.locker = env; \
s3jni_incr( &SJG.metrics.nMutexAutoExt ) s3jni_incr( &SJG.metrics.nMutexAutoExt )
#define S3JniMutex_Ext_leave \ #define S3JniMutex_Ext_leave \
/*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \
assert( env == SJG.autoExt.locker ); \
sqlite3_mutex_leave( SJG.autoExt.mutex ) sqlite3_mutex_leave( SJG.autoExt.mutex )
#define S3JniMutex_Ext_assertLocker \
assert( env == SJG.autoExt.locker )
#define S3JniMutex_Nph_enter \ #define S3JniMutex_Nph_enter \
S3JniMutex_Env_assertNotLocker; \ S3JniMutex_Env_assertNotLocker; \
/*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \
@ -630,6 +639,7 @@ static void s3jni_incr( volatile unsigned int * const p ){
#define S3JniMutex_Env_assertNotLocker #define S3JniMutex_Env_assertNotLocker
#define S3JniMutex_Env_enter #define S3JniMutex_Env_enter
#define S3JniMutex_Env_leave #define S3JniMutex_Env_leave
#define S3JniMutex_Ext_assertLocker
#define S3JniMutex_Ext_enter #define S3JniMutex_Ext_enter
#define S3JniMutex_Ext_leave #define S3JniMutex_Ext_leave
#define S3JniMutex_Nph_enter #define S3JniMutex_Nph_enter
@ -669,7 +679,7 @@ static JNIEnv * s3jni_env(void){
return env; return env;
} }
/* Declares local var env = s3jni_env(). */ /* Declares local var env = s3jni_env(). */
#define LocalJniGetEnv JNIEnv * const env = s3jni_env() #define S3JniDeclLocal_env JNIEnv * const env = s3jni_env()
/* /*
** Fetches the S3JniGlobal.envCache row for the given env, allocing a ** Fetches the S3JniGlobal.envCache row for the given env, allocing a
@ -943,6 +953,30 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest
memset(s, 0, sizeof(*s)); memset(s, 0, sizeof(*s));
} }
/*
** Internal helper for many hook callback impls. Locks the S3JniDb
** mutex, makes a copy of src into dest, with one change if src->jObj
** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj
** instead of a copy of the prior GLOBAL ref. Then unlocks the
** mutex. If dest->jObj is not NULL when this returns then the caller
** is obligated to eventually free the new ref by passing dest->jObj
** to S3JniUnrefLocal(). The dest pointer must NOT be passed to
** S3JniHook_unref(), as that one assumes that dest->jObj is a GLOBAL
** ref.
**
** Background: when running a hook we need a call-local copy lest
** another thread modify the hook while we're running it. That copy
** has to haves its own Java reference, but it need only be
** call-local.
*/
static void S3JniHook_copy( JNIEnv * const env, S3JniHook const * const src,
S3JniHook * const dest ){
S3JniMutex_S3JniDb_enter;
*dest = *src;
if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj);
S3JniMutex_S3JniDb_leave;
}
/* /*
** Clears s's state and moves it to the free-list. ** Clears s's state and moves it to the free-list.
*/ */
@ -962,18 +996,18 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){
} }
sqlite3_free( s->zMainDbName ); sqlite3_free( s->zMainDbName );
#define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) #define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY)
UNHOOK(trace, 0);
UNHOOK(progress, 0);
UNHOOK(commit, 0);
UNHOOK(rollback, 0);
UNHOOK(update, 0);
UNHOOK(auth, 0); UNHOOK(auth, 0);
UNHOOK(busyHandler, 1);
UNHOOK(collation, 1);
UNHOOK(collationNeeded, 0);
UNHOOK(commit, 0);
UNHOOK(progress, 0);
UNHOOK(rollback, 0);
UNHOOK(trace, 0);
UNHOOK(update, 0);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
UNHOOK(preUpdate, 0); UNHOOK(preUpdate, 0);
#endif #endif
UNHOOK(collation, 1);
UNHOOK(collationNeeded, 0);
UNHOOK(busyHandler, 1);
#undef UNHOOK #undef UNHOOK
S3JniUnrefGlobal(s->jDb); S3JniUnrefGlobal(s->jDb);
memset(s, 0, sizeof(S3JniDb)); memset(s, 0, sizeof(S3JniDb));
@ -1205,6 +1239,8 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
jobject const jAutoExt){ jobject const jAutoExt){
jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); jclass const klazz = (*env)->GetObjectClass(env, jAutoExt);
S3JniMutex_Ext_assertLocker;
assert( env == SJG.autoExt.locker );
ax->midFunc = (*env)->GetMethodID(env, klazz, "call", ax->midFunc = (*env)->GetMethodID(env, klazz, "call",
"(Lorg/sqlite/jni/sqlite3;)I"); "(Lorg/sqlite/jni/sqlite3;)I");
S3JniUnrefLocal(klazz); S3JniUnrefLocal(klazz);
@ -1232,8 +1268,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
** misbehave. ** misbehave.
*/ */
static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef,
const char * const zTypeSig, const char * const zTypeSig){
jobject const jOut){
S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef);
if( !pNC->fidValue ){ if( !pNC->fidValue ){
S3JniMutex_Nph_enter; S3JniMutex_Nph_enter;
@ -1252,7 +1287,7 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef,
*/ */
static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_Int32, "I", jOut env, &S3NphRefs.OutputPointer_Int32, "I"
); );
(*env)->SetIntField(env, jOut, setter, (jint)v); (*env)->SetIntField(env, jOut, setter, (jint)v);
S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value");
@ -1264,7 +1299,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int
*/ */
static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_Int64, "J", jOut env, &S3NphRefs.OutputPointer_Int64, "J"
); );
(*env)->SetLongField(env, jOut, setter, v); (*env)->SetLongField(env, jOut, setter, v);
S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value");
@ -1277,7 +1312,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon
static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut,
jobject jDb){ jobject jDb){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;"
); );
(*env)->SetObjectField(env, jOut, setter, jDb); (*env)->SetObjectField(env, jOut, setter, jDb);
S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3.value");
@ -1291,7 +1326,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu
jobject jStmt){ jobject jStmt){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_sqlite3_stmt, env, &S3NphRefs.OutputPointer_sqlite3_stmt,
"Lorg/sqlite/jni/sqlite3_stmt;", jOut "Lorg/sqlite/jni/sqlite3_stmt;"
); );
(*env)->SetObjectField(env, jOut, setter, jStmt); (*env)->SetObjectField(env, jOut, setter, jStmt);
S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_stmt.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_stmt.value");
@ -1306,7 +1341,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO
jobject jValue){ jobject jValue){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_sqlite3_value, env, &S3NphRefs.OutputPointer_sqlite3_value,
"Lorg/sqlite/jni/sqlite3_value;", jOut "Lorg/sqlite/jni/sqlite3_value;"
); );
(*env)->SetObjectField(env, jOut, setter, jValue); (*env)->SetObjectField(env, jOut, setter, jValue);
S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_value.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_value.value");
@ -1322,7 +1357,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO
static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
jbyteArray const v){ jbyteArray const v){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_ByteArray, "[B", jOut env, &S3NphRefs.OutputPointer_ByteArray, "[B"
); );
(*env)->SetObjectField(env, jOut, setter, v); (*env)->SetObjectField(env, jOut, setter, v);
S3JniExceptionIsFatal("Cannot set OutputPointer.ByteArray.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.ByteArray.value");
@ -1336,7 +1371,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut,
jstring const v){ jstring const v){
jfieldID const setter = setupOutputPointer( jfieldID const setter = setupOutputPointer(
env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;", jOut env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;"
); );
(*env)->SetObjectField(env, jOut, setter, v); (*env)->SetObjectField(env, jOut, setter, v);
S3JniExceptionIsFatal("Cannot set OutputPointer.String.value"); S3JniExceptionIsFatal("Cannot set OutputPointer.String.value");
@ -1358,13 +1393,17 @@ static int encodingTypeIsValid(int eTextRep){
} }
/* /*
** Proxy for Java-side Collation.xCompare() callbacks. ** Proxy for Java-side CollationCallback.xCompare() callbacks.
*/ */
static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs,
int nRhs, const void *rhs){ int nRhs, const void *rhs){
S3JniDb * const ps = pArg; S3JniDb * const ps = pArg;
LocalJniGetEnv; S3JniDeclLocal_env;
jint rc = 0; jint rc = 0;
S3JniHook hook;
S3JniHook_copy(env, &ps->hooks.collation, &hook );
if( hook.jObj ){
jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs);
jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL;
if( !jbaRhs ){ if( !jbaRhs ){
@ -1380,13 +1419,19 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs,
S3JniExceptionIgnore; S3JniExceptionIgnore;
S3JniUnrefLocal(jbaLhs); S3JniUnrefLocal(jbaLhs);
S3JniUnrefLocal(jbaRhs); S3JniUnrefLocal(jbaRhs);
S3JniUnrefLocal(hook.jObj);
}
return (int)rc; return (int)rc;
} }
/* Collation finalizer for use by the sqlite3 internals. */ /* Collation finalizer for use by the sqlite3 internals. */
static void CollationState_xDestroy(void *pArg){ static void CollationState_xDestroy(void *pArg){
S3JniDb * const ps = pArg; S3JniDb * const ps = pArg;
S3JniDeclLocal_env;
S3JniMutex_S3JniDb_enter;
S3JniHook_unref( s3jni_env(), &ps->hooks.collation, 1 ); S3JniHook_unref( s3jni_env(), &ps->hooks.collation, 1 );
S3JniMutex_S3JniDb_leave;
} }
/* /*
@ -1424,7 +1469,7 @@ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){
static void ResultJavaVal_finalizer(void *v){ static void ResultJavaVal_finalizer(void *v){
if( v ){ if( v ){
ResultJavaVal * const rv = (ResultJavaVal*)v; ResultJavaVal * const rv = (ResultJavaVal*)v;
LocalJniGetEnv; S3JniDeclLocal_env;
S3JniUnrefGlobal(rv->jObj); S3JniUnrefGlobal(rv->jObj);
sqlite3_free(rv); sqlite3_free(rv);
} }
@ -1539,7 +1584,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
} }
static void S3JniUdf_free(S3JniUdf * s){ static void S3JniUdf_free(S3JniUdf * s){
LocalJniGetEnv; S3JniDeclLocal_env;
if( env ){ if( env ){
//MARKER(("UDF cleanup: %s\n", s->zFuncName)); //MARKER(("UDF cleanup: %s\n", s->zFuncName));
s3jni_call_xDestroy(env, s->jObj); s3jni_call_xDestroy(env, s->jObj);
@ -1652,7 +1697,7 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
S3JniUdf * const s, S3JniUdf * const s,
jmethodID xMethodID, jmethodID xMethodID,
const char * const zFuncType){ const char * const zFuncType){
LocalJniGetEnv; S3JniDeclLocal_env;
udf_jargs args = {0,0}; udf_jargs args = {0,0};
int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
@ -1676,7 +1721,7 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc,
static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, static int udf_xFV(sqlite3_context* cx, S3JniUdf * s,
jmethodID xMethodID, jmethodID xMethodID,
const char *zFuncType){ const char *zFuncType){
LocalJniGetEnv; S3JniDeclLocal_env;
jobject jcx = new_sqlite3_context_wrapper(env, cx); jobject jcx = new_sqlite3_context_wrapper(env, cx);
int rc = 0; int rc = 0;
int const isFinal = 'F'==zFuncType[1]/*xFinal*/; int const isFinal = 'F'==zFuncType[1]/*xFinal*/;
@ -2044,15 +2089,19 @@ S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob64)(
static int s3jni_busy_handler(void* pState, int n){ static int s3jni_busy_handler(void* pState, int n){
S3JniDb * const ps = (S3JniDb *)pState; S3JniDb * const ps = (S3JniDb *)pState;
int rc = 0; int rc = 0;
if( ps->hooks.busyHandler.jObj ){ S3JniDeclLocal_env;
LocalJniGetEnv; S3JniHook hook;
rc = (*env)->CallIntMethod(env, ps->hooks.busyHandler.jObj,
ps->hooks.busyHandler.midCallback, (jint)n); S3JniHook_copy(env, &ps->hooks.busyHandler, &hook );
if( hook.jObj ){
rc = (*env)->CallIntMethod(env, hook.jObj,
hook.midCallback, (jint)n);
S3JniIfThrew{ S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback"); S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback");
rc = s3jni_db_exception(env, ps, SQLITE_ERROR, rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
"sqlite3_busy_handler() callback threw."); "sqlite3_busy_handler() callback threw.");
} }
S3JniUnrefLocal(hook.jObj);
} }
return rc; return rc;
} }
@ -2061,12 +2110,14 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
JniArgsEnvClass, jobject jDb, jobject jBusy JniArgsEnvClass, jobject jDb, jobject jBusy
){ ){
S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0;
int rc = 0; int rc = 0;
if( !ps ) return (jint)SQLITE_NOMEM; if( !ps ) return (jint)SQLITE_MISUSE;
S3JniMutex_S3JniDb_enter;
if( jBusy ){ if( jBusy ){
S3JniHook * const pHook = &ps->hooks.busyHandler;
if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){ if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){
/* Same object - this is a no-op. */ /* Same object - this is a no-op. */
S3JniMutex_S3JniDb_leave;
return 0; return 0;
} }
jclass klazz; jclass klazz;
@ -2079,15 +2130,16 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
S3JniHook_unref(env, pHook, 0); S3JniHook_unref(env, pHook, 0);
rc = SQLITE_ERROR; rc = SQLITE_ERROR;
} }
if( rc ){
return rc;
}
}else{ }else{
S3JniHook_unref(env, &ps->hooks.busyHandler, 1); S3JniHook_unref(env, pHook, 1);
} }
return jBusy if( 0==rc ){
rc = jBusy
? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps)
: sqlite3_busy_handler(ps->pDb, 0, 0); : sqlite3_busy_handler(ps->pDb, 0, 0);
}
S3JniMutex_S3JniDb_leave;
return rc;
} }
S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)(
@ -2172,7 +2224,11 @@ static unsigned int s3jni_utf16_strlen(void const * z){
static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
int eTextRep, const void * z16Name){ int eTextRep, const void * z16Name){
S3JniDb * const ps = pState; S3JniDb * const ps = pState;
LocalJniGetEnv; S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_copy(env, &ps->hooks.collationNeeded, &hook );
if( hook.jObj ){
unsigned int const nName = s3jni_utf16_strlen(z16Name); unsigned int const nName = s3jni_utf16_strlen(z16Name);
jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName);
S3JniIfThrew{ S3JniIfThrew{
@ -2187,44 +2243,46 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
} }
S3JniUnrefLocal(jName); S3JniUnrefLocal(jName);
} }
S3JniUnrefLocal(hook.jObj);
}
} }
S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
JniArgsEnvClass, jobject jDb, jobject jHook JniArgsEnvClass, jobject jDb, jobject jHook
){ ){
S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
jclass klazz;
jobject pOld = 0;
jmethodID xCallback;
S3JniHook * const pHook = &ps->hooks.collationNeeded; S3JniHook * const pHook = &ps->hooks.collationNeeded;
int rc; int rc = 0;
if( !ps ) return SQLITE_MISUSE; if( !ps ) return SQLITE_MISUSE;
pOld = pHook->jObj; S3JniMutex_S3JniDb_enter;
if( pOld && jHook && if( pHook->jObj && jHook &&
(*env)->IsSameObject(env, pOld, jHook) ){ (*env)->IsSameObject(env, pHook->jObj, jHook) ){
return 0; /* no-op */
} }else if( !jHook ){
if( !jHook ){ S3JniHook_unref(env, pHook, 0);
S3JniUnrefGlobal(pOld);
memset(pHook, 0, sizeof(S3JniHook));
sqlite3_collation_needed(ps->pDb, 0, 0); sqlite3_collation_needed(ps->pDb, 0, 0);
return 0; }else{
} jclass const klazz = (*env)->GetObjectClass(env, jHook);
klazz = (*env)->GetObjectClass(env, jHook); jmethodID const xCallback = (*env)->GetMethodID(
xCallback = (*env)->GetMethodID(env, klazz, "call", env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"
"(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); );
S3JniUnrefLocal(klazz); S3JniUnrefLocal(klazz);
S3JniIfThrew { S3JniIfThrew {
rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, rc = s3jni_db_exception(env, ps, SQLITE_MISUSE,
"Cannot not find matching callback on " "Cannot not find matching callback on "
"collation-needed hook object."); "collation-needed hook object.");
}else{ }else{
rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16);
if( rc ){
}else{
S3JniHook_unref(env, pHook, 0);
pHook->midCallback = xCallback; pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jHook); pHook->jObj = S3JniRefGlobal(jHook);
S3JniUnrefGlobal(pOld);
rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16);
} }
}
}
S3JniMutex_S3JniDb_leave;
return rc; return rc;
} }
@ -2281,22 +2339,31 @@ S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)(
S3JniApi(sqlite3_column_value(),jobject,1column_1value)( S3JniApi(sqlite3_column_value(),jobject,1column_1value)(
JniArgsEnvClass, jobject jpStmt, jint ndx JniArgsEnvClass, jobject jpStmt, jint ndx
){ ){
sqlite3_value * const sv = sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); sqlite3_value * const sv =
sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
return new_sqlite3_value_wrapper(env, sv); return new_sqlite3_value_wrapper(env, sv);
} }
static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ static int s3jni_commit_rollback_hook_impl(int isCommit,
LocalJniGetEnv; S3JniDb * const ps){
int rc = isCommit S3JniDeclLocal_env;
? (int)(*env)->CallIntMethod(env, ps->hooks.commit.jObj, int rc = 0;
ps->hooks.commit.midCallback) S3JniHook hook;
: (int)((*env)->CallVoidMethod(env, ps->hooks.rollback.jObj,
ps->hooks.rollback.midCallback), 0); S3JniHook_copy( env,
isCommit ? &ps->hooks.commit : &ps->hooks.rollback,
&hook);
if( hook.jObj ){
rc = isCommit
? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback)
: (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0);
S3JniIfThrew{ S3JniIfThrew{
S3JniExceptionClear; S3JniExceptionClear;
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw.");
} }
S3JniUnrefLocal(hook.jObj);
}
return rc; return rc;
} }
@ -2400,7 +2467,7 @@ S3JniApi(sqlite3_config() /*for a small subset of options.*/,
static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){
jobject jArg0 = 0; jobject jArg0 = 0;
jstring jArg1 = 0; jstring jArg1 = 0;
LocalJniGetEnv; S3JniDeclLocal_env;
S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb);
S3JniHook * const hook = &SJG.hooks.sqllog; S3JniHook * const hook = &SJG.hooks.sqllog;
@ -2431,9 +2498,11 @@ void sqlite3_init_sqllog(void){
} }
#endif #endif
S3JniApi(sqlite3_config(/* for SQLLOG */), S3JniApi(sqlite3_config() /* for SQLLOG */,
jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){ jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){
#ifdef SQLITE_ENABLE_SQLLOG #ifndef SQLITE_ENABLE_SQLLOG
return SQLITE_MISUSE;
#else
S3JniHook tmpHook; S3JniHook tmpHook;
S3JniHook * const hook = &tmpHook; S3JniHook * const hook = &tmpHook;
S3JniHook * const hookOld = & SJG.hooks.sqllog; S3JniHook * const hookOld = & SJG.hooks.sqllog;
@ -2466,9 +2535,6 @@ S3JniApi(sqlite3_config(/* for SQLLOG */),
*hookOld = *hook; *hookOld = *hook;
} }
return rc; return rc;
#else
MARKER(("Warning: built without SQLITE_ENABLE_SQLLOG.\n"));
return SQLITE_MISUSE;
#endif #endif
} }
@ -2482,32 +2548,35 @@ S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)(
S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
jint,1create_1collation jint,1create_1collation
)(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ )(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep,
jobject oCollation){
int rc; int rc;
const char *zName; const char *zName;
jclass klazz; jclass klazz;
S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
S3JniHook * const pHook = ps ? &ps->hooks.collation : 0; jmethodID midCallback;
if( !pHook ) return SQLITE_MISUSE; if( !ps ) return SQLITE_MISUSE;
klazz = (*env)->GetObjectClass(env, oCollation); klazz = (*env)->GetObjectClass(env, oCollation);
pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I");
"([B[B)I");
S3JniUnrefLocal(klazz); S3JniUnrefLocal(klazz);
S3JniIfThrew{ S3JniIfThrew{
S3JniUnrefLocal(klazz); S3JniUnrefLocal(klazz);
return s3jni_db_error(ps->pDb, SQLITE_ERROR, rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Could not get xCompare() method for object."); "Could not get xCompare() method for object.");
} }else{
zName = s3jni_jstring_to_mutf8(name); zName = s3jni_jstring_to_mutf8(name);
rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep,
ps, CollationState_xCompare, ps, CollationState_xCompare,
CollationState_xDestroy); CollationState_xDestroy);
s3jni_mutf8_release(name, zName); s3jni_mutf8_release(name, zName);
if( 0==rc ){ if( 0==rc ){
pHook->jObj = S3JniRefGlobal(oCollation); S3JniMutex_S3JniDb_enter;
}else{ S3JniHook_unref( env, &ps->hooks.collation, 1 );
S3JniHook_unref(env, pHook, 1); ps->hooks.collation.midCallback = midCallback;
ps->hooks.collation.jObj = S3JniRefGlobal(oCollation);
S3JniMutex_S3JniDb_leave;
}
} }
return (jint)rc; return (jint)rc;
} }
@ -2564,7 +2633,7 @@ error_cleanup:
return (jint)rc; return (jint)rc;
} }
S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/), S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/,
jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2
)(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){
S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
@ -2573,6 +2642,9 @@ S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/),
switch( (ps && jStr) ? op : 0 ){ switch( (ps && jStr) ? op : 0 ){
case SQLITE_DBCONFIG_MAINDBNAME: case SQLITE_DBCONFIG_MAINDBNAME:
S3JniMutex_S3JniDb_enter
/* Protect against a race in modifying/freeing
ps->zMainDbName. */;
zStr = s3jni_jstring_to_utf8(env, jStr, 0); zStr = s3jni_jstring_to_utf8(env, jStr, 0);
if( zStr ){ if( zStr ){
rc = sqlite3_db_config(ps->pDb, (int)op, zStr); rc = sqlite3_db_config(ps->pDb, (int)op, zStr);
@ -2585,6 +2657,7 @@ S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/),
}else{ }else{
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
} }
S3JniMutex_S3JniDb_leave;
break; break;
default: default:
rc = SQLITE_MISUSE; rc = SQLITE_MISUSE;
@ -2909,7 +2982,7 @@ jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self,
int rc = SQLITE_ERROR; int rc = SQLITE_ERROR;
assert(prepVersion==1 || prepVersion==2 || prepVersion==3); assert(prepVersion==1 || prepVersion==2 || prepVersion==3);
if( !pBuf ){ if( !pBuf ){
rc = baSql ? SQLITE_MISUSE : SQLITE_NOMEM; rc = baSql ? SQLITE_NOMEM : SQLITE_MISUSE;
goto end; goto end;
} }
jStmt = new_sqlite3_stmt_wrapper(env, 0); jStmt = new_sqlite3_stmt_wrapper(env, 0);
@ -2995,37 +3068,38 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
const char *zDb, const char *zTable, const char *zDb, const char *zTable,
sqlite3_int64 iKey1, sqlite3_int64 iKey2){ sqlite3_int64 iKey1, sqlite3_int64 iKey2){
S3JniDb * const ps = pState; S3JniDb * const ps = pState;
LocalJniGetEnv; S3JniDeclLocal_env;
jstring jDbName; jstring jDbName;
jstring jTable; jstring jTable;
S3JniHook * pHook;
const int isPre = 0!=pDb; const int isPre = 0!=pDb;
S3JniHook hook;
pHook = isPre ? S3JniHook_copy(env, isPre ?
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&ps->hooks.preUpdate &ps->hooks.preUpdate
#else #else
0 &S3JniHook_empty
#endif #endif
: &ps->hooks.update; : &ps->hooks.update, &hook);
if( !hook.jObj ){
assert( pHook ); return;
}
jDbName = s3jni_utf8_to_jstring(env, zDb, -1); jDbName = s3jni_utf8_to_jstring(env, zDb, -1);
jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0; jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0;
S3JniIfThrew { S3JniIfThrew {
S3JniExceptionClear; S3JniExceptionClear;
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
}else{ }else{
assert( pHook->jObj ); assert( hook.jObj );
assert( pHook->midCallback ); assert( hook.midCallback );
assert( ps->jDb ); assert( ps->jDb );
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
if( isPre ) (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, if( isPre ) (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
ps->jDb, (jint)opId, jDbName, jTable, ps->jDb, (jint)opId, jDbName, jTable,
(jlong)iKey1, (jlong)iKey2); (jlong)iKey1, (jlong)iKey2);
else else
#endif #endif
(*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
(jint)opId, jDbName, jTable, (jlong)iKey1); (jint)opId, jDbName, jTable, (jlong)iKey1);
S3JniIfThrew{ S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback"); S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback");
@ -3035,6 +3109,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
} }
S3JniUnrefLocal(jDbName); S3JniUnrefLocal(jDbName);
S3JniUnrefLocal(jTable); S3JniUnrefLocal(jTable);
S3JniUnrefLocal(hook.jObj);
} }
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@ -3070,21 +3145,23 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec
jclass klazz; jclass klazz;
jobject pOld = 0; jobject pOld = 0;
jmethodID xCallback; jmethodID xCallback;
S3JniHook * pHook = ps ? ( S3JniHook * pHook;
isPre ?
if( !ps ) return 0;
S3JniMutex_S3JniDb_enter;
pHook = isPre ?
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&ps->hooks.preUpdate &ps->hooks.preUpdate
#else #else
0 0
#endif #endif
: &ps->hooks.update) : 0; : &ps->hooks.update;
if( !pHook ){ if( !pHook ){
return 0; goto end;
} }
pOld = pHook->jObj; pOld = pHook->jObj;
if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){ if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){
return pOld; goto end;
} }
if( !jHook ){ if( !jHook ){
if( pOld ){ if( pOld ){
@ -3098,7 +3175,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec
else else
#endif #endif
sqlite3_update_hook(ps->pDb, 0, 0); sqlite3_update_hook(ps->pDb, 0, 0);
return pOld; goto end;
} }
klazz = (*env)->GetObjectClass(env, jHook); klazz = (*env)->GetObjectClass(env, jHook);
xCallback = isPre xCallback = isPre
@ -3130,6 +3207,8 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec
pOld = tmp; pOld = tmp;
} }
} }
end:
S3JniMutex_S3JniDb_leave;
return pOld; return pOld;
} }
@ -3185,32 +3264,36 @@ S3JniApi(sqlite3_preupdate_old(),jint,1preupdate_1old)(
/* Central C-to-Java sqlite3_progress_handler() proxy. */ /* Central C-to-Java sqlite3_progress_handler() proxy. */
static int s3jni_progress_handler_impl(void *pP){ static int s3jni_progress_handler_impl(void *pP){
S3JniDb * const ps = (S3JniDb *)pP; S3JniDb * const ps = (S3JniDb *)pP;
LocalJniGetEnv; int rc = 0;
int rc = (int)(*env)->CallIntMethod(env, ps->hooks.progress.jObj, S3JniDeclLocal_env;
ps->hooks.progress.midCallback); S3JniHook hook;
S3JniHook_copy( env, &ps->hooks.progress, &hook );
if( hook.jObj ){
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback);
S3JniIfThrew{ S3JniIfThrew{
rc = s3jni_db_exception(env, ps, rc, rc = s3jni_db_exception(env, ps, rc,
"sqlite3_progress_handler() callback threw"); "sqlite3_progress_handler() callback threw");
} }
S3JniUnrefLocal(hook.jObj);
}
return rc; return rc;
} }
S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
JniArgsEnvClass,jobject jDb, jint n, jobject jProgress JniArgsEnvClass,jobject jDb, jint n, jobject jProgress
){ ){
S3JniDb * ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
jclass klazz; jclass klazz;
jmethodID xCallback; jmethodID xCallback;
S3JniHook * const pHook = ps ? &ps->hooks.progress : 0;
if( !ps ) return;
S3JniMutex_S3JniDb_enter;
if( n<1 || !jProgress ){ if( n<1 || !jProgress ){
if( ps ){ S3JniHook_unref(env, pHook, 0);
S3JniUnrefGlobal(ps->hooks.progress.jObj);
memset(&ps->hooks.progress, 0, sizeof(ps->hooks.progress));
}
sqlite3_progress_handler(ps->pDb, 0, 0, 0); sqlite3_progress_handler(ps->pDb, 0, 0, 0);
return; S3JniMutex_S3JniDb_leave;
}
if( !ps ){
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
return; return;
} }
klazz = (*env)->GetObjectClass(env, jProgress); klazz = (*env)->GetObjectClass(env, jProgress);
@ -3222,22 +3305,19 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
"Cannot not find matching xCallback() on " "Cannot not find matching xCallback() on "
"ProgressHandler object."); "ProgressHandler object.");
}else{ }else{
S3JniUnrefGlobal(ps->hooks.progress.jObj); S3JniUnrefGlobal(pHook->jObj);
ps->hooks.progress.midCallback = xCallback; pHook->midCallback = xCallback;
ps->hooks.progress.jObj = S3JniRefGlobal(jProgress); pHook->jObj = S3JniRefGlobal(jProgress);
sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
} }
S3JniMutex_S3JniDb_leave;
} }
S3JniApi(sqlite3_reset(),jint,1reset)( S3JniApi(sqlite3_reset(),jint,1reset)(
JniArgsEnvClass, jobject jpStmt JniArgsEnvClass, jobject jpStmt
){ ){
int rc = 0;
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
if( pStmt ){ return pStmt ? sqlite3_reset(pStmt) : SQLITE_MISUSE;
rc = sqlite3_reset(pStmt);
}
return rc;
} }
/* Clears all entries from S3JniGlobal.autoExt. */ /* Clears all entries from S3JniGlobal.autoExt. */
@ -3479,16 +3559,18 @@ S3JniApi(sqlite3_rollback_hook(),jobject,1rollback_1hook)(
int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
const char*z2,const char*z3){ const char*z2,const char*z3){
S3JniDb * const ps = pState; S3JniDb * const ps = pState;
LocalJniGetEnv; S3JniDeclLocal_env;
S3JniHook const * const pHook = &ps->hooks.auth; S3JniHook hook;
int rc = 0;
S3JniHook_copy(env, &ps->hooks.auth, &hook );
if( hook.jObj ){
jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0;
jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0;
jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0; jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0;
jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0; jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0;
int rc;
assert( pHook->jObj ); rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op,
rc = (*env)->CallIntMethod(env, pHook->jObj, pHook->midCallback, (jint)op,
s0, s1, s3, s3); s0, s1, s3, s3);
S3JniIfThrew{ S3JniIfThrew{
rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback");
@ -3497,6 +3579,8 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
S3JniUnrefLocal(s1); S3JniUnrefLocal(s1);
S3JniUnrefLocal(s2); S3JniUnrefLocal(s2);
S3JniUnrefLocal(s3); S3JniUnrefLocal(s3);
S3JniUnrefLocal(hook.jObj);
}
return rc; return rc;
} }
@ -3505,17 +3589,19 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
){ ){
S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0);
S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; S3JniHook * const pHook = ps ? &ps->hooks.auth : 0;
int rc = 0;
if( !ps ) return SQLITE_MISUSE; if( !ps ) return SQLITE_MISUSE;
else if( !jHook ){ S3JniMutex_S3JniDb_enter;
if( !jHook ){
S3JniHook_unref(env, pHook, 0); S3JniHook_unref(env, pHook, 0);
return (jint)sqlite3_set_authorizer( ps->pDb, 0, 0 ); rc = sqlite3_set_authorizer( ps->pDb, 0, 0 );
}else{ }else{
int rc = 0;
jclass klazz; jclass klazz;
if( pHook->jObj ){ if( pHook->jObj ){
if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){ if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){
/* Same object - this is a no-op. */ /* Same object - this is a no-op. */
S3JniMutex_S3JniDb_leave;
return 0; return 0;
} }
S3JniHook_unref(env, pHook, 0); S3JniHook_unref(env, pHook, 0);
@ -3533,13 +3619,15 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
S3JniUnrefLocal(klazz); S3JniUnrefLocal(klazz);
S3JniIfThrew { S3JniIfThrew {
S3JniHook_unref(env, pHook, 0); S3JniHook_unref(env, pHook, 0);
return s3jni_db_error(ps->pDb, SQLITE_ERROR, rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Error setting up Java parts of authorizer hook."); "Error setting up Java parts of authorizer hook.");
} }else{
rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps);
if( rc ) S3JniHook_unref(env, pHook, 0); if( rc ) S3JniHook_unref(env, pHook, 0);
return rc;
} }
}
S3JniMutex_S3JniDb_leave;
return rc;
} }
@ -3658,15 +3746,21 @@ S3JniApi(sqlite3_step(),jint,1step)(
static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
S3JniDb * const ps = (S3JniDb *)pC; S3JniDb * const ps = (S3JniDb *)pC;
LocalJniGetEnv; S3JniDeclLocal_env;
jobject jX = NULL /* the tracer's X arg */; jobject jX = NULL /* the tracer's X arg */;
jobject jP = NULL /* the tracer's P arg */; jobject jP = NULL /* the tracer's P arg */;
jobject jPUnref = NULL /* potentially a local ref to jP */; jobject jPUnref = NULL /* potentially a local ref to jP */;
int rc; int rc = 0;
S3JniHook hook;
S3JniHook_copy( env, &ps->hooks.trace, &hook );
if( !hook.jObj ){
return 0;
}
switch( traceflag ){ switch( traceflag ){
case SQLITE_TRACE_STMT: case SQLITE_TRACE_STMT:
jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1);
if( !jX ) return SQLITE_NOMEM; if( !jX ) rc = SQLITE_NOMEM;
/*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/
break; break;
case SQLITE_TRACE_PROFILE: case SQLITE_TRACE_PROFILE:
@ -3674,7 +3768,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
(jlong)*((sqlite3_int64*)pX)); (jlong)*((sqlite3_int64*)pX));
// hmm. ^^^ (*pX) really is zero. // hmm. ^^^ (*pX) really is zero.
// MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX)));
if( !jX ) return SQLITE_NOMEM; if( !jX ) rc = SQLITE_NOMEM;
break; break;
case SQLITE_TRACE_ROW: case SQLITE_TRACE_ROW:
break; break;
@ -3683,26 +3777,28 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
break; break;
default: default:
assert(!"cannot happen - unkown trace flag"); assert(!"cannot happen - unkown trace flag");
return SQLITE_ERROR; rc = SQLITE_ERROR;
} }
if( 0==rc ){
if( !jP ){ if( !jP ){
/* Create a new temporary sqlite3_stmt wrapper */ /* Create a new temporary sqlite3_stmt wrapper */
jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
if( !jP ){ if( !jP ){
S3JniUnrefLocal(jX); S3JniUnrefLocal(jX);
return SQLITE_NOMEM; rc = SQLITE_NOMEM;
} }
} }
assert(jP); assert(jP);
rc = (int)(*env)->CallIntMethod(env, ps->hooks.trace.jObj, rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
ps->hooks.trace.midCallback,
(jint)traceflag, jP, jX); (jint)traceflag, jP, jX);
S3JniIfThrew{ S3JniIfThrew{
rc = s3jni_db_exception(env, ps, SQLITE_ERROR, rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
"sqlite3_trace_v2() callback threw."); "sqlite3_trace_v2() callback threw.");
} }
}
S3JniUnrefLocal(jPUnref); S3JniUnrefLocal(jPUnref);
S3JniUnrefLocal(jX); S3JniUnrefLocal(jX);
S3JniUnrefLocal(hook.jObj);
return rc; return rc;
} }
@ -3938,7 +4034,7 @@ typedef struct {
} Fts5JniAux; } Fts5JniAux;
static void Fts5JniAux_free(Fts5JniAux * const s){ static void Fts5JniAux_free(Fts5JniAux * const s){
LocalJniGetEnv; S3JniDeclLocal_env;
if( env ){ if( env ){
/*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/
s3jni_call_xDestroy(env, s->jObj); s3jni_call_xDestroy(env, s->jObj);
@ -4102,7 +4198,7 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
jobject jpFts = 0; jobject jpFts = 0;
jobject jFXA; jobject jFXA;
int rc; int rc;
LocalJniGetEnv; S3JniDeclLocal_env;
assert(pAux); assert(pAux);
jFXA = s3jni_getFts5ExensionApi(env); jFXA = s3jni_getFts5ExensionApi(env);
@ -4169,7 +4265,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){
if( x ){ if( x ){
S3JniFts5AuxData * const p = x; S3JniFts5AuxData * const p = x;
if( p->jObj ){ if( p->jObj ){
LocalJniGetEnv; S3JniDeclLocal_env;
s3jni_call_xDestroy(env, p->jObj); s3jni_call_xDestroy(env, p->jObj);
S3JniUnrefGlobal(p->jObj); S3JniUnrefGlobal(p->jObj);
} }
@ -4327,7 +4423,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
guaranteed to be the same one passed to xQueryPhrase(). If it's guaranteed to be the same one passed to xQueryPhrase(). If it's
not, we'll have to create a new wrapper object on every call. */ not, we'll have to create a new wrapper object on every call. */
struct s3jni_xQueryPhraseState const * s = pData; struct s3jni_xQueryPhraseState const * s = pData;
LocalJniGetEnv; S3JniDeclLocal_env;
int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback,
SJG.fts5.jFtsExt, s->jFcx); SJG.fts5.jFtsExt, s->jFcx);
S3JniIfThrew{ S3JniIfThrew{
@ -4396,9 +4492,10 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z,
int nZ, int iStart, int iEnd){ int nZ, int iStart, int iEnd){
int rc; int rc;
LocalJniGetEnv; S3JniDeclLocal_env;
struct s3jni_xQueryPhraseState * const s = p; struct s3jni_xQueryPhraseState * const s = p;
jbyteArray jba; jbyteArray jba;
if( s->tok.zPrev == z && s->tok.nPrev == nZ ){ if( s->tok.zPrev == z && s->tok.nPrev == nZ ){
jba = s->tok.jba; jba = s->tok.jba;
}else{ }else{

View File

@ -22,7 +22,8 @@ package org.sqlite.jni;
public interface XDestroyCallback { public interface XDestroyCallback {
/** /**
Must perform any cleanup required by this object. Must not Must perform any cleanup required by this object. Must not
throw. throw. Must not call back into the sqlite3 API, else it might
invoke a deadlock.
*/ */
public void xDestroy(); public void xDestroy();
} }

View File

@ -1,5 +1,5 @@
C Move\sthe\s3\sJava\sSQLFunction\ssubclasses\sfrom\sinner\sclasses\sto\spackage\sscope. C Improve\sthreading\ssupport\sfor\sall\stypes\sof\sJNI-side\scallback\shooks,\smaking\sthem\ssafe\sto\sinvoke\sif\sanother\sthread\sis\sbusy\sreplacing\sthem.
D 2023-08-25T16:43:51.353 D 2023-08-26T10:20:38.261
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d
F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c 29c10d96f81361b0d121e389320a1dd57958fd758e3790817542d2eb20c42bed F ext/jni/src/c/sqlite3-jni.c b34328504aa8a3e761e097ac3454a665dcb770e577d09f665191d98fe4a5a7b6
F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184
F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5
F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030
@ -270,7 +270,7 @@ F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd
F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253
F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471
F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba
F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c
F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa
@ -2103,8 +2103,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 P 21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1
R 7f0497e9841cedff7936a9c02791fa84 R b5acfb602720e59e7b14d5c62b0da1bf
U stephan U stephan
Z 3ac21aead20aa0563125716a8691ad77 Z 1a4a2d91841b9730ebb4db624f2ef288
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114