mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Bind sqlite3_rollback_hook() to JNI.
FossilOrigin-Name: 5f8ee44098d74ac2b98e4dd43ad80d3b919528358b3f992b425af7fa6262dcee
This commit is contained in:
@ -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),
|
||||
|
@ -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
|
||||
|
@ -21,5 +21,5 @@ public interface CommitHook {
|
||||
Works as documented for the sqlite3_commit_hook() callback.
|
||||
Must not throw.
|
||||
*/
|
||||
int xCallback();
|
||||
int xCommitHook();
|
||||
}
|
||||
|
25
ext/jni/src/org/sqlite/jni/RollbackHook.java
Normal file
25
ext/jni/src/org/sqlite/jni/RollbackHook.java
Normal file
@ -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();
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -756,7 +756,7 @@ public class Tester1 {
|
||||
final ValueHolder<Integer> counter = new ValueHolder<>(0);
|
||||
final ValueHolder<Integer> 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<Integer> 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();
|
||||
|
Reference in New Issue
Block a user