Bind sqlite3_preupdate_hook() and friends to JNI.
FossilOrigin-Name: d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4
This commit is contained in:
parent
4e97ab4296
commit
bea9ed0f1f
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
29
ext/jni/src/org/sqlite/jni/PreUpdateHook.java
Normal file
29
ext/jni/src/org/sqlite/jni/PreUpdateHook.java
Normal file
@ -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 );
|
||||
}
|
@ -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
|
||||
);
|
||||
|
@ -997,6 +997,7 @@ public class Tester1 implements Runnable {
|
||||
final ValueHolder<Integer> 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<Integer> counter = new ValueHolder<>(0);
|
||||
final ValueHolder<Integer> 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<Integer> counter = new ValueHolder<>(0);
|
||||
|
23
manifest
23
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.
|
||||
|
@ -1 +1 @@
|
||||
fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1
|
||||
d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4
|
Loading…
Reference in New Issue
Block a user