diff options
-rw-r--r-- | ext/jni/GNUmakefile | 3 | ||||
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.c | 188 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 124 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/package-info.java | 6 | ||||
-rw-r--r-- | manifest | 18 | ||||
-rw-r--r-- | manifest.uuid | 2 |
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; @@ -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 |