diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 9d923a5393..4daa1d104c 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -340,6 +340,12 @@ typedef struct { jmethodID jmidxCallback /* klazz's xCallback method */; } BusyHandlerJni; +typedef struct JniHookState JniHookState; +struct JniHookState{ + jobject jObj; + jmethodID midCallback; +}; + /** Per-(sqlite3*) state for bindings which do not have their own @@ -359,22 +365,10 @@ struct PerDbStateJni { sqlite3 * pDb; PerDbStateJni * pNext; PerDbStateJni * pPrev; - struct { - jobject jObj; - jmethodID midCallback; - } trace; - struct { - jobject jObj; - jmethodID midCallback; - } progress; - struct { - jobject jObj; - jmethodID midCallback; - } commitHook; - struct { - jobject jObj; - jmethodID midCallback; - } rollbackHook; + JniHookState trace; + JniHookState progress; + JniHookState commitHook; + JniHookState rollbackHook; BusyHandlerJni busyHandler; }; @@ -1512,55 +1506,71 @@ JDECL(jobject,1column_1value)(JENV_JSELF, jobject jpStmt, return new_sqlite3_value_wrapper(env, sv); } -static int s3jni_commit_hook_impl(void *pP){ - PerDbStateJni * const ps = (PerDbStateJni *)pP; + +static int s3jni_commit_rollback_hook_impl(int isCommit, PerDbStateJni * const ps){ JNIEnv * const env = ps->env; - int rc = (int)(*env)->CallIntMethod(env, ps->commitHook.jObj, - ps->commitHook.midCallback); + int rc = isCommit + ? (int)(*env)->CallIntMethod(env, ps->commitHook.jObj, + ps->commitHook.midCallback) + : (int)((*env)->CallVoidMethod(env, ps->rollbackHook.jObj, + ps->rollbackHook.midCallback), 0); IFTHREW{ EXCEPTION_CLEAR; - rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, - "sqlite3_commit_hook() callback threw."); + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); } return rc; } -JDECL(jobject,1commit_1hook)(JENV_JSELF,jobject jDb, jobject jCommitHook){ +static int s3jni_commit_hook_impl(void *pP){ + return s3jni_commit_rollback_hook_impl(1, pP); +} + +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 *env,jobject jDb, + jobject jHook){ sqlite3 * const pDb = PtrGet_sqlite3(jDb); - PerDbStateJni * ps = PerDbStateJni_for_db(env, pDb, 1); + PerDbStateJni * const ps = PerDbStateJni_for_db(env, pDb, 1); jclass klazz; jobject pOld = 0; jmethodID xCallback; + JniHookState * const pHook = isCommit ? &ps->commitHook : &ps->rollbackHook; if(!ps){ s3jni_db_error(pDb, SQLITE_NOMEM, 0); return 0; } - pOld = ps->commitHook.jObj; - if(pOld && jCommitHook && - (*env)->IsSameObject(env, pOld, jCommitHook)){ + pOld = pHook->jObj; + if(pOld && jHook && + (*env)->IsSameObject(env, pOld, jHook)){ return pOld; } - if( !jCommitHook ){ + if( !jHook ){ if(pOld){ jobject tmp = REF_L(pOld); UNREF_G(pOld); pOld = tmp; } - memset(&ps->commitHook, 0, sizeof(ps->commitHook)); - sqlite3_commit_hook(pDb, 0, 0); + memset(pHook, 0, sizeof(JniHookState)); + if( isCommit ) sqlite3_commit_hook(pDb, 0, 0); + else sqlite3_rollback_hook(pDb, 0, 0); return pOld; } - klazz = (*env)->GetObjectClass(env, jCommitHook); - xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I"); + klazz = (*env)->GetObjectClass(env, jHook); + xCallback = (*env)->GetMethodID(env, klazz, + isCommit ? "xCommitHook" : "xRollbackHook", + isCommit ? "()I" : "()V"); IFTHREW { EXCEPTION_CLEAR; s3jni_db_error(pDb, SQLITE_ERROR, - "Cannot not find matching xCallback() on " - "CommitHook object."); + "Cannot not find matching callback on " + "hook object."); }else{ - ps->commitHook.midCallback = xCallback; - ps->commitHook.jObj = REF_G(jCommitHook); - sqlite3_commit_hook(pDb, s3jni_commit_hook_impl, ps); + pHook->midCallback = xCallback; + pHook->jObj = REF_G(jHook); + if( isCommit ) sqlite3_commit_hook(pDb, s3jni_commit_hook_impl, ps); + else sqlite3_rollback_hook(pDb, s3jni_rollback_hook_impl, ps); if(pOld){ jobject tmp = REF_L(pOld); UNREF_G(pOld); @@ -1570,6 +1580,10 @@ JDECL(jobject,1commit_1hook)(JENV_JSELF,jobject jDb, jobject jCommitHook){ return pOld; } +JDECL(jobject,1commit_1hook)(JENV_JSELF,jobject jDb, jobject jHook){ + return s3jni_commit_rollback_hook(1, env, jDb, jHook); +} + JDECL(jstring,1compileoption_1get)(JENV_JSELF, jint n){ return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ); @@ -2007,6 +2021,9 @@ JDECL(jint,1result_1zeroblob64)(JENV_JSELF, jobject jpCx, jlong v){ return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v); } +JDECL(jobject,1rollback_1hook)(JENV_JSELF,jobject jDb, jobject jHook){ + return s3jni_commit_rollback_hook(0, env, jDb, jHook); +} JDECL(void,1set_1last_1insert_1rowid)(JENV_JSELF, jobject jpDb, jlong rowId){ sqlite3_set_last_insert_rowid(PtrGet_sqlite3_context(jpDb), diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 11f0f99e91..f9fa4a92b1 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1347,6 +1347,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64 (JNIEnv *, jclass, jobject, jbyteArray, jlong, jint); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_rollback_hook + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHook;)Lorg/sqlite/jni/RollbackHook; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook + (JNIEnv *, jclass, jobject, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_set_last_insert_rowid diff --git a/ext/jni/src/org/sqlite/jni/CommitHook.java b/ext/jni/src/org/sqlite/jni/CommitHook.java index c72d2df9b3..eaa75a0040 100644 --- a/ext/jni/src/org/sqlite/jni/CommitHook.java +++ b/ext/jni/src/org/sqlite/jni/CommitHook.java @@ -21,5 +21,5 @@ public interface CommitHook { Works as documented for the sqlite3_commit_hook() callback. Must not throw. */ - int xCallback(); + int xCommitHook(); } diff --git a/ext/jni/src/org/sqlite/jni/RollbackHook.java b/ext/jni/src/org/sqlite/jni/RollbackHook.java new file mode 100644 index 0000000000..4ce3cb93e3 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/RollbackHook.java @@ -0,0 +1,25 @@ +/* +** 2023-07-22 +** +** 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; + +/** + Callback proxy for use with sqlite3_rollback_hook(). +*/ +public interface RollbackHook { + /** + Works as documented for the sqlite3_rollback_hook() callback. + Must not throw. + */ + void xRollbackHook(); +} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 7bcfb70e88..36b824a808 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -631,7 +631,8 @@ public final class SQLite3Jni { sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16BE); } - //TODO void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); + public static native RollbackHook sqlite3_rollback_hook(@NotNull sqlite3 db, + @Nullable RollbackHook hook); public static native void sqlite3_set_last_insert_rowid(@NotNull sqlite3 db, long rowid); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 2c3c6bae8e..67e3bf639a 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -756,7 +756,7 @@ public class Tester1 { final ValueHolder counter = new ValueHolder<>(0); final ValueHolder hookResult = new ValueHolder<>(0); final CommitHook theHook = new CommitHook(){ - public int xCallback(){ + public int xCommitHook(){ ++counter.value; return hookResult.value; } @@ -783,7 +783,7 @@ public class Tester1 { affirm( 4 == counter.value ); final CommitHook newHook = new CommitHook(){ - public int xCallback(){return 0;} + public int xCommitHook(){return 0;} }; oldHook = sqlite3_commit_hook(db, newHook); affirm( null == oldHook ); @@ -800,6 +800,38 @@ public class Tester1 { sqlite3_close_v2(db); } + private static void testRollbackHook(){ + final sqlite3 db = createNewDb(); + final ValueHolder counter = new ValueHolder<>(0); + final RollbackHook theHook = new RollbackHook(){ + public void xRollbackHook(){ + ++counter.value; + } + }; + RollbackHook oldHook = sqlite3_rollback_hook(db, theHook); + affirm( null == oldHook ); + execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); + affirm( 0 == counter.value ); + execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;"); + affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ ); + + final RollbackHook newHook = new RollbackHook(){ + public void xRollbackHook(){return;} + }; + oldHook = sqlite3_rollback_hook(db, newHook); + affirm( theHook == oldHook ); + execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 1 == counter.value ); + oldHook = sqlite3_rollback_hook(db, theHook); + affirm( newHook == oldHook ); + execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 2 == counter.value ); + int rc = execSql(db, false, "BEGIN; SELECT 1; ROLLBACK;"); + affirm( 0 == rc ); + affirm( 3 == counter.value ); + sqlite3_close_v2(db); + } + private static void testSleep(){ out("Sleeping briefly... "); sqlite3_sleep(600); @@ -830,6 +862,7 @@ public class Tester1 { testBusy(); testProgress(); testCommitHook(); + testRollbackHook(); //testSleep(); if(liArgs.indexOf("-v")>0){ listBoundMethods(); diff --git a/manifest b/manifest index 974a60d6e5..d9877eabbf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\scommit\shook\sfailure\sto\sJNI\stests. -D 2023-07-30T06:00:53.315 +C Bind\ssqlite3_rollback_hook()\sto\sJNI. +D 2023-07-30T06:44:21.370 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,17 +232,18 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d F ext/jni/README.md b62f1f0e67a6295e9a0283d4dffad6ed30ec50352aa36b3bd323a26593606c0f -F ext/jni/src/c/sqlite3-jni.c bbc85b4bfe8ea05be23771790824f179b17106a806b2507a6b09c2710483f758 -F ext/jni/src/c/sqlite3-jni.h 2d6ec0b98c94fa111b101aa25c94dbc86aa8776e96048c27cf9d646e9aa2f68f +F ext/jni/src/c/sqlite3-jni.c 84c082bd07d130b3db4121c32429a9ea1e57c109366b78909b3a7bba4b9afd68 +F ext/jni/src/c/sqlite3-jni.h 58f1f1a363184348c56dd6af47a2311640dde61ab0d9f4ba51fadf6ae5ba88c3 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1 -F ext/jni/src/org/sqlite/jni/CommitHook.java a2e60ef94c56b9c5058130eaeadf98e6121d28f1828000e5e998397a0c3dde74 +F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 70dc7bc41f80352ff3d4331e2e24f45fcd23353b3641e2f68a81bd8262215861 F ext/jni/src/org/sqlite/jni/OutputPointer.java 08a752b58a33696c5eaf0eb9361a0966b188dec40f4a3613eb133123951f6c5f F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc +F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 663a4e479ec65bfbf893586439e12d30b8237898064a22ab64f5658b57315f37 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java ca239543968a4fd229322b92925a2754358964be6b1fc4099ff548372db2009e -F ext/jni/src/org/sqlite/jni/Tester1.java 76bca3c7a37019d2631aa224d23edb814c9be322db0529b2fd7cea5d52a202d6 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1e0ec8a4a5f95b0f595666f9b92246f3394f31f78f62503ed1d1ab879a695101 +F ext/jni/src/org/sqlite/jni/Tester1.java 7d65095d4d4e683f937de57f9340e7eda966a9bd0adba945b86b76570a12c3a7 F ext/jni/src/org/sqlite/jni/Tracer.java c2fe1eba4a76581b93b375a7b95ab1919e5ae60accfb06d6beb067b033e9bae1 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/sqlite3.java c7d0500c7269882243aafb41425928d094b2fcbdbc2fd1caffc276871cd3fae3 @@ -2068,8 +2069,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 c687297fcae082cbd7f9258c43f3841fd34904d8b62b0adf3cd61fcddeee483d -R f495472e9e17ebb6a754de3bf1538a92 +P be4459b495cc2555e4d8ca24dd56c3da1036d25af7cc077bbc3d93c2a3e8c40b +R 6da75ca1ede9a6d1b13c3955b22a3d72 U stephan -Z cadc90f478a05b2b1a81a1b897b5c5ad +Z 04a18495036c22c78ae28b1e24549558 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ff969cfbd7..5d2b1e0838 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -be4459b495cc2555e4d8ca24dd56c3da1036d25af7cc077bbc3d93c2a3e8c40b \ No newline at end of file +5f8ee44098d74ac2b98e4dd43ad80d3b919528358b3f992b425af7fa6262dcee \ No newline at end of file