1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Bind sqlite3_commit_hook() to JNI.

FossilOrigin-Name: c687297fcae082cbd7f9258c43f3841fd34904d8b62b0adf3cd61fcddeee483d
This commit is contained in:
stephan
2023-07-30 05:50:34 +00:00
parent ca379859c9
commit f52de0fdaf
7 changed files with 159 additions and 20 deletions

View File

@ -166,7 +166,7 @@
#define PtrGet_sqlite3_context(OBJ) getNativePointer(env,OBJ,ClassNames.sqlite3_context)
/* Helpers for Java value reference management. */
#define REF_G(VAR) (*env)->NewGlobalRef(env, VAR)
/*#define REF_L(VAR) (*env)->NewLocalRef(env, VAR)*/
#define REF_L(VAR) (*env)->NewLocalRef(env, VAR)
#define UNREF_G(VAR) if(VAR) (*env)->DeleteGlobalRef(env, (VAR))
#define UNREF_L(VAR) if(VAR) (*env)->DeleteLocalRef(env, (VAR))
@ -1512,6 +1512,65 @@ 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;
JNIEnv * const env = ps->env;
int rc = (int)(*env)->CallIntMethod(env, ps->commitHook.jObj,
ps->commitHook.midCallback);
IFTHREW{
EXCEPTION_CLEAR;
rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
"sqlite3_commit_hook() callback threw.");
}
return rc;
}
JDECL(jobject,1commit_1hook)(JENV_JSELF,jobject jDb, jobject jCommitHook){
sqlite3 * const pDb = PtrGet_sqlite3(jDb);
PerDbStateJni * ps = PerDbStateJni_for_db(env, pDb, 1);
jclass klazz;
jobject pOld = 0;
jmethodID xCallback;
if(!ps){
s3jni_db_error(pDb, SQLITE_NOMEM, 0);
return 0;
}
pOld = ps->commitHook.jObj;
if(pOld && jCommitHook &&
(*env)->IsSameObject(env, pOld, jCommitHook)){
return pOld;
}
if( !jCommitHook ){
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);
return pOld;
}
klazz = (*env)->GetObjectClass(env, jCommitHook);
xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I");
IFTHREW {
EXCEPTION_CLEAR;
s3jni_db_error(pDb, SQLITE_ERROR,
"Cannot not find matching xCallback() on "
"CommitHook object.");
}else{
ps->commitHook.midCallback = xCallback;
ps->commitHook.jObj = REF_G(jCommitHook);
sqlite3_commit_hook(pDb, s3jni_commit_hook_impl, ps);
if(pOld){
jobject tmp = REF_L(pOld);
UNREF_G(pOld);
pOld = tmp;
}
}
return pOld;
}
JDECL(jstring,1compileoption_1get)(JENV_JSELF, jint n){
return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) );
}
@ -1744,6 +1803,7 @@ JDECL(void,1progress_1handler)(JENV_JSELF,jobject jDb, jint n, jobject jProgress
sqlite3 * const pDb = PtrGet_sqlite3(jDb);
PerDbStateJni * ps = PerDbStateJni_for_db(env, pDb, 1);
jclass klazz;
jmethodID xCallback;
if( n<1 || !jProgress ){
if(ps){
UNREF_G(ps->progress.jObj);
@ -1757,13 +1817,15 @@ JDECL(void,1progress_1handler)(JENV_JSELF,jobject jDb, jint n, jobject jProgress
return;
}
klazz = (*env)->GetObjectClass(env, jProgress);
ps->progress.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I");
xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I");
IFTHREW {
EXCEPTION_CLEAR;
s3jni_db_error(pDb, SQLITE_ERROR,
"Cannot not find matching xCallback() on "
"ProgressHandler object.");
}else{
UNREF_G(ps->progress.jObj);
ps->progress.midCallback = xCallback;
ps->progress.jObj = REF_G(jProgress);
sqlite3_progress_handler(pDb, (int)n, s3jni_progress_handler_impl, ps);
}

View File

@ -1027,6 +1027,14 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1value
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1context_1db_1handle
(JNIEnv *, jclass, jobject);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_commit_hook
* Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/CommitHook;)Lorg/sqlite/jni/CommitHook;
*/
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1commit_1hook
(JNIEnv *, jclass, jobject, jobject);
/*
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_compileoption_get

View 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_commit_hook().
*/
public interface CommitHook {
/**
Works as documented for the sqlite3_commit_hook() callback.
Must not throw.
*/
int xCallback();
}

View File

@ -243,7 +243,7 @@ public final class SQLite3Jni {
public static native sqlite3 sqlite3_context_db_handle(@NotNull sqlite3_context cx);
//TODO? void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
public static native CommitHook sqlite3_commit_hook(@NotNull sqlite3 db, @Nullable CommitHook hook);
public static native String sqlite3_compileoption_get(int n);
@ -287,8 +287,6 @@ public final class SQLite3Jni {
public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db);
//TODO? void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
public static native String sqlite3_libversion();
public static native int sqlite3_libversion_number();
@ -397,6 +395,8 @@ public final class SQLite3Jni {
public static native void sqlite3_progress_handler(@NotNull sqlite3 db, int n,
@Nullable ProgressHandler h);
//TODO??? void *sqlite3_preupdate_hook(...) and friends
public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt);
public static native void sqlite3_result_double(@NotNull sqlite3_context cx, double v);
@ -631,11 +631,7 @@ public final class SQLite3Jni {
sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16BE);
}
/**
public static void sqlite3_result_text64(@NotNull sqlite3_context cx,
@Nullable byte[] text){
sqlite3_result_text64(cx, text, null==text ? 0 : text.length, SQLITE_UTF8);
}**/
//TODO void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
public static native void sqlite3_set_last_insert_rowid(@NotNull sqlite3 db, long rowid);
@ -664,6 +660,8 @@ public final class SQLite3Jni {
public static native int sqlite3_trace_v2(@NotNull sqlite3 db, int traceMask,
@Nullable Tracer tracer);
//TODO void *sqlite3_update_hook(sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void*);
public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v);
public static native int sqlite3_value_bytes(@NotNull sqlite3_value v);

View File

@ -741,6 +741,50 @@ public class Tester1 {
sqlite3_close_v2(db);
}
private static void testCommitHook(){
final sqlite3 db = createNewDb();
final ValueHolder<Integer> counter = new ValueHolder<>(0);
final CommitHook theHook = new CommitHook(){
public int xCallback(){
++counter.value;
return 0;
}
};
CommitHook oldHook = sqlite3_commit_hook(db, theHook);
affirm( null == oldHook );
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
affirm( 2 == counter.value );
execSql(db, "BEGIN; SELECT 1; SELECT 2; COMMIT;");
affirm( 2 == counter.value /* NOT invoked if no changes are made */ );
execSql(db, "BEGIN; update t set a='d' where a='c'; COMMIT;");
affirm( 3 == counter.value );
oldHook = sqlite3_commit_hook(db, theHook);
affirm( theHook == oldHook );
execSql(db, "BEGIN; update t set a='e' where a='d'; COMMIT;");
affirm( 4 == counter.value );
oldHook = sqlite3_commit_hook(db, null);
affirm( theHook == oldHook );
execSql(db, "BEGIN; update t set a='f' where a='e'; COMMIT;");
affirm( 4 == counter.value );
oldHook = sqlite3_commit_hook(db, null);
affirm( null == oldHook );
execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;");
affirm( 4 == counter.value );
final CommitHook newHook = new CommitHook(){
public int xCallback(){return 0;}
};
oldHook = sqlite3_commit_hook(db, newHook);
affirm( null == oldHook );
execSql(db, "BEGIN; update t set a='h' where a='g'; COMMIT;");
affirm( 4 == counter.value );
oldHook = sqlite3_commit_hook(db, theHook);
affirm( newHook == oldHook );
execSql(db, "BEGIN; update t set a='i' where a='h'; COMMIT;");
affirm( 5 == counter.value );
sqlite3_close_v2(db);
}
private static void testSleep(){
out("Sleeping briefly... ");
sqlite3_sleep(600);
@ -770,6 +814,7 @@ public class Tester1 {
testTrace();
testBusy();
testProgress();
testCommitHook();
//testSleep();
if(liArgs.indexOf("-v")>0){
listBoundMethods();