From: stephan Date: Wed, 23 Aug 2023 13:17:37 +0000 (+0000) Subject: Bind sqlite3_preupdate_hook() and friends to JNI. X-Git-Tag: version-3.44.0~305^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bea9ed0f1f4d99b8671b5b4255ea57fab99f6280;p=thirdparty%2Fsqlite.git Bind sqlite3_preupdate_hook() and friends to JNI. FossilOrigin-Name: d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 --- diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 1d77fc5f74..cc728003d7 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -161,6 +161,7 @@ SQLITE_OPT = \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ + -DSQLITE_ENABLE_PREUPDATE_HOOK \ -DSQLITE_ENABLE_SQLLOG \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 543f3be549..93c5d5f1d2 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -239,6 +239,7 @@ static const struct { const S3NphRef OutputPointer_ByteArray; const S3NphRef OutputPointer_sqlite3; const S3NphRef OutputPointer_sqlite3_stmt; + const S3NphRef OutputPointer_sqlite3_value; #ifdef SQLITE_ENABLE_FTS5 const S3NphRef Fts5Context; const S3NphRef Fts5ExtensionApi; @@ -248,22 +249,23 @@ static const struct { #endif } S3NphRefs = { #define NREF(INDEX, JAVANAME) { INDEX, "org/sqlite/jni/" JAVANAME } - NREF(0, "sqlite3"), - NREF(1, "sqlite3_stmt"), - NREF(2, "sqlite3_context"), - NREF(3, "sqlite3_value"), - NREF(4, "OutputPointer$Int32"), - NREF(5, "OutputPointer$Int64"), - NREF(6, "OutputPointer$String"), - NREF(7, "OutputPointer$ByteArray"), - NREF(8, "OutputPointer$sqlite3"), - NREF(9, "OutputPointer$sqlite3_stmt"), + NREF(0, "sqlite3"), + NREF(1, "sqlite3_stmt"), + NREF(2, "sqlite3_context"), + NREF(3, "sqlite3_value"), + NREF(4, "OutputPointer$Int32"), + NREF(5, "OutputPointer$Int64"), + NREF(6, "OutputPointer$String"), + NREF(7, "OutputPointer$ByteArray"), + NREF(8, "OutputPointer$sqlite3"), + NREF(9, "OutputPointer$sqlite3_stmt"), + NREF(10, "OutputPointer$sqlite3_value"), #ifdef SQLITE_ENABLE_FTS5 - NREF(10, "Fts5Context"), - NREF(11, "Fts5ExtensionApi"), - NREF(12, "fts5_api"), - NREF(13, "fts5_tokenizer"), - NREF(14, "Fts5Tokenizer") + NREF(11, "Fts5Context"), + NREF(12, "Fts5ExtensionApi"), + NREF(13, "fts5_api"), + NREF(14, "fts5_tokenizer"), + NREF(15, "Fts5Tokenizer") #endif #undef NREF }; @@ -394,15 +396,20 @@ struct S3JniDb { receive. */; char * zMainDbName /* Holds the string allocated on behalf of SQLITE_DBCONFIG_MAINDBNAME. */; - S3JniHook busyHandler; - S3JniHook collation; - S3JniHook collationNeeded; - S3JniHook commitHook; - S3JniHook progress; - S3JniHook rollbackHook; - S3JniHook trace; - S3JniHook updateHook; - S3JniHook authHook; + struct { + S3JniHook busyHandler; + S3JniHook collation; + S3JniHook collationNeeded; + S3JniHook commit; + S3JniHook progress; + S3JniHook rollback; + S3JniHook trace; + S3JniHook update; + S3JniHook auth; +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + S3JniHook preUpdate; +#endif + } hooks; #ifdef SQLITE_ENABLE_FTS5 jobject jFtsApi /* global ref to s3jni_fts5_api_from_db() */; #endif @@ -957,13 +964,16 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ SJG.perDb.aUsed = s->pNext; } sqlite3_free( s->zMainDbName ); -#define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->MEMBER, XDESTROY) +#define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) UNHOOK(trace, 0); UNHOOK(progress, 0); - UNHOOK(commitHook, 0); - UNHOOK(rollbackHook, 0); - UNHOOK(updateHook, 0); - UNHOOK(authHook, 0); + UNHOOK(commit, 0); + UNHOOK(rollback, 0); + UNHOOK(update, 0); + UNHOOK(auth, 0); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + UNHOOK(preUpdate, 0); +#endif UNHOOK(collation, 1); UNHOOK(collationNeeded, 1); UNHOOK(busyHandler, 1); @@ -1308,7 +1318,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon ** v. */ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, - jobject jDb){ + jobject jDb){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut ); @@ -1321,15 +1331,31 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, ** v. */ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, - jobject jStmt){ + jobject jStmt){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_sqlite3_stmt, - "Lorg/sqlite/jni/sqlite3_stmt;", jOut + "Lorg/sqlite/jni/sqlite3_stmt;", jOut ); (*env)->SetObjectField(env, jOut, setter, jStmt); EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value"); } +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +/* +** Sets the value property of the OutputPointer.sqlite3_value jOut object to +** v. +*/ +static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jOut, + jobject jValue){ + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_sqlite3_value, + "Lorg/sqlite/jni/sqlite3_value;", jOut + ); + (*env)->SetObjectField(env, jOut, setter, jValue); + EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_value.value"); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + #ifdef SQLITE_ENABLE_FTS5 #if 0 /* @@ -1391,7 +1417,8 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, } (*env)->SetByteArrayRegion(env, jbaLhs, 0, (jint)nLhs, (const jbyte*)lhs); (*env)->SetByteArrayRegion(env, jbaRhs, 0, (jint)nRhs, (const jbyte*)rhs); - rc = (*env)->CallIntMethod(env, ps->collation.jObj, ps->collation.midCallback, + rc = (*env)->CallIntMethod(env, ps->hooks.collation.jObj, + ps->hooks.collation.midCallback, jbaLhs, jbaRhs); EXCEPTION_IGNORE; UNREF_L(jbaLhs); @@ -1402,7 +1429,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, /* Collation finalizer for use by the sqlite3 internals. */ static void CollationState_xDestroy(void *pArg){ S3JniDb * const ps = pArg; - S3JniHook_unref( s3jni_get_env(), &ps->collation, 1 ); + S3JniHook_unref( s3jni_get_env(), &ps->hooks.collation, 1 ); } /* @@ -1777,6 +1804,11 @@ WRAP_INT_DB(1error_1offset, sqlite3_error_offset) WRAP_INT_DB(1extended_1errcode, sqlite3_extended_errcode) WRAP_MUTF8_VOID(1libversion, sqlite3_libversion) WRAP_INT_VOID(1libversion_1number, sqlite3_libversion_number) +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +WRAP_INT_DB(1preupdate_1blobwrite, sqlite3_preupdate_blobwrite) +WRAP_INT_DB(1preupdate_1count, sqlite3_preupdate_count) +WRAP_INT_DB(1preupdate_1depth, sqlite3_preupdate_depth) +#endif WRAP_INT_INT(1sleep, sqlite3_sleep) WRAP_MUTF8_VOID(1sourceid, sqlite3_sourceid) WRAP_INT_VOID(1threadsafe, sqlite3_threadsafe) @@ -1969,10 +2001,10 @@ JDECL(jint,1bind_1zeroblob64)(JENV_CSELF, jobject jpStmt, static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; int rc = 0; - if( ps->busyHandler.jObj ){ + if( ps->hooks.busyHandler.jObj ){ LocalJniGetEnv; - rc = (*env)->CallIntMethod(env, ps->busyHandler.jObj, - ps->busyHandler.midCallback, (jint)n); + rc = (*env)->CallIntMethod(env, ps->hooks.busyHandler.jObj, + ps->hooks.busyHandler.midCallback, (jint)n); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_busy_handler() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, @@ -1987,7 +2019,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ int rc = 0; if(!ps) return (jint)SQLITE_NOMEM; if(jBusy){ - S3JniHook * const pHook = &ps->busyHandler; + S3JniHook * const pHook = &ps->hooks.busyHandler; if(pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy)){ /* Same object - this is a no-op. */ return 0; @@ -2004,7 +2036,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ return rc; } }else{ - S3JniHook_unref(env, &ps->busyHandler, 1); + S3JniHook_unref(env, &ps->hooks.busyHandler, 1); } return jBusy ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) @@ -2014,7 +2046,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); if( ps ){ - S3JniHook_unref(env, &ps->busyHandler, 1); + S3JniHook_unref(env, &ps->hooks.busyHandler, 1); return sqlite3_busy_timeout(ps->pDb, (int)ms); } return SQLITE_MISUSE; @@ -2092,8 +2124,8 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); EXCEPTION_CLEAR; }else{ - (*env)->CallVoidMethod(env, ps->collationNeeded.jObj, - ps->collationNeeded.midCallback, + (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj, + ps->hooks.collationNeeded.midCallback, ps->jDb, (jint)eTextRep, jName); IFTHREW{ s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); @@ -2107,7 +2139,7 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ jclass klazz; jobject pOld = 0; jmethodID xCallback; - S3JniHook * const pHook = &ps->collationNeeded; + S3JniHook * const pHook = &ps->hooks.collationNeeded; int rc; if( !ps ) return SQLITE_MISUSE; @@ -2192,10 +2224,10 @@ JDECL(jobject,1column_1value)(JENV_CSELF, jobject jpStmt, static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ LocalJniGetEnv; int rc = isCommit - ? (int)(*env)->CallIntMethod(env, ps->commitHook.jObj, - ps->commitHook.midCallback) - : (int)((*env)->CallVoidMethod(env, ps->rollbackHook.jObj, - ps->rollbackHook.midCallback), 0); + ? (int)(*env)->CallIntMethod(env, ps->hooks.commit.jObj, + ps->hooks.commit.midCallback) + : (int)((*env)->CallVoidMethod(env, ps->hooks.rollback.jObj, + ps->hooks.rollback.midCallback), 0); IFTHREW{ EXCEPTION_CLEAR; rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); @@ -2211,13 +2243,14 @@ 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){ +static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, + jobject jDb, jobject jHook){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jobject pOld = 0; jmethodID xCallback; - S3JniHook * const pHook = isCommit ? &ps->commitHook : &ps->rollbackHook; + S3JniHook * const pHook = + isCommit ? &ps->hooks.commit : &ps->hooks.rollback; if(!ps){ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; @@ -2367,7 +2400,7 @@ JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ return rc; #else MARKER(("Warning: built without SQLITE_ENABLE_SQLLOG.\n")); - return SQLITE_RANGE; + return SQLITE_MISUSE; #endif } @@ -2380,13 +2413,13 @@ JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - jclass klazz; int rc; const char *zName; - S3JniHook * pHook; - if(!ps) return (jint)SQLITE_NOMEM; - pHook = &ps->collation; + jclass klazz; + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniHook * const pHook = ps ? &ps->hooks.collation : 0; + + if( !pHook ) return SQLITE_MISUSE; klazz = (*env)->GetObjectClass(env, oCollation); pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCompare", "([B[B)I"); @@ -2841,12 +2874,201 @@ JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jDb, jbyteArra prepFlags, jOutStmt, outTail); } +/* +** Impl for C-to-Java of the callbacks for both sqlite3_update_hook() +** and sqlite3_preupdate_hook(). The differences are that for +** update_hook(): +** +** - pDb is NULL +** - iKey1 is the row ID +** - iKey2 is unused +*/ +static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, + const char *zDb, const char *zTable, + sqlite3_int64 iKey1, sqlite3_int64 iKey2){ + S3JniDb * const ps = pState; + LocalJniGetEnv; + S3JniEnv * const jc = S3JniGlobal_env_cache(env); + jstring jDbName; + jstring jTable; + S3JniHook * pHook; + const int isPre = 0!=pDb; + + pHook = isPre ? +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + &ps->hooks.preUpdate +#else + 0 +#endif + : &ps->hooks.update; + + assert( pHook ); + jDbName = s3jni_utf8_to_jstring(jc, zDb, -1); + jTable = jDbName ? s3jni_utf8_to_jstring(jc, zTable, -1) : 0; + IFTHREW { + EXCEPTION_CLEAR; + s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + }else{ + assert( pHook->jObj ); + assert( pHook->midCallback ); + assert( ps->jDb ); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( isPre ) (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, + ps->jDb, (jint)opId, jDbName, jTable, + (jlong)iKey1, (jlong)iKey2); + else +#endif + (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, + (jint)opId, jDbName, jTable, (jlong)iKey1); + IFTHREW{ + EXCEPTION_WARN_CALLBACK_THREW("sqlite3_(pre)update_hook() callback"); + s3jni_db_exception(env, ps, 0, + "sqlite3_(pre)update_hook() callback threw"); + } + } + UNREF_L(jDbName); + UNREF_L(jTable); +} + +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK +static void s3jni_preupdate_hook_impl(void * pState, sqlite3 *pDb, int opId, + const char *zDb, const char *zTable, + sqlite3_int64 iKey1, sqlite3_int64 iKey2){ + return s3jni_updatepre_hook_impl(pState, pDb, opId, zDb, zTable, + iKey1, iKey2); +} +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ + +static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, + const char *zTable, sqlite3_int64 nRowid){ + return s3jni_updatepre_hook_impl(pState, NULL, opId, zDb, zTable, nRowid, 0); +} + +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK +/* We need no-op impls for preupdate_{count,depth,blobwrite}() */ +JDECL(int,1preupdate_1blobwrite)(JENV_CSELF, jobject jDb){ return SQLITE_MISUSE; } +JDECL(int,1preupdate_1count)(JENV_CSELF, jobject jDb){ return SQLITE_MISUSE; } +JDECL(int,1preupdate_1depth)(JENV_CSELF, jobject jDb){ return SQLITE_MISUSE; } +#endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */ + +/* +** JNI wrapper for both sqlite3_update_hook() and +** sqlite3_preupdate_hook() (if isPre is true). +*/ +static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobject jHook){ + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + jclass klazz; + jobject pOld = 0; + jmethodID xCallback; + S3JniHook * pHook = ps ? ( + isPre ? +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + &ps->hooks.preUpdate +#else + 0 +#endif + : &ps->hooks.update) : 0; + + if(!pHook){ + return 0; + } + pOld = pHook->jObj; + if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){ + return pOld; + } + if( !jHook ){ + if( pOld ){ + jobject tmp = REF_L(pOld); + UNREF_G(pOld); + pOld = tmp; + } + memset(pHook, 0, sizeof(S3JniHook)); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( isPre ) sqlite3_preupdate_hook(ps->pDb, 0, 0); + else +#endif + sqlite3_update_hook(ps->pDb, 0, 0); + return pOld; + } + klazz = (*env)->GetObjectClass(env, jHook); + xCallback = isPre + ? (*env)->GetMethodID(env, klazz, "xPreUpdate", + "(Lorg/sqlite/jni/sqlite3;" + "I" + "Ljava/lang/String;" + "Ljava/lang/String;" + "JJ)V") + : (*env)->GetMethodID(env, klazz, "xUpdateHook", + "(ILjava/lang/String;Ljava/lang/String;J)V"); + IFTHREW { + EXCEPTION_CLEAR; + s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Cannot not find matching callback on " + "(pre)update hook object."); + }else{ + pHook->midCallback = xCallback; + pHook->jObj = REF_G(jHook); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + if( isPre ) sqlite3_preupdate_hook(ps->pDb, s3jni_preupdate_hook_impl, ps); + else +#endif + sqlite3_update_hook(ps->pDb, s3jni_update_hook_impl, ps); + if(pOld){ + jobject tmp = REF_L(pOld); + UNREF_G(pOld); + pOld = tmp; + } + } + return pOld; +} + + +JDECL(jobject,1preupdate_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + return s3jni_updatepre_hook(env, 1, jDb, jHook); +#else + return NULL; +#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ +} + +/* Impl for sqlite3_preupdate_{new,old}(). */ +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); + UNREF_L(pWrap); + }else{ + rc = SQLITE_NOMEM; + } + } + return rc; +#else + return SQLITE_MISUSE; +#endif +} +JDECL(jint,1preupdate_1new)(JENV_CSELF, jobject jDb, jint iCol, jobject jOut){ + return s3jni_preupdate_newold(env, 1, jDb, iCol, jOut); +} +JDECL(jint,1preupdate_1old)(JENV_CSELF, jobject jDb, jint iCol, jobject jOut){ + return s3jni_preupdate_newold(env, 0, jDb, iCol, jOut); +} + + /* Central C-to-Java sqlite3_progress_handler() proxy. */ static int s3jni_progress_handler_impl(void *pP){ S3JniDb * const ps = (S3JniDb *)pP; LocalJniGetEnv; - int rc = (int)(*env)->CallIntMethod(env, ps->progress.jObj, - ps->progress.midCallback); + int rc = (int)(*env)->CallIntMethod(env, ps->hooks.progress.jObj, + ps->hooks.progress.midCallback); IFTHREW{ rc = s3jni_db_exception(env, ps, rc, "sqlite3_progress_handler() callback threw"); @@ -2860,8 +3082,8 @@ JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress jmethodID xCallback; if( n<1 || !jProgress ){ if(ps){ - UNREF_G(ps->progress.jObj); - memset(&ps->progress, 0, sizeof(ps->progress)); + UNREF_G(ps->hooks.progress.jObj); + memset(&ps->hooks.progress, 0, sizeof(ps->hooks.progress)); } sqlite3_progress_handler(ps->pDb, 0, 0, 0); return; @@ -2878,9 +3100,9 @@ JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress "Cannot not find matching xCallback() on " "ProgressHandler object."); }else{ - UNREF_G(ps->progress.jObj); - ps->progress.midCallback = xCallback; - ps->progress.jObj = REF_G(jProgress); + UNREF_G(ps->hooks.progress.jObj); + ps->hooks.progress.midCallback = xCallback; + ps->hooks.progress.jObj = REF_G(jProgress); sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); } } @@ -3099,7 +3321,7 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniDb * const ps = pState; LocalJniGetEnv; S3JniEnv * const jc = S3JniGlobal_env_cache(env); - S3JniHook const * const pHook = &ps->authHook; + S3JniHook const * const pHook = &ps->hooks.auth; jstring const s0 = z0 ? s3jni_utf8_to_jstring(jc, z0, -1) : 0; jstring const s1 = z1 ? s3jni_utf8_to_jstring(jc, z1, -1) : 0; jstring const s2 = z2 ? s3jni_utf8_to_jstring(jc, z2, -1) : 0; @@ -3121,7 +3343,7 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - S3JniHook * const pHook = ps ? &ps->authHook : 0; + S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; if( !ps ) return SQLITE_MISUSE; else if( !jHook ){ @@ -3287,8 +3509,8 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ } } assert(jP); - rc = (int)(*env)->CallIntMethod(env, ps->trace.jObj, - ps->trace.midCallback, + rc = (int)(*env)->CallIntMethod(env, ps->hooks.trace.jObj, + ps->hooks.trace.midCallback, (jint)traceflag, jP, jX); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_trace_v2() callback"); @@ -3303,96 +3525,28 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; + if( !traceMask || !jTracer ){ if(ps){ - UNREF_G(ps->trace.jObj); - memset(&ps->trace, 0, sizeof(ps->trace)); + S3JniHook_unref(env, &ps->hooks.trace, 0); } return (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); } if(!ps) return SQLITE_NOMEM; klazz = (*env)->GetObjectClass(env, jTracer); - ps->trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", + ps->hooks.trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(ILjava/lang/Object;Ljava/lang/Object;)I"); IFTHREW { EXCEPTION_CLEAR; return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on Tracer object."); } - ps->trace.jObj = REF_G(jTracer); + ps->hooks.trace.jObj = REF_G(jTracer); return sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); } -static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, - const char *zTable, sqlite3_int64 nRowid){ - S3JniDb * const ps = pState; - LocalJniGetEnv; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); - jstring jDbName; - jstring jTable; - jDbName = s3jni_utf8_to_jstring(jc, zDb, -1); - jTable = jDbName ? s3jni_utf8_to_jstring(jc, zTable, -1) : 0; - IFTHREW { - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); - }else{ - (*env)->CallVoidMethod(env, ps->updateHook.jObj, - ps->updateHook.midCallback, - (jint)opId, jDbName, jTable, (jlong)nRowid); - IFTHREW{ - EXCEPTION_WARN_CALLBACK_THREW("sqlite3_update_hook() callback"); - s3jni_db_exception(env, ps, 0, - "sqlite3_update_hook() callback threw"); - } - } - UNREF_L(jDbName); - UNREF_L(jTable); -} - - JDECL(jobject,1update_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - jclass klazz; - jobject pOld = 0; - jmethodID xCallback; - S3JniHook * const pHook = &ps->updateHook; - if(!ps){ - s3jni_db_error(ps->pDb, SQLITE_MISUSE, 0); - return 0; - } - pOld = pHook->jObj; - if(pOld && jHook && - (*env)->IsSameObject(env, pOld, jHook)){ - return pOld; - } - if( !jHook ){ - if(pOld){ - jobject tmp = REF_L(pOld); - UNREF_G(pOld); - pOld = tmp; - } - memset(pHook, 0, sizeof(S3JniHook)); - sqlite3_update_hook(ps->pDb, 0, 0); - return pOld; - } - klazz = (*env)->GetObjectClass(env, jHook); - xCallback = (*env)->GetMethodID(env, klazz, "xUpdateHook", - "(ILjava/lang/String;Ljava/lang/String;J)V"); - IFTHREW { - EXCEPTION_CLEAR; - s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Cannot not find matching callback on " - "update hook object."); - }else{ - pHook->midCallback = xCallback; - pHook->jObj = REF_G(jHook); - sqlite3_update_hook(ps->pDb, s3jni_update_hook_impl, ps); - if(pOld){ - jobject tmp = REF_L(pOld); - UNREF_G(pOld); - pOld = tmp; - } - } - return pOld; + return s3jni_updatepre_hook(env, 0, jDb, jHook); } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 0a8736f2a7..86fada9574 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1307,6 +1307,54 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v2 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v3 (JNIEnv *, jclass, jobject, jbyteArray, jint, jint, jobject, jobject); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_blobwrite + * Signature: (Lorg/sqlite/jni/sqlite3;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1blobwrite + (JNIEnv *, jclass, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_count + * Signature: (Lorg/sqlite/jni/sqlite3;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1count + (JNIEnv *, jclass, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_depth + * Signature: (Lorg/sqlite/jni/sqlite3;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1depth + (JNIEnv *, jclass, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_hook + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/PreUpdateHook;)Lorg/sqlite/jni/PreUpdateHook; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1hook + (JNIEnv *, jclass, jobject, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_new + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/OutputPointer/sqlite3_value;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1new + (JNIEnv *, jclass, jobject, jint, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_preupdate_old + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/OutputPointer/sqlite3_value;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1old + (JNIEnv *, jclass, jobject, jint, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_progress_handler diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index 416ad48e60..bf61656dd5 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -86,6 +86,28 @@ public final class OutputPointer { } } + /** + Output pointer for use with routines, such as sqlite3_prepupdate_new(), + which return a sqlite3_value handle via an output pointer. These + pointers can only be set by the JNI layer, not by client-level + code. + */ + public static final class sqlite3_value { + private org.sqlite.jni.sqlite3_value value; + //! Initializes with a null value. + public sqlite3_value(){value = null;} + //! Sets the current value to null. + public void clear(){value = null;} + //! Returns the current value. + public final org.sqlite.jni.sqlite3_value get(){return value;} + //! Equivalent to calling get() then clear(). + public final org.sqlite.jni.sqlite3_value take(){ + final org.sqlite.jni.sqlite3_value v = value; + value = null; + return v; + } + } + /** Output pointer for use with native routines which return integers via output pointers. diff --git a/ext/jni/src/org/sqlite/jni/PreUpdateHook.java b/ext/jni/src/org/sqlite/jni/PreUpdateHook.java new file mode 100644 index 0000000000..d5d82c72bc --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/PreUpdateHook.java @@ -0,0 +1,29 @@ +/* +** 2023-08-23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file is part of the JNI bindings for the sqlite3 C API. +*/ +package org.sqlite.jni; + +/** + A callback for use with sqlite3_preupdate_hook(). +*/ +public interface PreUpdateHook { + /** + Must function as described for the sqlite3_preupdate_hook(). + callback, with the slight signature change. + + Must not throw. Any exceptions may emit debugging messages and + will be suppressed. + */ + void xPreUpdate(sqlite3 db, int op, String dbName, String dbTable, + long iKey1, long iKey2 ); +} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index e64ecf4914..c22d2fa60e 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -475,7 +475,7 @@ public final class SQLite3Jni { ** retained. ** ** If not built with SQLITE_ENABLE_SQLLOG defined, this returns - ** SQLITE_RANGE. + ** SQLITE_MISUSE. */ public static native int sqlite3_config( @Nullable SQLLog logger ); @@ -706,6 +706,69 @@ public final class SQLite3Jni { return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null); } + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this + acts as a proxy for C's sqlite3_preupdate_blobwrite(), else it returns + SQLITE_MISUSE with no side effects. + */ + public static native int sqlite3_preupdate_blobwrite(@NotNull sqlite3 db); + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this + acts as a proxy for C's sqlite3_preupdate_count(), else it returns + SQLITE_MISUSE with no side effects. + */ + public static native int sqlite3_preupdate_count(@NotNull sqlite3 db); + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this + acts as a proxy for C's sqlite3_preupdate_depth(), else it returns + SQLITE_MISUSE with no side effects. + */ + public static native int sqlite3_preupdate_depth(@NotNull sqlite3 db); + + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this + acts as a proxy for C's sqlite3_preupdate_hook(), else it returns null + with no side effects. + */ + public static native PreUpdateHook sqlite3_preupdate_hook(@NotNull sqlite3 db, + @Nullable PreUpdateHook hook); + + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, + this acts as a proxy for C's sqlite3_preupdate_new(), else it + returns SQLITE_MISUSE with no side effects. + */ + public static native int sqlite3_preupdate_new(@NotNull sqlite3 db, int col, + @NotNull OutputPointer.sqlite3_value out); + + /** + Convenience wrapper for the 3-arg sqlite3_preupdate_new() which returns + null on error. + */ + public static sqlite3_value sqlite3_preupdate_new(@NotNull sqlite3 db, int col){ + final OutputPointer.sqlite3_value out = new OutputPointer.sqlite3_value(); + sqlite3_preupdate_new(db, col, out); + return out.take(); + } + + /** + If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, + this acts as a proxy for C's sqlite3_preupdate_old(), else it + returns SQLITE_MISUSE with no side effects. + */ + public static native int sqlite3_preupdate_old(@NotNull sqlite3 db, int col, + @NotNull OutputPointer.sqlite3_value out); + + /** + Convenience wrapper for the 3-arg sqlite3_preupdate_old() which returns + null on error. + */ + public static sqlite3_value sqlite3_preupdate_old(@NotNull sqlite3 db, int col){ + final OutputPointer.sqlite3_value out = new OutputPointer.sqlite3_value(); + sqlite3_preupdate_old(db, col, out); + return out.take(); + } + public static native void sqlite3_progress_handler( @NotNull sqlite3 db, int n, @Nullable ProgressHandler h ); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index fc350814b4..616e9fed22 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -997,6 +997,7 @@ public class Tester1 implements Runnable { final ValueHolder expectedOp = new ValueHolder<>(0); final UpdateHook theHook = new UpdateHook(){ @SuppressWarnings("unchecked") + @Override public void xUpdateHook(int opId, String dbName, String tableName, long rowId){ ++counter.value; if( 0!=expectedOp.value ){ @@ -1040,6 +1041,79 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } + /** + This test is functionally identical to testUpdateHook(), only with a + different callback type. + */ + private synchronized void testPreUpdateHook(){ + final sqlite3 db = createNewDb(); + final ValueHolder counter = new ValueHolder<>(0); + final ValueHolder expectedOp = new ValueHolder<>(0); + final PreUpdateHook theHook = new PreUpdateHook(){ + @SuppressWarnings("unchecked") + @Override + public void xPreUpdate(sqlite3 db, int opId, String dbName, String dbTable, + long iKey1, long iKey2 ){ + ++counter.value; + switch( opId ){ + case SQLITE_UPDATE: + affirm( 0 < sqlite3_preupdate_count(db) ); + affirm( null != sqlite3_preupdate_new(db, 0) ); + affirm( null != sqlite3_preupdate_old(db, 0) ); + break; + case SQLITE_INSERT: + affirm( null != sqlite3_preupdate_new(db, 0) ); + break; + case SQLITE_DELETE: + affirm( null != sqlite3_preupdate_old(db, 0) ); + break; + default: + break; + } + if( 0!=expectedOp.value ){ + affirm( expectedOp.value == opId ); + } + } + }; + PreUpdateHook oldHook = sqlite3_preupdate_hook(db, theHook); + affirm( null == oldHook ); + expectedOp.value = SQLITE_INSERT; + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + affirm( 3 == counter.value ); + expectedOp.value = SQLITE_UPDATE; + execSql(db, "update t set a='d' where a='c';"); + affirm( 4 == counter.value ); + oldHook = sqlite3_preupdate_hook(db, theHook); + affirm( theHook == oldHook ); + expectedOp.value = SQLITE_DELETE; + execSql(db, "DELETE FROM t where a='d'"); + affirm( 5 == counter.value ); + oldHook = sqlite3_preupdate_hook(db, null); + affirm( theHook == oldHook ); + execSql(db, "update t set a='e' where a='b';"); + affirm( 5 == counter.value ); + oldHook = sqlite3_preupdate_hook(db, null); + affirm( null == oldHook ); + + final PreUpdateHook newHook = new PreUpdateHook(){ + @Override + public void xPreUpdate(sqlite3 db, int opId, String dbName, + String tableName, long iKey1, long iKey2){ + } + }; + oldHook = sqlite3_preupdate_hook(db, newHook); + affirm( null == oldHook ); + execSql(db, "update t set a='h' where a='a'"); + affirm( 5 == counter.value ); + oldHook = sqlite3_preupdate_hook(db, theHook); + affirm( newHook == oldHook ); + expectedOp.value = SQLITE_UPDATE; + execSql(db, "update t set a='i' where a='h'"); + affirm( 6 == counter.value ); + + sqlite3_close_v2(db); + } + private void testRollbackHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); diff --git a/manifest b/manifest index ff7bb357eb..ca1266d086 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\sa\ssubset\sof\ssqlite3_config()\sto\sJNI:\sthreading\smodes\sand\ssqllog. -D 2023-08-23T10:36:12.341 +C Bind\ssqlite3_preupdate_hook()\sand\sfriends\sto\sJNI. +D 2023-08-23T13:17:37.782 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,11 +232,11 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 -F ext/jni/GNUmakefile 33abc2f4f4bbd5451d6be5e6f2e109c045cc326cd942d602a3908a0c7b3c6f49 +F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 F ext/jni/README.md ddcc6be0c0d65f1e2fd687de9f40d38c82630fd61f83cc9550773caa19dd8be1 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 01c6cf041d1b9937a97c7700006a532d3b11fd4991931e31ffa7a777b97fba11 -F ext/jni/src/c/sqlite3-jni.h 44bcb4eb3517c089f8f24f1546ea66b350d0661a4b0fa148425c9a41cabf487d +F ext/jni/src/c/sqlite3-jni.c 852c4812c9a3663d871cb334eaa60eb6fc22d67da47d4ff3868fdbfd6ebedb3a +F ext/jni/src/c/sqlite3-jni.h c5cb0348efe4e5f3d125a240e2437e8475de14a586c2f859e2acdcde4116244d F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -250,14 +250,15 @@ F ext/jni/src/org/sqlite/jni/Fts5Function.java 65cde7151e441fee012250a5e03277de7 F ext/jni/src/org/sqlite/jni/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab03a7aede43277b2c92859176d6bce3be9 F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 8110d4cfb20884e8ed241de7420c615b040a9f9c441d9cff06f34833399244a8 -F ext/jni/src/org/sqlite/jni/OutputPointer.java 464ea85c3eba673a7b575545f69fcd8aeb398477a26d155d88cee3e2459e7802 +F ext/jni/src/org/sqlite/jni/OutputPointer.java bb09fee5ad51d10e58075de000f8c1a3622a6c4b6a390ef134b6add1bfb32ca1 +F ext/jni/src/org/sqlite/jni/PreUpdateHook.java dec00a706b58c67989f0ff56c4f0a703821d25b45c62dd7fed1b462049f15c26 F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495380677e87daa29a1c57a0e2c06b0a131dc F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java f697cf2a81c4119f2baf0682af689686f0466f1dd83dba00885f5603e693fe16 F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2de5729a33cf2636160eb6893a4234c99669521a352bfffbf60410bd493ebece -F ext/jni/src/org/sqlite/jni/Tester1.java 4e17a30e9da15954ba71ef52beb5b347f312594a0facbaf86e1f29481c4dc65c +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java e99e073e3779d00e23842858276efac93c8b523193b77ff12469d12a0b6182ca +F ext/jni/src/org/sqlite/jni/Tester1.java 05ae085ed040bcc10b51cd12076a4151eda478f9773dc00a85d0cddd3dcc01f7 F ext/jni/src/org/sqlite/jni/TesterFts5.java de095e3b701fba0c56d7b8b2993dc22bcbaa9de8f992904a93729ad729a91576 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2093,8 +2094,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 6c92d884920e4ace54913fc60ceef6e43a4351f45a4cb3c4a0ed3d29d544a31b -R ebb24a95583279229c99fb88e45995e0 +P fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 +R 28457b0903a4397220c04d68facc73da U stephan -Z 0a740a88323f212cff509af7c6f7ae11 +Z d81ff8935be35c831285c9d98a32b81f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 23b760f68b..9a861544e3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 \ No newline at end of file +d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 \ No newline at end of file