aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/jni/GNUmakefile3
-rw-r--r--ext/jni/src/c/sqlite3-jni.c188
-rw-r--r--ext/jni/src/org/sqlite/jni/SQLite3Jni.java124
-rw-r--r--ext/jni/src/org/sqlite/jni/package-info.java6
-rw-r--r--manifest18
-rw-r--r--manifest.uuid2
6 files changed, 228 insertions, 113 deletions
diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile
index 351851852..44a0d7401 100644
--- a/ext/jni/GNUmakefile
+++ b/ext/jni/GNUmakefile
@@ -329,7 +329,8 @@ dir.doc := $(dir.jni)/javadoc
doc.index := $(dir.doc)/index.html
$(doc.index): $(JAVA_FILES.main) $(MAKEFILE)
@if [ -d $(dir.doc) ]; then rm -fr $(dir.doc)/*; fi
- $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet -subpackages org.sqlite.jni
+ $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet \
+ -subpackages org.sqlite.jni -exclude org.sqlite.jni.tester
@echo "javadoc output is in $@"
.PHONY: doc javadoc docserve
diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c
index 471cc1503..00138ba40 100644
--- a/ext/jni/src/c/sqlite3-jni.c
+++ b/ext/jni/src/c/sqlite3-jni.c
@@ -565,8 +565,9 @@ struct S3JniGlobalType {
struct {
S3JniEnv * aHead /* Linked list of in-use instances */;
S3JniEnv * aFree /* Linked list of free instances */;
- sqlite3_mutex * mutex /* mutex for aHead and aFree as well for
- first-time inits of nph[] entries. */;
+ sqlite3_mutex * mutex /* mutex for aHead and aFree, first-time
+ inits of nph[] entries, and
+ NativePointerHolder_get/set(). */;
void const * locker /* env mutex is held on this object's behalf.
Used only for sanity checking. */;
} envCache;
@@ -686,7 +687,7 @@ static S3JniGlobalType S3JniGlobal = {};
** argument is a Java sqlite3 object, as this operation only has void
** pointers to work with.
*/
-#define PtrGet_T(T,OBJ) NativePointerHolder_get(env, OBJ, &S3NphRefs.T)
+#define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3NphRefs.T)
#define PtrGet_sqlite3(OBJ) PtrGet_T(sqlite3, OBJ)
#define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ)
#define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ)
@@ -821,7 +822,7 @@ static JNIEnv * s3jni_env(void){
** insofar as possible. Calls (*env)->FatalError() if allocation of an
** entry fails. That's hypothetically possible but "shouldn't happen."
*/
-static S3JniEnv * S3JniEnv_get(JNIEnv * const env){
+static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
struct S3JniEnv * row;
S3JniMutex_Env_enter;
row = SJG.envCache.aHead;
@@ -852,6 +853,8 @@ static S3JniEnv * S3JniEnv_get(JNIEnv * const env){
return row;
}
+#define S3JniEnv_get() S3JniEnv__get(env)
+
/*
** This function is NOT part of the sqlite3 public API. It is strictly
** for use by the sqlite project's own Java/JNI bindings.
@@ -1162,12 +1165,13 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
}
/*
-** Clears s's state and moves it to the free-list. Requires that that the
-** caller has locked S3JniGlobal.perDb.mutex.
+** Clears s's state and moves it to the free-list. Requires that
+** S3JniGlobal.perDb.mutex is locked.
*/
-static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){
+static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
+ assert( s );
if( s ){
- S3JniMutex_S3JniDb_enter;
+ S3JniMutex_S3JniDb_assertLocker;
assert(s->pPrev != s);
assert(s->pNext != s);
assert(s->pPrev ? (s->pPrev!=s->pNext) : 1);
@@ -1181,10 +1185,16 @@ static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){
s->pNext = SJG.perDb.aFree;
if(s->pNext) s->pNext->pPrev = s;
SJG.perDb.aFree = s;
- S3JniMutex_S3JniDb_leave;
}
}
+static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){
+ S3JniMutex_S3JniDb_enter;
+ S3JniDb__set_aside_unlocked(env, s);
+ S3JniMutex_S3JniDb_leave;
+}
+#define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
+
/*
** Uncache any state for the given JNIEnv, clearing all Java
** references the cache owns. Returns true if env was cached and false
@@ -1220,16 +1230,20 @@ static int S3JniEnv_uncache(JNIEnv * const env){
/*
** Searches the NativePointerHolder cache for the given combination of
-** args. It returns a cache entry with its klazz member set.
+** args. It returns a cache entry with its klazz member set. This is
+** an O(1) operation except on the first call for a given pRef, during
+** which pRef->klazz and pRef->pRef are initialized thread-safely. In
+** the latter case it's still effectively O(1), but with a much longer
+** 1.
**
** It is up to the caller to populate the other members of the
-** returned object if needed, taking care to lock the population with
-** S3JniMutex_Nph_enter/LEAVE.
+** returned object if needed, taking care to lock the modification
+** with S3JniMutex_Nph_enter/leave.
**
** This simple cache catches >99% of searches in the current
** (2023-07-31) tests.
*/
-static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef){
+static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3NphRef const* pRef){
/**
According to:
@@ -1261,13 +1275,15 @@ static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef)
return pNC;
}
+#define S3JniGlobal_nph(PREF) S3JniGlobal__nph(env, PREF)
+
/*
** Returns the ID of the "nativePointer" field from the given
** NativePointerHolder<T> class.
*/
static jfieldID NativePointerHolder_field(JNIEnv * const env,
S3NphRef const* pRef){
- S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
if( !pNC->fidValue ){
S3JniMutex_Nph_enter;
if( !pNC->fidValue ){
@@ -1286,24 +1302,31 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env,
** zClassName must be a static string so we can use its address
** as a cache key.
*/
-static void NativePointerHolder_set(JNIEnv * env, S3NphRef const* pRef,
+static void NativePointerHolder__set(JNIEnv * env, S3NphRef const* pRef,
jobject ppOut, const void * p){
- (*env)->SetLongField(env, ppOut, NativePointerHolder_field(env, pRef),
- (jlong)p);
+ jfieldID const fid = NativePointerHolder_field(env, pRef);
+ S3JniMutex_Nph_enter;
+ (*env)->SetLongField(env, ppOut, fid, (jlong)p);
+ S3JniMutex_Nph_leave;
S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer.");
}
+#define NativePointerHolder_set(PREF,PPOUT,P) \
+ NativePointerHolder__set(env, PREF, PPOUT, P)
+
/*
** Fetches a native ptr value from NativePointerHolder object ppOut.
** zClassName must be a static string so we can use its address as a
** cache key. This is a no-op if pObj is NULL.
*/
-static void * NativePointerHolder_get(JNIEnv * env, jobject pObj,
- S3NphRef const* pRef){
+static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
+ S3NphRef const* pRef){
if( pObj ){
- void * const rv = (void*)(*env)->GetLongField(
- env, pObj, NativePointerHolder_field(env, pRef)
- );
+ jfieldID const fid = NativePointerHolder_field(env, pRef);
+ void * rv;
+ S3JniMutex_Nph_enter;
+ rv = (void*)(*env)->GetLongField(env, pObj, fid);
+ S3JniMutex_Nph_leave;
S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer.");
return rv;
}else{
@@ -1311,6 +1334,9 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj,
}
}
+#define NativePointerHolder_get(JOBJ,NPHREF) \
+ NativePointerHolder__get(env, (JOBJ), (NPHREF))
+
/*
** Extracts the new S3JniDb instance from the free-list, or allocates
** one if needed, associats it with pDb, and returns. Returns NULL on
@@ -1351,6 +1377,13 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
return rv;
}
+/* Short-lived code consolidator. */
+#define S3JniDb_search \
+ s = SJG.perDb.aHead; \
+ for( ; pDb && s; s = s->pNext){ \
+ if( s->pDb == pDb ) break; \
+ }
+
/*
** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3
** object, or NULL if jDb is NULL, no pointer can be extracted
@@ -1361,20 +1394,28 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
S3JniDb * s = 0;
sqlite3 * pDb = 0;
+
S3JniMutex_S3JniDb_enter;
- if( jDb ){
- pDb = PtrGet_sqlite3(jDb);
- }
- s = SJG.perDb.aHead;
- for( ; pDb && s; s = s->pNext){
- if( s->pDb == pDb ){
- break;
- }
- }
+ if( jDb ) pDb = PtrGet_sqlite3(jDb);
+ S3JniDb_search;
S3JniMutex_S3JniDb_leave;
return s;
}
+/* An experiment */
+//#define CLOSE_DB_LOCKED
+#if defined(CLOSE_DB_LOCKED)
+static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
+ S3JniDb * s = 0;
+ sqlite3 * pDb = 0;
+
+ S3JniMutex_S3JniDb_assertLocker;
+ if( jDb ) pDb = PtrGet_sqlite3(jDb);
+ S3JniDb_search;
+ return s;
+}
+#endif
+
/*
** Returns the S3JniDb object for the sqlite3 object, or NULL if pDb
** is NULL, or no matching entry
@@ -1384,13 +1425,9 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
*/
static S3JniDb * S3JniDb__from_c(JNIEnv * const env, sqlite3 *pDb){
S3JniDb * s = 0;
+
S3JniMutex_S3JniDb_enter;
- s = SJG.perDb.aHead;
- for( ; pDb && s; s = s->pNext){
- if( s->pDb == pDb ){
- break;
- }
- }
+ S3JniDb_search;
S3JniMutex_S3JniDb_leave;
return s;
}
@@ -1442,7 +1479,9 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
** misbehave.
*/
static jfieldID OutputPointer_field(JNIEnv * const env, S3NphRef const * pRef){
- S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
+
+ assert( pNC->klazz );
if( !pNC->fidValue ){
S3JniMutex_Nph_enter;
if( !pNC->fidValue ){
@@ -1628,7 +1667,7 @@ static void ResultJavaValue_finalizer(void *v){
static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef,
const void * pNative){
jobject rv = 0;
- S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef);
+ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
if( !pNC->midCtor ){
S3JniMutex_Nph_enter;
if( !pNC->midCtor ){
@@ -1640,7 +1679,7 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const
rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor);
S3JniExceptionIsFatal("No-arg constructor threw.");
s3jni_oom_check(rv);
- if( rv ) NativePointerHolder_set(env, pRef, rv, pNative);
+ if( rv ) NativePointerHolder_set(pRef, rv, pNative);
return rv;
}
@@ -1778,7 +1817,7 @@ static int udf_args(JNIEnv *env,
if( !jcx ) goto error_oom;
ja = (*env)->NewObjectArray(
env, argc, SJG.g.cObj
- /* S3JniGlobal_nph(env,&S3NphRefs.sqlite3_value)->klazz would be
+ /* S3JniGlobal_nph(&S3NphRefs.sqlite3_value)->klazz would be
more correct, but it unpredictably triggers an assert in the
JVM. */, NULL);
s3jni_oom_check( ja );
@@ -2056,7 +2095,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
if( 0==SJG.autoExt.nExt ) return 0;
env = s3jni_env();
- jc = S3JniEnv_get(env);
+ jc = S3JniEnv_get();
ps = jc->pdbOpening;
if( !ps ){
*pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in "
@@ -2067,7 +2106,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
assert( !ps->pDb && "it's still being opened" );
assert( ps->jDb );
ps->pDb = pDb;
- NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb)
+ NativePointerHolder_set(&S3NphRefs.sqlite3, ps->jDb, pDb)
/* As of here, the Java/C connection is complete */;
for( i = 0; go && 0==rc; ++i ){
S3JniAutoExtension ax = {0,0}
@@ -2373,6 +2412,8 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
/* Wrapper for sqlite3_close(_v2)(). */
static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
int rc = 0;
+
+#ifndef CLOSE_DB_LOCKED
S3JniDb * const ps = S3JniDb_from_java(jDb);
assert(version == 1 || version == 2);
if( ps ){
@@ -2380,11 +2421,39 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
? (jint)sqlite3_close(ps->pDb)
: (jint)sqlite3_close_v2(ps->pDb);
if( 0==rc ){
- S3JniDb_set_aside(env, ps)
+ S3JniDb_set_aside(ps)
/* MUST come after close() because of ps->trace. */;
- NativePointerHolder_set(env, &S3NphRefs.sqlite3, jDb, 0);
+ NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0);
}
}
+#else
+ /* This impl leads to an assertion in sqlite3_close[_v2]()
+
+ pthreadMutexEnter: Assertion `p->id==SQLITE_MUTEX_RECURSIVE
+ || pthreadMutexNotheld(p)' failed.
+
+ For reasons not yet fully understood.
+ */
+ assert(version == 1 || version == 2);
+ if( 0!=jDb ){
+ S3JniDb * ps;
+ S3JniMutex_S3JniDb_enter;
+ ps = S3JniDb__from_java_unlocked(env, jDb);
+ if( ps && ps->pDb ){
+ rc = 1==version
+ ? (jint)sqlite3_close(ps->pDb)
+ : (jint)sqlite3_close_v2(ps->pDb);
+ if( 0==rc ){
+ S3JniDb__set_aside_unlocked(env,ps)
+ /* MUST come after close() because of ps->trace. */;
+ NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0);
+ }
+ }else{
+ /* ps is from S3Global.perDb.aFree. */
+ }
+ S3JniMutex_S3JniDb_leave;
+ }
+#endif
return (jint)rc;
}
@@ -3031,7 +3100,7 @@ S3JniApi(sqlite3_finalize(),jint,1finalize)(
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
if( pStmt ){
rc = sqlite3_finalize(pStmt);
- NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jpStmt, 0);
+ NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jpStmt, 0);
}
return rc;
}
@@ -3086,7 +3155,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc,
S3JniDb ** ps){
int rc = 0;
jobject jDb = 0;
- *jc = S3JniEnv_get(env);
+ *jc = S3JniEnv_get();
if( !*jc ){
rc = SQLITE_NOMEM;
goto end;
@@ -3136,14 +3205,14 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc,
assert(ps->jDb);
if( 0==ps->pDb ){
ps->pDb = *ppDb;
- NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, *ppDb)
+ NativePointerHolder_set(&S3NphRefs.sqlite3, ps->jDb, *ppDb)
/* As of here, the Java/C connection is complete */;
}else{
assert( ps->pDb==*ppDb
&& "Set up via s3jni_run_java_auto_extensions()" );
}
}else{
- S3JniDb_set_aside(env, ps);
+ S3JniDb_set_aside(ps);
ps = 0;
}
OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0);
@@ -3241,7 +3310,7 @@ end:
OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0));
}
if( pStmt ){
- NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jStmt, pStmt);
+ NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jStmt, pStmt);
}else{
/* Happens for comments and whitespace. */
S3JniUnrefLocal(jStmt);
@@ -4203,7 +4272,7 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
printf("Mutex entry:"
"\n\tglobal = %u"
"\n\tenv = %u"
- "\n\tnph inits = %u"
+ "\n\tnph = %u"
"\n\tperDb = %u"
"\n\tautoExt list = %u"
"\n\tS3JniUdf free-list = %u"
@@ -4212,11 +4281,12 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb,
SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf,
SJG.metrics.nMetrics);
- printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n",
+ puts("Allocs:");
+ printf("\tS3JniDb: %u alloced (*%u = %u bytes), %u recycled\n",
SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb),
(unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)),
SJG.metrics.nPdbRecycled);
- printf("S3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n",
+ printf("\tS3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n",
SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf),
(unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)),
SJG.metrics.nUdfRecycled);
@@ -4254,10 +4324,10 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
JNIEXPORT ReturnType JNICALL \
JniFuncNameFtsTok(Suffix)
-#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.fts5_api)
-#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.fts5_tokenizer)
-#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.Fts5Context)
-#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.Fts5Tokenizer)
+#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.fts5_api)
+#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.fts5_tokenizer)
+#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.Fts5Context)
+#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3NphRefs.Fts5Tokenizer)
#define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
/**
@@ -4679,7 +4749,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase,
jobject jCallback){
Fts5ExtDecl;
- S3JniEnv * const jc = S3JniEnv_get(env);
+ S3JniEnv * const jc = S3JniEnv_get();
struct s3jni_xQueryPhraseState s;
jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
@@ -4763,7 +4833,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef,
jint tokFlags, jobject jFcx,
jbyteArray jbaText, jobject jCallback){
Fts5ExtDecl;
- S3JniEnv * const jc = S3JniEnv_get(env);
+ S3JniEnv * const jc = S3JniEnv_get();
struct s3jni_xQueryPhraseState s;
int rc = 0;
jbyte * const pText = jCallback ? s3jni_jbytearray_bytes(jbaText) : 0;
diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java
index 6cad93d74..d32320b26 100644
--- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java
+++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java
@@ -39,8 +39,13 @@ import org.sqlite.jni.annotation.*;
<p><a href="https://sqlite.org/c3ref/intro.html">https://sqlite.org/c3ref/intro.html</a>
- <p>A handful of Java-specific APIs have been added which are documented
- here.
+ <p>A handful of Java-specific APIs have been added which are
+ documented here. A number of convenience overloads are provided
+ which are not documented but whose semantics map 1-to-1 in an
+ intuitive manner. e.g. {@link
+ #sqlite3_result_set(sqlite3_context,int)} is equivalent to {@link
+ #sqlite3_result_int}, and sqlite3_result_set() has many
+ type-specific overloads.
<p>Though most of the {@code SQLITE_abc...} C macros represented by
this class are defined as final, a few are necessarily non-final
@@ -228,15 +233,16 @@ public final class SQLite3Jni {
/**
Works like the C-level sqlite3_bind_text() but assumes
- SQLITE_TRANSIENT for the final C API parameter.
+ SQLITE_TRANSIENT for the final C API parameter. The byte array is
+ assumed to be in UTF-8 encoding.
<p>Results are undefined if data is not null and
- maxBytes>=data.length. If maxBytes is negative then results are
+ maxBytes>=utf8.length. If maxBytes is negative then results are
undefined if data is not null and does not contain a NUL byte.
*/
@Canonical
- private static native int sqlite3_bind_text(
- @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
+ public static native int sqlite3_bind_text(
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8, int maxBytes
);
/**
@@ -253,14 +259,14 @@ public final class SQLite3Jni {
}
/**
- Requires that data be null or in UTF-8 encoding.
+ Requires that utf8 be null or in UTF-8 encoding.
*/
public static int sqlite3_bind_text(
- @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] utf8
){
- return (null == data)
+ return (null == utf8)
? sqlite3_bind_null(stmt, ndx)
- : sqlite3_bind_text(stmt, ndx, data, data.length);
+ : sqlite3_bind_text(stmt, ndx, utf8, utf8.length);
}
/**
@@ -269,7 +275,7 @@ public final class SQLite3Jni {
platform byte order.
*/
@Canonical
- private static native int sqlite3_bind_text16(
+ public static native int sqlite3_bind_text16(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
);
@@ -728,6 +734,14 @@ public final class SQLite3Jni {
class.) For that vast majority of uses, that capability is not
necessary, however, and overloads are provided which gloss over
that.
+
+ <p>Results are undefined if maxBytes>=sqlUtf8.length.
+
+ <p>This routine is private because its maxBytes value is not
+ strictly necessary in the Java interface, as sqlUtf8.length tells
+ us the information we need. Making this public would give clients
+ more ways to shoot themselves in the foot without providing any
+ real utility.
*/
@Canonical
private static native int sqlite3_prepare(
@@ -736,6 +750,14 @@ public final class SQLite3Jni {
@Nullable OutputPointer.Int32 pTailOffset
);
+ /**
+ Works like the canonical sqlite3_prepare() but its "tail" output
+ argument is returned as the index offset into the given
+ UTF-8-encoded byte array at which SQL parsing stopped. The
+ semantics are otherwise identical to the C API counterpart.
+
+ <p>Several overloads provided simplified call signatures.
+ */
public static int sqlite3_prepare(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
@NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -776,8 +798,7 @@ public final class SQLite3Jni {
}
/**
- See sqlite3_prepare() for details about the slight API differences
- from the C API.
+ @see #sqlite3_prepare
*/
@Canonical
private static native int sqlite3_prepare_v2(
@@ -786,6 +807,11 @@ public final class SQLite3Jni {
@Nullable OutputPointer.Int32 pTailOffset
);
+ /**
+ Works like the canonical sqlite3_prepare_v2() but its "tail"
+ output paramter is returned as the index offset into the given
+ byte array at which SQL parsing stopped.
+ */
public static int sqlite3_prepare_v2(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
@NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -821,6 +847,9 @@ public final class SQLite3Jni {
return out.take();
}
+ /**
+ @see #sqlite3_prepare
+ */
@Canonical
private static native int sqlite3_prepare_v3(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
@@ -828,6 +857,11 @@ public final class SQLite3Jni {
@Nullable OutputPointer.Int32 pTailOffset
);
+ /**
+ Works like the canonical sqlite3_prepare_v2() but its "tail"
+ output paramter is returned as the index offset into the given
+ byte array at which SQL parsing stopped.
+ */
public static int sqlite3_prepare_v3(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int prepFlags,
@NotNull OutputPointer.sqlite3_stmt outStmt,
@@ -993,13 +1027,15 @@ public final class SQLite3Jni {
}
/**
- Equivalent to passing e.getMessage() to
- sqlite3_result_error(db,String).
+ Equivalent to passing e.toString() to {@link
+ #sqlite3_result_error(sqlite3_context,String)}. Note that
+ toString() is used instead of getMessage() because the former
+ prepends the exception type name to the message.
*/
public static void sqlite3_result_error(
@NotNull sqlite3_context cx, @NotNull Exception e
){
- sqlite3_result_error(cx, e.getMessage());
+ sqlite3_result_error(cx, e.toString());
}
@Canonical
@@ -1033,18 +1069,14 @@ public final class SQLite3Jni {
);
/**
- Binds the SQL result to the given object, or
- {@link #sqlite3_result_null} if {@code o} is null. Use
- {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} to
- fetch it.
+ Binds the SQL result to the given object, or {@link
+ #sqlite3_result_null} if {@code o} is null. Use {@link
+ #sqlite3_value_java_object} to fetch it.
<p>This is implemented in terms of C's sqlite3_result_pointer(),
- but that function is not exposed to JNI because its 3rd argument
- must be a constant string (the library does not copy it), and
- those semantics are cumbersome to bridge cross-language. Java
- doesn't need that argument for type safety, in any case: the
- object can, after extraction on the other end of the API, be
- inspected with {@code instanceof}.
+ but that function is not exposed to JNI because (A)
+ cross-language semantic mismatch and (B) Java doesn't need that
+ argument for its intended purpose (type safety).
<p>Note that there is no sqlite3_column_java_object(), as the
C-level API has no sqlite3_column_pointer() to proxy.
@@ -1057,57 +1089,63 @@ public final class SQLite3Jni {
);
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, @NotNull Integer v
+ @NotNull sqlite3_context cx, @NotNull Boolean v
){
- sqlite3_result_int(cx, v);
+ sqlite3_result_int(cx, v ? 1 : 0);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, int v
+ @NotNull sqlite3_context cx, boolean v
){
- sqlite3_result_int(cx, v);
+ sqlite3_result_int(cx, v ? 1 : 0);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, @NotNull Boolean v
+ @NotNull sqlite3_context cx, @NotNull Double v
){
- sqlite3_result_int(cx, v ? 1 : 0);
+ sqlite3_result_double(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, boolean v
+ @NotNull sqlite3_context cx, double v
){
- sqlite3_result_int(cx, v ? 1 : 0);
+ sqlite3_result_double(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, @NotNull Long v
+ @NotNull sqlite3_context cx, @NotNull Integer v
){
- sqlite3_result_int64(cx, v);
+ sqlite3_result_int(cx, v);
+ }
+
+ public static void sqlite3_result_set(@NotNull sqlite3_context cx, int v){
+ sqlite3_result_int(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, long v
+ @NotNull sqlite3_context cx, @NotNull Long v
){
sqlite3_result_int64(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, @NotNull Double v
+ @NotNull sqlite3_context cx, long v
){
- sqlite3_result_double(cx, v);
+ sqlite3_result_int64(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, double v
+ @NotNull sqlite3_context cx, @Nullable String v
){
- sqlite3_result_double(cx, v);
+ if( null==v ) sqlite3_result_null(cx);
+ else sqlite3_result_text(cx, v);
}
public static void sqlite3_result_set(
- @NotNull sqlite3_context cx, @Nullable String v
+ @NotNull sqlite3_context cx, @Nullable byte[] blob
){
- sqlite3_result_text(cx, v);
+ if( null==blob ) sqlite3_result_null(cx);
+ else sqlite3_result_blob(cx, blob, blob.length);
}
@Canonical
diff --git a/ext/jni/src/org/sqlite/jni/package-info.java b/ext/jni/src/org/sqlite/jni/package-info.java
index ed9086c4c..2ca997955 100644
--- a/ext/jni/src/org/sqlite/jni/package-info.java
+++ b/ext/jni/src/org/sqlite/jni/package-info.java
@@ -5,6 +5,11 @@
<p>The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}.
+ <h1>State of this API</h1>
+
+ <p>As of version 3.43, this software is in "tech preview" form. We
+ tentatively plan to stamp it as stable with the 3.44 release.
+
<h1>Threading Considerations</h1>
<p>This API is, if built with SQLITE_THREADSAFE set to 1 or 2,
@@ -39,5 +44,6 @@
<p>When built with SQLITE_THREADSAFE=0 then no threading guarantees
are provided and multi-threaded use of the library will provoke
undefined behavior.
+
*/
package org.sqlite.jni;
diff --git a/manifest b/manifest
index 3b290f812..48f883fc4 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Lots\sof\sjavadoc-related\stweaks.
-D 2023-08-28T07:28:36.868
+C Further\sJNI\scleanups\sand\sjavadoc\sadditions.
+D 2023-08-28T11:10:13.491
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -233,10 +233,10 @@ 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 8bb7f82029eb7d6182f4af9c42f99abaf4cf476984f5aebd6dcf05d85da340c5
+F ext/jni/GNUmakefile 374873bf6d2cd6ceafb458e28b59140dbb074f01f7adddf7e15a3ee3daf44551
F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
-F ext/jni/src/c/sqlite3-jni.c 064d3a14650221582a1d1621bd1109efb2a455f556387613cd583731eac28674
+F ext/jni/src/c/sqlite3-jni.c c8f329d225c87c9af2c74508e6be48424a380502da7bca82fc7486dd91a7af9c
F ext/jni/src/c/sqlite3-jni.h 12e1a5ef5ee1795dc22577c285b4518dfd8aa4af45757f6cb81a555d967bf201
F ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java 95e88ba04f4aac51ffec65693e878e234088b2f21b387f4e4285c8b72b33e436
F ext/jni/src/org/sqlite/jni/AggregateFunction.java 7312486bc65fecdb91753c0a4515799194e031f45edbe16a6373cea18f404dc4
@@ -261,7 +261,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c
F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java 16042be9d072a26dbb2f1b1b63e7639989b747bb80d2bd667ba4f7555f56a825
F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c
F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 54b39846ee0540f8d8fc700739cd6701ba1231e12c3964c1d367b51e9315ed3b
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 440d64e8c4cff53bd3c0cc676381212489198302d7f1aaa535712c2d7163cc69
F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b
F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
@@ -277,7 +277,7 @@ F ext/jni/src/org/sqlite/jni/annotation/package-info.java f66bfb621c6494e67c03ed
F ext/jni/src/org/sqlite/jni/fts5_api.java ee47f1837d32968f7bb62278c7504c0fb572a68ec107371b714578312e9f734b
F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c
F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a92c2e55bda492e4c76d48ddc73369bcc0d5e8727940840f9339e3292ea58fa7
-F ext/jni/src/org/sqlite/jni/package-info.java c8f1c858ebcadd16ff047a74cf7a0556d4235386c47bc0a4d78c4a564bba03fe
+F ext/jni/src/org/sqlite/jni/package-info.java 73f7821c240e4d116f164e87b613c5836b8a33ce2666967a29d9acb1ced7ca92
F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc
F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc
@@ -2106,8 +2106,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 8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf
-R 9e09f148c76497a018ec611f2deafbbd
+P cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd
+R d8657bd6950439669ea385ca4259469a
U stephan
-Z cceffc0e8c2aa755651a9db7f05913c5
+Z 33c0cfaa6b6aa55ee1261a4aa78cec86
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index ce51adc40..efd91396c 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd \ No newline at end of file
+c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 \ No newline at end of file