Start adding JNI-side support for auto extensions, but hit a brick wall which requires slightly awkward semantics changes in the JNI bindings for sqlite3_open(_v2)() to resolve, so stash this #if'd out for the time being.

FossilOrigin-Name: 77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c
This commit is contained in:
stephan 2023-08-06 20:01:30 +00:00
parent 1a2563a950
commit af90dcf324
8 changed files with 146 additions and 26 deletions

View File

@ -73,11 +73,12 @@ Golden Rule: _Never_ Throw from Callbacks
------------------------------------------------------------------------
JNI bindings which accept client-defined functions _must never throw
exceptions_. There are _no exceptions_ to this rule. Exceptions are
reserved for higher-level bindings which are constructed to
specifically deal with them and ensure that they do not leak C-level
resources. Some of the JNI bindings are provided as Java functions
which expect this rule to always hold.
exceptions_ unless _very explicitly documented_ as being
throw-safe. Exceptions are generally reserved for higher-level
bindings which are constructed to specifically deal with them and
ensure that they do not leak C-level resources. Some of the JNI
bindings are provided as Java functions which expect this rule to
always hold.
UTF-8(-ish)
------------------------------------------------------------------------

View File

@ -393,6 +393,39 @@ static void NphCacheLine_clear(JNIEnv * const env, NphCacheLine * const p){
memset(p, 0, sizeof(NphCacheLine));
}
#define S3JNI_ENABLE_AUTOEXT 0
#if S3JNI_ENABLE_AUTOEXT
/*
Whether auto extensions are feasible here is currently unknown due
to...
1) JNIEnv/threading issues. A db instance is mapped to a specific
JNIEnv object but auto extensions may be added from any thread. In
such contexts, which JNIEnv do we use for the JNI APIs?
2) a chicken/egg problem involving the Java/C mapping of the db:
when auto extensions are run, the db has not yet been connected to
Java. If we do that during the auto-ext, sqlite3_open(_v2)() will not behave
properly because they have a different jobject and the API
guarantees the user that _that_ object is the one the API will bind
the native to.
If we change the open(_v2()) interfaces to use OutputPointer.sqlite3
instead of the client passing in an instance, we could work around
(2).
*/
typedef struct S3JniAutoExtension S3JniAutoExtension;
typedef void (*S3JniAutoExtension_xEntryPoint)(sqlite3*);
struct S3JniAutoExtension {
JNIEnv * env;
jobject jObj;
jmethodID midFunc;
S3JniAutoExtension_xEntryPoint xEntryPoint;
S3JniAutoExtension *pNext /* next linked-list entry */;
S3JniAutoExtension *pPrev /* previous linked-list entry */;
};
#endif
/** State for various hook callbacks. */
typedef struct JniHookState JniHookState;
struct JniHookState{
@ -476,6 +509,12 @@ static struct {
unsigned nInverse;
} udf;
} metrics;
#if S3JNI_ENABLE_AUTOEXT
struct {
S3JniAutoExtension *pHead;
int isRunning;
} autoExt;
#endif
} S3Global;
/**
@ -1066,6 +1105,22 @@ static PerDbStateJni * PerDbStateJni_for_db(JNIEnv * const env, jobject jDb,
return s;
}
#if 0
/**
An alternative form which searches for the PerDbStateJni instance for
pDb with no JNIEnv-specific info. This can be (but _should_ it be?)
called from the context of a separate JNIEnv than the one mapped
to in the returned object. Returns 0 if no match is found.
*/
FIXME_THREADING
static PerDbStateJni * PerDbStateJni_for_db2(sqlite3 *pDb){
PerDbStateJni * s = S3Global.perDb.aUsed;
for( ; pDb && s; s = s->pNext){
if(s->pDb == pDb) return s;
}
return 0;
}
#endif
/**
Requires that jCx be a Java-side sqlite3_context wrapper for pCx.
@ -1633,6 +1688,50 @@ WRAP_INT_SVALUE(1value_1numeric_1type, sqlite3_value_numeric_type)
WRAP_INT_SVALUE(1value_1subtype, sqlite3_value_subtype)
WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
#if S3JNI_ENABLE_AUTOEXT
/* auto-extension is very incomplete */
static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3, sv);
}
/*static*/ int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr,
const struct sqlite3_api_routines *pThunk){
S3JniAutoExtension const * pAX = S3Global.autoExt.pHead;
jobject jDb;
int rc;
JNIEnv * env = 0;
if(S3Global.autoExt.isRunning){
*pzErr = sqlite3_mprintf("Auto-extensions must not be triggered while "
"auto-extensions are running.");
return SQLITE_MISUSE;
}
if( S3Global.jvm->GetEnv(S3Global.jvm, (void **)&env, JNI_VERSION_1_8) ){
*pzErr = sqlite3_mprintf("Could not get current JNIEnv.");
return SQLITE_ERROR;
}
S3Global.autoExt.isRunning = 1;
jDb = new_sqlite3_wrapper( env, pDb );
EXCEPTION_IS_FATAL("Cannot create sqlite3 wrapper object.");
// Now we need PerDbStateJni_for_db(env, jDb, pDb, 1)
// and rewire sqlite3_open(_v2()) to use OutputPointer.sqlite3
// so that they can have this same jobject.
for( ; pAX; pAX = pAX->pNext ){
JNIEnv * const env = pAX->env
/* ^^^ is this correct, or must we use the JavaVM::GetEnv()'s env
instead? */;
rc = (*env)->CallVoidMethod(env, pAX->jObj, pAX->midFunc, jDb);
IFTHREW {
*pzErr = sqlite3_mprintf("auto-extension threw. TODO: extract error message.");
rc = SQLITE_ERROR;
break;
}
}
UNREF_L(jDb);
S3Global.autoExt.isRunning = 0;
return rc;
}
JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){}
#endif /* S3JNI_ENABLE_AUTOEXT */
JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt,
jint ndx, jbyteArray baData, jint nMax){
int rc;

View File

@ -1104,7 +1104,7 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
* Method: sqlite3_db_config
* Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/OutputPointer/Int32;)I
*/
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_Int32_2
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_00024Int32_2
(JNIEnv *, jclass, jobject, jint, jobject);
/*

View File

@ -14,7 +14,7 @@
package org.sqlite.jni;
/**
A wrapper for use with sqlite3_set_authorizer().
A callback for use with sqlite3_set_authorizer().
*/
public interface Authorizer {
/**

View File

@ -70,12 +70,12 @@ public final class Fts5ExtensionApi extends NativePointerHolder<Fts5ExtensionApi
public native int xRowCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int64 nRow);
public native long xRowid(@NotNull Fts5Context cx);
/* Note that this impl lacks the xDelete() callback
argument. Instead, if pAux has an xDestroy() method, it is called
if the FTS5 API finalizes the aux state (including if allocation
of storage for the auxdata fails). Any reference to pAux held by
the JNI layer will be relinquished regardless of whther pAux has
an xDestroy() method. */
/* Note that the JNI binding lacks the C version's xDelete()
callback argument. Instead, if pAux has an xDestroy() method, it
is called if the FTS5 API finalizes the aux state (including if
allocation of storage for the auxdata fails). Any reference to
pAux held by the JNI layer will be relinquished regardless of
whether pAux has an xDestroy() method. */
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte pText[],
@NotNull Fts5.xTokenizeCallback callback);

