Correct JNI mapping of collations to be 1-db-to-many-collations.

FossilOrigin-Name: b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f
This commit is contained in:
stephan 2023-08-28 20:21:56 +00:00
parent 35c7f538e9
commit a08f737503
5 changed files with 298 additions and 251 deletions

View File

@ -429,7 +429,6 @@ struct S3JniDb {
SQLITE_DBCONFIG_MAINDBNAME. */;
struct {
S3JniHook busyHandler;
S3JniHook collation;
S3JniHook collationNeeded;
S3JniHook commit;
S3JniHook progress;
@ -1021,12 +1020,14 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){
** problem extracting the exception's message, it's treated as
** non-fatal and zDfltMsg is used in its place.
**
** Locks the global S3JniDb mutex.
**
** This must only be called if a JNI exception is pending.
**
** Returns errCode unless it is 0, in which case SQLITE_ERROR is
** returned.
*/
static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
static int s3jni__db_exception(JNIEnv * const env, S3JniDb * const ps,
int errCode, const char *zDfltMsg){
jthrowable const ex = (*env)->ExceptionOccurred(env);
@ -1034,13 +1035,17 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
if( ex ){
char * zMsg;
S3JniExceptionClear;
S3JniMutex_S3JniDb_enter;
zMsg = s3jni_exception_error_msg(env, ex);
s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg);
sqlite3_free(zMsg);
S3JniUnrefLocal(ex);
S3JniMutex_S3JniDb_leave;
}
return errCode;
}
#define s3jni_db_exception(JniDb,ERRCODE,DFLTMSG) \
s3jni__db_exception(env, (JniDb), (ERRCODE), (DFLTMSG) )
/*
** Extracts the (void xDestroy()) method from jObj and applies it to
@ -1049,7 +1054,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps,
** trigger a warning to stdout or stderr and then the exception is
** suppressed.
*/
static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){
static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){
if( jObj ){
jclass const klazz = (*env)->GetObjectClass(env, jObj);
jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V");
@ -1068,25 +1073,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){
}
}
}
/*
** Removes any Java references from s and clears its state. If
** doXDestroy is true and s->jObj is not NULL, s->jObj's
** s is passed to s3jni_call_xDestroy() before any references are
** cleared. It is legal to call this when the object has no Java
** references.
*/
static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s,
int doXDestroy){
if( s->jObj ){
if( doXDestroy ){
s3jni_call_xDestroy(env, s->jObj);
}
S3JniUnrefGlobal(s->jObj);
}
memset(s, 0, sizeof(*s));
}
#define S3JniHook_unref(H,X) S3JniHook_unref_impl(env, (H), (X))
#define s3jni_call_xDestroy(JOBJ) s3jni__call_xDestroy(env, (JOBJ))
/*
** Internal helper for many hook callback impls. Locks the S3JniDb
@ -1105,15 +1092,36 @@ static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s,
** another thread modify the hook while we're running it. That copy
** has to have its own Java reference, but it need only be call-local.
*/
static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src,
S3JniHook * const dest ){
static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src,
S3JniHook * const dest ){
S3JniMutex_S3JniDb_enter;
*dest = *src;
if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj);
S3JniMutex_S3JniDb_leave;
}
#define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest)
#define S3JniHook_localundup(HOOK) S3JniUnrefLocal(HOOK.jObj)
/*
** Removes any Java references from s and clears its state. If
** doXDestroy is true and s->jObj is not NULL, s->jObj's
** s is passed to s3jni_call_xDestroy() before any references are
** cleared. It is legal to call this when the object has no Java
** references.
*/
static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s,
int doXDestroy){
if( s->jObj ){
if( doXDestroy ){
s3jni_call_xDestroy(s->jObj);
}
S3JniUnrefGlobal(s->jObj);
}
memset(s, 0, sizeof(*s));
}
#define S3JniHook_unref(hook,doDestroy) \
S3JniHook__unref(env, (hook), (doDestroy))
/*
** Clears all of s's state. Requires that that the caller has locked
** S3JniGlobal.perDb.mutex. Make sure to do anything needed with
@ -1125,7 +1133,6 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
#define UNHOOK(MEMBER) S3JniHook_unref(&s->hooks.MEMBER, 0)
UNHOOK(auth);
UNHOOK(busyHandler);
UNHOOK(collation);
UNHOOK(collationNeeded);
UNHOOK(commit);
UNHOOK(progress);
@ -1163,10 +1170,11 @@ static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
SJG.perDb.aFree = s;
}
}
#define S3JniDb_set_aside_unlocked(JniDb) S3JniDb__set_aside_unlocked(env, JniDb)
static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){
S3JniMutex_S3JniDb_enter;
S3JniDb__set_aside_unlocked(env, s);
S3JniDb_set_aside_unlocked(s);
S3JniMutex_S3JniDb_leave;
}
#define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
@ -1294,10 +1302,8 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){
*/
static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef,
jobject ppOut, const void * p){
jfieldID const fid = s3jni_nphop_field(env, pRef);
assert( ppOut );
(*env)->SetLongField(env, ppOut, fid, (jlong)p);
(*env)->SetLongField(env, ppOut, s3jni_nphop_field(env, pRef), (jlong)p);
S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer.");
}
@ -1313,8 +1319,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
S3JniNphRef const* pRef){
void * rv = 0;
if( pObj ){
jfieldID const fid = s3jni_nphop_field(env, pRef);
rv = (void*)(*env)->GetLongField(env, pObj, fid);
rv = (void*)(*env)->GetLongField(env, pObj, s3jni_nphop_field(env, pRef));
S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer.");
}
return rv;
@ -1427,7 +1432,7 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
/* An experiment */
//#define CLOSE_DB_LOCKED
#if defined(CLOSE_DB_LOCKED)
#if 1 || defined(CLOSE_DB_LOCKED)
static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
S3JniDb * s = 0;
sqlite3 * pDb = 0;
@ -1436,7 +1441,9 @@ static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
if( jDb ) pDb = PtrGet_sqlite3(jDb);
S3JniDb_search;
return s;
}
#define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB))
#endif
/*
@ -1589,45 +1596,44 @@ static int encodingTypeIsValid(int eTextRep){
}
}
/**
State for CollationCallbacks.
*/
typedef S3JniHook S3JniCollationCallback;
/*
** Proxy for Java-side CollationCallback.xCompare() callbacks.
*/
static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs,
int nRhs, const void *rhs){
S3JniDb * const ps = pArg;
S3JniCollationCallback * const pCC = pArg;
S3JniDeclLocal_env;
jint rc = 0;
S3JniHook hook;
S3JniHook_localdup(env, &ps->hooks.collation, &hook );
if( hook.jObj ){
if( pCC->jObj ){
jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs);
jbyteArray jbaRhs = jbaLhs
? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0;
if( !jbaRhs ){
S3JniUnrefLocal(jbaLhs);
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
/* We have no recovery strategy here. */
s3jni_oom_check( jbaRhs );
return 0;
}
rc = (*env)->CallIntMethod(env, ps->hooks.collation.jObj,
ps->hooks.collation.midCallback,
rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback,
jbaLhs, jbaRhs);
S3JniExceptionIgnore;
S3JniUnrefLocal(jbaLhs);
S3JniUnrefLocal(jbaRhs);
S3JniHook_localundup(hook);
}
return (int)rc;
}
/* CollationCallback finalizer for use by the sqlite3 internals. */
static void CollationCallback_xDestroy(void *pArg){
S3JniDb * const ps = pArg;
S3JniCollationCallback * const pCC = pArg;
S3JniDeclLocal_env;
S3JniMutex_S3JniDb_enter;
S3JniHook_unref(&ps->hooks.collation, 1);
S3JniMutex_S3JniDb_leave;
S3JniHook_unref(pCC, 1);
sqlite3_free(pCC);
}
/* For use with sqlite3_result/value_pointer() */
@ -1763,11 +1769,13 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s,
int cacheIt){
assert( !s->pNext );
s3jni_call_xDestroy(env, s->jObj);
S3JniUnrefGlobal(s->jObj);
sqlite3_free(s->zFuncName);
assert( !s->pNext );
memset(s, 0, sizeof(*s));
if( s->jObj ){
s3jni_call_xDestroy(s->jObj);
S3JniUnrefGlobal(s->jObj);
sqlite3_free(s->zFuncName);
assert( !s->pNext );
memset(s, 0, sizeof(*s));
}
if( cacheIt ){
S3JniMutex_Global_enter;
s->pNext = S3JniGlobal.udf.aFree;
@ -2309,13 +2317,13 @@ static int s3jni_busy_handler(void* pState, int n){
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_localdup(env, &ps->hooks.busyHandler, &hook );
S3JniHook_localdup(&ps->hooks.busyHandler, &hook );
if( hook.jObj ){
rc = (*env)->CallIntMethod(env, hook.jObj,
hook.midCallback, (jint)n);
S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback");
rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
rc = s3jni_db_exception(ps, SQLITE_ERROR,
"sqlite3_busy_handler() callback threw.");
}
S3JniHook_localundup(hook);
@ -2328,33 +2336,42 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
){
S3JniDb * const ps = S3JniDb_from_java(jDb);
S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0;
S3JniHook hook = S3JniHook_empty;
int rc = 0;
if( !ps ) return (jint)SQLITE_MISUSE;
S3JniMutex_S3JniDb_enter;
if( jBusy ){
if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){
/* Same object - this is a no-op. */
S3JniMutex_S3JniDb_leave;
return 0;
}else{
jclass const klazz = (*env)->GetObjectClass(env, jBusy);
hook.jObj = S3JniRefGlobal(jBusy);
hook.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
rc = SQLITE_ERROR;
}
}
jclass klazz;
S3JniHook_unref(pHook, 0);
pHook->jObj = S3JniRefGlobal(jBusy);
klazz = (*env)->GetObjectClass(env, jBusy);
pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniHook_unref(pHook, 0);
rc = SQLITE_ERROR;
}
}else{
S3JniHook_unref(pHook, 0);
}
if( 0==rc ){
rc = jBusy
? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps)
: sqlite3_busy_handler(ps->pDb, 0, 0);
if( jBusy ){
if( hook.jObj ){ /* Replace handler */
rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps);
if( 0==rc ){
S3JniHook_unref(pHook, 0);
*pHook = hook;
hook = S3JniHook_empty;
}
}/* else no-op */
}else{ /* Clear handler */
rc = sqlite3_busy_handler(ps->pDb, 0, 0);
if( 0==rc ){
S3JniHook_unref(pHook, 0);
}
}
}
S3JniHook_unref(&hook, 0);
S3JniMutex_S3JniDb_leave;
return rc;
}
@ -2388,9 +2405,8 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
/* Move final entry into this slot. */
--SJG.autoExt.nExt;
*ax = SJG.autoExt.aExt[SJG.autoExt.nExt];
memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0,
sizeof(S3JniAutoExtension));
assert(! SJG.autoExt.aExt[SJG.autoExt.nExt].jObj );
memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, sizeof(*ax));
assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj );
rc = JNI_TRUE;
break;
}
@ -2403,9 +2419,9 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
/* Wrapper for sqlite3_close(_v2)(). */
static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
int rc = 0;
#ifndef CLOSE_DB_LOCKED
S3JniDb * const ps = S3JniDb_from_java(jDb);
assert(version == 1 || version == 2);
if( ps ){
rc = 1==version
@ -2429,14 +2445,14 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
if( 0!=jDb ){
S3JniDb * ps;
S3JniMutex_S3JniDb_enter;
ps = S3JniDb__from_java_unlocked(env, jDb);
ps = S3JniDb_from_java_unlocked(jDb);
if( ps && ps->pDb ){
rc = 1==version
? (jint)sqlite3_close(ps->pDb)
: (jint)sqlite3_close_v2(ps->pDb);
if( 0==rc ){
S3JniDb__set_aside_unlocked(env,ps)
/* MUST come after close() because of ps->trace. */;
S3JniDb_set_aside_unlocked(ps)
/* MUST come after close() because of ps->hooks.trace. */;
NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0);
}
}else{
@ -2478,25 +2494,29 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_localdup(env, &ps->hooks.collationNeeded, &hook );
S3JniHook_localdup(&ps->hooks.collationNeeded, &hook );
if( hook.jObj ){
unsigned int const nName = s3jni_utf16_strlen(z16Name);
jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName);
s3jni_oom_check( jName );
S3JniIfThrew{
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
S3JniExceptionClear;
}else{
(*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj,
ps->hooks.collationNeeded.midCallback,
ps->jDb, (jint)eTextRep, jName);
S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback");
s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw");
jobject jDb;
S3JniMutex_S3JniDb_enter;
jDb = ps->jDb ? S3JniRefLocal(ps->jDb) : 0;
S3JniMutex_S3JniDb_leave;
if( jDb ){
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
jDb, (jint)eTextRep, jName);
S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback");
s3jni_db_exception(ps, 0, "sqlite3_collation_needed() callback threw");
}
S3JniUnrefLocal(jDb);
}
S3JniUnrefLocal(jName);
}
S3JniUnrefLocal(jName);
S3JniHook_localundup(hook);
}
}
@ -2504,18 +2524,25 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
JniArgsEnvClass, jobject jDb, jobject jHook
){
S3JniDb * const ps = S3JniDb_from_java(jDb);
S3JniHook * const pHook = &ps->hooks.collationNeeded;
S3JniDb * ps;
S3JniHook * pHook;
int rc = 0;
if( !ps ) return SQLITE_MISUSE;
S3JniMutex_S3JniDb_enter;
ps = S3JniDb_from_java_unlocked(jDb);
if( !ps ){
S3JniMutex_S3JniDb_leave;
return SQLITE_MISUSE;
}
pHook = &ps->hooks.collationNeeded;
if( pHook->jObj && jHook &&
(*env)->IsSameObject(env, pHook->jObj, jHook) ){
/* no-op */
}else if( !jHook ){
S3JniHook_unref(pHook, 0);
sqlite3_collation_needed(ps->pDb, 0, 0);
rc = sqlite3_collation_needed(ps->pDb, 0, 0);
if( 0==rc ){
S3JniHook_unref(pHook, 0);
}
}else{
jclass const klazz = (*env)->GetObjectClass(env, jHook);
jmethodID const xCallback = (*env)->GetMethodID(
@ -2523,13 +2550,12 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
);
S3JniUnrefLocal(klazz);
S3JniIfThrew {
rc = s3jni_db_exception(env, ps, SQLITE_MISUSE,
"Cannot not find matching callback on "
"collation-needed hook object.");
rc = s3jni_db_exception(ps, SQLITE_MISUSE,
"Cannot not find matching call() in "
"CollationNeededCallback object.");
}else{
rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16);
if( rc ){
}else{
if( 0==rc ){
S3JniHook_unref(pHook, 0);
pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jHook);
@ -2603,15 +2629,17 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)(
return new_sqlite3_value_wrapper(env, sv);
}
static int s3jni_commit_rollback_hook_impl(int isCommit,
S3JniDb * const ps){
/*
** Impl for both commit hooks (if isCommit is true) or rollback hooks.
*/
static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){
S3JniDeclLocal_env;
int rc = 0;
S3JniHook hook;
S3JniHook_localdup( env, isCommit
? &ps->hooks.commit : &ps->hooks.rollback,
&hook);
S3JniHook_localdup(isCommit
? &ps->hooks.commit : &ps->hooks.rollback,
&hook);
if( hook.jObj ){
rc = isCommit
? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback)
@ -2625,32 +2653,35 @@ static int s3jni_commit_rollback_hook_impl(int isCommit,
return rc;
}
/* C-to-Java commit hook wrapper. */
static int s3jni_commit_hook_impl(void *pP){
return s3jni_commit_rollback_hook_impl(1, pP);
}
/* C-to-Java rollback hook wrapper. */
static void s3jni_rollback_hook_impl(void *pP){
(void)s3jni_commit_rollback_hook_impl(0, pP);
}
static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
jobject jDb, jobject jHook){
S3JniDb * const ps = S3JniDb_from_java(jDb);
jclass klazz;
S3JniDb * ps;
jobject pOld = 0;
jmethodID xCallback;
S3JniHook * const pHook =
isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
S3JniHook * pHook;
S3JniMutex_S3JniDb_enter;
ps = S3JniDb_from_java_unlocked(jDb);
if( !ps ){
s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
S3JniMutex_S3JniDb_leave;
return 0;
}
pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
pOld = pHook->jObj;
if( pOld && jHook &&
(*env)->IsSameObject(env, pOld, jHook) ){
return pOld;
}
if( !jHook ){
/* No-op. */
}else if( !jHook ){
if( pOld ){
jobject tmp = S3JniRefLocal(pOld);
S3JniUnrefGlobal(pOld);
@ -2659,29 +2690,30 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
memset(pHook, 0, sizeof(S3JniHook));
if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0);
else sqlite3_rollback_hook(ps->pDb, 0, 0);
return pOld;
}
klazz = (*env)->GetObjectClass(env, jHook);
xCallback = (*env)->GetMethodID(env, klazz, "call",
isCommit ? "()I" : "()V");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionReport;
S3JniExceptionClear;
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching callback on "
"hook object.");
}else{
pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jHook);
if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps);
else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps);
if( pOld ){
jobject tmp = S3JniRefLocal(pOld);
S3JniUnrefGlobal(pOld);
pOld = tmp;
jclass const klazz = (*env)->GetObjectClass(env, jHook);
jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call",
isCommit ? "()I" : "()V");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionReport;
S3JniExceptionClear;
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching call() in"
"hook object.");
}else{
pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jHook);
if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps);
else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps);
if( pOld ){
jobject tmp = S3JniRefLocal(pOld);
S3JniUnrefGlobal(pOld);
pOld = tmp;
}
}
}
S3JniMutex_S3JniDb_leave;
return pOld;
}
@ -2733,7 +2765,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int
S3JniHook hook = S3JniHook_empty;
if( ps ){
S3JniHook_localdup(env, &SJG.hooks.sqllog, &hook);
S3JniHook_localdup(&SJG.hooks.sqllog, &hook);
}
if( !hook.jObj ) return;
jArg0 = S3JniRefLocal(ps->jDb);
@ -2775,7 +2807,10 @@ S3JniApi(sqlite3_config() /* for SQLLOG */,
S3JniMutex_Global_enter;
if( !jLog ){
S3JniHook_unref(pHook, 0);
rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
if( 0==rc ){
S3JniHook_unref(pHook, 0);
}
}else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){
/* No-op */
}else {
@ -2825,26 +2860,28 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I");
S3JniUnrefLocal(klazz);
S3JniIfThrew{
S3JniUnrefLocal(klazz);
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Could not get xCompare() method for object.");
"Could not get call() method from "
"CollationCallback object.");
}else{
char * const zName = s3jni_jstring_to_utf8( name, 0);
if( zName ){
S3JniMutex_S3JniDb_enter;
S3JniCollationCallback * const pCC =
zName ? s3jni_malloc(sizeof(S3JniCollationCallback)) : 0;
if( pCC ){
memset( pCC, 0, sizeof(*pCC) );
rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep,
ps, CollationCallback_xCompare,
pCC, CollationCallback_xCompare,
CollationCallback_xDestroy);
sqlite3_free(zName);
if( 0==rc ){
S3JniHook_unref( &ps->hooks.collation, 1 );
ps->hooks.collation.midCallback = midCallback;
ps->hooks.collation.jObj = S3JniRefGlobal(oCollation);
pCC->midCallback = midCallback;
pCC->jObj = S3JniRefGlobal(oCollation);
}else{
CollationCallback_xDestroy(pCC);
}
S3JniMutex_S3JniDb_leave;
}else{
rc = SQLITE_NOMEM;
}
sqlite3_free(zName);
}
return (jint)rc;
}
@ -3363,7 +3400,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
const int isPre = 0!=pDb;
S3JniHook hook;
S3JniHook_localdup(env, isPre ?
S3JniHook_localdup(isPre ?
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
&ps->hooks.preUpdate
#else
@ -3392,7 +3429,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
(jint)opId, jDbName, jTable, (jlong)iKey1);
S3JniIfThrew{
S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback");
s3jni_db_exception(env, ps, 0,
s3jni_db_exception(ps, 0,
"sqlite3_(pre)update_hook() callback threw");
}
}
@ -3516,19 +3553,21 @@ S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)(
static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb,
jint iCol, jobject jOut){
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
sqlite3_value * pOut = 0;
sqlite3 * const pDb = PtrGet_sqlite3(jDb);
int rc;
int (*fOrig)(sqlite3*,int,sqlite3_value**) =
isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
rc = fOrig(pDb, (int)iCol, &pOut);
if( 0==rc ){
jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
if( pWrap ){
OutputPointer_set_sqlite3_value(env, jOut, pWrap);
S3JniUnrefLocal(pWrap);
}else{
rc = SQLITE_NOMEM;
int rc = SQLITE_MISUSE;
if( pDb ){
sqlite3_value * pOut = 0;
int (*fOrig)(sqlite3*,int,sqlite3_value**) =
isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
rc = fOrig(pDb, (int)iCol, &pOut);
if( 0==rc ){
jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
if( pWrap ){
OutputPointer_set_sqlite3_value(env, jOut, pWrap);
S3JniUnrefLocal(pWrap);
}else{
rc = SQLITE_NOMEM;
}
}
}
return rc;
@ -3557,11 +3596,11 @@ static int s3jni_progress_handler_impl(void *pP){
S3JniDeclLocal_env;
S3JniHook hook;
S3JniHook_localdup( env, &ps->hooks.progress, &hook );
S3JniHook_localdup(&ps->hooks.progress, &hook);
if( hook.jObj ){
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback);
S3JniIfThrew{
rc = s3jni_db_exception(env, ps, rc,
rc = s3jni_db_exception(ps, rc,
"sqlite3_progress_handler() callback threw");
}
S3JniHook_localundup(hook);
@ -3573,8 +3612,6 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
JniArgsEnvClass,jobject jDb, jint n, jobject jProgress
){
S3JniDb * const ps = S3JniDb_from_java(jDb);
jclass klazz;
jmethodID xCallback;
S3JniHook * const pHook = ps ? &ps->hooks.progress : 0;
if( !ps ) return;
@ -3582,22 +3619,21 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
if( n<1 || !jProgress ){
S3JniHook_unref(pHook, 0);
sqlite3_progress_handler(ps->pDb, 0, 0, 0);
S3JniMutex_S3JniDb_leave;
return;
}
klazz = (*env)->GetObjectClass(env, jProgress);
xCallback = (*env)->GetMethodID(env, klazz, "call", "()I");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionClear;
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching xCallback() on "
"ProgressHandler object.");
}else{
S3JniUnrefGlobal(pHook->jObj);
pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jProgress);
sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
jclass const klazz = (*env)->GetObjectClass(env, jProgress);
jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", "()I");
S3JniUnrefLocal(klazz);
S3JniIfThrew {
S3JniExceptionClear;
s3jni_db_error(ps->pDb, SQLITE_ERROR,
"Cannot not find matching xCallback() on "
"ProgressHandler object.");
}else{
S3JniUnrefGlobal(pHook->jObj);
pHook->midCallback = xCallback;
pHook->jObj = S3JniRefGlobal(jProgress);
sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
}
}
S3JniMutex_S3JniDb_leave;
}
@ -3752,7 +3788,7 @@ S3JniApi(sqlite3_result_error(),void,1result_1error)(
S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)(
JniArgsEnvClass, jobject jpCx, jint v
){
sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), v ? (int)v : SQLITE_ERROR);
sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), (int)v);
}
S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)(
@ -3850,7 +3886,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
S3JniHook hook;
int rc = 0;
S3JniHook_localdup(env, &ps->hooks.auth, &hook );
S3JniHook_localdup(&ps->hooks.auth, &hook );
if( hook.jObj ){
jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0;
jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0;
@ -3860,7 +3896,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op,
s0, s1, s3, s3);
S3JniIfThrew{
rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback");
rc = s3jni_db_exception(ps, rc, "sqlite3_set_authorizer() callback");
}
S3JniUnrefLocal(s0);
S3JniUnrefLocal(s1);
@ -3924,6 +3960,52 @@ S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)(
(sqlite3_int64)rowId);
}
S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
JniArgsEnvClass
){
s3jni_reset_auto_extension(env);
/* Free up env cache. */
S3JniMutex_Env_enter;
while( SJG.envCache.aHead ){
S3JniEnv_uncache( SJG.envCache.aHead->env );
}
S3JniMutex_Env_leave;
/* Free up S3JniUdf recycling bin. */
S3JniMutex_Global_enter;
while( S3JniGlobal.udf.aFree ){
S3JniUdf * const u = S3JniGlobal.udf.aFree;
S3JniGlobal.udf.aFree = u->pNext;
u->pNext = 0;
S3JniUdf_free(env, u, 0);
}
S3JniMutex_Global_leave;
/* Free up S3JniDb recycling bin. */
S3JniMutex_S3JniDb_enter;
while( S3JniGlobal.perDb.aFree ){
S3JniDb * const d = S3JniGlobal.perDb.aFree;
S3JniGlobal.perDb.aFree = d->pNext;
d->pNext = 0;
S3JniDb_clear(env, d);
sqlite3_free(d);
}
S3JniMutex_S3JniDb_leave;
#if 0
/*
** Is automatically closing any still-open dbs a good idea? We will
** get rid of the perDb list once sqlite3 gets a per-db client
** state, at which point we won't have a central list of databases
** to close.
*/
S3JniMutex_S3JniDb_enter;
while( SJG.perDb.pHead ){
s3jni_close_db(env, SJG.perDb.pHead->jDb, 2);
}
S3JniMutex_S3JniDb_leave;
#endif
/* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
return sqlite3_shutdown();
}
S3JniApi(sqlite3_status(),jint,1status)(
JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
jboolean reset
@ -3968,54 +4050,6 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
return rc;
}
S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
JniArgsEnvClass
){
s3jni_reset_auto_extension(env);
/* Free up env cache. */
S3JniMutex_Env_enter;
while( SJG.envCache.aHead ){
S3JniEnv_uncache( SJG.envCache.aHead->env );
}
S3JniMutex_Env_leave;
/* Free up S3JniUdf recycling bin. */
S3JniMutex_Global_enter;
while( S3JniGlobal.udf.aFree ){
S3JniUdf * const u = S3JniGlobal.udf.aFree;
S3JniGlobal.udf.aFree = u->pNext;
u->pNext = 0;
S3JniUdf_free(env, u, 0);
}
S3JniMutex_Global_leave;
/* Free up S3JniDb recycling bin. */
S3JniMutex_S3JniDb_enter;
while( S3JniGlobal.perDb.aFree ){
S3JniDb * const d = S3JniGlobal.perDb.aFree;
S3JniGlobal.perDb.aFree = d->pNext;
d->pNext = 0;
S3JniDb_clear(env, d);
sqlite3_free(d);
}
S3JniMutex_S3JniDb_leave;
#if 0
/*
** Is automatically closing any still-open dbs a good idea? We will
** get rid of the perDb list once sqlite3 gets a per-db client
** state, at which point we won't have a central list of databases
** to close.
*/
S3JniMutex_S3JniDb_enter;
while( SJG.perDb.pHead ){
s3jni_close_db(env, SJG.perDb.pHead->jDb, 2);
}
S3JniMutex_S3JniDb_leave;
#endif
/* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
return sqlite3_shutdown();
}
S3JniApi(sqlite3_strglob(),jint,1strglob)(
JniArgsEnvClass, jbyteArray baG, jbyteArray baT
){
@ -4061,7 +4095,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
int rc = 0;
S3JniHook hook;
S3JniHook_localdup( env, &ps->hooks.trace, &hook );
S3JniHook_localdup(&ps->hooks.trace, &hook );
if( !hook.jObj ){
return 0;
}
@ -4092,16 +4126,17 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
/* Create a new temporary sqlite3_stmt wrapper */
jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
if( !jP ){
S3JniUnrefLocal(jX);
rc = SQLITE_NOMEM;
}
}
assert(jP);
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
(jint)traceflag, jP, jX);
S3JniIfThrew{
rc = s3jni_db_exception(env, ps, SQLITE_ERROR,
"sqlite3_trace_v2() callback threw.");
if( 0==rc ){
assert(jP);
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
(jint)traceflag, jP, jX);
S3JniIfThrew{
rc = s3jni_db_exception(ps, SQLITE_ERROR,
"sqlite3_trace_v2() callback threw.");
}
}
}
S3JniUnrefLocal(jPUnref);
@ -4135,8 +4170,8 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
"Cannot not find matching call() on "
"TracerCallback object.");
}else{
S3JniMutex_S3JniDb_enter;
hook.jObj = S3JniRefGlobal(jTracer);
S3JniMutex_S3JniDb_enter;
rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps);
if( 0==rc ){
S3JniHook_unref(&ps->hooks.trace, 0);
@ -4349,7 +4384,7 @@ static void Fts5JniAux_free(Fts5JniAux * const s){
S3JniDeclLocal_env;
if( env ){
/*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/
s3jni_call_xDestroy(env, s->jObj);
s3jni_call_xDestroy(s->jObj);
S3JniUnrefGlobal(s->jObj);
S3JniUnrefGlobal(s->jUserData);
}
@ -4579,7 +4614,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){
S3JniFts5AuxData * const p = x;
if( p->jObj ){
S3JniDeclLocal_env;
s3jni_call_xDestroy(env, p->jObj);
s3jni_call_xDestroy(p->jObj);
S3JniUnrefGlobal(p->jObj);
}
sqlite3_free(x);
@ -4792,7 +4827,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
if( jAux ){
/* Emulate how xSetAuxdata() behaves when it cannot alloc
** its auxdata wrapper. */
s3jni_call_xDestroy(env, jAux);
s3jni_call_xDestroy(jAux);
}
return SQLITE_NOMEM;
}

View File

@ -1498,7 +1498,7 @@ public class Tester1 implements Runnable {
if( sqlLog ){
if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){
int rc = sqlite3_config( new ConfigSqllogCallback() {
final ConfigSqllogCallback log = new ConfigSqllogCallback() {
@Override public void call(sqlite3 db, String msg, int op){
switch(op){
case 0: outln("Opening db: ",db); break;
@ -1506,7 +1506,12 @@ public class Tester1 implements Runnable {
case 2: outln("Closing db: ",db); break;
}
}
});
};
int rc = sqlite3_config( log );
affirm( 0==rc );
rc = sqlite3_config( null );
affirm( 0==rc );
rc = sqlite3_config( log );
affirm( 0==rc );
}else{
outln("WARNING: -sqllog is not active because library was built ",

View File

@ -24,6 +24,14 @@ public interface XDestroyCallback {
Must perform any cleanup required by this object. Must not
throw. Must not call back into the sqlite3 API, else it might
invoke a deadlock.
WARNING: as a rule, it is never safe to register individual
instances with this interface multiple times in the
library. e.g., do not register the same CollationCallback with
multiple arities or names using sqlite3_create_collation(). If
this rule is violated, the library will eventually try to free
each individual reference, leading to memory corruption or a
crash via duplicate free().
*/
public void xDestroy();
}

View File

@ -1,5 +1,5 @@
C Updates\sto\stestrunner.tcl\sso\sthat\sit\sruns\sfuzztest\susing\smultiple\sjobs.
D 2023-08-28T20:14:19.097
C Correct\sJNI\smapping\sof\scollations\sto\sbe\s1-db-to-many-collations.
D 2023-08-28T20:21:56.713
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3
F ext/jni/GNUmakefile 374873bf6d2cd6ceafb458e28b59140dbb074f01f7adddf7e15a3ee3daf44551
F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c c92a764a728a3fcd34882defb7517c47af22c9f7fe184113015cdab9fe401c21
F ext/jni/src/c/sqlite3-jni.c 3c16035f6d604c17e50a6477b7309ca8885c54cd8a9c85e8c36d2f8b918d71e7
F ext/jni/src/c/sqlite3-jni.h 12e1a5ef5ee1795dc22577c285b4518dfd8aa4af45757f6cb81a555d967bf201
F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
@ -264,13 +264,13 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b
F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 440d64e8c4cff53bd3c0cc676381212489198302d7f1aaa535712c2d7163cc69
F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
F ext/jni/src/org/sqlite/jni/Tester1.java a9558165dbb085494705525ef28e41d337d8348bf44259ce1f77cad72547bfdf
F ext/jni/src/org/sqlite/jni/Tester1.java 21d78aa59bfc5ce5ff242d4bb6f6d2255d162fba8be5859ab87c9201d61433f0
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246
F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246
F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471
F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 50c5ca124ef6c6b735a7e136e7a23a557be367e61b56d4aab5777a614ab46cc2
F ext/jni/src/org/sqlite/jni/annotation/Canonical.java e55b82c8259b617ff754ac493fd8b79602631d659b87a858b987540e4c4fdf56
F ext/jni/src/org/sqlite/jni/annotation/NotNull.java d48ebd7ae6bbb78bd47d54431c85e1521c89b1d3864a2b6eafd9c0e1b2341457
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 6f962a98c9a5c6e9d21c50ae8716b16bdfdc934a191608cbb7e12ea588ddb6af
@ -2107,9 +2107,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P ecf07a0144dc6402b1e0924b1775d99dc465b27aa86a2718cac60a9b4c974312 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b
R 31bc738c1437151db584e22f2c509958
T +closed 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b
U dan
Z 97aabfccc20038ea125aae98b8297d02
P ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf
R 68323f68f37601aa48cb5f2c61fd0c0a
U stephan
Z 6560ea32a29f03de2be1f2fe7d94526d
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf
b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f