View File

@ -165,6 +165,26 @@ public final class SQLite3Jni {
// alphabetized. The SQLITE_... values. on the other hand, are
// grouped by category.
// Auto-extensions cannot currently work properly in our setup
// for reasons explained in sqlite3-jni.c.
//
// /**
// Functions almost as documented for the C API, with these
// exceptions:
//
// - The callback interface is more limited because of
// cross-language differences.
//
// - All of the auto-extension routines will fail without side
// effects if invoked from within the execution of an
// auto-extension.
//
// See the AutoExtension class docs for more information.
// */
// private static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
public static int sqlite3_bind_blob(@NotNull sqlite3_stmt stmt, int ndx,
@Nullable byte[] data){
return (null == data)
@ -911,7 +931,7 @@ public final class SQLite3Jni {
to hook in arbitrary C-side code during development and testing
of this library.
*/
public static native void sqlite3_do_something_for_developer();
static native void sqlite3_do_something_for_developer();
//////////////////////////////////////////////////////////////////////
// SQLITE_... constants follow...

View File

@ -1,5 +1,5 @@
C Doc\scleanups.
D 2023-08-06T15:01:38.351
C Start\sadding\sJNI-side\ssupport\sfor\sauto\sextensions,\sbut\shit\sa\sbrick\swall\swhich\srequires\sslightly\sawkward\ssemantics\schanges\sin\sthe\sJNI\sbindings\sfor\ssqlite3_open(_v2)()\sto\sresolve,\sso\sstash\sthis\s#if'd\sout\sfor\sthe\stime\sbeing.
D 2023-08-06T20:01:30.439
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -231,17 +231,17 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 61d9bbc179a49523a142928455b3297779b9c40f25783ecf1538279e426cbc99
F ext/jni/README.md 6ff7e1f4100dee980434a6ee37a199b653bceec62e233a6e2ccde6e7ae0c58bf
F ext/jni/src/c/sqlite3-jni.c ec7de3a37d7a62598179bc602c2f81f28434264e9d2d62d894c8c4eb41098291
F ext/jni/src/c/sqlite3-jni.h 58678453c1b6cccb8d728a60f59b2b0819226658376c0b36e025ce8b0fea75c3
F ext/jni/src/org/sqlite/jni/Authorizer.java 8dde03bbe50896d2f426240a4af4dcb6d98b655af84fe6ed86e637f5d5ac1fc8
F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d
F ext/jni/src/c/sqlite3-jni.c e335541e3eac0e337cc22f0b78a040e73c477d41128845ffe4ba8029fc077994
F ext/jni/src/c/sqlite3-jni.h 2ef601ab7cef00047ef0907e873f8f7bc4bfa6ee510b0435e070eb8ee7b6c6f0
F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
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/CollationNeeded.java ebc7cd96d46a70daa76016a308e80f70a3f21d3282787c8d139aa840fdcb1bd7
F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a
F ext/jni/src/org/sqlite/jni/Fts5.java 13844685231e8b4840a706db3bed84d5dfcf15be0ae7e809eac40420dba24901
F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890
F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 9798c6288097f4619ded680fe0961132a3f3d3cbffd7ce22096159f114f28c61
F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java c908e5fdf6f5d15e388144fcd8160a3f46c18dade749f1b747122d2d37f2e726
F ext/jni/src/org/sqlite/jni/Fts5Function.java 65cde7151e441fee012250a5e03277de7babcd11a0c308a832b7940574259bcc
F ext/jni/src/org/sqlite/jni/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab03a7aede43277b2c92859176d6bce3be9
F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060
@ -250,7 +250,7 @@ F ext/jni/src/org/sqlite/jni/OutputPointer.java fcece068415b804aa7843534addb3905
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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java eb49c9e5424e80ed1465c184086b3895155eee0828fc1992bfee24b741735638
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2ee2d3522ab6bec9337653233f5fb50619120cc5b12ce5deb59035ca2502cdcd
F ext/jni/src/org/sqlite/jni/Tester1.java 04c43f3ec93b362fc1c66430a3125067285259cd4fa5afccdb2fa66b691db8d0
F ext/jni/src/org/sqlite/jni/TesterFts5.java cf2d687baafffdeba219b77cf611fd47a0556248820ea794ae3e8259bfbdc5ee
F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
@ -2082,8 +2082,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 6119289da85ac0c83e2a7236d24bbfff22334d6cf1d852756dc658ad6a75dfec
R 48b7c494d85b3192affc08b2af78551a
P 5f56b007704f2aad4cbc6f0ccd1e1f1c974865971f99451352714ee7e077c284
R 55ff516d4ad7789048494bf17442d9b9
U stephan
Z a195dbc9574f42748d3f2a6c1a7b8425
Z 7da38e0ddc75e18237df446c4596f88f
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
5f56b007704f2aad4cbc6f0ccd1e1f1c974865971f99451352714ee7e077c284
77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c