From 0c07549fd6b88c2b62b8c49c6409ee2e784bf239 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 12 Aug 2023 23:47:58 +0000 Subject: [PATCH 001/422] Bind sqlite3_interrupt() and sqlite3_is_interrupted() to JNI but with caveats regarding mutexing of the JNIEnv cache. Add a loud warning to the JNI 'dist' target that it should be built with JDK8 (a.k.a. Java 1.8) for compatibility reasons. FossilOrigin-Name: fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe --- ext/jni/GNUmakefile | 4 ++++ ext/jni/src/c/sqlite3-jni.c | 14 ++++++++++++++ ext/jni/src/c/sqlite3-jni.h | 18 +++++++++++++++++- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 19 +++++++++++++++++++ ext/jni/src/org/sqlite/jni/Tester1.java | 7 +++++++ manifest | 21 ++++++++++----------- manifest.uuid | 2 +- 7 files changed, 72 insertions(+), 13 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 36ef42d31a..22301a3245 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -317,6 +317,10 @@ dist: \ $(bin.version-info) $(sqlite3.canonical.c) \ $(package.jar) $(MAKEFILE) @echo "Making end-user deliverables..." + @echo "****************************************************************************"; \ + echo "*** WARNING: be sure to build this with JDK8 (javac 1.8) for compatibility."; \ + echo "*** reasons!"; $$($(bin.javac) -version); \ + echo "****************************************************************************" @rm -fr $(dist-dir.top) @mkdir -p $(dist-dir.src) @cp -p $(dist.top.extras) $(dist-dir.top)/. diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index b28ea71144..6c60aeaf3a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2624,6 +2624,20 @@ JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){ return rc; } +JDECL(void,1interrupt)(JENV_CSELF, jobject jpDb){ + sqlite3 * const pDb = PtrGet_sqlite3(jpDb); + if( pDb ) sqlite3_interrupt(pDb); +} + +JDECL(jboolean,1is_1interrupted)(JENV_CSELF, jobject jpDb){ + int rc = 0; + sqlite3 * const pDb = PtrGet_sqlite3(jpDb); + if( pDb ){ + rc = sqlite3_is_interrupted(pDb); + } + return rc ? JNI_TRUE : JNI_FALSE; +} + JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){ return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index bf2d5527b0..6722085fdd 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1120,7 +1120,7 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename * Method: sqlite3_db_config * Signature: (Lorg/sqlite/jni/sqlite3;IILorg/sqlite/jni/OutputPointer/Int32;)I */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_00024Int32_2 +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2 (JNIEnv *, jclass, jobject, jint, jint, jobject); /* @@ -1211,6 +1211,22 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1finalize JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1initialize (JNIEnv *, jclass); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_interrupt + * Signature: (Lorg/sqlite/jni/sqlite3;)V + */ +JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1interrupt + (JNIEnv *, jclass, jobject); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_is_interrupted + * Signature: (Lorg/sqlite/jni/sqlite3;)Z + */ +JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1is_1interrupted + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_last_insert_rowid diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 9b2a176504..adad718145 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -557,6 +557,25 @@ public final class SQLite3Jni { public static synchronized native int sqlite3_initialize(); + /** + Design note/FIXME: we have a problem vis-a-vis 'synchronized' + here: we specifically want other threads to be able to cancel a + long-running thread, but this routine requires access to C-side + global state which does not have a mutex. Making this function + synchronized would make it impossible for a long-running job to + be cancelled from another thread. + + The mutexing problem here is not within the core lib or Java, but + within the cached data held by the JNI binding. The cache holds + per-thread state, used by all but a tiny fraction of the JNI + binding layer, and access to that state needs to be + mutex-protected. + */ + public static native void sqlite3_interrupt(@NotNull sqlite3 db); + + //! See sqlite3_interrupt() for threading concerns. + public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db); + public static synchronized native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); public static synchronized native String sqlite3_libversion(); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index ffe0b83846..e9524e49a7 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -163,6 +163,13 @@ public class Tester1 { /* This function has different mangled names in jdk8 vs jdk19, and this call is here to ensure that the build fails if it cannot find both names. */; + + // These interrupt checks are only to make sure that the JNI binding + // has the proper exported symbol names. They don't actually test + // anything useful. + affirm( !sqlite3_is_interrupted(db) ); + sqlite3_interrupt(db); + affirm( sqlite3_is_interrupted(db) ); sqlite3_close_v2(db); affirm(0 == db.getNativePointer()); } diff --git a/manifest b/manifest index f112606117..0c792ccae0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\sJava\sNative\sInterface\s(JNI)\sbinding\sinto\strunk. -D 2023-08-12T21:39:18.053 +C Bind\ssqlite3_interrupt()\sand\ssqlite3_is_interrupted()\sto\sJNI\sbut\swith\scaveats\sregarding\smutexing\sof\sthe\sJNIEnv\scache.\sAdd\sa\sloud\swarning\sto\sthe\sJNI\s'dist'\starget\sthat\sit\sshould\sbe\sbuilt\swith\sJDK8\s(a.k.a.\sJava\s1.8)\sfor\scompatibility\sreasons. +D 2023-08-12T23:47:58.408 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -231,11 +231,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 435485ff2005c4bcdea808f5efe6d4ee66a00430c2499dcc4927b20378486bea +F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c bea6b8691a5fa3a8626a771757bb261208d3c5fc6598266d3b0ee23d88e35632 -F ext/jni/src/c/sqlite3-jni.h c5f941b057a24ee62942e6e1bf5a7fd527e5004d20d9638e84a9382813c3cf2a +F ext/jni/src/c/sqlite3-jni.c 1d3bb5113ba4dd7f8645fcc4c669155931e44e234816f528642a738914dd45a4 +F ext/jni/src/c/sqlite3-jni.h 11bf3ab9682f5c393e6ac6a3ddb0fdf7b8dd40f7c77f9ef122d3e5c011a5d329 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -254,8 +254,8 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 4b6fd22e04e63eb65d8e4e38fda39ecf15ce244d034607517627ce2e766e7e65 -F ext/jni/src/org/sqlite/jni/Tester1.java 07c14a90427529ceba54b5e8344ca03602f5789dc53c4163ce22f92d8c577a11 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1652af40fc0acb7a140dbe32e3146f980c37c28454b5115a4d0856cbdbc52696 +F ext/jni/src/org/sqlite/jni/Tester1.java fc2ec1f1be58474112b9df8284f0157b64872107f446154c3d0bf1742b924d2b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,9 +2091,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 0a6930a7ff8f8c6ca244d1d654532f3d2a02d77ef67c6cae0c53092743d59ea6 1ba7754045a009d9c94b23ac76b9bb8d9c9cb24d42dcdf1203ee75ac85765d3e -R 71919983f1228c04d42d3555e47fba1b -T +closed 1ba7754045a009d9c94b23ac76b9bb8d9c9cb24d42dcdf1203ee75ac85765d3e Closed\sby\sintegrate-merge. +P 48b13edcec6935bf125b265b41a3e6f7b2407afff89d5b4daa2939e3c5679ca0 +R 122775c5939e6f1540fadefe535658be U stephan -Z 06dd3519c67c3e383d12eeb6b6fbb0d1 +Z a13d97b8681eef4145a4ab842cd3a9d2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1c747eb83a..97ea0d6766 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -48b13edcec6935bf125b265b41a3e6f7b2407afff89d5b4daa2939e3c5679ca0 \ No newline at end of file +fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe \ No newline at end of file From 71e5694cd51bbf66546e58b52553a16fb7e842ff Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 13 Aug 2023 09:53:27 +0000 Subject: [PATCH 002/422] An initial attempt at protecting the JNI global state via mutexes at the C level instead of relying on Java's synchronized keyword. It seems to work but increases the run time of the single-threaded batch tester by roughly 3 times. FossilOrigin-Name: c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4 --- ext/jni/src/c/sqlite3-jni.c | 269 +++++++++++---------- ext/jni/src/c/sqlite3-jni.h | 16 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 246 +++++++++---------- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 277 insertions(+), 272 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 6c60aeaf3a..4522e05b45 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -498,10 +498,14 @@ static struct { struct { S3JniEnvCache * aHead /* Linked list of in-use instances */; S3JniEnvCache * aFree /* Linked list of free instances */; + sqlite3_mutex * mutex /* mutex for aHead and aFree */; + void const * locker /* env mutex is held on this object's behalf */; } envCache; struct { S3JniDb * aUsed /* Linked list of in-use instances */; S3JniDb * aFree /* Linked list of free instances */; + sqlite3_mutex * mutex /* mutex for aUsed and aFree */; + void const * locker /* perDb mutex is held on this object's behalf */; } perDb; struct { unsigned nphCacheHits; @@ -521,21 +525,50 @@ static struct { #if S3JNI_ENABLE_AUTOEXT struct { S3JniAutoExtension *pHead /* Head of the auto-extension list */; - S3JniDb * psOpening /* handle to the being-opened db. We - need this so that auto extensions - can have a consistent view of the - cross-language db connection and - behave property if they call further - db APIs. */; - int isRunning /* True while auto extensions are - running. This is used to prohibit - manipulation of the auto-extension - list while extensions are - running. */; + S3JniDb * psOpening /* FIXME: move into envCache. Handle to the + being-opened db. We need this so that auto + extensions can have a consistent view of + the cross-language db connection and + behave property if they call further db + APIs. */; + int isRunning /* True while auto extensions are + running. This is used to prohibit + manipulation of the auto-extension + list while extensions are + running. */; } autoExt; #endif } S3JniGlobal; +#define MUTEX_ASSERT_LOCKER_ENV \ + assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) +#define MUTEX_ASSERT_LOCKER_PDB \ + assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) +#define MUTEX_ASSERT_NOTLOCKER_ENV \ + assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) +#define MUTEX_ASSERT_NOTLOCKER_PDB \ + assert( 0 == S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) +#define MUTEX_ENTER_ENV \ + /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ + MUTEX_ASSERT_NOTLOCKER_ENV; \ + sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ + S3JniGlobal.envCache.locker = env +#define MUTEX_LEAVE_ENV \ + /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ + MUTEX_ASSERT_LOCKER_ENV; \ + S3JniGlobal.envCache.locker = 0; \ + sqlite3_mutex_leave( S3JniGlobal.envCache.mutex ) +#define MUTEX_ENTER_PDB \ + /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ + MUTEX_ASSERT_NOTLOCKER_PDB; \ + sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ + S3JniGlobal.perDb.locker = env; +#define MUTEX_LEAVE_PDB \ + /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ + MUTEX_ASSERT_LOCKER_PDB; \ + S3JniGlobal.perDb.locker = 0; \ + sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) + #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) static void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; @@ -559,12 +592,14 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ insofar as possible. Calls (*env)->FatalError() if allocation of an entry fails. That's hypothetically possible but "shouldn't happen." */ -FIXME_THREADING(S3JniEnvCache) static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){ - struct S3JniEnvCache * row = S3JniGlobal.envCache.aHead; + struct S3JniEnvCache * row; + MUTEX_ENTER_ENV; + row = S3JniGlobal.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ ++S3JniGlobal.metrics.envCacheHits; + MUTEX_LEAVE_ENV; return row; } } @@ -576,11 +611,7 @@ static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){ if( row->pNext ) row->pNext->pPrev = 0; }else{ row = sqlite3_malloc(sizeof(S3JniEnvCache)); - if( !row ){ - (*env)->FatalError(env, "Maintenance required: S3JniEnvCache is full.") - /* Does not return, but cc doesn't know that */; - return NULL; - } + OOM_CHECK(row); } memset(row, 0, sizeof(*row)); row->pNext = S3JniGlobal.envCache.aHead; @@ -616,11 +647,12 @@ static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){ EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class."); fUtf8 = (*env)->GetStaticFieldID(env, klazzSC, "UTF_8", "Ljava/nio/charset/Charset;"); - EXCEPTION_IS_FATAL("Error getting StndardCharsets.UTF_8 field."); + EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); row->g.oCharsetUtf8 = REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8)); EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); } + MUTEX_LEAVE_ENV; return row; } @@ -765,7 +797,6 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, System.out.println(e.getMessage()); // Hi } */ -FIXME_THREADING(S3JniEnvCache) static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); jmethodID mid; @@ -865,10 +896,10 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest /** Clears s's state and moves it to the free-list. */ -FIXME_THREADING(perDb) static void S3JniDb_set_aside(S3JniDb * const s){ if(s){ JNIEnv * const env = s->env; + MUTEX_ASSERT_LOCKER_PDB; assert(s->pDb && "Else this object is already in the free-list."); //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); @@ -904,39 +935,16 @@ static void S3JniDb_set_aside(S3JniDb * const s){ //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev)); } } - -/** - Requires that p has been snipped from any linked list it is - in. Clears all Java refs p holds and zeroes out p. -*/ -static void S3JniEnvCache_clear(S3JniEnvCache * const p){ - JNIEnv * const env = p->env; - if(env){ - int i; - UNREF_G(p->g.cObj); - UNREF_G(p->g.cLong); - UNREF_G(p->g.cString); - UNREF_G(p->g.oCharsetUtf8); -#ifdef SQLITE_ENABLE_FTS5 - UNREF_G(p->jFtsExt); - UNREF_G(p->jPhraseIter.klazz); -#endif - for( i = 0; i < NphCache_SIZE; ++i ){ - S3JniNphCache_clear(env, &p->nph[i]); - } - memset(p, 0, sizeof(S3JniEnvCache)); - } -} - /** Cleans up all state in S3JniGlobal.perDb for th given JNIEnv. Results are undefined if a Java-side db uses the API from the given JNIEnv after this call. */ -FIXME_THREADING(perDb) static void S3JniDb_free_for_env(JNIEnv *env){ - S3JniDb * ps = S3JniGlobal.perDb.aUsed; + S3JniDb * ps; S3JniDb * pNext = 0; + MUTEX_ENTER_PDB; + ps = S3JniGlobal.perDb.aUsed; for( ; ps; ps = pNext ){ pNext = ps->pNext; if(ps->env == env){ @@ -946,6 +954,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ pNext = pPrev; } } + MUTEX_LEAVE_PDB; } /** @@ -957,35 +966,43 @@ static void S3JniDb_free_for_env(JNIEnv *env){ what would otherwise be stale references. */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ - struct S3JniEnvCache * row = S3JniGlobal.envCache.aHead; + struct S3JniEnvCache * row; + int i; + assert( 0!=S3JniGlobal.envCache.mutex && "Env mutex misuse."); + row = S3JniGlobal.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ break; } } - if( !row ) return 0; + if( !row ){ + return 0; + } if( row->pNext ) row->pNext->pPrev = row->pPrev; if( row->pPrev ) row->pPrev->pNext = row->pNext; if( S3JniGlobal.envCache.aHead == row ){ assert( !row->pPrev ); S3JniGlobal.envCache.aHead = row->pNext; } - S3JniEnvCache_clear(row); - assert( !row->pNext ); - assert( !row->pPrev ); + S3JniDb_free_for_env(env); + UNREF_G(row->g.cObj); + UNREF_G(row->g.cLong); + UNREF_G(row->g.cString); + UNREF_G(row->g.oCharsetUtf8); +#ifdef SQLITE_ENABLE_FTS5 + UNREF_G(row->jFtsExt); + UNREF_G(row->jPhraseIter.klazz); +#endif + for( i = 0; i < NphCache_SIZE; ++i ){ + S3JniNphCache_clear(env, &row->nph[i]); + } + memset(row, 0, sizeof(S3JniEnvCache)); row->pNext = S3JniGlobal.envCache.aFree; if( row->pNext ) row->pNext->pPrev = row; S3JniGlobal.envCache.aFree = row; - S3JniDb_free_for_env(env); return 1; } -static void S3JniGlobal_S3JniEnvCache_clear(void){ - while( S3JniGlobal.envCache.aHead ){ - S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env ); - } -} - /** Searches the NativePointerHolder cache for the given combination. If it finds one, it returns it as-is. If it doesn't AND the cache @@ -1128,14 +1145,15 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zC } /** - Extracts the new S3JniDb instance from the free-list, or - allocates one if needed, associats it with pDb, and returns. - Returns NULL on OOM. pDb MUST be associated with jDb via - NativePointerHolder_set(). + Extracts the new S3JniDb instance from the free-list, or allocates + one if needed, associats it with pDb, and returns. Returns NULL on + OOM. pDb MUST, on success of the calling operation, subsequently be + associated with jDb via NativePointerHolder_set(). */ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; + MUTEX_ASSERT_LOCKER_PDB; if(S3JniGlobal.perDb.aFree){ rv = S3JniGlobal.perDb.aFree; //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb)); @@ -1185,62 +1203,37 @@ static void S3JniDb_dump(S3JniDb *s){ #endif /** - Returns the S3JniDb object for the given db. If allocIfNeeded is - true then a new instance will be allocated if no mapping currently - exists, else NULL is returned if no mapping is found. + Returns the S3JniDb object for the given db. - The 3rd and 4th args should normally only be non-0 for - sqlite3_open(_v2)(). For most other cases, they must be 0, in which - case the db handle will be fished out of the jDb object and NULL is - returned if jDb does not have any associated S3JniDb. + The 3rd argument should normally only be non-0 for routines which + are called from the C library and pass a native db handle instead of + a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 + object, from which the db is fished out. - If called with a NULL jDb and non-NULL pDb then allocIfNeeded MUST - be false and it will look for a matching db object. That usage is - required for functions, like sqlite3_context_db_handle(), which - return a (sqlite3*) but do not take one as an argument. + Returns NULL if jDb and pDb are both NULL or if there is no + matching S3JniDb entry for pDb or the pointer fished out of jDb. */ FIXME_THREADING(S3JniEnvCache) FIXME_THREADING(perDb) -static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, - sqlite3 *pDb, int allocIfNeeded){ - S3JniDb * s = S3JniGlobal.perDb.aUsed; - if(!jDb){ - if(pDb){ - assert(!allocIfNeeded); - }else{ - return 0; +static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ + S3JniDb * s = 0; + if(jDb || pDb){ + MUTEX_ENTER_PDB; + s = S3JniGlobal.perDb.aUsed; + if(!pDb){ + assert( jDb ); + pDb = PtrGet_sqlite3(jDb); } - } - assert(allocIfNeeded ? !!pDb : 1); - if(!allocIfNeeded && !pDb){ - pDb = PtrGet_sqlite3(jDb); - } - for( ; pDb && s; s = s->pNext){ - if(s->pDb == pDb) return s; - } - if(allocIfNeeded){ - s = S3JniDb_alloc(env, pDb, jDb); + for( ; pDb && s; s = s->pNext){ + if(s->pDb == pDb){ + break; + } + } + MUTEX_LEAVE_PDB; } return s; } -#if 0 -/** - An alternative form which searches for the S3JniDb 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(perDb) -static S3JniDb * S3JniDb_for_db2(sqlite3 *pDb){ - S3JniDb * s = S3JniGlobal.perDb.aUsed; - for( ; pDb && s; s = s->pNext){ - if(s->pDb == pDb) return s; - } - return 0; -} -#endif - #if S3JNI_ENABLE_AUTOEXT /** Unlink ax from S3JniGlobal.autoExt and free it. @@ -2048,7 +2041,7 @@ static int s3jni_busy_handler(void* pState, int n){ FIXME_THREADING(S3JniEnvCache) JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc = 0; if(!ps) return (jint)SQLITE_NOMEM; if(jBusy){ @@ -2079,7 +2072,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ FIXME_THREADING(S3JniEnvCache) FIXME_THREADING(perDb) JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); if( ps ){ S3JniHook_unref(env, &ps->busyHandler, 1); return sqlite3_busy_timeout(ps->pDb, (int)ms); @@ -2110,12 +2103,13 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ int rc = 0; S3JniDb * ps = 0; assert(version == 1 || version == 2); - ps = S3JniDb_for_db(env, jDb, 0, 0); + ps = S3JniDb_for_db(env, jDb, 0); if(ps){ - //MARKER(("close()ing db@%p\n", ps->pDb)); rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); + MUTEX_ENTER_PDB; S3JniDb_set_aside(ps) /* MUST come after close() because of ps->trace. */; + MUTEX_LEAVE_PDB; NativePointerHolder_set(env, jDb, 0, S3JniClassNames.sqlite3); } return (jint)rc; @@ -2170,7 +2164,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, FIXME_THREADING(S3JniEnvCache) FIXME_THREADING(perDb) JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jobject pOld = 0; jmethodID xCallback; @@ -2288,7 +2282,7 @@ static void s3jni_rollback_hook_impl(void *pP){ FIXME_THREADING(perDb) static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,jobject jDb, jobject jHook){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jobject pOld = 0; jmethodID xCallback; @@ -2357,14 +2351,14 @@ JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ FIXME_THREADING(perDb) JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){ sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); - S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb, 0) : 0; + S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0; return ps ? ps->jDb : 0; } JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; int rc; const char *zName; @@ -2457,7 +2451,7 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( JENV_CSELF, jobject jDb, jint op, jstring jStr ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc; char *zStr; @@ -2489,7 +2483,7 @@ FIXME_THREADING(perDb) JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc; switch( ps ? op : 0 ){ case SQLITE_DBCONFIG_ENABLE_FKEY: @@ -2538,7 +2532,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer } JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); char *zDbName; jstring jRv = 0; @@ -2657,7 +2651,9 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc, *zDbName = 0; return SQLITE_NOMEM; } + MUTEX_ENTER_PDB; *ps = S3JniDb_alloc(env, 0, *jDb); + MUTEX_LEAVE_PDB; #if S3JNI_ENABLE_AUTOEXT if(*ps){ assert(!S3JniGlobal.autoExt.psOpening); @@ -2688,15 +2684,12 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, #endif if(*ppDb){ assert(ps->jDb); -#if S3JNI_ENABLE_AUTOEXT - //MARKER(("*autoExt.pHead=%p, ppDb=%p, ps->pDb=%p\n", S3JniGlobal.autoExt.pHead, *ppDb, ps->pDb)); - // invalid when an autoext triggers another open(): - // assert( S3JniGlobal.autoExt.pHead ? *ppDb==ps->pDb : 0==ps->pDb ); -#endif ps->pDb = *ppDb; NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3); }else{ + MUTEX_ENTER_PDB; S3JniDb_set_aside(ps); + MUTEX_LEAVE_PDB; ps = 0; } OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0); @@ -2845,7 +2838,7 @@ static int s3jni_progress_handler_impl(void *pP){ } JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress){ - S3JniDb * ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jmethodID xCallback; if( n<1 || !jProgress ){ @@ -3101,7 +3094,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, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniHook * const pHook = ps ? &ps->authHook : 0; if( !ps ) return SQLITE_MISUSE; @@ -3195,7 +3188,11 @@ JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){ } JDECL(jint,1shutdown)(JENV_CSELF){ - S3JniGlobal_S3JniEnvCache_clear(); + MUTEX_ENTER_ENV; + while( S3JniGlobal.envCache.aHead ){ + S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env ); + } + MUTEX_LEAVE_ENV; /* Do not clear S3JniGlobal.jvm: it's legal to call sqlite3_initialize() again to restart the lib. */ return sqlite3_shutdown(); @@ -3279,7 +3276,7 @@ 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, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; if( !traceMask || !jTracer ){ if(ps){ @@ -3330,7 +3327,7 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, JDECL(jobject,1update_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jobject pOld = 0; jmethodID xCallback; @@ -3635,7 +3632,7 @@ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ } JDECLFtsApi(jobject,getInstanceForDb)(JENV_CSELF,jobject jDb){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jobject rv = 0; if(!ps) return 0; else if(ps->jFtsApi){ @@ -4338,7 +4335,11 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){ */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ - return S3JniGlobal_env_uncache(env) ? JNI_TRUE : JNI_FALSE; + int rc; + MUTEX_ENTER_ENV; + rc = S3JniGlobal_env_uncache(env); + MUTEX_LEAVE_ENV; + return rc ? JNI_TRUE : JNI_FALSE; } /** @@ -4402,6 +4403,10 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible."); return; } + S3JniGlobal.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( S3JniGlobal.envCache.mutex ); + S3JniGlobal.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( S3JniGlobal.perDb.mutex ); #if 0 /* Just for sanity checking... */ (void)S3JniGlobal_env_cache(env); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 6722085fdd..5d741859f1 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1443,6 +1443,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64 (JNIEnv *, jclass, jobject, jbyteArray, jlong, jint); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_shutdown + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown + (JNIEnv *, jclass); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_status @@ -1731,14 +1739,6 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1frombind JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1subtype (JNIEnv *, jclass, jobject); -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_shutdown - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown - (JNIEnv *, jclass); - /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_do_something_for_developer diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index adad718145..e7aa10ebae 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -195,7 +195,7 @@ public final class SQLite3Jni { Achtung: it is as yet unknown whether auto extensions registered from one JNIEnv (thread) can be safely called from another. */ - public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback); + public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @@ -205,27 +205,27 @@ public final class SQLite3Jni { : sqlite3_bind_blob(stmt, ndx, data, data.length); } - private static synchronized native int sqlite3_bind_blob( + private static native int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n ); - public static synchronized native int sqlite3_bind_double( + public static native int sqlite3_bind_double( @NotNull sqlite3_stmt stmt, int ndx, double v ); - public static synchronized native int sqlite3_bind_int( + public static native int sqlite3_bind_int( @NotNull sqlite3_stmt stmt, int ndx, int v ); - public static synchronized native int sqlite3_bind_int64( + public static native int sqlite3_bind_int64( @NotNull sqlite3_stmt stmt, int ndx, long v ); - public static synchronized native int sqlite3_bind_null( + public static native int sqlite3_bind_null( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native int sqlite3_bind_parameter_count( + public static native int sqlite3_bind_parameter_count( @NotNull sqlite3_stmt stmt ); @@ -233,7 +233,7 @@ public final class SQLite3Jni { /** A level of indirection required to ensure that the input to the C-level function of the same name is a NUL-terminated UTF-8 string. */ - private static synchronized native int sqlite3_bind_parameter_index( + private static native int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, byte[] paramName ); @@ -265,15 +265,15 @@ public final class SQLite3Jni { SQLITE_TRANSIENT for the final parameter and (B) behaves like sqlite3_bind_null() if the data argument is null. */ - private static synchronized native int sqlite3_bind_text( + private static native int sqlite3_bind_text( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes ); - public static synchronized native int sqlite3_bind_zeroblob( + public static native int sqlite3_bind_zeroblob( @NotNull sqlite3_stmt stmt, int ndx, int n ); - public static synchronized native int sqlite3_bind_zeroblob64( + public static native int sqlite3_bind_zeroblob64( @NotNull sqlite3_stmt stmt, int ndx, long n ); @@ -283,11 +283,11 @@ public final class SQLite3Jni { to clear the busy handler. Calling this multiple times with the same object is a no-op on the second and subsequent calls. */ - public static synchronized native int sqlite3_busy_handler( + public static native int sqlite3_busy_handler( @NotNull sqlite3 db, @Nullable BusyHandler handler ); - public static synchronized native int sqlite3_busy_timeout( + public static native int sqlite3_busy_timeout( @NotNull sqlite3 db, int ms ); @@ -296,63 +296,63 @@ public final class SQLite3Jni { effects, if auto extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ - public static synchronized native boolean sqlite3_cancel_auto_extension( + public static native boolean sqlite3_cancel_auto_extension( @NotNull AutoExtension ax ); - public static synchronized native int sqlite3_changes( + public static native int sqlite3_changes( @NotNull sqlite3 db ); - public static synchronized native long sqlite3_changes64( + public static native long sqlite3_changes64( @NotNull sqlite3 db ); - public static synchronized native int sqlite3_clear_bindings( + public static native int sqlite3_clear_bindings( @NotNull sqlite3_stmt stmt ); - public static synchronized native int sqlite3_close( + public static native int sqlite3_close( @NotNull sqlite3 db ); - public static synchronized native int sqlite3_close_v2( + public static native int sqlite3_close_v2( @NotNull sqlite3 db ); - public static synchronized native byte[] sqlite3_column_blob( + public static native byte[] sqlite3_column_blob( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native int sqlite3_column_bytes( + public static native int sqlite3_column_bytes( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native int sqlite3_column_bytes16( + public static native int sqlite3_column_bytes16( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native int sqlite3_column_count( + public static native int sqlite3_column_count( @NotNull sqlite3_stmt stmt ); - public static synchronized native double sqlite3_column_double( + public static native double sqlite3_column_double( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native int sqlite3_column_int( + public static native int sqlite3_column_int( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native long sqlite3_column_int64( + public static native long sqlite3_column_int64( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native String sqlite3_column_name( + public static native String sqlite3_column_name( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native String sqlite3_column_database_name( + public static native String sqlite3_column_database_name( @NotNull sqlite3_stmt stmt, int ndx ); @@ -385,11 +385,11 @@ public final class SQLite3Jni { return type.isInstance(o) ? (T)o : null; } - public static synchronized native String sqlite3_column_origin_name( + public static native String sqlite3_column_origin_name( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native String sqlite3_column_table_name( + public static native String sqlite3_column_table_name( @NotNull sqlite3_stmt stmt, int ndx ); @@ -398,7 +398,7 @@ public final class SQLite3Jni { This API includes no functions for working with Java's Modified UTF-8. */ - public static synchronized native String sqlite3_column_text16( + public static native String sqlite3_column_text16( @NotNull sqlite3_stmt stmt, int ndx ); @@ -406,7 +406,7 @@ public final class SQLite3Jni { Returns the given column's contents as UTF-8-encoded (not MUTF-8) text. Use sqlite3_column_text16() to fetch the text */ - public static synchronized native byte[] sqlite3_column_text( + public static native byte[] sqlite3_column_text( @NotNull sqlite3_stmt stmt, int ndx ); @@ -447,11 +447,11 @@ public final class SQLite3Jni { // return rv; // } - public static synchronized native int sqlite3_column_type( + public static native int sqlite3_column_type( @NotNull sqlite3_stmt stmt, int ndx ); - public static synchronized native sqlite3_value sqlite3_column_value( + public static native sqlite3_value sqlite3_column_value( @NotNull sqlite3_stmt stmt, int ndx ); @@ -459,7 +459,7 @@ public final class SQLite3Jni { This functions like C's sqlite3_collation_needed16() because Java's string type is compatible with that interface. */ - public static synchronized native int sqlite3_collation_needed( + public static native int sqlite3_collation_needed( @NotNull sqlite3 db, @Nullable CollationNeeded callback ); @@ -467,11 +467,11 @@ public final class SQLite3Jni { Returns the db handle passed to sqlite3_open() or sqlite3_open_v2(), as opposed to a new wrapper object. */ - public static synchronized native sqlite3 sqlite3_context_db_handle( + public static native sqlite3 sqlite3_context_db_handle( @NotNull sqlite3_context cx ); - public static synchronized native CommitHook sqlite3_commit_hook( + public static native CommitHook sqlite3_commit_hook( @NotNull sqlite3 db, @Nullable CommitHook hook ); @@ -483,7 +483,7 @@ public final class SQLite3Jni { @NotNull String optName ); - public static synchronized native int sqlite3_create_collation( + public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, @NotNull Collation col ); @@ -496,16 +496,16 @@ public final class SQLite3Jni { SQLFunction's inner classes (Scalar, Aggregate, and Window) for details. */ - public static synchronized native int sqlite3_create_function( + public static native int sqlite3_create_function( @NotNull sqlite3 db, @NotNull String functionName, int nArg, int eTextRep, @NotNull SQLFunction func ); - public static synchronized native int sqlite3_data_count( + public static native int sqlite3_data_count( @NotNull sqlite3_stmt stmt ); - public static synchronized native String sqlite3_db_filename( + public static native String sqlite3_db_filename( @NotNull sqlite3 db, @NotNull String dbName ); @@ -514,7 +514,7 @@ public final class SQLite3Jni { variadic arguments. Returns SQLITE_MISUSE if op is not one of the SQLITE_DBCONFIG_... options which uses this call form. */ - public static synchronized native int sqlite3_db_config( + public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out ); @@ -525,37 +525,37 @@ public final class SQLite3Jni { SQLITE_DBCONFIG_MAINDBNAME, but that set of options may be extended in future versions. */ - public static synchronized native int sqlite3_db_config( + public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, @NotNull String val ); - public static synchronized native int sqlite3_db_status( + public static native int sqlite3_db_status( @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset ); - public static synchronized native int sqlite3_errcode(@NotNull sqlite3 db); + public static native int sqlite3_errcode(@NotNull sqlite3 db); - public static synchronized native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt); + public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt); - public static synchronized native int sqlite3_extended_errcode(@NotNull sqlite3 db); + public static native int sqlite3_extended_errcode(@NotNull sqlite3 db); - public static synchronized native boolean sqlite3_extended_result_codes( + public static native boolean sqlite3_extended_result_codes( @NotNull sqlite3 db, boolean onoff ); - public static synchronized native String sqlite3_errmsg(@NotNull sqlite3 db); + public static native String sqlite3_errmsg(@NotNull sqlite3 db); - public static synchronized native String sqlite3_errstr(int resultCode); + public static native String sqlite3_errstr(int resultCode); /** Note that the offset values assume UTF-8-encoded SQL. */ - public static synchronized native int sqlite3_error_offset(@NotNull sqlite3 db); + public static native int sqlite3_error_offset(@NotNull sqlite3 db); - public static synchronized native int sqlite3_finalize(@NotNull sqlite3_stmt stmt); + public static native int sqlite3_finalize(@NotNull sqlite3_stmt stmt); - public static synchronized native int sqlite3_initialize(); + public static native int sqlite3_initialize(); /** Design note/FIXME: we have a problem vis-a-vis 'synchronized' @@ -576,11 +576,11 @@ public final class SQLite3Jni { //! See sqlite3_interrupt() for threading concerns. public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db); - public static synchronized native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); + public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); - public static synchronized native String sqlite3_libversion(); + public static native String sqlite3_libversion(); - public static synchronized native int sqlite3_libversion_number(); + public static native int sqlite3_libversion_number(); /** Works like its C counterpart and makes the native pointer of the @@ -601,11 +601,11 @@ public final class SQLite3Jni { or sqlite3_open_v2() so that they have a predictible object to pass to, e.g., the sqlite3_collation_needed() callback. */ - public static synchronized native int sqlite3_open( + public static native int sqlite3_open( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb ); - public static synchronized native int sqlite3_open_v2( + public static native int sqlite3_open_v2( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, int flags, @Nullable String zVfs ); @@ -629,7 +629,7 @@ public final class SQLite3Jni { necessary, however, and overloads are provided which gloss over that. */ - private static synchronized native int sqlite3_prepare( + private static native int sqlite3_prepare( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull OutputPointer.sqlite3_stmt outStmt, @Nullable OutputPointer.Int32 pTailOffset @@ -658,7 +658,7 @@ public final class SQLite3Jni { return sqlite3_prepare(db, utf8, utf8.length, outStmt, null); } - private static synchronized native int sqlite3_prepare_v2( + private static native int sqlite3_prepare_v2( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull OutputPointer.sqlite3_stmt outStmt, @Nullable OutputPointer.Int32 pTailOffset @@ -687,7 +687,7 @@ public final class SQLite3Jni { return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null); } - private static synchronized native int sqlite3_prepare_v3( + private static native int sqlite3_prepare_v3( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt, @Nullable OutputPointer.Int32 pTailOffset @@ -716,22 +716,22 @@ public final class SQLite3Jni { return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null); } - public static synchronized native void sqlite3_progress_handler( + public static native void sqlite3_progress_handler( @NotNull sqlite3 db, int n, @Nullable ProgressHandler h ); //TODO??? void *sqlite3_preupdate_hook(...) and friends - public static synchronized native int sqlite3_reset(@NotNull sqlite3_stmt stmt); + public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt); /** Works like the C API except that it has no side effects if auto extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ - public static synchronized native void sqlite3_reset_auto_extension(); + public static native void sqlite3_reset_auto_extension(); - public static synchronized native void sqlite3_result_double( + public static native void sqlite3_result_double( @NotNull sqlite3_context cx, double v ); @@ -742,7 +742,7 @@ public final class SQLite3Jni { results in the C-level sqlite3_result_error() being called with a complaint about the invalid argument. */ - private static synchronized native void sqlite3_result_error( + private static native void sqlite3_result_error( @NotNull sqlite3_context cx, @Nullable byte[] msg, int eTextRep ); @@ -785,27 +785,27 @@ public final class SQLite3Jni { sqlite3_result_error16(cx, e.getMessage()); } - public static synchronized native void sqlite3_result_error_toobig( + public static native void sqlite3_result_error_toobig( @NotNull sqlite3_context cx ); - public static synchronized native void sqlite3_result_error_nomem( + public static native void sqlite3_result_error_nomem( @NotNull sqlite3_context cx ); - public static synchronized native void sqlite3_result_error_code( + public static native void sqlite3_result_error_code( @NotNull sqlite3_context cx, int c ); - public static synchronized native void sqlite3_result_null( + public static native void sqlite3_result_null( @NotNull sqlite3_context cx ); - public static synchronized native void sqlite3_result_int( + public static native void sqlite3_result_int( @NotNull sqlite3_context cx, int v ); - public static synchronized native void sqlite3_result_int64( + public static native void sqlite3_result_int64( @NotNull sqlite3_context cx, long v ); @@ -825,7 +825,7 @@ public final class SQLite3Jni { Note that there is no sqlite3_bind_java_object() counterpart. */ - public static synchronized native void sqlite3_result_java_object( + public static native void sqlite3_result_java_object( @NotNull sqlite3_context cx, @NotNull Object o ); @@ -883,19 +883,19 @@ public final class SQLite3Jni { sqlite3_result_text(cx, v); } - public static synchronized native void sqlite3_result_value( + public static native void sqlite3_result_value( @NotNull sqlite3_context cx, @NotNull sqlite3_value v ); - public static synchronized native void sqlite3_result_zeroblob( + public static native void sqlite3_result_zeroblob( @NotNull sqlite3_context cx, int n ); - public static synchronized native int sqlite3_result_zeroblob64( + public static native int sqlite3_result_zeroblob64( @NotNull sqlite3_context cx, long n ); - private static synchronized native void sqlite3_result_blob( + private static native void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable byte[] blob, int maxLen ); @@ -915,7 +915,7 @@ public final class SQLite3Jni { If maxLen is larger than blob.length, it is truncated to that value. If it is negative, results are undefined. */ - private static synchronized native void sqlite3_result_blob64( + private static native void sqlite3_result_blob64( @NotNull sqlite3_context cx, @Nullable byte[] blob, long maxLen ); @@ -925,7 +925,7 @@ public final class SQLite3Jni { sqlite3_result_blob64(cx, blob, (long)(null==blob ? 0 : blob.length)); } - private static synchronized native void sqlite3_result_text( + private static native void sqlite3_result_text( @NotNull sqlite3_context cx, @Nullable byte[] text, int maxLen ); @@ -959,17 +959,23 @@ public final class SQLite3Jni { text.length, it is silently truncated to text.length. If it is negative, results are undefined. */ - private static synchronized native void sqlite3_result_text64( + private static native void sqlite3_result_text64( @NotNull sqlite3_context cx, @Nullable byte[] text, long maxLength, int encoding ); - public static synchronized native int sqlite3_status( + /** + Cleans up all per-JNIEnv and per-db state managed by the library + then calls the C-native sqlite3_shutdown(). + */ + public static synchronized native int sqlite3_shutdown(); + + public static native int sqlite3_status( int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset ); - public static synchronized native int sqlite3_status64( + public static native int sqlite3_status64( int op, @NotNull OutputPointer.Int64 pCurrent, @NotNull OutputPointer.Int64 pHighwater, boolean reset ); @@ -1025,32 +1031,32 @@ public final class SQLite3Jni { sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16BE); } - public static synchronized native RollbackHook sqlite3_rollback_hook( + public static native RollbackHook sqlite3_rollback_hook( @NotNull sqlite3 db, @Nullable RollbackHook hook ); //! Sets or unsets (if auth is null) the current authorizer. - public static synchronized native int sqlite3_set_authorizer( + public static native int sqlite3_set_authorizer( @NotNull sqlite3 db, @Nullable Authorizer auth ); - public static synchronized native void sqlite3_set_last_insert_rowid( + public static native void sqlite3_set_last_insert_rowid( @NotNull sqlite3 db, long rowid ); - public static synchronized native int sqlite3_sleep(int ms); + public static native int sqlite3_sleep(int ms); - public static synchronized native String sqlite3_sourceid(); + public static native String sqlite3_sourceid(); - public static synchronized native String sqlite3_sql(@NotNull sqlite3_stmt stmt); + public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt); - public static synchronized native int sqlite3_step(@NotNull sqlite3_stmt stmt); + public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); /** Internal impl of the public sqlite3_strglob() method. Neither argument may be NULL and both _MUST_ be NUL-terminated. */ - private static synchronized native int sqlite3_strglob( + private static native int sqlite3_strglob( @NotNull byte[] glob, @NotNull byte[] txt ); @@ -1067,7 +1073,7 @@ public final class SQLite3Jni { Internal impl of the public sqlite3_strlike() method. Neither argument may be NULL and both _MUST_ be NUL-terminated. */ - private static synchronized native int sqlite3_strlike( + private static native int sqlite3_strlike( @NotNull byte[] glob, @NotNull byte[] txt, int escChar ); @@ -1081,11 +1087,11 @@ public final class SQLite3Jni { ); } - public static synchronized native int sqlite3_threadsafe(); + public static native int sqlite3_threadsafe(); - public static synchronized native int sqlite3_total_changes(@NotNull sqlite3 db); + public static native int sqlite3_total_changes(@NotNull sqlite3 db); - public static synchronized native long sqlite3_total_changes64(@NotNull sqlite3 db); + public static native long sqlite3_total_changes64(@NotNull sqlite3 db); /** Works like C's sqlite3_trace_v2() except that the 3rd argument to that @@ -1097,33 +1103,33 @@ public final class SQLite3Jni { mapping state fails and SQLITE_ERROR if the given callback object cannot be processed propertly (i.e. an internal error). */ - public static synchronized native int sqlite3_trace_v2( + public static native int sqlite3_trace_v2( @NotNull sqlite3 db, int traceMask, @Nullable Tracer tracer ); - public static synchronized native UpdateHook sqlite3_update_hook( + public static native UpdateHook sqlite3_update_hook( sqlite3 db, UpdateHook hook ); - public static synchronized native byte[] sqlite3_value_blob(@NotNull sqlite3_value v); + public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_bytes(@NotNull sqlite3_value v); + public static native int sqlite3_value_bytes(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_bytes16(@NotNull sqlite3_value v); + public static native int sqlite3_value_bytes16(@NotNull sqlite3_value v); - public static synchronized native double sqlite3_value_double(@NotNull sqlite3_value v); + public static native double sqlite3_value_double(@NotNull sqlite3_value v); - public static synchronized native sqlite3_value sqlite3_value_dupe( + public static native sqlite3_value sqlite3_value_dupe( @NotNull sqlite3_value v ); - public static synchronized native int sqlite3_value_encoding(@NotNull sqlite3_value v); + public static native int sqlite3_value_encoding(@NotNull sqlite3_value v); - public static synchronized native void sqlite3_value_free(@Nullable sqlite3_value v); + public static native void sqlite3_value_free(@Nullable sqlite3_value v); - public static synchronized native int sqlite3_value_int(@NotNull sqlite3_value v); + public static native int sqlite3_value_int(@NotNull sqlite3_value v); - public static synchronized native long sqlite3_value_int64(@NotNull sqlite3_value v); + public static native long sqlite3_value_int64(@NotNull sqlite3_value v); /** If the given value was set using sqlite3_result_java_value() then @@ -1132,7 +1138,7 @@ public final class SQLite3Jni { It is up to the caller to inspect the object to determine its type, and cast it if necessary. */ - public static synchronized native Object sqlite3_value_java_object( + public static native Object sqlite3_value_java_object( @NotNull sqlite3_value v ); @@ -1153,41 +1159,35 @@ public final class SQLite3Jni { See sqlite3_value_text_utf8() for how to extract text in standard UTF-8. */ - public static synchronized native String sqlite3_value_text(@NotNull sqlite3_value v); + public static native String sqlite3_value_text(@NotNull sqlite3_value v); /** The sqlite3_value counterpart of sqlite3_column_text_utf8(). */ - public static synchronized native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v); + public static native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v); - public static synchronized native byte[] sqlite3_value_text16(@NotNull sqlite3_value v); + public static native byte[] sqlite3_value_text16(@NotNull sqlite3_value v); - public static synchronized native byte[] sqlite3_value_text16le(@NotNull sqlite3_value v); + public static native byte[] sqlite3_value_text16le(@NotNull sqlite3_value v); - public static synchronized native byte[] sqlite3_value_text16be(@NotNull sqlite3_value v); + public static native byte[] sqlite3_value_text16be(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_type(@NotNull sqlite3_value v); + public static native int sqlite3_value_type(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_numeric_type(@NotNull sqlite3_value v); + public static native int sqlite3_value_numeric_type(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_nochange(@NotNull sqlite3_value v); + public static native int sqlite3_value_nochange(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_frombind(@NotNull sqlite3_value v); + public static native int sqlite3_value_frombind(@NotNull sqlite3_value v); - public static synchronized native int sqlite3_value_subtype(@NotNull sqlite3_value v); - - /** - Cleans up all per-JNIEnv and per-db state managed by the library - then calls the C-native sqlite3_shutdown(). - */ - public static synchronized native int sqlite3_shutdown(); + public static native int sqlite3_value_subtype(@NotNull sqlite3_value v); /** This is NOT part of the public API. It exists solely as a place to hook in arbitrary C-side code during development and testing of this library. */ - public static synchronized native void sqlite3_do_something_for_developer(); + public static native void sqlite3_do_something_for_developer(); ////////////////////////////////////////////////////////////////////// // SQLITE_... constants follow... diff --git a/manifest b/manifest index 0c792ccae0..d3b949dbf9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\ssqlite3_interrupt()\sand\ssqlite3_is_interrupted()\sto\sJNI\sbut\swith\scaveats\sregarding\smutexing\sof\sthe\sJNIEnv\scache.\sAdd\sa\sloud\swarning\sto\sthe\sJNI\s'dist'\starget\sthat\sit\sshould\sbe\sbuilt\swith\sJDK8\s(a.k.a.\sJava\s1.8)\sfor\scompatibility\sreasons. -D 2023-08-12T23:47:58.408 +C An\sinitial\sattempt\sat\sprotecting\sthe\sJNI\sglobal\sstate\svia\smutexes\sat\sthe\sC\slevel\sinstead\sof\srelying\son\sJava's\ssynchronized\skeyword.\sIt\sseems\sto\swork\sbut\sincreases\sthe\srun\stime\sof\sthe\ssingle-threaded\sbatch\stester\sby\sroughly\s3\stimes. +D 2023-08-13T09:53:27.824 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,8 +234,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 1d3bb5113ba4dd7f8645fcc4c669155931e44e234816f528642a738914dd45a4 -F ext/jni/src/c/sqlite3-jni.h 11bf3ab9682f5c393e6ac6a3ddb0fdf7b8dd40f7c77f9ef122d3e5c011a5d329 +F ext/jni/src/c/sqlite3-jni.c 93fbeed06441352df68f6c0e4bc2cd895e81d8550dc9972cafb30621958b4784 +F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1652af40fc0acb7a140dbe32e3146f980c37c28454b5115a4d0856cbdbc52696 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 7933fabc267dbfdccb784563d8c404f90275fe9418e8cd6c43c584bc9c57f70f F ext/jni/src/org/sqlite/jni/Tester1.java fc2ec1f1be58474112b9df8284f0157b64872107f446154c3d0bf1742b924d2b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -2091,8 +2091,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 48b13edcec6935bf125b265b41a3e6f7b2407afff89d5b4daa2939e3c5679ca0 -R 122775c5939e6f1540fadefe535658be +P fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe +R 42a3d0d4c63d13c26ffa7bd8f71380ed U stephan -Z a13d97b8681eef4145a4ab842cd3a9d2 +Z 5906c49c68c9155fc27b7d9430c5551e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 97ea0d6766..684bcd68e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe \ No newline at end of file +c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4 \ No newline at end of file From 4b4a911c5f69a9a5ba5ff83f2f0f9a377e7e7119 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 13 Aug 2023 10:28:35 +0000 Subject: [PATCH 003/422] Add some docs and metrics for the new mutex internals. FossilOrigin-Name: 33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a --- ext/jni/src/c/sqlite3-jni.c | 33 ++++++++++++++++++++++----------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 4522e05b45..eebe124f89 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -499,19 +499,25 @@ static struct { S3JniEnvCache * aHead /* Linked list of in-use instances */; S3JniEnvCache * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree */; - void const * locker /* env mutex is held on this object's behalf */; + void const * locker /* env mutex is held on this object's behalf + (used only for sanity checking). */; } envCache; struct { S3JniDb * aUsed /* Linked list of in-use instances */; S3JniDb * aFree /* Linked list of free instances */; - sqlite3_mutex * mutex /* mutex for aUsed and aFree */; - void const * locker /* perDb mutex is held on this object's behalf */; + sqlite3_mutex * mutex /* mutex for aUsed and aFree */; + void const * locker /* perDb mutex is held on this object's + behalf. Unlike envCache.locker, we + cannot always have this set to the + current JNIEnv object. */; } perDb; struct { unsigned nphCacheHits; unsigned nphCacheMisses; unsigned envCacheHits; unsigned envCacheMisses; + unsigned nMutexEnv /* number of times envCache.mutex was entered */; + unsigned nMutexPerDb /* number of times perDb.mutex was entered */; unsigned nDestroy /* xDestroy() calls across all types */; struct { /* Number of calls for each type of UDF callback. */ @@ -542,30 +548,28 @@ static struct { #define MUTEX_ASSERT_LOCKER_ENV \ assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ASSERT_LOCKER_PDB \ - assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define MUTEX_ASSERT_NOTLOCKER_ENV \ assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ASSERT_NOTLOCKER_PDB \ - assert( 0 == S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define MUTEX_ENTER_ENV \ /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_NOTLOCKER_ENV; \ sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ + ++S3JniGlobal.metrics.nMutexEnv; \ S3JniGlobal.envCache.locker = env #define MUTEX_LEAVE_ENV \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_LOCKER_ENV; \ S3JniGlobal.envCache.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.envCache.mutex ) +#define MUTEX_ASSERT_LOCKED_PDB \ + assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define MUTEX_ENTER_PDB \ /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ - MUTEX_ASSERT_NOTLOCKER_PDB; \ sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ + ++S3JniGlobal.metrics.nMutexPerDb; \ S3JniGlobal.perDb.locker = env; #define MUTEX_LEAVE_PDB \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - MUTEX_ASSERT_LOCKER_PDB; \ S3JniGlobal.perDb.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) @@ -899,7 +903,7 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest static void S3JniDb_set_aside(S3JniDb * const s){ if(s){ JNIEnv * const env = s->env; - MUTEX_ASSERT_LOCKER_PDB; + MUTEX_ASSERT_LOCKED_PDB; assert(s->pDb && "Else this object is already in the free-list."); //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); @@ -1035,6 +1039,10 @@ static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zCl you should look them up once and then reuse them. Similarly, looking up class objects can be expensive, so they should be cached as well. + + Reminder: we do not need a mutex for the envRow->nph cache + because all nph entries are per-thread and envCache.mutex + already guards the fetching of envRow. */ struct S3JniEnvCache * const envRow = S3JniGlobal_env_cache(env); S3JniNphCache * freeSlot = 0; @@ -1153,7 +1161,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zC static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; - MUTEX_ASSERT_LOCKER_PDB; + MUTEX_ASSERT_LOCKED_PDB; if(S3JniGlobal.perDb.aFree){ rv = S3JniGlobal.perDb.aFree; //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb)); @@ -3496,6 +3504,9 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ printf("\tJNIEnv cache %u misses, %u hits\n", S3JniGlobal.metrics.envCacheMisses, S3JniGlobal.metrics.envCacheHits); + printf("\tMutex entry: %u env, %u perDb\n", + S3JniGlobal.metrics.nMutexEnv, + S3JniGlobal.metrics.nMutexPerDb); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, S3JniGlobal.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); diff --git a/manifest b/manifest index d3b949dbf9..48db0129da 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C An\sinitial\sattempt\sat\sprotecting\sthe\sJNI\sglobal\sstate\svia\smutexes\sat\sthe\sC\slevel\sinstead\sof\srelying\son\sJava's\ssynchronized\skeyword.\sIt\sseems\sto\swork\sbut\sincreases\sthe\srun\stime\sof\sthe\ssingle-threaded\sbatch\stester\sby\sroughly\s3\stimes. -D 2023-08-13T09:53:27.824 +C Add\ssome\sdocs\sand\smetrics\sfor\sthe\snew\smutex\sinternals. +D 2023-08-13T10:28:35.456 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 93fbeed06441352df68f6c0e4bc2cd895e81d8550dc9972cafb30621958b4784 +F ext/jni/src/c/sqlite3-jni.c 44c1da6d1ae9d8ea2af16dd5c1d17224a655c9c94135892f81571b2481896431 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -2091,8 +2091,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 fbf99a2423dd20e4544bdeea85f714e9368ce3b92fefe97efb39a0fb4a557abe -R 42a3d0d4c63d13c26ffa7bd8f71380ed +P c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4 +R cd02d0fb58628c824a2d7de7efc47b02 U stephan -Z 5906c49c68c9155fc27b7d9430c5551e +Z 6ab46f9c79559c9df9412a6d1bef3261 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 684bcd68e8..0fe79c981a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4 \ No newline at end of file +33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a \ No newline at end of file From 88381e53fc9a689b88bb8a60b687653b5c7f820b Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 13 Aug 2023 12:40:27 +0000 Subject: [PATCH 004/422] Add a mutex for auto-extensions, tied in to the open() process since that's the route into auto-extensions. FossilOrigin-Name: 8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1 --- ext/jni/src/c/sqlite3-jni.c | 197 ++++++++++++--------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 42 +++-- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 140 insertions(+), 115 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index eebe124f89..149feef554 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -400,8 +400,6 @@ static void S3JniNphCache_clear(JNIEnv * const env, S3JniNphCache * const p){ memset(p, 0, sizeof(S3JniNphCache)); } -#define S3JNI_ENABLE_AUTOEXT 1 -#if S3JNI_ENABLE_AUTOEXT /* Whether auto extensions are feasible here is currently unknown due to... @@ -430,7 +428,6 @@ struct S3JniAutoExtension { S3JniAutoExtension *pNext /* next linked-list entry */; S3JniAutoExtension *pPrev /* previous linked-list entry */; }; -#endif /** State for various hook callbacks. */ typedef struct S3JniHook S3JniHook; @@ -518,6 +515,7 @@ static struct { unsigned envCacheMisses; unsigned nMutexEnv /* number of times envCache.mutex was entered */; unsigned nMutexPerDb /* number of times perDb.mutex was entered */; + unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; unsigned nDestroy /* xDestroy() calls across all types */; struct { /* Number of calls for each type of UDF callback. */ @@ -528,10 +526,9 @@ static struct { unsigned nInverse; } udf; } metrics; -#if S3JNI_ENABLE_AUTOEXT struct { S3JniAutoExtension *pHead /* Head of the auto-extension list */; - S3JniDb * psOpening /* FIXME: move into envCache. Handle to the + S3JniDb * pdbOpening /* FIXME: move into envCache. Handle to the being-opened db. We need this so that auto extensions can have a consistent view of the cross-language db connection and @@ -542,36 +539,50 @@ static struct { manipulation of the auto-extension list while extensions are running. */; + sqlite3_mutex * mutex /* mutex for aUsed and aFree */; + void const * locker /* Mutex is locked on this object's behalf */; } autoExt; -#endif } S3JniGlobal; #define MUTEX_ASSERT_LOCKER_ENV \ assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ASSERT_NOTLOCKER_ENV \ assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENTER_ENV \ +#define MUTEX_ENTER_ENV \ /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ - MUTEX_ASSERT_NOTLOCKER_ENV; \ - sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ - ++S3JniGlobal.metrics.nMutexEnv; \ + MUTEX_ASSERT_NOTLOCKER_ENV; \ + sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ + ++S3JniGlobal.metrics.nMutexEnv; \ S3JniGlobal.envCache.locker = env -#define MUTEX_LEAVE_ENV \ +#define MUTEX_LEAVE_ENV \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ - MUTEX_ASSERT_LOCKER_ENV; \ - S3JniGlobal.envCache.locker = 0; \ + MUTEX_ASSERT_LOCKER_ENV; \ + S3JniGlobal.envCache.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.envCache.mutex ) -#define MUTEX_ASSERT_LOCKED_PDB \ +#define MUTEX_ASSERT_LOCKED_PDB \ assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) -#define MUTEX_ENTER_PDB \ +#define MUTEX_ENTER_PDB \ /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ - ++S3JniGlobal.metrics.nMutexPerDb; \ + sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ + ++S3JniGlobal.metrics.nMutexPerDb; \ S3JniGlobal.perDb.locker = env; -#define MUTEX_LEAVE_PDB \ +#define MUTEX_LEAVE_PDB \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - S3JniGlobal.perDb.locker = 0; \ + S3JniGlobal.perDb.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) +#define MUTEX_ENTER_EXT \ + /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \ + sqlite3_mutex_enter( S3JniGlobal.autoExt.mutex ); \ + ++S3JniGlobal.metrics.nMutexAutoExt +#define MUTEX_TRY_EXT(FAIL_EXPR) \ + /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ + if( sqlite3_mutex_try( S3JniGlobal.autoExt.mutex ) ){ FAIL_EXPR; } \ + S3JniGlobal.autoExt.locker = env; \ + ++S3JniGlobal.metrics.nMutexAutoExt +#define MUTEX_LEAVE_EXT \ + /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ + S3JniGlobal.autoExt.locker = 0; \ + sqlite3_mutex_leave( S3JniGlobal.autoExt.mutex ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) static void s3jni_oom(JNIEnv * const env){ @@ -1023,7 +1034,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ This simple cache catches >99% of searches in the current (2023-07-31) tests. */ -FIXME_THREADING(S3JniEnvCache) static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zClassName){ /** According to: @@ -1041,8 +1051,8 @@ static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zCl cached as well. Reminder: we do not need a mutex for the envRow->nph cache - because all nph entries are per-thread and envCache.mutex - already guards the fetching of envRow. + because all nph entries are per-thread and envCache.mutex already + guards the fetching of envRow. */ struct S3JniEnvCache * const envRow = S3JniGlobal_env_cache(env); S3JniNphCache * freeSlot = 0; @@ -1221,8 +1231,6 @@ static void S3JniDb_dump(S3JniDb *s){ Returns NULL if jDb and pDb are both NULL or if there is no matching S3JniDb entry for pDb or the pointer fished out of jDb. */ -FIXME_THREADING(S3JniEnvCache) -FIXME_THREADING(perDb) static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if(jDb || pDb){ @@ -1242,7 +1250,6 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ return s; } -#if S3JNI_ENABLE_AUTOEXT /** Unlink ax from S3JniGlobal.autoExt and free it. */ @@ -1279,7 +1286,7 @@ static S3JniAutoExtension * S3JniAutoExtension_alloc(JNIEnv *const env, return 0; } ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", - "(Lorg/sqlite/jni/sqlite3;)I"); + "(Lorg/sqlite/jni/sqlite3;)I"); if(!ax->midFunc){ MARKER(("Error getting xEntryPoint(sqlite3) from object.")); S3JniAutoExtension_free(env, ax); @@ -1292,7 +1299,6 @@ static S3JniAutoExtension * S3JniAutoExtension_alloc(JNIEnv *const env, } return ax; } -#endif /* S3JNI_ENABLE_AUTOEXT */ /** Requires that jCx be a Java-side sqlite3_context wrapper for pCx. @@ -1881,35 +1887,37 @@ 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 +static JNIEnv * s3jni_get_env(void){ + JNIEnv * env = 0; + if( (*S3JniGlobal.jvm)->GetEnv(S3JniGlobal.jvm, (void **)&env, + JNI_VERSION_1_8) ){ + fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); + abort(); + } + return env; +} + /* Central auto-extension handler. */ -FIXME_THREADING(autoExt) -static int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr, - const struct sqlite3_api_routines *ignored){ +static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, + const struct sqlite3_api_routines *ignored){ S3JniAutoExtension const * pAX = S3JniGlobal.autoExt.pHead; int rc; JNIEnv * env = 0; - S3JniDb * const ps = S3JniGlobal.autoExt.psOpening; - //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb)); - S3JniGlobal.autoExt.psOpening = 0; + S3JniDb * const ps = S3JniGlobal.autoExt.pdbOpening; + + assert( S3JniGlobal.autoExt.locker ); + assert( S3JniGlobal.autoExt.locker == ps ); + S3JniGlobal.autoExt.pdbOpening = 0; if( !pAX ){ assert( 0==S3JniGlobal.autoExt.isRunning ); return 0; - } - else if( S3JniGlobal.autoExt.isRunning ){ - /* Necessary to avoid certain endless loop/stack overflow cases. */ - *pzErr = sqlite3_mprintf("Auto-extensions must not be triggered while " - "auto-extensions are running."); - return SQLITE_MISUSE; - } - else if(!ps){ - MARKER(("Internal error: cannot find S3JniDb for auto-extension\n")); - return SQLITE_ERROR; - }else if( (*S3JniGlobal.jvm)->GetEnv(S3JniGlobal.jvm, (void **)&env, JNI_VERSION_1_8) ){ - assert(!"Cannot get JNIEnv"); - *pzErr = sqlite3_mprintf("Could not get current JNIEnv."); + }else if( S3JniGlobal.autoExt.locker != ps ) { + *pzErr = sqlite3_mprintf("Internal error: unexpected path lead to " + "running an auto-extension."); return SQLITE_ERROR; } + env = s3jni_get_env(); + //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb)); assert( !ps->pDb /* it's still being opened */ ); ps->pDb = pDb; assert( ps->jDb ); @@ -1941,8 +1949,9 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ S3JniAutoExtension * ax; if( !jAutoExt ) return SQLITE_MISUSE; - else if( 0==once && ++once ){ - sqlite3_auto_extension( (void(*)(void))s3jni_auto_extension ); + MUTEX_ENTER_EXT; + if( 0==once && ++once ){ + sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); } ax = S3JniGlobal.autoExt.pHead; for( ; ax; ax = ax->pNext ){ @@ -1950,9 +1959,10 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ return 0 /* C API treats this as a no-op. */; } } - return S3JniAutoExtension_alloc(env, jAutoExt) ? 0 : SQLITE_NOMEM; + ax = S3JniAutoExtension_alloc(env, jAutoExt); + MUTEX_LEAVE_EXT; + return ax ? 0 : SQLITE_NOMEM; } -#endif /* S3JNI_ENABLE_AUTOEXT */ FIXME_THREADING(S3JniEnvCache) JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, @@ -2088,20 +2098,23 @@ JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ return SQLITE_MISUSE; } -#if S3JNI_ENABLE_AUTOEXT FIXME_THREADING(autoExt) JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ - S3JniAutoExtension * ax;; - if( S3JniGlobal.autoExt.isRunning ) return JNI_FALSE; - for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ - if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - S3JniAutoExtension_free(env, ax); - return JNI_TRUE; + S3JniAutoExtension * ax; + jboolean rc = JNI_FALSE; + MUTEX_ENTER_EXT; + if( !S3JniGlobal.autoExt.isRunning ) { + for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ + if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + S3JniAutoExtension_free(env, ax); + rc = JNI_TRUE; + break; + } } } - return JNI_FALSE; + MUTEX_LEAVE_EXT; + return rc; } -#endif /* S3JNI_ENABLE_AUTOEXT */ /** @@ -2649,27 +2662,41 @@ JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc, jstring jDbName, char **zDbName, S3JniDb ** ps, jobject *jDb){ + int rc = 0; + MUTEX_TRY_EXT(return SQLITE_BUSY); *jc = S3JniGlobal_env_cache(env); - if(!*jc) return SQLITE_NOMEM; + if(!*jc){ + rc = SQLITE_NOMEM; + goto end; + } *zDbName = jDbName ? s3jni_jstring_to_utf8(*jc, jDbName, 0) : 0; - if(jDbName && !*zDbName) return SQLITE_NOMEM; + if(jDbName && !*zDbName){ + rc = SQLITE_NOMEM; + goto end; + } *jDb = new_sqlite3_wrapper(env, 0); if( !*jDb ){ sqlite3_free(*zDbName); *zDbName = 0; - return SQLITE_NOMEM; + rc = SQLITE_NOMEM; + goto end; } MUTEX_ENTER_PDB; *ps = S3JniDb_alloc(env, 0, *jDb); MUTEX_LEAVE_PDB; -#if S3JNI_ENABLE_AUTOEXT if(*ps){ - assert(!S3JniGlobal.autoExt.psOpening); - S3JniGlobal.autoExt.psOpening = *ps; + S3JniGlobal.autoExt.pdbOpening = *ps; + S3JniGlobal.autoExt.locker = *ps; + }else{ + rc = SQLITE_NOMEM; } -#endif //MARKER(("pre-open ps@%p\n", *ps)); - return *ps ? 0 : SQLITE_NOMEM; +end: + /* Remain in autoExt.mutex until s3jni_open_post(). */ + if(rc){ + MUTEX_LEAVE_EXT; + } + return rc; } /** @@ -2686,10 +2713,8 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc, static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, sqlite3 **ppDb, jobject jOut, int theRc){ //MARKER(("post-open() ps@%p db@%p\n", ps, *ppDb)); -#if S3JNI_ENABLE_AUTOEXT - assert( S3JniGlobal.autoExt.pHead ? ps!=S3JniGlobal.autoExt.psOpening : 1 ); - S3JniGlobal.autoExt.psOpening = 0; -#endif + assert( S3JniGlobal.autoExt.locker == ps ); + S3JniGlobal.autoExt.pdbOpening = 0; if(*ppDb){ assert(ps->jDb); ps->pDb = *ppDb; @@ -2701,6 +2726,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, ps = 0; } OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0); + MUTEX_LEAVE_EXT; return theRc; } @@ -2710,8 +2736,8 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ jobject jDb = 0; S3JniDb * ps = 0; S3JniEnvCache * jc = 0; - S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening; - int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); + S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; + int rc= s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); if( 0==rc ){ rc = sqlite3_open(zName, &pOut); //MARKER(("env=%p, *env=%p\n", env, *env)); @@ -2720,7 +2746,7 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); } - S3JniGlobal.autoExt.psOpening = prevOpening; + S3JniGlobal.autoExt.pdbOpening = prevOpening; return (jint)rc; } @@ -2732,7 +2758,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, S3JniDb * ps = 0; S3JniEnvCache * jc = 0; char *zVfs = 0; - S3JniDb * const prevOpening = S3JniGlobal.autoExt.psOpening; + S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); if( 0==rc && strVfs ){ zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0); @@ -2747,10 +2773,10 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, /*MARKER(("zName=%s, zVfs=%s, pOut=%p, flags=%d, nrc=%d\n", zName, zVfs, pOut, (int)flags, nrc));*/ rc = s3jni_open_post(env, ps, &pOut, jOut, rc); + S3JniGlobal.autoExt.pdbOpening = prevOpening; assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); sqlite3_free(zVfs); - S3JniGlobal.autoExt.psOpening = prevOpening; return (jint)rc; } @@ -2886,15 +2912,13 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ return rc; } -#if S3JNI_ENABLE_AUTOEXT JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ - if(!S3JniGlobal.autoExt.isRunning){ - while( S3JniGlobal.autoExt.pHead ){ - S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead); - } + MUTEX_ENTER_EXT; + while( S3JniGlobal.autoExt.pHead ){ + S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead); } + MUTEX_LEAVE_EXT; } -#endif /* S3JNI_ENABLE_AUTOEXT */ /* sqlite3_result_text/blob() and friends. */ static void result_blob_text(int asBlob, int as64, @@ -3504,9 +3528,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ printf("\tJNIEnv cache %u misses, %u hits\n", S3JniGlobal.metrics.envCacheMisses, S3JniGlobal.metrics.envCacheHits); - printf("\tMutex entry: %u env, %u perDb\n", + printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt (mostly via open[_v2]())\n", S3JniGlobal.metrics.nMutexEnv, - S3JniGlobal.metrics.nMutexPerDb); + S3JniGlobal.metrics.nMutexPerDb, + S3JniGlobal.metrics.nMutexAutoExt); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, S3JniGlobal.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); @@ -4418,6 +4443,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ OOM_CHECK( S3JniGlobal.envCache.mutex ); S3JniGlobal.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); OOM_CHECK( S3JniGlobal.perDb.mutex ); + S3JniGlobal.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( S3JniGlobal.autoExt.mutex ); #if 0 /* Just for sanity checking... */ (void)S3JniGlobal_env_cache(env); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index e7aa10ebae..dbd2e4bae7 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -178,24 +178,25 @@ public final class SQLite3Jni { not have access to the sqlite3_api object which native auto-extensions do. - - If an auto-extension opens a db, thereby triggering recursion - in the auto-extension handler, it will fail with a message - explaining that recursion is not permitted. + - If an auto-extension opens a db, opening will fail with SQLITE_BUSY. + The alternative would be endless recursion into the auto-extension. - - All of the other auto extension routines will fail without side - effects if invoked from within the execution of an - auto-extension. i.e. auto extensions can neither be added, + - The list of auto-extensions must not be manipulated from within + an auto-extension. Auto extensions can neither be added, removed, nor cleared while one registered with this function is - running. Auto-extensions registered directly with the library - via C code, as opposed to indirectly via Java, do not have that - limitation. + running. Attempting to do so may lead to a deadlock. See the AutoExtension class docs for more information. Achtung: it is as yet unknown whether auto extensions registered from one JNIEnv (thread) can be safely called from another. + + Design note: this family of methods is synchronized in order to + help avoid a small race condition where an in-progress + sqlite3_reset_auto_extension() or sqlite3_cancel_auto_extension() + could cause sqlite3_open() to fail with SQLITE_BUSY. */ - public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); + public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback); public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @@ -296,7 +297,7 @@ public final class SQLite3Jni { effects, if auto extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ - public static native boolean sqlite3_cancel_auto_extension( + public static synchronized native boolean sqlite3_cancel_auto_extension( @NotNull AutoExtension ax ); @@ -313,11 +314,11 @@ public final class SQLite3Jni { ); public static native int sqlite3_close( - @NotNull sqlite3 db + @Nullable sqlite3 db ); public static native int sqlite3_close_v2( - @NotNull sqlite3 db + @Nullable sqlite3 db ); public static native byte[] sqlite3_column_blob( @@ -593,19 +594,16 @@ public final class SQLite3Jni { Recall that even if opening fails, the output pointer might be non-null. Any error message about the failure will be in that object and it is up to the caller to sqlite3_close() that - db handle. + db handle. Passing a null to sqlite3_close() is legal. - Pedantic note: though any number of Java-level sqlite3 objects - may refer to/wrap a single C-level (sqlite3*), the JNI internals - take a reference to the object which is passed to sqlite3_open() - or sqlite3_open_v2() so that they have a predictible object to - pass to, e.g., the sqlite3_collation_needed() callback. + Design note: this method is synchronized in order to help + alleviate a race condition involving auto-extensions. */ - public static native int sqlite3_open( + public static synchronized native int sqlite3_open( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb ); - public static native int sqlite3_open_v2( + public static synchronized native int sqlite3_open_v2( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, int flags, @Nullable String zVfs ); @@ -729,7 +727,7 @@ public final class SQLite3Jni { extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ - public static native void sqlite3_reset_auto_extension(); + public static synchronized native void sqlite3_reset_auto_extension(); public static native void sqlite3_result_double( @NotNull sqlite3_context cx, double v diff --git a/manifest b/manifest index 48db0129da..dbbd37eaaa 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssome\sdocs\sand\smetrics\sfor\sthe\snew\smutex\sinternals. -D 2023-08-13T10:28:35.456 +C Add\sa\smutex\sfor\sauto-extensions,\stied\sin\sto\sthe\sopen()\sprocess\ssince\sthat's\sthe\sroute\sinto\sauto-extensions. +D 2023-08-13T12:40:27.217 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 44c1da6d1ae9d8ea2af16dd5c1d17224a655c9c94135892f81571b2481896431 +F ext/jni/src/c/sqlite3-jni.c 4f6f8f2dec309a6b117a6e8f460078b5f6f6b65a17c530ae2f5907b6425d714c F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 7933fabc267dbfdccb784563d8c404f90275fe9418e8cd6c43c584bc9c57f70f +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5eeba0b1a00fb34bc93fe60186f6032fcf4d568fc5868d70029883d3d07cc306 F ext/jni/src/org/sqlite/jni/Tester1.java fc2ec1f1be58474112b9df8284f0157b64872107f446154c3d0bf1742b924d2b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -2091,8 +2091,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 c64e6a52ac79164be37fe643a4a39bd187af198a379410def8b8419f7c2224d4 -R cd02d0fb58628c824a2d7de7efc47b02 +P 33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a +R 6a1ce79c2368c04d74cde070b44b6572 U stephan -Z 6ab46f9c79559c9df9412a6d1bef3261 +Z d637a436f204decfbfbf40fa44769c67 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0fe79c981a..4f1f622ee9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a \ No newline at end of file +8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1 \ No newline at end of file From 6a1ed4c811d3a62df9ff7a9f9662fe7060b6c271 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 13 Aug 2023 20:58:12 +0000 Subject: [PATCH 005/422] Internal API renaming for clarity's sake. FossilOrigin-Name: 911e4fc5aaf9478214095a65f74af3ebca883922c36cf7a8d911116c42cf9de8 --- ext/jni/src/c/sqlite3-jni.c | 133 ++++++++++++++++++------------------ manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 72 insertions(+), 75 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 149feef554..55ecd4b2dc 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -329,7 +329,8 @@ enum { }; /** - Cache entry for NativePointerHolder lookups. + Cache entry for NativePointerHolder subclasses and OutputPointer + types. */ typedef struct S3JniNphCache S3JniNphCache; struct S3JniNphCache { @@ -358,8 +359,8 @@ struct S3JniNphCache { Whereas we cache new refs for each thread. */ -typedef struct S3JniEnvCache S3JniEnvCache; -struct S3JniEnvCache { +typedef struct S3JniEnv S3JniEnv; +struct S3JniEnv { JNIEnv *env /* env in which this cache entry was created */; //! The various refs to global classes might be cacheable a single // time globally. Information online seems inconsistent on that @@ -377,20 +378,15 @@ struct S3JniEnvCache { jobject jFtsExt /* Global ref to Java singleton for the Fts5ExtensionApi instance. */; struct { - jclass klazz; - jfieldID fidA; - jfieldID fidB; + jclass klazz /* Global ref to the Fts5Phrase iter class */; + jfieldID fidA /* Fts5Phrase::a member */; + jfieldID fidB /* Fts5Phrase::b member */; } jPhraseIter; #endif - S3JniEnvCache * pPrev /* Previous entry in the linked list */; - S3JniEnvCache * pNext /* Next entry in the linked list */; - /** TODO?: S3JniNphCache *pNphHit; - - and always set it to the most recent cache search result. - - The intent would be to help fast-track cache lookups and would - speed up, e.g., the sqlite3_value-to-Java-array loop in a - multi-threaded app. + S3JniEnv * pPrev /* Previous entry in the linked list */; + S3JniEnv * pNext /* Next entry in the linked list */; + /** + Cache of Java refs/IDs for NativePointerHolder subclasses. */ S3JniNphCache nph[NphCache_SIZE]; }; @@ -493,8 +489,8 @@ static struct { */ JavaVM * jvm; struct { - S3JniEnvCache * aHead /* Linked list of in-use instances */; - S3JniEnvCache * aFree /* Linked list of free instances */; + S3JniEnv * aHead /* Linked list of in-use instances */; + S3JniEnv * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree */; void const * locker /* env mutex is held on this object's behalf (used only for sanity checking). */; @@ -607,8 +603,8 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ insofar as possible. Calls (*env)->FatalError() if allocation of an entry fails. That's hypothetically possible but "shouldn't happen." */ -static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){ - struct S3JniEnvCache * row; +static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ + struct S3JniEnv * row; MUTEX_ENTER_ENV; row = S3JniGlobal.envCache.aHead; for( ; row; row = row->pNext ){ @@ -625,7 +621,7 @@ static S3JniEnvCache * S3JniGlobal_env_cache(JNIEnv * const env){ S3JniGlobal.envCache.aFree = row->pNext; if( row->pNext ) row->pNext->pPrev = 0; }else{ - row = sqlite3_malloc(sizeof(S3JniEnvCache)); + row = sqlite3_malloc(sizeof(S3JniEnv)); OOM_CHECK(row); } memset(row, 0, sizeof(*row)); @@ -721,7 +717,7 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, const unsigned char * standard UTF-8 to a Java string, but JNI offers only algorithms for working with MUTF-8, not UTF-8. */ -static jstring s3jni_utf8_to_jstring(S3JniEnvCache * const jc, +static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, const char * const z, int n){ jstring rv = NULL; JNIEnv * const env = jc->env; @@ -758,7 +754,7 @@ static jstring s3jni_utf8_to_jstring(S3JniEnvCache * const jc, The returned memory is allocated from sqlite3_malloc() and ownership is transferred to the caller. */ -static char * s3jni_jstring_to_utf8(S3JniEnvCache * const jc, +static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, jstring jstr, int *nLen){ JNIEnv * const env = jc->env; jbyteArray jba; @@ -813,7 +809,7 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, } */ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); jmethodID mid; jstring msg; char * zMsg; @@ -981,7 +977,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ what would otherwise be stale references. */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ - struct S3JniEnvCache * row; + struct S3JniEnv * row; int i; assert( 0!=S3JniGlobal.envCache.mutex && "Env mutex misuse."); row = S3JniGlobal.envCache.aHead; @@ -1011,7 +1007,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ for( i = 0; i < NphCache_SIZE; ++i ){ S3JniNphCache_clear(env, &row->nph[i]); } - memset(row, 0, sizeof(S3JniEnvCache)); + memset(row, 0, sizeof(S3JniEnv)); row->pNext = S3JniGlobal.envCache.aFree; if( row->pNext ) row->pNext->pPrev = row; S3JniGlobal.envCache.aFree = row; @@ -1054,7 +1050,7 @@ static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zCl because all nph entries are per-thread and envCache.mutex already guards the fetching of envRow. */ - struct S3JniEnvCache * const envRow = S3JniGlobal_env_cache(env); + struct S3JniEnv * const envRow = S3JniGlobal_env_cache(env); S3JniNphCache * freeSlot = 0; S3JniNphCache * pCache = 0; int i; @@ -1438,6 +1434,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, EXCEPTION_IS_FATAL("Cannot set OutputPointer.ByteArray.value"); } #endif + /* Sets the value property of the OutputPointer.String jOut object to v. */ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, @@ -1964,7 +1961,7 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ return ax ? 0 : SQLITE_NOMEM; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ int rc; @@ -1979,31 +1976,31 @@ JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, return (jint)rc; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1double)(JENV_CSELF, jobject jpStmt, jint ndx, jdouble val){ return (jint)sqlite3_bind_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1int)(JENV_CSELF, jobject jpStmt, jint ndx, jint val){ return (jint)sqlite3_bind_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1int64)(JENV_CSELF, jobject jpStmt, jint ndx, jlong val){ return (jint)sqlite3_bind_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1null)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jint)sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName){ int rc = 0; jbyte * const pBuf = JBA_TOC(jName); @@ -2015,7 +2012,7 @@ JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName return rc; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ if(baData){ @@ -2029,13 +2026,13 @@ JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, } } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1zeroblob)(JENV_CSELF, jobject jpStmt, jint ndx, jint n){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1zeroblob64)(JENV_CSELF, jobject jpStmt, jint ndx, jlong n){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); @@ -2057,7 +2054,7 @@ static int s3jni_busy_handler(void* pState, int n){ return rc; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc = 0; @@ -2087,7 +2084,7 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ : sqlite3_busy_handler(ps->pDb, 0, 0); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) FIXME_THREADING(perDb) JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); @@ -2136,13 +2133,13 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ return (jint)rc; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) FIXME_THREADING(perDb) JDECL(jint,1close_1v2)(JENV_CSELF, jobject pDb){ return s3jni_close_db(env, pDb, 2); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) FIXME_THREADING(perDb) JDECL(jint,1close)(JENV_CSELF, jobject pDb){ return s3jni_close_db(env, pDb, 1); @@ -2182,7 +2179,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, UNREF_L(jName); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) FIXME_THREADING(perDb) JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); @@ -2220,7 +2217,7 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ return rc; } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2234,25 +2231,25 @@ JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, } } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jdouble,1column_1double)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jdouble)sqlite3_column_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jint,1column_1int)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jint)sqlite3_column_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jlong,1column_1int64)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jbyteArray,1column_1text)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2261,7 +2258,7 @@ JDECL(jbyteArray,1column_1text)(JENV_CSELF, jobject jpStmt, return s3jni_new_jbyteArray(env, p, n); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2270,7 +2267,7 @@ JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, return s3jni_text16_to_jstring(env, p, n); } -FIXME_THREADING(S3JniEnvCache) +FIXME_THREADING(S3JniEnv) JDECL(jobject,1column_1value)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_value * const sv = sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); @@ -2554,7 +2551,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); char *zDbName; jstring jRv = 0; int nStr = 0; @@ -2594,7 +2591,7 @@ JDECL(jint,1errcode)(JENV_CSELF, jobject jpDb){ JDECL(jstring,1errmsg)(JENV_CSELF, jobject jpDb){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - S3JniEnvCache * const jc = pDb ? S3JniGlobal_env_cache(env) : 0; + S3JniEnv * const jc = pDb ? S3JniGlobal_env_cache(env) : 0; return jc ? s3jni_utf8_to_jstring(jc, sqlite3_errmsg(pDb), -1) : 0; } @@ -2608,7 +2605,7 @@ JDECL(jstring,1expanded_1sql)(JENV_CSELF, jobject jpStmt){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); jstring rv = 0; if( pStmt ){ - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); char * zSql = sqlite3_expanded_sql(pStmt); OOM_CHECK(zSql); if( zSql ){ @@ -2659,7 +2656,7 @@ JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){ } //! Pre-open() code common to sqlite3_open(_v2)(). -static int s3jni_open_pre(JNIEnv * const env, S3JniEnvCache **jc, +static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, S3JniDb ** ps, jobject *jDb){ int rc = 0; @@ -2735,7 +2732,7 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ char *zName = 0; jobject jDb = 0; S3JniDb * ps = 0; - S3JniEnvCache * jc = 0; + S3JniEnv * jc = 0; S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; int rc= s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); if( 0==rc ){ @@ -2756,7 +2753,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, char *zName = 0; jobject jDb = 0; S3JniDb * ps = 0; - S3JniEnvCache * jc = 0; + S3JniEnv * jc = 0; char *zVfs = 0; S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); @@ -3235,7 +3232,7 @@ JDECL(jstring,1sql)(JENV_CSELF, jobject jpStmt){ jstring rv = 0; if( pStmt ){ const char * zSql = 0; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); zSql = sqlite3_sql(pStmt); rv = s3jni_utf8_to_jstring(jc, zSql, -1); OOM_CHECK(rv); @@ -3258,7 +3255,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ jobject jX = NULL /* the tracer's X arg */; jobject jP = NULL /* the tracer's P arg */; jobject jPUnref = NULL /* potentially a local ref to jP */; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); int rc; int createStmt = 0; switch(traceflag){ @@ -3512,7 +3509,7 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ puts("sizeofs:"); #define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T)) SO(void*); - SO(S3JniEnvCache); + SO(S3JniEnv); SO(S3JniHook); SO(S3JniDb); SO(S3JniClassNames); @@ -3642,7 +3639,7 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ instance, or NULL on OOM. */ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ - S3JniEnvCache * const row = S3JniGlobal_env_cache(env); + S3JniEnv * const row = S3JniGlobal_env_cache(env); if( !row->jFtsExt ){ row->jFtsExt = new_NativePointerHolder_object(env, S3JniClassNames.Fts5ExtensionApi, s3jni_ftsext()); @@ -3709,7 +3706,7 @@ JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol, int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol, &pz, &pn); if( 0==rc ){ - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0; if( pz ){ if( jstr ){ @@ -3866,7 +3863,7 @@ JDECLFtsXA(jint,xPhraseCount)(JENV_OSELF,jobject jCtx){ /** Initializes jc->jPhraseIter if it needed it. */ -static void s3jni_phraseIter_init(JNIEnv *const env, S3JniEnvCache * const jc, +static void s3jni_phraseIter_init(JNIEnv *const env, S3JniEnv * const jc, jobject jIter){ if(!jc->jPhraseIter.klazz){ jclass klazz = (*env)->GetObjectClass(env, jIter); @@ -3879,7 +3876,7 @@ static void s3jni_phraseIter_init(JNIEnv *const env, S3JniEnvCache * const jc, } /* Copy the 'a' and 'b' fields from pSrc to Fts5PhraseIter object jIter. */ -static void s3jni_phraseIter_NToJ(JNIEnv *const env, S3JniEnvCache const * const jc, +static void s3jni_phraseIter_NToJ(JNIEnv *const env, S3JniEnv const * const jc, Fts5PhraseIter const * const pSrc, jobject jIter){ assert(jc->jPhraseIter.klazz); @@ -3890,7 +3887,7 @@ static void s3jni_phraseIter_NToJ(JNIEnv *const env, S3JniEnvCache const * const } /* Copy the 'a' and 'b' fields from Fts5PhraseIter object jIter to pDest. */ -static void s3jni_phraseIter_JToN(JNIEnv *const env, S3JniEnvCache const * const jc, +static void s3jni_phraseIter_JToN(JNIEnv *const env, S3JniEnv const * const jc, jobject jIter, Fts5PhraseIter * const pDest){ assert(jc->jPhraseIter.klazz); pDest->a = @@ -3905,7 +3902,7 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int rc, iCol = 0, iOff = 0; s3jni_phraseIter_init(env, jc, jIter); @@ -3922,7 +3919,7 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int rc, iCol = 0; s3jni_phraseIter_init(env, jc, jIter); @@ -3938,7 +3935,7 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int iCol = 0, iOff = 0; if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/; @@ -3953,7 +3950,7 @@ JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter, jobject jOutCol){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int iCol = 0; if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/; @@ -3975,7 +3972,7 @@ JDECLFtsXA(jint,xPhraseSize)(JENV_OSELF,jobject jCtx, jint iPhrase){ struct s3jni_xQueryPhraseState { JNIEnv *env; Fts5ExtensionApi const * fext; - S3JniEnvCache const * jc; + S3JniEnv const * jc; jmethodID midCallback; jobject jCallback; jobject jFcx; @@ -4007,7 +4004,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, jobject jCallback){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); struct s3jni_xQueryPhraseState s; jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; if( !klazz ) return SQLITE_MISUSE; @@ -4093,7 +4090,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, const char *zClassName, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; - S3JniEnvCache * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniGlobal_env_cache(env); struct s3jni_xQueryPhraseState s; int rc = 0; jbyte * const pText = jCallback ? JBA_TOC(jbaText) : 0; diff --git a/manifest b/manifest index dbbd37eaaa..ad2950dc49 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\smutex\sfor\sauto-extensions,\stied\sin\sto\sthe\sopen()\sprocess\ssince\sthat's\sthe\sroute\sinto\sauto-extensions. -D 2023-08-13T12:40:27.217 +C Internal\sAPI\srenaming\sfor\sclarity's\ssake. +D 2023-08-13T20:58:12.729 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 4f6f8f2dec309a6b117a6e8f460078b5f6f6b65a17c530ae2f5907b6425d714c +F ext/jni/src/c/sqlite3-jni.c 1b1f3fa286476933171e75465214deab44c55714aaaa80b5ce145e89f10b0df8 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -2091,8 +2091,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 33d1780b43182d2574adbc1928707af825c485c99762738e58bc6d7c6c52ac6a -R 6a1ce79c2368c04d74cde070b44b6572 +P 8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1 +R 582810911695a14eb1f701cb46e67a37 U stephan -Z d637a436f204decfbfbf40fa44769c67 +Z 414dea18ee7e2187dd201f9308cbe98b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4f1f622ee9..6ade59ea97 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1 \ No newline at end of file +911e4fc5aaf9478214095a65f74af3ebca883922c36cf7a8d911116c42cf9de8 \ No newline at end of file From d518e94adbf2555b603bee5f2aa628f82cf1fdd9 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 14 Aug 2023 08:28:46 +0000 Subject: [PATCH 006/422] JNI-internal docs and removal of obsolete code. FossilOrigin-Name: b62d93258b6a661f3a9b61468b3b641c14faf2d2196f78aca95fe14de43c9444 --- ext/jni/src/c/sqlite3-jni.c | 81 ++++++++++++++++--------- ext/jni/src/org/sqlite/jni/Tester1.java | 9 ++- manifest | 14 ++--- manifest.uuid | 2 +- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 55ecd4b2dc..129a601210 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -432,9 +432,11 @@ struct S3JniHook{ jmethodID midCallback /* callback method. Signature depends on jObj's type */; jclass klazz /* global ref to jObj's class. Only needed - by hooks which have an xDestroy() method, - as lookup of that method is deferred - until the object requires cleanup. */; + by hooks which have an xDestroy() method. + We can probably eliminate this and simply + do the class lookup at the same + (deferred) time we do the xDestroy() + lookup. */; }; /** @@ -522,19 +524,38 @@ static struct { unsigned nInverse; } udf; } metrics; + /** + The list of bound auto-extensions (Java-side: + org.sqlite.jni.AutoExtension objects). Because this data + structure cannot be manipulated during traversal (without adding + more code to deal with it), and to avoid a small window where a + call to sqlite3_reset/clear_auto_extension() could lock the + structure during an open() call, we lock this mutex before + sqlite3_open() is called and unlock it once sqlite3_open() + returns. + */ struct { S3JniAutoExtension *pHead /* Head of the auto-extension list */; - S3JniDb * pdbOpening /* FIXME: move into envCache. Handle to the - being-opened db. We need this so that auto - extensions can have a consistent view of - the cross-language db connection and - behave property if they call further db - APIs. */; - int isRunning /* True while auto extensions are - running. This is used to prohibit - manipulation of the auto-extension - list while extensions are - running. */; + /** + pdbOpening is used to coordinate the Java/DB connection of a + being-open()'d db. "The problem" is that auto-extensions run + before we can bind the C db to its Java representation, but + auto-extensions require that binding. We handle this as + follows: + + - At the start of open(), we lock on this->mutex. + - Allocate the Java side of that connection and set pdbOpening + to point to that object. + - Call open(), which triggers the auto-extension handler. + That handler uses pdbOpening to connect the native db handle + which it recieves with pdbOpening. + - Return from open(). + - Clean up and unlock the mutex. + + If open() did not block on a mutex, there would be a race + condition in which two open() calls could set pdbOpening. + */ + S3JniDb * pdbOpening; sqlite3_mutex * mutex /* mutex for aUsed and aFree */; void const * locker /* Mutex is locked on this object's behalf */; } autoExt; @@ -1906,7 +1927,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, assert( S3JniGlobal.autoExt.locker == ps ); S3JniGlobal.autoExt.pdbOpening = 0; if( !pAX ){ - assert( 0==S3JniGlobal.autoExt.isRunning ); return 0; }else if( S3JniGlobal.autoExt.locker != ps ) { *pzErr = sqlite3_mprintf("Internal error: unexpected path lead to " @@ -1919,7 +1939,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, ps->pDb = pDb; assert( ps->jDb ); NativePointerHolder_set(env, ps->jDb, pDb, S3JniClassNames.sqlite3); - ++S3JniGlobal.autoExt.isRunning; for( ; pAX; pAX = pAX->pNext ){ rc = (*env)->CallIntMethod(env, pAX->jObj, pAX->midFunc, ps->jDb); IFTHREW { @@ -1936,7 +1955,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, break; } } - --S3JniGlobal.autoExt.isRunning; return rc; } @@ -2100,13 +2118,11 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; MUTEX_ENTER_EXT; - if( !S3JniGlobal.autoExt.isRunning ) { - for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ - if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - S3JniAutoExtension_free(env, ax); - rc = JNI_TRUE; - break; - } + for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ + if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + S3JniAutoExtension_free(env, ax); + rc = JNI_TRUE; + break; } } MUTEX_LEAVE_EXT; @@ -2660,7 +2676,14 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, S3JniDb ** ps, jobject *jDb){ int rc = 0; - MUTEX_TRY_EXT(return SQLITE_BUSY); + MUTEX_TRY_EXT(return SQLITE_BUSY) + /* we don't wait forever here because it could lead to a deadlock + if an auto-extension opens a database. Without a mutex, that + situation leads to infinite recursion and stack overflow, which + is infinitely easier to track down from client code. Note that + we rely on the Java methods for open() and auto-extension + handling to be synchronized so that this BUSY cannot be + triggered by a race condition with those functions. */; *jc = S3JniGlobal_env_cache(env); if(!*jc){ rc = SQLITE_NOMEM; @@ -2714,8 +2737,12 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, S3JniGlobal.autoExt.pdbOpening = 0; if(*ppDb){ assert(ps->jDb); - ps->pDb = *ppDb; - NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3); + if( 0==ps->pDb ){ + ps->pDb = *ppDb; + NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3); + }else{ + assert( ps->pDb == *ppDb /* set up via s3jni_run_java_auto_extensions() */); + } }else{ MUTEX_ENTER_PDB; S3JniDb_set_aside(ps); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index e9524e49a7..055e070bfd 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1079,10 +1079,17 @@ public class Tester1 { affirm( 0==rc ); sqlite3_close( createNewDb() ); affirm( 3==val.value ); + + sqlite3 db = createNewDb(); + affirm( 4==val.value ); + execSql(db, "ATTACH ':memory' as foo"); + affirm( 4==val.value /* ATTACH uses the same connection, not sub-connections. */ ); + sqlite3_close(db); + affirm( sqlite3_cancel_auto_extension(ax) ); affirm( !sqlite3_cancel_auto_extension(ax) ); sqlite3_close(createNewDb()); - affirm( 3==val.value ); + affirm( 4==val.value ); rc = sqlite3_auto_extension( ax ); affirm( 0==rc ); Exception err = null; diff --git a/manifest b/manifest index ad2950dc49..8673db6eeb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Internal\sAPI\srenaming\sfor\sclarity's\ssake. -D 2023-08-13T20:58:12.729 +C JNI-internal\sdocs\sand\sremoval\sof\sobsolete\scode. +D 2023-08-14T08:28:46.677 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 1b1f3fa286476933171e75465214deab44c55714aaaa80b5ce145e89f10b0df8 +F ext/jni/src/c/sqlite3-jni.c 1bc1cbaf075065793f83cf5bfba631216ced48d7beae0768ebd838e923e7e590 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 5eeba0b1a00fb34bc93fe60186f6032fcf4d568fc5868d70029883d3d07cc306 -F ext/jni/src/org/sqlite/jni/Tester1.java fc2ec1f1be58474112b9df8284f0157b64872107f446154c3d0bf1742b924d2b +F ext/jni/src/org/sqlite/jni/Tester1.java 368e836d943d9e882d2a217d0f582ed4141d164f174bebc50715acd57549a09b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,8 +2091,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 8da97e0db4eeacf91aa6fd909fd7cb73b050d194dfc7739a502b55f7eca6d7b1 -R 582810911695a14eb1f701cb46e67a37 +P 911e4fc5aaf9478214095a65f74af3ebca883922c36cf7a8d911116c42cf9de8 +R c6d49900ed87c14449d9c08a36536c71 U stephan -Z 414dea18ee7e2187dd201f9308cbe98b +Z 09908deaf1ed6a1fae2d9bef97861a7c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6ade59ea97..7a0d99b2d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -911e4fc5aaf9478214095a65f74af3ebca883922c36cf7a8d911116c42cf9de8 \ No newline at end of file +b62d93258b6a661f3a9b61468b3b641c14faf2d2196f78aca95fe14de43c9444 \ No newline at end of file From 7f2dea75ad0b5ada4de690e84ed07430e64059f8 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 14 Aug 2023 13:27:40 +0000 Subject: [PATCH 007/422] More work on the JNI-specific mutexes. Rework the NativePointerHolder cache lookup to be slightly simpler and O(1) instead of O(N). FossilOrigin-Name: c84ded0e59aea4861d72b53b4b40cf580747c0f6ca58c334a996f1a825276cb5 --- ext/jni/src/c/sqlite3-jni.c | 331 +++++++++------------ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 5 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 159 insertions(+), 193 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 129a601210..d33329575d 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -105,7 +105,7 @@ # define SQLITE_TEMP_STORE 2 #endif #ifndef SQLITE_THREADSAFE -# define SQLITE_THREADSAFE 0 +# define SQLITE_THREADSAFE 1 #endif /**********************************************************************/ @@ -189,10 +189,10 @@ /** Helpers for extracting pointers from jobjects, noting that the corresponding Java interfaces have already done the type-checking. */ -#define PtrGet_sqlite3(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.sqlite3) -#define PtrGet_sqlite3_stmt(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.sqlite3_stmt) -#define PtrGet_sqlite3_value(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.sqlite3_value) -#define PtrGet_sqlite3_context(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.sqlite3_context) +#define PtrGet_sqlite3(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3) +#define PtrGet_sqlite3_stmt(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_stmt) +#define PtrGet_sqlite3_value(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_value) +#define PtrGet_sqlite3_context(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_context) /* Helpers for Java value reference management. */ static inline jobject new_global_ref(JNIEnv * const env, jobject const v){ return v ? (*env)->NewGlobalRef(env, v) : NULL; @@ -209,47 +209,55 @@ static inline void delete_local_ref(JNIEnv * const env, jobject const v){ #define UNREF_L(VAR) delete_local_ref(env,(VAR)) /** - Constant string class names used as keys for S3JniGlobal_nph_cache(), -S3Jni - and - friends. + Keys for use with S3JniGlobal_nph_cache(). +*/ +typedef struct S3NphRef S3NphRef; +struct S3NphRef { + const int index /* index into S3JniEnv->nph[] */; + const char * const zName /* Full Java name of the class */; +}; + +/** + Keys for each concrete NativePointerHolder subclass. */ static const struct { - const char * const sqlite3; - const char * const sqlite3_stmt; - const char * const sqlite3_context; - const char * const sqlite3_value; - const char * const OutputPointer_Int32; - const char * const OutputPointer_Int64; - const char * const OutputPointer_String; - const char * const OutputPointer_ByteArray; - const char * const OutputPointer_sqlite3; - const char * const OutputPointer_sqlite3_stmt; + const S3NphRef sqlite3; + const S3NphRef sqlite3_stmt; + const S3NphRef sqlite3_context; + const S3NphRef sqlite3_value; + const S3NphRef OutputPointer_Int32; + const S3NphRef OutputPointer_Int64; + const S3NphRef OutputPointer_String; + const S3NphRef OutputPointer_ByteArray; + const S3NphRef OutputPointer_sqlite3; + const S3NphRef OutputPointer_sqlite3_stmt; #ifdef SQLITE_ENABLE_FTS5 - const char * const Fts5Context; - const char * const Fts5ExtensionApi; - const char * const fts5_api; - const char * const fts5_tokenizer; - const char * const Fts5Tokenizer; + const S3NphRef Fts5Context; + const S3NphRef Fts5ExtensionApi; + const S3NphRef fts5_api; + const S3NphRef fts5_tokenizer; + const S3NphRef Fts5Tokenizer; #endif -} S3JniClassNames = { - "org/sqlite/jni/sqlite3", - "org/sqlite/jni/sqlite3_stmt", - "org/sqlite/jni/sqlite3_context", - "org/sqlite/jni/sqlite3_value", - "org/sqlite/jni/OutputPointer$Int32", - "org/sqlite/jni/OutputPointer$Int64", - "org/sqlite/jni/OutputPointer$String", - "org/sqlite/jni/OutputPointer$ByteArray", - "org/sqlite/jni/OutputPointer$sqlite3", - "org/sqlite/jni/OutputPointer$sqlite3_stmt", +} S3NphRefs = { +#define NREF(INDEX, NAME) { INDEX, "org/sqlite/jni/" NAME } + 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"), #ifdef SQLITE_ENABLE_FTS5 - "org/sqlite/jni/Fts5Context", - "org/sqlite/jni/Fts5ExtensionApi", - "org/sqlite/jni/fts5_api", - "org/sqlite/jni/fts5_tokenizer", - "org/sqlite/jni/Fts5Tokenizer" + NREF(10, "Fts5Context"), + NREF(11, "Fts5ExtensionApi"), + NREF(12, "fts5_api"), + NREF(13, "fts5_tokenizer"), + NREF(14, "Fts5Tokenizer") #endif +#undef NREF }; /** Create a trivial JNI wrapper for (int CName(void)). */ @@ -321,22 +329,20 @@ enum { (only) the library's NativePointerHolder types, a fixed count known at build-time. If we add more than this a fatal error will be triggered with a reminder to increase this. This value needs - to be exactly the number of entries in the S3JniClassNames - object. The S3JniClassNames entries are the keys for this particular + to be exactly the number of entries in the S3NphRefs object. The + index field of those entries are the keys for this particular cache. */ - NphCache_SIZE = sizeof(S3JniClassNames) / sizeof(char const *) + NphCache_SIZE = sizeof(S3NphRefs) / sizeof(S3NphRef) }; /** Cache entry for NativePointerHolder subclasses and OutputPointer types. */ -typedef struct S3JniNphCache S3JniNphCache; -struct S3JniNphCache { - const char * zClassName /* "full/class/Name". Must be a static - string pointer from the S3JniClassNames - struct. */; +typedef struct S3JniNphClass S3JniNphClass; +struct S3JniNphClass { + const S3NphRef * pRef /* Entry from S3NphRefs. */; jclass klazz /* global ref to the concrete NativePointerHolder subclass represented by zClassName */; @@ -388,12 +394,12 @@ struct S3JniEnv { /** Cache of Java refs/IDs for NativePointerHolder subclasses. */ - S3JniNphCache nph[NphCache_SIZE]; + S3JniNphClass nph[NphCache_SIZE]; }; -static void S3JniNphCache_clear(JNIEnv * const env, S3JniNphCache * const p){ +static void S3JniNphClass_clear(JNIEnv * const env, S3JniNphClass * const p){ UNREF_G(p->klazz); - memset(p, 0, sizeof(S3JniNphCache)); + memset(p, 0, sizeof(S3JniNphClass)); } /* @@ -507,8 +513,6 @@ static struct { current JNIEnv object. */; } perDb; struct { - unsigned nphCacheHits; - unsigned nphCacheMisses; unsigned envCacheHits; unsigned envCacheMisses; unsigned nMutexEnv /* number of times envCache.mutex was entered */; @@ -556,7 +560,7 @@ static struct { condition in which two open() calls could set pdbOpening. */ S3JniDb * pdbOpening; - sqlite3_mutex * mutex /* mutex for aUsed and aFree */; + sqlite3_mutex * mutex /* mutex for manipulation/traversal of pHead */; void const * locker /* Mutex is locked on this object's behalf */; } autoExt; } S3JniGlobal; @@ -565,29 +569,29 @@ static struct { assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ASSERT_NOTLOCKER_ENV \ assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENTER_ENV \ +#define MUTEX_ENV_ENTER \ /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_NOTLOCKER_ENV; \ sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ ++S3JniGlobal.metrics.nMutexEnv; \ S3JniGlobal.envCache.locker = env -#define MUTEX_LEAVE_ENV \ +#define MUTEX_ENV_LEAVE \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_LOCKER_ENV; \ S3JniGlobal.envCache.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.envCache.mutex ) #define MUTEX_ASSERT_LOCKED_PDB \ assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) -#define MUTEX_ENTER_PDB \ +#define MUTEX_PDB_ENTER \ /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ ++S3JniGlobal.metrics.nMutexPerDb; \ S3JniGlobal.perDb.locker = env; -#define MUTEX_LEAVE_PDB \ +#define MUTEX_PDB_LEAVE \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ S3JniGlobal.perDb.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) -#define MUTEX_ENTER_EXT \ +#define MUTEX_ENV_EXT \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \ sqlite3_mutex_enter( S3JniGlobal.autoExt.mutex ); \ ++S3JniGlobal.metrics.nMutexAutoExt @@ -596,7 +600,7 @@ static struct { if( sqlite3_mutex_try( S3JniGlobal.autoExt.mutex ) ){ FAIL_EXPR; } \ S3JniGlobal.autoExt.locker = env; \ ++S3JniGlobal.metrics.nMutexAutoExt -#define MUTEX_LEAVE_EXT \ +#define MUTEX_EXT_LEAVE \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ S3JniGlobal.autoExt.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.autoExt.mutex ) @@ -626,12 +630,12 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ */ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ struct S3JniEnv * row; - MUTEX_ENTER_ENV; + MUTEX_ENV_ENTER; row = S3JniGlobal.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ ++S3JniGlobal.metrics.envCacheHits; - MUTEX_LEAVE_ENV; + MUTEX_ENV_LEAVE; return row; } } @@ -684,7 +688,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8)); EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); } - MUTEX_LEAVE_ENV; + MUTEX_ENV_LEAVE; return row; } @@ -975,18 +979,18 @@ static void S3JniDb_set_aside(S3JniDb * const s){ static void S3JniDb_free_for_env(JNIEnv *env){ S3JniDb * ps; S3JniDb * pNext = 0; - MUTEX_ENTER_PDB; + MUTEX_PDB_ENTER; ps = S3JniGlobal.perDb.aUsed; for( ; ps; ps = pNext ){ pNext = ps->pNext; if(ps->env == env){ S3JniDb * const pPrev = ps->pPrev; S3JniDb_set_aside(ps); - assert(pPrev ? pPrev->pNext!=ps : 1); - pNext = pPrev; + assert(pPrev ? pPrev->pNext==pNext : 1); + assert( ps == S3JniGlobal.perDb.aFree ); } } - MUTEX_LEAVE_PDB; + MUTEX_PDB_LEAVE; } /** @@ -1026,7 +1030,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ UNREF_G(row->jPhraseIter.klazz); #endif for( i = 0; i < NphCache_SIZE; ++i ){ - S3JniNphCache_clear(env, &row->nph[i]); + S3JniNphClass_clear(env, &row->nph[i]); } memset(row, 0, sizeof(S3JniEnv)); row->pNext = S3JniGlobal.envCache.aFree; @@ -1037,10 +1041,8 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ /** Searches the NativePointerHolder cache for the given combination. - If it finds one, it returns it as-is. If it doesn't AND the cache - has a free slot, it populates that slot with (env, zClassName, - klazz) and returns it. If the cache is full with no match it - returns NULL. + If it finds one, it returns it as-is. If it doesn't, it populates a + cache slot's klazz member and returns the cache slot. It is up to the caller to populate the other members of the returned object if needed. @@ -1051,7 +1053,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ This simple cache catches >99% of searches in the current (2023-07-31) tests. */ -static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zClassName){ +static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* pRef){ /** According to: @@ -1072,43 +1074,16 @@ static S3JniNphCache * S3JniGlobal_nph_cache(JNIEnv * const env, const char *zCl guards the fetching of envRow. */ struct S3JniEnv * const envRow = S3JniGlobal_env_cache(env); - S3JniNphCache * freeSlot = 0; - S3JniNphCache * pCache = 0; - int i; + S3JniNphClass * pCache; assert(envRow); - for( i = 0; i < NphCache_SIZE; ++i ){ - pCache = &envRow->nph[i]; - if(zClassName == pCache->zClassName){ - ++S3JniGlobal.metrics.nphCacheHits; -#define DUMP_NPH_CACHES 0 -#if DUMP_NPH_CACHES - MARKER(("Cache hit #%u %s klazz@%p nativePointer field@%p, ctor@%p\n", - S3JniGlobal.metrics.nphCacheHits, zClassName, pCache->klazz, pCache->fidValue, - pCache->midCtor)); -#endif - assert(pCache->klazz); - return pCache; - }else if(!freeSlot && !pCache->zClassName){ - freeSlot = pCache; - } - } - if(freeSlot){ - freeSlot->zClassName = zClassName; - freeSlot->klazz = (*env)->FindClass(env, zClassName); + pCache = &envRow->nph[pRef->index]; + if( !pCache->pRef ){ + pCache->pRef = pRef; + pCache->klazz = (*env)->FindClass(env, pRef->zName); EXCEPTION_IS_FATAL("FindClass() unexpectedly threw"); - freeSlot->klazz = REF_G(freeSlot->klazz); - ++S3JniGlobal.metrics.nphCacheMisses; -#if DUMP_NPH_CACHES - static unsigned int cacheMisses = 0; - MARKER(("Cache miss #%u %s klazz@%p nativePointer field@%p, ctor@%p\n", - S3JniGlobal.metrics.nphCacheMisses, zClassName, freeSlot->klazz, - freeSlot->fidValue, freeSlot->midCtor)); -#endif -#undef DUMP_NPH_CACHES - }else{ - (*env)->FatalError(env, "MAINTENANCE REQUIRED: NphCache_SIZE is too low."); + pCache->klazz = REF_G(pCache->klazz); } - return freeSlot; + return pCache; } /** @@ -1127,11 +1102,10 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, jclass klazz){ as a cache key. */ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, - const char *zClassName){ + S3NphRef const* pRef){ jfieldID setter = 0; - S3JniNphCache * const pCache = S3JniGlobal_nph_cache(env, zClassName); + S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); if(pCache && pCache->klazz && pCache->fidValue){ - assert(zClassName == pCache->zClassName); setter = pCache->fidValue; assert(setter); }else{ @@ -1141,7 +1115,6 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, if(pCache){ assert(pCache->klazz); assert(!pCache->fidValue); - assert(zClassName == pCache->zClassName); pCache->fidValue = setter; } } @@ -1154,11 +1127,11 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, zClassName must be a static string so we can use its address as a cache key. */ -static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zClassName){ +static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const* pRef){ if( pObj ){ jfieldID getter = 0; void * rv = 0; - S3JniNphCache * const pCache = S3JniGlobal_nph_cache(env, zClassName); + S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); if(pCache && pCache->fidValue){ getter = pCache->fidValue; }else{ @@ -1167,7 +1140,6 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zC getter = NativePointerHolder_getField(env, klazz); if(pCache){ assert(pCache->klazz); - assert(zClassName == pCache->zClassName); pCache->fidValue = getter; } } @@ -1188,7 +1160,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zC static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; - MUTEX_ASSERT_LOCKED_PDB; + MUTEX_PDB_ENTER; if(S3JniGlobal.perDb.aFree){ rv = S3JniGlobal.perDb.aFree; //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb)); @@ -1221,6 +1193,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->pDb = pDb; rv->env = env; } + MUTEX_PDB_LEAVE; return rv; } @@ -1251,7 +1224,7 @@ static void S3JniDb_dump(S3JniDb *s){ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if(jDb || pDb){ - MUTEX_ENTER_PDB; + MUTEX_PDB_ENTER; s = S3JniGlobal.perDb.aUsed; if(!pDb){ assert( jDb ); @@ -1262,7 +1235,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ break; } } - MUTEX_LEAVE_PDB; + MUTEX_PDB_LEAVE; } return s; } @@ -1341,8 +1314,8 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, jfieldID member; void * pAgg; int rc = 0; - S3JniNphCache * const pCache = - S3JniGlobal_nph_cache(env, S3JniClassNames.sqlite3_context); + S3JniNphClass * const pCache = + S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context); if(pCache && pCache->klazz && pCache->fidSetAgg){ member = pCache->fidSetAgg; assert(member); @@ -1375,7 +1348,7 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, /** Common init for OutputPointer_set_Int32() and friends. zClassName must be a - pointer from S3JniClassNames. jOut must be an instance of that + pointer from S3NphRefs. jOut must be an instance of that class. Fetches the jfieldID for jOut's [value] property, which must be of the type represented by the JNI type signature zTypeSig, and stores it in pFieldId. Fails fatally if the property is not found, @@ -1385,12 +1358,11 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, this routine with the same zClassName but different zTypeSig: it will misbehave. */ -static void setupOutputPointer(JNIEnv * const env, const char *zClassName, +static void setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, const char * const zTypeSig, jobject const jOut, jfieldID * const pFieldId){ jfieldID setter = 0; - S3JniNphCache * const pCache = - S3JniGlobal_nph_cache(env, zClassName); + S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); if(pCache && pCache->klazz && pCache->fidValue){ setter = pCache->fidValue; }else{ @@ -1410,7 +1382,7 @@ static void setupOutputPointer(JNIEnv * const env, const char *zClassName, to v. */ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_Int32, "I", jOut, &setter); + setupOutputPointer(env, &S3NphRefs.OutputPointer_Int32, "I", jOut, &setter); (*env)->SetIntField(env, jOut, setter, (jint)v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value"); } @@ -1419,7 +1391,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int to v. */ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_Int64, "J", jOut, &setter); + setupOutputPointer(env, &S3NphRefs.OutputPointer_Int64, "J", jOut, &setter); (*env)->SetLongField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value"); } @@ -1427,7 +1399,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3, + setupOutputPointer(env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut, &setter); (*env)->SetObjectField(env, jOut, setter, jDb); EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value"); @@ -1436,7 +1408,7 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, jobject jStmt){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3_stmt, + setupOutputPointer(env, &S3NphRefs.OutputPointer_sqlite3_stmt, "Lorg/sqlite/jni/sqlite3_stmt;", jOut, &setter); (*env)->SetObjectField(env, jOut, setter, jStmt); EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value"); @@ -1449,7 +1421,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, jbyteArray const v){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_ByteArray, "[B", + setupOutputPointer(env, &S3NphRefs.OutputPointer_ByteArray, "[B", jOut, &setter); (*env)->SetObjectField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.ByteArray.value"); @@ -1461,7 +1433,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, jstring const v){ jfieldID setter = 0; - setupOutputPointer(env, S3JniClassNames.OutputPointer_String, + setupOutputPointer(env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;", jOut, &setter); (*env)->SetObjectField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.String.value"); @@ -1587,28 +1559,26 @@ static void ResultJavaVal_finalizer(void *v){ if Java fails to allocate, but the JNI docs are not entirely clear on that detail. - Always use a static string pointer from S3JniClassNames for the 2nd + Always use an static pointer from the S3NphRefs struct for the 2nd argument so that we can use its address as a cache key. */ -static jobject new_NativePointerHolder_object(JNIEnv * const env, const char *zClassName, +static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef, const void * pNative){ jobject rv = 0; jclass klazz = 0; jmethodID ctor = 0; - S3JniNphCache * const pCache = - S3JniGlobal_nph_cache(env, zClassName); - if(pCache && pCache->midCtor){ + S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); + if(pCache->midCtor){ assert( pCache->klazz ); klazz = pCache->klazz; ctor = pCache->midCtor; }else{ klazz = pCache ? pCache->klazz - : (*env)->FindClass(env, zClassName); + : (*env)->FindClass(env, pRef->zName); ctor = klazz ? (*env)->GetMethodID(env, klazz, "", "()V") : 0; EXCEPTION_IS_FATAL("Cannot find constructor for class."); if(pCache){ - assert(zClassName == pCache->zClassName); assert(pCache->klazz); assert(!pCache->midCtor); pCache->midCtor = ctor; @@ -1618,21 +1588,21 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, const char *zC assert(ctor); rv = (*env)->NewObject(env, klazz, ctor); EXCEPTION_IS_FATAL("No-arg constructor threw."); - if(rv) NativePointerHolder_set(env, rv, pNative, zClassName); + if(rv) NativePointerHolder_set(env, rv, pNative, pRef); return rv; } static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.sqlite3, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3, sv); } static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.sqlite3_context, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_context, sv); } static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.sqlite3_stmt, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_stmt, sv); } static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.sqlite3_value, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_value, sv); } enum UDFType { @@ -1938,7 +1908,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, assert( !ps->pDb /* it's still being opened */ ); ps->pDb = pDb; assert( ps->jDb ); - NativePointerHolder_set(env, ps->jDb, pDb, S3JniClassNames.sqlite3); + NativePointerHolder_set(env, ps->jDb, pDb, &S3NphRefs.sqlite3); for( ; pAX; pAX = pAX->pNext ){ rc = (*env)->CallIntMethod(env, pAX->jObj, pAX->midFunc, ps->jDb); IFTHREW { @@ -1964,7 +1934,7 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ S3JniAutoExtension * ax; if( !jAutoExt ) return SQLITE_MISUSE; - MUTEX_ENTER_EXT; + MUTEX_ENV_EXT; if( 0==once && ++once ){ sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); } @@ -1975,7 +1945,7 @@ JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ } } ax = S3JniAutoExtension_alloc(env, jAutoExt); - MUTEX_LEAVE_EXT; + MUTEX_EXT_LEAVE; return ax ? 0 : SQLITE_NOMEM; } @@ -2117,7 +2087,7 @@ FIXME_THREADING(autoExt) JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; - MUTEX_ENTER_EXT; + MUTEX_ENV_EXT; for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniAutoExtension_free(env, ax); @@ -2125,7 +2095,7 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ break; } } - MUTEX_LEAVE_EXT; + MUTEX_EXT_LEAVE; return rc; } @@ -2140,11 +2110,11 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ ps = S3JniDb_for_db(env, jDb, 0); if(ps){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); - MUTEX_ENTER_PDB; + MUTEX_PDB_ENTER; S3JniDb_set_aside(ps) /* MUST come after close() because of ps->trace. */; - MUTEX_LEAVE_PDB; - NativePointerHolder_set(env, jDb, 0, S3JniClassNames.sqlite3); + MUTEX_PDB_LEAVE; + NativePointerHolder_set(env, jDb, 0, &S3NphRefs.sqlite3); } return (jint)rc; } @@ -2647,7 +2617,7 @@ JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ rc = sqlite3_finalize(pStmt); - NativePointerHolder_set(env, jpStmt, 0, S3JniClassNames.sqlite3_stmt); + NativePointerHolder_set(env, jpStmt, 0, &S3NphRefs.sqlite3_stmt); } return rc; } @@ -2683,7 +2653,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, is infinitely easier to track down from client code. Note that we rely on the Java methods for open() and auto-extension handling to be synchronized so that this BUSY cannot be - triggered by a race condition with those functions. */; + triggered by a race condition with the auto-ext functions. */; *jc = S3JniGlobal_env_cache(env); if(!*jc){ rc = SQLITE_NOMEM; @@ -2701,9 +2671,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, rc = SQLITE_NOMEM; goto end; } - MUTEX_ENTER_PDB; *ps = S3JniDb_alloc(env, 0, *jDb); - MUTEX_LEAVE_PDB; if(*ps){ S3JniGlobal.autoExt.pdbOpening = *ps; S3JniGlobal.autoExt.locker = *ps; @@ -2712,9 +2680,9 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, } //MARKER(("pre-open ps@%p\n", *ps)); end: - /* Remain in autoExt.mutex until s3jni_open_post(). */ if(rc){ - MUTEX_LEAVE_EXT; + MUTEX_EXT_LEAVE; + /* Else remain in autoExt.mutex until s3jni_open_post(). */ } return rc; } @@ -2739,18 +2707,18 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, assert(ps->jDb); if( 0==ps->pDb ){ ps->pDb = *ppDb; - NativePointerHolder_set(env, ps->jDb, *ppDb, S3JniClassNames.sqlite3); + NativePointerHolder_set(env, ps->jDb, *ppDb, &S3NphRefs.sqlite3); }else{ assert( ps->pDb == *ppDb /* set up via s3jni_run_java_auto_extensions() */); } }else{ - MUTEX_ENTER_PDB; + MUTEX_PDB_ENTER; S3JniDb_set_aside(ps); - MUTEX_LEAVE_PDB; + MUTEX_PDB_LEAVE; ps = 0; } OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0); - MUTEX_LEAVE_EXT; + MUTEX_EXT_LEAVE /* locked in s3jni_open_pre() */; return theRc; } @@ -2848,7 +2816,7 @@ end: OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0)); } if( pStmt ){ - NativePointerHolder_set(env, jStmt, pStmt, S3JniClassNames.sqlite3_stmt); + NativePointerHolder_set(env, jStmt, pStmt, &S3NphRefs.sqlite3_stmt); }else{ /* Happens for comments and whitespace */ UNREF_L(jStmt); @@ -2937,11 +2905,11 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ } JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ - MUTEX_ENTER_EXT; + MUTEX_ENV_EXT; while( S3JniGlobal.autoExt.pHead ){ S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead); } - MUTEX_LEAVE_EXT; + MUTEX_EXT_LEAVE; } /* sqlite3_result_text/blob() and friends. */ @@ -3244,11 +3212,11 @@ JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){ } JDECL(jint,1shutdown)(JENV_CSELF){ - MUTEX_ENTER_ENV; + MUTEX_ENV_ENTER; while( S3JniGlobal.envCache.aHead ){ S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env ); } - MUTEX_LEAVE_ENV; + MUTEX_ENV_LEAVE; /* Do not clear S3JniGlobal.jvm: it's legal to call sqlite3_initialize() again to restart the lib. */ return sqlite3_shutdown(); @@ -3539,16 +3507,13 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ SO(S3JniEnv); SO(S3JniHook); SO(S3JniDb); - SO(S3JniClassNames); + SO(S3NphRefs); printf("\t(^^^ %u NativePointerHolder subclasses)\n", - (unsigned)(sizeof(S3JniClassNames) / sizeof(const char *))); + (unsigned)NphCache_SIZE); SO(S3JniGlobal); SO(S3JniAutoExtension); SO(S3JniUdf); printf("Cache info:\n"); - printf("\tNativePointerHolder cache: %u misses, %u hits\n", - S3JniGlobal.metrics.nphCacheMisses, - S3JniGlobal.metrics.nphCacheHits); printf("\tJNIEnv cache %u misses, %u hits\n", S3JniGlobal.metrics.envCacheMisses, S3JniGlobal.metrics.envCacheHits); @@ -3588,10 +3553,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ JNIEXPORT ReturnType JNICALL \ JFuncNameFtsTok(Suffix) -#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.fts5_api) -#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.fts5_tokenizer) -#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.Fts5Context) -#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3JniClassNames.Fts5Tokenizer) +#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 Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext() /** @@ -3655,10 +3620,10 @@ static inline Fts5ExtensionApi const * s3jni_ftsext(void){ } static inline jobject new_Fts5Context_wrapper(JNIEnv * const env, Fts5Context *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.Fts5Context, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.Fts5Context, sv); } static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ - return new_NativePointerHolder_object(env, S3JniClassNames.fts5_api, sv); + return new_NativePointerHolder_object(env, &S3NphRefs.fts5_api, sv); } /** @@ -3668,7 +3633,7 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ S3JniEnv * const row = S3JniGlobal_env_cache(env); if( !row->jFtsExt ){ - row->jFtsExt = new_NativePointerHolder_object(env, S3JniClassNames.Fts5ExtensionApi, + row->jFtsExt = new_NativePointerHolder_object(env, &S3NphRefs.Fts5ExtensionApi, s3jni_ftsext()); if(row->jFtsExt) row->jFtsExt = REF_G(row->jFtsExt); } @@ -4113,7 +4078,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, /** Proxy for Fts5ExtensionApi.xTokenize() and fts5_tokenizer.xTokenize() */ -static jint s3jni_fts5_xTokenize(JENV_OSELF, const char *zClassName, +static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; @@ -4140,11 +4105,11 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, const char *zClassName, s.tok.jba = REF_L(jbaText); s.tok.zPrev = (const char *)pText; s.tok.nPrev = (int)nText; - if( zClassName == S3JniClassNames.Fts5ExtensionApi ){ + if( pRef == &S3NphRefs.Fts5ExtensionApi ){ rc = fext->xTokenize(PtrGet_Fts5Context(jFcx), (const char *)pText, (int)nText, &s, s3jni_xTokenize_xToken); - }else if( zClassName == S3JniClassNames.fts5_tokenizer ){ + }else if( pRef == &S3NphRefs.fts5_tokenizer ){ fts5_tokenizer * const pTok = PtrGet_fts5_tokenizer(jSelf); rc = pTok->xTokenize(PtrGet_Fts5Tokenizer(jFcx), &s, tokFlags, (const char *)pText, (int)nText, @@ -4162,13 +4127,13 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, const char *zClassName, JDECLFtsXA(jint,xTokenize)(JENV_OSELF,jobject jFcx, jbyteArray jbaText, jobject jCallback){ - return s3jni_fts5_xTokenize(env, jSelf, S3JniClassNames.Fts5ExtensionApi, + return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5ExtensionApi, 0, jFcx, jbaText, jCallback); } JDECLFtsTok(jint,xTokenize)(JENV_OSELF,jobject jFcx, jint tokFlags, jbyteArray jbaText, jobject jCallback){ - return s3jni_fts5_xTokenize(env, jSelf, S3JniClassNames.Fts5Tokenizer, + return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5Tokenizer, tokFlags, jFcx, jbaText, jCallback); } @@ -4396,9 +4361,9 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ int rc; - MUTEX_ENTER_ENV; + MUTEX_ENV_ENTER; rc = S3JniGlobal_env_uncache(env); - MUTEX_LEAVE_ENV; + MUTEX_ENV_LEAVE; return rc ? JNI_TRUE : JNI_FALSE; } diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index dbd2e4bae7..073a79835f 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -178,8 +178,9 @@ public final class SQLite3Jni { not have access to the sqlite3_api object which native auto-extensions do. - - If an auto-extension opens a db, opening will fail with SQLITE_BUSY. - The alternative would be endless recursion into the auto-extension. + - If an auto-extension opens a db from the same thread, opening + will fail with SQLITE_BUSY. The alternative would be endless + recursion into the auto-extension. - The list of auto-extensions must not be manipulated from within an auto-extension. Auto extensions can neither be added, diff --git a/manifest b/manifest index 8673db6eeb..8cb273b6f5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI-internal\sdocs\sand\sremoval\sof\sobsolete\scode. -D 2023-08-14T08:28:46.677 +C More\swork\son\sthe\sJNI-specific\smutexes.\sRework\sthe\sNativePointerHolder\scache\slookup\sto\sbe\sslightly\ssimpler\sand\sO(1)\sinstead\sof\sO(N). +D 2023-08-14T13:27:40.885 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 1bc1cbaf075065793f83cf5bfba631216ced48d7beae0768ebd838e923e7e590 +F ext/jni/src/c/sqlite3-jni.c 0ca77c27d05b677191f105bc6f8570c916b78991a809d44566c60cfc16b50612 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5eeba0b1a00fb34bc93fe60186f6032fcf4d568fc5868d70029883d3d07cc306 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cd0627b5317435f9a6c72247915f9e32d6e8c225fd6f0db2c66b4a7f0b4e5601 F ext/jni/src/org/sqlite/jni/Tester1.java 368e836d943d9e882d2a217d0f582ed4141d164f174bebc50715acd57549a09b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -2091,8 +2091,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 911e4fc5aaf9478214095a65f74af3ebca883922c36cf7a8d911116c42cf9de8 -R c6d49900ed87c14449d9c08a36536c71 +P b62d93258b6a661f3a9b61468b3b641c14faf2d2196f78aca95fe14de43c9444 +R 640b629cd539e67d64418aa6a08729eb U stephan -Z 09908deaf1ed6a1fae2d9bef97861a7c +Z db03beab64a32e82d4161306677f6282 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7a0d99b2d3..0f159ac8d9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b62d93258b6a661f3a9b61468b3b641c14faf2d2196f78aca95fe14de43c9444 \ No newline at end of file +c84ded0e59aea4861d72b53b4b40cf580747c0f6ca58c334a996f1a825276cb5 \ No newline at end of file From 24b4cdd8d31b32deacf3e606d9f23de4a845b39c Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 14 Aug 2023 14:21:28 +0000 Subject: [PATCH 008/422] Remove some obsolete, commented-out makefile code. No functional or build changes. FossilOrigin-Name: ea574cd5de6dbc7db2f4cffe0b6c83c795c4ba634ae1984e5c95bb3529daa5bb --- ext/wasm/GNUmakefile | 7 ------- manifest | 17 ++++++++++------- manifest.uuid | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 080c427045..2cd44ac55a 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -456,13 +456,6 @@ emcc.exportedRuntimeMethods := \ emcc.jsflags += $(emcc.exportedRuntimeMethods) emcc.jsflags += -sUSE_CLOSURE_COMPILER=0 emcc.jsflags += -sIMPORTED_MEMORY -#emcc.jsflags += -sASYNCIFY=2 -# ^^^ ASYNCIFY=2 is for experimental JSPI support -# (https://v8.dev/blog/jspi), but enabling it causes the lib-level -# init code to throw inexplicable complaints about C-level function -# signatures not matching what we expect them to be. JSPI requires, as of -# this writing, requires an experimental Chrome flag: -# chrome://flags/#enable-experimental-webassembly-stack-switching emcc.jsflags += -sSTRICT_JS=0 # STRICT_JS disabled due to: # https://github.com/emscripten-core/emscripten/issues/18610 diff --git a/manifest b/manifest index d123a018ac..291b52a5b2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sname\sof\sthe\s".binary"\scommand\sin\sthe\sCLI\sto\s".crnl".\s\sThe\ssense\sof\nthe\ssetting\sis\sinverted.\s\sThe\soriginal\s".binary"\sstill\sworks\sfor\sbackwards\ncompatibility\sbut\sis\snow\sundocumented.\s\s".crnl"\sis\salso\sundocumented\son\smachines\nwhere\sit\sis\sa\sno-op\s(all\smachines\sother\sthan\sWindows).\n[forum:/forumpost/8bd0b0fbdbc12477|forum\sthread\s8bd0b0fbdbc12477] -D 2023-08-14T13:33:19.213 +C Remove\ssome\sobsolete,\scommented-out\smakefile\scode.\sNo\sfunctional\sor\sbuild\schanges. +D 2023-08-14T14:21:28.009 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -528,7 +528,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile d02f3c8798b754f68b1f6b422ccff894a10bf352fc9c4eb8945baeace1acac28 +F ext/wasm/GNUmakefile 594f5a6dc889f836ebdf0d4a35a9c39fbf56cd2827416cdd98b5244f51519901 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab @@ -2091,8 +2091,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 391e21bb6e0b2f4632972b6617a3a18192f88deb29eee5bc060846468e624b21 -R 676721f5b95d5972349e28e60fde58e8 -U drh -Z 343246f1d1f398cedd6a7b7f3f3868a2 +P 544de2da09cd7b9bbb6cb4c52bb22325ace3391fca00c6a43847bfc158032b66 +R f380fd55d7d477e32c828eb055b6a7ec +T *branch * wasm-post-343 +T *sym-wasm-post-343 * +T -sym-trunk * Cancelled\sby\sbranch. +U stephan +Z 9a98a8a06619bcf87fff235150138679 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e65d56dc16..416c400a52 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -544de2da09cd7b9bbb6cb4c52bb22325ace3391fca00c6a43847bfc158032b66 \ No newline at end of file +ea574cd5de6dbc7db2f4cffe0b6c83c795c4ba634ae1984e5c95bb3529daa5bb \ No newline at end of file From 9019e2e667b297a4c9548ffeb8651d914f70d509 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 14 Aug 2023 17:12:55 +0000 Subject: [PATCH 009/422] Bring handling of the Java auto-ext handler more in line with the core in terms of locking and mutability during traversal. This removes the explicit synchronous requirement from the Java open() and auto-ext bindings. FossilOrigin-Name: 42994b952e092ae4fa319395208622e887387ca3ff8ac57961c824a6c272bf0e --- ext/jni/src/c/sqlite3-jni.c | 459 +++++++++++---------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 37 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 254 insertions(+), 258 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d33329575d..37aa06460e 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -354,83 +354,11 @@ struct S3JniNphClass { by the sqlite3_context binding. */; }; -/** - Cache for per-JNIEnv data. - - Potential TODO: move the jclass entries to global space because, - per https://developer.android.com/training/articles/perf-jni: - - > once you have a valid jclass global reference you can use it from - any attached thread. - - Whereas we cache new refs for each thread. -*/ -typedef struct S3JniEnv S3JniEnv; -struct S3JniEnv { - JNIEnv *env /* env in which this cache entry was created */; - //! The various refs to global classes might be cacheable a single - // time globally. Information online seems inconsistent on that - // point. - struct { - jclass cObj /* global ref to java.lang.Object */; - jclass cLong /* global ref to java.lang.Long */; - jclass cString /* global ref to java.lang.String */; - jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; - jmethodID ctorLong1 /* the Long(long) constructor */; - jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; - jmethodID stringGetBytes /* the String.getBytes(Charset) method */; - } g /* refs to global Java state */; -#ifdef SQLITE_ENABLE_FTS5 - jobject jFtsExt /* Global ref to Java singleton for the - Fts5ExtensionApi instance. */; - struct { - jclass klazz /* Global ref to the Fts5Phrase iter class */; - jfieldID fidA /* Fts5Phrase::a member */; - jfieldID fidB /* Fts5Phrase::b member */; - } jPhraseIter; -#endif - S3JniEnv * pPrev /* Previous entry in the linked list */; - S3JniEnv * pNext /* Next entry in the linked list */; - /** - Cache of Java refs/IDs for NativePointerHolder subclasses. - */ - S3JniNphClass nph[NphCache_SIZE]; -}; - static void S3JniNphClass_clear(JNIEnv * const env, S3JniNphClass * const p){ UNREF_G(p->klazz); memset(p, 0, sizeof(S3JniNphClass)); } -/* - 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 { - jobject jObj; - jmethodID midFunc; - S3JniAutoExtension_xEntryPoint xEntryPoint; - S3JniAutoExtension *pNext /* next linked-list entry */; - S3JniAutoExtension *pPrev /* previous linked-list entry */; -}; - /** State for various hook callbacks. */ typedef struct S3JniHook S3JniHook; struct S3JniHook{ @@ -480,6 +408,95 @@ struct S3JniDb { S3JniDb * pPrev /* Previous entry in the available/free list */; }; +/** + Cache for per-JNIEnv data. + + Potential TODO: move the jclass entries to global space because, + per https://developer.android.com/training/articles/perf-jni: + + > once you have a valid jclass global reference you can use it from + any attached thread. + + Whereas we cache new refs for each thread. +*/ +typedef struct S3JniEnv S3JniEnv; +struct S3JniEnv { + JNIEnv *env /* env in which this cache entry was created */; + //! The various refs to global classes might be cacheable a single + // time globally. Information online seems inconsistent on that + // point. + struct { + jclass cObj /* global ref to java.lang.Object */; + jclass cLong /* global ref to java.lang.Long */; + jclass cString /* global ref to java.lang.String */; + jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; + jmethodID ctorLong1 /* the Long(long) constructor */; + jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; + jmethodID stringGetBytes /* the String.getBytes(Charset) method */; + } g /* refs to global Java state */; + /** + pdbOpening is used to coordinate the Java/DB connection of a + being-open()'d db in the face of auto-extensions. "The problem" + is that auto-extensions run before we can bind the C db to its + Java representation, but auto-extensions require that binding. We + handle this as follows: + + - In open(), allocate the Java side of that connection and set + pdbOpening to point to that object. Note that it's per-thread, + and we remain in that thread until after the auto-extensions + are run. + + - Call open(), which triggers the auto-extension handler. + That handler uses pdbOpening to connect the native db handle + which it receives with pdbOpening. + + - When open() returns, check whether it invoked the auto-ext + handler. If not, complete the Java/C binding unless open() + returns a NULL db, in which case free pdbOpening. + */ + S3JniDb * pdbOpening; +#ifdef SQLITE_ENABLE_FTS5 + jobject jFtsExt /* Global ref to Java singleton for the + Fts5ExtensionApi instance. */; + struct { + jclass klazz /* Global ref to the Fts5Phrase iter class */; + jfieldID fidA /* Fts5Phrase::a member */; + jfieldID fidB /* Fts5Phrase::b member */; + } jPhraseIter; +#endif + S3JniEnv * pPrev /* Previous entry in the linked list */; + S3JniEnv * pNext /* Next entry in the linked list */; + /** + Cache of Java refs/IDs for NativePointerHolder subclasses. + */ + S3JniNphClass nph[NphCache_SIZE]; +}; + +/* + 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; +struct S3JniAutoExtension { + jobject jObj /* Java object */; + jmethodID midFunc /* xEntryPoint() callback */; +}; + /** Global state, e.g. caches and metrics. */ @@ -530,38 +547,15 @@ static struct { } metrics; /** The list of bound auto-extensions (Java-side: - org.sqlite.jni.AutoExtension objects). Because this data - structure cannot be manipulated during traversal (without adding - more code to deal with it), and to avoid a small window where a - call to sqlite3_reset/clear_auto_extension() could lock the - structure during an open() call, we lock this mutex before - sqlite3_open() is called and unlock it once sqlite3_open() - returns. + org.sqlite.jni.AutoExtension objects). */ struct { - S3JniAutoExtension *pHead /* Head of the auto-extension list */; - /** - pdbOpening is used to coordinate the Java/DB connection of a - being-open()'d db. "The problem" is that auto-extensions run - before we can bind the C db to its Java representation, but - auto-extensions require that binding. We handle this as - follows: - - - At the start of open(), we lock on this->mutex. - - Allocate the Java side of that connection and set pdbOpening - to point to that object. - - Call open(), which triggers the auto-extension handler. - That handler uses pdbOpening to connect the native db handle - which it recieves with pdbOpening. - - Return from open(). - - Clean up and unlock the mutex. - - If open() did not block on a mutex, there would be a race - condition in which two open() calls could set pdbOpening. - */ - S3JniDb * pdbOpening; - sqlite3_mutex * mutex /* mutex for manipulation/traversal of pHead */; - void const * locker /* Mutex is locked on this object's behalf */; + S3JniAutoExtension *pExt /* Head of the auto-extension list */; + int nAlloc /* number of entries allocated for pExt, + as distinct from the number of active + entries. */; + int nExt /* number of active entries in pExt. */; + sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; } autoExt; } S3JniGlobal; @@ -591,18 +585,12 @@ static struct { /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ S3JniGlobal.perDb.locker = 0; \ sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) -#define MUTEX_ENV_EXT \ +#define MUTEX_EXT_ENTER \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \ sqlite3_mutex_enter( S3JniGlobal.autoExt.mutex ); \ ++S3JniGlobal.metrics.nMutexAutoExt -#define MUTEX_TRY_EXT(FAIL_EXPR) \ - /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - if( sqlite3_mutex_try( S3JniGlobal.autoExt.mutex ) ){ FAIL_EXPR; } \ - S3JniGlobal.autoExt.locker = env; \ - ++S3JniGlobal.metrics.nMutexAutoExt #define MUTEX_EXT_LEAVE \ - /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - S3JniGlobal.autoExt.locker = 0; \ + /*MARKER(("Leaving autoExt mutex@%p %s.\n", env, __func__));*/ \ sqlite3_mutex_leave( S3JniGlobal.autoExt.mutex ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) @@ -1241,53 +1229,40 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ } /** - Unlink ax from S3JniGlobal.autoExt and free it. + Unref any Java-side state in ax. */ -static void S3JniAutoExtension_free(JNIEnv * const env, - S3JniAutoExtension * const ax){ - if( ax ){ - if( ax->pNext ) ax->pNext->pPrev = ax->pPrev; - if( ax == S3JniGlobal.autoExt.pHead ){ - assert( !ax->pNext ); - S3JniGlobal.autoExt.pHead = ax->pNext; - }else if( ax->pPrev ){ - ax->pPrev->pNext = ax->pNext; - } - ax->pNext = ax->pPrev = 0; +static void S3JniAutoExtension_clear(JNIEnv * const env, + S3JniAutoExtension * const ax){ + if( ax->jObj ){ UNREF_G(ax->jObj); - sqlite3_free(ax); + memset(ax, 0, sizeof(*ax)); } } /** - Allocates a new auto extension and plugs it in to S3JniGlobal.autoExt. - Returns 0 on OOM or if there is an error collecting the required - state from jAutoExt (which must be an AutoExtension object). + Initializes a pre-allocated S3JniAutoExtension object. Returns + non-0 if there is an error collecting the required state from + jAutoExt (which must be an AutoExtension object). */ -static S3JniAutoExtension * S3JniAutoExtension_alloc(JNIEnv *const env, - jobject const jAutoExt){ - S3JniAutoExtension * const ax = sqlite3_malloc(sizeof(*ax)); - if( ax ){ - jclass klazz; - memset(ax, 0, sizeof(*ax)); - klazz = (*env)->GetObjectClass(env, jAutoExt); - if(!klazz){ - S3JniAutoExtension_free(env, ax); - return 0; - } - ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", - "(Lorg/sqlite/jni/sqlite3;)I"); - if(!ax->midFunc){ - MARKER(("Error getting xEntryPoint(sqlite3) from object.")); - S3JniAutoExtension_free(env, ax); - return 0; - } - ax->jObj = REF_G(jAutoExt); - ax->pNext = S3JniGlobal.autoExt.pHead; - if( ax->pNext ) ax->pNext->pPrev = ax; - S3JniGlobal.autoExt.pHead = ax; +static int S3JniAutoExtension_init(JNIEnv *const env, + S3JniAutoExtension * const ax, + jobject const jAutoExt){ + jclass klazz; + klazz = (*env)->GetObjectClass(env, jAutoExt); + if(!klazz){ + S3JniAutoExtension_clear(env, ax); + return SQLITE_ERROR; } - return ax; + ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", + "(Lorg/sqlite/jni/sqlite3;)I"); + if(!ax->midFunc){ + MARKER(("Error getting xEntryPoint(sqlite3) from object.")); + S3JniAutoExtension_clear(env, ax); + return SQLITE_ERROR; + } + UNREF_L(klazz); + ax->jObj = REF_G(jAutoExt); + return 0; } /** @@ -1888,65 +1863,96 @@ static JNIEnv * s3jni_get_env(void){ /* Central auto-extension handler. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ - S3JniAutoExtension const * pAX = S3JniGlobal.autoExt.pHead; - int rc; + int rc = 0; + unsigned i, go = 1; JNIEnv * env = 0; - S3JniDb * const ps = S3JniGlobal.autoExt.pdbOpening; - - assert( S3JniGlobal.autoExt.locker ); - assert( S3JniGlobal.autoExt.locker == ps ); - S3JniGlobal.autoExt.pdbOpening = 0; - if( !pAX ){ - return 0; - }else if( S3JniGlobal.autoExt.locker != ps ) { - *pzErr = sqlite3_mprintf("Internal error: unexpected path lead to " - "running an auto-extension."); - return SQLITE_ERROR; - } + S3JniDb * ps; + S3JniEnv * jc; + if( 0==S3JniGlobal.autoExt.nExt ) return 0; env = s3jni_get_env(); + jc = S3JniGlobal_env_cache(env); + ps = jc->pdbOpening; + jc->pdbOpening = 0; + assert( ps && "Unexpected arrival of null S3JniDb in auto-extension runner." ); //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb)); - assert( !ps->pDb /* it's still being opened */ ); + assert( !ps->pDb && "it's still being opened" ); ps->pDb = pDb; assert( ps->jDb ); NativePointerHolder_set(env, ps->jDb, pDb, &S3NphRefs.sqlite3); - for( ; pAX; pAX = pAX->pNext ){ - rc = (*env)->CallIntMethod(env, pAX->jObj, pAX->midFunc, ps->jDb); - IFTHREW { - jthrowable const ex = (*env)->ExceptionOccurred(env); - char * zMsg; - EXCEPTION_CLEAR; - zMsg = s3jni_exception_error_msg(env, ex); - UNREF_L(ex); - *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); - sqlite3_free(zMsg); - rc = rc ? rc : SQLITE_ERROR; - break; - }else if( rc ){ - break; + for( i = 0; go && 0==rc; ++i ){ + S3JniAutoExtension const * ax; + MUTEX_EXT_ENTER; + if( i >= S3JniGlobal.autoExt.nAlloc ){ + ax = 0; + go = 0; + }else{ + ax = &S3JniGlobal.autoExt.pExt[i]; + } + MUTEX_EXT_LEAVE; + if( ax && ax->jObj ){ + rc = (*env)->CallIntMethod(env, ax->jObj, ax->midFunc, ps->jDb); + IFTHREW { + jthrowable const ex = (*env)->ExceptionOccurred(env); + char * zMsg; + EXCEPTION_CLEAR; + zMsg = s3jni_exception_error_msg(env, ex); + UNREF_L(ex); + *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); + sqlite3_free(zMsg); + rc = rc ? rc : SQLITE_ERROR; + } } } return rc; } FIXME_THREADING(autoExt) -JDECL(jint,1auto_1extension)(JENV_OSELF, jobject jAutoExt){ +JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ static int once = 0; + int i; S3JniAutoExtension * ax; + int rc = 0; + int firstEmptySlot = -1; if( !jAutoExt ) return SQLITE_MISUSE; - MUTEX_ENV_EXT; - if( 0==once && ++once ){ - sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); - } - ax = S3JniGlobal.autoExt.pHead; - for( ; ax; ax = ax->pNext ){ - if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - return 0 /* C API treats this as a no-op. */; + MUTEX_EXT_ENTER; + for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + /* Look for match or first empty slot. */ + ax = &S3JniGlobal.autoExt.pExt[i]; + if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + break /* this as a no-op. */; + }else if( !ax->jObj && firstEmptySlot<0 ){ + firstEmptySlot = (int)i; + } + } + if(i == S3JniGlobal.autoExt.nAlloc ){ + if( firstEmptySlot >= 0 ){ + ax = &S3JniGlobal.autoExt.pExt[firstEmptySlot]; + rc = S3JniAutoExtension_init(env, ax, jAutoExt); + }else{ + unsigned n = 1 + S3JniGlobal.autoExt.nAlloc; + S3JniAutoExtension * const aNew = + sqlite3_realloc( S3JniGlobal.autoExt.pExt, + n * sizeof(S3JniAutoExtension) ); + if( !aNew ){ + rc = SQLITE_NOMEM; + }else{ + S3JniGlobal.autoExt.pExt = aNew; + ax = &S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc]; + ++S3JniGlobal.autoExt.nAlloc; + rc = S3JniAutoExtension_init(env, ax, jAutoExt); + assert( rc ? 0==ax->jObj : 0!=ax->jObj ); + } + } + } + if( 0==rc ){ + ++S3JniGlobal.autoExt.nExt; + if( 0==once && ++once ){ + sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); } } - ax = S3JniAutoExtension_alloc(env, jAutoExt); MUTEX_EXT_LEAVE; - return ax ? 0 : SQLITE_NOMEM; + return rc; } FIXME_THREADING(S3JniEnv) @@ -2087,14 +2093,37 @@ FIXME_THREADING(autoExt) JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; - MUTEX_ENV_EXT; - for( ax = S3JniGlobal.autoExt.pHead; ax; ax = ax->pNext ){ - if( (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - S3JniAutoExtension_free(env, ax); + int i; + MUTEX_EXT_ENTER; +#if 1 + for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + ax = &S3JniGlobal.autoExt.pExt[i]; + if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + S3JniAutoExtension_clear(env, ax); + /* Move final entry into this slot. */ + *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1]; + memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1], 0, + sizeof(S3JniAutoExtension)); + --S3JniGlobal.autoExt.nExt; rc = JNI_TRUE; break; } } +#else + /* Why does this impl lead to an invalid unref error? */ + for( i = S3JniGlobal.autoExt.nAlloc-1; i <= 0; --i ){ + ax = &S3JniGlobal.autoExt.pExt[i]; + if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + S3JniAutoExtension_clear(env, ax); + /* Move final entry into this slot. */ + *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1]; + memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1], 0, + sizeof(S3JniAutoExtension)); + rc = JNI_TRUE; + break; + } + } +#endif MUTEX_EXT_LEAVE; return rc; } @@ -2646,14 +2675,6 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, S3JniDb ** ps, jobject *jDb){ int rc = 0; - MUTEX_TRY_EXT(return SQLITE_BUSY) - /* we don't wait forever here because it could lead to a deadlock - if an auto-extension opens a database. Without a mutex, that - situation leads to infinite recursion and stack overflow, which - is infinitely easier to track down from client code. Note that - we rely on the Java methods for open() and auto-extension - handling to be synchronized so that this BUSY cannot be - triggered by a race condition with the auto-ext functions. */; *jc = S3JniGlobal_env_cache(env); if(!*jc){ rc = SQLITE_NOMEM; @@ -2673,17 +2694,12 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, } *ps = S3JniDb_alloc(env, 0, *jDb); if(*ps){ - S3JniGlobal.autoExt.pdbOpening = *ps; - S3JniGlobal.autoExt.locker = *ps; + (*jc)->pdbOpening = *ps; }else{ rc = SQLITE_NOMEM; } //MARKER(("pre-open ps@%p\n", *ps)); end: - if(rc){ - MUTEX_EXT_LEAVE; - /* Else remain in autoExt.mutex until s3jni_open_post(). */ - } return rc; } @@ -2698,11 +2714,11 @@ end: Returns theRc. */ -static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, - sqlite3 **ppDb, jobject jOut, int theRc){ +static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, + S3JniDb * ps, sqlite3 **ppDb, + jobject jOut, int theRc){ //MARKER(("post-open() ps@%p db@%p\n", ps, *ppDb)); - assert( S3JniGlobal.autoExt.locker == ps ); - S3JniGlobal.autoExt.pdbOpening = 0; + jc->pdbOpening = 0; if(*ppDb){ assert(ps->jDb); if( 0==ps->pDb ){ @@ -2718,7 +2734,6 @@ static int s3jni_open_post(JNIEnv * const env, S3JniDb * ps, ps = 0; } OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0); - MUTEX_EXT_LEAVE /* locked in s3jni_open_pre() */; return theRc; } @@ -2728,17 +2743,15 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ jobject jDb = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; - S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; - int rc= s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); + int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); if( 0==rc ){ rc = sqlite3_open(zName, &pOut); //MARKER(("env=%p, *env=%p\n", env, *env)); //MARKER(("open() ps@%p db@%p\n", ps, pOut)); - rc = s3jni_open_post(env, ps, &pOut, jOut, rc); + rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); } - S3JniGlobal.autoExt.pdbOpening = prevOpening; return (jint)rc; } @@ -2750,7 +2763,6 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, S3JniDb * ps = 0; S3JniEnv * jc = 0; char *zVfs = 0; - S3JniDb * const prevOpening = S3JniGlobal.autoExt.pdbOpening; int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); if( 0==rc && strVfs ){ zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0); @@ -2764,8 +2776,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, //MARKER(("open_v2() ps@%p db@%p\n", ps, pOut)); /*MARKER(("zName=%s, zVfs=%s, pOut=%p, flags=%d, nrc=%d\n", zName, zVfs, pOut, (int)flags, nrc));*/ - rc = s3jni_open_post(env, ps, &pOut, jOut, rc); - S3JniGlobal.autoExt.pdbOpening = prevOpening; + rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); sqlite3_free(zVfs); @@ -2905,10 +2916,12 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ } JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ - MUTEX_ENV_EXT; - while( S3JniGlobal.autoExt.pHead ){ - S3JniAutoExtension_free(env, S3JniGlobal.autoExt.pHead); + int i; + MUTEX_EXT_ENTER; + for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + S3JniAutoExtension_clear( env, &S3JniGlobal.autoExt.pExt[i] ); } + S3JniGlobal.autoExt.nExt = 0; MUTEX_EXT_LEAVE; } @@ -3517,7 +3530,7 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ printf("\tJNIEnv cache %u misses, %u hits\n", S3JniGlobal.metrics.envCacheMisses, S3JniGlobal.metrics.envCacheHits); - printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt (mostly via open[_v2]())\n", + printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt\n", S3JniGlobal.metrics.nMutexEnv, S3JniGlobal.metrics.nMutexPerDb, S3JniGlobal.metrics.nMutexAutoExt); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 073a79835f..0833c1d4a3 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -178,26 +178,17 @@ public final class SQLite3Jni { not have access to the sqlite3_api object which native auto-extensions do. - - If an auto-extension opens a db from the same thread, opening - will fail with SQLITE_BUSY. The alternative would be endless - recursion into the auto-extension. - - - The list of auto-extensions must not be manipulated from within - an auto-extension. Auto extensions can neither be added, - removed, nor cleared while one registered with this function is - running. Attempting to do so may lead to a deadlock. + - If the list of auto-extensions is manipulated from an + auto-extension, it is undefined which, if any, auto-extensions + will subsequently execute for the current database (it depends + on multiple factors). See the AutoExtension class docs for more information. Achtung: it is as yet unknown whether auto extensions registered from one JNIEnv (thread) can be safely called from another. - - Design note: this family of methods is synchronized in order to - help avoid a small race condition where an in-progress - sqlite3_reset_auto_extension() or sqlite3_cancel_auto_extension() - could cause sqlite3_open() to fail with SQLITE_BUSY. */ - public static synchronized native int sqlite3_auto_extension(@NotNull AutoExtension callback); + public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @@ -293,12 +284,7 @@ public final class SQLite3Jni { @NotNull sqlite3 db, int ms ); - /** - Works like the C API except that it returns false, without side - effects, if auto extensions are currently running. (The JNI-level - list of extensions cannot be manipulated while it is being traversed.) - */ - public static synchronized native boolean sqlite3_cancel_auto_extension( + public static native boolean sqlite3_cancel_auto_extension( @NotNull AutoExtension ax ); @@ -595,16 +581,13 @@ public final class SQLite3Jni { Recall that even if opening fails, the output pointer might be non-null. Any error message about the failure will be in that object and it is up to the caller to sqlite3_close() that - db handle. Passing a null to sqlite3_close() is legal. - - Design note: this method is synchronized in order to help - alleviate a race condition involving auto-extensions. + db handle. */ - public static synchronized native int sqlite3_open( + public static native int sqlite3_open( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb ); - public static synchronized native int sqlite3_open_v2( + public static native int sqlite3_open_v2( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, int flags, @Nullable String zVfs ); @@ -728,7 +711,7 @@ public final class SQLite3Jni { extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ - public static synchronized native void sqlite3_reset_auto_extension(); + public static native void sqlite3_reset_auto_extension(); public static native void sqlite3_result_double( @NotNull sqlite3_context cx, double v diff --git a/manifest b/manifest index 8cb273b6f5..611838b934 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\swork\son\sthe\sJNI-specific\smutexes.\sRework\sthe\sNativePointerHolder\scache\slookup\sto\sbe\sslightly\ssimpler\sand\sO(1)\sinstead\sof\sO(N). -D 2023-08-14T13:27:40.885 +C Bring\shandling\sof\sthe\sJava\sauto-ext\shandler\smore\sin\sline\swith\sthe\score\sin\sterms\sof\slocking\sand\smutability\sduring\straversal.\sThis\sremoves\sthe\sexplicit\ssynchronous\srequirement\sfrom\sthe\sJava\sopen()\sand\sauto-ext\sbindings. +D 2023-08-14T17:12:55.531 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 0ca77c27d05b677191f105bc6f8570c916b78991a809d44566c60cfc16b50612 +F ext/jni/src/c/sqlite3-jni.c 4b93d970b142e62712f2cbdde01e1c5ed78af5f306238efad0e53276f26f1211 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -254,7 +254,7 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cd0627b5317435f9a6c72247915f9e32d6e8c225fd6f0db2c66b4a7f0b4e5601 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5897c1d11f6c780825c7ac739270365e6312990195fc135fc6b02d5536dbae18 F ext/jni/src/org/sqlite/jni/Tester1.java 368e836d943d9e882d2a217d0f582ed4141d164f174bebc50715acd57549a09b F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -2091,8 +2091,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 b62d93258b6a661f3a9b61468b3b641c14faf2d2196f78aca95fe14de43c9444 -R 640b629cd539e67d64418aa6a08729eb +P c84ded0e59aea4861d72b53b4b40cf580747c0f6ca58c334a996f1a825276cb5 +R 49ea94318acd6a04e4782997e6036b52 U stephan -Z db03beab64a32e82d4161306677f6282 +Z f760a4c17dc36361fbad3526fa5aa4c4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0f159ac8d9..a0b706b5cf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c84ded0e59aea4861d72b53b4b40cf580747c0f6ca58c334a996f1a825276cb5 \ No newline at end of file +42994b952e092ae4fa319395208622e887387ca3ff8ac57961c824a6c272bf0e \ No newline at end of file From 39c3f5f363726f9f56ebbafb43df0f18f415560d Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 15 Aug 2023 09:16:41 +0000 Subject: [PATCH 010/422] Remove a piece of stray debug output. No functional changes. FossilOrigin-Name: 00ac653562a66aad3112ea322d08be68e05e6bf7413c814dd3f81bf850fcf43b --- ext/jni/src/org/sqlite/jni/Tester1.java | 1 - manifest | 15 ++++++--------- manifest.uuid | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index ffe0b83846..dca49faf66 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -512,7 +512,6 @@ public class Tester1 { rc = sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, cur32, high32, false); affirm( 0 == rc ); affirm( cur32.value > 0 ); - outln(cur32.value," ",high32.value); affirm( high32.value == 0 /* always 0 for SCHEMA_USED */ ); sqlite3_close_v2(db); diff --git a/manifest b/manifest index 291b52a5b2..ae82a99351 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\ssome\sobsolete,\scommented-out\smakefile\scode.\sNo\sfunctional\sor\sbuild\schanges. -D 2023-08-14T14:21:28.009 +C Remove\sa\spiece\sof\sstray\sdebug\soutput.\sNo\sfunctional\schanges. +D 2023-08-15T09:16:41.186 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 4b6fd22e04e63eb65d8e4e38fda39ecf15ce244d034607517627ce2e766e7e65 -F ext/jni/src/org/sqlite/jni/Tester1.java 07c14a90427529ceba54b5e8344ca03602f5789dc53c4163ce22f92d8c577a11 +F ext/jni/src/org/sqlite/jni/Tester1.java 4253dc7bcff64500a9388f1a17d3d39dbe4eb9d7db9fc035ce6e2380d45ad5fc F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,11 +2091,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 544de2da09cd7b9bbb6cb4c52bb22325ace3391fca00c6a43847bfc158032b66 -R f380fd55d7d477e32c828eb055b6a7ec -T *branch * wasm-post-343 -T *sym-wasm-post-343 * -T -sym-trunk * Cancelled\sby\sbranch. +P ea574cd5de6dbc7db2f4cffe0b6c83c795c4ba634ae1984e5c95bb3529daa5bb +R 97fde9343e0b4cbe040d12a977e980b5 U stephan -Z 9a98a8a06619bcf87fff235150138679 +Z b97cc58b42337b264e35e5dff958b84d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 416c400a52..864e2f6649 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ea574cd5de6dbc7db2f4cffe0b6c83c795c4ba634ae1984e5c95bb3529daa5bb \ No newline at end of file +00ac653562a66aad3112ea322d08be68e05e6bf7413c814dd3f81bf850fcf43b \ No newline at end of file From 6b51e35a9b76f0896c4d2a986093d659c8ac8255 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 15 Aug 2023 09:26:47 +0000 Subject: [PATCH 011/422] Minor reshaping of Tester1 moving towards making a multi-threaded run mode. FossilOrigin-Name: f104c14c26c123ee78c09fc1bc59efb8668dc624da05c1d8dbeaf3c9dd02a393 --- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 2 +- ext/jni/src/org/sqlite/jni/Tester1.java | 49 +++++++++++++++------- manifest | 14 +++---- manifest.uuid | 2 +- 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 0833c1d4a3..74676b48b6 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -141,7 +141,7 @@ public final class SQLite3Jni { undefined if any database objects are (A) still active at the time it is called _and_ (B) calls are subsequently made into the library with such a database. Doing so will, at best, lead to a - crash. Azt worst, it will lead to the db possibly misbehaving + crash. At worst, it will lead to the db possibly misbehaving because some of its Java-bound state has been cleared. There is no immediate harm in (A) so long as condition (B) is not met. This process does _not_ actually close any databases or finalize diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 055e070bfd..2629c394c9 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -21,6 +21,12 @@ public class Tester1 { int dbOpen; } + private String name; + + Tester1(String name){ + this.name = name; + } + static final Metrics metrics = new Metrics(); private static final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); @@ -45,11 +51,17 @@ public class Tester1 { } static int affirmCount = 0; - public static void affirm(Boolean v){ + public static void affirm(Boolean v, String comment){ ++affirmCount; assert( v /* prefer assert over exception if it's enabled because - the JNI layer sometimes has to suppress exceptions. */); - if( !v ) throw new RuntimeException("Assertion failed."); + the JNI layer sometimes has to suppress exceptions, + so they might be squelched on their way back to the + top. */); + if( !v ) throw new RuntimeException(comment); + } + + public static void affirm(Boolean v){ + affirm(v, "Affirmation failed."); } private static void test1(){ @@ -65,7 +77,7 @@ public class Tester1 { affirm(SQLITE_MAX_TRIGGER_DEPTH>0); } - public static sqlite3 createNewDb(){ + static sqlite3 createNewDb(){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); int rc = sqlite3_open(":memory:", out); ++metrics.dbOpen; @@ -83,11 +95,11 @@ public class Tester1 { return db; } - public static void execSql(sqlite3 db, String[] sql){ + static void execSql(sqlite3 db, String[] sql){ execSql(db, String.join("", sql)); } - public static int execSql(sqlite3 db, boolean throwOnError, String sql){ + static int execSql(sqlite3 db, boolean throwOnError, String sql){ OutputPointer.Int32 oTail = new OutputPointer.Int32(); final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8); int pos = 0, n = 1; @@ -127,11 +139,11 @@ public class Tester1 { return rc; } - public static void execSql(sqlite3 db, String sql){ + static void execSql(sqlite3 db, String sql){ execSql(db, true, sql); } - public static sqlite3_stmt prepare(sqlite3 db, String sql){ + static sqlite3_stmt prepare(sqlite3 db, String sql){ outStmt.clear(); int rc = sqlite3_prepare(db, sql, outStmt); affirm( 0 == rc ); @@ -519,7 +531,6 @@ public class Tester1 { rc = sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, cur32, high32, false); affirm( 0 == rc ); affirm( cur32.value > 0 ); - outln(cur32.value," ",high32.value); affirm( high32.value == 0 /* always 0 for SCHEMA_USED */ ); sqlite3_close_v2(db); @@ -1110,12 +1121,9 @@ public class Tester1 { outln("Woke up."); } - public static void main(String[] args) throws Exception { - final long timeStart = System.nanoTime(); - test1(); + private void runTests() throws Exception { if(false) testCompileOption(); - final java.util.List liArgs = - java.util.Arrays.asList(args); + test1(); testOpenDb1(); testOpenDb2(); testPrepare123(); @@ -1141,12 +1149,21 @@ public class Tester1 { testAuthorizer(); testFts5(); testAutoExtension(); + } + + public static void main(String[] args) throws Exception { + + final long timeStart = System.nanoTime(); + new Tester1("main thread").runTests(); + final long timeEnd = System.nanoTime(); + + final java.util.List liArgs = + java.util.Arrays.asList(args); //testSleep(); if(liArgs.indexOf("-v")>0){ sqlite3_do_something_for_developer(); //listBoundMethods(); } - final long timeEnd = System.nanoTime(); affirm( SQLite3Jni.uncacheJniEnv() ); affirm( !SQLite3Jni.uncacheJniEnv() ); outln("Tests done. Metrics:"); @@ -1172,7 +1189,7 @@ public class Tester1 { outln("\tSQLite3Jni sqlite3_*() methods: "+ nNatives+" native methods and "+ (nMethods - nNatives)+" Java impls"); - outln("\tTotal time = " + outln("\tTotal test time = " +((timeEnd - timeStart)/1000000.0)+"ms"); } } diff --git a/manifest b/manifest index 611838b934..c5fa07875d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bring\shandling\sof\sthe\sJava\sauto-ext\shandler\smore\sin\sline\swith\sthe\score\sin\sterms\sof\slocking\sand\smutability\sduring\straversal.\sThis\sremoves\sthe\sexplicit\ssynchronous\srequirement\sfrom\sthe\sJava\sopen()\sand\sauto-ext\sbindings. -D 2023-08-14T17:12:55.531 +C Minor\sreshaping\sof\sTester1\smoving\stowards\smaking\sa\smulti-threaded\srun\smode. +D 2023-08-15T09:26:47.524 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -254,8 +254,8 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5897c1d11f6c780825c7ac739270365e6312990195fc135fc6b02d5536dbae18 -F ext/jni/src/org/sqlite/jni/Tester1.java 368e836d943d9e882d2a217d0f582ed4141d164f174bebc50715acd57549a09b +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 99334f54f5f41feb4c14dc988b93219e37799e032f2bc07bda6323b1dfb99e75 +F ext/jni/src/org/sqlite/jni/Tester1.java 63f02d45ad073ac9d98eb7d681a024b38f6abf978dd1454be9346cbf347b1b57 F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,8 +2091,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 c84ded0e59aea4861d72b53b4b40cf580747c0f6ca58c334a996f1a825276cb5 -R 49ea94318acd6a04e4782997e6036b52 +P 42994b952e092ae4fa319395208622e887387ca3ff8ac57961c824a6c272bf0e +R efccc6a7a6eed17207843d66c0fb4c2d U stephan -Z f760a4c17dc36361fbad3526fa5aa4c4 +Z 6864924ac8d4acb7bd32dec052e883fa # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a0b706b5cf..0857f50b77 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -42994b952e092ae4fa319395208622e887387ca3ff8ac57961c824a6c272bf0e \ No newline at end of file +f104c14c26c123ee78c09fc1bc59efb8668dc624da05c1d8dbeaf3c9dd02a393 \ No newline at end of file From abfe646c1223ff8b8c7e9de1ea69da75ff8136b7 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 15 Aug 2023 13:01:20 +0000 Subject: [PATCH 012/422] Add note about the current threading limitation to ext/jni/README.md. No code changes. FossilOrigin-Name: 653ed92dc39185cdedfab3ea518bc7ec2d2826120e5fa4cbdee3343301396184 --- ext/jni/README.md | 5 +++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ext/jni/README.md b/ext/jni/README.md index 80486d4a07..cb51a21cd3 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -44,8 +44,9 @@ Non-goals: Significant TODOs ======================================================================== -- Lots of APIs left to bind. Most "day-to-day" functionality is already - in place and is believed to work well. +- The initial beta release with version 3.43 has severe threading + limitations. Namely, two threads cannot call into the JNI-bound API + at once. This limitation will be remove in a subsequent release. Building diff --git a/manifest b/manifest index ae82a99351..0da289e830 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\spiece\sof\sstray\sdebug\soutput.\sNo\sfunctional\schanges. -D 2023-08-15T09:16:41.186 +C Add\snote\sabout\sthe\scurrent\sthreading\slimitation\sto\sext/jni/README.md.\sNo\scode\schanges. +D 2023-08-15T13:01:20.690 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,7 +232,7 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 3deba6bc0bf37c1ee5f15d1ff3c3512ae2f3cf44a2b8ae7b4af92690514b0cb4 -F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb +F ext/jni/README.md 5c60e4580aa5c94ff74d7bef1fb6231e578f7764e831a07b5981b6ab62b35560 F ext/jni/jar-dist.make 93da95f8fe01ef22fccacc27f2e805938058e91e8c72c0532558d3a812a42e74 F ext/jni/src/c/sqlite3-jni.c bea6b8691a5fa3a8626a771757bb261208d3c5fc6598266d3b0ee23d88e35632 F ext/jni/src/c/sqlite3-jni.h c5f941b057a24ee62942e6e1bf5a7fd527e5004d20d9638e84a9382813c3cf2a @@ -2091,8 +2091,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 ea574cd5de6dbc7db2f4cffe0b6c83c795c4ba634ae1984e5c95bb3529daa5bb -R 97fde9343e0b4cbe040d12a977e980b5 +P 00ac653562a66aad3112ea322d08be68e05e6bf7413c814dd3f81bf850fcf43b +R 68ba6514d93093aa325f9f497a3147a9 U stephan -Z b97cc58b42337b264e35e5dff958b84d +Z 5cbd8ac0238d9d299017246e54070fc0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 864e2f6649..c54d1a072a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00ac653562a66aad3112ea322d08be68e05e6bf7413c814dd3f81bf850fcf43b \ No newline at end of file +653ed92dc39185cdedfab3ea518bc7ec2d2826120e5fa4cbdee3343301396184 \ No newline at end of file From 95484726f607b332a46fa25fe8c46103141becb6 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 17 Aug 2023 09:49:53 +0000 Subject: [PATCH 013/422] Add SQLITE_EXTRA_AUTOEXT, similar to SQLITE_EXTRA_INIT but adds a builtin auto-extension provided by the client. Suggestion from [forum:00829394c74a670f| forum post 00829394c74a670f]. FossilOrigin-Name: 423e77277a61d7febf4c3fc737981fa22a82b5c774a8ada5375a01a0611535b2 --- manifest | 19 +++++++++++-------- manifest.uuid | 2 +- src/ctime.c | 3 +++ src/main.c | 7 ++++++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 49c9bc8c34..5fae43dfac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Refinements\sto\sinstructions\son\scompiling\sfor\sWindows. -D 2023-08-16T17:23:42.222 +C Add\sSQLITE_EXTRA_AUTOEXT,\ssimilar\sto\sSQLITE_EXTRA_INIT\sbut\sadds\sa\sbuiltin\sauto-extension\sprovided\sby\sthe\sclient.\sSuggestion\sfrom\s[forum:00829394c74a670f|\sforum\spost\s00829394c74a670f]. +D 2023-08-17T09:49:53.877 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -629,7 +629,7 @@ F src/btreeInt.h 91a9e0c41a0e71fa91a742ec285c63dd8dcb38b73d14fae0ed7209174ff0fdc F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e -F src/ctime.c cff2b493de894383832347c3a3f936a05f4e089a92b42366838da1735f3848b5 +F src/ctime.c db847fac81837ff5e5028a5f7505147ac645ae676104adc5bc08e356f243de40 F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94 F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387 F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef @@ -647,7 +647,7 @@ F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276 F src/json.c ae840f87b418f039f5d336b488933d09396bd31e6b31e855b93055ccaee4e255 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 98cfba10989b3da6f1807ad42444017742db7f100a54f1032af7a8b1295912c0 -F src/main.c fde8f13c876a658b4e8b74b77d875ca887915c174ea6a2f3122d80966f93d865 +F src/main.c 6287177c498427658b892fd816434d36bf9b85b33bf65d5280b897c3b34cee8c F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2 @@ -2092,8 +2092,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 0f6b2b33736ee07f17f3a4e5f077bb4d0e2481c8f81251b8ce6b78510f372237 -R 6b56e789e5267410fdb3971aefa50798 -U drh -Z 6657d0ca5d125a89c7fdb5a9a6cb9ceb +P d543c36c35f71c5f0a7ebf6f496feca40d16566d0c5b2c2ba205ff43437ffcd1 +R 3519e615bc0924b4429d475a70546643 +T *branch * extra-autoext +T *sym-extra-autoext * +T -sym-trunk * Cancelled\sby\sbranch. +U stephan +Z 1b624c06be0989f0074081f40aaff915 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e30cdd08fc..a1ac7d572e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d543c36c35f71c5f0a7ebf6f496feca40d16566d0c5b2c2ba205ff43437ffcd1 \ No newline at end of file +423e77277a61d7febf4c3fc737981fa22a82b5c774a8ada5375a01a0611535b2 \ No newline at end of file diff --git a/src/ctime.c b/src/ctime.c index 3687241680..03c89ff726 100644 --- a/src/ctime.c +++ b/src/ctime.c @@ -355,6 +355,9 @@ static const char * const sqlite3azCompileOpt[] = { #ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS "EXPLAIN_ESTIMATED_ROWS", #endif +#ifdef SQLITE_EXTRA_AUTOEXT + "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT), +#endif #ifdef SQLITE_EXTRA_IFNULLROW "EXTRA_IFNULLROW", #endif diff --git a/src/main.c b/src/main.c index cd02008909..b8f2622612 100644 --- a/src/main.c +++ b/src/main.c @@ -47,7 +47,9 @@ int sqlite3Fts5Init(sqlite3*); #ifdef SQLITE_ENABLE_STMTVTAB int sqlite3StmtVtabInit(sqlite3*); #endif - +#ifdef SQLITE_EXTRA_AUTOEXT +int SQLITE_EXTRA_AUTOEXT(sqlite3*); +#endif /* ** An array of pointers to extension initializer functions for ** built-in extensions. @@ -81,6 +83,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = { #ifdef SQLITE_ENABLE_BYTECODE_VTAB sqlite3VdbeBytecodeVtabInit, #endif +#ifdef SQLITE_EXTRA_AUTOEXT + SQLITE_EXTRA_AUTOEXT, +#endif }; #ifndef SQLITE_AMALGAMATION From 0fa2545e7f3aa39689eb1ca57e1bc886c1812fd3 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 17 Aug 2023 10:49:06 +0000 Subject: [PATCH 014/422] Remove the FIXME markers related to threading. Code style cleanups. FossilOrigin-Name: 154ab26dc6ba2d1fd976e8fe6dc1b1a06c734f7e9a276a3edc5c2f30b0d6d36a --- ext/jni/src/c/sqlite3-jni.c | 268 ++++++++++++++++-------------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 126 insertions(+), 156 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 37aa06460e..59c591f074 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -13,15 +13,15 @@ ** org.sqlite.jni.SQLiteJni (from which sqlite3-jni.h is generated). */ -/** - If you found this comment by searching the code for - CallStaticObjectMethod then you're the victim of an OpenJDK bug: - - https://bugs.openjdk.org/browse/JDK-8130659 - - It's known to happen with OpenJDK v8 but not with v19. - - This code does not use JNI's CallStaticObjectMethod(). +/* +** If you found this comment by searching the code for +** CallStaticObjectMethod then you're the victim of an OpenJDK bug: +** +** https://bugs.openjdk.org/browse/JDK-8130659 +** +** It's known to happen with OpenJDK v8 but not with v19. +** +** This code does not use JNI's CallStaticObjectMethod(). */ /* @@ -133,9 +133,13 @@ #undef INC__STRINGIFY #undef SQLITE_C +/* +** End of the sqlite3 lib setup. What follows is JNI-specific. +*/ + #include "sqlite3-jni.h" -#include /* only for testing/debugging */ #include +#include /* only for testing/debugging */ /* Only for debugging */ #define MARKER(pfexp) \ @@ -151,24 +155,24 @@ #define JDECL(ReturnType,Suffix) \ JNIEXPORT ReturnType JNICALL \ JFuncName(Suffix) -/** - Shortcuts for the first 2 parameters to all JNI bindings. - - The type of the jSelf arg differs, but no docs seem to mention - this: for static methods it's of type jclass and for non-static - it's jobject. jobject actually works for all funcs, in the sense - that it compiles and runs so long as we don't use jSelf (which is - only rarely needed in this code), but to be pedantically correct we - need the proper type in the signature. - - Not even the official docs mention this discrepancy: - - https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers +/* +** Shortcuts for the first 2 parameters to all JNI bindings. +** +** The type of the jSelf arg differs, but no docs seem to mention +** this: for static methods it's of type jclass and for non-static +** it's jobject. jobject actually works for all funcs, in the sense +** that it compiles and runs so long as we don't use jSelf (which is +** only rarely needed in this code), but to be pedantically correct we +** need the proper type in the signature. +** +** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers */ #define JENV_OSELF JNIEnv * const env, jobject jSelf #define JENV_CSELF JNIEnv * const env, jclass jKlazz -/* Helpers to account for -Xcheck:jni warnings about not having - checked for exceptions. */ +/* +** Helpers to account for -Xcheck:jni warnings about not having +** checked for exceptions. +*/ #define IFTHREW if((*env)->ExceptionCheck(env)) #define EXCEPTION_IGNORE (void)((*env)->ExceptionCheck(env)) #define EXCEPTION_CLEAR (*env)->ExceptionClear(env) @@ -272,9 +276,11 @@ static const struct { return (jint)CName((int)arg); \ } -/** Create a trivial JNI wrapper for (const mutf8_string * - CName(void)). This is only valid for functions which are known to - return ASCII or text which is equivalent in UTF-8 and MUTF-8. */ +/* +** Create a trivial JNI wrapper for (const mutf8_string * +** CName(void)). This is only valid for functions which are known to +** return ASCII or text which is equivalent in UTF-8 and MUTF-8. + */ #define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ JDECL(jstring,JniNameSuffix)(JENV_CSELF){ \ return (*env)->NewStringUTF( env, CName() ); \ @@ -318,40 +324,35 @@ static const struct { #define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) #define JBA_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) -/* Marker for code which needs(?) to be made thread-safe. REASON is a - terse reminder about why that function requires a mutex. -*/ -#define FIXME_THREADING(REASON) - enum { - /** - Size of the NativePointerHolder cache. Need enough space for - (only) the library's NativePointerHolder types, a fixed count - known at build-time. If we add more than this a fatal error will - be triggered with a reminder to increase this. This value needs - to be exactly the number of entries in the S3NphRefs object. The - index field of those entries are the keys for this particular - cache. + /* + ** Size of the NativePointerHolder cache. Need enough space for + ** (only) the library's NativePointerHolder types, a fixed count + ** known at build-time. If we add more than this a fatal error will + ** be triggered with a reminder to increase this. This value needs + ** to be exactly the number of entries in the S3NphRefs object. The + ** index field of those entries are the keys for this particular + ** cache. */ NphCache_SIZE = sizeof(S3NphRefs) / sizeof(S3NphRef) }; -/** - Cache entry for NativePointerHolder subclasses and OutputPointer - types. +/* +** Cache entry for NativePointerHolder subclasses and OutputPointer +** types. */ typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { const S3NphRef * pRef /* Entry from S3NphRefs. */; - jclass klazz /* global ref to the concrete - NativePointerHolder subclass represented by - zClassName */; - jmethodID midCtor /* klazz's no-arg constructor. Used by - new_NativePointerHolder_object(). */; - jfieldID fidValue /* NativePointerHolder.nativePointer and - OutputPointer.X.value */; - jfieldID fidSetAgg /* sqlite3_context::aggregateContext. Used only - by the sqlite3_context binding. */; + jclass klazz /* global ref to the concrete + NativePointerHolder subclass represented by + zClassName */; + jmethodID midCtor /* klazz's no-arg constructor. Used by + new_NativePointerHolder_object(). */; + jfieldID fidValue /* NativePointerHolder.nativePointer and + OutputPointer.X.value */; + jfieldID fidSetAgg /* sqlite3_context::aggregateContext. Used only + by the sqlite3_context binding. */; }; static void S3JniNphClass_clear(JNIEnv * const env, S3JniNphClass * const p){ @@ -373,10 +374,10 @@ struct S3JniHook{ lookup. */; }; -/** - Per-(sqlite3*) state for various JNI bindings. This state is - allocated as needed, cleaned up in sqlite3_close(_v2)(), and - recycled when possible. It is freed during sqlite3_shutdown(). +/* +** Per-(sqlite3*) state for various JNI bindings. This state is +** allocated as needed, cleaned up in sqlite3_close(_v2)(), and +** recycled when possible. It is freed during sqlite3_shutdown(). */ typedef struct S3JniDb S3JniDb; struct S3JniDb { @@ -408,16 +409,16 @@ struct S3JniDb { S3JniDb * pPrev /* Previous entry in the available/free list */; }; -/** - Cache for per-JNIEnv data. - - Potential TODO: move the jclass entries to global space because, - per https://developer.android.com/training/articles/perf-jni: - - > once you have a valid jclass global reference you can use it from - any attached thread. - - Whereas we cache new refs for each thread. +/* +** Cache for per-JNIEnv data. +** +** Potential TODO: move the jclass entries to global space because, +** per https://developer.android.com/training/articles/perf-jni: +** +** > once you have a valid jclass global reference you can use it from +** any attached thread. +** +** Whereas we cache new refs for each thread. */ typedef struct S3JniEnv S3JniEnv; struct S3JniEnv { @@ -434,25 +435,25 @@ struct S3JniEnv { jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; } g /* refs to global Java state */; - /** - pdbOpening is used to coordinate the Java/DB connection of a - being-open()'d db in the face of auto-extensions. "The problem" - is that auto-extensions run before we can bind the C db to its - Java representation, but auto-extensions require that binding. We - handle this as follows: - - - In open(), allocate the Java side of that connection and set - pdbOpening to point to that object. Note that it's per-thread, - and we remain in that thread until after the auto-extensions - are run. - - - Call open(), which triggers the auto-extension handler. - That handler uses pdbOpening to connect the native db handle - which it receives with pdbOpening. - - - When open() returns, check whether it invoked the auto-ext - handler. If not, complete the Java/C binding unless open() - returns a NULL db, in which case free pdbOpening. + /* + ** pdbOpening is used to coordinate the Java/DB connection of a + ** being-open()'d db in the face of auto-extensions. "The problem" + ** is that auto-extensions run before we can bind the C db to its + ** Java representation, but auto-extensions require that binding. We + ** handle this as follows: + ** + ** - In open(), allocate the Java side of that connection and set + ** pdbOpening to point to that object. Note that it's per-thread, + ** and we remain in that thread until after the auto-extensions + ** are run. + ** + ** - Call open(), which triggers the auto-extension handler. That + ** handler uses pdbOpening to connect the native db handle which + ** it receives with pdbOpening. + ** + ** - When open() returns, check whether it invoked the auto-ext + ** handler. If not, complete the Java/C binding unless open() + ** returns a NULL db, in which case free pdbOpening. */ S3JniDb * pdbOpening; #ifdef SQLITE_ENABLE_FTS5 @@ -466,30 +467,30 @@ struct S3JniEnv { #endif S3JniEnv * pPrev /* Previous entry in the linked list */; S3JniEnv * pNext /* Next entry in the linked list */; - /** - Cache of Java refs/IDs for NativePointerHolder subclasses. + /* + ** Cache of Java refs/IDs for NativePointerHolder subclasses. */ S3JniNphClass nph[NphCache_SIZE]; }; /* - 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). +** 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; struct S3JniAutoExtension { @@ -497,20 +498,20 @@ struct S3JniAutoExtension { jmethodID midFunc /* xEntryPoint() callback */; }; -/** - Global state, e.g. caches and metrics. +/* +** Global state, e.g. caches and metrics. */ static struct { - /** - According to: https://developer.ibm.com/articles/j-jni/ - - > A thread can get a JNIEnv by calling GetEnv() using the JNI - invocation interface through a JavaVM object. The JavaVM object - itself can be obtained by calling the JNI GetJavaVM() method - using a JNIEnv object and can be cached and shared across - threads. Caching a copy of the JavaVM object enables any thread - with access to the cached object to get access to its own - JNIEnv when necessary. + /* + ** According to: https://developer.ibm.com/articles/j-jni/ + ** + ** > A thread can get a JNIEnv by calling GetEnv() using the JNI + ** invocation interface through a JavaVM object. The JavaVM object + ** itself can be obtained by calling the JNI GetJavaVM() method + ** using a JNIEnv object and can be cached and shared across + ** threads. Caching a copy of the JavaVM object enables any thread + ** with access to the cached object to get access to its own + ** JNIEnv when necessary. */ JavaVM * jvm; struct { @@ -529,6 +530,7 @@ static struct { cannot always have this set to the current JNIEnv object. */; } perDb; + /* Internal metrics. */ struct { unsigned envCacheHits; unsigned envCacheMisses; @@ -1906,7 +1908,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, return rc; } -FIXME_THREADING(autoExt) JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ static int once = 0; int i; @@ -1955,7 +1956,6 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ return rc; } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ int rc; @@ -1970,31 +1970,26 @@ JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, return (jint)rc; } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1double)(JENV_CSELF, jobject jpStmt, jint ndx, jdouble val){ return (jint)sqlite3_bind_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1int)(JENV_CSELF, jobject jpStmt, jint ndx, jint val){ return (jint)sqlite3_bind_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1int64)(JENV_CSELF, jobject jpStmt, jint ndx, jlong val){ return (jint)sqlite3_bind_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1null)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jint)sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName){ int rc = 0; jbyte * const pBuf = JBA_TOC(jName); @@ -2006,7 +2001,6 @@ JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName return rc; } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ if(baData){ @@ -2020,13 +2014,11 @@ JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, } } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1zeroblob)(JENV_CSELF, jobject jpStmt, jint ndx, jint n){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1bind_1zeroblob64)(JENV_CSELF, jobject jpStmt, jint ndx, jlong n){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); @@ -2048,7 +2040,6 @@ static int s3jni_busy_handler(void* pState, int n){ return rc; } -FIXME_THREADING(S3JniEnv) JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc = 0; @@ -2078,8 +2069,6 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ : sqlite3_busy_handler(ps->pDb, 0, 0); } -FIXME_THREADING(S3JniEnv) -FIXME_THREADING(perDb) JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); if( ps ){ @@ -2089,7 +2078,6 @@ JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ return SQLITE_MISUSE; } -FIXME_THREADING(autoExt) JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; @@ -2148,14 +2136,10 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ return (jint)rc; } -FIXME_THREADING(S3JniEnv) -FIXME_THREADING(perDb) JDECL(jint,1close_1v2)(JENV_CSELF, jobject pDb){ return s3jni_close_db(env, pDb, 2); } -FIXME_THREADING(S3JniEnv) -FIXME_THREADING(perDb) JDECL(jint,1close)(JENV_CSELF, jobject pDb){ return s3jni_close_db(env, pDb, 1); } @@ -2194,8 +2178,6 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, UNREF_L(jName); } -FIXME_THREADING(S3JniEnv) -FIXME_THREADING(perDb) JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; @@ -2232,7 +2214,6 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ return rc; } -FIXME_THREADING(S3JniEnv) JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2246,25 +2227,21 @@ JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, } } -FIXME_THREADING(S3JniEnv) JDECL(jdouble,1column_1double)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jdouble)sqlite3_column_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnv) JDECL(jint,1column_1int)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jint)sqlite3_column_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnv) JDECL(jlong,1column_1int64)(JENV_CSELF, jobject jpStmt, jint ndx){ return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -FIXME_THREADING(S3JniEnv) JDECL(jbyteArray,1column_1text)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2273,7 +2250,6 @@ JDECL(jbyteArray,1column_1text)(JENV_CSELF, jobject jpStmt, return s3jni_new_jbyteArray(env, p, n); } -FIXME_THREADING(S3JniEnv) JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); @@ -2282,7 +2258,6 @@ JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, return s3jni_text16_to_jstring(env, p, n); } -FIXME_THREADING(S3JniEnv) JDECL(jobject,1column_1value)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_value * const sv = sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); @@ -2312,7 +2287,6 @@ static void s3jni_rollback_hook_impl(void *pP){ (void)s3jni_commit_rollback_hook_impl(0, pP); } -FIXME_THREADING(perDb) static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,jobject jDb, jobject jHook){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); @@ -2381,7 +2355,6 @@ JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ return rc; } -FIXME_THREADING(perDb) JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){ sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0; @@ -2509,7 +2482,6 @@ JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( return rc; } -FIXME_THREADING(perDb) /* sqlite3_db_config() for (int,int*) */ /* ACHTUNG: openjdk v19 creates a different mangled name for this function than openjdk v8 does. */ @@ -3174,7 +3146,6 @@ JDECL(void,1set_1last_1insert_1rowid)(JENV_CSELF, jobject jpDb, jlong rowId){ (sqlite3_int64)rowId); } -FIXME_THREADING(nphCache) JDECL(jint,1status)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, jboolean reset ){ int iCur = 0, iHigh = 0; @@ -3186,7 +3157,6 @@ JDECL(jint,1status)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, return (jint)rc; } -FIXME_THREADING(nphCache) JDECL(jint,1status64)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, jboolean reset ){ sqlite3_int64 iCur = 0, iHigh = 0; diff --git a/manifest b/manifest index c5fa07875d..baff99aba0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sreshaping\sof\sTester1\smoving\stowards\smaking\sa\smulti-threaded\srun\smode. -D 2023-08-15T09:26:47.524 +C Remove\sthe\sFIXME\smarkers\srelated\sto\sthreading.\sCode\sstyle\scleanups. +D 2023-08-17T10:49:06.982 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 4b93d970b142e62712f2cbdde01e1c5ed78af5f306238efad0e53276f26f1211 +F ext/jni/src/c/sqlite3-jni.c f3c4512da82b1e4b735bd350912f7434006536daba42034e67ca1c9f55a3311c F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -2091,8 +2091,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 42994b952e092ae4fa319395208622e887387ca3ff8ac57961c824a6c272bf0e -R efccc6a7a6eed17207843d66c0fb4c2d +P f104c14c26c123ee78c09fc1bc59efb8668dc624da05c1d8dbeaf3c9dd02a393 +R 752949cb94cc31a882224e4bb6c2f9c9 U stephan -Z 6864924ac8d4acb7bd32dec052e883fa +Z 3fb553739319fd15e325c90ff8ac1d2c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0857f50b77..f9ca946375 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f104c14c26c123ee78c09fc1bc59efb8668dc624da05c1d8dbeaf3c9dd02a393 \ No newline at end of file +154ab26dc6ba2d1fd976e8fe6dc1b1a06c734f7e9a276a3edc5c2f30b0d6d36a \ No newline at end of file From 1dcb24698875a44919cf843daa86b8f08de0ccc7 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 17 Aug 2023 12:44:52 +0000 Subject: [PATCH 015/422] Minor internal JNI cleanups and fixes. FossilOrigin-Name: 0e9437de026cbfb333b90bb3400f1c015f85d49d73a25ad1000623216b88bfa0 --- ext/jni/src/c/sqlite3-jni.c | 157 +++++++++++------------- ext/jni/src/org/sqlite/jni/Tester1.java | 12 +- manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 90 insertions(+), 95 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 59c591f074..bfc885eb44 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -222,7 +222,8 @@ struct S3NphRef { }; /** - Keys for each concrete NativePointerHolder subclass. + Keys for each concrete NativePointerHolder subclass. These are to + be used with S3JniGlobal_nph_cache() and friends. */ static const struct { const S3NphRef sqlite3; @@ -243,7 +244,7 @@ static const struct { const S3NphRef Fts5Tokenizer; #endif } S3NphRefs = { -#define NREF(INDEX, NAME) { INDEX, "org/sqlite/jni/" NAME } +#define NREF(INDEX, JAVANAME) { INDEX, "org/sqlite/jni/" JAVANAME } NREF(0, "sqlite3"), NREF(1, "sqlite3_stmt"), NREF(2, "sqlite3_context"), @@ -298,9 +299,11 @@ static const struct { return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \ } /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ -#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ - JDECL(jstring,JniNameSuffix)(JENV_CSELF, jobject pStmt, jint ndx){ \ - return (*env)->NewStringUTF(env, CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx)); \ +#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ + JDECL(jstring,JniNameSuffix)(JENV_CSELF, jobject pStmt, jint ndx){ \ + return s3jni_utf8_to_jstring(S3JniGlobal_env_cache(env), \ + CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ + -1); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ #define WRAP_INT_DB(JniNameSuffix,CName) \ @@ -442,18 +445,19 @@ struct S3JniEnv { ** Java representation, but auto-extensions require that binding. We ** handle this as follows: ** - ** - In open(), allocate the Java side of that connection and set - ** pdbOpening to point to that object. Note that it's per-thread, - ** and we remain in that thread until after the auto-extensions - ** are run. + ** - In the JNI side of sqlite3_open(), allocate the Java side of + ** that connection and set pdbOpening to point to that + ** object. Note that it's per-thread, and we remain in that + ** thread until after the auto-extensions are run. ** - ** - Call open(), which triggers the auto-extension handler. That - ** handler uses pdbOpening to connect the native db handle which - ** it receives with pdbOpening. + ** - Call sqlite3_open(), which triggers the auto-extension + ** handler. That handler uses pdbOpening to connect the native + ** db handle which it receives with pdbOpening. ** - ** - When open() returns, check whether it invoked the auto-ext - ** handler. If not, complete the Java/C binding unless open() - ** returns a NULL db, in which case free pdbOpening. + ** - When sqlite3_open() returns, check whether pdbOpening->pDb is + ** NULL. If it isn't, auto-extension handling set it up. If it + ** is, complete the Java/C binding unless sqlite3_open() returns + ** a NULL db, in which case free pdbOpening. */ S3JniDb * pdbOpening; #ifdef SQLITE_ENABLE_FTS5 @@ -636,8 +640,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ S3JniGlobal.envCache.aFree = row->pNext; if( row->pNext ) row->pNext->pPrev = 0; }else{ - row = sqlite3_malloc(sizeof(S3JniEnv)); - OOM_CHECK(row); + row = s3jni_malloc(env, sizeof(S3JniEnv)); } memset(row, 0, sizeof(*row)); row->pNext = S3JniGlobal.envCache.aHead; @@ -976,7 +979,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ if(ps->env == env){ S3JniDb * const pPrev = ps->pPrev; S3JniDb_set_aside(ps); - assert(pPrev ? pPrev->pNext==pNext : 1); + assert( pPrev ? pPrev->pNext==pNext : 1 ); assert( ps == S3JniGlobal.perDb.aFree ); } } @@ -994,7 +997,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; int i; - assert( 0!=S3JniGlobal.envCache.mutex && "Env mutex misuse."); + MUTEX_ASSERT_LOCKER_ENV; row = S3JniGlobal.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ @@ -1029,19 +1032,15 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ return 1; } -/** - Searches the NativePointerHolder cache for the given combination. - If it finds one, it returns it as-is. If it doesn't, it populates a - cache slot's klazz member and returns the cache slot. - - It is up to the caller to populate the other members of the returned - object if needed. - - zClassName must be a static string so we can use its address as a - cache key. - - This simple cache catches >99% of searches in the current - (2023-07-31) tests. +/* +** Searches the NativePointerHolder cache for the given combination of +** args. It returns a cache entry with its klazz member set. +** +** It is up to the caller to populate the other members of the returned +** object if needed. +** +** This simple cache catches >99% of searches in the current +** (2023-07-31) tests. */ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* pRef){ /** @@ -1094,19 +1093,15 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, jclass klazz){ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, S3NphRef const* pRef){ jfieldID setter = 0; - S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); - if(pCache && pCache->klazz && pCache->fidValue){ - setter = pCache->fidValue; + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); + if(pNC->fidValue){ + setter = pNC->fidValue; assert(setter); }else{ - jclass const klazz = - pCache ? pCache->klazz : (*env)->GetObjectClass(env, ppOut); - setter = NativePointerHolder_getField(env, klazz); - if(pCache){ - assert(pCache->klazz); - assert(!pCache->fidValue); - pCache->fidValue = setter; - } + jclass const klazz = pNC->klazz + ? pNC->klazz + : (pNC->klazz = (*env)->GetObjectClass(env, ppOut)); + setter = pNC->fidValue = NativePointerHolder_getField(env, klazz); } (*env)->SetLongField(env, ppOut, setter, (jlong)p); EXCEPTION_IS_FATAL("Could not set NativePointerHolder.nativePointer."); @@ -1115,23 +1110,20 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * 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. + cache key. This is a no-op if pObj is NULL. */ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const* pRef){ if( pObj ){ jfieldID getter = 0; void * rv = 0; - S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); - if(pCache && pCache->fidValue){ - getter = pCache->fidValue; + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); + if(pNC->fidValue){ + getter = pNC->fidValue; }else{ - jclass const klazz = - pCache ? pCache->klazz : (*env)->GetObjectClass(env, pObj); - getter = NativePointerHolder_getField(env, klazz); - if(pCache){ - assert(pCache->klazz); - pCache->fidValue = getter; - } + jclass const klazz = pNC->klazz + ? pNC->klazz + : (pNC->klazz = (*env)->GetObjectClass(env, pObj)); + getter = pNC->fidValue = NativePointerHolder_getField(env, klazz); } rv = (void*)(*env)->GetLongField(env, pObj, getter); IFTHREW_REPORT; @@ -1507,7 +1499,7 @@ typedef struct { } ResultJavaVal; /* For use with sqlite3_result/value_pointer() */ -#define RESULT_JAVA_VAL_STRING "ResultJavaVal" +#define ResultJavaValuePtrStr "ResultJavaVal" static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); @@ -1921,7 +1913,8 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ /* Look for match or first empty slot. */ ax = &S3JniGlobal.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - break /* this as a no-op. */; + MUTEX_EXT_LEAVE; + return 0 /* this as a no-op. */; }else if( !ax->jObj && firstEmptySlot<0 ){ firstEmptySlot = (int)i; } @@ -2083,8 +2076,8 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ jboolean rc = JNI_FALSE; int i; MUTEX_EXT_ENTER; -#if 1 - for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + /* This algo mirrors the one in the core. */ + for( i = S3JniGlobal.autoExt.nAlloc-1; i >= 0; --i ){ ax = &S3JniGlobal.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniAutoExtension_clear(env, ax); @@ -2097,21 +2090,6 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ break; } } -#else - /* Why does this impl lead to an invalid unref error? */ - for( i = S3JniGlobal.autoExt.nAlloc-1; i <= 0; --i ){ - ax = &S3JniGlobal.autoExt.pExt[i]; - if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - S3JniAutoExtension_clear(env, ax); - /* Move final entry into this slot. */ - *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1]; - memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1], 0, - sizeof(S3JniAutoExtension)); - rc = JNI_TRUE; - break; - } - } -#endif MUTEX_EXT_LEAVE; return rc; } @@ -2887,7 +2865,7 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ return rc; } -JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ +static void s3jni_reset_auto_extension(JNIEnv *env){ int i; MUTEX_EXT_ENTER; for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ @@ -2897,6 +2875,10 @@ JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ MUTEX_EXT_LEAVE; } +JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ + s3jni_reset_auto_extension(env); +} + /* sqlite3_result_text/blob() and friends. */ static void result_blob_text(int asBlob, int as64, int eTextRep/*only for (asBlob=0)*/, @@ -3037,8 +3019,8 @@ JDECL(void,1result_1java_1object)(JENV_CSELF, jobject jpCx, jobject v){ if(v){ ResultJavaVal * const rjv = ResultJavaVal_alloc(env, v); if(rjv){ - sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv, RESULT_JAVA_VAL_STRING, - ResultJavaVal_finalizer); + sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv, + ResultJavaValuePtrStr, ResultJavaVal_finalizer); }else{ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } @@ -3052,16 +3034,19 @@ JDECL(void,1result_1null)(JENV_CSELF, jobject jpCx){ } JDECL(void,1result_1text)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jint nMax){ - return result_blob_text(0, 0, SQLITE_UTF8, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); + return result_blob_text(0, 0, SQLITE_UTF8, env, + PtrGet_sqlite3_context(jpCx), jBa, nMax); } JDECL(void,1result_1text64)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jlong nMax, jint eTextRep){ - return result_blob_text(0, 1, eTextRep, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); + return result_blob_text(0, 1, eTextRep, env, + PtrGet_sqlite3_context(jpCx), jBa, nMax); } JDECL(void,1result_1value)(JENV_CSELF, jobject jpCx, jobject jpSVal){ - sqlite3_result_value(PtrGet_sqlite3_context(jpCx), PtrGet_sqlite3_value(jpSVal)); + sqlite3_result_value(PtrGet_sqlite3_context(jpCx), + PtrGet_sqlite3_value(jpSVal)); } JDECL(void,1result_1zeroblob)(JENV_CSELF, jobject jpCx, jint v){ @@ -3069,10 +3054,11 @@ JDECL(void,1result_1zeroblob)(JENV_CSELF, jobject jpCx, jint v){ } JDECL(jint,1result_1zeroblob64)(JENV_CSELF, jobject jpCx, jlong v){ - return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v); + return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx), + (sqlite3_int64)v); } -JDECL(jobject,1rollback_1hook)(JENV_CSELF,jobject jDb, jobject jHook){ +JDECL(jobject,1rollback_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ return s3jni_commit_rollback_hook(0, env, jDb, jHook); } @@ -3094,6 +3080,7 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_set_authorizer() callback"); EXCEPTION_CLEAR; + if( !rc ) rc = SQLITE_ERROR; } UNREF_L(s0); UNREF_L(s1); @@ -3173,8 +3160,8 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, int rc = 0; jbyte * const pG = JBA_TOC(baG); jbyte * const pT = pG ? JBA_TOC(baT) : 0; - OOM_CHECK(pT); + OOM_CHECK(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike @@ -3195,6 +3182,7 @@ JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){ } JDECL(jint,1shutdown)(JENV_CSELF){ + s3jni_reset_auto_extension(env); MUTEX_ENV_ENTER; while( S3JniGlobal.envCache.aHead ){ S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env ); @@ -3417,7 +3405,8 @@ JDECL(jlong,1value_1int64)(JENV_CSELF, jobject jpSVal){ } JDECL(jobject,1value_1java_1object)(JENV_CSELF, jobject jpSVal){ - ResultJavaVal * const rv = sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal), RESULT_JAVA_VAL_STRING); + ResultJavaVal * const rv = sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal), + ResultJavaValuePtrStr); return rv ? rv->jObj : NULL; } @@ -4304,8 +4293,8 @@ Java_org_sqlite_jni_tester_SQLTester_strglob( int rc = 0; jbyte * const pG = JBA_TOC(baG); jbyte * const pT = pG ? JBA_TOC(baT) : 0; - OOM_CHECK(pT); + OOM_CHECK(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = !SQLTester_strnotglob((const char *)pG, (const char *)pT); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 2629c394c9..d5ae0c44e5 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1121,7 +1121,7 @@ public class Tester1 { outln("Woke up."); } - private void runTests() throws Exception { + private void runTests(boolean fromThread) throws Exception { if(false) testCompileOption(); test1(); testOpenDb1(); @@ -1148,13 +1148,19 @@ public class Tester1 { testUpdateHook(); testAuthorizer(); testFts5(); - testAutoExtension(); + if(!fromThread){ + testAutoExtension(); + } + } + + public void run() throws Exception{ + runTests(true); } public static void main(String[] args) throws Exception { final long timeStart = System.nanoTime(); - new Tester1("main thread").runTests(); + new Tester1("main thread").runTests(false); final long timeEnd = System.nanoTime(); final java.util.List liArgs = diff --git a/manifest b/manifest index baff99aba0..c9a1ac5402 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\sFIXME\smarkers\srelated\sto\sthreading.\sCode\sstyle\scleanups. -D 2023-08-17T10:49:06.982 +C Minor\sinternal\sJNI\scleanups\sand\sfixes. +D 2023-08-17T12:44:52.096 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c f3c4512da82b1e4b735bd350912f7434006536daba42034e67ca1c9f55a3311c +F ext/jni/src/c/sqlite3-jni.c 4af91793e92f5d195c8668cf323ca7dfd25549efa9dfc6a0344020da3c42cb4b F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 99334f54f5f41feb4c14dc988b93219e37799e032f2bc07bda6323b1dfb99e75 -F ext/jni/src/org/sqlite/jni/Tester1.java 63f02d45ad073ac9d98eb7d681a024b38f6abf978dd1454be9346cbf347b1b57 +F ext/jni/src/org/sqlite/jni/Tester1.java f2f8fa157ddc42f91b6102d7ed78d1045d5072ae702bcefd868984518c97f9ae F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,8 +2091,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 f104c14c26c123ee78c09fc1bc59efb8668dc624da05c1d8dbeaf3c9dd02a393 -R 752949cb94cc31a882224e4bb6c2f9c9 +P 154ab26dc6ba2d1fd976e8fe6dc1b1a06c734f7e9a276a3edc5c2f30b0d6d36a +R b13f61ed94c6ec470e8a131c5363f0b3 U stephan -Z 3fb553739319fd15e325c90ff8ac1d2c +Z c23fb72e5b16d04504744e0f8c1cbcfd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f9ca946375..5a48967def 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -154ab26dc6ba2d1fd976e8fe6dc1b1a06c734f7e9a276a3edc5c2f30b0d6d36a \ No newline at end of file +0e9437de026cbfb333b90bb3400f1c015f85d49d73a25ad1000623216b88bfa0 \ No newline at end of file From 6c5f96fc4df5c92a3ba109b999ff3c6422e88b05 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 17 Aug 2023 13:13:22 +0000 Subject: [PATCH 016/422] Tighten up the JNI auto-ext handling. FossilOrigin-Name: c09c8d05a20d916a9d9304eeea723ef7666a862a9e53f5feeeb1b03f9153d4b2 --- ext/jni/src/c/sqlite3-jni.c | 54 +++++++++++++++---------- ext/jni/src/org/sqlite/jni/Tester1.java | 36 +++++++++++++++++ manifest | 14 +++---- manifest.uuid | 2 +- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index bfc885eb44..aafb99efe5 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1236,13 +1236,19 @@ static void S3JniAutoExtension_clear(JNIEnv * const env, /** Initializes a pre-allocated S3JniAutoExtension object. Returns non-0 if there is an error collecting the required state from - jAutoExt (which must be an AutoExtension object). + jAutoExt (which must be an AutoExtension object). On error, it + passes ax to S3JniAutoExtension_clear(). */ static int S3JniAutoExtension_init(JNIEnv *const env, S3JniAutoExtension * const ax, jobject const jAutoExt){ jclass klazz; klazz = (*env)->GetObjectClass(env, jAutoExt); + IFTHREW{ + EXCEPTION_REPORT; + EXCEPTION_CLEAR; + assert(!klazz); + } if(!klazz){ S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; @@ -1876,7 +1882,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, for( i = 0; go && 0==rc; ++i ){ S3JniAutoExtension const * ax; MUTEX_EXT_ENTER; - if( i >= S3JniGlobal.autoExt.nAlloc ){ + if( i >= S3JniGlobal.autoExt.nExt ){ ax = 0; go = 0; }else{ @@ -1893,7 +1899,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, UNREF_L(ex); *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); sqlite3_free(zMsg); - rc = rc ? rc : SQLITE_ERROR; + if( !rc ) rc = SQLITE_ERROR; } } } @@ -1905,25 +1911,20 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ int i; S3JniAutoExtension * ax; int rc = 0; - int firstEmptySlot = -1; if( !jAutoExt ) return SQLITE_MISUSE; MUTEX_EXT_ENTER; - for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + for( i = 0; i < S3JniGlobal.autoExt.nExt; ++i ){ /* Look for match or first empty slot. */ ax = &S3JniGlobal.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ MUTEX_EXT_LEAVE; return 0 /* this as a no-op. */; - }else if( !ax->jObj && firstEmptySlot<0 ){ - firstEmptySlot = (int)i; } } - if(i == S3JniGlobal.autoExt.nAlloc ){ - if( firstEmptySlot >= 0 ){ - ax = &S3JniGlobal.autoExt.pExt[firstEmptySlot]; - rc = S3JniAutoExtension_init(env, ax, jAutoExt); - }else{ + if(i == S3JniGlobal.autoExt.nExt ){ + assert( S3JniGlobal.autoExt.nExt <= S3JniGlobal.autoExt.nAlloc ); + if( S3JniGlobal.autoExt.nExt == S3JniGlobal.autoExt.nAlloc ){ unsigned n = 1 + S3JniGlobal.autoExt.nAlloc; S3JniAutoExtension * const aNew = sqlite3_realloc( S3JniGlobal.autoExt.pExt, @@ -1932,17 +1933,25 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ rc = SQLITE_NOMEM; }else{ S3JniGlobal.autoExt.pExt = aNew; - ax = &S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc]; ++S3JniGlobal.autoExt.nAlloc; - rc = S3JniAutoExtension_init(env, ax, jAutoExt); - assert( rc ? 0==ax->jObj : 0!=ax->jObj ); } } + if( 0==rc ){ + ax = &S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt]; + rc = S3JniAutoExtension_init(env, ax, jAutoExt); + assert( rc ? 0==ax->jObj : 0!=ax->jObj ); + } } if( 0==rc ){ - ++S3JniGlobal.autoExt.nExt; if( 0==once && ++once ){ - sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); + rc = sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); + if( rc ){ + assert( ax ); + S3JniAutoExtension_clear(env, ax); + } + } + if( 0==rc ){ + ++S3JniGlobal.autoExt.nExt; } } MUTEX_EXT_LEAVE; @@ -2077,15 +2086,16 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ int i; MUTEX_EXT_ENTER; /* This algo mirrors the one in the core. */ - for( i = S3JniGlobal.autoExt.nAlloc-1; i >= 0; --i ){ + for( i = S3JniGlobal.autoExt.nExt-1; i >= 0; --i ){ ax = &S3JniGlobal.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniAutoExtension_clear(env, ax); /* Move final entry into this slot. */ - *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1]; - memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nAlloc - 1], 0, - sizeof(S3JniAutoExtension)); --S3JniGlobal.autoExt.nExt; + *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt]; + memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt], 0, + sizeof(S3JniAutoExtension)); + assert(! S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt].jObj ); rc = JNI_TRUE; break; } @@ -2868,7 +2878,7 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; MUTEX_EXT_ENTER; - for( i = 0; i < S3JniGlobal.autoExt.nAlloc; ++i ){ + for( i = 0; i < S3JniGlobal.autoExt.nExt; ++i ){ S3JniAutoExtension_clear( env, &S3JniGlobal.autoExt.pExt[i] ); } S3JniGlobal.autoExt.nExt = 0; diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index d5ae0c44e5..f6ca7a8547 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1112,7 +1112,43 @@ public class Tester1 { } affirm( err!=null ); affirm( err.getMessage().indexOf(toss.value)>0 ); + toss.value = null; + + val.value = 0; + final AutoExtension ax2 = new AutoExtension(){ + public synchronized int xEntryPoint(sqlite3 db){ + ++val.value; + return 0; + } + }; + rc = sqlite3_auto_extension( ax2 ); + affirm( 0 == rc ); + sqlite3_close(createNewDb()); + affirm( 2 == val.value ); affirm( sqlite3_cancel_auto_extension(ax) ); + affirm( !sqlite3_cancel_auto_extension(ax) ); + sqlite3_close(createNewDb()); + affirm( 3 == val.value ); + rc = sqlite3_auto_extension( ax ); + affirm( 0 == rc ); + sqlite3_close(createNewDb()); + affirm( 5 == val.value ); + affirm( sqlite3_cancel_auto_extension(ax2) ); + affirm( !sqlite3_cancel_auto_extension(ax2) ); + sqlite3_close(createNewDb()); + affirm( 6 == val.value ); + rc = sqlite3_auto_extension( ax2 ); + affirm( 0 == rc ); + sqlite3_close(createNewDb()); + affirm( 8 == val.value ); + + sqlite3_reset_auto_extension(); + sqlite3_close(createNewDb()); + affirm( 8 == val.value ); + affirm( !sqlite3_cancel_auto_extension(ax) ); + affirm( !sqlite3_cancel_auto_extension(ax2) ); + sqlite3_close(createNewDb()); + affirm( 8 == val.value ); } private static void testSleep(){ diff --git a/manifest b/manifest index c9a1ac5402..ba1b065e5c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sinternal\sJNI\scleanups\sand\sfixes. -D 2023-08-17T12:44:52.096 +C Tighten\sup\sthe\sJNI\sauto-ext\shandling. +D 2023-08-17T13:13:22.545 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c 4af91793e92f5d195c8668cf323ca7dfd25549efa9dfc6a0344020da3c42cb4b +F ext/jni/src/c/sqlite3-jni.c d13cceb21d449d479a7772ad004eeb3a659eebc2759ad22284a7ee6a0a4a9f62 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 99334f54f5f41feb4c14dc988b93219e37799e032f2bc07bda6323b1dfb99e75 -F ext/jni/src/org/sqlite/jni/Tester1.java f2f8fa157ddc42f91b6102d7ed78d1045d5072ae702bcefd868984518c97f9ae +F ext/jni/src/org/sqlite/jni/Tester1.java 68b88b3098ce60134f4298488f890871398a77477af0a1b21797c59c911060c1 F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,8 +2091,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 154ab26dc6ba2d1fd976e8fe6dc1b1a06c734f7e9a276a3edc5c2f30b0d6d36a -R b13f61ed94c6ec470e8a131c5363f0b3 +P 0e9437de026cbfb333b90bb3400f1c015f85d49d73a25ad1000623216b88bfa0 +R 9c230dc36749890d5273d4c55c46fd59 U stephan -Z c23fb72e5b16d04504744e0f8c1cbcfd +Z d4ea42705a0c21f8fa4c587cb8cbbc10 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5a48967def..d5ad50128f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0e9437de026cbfb333b90bb3400f1c015f85d49d73a25ad1000623216b88bfa0 \ No newline at end of file +c09c8d05a20d916a9d9304eeea723ef7666a862a9e53f5feeeb1b03f9153d4b2 \ No newline at end of file From 383df02b16891a71c726194dbf08383a74465cd1 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 17 Aug 2023 22:04:07 +0000 Subject: [PATCH 017/422] Remove some obsolete JNI-internal docs. FossilOrigin-Name: 00a2a3736a6dcde81d920815520040f3c47f965165e7128ca1f4062e6ec7c17c --- ext/jni/src/c/sqlite3-jni.c | 13 +------------ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index aafb99efe5..d68c29e220 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -481,20 +481,9 @@ struct S3JniEnv { ** Whether auto extensions are feasible here is currently unknown due ** to... ** -** 1) JNIEnv/threading issues. A db instance is mapped to a specific +** 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; struct S3JniAutoExtension { diff --git a/manifest b/manifest index ba1b065e5c..fb4339f427 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Tighten\sup\sthe\sJNI\sauto-ext\shandling. -D 2023-08-17T13:13:22.545 +C Remove\ssome\sobsolete\sJNI-internal\sdocs. +D 2023-08-17T22:04:07.712 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -234,7 +234,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c d13cceb21d449d479a7772ad004eeb3a659eebc2759ad22284a7ee6a0a4a9f62 +F ext/jni/src/c/sqlite3-jni.c 2682b02c376290924927386e8e0ab16c8b88feb50df584147e81696d8fb459e2 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -2091,8 +2091,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 0e9437de026cbfb333b90bb3400f1c015f85d49d73a25ad1000623216b88bfa0 -R 9c230dc36749890d5273d4c55c46fd59 +P c09c8d05a20d916a9d9304eeea723ef7666a862a9e53f5feeeb1b03f9153d4b2 +R 8dc048810b080415033a7139c2c39321 U stephan -Z d4ea42705a0c21f8fa4c587cb8cbbc10 +Z f47443de82631501c87889e378989189 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d5ad50128f..38b3001c56 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c09c8d05a20d916a9d9304eeea723ef7666a862a9e53f5feeeb1b03f9153d4b2 \ No newline at end of file +00a2a3736a6dcde81d920815520040f3c47f965165e7128ca1f4062e6ec7c17c \ No newline at end of file From ccbfe97cd5ff9138cfe6134657aae13e9d27bcf5 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 18 Aug 2023 14:16:26 +0000 Subject: [PATCH 018/422] Extend the importDb() method of both OPFS VFSes to (A) support reading in an async streaming fashion via a callback and (B) automatically disable WAL mode in the imported db. FossilOrigin-Name: 9b1398c96a4fd0b59e65faa8d5c98de4129f0f0357732f12cb2f5c53a08acdc2 --- ext/wasm/api/sqlite3-api-prologue.js | 39 +++++++- ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js | 61 +++++++++++- ext/wasm/api/sqlite3-vfs-opfs.c-pp.js | 95 +++++++++++++++---- ext/wasm/tester1.c-pp.js | 45 ++++++++- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 223 insertions(+), 37 deletions(-) diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index ca5d1c44ff..5abb13b990 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -772,8 +772,43 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( isSharedTypedArray, toss: function(...args){throw new Error(args.join(' '))}, toss3, - typedArrayPart - }; + typedArrayPart, + /** + Given a byte array or ArrayBuffer, this function throws if the + lead bytes of that buffer do not hold a SQLite3 database header, + else it returns without side effects. + + Added in 3.44. + */ + affirmDbHeader: function(bytes){ + if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const header = "SQLite format 3"; + if( header.length > bytes.byteLength ){ + toss3("Input does not contain an SQLite3 database header."); + } + for(let i = 0; i < header.length; ++i){ + if( header.charCodeAt(i) !== bytes[i] ){ + toss3("Input does not contain an SQLite3 database header."); + } + } + }, + /** + Given a byte array or ArrayBuffer, this function throws if the + database does not, at a cursory glance, appear to be an SQLite3 + database. It only examines the size and header, but further + checks may be added in the future. + + Added in 3.44. + */ + affirmIsDb: function(bytes){ + if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + const n = bytes.byteLength; + if(n<512 || n%512!==0) { + toss3("Byte array size",n,"is invalid for an SQLite3 db."); + } + util.affirmDbHeader(bytes); + } + }/*util*/; Object.assign(wasm, { /** diff --git a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js index 709d3414c3..8e874f7282 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js @@ -59,6 +59,7 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss3 = sqlite3.util.toss3; const initPromises = Object.create(null); const capi = sqlite3.capi; + const util = sqlite3.util; const wasm = sqlite3.wasm; // Config opts for the VFS... const SECTOR_SIZE = 4096; @@ -869,9 +870,48 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ return b; } + //! Impl for importDb() when its 2nd arg is a function. + async importDbChunked(name, callback){ + const sah = this.#mapFilenameToSAH.get(name) + || this.nextAvailableSAH() + || toss("No available handles to import to."); + sah.truncate(0); + let nWrote = 0, chunk, checkedHeader = false, err = false; + try{ + while( undefined !== (chunk = await callback()) ){ + if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if( 0===nWrote && chunk.byteLength>=15 ){ + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, {at: HEADER_OFFSET_DATA + nWrote}); + nWrote += chunk.byteLength; + } + if( nWrote < 512 || 0!==nWrote % 512 ){ + toss("Input size",nWrote,"is not correct for an SQLite database."); + } + if( !checkedHeader ){ + const header = new Uint8Array(20); + sah.read( header, {at: 0} ); + util.affirmDbHeader( header ); + } + sah.write(new Uint8Array(2), { + at: HEADER_OFFSET_DATA + 18 + }/*force db out of WAL mode*/); + }catch(e){ + this.setAssociatedPath(sah, '', 0); + } + this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); + return nWrote; + } + //! Documented elsewhere in this file. importDb(name, bytes){ - if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + if( bytes instanceof ArrayBuffer ) bytes = new Uint8Array(bytes); + else if( bytes instanceof Function ) return this.importDbChunked(name, bytes); + const sah = this.#mapFilenameToSAH.get(name) + || this.nextAvailableSAH() + || toss("No available handles to import to."); const n = bytes.byteLength; if(n<512 || n%512!=0){ toss("Byte array size is invalid for an SQLite db."); @@ -882,16 +922,16 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ toss("Input does not contain an SQLite database header."); } } - const sah = this.#mapFilenameToSAH.get(name) - || this.nextAvailableSAH() - || toss("No available handles to import to."); const nWrote = sah.write(bytes, {at: HEADER_OFFSET_DATA}); if(nWrote != n){ this.setAssociatedPath(sah, '', 0); toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); }else{ + sah.write(new Uint8Array([0,0]), {at: HEADER_OFFSET_DATA+18} + /* force db out of WAL mode */); this.setAssociatedPath(sah, name, capi.SQLITE_OPEN_MAIN_DB); } + return nWrote; } }/*class OpfsSAHPool*/; @@ -1098,6 +1138,19 @@ globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ automatically clean up any non-database files so importing them is pointless. + If passed a function for its second argument, its behavior + changes to asynchronous and it imports its data in chunks fed to + it by the given callback function. It calls the callback (which + may be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. The result of the resolved Promise when + called this way is the size of the resulting database. + + On succes this routine rewrites the database header bytes in the + output file (not the input array) to force disabling of WAL mode. + On a write error, the handle is removed from the pool and made available for re-use. diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index 93482505ac..5edb129eaf 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -136,6 +136,7 @@ const installOpfsVfs = function callee(options){ const error = (...args)=>logImpl(0, ...args); const toss = sqlite3.util.toss; const capi = sqlite3.capi; + const util = sqlite3.util; const wasm = sqlite3.wasm; const sqlite3_vfs = capi.sqlite3_vfs; const sqlite3_file = capi.sqlite3_file; @@ -1168,40 +1169,98 @@ const installOpfsVfs = function callee(options){ doDir(opt.directory, 0); }; + /** + impl of importDb() when it's given a function as its second + argument. + */ + const importDbChunked = async function(filename, callback){ + const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + const sah = await hFile.createSyncAccessHandle(); + sah.truncate(0); + let nWrote = 0, chunk, checkedHeader = false, err = false; + try{ + while( undefined !== (chunk = await callback()) ){ + if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); + if( 0===nWrote && chunk.byteLength>=15 ){ + util.affirmDbHeader(chunk); + checkedHeader = true; + } + sah.write(chunk, {at: nWrote}); + nWrote += chunk.byteLength; + } + if( nWrote < 512 || 0!==nWrote % 512 ){ + toss("Input size",nWrote,"is not correct for an SQLite database."); + } + if( !checkedHeader ){ + const header = new Uint8Array(20); + sah.read( header, {at: 0} ); + util.affirmDbHeader( header ); + } + sah.write(new Uint8Array(2), {at: 18}/*force db out of WAL mode*/); + return nWrote; + }catch(e){ + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally { + await sah.close(); + } + }; + /** Asynchronously imports the given bytes (a byte array or ArrayBuffer) into the given database file. + If passed a function for its second argument, its behaviour + changes to async and it imports its data in chunks fed to it by + the given callback function. It calls the callback (which may + be async) repeatedly, expecting either a Uint8Array or + ArrayBuffer (to denote new input) or undefined (to denote + EOF). For so long as the callback continues to return + non-undefined, it will append incoming data to the given + VFS-hosted database file. When called this way, the resolved + value of the returned Promise is the number of bytes written to + the target file. + It very specifically requires the input to be an SQLite3 database and throws if that's not the case. It does so in order to prevent this function from taking on a larger scope than it is specifically intended to. i.e. we do not want it to become a convenience for importing arbitrary files into OPFS. - Throws on error. Resolves to the number of bytes written. + This routine rewrites the database header bytes in the output + file (not the input array) to force disabling of WAL mode. + + On error this throws and the state of the input file is + undefined (it depends on where the exception was triggered). + + On success, resolves to the number of bytes written. */ opfsUtil.importDb = async function(filename, bytes){ + if( bytes instanceof Function ){ + return importDbChunked(filename, bytes); + } if(bytes instanceof ArrayBuffer) bytes = new Uint8Array(bytes); + util.affirmIsDb(bytes); const n = bytes.byteLength; - if(n<512 || n%512!=0){ - toss("Byte array size is invalid for an SQLite db."); - } - const header = "SQLite format 3"; - for(let i = 0; i < header.length; ++i){ - if( header.charCodeAt(i) !== bytes[i] ){ - toss("Input does not contain an SQLite database header."); - } - } const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); - const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - const sah = await hFile.createSyncAccessHandle(); - sah.truncate(0); - const nWrote = sah.write(bytes, {at: 0}); - sah.close(); - if(nWrote != n){ - toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); + let sah, err, nWrote = 0; + try { + const hFile = await hDir.getFileHandle(fnamePart, {create:true}); + sah = await hFile.createSyncAccessHandle(); + sah.truncate(0); + nWrote = sah.write(bytes, {at: 0}); + if(nWrote != n){ + toss("Expected to write "+n+" bytes but wrote "+nWrote+"."); + } + sah.write(new Uint8Array(2), {at: 18}) /* force db out of WAL mode */; + return nWrote; + }catch(e){ + await hDir.removeEntry( fnamePart ).catch(()=>{}); + throw e; + }finally{ + if( sah ) await sah.close(); } - return nWrote; }; if(sqlite3.oo1){ diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index bd945dcab7..f694598eab 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -2939,8 +2939,27 @@ globalThis.sqlite3InitModule = sqlite3InitModule; let db; try { const exp = this.opfsDbExport; + const filename = this.opfsDbFile; delete this.opfsDbExport; - this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(this.opfsDbFile, exp); + this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(filename, exp); + db = new sqlite3.oo1.OpfsDb(this.opfsDbFile); + T.assert(6 === db.selectValue('select count(*) from p')). + assert( this.opfsImportSize == exp.byteLength ); + db.close(); + this.opfsUnlink(filename); + T.assert(!(await sqlite3.opfs.entryExists(filename))); + // Try again with a function as an input source: + let cursor = 0; + const blockSize = 512, end = exp.byteLength; + const reader = async function(){ + if(cursor >= exp.byteLength){ + return undefined; + } + const rv = exp.subarray(cursor, cursor+blockSize>end ? end : cursor+blockSize); + cursor += blockSize; + return rv; + }; + this.opfsImportSize = await sqlite3.oo1.OpfsDb.importDb(filename, reader); db = new sqlite3.oo1.OpfsDb(this.opfsDbFile); T.assert(6 === db.selectValue('select count(*) from p')). assert( this.opfsImportSize == exp.byteLength ); @@ -3059,8 +3078,9 @@ globalThis.sqlite3InitModule = sqlite3InitModule; const dbytes = u1.exportFile(dbName); T.assert(dbytes.length >= 4096); const dbName2 = '/exported.db'; - u1.importDb(dbName2, dbytes); - T.assert( 2 == u1.getFileCount() ); + let nWrote = u1.importDb(dbName2, dbytes); + T.assert( 2 == u1.getFileCount() ) + .assert( dbytes.byteLength == nWrote ); let db2 = new u1.OpfsSAHPoolDb(dbName2); T.assert(db2 instanceof sqlite3.oo1.DB) .assert(3 === db2.selectValue('select count(*) from t')); @@ -3069,6 +3089,25 @@ globalThis.sqlite3InitModule = sqlite3InitModule; .assert(false === u1.unlink(dbName2)) .assert(1 === u1.getFileCount()) .assert(1 === u1.getFileNames().length); + // Try again with a function as an input source: + let cursor = 0; + const blockSize = 1024, end = dbytes.byteLength; + const reader = async function(){ + if(cursor >= dbytes.byteLength){ + return undefined; + } + const rv = dbytes.subarray(cursor, cursor+blockSize>end ? end : cursor+blockSize); + cursor += blockSize; + return rv; + }; + nWrote = await u1.importDb(dbName2, reader); + T.assert( 2 == u1.getFileCount() ); + db2 = new u1.OpfsSAHPoolDb(dbName2); + T.assert(db2 instanceof sqlite3.oo1.DB) + .assert(3 === db2.selectValue('select count(*) from t')); + db2.close(); + T.assert(true === u1.unlink(dbName2)) + .assert(dbytes.byteLength == nWrote); } T.assert(true === u1.unlink(dbName)) diff --git a/manifest b/manifest index 0da289e830..a96201b44c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\snote\sabout\sthe\scurrent\sthreading\slimitation\sto\sext/jni/README.md.\sNo\scode\schanges. -D 2023-08-15T13:01:20.690 +C Extend\sthe\simportDb()\smethod\sof\sboth\sOPFS\sVFSes\sto\s(A)\ssupport\sreading\sin\san\sasync\sstreaming\sfashion\svia\sa\scallback\sand\s(B)\sautomatically\sdisable\sWAL\smode\sin\sthe\simported\sdb. +D 2023-08-18T14:16:26.669 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -543,13 +543,13 @@ F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057af F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e F ext/wasm/api/sqlite3-api-glue.js b65e546568f1dfb35205b9792feb5146a6323d71b55cda58e2ed30def6dd52f3 F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8 -F ext/wasm/api/sqlite3-api-prologue.js 5f283b096b98bfb1ee2f2201e7ff0489dff00e29e1030c30953bdb4f5b87f4bd +F ext/wasm/api/sqlite3-api-prologue.js ef6f67c5ea718490806e5e17d2644b8b2f6e6ba5284d23dc1fbfd14d401c1ab5 F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 -F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js abb69b5e008961026bf5ff433d7116cb046359af92a5daf73208af2e7ac80ae7 -F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js e04fc2fda6a0200ef80efdbb4ddfa0254453558adb17ec3a230f93d2bf1d711c +F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 561463ac5380e4ccf1839a1922e6d7a5585660f32e3b9701a270b78cd35566cf +F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 656952a75c36d96e3248b03ae26d6a7f8d6ff31e66432c63e1c0bb021f1234ab F ext/wasm/api/sqlite3-wasm.c d4d4c2b349b43b7b861e6d2994299630fb79e07573ea6b61e28e8071b7d16b61 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75 @@ -595,7 +595,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555 F ext/wasm/test-opfs-vfs.js f09266873e1a34d9bdb6d3981ec8c9e382f31f215c9fd2f9016d2394b8ae9b7b F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2 -F ext/wasm/tester1.c-pp.js 64eb0ee6e695d5638d0f758f31a0ca2231e627ca5d768de3d8b44f9f494de8d4 +F ext/wasm/tester1.c-pp.js 9e0f4da49f02753a73a5f931bfb9b1458175518daa3fec40b5ebdc06c285539c F ext/wasm/tests/opfs/concurrency/index.html 0802373d57034d51835ff6041cda438c7a982deea6079efd98098d3e42fbcbc1 F ext/wasm/tests/opfs/concurrency/test.js a98016113eaf71e81ddbf71655aa29b0fed9a8b79a3cdd3620d1658eb1cc9a5d F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -2091,8 +2091,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 00ac653562a66aad3112ea322d08be68e05e6bf7413c814dd3f81bf850fcf43b -R 68ba6514d93093aa325f9f497a3147a9 +P 653ed92dc39185cdedfab3ea518bc7ec2d2826120e5fa4cbdee3343301396184 +R 6b40a7c1cd4b822a33b7a897821b1648 U stephan -Z 5cbd8ac0238d9d299017246e54070fc0 +Z b0064c5da7f02d0dcda913e568fa1924 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c54d1a072a..ff8af35694 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -653ed92dc39185cdedfab3ea518bc7ec2d2826120e5fa4cbdee3343301396184 \ No newline at end of file +9b1398c96a4fd0b59e65faa8d5c98de4129f0f0357732f12cb2f5c53a08acdc2 \ No newline at end of file From 656f6c0c80004aa1dc96bde9019761fb3d7cfacc Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 18 Aug 2023 14:41:21 +0000 Subject: [PATCH 019/422] Improve exception handling in OpfsDb.importDb(). FossilOrigin-Name: a4eedd63b58f5c273b671f687cb68a32259963dbb053a710770383c735fb7f26 --- ext/wasm/api/sqlite3-vfs-opfs.c-pp.js | 9 ++++++--- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js index 5edb129eaf..ca2fde985e 100644 --- a/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js +++ b/ext/wasm/api/sqlite3-vfs-opfs.c-pp.js @@ -1176,10 +1176,10 @@ const installOpfsVfs = function callee(options){ const importDbChunked = async function(filename, callback){ const [hDir, fnamePart] = await opfsUtil.getDirForFilename(filename, true); const hFile = await hDir.getFileHandle(fnamePart, {create:true}); - const sah = await hFile.createSyncAccessHandle(); - sah.truncate(0); + let sah = await hFile.createSyncAccessHandle(); let nWrote = 0, chunk, checkedHeader = false, err = false; try{ + sah.truncate(0); while( undefined !== (chunk = await callback()) ){ if(chunk instanceof ArrayBuffer) chunk = new Uint8Array(chunk); if( 0===nWrote && chunk.byteLength>=15 ){ @@ -1200,10 +1200,12 @@ const installOpfsVfs = function callee(options){ sah.write(new Uint8Array(2), {at: 18}/*force db out of WAL mode*/); return nWrote; }catch(e){ + await sah.close(); + sah = undefined; await hDir.removeEntry( fnamePart ).catch(()=>{}); throw e; }finally { - await sah.close(); + if( sah ) await sah.close(); } }; @@ -1256,6 +1258,7 @@ const installOpfsVfs = function callee(options){ sah.write(new Uint8Array(2), {at: 18}) /* force db out of WAL mode */; return nWrote; }catch(e){ + if( sah ){ await sah.close(); sah = undefined; } await hDir.removeEntry( fnamePart ).catch(()=>{}); throw e; }finally{ diff --git a/manifest b/manifest index a96201b44c..25437bb3be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Extend\sthe\simportDb()\smethod\sof\sboth\sOPFS\sVFSes\sto\s(A)\ssupport\sreading\sin\san\sasync\sstreaming\sfashion\svia\sa\scallback\sand\s(B)\sautomatically\sdisable\sWAL\smode\sin\sthe\simported\sdb. -D 2023-08-18T14:16:26.669 +C Improve\sexception\shandling\sin\sOpfsDb.importDb(). +D 2023-08-18T14:41:21.668 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -549,7 +549,7 @@ F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d299 F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 561463ac5380e4ccf1839a1922e6d7a5585660f32e3b9701a270b78cd35566cf -F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 656952a75c36d96e3248b03ae26d6a7f8d6ff31e66432c63e1c0bb021f1234ab +F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js d9e62d42b86f7bb3143eb071628b24e2ba7dcc749e41a0e9d3e2451bfea1a6b6 F ext/wasm/api/sqlite3-wasm.c d4d4c2b349b43b7b861e6d2994299630fb79e07573ea6b61e28e8071b7d16b61 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75 @@ -2091,8 +2091,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 653ed92dc39185cdedfab3ea518bc7ec2d2826120e5fa4cbdee3343301396184 -R 6b40a7c1cd4b822a33b7a897821b1648 +P 9b1398c96a4fd0b59e65faa8d5c98de4129f0f0357732f12cb2f5c53a08acdc2 +R db128ff4a61eed058302d85bb5b8cb1c U stephan -Z b0064c5da7f02d0dcda913e568fa1924 +Z 10bacbfa41c608cf282110ffda9bd8fa # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ff8af35694..c605058805 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9b1398c96a4fd0b59e65faa8d5c98de4129f0f0357732f12cb2f5c53a08acdc2 \ No newline at end of file +a4eedd63b58f5c273b671f687cb68a32259963dbb053a710770383c735fb7f26 \ No newline at end of file From 46d677e713819706b723d5319742d45598c0d957 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 19 Aug 2023 08:22:34 +0000 Subject: [PATCH 020/422] Replace JNI::NewStringUTF() for the remaining cases where output may be incompatible with MUTF-8. It is now only used when we know the output to be plain ASCII. FossilOrigin-Name: 2d955eef25ab116c487ebc34c6f2d2836d310af239ef1993f5aeee5a3f68d590 --- ext/jni/src/c/sqlite3-jni.c | 29 +++++++++---------- .../src/org/sqlite/jni/Fts5ExtensionApi.java | 2 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 19 +++--------- manifest | 16 +++++----- manifest.uuid | 2 +- 5 files changed, 27 insertions(+), 41 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d68c29e220..3595eba0aa 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2321,7 +2321,8 @@ JDECL(jobject,1commit_1hook)(JENV_CSELF,jobject jDb, jobject jHook){ JDECL(jstring,1compileoption_1get)(JENV_CSELF, jint n){ - return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ); + return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ) + /* We know these to be ASCII, so MUTF-8 is fine. */; } JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ @@ -2515,7 +2516,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - S3JniEnv * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = ps ? S3JniGlobal_env_cache(env) : 0; char *zDbName; jstring jRv = 0; int nStr = 0; @@ -3066,20 +3067,19 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, const char*z2,const char*z3){ S3JniDb * const ps = pState; JNIEnv * const env = ps->env; - jstring const s0 = z0 ? (*env)->NewStringUTF(env, z0) : 0; - jstring const s1 = z1 ? (*env)->NewStringUTF(env, z1) : 0; - jstring const s2 = z2 ? (*env)->NewStringUTF(env, z2) : 0; - jstring const s3 = z3 ? (*env)->NewStringUTF(env, z3) : 0; + S3JniEnv * const jc = S3JniGlobal_env_cache(env); S3JniHook const * const pHook = &ps->authHook; + 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; + jstring const s3 = z3 ? s3jni_utf8_to_jstring(jc, z3, -1) : 0; int rc; assert( pHook->jObj ); rc = (*env)->CallIntMethod(env, pHook->jObj, pHook->midCallback, (jint)op, s0, s1, s3, s3); IFTHREW{ - EXCEPTION_WARN_CALLBACK_THREW("sqlite3_set_authorizer() callback"); - EXCEPTION_CLEAR; - if( !rc ) rc = SQLITE_ERROR; + rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); } UNREF_L(s0); UNREF_L(s1); @@ -3296,13 +3296,11 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, const char *zTable, sqlite3_int64 nRowid){ S3JniDb * const ps = pState; JNIEnv * const env = ps->env; - /* ACHTUNG: this will break if zDb or zTable contain chars which are - different in MUTF-8 than UTF-8. That seems like a low risk, - but it's possible. */ + S3JniEnv * const jc = S3JniGlobal_env_cache(env); jstring jDbName; jstring jTable; - jDbName = (*env)->NewStringUTF(env, zDb); - jTable = jDbName ? (*env)->NewStringUTF(env, zTable) : 0; + 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{ @@ -3311,8 +3309,7 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, (jint)opId, jDbName, jTable, (jlong)nRowid); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("update hook"); - EXCEPTION_CLEAR; - s3jni_db_error(ps->pDb, SQLITE_ERROR, "update hook callback threw."); + s3jni_db_exception(env, ps, 0, "update hook callback threw"); } } UNREF_L(jDbName); diff --git a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java index ac041e3001..4937a32fa9 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java +++ b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java @@ -77,7 +77,7 @@ public final class Fts5ExtensionApi extends NativePointerHolder Date: Sat, 19 Aug 2023 10:43:05 +0000 Subject: [PATCH 021/422] Add multi-thread run mode to JNI Tester1. It works but hangs on exit sometimes for Java reasons as yet not understood. FossilOrigin-Name: bdbaf7a4534f40e550b646979e67e7b7731566bb5a2631ed376ac85a9bec40a7 --- ext/jni/GNUmakefile | 9 +- ext/jni/src/c/sqlite3-jni.c | 24 ++- ext/jni/src/org/sqlite/jni/OutputPointer.java | 3 + ext/jni/src/org/sqlite/jni/Tester1.java | 161 +++++++++++------- ext/jni/src/org/sqlite/jni/TesterFts5.java | 2 +- manifest | 20 +-- manifest.uuid | 2 +- 7 files changed, 138 insertions(+), 83 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 22301a3245..6227be435b 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -163,14 +163,11 @@ SQLITE_OPT = \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_THREADSAFE=0 \ + -DSQLITE_THREADSAFE=1 \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ -DSQLITE_DEBUG -# -DSQLITE_DEBUG is just to work around a -Wall warning -# for a var which gets set in all builds but only read -# via assert(). SQLITE_OPT += -g -DDEBUG -UNDEBUG @@ -223,7 +220,7 @@ $(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE) $(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h) $(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE) $(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \ - $(sqlite3-jni.c) -shared -o $@ + $(sqlite3-jni.c) -shared -o $@ -lpthread all: $(sqlite3-jni.dll) .PHONY: test @@ -231,7 +228,7 @@ test.flags ?= -v test: $(SQLite3Jni.class) $(sqlite3-jni.dll) $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ - org.sqlite.jni.Tester1 $(if $(test.flags),-- $(test.flags),) + org.sqlite.jni.Tester1 $(test.flags) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 3595eba0aa..ebd1e7940c 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -559,9 +559,9 @@ static struct { #define MUTEX_ASSERT_NOTLOCKER_ENV \ assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ENV_ENTER \ - /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_NOTLOCKER_ENV; \ sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ + /*MARKER(("Entered ENV mutex@%p %s.\n", env, __func__));*/ \ ++S3JniGlobal.metrics.nMutexEnv; \ S3JniGlobal.envCache.locker = env #define MUTEX_ENV_LEAVE \ @@ -637,6 +637,8 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ S3JniGlobal.envCache.aHead = row; row->env = env; + //MARKER(("Initalizing cache for JNIEnv@%p\n", env)); + /* Grab references to various global classes and objects... */ row->g.cObj = REF_G((*env)->FindClass(env,"java/lang/Object")); EXCEPTION_IS_FATAL("Error getting reference to Object class."); @@ -918,7 +920,6 @@ static void S3JniDb_set_aside(S3JniDb * const s){ if(s){ JNIEnv * const env = s->env; MUTEX_ASSERT_LOCKED_PDB; - assert(s->pDb && "Else this object is already in the free-list."); //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); assert(s->pNext != s); @@ -966,7 +967,9 @@ static void S3JniDb_free_for_env(JNIEnv *env){ for( ; ps; ps = pNext ){ pNext = ps->pNext; if(ps->env == env){ +#ifndef NDEBUG S3JniDb * const pPrev = ps->pPrev; +#endif S3JniDb_set_aside(ps); assert( pPrev ? pPrev->pNext==pNext : 1 ); assert( ps == S3JniGlobal.perDb.aFree ); @@ -996,6 +999,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ if( !row ){ return 0; } + //MARKER(("Uncaching JNIEnv@%p\n", env)); if( row->pNext ) row->pNext->pPrev = row->pPrev; if( row->pPrev ) row->pPrev->pNext = row->pNext; if( S3JniGlobal.envCache.aHead == row ){ @@ -2104,11 +2108,13 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ ps = S3JniDb_for_db(env, jDb, 0); if(ps){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); - MUTEX_PDB_ENTER; - S3JniDb_set_aside(ps) - /* MUST come after close() because of ps->trace. */; - MUTEX_PDB_LEAVE; - NativePointerHolder_set(env, jDb, 0, &S3NphRefs.sqlite3); + if( 0==rc ){ + MUTEX_PDB_ENTER; + S3JniDb_set_aside(ps) + /* MUST come after close() because of ps->trace. */; + MUTEX_PDB_LEAVE; + NativePointerHolder_set(env, jDb, 0, &S3NphRefs.sqlite3); + } } return (jint)rc; } @@ -4391,6 +4397,10 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ jfieldID fieldId; const ConfigFlagEntry * pConfFlag; + if( 0==sqlite3_threadsafe() ){ + (*env)->FatalError(env, "sqlite3 was not built with SQLITE_THREADSAFE."); + return; + } memset(&S3JniGlobal, 0, sizeof(S3JniGlobal)); if( (*env)->GetJavaVM(env, &S3JniGlobal.jvm) ){ (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible."); diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index 82a90c9185..416ad48e60 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -36,6 +36,9 @@ package org.sqlite.jni; access to the object's value via the `value` property, whereas the JNI-level opaque types do not permit client-level code to set that property. + + Warning: do not share instances of these classes across + threads. Doing so may lead to corrupting sqlite3-internal state. */ public final class OutputPointer { diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index f6ca7a8547..ba32bd15c4 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -15,43 +15,48 @@ package org.sqlite.jni; import static org.sqlite.jni.SQLite3Jni.*; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; -public class Tester1 { +public class Tester1 implements Runnable { private static final class Metrics { int dbOpen; } - private String name; + private Integer tId; - Tester1(String name){ - this.name = name; + Tester1(Integer id){ + tId = id; } static final Metrics metrics = new Metrics(); - private static final OutputPointer.sqlite3_stmt outStmt - = new OutputPointer.sqlite3_stmt(); - public static void out(Object val){ + public synchronized static void out(Object val){ System.out.print(val); } - public static void outln(Object val){ + public synchronized static void outln(Object val){ + System.out.print(Thread.currentThread().getName()+": "); System.out.println(val); } @SuppressWarnings("unchecked") - public static void out(Object... vals){ + public synchronized static void out(Object... vals){ int n = 0; + System.out.print(Thread.currentThread().getName()+": "); for(Object v : vals) out((n++>0 ? " " : "")+v); } @SuppressWarnings("unchecked") - public static void outln(Object... vals){ + public synchronized static void outln(Object... vals){ out(vals); out("\n"); } - static int affirmCount = 0; - public static void affirm(Boolean v, String comment){ + static volatile int affirmCount = 0; + public synchronized static void affirm(Boolean v, String comment){ ++affirmCount; assert( v /* prefer assert over exception if it's enabled because the JNI layer sometimes has to suppress exceptions, @@ -66,11 +71,8 @@ public class Tester1 { private static void test1(){ outln("libversion_number:", - sqlite3_libversion_number() - + "\n" - + sqlite3_libversion() - + "\n" - + SQLITE_SOURCE_ID); + sqlite3_libversion_number(),"\n", + sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); //outln("threadsafe = "+sqlite3_threadsafe()); affirm(SQLITE_MAX_LENGTH > 0); @@ -106,6 +108,7 @@ public class Tester1 { byte[] sqlChunk = sqlUtf8; int rc = 0; sqlite3_stmt stmt = null; + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); while(pos < sqlChunk.length){ if(pos > 0){ sqlChunk = Arrays.copyOfRange(sqlChunk, pos, @@ -144,7 +147,7 @@ public class Tester1 { } static sqlite3_stmt prepare(sqlite3 db, String sql){ - outStmt.clear(); + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); int rc = sqlite3_prepare(db, sql, outStmt); affirm( 0 == rc ); final sqlite3_stmt rv = outStmt.take(); @@ -202,11 +205,15 @@ public class Tester1 { private static void testPrepare123(){ sqlite3 db = createNewDb(); int rc; + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); rc = sqlite3_prepare(db, "CREATE TABLE t1(a);", outStmt); affirm(0 == rc); - sqlite3_stmt stmt = outStmt.get(); + sqlite3_stmt stmt = outStmt.take(); affirm(0 != stmt.getNativePointer()); rc = sqlite3_step(stmt); + if( SQLITE_DONE != rc ){ + outln("step failed ??? ",rc, sqlite3_errmsg(db)); + } affirm(SQLITE_DONE == rc); sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer()); @@ -811,6 +818,7 @@ public class Tester1 { private static void testBusy(){ final String dbName = "_busy-handler.db"; final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); int rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; @@ -1163,55 +1171,92 @@ public class Tester1 { testOpenDb1(); testOpenDb2(); testPrepare123(); - testBindFetchInt(); - testBindFetchInt64(); - testBindFetchDouble(); - testBindFetchText(); - testBindFetchBlob(); - testSql(); - testCollation(); - testToUtf8(); - testStatus(); - testUdf1(); - testUdfJavaObject(); - testUdfAggregate(); - testUdfWindow(); - testTrace(); - testBusy(); - testProgress(); - testCommitHook(); - testRollbackHook(); - testUpdateHook(); - testAuthorizer(); - testFts5(); - if(!fromThread){ - testAutoExtension(); + if( true ){ + testBindFetchInt(); + testBindFetchInt64(); + testBindFetchDouble(); + testBindFetchText(); + testBindFetchBlob(); + testSql(); + testCollation(); + testToUtf8(); + testStatus(); + testUdf1(); + testUdfJavaObject(); + testUdfAggregate(); + testUdfWindow(); + testTrace(); + testProgress(); + testCommitHook(); + testRollbackHook(); + testUpdateHook(); + testAuthorizer(); + if(!fromThread){ + // skip for now: messes with affirm() counts. testFts5(); + testBusy(); + testAutoExtension(); + } } } - public void run() throws Exception{ - runTests(true); + public void run(){ + try { + runTests(0!=this.tId); + }catch(Exception e){ + throw new RuntimeException(e); + }finally{ + affirm( SQLite3Jni.uncacheJniEnv() ); + affirm( !SQLite3Jni.uncacheJniEnv() ); + } } public static void main(String[] args) throws Exception { - - final long timeStart = System.nanoTime(); - new Tester1("main thread").runTests(false); - final long timeEnd = System.nanoTime(); - - final java.util.List liArgs = - java.util.Arrays.asList(args); - //testSleep(); - if(liArgs.indexOf("-v")>0){ - sqlite3_do_something_for_developer(); - //listBoundMethods(); + Integer nThread = null; + boolean doSomethingForDev = false; + Integer nRepeat = 1; + for( int i = 0; i < args.length; ){ + String arg = args[i++]; + if(arg.startsWith("-")){ + arg = arg.replaceFirst("-+",""); + if(arg.equals("v")){ + doSomethingForDev = true; + //listBoundMethods(); + }else if(arg.equals("t") || arg.equals("thread")){ + nThread = Integer.parseInt(args[i++]); + }else if(arg.equals("r") || arg.equals("runs")){ + nRepeat = Integer.parseInt(args[i++]); + }else{ + throw new IllegalArgumentException("Unhandled flag:"+arg); + } + } } - affirm( SQLite3Jni.uncacheJniEnv() ); - affirm( !SQLite3Jni.uncacheJniEnv() ); + + final long timeStart = System.currentTimeMillis(); + int nLoop = 0; + for( int n = 0; n < nRepeat; ++n ){ + if( nThread==null || nThread<=1 ){ + new Tester1(0).runTests(false); + }else{ + final ExecutorService ex = Executors.newFixedThreadPool( nThread ); + //final List> futures = new ArrayList<>(); + ++nLoop; + outln("Running loop #",nLoop," over ",nThread," threads."); + for( int i = 0; i < nThread; ++i ){ + ex.submit( new Tester1(i) ); + } + ex.shutdown(); + ex.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS); + ex.shutdownNow(); + } + } + final long timeEnd = System.currentTimeMillis(); outln("Tests done. Metrics:"); outln("\tAssertions checked: "+affirmCount); outln("\tDatabases opened: "+metrics.dbOpen); + if( doSomethingForDev ){ + sqlite3_do_something_for_developer(); + } int nMethods = 0; int nNatives = 0; final java.lang.reflect.Method[] declaredMethods = @@ -1232,6 +1277,6 @@ public class Tester1 { nNatives+" native methods and "+ (nMethods - nNatives)+" Java impls"); outln("\tTotal test time = " - +((timeEnd - timeStart)/1000000.0)+"ms"); + +(timeEnd - timeStart)+"ms"); } } diff --git a/ext/jni/src/org/sqlite/jni/TesterFts5.java b/ext/jni/src/org/sqlite/jni/TesterFts5.java index 6439768e29..3fd7c9bfe0 100644 --- a/ext/jni/src/org/sqlite/jni/TesterFts5.java +++ b/ext/jni/src/org/sqlite/jni/TesterFts5.java @@ -72,7 +72,7 @@ public class TesterFts5 { affirm( xDestroyCalled.value ); } - public TesterFts5(){ + public TesterFts5(boolean outputStats){ int oldAffirmCount = Tester1.affirmCount; Tester1.affirmCount = 0; final long timeStart = System.nanoTime(); diff --git a/manifest b/manifest index 0ca122a07a..0ffb551daf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sJNI::NewStringUTF()\sfor\sthe\sremaining\scases\swhere\soutput\smay\sbe\sincompatible\swith\sMUTF-8.\sIt\sis\snow\sonly\sused\swhen\swe\sknow\sthe\soutput\sto\sbe\splain\sASCII. -D 2023-08-19T08:22:34.056 +C Add\smulti-thread\srun\smode\sto\sJNI\sTester1.\sIt\sworks\sbut\shangs\son\sexit\ssometimes\sfor\sJava\sreasons\sas\syet\snot\sunderstood. +D 2023-08-19T10:43:05.945 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -231,10 +231,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 a9e11b92e620058558cbc1a2d49f8ec53c78d6a989b9db0b7d0b649b9f174881 +F ext/jni/GNUmakefile 28ef565d7a2df7b8db61826a4db3806e24bfc25f0bfa2f56fdd5527c93ecdb10 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c a8b51e4c63572d1655dafea38b80fd63528684264c6483b5c4d1eb9098c44712 +F ext/jni/src/c/sqlite3-jni.c a4a762bff193e52a264778f64545674d5b58dbcb45478e9186d603fae2c312cd F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -249,14 +249,14 @@ 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 9c5d901cce4f7e57c3d623f4e2476f9f79a8eed6e51b2a603f37866018e040ee -F ext/jni/src/org/sqlite/jni/OutputPointer.java d81f8bd43d2296ae373692370cfad16ddde76f5c14cd2760f7b4e1113ef56d4c +F ext/jni/src/org/sqlite/jni/OutputPointer.java 464ea85c3eba673a7b575545f69fcd8aeb398477a26d155d88cee3e2459e7802 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26b3083501a9f42e9aa49b941f6b378213cf91ae1a8f705602773ed750043a3c -F ext/jni/src/org/sqlite/jni/Tester1.java 68b88b3098ce60134f4298488f890871398a77477af0a1b21797c59c911060c1 -F ext/jni/src/org/sqlite/jni/TesterFts5.java 59e22dd24af033ea8827d36225a2f3297908fb6af8818ead8850c6c6847557b1 +F ext/jni/src/org/sqlite/jni/Tester1.java 655d7109a1079be898f2631930493bd86e0c0259582014bb7af41b87d21d9a27 +F ext/jni/src/org/sqlite/jni/TesterFts5.java 3914b0a7ab0ff752c1082b1ae0c09b32827d81962fff62bcd0e13b9ec3a6f03f F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee @@ -2091,8 +2091,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 00a2a3736a6dcde81d920815520040f3c47f965165e7128ca1f4062e6ec7c17c -R 844cfc265e8ec15b67feebeb0f56ea87 +P 2d955eef25ab116c487ebc34c6f2d2836d310af239ef1993f5aeee5a3f68d590 +R 8a1b71ae57321d848d8875e47d990d30 U stephan -Z a99a318492abbd023ca99730c8ec8838 +Z 68dec3da5d321e77fd308ae3996624ba # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6d462b62c8..f2c6616be1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2d955eef25ab116c487ebc34c6f2d2836d310af239ef1993f5aeee5a3f68d590 \ No newline at end of file +bdbaf7a4534f40e550b646979e67e7b7731566bb5a2631ed376ac85a9bec40a7 \ No newline at end of file From 8d9179bd0771f291df7b315de504734a82fc1fc1 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 19 Aug 2023 11:26:52 +0000 Subject: [PATCH 022/422] Minor JNI cleanups. FossilOrigin-Name: 1cecb9e0383aa78c491f9ba88c831a88b4b2d40ceef1b87be494b6ddc0789e41 --- ext/jni/jar-dist.make | 7 +- ext/jni/src/c/sqlite3-jni.c | 34 +++++----- ext/jni/src/org/sqlite/jni/SQLFunction.java | 14 ++-- ext/jni/src/org/sqlite/jni/Tester1.java | 68 ++++++++++--------- .../src/org/sqlite/jni/sqlite3_context.java | 3 +- .../src/org/sqlite/jni/tester/SQLTester.java | 12 ++-- manifest | 22 +++--- manifest.uuid | 2 +- 8 files changed, 82 insertions(+), 80 deletions(-) diff --git a/ext/jni/jar-dist.make b/ext/jni/jar-dist.make index 9f9d13002a..9dd9f7b296 100644 --- a/ext/jni/jar-dist.make +++ b/ext/jni/jar-dist.make @@ -6,7 +6,9 @@ # proper top-level JDK directory and, depending on the platform, add a # platform-specific -I directory. It should build as-is with any # 2020s-era version of gcc or clang. It requires JDK version 8 or -# higher. +# higher and that JAVA_HOME points to the top-most installation +# directory of that JDK. On Ubuntu-style systems the JDK is typically +# installed under /usr/lib/jvm/java-VERSION-PLATFORM. default: all @@ -36,9 +38,6 @@ SQLITE_OPT = \ -DSQLITE_USE_URI=1 \ -DSQLITE_ENABLE_FTS5 \ -DSQLITE_DEBUG -# -DSQLITE_DEBUG is just to work around a -Wall warning -# for a var which gets set in all builds but only read -# via assert(). sqlite3-jni.dll = libsqlite3-jni.so $(sqlite3-jni.dll): diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index ebd1e7940c..347070ccbe 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -511,33 +511,33 @@ static struct { S3JniEnv * aHead /* Linked list of in-use instances */; S3JniEnv * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree */; - void const * locker /* env mutex is held on this object's behalf - (used only for sanity checking). */; + void const * locker /* env mutex is held on this object's behalf. + Used only for sanity checking. */; } envCache; struct { S3JniDb * aUsed /* Linked list of in-use instances */; S3JniDb * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aUsed and aFree */; - void const * locker /* perDb mutex is held on this object's - behalf. Unlike envCache.locker, we - cannot always have this set to the - current JNIEnv object. */; + void const * locker /* perDb mutex is held on this object's + behalf. Unlike envCache.locker, we cannot + always have this set to the current JNIEnv + object. Used only for sanity checking. */; } perDb; /* Internal metrics. */ struct { - unsigned envCacheHits; - unsigned envCacheMisses; - unsigned nMutexEnv /* number of times envCache.mutex was entered */; - unsigned nMutexPerDb /* number of times perDb.mutex was entered */; - unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; - unsigned nDestroy /* xDestroy() calls across all types */; + volatile unsigned envCacheHits; + volatile unsigned envCacheMisses; + volatile unsigned nMutexEnv /* number of times envCache.mutex was entered */; + volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; + volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; + volatile unsigned nDestroy /* xDestroy() calls across all types */; struct { /* Number of calls for each type of UDF callback. */ - unsigned nFunc; - unsigned nStep; - unsigned nFinal; - unsigned nValue; - unsigned nInverse; + volatile unsigned nFunc; + volatile unsigned nStep; + volatile unsigned nFinal; + volatile unsigned nValue; + volatile unsigned nInverse; } udf; } metrics; /** diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index 21e5fe622a..28775608ad 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -21,17 +21,19 @@ package org.sqlite.jni; This class is not used by itself, but is a marker base class. The three UDF types are modelled by the inner classes Scalar, - Aggregate, and Window. Most simply, clients may create - anonymous classes from those to implement UDFs. Clients are free to - create their own classes for use with UDFs, so long as they conform - to the public interfaces defined by those three classes. The JNI - layer only actively relies on the SQLFunction base class. + Aggregate, and Window. Most simply, clients may subclass + those, or create anonymous classes from them, to implement + UDFs. Clients are free to create their own classes for use with + UDFs, so long as they conform to the public interfaces defined by + those three classes. The JNI layer only actively relies on the + SQLFunction base class and the method names and signatures used by + the UDF callback interfaces. */ public abstract class SQLFunction { /** PerContextState assists aggregate and window functions in - managinga their accumulator state across calls to the UDF's + managing their accumulator state across calls to the UDF's callbacks. If a given aggregate or window function is called multiple times diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index ba32bd15c4..74cd7d21f4 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -69,10 +69,12 @@ public class Tester1 implements Runnable { affirm(v, "Affirmation failed."); } - private static void test1(){ - outln("libversion_number:", - sqlite3_libversion_number(),"\n", - sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); + private void test1(){ + if( 0==tId ){ + outln("libversion_number:", + sqlite3_libversion_number(),"\n", + sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); + } affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); //outln("threadsafe = "+sqlite3_threadsafe()); affirm(SQLITE_MAX_LENGTH > 0); @@ -156,7 +158,7 @@ public class Tester1 implements Runnable { return rv; } - private static void testCompileOption(){ + private void testCompileOption(){ int i = 0; String optName; outln("compile options:"); @@ -167,7 +169,7 @@ public class Tester1 implements Runnable { } - private static void testOpenDb1(){ + private void testOpenDb1(){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); int rc = sqlite3_open(":memory:", out); ++metrics.dbOpen; @@ -189,7 +191,7 @@ public class Tester1 implements Runnable { affirm(0 == db.getNativePointer()); } - private static void testOpenDb2(){ + private void testOpenDb2(){ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); int rc = sqlite3_open_v2(":memory:", out, SQLITE_OPEN_READWRITE @@ -202,7 +204,7 @@ public class Tester1 implements Runnable { affirm(0 == db.getNativePointer()); } - private static void testPrepare123(){ + private void testPrepare123(){ sqlite3 db = createNewDb(); int rc; final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); @@ -265,7 +267,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testBindFetchInt(){ + private void testBindFetchInt(){ sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); @@ -312,7 +314,7 @@ public class Tester1 implements Runnable { affirm(0 == db.getNativePointer()); } - private static void testBindFetchInt64(){ + private void testBindFetchInt64(){ sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); @@ -334,7 +336,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testBindFetchDouble(){ + private void testBindFetchDouble(){ sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); @@ -359,7 +361,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testBindFetchText(){ + private void testBindFetchText(){ sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); @@ -388,7 +390,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testBindFetchBlob(){ + private void testBindFetchBlob(){ sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a)"); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); @@ -417,7 +419,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testSql(){ + private void testSql(){ sqlite3 db = createNewDb(); sqlite3_stmt stmt = prepare(db, "SELECT 1"); affirm( "SELECT 1".equals(sqlite3_sql(stmt)) ); @@ -428,7 +430,7 @@ public class Tester1 implements Runnable { sqlite3_finalize(stmt); } - private static void testCollation(){ + private void testCollation(){ final sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); final ValueHolder xDestroyCalled = new ValueHolder<>(false); @@ -501,7 +503,7 @@ public class Tester1 implements Runnable { affirm(xDestroyCalled.value); } - private static void testToUtf8(){ + private void testToUtf8(){ /** Java docs seem contradictory, claiming to use "modified UTF-8" encoding while also claiming to export using RFC 2279: @@ -515,7 +517,7 @@ public class Tester1 implements Runnable { affirm( 5 == ba.length /* as opposed to 6 in modified utf-8 */); } - private static void testStatus(){ + private void testStatus(){ final OutputPointer.Int64 cur64 = new OutputPointer.Int64(); final OutputPointer.Int64 high64 = new OutputPointer.Int64(); final OutputPointer.Int32 cur32 = new OutputPointer.Int32(); @@ -543,7 +545,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testUdf1(){ + private void testUdf1(){ final sqlite3 db = createNewDb(); // These ValueHolders are just to confirm that the func did what we want... final ValueHolder xDestroyCalled = new ValueHolder<>(false); @@ -587,7 +589,7 @@ public class Tester1 implements Runnable { affirm( xDestroyCalled.value ); } - private static void testUdfJavaObject(){ + private void testUdfJavaObject(){ final sqlite3 db = createNewDb(); final ValueHolder testResult = new ValueHolder<>(db); final SQLFunction func = new SQLFunction.Scalar(){ @@ -616,7 +618,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testUdfAggregate(){ + private void testUdfAggregate(){ final sqlite3 db = createNewDb(); final ValueHolder xFinalNull = // To confirm that xFinal() is called with no aggregate state @@ -678,7 +680,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testUdfWindow(){ + private void testUdfWindow(){ final sqlite3 db = createNewDb(); /* Example window function, table, and results taken from: https://sqlite.org/windowfunctions.html#udfwinfunc */ @@ -735,7 +737,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void listBoundMethods(){ + private void listBoundMethods(){ if(false){ final java.lang.reflect.Field[] declaredFields = SQLite3Jni.class.getDeclaredFields(); @@ -766,7 +768,7 @@ public class Tester1 implements Runnable { outln(count+" functions named sqlite3_*."); } - private static void testTrace(){ + private void testTrace(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); /* Ensure that characters outside of the UTF BMP survive the trip @@ -815,7 +817,7 @@ public class Tester1 implements Runnable { affirm( 7 == counter.value ); } - private static void testBusy(){ + private void testBusy(){ final String dbName = "_busy-handler.db"; final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); @@ -866,7 +868,7 @@ public class Tester1 implements Runnable { } } - private static void testProgress(){ + private void testProgress(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); sqlite3_progress_handler(db, 1, new ProgressHandler(){ @@ -884,7 +886,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testCommitHook(){ + private void testCommitHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder hookResult = new ValueHolder<>(0); @@ -933,7 +935,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testUpdateHook(){ + private void testUpdateHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); @@ -982,7 +984,7 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } - private static void testRollbackHook(){ + private void testRollbackHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final RollbackHook theHook = new RollbackHook(){ @@ -1020,7 +1022,7 @@ public class Tester1 implements Runnable { it throws. */ @SuppressWarnings("unchecked") - private static void testFts5() throws Exception { + private void testFts5() throws Exception { if( !SQLITE_ENABLE_FTS5 ){ outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); return; @@ -1048,7 +1050,7 @@ public class Tester1 implements Runnable { } } - private static void testAuthorizer(){ + private void testAuthorizer(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder authRc = new ValueHolder<>(0); @@ -1070,7 +1072,7 @@ public class Tester1 implements Runnable { sqlite3_close(db); } - private static void testAutoExtension(){ + private void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); final AutoExtension ax = new AutoExtension(){ @@ -1159,7 +1161,7 @@ public class Tester1 implements Runnable { affirm( 8 == val.value ); } - private static void testSleep(){ + private void testSleep(){ out("Sleeping briefly... "); sqlite3_sleep(600); outln("Woke up."); @@ -1273,7 +1275,7 @@ public class Tester1 implements Runnable { } } } - outln("\tSQLite3Jni sqlite3_*() methods: "+ + outln("\tSQLite3Jni.sqlite3_*() methods: "+ nNatives+" native methods and "+ (nMethods - nNatives)+" Java impls"); outln("\tTotal test time = " diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_context.java b/ext/jni/src/org/sqlite/jni/sqlite3_context.java index a61ff21c7e..d582df7838 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_context.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_context.java @@ -19,8 +19,7 @@ package org.sqlite.jni; */ public final class sqlite3_context extends NativePointerHolder { /** - For use only by the JNI layer. It's permitted to set this even - though it's private. + Only set by the JNI layer. */ private long aggregateContext = 0; diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index ffdb867d9b..ef3b839bc1 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -250,14 +250,14 @@ public class SQLTester { } public void runTests() throws Exception { - final long tStart = System.nanoTime(); + final long tStart = System.currentTimeMillis(); for(String f : listInFiles){ reset(); ++nTestFile; final TestScript ts = new TestScript(f); outln(nextStartEmoji(), " starting [",f,"]"); boolean threw = false; - final long timeStart = System.nanoTime(); + final long timeStart = System.currentTimeMillis(); try{ ts.run(this); }catch(SQLTesterException e){ @@ -267,14 +267,14 @@ public class SQLTester { if( keepGoing ) outln("Continuing anyway becaure of the keep-going option."); else if( e.isFatal() ) throw e; }finally{ - final long timeEnd = System.nanoTime(); + final long timeEnd = System.currentTimeMillis(); outln("🏁",(threw ? "❌" : "✅")," ",nTest," test(s) in ", - ((timeEnd-timeStart)/1000000.0),"ms."); + (timeEnd-timeStart),"ms."); //ts.getFilename()); } } - final long tEnd = System.nanoTime(); - outln("Total run-time: ",((tEnd-tStart)/1000000.0),"ms"); + final long tEnd = System.currentTimeMillis(); + outln("Total run-time: ",(tEnd-tStart),"ms"); Util.unlink(initialDbName); } diff --git a/manifest b/manifest index 0ffb551daf..ca1c0333b0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smulti-thread\srun\smode\sto\sJNI\sTester1.\sIt\sworks\sbut\shangs\son\sexit\ssometimes\sfor\sJava\sreasons\sas\syet\snot\sunderstood. -D 2023-08-19T10:43:05.945 +C Minor\sJNI\scleanups. +D 2023-08-19T11:26:52.575 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,8 +233,8 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 28ef565d7a2df7b8db61826a4db3806e24bfc25f0bfa2f56fdd5527c93ecdb10 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb -F ext/jni/jar-dist.make f90a553203a57934bf275bed86479485135a52f48ac5c1cfe6499ae07b0b35a4 -F ext/jni/src/c/sqlite3-jni.c a4a762bff193e52a264778f64545674d5b58dbcb45478e9186d603fae2c312cd +F ext/jni/jar-dist.make bb29ff5c369c95ffcd3687cacf35f7730fd33be2fe9b1ec31670fcd7d223980e +F ext/jni/src/c/sqlite3-jni.c 8608cb36223d6bc64e8ddd9296b6d63a4fc54efaf8dbc3ea7e5eca81f4801d42 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -253,9 +253,9 @@ F ext/jni/src/org/sqlite/jni/OutputPointer.java 464ea85c3eba673a7b575545f69fcd8a 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46 +F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26b3083501a9f42e9aa49b941f6b378213cf91ae1a8f705602773ed750043a3c -F ext/jni/src/org/sqlite/jni/Tester1.java 655d7109a1079be898f2631930493bd86e0c0259582014bb7af41b87d21d9a27 +F ext/jni/src/org/sqlite/jni/Tester1.java 42341a1031fe6f1433b86a55718c38bd75b96105ef38b0c9ea88003ec637968c F ext/jni/src/org/sqlite/jni/TesterFts5.java 3914b0a7ab0ff752c1082b1ae0c09b32827d81962fff62bcd0e13b9ec3a6f03f F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -264,10 +264,10 @@ F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8 F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc -F ext/jni/src/org/sqlite/jni/sqlite3_context.java d26573fc7b309228cb49786e9078597d96232257defa955a3425d10897bca810 +F ext/jni/src/org/sqlite/jni/sqlite3_context.java fe7797a696978f057528a57b7a11e7797ed41fd7afcf100c5ebb67055d9f706f F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 1f1286428fab38dfefe328e72b5735f533b19af8dd17712dd3df7e044d21c8b8 +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 2835eb3dd1e14767ca49354c224150c70300d8013d6d51dd875f7d9380faa278 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2091,8 +2091,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 2d955eef25ab116c487ebc34c6f2d2836d310af239ef1993f5aeee5a3f68d590 -R 8a1b71ae57321d848d8875e47d990d30 +P bdbaf7a4534f40e550b646979e67e7b7731566bb5a2631ed376ac85a9bec40a7 +R ff1ad0e885103241e4eaf57ec50ed798 U stephan -Z 68dec3da5d321e77fd308ae3996624ba +Z abc1dde430026808a827edd11bd0eed1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f2c6616be1..1ceb1bcd4d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bdbaf7a4534f40e550b646979e67e7b7731566bb5a2631ed376ac85a9bec40a7 \ No newline at end of file +1cecb9e0383aa78c491f9ba88c831a88b4b2d40ceef1b87be494b6ddc0789e41 \ No newline at end of file From 187da4337990932947de3592487d21c01a809e62 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 19 Aug 2023 11:52:36 +0000 Subject: [PATCH 023/422] JNI test code cleanups. FossilOrigin-Name: e202b6e69da8cced114d027cf2e91a04dfdd50b601b3274214783f7d750c558c --- ext/jni/GNUmakefile | 6 ++- ext/jni/jar-dist.make | 4 +- ext/jni/src/org/sqlite/jni/Tester1.java | 61 ++++++++++++------------- manifest | 16 +++---- manifest.uuid | 2 +- 5 files changed, 44 insertions(+), 45 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 6227be435b..a2efff13bb 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -229,6 +229,10 @@ test: $(SQLite3Jni.class) $(sqlite3-jni.dll) $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ org.sqlite.jni.Tester1 $(test.flags) +test-mt: $(SQLite3Jni.class) $(sqlite3-jni.dll) + $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ + $(java.flags) -cp $(classpath) \ + org.sqlite.jni.Tester1 -t 4 -r 5 $(test.flags) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose @@ -264,7 +268,7 @@ endif tester-ext: tester-local tester: tester-ext -tests: test tester +tests: test tester test-mt package.jar.in := $(abspath $(dir.src)/jar.in) CLEAN_FILES += $(package.jar.in) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) diff --git a/ext/jni/jar-dist.make b/ext/jni/jar-dist.make index 9dd9f7b296..ac1a768b8c 100644 --- a/ext/jni/jar-dist.make +++ b/ext/jni/jar-dist.make @@ -33,7 +33,7 @@ SQLITE_OPT = \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_THREADSAFE=0 \ + -DSQLITE_THREADSAFE=1 \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_ENABLE_FTS5 \ @@ -46,7 +46,7 @@ $(sqlite3-jni.dll): echo "*** to configure it for your system. ***"; \ echo "************************************************************************" $(CC) $(CFLAGS) $(SQLITE_OPT) \ - src/sqlite3-jni.c -shared -o $@ + src/sqlite3-jni.c -lpthread -shared -o $@ @echo "Now try running it with: make test" test: $(sqlite3-jni.dll) diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 74cd7d21f4..41cd154b61 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -70,13 +70,7 @@ public class Tester1 implements Runnable { } private void test1(){ - if( 0==tId ){ - outln("libversion_number:", - sqlite3_libversion_number(),"\n", - sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); - } affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); - //outln("threadsafe = "+sqlite3_threadsafe()); affirm(SQLITE_MAX_LENGTH > 0); affirm(SQLITE_MAX_TRIGGER_DEPTH>0); } @@ -1072,7 +1066,7 @@ public class Tester1 implements Runnable { sqlite3_close(db); } - private void testAutoExtension(){ + private synchronized void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); final AutoExtension ax = new AutoExtension(){ @@ -1173,31 +1167,29 @@ public class Tester1 implements Runnable { testOpenDb1(); testOpenDb2(); testPrepare123(); - if( true ){ - testBindFetchInt(); - testBindFetchInt64(); - testBindFetchDouble(); - testBindFetchText(); - testBindFetchBlob(); - testSql(); - testCollation(); - testToUtf8(); - testStatus(); - testUdf1(); - testUdfJavaObject(); - testUdfAggregate(); - testUdfWindow(); - testTrace(); - testProgress(); - testCommitHook(); - testRollbackHook(); - testUpdateHook(); - testAuthorizer(); - if(!fromThread){ - // skip for now: messes with affirm() counts. testFts5(); - testBusy(); - testAutoExtension(); - } + testBindFetchInt(); + testBindFetchInt64(); + testBindFetchDouble(); + testBindFetchText(); + testBindFetchBlob(); + testSql(); + testCollation(); + testToUtf8(); + testStatus(); + testUdf1(); + testUdfJavaObject(); + testUdfAggregate(); + testUdfWindow(); + testTrace(); + testProgress(); + testCommitHook(); + testRollbackHook(); + testUpdateHook(); + testAuthorizer(); + testAutoExtension(); + if(!fromThread){ + // testFts5(); // skip for now: messes with affirm() counts. + testBusy(); } } @@ -1235,6 +1227,9 @@ public class Tester1 implements Runnable { final long timeStart = System.currentTimeMillis(); int nLoop = 0; + outln("libversion_number:", + sqlite3_libversion_number(),"\n", + sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); for( int n = 0; n < nRepeat; ++n ){ if( nThread==null || nThread<=1 ){ new Tester1(0).runTests(false); @@ -1247,7 +1242,7 @@ public class Tester1 implements Runnable { ex.submit( new Tester1(i) ); } ex.shutdown(); - ex.awaitTermination(2, java.util.concurrent.TimeUnit.SECONDS); + ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS); ex.shutdownNow(); } } diff --git a/manifest b/manifest index ca1c0333b0..bc94cb5d24 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sJNI\scleanups. -D 2023-08-19T11:26:52.575 +C JNI\stest\scode\scleanups. +D 2023-08-19T11:52:36.349 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -231,9 +231,9 @@ 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 28ef565d7a2df7b8db61826a4db3806e24bfc25f0bfa2f56fdd5527c93ecdb10 +F ext/jni/GNUmakefile 4217266579b74499708d0b9fd7de598d67cc1992646518596ec83f134c3e2d85 F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb -F ext/jni/jar-dist.make bb29ff5c369c95ffcd3687cacf35f7730fd33be2fe9b1ec31670fcd7d223980e +F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 F ext/jni/src/c/sqlite3-jni.c 8608cb36223d6bc64e8ddd9296b6d63a4fc54efaf8dbc3ea7e5eca81f4801d42 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 @@ -255,7 +255,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26b3083501a9f42e9aa49b941f6b378213cf91ae1a8f705602773ed750043a3c -F ext/jni/src/org/sqlite/jni/Tester1.java 42341a1031fe6f1433b86a55718c38bd75b96105ef38b0c9ea88003ec637968c +F ext/jni/src/org/sqlite/jni/Tester1.java b0ad66a7e7b70dcff98557d3b092e1a2b195d1c7e91e02e51ea8597f842dc5a4 F ext/jni/src/org/sqlite/jni/TesterFts5.java 3914b0a7ab0ff752c1082b1ae0c09b32827d81962fff62bcd0e13b9ec3a6f03f F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2091,8 +2091,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 bdbaf7a4534f40e550b646979e67e7b7731566bb5a2631ed376ac85a9bec40a7 -R ff1ad0e885103241e4eaf57ec50ed798 +P 1cecb9e0383aa78c491f9ba88c831a88b4b2d40ceef1b87be494b6ddc0789e41 +R a49c27c6a99f3ab466acd799b27c01cf U stephan -Z abc1dde430026808a827edd11bd0eed1 +Z a181fae7a989c6647ffb5b92efaaa712 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1ceb1bcd4d..9f668d406a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1cecb9e0383aa78c491f9ba88c831a88b4b2d40ceef1b87be494b6ddc0789e41 \ No newline at end of file +e202b6e69da8cced114d027cf2e91a04dfdd50b601b3274214783f7d750c558c \ No newline at end of file From bfa486d5fce78053bdcf30921006ffd58ccaa384 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 19 Aug 2023 12:32:00 +0000 Subject: [PATCH 024/422] JNI doc additions. FossilOrigin-Name: 0c7ac34f30e1f7e35a2ac4e5e55e5f24857b24afa81a7abecba60f1c9c68b9ff --- ext/jni/GNUmakefile | 3 +- ext/jni/README.md | 103 +++++++++++++++++++++++--------------------- manifest | 14 +++--- manifest.uuid | 2 +- 4 files changed, 62 insertions(+), 60 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index a2efff13bb..699d5aaf25 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -273,8 +273,7 @@ package.jar.in := $(abspath $(dir.src)/jar.in) CLEAN_FILES += $(package.jar.in) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) cd $(dir.src); ls -1 org/sqlite/jni/*.java org/sqlite/jni/*.class > $@ - @ls -la $@ - @echo "To use this jar you will need the -Djava.library.path=DIR/WITH/libsqlite3-jni.so flag." + @echo "To use this jar you will need the -Djava.library.path=DIR/CONTAINING/libsqlite3-jni.so flag." @echo "e.g. java -jar $@ -Djava.library.path=bld" $(package.jar): $(CLASS_FILES) $(MAKEFILE) $(package.jar.in) diff --git a/ext/jni/README.md b/ext/jni/README.md index 80486d4a07..395365292f 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -15,7 +15,10 @@ Technical support is available in the forum: > **FOREWARNING:** this subproject is very much in development and subject to any number of changes. Please do not rely on any - information about its API until this disclaimer is removed. + information about its API until this disclaimer is removed. The JNI + bindgins released with version 3.43 are a "tech preview" and 3.44 + will be "final," at which point strong backward compatibility + guarantees will apply. Project goals/requirements: @@ -40,13 +43,30 @@ Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. +Hello World +----------------------------------------------------------------------- -Significant TODOs -======================================================================== +```java +import org.sqlite.jni.*; +import static org.sqlite.jni.SQLite3Jni; +... +OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); +int rc = sqlite3_open(":memory:", out); +final sqlite3 db = out.take(); +if( 0 != rc ){ + if( null != db ){ + System.out.print("Error opening db: "+sqlite3_errmsg(db)); + sqlite3_close(db); + }else{ + System.out.print("Error opening db: rc="+rc); + } + ... handle error ... +} -- Lots of APIs left to bind. Most "day-to-day" functionality is already - in place and is believed to work well. +... use db ... +sqlite3_close_v2(db); +``` Building ======================================================================== @@ -59,55 +79,33 @@ The canonical builds assumes a Linux-like environment and requires: Put simply: -``` +```console $ export JAVA_HOME=/path/to/jdk/root $ make $ make test $ make clean ``` +The jar distribution can be created with `make jar`. + One-to-One(-ish) Mapping to C ======================================================================== This JNI binding aims to provide as close to a 1-to-1 experience with -the C API as cross-language semantics allow. Exceptions are -necessarily made where cross-language semantics do not allow a 1-to-1, -and judiciously made where a 1-to-1 mapping would be unduly cumbersome -to use in Java. +the C API as cross-language semantics allow. Changes are necessarily +made where cross-language semantics do not allow a 1-to-1, and +judiciously made where a 1-to-1 mapping would be unduly cumbersome to +use in Java. -Golden Rule: _Never_ Throw from Callbacks +Golden Rule: _Never_ Throw from Callbacks (Unless...) ------------------------------------------------------------------------ -JNI bindings which accept client-defined functions _must never throw -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) ------------------------------------------------------------------------- - -SQLite internally uses UTF-8 encoding, whereas Java natively uses -UTF-16. Java JNI has routines for converting to and from UTF-8, _but_ -Java uses what its docs call "[modified UTF-8][modutf8]." Care must be -taken when converting Java strings to UTF-8 to ensure that the proper -conversion is performed. In short, -`String.getBytes(StandardCharsets.UTF_8)` performs the proper -conversion in Java, and there is no JNI C API for that conversion -(JNI's `NewStringUTF()` returns MUTF-8). - -Known consequences and limitations of this discrepancy include: - -- Names of databases, tables, and collations must not contain - characters which differ in MUTF-8 and UTF-8, or certain APIs will - mis-translate them on their way between languages. APIs which - transfer other client-side data to Java take extra care to - convert the data at the cost of performance. - -[modutf8]: https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8 +Client-defined callbacks _must never throw 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. Unwieldy Constructs are Re-mapped @@ -125,7 +123,7 @@ A prime example of where interface changes for Java are necessary for usability is [registration of a custom collation](https://sqlite.org/c3ref/create_collation.html): -``` +```c // C: int sqlite3_create_collation(sqlite3 * db, const char * name, int eTextRep, void *pUserData, @@ -144,7 +142,7 @@ passed that object as their first argument. That data is passed around bind that part as-is to Java, the result would be awkward to use (^Yes, we tried this.): -``` +```java // Java: int sqlite3_create_collation(sqlite3 db, String name, int eTextRep, Object pUserData, xCompareType xCompare); @@ -159,7 +157,7 @@ for callbacks and (B) having their internal state provided separately, which is ill-fitting in Java. For the sake of usability, C APIs which follow that pattern use a slightly different Java interface: -``` +```java int sqlite3_create_collation(sqlite3 db, String name, int eTextRep, Collation collation); ``` @@ -168,7 +166,7 @@ Where the `Collation` class has an abstract `xCompare()` method and no-op `xDestroy()` method which can be overridden if needed, leading to a much more Java-esque usage: -``` +```java int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new Collation(){ // Required comparison function: @@ -187,8 +185,8 @@ int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new Collation( Noting that: -- It is still possible to bind in call-scope-local state via closures, - if desired. +- It is possible to bind in call-scope-local state via closures, if + desired, as opposed to packing it into the Collation object. - No capabilities of the C API are lost or unduly obscured via the above API reshaping, so power users need not make any compromises. @@ -199,6 +197,7 @@ Noting that: overriding the `xDestroy()` method effectively gives it v2 semantics. + ### User-defined SQL Functions (a.k.a. UDFs) The [`sqlite3_create_function()`](https://sqlite.org/c3ref/create_function.html) @@ -206,12 +205,13 @@ family of APIs make heavy use of function pointers to provide client-defined callbacks, necessitating interface changes in the JNI binding. The Java API has only one core function-registration function: -``` +```java int sqlite3_create_function(sqlite3 db, String funcName, int nArgs, int encoding, SQLFunction func); ``` -> Design question: does the encoding argument serve any purpose in JS? +> Design question: does the encoding argument serve any purpose in + Java? That's as-yet undetermined. If not, it will be removed. `SQLFunction` is not used directly, but is instead instantiated via one of its three subclasses: @@ -229,5 +229,8 @@ Search [`Tester1.java`](/file/ext/jni/src/org/sqlite/jni/Tester1.java) for Reminder: see the disclaimer at the top of this document regarding the in-flux nature of this API. -[jsrc]: /file/ -[www]: https://sqlite.org +### And so on... + +Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and +`sqlite3_update_hook()`, use interfaces similar to those shown above. + diff --git a/manifest b/manifest index bc94cb5d24..560dbc569e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\stest\scode\scleanups. -D 2023-08-19T11:52:36.349 +C JNI\sdoc\sadditions. +D 2023-08-19T12:32:00.131 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -231,8 +231,8 @@ 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 4217266579b74499708d0b9fd7de598d67cc1992646518596ec83f134c3e2d85 -F ext/jni/README.md 7a614a2fa6c561205f7a53fd8626cf93a7b5711ff454fc1814517f796df398eb +F ext/jni/GNUmakefile ace719d6d7025a0a454b7c661de2beef9a0e819535ad0ce0742034da8ff8d27f +F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 F ext/jni/src/c/sqlite3-jni.c 8608cb36223d6bc64e8ddd9296b6d63a4fc54efaf8dbc3ea7e5eca81f4801d42 F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 @@ -2091,8 +2091,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 1cecb9e0383aa78c491f9ba88c831a88b4b2d40ceef1b87be494b6ddc0789e41 -R a49c27c6a99f3ab466acd799b27c01cf +P e202b6e69da8cced114d027cf2e91a04dfdd50b601b3274214783f7d750c558c +R 109361c70c87f2804d75b9c237ee841f U stephan -Z a181fae7a989c6647ffb5b92efaaa712 +Z 56d27110aeccda606f4b75566f7a0870 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9f668d406a..a2e0dd926c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e202b6e69da8cced114d027cf2e91a04dfdd50b601b3274214783f7d750c558c \ No newline at end of file +0c7ac34f30e1f7e35a2ac4e5e55e5f24857b24afa81a7abecba60f1c9c68b9ff \ No newline at end of file From 10892771054286b05ef843e8d726a6270f508125 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 19 Aug 2023 14:49:08 +0000 Subject: [PATCH 025/422] JNI cleanups. FossilOrigin-Name: 0a84131008a2e7886dac64a3545dea634811f6eac2b90885ec9c61ed1e6544c3 --- ext/jni/src/c/sqlite3-jni.c | 13 ++---- ext/jni/src/c/sqlite3-jni.h | 20 +++------ .../src/org/sqlite/jni/Fts5ExtensionApi.java | 42 +++++++++---------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 36 ++++++++-------- ext/jni/src/org/sqlite/jni/Tester1.java | 8 +++- ext/jni/src/org/sqlite/jni/TesterFts5.java | 10 ++--- manifest | 22 +++++----- manifest.uuid | 2 +- 8 files changed, 72 insertions(+), 81 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 347070ccbe..2216af6259 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2225,12 +2225,12 @@ JDECL(jlong,1column_1int64)(JENV_CSELF, jobject jpStmt, return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -JDECL(jbyteArray,1column_1text)(JENV_CSELF, jobject jpStmt, +JDECL(jbyteArray,1column_1text_1utf8)(JENV_CSELF, jobject jpStmt, jint ndx){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const int n = sqlite3_column_bytes(stmt, (int)ndx); const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - return s3jni_new_jbyteArray(env, p, n); + return p ? s3jni_new_jbyteArray(env, p, n) : NULL; } JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, @@ -3412,18 +3412,11 @@ JDECL(jobject,1value_1java_1object)(JENV_CSELF, jobject jpSVal){ return rv ? rv->jObj : NULL; } -JDECL(jstring,1value_1text)(JENV_CSELF, jobject jpSVal){ - sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); - int const n = sqlite3_value_bytes16(sv); - const void * const p = sqlite3_value_text16(sv); - return s3jni_text16_to_jstring(env, p, n); -} - JDECL(jbyteArray,1value_1text_1utf8)(JENV_CSELF, jobject jpSVal){ sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); int const n = sqlite3_value_bytes(sv); const unsigned char * const p = sqlite3_value_text(sv); - return s3jni_new_jbyteArray(env, p, n); + return p ? s3jni_new_jbyteArray(env, p, n) : 0; } static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 5d741859f1..e189df6947 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1013,18 +1013,18 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1table_ /* * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_column_text16 - * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)Ljava/lang/String; + * Method: sqlite3_column_text_utf8 + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)[B */ -JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text16 +JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text_1utf8 (JNIEnv *, jclass, jobject, jint); /* * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_column_text - * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)[B + * Method: sqlite3_column_text16 + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)Ljava/lang/String; */ -JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text16 (JNIEnv *, jclass, jobject, jint); /* @@ -1659,14 +1659,6 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1int64 JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1java_1object (JNIEnv *, jclass, jobject); -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_value_text - * Signature: (Lorg/sqlite/jni/sqlite3_value;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text - (JNIEnv *, jclass, jobject); - /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_value_text_utf8 diff --git a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java index 4937a32fa9..4b04ed48a6 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java +++ b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java @@ -23,7 +23,7 @@ import java.nio.charset.StandardCharsets; public final class Fts5ExtensionApi extends NativePointerHolder { //! Only called from JNI private Fts5ExtensionApi(){} - private int iVersion = 2; + private final int iVersion = 2; /* Callback type for used by xQueryPhrase(). */ public static interface xQueryPhraseCallback { @@ -33,54 +33,54 @@ public final class Fts5ExtensionApi extends NativePointerHolder> futures = new ArrayList<>(); ++nLoop; diff --git a/ext/jni/src/org/sqlite/jni/TesterFts5.java b/ext/jni/src/org/sqlite/jni/TesterFts5.java index 3fd7c9bfe0..bc66f5c9fa 100644 --- a/ext/jni/src/org/sqlite/jni/TesterFts5.java +++ b/ext/jni/src/org/sqlite/jni/TesterFts5.java @@ -72,16 +72,14 @@ public class TesterFts5 { affirm( xDestroyCalled.value ); } - public TesterFts5(boolean outputStats){ + public TesterFts5(){ int oldAffirmCount = Tester1.affirmCount; - Tester1.affirmCount = 0; - final long timeStart = System.nanoTime(); + final long timeStart = System.currentTimeMillis(); test1(); - final long timeEnd = System.nanoTime(); + final long timeEnd = System.currentTimeMillis(); outln("FTS5 Tests done. Metrics:"); outln("\tAssertions checked: "+Tester1.affirmCount); outln("\tTotal time = " - +((timeEnd - timeStart)/1000000.0)+"ms"); - Tester1.affirmCount = oldAffirmCount; + +(timeEnd - timeStart)+"ms"); } } diff --git a/manifest b/manifest index 7a9e3a9c76..24c718d165 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\sinto\sjni-threading\sbranch. -D 2023-08-19T12:34:23.970 +C JNI\scleanups. +D 2023-08-19T14:49:08.030 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,8 +235,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 4849b0ac41c3a92777aebf0ec3d51c2be7c78d3ea9b91ece03ade6f9fa13d99a F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 8608cb36223d6bc64e8ddd9296b6d63a4fc54efaf8dbc3ea7e5eca81f4801d42 -F ext/jni/src/c/sqlite3-jni.h f10d2f38720687c70ecdd5e44f6e8db98efee2caa05fc86b2d9e0c76e6cc0a18 +F ext/jni/src/c/sqlite3-jni.c fc9ab59b3d966219f20fdc93fae7bc5ce19900f7c979b84489be4384c8532120 +F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -245,7 +245,7 @@ F ext/jni/src/org/sqlite/jni/CollationNeeded.java ad67843b6dd1c06b6b0a1dc72887b7 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 93c0643e77a0226dad31610489490c6348ed9842c91e98f3ab0c440a173e75e7 +F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 7ecfce8075381999fcdfb94467535be9c63df0332e63bf57cbcb072036e1d113 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 @@ -255,9 +255,9 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26b3083501a9f42e9aa49b941f6b378213cf91ae1a8f705602773ed750043a3c -F ext/jni/src/org/sqlite/jni/Tester1.java b0ad66a7e7b70dcff98557d3b092e1a2b195d1c7e91e02e51ea8597f842dc5a4 -F ext/jni/src/org/sqlite/jni/TesterFts5.java 3914b0a7ab0ff752c1082b1ae0c09b32827d81962fff62bcd0e13b9ec3a6f03f +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5c469585946b63592cafe134b01af0b9144a12131f22ea352e12f4c3ec70efb2 +F ext/jni/src/org/sqlite/jni/Tester1.java d1e59c7601100e60f5467e52a04f032881344246436601912eafc5f61aeea134 +F ext/jni/src/org/sqlite/jni/TesterFts5.java bd4b6316ef83e2c85b5f1f9729383c736c1771652339d1135493b5217c9d1bb3 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee @@ -2092,8 +2092,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 0c7ac34f30e1f7e35a2ac4e5e55e5f24857b24afa81a7abecba60f1c9c68b9ff c319033276c3565d0f1f2cae1c91791940d322fe79696bc26d74fddeb2664373 -R 35896553571ede397fc34e5af8e0e6bc +P 8254479c6ff1ea3cc9e56de1698db8405c03da90b9bf4c401182e47e0842baf8 +R 846f6f71bef64f8d4f48e59243174d3b U stephan -Z 1f979ceda0d0b798d0ca7e29bc8a0301 +Z 63d30481f30daf5779abda92ef1829f2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index aaad1b3cae..7a96dc38dd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8254479c6ff1ea3cc9e56de1698db8405c03da90b9bf4c401182e47e0842baf8 \ No newline at end of file +0a84131008a2e7886dac64a3545dea634811f6eac2b90885ec9c61ed1e6544c3 \ No newline at end of file From 484f9bed4e96e67386304d6c8dd5b8263ec85c49 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 21 Aug 2023 23:45:19 +0000 Subject: [PATCH 026/422] Minor JNI cleanups. FossilOrigin-Name: b88910aaaaaaa0936974379bb3eb8a5a3a634395b14e67cc9030f8a520f471f1 --- ext/jni/src/c/sqlite3-jni.c | 17 +++++++++------- ext/jni/src/org/sqlite/jni/Fts5.java | 2 +- .../src/org/sqlite/jni/Fts5ExtensionApi.java | 2 +- ext/jni/src/org/sqlite/jni/Tester1.java | 13 ++++++++---- ext/jni/src/org/sqlite/jni/TesterFts5.java | 5 +++-- manifest | 20 +++++++++---------- manifest.uuid | 2 +- 7 files changed, 35 insertions(+), 26 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 2216af6259..25f2420996 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -3735,6 +3735,7 @@ JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, int rc; char const * zName; Fts5JniAux * pAux; + assert(pApi); zName = JSTR_TOC(jName); if(!zName) return SQLITE_NOMEM; @@ -3756,15 +3757,15 @@ JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, } -typedef struct s3jni_fts5AuxData s3jni_fts5AuxData; -struct s3jni_fts5AuxData { +typedef struct S3JniFts5AuxData S3JniFts5AuxData; +struct S3JniFts5AuxData { JNIEnv *env; jobject jObj; }; -static void s3jni_fts5AuxData_xDestroy(void *x){ +static void S3JniFts5AuxData_xDestroy(void *x){ if(x){ - s3jni_fts5AuxData * const p = x; + S3JniFts5AuxData * const p = x; if(p->jObj){ JNIEnv *env = p->env; s3jni_call_xDestroy(env, p->jObj, 0); @@ -3777,7 +3778,7 @@ static void s3jni_fts5AuxData_xDestroy(void *x){ JDECLFtsXA(jobject,xGetAuxdata)(JENV_OSELF,jobject jCtx, jboolean bClear){ Fts5ExtDecl; jobject rv = 0; - s3jni_fts5AuxData * const pAux = fext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear); + S3JniFts5AuxData * const pAux = fext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear); if(pAux){ if(bClear){ if( pAux->jObj ){ @@ -3966,6 +3967,7 @@ JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, S3JniEnv * const jc = S3JniGlobal_env_cache(env); struct s3jni_xQueryPhraseState s; jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; + if( !klazz ) return SQLITE_MISUSE; s.env = env; s.jc = jc; @@ -3997,7 +3999,7 @@ JDECLFtsXA(jlong,xRowid)(JENV_OSELF,jobject jCtx){ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; - s3jni_fts5AuxData * pAux; + S3JniFts5AuxData * pAux; pAux = sqlite3_malloc(sizeof(*pAux)); if(!pAux){ if(jAux){ @@ -4010,7 +4012,7 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ pAux->env = env; pAux->jObj = REF_G(jAux); rc = fext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux, - s3jni_fts5AuxData_xDestroy); + S3JniFts5AuxData_xDestroy); return rc; } @@ -4055,6 +4057,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, jbyte * const pText = jCallback ? JBA_TOC(jbaText) : 0; jsize nText = pText ? (*env)->GetArrayLength(env, jbaText) : 0; jclass const klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; + if( !klazz ) return SQLITE_MISUSE; memset(&s, 0, sizeof(s)); s.env = env; diff --git a/ext/jni/src/org/sqlite/jni/Fts5.java b/ext/jni/src/org/sqlite/jni/Fts5.java index 102cf575a8..443a69a409 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5.java +++ b/ext/jni/src/org/sqlite/jni/Fts5.java @@ -27,7 +27,7 @@ public final class Fts5 { //! Callback type for use with xTokenize() variants public static interface xTokenizeCallback { - int xToken(int tFlags, byte txt[], int iStart, int iEnd); + int xToken(int tFlags, byte[] txt, int iStart, int iEnd); } public static final int FTS5_TOKENIZE_QUERY = 0x0001; diff --git a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java index 4b04ed48a6..205f110f41 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java +++ b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java @@ -82,5 +82,5 @@ public final class Fts5ExtensionApi extends NativePointerHolder Date: Tue, 22 Aug 2023 11:34:34 +0000 Subject: [PATCH 027/422] Move most of the per-JNIEnv global Java class refs into the global state, saving a bit of per-thread overhead. FossilOrigin-Name: 7342bf578790e1a87c128a7c1c7745fe2e7c442890370feb160d406597d4d8ec --- ext/jni/src/c/sqlite3-jni.c | 506 ++++++++++++++++++------------------ manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 254 insertions(+), 266 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 25f2420996..d3fd0db82a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -386,15 +386,16 @@ typedef struct S3JniDb S3JniDb; struct S3JniDb { JNIEnv *env /* The associated JNIEnv handle */; sqlite3 *pDb /* The associated db handle */; - jobject jDb /* A global ref of the object which was passed to - sqlite3_open(_v2)(). We need this in order to have - an object to pass to sqlite3_collation_needed()'s - callback, or else we have to dynamically create one - for that purpose, which would be fine except that - it would be a different instance (and maybe even a - different class) than the one the user may expect - to receive. */; - char * zMainDbName /* Holds any string allocated on behave of + jobject jDb /* A global ref of the output object which gets + returned from sqlite3_open(_v2)(). We need this in + order to have an object to pass to routines like + sqlite3_collation_needed()'s callback, or else we + have to dynamically create one for that purpose, + which would be fine except that it would be a + different instance (and maybe even a different + class) than the one the user may expect to + receive. */; + char * zMainDbName /* Holds the string allocated on behalf of SQLITE_DBCONFIG_MAINDBNAME. */; S3JniHook busyHandler; S3JniHook collation; @@ -413,77 +414,48 @@ struct S3JniDb { }; /* -** Cache for per-JNIEnv data. -** -** Potential TODO: move the jclass entries to global space because, -** per https://developer.android.com/training/articles/perf-jni: -** -** > once you have a valid jclass global reference you can use it from -** any attached thread. -** -** Whereas we cache new refs for each thread. +** Cache for per-JNIEnv (i.e. per-thread) data. */ typedef struct S3JniEnv S3JniEnv; struct S3JniEnv { JNIEnv *env /* env in which this cache entry was created */; - //! The various refs to global classes might be cacheable a single - // time globally. Information online seems inconsistent on that - // point. - struct { - jclass cObj /* global ref to java.lang.Object */; - jclass cLong /* global ref to java.lang.Long */; - jclass cString /* global ref to java.lang.String */; - jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; - jmethodID ctorLong1 /* the Long(long) constructor */; - jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; - jmethodID stringGetBytes /* the String.getBytes(Charset) method */; - } g /* refs to global Java state */; /* - ** pdbOpening is used to coordinate the Java/DB connection of a - ** being-open()'d db in the face of auto-extensions. "The problem" - ** is that auto-extensions run before we can bind the C db to its - ** Java representation, but auto-extensions require that binding. We - ** handle this as follows: - ** - ** - In the JNI side of sqlite3_open(), allocate the Java side of - ** that connection and set pdbOpening to point to that - ** object. Note that it's per-thread, and we remain in that - ** thread until after the auto-extensions are run. - ** - ** - Call sqlite3_open(), which triggers the auto-extension - ** handler. That handler uses pdbOpening to connect the native - ** db handle which it receives with pdbOpening. - ** - ** - When sqlite3_open() returns, check whether pdbOpening->pDb is - ** NULL. If it isn't, auto-extension handling set it up. If it - ** is, complete the Java/C binding unless sqlite3_open() returns - ** a NULL db, in which case free pdbOpening. + ** pdbOpening is used to coordinate the Java/DB connection of a + ** being-open()'d db in the face of auto-extensions. "The problem" + ** is that auto-extensions run before we can bind the C db to its + ** Java representation, but auto-extensions require that binding. We + ** handle this as follows: + ** + ** - In the JNI side of sqlite3_open(), allocate the Java side of + ** that connection and set pdbOpening to point to that + ** object. Note that it's per-thread, and we remain in that + ** thread until after the auto-extensions are run, so we don't + ** need to mutex-lock this. + ** + ** - Call sqlite3_open(), which triggers the auto-extension + ** handler. That handler uses pdbOpening to connect the native + ** db handle which it receives with pdbOpening. + ** + ** - When sqlite3_open() returns, check whether pdbOpening->pDb is + ** NULL. If it isn't, auto-extension handling set it up. If it + ** is, complete the Java/C binding unless sqlite3_open() returns + ** a NULL db, in which case free pdbOpening. */ S3JniDb * pdbOpening; -#ifdef SQLITE_ENABLE_FTS5 - jobject jFtsExt /* Global ref to Java singleton for the - Fts5ExtensionApi instance. */; - struct { - jclass klazz /* Global ref to the Fts5Phrase iter class */; - jfieldID fidA /* Fts5Phrase::a member */; - jfieldID fidB /* Fts5Phrase::b member */; - } jPhraseIter; -#endif S3JniEnv * pPrev /* Previous entry in the linked list */; S3JniEnv * pNext /* Next entry in the linked list */; /* - ** Cache of Java refs/IDs for NativePointerHolder subclasses. + ** Cache of Java refs/IDs for NativePointerHolder subclasses. We + ** "could" move this into S3JniGLobal but doing so would require + ** adding a mutex for this state in several places. It might be a + ** win, though, as it would eliminate many locks of of the + ** S3JniGlobal.envCache.mutex. */ S3JniNphClass nph[NphCache_SIZE]; }; /* -** Whether auto extensions are feasible here is currently unknown due -** to... -** -** 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? +** State for proxying sqlite3_auto_extension() in Java. */ typedef struct S3JniAutoExtension S3JniAutoExtension; struct S3JniAutoExtension { @@ -494,7 +466,8 @@ struct S3JniAutoExtension { /* ** Global state, e.g. caches and metrics. */ -static struct { +typedef struct S3JniGlobalType S3JniGlobalType; +struct S3JniGlobalType { /* ** According to: https://developer.ibm.com/articles/j-jni/ ** @@ -523,6 +496,30 @@ static struct { always have this set to the current JNIEnv object. Used only for sanity checking. */; } perDb; + /* + ** Refs to global classes and methods. Obtained during static init + ** and never released. + */ + struct { + jclass cObj /* global ref to java.lang.Object */; + jclass cLong /* global ref to java.lang.Long */; + jclass cString /* global ref to java.lang.String */; + jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; + jmethodID ctorLong1 /* the Long(long) constructor */; + jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; + jmethodID stringGetBytes /* the String.getBytes(Charset) method */; + } g /* refs to global Java state */; +#ifdef SQLITE_ENABLE_FTS5 + struct { + volatile jobject jFtsExt /* Global ref to Java singleton for the + Fts5ExtensionApi instance. */; + struct { + volatile jclass klazz /* Global ref to the Fts5Phrase iter class */; + jfieldID fidA /* Fts5Phrase::a member */; + jfieldID fidB /* Fts5Phrase::b member */; + } jPhraseIter; + } fts5; +#endif /* Internal metrics. */ struct { volatile unsigned envCacheHits; @@ -552,41 +549,43 @@ static struct { int nExt /* number of active entries in pExt. */; sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; } autoExt; -} S3JniGlobal; +}; +static S3JniGlobalType S3JniGlobal = {}; +#define SJG S3JniGlobal #define MUTEX_ASSERT_LOCKER_ENV \ - assert( (env) == S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) + assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ASSERT_NOTLOCKER_ENV \ - assert( (env) != S3JniGlobal.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) + assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ENV_ENTER \ MUTEX_ASSERT_NOTLOCKER_ENV; \ - sqlite3_mutex_enter( S3JniGlobal.envCache.mutex ); \ - /*MARKER(("Entered ENV mutex@%p %s.\n", env, __func__));*/ \ - ++S3JniGlobal.metrics.nMutexEnv; \ - S3JniGlobal.envCache.locker = env + /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ + sqlite3_mutex_enter( SJG.envCache.mutex ); \ + ++SJG.metrics.nMutexEnv; \ + SJG.envCache.locker = env #define MUTEX_ENV_LEAVE \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ MUTEX_ASSERT_LOCKER_ENV; \ - S3JniGlobal.envCache.locker = 0; \ - sqlite3_mutex_leave( S3JniGlobal.envCache.mutex ) + SJG.envCache.locker = 0; \ + sqlite3_mutex_leave( SJG.envCache.mutex ) #define MUTEX_ASSERT_LOCKED_PDB \ - assert( 0 != S3JniGlobal.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) + assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define MUTEX_PDB_ENTER \ /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( S3JniGlobal.perDb.mutex ); \ - ++S3JniGlobal.metrics.nMutexPerDb; \ - S3JniGlobal.perDb.locker = env; + sqlite3_mutex_enter( SJG.perDb.mutex ); \ + ++SJG.metrics.nMutexPerDb; \ + SJG.perDb.locker = env; #define MUTEX_PDB_LEAVE \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - S3JniGlobal.perDb.locker = 0; \ - sqlite3_mutex_leave( S3JniGlobal.perDb.mutex ) + SJG.perDb.locker = 0; \ + sqlite3_mutex_leave( SJG.perDb.mutex ) #define MUTEX_EXT_ENTER \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( S3JniGlobal.autoExt.mutex ); \ - ++S3JniGlobal.metrics.nMutexAutoExt + sqlite3_mutex_enter( SJG.autoExt.mutex ); \ + ++SJG.metrics.nMutexAutoExt #define MUTEX_EXT_LEAVE \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_leave( S3JniGlobal.autoExt.mutex ) + sqlite3_mutex_leave( SJG.autoExt.mutex ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) static void s3jni_oom(JNIEnv * const env){ @@ -614,64 +613,29 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ struct S3JniEnv * row; MUTEX_ENV_ENTER; - row = S3JniGlobal.envCache.aHead; + row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ - ++S3JniGlobal.metrics.envCacheHits; + ++SJG.metrics.envCacheHits; MUTEX_ENV_LEAVE; return row; } } - ++S3JniGlobal.metrics.envCacheMisses; - row = S3JniGlobal.envCache.aFree; + ++SJG.metrics.envCacheMisses; + row = SJG.envCache.aFree; if( row ){ assert(!row->pPrev); - S3JniGlobal.envCache.aFree = row->pNext; + SJG.envCache.aFree = row->pNext; if( row->pNext ) row->pNext->pPrev = 0; }else{ row = s3jni_malloc(env, sizeof(S3JniEnv)); } memset(row, 0, sizeof(*row)); - row->pNext = S3JniGlobal.envCache.aHead; + row->pNext = SJG.envCache.aHead; if(row->pNext) row->pNext->pPrev = row; - S3JniGlobal.envCache.aHead = row; + SJG.envCache.aHead = row; row->env = env; - //MARKER(("Initalizing cache for JNIEnv@%p\n", env)); - - /* Grab references to various global classes and objects... */ - row->g.cObj = REF_G((*env)->FindClass(env,"java/lang/Object")); - EXCEPTION_IS_FATAL("Error getting reference to Object class."); - - row->g.cLong = REF_G((*env)->FindClass(env,"java/lang/Long")); - EXCEPTION_IS_FATAL("Error getting reference to Long class."); - row->g.ctorLong1 = (*env)->GetMethodID(env, row->g.cLong, - "", "(J)V"); - EXCEPTION_IS_FATAL("Error getting reference to Long constructor."); - - row->g.cString = REF_G((*env)->FindClass(env,"java/lang/String")); - EXCEPTION_IS_FATAL("Error getting reference to String class."); - row->g.ctorStringBA = - (*env)->GetMethodID(env, row->g.cString, - "", "([BLjava/nio/charset/Charset;)V"); - EXCEPTION_IS_FATAL("Error getting reference to String(byte[],Charset) ctor."); - row->g.stringGetBytes = - (*env)->GetMethodID(env, row->g.cString, - "getBytes", "(Ljava/nio/charset/Charset;)[B"); - EXCEPTION_IS_FATAL("Error getting reference to String.getBytes(Charset)."); - - { /* StandardCharsets.UTF_8 */ - jfieldID fUtf8; - jclass const klazzSC = - (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); - EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class."); - fUtf8 = (*env)->GetStaticFieldID(env, klazzSC, "UTF_8", - "Ljava/nio/charset/Charset;"); - EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); - row->g.oCharsetUtf8 = - REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8)); - EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); - } MUTEX_ENV_LEAVE; return row; } @@ -740,8 +704,8 @@ static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, if( n<0 ) n = sqlite3Strlen30(z); jba = s3jni_new_jbyteArray(env, (unsigned const char *)z, (jsize)n); if( jba ){ - rv = (*env)->NewObject(env, jc->g.cString, jc->g.ctorStringBA, - jba, jc->g.oCharsetUtf8); + rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA, + jba, SJG.g.oCharsetUtf8); UNREF_L(jba); } } @@ -771,8 +735,8 @@ static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, char *rv; if(!jstr) return 0; - jba = (*env)->CallObjectMethod(env, jstr, jc->g.stringGetBytes, - jc->g.oCharsetUtf8); + jba = (*env)->CallObjectMethod(env, jstr, SJG.g.stringGetBytes, + SJG.g.oCharsetUtf8); if( (*env)->ExceptionCheck(env) || !jba /* order of these checks is significant for -Xlint:jni */ ) { EXCEPTION_REPORT; @@ -885,7 +849,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); //MARKER(("jObj=%p, klazz=%p, method=%p\n", jObj, klazz, method)); if(method){ - ++S3JniGlobal.metrics.nDestroy; + ++SJG.metrics.nDestroy; (*env)->CallVoidMethod(env, jObj, method); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("xDestroy() callback"); @@ -926,9 +890,9 @@ static void S3JniDb_set_aside(S3JniDb * const s){ assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); if(s->pNext) s->pNext->pPrev = s->pPrev; if(s->pPrev) s->pPrev->pNext = s->pNext; - else if(S3JniGlobal.perDb.aUsed == s){ + else if(SJG.perDb.aUsed == s){ assert(!s->pPrev); - S3JniGlobal.perDb.aUsed = s->pNext; + SJG.perDb.aUsed = s->pNext; } sqlite3_free( s->zMainDbName ); #define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->MEMBER, XDESTROY) @@ -943,13 +907,10 @@ static void S3JniDb_set_aside(S3JniDb * const s){ UNHOOK(busyHandler, 1); #undef UNHOOK UNREF_G(s->jDb); -#ifdef SQLITE_ENABLE_FTS5 - UNREF_G(s->jFtsApi); -#endif memset(s, 0, sizeof(S3JniDb)); - s->pNext = S3JniGlobal.perDb.aFree; + s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; - S3JniGlobal.perDb.aFree = s; + SJG.perDb.aFree = s; //MARKER(("%p->pPrev@%p, pNext@%p\n", s, s->pPrev, s->pNext)); //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev)); } @@ -963,7 +924,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ S3JniDb * ps; S3JniDb * pNext = 0; MUTEX_PDB_ENTER; - ps = S3JniGlobal.perDb.aUsed; + ps = SJG.perDb.aUsed; for( ; ps; ps = pNext ){ pNext = ps->pNext; if(ps->env == env){ @@ -972,7 +933,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ #endif S3JniDb_set_aside(ps); assert( pPrev ? pPrev->pNext==pNext : 1 ); - assert( ps == S3JniGlobal.perDb.aFree ); + assert( ps == SJG.perDb.aFree ); } } MUTEX_PDB_LEAVE; @@ -990,7 +951,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; int i; MUTEX_ASSERT_LOCKER_ENV; - row = S3JniGlobal.envCache.aHead; + row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ break; @@ -1002,26 +963,18 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ //MARKER(("Uncaching JNIEnv@%p\n", env)); if( row->pNext ) row->pNext->pPrev = row->pPrev; if( row->pPrev ) row->pPrev->pNext = row->pNext; - if( S3JniGlobal.envCache.aHead == row ){ + if( SJG.envCache.aHead == row ){ assert( !row->pPrev ); - S3JniGlobal.envCache.aHead = row->pNext; + SJG.envCache.aHead = row->pNext; } S3JniDb_free_for_env(env); - UNREF_G(row->g.cObj); - UNREF_G(row->g.cLong); - UNREF_G(row->g.cString); - UNREF_G(row->g.oCharsetUtf8); -#ifdef SQLITE_ENABLE_FTS5 - UNREF_G(row->jFtsExt); - UNREF_G(row->jPhraseIter.klazz); -#endif for( i = 0; i < NphCache_SIZE; ++i ){ S3JniNphClass_clear(env, &row->nph[i]); } memset(row, 0, sizeof(S3JniEnv)); - row->pNext = S3JniGlobal.envCache.aFree; + row->pNext = SJG.envCache.aFree; if( row->pNext ) row->pNext->pPrev = row; - S3JniGlobal.envCache.aFree = row; + SJG.envCache.aFree = row; return 1; } @@ -1136,11 +1089,11 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; MUTEX_PDB_ENTER; - if(S3JniGlobal.perDb.aFree){ - rv = S3JniGlobal.perDb.aFree; + if(SJG.perDb.aFree){ + rv = SJG.perDb.aFree; //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb)); //MARKER(("%p->pPrev@%p, pNext@%p\n", rv, rv->pPrev, rv->pNext)); - S3JniGlobal.perDb.aFree = rv->pNext; + SJG.perDb.aFree = rv->pNext; assert(rv->pNext != rv); assert(rv->pPrev != rv); assert(rv->pPrev ? (rv->pPrev!=rv->pNext) : 1); @@ -1158,8 +1111,8 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, } } if(rv){ - rv->pNext = S3JniGlobal.perDb.aUsed; - S3JniGlobal.perDb.aUsed = rv; + rv->pNext = SJG.perDb.aUsed; + SJG.perDb.aUsed = rv; if(rv->pNext){ assert(!rv->pNext->pPrev); rv->pNext->pPrev = rv; @@ -1200,7 +1153,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if(jDb || pDb){ MUTEX_PDB_ENTER; - s = S3JniGlobal.perDb.aUsed; + s = SJG.perDb.aUsed; if(!pDb){ assert( jDb ); pDb = PtrGet_sqlite3(jDb); @@ -1556,6 +1509,7 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const assert(ctor); rv = (*env)->NewObject(env, klazz, ctor); EXCEPTION_IS_FATAL("No-arg constructor threw."); + OOM_CHECK(rv); if(rv) NativePointerHolder_set(env, rv, pNative, pRef); return rv; } @@ -1680,7 +1634,7 @@ static int udf_args(JNIEnv *env, *jArgv = 0; if(!jcx) goto error_oom; ja = (*env)->NewObjectArray(env, argc, - S3JniGlobal_env_cache(env)->g.cObj, + SJG.g.cObj, NULL); if(!ja) goto error_oom; for(i = 0; i < argc; ++i){ @@ -1779,29 +1733,29 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, static void udf_xFunc(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++S3JniGlobal.metrics.udf.nFunc; + ++SJG.metrics.udf.nFunc; udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc"); } static void udf_xStep(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++S3JniGlobal.metrics.udf.nStep; + ++SJG.metrics.udf.nStep; udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep"); } static void udf_xFinal(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++S3JniGlobal.metrics.udf.nFinal; + ++SJG.metrics.udf.nFinal; udf_xFV(cx, s, s->jmidxFinal, "xFinal"); } static void udf_xValue(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++S3JniGlobal.metrics.udf.nValue; + ++SJG.metrics.udf.nValue; udf_xFV(cx, s, s->jmidxValue, "xValue"); } static void udf_xInverse(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++S3JniGlobal.metrics.udf.nInverse; + ++SJG.metrics.udf.nInverse; udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse"); } @@ -1845,7 +1799,7 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type) static JNIEnv * s3jni_get_env(void){ JNIEnv * env = 0; - if( (*S3JniGlobal.jvm)->GetEnv(S3JniGlobal.jvm, (void **)&env, + if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, JNI_VERSION_1_8) ){ fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); abort(); @@ -1861,7 +1815,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, JNIEnv * env = 0; S3JniDb * ps; S3JniEnv * jc; - if( 0==S3JniGlobal.autoExt.nExt ) return 0; + if( 0==SJG.autoExt.nExt ) return 0; env = s3jni_get_env(); jc = S3JniGlobal_env_cache(env); ps = jc->pdbOpening; @@ -1875,11 +1829,11 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, for( i = 0; go && 0==rc; ++i ){ S3JniAutoExtension const * ax; MUTEX_EXT_ENTER; - if( i >= S3JniGlobal.autoExt.nExt ){ + if( i >= SJG.autoExt.nExt ){ ax = 0; go = 0; }else{ - ax = &S3JniGlobal.autoExt.pExt[i]; + ax = &SJG.autoExt.pExt[i]; } MUTEX_EXT_LEAVE; if( ax && ax->jObj ){ @@ -1907,30 +1861,30 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ if( !jAutoExt ) return SQLITE_MISUSE; MUTEX_EXT_ENTER; - for( i = 0; i < S3JniGlobal.autoExt.nExt; ++i ){ + for( i = 0; i < SJG.autoExt.nExt; ++i ){ /* Look for match or first empty slot. */ - ax = &S3JniGlobal.autoExt.pExt[i]; + ax = &SJG.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ MUTEX_EXT_LEAVE; return 0 /* this as a no-op. */; } } - if(i == S3JniGlobal.autoExt.nExt ){ - assert( S3JniGlobal.autoExt.nExt <= S3JniGlobal.autoExt.nAlloc ); - if( S3JniGlobal.autoExt.nExt == S3JniGlobal.autoExt.nAlloc ){ - unsigned n = 1 + S3JniGlobal.autoExt.nAlloc; + if(i == SJG.autoExt.nExt ){ + assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc ); + if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ + unsigned n = 1 + SJG.autoExt.nAlloc; S3JniAutoExtension * const aNew = - sqlite3_realloc( S3JniGlobal.autoExt.pExt, + sqlite3_realloc( SJG.autoExt.pExt, n * sizeof(S3JniAutoExtension) ); if( !aNew ){ rc = SQLITE_NOMEM; }else{ - S3JniGlobal.autoExt.pExt = aNew; - ++S3JniGlobal.autoExt.nAlloc; + SJG.autoExt.pExt = aNew; + ++SJG.autoExt.nAlloc; } } if( 0==rc ){ - ax = &S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt]; + ax = &SJG.autoExt.pExt[SJG.autoExt.nExt]; rc = S3JniAutoExtension_init(env, ax, jAutoExt); assert( rc ? 0==ax->jObj : 0!=ax->jObj ); } @@ -1944,7 +1898,7 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ } } if( 0==rc ){ - ++S3JniGlobal.autoExt.nExt; + ++SJG.autoExt.nExt; } } MUTEX_EXT_LEAVE; @@ -2079,16 +2033,16 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ int i; MUTEX_EXT_ENTER; /* This algo mirrors the one in the core. */ - for( i = S3JniGlobal.autoExt.nExt-1; i >= 0; --i ){ - ax = &S3JniGlobal.autoExt.pExt[i]; + for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ + ax = &SJG.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniAutoExtension_clear(env, ax); /* Move final entry into this slot. */ - --S3JniGlobal.autoExt.nExt; - *ax = S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt]; - memset(&S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt], 0, + --SJG.autoExt.nExt; + *ax = SJG.autoExt.pExt[SJG.autoExt.nExt]; + memset(&SJG.autoExt.pExt[SJG.autoExt.nExt], 0, sizeof(S3JniAutoExtension)); - assert(! S3JniGlobal.autoExt.pExt[S3JniGlobal.autoExt.nExt].jObj ); + assert(! SJG.autoExt.pExt[SJG.autoExt.nExt].jObj ); rc = JNI_TRUE; break; } @@ -2874,10 +2828,10 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; MUTEX_EXT_ENTER; - for( i = 0; i < S3JniGlobal.autoExt.nExt; ++i ){ - S3JniAutoExtension_clear( env, &S3JniGlobal.autoExt.pExt[i] ); + for( i = 0; i < SJG.autoExt.nExt; ++i ){ + S3JniAutoExtension_clear( env, &SJG.autoExt.pExt[i] ); } - S3JniGlobal.autoExt.nExt = 0; + SJG.autoExt.nExt = 0; MUTEX_EXT_LEAVE; } @@ -3189,8 +3143,8 @@ JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){ JDECL(jint,1shutdown)(JENV_CSELF){ s3jni_reset_auto_extension(env); MUTEX_ENV_ENTER; - while( S3JniGlobal.envCache.aHead ){ - S3JniGlobal_env_uncache( S3JniGlobal.envCache.aHead->env ); + while( SJG.envCache.aHead ){ + S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); } MUTEX_ENV_LEAVE; /* Do not clear S3JniGlobal.jvm: it's legal to call @@ -3237,7 +3191,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ createStmt = 1; break; case SQLITE_TRACE_PROFILE: - jX = (*env)->NewObject(env, jc->g.cLong, jc->g.ctorLong1, + jX = (*env)->NewObject(env, SJG.g.cLong, SJG.g.ctorLong1, (jlong)*((sqlite3_int64*)pX)); // hmm. ^^^ (*pX) really is zero. // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); @@ -3471,6 +3425,8 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ puts("sizeofs:"); #define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T)) SO(void*); + SO(jmethodID); + SO(jfieldID); SO(S3JniEnv); SO(S3JniHook); SO(S3JniDb); @@ -3482,18 +3438,18 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ SO(S3JniUdf); printf("Cache info:\n"); printf("\tJNIEnv cache %u misses, %u hits\n", - S3JniGlobal.metrics.envCacheMisses, - S3JniGlobal.metrics.envCacheHits); + SJG.metrics.envCacheMisses, + SJG.metrics.envCacheHits); printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt\n", - S3JniGlobal.metrics.nMutexEnv, - S3JniGlobal.metrics.nMutexPerDb, - S3JniGlobal.metrics.nMutexAutoExt); + SJG.metrics.nMutexEnv, + SJG.metrics.nMutexPerDb, + SJG.metrics.nMutexAutoExt); puts("Java-side UDF calls:"); -#define UDF(T) printf("\t%-8s = %u\n", "x" #T, S3JniGlobal.metrics.udf.n##T) +#define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); #undef UDF printf("xDestroy calls across all callback types: %u\n", - S3JniGlobal.metrics.nDestroy); + SJG.metrics.nDestroy); #undef SO } @@ -3598,13 +3554,20 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ instance, or NULL on OOM. */ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ - S3JniEnv * const row = S3JniGlobal_env_cache(env); - if( !row->jFtsExt ){ - row->jFtsExt = new_NativePointerHolder_object(env, &S3NphRefs.Fts5ExtensionApi, - s3jni_ftsext()); - if(row->jFtsExt) row->jFtsExt = REF_G(row->jFtsExt); + if( !SJG.fts5.jFtsExt ){ + jobject pNPH = new_NativePointerHolder_object( + env, &S3NphRefs.Fts5ExtensionApi, s3jni_ftsext() + ); + MUTEX_ENV_ENTER; + if( pNPH ){ + if( !SJG.fts5.jFtsExt ){ + SJG.fts5.jFtsExt = REF_G(pNPH); + } + UNREF_L(pNPH); + } + MUTEX_ENV_LEAVE; } - return row->jFtsExt; + return SJG.fts5.jFtsExt; } /* @@ -3820,41 +3783,28 @@ JDECLFtsXA(jint,xPhraseCount)(JENV_OSELF,jobject jCtx){ return (jint)fext->xPhraseCount(PtrGet_Fts5Context(jCtx)); } -/** - Initializes jc->jPhraseIter if it needed it. -*/ -static void s3jni_phraseIter_init(JNIEnv *const env, S3JniEnv * const jc, - jobject jIter){ - if(!jc->jPhraseIter.klazz){ - jclass klazz = (*env)->GetObjectClass(env, jIter); - jc->jPhraseIter.klazz = REF_G(klazz); - jc->jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J"); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); - jc->jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "a", "J"); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); - } -} - /* Copy the 'a' and 'b' fields from pSrc to Fts5PhraseIter object jIter. */ -static void s3jni_phraseIter_NToJ(JNIEnv *const env, S3JniEnv const * const jc, - Fts5PhraseIter const * const pSrc, - jobject jIter){ - assert(jc->jPhraseIter.klazz); - (*env)->SetLongField(env, jIter, jc->jPhraseIter.fidA, (jlong)pSrc->a); +static void s3jni_phraseIter_NToJ(JNIEnv *const env, + Fts5PhraseIter const * const pSrc, + jobject jIter){ + S3JniGlobalType * const g = &S3JniGlobal; + assert(g->fts5.jPhraseIter.klazz); + (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidA, (jlong)pSrc->a); EXCEPTION_IS_FATAL("Cannot set Fts5PhraseIter.a field."); - (*env)->SetLongField(env, jIter, jc->jPhraseIter.fidB, (jlong)pSrc->b); + (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidB, (jlong)pSrc->b); EXCEPTION_IS_FATAL("Cannot set Fts5PhraseIter.b field."); } /* Copy the 'a' and 'b' fields from Fts5PhraseIter object jIter to pDest. */ -static void s3jni_phraseIter_JToN(JNIEnv *const env, S3JniEnv const * const jc, - jobject jIter, Fts5PhraseIter * const pDest){ - assert(jc->jPhraseIter.klazz); +static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter, + Fts5PhraseIter * const pDest){ + S3JniGlobalType * const g = &S3JniGlobal; + assert(g->fts5.jPhraseIter.klazz); pDest->a = - (const unsigned char *)(*env)->GetLongField(env, jIter, jc->jPhraseIter.fidA); + (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidA); EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); pDest->b = - (const unsigned char *)(*env)->GetLongField(env, jIter, jc->jPhraseIter.fidB); + (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidB); EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); } @@ -3862,16 +3812,14 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int rc, iCol = 0, iOff = 0; - s3jni_phraseIter_init(env, jc, jIter); rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase, &iter, &iCol, &iOff); if( 0==rc ){ OutputPointer_set_Int32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutOff, iOff); - s3jni_phraseIter_NToJ(env, jc, &iter, jIter); + s3jni_phraseIter_NToJ(env, &iter, jIter); } return rc; } @@ -3879,15 +3827,13 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int rc, iCol = 0; - s3jni_phraseIter_init(env, jc, jIter); rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase, &iter, &iCol); if( 0==rc ){ OutputPointer_set_Int32(env, jOutCol, iCol); - s3jni_phraseIter_NToJ(env, jc, &iter, jIter); + s3jni_phraseIter_NToJ(env, &iter, jIter); } return rc; } @@ -3895,29 +3841,26 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int iCol = 0, iOff = 0; - if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/; - s3jni_phraseIter_JToN(env, jc, jIter, &iter); - fext->xPhraseNext(PtrGet_Fts5Context(jCtx), - &iter, &iCol, &iOff); + if(!SJG.fts5.jPhraseIter.klazz) return /*SQLITE_MISUSE*/; + s3jni_phraseIter_JToN(env, jIter, &iter); + fext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff); OutputPointer_set_Int32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutOff, iOff); - s3jni_phraseIter_NToJ(env, jc, &iter, jIter); + s3jni_phraseIter_NToJ(env, &iter, jIter); } JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter, jobject jOutCol){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); Fts5PhraseIter iter; int iCol = 0; - if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/; - s3jni_phraseIter_JToN(env, jc, jIter, &iter); + if(!SJG.fts5.jPhraseIter.klazz) return /*SQLITE_MISUSE*/; + s3jni_phraseIter_JToN(env, jIter, &iter); fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol); OutputPointer_set_Int32(env, jOutCol, iCol); - s3jni_phraseIter_NToJ(env, jc, &iter, jIter); + s3jni_phraseIter_NToJ(env, &iter, jIter); } @@ -3952,7 +3895,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, struct s3jni_xQueryPhraseState const * s = pData; JNIEnv * const env = s->env; int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, - s->jc->jFtsExt, s->jFcx); + SJG.fts5.jFtsExt, s->jFcx); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("xQueryPhrase callback"); EXCEPTION_CLEAR; @@ -4391,6 +4334,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ {0,0} }; jfieldID fieldId; + jclass klazz; const ConfigFlagEntry * pConfFlag; if( 0==sqlite3_threadsafe() ){ @@ -4398,26 +4342,70 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ return; } memset(&S3JniGlobal, 0, sizeof(S3JniGlobal)); - if( (*env)->GetJavaVM(env, &S3JniGlobal.jvm) ){ + if( (*env)->GetJavaVM(env, &SJG.jvm) ){ (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible."); return; } - S3JniGlobal.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( S3JniGlobal.envCache.mutex ); - S3JniGlobal.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( S3JniGlobal.perDb.mutex ); - S3JniGlobal.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( S3JniGlobal.autoExt.mutex ); + + /* Grab references to various global classes and objects... */ + SJG.g.cObj = REF_G((*env)->FindClass(env,"java/lang/Object")); + EXCEPTION_IS_FATAL("Error getting reference to Object class."); + + SJG.g.cLong = REF_G((*env)->FindClass(env,"java/lang/Long")); + EXCEPTION_IS_FATAL("Error getting reference to Long class."); + SJG.g.ctorLong1 = (*env)->GetMethodID(env, SJG.g.cLong, + "", "(J)V"); + EXCEPTION_IS_FATAL("Error getting reference to Long constructor."); + + SJG.g.cString = REF_G((*env)->FindClass(env,"java/lang/String")); + EXCEPTION_IS_FATAL("Error getting reference to String class."); + SJG.g.ctorStringBA = + (*env)->GetMethodID(env, SJG.g.cString, + "", "([BLjava/nio/charset/Charset;)V"); + EXCEPTION_IS_FATAL("Error getting reference to String(byte[],Charset) ctor."); + SJG.g.stringGetBytes = + (*env)->GetMethodID(env, SJG.g.cString, + "getBytes", "(Ljava/nio/charset/Charset;)[B"); + EXCEPTION_IS_FATAL("Error getting reference to String.getBytes(Charset)."); + + { /* StandardCharsets.UTF_8 */ + jfieldID fUtf8; + jclass const klazzSC = + (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); + EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class."); + fUtf8 = (*env)->GetStaticFieldID(env, klazzSC, "UTF_8", + "Ljava/nio/charset/Charset;"); + EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); + SJG.g.oCharsetUtf8 = + REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8)); + EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); + } + +#ifdef SQLITE_ENABLE_FTS5 + klazz = (*env)->FindClass(env, "org/sqlite/jni/Fts5PhraseIter"); + EXCEPTION_IS_FATAL("Error getting reference to org.sqlite.jni.Fts5PhraseIter."); + SJG.fts5.jPhraseIter.klazz = REF_G(klazz); + SJG.fts5.jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J"); + EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); + SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "a", "J"); + EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); +#endif + + SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( SJG.envCache.mutex ); + SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( SJG.perDb.mutex ); + SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( SJG.autoExt.mutex ); #if 0 /* Just for sanity checking... */ (void)S3JniGlobal_env_cache(env); - if( !S3JniGlobal.envCache.aHead ){ + if( !SJG.envCache.aHead ){ (*env)->FatalError(env, "Could not allocate JNIEnv-specific cache."); return; } - assert( 1 == S3JniGlobal.metrics.envCacheMisses ); - assert( env == S3JniGlobal.envCache.aHead->env ); - assert( 0 != S3JniGlobal.envCache.aHead->g.cObj ); + assert( 1 == SJG.metrics.envCacheMisses ); + assert( env == SJG.envCache.aHead->env ); #endif for( pConfFlag = &aLimits[0]; pConfFlag->zName; ++pConfFlag ){ diff --git a/manifest b/manifest index 98f0ce772d..62da389266 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sJNI\scleanups. -D 2023-08-21T23:45:19.079 +C Move\smost\sof\sthe\sper-JNIEnv\sglobal\sJava\sclass\srefs\sinto\sthe\sglobal\sstate,\ssaving\sa\sbit\sof\sper-thread\soverhead. +D 2023-08-22T11:34:34.096 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 4849b0ac41c3a92777aebf0ec3d51c2be7c78d3ea9b91ece03ade6f9fa13d99a F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 77ff2a7d608737aef5734d60881cd167d4b5916bcca8669067aa13afd641ba6e +F ext/jni/src/c/sqlite3-jni.c 96252788a8eea13e8da997103c05b8c1d2c878eb2c5a71a123baeb2bd5e91027 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -2092,8 +2092,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 0a84131008a2e7886dac64a3545dea634811f6eac2b90885ec9c61ed1e6544c3 -R e61cca732e3141bc7655ea7dcc403579 +P b88910aaaaaaa0936974379bb3eb8a5a3a634395b14e67cc9030f8a520f471f1 +R 76968b303e9bd6c480af577a480bf905 U stephan -Z 1c9a32335d26ec39640641e263699a74 +Z edf907aa4738f3b50b53a088953efc54 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 48b34f86bd..ee5d057164 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b88910aaaaaaa0936974379bb3eb8a5a3a634395b14e67cc9030f8a520f471f1 \ No newline at end of file +7342bf578790e1a87c128a7c1c7745fe2e7c442890370feb160d406597d4d8ec \ No newline at end of file From 9828aa223ab09cb208d639e0e3c367c8ef34faa8 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 15:30:35 +0000 Subject: [PATCH 028/422] Move the JNI per-thread cache of NativePointerHolder refs into global space. This allows better-targeted mutex locks and incidentally eliminates the lagginess and post-run hangs in Tester1's multi-thread mode (presumably caused by deadlocks). FossilOrigin-Name: e209f56a9745695aadc04418c7bebe62b79e38e5aee26c3248a30f73bfa460c2 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 402 +++++++++++------------- ext/jni/src/org/sqlite/jni/Tester1.java | 29 +- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 218 insertions(+), 233 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 0bb3bec441..de9204fd64 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -232,7 +232,7 @@ test: $(SQLite3Jni.class) $(sqlite3-jni.dll) test-mt: $(SQLite3Jni.class) $(sqlite3-jni.dll) $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ - org.sqlite.jni.Tester1 -t 4 -r 5 $(test.flags) + org.sqlite.jni.Tester1 -t 7 -r 50 $(test.flags) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d3fd0db82a..bce6a1a63c 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -217,13 +217,14 @@ static inline void delete_local_ref(JNIEnv * const env, jobject const v){ */ typedef struct S3NphRef S3NphRef; struct S3NphRef { - const int index /* index into S3JniEnv->nph[] */; + const int index /* index into S3JniGlobal.nph[] */; const char * const zName /* Full Java name of the class */; }; /** - Keys for each concrete NativePointerHolder subclass. These are to - be used with S3JniGlobal_nph_cache() and friends. + Keys for each concrete NativePointerHolder subclass. These are to + be used with S3JniGlobal_nph_cache() and friends. These are + initialized on-demand by S3JniGlobal_nph_cache(). */ static const struct { const S3NphRef sqlite3; @@ -346,23 +347,18 @@ enum { */ typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { - const S3NphRef * pRef /* Entry from S3NphRefs. */; + volatile const S3NphRef * pRef /* Entry from S3NphRefs. */; jclass klazz /* global ref to the concrete NativePointerHolder subclass represented by zClassName */; - jmethodID midCtor /* klazz's no-arg constructor. Used by - new_NativePointerHolder_object(). */; - jfieldID fidValue /* NativePointerHolder.nativePointer and - OutputPointer.X.value */; - jfieldID fidSetAgg /* sqlite3_context::aggregateContext. Used only - by the sqlite3_context binding. */; + volatile jmethodID midCtor /* klazz's no-arg constructor. Used by + new_NativePointerHolder_object(). */; + volatile jfieldID fidValue /* NativePointerHolder.nativePointer or + OutputPointer.T.value */; + volatile jfieldID fidAggCtx /* sqlite3_context::aggregateContext. Used only + by the sqlite3_context binding. */; }; -static void S3JniNphClass_clear(JNIEnv * const env, S3JniNphClass * const p){ - UNREF_G(p->klazz); - memset(p, 0, sizeof(S3JniNphClass)); -} - /** State for various hook callbacks. */ typedef struct S3JniHook S3JniHook; struct S3JniHook{ @@ -444,14 +440,6 @@ struct S3JniEnv { S3JniDb * pdbOpening; S3JniEnv * pPrev /* Previous entry in the linked list */; S3JniEnv * pNext /* Next entry in the linked list */; - /* - ** Cache of Java refs/IDs for NativePointerHolder subclasses. We - ** "could" move this into S3JniGLobal but doing so would require - ** adding a mutex for this state in several places. It might be a - ** win, though, as it would eliminate many locks of of the - ** S3JniGlobal.envCache.mutex. - */ - S3JniNphClass nph[NphCache_SIZE]; }; /* @@ -480,10 +468,18 @@ struct S3JniGlobalType { ** JNIEnv when necessary. */ JavaVM * jvm; + /* Cache of Java refs/IDs for NativePointerHolder subclasses. + ** Initialized on demand. + */ + S3JniNphClass nph[NphCache_SIZE]; + /* + ** Cache of per-thread state. + */ struct { S3JniEnv * aHead /* Linked list of in-use instances */; S3JniEnv * aFree /* Linked list of free instances */; - sqlite3_mutex * mutex /* mutex for aHead and aFree */; + sqlite3_mutex * mutex /* mutex for aHead and aFree as well for + first-time inits of nph members. */; void const * locker /* env mutex is held on this object's behalf. Used only for sanity checking. */; } envCache; @@ -524,7 +520,11 @@ struct S3JniGlobalType { struct { volatile unsigned envCacheHits; volatile unsigned envCacheMisses; - volatile unsigned nMutexEnv /* number of times envCache.mutex was entered */; + volatile unsigned envCacheAllocs; + volatile unsigned nMutexEnv /* number of times envCache.mutex was entered for + a S3JniEnv operation. */; + volatile unsigned nMutexEnv2 /* number of times envCache.mutex was entered for + a S3JniNphClass operation. */; volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; volatile unsigned nDestroy /* xDestroy() calls across all types */; @@ -553,39 +553,50 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal -#define MUTEX_ASSERT_LOCKER_ENV \ +#define MUTEX_ENV_ASSERT_LOCKER \ assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ASSERT_NOTLOCKER_ENV \ +#define MUTEX_ENV_ASSERT_NOTLOCKER \ assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENV_ENTER \ - MUTEX_ASSERT_NOTLOCKER_ENV; \ - /*MARKER(("Entering ENV mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( SJG.envCache.mutex ); \ - ++SJG.metrics.nMutexEnv; \ +#define MUTEX_ENV_ENTER \ + MUTEX_ENV_ASSERT_NOTLOCKER; \ + /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ + sqlite3_mutex_enter( SJG.envCache.mutex ); \ + ++SJG.metrics.nMutexEnv; \ SJG.envCache.locker = env -#define MUTEX_ENV_LEAVE \ - /*MARKER(("Leaving ENV mutex @%p %s.\n", env, __func__));*/ \ - MUTEX_ASSERT_LOCKER_ENV; \ - SJG.envCache.locker = 0; \ +#define MUTEX_ENV_LEAVE \ + /*MARKER(("Leaving ENV mutex @%p %s.\n", env));*/ \ + MUTEX_ENV_ASSERT_LOCKER; \ + SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) -#define MUTEX_ASSERT_LOCKED_PDB \ - assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) -#define MUTEX_PDB_ENTER \ - /*MARKER(("Entering PerDb mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( SJG.perDb.mutex ); \ - ++SJG.metrics.nMutexPerDb; \ - SJG.perDb.locker = env; -#define MUTEX_PDB_LEAVE \ - /*MARKER(("Leaving PerDb mutex@%p %s.\n", env, __func__));*/ \ - SJG.perDb.locker = 0; \ - sqlite3_mutex_leave( SJG.perDb.mutex ) -#define MUTEX_EXT_ENTER \ - /*MARKER(("Entering autoExt mutex@%p %s.\n", env, __func__));*/ \ - sqlite3_mutex_enter( SJG.autoExt.mutex ); \ +#define MUTEX_EXT_ENTER \ + /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ + sqlite3_mutex_enter( SJG.autoExt.mutex ); \ ++SJG.metrics.nMutexAutoExt -#define MUTEX_EXT_LEAVE \ - /*MARKER(("Leaving autoExt mutex@%p %s.\n", env, __func__));*/ \ +#define MUTEX_EXT_LEAVE \ + /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_leave( SJG.autoExt.mutex ) +#define MUTEX_NPH_ENTER \ + MUTEX_ENV_ASSERT_NOTLOCKER; \ + /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ + sqlite3_mutex_enter( SJG.envCache.mutex ); \ + ++SJG.metrics.nMutexEnv2; \ + SJG.envCache.locker = env +#define MUTEX_NPH_LEAVE \ + /*MARKER(("Leaving NPH mutex @%p %s.\n", env));*/ \ + MUTEX_ENV_ASSERT_LOCKER; \ + SJG.envCache.locker = 0; \ + sqlite3_mutex_leave( SJG.envCache.mutex ) +#define MUTEX_PDB_ENTER \ + /*MARKER(("Entering PerDb mutex@%p %s.\n", env));*/ \ + sqlite3_mutex_enter( SJG.perDb.mutex ); \ + ++SJG.metrics.nMutexPerDb; \ + SJG.perDb.locker = env; +#define MUTEX_PDB_LEAVE \ + /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ + SJG.perDb.locker = 0; \ + sqlite3_mutex_leave( SJG.perDb.mutex ) +#define MUTEX_PDB_ASSERT_LOCKED \ + assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) static void s3jni_oom(JNIEnv * const env){ @@ -629,6 +640,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ if( row->pNext ) row->pNext->pPrev = 0; }else{ row = s3jni_malloc(env, sizeof(S3JniEnv)); + ++SJG.metrics.envCacheAllocs; } memset(row, 0, sizeof(*row)); row->pNext = SJG.envCache.aHead; @@ -847,7 +859,6 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ assert(klazz); } method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); - //MARKER(("jObj=%p, klazz=%p, method=%p\n", jObj, klazz, method)); if(method){ ++SJG.metrics.nDestroy; (*env)->CallVoidMethod(env, jObj, method); @@ -883,7 +894,7 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest static void S3JniDb_set_aside(S3JniDb * const s){ if(s){ JNIEnv * const env = s->env; - MUTEX_ASSERT_LOCKED_PDB; + MUTEX_PDB_ASSERT_LOCKED; //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); assert(s->pNext != s); @@ -949,8 +960,7 @@ static void S3JniDb_free_for_env(JNIEnv *env){ */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; - int i; - MUTEX_ASSERT_LOCKER_ENV; + MUTEX_ENV_ASSERT_LOCKER; row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ @@ -968,9 +978,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ SJG.envCache.aHead = row->pNext; } S3JniDb_free_for_env(env); - for( i = 0; i < NphCache_SIZE; ++i ){ - S3JniNphClass_clear(env, &row->nph[i]); - } memset(row, 0, sizeof(S3JniEnv)); row->pNext = SJG.envCache.aFree; if( row->pNext ) row->pNext->pPrev = row; @@ -1008,27 +1015,35 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* because all nph entries are per-thread and envCache.mutex already guards the fetching of envRow. */ - struct S3JniEnv * const envRow = S3JniGlobal_env_cache(env); - S3JniNphClass * pCache; - assert(envRow); - pCache = &envRow->nph[pRef->index]; - if( !pCache->pRef ){ - pCache->pRef = pRef; - pCache->klazz = (*env)->FindClass(env, pRef->zName); - EXCEPTION_IS_FATAL("FindClass() unexpectedly threw"); - pCache->klazz = REF_G(pCache->klazz); + S3JniNphClass * const pNC = &SJG.nph[pRef->index]; + if( !pNC->pRef ){ + MUTEX_NPH_ENTER; + if( !pNC->pRef ){ + pNC->pRef = pRef; + pNC->klazz = (*env)->FindClass(env, pRef->zName); + EXCEPTION_IS_FATAL("FindClass() unexpectedly threw"); + pNC->klazz = REF_G(pNC->klazz); + } + MUTEX_NPH_LEAVE; } - return pCache; + return pNC; } /** Returns the ID of the "nativePointer" field from the given NativePointerHolder class. - */ -static jfieldID NativePointerHolder_getField(JNIEnv * const env, jclass klazz){ - jfieldID rv = (*env)->GetFieldID(env, klazz, "nativePointer", "J"); - EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); - return rv; +*/ +static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* pRef){ + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); + if(!pNC->fidValue){ + MUTEX_NPH_ENTER; + if(!pNC->fidValue){ + pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); + EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); + } + MUTEX_NPH_LEAVE; + } + return pNC->fidValue; } /** @@ -1038,18 +1053,8 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, jclass klazz){ */ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, S3NphRef const* pRef){ - jfieldID setter = 0; - S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); - if(pNC->fidValue){ - setter = pNC->fidValue; - assert(setter); - }else{ - jclass const klazz = pNC->klazz - ? pNC->klazz - : (pNC->klazz = (*env)->GetObjectClass(env, ppOut)); - setter = pNC->fidValue = NativePointerHolder_getField(env, klazz); - } - (*env)->SetLongField(env, ppOut, setter, (jlong)p); + (*env)->SetLongField(env, ppOut, NativePointerHolder_getField(env, pRef), + (jlong)p); EXCEPTION_IS_FATAL("Could not set NativePointerHolder.nativePointer."); } @@ -1060,19 +1065,10 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, */ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const* pRef){ if( pObj ){ - jfieldID getter = 0; - void * rv = 0; - S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); - if(pNC->fidValue){ - getter = pNC->fidValue; - }else{ - jclass const klazz = pNC->klazz - ? pNC->klazz - : (pNC->klazz = (*env)->GetObjectClass(env, pObj)); - getter = pNC->fidValue = NativePointerHolder_getField(env, klazz); - } - rv = (void*)(*env)->GetLongField(env, pObj, getter); - IFTHREW_REPORT; + void * rv = (void*)(*env)->GetLongField( + env, pObj, NativePointerHolder_getField(env, pRef) + ); + EXCEPTION_IS_FATAL("Cannot fetch NativePointerHolder.nativePointer."); return rv; }else{ return 0; @@ -1201,12 +1197,12 @@ static int S3JniAutoExtension_init(JNIEnv *const env, } ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", "(Lorg/sqlite/jni/sqlite3;)I"); + //UNREF_L(klazz); if(!ax->midFunc){ MARKER(("Error getting xEntryPoint(sqlite3) from object.")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } - UNREF_L(klazz); ax->jObj = REF_G(jAutoExt); return 0; } @@ -1232,34 +1228,22 @@ static int S3JniAutoExtension_init(JNIEnv *const env, static int udf_setAggregateContext(JNIEnv * env, jobject jCx, sqlite3_context * pCx, int isFinal){ - jfieldID member; void * pAgg; int rc = 0; - S3JniNphClass * const pCache = + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context); - if(pCache && pCache->klazz && pCache->fidSetAgg){ - member = pCache->fidSetAgg; - assert(member); - }else{ - jclass const klazz = - pCache ? pCache->klazz : (*env)->GetObjectClass(env, jCx); - member = (*env)->GetFieldID(env, klazz, "aggregateContext", "J"); - if( !member ){ - IFTHREW{ EXCEPTION_REPORT; EXCEPTION_CLEAR; } - return s3jni_db_error(sqlite3_context_db_handle(pCx), - SQLITE_ERROR, - "Internal error: cannot find " - "sqlite3_context::aggregateContext field."); - } - if(pCache){ - assert(pCache->klazz); - assert(!pCache->fidSetAgg); - pCache->fidSetAgg = member; + if(!pNC->fidAggCtx){ + MUTEX_NPH_ENTER; + if(!pNC->fidAggCtx){ + pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, + "aggregateContext", "J"); + EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member."); } + MUTEX_NPH_LEAVE; } - pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : 4); + pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : sizeof(void*)); if( pAgg || isFinal ){ - (*env)->SetLongField(env, jCx, member, (jlong)pAgg); + (*env)->SetLongField(env, jCx, pNC->fidAggCtx, (jlong)pAgg); }else{ assert(!pAgg); rc = SQLITE_NOMEM; @@ -1268,42 +1252,39 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, } /** - Common init for OutputPointer_set_Int32() and friends. zClassName must be a - pointer from S3NphRefs. jOut must be an instance of that - class. Fetches the jfieldID for jOut's [value] property, which must - be of the type represented by the JNI type signature zTypeSig, and - stores it in pFieldId. Fails fatally if the property is not found, - as that presents a serious internal misuse. + Common init for OutputPointer_set_Int32() and friends. pRef must be + a pointer from S3NphRefs. jOut must be an instance of that + class. If necessary, this fetches the jfieldID for jOut's [value] + property, which must be of the type represented by the JNI type + signature zTypeSig, and stores it in pRef's S3JniGlobal.nph entry. + Fails fatally if the property is not found, as that presents a + serious internal misuse. - Property lookups are cached on a per-zClassName basis. Do not use - this routine with the same zClassName but different zTypeSig: it - will misbehave. + Property lookups are cached on a per-pRef basis. Do not use this + routine with the same pRef but different zTypeSig: it will + misbehave. */ -static void setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, - const char * const zTypeSig, - jobject const jOut, jfieldID * const pFieldId){ - jfieldID setter = 0; - S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); - if(pCache && pCache->klazz && pCache->fidValue){ - setter = pCache->fidValue; - }else{ - const jclass klazz = (*env)->GetObjectClass(env, jOut); - /*MARKER(("%s => %s\n", zClassName, zTypeSig));*/ - setter = (*env)->GetFieldID(env, klazz, "value", zTypeSig); - EXCEPTION_IS_FATAL("setupOutputPointer() could not find OutputPointer.*.value"); - if(pCache){ - assert(!pCache->fidValue); - pCache->fidValue = setter; +static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, + const char * const zTypeSig, + jobject const jOut){ + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); + if(!pNC->fidValue){ + MUTEX_NPH_ENTER; + if(!pNC->fidValue){ + pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "value", zTypeSig); + EXCEPTION_IS_FATAL("setupOutputPointer() could not find OutputPointer.*.value"); } + MUTEX_NPH_LEAVE; } - *pFieldId = setter; + return pNC->fidValue; } /* Sets the value property of the OutputPointer.Int32 jOut object to v. */ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_Int32, "I", jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_Int32, "I", jOut + ); (*env)->SetIntField(env, jOut, setter, (jint)v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value"); } @@ -1311,26 +1292,28 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int /* Sets the value property of the OutputPointer.Int64 jOut object to v. */ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_Int64, "J", jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_Int64, "J", jOut + ); (*env)->SetLongField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value"); } static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_sqlite3, - "Lorg/sqlite/jni/sqlite3;", jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut + ); (*env)->SetObjectField(env, jOut, setter, jDb); EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value"); } static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, jobject jStmt){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_sqlite3_stmt, - "Lorg/sqlite/jni/sqlite3_stmt;", jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_sqlite3_stmt, + "Lorg/sqlite/jni/sqlite3_stmt;", jOut + ); (*env)->SetObjectField(env, jOut, setter, jStmt); EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value"); } @@ -1341,9 +1324,9 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu to v. */ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, jbyteArray const v){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_ByteArray, "[B", - jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_ByteArray, "[B", jOut + ); (*env)->SetObjectField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.ByteArray.value"); } @@ -1353,9 +1336,9 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, to v. */ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, jstring const v){ - jfieldID setter = 0; - setupOutputPointer(env, &S3NphRefs.OutputPointer_String, - "Ljava/lang/String;", jOut, &setter); + jfieldID const setter = setupOutputPointer( + env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;", jOut + ); (*env)->SetObjectField(env, jOut, setter, v); EXCEPTION_IS_FATAL("Cannot set OutputPointer.String.value"); } @@ -1486,28 +1469,16 @@ static void ResultJavaVal_finalizer(void *v){ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef, const void * pNative){ jobject rv = 0; - jclass klazz = 0; - jmethodID ctor = 0; - S3JniNphClass * const pCache = S3JniGlobal_nph_cache(env, pRef); - if(pCache->midCtor){ - assert( pCache->klazz ); - klazz = pCache->klazz; - ctor = pCache->midCtor; - }else{ - klazz = pCache - ? pCache->klazz - : (*env)->FindClass(env, pRef->zName); - ctor = klazz ? (*env)->GetMethodID(env, klazz, "", "()V") : 0; - EXCEPTION_IS_FATAL("Cannot find constructor for class."); - if(pCache){ - assert(pCache->klazz); - assert(!pCache->midCtor); - pCache->midCtor = ctor; + S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); + if(!pNC->midCtor){ + MUTEX_NPH_ENTER; + if(!pNC->midCtor){ + pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); + EXCEPTION_IS_FATAL("Cannot find constructor for class."); } + MUTEX_NPH_LEAVE; } - assert(klazz); - assert(ctor); - rv = (*env)->NewObject(env, klazz, ctor); + rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); EXCEPTION_IS_FATAL("No-arg constructor threw."); OOM_CHECK(rv); if(rv) NativePointerHolder_set(env, rv, pNative, pRef); @@ -1819,35 +1790,45 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, env = s3jni_get_env(); jc = S3JniGlobal_env_cache(env); ps = jc->pdbOpening; + if( !ps ){ + MARKER(("Unexpected arrival of null S3JniDb in auto-extension runner.\n")); + *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in auto-extension runner."); + return SQLITE_ERROR; + } jc->pdbOpening = 0; - assert( ps && "Unexpected arrival of null S3JniDb in auto-extension runner." ); //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb)); assert( !ps->pDb && "it's still being opened" ); ps->pDb = pDb; assert( ps->jDb ); NativePointerHolder_set(env, ps->jDb, pDb, &S3NphRefs.sqlite3); for( i = 0; go && 0==rc; ++i ){ - S3JniAutoExtension const * ax; + S3JniAutoExtension ax = {0,0} + /* We need a copy of the auto-extension object, with our own + ** local reference to it, to avoid a race condition with another + ** thread manipulating the list during the call and invaliding + ** what ax points to. */; MUTEX_EXT_ENTER; if( i >= SJG.autoExt.nExt ){ - ax = 0; go = 0; }else{ - ax = &SJG.autoExt.pExt[i]; + ax.jObj = REF_L(SJG.autoExt.pExt[i].jObj); + ax.midFunc = SJG.autoExt.pExt[i].midFunc; } MUTEX_EXT_LEAVE; - if( ax && ax->jObj ){ - rc = (*env)->CallIntMethod(env, ax->jObj, ax->midFunc, ps->jDb); + if( ax.jObj ){ + //MARKER(("Running auto-ext #%d from env@%p\n", i, env)); + rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); IFTHREW { jthrowable const ex = (*env)->ExceptionOccurred(env); char * zMsg; EXCEPTION_CLEAR; zMsg = s3jni_exception_error_msg(env, ex); - UNREF_L(ex); + //UNREF_L(ex); *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); sqlite3_free(zMsg); if( !rc ) rc = SQLITE_ERROR; } + UNREF_L(ax.jObj); } } return rc; @@ -2580,11 +2561,12 @@ JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){ return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); } -//! Pre-open() code common to sqlite3_open(_v2)(). +/* Pre-open() code common to sqlite3_open(_v2)(). */ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, - S3JniDb ** ps, jobject *jDb){ + S3JniDb ** ps){ int rc = 0; + jobject jDb = 0; *jc = S3JniGlobal_env_cache(env); if(!*jc){ rc = SQLITE_NOMEM; @@ -2595,20 +2577,20 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, rc = SQLITE_NOMEM; goto end; } - *jDb = new_sqlite3_wrapper(env, 0); - if( !*jDb ){ + jDb = new_sqlite3_wrapper(env, 0); + if( !jDb ){ sqlite3_free(*zDbName); *zDbName = 0; rc = SQLITE_NOMEM; goto end; } - *ps = S3JniDb_alloc(env, 0, *jDb); + *ps = S3JniDb_alloc(env, 0, jDb); if(*ps){ (*jc)->pdbOpening = *ps; }else{ + UNREF_L(jDb); rc = SQLITE_NOMEM; } - //MARKER(("pre-open ps@%p\n", *ps)); end: return rc; } @@ -2627,7 +2609,6 @@ end: static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, S3JniDb * ps, sqlite3 **ppDb, jobject jOut, int theRc){ - //MARKER(("post-open() ps@%p db@%p\n", ps, *ppDb)); jc->pdbOpening = 0; if(*ppDb){ assert(ps->jDb); @@ -2650,14 +2631,12 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ sqlite3 * pOut = 0; char *zName = 0; - jobject jDb = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; - int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); + int rc; + rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ rc = sqlite3_open(zName, &pOut); - //MARKER(("env=%p, *env=%p\n", env, *env)); - //MARKER(("open() ps@%p db@%p\n", ps, pOut)); rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); @@ -2669,11 +2648,10 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, jobject jOut, jint flags, jstring strVfs){ sqlite3 * pOut = 0; char *zName = 0; - jobject jDb = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; char *zVfs = 0; - int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps, &jDb); + int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc && strVfs ){ zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0); if( !zVfs ){ @@ -2683,9 +2661,6 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, if( 0==rc ){ rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs); } - //MARKER(("open_v2() ps@%p db@%p\n", ps, pOut)); - /*MARKER(("zName=%s, zVfs=%s, pOut=%p, flags=%d, nrc=%d\n", - zName, zVfs, pOut, (int)flags, nrc));*/ rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); @@ -3437,13 +3412,17 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ SO(S3JniAutoExtension); SO(S3JniUdf); printf("Cache info:\n"); - printf("\tJNIEnv cache %u misses, %u hits\n", + printf("\tJNIEnv cache: %u allocs, %u misses, %u hits\n", + SJG.metrics.envCacheAllocs, SJG.metrics.envCacheMisses, SJG.metrics.envCacheHits); - printf("Mutex entry:\n\t%u env\n\t%u perDb\n\t%u autoExt\n", - SJG.metrics.nMutexEnv, - SJG.metrics.nMutexPerDb, - SJG.metrics.nMutexAutoExt); + printf("Mutex entry:" + "\n\tenv %u" + "\n\tnph inits %u" + "\n\tperDb %u" + "\n\tautoExt %u container access\n", + SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, + SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); @@ -4397,6 +4376,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ OOM_CHECK( SJG.perDb.mutex ); SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); OOM_CHECK( SJG.autoExt.mutex ); + #if 0 /* Just for sanity checking... */ (void)S3JniGlobal_env_cache(env); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 96e1da1b05..1543a80052 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -37,8 +37,8 @@ public class Tester1 implements Runnable { static final Metrics metrics = new Metrics(); - public synchronized static void out(Object val){ - System.out.print(val); + public synchronized static void outln(){ + System.out.println(""); } public synchronized static void outln(Object val){ @@ -46,11 +46,14 @@ public class Tester1 implements Runnable { System.out.println(val); } + public synchronized static void out(Object val){ + System.out.print(val); + } + @SuppressWarnings("unchecked") public synchronized static void out(Object... vals){ - int n = 0; System.out.print(Thread.currentThread().getName()+": "); - for(Object v : vals) out((n++>0 ? " " : "")+v); + for(Object v : vals) out(v); } @SuppressWarnings("unchecked") @@ -211,7 +214,7 @@ public class Tester1 implements Runnable { affirm(0 != stmt.getNativePointer()); rc = sqlite3_step(stmt); if( SQLITE_DONE != rc ){ - outln("step failed ??? ",rc, sqlite3_errmsg(db)); + outln("step failed ??? ",rc, " ",sqlite3_errmsg(db)); } affirm(SQLITE_DONE == rc); sqlite3_finalize(stmt); @@ -741,7 +744,7 @@ public class Tester1 implements Runnable { outln("Bound constants:\n"); for(java.lang.reflect.Field field : declaredFields) { if(java.lang.reflect.Modifier.isStatic(field.getModifiers())) { - outln("\t"+field.getName()); + outln("\t",field.getName()); } } } @@ -760,9 +763,9 @@ public class Tester1 implements Runnable { java.util.Collections.sort(funcList); for(String n : funcList){ ++count; - outln("\t"+n+"()"); + outln("\t",n,"()"); } - outln(count+" functions named sqlite3_*."); + outln(count," functions named sqlite3_*."); } private void testTrace(){ @@ -1232,9 +1235,10 @@ public class Tester1 implements Runnable { final long timeStart = System.currentTimeMillis(); int nLoop = 0; - outln("libversion_number:", + outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); + outln("Running ",nRepeat," loop(s) over ",nThread," thread(s)."); for( int n = 0; n < nRepeat; ++n ){ if( nThread==null || nThread<=1 ){ new Tester1(0).runTests(false); @@ -1243,7 +1247,7 @@ public class Tester1 implements Runnable { final ExecutorService ex = Executors.newFixedThreadPool( nThread ); //final List> futures = new ArrayList<>(); ++nLoop; - outln("Running loop #",nLoop," over ",nThread," threads."); + out(nLoop+" "); for( int i = 0; i < nThread; ++i ){ ex.submit( new Tester1(i), i ); } @@ -1257,11 +1261,12 @@ public class Tester1 implements Runnable { } } } + outln(); final long timeEnd = System.currentTimeMillis(); outln("Tests done. Metrics:"); - outln("\tAssertions checked: "+affirmCount); - outln("\tDatabases opened: "+metrics.dbOpen); + outln("\tAssertions checked: ",affirmCount); + outln("\tDatabases opened: ",metrics.dbOpen); if( doSomethingForDev ){ sqlite3_do_something_for_developer(); } diff --git a/manifest b/manifest index 62da389266..105f01ad91 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\smost\sof\sthe\sper-JNIEnv\sglobal\sJava\sclass\srefs\sinto\sthe\sglobal\sstate,\ssaving\sa\sbit\sof\sper-thread\soverhead. -D 2023-08-22T11:34:34.096 +C Move\sthe\sJNI\sper-thread\scache\sof\sNativePointerHolder\srefs\sinto\sglobal\sspace.\sThis\sallows\sbetter-targeted\smutex\slocks\sand\sincidentally\seliminates\sthe\slagginess\sand\spost-run\shangs\sin\sTester1's\smulti-thread\smode\s(presumably\scaused\sby\sdeadlocks). +D 2023-08-22T15:30:35.368 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,10 +232,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 4849b0ac41c3a92777aebf0ec3d51c2be7c78d3ea9b91ece03ade6f9fa13d99a +F ext/jni/GNUmakefile 30f0926a69edbd9e9932283ec8e4cea02b785f373395f2093dbbc6d65866a196 F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 96252788a8eea13e8da997103c05b8c1d2c878eb2c5a71a123baeb2bd5e91027 +F ext/jni/src/c/sqlite3-jni.c ad002976687e294936ce8f50b9efb1e531fa1d78a076b8a09089328082b48af4 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 @@ -256,7 +256,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5c469585946b63592cafe134b01af0b9144a12131f22ea352e12f4c3ec70efb2 -F ext/jni/src/org/sqlite/jni/Tester1.java a3eef17c60b4770c6fcf70d6efc2273e74470f7c8067c0748d12f393bf260d34 +F ext/jni/src/org/sqlite/jni/Tester1.java 63e1e4285a0f050580490323f656809bdadc0f1f28c4454f5cca82a6ccdfaf0f F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 b88910aaaaaaa0936974379bb3eb8a5a3a634395b14e67cc9030f8a520f471f1 -R 76968b303e9bd6c480af577a480bf905 +P 7342bf578790e1a87c128a7c1c7745fe2e7c442890370feb160d406597d4d8ec +R 2848048c8acd75d9850655e629905695 U stephan -Z edf907aa4738f3b50b53a088953efc54 +Z 76a56828e8164766c79107d6b6e04f3f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ee5d057164..4a981ac71b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7342bf578790e1a87c128a7c1c7745fe2e7c442890370feb160d406597d4d8ec \ No newline at end of file +e209f56a9745695aadc04418c7bebe62b79e38e5aee26c3248a30f73bfa460c2 \ No newline at end of file From a7e3a1c09b9d0d6a8f4da623012d3dae0d8f64a7 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 17:36:59 +0000 Subject: [PATCH 029/422] JNI internal cleanups and correct two leaked db handles in test code. FossilOrigin-Name: f927a30b5bba35991f472084ebaf02779e84c343a4e84f0efb3df7679ff212f8 --- ext/jni/src/c/sqlite3-jni.c | 178 +++++++----------- ext/jni/src/org/sqlite/jni/AutoExtension.java | 17 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 37 ++-- ext/jni/src/org/sqlite/jni/Tester1.java | 18 +- manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 122 insertions(+), 148 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index bce6a1a63c..8ae78ea3d8 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -348,15 +348,15 @@ enum { typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { volatile const S3NphRef * pRef /* Entry from S3NphRefs. */; - jclass klazz /* global ref to the concrete - NativePointerHolder subclass represented by - zClassName */; + jclass klazz /* global ref to the concrete + ** NativePointerHolder subclass represented by + ** zClassName */; volatile jmethodID midCtor /* klazz's no-arg constructor. Used by - new_NativePointerHolder_object(). */; + ** new_NativePointerHolder_object(). */; volatile jfieldID fidValue /* NativePointerHolder.nativePointer or - OutputPointer.T.value */; + ** OutputPointer.T.value */; volatile jfieldID fidAggCtx /* sqlite3_context::aggregateContext. Used only - by the sqlite3_context binding. */; + ** by the sqlite3_context binding. */; }; /** State for various hook callbacks. */ @@ -364,13 +364,13 @@ typedef struct S3JniHook S3JniHook; struct S3JniHook{ jobject jObj /* global ref to Java instance */; jmethodID midCallback /* callback method. Signature depends on - jObj's type */; + ** jObj's type */; jclass klazz /* global ref to jObj's class. Only needed - by hooks which have an xDestroy() method. - We can probably eliminate this and simply - do the class lookup at the same - (deferred) time we do the xDestroy() - lookup. */; + ** by hooks which have an xDestroy() method. + ** We can probably eliminate this and simply + ** do the class lookup at the same + ** (deferred) time we do the xDestroy() + ** lookup. */; }; /* @@ -380,7 +380,10 @@ struct S3JniHook{ */ typedef struct S3JniDb S3JniDb; struct S3JniDb { - JNIEnv *env /* The associated JNIEnv handle */; + JNIEnv *env /* Used for cleaning up all dbs owned by a given + ** thread, noting that this ownership is an artificial + ** one imposed by our threading constraints, not by + ** the core library. */; sqlite3 *pDb /* The associated db handle */; jobject jDb /* A global ref of the output object which gets returned from sqlite3_open(_v2)(). We need this in @@ -528,6 +531,8 @@ struct S3JniGlobalType { volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; volatile unsigned nDestroy /* xDestroy() calls across all types */; + volatile unsigned nPdbAlloc /* Number of S3JniDb alloced. */; + volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */; struct { /* Number of calls for each type of UDF callback. */ volatile unsigned nFunc; @@ -689,6 +694,16 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, const unsigned char * } return jba; } +static JNIEnv * s3jni_get_env(void){ + JNIEnv * env = 0; + if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, + JNI_VERSION_1_8) ){ + fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); + abort(); + } + return env; +} +#define LocalJniGetEnv JNIEnv * const env = s3jni_get_env() /** Uses the java.lang.String(byte[],Charset) constructor to create a @@ -705,7 +720,7 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, const unsigned char * static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, const char * const z, int n){ jstring rv = NULL; - JNIEnv * const env = jc->env; + LocalJniGetEnv; if( 0==n || (n<0 && z && !z[0]) ){ /* Fast-track the empty-string case via the MUTF-8 API. We could hypothetically do this for any strings where n<4 and z is @@ -741,7 +756,7 @@ static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, */ static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, jstring jstr, int *nLen){ - JNIEnv * const env = jc->env; + LocalJniGetEnv; jbyteArray jba; jsize nBa; char *rv; @@ -893,7 +908,7 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest */ static void S3JniDb_set_aside(S3JniDb * const s){ if(s){ - JNIEnv * const env = s->env; + LocalJniGetEnv; MUTEX_PDB_ASSERT_LOCKED; //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); @@ -1087,8 +1102,6 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, MUTEX_PDB_ENTER; if(SJG.perDb.aFree){ rv = SJG.perDb.aFree; - //MARKER(("state@%p for db allocating for db@%p from free-list\n", rv, pDb)); - //MARKER(("%p->pPrev@%p, pNext@%p\n", rv, rv->pPrev, rv->pNext)); SJG.perDb.aFree = rv->pNext; assert(rv->pNext != rv); assert(rv->pPrev != rv); @@ -1099,11 +1112,13 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->pNext->pPrev = 0; rv->pNext = 0; } + ++SJG.metrics.nPdbRecycled; }else{ rv = s3jni_malloc(env, sizeof(S3JniDb)); //MARKER(("state@%p for db allocating for db@%p from heap\n", rv, pDb)); if(rv){ memset(rv, 0, sizeof(S3JniDb)); + ++SJG.metrics.nPdbAlloc; } } if(rv){ @@ -1357,7 +1372,7 @@ static int encodingTypeIsValid(int eTextRep){ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, int nRhs, const void *rhs){ S3JniDb * const ps = pArg; - JNIEnv * env = ps->env; + LocalJniGetEnv; jint rc = 0; jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; @@ -1380,56 +1395,17 @@ 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( ps->env, &ps->collation, 1 ); + S3JniHook_unref( s3jni_get_env(), &ps->collation, 1 ); } -/* State for sqlite3_result_java_object() and - sqlite3_value_java_object(). */ +/* +** State for sqlite3_result_java_object() and +** sqlite3_value_java_object(). +** +** TODO: this middle-man struct is no longer necessary. Conider +** removing it and passing around jObj itself instead. +*/ typedef struct { - /* The JNI docs say: - - https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html - - > The VM is guaranteed to pass the same interface pointer to a - native method when it makes multiple calls to the native method - from the same Java thread. - - Per the accompanying diagram, the "interface pointer" is the - pointer-to-pointer which is passed to all JNI calls - (`JNIEnv *env`), implying that we need to be caching that. The - verbiage "interface pointer" implies, however, that we should be - storing the dereferenced `(*env)` pointer. - - This posts claims it's unsafe to cache JNIEnv at all, even when - it's always used in the same thread: - - https://stackoverflow.com/questions/12420463 - - And this one seems to contradict that: - - https://stackoverflow.com/questions/13964608 - - For later reference: - - https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/design.html#wp1242 - - https://developer.android.com/training/articles/perf-jni - - The later has the following say about caching: - - > If performance is important, it's useful to look the - [class/method ID] values up once and cache the results in your - native code. Because there is a limit of one JavaVM per - process, it's reasonable to store this data in a static local - structure. ... The class references, field IDs, and method IDs - are guaranteed valid until the class is unloaded. Classes are - only unloaded if all classes associated with a ClassLoader can - be garbage collected, which is rare but will not be impossible - in Android. Note however that the jclass is a class reference - and must be protected with a call to NewGlobalRef (see the next - section). - */ - JNIEnv * env; jobject jObj; } ResultJavaVal; @@ -1439,7 +1415,6 @@ typedef struct { static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); if(rv){ - rv->env = env; rv->jObj = jObj ? REF_G(jObj) : 0; } return rv; @@ -1448,7 +1423,8 @@ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ static void ResultJavaVal_finalizer(void *v){ if(v){ ResultJavaVal * const rv = (ResultJavaVal*)v; - if(rv->jObj) (*(rv->env))->DeleteGlobalRef(rv->env, rv->jObj); + LocalJniGetEnv; + UNREF_G(rv->jObj); sqlite3_free(rv); } } @@ -1516,7 +1492,6 @@ typedef void (*udf_xFinal_f)(sqlite3_context*); */ typedef struct S3JniUdf S3JniUdf; struct S3JniUdf { - JNIEnv * env; /* env registered from */; jobject jObj /* SQLFunction instance */; jclass klazz /* jObj's class */; char * zFuncName /* Only for error reporting and debug logging */; @@ -1537,7 +1512,6 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ const char * zFV = /* signature for xFinal, xValue */ "(Lorg/sqlite/jni/sqlite3_context;)V"; memset(s, 0, sizeof(S3JniUdf)); - s->env = env; s->jObj = REF_G(jObj); s->klazz = REF_G((*env)->GetObjectClass(env, jObj)); #define FGET(FuncName,FuncType,Field) \ @@ -1560,7 +1534,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ } static void S3JniUdf_free(S3JniUdf * s){ - JNIEnv * const env = s->env; + LocalJniGetEnv; if(env){ //MARKER(("UDF cleanup: %s\n", s->zFuncName)); s3jni_call_xDestroy(env, s->jObj, s->klazz); @@ -1589,10 +1563,6 @@ typedef struct { Converts the given (cx, argc, argv) into arguments for the given UDF, placing the result in the final argument. Returns 0 on success, SQLITE_NOMEM on allocation error. - - TODO: see what we can do to optimize the - new_sqlite3_value_wrapper() call. e.g. find the ctor a single time - and call it here, rather than looking it up repeatedly. */ static int udf_args(JNIEnv *env, sqlite3_context * const cx, @@ -1652,9 +1622,9 @@ static int udf_xFSI(sqlite3_context* pCx, int argc, S3JniUdf * s, jmethodID xMethodID, const char * zFuncType){ - JNIEnv * const env = s->env; + LocalJniGetEnv; udf_jargs args = {0,0}; - int rc = udf_args(s->env, pCx, argc, argv, &args.jcx, &args.jargv); + int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); //MARKER(("%s.%s() pCx = %p\n", s->zFuncName, zFuncType, pCx)); if(rc) return rc; //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); @@ -1679,8 +1649,8 @@ static int udf_xFSI(sqlite3_context* pCx, int argc, static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jmethodID xMethodID, const char *zFuncType){ - JNIEnv * const env = s->env; - jobject jcx = new_sqlite3_context_wrapper(s->env, cx); + LocalJniGetEnv; + jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; //MARKER(("%s.%s() cx = %p\n", s->zFuncName, zFuncType, cx)); if(!jcx){ @@ -1768,16 +1738,6 @@ 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) -static JNIEnv * s3jni_get_env(void){ - JNIEnv * env = 0; - if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, - JNI_VERSION_1_8) ){ - fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); - abort(); - } - return env; -} - /* Central auto-extension handler. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ @@ -1958,7 +1918,7 @@ static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; int rc = 0; if( ps->busyHandler.jObj ){ - JNIEnv * const env = ps->env; + LocalJniGetEnv; rc = (*env)->CallIntMethod(env, ps->busyHandler.jObj, ps->busyHandler.midCallback, (jint)n); IFTHREW{ @@ -2079,7 +2039,7 @@ static unsigned int s3jni_utf16_strlen(void const * z){ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, int eTextRep, const void * z16Name){ S3JniDb * const ps = pState; - JNIEnv * const env = ps->env; + LocalJniGetEnv; unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); IFTHREW{ @@ -2184,7 +2144,7 @@ JDECL(jobject,1column_1value)(JENV_CSELF, jobject jpStmt, static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ - JNIEnv * const env = ps->env; + LocalJniGetEnv; int rc = isCommit ? (int)(*env)->CallIntMethod(env, ps->commitHook.jObj, ps->commitHook.midCallback) @@ -2749,7 +2709,7 @@ JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jDb, jbyteArra static int s3jni_progress_handler_impl(void *pP){ S3JniDb * const ps = (S3JniDb *)pP; - JNIEnv * const env = ps->env; + LocalJniGetEnv; int rc = (int)(*env)->CallIntMethod(env, ps->progress.jObj, ps->progress.midCallback); IFTHREW{ @@ -3001,7 +2961,7 @@ JDECL(jobject,1rollback_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, const char*z2,const char*z3){ S3JniDb * const ps = pState; - JNIEnv * const env = ps->env; + LocalJniGetEnv; S3JniEnv * const jc = S3JniGlobal_env_cache(env); S3JniHook const * const pHook = &ps->authHook; jstring const s0 = z0 ? s3jni_utf8_to_jstring(jc, z0, -1) : 0; @@ -3119,7 +3079,7 @@ JDECL(jint,1shutdown)(JENV_CSELF){ s3jni_reset_auto_extension(env); MUTEX_ENV_ENTER; while( SJG.envCache.aHead ){ - S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); + S3JniGlobal_env_uncache( env );//SJG.envCache.aHead->env ); } MUTEX_ENV_LEAVE; /* Do not clear S3JniGlobal.jvm: it's legal to call @@ -3151,7 +3111,7 @@ JDECL(jint,1step)(JENV_CSELF,jobject jStmt){ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ S3JniDb * const ps = (S3JniDb *)pC; - JNIEnv * const env = ps->env; + LocalJniGetEnv; jobject jX = NULL /* the tracer's X arg */; jobject jP = NULL /* the tracer's P arg */; jobject jPUnref = NULL /* potentially a local ref to jP */; @@ -3230,7 +3190,7 @@ JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, const char *zTable, sqlite3_int64 nRowid){ S3JniDb * const ps = pState; - JNIEnv * const env = ps->env; + LocalJniGetEnv; S3JniEnv * const jc = S3JniGlobal_env_cache(env); jstring jDbName; jstring jTable; @@ -3423,6 +3383,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ "\n\tautoExt %u container access\n", SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt); + printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n", + SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb), + (unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)), + SJG.metrics.nPdbRecycled); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); @@ -3465,7 +3429,6 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ State for binding Java-side FTS5 auxiliary functions. */ typedef struct { - JNIEnv * env; /* env registered from */; jobject jObj /* functor instance */; jclass klazz /* jObj's class */; jobject jUserData /* 2nd arg to JNI binding of @@ -3478,7 +3441,7 @@ typedef struct { } Fts5JniAux; static void Fts5JniAux_free(Fts5JniAux * const s){ - JNIEnv * const env = s->env; + LocalJniGetEnv; if(env){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ s3jni_call_xDestroy(env, s->jObj, s->klazz); @@ -3503,7 +3466,6 @@ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ "Lorg/sqlite/jni/sqlite3_context;" "[Lorg/sqlite/jni/sqlite3_value;)V"; memset(s, 0, sizeof(Fts5JniAux)); - s->env = env; s->jObj = REF_G(jObj); s->klazz = REF_G((*env)->GetObjectClass(env, jObj)); s->jmid = (*env)->GetMethodID(env, s->klazz, "xFunction", zSig); @@ -3639,14 +3601,14 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, int argc, sqlite3_value **argv){ Fts5JniAux * const pAux = pApi->xUserData(pFts); - JNIEnv *env; jobject jpCx = 0; jobjectArray jArgv = 0; jobject jpFts = 0; jobject jFXA; int rc; + LocalJniGetEnv; + assert(pAux); - env = pAux->env; jFXA = s3jni_getFts5ExensionApi(env); if( !jFXA ) goto error_oom; jpFts = new_Fts5Context_wrapper(env, pFts); @@ -3700,8 +3662,11 @@ JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, typedef struct S3JniFts5AuxData S3JniFts5AuxData; +/* +** TODO: this middle-man struct is no longer necessary. Conider +** removing it and passing around jObj itself instead. +*/ struct S3JniFts5AuxData { - JNIEnv *env; jobject jObj; }; @@ -3709,7 +3674,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){ if(x){ S3JniFts5AuxData * const p = x; if(p->jObj){ - JNIEnv *env = p->env; + LocalJniGetEnv; s3jni_call_xDestroy(env, p->jObj, 0); UNREF_G(p->jObj); } @@ -3872,7 +3837,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, guaranteed to be the same one passed to xQueryPhrase(). If it's not, we'll have to create a new wrapper object on every call. */ struct s3jni_xQueryPhraseState const * s = pData; - JNIEnv * const env = s->env; + LocalJniGetEnv; int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, SJG.fts5.jFtsExt, s->jFcx); IFTHREW{ @@ -3931,7 +3896,6 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ } return SQLITE_NOMEM; } - pAux->env = env; pAux->jObj = REF_G(jAux); rc = fext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux, S3JniFts5AuxData_xDestroy); @@ -3944,8 +3908,8 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, int nZ, int iStart, int iEnd){ int rc; + LocalJniGetEnv; struct s3jni_xQueryPhraseState * const s = p; - JNIEnv * const env = s->env; jbyteArray jba; if( s->tok.zPrev == z && s->tok.nPrev == nZ ){ jba = s->tok.jba; diff --git a/ext/jni/src/org/sqlite/jni/AutoExtension.java b/ext/jni/src/org/sqlite/jni/AutoExtension.java index a2ab6a0f75..fcad273d3d 100644 --- a/ext/jni/src/org/sqlite/jni/AutoExtension.java +++ b/ext/jni/src/org/sqlite/jni/AutoExtension.java @@ -18,14 +18,23 @@ package org.sqlite.jni; */ public interface AutoExtension { /** - Must function as described for the sqlite3_auto_extension(), - with the caveat that the signature is more limited. + Must function as described for a sqlite3_auto_extension() + callback, with the caveat that the signature is more limited. As an exception (as it were) to the callbacks-must-not-throw - rule, AutoExtensions may do so and the exception's error message + rule, AutoExtensions may throw and the exception's error message will be set as the db's error string. - Results are undefined if db is closed by an auto-extension. + Hints for implementations: + + - Opening a database from an auto-extension handler will lead to + an endless recursion of the auto-handler triggering itself + indirectly for each newly-opened database. + + - If this routine is stateful, it is a good idea to make the + overridden method synchronized. + + - Results are undefined if db is closed by an auto-extension. */ int xEntryPoint(sqlite3 db); } diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index dc34694185..cfca4fc77c 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -173,9 +173,6 @@ public final class SQLite3Jni { on multiple factors). See the AutoExtension class docs for more information. - - Achtung: it is as yet unknown whether auto extensions registered - from one JNIEnv (thread) can be safely called from another. */ public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); @@ -212,9 +209,11 @@ public final class SQLite3Jni { ); - /** A level of indirection required to ensure that the input to the - C-level function of the same name is a NUL-terminated UTF-8 - string. */ + /** + A level of indirection required to ensure that the input to the + C-level function of the same name is a NUL-terminated UTF-8 + string. + */ private static native int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, byte[] paramName ); @@ -226,6 +225,15 @@ public final class SQLite3Jni { return sqlite3_bind_parameter_index(stmt, utf8); } + /** + Works like the C-level sqlite3_bind_text() but (A) assumes + SQLITE_TRANSIENT for the final parameter and (B) behaves like + sqlite3_bind_null() if the data argument is null. + */ + private static native int sqlite3_bind_text( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes + ); + public static int sqlite3_bind_text( @NotNull sqlite3_stmt stmt, int ndx, @Nullable String data ){ @@ -242,15 +250,6 @@ public final class SQLite3Jni { : sqlite3_bind_text(stmt, ndx, data, data.length); } - /** - Works like the C-level sqlite3_bind_text() but (A) assumes - SQLITE_TRANSIENT for the final parameter and (B) behaves like - sqlite3_bind_null() if the data argument is null. - */ - private static native int sqlite3_bind_text( - @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes - ); - public static native int sqlite3_bind_zeroblob( @NotNull sqlite3_stmt stmt, int ndx, int n ); @@ -931,7 +930,8 @@ public final class SQLite3Jni { If maxLength (in bytes, not characters) is larger than text.length, it is silently truncated to text.length. If it is - negative, results are undefined. + negative, results are undefined. If text is null, the following + arguments are ignored. */ private static native void sqlite3_result_text64( @NotNull sqlite3_context cx, @Nullable byte[] text, @@ -939,8 +939,9 @@ public final class SQLite3Jni { ); /** - Cleans up all per-JNIEnv and per-db state managed by the library - then calls the C-native sqlite3_shutdown(). + Cleans up all per-JNIEnv and per-db state managed by the library, + as well as any registered auto-extensions, then calls the + C-native sqlite3_shutdown(). */ public static synchronized native int sqlite3_shutdown(); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 1543a80052..fe8bc542c6 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -87,9 +87,9 @@ public class Tester1 implements Runnable { ++metrics.dbOpen; sqlite3 db = out.take(); if( 0!=rc ){ - final String msg = db.getNativePointer()==0 - ? sqlite3_errstr(rc) - : sqlite3_errmsg(db); + final String msg = + null==db ? sqlite3_errstr(rc) : sqlite3_errmsg(db); + sqlite3_close(db); throw new RuntimeException("Opening db failed: "+msg); } affirm( null == out.get() ); @@ -428,6 +428,7 @@ public class Tester1 implements Runnable { sqlite3_bind_text(stmt, 1, "hell😃"); affirm( "SELECT 'hell😃'".equals(sqlite3_expanded_sql(stmt)) ); sqlite3_finalize(stmt); + sqlite3_close(db); } private void testCollation(){ @@ -500,14 +501,12 @@ public class Tester1 implements Runnable { rc = sqlite3_collation_needed(db, null); affirm( 0 == rc ); sqlite3_close_v2(db); + affirm( 0 == db.getNativePointer() ); affirm(xDestroyCalled.value); } private void testToUtf8(){ /** - Java docs seem contradictory, claiming to use "modified UTF-8" - encoding while also claiming to export using RFC 2279: - https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html Let's ensure that we can convert to standard UTF-8 in Java code @@ -1106,6 +1105,7 @@ public class Tester1 implements Runnable { execSql(db, "ATTACH ':memory' as foo"); affirm( 4==val.value /* ATTACH uses the same connection, not sub-connections. */ ); sqlite3_close(db); + db = null; affirm( sqlite3_cancel_auto_extension(ax) ); affirm( !sqlite3_cancel_auto_extension(ax) ); @@ -1116,7 +1116,7 @@ public class Tester1 implements Runnable { Exception err = null; toss.value = "Throwing from AutoExtension."; try{ - createNewDb(); + sqlite3_close(createNewDb()); }catch(Exception e){ err = e; } @@ -1169,9 +1169,11 @@ public class Tester1 implements Runnable { private void runTests(boolean fromThread) throws Exception { if(false) testCompileOption(); + testToUtf8(); test1(); testOpenDb1(); testOpenDb2(); + testCollation(); testPrepare123(); testBindFetchInt(); testBindFetchInt64(); @@ -1179,8 +1181,6 @@ public class Tester1 implements Runnable { testBindFetchText(); testBindFetchBlob(); testSql(); - testCollation(); - testToUtf8(); testStatus(); testUdf1(); testUdfJavaObject(); diff --git a/manifest b/manifest index 105f01ad91..94ab0dbcd7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sthe\sJNI\sper-thread\scache\sof\sNativePointerHolder\srefs\sinto\sglobal\sspace.\sThis\sallows\sbetter-targeted\smutex\slocks\sand\sincidentally\seliminates\sthe\slagginess\sand\spost-run\shangs\sin\sTester1's\smulti-thread\smode\s(presumably\scaused\sby\sdeadlocks). -D 2023-08-22T15:30:35.368 +C JNI\sinternal\scleanups\sand\scorrect\stwo\sleaked\sdb\shandles\sin\stest\scode. +D 2023-08-22T17:36:59.339 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,10 +235,10 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 30f0926a69edbd9e9932283ec8e4cea02b785f373395f2093dbbc6d65866a196 F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c ad002976687e294936ce8f50b9efb1e531fa1d78a076b8a09089328082b48af4 +F ext/jni/src/c/sqlite3-jni.c b54056176060ef68dba31baaee43ad90a8ac3d4e7a477224377026110bb213ac F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 -F ext/jni/src/org/sqlite/jni/AutoExtension.java 18e83f6f463e306df60b2dceb65247d32af1f78af4bbbae9155411a8c6cdb093 +F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd 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 ad67843b6dd1c06b6b0a1dc72887b7c48e2a98042fcf6cacf14d42444037eab8 @@ -255,8 +255,8 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5c469585946b63592cafe134b01af0b9144a12131f22ea352e12f4c3ec70efb2 -F ext/jni/src/org/sqlite/jni/Tester1.java 63e1e4285a0f050580490323f656809bdadc0f1f28c4454f5cca82a6ccdfaf0f +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 0eea21f1015704e495b1a47aa9c7c90d081f51777981fe3f07760486aed092d8 +F ext/jni/src/org/sqlite/jni/Tester1.java b6be63a8e80c7362073f2a799719315a1459d1eff97cebb944b1309522758de2 F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 7342bf578790e1a87c128a7c1c7745fe2e7c442890370feb160d406597d4d8ec -R 2848048c8acd75d9850655e629905695 +P e209f56a9745695aadc04418c7bebe62b79e38e5aee26c3248a30f73bfa460c2 +R 187f43ad9bb697fc679503d683587e56 U stephan -Z 76a56828e8164766c79107d6b6e04f3f +Z 1a62043be3662d747eb273c051b3d9b0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4a981ac71b..296aadaa87 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e209f56a9745695aadc04418c7bebe62b79e38e5aee26c3248a30f73bfa460c2 \ No newline at end of file +f927a30b5bba35991f472084ebaf02779e84c343a4e84f0efb3df7679ff212f8 \ No newline at end of file From c675add616c98422b3f0ec7f945fddd27a6a1afe Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 17:51:57 +0000 Subject: [PATCH 030/422] Correct JNI binding of sqlite3_shutdown() to clean up all cached JNIEnv objects. FossilOrigin-Name: 02e868690f97ca728b0f2dd018aa79a9d13c85dd85b164caa895d319ae8f3ff5 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 17 ++--------------- ext/jni/src/org/sqlite/jni/Tester1.java | 1 + manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 13 insertions(+), 25 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index de9204fd64..788c072062 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -268,7 +268,7 @@ endif tester-ext: tester-local tester: tester-ext -tests: test tester test-mt +tests: test test-mt tester package.jar.in := $(abspath $(dir.src)/jar.in) CLEAN_FILES += $(package.jar.in) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 8ae78ea3d8..fc96bdd384 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -376,7 +376,7 @@ struct S3JniHook{ /* ** Per-(sqlite3*) state for various JNI bindings. This state is ** allocated as needed, cleaned up in sqlite3_close(_v2)(), and -** recycled when possible. It is freed during sqlite3_shutdown(). +** recycled when possible. */ typedef struct S3JniDb S3JniDb; struct S3JniDb { @@ -1136,19 +1136,6 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, return rv; } -#if 0 -static void S3JniDb_dump(S3JniDb *s){ - MARKER(("S3JniDb->env @ %p\n", s->env)); - MARKER(("S3JniDb->pDb @ %p\n", s->pDb)); - MARKER(("S3JniDb->trace.jObj @ %p\n", s->trace.jObj)); - MARKER(("S3JniDb->progress.jObj @ %p\n", s->progress.jObj)); - MARKER(("S3JniDb->commitHook.jObj @ %p\n", s->commitHook.jObj)); - MARKER(("S3JniDb->rollbackHook.jObj @ %p\n", s->rollbackHook.jObj)); - MARKER(("S3JniDb->busyHandler.jObj @ %p\n", s->busyHandler.jObj)); - MARKER(("S3JniDb->env @ %p\n", s->env)); -} -#endif - /** Returns the S3JniDb object for the given db. @@ -3079,7 +3066,7 @@ JDECL(jint,1shutdown)(JENV_CSELF){ s3jni_reset_auto_extension(env); MUTEX_ENV_ENTER; while( SJG.envCache.aHead ){ - S3JniGlobal_env_uncache( env );//SJG.envCache.aHead->env ); + S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); } MUTEX_ENV_LEAVE; /* Do not clear S3JniGlobal.jvm: it's legal to call diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index fe8bc542c6..9677ca57d1 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1270,6 +1270,7 @@ public class Tester1 implements Runnable { if( doSomethingForDev ){ sqlite3_do_something_for_developer(); } + sqlite3_shutdown(); int nMethods = 0; int nNatives = 0; final java.lang.reflect.Method[] declaredMethods = diff --git a/manifest b/manifest index 94ab0dbcd7..39fe198d94 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\sinternal\scleanups\sand\scorrect\stwo\sleaked\sdb\shandles\sin\stest\scode. -D 2023-08-22T17:36:59.339 +C Correct\sJNI\sbinding\sof\ssqlite3_shutdown()\sto\sclean\sup\sall\scached\sJNIEnv\sobjects. +D 2023-08-22T17:51:57.423 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,10 +232,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 30f0926a69edbd9e9932283ec8e4cea02b785f373395f2093dbbc6d65866a196 +F ext/jni/GNUmakefile 1ccd09095447709ffd7a4f32514fd586512491c6bed06d009bab4294b451ed62 F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c b54056176060ef68dba31baaee43ad90a8ac3d4e7a477224377026110bb213ac +F ext/jni/src/c/sqlite3-jni.c fb2ca8b6f846632b3432096d5533bef8251965dd5ed9f490441293da641a3fb5 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -256,7 +256,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 0eea21f1015704e495b1a47aa9c7c90d081f51777981fe3f07760486aed092d8 -F ext/jni/src/org/sqlite/jni/Tester1.java b6be63a8e80c7362073f2a799719315a1459d1eff97cebb944b1309522758de2 +F ext/jni/src/org/sqlite/jni/Tester1.java e83a5635878cf73b463014b5137ce5c057ee9c5f6d67fbb40496894552785f46 F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 e209f56a9745695aadc04418c7bebe62b79e38e5aee26c3248a30f73bfa460c2 -R 187f43ad9bb697fc679503d683587e56 +P f927a30b5bba35991f472084ebaf02779e84c343a4e84f0efb3df7679ff212f8 +R fd18badbeb7e1f743addadd7405d6449 U stephan -Z 1a62043be3662d747eb273c051b3d9b0 +Z 7d01e74d30abc2fec1f43939a768f2c1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 296aadaa87..3da3391a9e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f927a30b5bba35991f472084ebaf02779e84c343a4e84f0efb3df7679ff212f8 \ No newline at end of file +02e868690f97ca728b0f2dd018aa79a9d13c85dd85b164caa895d319ae8f3ff5 \ No newline at end of file From 87bb103038cc4eee800f9f75f9bcb616a752ce8d Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 18:36:30 +0000 Subject: [PATCH 031/422] Disassociate JNI db handles from the thread that created them, as it's no longer relevant. FossilOrigin-Name: 8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf --- ext/jni/src/c/sqlite3-jni.c | 39 ++------------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 20 +++----- ext/jni/src/org/sqlite/jni/Tester1.java | 55 +++++++++++++--------- manifest | 16 +++---- manifest.uuid | 2 +- 5 files changed, 52 insertions(+), 80 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index fc96bdd384..47269824b5 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -380,10 +380,6 @@ struct S3JniHook{ */ typedef struct S3JniDb S3JniDb; struct S3JniDb { - JNIEnv *env /* Used for cleaning up all dbs owned by a given - ** thread, noting that this ownership is an artificial - ** one imposed by our threading constraints, not by - ** the core library. */; sqlite3 *pDb /* The associated db handle */; jobject jDb /* A global ref of the output object which gets returned from sqlite3_open(_v2)(). We need this in @@ -558,6 +554,9 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal +/* Helpers for working with specific mutexes. */ +#define MUTEX_ENV_ASSERT_LOCKED \ + assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ENV_ASSERT_LOCKER \ assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define MUTEX_ENV_ASSERT_NOTLOCKER \ @@ -604,7 +603,7 @@ static S3JniGlobalType S3JniGlobal = {}; assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) -static void s3jni_oom(JNIEnv * const env){ +static inline void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; } @@ -941,41 +940,15 @@ static void S3JniDb_set_aside(S3JniDb * const s){ //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev)); } } -/** - Cleans up all state in S3JniGlobal.perDb for th given JNIEnv. - Results are undefined if a Java-side db uses the API - from the given JNIEnv after this call. -*/ -static void S3JniDb_free_for_env(JNIEnv *env){ - S3JniDb * ps; - S3JniDb * pNext = 0; - MUTEX_PDB_ENTER; - ps = SJG.perDb.aUsed; - for( ; ps; ps = pNext ){ - pNext = ps->pNext; - if(ps->env == env){ -#ifndef NDEBUG - S3JniDb * const pPrev = ps->pPrev; -#endif - S3JniDb_set_aside(ps); - assert( pPrev ? pPrev->pNext==pNext : 1 ); - assert( ps == SJG.perDb.aFree ); - } - } - MUTEX_PDB_LEAVE; -} /** Uncache any state for the given JNIEnv, clearing all Java references the cache owns. Returns true if env was cached and false if it was not found in the cache. - - Also passes env to S3JniDb_free_for_env() to free up - what would otherwise be stale references. */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; - MUTEX_ENV_ASSERT_LOCKER; + MUTEX_ENV_ASSERT_LOCKED; row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ @@ -992,7 +965,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ assert( !row->pPrev ); SJG.envCache.aHead = row->pNext; } - S3JniDb_free_for_env(env); memset(row, 0, sizeof(S3JniEnv)); row->pNext = SJG.envCache.aFree; if( row->pNext ) row->pNext->pPrev = row; @@ -1130,7 +1102,6 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, } rv->jDb = REF_G(jDb); rv->pDb = pDb; - rv->env = env; } MUTEX_PDB_LEAVE; return rv; diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index cfca4fc77c..22178faed6 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -122,21 +122,13 @@ public final class SQLite3Jni { uncacheJniEnv() when it is done with the library - either right before it terminates or when it is finished using the SQLite API. This will clean up any cached per-JNIEnv info. Calling into the - library again after that "should" re-initialize the cache on - demand, but that's untested. + library will re-initialize the cache on demand. - This call forcibly wipes out all cached information for the - current JNIEnv, a side-effect of which is that behavior is - undefined if any database objects are (A) still active at the - time it is called _and_ (B) calls are subsequently made into the - library with such a database. Doing so will, at best, lead to a - crash. At worst, it will lead to the db possibly misbehaving - because some of its Java-bound state has been cleared. There is - no immediate harm in (A) so long as condition (B) is not met. - This process does _not_ actually close any databases or finalize - any prepared statements. For proper library behavior, and to - avoid C-side leaks, be sure to close them before calling this - function. + This process does _not_ close any databases or finalize + any prepared statements because their ownership does not depend on + a given thread. For proper library behavior, and to + avoid C-side leaks, be sure to finalize all statements and close + all databases before calling this function. Calling this from the main application thread is not strictly required but is "polite." Additional threads must call this diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 9677ca57d1..e010e24703 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -24,6 +24,7 @@ import java.util.concurrent.Future; public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; + private static boolean takeNaps = false; private static final class Metrics { int dbOpen; @@ -1167,32 +1168,38 @@ public class Tester1 implements Runnable { outln("Woke up."); } + private void nap() throws InterruptedException { + if( takeNaps ){ + Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 28), 0); + } + } + private void runTests(boolean fromThread) throws Exception { if(false) testCompileOption(); testToUtf8(); test1(); - testOpenDb1(); - testOpenDb2(); - testCollation(); - testPrepare123(); - testBindFetchInt(); - testBindFetchInt64(); - testBindFetchDouble(); - testBindFetchText(); - testBindFetchBlob(); - testSql(); - testStatus(); - testUdf1(); - testUdfJavaObject(); - testUdfAggregate(); - testUdfWindow(); - testTrace(); - testProgress(); - testCommitHook(); - testRollbackHook(); - testUpdateHook(); - testAuthorizer(); - testAutoExtension(); + nap(); testOpenDb1(); + nap(); testOpenDb2(); + nap(); testCollation(); + nap(); testPrepare123(); + nap(); testBindFetchInt(); + nap(); testBindFetchInt64(); + nap(); testBindFetchDouble(); + nap(); testBindFetchText(); + nap(); testBindFetchBlob(); + nap(); testSql(); + nap(); testStatus(); + nap(); testUdf1(); + nap(); testUdfJavaObject(); + nap(); testUdfAggregate(); + nap(); testUdfWindow(); + nap(); testTrace(); + nap(); testProgress(); + nap(); testCommitHook(); + nap(); testRollbackHook(); + nap(); testUpdateHook(); + nap(); testAuthorizer(); + nap(); testAutoExtension(); if(!fromThread){ testBusy(); if( !mtMode ){ @@ -1227,6 +1234,8 @@ public class Tester1 implements Runnable { nThread = Integer.parseInt(args[i++]); }else if(arg.equals("r") || arg.equals("runs")){ nRepeat = Integer.parseInt(args[i++]); + }else if(arg.equals("naps")){ + takeNaps = true; }else{ throw new IllegalArgumentException("Unhandled flag:"+arg); } @@ -1247,7 +1256,7 @@ public class Tester1 implements Runnable { final ExecutorService ex = Executors.newFixedThreadPool( nThread ); //final List> futures = new ArrayList<>(); ++nLoop; - out(nLoop+" "); + out((1==nLoop ? "" : " ")+nLoop); for( int i = 0; i < nThread; ++i ){ ex.submit( new Tester1(i), i ); } diff --git a/manifest b/manifest index 39fe198d94..71e4b52cfe 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sJNI\sbinding\sof\ssqlite3_shutdown()\sto\sclean\sup\sall\scached\sJNIEnv\sobjects. -D 2023-08-22T17:51:57.423 +C Disassociate\sJNI\sdb\shandles\sfrom\sthe\sthread\sthat\screated\sthem,\sas\sit's\sno\slonger\srelevant. +D 2023-08-22T18:36:30.981 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 1ccd09095447709ffd7a4f32514fd586512491c6bed06d009bab4294b451ed62 F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c fb2ca8b6f846632b3432096d5533bef8251965dd5ed9f490441293da641a3fb5 +F ext/jni/src/c/sqlite3-jni.c 50edc462e8fdf54f9b8ede692a7c865c5e4315930899276664dd6744764d4723 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -255,8 +255,8 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 0eea21f1015704e495b1a47aa9c7c90d081f51777981fe3f07760486aed092d8 -F ext/jni/src/org/sqlite/jni/Tester1.java e83a5635878cf73b463014b5137ce5c057ee9c5f6d67fbb40496894552785f46 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a +F ext/jni/src/org/sqlite/jni/Tester1.java 58a058f718215ff32fbdf8026a2d4eb88f9d7e939a5640d5a944efafdfda4b7c F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 f927a30b5bba35991f472084ebaf02779e84c343a4e84f0efb3df7679ff212f8 -R fd18badbeb7e1f743addadd7405d6449 +P 02e868690f97ca728b0f2dd018aa79a9d13c85dd85b164caa895d319ae8f3ff5 +R 4fc9fbe1829e4bd8b0e17e9933291453 U stephan -Z 7d01e74d30abc2fec1f43939a768f2c1 +Z 9fda786e7e193bbf92304385fbc27c96 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3da3391a9e..cfa2599e0d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -02e868690f97ca728b0f2dd018aa79a9d13c85dd85b164caa895d319ae8f3ff5 \ No newline at end of file +8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf \ No newline at end of file From d1c7216b2f5ac4011780d234df7566d250cf57dc Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 20:10:28 +0000 Subject: [PATCH 032/422] More work on the JNI multi-threaded test runner. FossilOrigin-Name: 9a74ad716bded1e14333bf7c72392916f800d58a96240eabe4988ca5fc9e8752 --- ext/jni/src/c/sqlite3-jni.c | 24 ++-- .../org/sqlite/jni/NativePointerHolder.java | 2 +- ext/jni/src/org/sqlite/jni/Tester1.java | 107 +++++++++++++----- manifest | 16 +-- manifest.uuid | 2 +- 5 files changed, 101 insertions(+), 50 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 47269824b5..2299a4c6c0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -599,8 +599,6 @@ static S3JniGlobalType S3JniGlobal = {}; /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) -#define MUTEX_PDB_ASSERT_LOCKED \ - assert( 0 != SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) static inline void s3jni_oom(JNIEnv * const env){ @@ -903,12 +901,12 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest } /** - Clears s's state and moves it to the free-list. + Clears s's state and moves it to the free-list. Requires that + S3JniGlobal.perDb.mutex be unlocked. */ -static void S3JniDb_set_aside(S3JniDb * const s){ +static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ if(s){ - LocalJniGetEnv; - MUTEX_PDB_ASSERT_LOCKED; + MUTEX_PDB_ENTER; //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); assert(s->pNext != s); @@ -938,6 +936,7 @@ static void S3JniDb_set_aside(S3JniDb * const s){ SJG.perDb.aFree = s; //MARKER(("%p->pPrev@%p, pNext@%p\n", s, s->pPrev, s->pNext)); //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev)); + MUTEX_PDB_LEAVE; } } @@ -1962,10 +1961,8 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ if(ps){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ - MUTEX_PDB_ENTER; - S3JniDb_set_aside(ps) + S3JniDb_set_aside(env, ps) /* MUST come after close() because of ps->trace. */; - MUTEX_PDB_LEAVE; NativePointerHolder_set(env, jDb, 0, &S3NphRefs.sqlite3); } } @@ -2537,9 +2534,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, assert( ps->pDb == *ppDb /* set up via s3jni_run_java_auto_extensions() */); } }else{ - MUTEX_PDB_ENTER; - S3JniDb_set_aside(ps); - MUTEX_PDB_LEAVE; + S3JniDb_set_aside(env, ps); ps = 0; } OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0); @@ -3306,7 +3301,8 @@ JDECL(jbyteArray,1value_1text16be)(JENV_CSELF, jobject jpSVal){ } JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ - MARKER(("\nVarious bits of internal info:\n")); + MARKER(("\nVarious bits of internal info:\n" + "Any metrics here are invalid in multi-thread use.\n")); puts("FTS5 is " #ifdef SQLITE_ENABLE_FTS5 "available" @@ -3338,7 +3334,7 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ "\n\tenv %u" "\n\tnph inits %u" "\n\tperDb %u" - "\n\tautoExt %u container access\n", + "\n\tautoExt %u list accesses\n", SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt); printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n", diff --git a/ext/jni/src/org/sqlite/jni/NativePointerHolder.java b/ext/jni/src/org/sqlite/jni/NativePointerHolder.java index afe2618a00..32aee978df 100644 --- a/ext/jni/src/org/sqlite/jni/NativePointerHolder.java +++ b/ext/jni/src/org/sqlite/jni/NativePointerHolder.java @@ -28,6 +28,6 @@ package org.sqlite.jni; */ public class NativePointerHolder { //! Only set from JNI, where access permissions don't matter. - private long nativePointer = 0; + private volatile long nativePointer = 0; public final long getNativePointer(){ return nativePointer; } } diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index e010e24703..951ded16aa 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -25,7 +25,9 @@ public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; private static boolean takeNaps = false; - + private static boolean shuffle = false; + private static boolean listRunTests = false; + private static List testMethods = null; private static final class Metrics { int dbOpen; } @@ -159,7 +161,7 @@ public class Tester1 implements Runnable { return rv; } - private void testCompileOption(){ + private void showCompileOption(){ int i = 0; String optName; outln("compile options:"); @@ -1175,31 +1177,41 @@ public class Tester1 implements Runnable { } private void runTests(boolean fromThread) throws Exception { - if(false) testCompileOption(); + if(false) showCompileOption(); + List mlist = testMethods; + affirm( null!=mlist ); + if( shuffle ){ + mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); + java.util.Collections.shuffle( + mlist + //java.util.concurrent.ThreadLocalRandom.current() + ); + } + if( listRunTests ){ + synchronized(this.getClass()){ + out("Initial test"," list: "); + for(java.lang.reflect.Method m : testMethods){ + out(m.getName()+" "); + } + outln(); + + out("Running"," tests: "); + for(java.lang.reflect.Method m : mlist){ + out(m.getName()+" "); + } + outln(); + out("(That list excludes some which are hard-coded to run.)\n"); + } + } testToUtf8(); test1(); - nap(); testOpenDb1(); - nap(); testOpenDb2(); - nap(); testCollation(); - nap(); testPrepare123(); - nap(); testBindFetchInt(); - nap(); testBindFetchInt64(); - nap(); testBindFetchDouble(); - nap(); testBindFetchText(); - nap(); testBindFetchBlob(); - nap(); testSql(); - nap(); testStatus(); - nap(); testUdf1(); - nap(); testUdfJavaObject(); - nap(); testUdfAggregate(); - nap(); testUdfWindow(); - nap(); testTrace(); - nap(); testProgress(); - nap(); testCommitHook(); - nap(); testRollbackHook(); - nap(); testUpdateHook(); - nap(); testAuthorizer(); - nap(); testAutoExtension(); + int n = 0; + for(java.lang.reflect.Method m : mlist){ + ++n; + nap(); + m.invoke(this); + } + affirm( n == mlist.size() ); if(!fromThread){ testBusy(); if( !mtMode ){ @@ -1219,6 +1231,27 @@ public class Tester1 implements Runnable { } } + /** + Runs the basic sqlite3 JNI binding sanity-check suite. + + CLI flags: + + -t|-thread N: runs the tests in N threads + concurrently. Default=1. + + -r|-repeat N: repeats the tests in a loop N times, each one + consisting of the -thread value's threads. + + -shuffle: randomizes the order of most of the test functions. + + -naps: sleep small random intervals between tests in order to add + some chaos for cross-thread contention. + + -list-tests: outputs the list of tests being run, minus some + which are hard-coded, + + -v: emit some developer-mode info at the end. + */ public static void main(String[] args) throws Exception { Integer nThread = null; boolean doSomethingForDev = false; @@ -1232,8 +1265,12 @@ public class Tester1 implements Runnable { //listBoundMethods(); }else if(arg.equals("t") || arg.equals("thread")){ nThread = Integer.parseInt(args[i++]); - }else if(arg.equals("r") || arg.equals("runs")){ + }else if(arg.equals("r") || arg.equals("repeat")){ nRepeat = Integer.parseInt(args[i++]); + }else if(arg.equals("shuffle")){ + shuffle = true; + }else if(arg.equals("list-tests")){ + listRunTests = true; }else if(arg.equals("naps")){ takeNaps = true; }else{ @@ -1242,6 +1279,24 @@ public class Tester1 implements Runnable { } } + { + // Build list of tests to run from the methods named test*(). + testMethods = new ArrayList<>(); + final List excludes = new ArrayList<>(); + // Tests we want to control the order of: + excludes.add("testSleep"); + excludes.add("testToUtf8"); + excludes.add("test1"); + excludes.add("testBusy"); + excludes.add("testFts5"); + for(java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ + final String name = m.getName(); + if( name.startsWith("test") && excludes.indexOf(name)<0 ){ + testMethods.add(m); + } + } + } + final long timeStart = System.currentTimeMillis(); int nLoop = 0; outln("libversion_number: ", diff --git a/manifest b/manifest index 71e4b52cfe..eb006ab13a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Disassociate\sJNI\sdb\shandles\sfrom\sthe\sthread\sthat\screated\sthem,\sas\sit's\sno\slonger\srelevant. -D 2023-08-22T18:36:30.981 +C More\swork\son\sthe\sJNI\smulti-threaded\stest\srunner. +D 2023-08-22T20:10:28.237 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 1ccd09095447709ffd7a4f32514fd586512491c6bed06d009bab4294b451ed62 F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 50edc462e8fdf54f9b8ede692a7c865c5e4315930899276664dd6744764d4723 +F ext/jni/src/c/sqlite3-jni.c c38c18875b946a3bdc4eda0b2f19ad53b895118979ec85a630706c1c5575079b F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -249,14 +249,14 @@ F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 10cb2e0eb4dc5cf4241a7ccc0442a 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 -F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 9c5d901cce4f7e57c3d623f4e2476f9f79a8eed6e51b2a603f37866018e040ee +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/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 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a -F ext/jni/src/org/sqlite/jni/Tester1.java 58a058f718215ff32fbdf8026a2d4eb88f9d7e939a5640d5a944efafdfda4b7c +F ext/jni/src/org/sqlite/jni/Tester1.java da8bc65f52d310ae17b372eeaef25726be47d3a2052e8a33ce44606a7dc451d7 F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 02e868690f97ca728b0f2dd018aa79a9d13c85dd85b164caa895d319ae8f3ff5 -R 4fc9fbe1829e4bd8b0e17e9933291453 +P 8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf +R 31a03bd5ee6de6309ec09a8781691d85 U stephan -Z 9fda786e7e193bbf92304385fbc27c96 +Z 973ac484c97d56f1bb0793972fc0262a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cfa2599e0d..0f65722775 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf \ No newline at end of file +9a74ad716bded1e14333bf7c72392916f800d58a96240eabe4988ca5fc9e8752 \ No newline at end of file From 3600976bf1b4224858e16ae513049abb80325067 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 22:13:08 +0000 Subject: [PATCH 033/422] Fix Tester1 so that exceptions triggered via threads are not silently ignored. Disable auto-extension tests in multi-thread mode because concurrent threads rightfully interfere with that. FossilOrigin-Name: 56b2a077ace6e6ad5834e1a597b710f212a5b7d5db5b9a27a41f2aa0f6952c55 --- ext/jni/GNUmakefile | 4 +- ext/jni/src/c/sqlite3-jni.c | 62 ++++++++++++---- ext/jni/src/org/sqlite/jni/Tester1.java | 99 ++++++++++++++++--------- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 120 insertions(+), 63 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 788c072062..0cda6bf2e4 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -166,8 +166,8 @@ SQLITE_OPT = \ -DSQLITE_THREADSAFE=1 \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ - -DSQLITE_C=$(sqlite3.c) -# -DSQLITE_DEBUG + -DSQLITE_C=$(sqlite3.c) \ + -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 2299a4c6c0..f1bc049545 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -450,6 +450,16 @@ struct S3JniAutoExtension { jmethodID midFunc /* xEntryPoint() callback */; }; +/* +** If true, modifying S3JniGlobal.metrics is protected by a mutex, +** else it isn't. +*/ +#ifdef SQLITE_DEBUG +#define S3JNI_METRICS_MUTEX 1 +#else +#define S3JNI_METRICS_MUTEX 0 +#endif + /* ** Global state, e.g. caches and metrics. */ @@ -537,6 +547,10 @@ struct S3JniGlobalType { volatile unsigned nValue; volatile unsigned nInverse; } udf; + unsigned nMetrics /* Total number of mutex-locked metrics increments. */; +#if S3JNI_METRICS_MUTEX + sqlite3_mutex * mutex; +#endif } metrics; /** The list of bound auto-extensions (Java-side: @@ -554,6 +568,18 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal +static void s3jni_incr( volatile unsigned int * const p ){ +#if S3JNI_METRICS_MUTEX + sqlite3_mutex * const m = SJG.metrics.mutex; + sqlite3_mutex_enter(m); + ++SJG.metrics.nMetrics; + ++(*p); + sqlite3_mutex_leave(m); +#else + ++(*p); +#endif +} + /* Helpers for working with specific mutexes. */ #define MUTEX_ENV_ASSERT_LOCKED \ assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) @@ -629,12 +655,12 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ - ++SJG.metrics.envCacheHits; + s3jni_incr( &SJG.metrics.envCacheHits ); MUTEX_ENV_LEAVE; return row; } } - ++SJG.metrics.envCacheMisses; + s3jni_incr( &SJG.metrics.envCacheMisses ); row = SJG.envCache.aFree; if( row ){ assert(!row->pPrev); @@ -642,7 +668,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ if( row->pNext ) row->pNext->pPrev = 0; }else{ row = s3jni_malloc(env, sizeof(S3JniEnv)); - ++SJG.metrics.envCacheAllocs; + s3jni_incr( &SJG.metrics.envCacheAllocs ); } memset(row, 0, sizeof(*row)); row->pNext = SJG.envCache.aHead; @@ -872,7 +898,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ } method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); if(method){ - ++SJG.metrics.nDestroy; + s3jni_incr( &SJG.metrics.nDestroy ); (*env)->CallVoidMethod(env, jObj, method); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("xDestroy() callback"); @@ -1083,13 +1109,13 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->pNext->pPrev = 0; rv->pNext = 0; } - ++SJG.metrics.nPdbRecycled; + s3jni_incr( &SJG.metrics.nPdbRecycled ); }else{ rv = s3jni_malloc(env, sizeof(S3JniDb)); //MARKER(("state@%p for db allocating for db@%p from heap\n", rv, pDb)); if(rv){ memset(rv, 0, sizeof(S3JniDb)); - ++SJG.metrics.nPdbAlloc; + s3jni_incr( &SJG.metrics.nPdbAlloc ); } } if(rv){ @@ -1631,29 +1657,29 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, static void udf_xFunc(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++SJG.metrics.udf.nFunc; + s3jni_incr( &SJG.metrics.udf.nFunc ); udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc"); } static void udf_xStep(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++SJG.metrics.udf.nStep; + s3jni_incr( &SJG.metrics.udf.nStep ); udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep"); } static void udf_xFinal(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++SJG.metrics.udf.nFinal; + s3jni_incr( &SJG.metrics.udf.nFinal ); udf_xFV(cx, s, s->jmidxFinal, "xFinal"); } static void udf_xValue(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++SJG.metrics.udf.nValue; + s3jni_incr( &SJG.metrics.udf.nValue ); udf_xFV(cx, s, s->jmidxValue, "xValue"); } static void udf_xInverse(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); - ++SJG.metrics.udf.nInverse; + s3jni_incr( &SJG.metrics.udf.nInverse ); udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse"); } @@ -3301,8 +3327,7 @@ JDECL(jbyteArray,1value_1text16be)(JENV_CSELF, jobject jpSVal){ } JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ - MARKER(("\nVarious bits of internal info:\n" - "Any metrics here are invalid in multi-thread use.\n")); + MARKER(("\nVarious bits of internal info:\n")); puts("FTS5 is " #ifdef SQLITE_ENABLE_FTS5 "available" @@ -3334,9 +3359,11 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ "\n\tenv %u" "\n\tnph inits %u" "\n\tperDb %u" - "\n\tautoExt %u list accesses\n", + "\n\tautoExt %u list accesses" + "\n\tmetrics %u\n", SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, - SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt); + SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt, + SJG.metrics.nMetrics); printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n", SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb), (unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)), @@ -4295,6 +4322,11 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); OOM_CHECK( SJG.autoExt.mutex ); +#if S3JNI_METRICS_MUTEX + SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + OOM_CHECK( SJG.metrics.mutex ); +#endif + #if 0 /* Just for sanity checking... */ (void)S3JniGlobal_env_cache(env); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 951ded16aa..602134f155 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -28,6 +28,7 @@ public class Tester1 implements Runnable { private static boolean shuffle = false; private static boolean listRunTests = false; private static List testMethods = null; + private static List listErrors = new ArrayList<>(); private static final class Metrics { int dbOpen; } @@ -67,7 +68,7 @@ public class Tester1 implements Runnable { static volatile int affirmCount = 0; public synchronized static void affirm(Boolean v, String comment){ ++affirmCount; - assert( v /* prefer assert over exception if it's enabled because + if( false ) assert( v /* prefer assert over exception if it's enabled because the JNI layer sometimes has to suppress exceptions, so they might be squelched on their way back to the top. */); @@ -1106,7 +1107,7 @@ public class Tester1 implements Runnable { sqlite3 db = createNewDb(); affirm( 4==val.value ); execSql(db, "ATTACH ':memory' as foo"); - affirm( 4==val.value /* ATTACH uses the same connection, not sub-connections. */ ); + affirm( 4==val.value, "ATTACH uses the same connection, not sub-connections." ); sqlite3_close(db); db = null; @@ -1176,55 +1177,62 @@ public class Tester1 implements Runnable { } } + private void testFail(){ + affirm( false, "Intentional failure." ); + } + private void runTests(boolean fromThread) throws Exception { if(false) showCompileOption(); List mlist = testMethods; affirm( null!=mlist ); if( shuffle ){ mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) ); - java.util.Collections.shuffle( - mlist - //java.util.concurrent.ThreadLocalRandom.current() - ); + java.util.Collections.shuffle(mlist); } if( listRunTests ){ synchronized(this.getClass()){ - out("Initial test"," list: "); - for(java.lang.reflect.Method m : testMethods){ - out(m.getName()+" "); + if( !fromThread ){ + out("Initial test"," list: "); + for(java.lang.reflect.Method m : testMethods){ + out(m.getName()+" "); + } + outln(); + outln("(That list excludes some which are hard-coded to run.)"); } - outln(); - out("Running"," tests: "); for(java.lang.reflect.Method m : mlist){ out(m.getName()+" "); } outln(); - out("(That list excludes some which are hard-coded to run.)\n"); } } testToUtf8(); test1(); - int n = 0; for(java.lang.reflect.Method m : mlist){ - ++n; nap(); - m.invoke(this); + try{ + m.invoke(this); + }catch(java.lang.reflect.InvocationTargetException e){ + outln("FAILURE: ",m.getName(),"(): ", e.getCause()); + throw e; + } } - affirm( n == mlist.size() ); - if(!fromThread){ + if( !fromThread ){ testBusy(); if( !mtMode ){ + testAutoExtension() /* threads rightfully muck up these results */; testFts5(); } } } - public void run(){ + public void run() { try { runTests(0!=this.tId); }catch(Exception e){ - throw new RuntimeException(e); + synchronized( listErrors ){ + listErrors.add(e); + } }finally{ affirm( SQLite3Jni.uncacheJniEnv() ); affirm( !SQLite3Jni.uncacheJniEnv() ); @@ -1256,6 +1264,7 @@ public class Tester1 implements Runnable { Integer nThread = null; boolean doSomethingForDev = false; Integer nRepeat = 1; + boolean forceFail = false; for( int i = 0; i < args.length; ){ String arg = args[i++]; if(arg.startsWith("-")){ @@ -1269,8 +1278,13 @@ public class Tester1 implements Runnable { nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; + outln("WARNING: -shuffle mode is known to run ", + "the same number of tests but provide far ", + "lower, unpredictable metrics for unknown reasons."); }else if(arg.equals("list-tests")){ listRunTests = true; + }else if(arg.equals("fail")){ + forceFail = true; }else if(arg.equals("naps")){ takeNaps = true; }else{ @@ -1284,11 +1298,13 @@ public class Tester1 implements Runnable { testMethods = new ArrayList<>(); final List excludes = new ArrayList<>(); // Tests we want to control the order of: - excludes.add("testSleep"); - excludes.add("testToUtf8"); + if( !forceFail ) excludes.add("testFail"); excludes.add("test1"); + excludes.add("testAutoExtension"); excludes.add("testBusy"); excludes.add("testFts5"); + excludes.add("testSleep"); + excludes.add("testToUtf8"); for(java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ final String name = m.getName(); if( name.startsWith("test") && excludes.indexOf(name)<0 ){ @@ -1306,23 +1322,32 @@ public class Tester1 implements Runnable { for( int n = 0; n < nRepeat; ++n ){ if( nThread==null || nThread<=1 ){ new Tester1(0).runTests(false); - }else{ - Tester1.mtMode = true; - final ExecutorService ex = Executors.newFixedThreadPool( nThread ); - //final List> futures = new ArrayList<>(); - ++nLoop; - out((1==nLoop ? "" : " ")+nLoop); - for( int i = 0; i < nThread; ++i ){ - ex.submit( new Tester1(i), i ); - } - ex.shutdown(); - try { - ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS); - ex.shutdownNow(); - } catch (InterruptedException ie) { - ex.shutdownNow(); - Thread.currentThread().interrupt(); + continue; + } + Tester1.mtMode = true; + final ExecutorService ex = Executors.newFixedThreadPool( nThread ); + //final List> futures = new ArrayList<>(); + ++nLoop; + out((1==nLoop ? "" : " ")+nLoop); + for( int i = 0; i < nThread; ++i ){ + ex.submit( new Tester1(i), i ); + } + ex.shutdown(); + try{ + ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS); + ex.shutdownNow(); + }catch (InterruptedException ie){ + ex.shutdownNow(); + Thread.currentThread().interrupt(); + } + if( !listErrors.isEmpty() ){ + outln("TEST ERRORS:"); + Exception err = null; + for( Exception e : listErrors ){ + e.printStackTrace(); + if( null==err ) err = e; } + if( null!=err ) throw err; } } outln(); diff --git a/manifest b/manifest index eb006ab13a..dbd6267c34 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\swork\son\sthe\sJNI\smulti-threaded\stest\srunner. -D 2023-08-22T20:10:28.237 +C Fix\sTester1\sso\sthat\sexceptions\striggered\svia\sthreads\sare\snot\ssilently\signored.\sDisable\sauto-extension\stests\sin\smulti-thread\smode\sbecause\sconcurrent\sthreads\srightfully\sinterfere\swith\sthat. +D 2023-08-22T22:13:08.053 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,10 +232,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 1ccd09095447709ffd7a4f32514fd586512491c6bed06d009bab4294b451ed62 +F ext/jni/GNUmakefile 95dfae98709a8ffd9bb2087fc53239b37a22cad6d7495db77f46ae56835623bc F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c c38c18875b946a3bdc4eda0b2f19ad53b895118979ec85a630706c1c5575079b +F ext/jni/src/c/sqlite3-jni.c 1064441d33cef541c9c9c84fb5093573a0767bb36563e6ea538c355b6148c4c6 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -256,7 +256,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a -F ext/jni/src/org/sqlite/jni/Tester1.java da8bc65f52d310ae17b372eeaef25726be47d3a2052e8a33ce44606a7dc451d7 +F ext/jni/src/org/sqlite/jni/Tester1.java 9e44d27226eea7486c32eaea6789e8505422c9202f328ff0c1473b75f4ebeeb8 F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2092,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 8b78b737e66a399b04e555a8197f63a73198a4105cb2f37ffd5b0e6014302caf -R 31a03bd5ee6de6309ec09a8781691d85 +P 9a74ad716bded1e14333bf7c72392916f800d58a96240eabe4988ca5fc9e8752 +R 1b5653fffe70ebb6615cb201c18f879a U stephan -Z 973ac484c97d56f1bb0793972fc0262a +Z a4cd6dac629b552f49728a69325b689c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0f65722775..ccf7dfc8fb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9a74ad716bded1e14333bf7c72392916f800d58a96240eabe4988ca5fc9e8752 \ No newline at end of file +56b2a077ace6e6ad5834e1a597b710f212a5b7d5db5b9a27a41f2aa0f6952c55 \ No newline at end of file From d53565b4f854a78b4b9c673f18d27f7dd2c4b7f0 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 22 Aug 2023 23:00:44 +0000 Subject: [PATCH 034/422] Minor Tester1.java cleanups. FossilOrigin-Name: 70d936953ba55cfed32efe7e3a9d4b71da9a7ffc8818b6540471e0bf311bc688 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/org/sqlite/jni/Tester1.java | 19 +++++++++++++------ ext/jni/src/org/sqlite/jni/TesterFts5.java | 6 ++---- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 0cda6bf2e4..f1a6c552e9 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -232,7 +232,7 @@ test: $(SQLite3Jni.class) $(sqlite3-jni.dll) test-mt: $(SQLite3Jni.class) $(sqlite3-jni.dll) $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ - org.sqlite.jni.Tester1 -t 7 -r 50 $(test.flags) + org.sqlite.jni.Tester1 -t 7 -r 50 -shuffle $(test.flags) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 602134f155..cc221d1013 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -24,13 +24,19 @@ import java.util.concurrent.Future; public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; + //! True to sleep briefly between tests. private static boolean takeNaps = false; + //! True to shuffle the order of the tests. private static boolean shuffle = false; + //! True to dump the list of to-run tests to stdout. private static boolean listRunTests = false; + //! List of test*() methods to run. private static List testMethods = null; + //! List of exceptions collected by run() private static List listErrors = new ArrayList<>(); private static final class Metrics { - int dbOpen; + //! Number of times createNewDb() (or equivalent) is invoked. + volatile int dbOpen = 0; } private Integer tId; @@ -1173,7 +1179,7 @@ public class Tester1 implements Runnable { private void nap() throws InterruptedException { if( takeNaps ){ - Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 28), 0); + Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 17), 0); } } @@ -1256,7 +1262,10 @@ public class Tester1 implements Runnable { some chaos for cross-thread contention. -list-tests: outputs the list of tests being run, minus some - which are hard-coded, + which are hard-coded. This is noisy in multi-threaded mode. + + -fail: forces an exception to be thrown during the test run. Use + with -shuffle to make its appearance unpredictable. -v: emit some developer-mode info at the end. */ @@ -1278,9 +1287,6 @@ public class Tester1 implements Runnable { nRepeat = Integer.parseInt(args[i++]); }else if(arg.equals("shuffle")){ shuffle = true; - outln("WARNING: -shuffle mode is known to run ", - "the same number of tests but provide far ", - "lower, unpredictable metrics for unknown reasons."); }else if(arg.equals("list-tests")){ listRunTests = true; }else if(arg.equals("fail")){ @@ -1319,6 +1325,7 @@ public class Tester1 implements Runnable { sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); outln("Running ",nRepeat," loop(s) over ",nThread," thread(s)."); + if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ if( nThread==null || nThread<=1 ){ new Tester1(0).runTests(false); diff --git a/ext/jni/src/org/sqlite/jni/TesterFts5.java b/ext/jni/src/org/sqlite/jni/TesterFts5.java index 7edb29dd4d..3856573169 100644 --- a/ext/jni/src/org/sqlite/jni/TesterFts5.java +++ b/ext/jni/src/org/sqlite/jni/TesterFts5.java @@ -78,9 +78,7 @@ public class TesterFts5 { test1(); final int affirmCount = Tester1.affirmCount - oldAffirmCount; final long timeEnd = System.currentTimeMillis(); - outln("FTS5 Tests done. Metrics:"); - outln("\tAssertions checked: ",affirmCount); - outln("\tTotal time = " - +(timeEnd - timeStart)+"ms"); + outln("FTS5 Tests done. Assertions checked = ",affirmCount, + ", Total time = ",(timeEnd - timeStart),"ms"); } } diff --git a/manifest b/manifest index dbd6267c34..29963f490c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sTester1\sso\sthat\sexceptions\striggered\svia\sthreads\sare\snot\ssilently\signored.\sDisable\sauto-extension\stests\sin\smulti-thread\smode\sbecause\sconcurrent\sthreads\srightfully\sinterfere\swith\sthat. -D 2023-08-22T22:13:08.053 +C Minor\sTester1.java\scleanups. +D 2023-08-22T23:00:44.674 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,7 +232,7 @@ 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 95dfae98709a8ffd9bb2087fc53239b37a22cad6d7495db77f46ae56835623bc +F ext/jni/GNUmakefile a38d7c5ad4ab1e209e2a9352ff06add1d9a0bc666351714bfc8858625330c16b F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 F ext/jni/src/c/sqlite3-jni.c 1064441d33cef541c9c9c84fb5093573a0767bb36563e6ea538c355b6148c4c6 @@ -256,8 +256,8 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a -F ext/jni/src/org/sqlite/jni/Tester1.java 9e44d27226eea7486c32eaea6789e8505422c9202f328ff0c1473b75f4ebeeb8 -F ext/jni/src/org/sqlite/jni/TesterFts5.java c729d5b3cb91888b7e2a3a3ef450852f184697df78721574f6c0bf9043e4b84c +F ext/jni/src/org/sqlite/jni/Tester1.java 1a9d9a0ce93aa105dc409273bb50bf10f15e8b546d9ef0922b14e69dc3f14107 +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 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee @@ -2092,8 +2092,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 9a74ad716bded1e14333bf7c72392916f800d58a96240eabe4988ca5fc9e8752 -R 1b5653fffe70ebb6615cb201c18f879a +P 56b2a077ace6e6ad5834e1a597b710f212a5b7d5db5b9a27a41f2aa0f6952c55 +R 786162271678eb8ff4639708ab69b4fa U stephan -Z a4cd6dac629b552f49728a69325b689c +Z 0962b73394fcdbf4989509de32c7e31a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ccf7dfc8fb..767f632dca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -56b2a077ace6e6ad5834e1a597b710f212a5b7d5db5b9a27a41f2aa0f6952c55 \ No newline at end of file +70d936953ba55cfed32efe7e3a9d4b71da9a7ffc8818b6540471e0bf311bc688 \ No newline at end of file From 336bc8a28114364d42987dc1c21f48a6910080f9 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 00:17:28 +0000 Subject: [PATCH 035/422] Improve C-side exception handling from Java-side UDF callbacks. FossilOrigin-Name: aebbc24afb339ed07b7cd767fbc0d25f3e9c3d9bb5ef3d1c10b04b605c7261bc --- ext/jni/README.md | 4 +- ext/jni/src/c/sqlite3-jni.c | 79 ++++++++++++++------- ext/jni/src/org/sqlite/jni/SQLFunction.java | 30 ++++++-- ext/jni/src/org/sqlite/jni/Tester1.java | 55 ++++++++++++-- manifest | 18 ++--- manifest.uuid | 2 +- 6 files changed, 142 insertions(+), 46 deletions(-) diff --git a/ext/jni/README.md b/ext/jni/README.md index 395365292f..5e100c4f07 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -105,7 +105,9 @@ Client-defined callbacks _must never throw 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. +resources. In some cases, callback handlers (see below) are permitted +to throw, in which cases they get translated to C-level result codes +and/or messages. Unwieldy Constructs are Re-mapped diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index f1bc049545..3159116f72 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1577,21 +1577,46 @@ error_oom: return SQLITE_NOMEM; } -static int udf_report_exception(sqlite3_context * cx, - const char *zFuncName, - const char *zFuncType){ - int rc; - char * z = - sqlite3_mprintf("Client-defined function %s.%s() threw. It should " - "not do that.", - zFuncName ? zFuncName : "", zFuncType); - if(z){ - sqlite3_result_error(cx, z, -1); - sqlite3_free(z); - rc = SQLITE_ERROR; +/* +** Must be called immediately after a Java-side UDF callback throws. +** If translateToErr is true then it sets the exception's message in +** the result error using sqlite3_result_error(). If translateToErr is +** false then it emits a warning that the function threw but should +** not do so. In either case, it clears the exception state. +** +** Returns SQLITE_NOMEM if an allocation fails, else SQLITE_ERROR. In +** the latter case it calls sqlite3_result_error_nomem(). +*/ +static int udf_report_exception(JNIEnv * const env, int translateToErr, + sqlite3_context * cx, + const char *zFuncName, const char *zFuncType ){ + jthrowable const ex = (*env)->ExceptionOccurred(env); + int rc = SQLITE_ERROR; + + assert(ex && "This must only be called when a Java exception is pending."); + if( translateToErr ){ + char * zMsg; + char * z; + + EXCEPTION_CLEAR; + zMsg = s3jni_exception_error_msg(env, ex); + z = sqlite3_mprintf("Client-defined SQL function %s.%s() threw: %s", + zFuncName ? zFuncName : "", zFuncType, + zMsg ? zMsg : "Unknown exception" ); + sqlite3_free(zMsg); + if( z ){ + sqlite3_result_error(cx, z, -1); + sqlite3_free(z); + }else{ + sqlite3_result_error_nomem(cx); + rc = SQLITE_NOMEM; + } }else{ - sqlite3_result_error_nomem(cx); - rc = SQLITE_NOMEM; + MARKER(("Client-defined SQL function %s.%s() threw. " + "It should not do that.\n", + zFuncName ? zFuncName : "", zFuncType)); + (*env)->ExceptionDescribe( env ); + EXCEPTION_CLEAR; } return rc; } @@ -1600,24 +1625,25 @@ static int udf_report_exception(sqlite3_context * cx, Sets up the state for calling a Java-side xFunc/xStep/xInverse() UDF, calls it, and returns 0 on success. */ -static int udf_xFSI(sqlite3_context* pCx, int argc, - sqlite3_value** argv, - S3JniUdf * s, +static int udf_xFSI(sqlite3_context* const pCx, int argc, + sqlite3_value** const argv, + S3JniUdf * const s, jmethodID xMethodID, - const char * zFuncType){ + const char * const zFuncType){ LocalJniGetEnv; udf_jargs args = {0,0}; int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); - //MARKER(("%s.%s() pCx = %p\n", s->zFuncName, zFuncType, pCx)); - if(rc) return rc; + //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); + if(rc) return rc; if( UDF_SCALAR != s->type ){ rc = udf_setAggregateContext(env, args.jcx, pCx, 0); } if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv); IFTHREW{ - rc = udf_report_exception(pCx, s->zFuncName, zFuncType); + rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx, + s->zFuncName, zFuncType); } } UNREF_L(args.jcx); @@ -1635,19 +1661,21 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, LocalJniGetEnv; jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; + int const isFinal = 'F'==zFuncType[1]/*xFinal*/; //MARKER(("%s.%s() cx = %p\n", s->zFuncName, zFuncType, cx)); if(!jcx){ - sqlite3_result_error_nomem(cx); + if( isFinal ) sqlite3_result_error_nomem(cx); return SQLITE_NOMEM; } //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); if( UDF_SCALAR != s->type ){ - rc = udf_setAggregateContext(env, jcx, cx, 1); + rc = udf_setAggregateContext(env, jcx, cx, isFinal); } if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); IFTHREW{ - rc = udf_report_exception(cx,s->zFuncName, zFuncType); + rc = udf_report_exception(env, isFinal, cx, s->zFuncName, + zFuncType); } } UNREF_L(jcx); @@ -3599,8 +3627,7 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, (*env)->CallVoidMethod(env, pAux->jObj, pAux->jmid, jFXA, jpFts, jpCx, jArgv); IFTHREW{ - EXCEPTION_CLEAR; - udf_report_exception(pCx, pAux->zFuncName, "xFunction"); + udf_report_exception(env, 1, pCx, pAux->zFuncName, "xFunction"); } UNREF_L(jpFts); UNREF_L(jpCx); diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index 28775608ad..c8e87ff827 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -98,7 +98,11 @@ public abstract class SQLFunction { //! Subclass for creating scalar functions. public static abstract class Scalar extends SQLFunction { - //! As for the xFunc() argument of the C API's sqlite3_create_function() + /** + As for the xFunc() argument of the C API's + sqlite3_create_function(). If this function throws, it is + translated into an sqlite3_result_error(). + */ public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args); /** @@ -116,10 +120,18 @@ public abstract class SQLFunction { */ public static abstract class Aggregate extends SQLFunction { - //! As for the xStep() argument of the C API's sqlite3_create_function() + /** + As for the xStep() argument of the C API's + sqlite3_create_function(). If this function throws, the + exception is not propagated and a warning might be emitted to a + debugging channel. + */ public abstract void xStep(sqlite3_context cx, sqlite3_value[] args); - //! As for the xFinal() argument of the C API's sqlite3_create_function() + /** + As for the xFinal() argument of the C API's sqlite3_create_function(). + If this function throws, it is translated into an sqlite3_result_error(). + */ public abstract void xFinal(sqlite3_context cx); /** @@ -165,10 +177,18 @@ public abstract class SQLFunction { */ public static abstract class Window extends Aggregate { - //! As for the xInverse() argument of the C API's sqlite3_create_window_function() + /** + As for the xInverse() argument of the C API's + sqlite3_create_window_function(). If this function throws, the + exception is not propagated and a warning might be emitted + to a debugging channel. + */ public abstract void xInverse(sqlite3_context cx, sqlite3_value[] args); - //! As for the xValue() argument of the C API's sqlite3_create_window_function() + /** + As for the xValue() argument of the C API's sqlite3_create_window_function(). + See xInverse() for the fate of any exceptions this throws. + */ public abstract void xValue(sqlite3_context cx); } } diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index cc221d1013..c075a2d036 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -158,16 +158,22 @@ public class Tester1 implements Runnable { execSql(db, true, sql); } - static sqlite3_stmt prepare(sqlite3 db, String sql){ + static sqlite3_stmt prepare(sqlite3 db, boolean throwOnError, String sql){ final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); int rc = sqlite3_prepare(db, sql, outStmt); - affirm( 0 == rc ); + if( throwOnError ){ + affirm( 0 == rc ); + } final sqlite3_stmt rv = outStmt.take(); affirm( null == outStmt.get() ); - affirm( 0 != rv.getNativePointer() ); + if( throwOnError ){ + affirm( 0 != rv.getNativePointer() ); + } return rv; } - + static sqlite3_stmt prepare(sqlite3 db, String sql){ + return prepare(db, true, sql); + } private void showCompileOption(){ int i = 0; String optName; @@ -598,6 +604,47 @@ public class Tester1 implements Runnable { affirm( xDestroyCalled.value ); } + private void testUdfThrows(){ + final sqlite3 db = createNewDb(); + final ValueHolder xFuncAccum = new ValueHolder<>(0); + + SQLFunction funcAgg = new SQLFunction.Aggregate(){ + @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ + /** Throwing from here should emit loud noise on stdout or stderr + but the exception is supressed because we have no way to inform + sqlite about it from these callbacks. */ + //throw new RuntimeException("Throwing from an xStep"); + } + @Override public void xFinal(sqlite3_context cx){ + throw new RuntimeException("Throwing from an xFinal"); + } + }; + int rc = sqlite3_create_function(db, "myagg", 1, SQLITE_UTF8, funcAgg); + affirm(0 == rc); + affirm(0 == xFuncAccum.value); + sqlite3_stmt stmt = prepare(db, "SELECT myagg(1)"); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + affirm( 0 != rc ); + affirm( sqlite3_errmsg(db).indexOf("an xFinal") > 0 ); + + SQLFunction funcSc = new SQLFunction.Scalar(){ + @Override public void xFunc(sqlite3_context cx, sqlite3_value[] args){ + throw new RuntimeException("Throwing from an xFunc"); + } + }; + rc = sqlite3_create_function(db, "mysca", 0, SQLITE_UTF8, funcSc); + affirm(0 == rc); + affirm(0 == xFuncAccum.value); + stmt = prepare(db, "SELECT mysca()"); + rc = sqlite3_step(stmt); + sqlite3_finalize(stmt); + affirm( 0 != rc ); + affirm( sqlite3_errmsg(db).indexOf("an xFunc") > 0 ); + + sqlite3_close_v2(db); + } + private void testUdfJavaObject(){ final sqlite3 db = createNewDb(); final ValueHolder testResult = new ValueHolder<>(db); diff --git a/manifest b/manifest index 29963f490c..56dd2871d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Minor\sTester1.java\scleanups. -D 2023-08-22T23:00:44.674 +C Improve\sC-side\sexception\shandling\sfrom\sJava-side\sUDF\scallbacks. +D 2023-08-23T00:17:28.362 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,9 +233,9 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile a38d7c5ad4ab1e209e2a9352ff06add1d9a0bc666351714bfc8858625330c16b -F ext/jni/README.md 975b35173debbbf3a4ab7166e14d2ffa2bacff9b6850414f09cc919805e81ba4 +F ext/jni/README.md ddcc6be0c0d65f1e2fd687de9f40d38c82630fd61f83cc9550773caa19dd8be1 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 1064441d33cef541c9c9c84fb5093573a0767bb36563e6ea538c355b6148c4c6 +F ext/jni/src/c/sqlite3-jni.c 8e7dac68246edec8d85e34104d5c749c544ae10c55b1bbf6eeff116447da7f54 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -254,9 +254,9 @@ F ext/jni/src/org/sqlite/jni/OutputPointer.java 464ea85c3eba673a7b575545f69fcd8a 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 8c1ad92c35bcc1b2f7256cf6e229b31340ed6d1a404d487f0a9adb28ba7fc332 +F ext/jni/src/org/sqlite/jni/SQLFunction.java f697cf2a81c4119f2baf0682af689686f0466f1dd83dba00885f5603e693fe16 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a -F ext/jni/src/org/sqlite/jni/Tester1.java 1a9d9a0ce93aa105dc409273bb50bf10f15e8b546d9ef0922b14e69dc3f14107 +F ext/jni/src/org/sqlite/jni/Tester1.java 93bd76f2294fa2f592395c9d823adb38a9be3846bff00debeff02310f4e9e6be 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 @@ -2092,8 +2092,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 56b2a077ace6e6ad5834e1a597b710f212a5b7d5db5b9a27a41f2aa0f6952c55 -R 786162271678eb8ff4639708ab69b4fa +P 70d936953ba55cfed32efe7e3a9d4b71da9a7ffc8818b6540471e0bf311bc688 +R f4b3990a7d714ecf1a549380689ce9aa U stephan -Z 0962b73394fcdbf4989509de32c7e31a +Z b8494717453324d170d4924e0442cf8e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 767f632dca..d1c49b53b7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -70d936953ba55cfed32efe7e3a9d4b71da9a7ffc8818b6540471e0bf311bc688 \ No newline at end of file +aebbc24afb339ed07b7cd767fbc0d25f3e9c3d9bb5ef3d1c10b04b605c7261bc \ No newline at end of file From 4c8ef3894e98ed3a32418b6fb354b338cbc7cbf2 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 09:05:16 +0000 Subject: [PATCH 036/422] Numerous minor cleanups and code style conformance improvements. FossilOrigin-Name: 6c92d884920e4ace54913fc60ceef6e43a4351f45a4cb3c4a0ed3d29d544a31b --- ext/jni/src/c/sqlite3-jni.c | 621 +++++++++++++++++++----------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 327 insertions(+), 308 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 3159116f72..e6a4b71c30 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -174,9 +174,11 @@ ** checked for exceptions. */ #define IFTHREW if((*env)->ExceptionCheck(env)) -#define EXCEPTION_IGNORE (void)((*env)->ExceptionCheck(env)) #define EXCEPTION_CLEAR (*env)->ExceptionClear(env) #define EXCEPTION_REPORT (*env)->ExceptionDescribe(env) +#define EXCEPTION_IGNORE IFTHREW EXCEPTION_CLEAR +#define EXCEPTION_WARN_IGNORE \ + IFTHREW {EXCEPTION_REPORT; EXCEPTION_CLEAR;}(void)0 #define EXCEPTION_WARN_CALLBACK_THREW(STR) \ MARKER(("WARNING: " STR " MUST NOT THROW.\n")); \ (*env)->ExceptionDescribe(env) @@ -568,17 +570,18 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal -static void s3jni_incr( volatile unsigned int * const p ){ +/* Increments *p, possibly protected by a mutex. */ #if S3JNI_METRICS_MUTEX +static void s3jni_incr( volatile unsigned int * const p ){ sqlite3_mutex * const m = SJG.metrics.mutex; sqlite3_mutex_enter(m); ++SJG.metrics.nMetrics; ++(*p); sqlite3_mutex_leave(m); -#else - ++(*p); -#endif } +#else +#define s3jni_incr(PTR) ++(*(PTR)) +#endif /* Helpers for working with specific mutexes. */ #define MUTEX_ENV_ASSERT_LOCKED \ @@ -694,7 +697,8 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ ** ** Returns err_code. */ -static int s3jni_db_error(sqlite3* const db, int err_code, const char * const zMsg){ +static int s3jni_db_error(sqlite3* const db, int err_code, + const char * const zMsg){ if( db!=0 ){ if( 0==zMsg ){ sqlite3Error(db, err_code); @@ -706,17 +710,23 @@ static int s3jni_db_error(sqlite3* const db, int err_code, const char * const zM return err_code; } -/** - Creates a new jByteArray of length nP, copies p's contents into it, and - returns that byte array. - */ -static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, const unsigned char * const p, int nP){ +/* +** Creates a new jByteArray of length nP, copies p's contents into it, and +** returns that byte array (NULL on OOM). +*/ +static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, + const unsigned char * const p, int nP){ jbyteArray jba = (*env)->NewByteArray(env, (jint)nP); if(jba){ (*env)->SetByteArrayRegion(env, jba, 0, (jint)nP, (const jbyte*)p); } return jba; } + +/* +** Returns the current JNIEnv object. Fails fatally if it cannot find +** it. +*/ static JNIEnv * s3jni_get_env(void){ JNIEnv * env = 0; if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, @@ -726,19 +736,20 @@ static JNIEnv * s3jni_get_env(void){ } return env; } +/* Declares local var env = s3jni_get_env(). */ #define LocalJniGetEnv JNIEnv * const env = s3jni_get_env() -/** - Uses the java.lang.String(byte[],Charset) constructor to create a - new String from UTF-8 string z. n is the number of bytes to - copy. If n<0 then sqlite3Strlen30() is used to calculate it. - - Returns NULL if z is NULL or on OOM, else returns a new jstring - owned by the caller. - - Sidebar: this is a painfully inefficient way to convert from - standard UTF-8 to a Java string, but JNI offers only algorithms for - working with MUTF-8, not UTF-8. +/* +** Uses the java.lang.String(byte[],Charset) constructor to create a +** new String from UTF-8 string z. n is the number of bytes to +** copy. If n<0 then sqlite3Strlen30() is used to calculate it. +** +** Returns NULL if z is NULL or on OOM, else returns a new jstring +** owned by the caller. +** +** Sidebar: this is a painfully inefficient way to convert from +** standard UTF-8 to a Java string, but JNI offers only algorithms for +** working with MUTF-8, not UTF-8. */ static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, const char * const z, int n){ @@ -762,20 +773,20 @@ static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, return rv; } -/** - Converts the given java.lang.String object into a NUL-terminated - UTF-8 C-string by calling jstr.getBytes(StandardCharset.UTF_8). - Returns NULL if jstr is NULL or on allocation error. If jstr is not - NULL and nLen is not NULL then nLen is set to the length of the - returned string, not including the terminating NUL. If jstr is not - NULL and it returns NULL, this indicates an allocation error. In - that case, if nLen is not NULL then it is either set to 0 (if - fetching of jstr's bytes fails to allocate) or set to what would - have been the length of the string had C-string allocation - succeeded. - - The returned memory is allocated from sqlite3_malloc() and - ownership is transferred to the caller. +/* +** Converts the given java.lang.String object into a NUL-terminated +** UTF-8 C-string by calling jstr.getBytes(StandardCharset.UTF_8). +** Returns NULL if jstr is NULL or on allocation error. If jstr is not +** NULL and nLen is not NULL then nLen is set to the length of the +** returned string, not including the terminating NUL. If jstr is not +** NULL and it returns NULL, this indicates an allocation error. In +** that case, if nLen is not NULL then it is either set to 0 (if +** fetching of jstr's bytes fails to allocate) or set to what would +** have been the length of the string had C-string allocation +** succeeded. +** +** The returned memory is allocated from sqlite3_malloc() and +** ownership is transferred to the caller. */ static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, jstring jstr, int *nLen){ @@ -804,12 +815,12 @@ static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, return rv; } -/** - Expects to be passed a pointer from sqlite3_column_text16() or - sqlite3_value_text16() and a byte-length value from - sqlite3_column_bytes16() or sqlite3_value_bytes16(). It creates a - Java String of exactly half that character length, returning NULL - if !p or (*env)->NewString() fails. +/* +** Expects to be passed a pointer from sqlite3_column_text16() or +** sqlite3_value_text16() and a byte-length value from +** sqlite3_column_bytes16() or sqlite3_value_bytes16(). It creates a +** Java String of exactly half that character length, returning NULL +** if !p or (*env)->NewString() fails. */ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){ return p @@ -817,19 +828,18 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, : NULL; } -/** - Requires jx to be a Throwable. Calls its toString() method and - returns its value converted to a UTF-8 string. The caller owns the - returned string and must eventually sqlite3_free() it. Returns 0 - if there is a problem fetching the info or on OOM. - - Design note: we use toString() instead of getMessage() because the - former includes the exception type's name: - - Exception e = new RuntimeException("Hi"); - System.out.println(e.toString()); // java.lang.RuntimeException: Hi - System.out.println(e.getMessage()); // Hi - } +/* +** Requires jx to be a Throwable. Calls its toString() method and +** returns its value converted to a UTF-8 string. The caller owns the +** returned string and must eventually sqlite3_free() it. Returns 0 +** if there is a problem fetching the info or on OOM. +** +** Design note: we use toString() instead of getMessage() because the +** former includes the exception type's name: +** +** Exception e = new RuntimeException("Hi"); +** System.out.println(e.toString()); // java.lang.RuntimeException: Hi +** System.out.println(e.getMessage()); // Hi */ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ S3JniEnv * const jc = S3JniGlobal_env_cache(env); @@ -854,17 +864,17 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ return zMsg; } -/** - Extracts the current JNI exception, sets ps->pDb's error message to - its message string, and clears the exception. If errCode is non-0, - it is used as-is, else SQLITE_ERROR is assumed. If there's a - problem extracting the exception's message, it's treated as - non-fatal and zDfltMsg is used in its place. - - This must only be called if a JNI exception is pending. - - Returns errCode unless it is 0, in which case SQLITE_ERROR is - returned. +/* +** Extracts the current JNI exception, sets ps->pDb's error message to +** its message string, and clears the exception. If errCode is non-0, +** it is used as-is, else SQLITE_ERROR is assumed. If there's a +** problem extracting the exception's message, it's treated as +** non-fatal and zDfltMsg is used in its place. +** +** This must only be called if a JNI exception is pending. +** +** Returns errCode unless it is 0, in which case SQLITE_ERROR is +** returned. */ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, int errCode, const char *zDfltMsg){ @@ -882,12 +892,12 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, return errCode; } -/** - Extracts the (void xDestroy()) method from the given jclass and - applies it to jobj. If jObj is NULL, this is a no-op. If klazz is - NULL then it's derived from jobj. The lack of an xDestroy() method - is silently ignored and any exceptions thrown by the method trigger - a warning to stdout or stderr and then the exception is suppressed. +/* +** Extracts the (void xDestroy()) method from the given jclass and +** applies it to jobj. If jObj is NULL, this is a no-op. If klazz is +** NULL then it's derived from jobj. The lack of an xDestroy() method +** is silently ignored and any exceptions thrown by xDestroy() trigger +** a warning to stdout or stderr and then the exception is suppressed. */ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ if(jObj){ @@ -910,12 +920,12 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ } } -/** - Removes any Java references from s and clears its state. If - doXDestroy is true and s->klazz and s->jObj are not NULL, s->jObj's - s is passed to s3jni_call_xDestroy() before any references are - cleared. It is legal to call this when the object has no Java - references. +/* +** Removes any Java references from s and clears its state. If +** doXDestroy is true and s->klazz and s->jObj are not NULL, s->jObj's +** s is passed to s3jni_call_xDestroy() before any references are +** cleared. It is legal to call this when the object has no Java +** references. */ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDestroy){ if(doXDestroy && s->klazz && s->jObj){ @@ -926,14 +936,12 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest memset(s, 0, sizeof(*s)); } -/** - Clears s's state and moves it to the free-list. Requires that - S3JniGlobal.perDb.mutex be unlocked. +/* +** Clears s's state and moves it to the free-list. */ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ if(s){ MUTEX_PDB_ENTER; - //MARKER(("state@%p for db@%p setting aside\n", s, s->pDb)); assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); @@ -960,16 +968,14 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; - //MARKER(("%p->pPrev@%p, pNext@%p\n", s, s->pPrev, s->pNext)); - //if(s->pNext) MARKER(("next: %p->pPrev@%p\n", s->pNext, s->pNext->pPrev)); MUTEX_PDB_LEAVE; } } -/** - Uncache any state for the given JNIEnv, clearing all Java - references the cache owns. Returns true if env was cached and false - if it was not found in the cache. +/* +** Uncache any state for the given JNIEnv, clearing all Java +** references the cache owns. Returns true if env was cached and false +** if it was not found in the cache. */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; @@ -983,7 +989,6 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ if( !row ){ return 0; } - //MARKER(("Uncaching JNIEnv@%p\n", env)); if( row->pNext ) row->pNext->pPrev = row->pPrev; if( row->pPrev ) row->pPrev->pNext = row->pNext; if( SJG.envCache.aHead == row ){ @@ -1001,15 +1006,16 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ ** Searches the NativePointerHolder cache for the given combination of ** args. It returns a cache entry with its klazz member set. ** -** It is up to the caller to populate the other members of the returned -** object if needed. +** It is up to the caller to populate the other members of the +** returned object if needed, taking care to lock the population with +** MUTEX_NPH_ENTER/LEAVE. ** ** This simple cache catches >99% of searches in the current ** (2023-07-31) tests. */ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* pRef){ /** - According to: + According to: https://developer.ibm.com/articles/j-jni/ @@ -1022,10 +1028,6 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* you should look them up once and then reuse them. Similarly, looking up class objects can be expensive, so they should be cached as well. - - Reminder: we do not need a mutex for the envRow->nph cache - because all nph entries are per-thread and envCache.mutex already - guards the fetching of envRow. */ S3JniNphClass * const pNC = &SJG.nph[pRef->index]; if( !pNC->pRef ){ @@ -1041,9 +1043,9 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* return pNC; } -/** - Returns the ID of the "nativePointer" field from the given - NativePointerHolder class. +/* +** Returns the ID of the "nativePointer" field from the given +** NativePointerHolder class. */ static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); @@ -1058,10 +1060,10 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* return pNC->fidValue; } -/** - Sets a native ptr value in NativePointerHolder object ppOut. - zClassName must be a static string so we can use its address - as a cache key. +/* +** Sets a native ptr value in NativePointerHolder object ppOut. +** zClassName must be a static string so we can use its address +** as a cache key. */ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, S3NphRef const* pRef){ @@ -1070,10 +1072,10 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, EXCEPTION_IS_FATAL("Could not set NativePointerHolder.nativePointer."); } -/** - 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. +/* +** 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){ if( pObj ){ @@ -1087,17 +1089,17 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const } } -/** - Extracts the new S3JniDb instance from the free-list, or allocates - one if needed, associats it with pDb, and returns. Returns NULL on - OOM. pDb MUST, on success of the calling operation, subsequently be - associated with jDb via NativePointerHolder_set(). +/* +** Extracts the new S3JniDb instance from the free-list, or allocates +** one if needed, associats it with pDb, and returns. Returns NULL on +** OOM. pDb MUST, on success of the calling operation, subsequently be +** associated with jDb via NativePointerHolder_set(). */ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; MUTEX_PDB_ENTER; - if(SJG.perDb.aFree){ + if( SJG.perDb.aFree ){ rv = SJG.perDb.aFree; SJG.perDb.aFree = rv->pNext; assert(rv->pNext != rv); @@ -1112,13 +1114,12 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, s3jni_incr( &SJG.metrics.nPdbRecycled ); }else{ rv = s3jni_malloc(env, sizeof(S3JniDb)); - //MARKER(("state@%p for db allocating for db@%p from heap\n", rv, pDb)); - if(rv){ + if( rv ){ memset(rv, 0, sizeof(S3JniDb)); s3jni_incr( &SJG.metrics.nPdbAlloc ); } } - if(rv){ + if( rv ){ rv->pNext = SJG.perDb.aUsed; SJG.perDb.aUsed = rv; if(rv->pNext){ @@ -1132,16 +1133,16 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, return rv; } -/** - Returns the S3JniDb object for the given db. - - The 3rd argument should normally only be non-0 for routines which - are called from the C library and pass a native db handle instead of - a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 - object, from which the db is fished out. - - Returns NULL if jDb and pDb are both NULL or if there is no - matching S3JniDb entry for pDb or the pointer fished out of jDb. +/* +** Returns the S3JniDb object for the given db. +** +** The 3rd argument should normally only be non-0 for routines which +** are called from the C library and pass a native db handle instead of +** a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 +** object, from which the db is fished out. +** +** Returns NULL if jDb and pDb are both NULL or if there is no +** matching S3JniDb entry for pDb or the pointer fished out of jDb. */ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; @@ -1162,8 +1163,8 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ return s; } -/** - Unref any Java-side state in ax. +/* +** Unref any Java-side state in ax and zero out ax. */ static void S3JniAutoExtension_clear(JNIEnv * const env, S3JniAutoExtension * const ax){ @@ -1173,31 +1174,27 @@ static void S3JniAutoExtension_clear(JNIEnv * const env, } } -/** - Initializes a pre-allocated S3JniAutoExtension object. Returns - non-0 if there is an error collecting the required state from - jAutoExt (which must be an AutoExtension object). On error, it - passes ax to S3JniAutoExtension_clear(). +/* +** Initializes a pre-allocated S3JniAutoExtension object. Returns +** non-0 if there is an error collecting the required state from +** jAutoExt (which must be an AutoExtension object). On error, it +** passes ax to S3JniAutoExtension_clear(). */ static int S3JniAutoExtension_init(JNIEnv *const env, S3JniAutoExtension * const ax, jobject const jAutoExt){ jclass klazz; klazz = (*env)->GetObjectClass(env, jAutoExt); - IFTHREW{ - EXCEPTION_REPORT; - EXCEPTION_CLEAR; - assert(!klazz); - } if(!klazz){ S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", "(Lorg/sqlite/jni/sqlite3;)I"); - //UNREF_L(klazz); + EXCEPTION_WARN_IGNORE; + UNREF_L(klazz); if(!ax->midFunc){ - MARKER(("Error getting xEntryPoint(sqlite3) from object.")); + MARKER(("Error getting xEntryPoint(sqlite3) from AutoExtension object.")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } @@ -1205,23 +1202,23 @@ static int S3JniAutoExtension_init(JNIEnv *const env, return 0; } -/** - Requires that jCx be a Java-side sqlite3_context wrapper for pCx. - This function calls sqlite3_aggregate_context() to allocate a tiny - sliver of memory, the address of which is set in - jCx->aggregateContext. The memory is only used as a key for - mapping client-side results of aggregate result sets across - calls to the UDF's callbacks. - - isFinal must be 1 for xFinal() calls and 0 for all others, the - difference being that the xFinal() invocation will not allocate - new memory if it was not already, resulting in a value of 0 - for jCx->aggregateContext. - - Returns 0 on success. Returns SQLITE_NOMEM on allocation error, - noting that it will not allocate when isFinal is true. It returns - SQLITE_ERROR if there's a serious internal error in dealing with - the JNI state. +/* +** Requires that jCx be a Java-side sqlite3_context wrapper for pCx. +** This function calls sqlite3_aggregate_context() to allocate a tiny +** sliver of memory, the address of which is set in +** jCx->aggregateContext. The memory is only used as a key for +** mapping client-side results of aggregate result sets across +** calls to the UDF's callbacks. +** +** isFinal must be 1 for xFinal() calls and 0 for all others, the +** difference being that the xFinal() invocation will not allocate +** new memory if it was not already, resulting in a value of 0 +** for jCx->aggregateContext. +** +** Returns 0 on success. Returns SQLITE_NOMEM on allocation error, +** noting that it will not allocate when isFinal is true. It returns +** SQLITE_ERROR if there's a serious internal error in dealing with +** the JNI state. */ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, sqlite3_context * pCx, @@ -1249,18 +1246,18 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, return rc; } -/** - Common init for OutputPointer_set_Int32() and friends. pRef must be - a pointer from S3NphRefs. jOut must be an instance of that - class. If necessary, this fetches the jfieldID for jOut's [value] - property, which must be of the type represented by the JNI type - signature zTypeSig, and stores it in pRef's S3JniGlobal.nph entry. - Fails fatally if the property is not found, as that presents a - serious internal misuse. - - Property lookups are cached on a per-pRef basis. Do not use this - routine with the same pRef but different zTypeSig: it will - misbehave. +/* +** Common init for OutputPointer_set_Int32() and friends. pRef must be +** a pointer from S3NphRefs. jOut must be an instance of that +** class. If necessary, this fetches the jfieldID for jOut's [value] +** property, which must be of the type represented by the JNI type +** signature zTypeSig, and stores it in pRef's S3JniGlobal.nph entry. +** Fails fatally if the property is not found, as that presents a +** serious internal misuse. +** +** Property lookups are cached on a per-pRef basis. Do not use this +** routine with the same pRef but different zTypeSig: it will +** misbehave. */ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, const char * const zTypeSig, @@ -1277,8 +1274,10 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, return pNC->fidValue; } -/* Sets the value property of the OutputPointer.Int32 jOut object - to v. */ +/* +** Sets the value property of the OutputPointer.Int32 jOut object to +** v. +*/ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_Int32, "I", jOut @@ -1287,8 +1286,10 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value"); } -/* Sets the value property of the OutputPointer.Int64 jOut object - to v. */ +/* +** Sets the value property of the OutputPointer.Int64 jOut object to +** v. +*/ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_Int64, "J", jOut @@ -1297,6 +1298,10 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value"); } +/* +** Sets the value property of the OutputPointer.sqlite3 jOut object to +** v. +*/ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ jfieldID const setter = setupOutputPointer( @@ -1306,6 +1311,10 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value"); } +/* +** Sets the value property of the OutputPointer.sqlite3_stmt jOut object to +** v. +*/ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, jobject jStmt){ jfieldID const setter = setupOutputPointer( @@ -1318,8 +1327,10 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu #ifdef SQLITE_ENABLE_FTS5 #if 0 -/* Sets the value property of the OutputPointer.ByteArray jOut object - to v. */ +/* +** Sets the value property of the OutputPointer.ByteArray jOut object +** to v. +*/ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, jbyteArray const v){ jfieldID const setter = setupOutputPointer( @@ -1330,8 +1341,10 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, } #endif -/* Sets the value property of the OutputPointer.String jOut object - to v. */ +/* +** Sets the value property of the OutputPointer.String jOut object to +** v. +*/ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, jstring const v){ jfieldID const setter = setupOutputPointer( @@ -1342,6 +1355,10 @@ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, } #endif /* SQLITE_ENABLE_FTS5 */ +/* +** Returns true if eTextRep is a valid sqlite3 encoding constant, else +** returns false. +*/ static int encodingTypeIsValid(int eTextRep){ switch(eTextRep){ case SQLITE_UTF8: case SQLITE_UTF16: @@ -1352,6 +1369,9 @@ static int encodingTypeIsValid(int eTextRep){ } } +/* +** Proxy for Java-side Collation.xCompare() callbacks. +*/ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, int nRhs, const void *rhs){ S3JniDb * const ps = pArg; @@ -1359,11 +1379,10 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, jint rc = 0; jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; - //MARKER(("native xCompare nLhs=%d nRhs=%d\n", nLhs, nRhs)); if(!jbaRhs){ + UNREF_L(jbaLhs); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; - //(*env)->FatalError(env, "Out of memory. Cannot allocate arrays for collation."); } (*env)->SetByteArrayRegion(env, jbaLhs, 0, (jint)nLhs, (const jbyte*)lhs); (*env)->SetByteArrayRegion(env, jbaRhs, 0, (jint)nRhs, (const jbyte*)rhs); @@ -1386,7 +1405,8 @@ static void CollationState_xDestroy(void *pArg){ ** sqlite3_value_java_object(). ** ** TODO: this middle-man struct is no longer necessary. Conider -** removing it and passing around jObj itself instead. +** removing it and passing around jObj itself instead. OTOH, we might +** find more state to pack in here. */ typedef struct { jobject jObj; @@ -1395,6 +1415,11 @@ typedef struct { /* For use with sqlite3_result/value_pointer() */ #define ResultJavaValuePtrStr "ResultJavaVal" +/* +** Allocate a new ResultJavaVal and assign it a new global ref of +** jObj. Caller owns the returned object and must eventually pass it +** to ResultJavaVal_finalizer(). +*/ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); if(rv){ @@ -1403,6 +1428,10 @@ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ return rv; } +/* +** If v is not NULL, it must point to a a ResultJavaVal object. Its +** object reference is relinquished and v is freed. +*/ static void ResultJavaVal_finalizer(void *v){ if(v){ ResultJavaVal * const rv = (ResultJavaVal*)v; @@ -1414,16 +1443,16 @@ static void ResultJavaVal_finalizer(void *v){ -/** - Returns a new Java instance of the class named by zClassName, which - MUST be interface-compatible with NativePointerHolder and MUST have - a no-arg constructor. The NativePointerHolder_set() method is - passed the new Java object and pNative. Hypothetically returns NULL - if Java fails to allocate, but the JNI docs are not entirely clear - on that detail. - - Always use an static pointer from the S3NphRefs struct for the 2nd - argument so that we can use its address as a cache key. +/* +** Returns a new Java instance of the class named by zClassName, which +** MUST be interface-compatible with NativePointerHolder and MUST have +** a no-arg constructor. The NativePointerHolder_set() method is +** passed the new Java object and pNative. Hypothetically returns NULL +** if Java fails to allocate, but the JNI docs are not entirely clear +** on that detail. +** +** Always use a static pointer from the S3NphRefs struct for the 2nd +** argument so that we can use pRef->index as an O(1) cache key. */ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef, const void * pNative){ @@ -1457,6 +1486,9 @@ static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_valu return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_value, sv); } +/* +** Type IDs for SQL function categories. +*/ enum UDFType { UDF_SCALAR = 1, UDF_AGGREGATE, @@ -1621,9 +1653,9 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, return rc; } -/** - Sets up the state for calling a Java-side xFunc/xStep/xInverse() - UDF, calls it, and returns 0 on success. +/* +** Sets up the state for calling a Java-side xFunc/xStep/xInverse() +** UDF, calls it, and returns 0 on success. */ static int udf_xFSI(sqlite3_context* const pCx, int argc, sqlite3_value** const argv, @@ -1651,9 +1683,9 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, return rc; } -/** - Sets up the state for calling a Java-side xFinal/xValue() UDF, - calls it, and returns 0 on success. +/* +** Sets up the state for calling a Java-side xFinal/xValue() UDF, +** calls it, and returns 0 on success. */ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jmethodID xMethodID, @@ -1682,28 +1714,33 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, return rc; } +/* Proxy for C-to-Java xFunc. */ static void udf_xFunc(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); s3jni_incr( &SJG.metrics.udf.nFunc ); udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc"); } +/* Proxy for C-to-Java xStep. */ static void udf_xStep(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); s3jni_incr( &SJG.metrics.udf.nStep ); udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep"); } +/* Proxy for C-to-Java xFinal. */ static void udf_xFinal(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); s3jni_incr( &SJG.metrics.udf.nFinal ); udf_xFV(cx, s, s->jmidxFinal, "xFinal"); } +/* Proxy for C-to-Java xValue. */ static void udf_xValue(sqlite3_context* cx){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); s3jni_incr( &SJG.metrics.udf.nValue ); udf_xFV(cx, s, s->jmidxValue, "xValue"); } +/* Proxy for C-to-Java xInverse. */ static void udf_xInverse(sqlite3_context* cx, int argc, sqlite3_value** argv){ S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); @@ -1714,8 +1751,8 @@ static void udf_xInverse(sqlite3_context* cx, int argc, //////////////////////////////////////////////////////////////////////// // What follows is the JNI/C bindings. They are in alphabetical order -// except for this macro-generated subset which are kept together here -// at the front... +// except for this macro-generated subset which are kept together +// (alphabetized) here at the front... //////////////////////////////////////////////////////////////////////// WRAP_INT_STMT(1bind_1parameter_1count, sqlite3_bind_parameter_count) WRAP_INT_DB(1changes, sqlite3_changes) @@ -1767,7 +1804,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, return SQLITE_ERROR; } jc->pdbOpening = 0; - //MARKER(("auto-extension on open()ing ps@%p db@%p\n", ps, pDb)); assert( !ps->pDb && "it's still being opened" ); ps->pDb = pDb; assert( ps->jDb ); @@ -1787,14 +1823,13 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, } MUTEX_EXT_LEAVE; if( ax.jObj ){ - //MARKER(("Running auto-ext #%d from env@%p\n", i, env)); rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); IFTHREW { jthrowable const ex = (*env)->ExceptionOccurred(env); char * zMsg; EXCEPTION_CLEAR; zMsg = s3jni_exception_error_msg(env, ex); - //UNREF_L(ex); + UNREF_L(ex); *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); sqlite3_free(zMsg); if( !rc ) rc = SQLITE_ERROR; @@ -1925,6 +1960,7 @@ JDECL(jint,1bind_1zeroblob64)(JENV_CSELF, jobject jpStmt, return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); } +/* Central C-to-Java busy handler proxy. */ static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; int rc = 0; @@ -1933,9 +1969,9 @@ static int s3jni_busy_handler(void* pState, int n){ rc = (*env)->CallIntMethod(env, ps->busyHandler.jObj, ps->busyHandler.midCallback, (jint)n); IFTHREW{ - EXCEPTION_WARN_CALLBACK_THREW("busy-handler callback"); - EXCEPTION_CLEAR; - rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "busy-handle callback threw."); + EXCEPTION_WARN_CALLBACK_THREW("sqlite3_busy_handler() callback"); + rc = s3jni_db_exception(env, ps, SQLITE_ERROR, + "sqlite3_busy_handler() callback threw."); } } return rc; @@ -2004,9 +2040,7 @@ JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ } -/** - Wrapper for sqlite3_close(_v2)(). -*/ +/* Wrapper for sqlite3_close(_v2)(). */ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ int rc = 0; S3JniDb * ps = 0; @@ -2031,9 +2065,9 @@ JDECL(jint,1close)(JENV_CSELF, jobject pDb){ return s3jni_close_db(env, pDb, 1); } -/** - Assumes z is an array of unsigned short and returns the index in - that array of the first element with the value 0. +/* +** Assumes z is an array of unsigned short and returns the index in +** that array of the first element with the value 0. */ static unsigned int s3jni_utf16_strlen(void const * z){ unsigned int i = 0; @@ -2042,9 +2076,7 @@ static unsigned int s3jni_utf16_strlen(void const * z){ return i; } -/** - sqlite3_collation_needed16() hook impl. - */ +/* Central C-to-Java sqlite3_collation_needed16() hook impl. */ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, int eTextRep, const void * z16Name){ S3JniDb * const ps = pState; @@ -2059,10 +2091,10 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, ps->collationNeeded.midCallback, ps->jDb, (jint)eTextRep, jName); IFTHREW{ - s3jni_db_exception(env, ps, 0, "collation-needed callback threw"); + s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); } + UNREF_L(jName); } - UNREF_L(jName); } JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ @@ -2281,8 +2313,8 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, return (jint)rc; } -static jint create_function(JNIEnv * env, jobject jDb, jstring jFuncName, - jint nArg, jint eTextRep, jobject jFunctor){ +JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, + jint nArg, jint eTextRep, jobject jFunctor){ S3JniUdf * s = 0; int rc; sqlite3 * const pDb = PtrGet_sqlite3(jDb); @@ -2290,7 +2322,7 @@ static jint create_function(JNIEnv * env, jobject jDb, jstring jFuncName, if( !encodingTypeIsValid(eTextRep) ){ return s3jni_db_error(pDb, SQLITE_FORMAT, - "Invalid function encoding option."); + "Invalid function encoding option."); } s = S3JniUdf_alloc(env, jFunctor); if( !s ) return SQLITE_NOMEM; @@ -2327,20 +2359,16 @@ static jint create_function(JNIEnv * env, jobject jDb, jstring jFuncName, if( 0==rc ){ s->zFuncName = sqlite3_mprintf("%s", zFuncName) /* OOM here is non-fatal. Ignore it. Handling it would require - re-calling the appropriate create_function() func with 0 - for all xAbc args so that s would be finalized. */; + ** re-calling the appropriate create_function() func with 0 + ** for all xAbc args so that s would be finalized. */; } error_cleanup: JSTR_RELEASE(jFuncName, zFuncName); - /* on create_function() error, s will be destroyed via create_function() */ + /* on sqlite3_create_function() error, s will be destroyed via + ** create_function(), so we're not leaking s. */ return (jint)rc; } -JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, - jint nArg, jint eTextRep, jobject jFunctor){ - return create_function(env, jDb, jFuncName, nArg, eTextRep, jFunctor); -} - /* sqlite3_db_config() for (int,const char *) */ JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( JENV_CSELF, jobject jDb, jint op, jstring jStr @@ -2371,8 +2399,11 @@ JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( } /* sqlite3_db_config() for (int,int*) */ -/* ACHTUNG: openjdk v19 creates a different mangled name for this - function than openjdk v8 does. */ +/* +** ACHTUNG: openjdk v19 creates a different mangled name for this +** function than openjdk v8 does. We account for that by exporting +** both versions of the name. +*/ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut ){ @@ -2410,11 +2441,11 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer return (jint)rc; } -/** - This is a workaround for openjdk v19 (and possibly others) encoding - this function's name differently than JDK v8 does. If we do not - install both names for this function then Java will not be able to - find the function in both environments. +/* +** This is a workaround for openjdk v19 (and possibly others) encoding +** this function's name differently than JDK v8 does. If we do not +** install both names for this function then Java will not be able to +** find the function in both environments. */ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_00024Int32_2)( JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut @@ -2472,8 +2503,8 @@ JDECL(jstring,1errmsg)(JENV_CSELF, jobject jpDb){ JDECL(jstring,1errstr)(JENV_CSELF, jint rcCode){ return (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode)) - /* We know these values to be plain ASCII, so pose no - MUTF-8 incompatibility */; + /* We know these values to be plain ASCII, so pose no MUTF-8 + ** incompatibility */; } JDECL(jstring,1expanded_1sql)(JENV_CSELF, jobject jpStmt){ @@ -2564,16 +2595,16 @@ end: return rc; } -/** - Post-open() code common to both the sqlite3_open() and - sqlite3_open_v2() bindings. ps->jDb must be the - org.sqlite.jni.sqlite3 object which will hold the db's native - pointer. theRc must be the result code of the open() op. If - *ppDb is NULL then ps is set aside and its state cleared, - else ps is associated with *ppDb. If *ppDb is not NULL then - ps->jDb is stored in jOut (an OutputPointer.sqlite3 instance). - - Returns theRc. +/* +** Post-open() code common to both the sqlite3_open() and +** sqlite3_open_v2() bindings. ps->jDb must be the +** org.sqlite.jni.sqlite3 object which will hold the db's native +** pointer. theRc must be the result code of the open() op. If +** *ppDb is NULL then ps is set aside and its state cleared, +** else ps is associated with *ppDb. If *ppDb is not NULL then +** ps->jDb is stored in jOut (an OutputPointer.sqlite3 instance). +** +** Returns theRc. */ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, S3JniDb * ps, sqlite3 **ppDb, @@ -2673,7 +2704,8 @@ end: JBA_RELEASE(baSql,pBuf); if( 0==rc ){ if( 0!=outTail ){ - /* Noting that pBuf is deallocated now but its address is all we need. */ + /* Noting that pBuf is deallocated now but its address is all we need for + ** what follows... */ assert(zTail ? ((void*)zTail>=(void*)pBuf) : 1); assert(zTail ? (((int)((void*)zTail - (void*)pBuf)) >= 0) : 1); OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0)); @@ -2681,7 +2713,7 @@ end: if( pStmt ){ NativePointerHolder_set(env, jStmt, pStmt, &S3NphRefs.sqlite3_stmt); }else{ - /* Happens for comments and whitespace */ + /* Happens for comments and whitespace. */ UNREF_L(jStmt); jStmt = 0; } @@ -2713,7 +2745,7 @@ JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jDb, jbyteArra prepFlags, jOutStmt, outTail); } - +/* Central C-to-Java sqlite3_progress_handler() proxy. */ static int s3jni_progress_handler_impl(void *pP){ S3JniDb * const ps = (S3JniDb *)pP; LocalJniGetEnv; @@ -2767,6 +2799,7 @@ JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ return rc; } +/* Clears all entries from S3JniGlobal.autoExt. */ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; MUTEX_EXT_ENTER; @@ -3163,8 +3196,8 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ (jint)traceflag, jP, jX); IFTHREW{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_trace_v2() callback"); - EXCEPTION_CLEAR; - rc = SQLITE_ERROR; + rc = s3jni_db_exception(env, ps, SQLITE_ERROR, + "sqlite3_trace_v2() callback threw."); } UNREF_L(jPUnref); UNREF_L(jX); @@ -3210,8 +3243,9 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, ps->updateHook.midCallback, (jint)opId, jDbName, jTable, (jlong)nRowid); IFTHREW{ - EXCEPTION_WARN_CALLBACK_THREW("update hook"); - s3jni_db_exception(env, ps, 0, "update hook callback threw"); + EXCEPTION_WARN_CALLBACK_THREW("sqlite3_update_hook() callback"); + s3jni_db_exception(env, ps, 0, + "sqlite3_update_hook() callback threw"); } } UNREF_L(jDbName); @@ -3469,15 +3503,14 @@ static void Fts5JniAux_xDestroy(void *p){ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux)); if(s){ - const char * zSig = - "(Lorg/sqlite/jni/Fts5ExtensionApi;" - "Lorg/sqlite/jni/Fts5Context;" - "Lorg/sqlite/jni/sqlite3_context;" - "[Lorg/sqlite/jni/sqlite3_value;)V"; memset(s, 0, sizeof(Fts5JniAux)); s->jObj = REF_G(jObj); s->klazz = REF_G((*env)->GetObjectClass(env, jObj)); - s->jmid = (*env)->GetMethodID(env, s->klazz, "xFunction", zSig); + s->jmid = (*env)->GetMethodID(env, s->klazz, "xFunction", + "(Lorg/sqlite/jni/Fts5ExtensionApi;" + "Lorg/sqlite/jni/Fts5Context;" + "Lorg/sqlite/jni/sqlite3_context;" + "[Lorg/sqlite/jni/sqlite3_value;)V"); IFTHREW{ EXCEPTION_REPORT; EXCEPTION_CLEAR; @@ -3600,9 +3633,9 @@ JDECLFtsXA(jint,xColumnTotalSize)(JENV_OSELF,jobject jCtx, jint iCol, jobject jO return (jint)rc; } -/** - Proxy for fts5_extension_function instances plugged in via - fts5_api::xCreateFunction(). +/* +** Proxy for fts5_extension_function instances plugged in via +** fts5_api::xCreateFunction(). */ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, Fts5Context *pFts, @@ -3821,11 +3854,8 @@ JDECLFtsXA(jint,xPhraseSize)(JENV_OSELF,jobject jCtx, jint iPhrase){ return (jint)fext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase); } -/** - State for use with xQueryPhrase() and xTokenize(). -*/ +/* State for use with xQueryPhrase() and xTokenize(). */ struct s3jni_xQueryPhraseState { - JNIEnv *env; Fts5ExtensionApi const * fext; S3JniEnv const * jc; jmethodID midCallback; @@ -3849,7 +3879,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, SJG.fts5.jFtsExt, s->jFcx); IFTHREW{ - EXCEPTION_WARN_CALLBACK_THREW("xQueryPhrase callback"); + EXCEPTION_WARN_CALLBACK_THREW("xQueryPhrase() callback"); EXCEPTION_CLEAR; rc = SQLITE_ERROR; } @@ -3864,7 +3894,6 @@ JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; if( !klazz ) return SQLITE_MISUSE; - s.env = env; s.jc = jc; s.jCallback = jCallback; s.jFcx = jFcx; @@ -3898,8 +3927,8 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ pAux = sqlite3_malloc(sizeof(*pAux)); if(!pAux){ if(jAux){ - // Emulate how xSetAuxdata() behaves when it cannot alloc - // its auxdata wrapper. + /* Emulate how xSetAuxdata() behaves when it cannot alloc + ** its auxdata wrapper. */ s3jni_call_xDestroy(env, jAux, 0); } return SQLITE_NOMEM; @@ -3910,9 +3939,7 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ return rc; } -/** - xToken() impl for xTokenize(). -*/ +/* xToken() impl for xTokenize(). */ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, int nZ, int iStart, int iEnd){ int rc; @@ -3938,8 +3965,9 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, return rc; } -/** - Proxy for Fts5ExtensionApi.xTokenize() and fts5_tokenizer.xTokenize() +/* +** Proxy for Fts5ExtensionApi.xTokenize() and +** fts5_tokenizer.xTokenize() */ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, jint tokFlags, jobject jFcx, @@ -3954,7 +3982,6 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, if( !klazz ) return SQLITE_MISUSE; memset(&s, 0, sizeof(s)); - s.env = env; s.jc = jc; s.jCallback = jCallback; s.jFcx = jFcx; @@ -4216,11 +4243,11 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){ //////////////////////////////////////////////////////////////////////// -/** - Uncaches the current JNIEnv from the S3JniGlobal state, clearing any - resources owned by that cache entry and making that slot available - for re-use. It is important that the Java-side decl of this - function be declared as synchronous. +/* +** Uncaches the current JNIEnv from the S3JniGlobal state, clearing any +** resources owned by that cache entry and making that slot available +** for re-use. It is important that the Java-side decl of this +** function be declared as synchronous. */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ @@ -4231,12 +4258,12 @@ Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ return rc ? JNI_TRUE : JNI_FALSE; } -/** - Called during static init of the SQLite3Jni class to sync certain - compile-time constants to Java-space. - - This routine is part of the reason why we have to #include - sqlite3.c instead of sqlite3.h. +/* +** Called during static init of the SQLite3Jni class to sync certain +** compile-time constants to Java-space. +** +** This routine is part of the reason why we have to #include +** sqlite3.c instead of sqlite3.h. */ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ @@ -4321,14 +4348,13 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ { /* StandardCharsets.UTF_8 */ jfieldID fUtf8; - jclass const klazzSC = - (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); + klazz = (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class."); - fUtf8 = (*env)->GetStaticFieldID(env, klazzSC, "UTF_8", + fUtf8 = (*env)->GetStaticFieldID(env, klazz, "UTF_8", "Ljava/nio/charset/Charset;"); EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); SJG.g.oCharsetUtf8 = - REF_G((*env)->GetStaticObjectField(env, klazzSC, fUtf8)); + REF_G((*env)->GetStaticObjectField(env, klazz, fUtf8)); EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); } @@ -4354,22 +4380,15 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ OOM_CHECK( SJG.metrics.mutex ); #endif -#if 0 - /* Just for sanity checking... */ - (void)S3JniGlobal_env_cache(env); - if( !SJG.envCache.aHead ){ - (*env)->FatalError(env, "Could not allocate JNIEnv-specific cache."); - return; - } - assert( 1 == SJG.metrics.envCacheMisses ); - assert( env == SJG.envCache.aHead->env ); -#endif + sqlite3_shutdown() + /* So that it becomes legal for Java-level code to call + ** sqlite3_config(), if it's ever implemented. */; + /* Set up static "consts" of the SQLite3Jni class. */ for( pConfFlag = &aLimits[0]; pConfFlag->zName; ++pConfFlag ){ char const * zSig = (JTYPE_BOOL == pConfFlag->jtype) ? "Z" : "I"; fieldId = (*env)->GetStaticFieldID(env, jKlazz, pConfFlag->zName, zSig); EXCEPTION_IS_FATAL("Missing an expected static member of the SQLite3Jni class."); - //MARKER(("Setting %s (field=%p) = %d\n", pConfFlag->zName, fieldId, pConfFlag->value)); assert(fieldId); switch(pConfFlag->jtype){ case JTYPE_INT: diff --git a/manifest b/manifest index 56dd2871d2..b7b5f63421 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sC-side\sexception\shandling\sfrom\sJava-side\sUDF\scallbacks. -D 2023-08-23T00:17:28.362 +C Numerous\sminor\scleanups\sand\scode\sstyle\sconformance\simprovements. +D 2023-08-23T09:05:16.380 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile a38d7c5ad4ab1e209e2a9352ff06add1d9a0bc666351714bfc8858625330c16b F ext/jni/README.md ddcc6be0c0d65f1e2fd687de9f40d38c82630fd61f83cc9550773caa19dd8be1 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 8e7dac68246edec8d85e34104d5c749c544ae10c55b1bbf6eeff116447da7f54 +F ext/jni/src/c/sqlite3-jni.c aba2bf845c512b835c795263c2c460faaa926967b497d7dbcf3cdbe98a2d0396 F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd @@ -2092,8 +2092,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 70d936953ba55cfed32efe7e3a9d4b71da9a7ffc8818b6540471e0bf311bc688 -R f4b3990a7d714ecf1a549380689ce9aa +P aebbc24afb339ed07b7cd767fbc0d25f3e9c3d9bb5ef3d1c10b04b605c7261bc +R 8d2509e7997f13cf4f5b727ee9382226 U stephan -Z b8494717453324d170d4924e0442cf8e +Z 2c18e9ca7bc91670a10ef4d3cc234c7a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d1c49b53b7..15b46f2538 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -aebbc24afb339ed07b7cd767fbc0d25f3e9c3d9bb5ef3d1c10b04b605c7261bc \ No newline at end of file +6c92d884920e4ace54913fc60ceef6e43a4351f45a4cb3c4a0ed3d29d544a31b \ No newline at end of file From 4e97ab42968863298de499151c3f073a4b749a36 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 10:36:12 +0000 Subject: [PATCH 037/422] Bind a subset of sqlite3_config() to JNI: threading modes and sqllog. FossilOrigin-Name: fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 --- ext/jni/GNUmakefile | 2 + ext/jni/src/c/sqlite3-jni.c | 96 ++++++++++++++++++++++ ext/jni/src/c/sqlite3-jni.h | 16 ++++ ext/jni/src/org/sqlite/jni/SQLLog.java | 25 ++++++ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 25 ++++++ ext/jni/src/org/sqlite/jni/Tester1.java | 22 +++++ manifest | 21 ++--- manifest.uuid | 2 +- 8 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/SQLLog.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index f1a6c552e9..1d77fc5f74 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -68,6 +68,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ ResultCode.java \ RollbackHook.java \ SQLFunction.java \ + SQLLog.java \ sqlite3_context.java \ sqlite3.java \ SQLite3Jni.java \ @@ -160,6 +161,7 @@ SQLITE_OPT = \ -DSQLITE_ENABLE_DBSTAT_VTAB \ -DSQLITE_ENABLE_BYTECODE_VTAB \ -DSQLITE_ENABLE_OFFSET_SQL_FUNC \ + -DSQLITE_ENABLE_SQLLOG \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_SHARED_CACHE \ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index e6a4b71c30..543f3be549 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -503,6 +503,11 @@ struct S3JniGlobalType { always have this set to the current JNIEnv object. Used only for sanity checking. */; } perDb; +#ifdef SQLITE_ENABLE_SQLLOG + struct { + S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */; + } hooks; +#endif /* ** Refs to global classes and methods. Obtained during static init ** and never released. @@ -2275,6 +2280,97 @@ JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ return rc; } +/* +** sqlite3_config(SQLITE_CONFIG_...) wrapper for a small subset of +** options. +*/ +JDECL(jint,1config__I)(JENV_CSELF, jint n){ + switch(n){ + case SQLITE_CONFIG_SINGLETHREAD: + case SQLITE_CONFIG_MULTITHREAD: + case SQLITE_CONFIG_SERIALIZED: + return sqlite3_config( n ); + default: + return SQLITE_MISUSE; + } +} + +#ifdef SQLITE_ENABLE_SQLLOG +/* C-to-Java SQLITE_CONFIG_SQLLOG wrapper. */ +static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){ + jobject jArg0 = 0; + jstring jArg1 = 0; + LocalJniGetEnv; + S3JniEnv * const jc = S3JniGlobal_env_cache(env); + S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); + S3JniHook * const hook = &SJG.hooks.sqllog; + + if( !ps || !hook->jObj ) return; + jArg0 = REF_L(ps->jDb); + switch(op){ + case 0: /* db opened */ + case 1: /* SQL executed */ + jArg1 = s3jni_utf8_to_jstring(jc, z, -1); + break; + case 2: /* db closed */ + break; + default: + (*env)->FatalError(env, "Unhandled 4th arg to SQLITE_CONFIG_SQLLOG."); + break; + } + (*env)->CallVoidMethod(env, hook->jObj, hook->midCallback, jArg0, jArg1, op); + IFTHREW{ + EXCEPTION_WARN_CALLBACK_THREW("SQLITE_CONFIG_SQLLOG callback"); + EXCEPTION_CLEAR; + } + UNREF_L(jArg0); + UNREF_L(jArg1); +} +//! Requirement of SQLITE_CONFIG_SQLLOG. +void sqlite3_init_sqllog(void){ + sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); +} +#endif + +/* sqlite3_config(SQLITE_CONFIG_SQLLOG) wrapper. */ +JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ +#ifdef SQLITE_ENABLE_SQLLOG + S3JniHook tmpHook; + S3JniHook * const hook = &tmpHook; + S3JniHook * const hookOld = & SJG.hooks.sqllog; + int rc = 0; + if( !jLog ){ + S3JniHook_unref(env, hookOld, 0); + return 0; + } + if( hookOld->jObj && (*env)->IsSameObject(env, jLog, hookOld->jObj) ){ + return 0; + } + hook->klazz = REF_G( (*env)->GetObjectClass(env, jLog) ); + hook->midCallback = (*env)->GetMethodID(env, hook->klazz, "xSqllog", + "(Lorg/sqlite/jni/sqlite3;" + "Ljava/lang/String;" + "I)V"); + if( !hook->midCallback ){ + EXCEPTION_WARN_IGNORE; + S3JniHook_unref(env, hook, 0); + return SQLITE_ERROR; + } + hook->jObj = REF_G(jLog); + rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); + if( rc ){ + S3JniHook_unref(env, hook, 0); + }else{ + S3JniHook_unref(env, hookOld, 0); + *hookOld = *hook; + } + return rc; +#else + MARKER(("Warning: built without SQLITE_ENABLE_SQLLOG.\n")); + return SQLITE_RANGE; +#endif +} + JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){ sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0; diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index e189df6947..0a8736f2a7 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1083,6 +1083,22 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1compileoption_ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1compileoption_1used (JNIEnv *, jclass, jstring); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_config + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__I + (JNIEnv *, jclass, jint); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_config + * Signature: (Lorg/sqlite/jni/SQLLog;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqlite_jni_SQLLog_2 + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_create_collation diff --git a/ext/jni/src/org/sqlite/jni/SQLLog.java b/ext/jni/src/org/sqlite/jni/SQLLog.java new file mode 100644 index 0000000000..c1bc0aab61 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/SQLLog.java @@ -0,0 +1,25 @@ +/* +** 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_config(SQLLog). +*/ +public interface SQLLog { + /** + Must function as described for sqlite3_config(SQLITE_CONFIG_SQLLOG) + callback, with the slight signature change. + */ + void xSqllog(sqlite3 db, String msg, int msgType ); +} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 22178faed6..e64ecf4914 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -454,6 +454,31 @@ public final class SQLite3Jni { @NotNull String optName ); + /* + ** Works like in the C API with the exception that it only supports + ** the following subset of configution flags: + ** + ** - SQLITE_CONFIG_SINGLETHREAD + ** - SQLITE_CONFIG_MULTITHREAD + ** - SQLITE_CONFIG_SERIALIZED + ** + ** Others may be added in the future. It returns SQLITE_MISUSE if + ** given an argument it does not handle. + */ + public static native int sqlite3_config(int op); + + /* + ** If the native library was built with SQLITE_ENABLE_SQLLOG defined + ** then this acts as a proxy for C's + ** sqlite3_config(SQLITE_ENABLE_SQLLOG,...). This sets or clears the + ** logger. If installation of a logger fails, any previous logger is + ** retained. + ** + ** If not built with SQLITE_ENABLE_SQLLOG defined, this returns + ** SQLITE_RANGE. + */ + public static native int sqlite3_config( @Nullable SQLLog logger ); + public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, @NotNull Collation col diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index c075a2d036..fc350814b4 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1321,6 +1321,7 @@ public class Tester1 implements Runnable { boolean doSomethingForDev = false; Integer nRepeat = 1; boolean forceFail = false; + boolean sqlLog = false; for( int i = 0; i < args.length; ){ String arg = args[i++]; if(arg.startsWith("-")){ @@ -1338,6 +1339,8 @@ public class Tester1 implements Runnable { listRunTests = true; }else if(arg.equals("fail")){ forceFail = true; + }else if(arg.equals("sqllog")){ + sqlLog = true; }else if(arg.equals("naps")){ takeNaps = true; }else{ @@ -1366,8 +1369,27 @@ public class Tester1 implements Runnable { } } + if( sqlLog ){ + int rc = sqlite3_config( new SQLLog() { + @Override public void xSqllog(sqlite3 db, String msg, int op){ + switch(op){ + case 0: outln("Opening db: ",db); break; + case 1: outln(db,": ",msg); break; + case 2: outln("Closing db: ",db); break; + } + } + }); + affirm( 0==rc ); + } + final long timeStart = System.currentTimeMillis(); int nLoop = 0; + affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), + "Could not switch to single-thread mode." ); + affirm( 0==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), + "Could not switch to multithread mode." ); + affirm( 0==sqlite3_config( SQLITE_CONFIG_SERIALIZED ), + "Could not switch to serialized threading mode." ); outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); diff --git a/manifest b/manifest index b7b5f63421..ff7bb357eb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Numerous\sminor\scleanups\sand\scode\sstyle\sconformance\simprovements. -D 2023-08-23T09:05:16.380 +C Bind\sa\ssubset\sof\ssqlite3_config()\sto\sJNI:\sthreading\smodes\sand\ssqllog. +D 2023-08-23T10:36:12.341 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 a38d7c5ad4ab1e209e2a9352ff06add1d9a0bc666351714bfc8858625330c16b +F ext/jni/GNUmakefile 33abc2f4f4bbd5451d6be5e6f2e109c045cc326cd942d602a3908a0c7b3c6f49 F ext/jni/README.md ddcc6be0c0d65f1e2fd687de9f40d38c82630fd61f83cc9550773caa19dd8be1 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c aba2bf845c512b835c795263c2c460faaa926967b497d7dbcf3cdbe98a2d0396 -F ext/jni/src/c/sqlite3-jni.h 8b0ab1a3f0f92b75d4ff50db4a88b66a137cfb561268eb15bb3993ed174dbb74 +F ext/jni/src/c/sqlite3-jni.c 01c6cf041d1b9937a97c7700006a532d3b11fd4991931e31ffa7a777b97fba11 +F ext/jni/src/c/sqlite3-jni.h 44bcb4eb3517c089f8f24f1546ea66b350d0661a4b0fa148425c9a41cabf487d 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 @@ -255,8 +255,9 @@ F ext/jni/src/org/sqlite/jni/ProgressHandler.java 6f62053a828a572de809828b1ee495 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/SQLite3Jni.java 2f36370cfdec01d309720392b2c3e4af6afce0b6ece8188b5c3ed688a5a1e63a -F ext/jni/src/org/sqlite/jni/Tester1.java 93bd76f2294fa2f592395c9d823adb38a9be3846bff00debeff02310f4e9e6be +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/TesterFts5.java de095e3b701fba0c56d7b8b2993dc22bcbaa9de8f992904a93729ad729a91576 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2092,8 +2093,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 aebbc24afb339ed07b7cd767fbc0d25f3e9c3d9bb5ef3d1c10b04b605c7261bc -R 8d2509e7997f13cf4f5b727ee9382226 +P 6c92d884920e4ace54913fc60ceef6e43a4351f45a4cb3c4a0ed3d29d544a31b +R ebb24a95583279229c99fb88e45995e0 U stephan -Z 2c18e9ca7bc91670a10ef4d3cc234c7a +Z 0a740a88323f212cff509af7c6f7ae11 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 15b46f2538..23b760f68b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6c92d884920e4ace54913fc60ceef6e43a4351f45a4cb3c4a0ed3d29d544a31b \ No newline at end of file +fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 \ No newline at end of file From bea9ed0f1f4d99b8671b5b4255ea57fab99f6280 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 13:17:37 +0000 Subject: [PATCH 038/422] Bind sqlite3_preupdate_hook() and friends to JNI. FossilOrigin-Name: d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 --- ext/jni/GNUmakefile | 1 + ext/jni/src/c/sqlite3-jni.c | 434 ++++++++++++------ ext/jni/src/c/sqlite3-jni.h | 48 ++ ext/jni/src/org/sqlite/jni/OutputPointer.java | 22 + ext/jni/src/org/sqlite/jni/PreUpdateHook.java | 29 ++ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 65 ++- ext/jni/src/org/sqlite/jni/Tester1.java | 74 +++ manifest | 23 +- manifest.uuid | 2 +- 9 files changed, 545 insertions(+), 153 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/PreUpdateHook.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 1d77fc5f74..cc728003d7 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -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 \ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 543f3be549..93c5d5f1d2 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -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); } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 0a8736f2a7..86fada9574 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -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 diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index 416ad48e60..bf61656dd5 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -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. diff --git a/ext/jni/src/org/sqlite/jni/PreUpdateHook.java b/ext/jni/src/org/sqlite/jni/PreUpdateHook.java new file mode 100644 index 0000000000..d5d82c72bc --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/PreUpdateHook.java @@ -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 ); +} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index e64ecf4914..c22d2fa60e 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -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 ); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index fc350814b4..616e9fed22 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -997,6 +997,7 @@ public class Tester1 implements Runnable { final ValueHolder 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 counter = new ValueHolder<>(0); + final ValueHolder 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 counter = new ValueHolder<>(0); diff --git a/manifest b/manifest index ff7bb357eb..ca1266d086 100644 --- a/manifest +++ b/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. diff --git a/manifest.uuid b/manifest.uuid index 23b760f68b..9a861544e3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 \ No newline at end of file +d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 \ No newline at end of file From 495046ef88ed1ff05e414b0ede46ef2149bd696e Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 13:36:27 +0000 Subject: [PATCH 039/422] Add a note to the JNI README explaining why the Java API has callback names like xFunc() and xPreUpdate(). FossilOrigin-Name: 415447a310f6a7d06b4aa9ef51f110cf8e2ef9545c69cb5983c367c50fe641d2 --- ext/jni/README.md | 11 +++++++++++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/ext/jni/README.md b/ext/jni/README.md index 5e100c4f07..90067556d2 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -110,6 +110,17 @@ to throw, in which cases they get translated to C-level result codes and/or messages. +Awkward Callback Names +------------------------------------------------------------------------ + +In places where the Java interface uses callbacks (see below), those +callbacks often have what might fairly be labeled as awkward names, +e.g. `sqlScalarFunction.xFunc()` and `preupdateHook.xPreUpdate()`. +Those names were chosen because they match the corresponding arguments +in the C-level API docs. If they were renamed to be more Java-esque, +documentation transparency would suffer. + + Unwieldy Constructs are Re-mapped ------------------------------------------------------------------------ diff --git a/manifest b/manifest index ca1266d086..58f3c72d7e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\ssqlite3_preupdate_hook()\sand\sfriends\sto\sJNI. -D 2023-08-23T13:17:37.782 +C Add\sa\snote\sto\sthe\sJNI\sREADME\sexplaining\swhy\sthe\sJava\sAPI\shas\scallback\snames\slike\sxFunc()\sand\sxPreUpdate(). +D 2023-08-23T13:36:27.524 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,7 +233,7 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 -F ext/jni/README.md ddcc6be0c0d65f1e2fd687de9f40d38c82630fd61f83cc9550773caa19dd8be1 +F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 F ext/jni/src/c/sqlite3-jni.c 852c4812c9a3663d871cb334eaa60eb6fc22d67da47d4ff3868fdbfd6ebedb3a F ext/jni/src/c/sqlite3-jni.h c5cb0348efe4e5f3d125a240e2437e8475de14a586c2f859e2acdcde4116244d @@ -2094,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 fce8ecaf7f2e69a168978e6993e58c452c45f76c39da33f2869c9d947c16cab1 -R 28457b0903a4397220c04d68facc73da +P d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 +R 2838b81803960048740e7919bfbbb667 U stephan -Z d81ff8935be35c831285c9d98a32b81f +Z 6887128552106c38adfd2773e845a05f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9a861544e3..69eb67759c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 \ No newline at end of file +415447a310f6a7d06b4aa9ef51f110cf8e2ef9545c69cb5983c367c50fe641d2 \ No newline at end of file From 1c3bf8a3e15fc6dcdfdb508f67c8e747b2621014 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 17:15:48 +0000 Subject: [PATCH 040/422] Remove unnecessary jclass-type struct members. FossilOrigin-Name: d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 --- ext/jni/src/c/sqlite3-jni.c | 117 ++++++++++++++++++------------------ manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 65 insertions(+), 66 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 93c5d5f1d2..494e64a3f6 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -356,7 +356,7 @@ struct S3JniNphClass { ** NativePointerHolder subclass represented by ** zClassName */; volatile jmethodID midCtor /* klazz's no-arg constructor. Used by - ** new_NativePointerHolder_object(). */; + ** new_NativePointerHolder_object(). */; volatile jfieldID fidValue /* NativePointerHolder.nativePointer or ** OutputPointer.T.value */; volatile jfieldID fidAggCtx /* sqlite3_context::aggregateContext. Used only @@ -369,12 +369,6 @@ struct S3JniHook{ jobject jObj /* global ref to Java instance */; jmethodID midCallback /* callback method. Signature depends on ** jObj's type */; - jclass klazz /* global ref to jObj's class. Only needed - ** by hooks which have an xDestroy() method. - ** We can probably eliminate this and simply - ** do the class lookup at the same - ** (deferred) time we do the xDestroy() - ** lookup. */; }; /* @@ -533,7 +527,6 @@ struct S3JniGlobalType { volatile jobject jFtsExt /* Global ref to Java singleton for the Fts5ExtensionApi instance. */; struct { - volatile jclass klazz /* Global ref to the Fts5Phrase iter class */; jfieldID fidA /* Fts5Phrase::a member */; jfieldID fidB /* Fts5Phrase::b member */; } jPhraseIter; @@ -860,6 +853,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ char * zMsg; jclass const klazz = (*env)->GetObjectClass(env, jx); mid = (*env)->GetMethodID(env, klazz, "toString", "()Ljava/lang/String;"); + UNREF_L(klazz); IFTHREW{ EXCEPTION_REPORT; EXCEPTION_CLEAR; @@ -905,20 +899,18 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, } /* -** Extracts the (void xDestroy()) method from the given jclass and -** applies it to jobj. If jObj is NULL, this is a no-op. If klazz is -** NULL then it's derived from jobj. The lack of an xDestroy() method -** is silently ignored and any exceptions thrown by xDestroy() trigger -** a warning to stdout or stderr and then the exception is suppressed. +** Extracts the (void xDestroy()) method from jObj and applies it to +** jObj. If jObj is NULL, this is a no-op. The lack of an xDestroy() +** method is silently ignored and any exceptions thrown by xDestroy() +** trigger a warning to stdout or stderr and then the exception is +** suppressed. */ -static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ +static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ if(jObj){ - jmethodID method; - if(!klazz){ - klazz = (*env)->GetObjectClass(env, jObj); - assert(klazz); - } - method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); + jclass const klazz = (*env)->GetObjectClass(env, jObj); + jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); + + UNREF_L(klazz); if(method){ s3jni_incr( &SJG.metrics.nDestroy ); (*env)->CallVoidMethod(env, jObj, method); @@ -927,6 +919,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ EXCEPTION_CLEAR; } }else{ + /* Non-fatal. */ EXCEPTION_CLEAR; } } @@ -934,17 +927,16 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj, jclass klazz){ /* ** Removes any Java references from s and clears its state. If -** doXDestroy is true and s->klazz and s->jObj are not NULL, s->jObj's +** doXDestroy is true and s->jObj is not NULL, s->jObj's ** s is passed to s3jni_call_xDestroy() before any references are ** cleared. It is legal to call this when the object has no Java ** references. */ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDestroy){ - if(doXDestroy && s->klazz && s->jObj){ - s3jni_call_xDestroy(env, s->jObj, s->klazz); + if(doXDestroy && s->jObj){ + s3jni_call_xDestroy(env, s->jObj); } UNREF_G(s->jObj); - UNREF_G(s->klazz); memset(s, 0, sizeof(*s)); } @@ -1198,16 +1190,12 @@ static void S3JniAutoExtension_clear(JNIEnv * const env, static int S3JniAutoExtension_init(JNIEnv *const env, S3JniAutoExtension * const ax, jobject const jAutoExt){ - jclass klazz; - klazz = (*env)->GetObjectClass(env, jAutoExt); - if(!klazz){ - S3JniAutoExtension_clear(env, ax); - return SQLITE_ERROR; - } + jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); + ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", "(Lorg/sqlite/jni/sqlite3;)I"); - EXCEPTION_WARN_IGNORE; UNREF_L(klazz); + EXCEPTION_WARN_IGNORE; if(!ax->midFunc){ MARKER(("Error getting xEntryPoint(sqlite3) from AutoExtension object.")); S3JniAutoExtension_clear(env, ax); @@ -1245,8 +1233,7 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, if(!pNC->fidAggCtx){ MUTEX_NPH_ENTER; if(!pNC->fidAggCtx){ - pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, - "aggregateContext", "J"); + pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, "aggregateContext", "J"); EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member."); } MUTEX_NPH_LEAVE; @@ -1540,7 +1527,6 @@ typedef void (*udf_xFinal_f)(sqlite3_context*); typedef struct S3JniUdf S3JniUdf; struct S3JniUdf { jobject jObj /* SQLFunction instance */; - jclass klazz /* jObj's class */; char * zFuncName /* Only for error reporting and debug logging */; enum UDFType type; /** Method IDs for the various UDF methods. */ @@ -1558,11 +1544,12 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ "(Lorg/sqlite/jni/sqlite3_context;[Lorg/sqlite/jni/sqlite3_value;)V"; const char * zFV = /* signature for xFinal, xValue */ "(Lorg/sqlite/jni/sqlite3_context;)V"; + jclass const klazz = (*env)->GetObjectClass(env, jObj); + memset(s, 0, sizeof(S3JniUdf)); s->jObj = REF_G(jObj); - s->klazz = REF_G((*env)->GetObjectClass(env, jObj)); #define FGET(FuncName,FuncType,Field) \ - s->Field = (*env)->GetMethodID(env, s->klazz, FuncName, FuncType); \ + s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncType); \ if(!s->Field) (*env)->ExceptionClear(env) FGET("xFunc", zFSI, jmidxFunc); FGET("xStep", zFSI, jmidxStep); @@ -1570,6 +1557,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ FGET("xValue", zFV, jmidxValue); FGET("xInverse", zFSI, jmidxInverse); #undef FGET + UNREF_L(klazz); if(s->jmidxFunc) s->type = UDF_SCALAR; else if(s->jmidxStep && s->jmidxFinal){ s->type = s->jmidxValue ? UDF_WINDOW : UDF_AGGREGATE; @@ -1584,9 +1572,8 @@ static void S3JniUdf_free(S3JniUdf * s){ LocalJniGetEnv; if(env){ //MARKER(("UDF cleanup: %s\n", s->zFuncName)); - s3jni_call_xDestroy(env, s->jObj, s->klazz); + s3jni_call_xDestroy(env, s->jObj); UNREF_G(s->jObj); - UNREF_G(s->klazz); } sqlite3_free(s->zFuncName); sqlite3_free(s); @@ -2024,10 +2011,12 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ /* Same object - this is a no-op. */ return 0; } + jclass klazz; S3JniHook_unref(env, pHook, 1); pHook->jObj = REF_G(jBusy); - pHook->klazz = REF_G((*env)->GetObjectClass(env, jBusy)); - pHook->midCallback = (*env)->GetMethodID(env, pHook->klazz, "xCallback", "(I)I"); + klazz = (*env)->GetObjectClass(env, jBusy); + pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(I)I"); + UNREF_L(klazz); IFTHREW { S3JniHook_unref(env, pHook, 0); rc = SQLITE_ERROR; @@ -2157,6 +2146,7 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ klazz = (*env)->GetObjectClass(env, jHook); xCallback = (*env)->GetMethodID(env, klazz, "xCollationNeeded", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); + UNREF_L(klazz); IFTHREW { rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, "Cannot not find matching callback on " @@ -2275,6 +2265,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, xCallback = (*env)->GetMethodID(env, klazz, isCommit ? "xCommitHook" : "xRollbackHook", isCommit ? "()I" : "()V"); + UNREF_L(klazz); IFTHREW { EXCEPTION_REPORT; EXCEPTION_CLEAR; @@ -2371,6 +2362,7 @@ JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ S3JniHook tmpHook; S3JniHook * const hook = &tmpHook; S3JniHook * const hookOld = & SJG.hooks.sqllog; + jclass klazz; int rc = 0; if( !jLog ){ S3JniHook_unref(env, hookOld, 0); @@ -2379,11 +2371,12 @@ JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ if( hookOld->jObj && (*env)->IsSameObject(env, jLog, hookOld->jObj) ){ return 0; } - hook->klazz = REF_G( (*env)->GetObjectClass(env, jLog) ); - hook->midCallback = (*env)->GetMethodID(env, hook->klazz, "xSqllog", + klazz = (*env)->GetObjectClass(env, jLog); + hook->midCallback = (*env)->GetMethodID(env, klazz, "xSqllog", "(Lorg/sqlite/jni/sqlite3;" "Ljava/lang/String;" "I)V"); + UNREF_L(klazz); if( !hook->midCallback ){ EXCEPTION_WARN_IGNORE; S3JniHook_unref(env, hook, 0); @@ -2423,8 +2416,9 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, klazz = (*env)->GetObjectClass(env, oCollation); pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCompare", "([B[B)I"); + UNREF_L(klazz); IFTHREW{ - EXCEPTION_REPORT; + UNREF_L(klazz); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); } @@ -2435,7 +2429,6 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, JSTR_RELEASE(name, zName); if( 0==rc ){ pHook->jObj = REF_G(oCollation); - pHook->klazz = REF_G(klazz); }else{ S3JniHook_unref(env, pHook, 1); } @@ -3000,6 +2993,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec "JJ)V") : (*env)->GetMethodID(env, klazz, "xUpdateHook", "(ILjava/lang/String;Ljava/lang/String;J)V"); + UNREF_L(klazz); IFTHREW { EXCEPTION_CLEAR; s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3094,6 +3088,7 @@ JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress } klazz = (*env)->GetObjectClass(env, jProgress); xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I"); + UNREF_L(klazz); IFTHREW { EXCEPTION_CLEAR; s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3351,6 +3346,7 @@ JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ return (jint)sqlite3_set_authorizer( ps->pDb, 0, 0 ); }else{ int rc = 0; + jclass klazz; if( pHook->jObj ){ if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){ /* Same object - this is a no-op. */ @@ -3359,8 +3355,8 @@ JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ S3JniHook_unref(env, pHook, 0); } pHook->jObj = REF_G(jHook); - pHook->klazz = REF_G((*env)->GetObjectClass(env, jHook)); - pHook->midCallback = (*env)->GetMethodID(env, pHook->klazz, + klazz = (*env)->GetObjectClass(env, jHook); + pHook->midCallback = (*env)->GetMethodID(env, klazz, "xAuth", "(I" "Ljava/lang/String;" @@ -3368,6 +3364,7 @@ JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ "Ljava/lang/String;" "Ljava/lang/String;" ")I"); + UNREF_L(klazz); IFTHREW { S3JniHook_unref(env, pHook, 0); return s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3536,6 +3533,7 @@ JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){ klazz = (*env)->GetObjectClass(env, jTracer); ps->hooks.trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(ILjava/lang/Object;Ljava/lang/Object;)I"); + UNREF_L(klazz); IFTHREW { EXCEPTION_CLEAR; return s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3723,7 +3721,6 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ */ typedef struct { jobject jObj /* functor instance */; - jclass klazz /* jObj's class */; jobject jUserData /* 2nd arg to JNI binding of xCreateFunction(), ostensibly the 3rd arg to the lib-level xCreateFunction(), except @@ -3737,9 +3734,8 @@ static void Fts5JniAux_free(Fts5JniAux * const s){ LocalJniGetEnv; if(env){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ - s3jni_call_xDestroy(env, s->jObj, s->klazz); + s3jni_call_xDestroy(env, s->jObj); UNREF_G(s->jObj); - UNREF_G(s->klazz); UNREF_G(s->jUserData); } sqlite3_free(s->zFuncName); @@ -3753,14 +3749,16 @@ static void Fts5JniAux_xDestroy(void *p){ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux)); if(s){ + jclass klazz; memset(s, 0, sizeof(Fts5JniAux)); s->jObj = REF_G(jObj); - s->klazz = REF_G((*env)->GetObjectClass(env, jObj)); - s->jmid = (*env)->GetMethodID(env, s->klazz, "xFunction", + klazz = (*env)->GetObjectClass(env, jObj); + s->jmid = (*env)->GetMethodID(env, klazz, "xFunction", "(Lorg/sqlite/jni/Fts5ExtensionApi;" "Lorg/sqlite/jni/Fts5Context;" "Lorg/sqlite/jni/sqlite3_context;" "[Lorg/sqlite/jni/sqlite3_value;)V"); + UNREF_L(klazz); IFTHREW{ EXCEPTION_REPORT; EXCEPTION_CLEAR; @@ -3966,7 +3964,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){ S3JniFts5AuxData * const p = x; if(p->jObj){ LocalJniGetEnv; - s3jni_call_xDestroy(env, p->jObj, 0); + s3jni_call_xDestroy(env, p->jObj); UNREF_G(p->jObj); } sqlite3_free(x); @@ -4023,7 +4021,7 @@ static void s3jni_phraseIter_NToJ(JNIEnv *const env, Fts5PhraseIter const * const pSrc, jobject jIter){ S3JniGlobalType * const g = &S3JniGlobal; - assert(g->fts5.jPhraseIter.klazz); + assert(g->fts5.jPhraseIter.fidA); (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidA, (jlong)pSrc->a); EXCEPTION_IS_FATAL("Cannot set Fts5PhraseIter.a field."); (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidB, (jlong)pSrc->b); @@ -4034,7 +4032,7 @@ static void s3jni_phraseIter_NToJ(JNIEnv *const env, static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter, Fts5PhraseIter * const pDest){ S3JniGlobalType * const g = &S3JniGlobal; - assert(g->fts5.jPhraseIter.klazz); + assert(g->fts5.jPhraseIter.fidA); pDest->a = (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidA); EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); @@ -4078,7 +4076,6 @@ JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, Fts5ExtDecl; Fts5PhraseIter iter; int iCol = 0, iOff = 0; - if(!SJG.fts5.jPhraseIter.klazz) return /*SQLITE_MISUSE*/; s3jni_phraseIter_JToN(env, jIter, &iter); fext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff); OutputPointer_set_Int32(env, jOutCol, iCol); @@ -4091,7 +4088,6 @@ JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter, Fts5ExtDecl; Fts5PhraseIter iter; int iCol = 0; - if(!SJG.fts5.jPhraseIter.klazz) return /*SQLITE_MISUSE*/; s3jni_phraseIter_JToN(env, jIter, &iter); fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol); OutputPointer_set_Int32(env, jOutCol, iCol); @@ -4151,6 +4147,7 @@ JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, s.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(Lorg.sqlite.jni.Fts5ExtensionApi;" "Lorg.sqlite.jni.Fts5Context;)I"); + UNREF_L(klazz); EXCEPTION_IS_FATAL("Could not extract xQueryPhraseCallback.xCallback method."); return (jint)fext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s, s3jni_xQueryPhrase); @@ -4179,7 +4176,7 @@ JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ if(jAux){ /* Emulate how xSetAuxdata() behaves when it cannot alloc ** its auxdata wrapper. */ - s3jni_call_xDestroy(env, jAux, 0); + s3jni_call_xDestroy(env, jAux); } return SQLITE_NOMEM; } @@ -4237,6 +4234,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, s.jFcx = jFcx; s.fext = fext; s.midCallback = (*env)->GetMethodID(env, klazz, "xToken", "(I[BII)I"); + UNREF_L(klazz); IFTHREW { EXCEPTION_REPORT; EXCEPTION_CLEAR; @@ -4606,16 +4604,17 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ SJG.g.oCharsetUtf8 = REF_G((*env)->GetStaticObjectField(env, klazz, fUtf8)); EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); + UNREF_L(klazz); } #ifdef SQLITE_ENABLE_FTS5 klazz = (*env)->FindClass(env, "org/sqlite/jni/Fts5PhraseIter"); EXCEPTION_IS_FATAL("Error getting reference to org.sqlite.jni.Fts5PhraseIter."); - SJG.fts5.jPhraseIter.klazz = REF_G(klazz); SJG.fts5.jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J"); EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); - SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "a", "J"); + SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "b", "J"); EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); + UNREF_L(klazz); #endif SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); diff --git a/manifest b/manifest index 58f3c72d7e..f6449b99b7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\snote\sto\sthe\sJNI\sREADME\sexplaining\swhy\sthe\sJava\sAPI\shas\scallback\snames\slike\sxFunc()\sand\sxPreUpdate(). -D 2023-08-23T13:36:27.524 +C Remove\sunnecessary\sjclass-type\sstruct\smembers. +D 2023-08-23T17:15:48.658 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 852c4812c9a3663d871cb334eaa60eb6fc22d67da47d4ff3868fdbfd6ebedb3a +F ext/jni/src/c/sqlite3-jni.c adb773d104abe72e93364f21c52d455f361692f3cd7dd6d9fdab6110b8b4f3ee 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 @@ -2094,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 d0c425b5c1d3aac5ead18a501a3760b4506d68d373cb3be484247042cf2fa8d4 -R 2838b81803960048740e7919bfbbb667 +P 415447a310f6a7d06b4aa9ef51f110cf8e2ef9545c69cb5983c367c50fe641d2 +R fddefcd0dbe857a799e7e6b983a3459a U stephan -Z 6887128552106c38adfd2773e845a05f +Z b441a1d2bae36389c447acd3134d6be2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 69eb67759c..3df5deea50 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -415447a310f6a7d06b4aa9ef51f110cf8e2ef9545c69cb5983c367c50fe641d2 \ No newline at end of file +d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 \ No newline at end of file From 8cafdfa916d31818e4713c2e5aac1d3d105f57fa Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 23 Aug 2023 17:52:51 +0000 Subject: [PATCH 041/422] JNI cleanups regarding building with certain features disabled. FossilOrigin-Name: a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 --- ext/jni/src/c/sqlite3-jni.c | 26 ++++++------------ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 5 ---- ext/jni/src/org/sqlite/jni/Tester1.java | 31 ++++++++++++++-------- manifest | 16 +++++------ manifest.uuid | 2 +- 5 files changed, 37 insertions(+), 43 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 494e64a3f6..debb221d6a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -61,9 +61,6 @@ #ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC # define SQLITE_ENABLE_OFFSET_SQL_FUNC 1 #endif -#ifndef SQLITE_ENABLE_PREUPDATE_HOOK -# define SQLITE_ENABLE_PREUPDATE_HOOK 1 /*required by session extension*/ -#endif #ifndef SQLITE_ENABLE_RTREE # define SQLITE_ENABLE_RTREE 1 #endif @@ -235,12 +232,12 @@ static const struct { const S3NphRef sqlite3_value; const S3NphRef OutputPointer_Int32; const S3NphRef OutputPointer_Int64; - const S3NphRef OutputPointer_String; - const S3NphRef OutputPointer_ByteArray; const S3NphRef OutputPointer_sqlite3; const S3NphRef OutputPointer_sqlite3_stmt; const S3NphRef OutputPointer_sqlite3_value; #ifdef SQLITE_ENABLE_FTS5 + const S3NphRef OutputPointer_String; + const S3NphRef OutputPointer_ByteArray; const S3NphRef Fts5Context; const S3NphRef Fts5ExtensionApi; const S3NphRef fts5_api; @@ -255,12 +252,12 @@ static const struct { 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"), + NREF(6, "OutputPointer$sqlite3"), + NREF(7, "OutputPointer$sqlite3_stmt"), + NREF(8, "OutputPointer$sqlite3_value"), #ifdef SQLITE_ENABLE_FTS5 + NREF(9, "OutputPointer$String"), + NREF(10, "OutputPointer$ByteArray"), NREF(11, "Fts5Context"), NREF(12, "Fts5ExtensionApi"), NREF(13, "fts5_api"), @@ -4525,13 +4522,6 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ int value; } ConfigFlagEntry; const ConfigFlagEntry aLimits[] = { - {"SQLITE_ENABLE_FTS5", JTYPE_BOOL, -#ifdef SQLITE_ENABLE_FTS5 - 1 -#else - 0 -#endif - }, {"SQLITE_MAX_ALLOCATION_SIZE", JTYPE_INT, SQLITE_MAX_ALLOCATION_SIZE}, {"SQLITE_LIMIT_LENGTH", JTYPE_INT, SQLITE_LIMIT_LENGTH}, {"SQLITE_MAX_LENGTH", JTYPE_INT, SQLITE_MAX_LENGTH}, @@ -4597,7 +4587,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ { /* StandardCharsets.UTF_8 */ jfieldID fUtf8; klazz = (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); - EXCEPTION_IS_FATAL("Error getting reference to StndardCharsets class."); + EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets class."); fUtf8 = (*env)->GetStaticFieldID(env, klazz, "UTF_8", "Ljava/nio/charset/Charset;"); EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index c22d2fa60e..29ed0e5602 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1251,11 +1251,6 @@ public final class SQLite3Jni { public static final String SQLITE_VERSION = sqlite3_libversion(); public static final String SQLITE_SOURCE_ID = sqlite3_sourceid(); - //! Feature flags which are initialized at lib startup. Necessarily - // non-final so that lib init can fill out the proper values, - // but modifying them from client code has no effect. - public static boolean SQLITE_ENABLE_FTS5 = false; - // access public static final int SQLITE_ACCESS_EXISTS = 0; public static final int SQLITE_ACCESS_READWRITE = 1; diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 616e9fed22..c655ae7ffd 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1046,6 +1046,10 @@ public class Tester1 implements Runnable { different callback type. */ private synchronized void testPreUpdateHook(){ + if( !sqlite3_compileoption_used("ENABLE_PREUPDATE_HOOK") ){ + //outln("Skipping testPreUpdateHook(): no pre-update hook support."); + return; + } final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); @@ -1153,8 +1157,8 @@ public class Tester1 implements Runnable { */ @SuppressWarnings("unchecked") private void testFts5() throws Exception { - if( !SQLITE_ENABLE_FTS5 ){ - outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); + if( !sqlite3_compileoption_used("ENABLE_FTS5") ){ + //outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); return; } Exception err = null; @@ -1444,16 +1448,21 @@ public class Tester1 implements Runnable { } if( sqlLog ){ - int rc = sqlite3_config( new SQLLog() { - @Override public void xSqllog(sqlite3 db, String msg, int op){ - switch(op){ - case 0: outln("Opening db: ",db); break; - case 1: outln(db,": ",msg); break; - case 2: outln("Closing db: ",db); break; + if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ + int rc = sqlite3_config( new SQLLog() { + @Override public void xSqllog(sqlite3 db, String msg, int op){ + switch(op){ + case 0: outln("Opening db: ",db); break; + case 1: outln(db,": ",msg); break; + case 2: outln("Closing db: ",db); break; + } } - } - }); - affirm( 0==rc ); + }); + affirm( 0==rc ); + }else{ + outln("WARNING: -sqllog is not active because library was built ", + "without SQLITE_ENABLE_SQLLOG."); + } } final long timeStart = System.currentTimeMillis(); diff --git a/manifest b/manifest index f6449b99b7..f1c1761dd4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sunnecessary\sjclass-type\sstruct\smembers. -D 2023-08-23T17:15:48.658 +C JNI\scleanups\sregarding\sbuilding\swith\scertain\sfeatures\sdisabled. +D 2023-08-23T17:52:51.175 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0 F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c adb773d104abe72e93364f21c52d455f361692f3cd7dd6d9fdab6110b8b4f3ee +F ext/jni/src/c/sqlite3-jni.c 0ca96134d7fb3f313a7a49487f68a8d7a6d7545470c84532aa1ce63d2cdc432e 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 @@ -257,8 +257,8 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 e99e073e3779d00e23842858276efac93c8b523193b77ff12469d12a0b6182ca -F ext/jni/src/org/sqlite/jni/Tester1.java 05ae085ed040bcc10b51cd12076a4151eda478f9773dc00a85d0cddd3dcc01f7 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java f64554457fa30a048ef99374bfac3c4b986e3528353dce3086a98a858e3fe000 +F ext/jni/src/org/sqlite/jni/Tester1.java 69ea63a5b235f94f914dff6fe3ecd103ee0a8023b8737db071b46c0c75375e26 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 @@ -2094,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 415447a310f6a7d06b4aa9ef51f110cf8e2ef9545c69cb5983c367c50fe641d2 -R fddefcd0dbe857a799e7e6b983a3459a +P d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 +R b911867dcaf18c3e131e156c82d306fe U stephan -Z b441a1d2bae36389c447acd3134d6be2 +Z bdef86836c9254e732e1b6d744febf17 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3df5deea50..5245bf9cfe 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 \ No newline at end of file +a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 \ No newline at end of file From bfdc7ab5a7c988e1b1d4b6361eeffbb5cd173eee Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 11:57:51 +0000 Subject: [PATCH 042/422] Add more JNI docs, tests, and a handful of Java-side overloads. FossilOrigin-Name: d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0 --- ext/jni/GNUmakefile | 19 +- ext/jni/README.md | 120 ++++++++++--- ext/jni/jar-dist.make | 6 +- ext/jni/src/c/sqlite3-jni.c | 48 ++--- ext/jni/src/c/sqlite3-jni.h | 8 + ext/jni/src/org/sqlite/jni/AutoExtension.java | 12 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 165 +++++++++++++++--- ext/jni/src/org/sqlite/jni/Tester1.java | 104 ++++++++--- ext/jni/src/org/sqlite/jni/TesterFts5.java | 21 ++- manifest | 28 +-- manifest.uuid | 2 +- 11 files changed, 395 insertions(+), 138 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index cc728003d7..6b32c40a82 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -223,19 +223,18 @@ $(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE) $(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h) $(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE) $(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \ - $(sqlite3-jni.c) -shared -o $@ -lpthread + $(sqlite3-jni.c) -shared -o $@ all: $(sqlite3-jni.dll) .PHONY: test -test.flags ?= -v +test.flags ?= +test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \ + $(java.flags) -cp $(classpath) \ + org.sqlite.jni.Tester1 test: $(SQLite3Jni.class) $(sqlite3-jni.dll) - $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ - $(java.flags) -cp $(classpath) \ - org.sqlite.jni.Tester1 $(test.flags) -test-mt: $(SQLite3Jni.class) $(sqlite3-jni.dll) - $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ - $(java.flags) -cp $(classpath) \ - org.sqlite.jni.Tester1 -t 7 -r 50 -shuffle $(test.flags) + $(bin.java) $(test.main.flags) $(test.flags) + @echo "Again in multi-threaded mode:" + $(bin.java) $(test.main.flags) -t 5 -r 20 -shuffle $(test.flags) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose @@ -271,7 +270,7 @@ endif tester-ext: tester-local tester: tester-ext -tests: test test-mt tester +tests: test tester package.jar.in := $(abspath $(dir.src)/jar.in) CLEAN_FILES += $(package.jar.in) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) diff --git a/ext/jni/README.md b/ext/jni/README.md index 90067556d2..6e5e07cc03 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -16,7 +16,7 @@ Technical support is available in the forum: > **FOREWARNING:** this subproject is very much in development and subject to any number of changes. Please do not rely on any information about its API until this disclaimer is removed. The JNI - bindgins released with version 3.43 are a "tech preview" and 3.44 + bindings released with version 3.43 are a "tech preview" and 3.44 will be "final," at which point strong backward compatibility guarantees will apply. @@ -43,29 +43,34 @@ Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. + Hello World ----------------------------------------------------------------------- ```java import org.sqlite.jni.*; -import static org.sqlite.jni.SQLite3Jni; +import static SQLite3Jni.*; + ... -OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); -int rc = sqlite3_open(":memory:", out); -final sqlite3 db = out.take(); -if( 0 != rc ){ - if( null != db ){ - System.out.print("Error opening db: "+sqlite3_errmsg(db)); - sqlite3_close(db); - }else{ - System.out.print("Error opening db: rc="+rc); + +final sqlite3 db = sqlite3_open(":memory:"); +try { + final int rc = sqlite3_errcode(db); + if( 0 != rc ){ + if( null != db ){ + System.out.print("Error opening db: "+sqlite3_errmsg(db)); + }else{ + System.out.print("Error opening db: rc="+rc); + } + ... handle error ... } - ... handle error ... + // ... else use the db ... +}finally{ + // ALWAYS close databases using sqlite3_close() or sqlite3_close_v2() + // when done with them. All of their active statement handles must + // first have been passed to sqlite3_finalize(). + sqlite3_close_v2(db); } - -... use db ... - -sqlite3_close_v2(db); ``` Building @@ -86,28 +91,90 @@ $ make test $ make clean ``` -The jar distribution can be created with `make jar`. +The jar distribution can be created with `make jar`, but note that it +does not contain the binary DLL file. A different DLL is needed for +each target platform. + One-to-One(-ish) Mapping to C ======================================================================== This JNI binding aims to provide as close to a 1-to-1 experience with -the C API as cross-language semantics allow. Changes are necessarily -made where cross-language semantics do not allow a 1-to-1, and -judiciously made where a 1-to-1 mapping would be unduly cumbersome to -use in Java. +the C API as cross-language semantics allow. Interface changes are +necessarily made where cross-language semantics do not allow a 1-to-1, +and judiciously made where a 1-to-1 mapping would be unduly cumbersome +to use in Java. In all cases, this binding makes every effort to +provide semantics compatible with the C API documentation even if the +interface to those semantics is slightly different. Any cases which +deviate from those semantics (either removing or adding semantics) are +clearly documented. -Golden Rule: _Never_ Throw from Callbacks (Unless...) +Where it makes sense to do so for usability, Java-side overloads are +provided which accept or return data in alternative forms or provide +sensible default argument values. In all such cases they are thin +proxies around the corresponding C APIs and do not introduce new +semantics. + +In some very few cases, Java-specific capabilities have been added in +new APIs, all of which have "_java" somewhere in their names. +Examples include: + +- `sqlite3_result_java_object()` +- `sqlite3_column_java_object()` +- `sqlite3_column_java_casted()` +- `sqlite3_value_java_object()` +- `sqlite3_value_java_casted()` + +which, as one might surmise, collectively enable the passing of +arbitrary Java objects from user-defined SQL functions through to the +caller. + + +Golden Rule: Garbage Collection Cannot Free SQLite Resources ------------------------------------------------------------------------ +It is important that all databases and prepared statement handles get +cleaned up by client code. A database cannot be closed if it has open +statement handles. `sqlite3_close()` fails if the db cannot be closed +whereas `sqlite3_close_v2()` recognizes that case and marks the db as +a "zombie," pending finalization when the library detects that all +pending statements have been closed. Be aware that Java garbage +collection _cannot_ close a database or finalize a prepared statement. +Those things require explicit API calls. + + +Golden Rule #2: _Never_ Throw from Callbacks (Unless...) +------------------------------------------------------------------------ + +All routines in this API, barring explicitly documented exceptions, +retain C-like semantics. For example, they are not permitted to throw +or propagate exceptions and must return error information (if any) via +result codes or `null`. The only cases where the C-style APIs may +throw is through client-side misuse, e.g. passing in a null where it +shouldn't be used. The APIs clearly mark function parameters which +should not be null, but does not actively defend itself against such +misuse. Some C-style APIs explicitly accept `null` as a no-op for +usability's sake, and some of the JNI APIs deliberately return an +error code, instead of segfaulting, when passed a `null`. + Client-defined callbacks _must never throw 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. In some cases, callback handlers (see below) are permitted -to throw, in which cases they get translated to C-level result codes -and/or messages. +resources. In some cases, callback handlers are permitted to throw, in +which cases they get translated to C-level result codes and/or +messages. If a callback which is not permitted to throw throws, its +exception may trigger debug output but will otherwise be suppressed. + +The reason some callbacks are permitted to throw and others not is +because all such callbacks act as proxies for C function callback +interfaces and some of those interfaces have no error-reporting +mechanism. Those which are capable of propagating errors back through +the library convert exceptions from callbacks into corresponding +C-level error information. Those which cannot propagate errors +necessarily suppress any exceptions in order to maintain the C-style +semantics of the APIs. Awkward Callback Names @@ -246,4 +313,5 @@ in-flux nature of this API. Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and `sqlite3_update_hook()`, use interfaces similar to those shown above. - +Despite the changes in signature, the JNI layer makes every effort to +provide the same semantics as the C API documentation suggests. diff --git a/ext/jni/jar-dist.make b/ext/jni/jar-dist.make index ac1a768b8c..7596c99f3f 100644 --- a/ext/jni/jar-dist.make +++ b/ext/jni/jar-dist.make @@ -46,11 +46,13 @@ $(sqlite3-jni.dll): echo "*** to configure it for your system. ***"; \ echo "************************************************************************" $(CC) $(CFLAGS) $(SQLITE_OPT) \ - src/sqlite3-jni.c -lpthread -shared -o $@ + src/sqlite3-jni.c -shared -o $@ @echo "Now try running it with: make test" +test.flags = -Djava.library.path=. sqlite3-jni-*.jar test: $(sqlite3-jni.dll) - java -jar -Djava.library.path=. sqlite3-jni-*.jar + java -jar $(test.flags) + java -jar $(test.flags) -t 7 -r 10 -shuffle clean: -rm -f $(sqlite3-jni.dll) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index debb221d6a..03ba8be57a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1429,7 +1429,7 @@ typedef struct { } ResultJavaVal; /* For use with sqlite3_result/value_pointer() */ -#define ResultJavaValuePtrStr "ResultJavaVal" +#define ResultJavaValuePtrStr "org.sqlite.jni.ResultJavaVal" /* ** Allocate a new ResultJavaVal and assign it a new global ref of @@ -1915,15 +1915,10 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ - int rc; - if(!baData){ - rc = sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), ndx); - }else{ - jbyte * const pBuf = JBA_TOC(baData); - rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, - SQLITE_TRANSIENT); - JBA_RELEASE(baData,pBuf); - } + jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, + pBuf, (int)nMax, SQLITE_TRANSIENT); + JBA_RELEASE(baData,pBuf); return (jint)rc; } @@ -1960,15 +1955,21 @@ JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ - if(baData){ - jbyte * const pBuf = JBA_TOC(baData); - int rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, - (int)nMax, SQLITE_TRANSIENT); - JBA_RELEASE(baData, pBuf); - return (jint)rc; - }else{ - return sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); - } + jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, + (const char *)pBuf, + (int)nMax, SQLITE_TRANSIENT); + JBA_RELEASE(baData, pBuf); + return (jint)rc; +} + +JDECL(jint,1bind_1text16)(JENV_CSELF, jobject jpStmt, + jint ndx, jbyteArray baData, jint nMax){ + jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + int const rc = sqlite3_bind_text16(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, + pBuf, (int)nMax, SQLITE_TRANSIENT); + JBA_RELEASE(baData, pBuf); + return (jint)rc; } JDECL(jint,1bind_1zeroblob)(JENV_CSELF, jobject jpStmt, @@ -3595,18 +3596,19 @@ JDECL(jbyteArray,1value_1text_1utf8)(JENV_CSELF, jobject jpSVal){ } static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){ - int const nLen = sqlite3_value_bytes16(PtrGet_sqlite3_value(jpSVal)); + sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); + int const nLen = sqlite3_value_bytes16(sv); jbyteArray jba; const jbyte * pBytes; switch(mode){ case SQLITE_UTF16: - pBytes = sqlite3_value_text16(PtrGet_sqlite3_value(jpSVal)); + pBytes = sqlite3_value_text16(sv); break; case SQLITE_UTF16LE: - pBytes = sqlite3_value_text16le(PtrGet_sqlite3_value(jpSVal)); + pBytes = sqlite3_value_text16le(sv); break; case SQLITE_UTF16BE: - pBytes = sqlite3_value_text16be(PtrGet_sqlite3_value(jpSVal)); + pBytes = sqlite3_value_text16be(sv); break; default: assert(!"not possible"); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 86fada9574..8052a6027e 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -843,6 +843,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1parameter_1 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1text (JNIEnv *, jclass, jobject, jint, jbyteArray, jint); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_bind_text16 + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I[BI)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1text16 + (JNIEnv *, jclass, jobject, jint, jbyteArray, jint); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_bind_zeroblob diff --git a/ext/jni/src/org/sqlite/jni/AutoExtension.java b/ext/jni/src/org/sqlite/jni/AutoExtension.java index fcad273d3d..443345fde4 100644 --- a/ext/jni/src/org/sqlite/jni/AutoExtension.java +++ b/ext/jni/src/org/sqlite/jni/AutoExtension.java @@ -14,24 +14,24 @@ package org.sqlite.jni; /** - A callback for use with sqlite3_auto_extension(). + A callback for use with the sqlite3_auto_extension() family of + APIs. */ public interface AutoExtension { /** Must function as described for a sqlite3_auto_extension() - callback, with the caveat that the signature is more limited. + callback, with the caveat that the signature is shorter. - As an exception (as it were) to the callbacks-must-not-throw - rule, AutoExtensions may throw and the exception's error message + AutoExtensions may throw and the exception's error message will be set as the db's error string. - Hints for implementations: + Tips for implementations: - Opening a database from an auto-extension handler will lead to an endless recursion of the auto-handler triggering itself indirectly for each newly-opened database. - - If this routine is stateful, it is a good idea to make the + - If this routine is stateful, it may be useful to make the overridden method synchronized. - Results are undefined if db is closed by an auto-extension. diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 29ed0e5602..ef67890918 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -154,32 +154,35 @@ public final class SQLite3Jni { Functions almost as documented for the C API, with these exceptions: - - The callback interface is more limited because of - cross-language differences. Specifically, auto-extensions do - not have access to the sqlite3_api object which native - auto-extensions do. + - The callback interface is is shorter because of cross-language + differences. Specifically, 3rd argument to the C auto-extension + callback interface is unnecessary here. - - If the list of auto-extensions is manipulated from an - auto-extension, it is undefined which, if any, auto-extensions - will subsequently execute for the current database (it depends - on multiple factors). + + The C API docs do not specifically say so, if the list of + auto-extensions is manipulated from an auto-extension, it is + undefined which, if any, auto-extensions will subsequently + execute for the current database. See the AutoExtension class docs for more information. */ public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); + /** + Results are undefined if data is not null and n<0 || n>=data.length. + */ + public static native int sqlite3_bind_blob( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n + ); + public static int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data ){ - return (null == data) + return (null==data) ? sqlite3_bind_null(stmt, ndx) : sqlite3_bind_blob(stmt, ndx, data, data.length); } - private static native int sqlite3_bind_blob( - @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n - ); - public static native int sqlite3_bind_double( @NotNull sqlite3_stmt stmt, int ndx, double v ); @@ -200,13 +203,10 @@ public final class SQLite3Jni { @NotNull sqlite3_stmt stmt ); - /** - A level of indirection required to ensure that the input to the - C-level function of the same name is a NUL-terminated UTF-8 - string. + Requires that paramName be a NUL-terminated UTF-8 string. */ - private static native int sqlite3_bind_parameter_index( + public static native int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, byte[] paramName ); @@ -218,14 +218,22 @@ public final class SQLite3Jni { } /** - Works like the C-level sqlite3_bind_text() but (A) assumes - SQLITE_TRANSIENT for the final parameter and (B) behaves like - sqlite3_bind_null() if the data argument is null. + Works like the C-level sqlite3_bind_text() but assumes + SQLITE_TRANSIENT for the final C API parameter. + + Results are undefined if data is not null and + maxBytes>=data.length. If maxBytes is negative then results are + undefined if data is not null and does not contain a NUL byte. */ private static native int sqlite3_bind_text( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes ); + /** + Converts data, if not null, to a UTF-8-encoded byte array and + binds it as such, returning the result of the C-level + sqlite3_bind_null() or sqlite3_bind_text(). + */ public static int sqlite3_bind_text( @NotNull sqlite3_stmt stmt, int ndx, @Nullable String data ){ @@ -234,6 +242,9 @@ public final class SQLite3Jni { return sqlite3_bind_text(stmt, ndx, utf8, utf8.length); } + /** + Requires that data be null or in UTF-8 encoding. + */ public static int sqlite3_bind_text( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data ){ @@ -242,6 +253,41 @@ public final class SQLite3Jni { : sqlite3_bind_text(stmt, ndx, data, data.length); } + /** + Identical to the sqlite3_bind_text() overload with the same + signature but requires that its input be encoded in UTF-16 in + platform byte order. + */ + private static native int sqlite3_bind_text16( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes + ); + + /** + Converts its string argument to UTF-16 and binds it as such, returning + the result of the C-side function of the same name. The 3rd argument + may be null. + */ + public static int sqlite3_bind_text16( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable String data + ){ + if(null == data) return sqlite3_bind_null(stmt, ndx); + final byte[] bytes = data.getBytes(StandardCharsets.UTF_16); + return sqlite3_bind_text16(stmt, ndx, bytes, bytes.length); + } + + /** + Requires that data be null or in UTF-16 encoding in platform byte + order. Returns the result of the C-level sqlite3_bind_null() or + sqlite3_bind_text(). + */ + public static int sqlite3_bind_text16( + @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data + ){ + return (null == data) + ? sqlite3_bind_null(stmt, ndx) + : sqlite3_bind_text16(stmt, ndx, data, data.length); + } + public static native int sqlite3_bind_zeroblob( @NotNull sqlite3_stmt stmt, int ndx, int n ); @@ -253,8 +299,7 @@ public final class SQLite3Jni { /** As for the C-level function of the same name, with a BusyHandler instance in place of a callback function. Pass it a null handler - to clear the busy handler. Calling this multiple times with the - same object is a no-op on the second and subsequent calls. + to clear the busy handler. */ public static native int sqlite3_busy_handler( @NotNull sqlite3 db, @Nullable BusyHandler handler @@ -595,15 +640,41 @@ public final class SQLite3Jni { @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb ); + /** + Convenience overload which returns its db handle directly. The returned + object might not have been successfully opened: use sqlite3_errcode() to + check whether it is in an error state. + + Ownership of the returned value is passed to the caller, who must eventually + pass it to sqlite3_close() or sqlite3_close_v2(). + */ + public static sqlite3 sqlite3_open(@Nullable String filename){ + final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); + sqlite3_open(filename, out); + return out.take(); + }; + public static native int sqlite3_open_v2( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, int flags, @Nullable String zVfs ); + /** + Has the same semantics as the sqlite3-returning sqlite3_open() + but uses sqlite3_open_v2() instead of sqlite3_open(). + */ + public static sqlite3 sqlite3_open_v2(@Nullable String filename, int flags, + @Nullable String zVfs){ + final OutputPointer.sqlite3 out = new OutputPointer.sqlite3(); + sqlite3_open_v2(filename, out, flags, zVfs); + return out.take(); + }; + /** The sqlite3_prepare() family of functions require slightly - different signatures than their native counterparts, but - overloading allows us to install several convenience forms. + different signatures than their native counterparts, but (A) they + retain functionally equivalent semantics and (B) overloading + allows us to install several convenience forms. All of them which take their SQL in the form of a byte[] require that it be in UTF-8 encoding unless explicitly noted otherwise. @@ -648,6 +719,26 @@ public final class SQLite3Jni { return sqlite3_prepare(db, utf8, utf8.length, outStmt, null); } + /** + Convenience overload which returns its statement handle directly, + or null on error or when reading only whitespace or + comments. sqlite3_errcode() can be used to determine whether + there was an error or the input was empty. Ownership of the + returned object is passed to the caller, who must eventually pass + it to sqlite3_finalize(). + */ + public static sqlite3_stmt sqlite3_prepare( + @NotNull sqlite3 db, @NotNull String sql + ){ + final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); + sqlite3_prepare(db, sql, out); + return out.take(); + } + + /** + See sqlite3_prepare() for details about the slight API differences + from the C API. + */ private static native int sqlite3_prepare_v2( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull OutputPointer.sqlite3_stmt outStmt, @@ -677,6 +768,18 @@ public final class SQLite3Jni { return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null); } + /** + Works identically to the sqlite3_stmt-returning sqlite3_prepare() + but uses sqlite3_prepare_v2(). + */ + public static sqlite3_stmt sqlite3_prepare_v2( + @NotNull sqlite3 db, @NotNull String sql + ){ + final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); + sqlite3_prepare_v2(db, sql, out); + return out.take(); + } + private static native int sqlite3_prepare_v3( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt, @@ -706,6 +809,18 @@ public final class SQLite3Jni { return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null); } + /** + Works identically to the sqlite3_stmt-returning sqlite3_prepare() + but uses sqlite3_prepare_v3(). + */ + public static sqlite3_stmt sqlite3_prepare_v3( + @NotNull sqlite3 db, @NotNull String sql, int prepFlags + ){ + final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt(); + sqlite3_prepare_v3(db, sql, prepFlags, 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_blobwrite(), else it returns diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index c655ae7ffd..bafc73bf68 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -21,6 +21,16 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +/** + An annotation for Tester1 tests which we do not want to run in + reflection-driven test mode because either they are not suitable + for multi-threaded threaded mode or we have to control their execution + order. +*/ +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) +@interface ManualTest{} + public class Tester1 implements Runnable { //! True when running in multi-threaded mode. private static boolean mtMode = false; @@ -30,6 +40,10 @@ public class Tester1 implements Runnable { private static boolean shuffle = false; //! True to dump the list of to-run tests to stdout. private static boolean listRunTests = false; + //! True to squelch all out() and outln() output. + private static boolean quietMode = false; + //! Total number of runTests() calls. + private static int nTestRuns = 0; //! List of test*() methods to run. private static List testMethods = null; //! List of exceptions collected by run() @@ -48,27 +62,37 @@ public class Tester1 implements Runnable { static final Metrics metrics = new Metrics(); public synchronized static void outln(){ - System.out.println(""); + if( !quietMode ){ + System.out.println(""); + } } public synchronized static void outln(Object val){ - System.out.print(Thread.currentThread().getName()+": "); - System.out.println(val); + if( !quietMode ){ + System.out.print(Thread.currentThread().getName()+": "); + System.out.println(val); + } } public synchronized static void out(Object val){ - System.out.print(val); + if( !quietMode ){ + System.out.print(val); + } } @SuppressWarnings("unchecked") public synchronized static void out(Object... vals){ - System.out.print(Thread.currentThread().getName()+": "); - for(Object v : vals) out(v); + if( !quietMode ){ + System.out.print(Thread.currentThread().getName()+": "); + for(Object v : vals) out(v); + } } @SuppressWarnings("unchecked") public synchronized static void outln(Object... vals){ - out(vals); out("\n"); + if( !quietMode ){ + out(vals); out("\n"); + } } static volatile int affirmCount = 0; @@ -85,6 +109,7 @@ public class Tester1 implements Runnable { affirm(v, "Affirmation failed."); } + @ManualTest /* because testing this for threading is pointless */ private void test1(){ affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); affirm(SQLITE_MAX_LENGTH > 0); @@ -280,6 +305,16 @@ public class Tester1 implements Runnable { affirm(0 != stmt.getNativePointer()); sqlite3_finalize(stmt); affirm(0 == stmt.getNativePointer() ); + + affirm( 0==sqlite3_errcode(db) ); + stmt = sqlite3_prepare(db, "intentional error"); + affirm( null==stmt ); + affirm( 0!=sqlite3_errcode(db) ); + affirm( 0==sqlite3_errmsg(db).indexOf("near \"intentional\"") ); + sqlite3_finalize(stmt); + stmt = sqlite3_prepare(db, "/* empty input*/\n-- comments only"); + affirm( null==stmt ); + affirm( 0==sqlite3_errcode(db) ); sqlite3_close_v2(db); } @@ -383,8 +418,11 @@ public class Tester1 implements Runnable { sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); String[] list1 = { "hell🤩", "w😃rld", "!" }; int rc; + int n = 0; for( String e : list1 ){ - rc = sqlite3_bind_text(stmt, 1, e); + rc = (0==n) + ? sqlite3_bind_text(stmt, 1, e) + : sqlite3_bind_text16(stmt, 1, e); affirm(0 == rc); rc = sqlite3_step(stmt); affirm(SQLITE_DONE==rc); @@ -393,7 +431,7 @@ public class Tester1 implements Runnable { sqlite3_finalize(stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); StringBuilder sbuf = new StringBuilder(); - int n = 0; + n = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ String txt = sqlite3_column_text16(stmt, 0); //outln("txt = "+txt); @@ -521,6 +559,7 @@ public class Tester1 implements Runnable { affirm(xDestroyCalled.value); } + @ManualTest /* because threading is meaningless here */ private void testToUtf8(){ /** https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html @@ -873,6 +912,7 @@ public class Tester1 implements Runnable { affirm( 7 == counter.value ); } + @ManualTest /* because threads inherently break this test */ private void testBusy(){ final String dbName = "_busy-handler.db"; final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); @@ -1156,6 +1196,8 @@ public class Tester1 implements Runnable { it throws. */ @SuppressWarnings("unchecked") + @ManualTest /* because the Fts5 parts are not yet known to be + thread-safe */ private void testFts5() throws Exception { if( !sqlite3_compileoption_used("ENABLE_FTS5") ){ //outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); @@ -1206,6 +1248,8 @@ public class Tester1 implements Runnable { sqlite3_close(db); } + @ManualTest/* because multiple threads legitimately make these + results unpredictable */ private synchronized void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); @@ -1296,6 +1340,7 @@ public class Tester1 implements Runnable { affirm( 8 == val.value ); } + @ManualTest /* because we only want to run this test manually */ private void testSleep(){ out("Sleeping briefly... "); sqlite3_sleep(600); @@ -1308,6 +1353,7 @@ public class Tester1 implements Runnable { } } + @ManualTest /* because we only want to run this test on demand */ private void testFail(){ affirm( false, "Intentional failure." ); } @@ -1355,6 +1401,9 @@ public class Tester1 implements Runnable { testFts5(); } } + synchronized( this.getClass() ){ + ++nTestRuns; + } } public void run() { @@ -1375,6 +1424,8 @@ public class Tester1 implements Runnable { CLI flags: + -q|-quiet: disables most test output. + -t|-thread N: runs the tests in N threads concurrently. Default=1. @@ -1400,6 +1451,7 @@ public class Tester1 implements Runnable { Integer nRepeat = 1; boolean forceFail = false; boolean sqlLog = false; + boolean squelchTestOutput = false; for( int i = 0; i < args.length; ){ String arg = args[i++]; if(arg.startsWith("-")){ @@ -1421,6 +1473,8 @@ public class Tester1 implements Runnable { sqlLog = true; }else if(arg.equals("naps")){ takeNaps = true; + }else if(arg.equals("q") || arg.equals("quiet")){ + squelchTestOutput = true; }else{ throw new IllegalArgumentException("Unhandled flag:"+arg); } @@ -1430,19 +1484,16 @@ public class Tester1 implements Runnable { { // Build list of tests to run from the methods named test*(). testMethods = new ArrayList<>(); - final List excludes = new ArrayList<>(); - // Tests we want to control the order of: - if( !forceFail ) excludes.add("testFail"); - excludes.add("test1"); - excludes.add("testAutoExtension"); - excludes.add("testBusy"); - excludes.add("testFts5"); - excludes.add("testSleep"); - excludes.add("testToUtf8"); - for(java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ + for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ final String name = m.getName(); - if( name.startsWith("test") && excludes.indexOf(name)<0 ){ - testMethods.add(m); + if( name.equals("testFail") ){ + if( forceFail ){ + testMethods.add(m); + } + }else if( !m.isAnnotationPresent( ManualTest.class ) ){ + if( name.startsWith("test") ){ + testMethods.add(m); + } } } } @@ -1465,6 +1516,11 @@ public class Tester1 implements Runnable { } } + quietMode = squelchTestOutput; + outln("If you just saw warning messages regarding CallStaticObjectMethod, ", + "you are very likely seeing the side effects of a known openjdk8 ", + "bug. It is unsightly but does not affect the library."); + final long timeStart = System.currentTimeMillis(); int nLoop = 0; affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), @@ -1476,7 +1532,7 @@ public class Tester1 implements Runnable { outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); - outln("Running ",nRepeat," loop(s) over ",nThread," thread(s)."); + outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ if( nThread==null || nThread<=1 ){ @@ -1500,6 +1556,7 @@ public class Tester1 implements Runnable { Thread.currentThread().interrupt(); } if( !listErrors.isEmpty() ){ + quietMode = false; outln("TEST ERRORS:"); Exception err = null; for( Exception e : listErrors ){ @@ -1510,9 +1567,10 @@ public class Tester1 implements Runnable { } } outln(); + quietMode = false; final long timeEnd = System.currentTimeMillis(); - outln("Tests done. Metrics:"); + outln("Tests done. Metrics across ",nTestRuns," total iteration(s):"); outln("\tAssertions checked: ",affirmCount); outln("\tDatabases opened: ",metrics.dbOpen); if( doSomethingForDev ){ diff --git a/ext/jni/src/org/sqlite/jni/TesterFts5.java b/ext/jni/src/org/sqlite/jni/TesterFts5.java index 3856573169..feb6d6303d 100644 --- a/ext/jni/src/org/sqlite/jni/TesterFts5.java +++ b/ext/jni/src/org/sqlite/jni/TesterFts5.java @@ -72,13 +72,18 @@ public class TesterFts5 { affirm( xDestroyCalled.value ); } - public TesterFts5(){ - final long timeStart = System.currentTimeMillis(); - final int oldAffirmCount = Tester1.affirmCount; - test1(); - final int affirmCount = Tester1.affirmCount - oldAffirmCount; - final long timeEnd = System.currentTimeMillis(); - outln("FTS5 Tests done. Assertions checked = ",affirmCount, - ", Total time = ",(timeEnd - timeStart),"ms"); + public TesterFts5(boolean verbose){ + if(verbose){ + final long timeStart = System.currentTimeMillis(); + final int oldAffirmCount = Tester1.affirmCount; + test1(); + final int affirmCount = Tester1.affirmCount - oldAffirmCount; + final long timeEnd = System.currentTimeMillis(); + outln("FTS5 Tests done. Assertions checked = ",affirmCount, + ", Total time = ",(timeEnd - timeStart),"ms"); + }else{ + test1(); + } } + public TesterFts5(){ this(false); } } diff --git a/manifest b/manifest index f1c1761dd4..2da52989e3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\scleanups\sregarding\sbuilding\swith\scertain\sfeatures\sdisabled. -D 2023-08-23T17:52:51.175 +C Add\smore\sJNI\sdocs,\stests,\sand\sa\shandful\sof\sJava-side\soverloads. +D 2023-08-24T11:57:51.863 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,13 +232,13 @@ 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 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 -F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0 -F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 -F ext/jni/src/c/sqlite3-jni.c 0ca96134d7fb3f313a7a49487f68a8d7a6d7545470c84532aa1ce63d2cdc432e -F ext/jni/src/c/sqlite3-jni.h c5cb0348efe4e5f3d125a240e2437e8475de14a586c2f859e2acdcde4116244d +F ext/jni/GNUmakefile 0a823c56f081294e7797dae303380ac989ebaa801bba970968342b7358f07aed +F ext/jni/README.md 64bf1da0d562d051207ca1c5cfa52e8b7a69120533cc034a3da7670ef920cbef +F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa +F ext/jni/src/c/sqlite3-jni.c e1e3cde4d08925282b5bc949f9ed8f613a6a2c6f60d0c697e79d59fb49f9fe4b +F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2 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/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 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 ad67843b6dd1c06b6b0a1dc72887b7c48e2a98042fcf6cacf14d42444037eab8 @@ -257,9 +257,9 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 f64554457fa30a048ef99374bfac3c4b986e3528353dce3086a98a858e3fe000 -F ext/jni/src/org/sqlite/jni/Tester1.java 69ea63a5b235f94f914dff6fe3ecd103ee0a8023b8737db071b46c0c75375e26 -F ext/jni/src/org/sqlite/jni/TesterFts5.java de095e3b701fba0c56d7b8b2993dc22bcbaa9de8f992904a93729ad729a91576 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 956063c854c4f662183c41c65d0ab48b5e2127824b8053eeb05b9fc40f0d09e3 +F ext/jni/src/org/sqlite/jni/Tester1.java b5a4bb2a969df053d5c138887f04039a79b36170372a2efdf5dfbd6ac90db4c9 +F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee @@ -2094,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 d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 -R b911867dcaf18c3e131e156c82d306fe +P a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 +R cb79df5ad40cc86377d98f5a1329b589 U stephan -Z bdef86836c9254e732e1b6d744febf17 +Z 39e8fbdcbc2ebaa58ed84b96f0374c06 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5245bf9cfe..180d574846 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 \ No newline at end of file +d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0 \ No newline at end of file From 6043947eee46fdbd63bfede74bc969ad6ea22c30 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 14:17:30 +0000 Subject: [PATCH 043/422] Ensure that db mutex is held when using sqlite3ErrorWithMessage() to avoid a potential assertion in debug builds. FossilOrigin-Name: b8f6a50a4bf9478324f0272d79f2fe6992a49b753e79e39a268c6afb261bb01e --- ext/wasm/api/sqlite3-wasm.c | 2 ++ manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index fcfbc06929..ff15e3b4fb 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -352,7 +352,9 @@ int sqlite3_wasm_db_error(sqlite3*db, int err_code, const char *zMsg){ if( db!=0 ){ if( 0!=zMsg ){ const int nMsg = sqlite3Strlen30(zMsg); + sqlite3_mutex_enter(sqlite3_db_mutex(db)); sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg); + sqlite3_mutex_leave(sqlite3_db_mutex(db)); }else{ sqlite3ErrorWithMsg(db, err_code, NULL); } diff --git a/manifest b/manifest index 25437bb3be..539b84375f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sexception\shandling\sin\sOpfsDb.importDb(). -D 2023-08-18T14:41:21.668 +C Ensure\sthat\sdb\smutex\sis\sheld\swhen\susing\ssqlite3ErrorWithMessage()\sto\savoid\sa\spotential\sassertion\sin\sdebug\sbuilds. +D 2023-08-24T14:17:30.272 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -550,7 +550,7 @@ F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 561463ac5380e4ccf1839a1922e6d7a5585660f32e3b9701a270b78cd35566cf F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js d9e62d42b86f7bb3143eb071628b24e2ba7dcc749e41a0e9d3e2451bfea1a6b6 -F ext/wasm/api/sqlite3-wasm.c d4d4c2b349b43b7b861e6d2994299630fb79e07573ea6b61e28e8071b7d16b61 +F ext/wasm/api/sqlite3-wasm.c 6773e949034369ddd2a1efdedc39b2808a10b7274b0769188905432e561feebe F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -2091,8 +2091,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 9b1398c96a4fd0b59e65faa8d5c98de4129f0f0357732f12cb2f5c53a08acdc2 -R db128ff4a61eed058302d85bb5b8cb1c +P a4eedd63b58f5c273b671f687cb68a32259963dbb053a710770383c735fb7f26 +R a8ed4c07ea7e64b510f57a0d91378964 U stephan -Z 10bacbfa41c608cf282110ffda9bd8fa +Z 37b33fa5d7dd5ab7079270bdd3d45073 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c605058805..73fc39b325 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a4eedd63b58f5c273b671f687cb68a32259963dbb053a710770383c735fb7f26 \ No newline at end of file +b8f6a50a4bf9478324f0272d79f2fe6992a49b753e79e39a268c6afb261bb01e \ No newline at end of file From e7a46858371a7146fce62f96bb1dbae94cf9b4f3 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 14:31:36 +0000 Subject: [PATCH 044/422] Correct JNI layer's misuse of an sqlite3-internal error-reporting API (no mutex held). Style cleanups. Eliminate lookups of per-thread state by approximately 85% across the test suite. FossilOrigin-Name: 1f46ba8d3bc61af771c1e33d09ad25f0da4fc4f915f7a9f6223ebfd99526d81d --- ext/jni/src/c/sqlite3-jni.c | 172 +++++++++++------------- ext/jni/src/org/sqlite/jni/Tester1.java | 3 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 92 insertions(+), 99 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 03ba8be57a..7c4aa7c28b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -303,7 +303,7 @@ static const struct { /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ #define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ JDECL(jstring,JniNameSuffix)(JENV_CSELF, jobject pStmt, jint ndx){ \ - return s3jni_utf8_to_jstring(S3JniGlobal_env_cache(env), \ + return s3jni_utf8_to_jstring(env, \ CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ -1); \ } @@ -324,10 +324,10 @@ static const struct { } /* Helpers for jstring and jbyteArray. */ -#define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) -#define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR) -#define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) -#define JBA_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) +#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) +#define s3jni_mutf8_release(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR) +#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) +#define s3jni_jbytearray_release(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) enum { /* @@ -423,9 +423,7 @@ struct S3JniEnv { ** ** - In the JNI side of sqlite3_open(), allocate the Java side of ** that connection and set pdbOpening to point to that - ** object. Note that it's per-thread, and we remain in that - ** thread until after the auto-extensions are run, so we don't - ** need to mutex-lock this. + ** object. ** ** - Call sqlite3_open(), which triggers the auto-extension ** handler. That handler uses pdbOpening to connect the native @@ -631,7 +629,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) -#define OOM_CHECK(VAR) if(!(VAR)) s3jni_oom(env) +#define s3jni_oom_check(VAR) if(!(VAR)) s3jni_oom(env) static inline void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; } @@ -706,7 +704,9 @@ static int s3jni_db_error(sqlite3* const db, int err_code, sqlite3Error(db, err_code); }else{ const int nMsg = sqlite3Strlen30(zMsg); + sqlite3_mutex_enter(sqlite3_db_mutex(db)); sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg); + sqlite3_mutex_leave(sqlite3_db_mutex(db)); } } return err_code; @@ -753,10 +753,9 @@ static JNIEnv * s3jni_get_env(void){ ** standard UTF-8 to a Java string, but JNI offers only algorithms for ** working with MUTF-8, not UTF-8. */ -static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, +static jstring s3jni_utf8_to_jstring(JNIEnv * const env, const char * const z, int n){ jstring rv = NULL; - LocalJniGetEnv; if( 0==n || (n<0 && z && !z[0]) ){ /* Fast-track the empty-string case via the MUTF-8 API. We could hypothetically do this for any strings where n<4 and z is @@ -790,9 +789,8 @@ static jstring s3jni_utf8_to_jstring(S3JniEnv * const jc, ** The returned memory is allocated from sqlite3_malloc() and ** ownership is transferred to the caller. */ -static char * s3jni_jstring_to_utf8(S3JniEnv * const jc, +static char * s3jni_jstring_to_utf8(JNIEnv * const env, jstring jstr, int *nLen){ - LocalJniGetEnv; jbyteArray jba; jsize nBa; char *rv; @@ -844,7 +842,6 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, ** System.out.println(e.getMessage()); // Hi */ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ - S3JniEnv * const jc = S3JniGlobal_env_cache(env); jmethodID mid; jstring msg; char * zMsg; @@ -862,7 +859,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ EXCEPTION_CLEAR; return 0; } - zMsg = s3jni_jstring_to_utf8(jc, msg, 0); + zMsg = s3jni_jstring_to_utf8(env, msg, 0); UNREF_L(msg); return zMsg; } @@ -1484,7 +1481,7 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const } rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); EXCEPTION_IS_FATAL("No-arg constructor threw."); - OOM_CHECK(rv); + s3jni_oom_check(rv); if(rv) NativePointerHolder_set(env, rv, pNative, pRef); return rv; } @@ -1807,6 +1804,16 @@ 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) +#undef WRAP_INT64_DB +#undef WRAP_INT_DB +#undef WRAP_INT_INT +#undef WRAP_INT_STMT +#undef WRAP_INT_STMT_INT +#undef WRAP_INT_SVALUE +#undef WRAP_INT_VOID +#undef WRAP_MUTF8_VOID +#undef WRAP_STR_STMT_INT + /* Central auto-extension handler. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ @@ -1915,10 +1922,10 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ - jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); - JBA_RELEASE(baData,pBuf); + s3jni_jbytearray_release(baData,pBuf); return (jint)rc; } @@ -1944,31 +1951,31 @@ JDECL(jint,1bind_1null)(JENV_CSELF, jobject jpStmt, JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName){ int rc = 0; - jbyte * const pBuf = JBA_TOC(jName); + jbyte * const pBuf = s3jni_jbytearray_bytes(jName); if(pBuf){ rc = sqlite3_bind_parameter_index(PtrGet_sqlite3_stmt(jpStmt), (const char *)pBuf); - JBA_RELEASE(jName, pBuf); + s3jni_jbytearray_release(jName, pBuf); } return rc; } JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ - jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, (int)nMax, SQLITE_TRANSIENT); - JBA_RELEASE(baData, pBuf); + s3jni_jbytearray_release(baData, pBuf); return (jint)rc; } JDECL(jint,1bind_1text16)(JENV_CSELF, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax){ - jbyte * const pBuf = baData ? JBA_TOC(baData) : 0; + jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_text16(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); - JBA_RELEASE(baData, pBuf); + s3jni_jbytearray_release(baData, pBuf); return (jint)rc; } @@ -2295,10 +2302,10 @@ JDECL(jstring,1compileoption_1get)(JENV_CSELF, jint n){ } JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ - const char *zUtf8 = JSTR_TOC(name); + const char *zUtf8 = s3jni_jstring_to_mutf8(name); const jboolean rc = 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; - JSTR_RELEASE(name, zUtf8); + s3jni_mutf8_release(name, zUtf8); return rc; } @@ -2323,7 +2330,6 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int jobject jArg0 = 0; jstring jArg1 = 0; LocalJniGetEnv; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); S3JniHook * const hook = &SJG.hooks.sqllog; @@ -2332,7 +2338,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int switch(op){ case 0: /* db opened */ case 1: /* SQL executed */ - jArg1 = s3jni_utf8_to_jstring(jc, z, -1); + jArg1 = s3jni_utf8_to_jstring(env, z, -1); break; case 2: /* db closed */ break; @@ -2420,11 +2426,11 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); } - zName = JSTR_TOC(name); + zName = s3jni_jstring_to_mutf8(name); rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, ps, CollationState_xCompare, CollationState_xDestroy); - JSTR_RELEASE(name, zName); + s3jni_mutf8_release(name, zName); if( 0==rc ){ pHook->jObj = REF_G(oCollation); }else{ @@ -2438,7 +2444,7 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, S3JniUdf * s = 0; int rc; sqlite3 * const pDb = PtrGet_sqlite3(jDb); - const char * zFuncName = 0; + char * zFuncName = 0; if( !encodingTypeIsValid(eTextRep) ){ return s3jni_db_error(pDb, SQLITE_FORMAT, @@ -2452,7 +2458,7 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, S3JniUdf_free(s); goto error_cleanup; } - zFuncName = JSTR_TOC(jFuncName); + zFuncName = s3jni_jstring_to_utf8(env,jFuncName,0); if(!zFuncName){ rc = SQLITE_NOMEM; S3JniUdf_free(s); @@ -2476,14 +2482,8 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, rc = sqlite3_create_function_v2(pDb, zFuncName, nArg, eTextRep, s, xFunc, xStep, xFinal, S3JniUdf_finalizer); } - if( 0==rc ){ - s->zFuncName = sqlite3_mprintf("%s", zFuncName) - /* OOM here is non-fatal. Ignore it. Handling it would require - ** re-calling the appropriate create_function() func with 0 - ** for all xAbc args so that s would be finalized. */; - } error_cleanup: - JSTR_RELEASE(jFuncName, zFuncName); + sqlite3_free(zFuncName); /* on sqlite3_create_function() error, s will be destroyed via ** create_function(), so we're not leaking s. */ return (jint)rc; @@ -2499,7 +2499,7 @@ JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( switch( (ps && jStr) ? op : 0 ){ case SQLITE_DBCONFIG_MAINDBNAME: - zStr = s3jni_jstring_to_utf8(S3JniGlobal_env_cache(env), jStr, 0); + zStr = s3jni_jstring_to_utf8(env, jStr, 0); if( zStr ){ rc = sqlite3_db_config(ps->pDb, (int)op, zStr); if( rc ){ @@ -2577,7 +2577,6 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - S3JniEnv * const jc = ps ? S3JniGlobal_env_cache(env) : 0; char *zDbName; jstring jRv = 0; int nStr = 0; @@ -2585,12 +2584,12 @@ JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ if( !ps || !jDbName ){ return 0; } - zDbName = s3jni_jstring_to_utf8(jc, jDbName, &nStr); + zDbName = s3jni_jstring_to_utf8(env, jDbName, &nStr); if( zDbName ){ char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); sqlite3_free(zDbName); if( zRv ){ - jRv = s3jni_utf8_to_jstring(jc, zRv, -1); + jRv = s3jni_utf8_to_jstring(env, zRv, -1); } } return jRv; @@ -2617,8 +2616,7 @@ JDECL(jint,1errcode)(JENV_CSELF, jobject jpDb){ JDECL(jstring,1errmsg)(JENV_CSELF, jobject jpDb){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - S3JniEnv * const jc = pDb ? S3JniGlobal_env_cache(env) : 0; - return jc ? s3jni_utf8_to_jstring(jc, sqlite3_errmsg(pDb), -1) : 0; + return pDb ? s3jni_utf8_to_jstring(env, sqlite3_errmsg(pDb), -1) : 0; } JDECL(jstring,1errstr)(JENV_CSELF, jint rcCode){ @@ -2631,11 +2629,10 @@ JDECL(jstring,1expanded_1sql)(JENV_CSELF, jobject jpStmt){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); jstring rv = 0; if( pStmt ){ - S3JniEnv * const jc = S3JniGlobal_env_cache(env); char * zSql = sqlite3_expanded_sql(pStmt); - OOM_CHECK(zSql); + s3jni_oom_check(zSql); if( zSql ){ - rv = s3jni_utf8_to_jstring(jc, zSql, -1); + rv = s3jni_utf8_to_jstring(env, zSql, -1); sqlite3_free(zSql); } } @@ -2692,7 +2689,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, rc = SQLITE_NOMEM; goto end; } - *zDbName = jDbName ? s3jni_jstring_to_utf8(*jc, jDbName, 0) : 0; + *zDbName = jDbName ? s3jni_jstring_to_utf8(env, jDbName, 0) : 0; if(jDbName && !*zDbName){ rc = SQLITE_NOMEM; goto end; @@ -2771,7 +2768,7 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, char *zVfs = 0; int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc && strVfs ){ - zVfs = s3jni_jstring_to_utf8(jc, strVfs, 0); + zVfs = s3jni_jstring_to_utf8(env, strVfs, 0); if( !zVfs ){ rc = SQLITE_NOMEM; } @@ -2794,7 +2791,7 @@ static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass sqlite3_stmt * pStmt = 0; jobject jStmt = 0; const char * zTail = 0; - jbyte * const pBuf = JBA_TOC(baSql); + jbyte * const pBuf = s3jni_jbytearray_bytes(baSql); int rc = SQLITE_ERROR; assert(prepVersion==1 || prepVersion==2 || prepVersion==3); if( !pBuf ){ @@ -2821,7 +2818,7 @@ static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass assert(0 && "Invalid prepare() version"); } end: - JBA_RELEASE(baSql,pBuf); + s3jni_jbytearray_release(baSql,pBuf); if( 0==rc ){ if( 0!=outTail ){ /* Noting that pBuf is deallocated now but its address is all we need for @@ -2879,7 +2876,6 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, sqlite3_int64 iKey1, sqlite3_int64 iKey2){ S3JniDb * const ps = pState; LocalJniGetEnv; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); jstring jDbName; jstring jTable; S3JniHook * pHook; @@ -2894,8 +2890,8 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, : &ps->hooks.update; assert( pHook ); - jDbName = s3jni_utf8_to_jstring(jc, zDb, -1); - jTable = jDbName ? s3jni_utf8_to_jstring(jc, zTable, -1) : 0; + jDbName = s3jni_utf8_to_jstring(env, zDb, -1); + jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0; IFTHREW { EXCEPTION_CLEAR; s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); @@ -3131,7 +3127,7 @@ static void result_blob_text(int asBlob, int as64, JNIEnv * const env, sqlite3_context *pCx, jbyteArray jBa, jlong nMax){ if(jBa){ - jbyte * const pBuf = JBA_TOC(jBa); + jbyte * const pBuf = s3jni_jbytearray_bytes(jBa); jsize nBa = (*env)->GetArrayLength(env, jBa); if( nMax>=0 && nBa>(jsize)nMax ){ nBa = (jsize)nMax; @@ -3196,7 +3192,7 @@ static void result_blob_text(int asBlob, int as64, break; } } - JBA_RELEASE(jBa, pBuf); + s3jni_jbytearray_release(jBa, pBuf); } }else{ sqlite3_result_null(pCx); @@ -3219,7 +3215,7 @@ JDECL(void,1result_1error)(JENV_CSELF, jobject jpCx, jbyteArray baMsg, int eTextRep){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); - jbyte * const pjBuf = baMsg ? JBA_TOC(baMsg) : NULL; + jbyte * const pjBuf = baMsg ? s3jni_jbytearray_bytes(baMsg) : NULL; switch(pjBuf ? eTextRep : SQLITE_UTF8){ case SQLITE_UTF8: { const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; @@ -3238,7 +3234,7 @@ JDECL(void,1result_1error)(JENV_CSELF, jobject jpCx, jbyteArray baMsg, "to sqlite3_result_error().", -1); break; } - JBA_RELEASE(baMsg,pjBuf); + s3jni_jbytearray_release(baMsg,pjBuf); } JDECL(void,1result_1error_1code)(JENV_CSELF, jobject jpCx, jint v){ @@ -3313,12 +3309,11 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, const char*z2,const char*z3){ S3JniDb * const ps = pState; LocalJniGetEnv; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); 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; - jstring const s3 = z3 ? s3jni_utf8_to_jstring(jc, z3, -1) : 0; + jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; + jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; + jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0; + jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0; int rc; assert( pHook->jObj ); @@ -3405,18 +3400,18 @@ JDECL(jint,1status64)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyteArray baG, jbyteArray baT, jint escLike){ int rc = 0; - jbyte * const pG = JBA_TOC(baG); - jbyte * const pT = pG ? JBA_TOC(baT) : 0; + jbyte * const pG = s3jni_jbytearray_bytes(baG); + jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0; - OOM_CHECK(pT); + s3jni_oom_check(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike ? sqlite3_strlike((const char *)pG, (const char *)pT, (unsigned int)escLike) : sqlite3_strglob((const char *)pG, (const char *)pT); - JBA_RELEASE(baG, pG); - JBA_RELEASE(baT, pT); + s3jni_jbytearray_release(baG, pG); + s3jni_jbytearray_release(baT, pT); return rc; } @@ -3445,10 +3440,9 @@ JDECL(jstring,1sql)(JENV_CSELF, jobject jpStmt){ jstring rv = 0; if( pStmt ){ const char * zSql = 0; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); zSql = sqlite3_sql(pStmt); - rv = s3jni_utf8_to_jstring(jc, zSql, -1); - OOM_CHECK(rv); + rv = s3jni_utf8_to_jstring(env, zSql, -1); + s3jni_oom_check(rv); } return rv; } @@ -3468,12 +3462,11 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ jobject jX = NULL /* the tracer's X arg */; jobject jP = NULL /* the tracer's P arg */; jobject jPUnref = NULL /* potentially a local ref to jP */; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); int rc; int createStmt = 0; switch(traceflag){ case SQLITE_TRACE_STMT: - jX = s3jni_utf8_to_jstring(jc, (const char *)pX, -1); + jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); if(!jX) return SQLITE_NOMEM; /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ createStmt = 1; @@ -3858,8 +3851,7 @@ JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol, int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol, &pz, &pn); if( 0==rc ){ - S3JniEnv * const jc = S3JniGlobal_env_cache(env); - jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0; + jstring jstr = pz ? s3jni_utf8_to_jstring(env, pz, pn) : 0; if( pz ){ if( jstr ){ OutputPointer_set_String(env, jOut, jstr); @@ -3929,7 +3921,7 @@ JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, Fts5JniAux * pAux; assert(pApi); - zName = JSTR_TOC(jName); + zName = s3jni_jstring_to_mutf8(jName); if(!zName) return SQLITE_NOMEM; pAux = Fts5JniAux_alloc(env, jFunc); if( pAux ){ @@ -3944,7 +3936,7 @@ JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, pAux->zFuncName = sqlite3_mprintf("%s", zName) /* OOM here is non-fatal. Ignore it. */; } - JSTR_RELEASE(jName, zName); + s3jni_mutf8_release(jName, zName); return (jint)rc; } @@ -4222,7 +4214,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, S3JniEnv * const jc = S3JniGlobal_env_cache(env); struct s3jni_xQueryPhraseState s; int rc = 0; - jbyte * const pText = jCallback ? JBA_TOC(jbaText) : 0; + jbyte * const pText = jCallback ? s3jni_jbytearray_bytes(jbaText) : 0; jsize nText = pText ? (*env)->GetArrayLength(env, jbaText) : 0; jclass const klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; @@ -4237,7 +4229,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, IFTHREW { EXCEPTION_REPORT; EXCEPTION_CLEAR; - JBA_RELEASE(jbaText, pText); + s3jni_jbytearray_release(jbaText, pText); return SQLITE_ERROR; } s.tok.jba = REF_L(jbaText); @@ -4259,7 +4251,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, assert( s.tok.zPrev ); UNREF_L(s.tok.jba); } - JBA_RELEASE(jbaText, pText); + s3jni_jbytearray_release(jbaText, pText); return (jint)rc; } @@ -4457,15 +4449,15 @@ Java_org_sqlite_jni_tester_SQLTester_strglob( JENV_CSELF, jbyteArray baG, jbyteArray baT ){ int rc = 0; - jbyte * const pG = JBA_TOC(baG); - jbyte * const pT = pG ? JBA_TOC(baT) : 0; + jbyte * const pG = s3jni_jbytearray_bytes(baG); + jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0; - OOM_CHECK(pT); + s3jni_oom_check(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = !SQLTester_strnotglob((const char *)pG, (const char *)pT); - JBA_RELEASE(baG, pG); - JBA_RELEASE(baT, pT); + s3jni_jbytearray_release(baG, pG); + s3jni_jbytearray_release(baT, pT); return rc; } @@ -4610,15 +4602,15 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ #endif SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( SJG.envCache.mutex ); + s3jni_oom_check( SJG.envCache.mutex ); SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( SJG.perDb.mutex ); + s3jni_oom_check( SJG.perDb.mutex ); SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( SJG.autoExt.mutex ); + s3jni_oom_check( SJG.autoExt.mutex ); #if S3JNI_METRICS_MUTEX SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - OOM_CHECK( SJG.metrics.mutex ); + s3jni_oom_check( SJG.metrics.mutex ); #endif sqlite3_shutdown() diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index bafc73bf68..cdbf860666 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -680,7 +680,8 @@ public class Tester1 implements Runnable { sqlite3_finalize(stmt); affirm( 0 != rc ); affirm( sqlite3_errmsg(db).indexOf("an xFunc") > 0 ); - + rc = sqlite3_create_function(db, "mysca", 1, -1, funcSc); + affirm( SQLITE_FORMAT==rc, "invalid encoding value." ); sqlite3_close_v2(db); } diff --git a/manifest b/manifest index 2da52989e3..681eca27b3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\smore\sJNI\sdocs,\stests,\sand\sa\shandful\sof\sJava-side\soverloads. -D 2023-08-24T11:57:51.863 +C Correct\sJNI\slayer's\smisuse\sof\san\ssqlite3-internal\serror-reporting\sAPI\s(no\smutex\sheld).\sStyle\scleanups.\sEliminate\slookups\sof\sper-thread\sstate\sby\sapproximately\s85%\sacross\sthe\stest\ssuite. +D 2023-08-24T14:31:36.028 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,7 +235,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 0a823c56f081294e7797dae303380ac989ebaa801bba970968342b7358f07aed F ext/jni/README.md 64bf1da0d562d051207ca1c5cfa52e8b7a69120533cc034a3da7670ef920cbef F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c e1e3cde4d08925282b5bc949f9ed8f613a6a2c6f60d0c697e79d59fb49f9fe4b +F ext/jni/src/c/sqlite3-jni.c d7d6d420f2a13d55828cee19ba17a37c4244532dafbc5822582d7fd52ae2aaf0 F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 @@ -258,7 +258,7 @@ F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f 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 956063c854c4f662183c41c65d0ab48b5e2127824b8053eeb05b9fc40f0d09e3 -F ext/jni/src/org/sqlite/jni/Tester1.java b5a4bb2a969df053d5c138887f04039a79b36170372a2efdf5dfbd6ac90db4c9 +F ext/jni/src/org/sqlite/jni/Tester1.java 9b6ec0ae299a56822e82e7dc2cf7ef1031ae87bcb595065bef84b7edac7114f5 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2094,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 a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 -R cb79df5ad40cc86377d98f5a1329b589 +P d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0 +R a3460d6a3bf6ffdc6985db3e6bc764fc U stephan -Z 39e8fbdcbc2ebaa58ed84b96f0374c06 +Z 4bd864e1476ea863a52783451fa010a4 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 180d574846..bc1f81d857 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0 \ No newline at end of file +1f46ba8d3bc61af771c1e33d09ad25f0da4fc4f915f7a9f6223ebfd99526d81d \ No newline at end of file From e6c299a5c8414a65b8ad8ea18f52206d90276755 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 15:11:28 +0000 Subject: [PATCH 045/422] Add a new non-goal to the JNI README.md. FossilOrigin-Name: 53000cdae9b265752f8d8616679da621a571a6590c6eaeca23cf87a0e13aa4c4 --- ext/jni/README.md | 7 +++++++ manifest | 13 ++++++------- manifest.uuid | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/ext/jni/README.md b/ext/jni/README.md index 6e5e07cc03..9fe49ba4da 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -43,6 +43,12 @@ Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. +- Support for mixed-mode operation, where client code accesses SQLite3 + both via the Java-side API and the C API via their own native + code. In such cases, proxy functionalities (primarily callback + handler wrappers of all sorts) may fail because the C-side use of + the sqlite3 APIs will bypass those proxies. + Hello World ----------------------------------------------------------------------- @@ -73,6 +79,7 @@ try { } ``` + Building ======================================================================== diff --git a/manifest b/manifest index b03cdd8592..55422aa993 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sthe\sjni-threading\sbranch\sinto\strunk,\seliminating\sthe\sJNI\sAPI's\sprior\sthreading\slimitations. -D 2023-08-24T14:49:29.051 +C Add\sa\snew\snon-goal\sto\sthe\sJNI\sREADME.md. +D 2023-08-24T15:11:28.172 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,7 +233,7 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 2e17aae8debf0b0ee12010500eae7bd9557f8ad5554f0161c39a41f229e84e3e -F ext/jni/README.md 64bf1da0d562d051207ca1c5cfa52e8b7a69120533cc034a3da7670ef920cbef +F ext/jni/README.md 84e67bac56770101c23bff675ab0689d1fa902e8b093959eff5a0c412159b77c F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c d7d6d420f2a13d55828cee19ba17a37c4244532dafbc5822582d7fd52ae2aaf0 F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2 @@ -2094,9 +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 ac9da5c79a4f56d25202d50974e16e2a463d77c99b1907aee2605d5a3e50a565 1f46ba8d3bc61af771c1e33d09ad25f0da4fc4f915f7a9f6223ebfd99526d81d -R 77949ce12a43210a6d0344be95da4143 -T +closed 1f46ba8d3bc61af771c1e33d09ad25f0da4fc4f915f7a9f6223ebfd99526d81d Closed\sby\sintegrate-merge. +P 3739c8aa7080d8e1044ca51ab7b699b50da4d29620b35acfcea2745059b65bac +R 56fa7f8d8a5bc26205acb574e7ed0a30 U stephan -Z 8355fada3860b53acd7e17e529df543a +Z 25449f71cf63b75fe626873474e07663 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 220fc74d64..fc2178b372 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3739c8aa7080d8e1044ca51ab7b699b50da4d29620b35acfcea2745059b65bac \ No newline at end of file +53000cdae9b265752f8d8616679da621a571a6590c6eaeca23cf87a0e13aa4c4 \ No newline at end of file From cf8a93f804161db8896e4e656bc1d777be4ea870 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 15:42:08 +0000 Subject: [PATCH 046/422] Doc style fixes. FossilOrigin-Name: 862f9828e078ae138c3533658c45e4c45155975794e752b9b3a71a693842f37a --- ext/jni/README.md | 4 ++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/jni/README.md b/ext/jni/README.md index 9fe49ba4da..1c8ab4a9d8 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -43,11 +43,11 @@ Non-goals: - Creation of high-level OO wrapper APIs. Clients are free to create them off of the C-style API. -- Support for mixed-mode operation, where client code accesses SQLite3 +- Support for mixed-mode operation, where client code accesses SQLite both via the Java-side API and the C API via their own native code. In such cases, proxy functionalities (primarily callback handler wrappers of all sorts) may fail because the C-side use of - the sqlite3 APIs will bypass those proxies. + the SQLite APIs will bypass those proxies. Hello World diff --git a/manifest b/manifest index 55422aa993..875bc7a4b2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\snew\snon-goal\sto\sthe\sJNI\sREADME.md. -D 2023-08-24T15:11:28.172 +C Doc\sstyle\sfixes. +D 2023-08-24T15:42:08.168 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,7 +233,7 @@ F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f4 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 2e17aae8debf0b0ee12010500eae7bd9557f8ad5554f0161c39a41f229e84e3e -F ext/jni/README.md 84e67bac56770101c23bff675ab0689d1fa902e8b093959eff5a0c412159b77c +F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c d7d6d420f2a13d55828cee19ba17a37c4244532dafbc5822582d7fd52ae2aaf0 F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2 @@ -2094,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 3739c8aa7080d8e1044ca51ab7b699b50da4d29620b35acfcea2745059b65bac -R 56fa7f8d8a5bc26205acb574e7ed0a30 +P 53000cdae9b265752f8d8616679da621a571a6590c6eaeca23cf87a0e13aa4c4 +R 8df76b9c961752a2b50a8bb41ef16d24 U stephan -Z 25449f71cf63b75fe626873474e07663 +Z ce40bd6a5799196ea7241aee36380622 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fc2178b372..fbb96c440f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -53000cdae9b265752f8d8616679da621a571a6590c6eaeca23cf87a0e13aa4c4 \ No newline at end of file +862f9828e078ae138c3533658c45e4c45155975794e752b9b3a71a693842f37a \ No newline at end of file From e336d9268855499342cbe16979800b40ecdb6e05 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 17:25:05 +0000 Subject: [PATCH 047/422] Doc, code style, and legibility cleanups. FossilOrigin-Name: cf185bcd25629d882a030b8b87048179a120ab1f84aa1d68b279c499dbdf0dba --- ext/jni/src/c/sqlite3-jni.c | 1090 +++++++++++--------- ext/jni/src/c/sqlite3-jni.h | 16 - ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 44 +- ext/jni/src/org/sqlite/jni/Tester1.java | 4 +- manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 644 insertions(+), 530 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 7c4aa7c28b..def5f813fb 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -144,14 +144,26 @@ printf pfexp; \ } while(0) -/* Creates a verbose JNI function name. */ -#define JFuncName(Suffix) \ +/* +** Creates a verbose JNI function name. Suffix must be +** the JNI-mangled form of the function's name, minus the +** prefix seen in this macro. +*/ +#define JniFuncName(Suffix) \ Java_org_sqlite_jni_SQLite3Jni_sqlite3_ ## Suffix -/* Prologue for JNI functions. */ -#define JDECL(ReturnType,Suffix) \ +/* Prologue for JNI function declarations and definitions. */ +#define JniDecl(ReturnType,Suffix) \ JNIEXPORT ReturnType JNICALL \ - JFuncName(Suffix) + JniFuncName(Suffix) + +/* +** S3JniApi's intent is that CFunc be the C API func(s) the +** being-declared JNI function is wrapping, making it easier to find +** that function's JNI-side entry point. The other args are for JniDecl. + */ +#define S3JniApi(CFunc,ReturnType,Suffix) JniDecl(ReturnType,Suffix) + /* ** Shortcuts for the first 2 parameters to all JNI bindings. ** @@ -164,47 +176,49 @@ ** ** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers */ -#define JENV_OSELF JNIEnv * const env, jobject jSelf -#define JENV_CSELF JNIEnv * const env, jclass jKlazz +#define JniArgsEnvObj JNIEnv * const env, jobject jSelf +#define JniArgsEnvClass JNIEnv * const env, jclass jKlazz /* ** Helpers to account for -Xcheck:jni warnings about not having ** checked for exceptions. */ -#define IFTHREW if((*env)->ExceptionCheck(env)) -#define EXCEPTION_CLEAR (*env)->ExceptionClear(env) -#define EXCEPTION_REPORT (*env)->ExceptionDescribe(env) -#define EXCEPTION_IGNORE IFTHREW EXCEPTION_CLEAR -#define EXCEPTION_WARN_IGNORE \ - IFTHREW {EXCEPTION_REPORT; EXCEPTION_CLEAR;}(void)0 +#define S3JniIfThrew if( (*env)->ExceptionCheck(env) ) +#define S3JniExceptionClear (*env)->ExceptionClear(env) +#define S3JniExceptionReport (*env)->ExceptionDescribe(env) +#define S3JniExceptionIgnore S3JniIfThrew S3JniExceptionClear +#define S3JniExceptionWarnIgnore \ + S3JniIfThrew {S3JniExceptionReport; S3JniExceptionClear;}(void)0 #define EXCEPTION_WARN_CALLBACK_THREW(STR) \ MARKER(("WARNING: " STR " MUST NOT THROW.\n")); \ (*env)->ExceptionDescribe(env) -#define IFTHREW_REPORT IFTHREW EXCEPTION_REPORT -#define IFTHREW_CLEAR IFTHREW EXCEPTION_CLEAR /** To be used for cases where we're _really_ not expecting an exception, e.g. looking up well-defined Java class members. */ -#define EXCEPTION_IS_FATAL(MSG) IFTHREW {\ - EXCEPTION_REPORT; EXCEPTION_CLEAR; \ +#define EXCEPTION_IS_FATAL(MSG) S3JniIfThrew {\ + S3JniExceptionReport; S3JniExceptionClear; \ (*env)->FatalError(env, MSG); \ } -/** Helpers for extracting pointers from jobjects, noting that the - corresponding Java interfaces have already done the type-checking. - */ -#define PtrGet_sqlite3(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3) -#define PtrGet_sqlite3_stmt(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_stmt) -#define PtrGet_sqlite3_value(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_value) -#define PtrGet_sqlite3_context(OBJ) NativePointerHolder_get(env,OBJ,&S3NphRefs.sqlite3_context) +/* +** Helpers for extracting pointers from jobjects, noting that we rely +** on the corresponding Java interfaces having already done the +** type-checking. Don't use these in contexts where that's not the +** case. +*/ +#define PtrGet_T(T,OBJ) NativePointerHolder_get(env, 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) +#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) /* Helpers for Java value reference management. */ static inline jobject new_global_ref(JNIEnv * const env, jobject const v){ return v ? (*env)->NewGlobalRef(env, v) : NULL; } static inline void delete_global_ref(JNIEnv * const env, jobject const v){ - if(v) (*env)->DeleteGlobalRef(env, v); + if( v ) (*env)->DeleteGlobalRef(env, v); } static inline void delete_local_ref(JNIEnv * const env, jobject const v){ - if(v) (*env)->DeleteLocalRef(env, v); + if( v ) (*env)->DeleteLocalRef(env, v); } #define REF_G(VAR) new_global_ref(env, (VAR)) #define REF_L(VAR) (*env)->NewLocalRef(env, VAR) @@ -267,67 +281,11 @@ static const struct { #undef NREF }; -/** Create a trivial JNI wrapper for (int CName(void)). */ -#define WRAP_INT_VOID(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF){ \ - return (jint)CName(); \ - } - -/** Create a trivial JNI wrapper for (int CName(int)). */ -#define WRAP_INT_INT(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF, jint arg){ \ - return (jint)CName((int)arg); \ - } - -/* -** Create a trivial JNI wrapper for (const mutf8_string * -** CName(void)). This is only valid for functions which are known to -** return ASCII or text which is equivalent in UTF-8 and MUTF-8. - */ -#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ - JDECL(jstring,JniNameSuffix)(JENV_CSELF){ \ - return (*env)->NewStringUTF( env, CName() ); \ - } -/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ -#define WRAP_INT_STMT(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF, jobject jpStmt){ \ - jint const rc = (jint)CName(PtrGet_sqlite3_stmt(jpStmt)); \ - EXCEPTION_IGNORE /* squelch -Xcheck:jni */; \ - return rc; \ - } -/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */ -#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF, jobject pStmt, jint n){ \ - return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \ - } -/** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ -#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ - JDECL(jstring,JniNameSuffix)(JENV_CSELF, jobject pStmt, jint ndx){ \ - return s3jni_utf8_to_jstring(env, \ - CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ - -1); \ - } -/** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ -#define WRAP_INT_DB(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF, jobject pDb){ \ - return (jint)CName(PtrGet_sqlite3(pDb)); \ - } -/** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ -#define WRAP_INT64_DB(JniNameSuffix,CName) \ - JDECL(jlong,JniNameSuffix)(JENV_CSELF, jobject pDb){ \ - return (jlong)CName(PtrGet_sqlite3(pDb)); \ - } -/** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ -#define WRAP_INT_SVALUE(JniNameSuffix,CName) \ - JDECL(jint,JniNameSuffix)(JENV_CSELF, jobject jpSValue){ \ - return (jint)CName(PtrGet_sqlite3_value(jpSValue)); \ - } - /* Helpers for jstring and jbyteArray. */ #define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) -#define s3jni_mutf8_release(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR) +#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) #define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) -#define s3jni_jbytearray_release(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) +#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) enum { /* @@ -629,28 +587,28 @@ static void s3jni_incr( volatile unsigned int * const p ){ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) -#define s3jni_oom_check(VAR) if(!(VAR)) s3jni_oom(env) +#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env) static inline void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; } -/** - sqlite3_malloc() proxy which fails fatally on OOM. This should - only be used for routines which manage global state and have no - recovery strategy for OOM. For sqlite3 API which can reasonably - return SQLITE_NOMEM, sqlite3_malloc() should be used instead. +/* +** sqlite3_malloc() proxy which fails fatally on OOM. This should +** only be used for routines which manage global state and have no +** recovery strategy for OOM. For sqlite3 API which can reasonably +** return SQLITE_NOMEM, sqlite3_malloc() should be used instead. */ static void * s3jni_malloc(JNIEnv * const env, size_t n){ void * const rv = sqlite3_malloc(n); - if(n && !rv) s3jni_oom(env); + if( n && !rv ) s3jni_oom(env); return rv; } -/** - Fetches the S3JniGlobal.envCache row for the given env, allocing - a row if needed. When a row is allocated, its state is initialized - insofar as possible. Calls (*env)->FatalError() if allocation of - an entry fails. That's hypothetically possible but "shouldn't happen." +/* +** Fetches the S3JniGlobal.envCache row for the given env, allocing +** a row if needed. When a row is allocated, its state is initialized +** insofar as possible. Calls (*env)->FatalError() if allocation of +** an entry fails. That's hypothetically possible but "shouldn't happen." */ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ struct S3JniEnv * row; @@ -675,7 +633,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ } memset(row, 0, sizeof(*row)); row->pNext = SJG.envCache.aHead; - if(row->pNext) row->pNext->pPrev = row; + if( row->pNext ) row->pNext->pPrev = row; SJG.envCache.aHead = row; row->env = env; @@ -719,7 +677,7 @@ static int s3jni_db_error(sqlite3* const db, int err_code, static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, const unsigned char * const p, int nP){ jbyteArray jba = (*env)->NewByteArray(env, (jint)nP); - if(jba){ + if( jba ){ (*env)->SetByteArrayRegion(env, jba, 0, (jint)nP, (const jbyte*)p); } return jba; @@ -795,12 +753,12 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, jsize nBa; char *rv; - if(!jstr) return 0; + if( !jstr ) return 0; jba = (*env)->CallObjectMethod(env, jstr, SJG.g.stringGetBytes, SJG.g.oCharsetUtf8); if( (*env)->ExceptionCheck(env) || !jba /* order of these checks is significant for -Xlint:jni */ ) { - EXCEPTION_REPORT; + S3JniExceptionReport; if( nLen ) *nLen = 0; return 0; } @@ -848,15 +806,15 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ jclass const klazz = (*env)->GetObjectClass(env, jx); mid = (*env)->GetMethodID(env, klazz, "toString", "()Ljava/lang/String;"); UNREF_L(klazz); - IFTHREW{ - EXCEPTION_REPORT; - EXCEPTION_CLEAR; + S3JniIfThrew{ + S3JniExceptionReport; + S3JniExceptionClear; return 0; } msg = (*env)->CallObjectMethod(env, jx, mid); - IFTHREW{ - EXCEPTION_REPORT; - EXCEPTION_CLEAR; + S3JniIfThrew{ + S3JniExceptionReport; + S3JniExceptionClear; return 0; } zMsg = s3jni_jstring_to_utf8(env, msg, 0); @@ -883,7 +841,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, if( 0==errCode ) errCode = SQLITE_ERROR; if( ex ){ char * zMsg; - EXCEPTION_CLEAR; + S3JniExceptionClear; zMsg = s3jni_exception_error_msg(env, ex); s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg); sqlite3_free(zMsg); @@ -900,21 +858,21 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, ** suppressed. */ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ - if(jObj){ + if( jObj ){ jclass const klazz = (*env)->GetObjectClass(env, jObj); jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); UNREF_L(klazz); - if(method){ + if( method ){ s3jni_incr( &SJG.metrics.nDestroy ); (*env)->CallVoidMethod(env, jObj, method); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("xDestroy() callback"); - EXCEPTION_CLEAR; + S3JniExceptionClear; } }else{ /* Non-fatal. */ - EXCEPTION_CLEAR; + S3JniExceptionClear; } } } @@ -1050,9 +1008,9 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* */ static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); - if(!pNC->fidValue){ + if( !pNC->fidValue ){ MUTEX_NPH_ENTER; - if(!pNC->fidValue){ + if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); } @@ -1080,7 +1038,7 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, */ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const* pRef){ if( pObj ){ - void * rv = (void*)(*env)->GetLongField( + void * const rv = (void*)(*env)->GetLongField( env, pObj, NativePointerHolder_getField(env, pRef) ); EXCEPTION_IS_FATAL("Cannot fetch NativePointerHolder.nativePointer."); @@ -1106,7 +1064,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, assert(rv->pNext != rv); assert(rv->pPrev != rv); assert(rv->pPrev ? (rv->pPrev!=rv->pNext) : 1); - if(rv->pNext){ + if( rv->pNext ){ assert(rv->pNext->pPrev == rv); assert(rv->pPrev ? (rv->pNext == rv->pPrev->pNext) : 1); rv->pNext->pPrev = 0; @@ -1123,7 +1081,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, if( rv ){ rv->pNext = SJG.perDb.aUsed; SJG.perDb.aUsed = rv; - if(rv->pNext){ + if( rv->pNext ){ assert(!rv->pNext->pPrev); rv->pNext->pPrev = rv; } @@ -1147,15 +1105,15 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, */ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; - if(jDb || pDb){ + if( jDb || pDb ){ MUTEX_PDB_ENTER; s = SJG.perDb.aUsed; - if(!pDb){ + if( !pDb ){ assert( jDb ); pDb = PtrGet_sqlite3(jDb); } for( ; pDb && s; s = s->pNext){ - if(s->pDb == pDb){ + if( s->pDb == pDb ){ break; } } @@ -1189,8 +1147,8 @@ static int S3JniAutoExtension_init(JNIEnv *const env, ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", "(Lorg/sqlite/jni/sqlite3;)I"); UNREF_L(klazz); - EXCEPTION_WARN_IGNORE; - if(!ax->midFunc){ + S3JniExceptionWarnIgnore; + if( !ax->midFunc ){ MARKER(("Error getting xEntryPoint(sqlite3) from AutoExtension object.")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; @@ -1224,9 +1182,9 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, int rc = 0; S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context); - if(!pNC->fidAggCtx){ + if( !pNC->fidAggCtx ){ MUTEX_NPH_ENTER; - if(!pNC->fidAggCtx){ + if( !pNC->fidAggCtx ){ pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, "aggregateContext", "J"); EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member."); } @@ -1259,9 +1217,9 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, const char * const zTypeSig, jobject const jOut){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); - if(!pNC->fidValue){ + if( !pNC->fidValue ){ MUTEX_NPH_ENTER; - if(!pNC->fidValue){ + if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "value", zTypeSig); EXCEPTION_IS_FATAL("setupOutputPointer() could not find OutputPointer.*.value"); } @@ -1372,7 +1330,7 @@ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, ** returns false. */ static int encodingTypeIsValid(int eTextRep){ - switch(eTextRep){ + switch( eTextRep ){ case SQLITE_UTF8: case SQLITE_UTF16: case SQLITE_UTF16LE: case SQLITE_UTF16BE: return 1; @@ -1391,7 +1349,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, jint rc = 0; jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; - if(!jbaRhs){ + if( !jbaRhs ){ UNREF_L(jbaLhs); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; @@ -1401,7 +1359,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, rc = (*env)->CallIntMethod(env, ps->hooks.collation.jObj, ps->hooks.collation.midCallback, jbaLhs, jbaRhs); - EXCEPTION_IGNORE; + S3JniExceptionIgnore; UNREF_L(jbaLhs); UNREF_L(jbaRhs); return (int)rc; @@ -1435,7 +1393,7 @@ typedef struct { */ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); - if(rv){ + if( rv ){ rv->jObj = jObj ? REF_G(jObj) : 0; } return rv; @@ -1446,7 +1404,7 @@ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ** object reference is relinquished and v is freed. */ static void ResultJavaVal_finalizer(void *v){ - if(v){ + if( v ){ ResultJavaVal * const rv = (ResultJavaVal*)v; LocalJniGetEnv; UNREF_G(rv->jObj); @@ -1471,9 +1429,9 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const const void * pNative){ jobject rv = 0; S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); - if(!pNC->midCtor){ + if( !pNC->midCtor ){ MUTEX_NPH_ENTER; - if(!pNC->midCtor){ + if( !pNC->midCtor ){ pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); EXCEPTION_IS_FATAL("Cannot find constructor for class."); } @@ -1482,7 +1440,7 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); EXCEPTION_IS_FATAL("No-arg constructor threw."); s3jni_oom_check(rv); - if(rv) NativePointerHolder_set(env, rv, pNative, pRef); + if( rv ) NativePointerHolder_set(env, rv, pNative, pRef); return rv; } @@ -1533,7 +1491,7 @@ struct S3JniUdf { static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ S3JniUdf * const s = sqlite3_malloc(sizeof(S3JniUdf)); - if(s){ + if( s ){ const char * zFSI = /* signature for xFunc, xStep, xInverse */ "(Lorg/sqlite/jni/sqlite3_context;[Lorg/sqlite/jni/sqlite3_value;)V"; const char * zFV = /* signature for xFinal, xValue */ @@ -1544,7 +1502,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ s->jObj = REF_G(jObj); #define FGET(FuncName,FuncType,Field) \ s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncType); \ - if(!s->Field) (*env)->ExceptionClear(env) + if( !s->Field ) (*env)->ExceptionClear(env) FGET("xFunc", zFSI, jmidxFunc); FGET("xStep", zFSI, jmidxStep); FGET("xFinal", zFV, jmidxFinal); @@ -1552,8 +1510,8 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ FGET("xInverse", zFSI, jmidxInverse); #undef FGET UNREF_L(klazz); - if(s->jmidxFunc) s->type = UDF_SCALAR; - else if(s->jmidxStep && s->jmidxFinal){ + if( s->jmidxFunc ) s->type = UDF_SCALAR; + else if( s->jmidxStep && s->jmidxFinal ){ s->type = s->jmidxValue ? UDF_WINDOW : UDF_AGGREGATE; }else{ s->type = UDF_UNKNOWN_TYPE; @@ -1564,7 +1522,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ static void S3JniUdf_free(S3JniUdf * s){ LocalJniGetEnv; - if(env){ + if( env ){ //MARKER(("UDF cleanup: %s\n", s->zFuncName)); s3jni_call_xDestroy(env, s->jObj); UNREF_G(s->jObj); @@ -1575,7 +1533,7 @@ static void S3JniUdf_free(S3JniUdf * s){ static void S3JniUdf_finalizer(void * s){ //MARKER(("UDF finalizer @ %p\n", s)); - if(s) S3JniUdf_free((S3JniUdf*)s); + if( s ) S3JniUdf_free((S3JniUdf*)s); } /** @@ -1601,14 +1559,14 @@ static int udf_args(JNIEnv *env, jint i; *jCx = 0; *jArgv = 0; - if(!jcx) goto error_oom; + if( !jcx ) goto error_oom; ja = (*env)->NewObjectArray(env, argc, SJG.g.cObj, NULL); - if(!ja) goto error_oom; + if( !ja ) goto error_oom; for(i = 0; i < argc; ++i){ jobject jsv = new_sqlite3_value_wrapper(env, argv[i]); - if(!jsv) goto error_oom; + if( !jsv ) goto error_oom; (*env)->SetObjectArrayElement(env, ja, i, jsv); UNREF_L(jsv)/*array has a ref*/; } @@ -1643,7 +1601,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, char * zMsg; char * z; - EXCEPTION_CLEAR; + S3JniExceptionClear; zMsg = s3jni_exception_error_msg(env, ex); z = sqlite3_mprintf("Client-defined SQL function %s.%s() threw: %s", zFuncName ? zFuncName : "", zFuncType, @@ -1661,7 +1619,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, "It should not do that.\n", zFuncName ? zFuncName : "", zFuncType)); (*env)->ExceptionDescribe( env ); - EXCEPTION_CLEAR; + S3JniExceptionClear; } return rc; } @@ -1680,13 +1638,13 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); - if(rc) return rc; + if( rc ) return rc; if( UDF_SCALAR != s->type ){ rc = udf_setAggregateContext(env, args.jcx, pCx, 0); } if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv); - IFTHREW{ + S3JniIfThrew{ rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx, s->zFuncName, zFuncType); } @@ -1708,7 +1666,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, int rc = 0; int const isFinal = 'F'==zFuncType[1]/*xFinal*/; //MARKER(("%s.%s() cx = %p\n", s->zFuncName, zFuncType, cx)); - if(!jcx){ + if( !jcx ){ if( isFinal ) sqlite3_result_error_nomem(cx); return SQLITE_NOMEM; } @@ -1718,7 +1676,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, } if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); - IFTHREW{ + S3JniIfThrew{ rc = udf_report_exception(env, isFinal, cx, s->zFuncName, zFuncType); } @@ -1767,6 +1725,61 @@ static void udf_xInverse(sqlite3_context* cx, int argc, // except for this macro-generated subset which are kept together // (alphabetized) here at the front... //////////////////////////////////////////////////////////////////////// + +/** Create a trivial JNI wrapper for (int CName(void)). */ +#define WRAP_INT_VOID(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass){ \ + return (jint)CName(); \ + } +/** Create a trivial JNI wrapper for (int CName(int)). */ +#define WRAP_INT_INT(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jint arg){ \ + return (jint)CName((int)arg); \ + } +/* +** Create a trivial JNI wrapper for (const mutf8_string * +** CName(void)). This is only valid for functions which are known to +** return ASCII or text which is equivalent in UTF-8 and MUTF-8. +*/ +#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ + return (*env)->NewStringUTF( env, CName() ); \ + } +/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ +#define WRAP_INT_STMT(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpStmt){ \ + jint const rc = (jint)CName(PtrGet_sqlite3_stmt(jpStmt)); \ + S3JniExceptionIgnore /* squelch -Xcheck:jni */; \ + return rc; \ + } +/** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */ +#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint n){ \ + return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \ + } +/** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ +#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \ + return s3jni_utf8_to_jstring(env, \ + CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ + -1); \ + } +/** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ +#define WRAP_INT_DB(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ + return (jint)CName(PtrGet_sqlite3(pDb)); \ + } +/** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ +#define WRAP_INT64_DB(JniNameSuffix,CName) \ + JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ + return (jlong)CName(PtrGet_sqlite3(pDb)); \ + } +/** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ +#define WRAP_INT_SVALUE(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpSValue){ \ + return (jint)CName(PtrGet_sqlite3_value(jpSValue)); \ + } + WRAP_INT_STMT(1bind_1parameter_1count, sqlite3_bind_parameter_count) WRAP_INT_DB(1changes, sqlite3_changes) WRAP_INT64_DB(1changes64, sqlite3_changes64) @@ -1852,23 +1865,25 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, MUTEX_EXT_LEAVE; if( ax.jObj ){ rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); - IFTHREW { + UNREF_L(ax.jObj); + S3JniIfThrew { jthrowable const ex = (*env)->ExceptionOccurred(env); char * zMsg; - EXCEPTION_CLEAR; + S3JniExceptionClear; zMsg = s3jni_exception_error_msg(env, ex); UNREF_L(ex); *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); sqlite3_free(zMsg); if( !rc ) rc = SQLITE_ERROR; } - UNREF_L(ax.jObj); } } return rc; } -JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ +S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( + JniArgsEnvClass, jobject jAutoExt +){ static int once = 0; int i; S3JniAutoExtension * ax; @@ -1884,7 +1899,7 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ return 0 /* this as a no-op. */; } } - if(i == SJG.autoExt.nExt ){ + if( i == SJG.autoExt.nExt ){ assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc ); if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ unsigned n = 1 + SJG.autoExt.nAlloc; @@ -1920,8 +1935,9 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){ return rc; } -JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, - jint ndx, jbyteArray baData, jint nMax){ +S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax +){ jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); @@ -1929,30 +1945,36 @@ JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt, return (jint)rc; } -JDECL(jint,1bind_1double)(JENV_CSELF, jobject jpStmt, - jint ndx, jdouble val){ +S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jdouble val +){ return (jint)sqlite3_bind_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val); } -JDECL(jint,1bind_1int)(JENV_CSELF, jobject jpStmt, - jint ndx, jint val){ +S3JniApi(sqlite3_bind_int(),jint,1bind_1int)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jint val +){ return (jint)sqlite3_bind_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); } -JDECL(jint,1bind_1int64)(JENV_CSELF, jobject jpStmt, - jint ndx, jlong val){ +S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jlong val +){ return (jint)sqlite3_bind_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } -JDECL(jint,1bind_1null)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_bind_null(),jint,1bind_1null)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ return (jint)sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName){ +S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)( + JniArgsEnvClass, jobject jpStmt, jbyteArray jName +){ int rc = 0; jbyte * const pBuf = s3jni_jbytearray_bytes(jName); - if(pBuf){ + if( pBuf ){ rc = sqlite3_bind_parameter_index(PtrGet_sqlite3_stmt(jpStmt), (const char *)pBuf); s3jni_jbytearray_release(jName, pBuf); @@ -1960,8 +1982,9 @@ JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName return rc; } -JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, - jint ndx, jbyteArray baData, jint nMax){ +S3JniApi(sqlite3_bind_text(),jint,1bind_1text)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax +){ jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, @@ -1970,8 +1993,9 @@ JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt, return (jint)rc; } -JDECL(jint,1bind_1text16)(JENV_CSELF, jobject jpStmt, - jint ndx, jbyteArray baData, jint nMax){ +S3JniApi(sqlite3_text16(),jint,1bind_1text16)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax +){ jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; int const rc = sqlite3_bind_text16(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); @@ -1979,13 +2003,15 @@ JDECL(jint,1bind_1text16)(JENV_CSELF, jobject jpStmt, return (jint)rc; } -JDECL(jint,1bind_1zeroblob)(JENV_CSELF, jobject jpStmt, - jint ndx, jint n){ +S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jint n +){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n); } -JDECL(jint,1bind_1zeroblob64)(JENV_CSELF, jobject jpStmt, - jint ndx, jlong n){ +S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob64)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jlong n +){ return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n); } @@ -1997,7 +2023,7 @@ static int s3jni_busy_handler(void* pState, int n){ LocalJniGetEnv; rc = (*env)->CallIntMethod(env, ps->hooks.busyHandler.jObj, ps->hooks.busyHandler.midCallback, (jint)n); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_busy_handler() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_busy_handler() callback threw."); @@ -2006,13 +2032,15 @@ static int s3jni_busy_handler(void* pState, int n){ return rc; } -JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ +S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( + JniArgsEnvClass, jobject jDb, jobject jBusy +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc = 0; - if(!ps) return (jint)SQLITE_NOMEM; - if(jBusy){ + if( !ps ) return (jint)SQLITE_NOMEM; + if( jBusy ){ S3JniHook * const pHook = &ps->hooks.busyHandler; - if(pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy)){ + if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){ /* Same object - this is a no-op. */ return 0; } @@ -2022,11 +2050,11 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ klazz = (*env)->GetObjectClass(env, jBusy); pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(I)I"); UNREF_L(klazz); - IFTHREW { + S3JniIfThrew { S3JniHook_unref(env, pHook, 0); rc = SQLITE_ERROR; } - if(rc){ + if( rc ){ return rc; } }else{ @@ -2037,7 +2065,9 @@ JDECL(jint,1busy_1handler)(JENV_CSELF, jobject jDb, jobject jBusy){ : sqlite3_busy_handler(ps->pDb, 0, 0); } -JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ +S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( + JniArgsEnvClass, jobject jDb, jint ms +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); if( ps ){ S3JniHook_unref(env, &ps->hooks.busyHandler, 1); @@ -2046,7 +2076,9 @@ JDECL(jint,1busy_1timeout)(JENV_CSELF, jobject jDb, jint ms){ return SQLITE_MISUSE; } -JDECL(jboolean,1cancel_1auto_1extension)(JENV_CSELF, jobject jAutoExt){ +S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( + JniArgsEnvClass, jobject jAutoExt +){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; int i; @@ -2077,7 +2109,7 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ S3JniDb * ps = 0; assert(version == 1 || version == 2); ps = S3JniDb_for_db(env, jDb, 0); - if(ps){ + if( ps ){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ S3JniDb_set_aside(env, ps) @@ -2088,11 +2120,15 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ return (jint)rc; } -JDECL(jint,1close_1v2)(JENV_CSELF, jobject pDb){ +S3JniApi(sqlite3_close_v2(),jint,1close_1v2)( + JniArgsEnvClass, jobject pDb +){ return s3jni_close_db(env, pDb, 2); } -JDECL(jint,1close)(JENV_CSELF, jobject pDb){ +S3JniApi(sqlite3_close(),jint,1close)( + JniArgsEnvClass, jobject pDb +){ return s3jni_close_db(env, pDb, 1); } @@ -2114,21 +2150,23 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, LocalJniGetEnv; unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); - IFTHREW{ + S3JniIfThrew{ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); - EXCEPTION_CLEAR; + S3JniExceptionClear; }else{ (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj, ps->hooks.collationNeeded.midCallback, ps->jDb, (jint)eTextRep, jName); - IFTHREW{ + S3JniIfThrew{ s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); } UNREF_L(jName); } } -JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ +S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( + JniArgsEnvClass, jobject jDb, jobject jHook +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jobject pOld = 0; @@ -2138,8 +2176,8 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ if( !ps ) return SQLITE_MISUSE; pOld = pHook->jObj; - if(pOld && jHook && - (*env)->IsSameObject(env, pOld, jHook)){ + if( pOld && jHook && + (*env)->IsSameObject(env, pOld, jHook) ){ return 0; } if( !jHook ){ @@ -2152,7 +2190,7 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ xCallback = (*env)->GetMethodID(env, klazz, "xCollationNeeded", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); UNREF_L(klazz); - IFTHREW { + S3JniIfThrew { rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, "Cannot not find matching callback on " "collation-needed hook object."); @@ -2165,8 +2203,9 @@ JDECL(jint,1collation_1needed)(JENV_CSELF, jobject jDb, jobject jHook){ return rc; } -JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_blob(),jbyteArray,1column_1blob)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); void const * const p = sqlite3_column_blob(pStmt, (int)ndx); int const n = p ? sqlite3_column_bytes(pStmt, (int)ndx) : 0; @@ -2178,39 +2217,45 @@ JDECL(jbyteArray,1column_1blob)(JENV_CSELF, jobject jpStmt, } } -JDECL(jdouble,1column_1double)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_double(),jdouble,1column_1double)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ return (jdouble)sqlite3_column_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -JDECL(jint,1column_1int)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_int(),jint,1column_1int)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ return (jint)sqlite3_column_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -JDECL(jlong,1column_1int64)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); } -JDECL(jbyteArray,1column_1text_1utf8)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text_1utf8)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const int n = sqlite3_column_bytes(stmt, (int)ndx); const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); return p ? s3jni_new_jbyteArray(env, p, n) : NULL; } -JDECL(jstring,1column_1text16)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const int n = sqlite3_column_bytes16(stmt, (int)ndx); const void * const p = sqlite3_column_text16(stmt, (int)ndx); return s3jni_text16_to_jstring(env, p, n); } -JDECL(jobject,1column_1value)(JENV_CSELF, jobject jpStmt, - jint ndx){ +S3JniApi(sqlite3_column_value(),jobject,1column_1value)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ sqlite3_value * const sv = sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); return new_sqlite3_value_wrapper(env, sv); } @@ -2223,8 +2268,8 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ ps->hooks.commit.midCallback) : (int)((*env)->CallVoidMethod(env, ps->hooks.rollback.jObj, ps->hooks.rollback.midCallback), 0); - IFTHREW{ - EXCEPTION_CLEAR; + S3JniIfThrew{ + S3JniExceptionClear; rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); } return rc; @@ -2246,17 +2291,17 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, jmethodID xCallback; S3JniHook * const pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback; - if(!ps){ + if( !ps ){ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; } pOld = pHook->jObj; - if(pOld && jHook && - (*env)->IsSameObject(env, pOld, jHook)){ + if( pOld && jHook && + (*env)->IsSameObject(env, pOld, jHook) ){ return pOld; } if( !jHook ){ - if(pOld){ + if( pOld ){ jobject tmp = REF_L(pOld); UNREF_G(pOld); pOld = tmp; @@ -2271,9 +2316,9 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, isCommit ? "xCommitHook" : "xRollbackHook", isCommit ? "()I" : "()V"); UNREF_L(klazz); - IFTHREW { - EXCEPTION_REPORT; - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionReport; + S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching callback on " "hook object."); @@ -2282,7 +2327,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, pHook->jObj = REF_G(jHook); if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps); else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps); - if(pOld){ + if( pOld ){ jobject tmp = REF_L(pOld); UNREF_G(pOld); pOld = tmp; @@ -2291,17 +2336,22 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, return pOld; } -JDECL(jobject,1commit_1hook)(JENV_CSELF,jobject jDb, jobject jHook){ +S3JniApi(sqlite3_commit_hook(),jobject,1commit_1hook)( + JniArgsEnvClass,jobject jDb, jobject jHook +){ return s3jni_commit_rollback_hook(1, env, jDb, jHook); } - -JDECL(jstring,1compileoption_1get)(JENV_CSELF, jint n){ +S3JniApi(sqlite3_compileoption_get(),jstring,1compileoption_1get)( + JniArgsEnvClass, jint n +){ return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ) /* We know these to be ASCII, so MUTF-8 is fine. */; } -JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ +S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( + JniArgsEnvClass, jstring name +){ const char *zUtf8 = s3jni_jstring_to_mutf8(name); const jboolean rc = 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; @@ -2309,12 +2359,9 @@ JDECL(jboolean,1compileoption_1used)(JENV_CSELF, jstring name){ return rc; } -/* -** sqlite3_config(SQLITE_CONFIG_...) wrapper for a small subset of -** options. -*/ -JDECL(jint,1config__I)(JENV_CSELF, jint n){ - switch(n){ +S3JniApi(sqlite3_config(/*for a small subset of options.*/), + jint,1config__I)(JniArgsEnvClass, jint n){ + switch( n ){ case SQLITE_CONFIG_SINGLETHREAD: case SQLITE_CONFIG_MULTITHREAD: case SQLITE_CONFIG_SERIALIZED: @@ -2335,7 +2382,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int if( !ps || !hook->jObj ) return; jArg0 = REF_L(ps->jDb); - switch(op){ + switch( op ){ case 0: /* db opened */ case 1: /* SQL executed */ jArg1 = s3jni_utf8_to_jstring(env, z, -1); @@ -2347,9 +2394,9 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int break; } (*env)->CallVoidMethod(env, hook->jObj, hook->midCallback, jArg0, jArg1, op); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("SQLITE_CONFIG_SQLLOG callback"); - EXCEPTION_CLEAR; + S3JniExceptionClear; } UNREF_L(jArg0); UNREF_L(jArg1); @@ -2360,8 +2407,8 @@ void sqlite3_init_sqllog(void){ } #endif -/* sqlite3_config(SQLITE_CONFIG_SQLLOG) wrapper. */ -JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ +S3JniApi(sqlite3_config(/* for SQLLOG */), + jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){ #ifdef SQLITE_ENABLE_SQLLOG S3JniHook tmpHook; S3JniHook * const hook = &tmpHook; @@ -2382,7 +2429,7 @@ JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ "I)V"); UNREF_L(klazz); if( !hook->midCallback ){ - EXCEPTION_WARN_IGNORE; + S3JniExceptionWarnIgnore; S3JniHook_unref(env, hook, 0); return SQLITE_ERROR; } @@ -2401,15 +2448,17 @@ JDECL(jint,1config__Lorg_sqlite_jni_SQLLog_2)(JENV_CSELF, jobject jLog){ #endif } -JDECL(jobject,1context_1db_1handle)(JENV_CSELF, jobject jpCx){ +S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( + JniArgsEnvClass, jobject jpCx +){ sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0; return ps ? ps->jDb : 0; } -JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, - jstring name, jint eTextRep, - jobject oCollation){ +S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), + jint,1create_1collation +)(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ int rc; const char *zName; jclass klazz; @@ -2421,7 +2470,7 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCompare", "([B[B)I"); UNREF_L(klazz); - IFTHREW{ + S3JniIfThrew{ UNREF_L(klazz); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); @@ -2439,8 +2488,10 @@ JDECL(jint,1create_1collation)(JENV_CSELF, jobject jDb, return (jint)rc; } -JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, - jint nArg, jint eTextRep, jobject jFunctor){ +S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_window_function(), + jint,1create_1function +)(JniArgsEnvClass, jobject jDb, jstring jFuncName, jint nArg, + jint eTextRep, jobject jFunctor){ S3JniUdf * s = 0; int rc; sqlite3 * const pDb = PtrGet_sqlite3(jDb); @@ -2459,7 +2510,7 @@ JDECL(jint,1create_1function)(JENV_CSELF, jobject jDb, jstring jFuncName, goto error_cleanup; } zFuncName = s3jni_jstring_to_utf8(env,jFuncName,0); - if(!zFuncName){ + if( !zFuncName ){ rc = SQLITE_NOMEM; S3JniUdf_free(s); goto error_cleanup; @@ -2489,10 +2540,9 @@ error_cleanup: return (jint)rc; } -/* sqlite3_db_config() for (int,const char *) */ -JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( - JENV_CSELF, jobject jDb, jint op, jstring jStr -){ +S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/), + jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 +)(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc; char *zStr; @@ -2518,14 +2568,14 @@ JDECL(int,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2)( return rc; } -/* sqlite3_db_config() for (int,int*) */ -/* -** ACHTUNG: openjdk v19 creates a different mangled name for this -** function than openjdk v8 does. We account for that by exporting -** both versions of the name. -*/ -JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( - JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut +S3JniApi( + sqlite3_db_config(), + /* WARNING: openjdk v19 creates a different mangled name for this + ** function than openjdk v8 does. We account for that by exporting + ** both versions of the name. */ + jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2 +)( + JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); int rc; @@ -2567,15 +2617,17 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer ** install both names for this function then Java will not be able to ** find the function in both environments. */ -JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_00024Int32_2)( - JENV_CSELF, jobject jDb, jint op, jint onOff, jobject jOut +JniDecl(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_00024Int32_2)( + JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut ){ - return JFuncName(1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( + return JniFuncName(1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( env, jKlazz, jDb, op, onOff, jOut ); } -JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ +S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( + JniArgsEnvClass, jobject jDb, jstring jDbName +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); char *zDbName; jstring jRv = 0; @@ -2595,9 +2647,10 @@ JDECL(jstring,1db_1filename)(JENV_CSELF, jobject jDb, jstring jDbName){ return jRv; } - -JDECL(jint,1db_1status)(JENV_CSELF, jobject jDb, jint op, jobject jOutCurrent, - jobject jOutHigh, jboolean reset ){ +S3JniApi(sqlite3_db_status(),jint,1db_1status)( + JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent, + jobject jOutHigh, jboolean reset +){ int iCur = 0, iHigh = 0; sqlite3 * const pDb = PtrGet_sqlite3(jDb); int rc = sqlite3_db_status( pDb, op, &iCur, &iHigh, reset ); @@ -2608,24 +2661,31 @@ JDECL(jint,1db_1status)(JENV_CSELF, jobject jDb, jint op, jobject jOutCurrent, return (jint)rc; } - -JDECL(jint,1errcode)(JENV_CSELF, jobject jpDb){ +S3JniApi(sqlite3_errcode(),jint,1errcode)( + JniArgsEnvClass, jobject jpDb +){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); return pDb ? sqlite3_errcode(pDb) : SQLITE_MISUSE; } -JDECL(jstring,1errmsg)(JENV_CSELF, jobject jpDb){ +S3JniApi(sqlite3_errmsg(),jstring,1errmsg)( + JniArgsEnvClass, jobject jpDb +){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); return pDb ? s3jni_utf8_to_jstring(env, sqlite3_errmsg(pDb), -1) : 0; } -JDECL(jstring,1errstr)(JENV_CSELF, jint rcCode){ +S3JniApi(sqlite3_errstr(),jstring,1errstr)( + JniArgsEnvClass, jint rcCode +){ return (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode)) /* We know these values to be plain ASCII, so pose no MUTF-8 ** incompatibility */; } -JDECL(jstring,1expanded_1sql)(JENV_CSELF, jobject jpStmt){ +S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( + JniArgsEnvClass, jobject jpStmt +){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); jstring rv = 0; if( pStmt ){ @@ -2639,17 +2699,17 @@ JDECL(jstring,1expanded_1sql)(JENV_CSELF, jobject jpStmt){ return rv; } -JDECL(jboolean,1extended_1result_1codes)(JENV_CSELF, jobject jpDb, - jboolean onoff){ +S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)( + JniArgsEnvClass, jobject jpDb, + jboolean onoff +){ int const rc = sqlite3_extended_result_codes(PtrGet_sqlite3(jpDb), onoff ? 1 : 0); return rc ? JNI_TRUE : JNI_FALSE; } -JDECL(jint,1initialize)(JENV_CSELF){ - return sqlite3_initialize(); -} - -JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){ +S3JniApi(sqlite3_finalize(),jint,1finalize)( + JniArgsEnvClass, jobject jpStmt +){ int rc = 0; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ @@ -2659,12 +2719,22 @@ JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){ return rc; } -JDECL(void,1interrupt)(JENV_CSELF, jobject jpDb){ +S3JniApi(sqlite3_initialize(),jint,1initialize)( + JniArgsEnvClass +){ + return sqlite3_initialize(); +} + +S3JniApi(sqlite3_interrupt(),void,1interrupt)( + JniArgsEnvClass, jobject jpDb +){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); if( pDb ) sqlite3_interrupt(pDb); } -JDECL(jboolean,1is_1interrupted)(JENV_CSELF, jobject jpDb){ +S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( + JniArgsEnvClass, jobject jpDb +){ int rc = 0; sqlite3 * const pDb = PtrGet_sqlite3(jpDb); if( pDb ){ @@ -2673,8 +2743,9 @@ JDECL(jboolean,1is_1interrupted)(JENV_CSELF, jobject jpDb){ return rc ? JNI_TRUE : JNI_FALSE; } - -JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){ +S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( + JniArgsEnvClass, jobject jpDb +){ return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); } @@ -2685,12 +2756,12 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, int rc = 0; jobject jDb = 0; *jc = S3JniGlobal_env_cache(env); - if(!*jc){ + if( !*jc ){ rc = SQLITE_NOMEM; goto end; } *zDbName = jDbName ? s3jni_jstring_to_utf8(env, jDbName, 0) : 0; - if(jDbName && !*zDbName){ + if( jDbName && !*zDbName ){ rc = SQLITE_NOMEM; goto end; } @@ -2702,7 +2773,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, goto end; } *ps = S3JniDb_alloc(env, 0, jDb); - if(*ps){ + if( *ps ){ (*jc)->pdbOpening = *ps; }else{ UNREF_L(jDb); @@ -2727,7 +2798,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, S3JniDb * ps, sqlite3 **ppDb, jobject jOut, int theRc){ jc->pdbOpening = 0; - if(*ppDb){ + if( *ppDb ){ assert(ps->jDb); if( 0==ps->pDb ){ ps->pDb = *ppDb; @@ -2743,7 +2814,9 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, return theRc; } -JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ +S3JniApi(sqlite3_open(),jint,1open)( + JniArgsEnvClass, jstring strName, jobject jOut +){ sqlite3 * pOut = 0; char *zName = 0; S3JniDb * ps = 0; @@ -2759,8 +2832,10 @@ JDECL(jint,1open)(JENV_CSELF, jstring strName, jobject jOut){ return (jint)rc; } -JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, - jobject jOut, jint flags, jstring strVfs){ +S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( + JniArgsEnvClass, jstring strName, + jobject jOut, jint flags, jstring strVfs +){ sqlite3 * pOut = 0; char *zName = 0; S3JniDb * ps = 0; @@ -2784,10 +2859,10 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName, } /* Proxy for the sqlite3_prepare[_v2/3]() family. */ -static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass self, - jobject jDb, jbyteArray baSql, - jint nMax, jint prepFlags, - jobject jOutStmt, jobject outTail){ +jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self, + jobject jDb, jbyteArray baSql, + jint nMax, jint prepFlags, + jobject jOutStmt, jobject outTail){ sqlite3_stmt * pStmt = 0; jobject jStmt = 0; const char * zTail = 0; @@ -2846,18 +2921,24 @@ end: OutputPointer_set_sqlite3_stmt(env, jOutStmt, jStmt); return (jint)rc; } -JDECL(jint,1prepare)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, - jint nMax, jobject jOutStmt, jobject outTail){ +S3JniApi(sqlite3_prepare(),jint,1prepare)( + JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, + jint nMax, jobject jOutStmt, jobject outTail +){ return sqlite3_jni_prepare_v123(1, env, self, jDb, baSql, nMax, 0, jOutStmt, outTail); } -JDECL(jint,1prepare_1v2)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, - jint nMax, jobject jOutStmt, jobject outTail){ +S3JniApi(sqlite3_prepare_v2(),jint,1prepare_1v2)( + JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, + jint nMax, jobject jOutStmt, jobject outTail +){ return sqlite3_jni_prepare_v123(2, env, self, jDb, baSql, nMax, 0, jOutStmt, outTail); } -JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, - jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail){ +S3JniApi(sqlite3_prepare_v3(),jint,1prepare_1v3)( + JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql, + jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail +){ return sqlite3_jni_prepare_v123(3, env, self, jDb, baSql, nMax, prepFlags, jOutStmt, outTail); } @@ -2892,8 +2973,8 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, assert( pHook ); jDbName = s3jni_utf8_to_jstring(env, zDb, -1); jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0; - IFTHREW { - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); }else{ assert( pHook->jObj ); @@ -2907,7 +2988,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, #endif (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, (jint)opId, jDbName, jTable, (jlong)iKey1); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_(pre)update_hook() callback"); s3jni_db_exception(env, ps, 0, "sqlite3_(pre)update_hook() callback threw"); @@ -2933,9 +3014,12 @@ static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, #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; } +S3JniApi(sqlite3_preupdate_blobwrite(),int,1preupdate_1blobwrite)( + JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; } +S3JniApi(sqlite3_preupdate_count(),int,1preupdate_1count)( + JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; } +S3JniApi(sqlite3_preupdate_depth(),int,1preupdate_1depth)( + JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; } #endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */ /* @@ -2956,7 +3040,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec #endif : &ps->hooks.update) : 0; - if(!pHook){ + if( !pHook ){ return 0; } pOld = pHook->jObj; @@ -2988,8 +3072,8 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec : (*env)->GetMethodID(env, klazz, "xUpdateHook", "(ILjava/lang/String;Ljava/lang/String;J)V"); UNREF_L(klazz); - IFTHREW { - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching callback on " "(pre)update hook object."); @@ -3001,7 +3085,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec else #endif sqlite3_update_hook(ps->pDb, s3jni_update_hook_impl, ps); - if(pOld){ + if( pOld ){ jobject tmp = REF_L(pOld); UNREF_G(pOld); pOld = tmp; @@ -3011,7 +3095,9 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec } -JDECL(jobject,1preupdate_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ +S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)( + JniArgsEnvClass, jobject jDb, jobject jHook +){ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK return s3jni_updatepre_hook(env, 1, jDb, jHook); #else @@ -3043,10 +3129,16 @@ static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb, return SQLITE_MISUSE; #endif } -JDECL(jint,1preupdate_1new)(JENV_CSELF, jobject jDb, jint iCol, jobject jOut){ + +S3JniApi(sqlite3_preupdate_new(),jint,1preupdate_1new)( + JniArgsEnvClass, 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){ + +S3JniApi(sqlite3_preupdate_old(),jint,1preupdate_1old)( + JniArgsEnvClass, jobject jDb, jint iCol, jobject jOut +){ return s3jni_preupdate_newold(env, 0, jDb, iCol, jOut); } @@ -3057,34 +3149,36 @@ static int s3jni_progress_handler_impl(void *pP){ LocalJniGetEnv; int rc = (int)(*env)->CallIntMethod(env, ps->hooks.progress.jObj, ps->hooks.progress.midCallback); - IFTHREW{ + S3JniIfThrew{ rc = s3jni_db_exception(env, ps, rc, "sqlite3_progress_handler() callback threw"); } return rc; } -JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress){ +S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( + JniArgsEnvClass,jobject jDb, jint n, jobject jProgress +){ S3JniDb * ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jmethodID xCallback; if( n<1 || !jProgress ){ - if(ps){ + if( ps ){ UNREF_G(ps->hooks.progress.jObj); memset(&ps->hooks.progress, 0, sizeof(ps->hooks.progress)); } sqlite3_progress_handler(ps->pDb, 0, 0, 0); return; } - if(!ps){ + if( !ps ){ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return; } klazz = (*env)->GetObjectClass(env, jProgress); xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I"); UNREF_L(klazz); - IFTHREW { - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on " "ProgressHandler object."); @@ -3096,8 +3190,9 @@ JDECL(void,1progress_1handler)(JENV_CSELF,jobject jDb, jint n, jobject jProgress } } - -JDECL(jint,1reset)(JENV_CSELF, jobject jpStmt){ +S3JniApi(sqlite3_reset(),jint,1reset)( + JniArgsEnvClass, jobject jpStmt +){ int rc = 0; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ @@ -3117,16 +3212,19 @@ static void s3jni_reset_auto_extension(JNIEnv *env){ MUTEX_EXT_LEAVE; } -JDECL(void,1reset_1auto_1extension)(JENV_CSELF){ +S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)( + JniArgsEnvClass +){ s3jni_reset_auto_extension(env); } -/* sqlite3_result_text/blob() and friends. */ -static void result_blob_text(int asBlob, int as64, +/* Impl for sqlite3_result_text/blob() and friends. */ +static void result_blob_text(int as64, int eTextRep/*only for (asBlob=0)*/, JNIEnv * const env, sqlite3_context *pCx, jbyteArray jBa, jlong nMax){ - if(jBa){ + int const asBlob = 0==eTextRep; + if( jBa ){ jbyte * const pBuf = s3jni_jbytearray_bytes(jBa); jsize nBa = (*env)->GetArrayLength(env, jBa); if( nMax>=0 && nBa>(jsize)nMax ){ @@ -3147,17 +3245,17 @@ static void result_blob_text(int asBlob, int as64, have result values). */ } - if(as64){ /* 64-bit... */ + if( as64 ){ /* 64-bit... */ static const jsize nLimit64 = SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary!*/ /* jsize is int32, not int64! */; - if(nBa > nLimit64){ + if( nBa > nLimit64 ){ sqlite3_result_error_toobig(pCx); - }else if(asBlob){ + }else if( asBlob ){ sqlite3_result_blob64(pCx, pBuf, (sqlite3_uint64)nBa, SQLITE_TRANSIENT); }else{ /* text64... */ - if(encodingTypeIsValid(eTextRep)){ + if( encodingTypeIsValid(eTextRep) ){ sqlite3_result_text64(pCx, (const char *)pBuf, (sqlite3_uint64)nBa, SQLITE_TRANSIENT, eTextRep); @@ -3167,13 +3265,13 @@ static void result_blob_text(int asBlob, int as64, } }else{ /* 32-bit... */ static const jsize nLimit = SQLITE_MAX_ALLOCATION_SIZE; - if(nBa > nLimit){ + if( nBa > nLimit ){ sqlite3_result_error_toobig(pCx); - }else if(asBlob){ + }else if( asBlob ){ sqlite3_result_blob(pCx, pBuf, (int)nBa, SQLITE_TRANSIENT); }else{ - switch(eTextRep){ + switch( eTextRep ){ case SQLITE_UTF8: sqlite3_result_text(pCx, (const char *)pBuf, (int)nBa, SQLITE_TRANSIENT); @@ -3199,24 +3297,32 @@ static void result_blob_text(int asBlob, int as64, } } -JDECL(void,1result_1blob)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jint nMax){ - return result_blob_text(1, 0, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); +S3JniApi(sqlite3_result_blob(),void,1result_1blob)( + JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax +){ + return result_blob_text(0, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); } -JDECL(void,1result_1blob64)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jlong nMax){ - return result_blob_text(1, 1, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); +S3JniApi(sqlite3_result_blob64(),void,1result_1blob64)( + JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax +){ + return result_blob_text(1, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); } -JDECL(void,1result_1double)(JENV_CSELF, jobject jpCx, jdouble v){ +S3JniApi(sqlite3_result_double(),void,1result_1double)( + JniArgsEnvClass, jobject jpCx, jdouble v +){ sqlite3_result_double(PtrGet_sqlite3_context(jpCx), v); } -JDECL(void,1result_1error)(JENV_CSELF, jobject jpCx, jbyteArray baMsg, - int eTextRep){ +S3JniApi(sqlite3_result_error(),void,1result_1error)( + JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, + int eTextRep +){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); jbyte * const pjBuf = baMsg ? s3jni_jbytearray_bytes(baMsg) : NULL; - switch(pjBuf ? eTextRep : SQLITE_UTF8){ + switch( pjBuf ? eTextRep : SQLITE_UTF8 ){ case SQLITE_UTF8: { const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); @@ -3237,30 +3343,42 @@ JDECL(void,1result_1error)(JENV_CSELF, jobject jpCx, jbyteArray baMsg, s3jni_jbytearray_release(baMsg,pjBuf); } -JDECL(void,1result_1error_1code)(JENV_CSELF, jobject jpCx, jint v){ +S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)( + JniArgsEnvClass, jobject jpCx, jint v +){ sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), v ? (int)v : SQLITE_ERROR); } -JDECL(void,1result_1error_1nomem)(JENV_CSELF, jobject jpCx){ +S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)( + JniArgsEnvClass, jobject jpCx +){ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } -JDECL(void,1result_1error_1toobig)(JENV_CSELF, jobject jpCx){ +S3JniApi(sqlite3_result_error_toobig(),void,1result_1error_1toobig)( + JniArgsEnvClass, jobject jpCx +){ sqlite3_result_error_toobig(PtrGet_sqlite3_context(jpCx)); } -JDECL(void,1result_1int)(JENV_CSELF, jobject jpCx, jint v){ +S3JniApi(sqlite3_result_int(),void,1result_1int)( + JniArgsEnvClass, jobject jpCx, jint v +){ sqlite3_result_int(PtrGet_sqlite3_context(jpCx), (int)v); } -JDECL(void,1result_1int64)(JENV_CSELF, jobject jpCx, jlong v){ +S3JniApi(sqlite3_result_int64(),void,1result_1int64)( + JniArgsEnvClass, jobject jpCx, jlong v +){ sqlite3_result_int64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v); } -JDECL(void,1result_1java_1object)(JENV_CSELF, jobject jpCx, jobject v){ - if(v){ +S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( + JniArgsEnvClass, jobject jpCx, jobject v +){ + if( v ){ ResultJavaVal * const rjv = ResultJavaVal_alloc(env, v); - if(rjv){ + if( rjv ){ sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv, ResultJavaValuePtrStr, ResultJavaVal_finalizer); }else{ @@ -3271,42 +3389,56 @@ JDECL(void,1result_1java_1object)(JENV_CSELF, jobject jpCx, jobject v){ } } -JDECL(void,1result_1null)(JENV_CSELF, jobject jpCx){ +S3JniApi(sqlite3_result_null(),void,1result_1null)( + JniArgsEnvClass, jobject jpCx +){ sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); } -JDECL(void,1result_1text)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jint nMax){ - return result_blob_text(0, 0, SQLITE_UTF8, env, +S3JniApi(sqlite3_result_text(),void,1result_1text)( + JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax +){ + return result_blob_text(0, SQLITE_UTF8, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); } -JDECL(void,1result_1text64)(JENV_CSELF, jobject jpCx, jbyteArray jBa, jlong nMax, - jint eTextRep){ - return result_blob_text(0, 1, eTextRep, env, +S3JniApi(sqlite3_result_text64(),void,1result_1text64)( + JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax, + jint eTextRep +){ + return result_blob_text(1, eTextRep, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); } -JDECL(void,1result_1value)(JENV_CSELF, jobject jpCx, jobject jpSVal){ +S3JniApi(sqlite3_result_value(),void,1result_1value)( + JniArgsEnvClass, jobject jpCx, jobject jpSVal +){ sqlite3_result_value(PtrGet_sqlite3_context(jpCx), PtrGet_sqlite3_value(jpSVal)); } -JDECL(void,1result_1zeroblob)(JENV_CSELF, jobject jpCx, jint v){ +S3JniApi(sqlite3_result_zeroblob(),void,1result_1zeroblob)( + JniArgsEnvClass, jobject jpCx, jint v +){ sqlite3_result_zeroblob(PtrGet_sqlite3_context(jpCx), (int)v); } -JDECL(jint,1result_1zeroblob64)(JENV_CSELF, jobject jpCx, jlong v){ +S3JniApi(sqlite3_result_zeroblob64(),jint,1result_1zeroblob64)( + JniArgsEnvClass, jobject jpCx, jlong v +){ return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v); } -JDECL(jobject,1rollback_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ +S3JniApi(sqlite3_rollback_hook(),jobject,1rollback_1hook)( + JniArgsEnvClass, jobject jDb, jobject jHook +){ return s3jni_commit_rollback_hook(0, env, jDb, jHook); } -/* sqlite3_set_authorizer() callback proxy. */ -static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, - const char*z2,const char*z3){ +/* Callback for sqlite3_set_authorizer(). */ +int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, + const char*z2,const char*z3){ S3JniDb * const ps = pState; LocalJniGetEnv; S3JniHook const * const pHook = &ps->hooks.auth; @@ -3319,7 +3451,7 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, assert( pHook->jObj ); rc = (*env)->CallIntMethod(env, pHook->jObj, pHook->midCallback, (jint)op, s0, s1, s3, s3); - IFTHREW{ + S3JniIfThrew{ rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); } UNREF_L(s0); @@ -3329,7 +3461,9 @@ static int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, return rc; } -JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ +S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( + JniArgsEnvClass,jobject jDb, jobject jHook +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; @@ -3358,7 +3492,7 @@ JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ "Ljava/lang/String;" ")I"); UNREF_L(klazz); - IFTHREW { + S3JniIfThrew { S3JniHook_unref(env, pHook, 0); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Error setting up Java parts of authorizer hook."); @@ -3370,13 +3504,17 @@ JDECL(jint,1set_1authorizer)(JENV_CSELF,jobject jDb, jobject jHook){ } -JDECL(void,1set_1last_1insert_1rowid)(JENV_CSELF, jobject jpDb, jlong rowId){ +S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)( + JniArgsEnvClass, jobject jpDb, jlong rowId +){ sqlite3_set_last_insert_rowid(PtrGet_sqlite3_context(jpDb), (sqlite3_int64)rowId); } -JDECL(jint,1status)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, - jboolean reset ){ +S3JniApi(sqlite3_status(),jint,1status)( + JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, + jboolean reset +){ int iCur = 0, iHigh = 0; int rc = sqlite3_status( op, &iCur, &iHigh, reset ); if( 0==rc ){ @@ -3386,8 +3524,10 @@ JDECL(jint,1status)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, return (jint)rc; } -JDECL(jint,1status64)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh, - jboolean reset ){ +S3JniApi(sqlite3_status64(),jint,1status64)( + JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, + jboolean reset +){ sqlite3_int64 iCur = 0, iHigh = 0; int rc = sqlite3_status64( op, &iCur, &iHigh, reset ); if( 0==rc ){ @@ -3415,15 +3555,21 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, return rc; } -JDECL(jint,1strglob)(JENV_CSELF, jbyteArray baG, jbyteArray baT){ +S3JniApi(sqlite3_strglob(),jint,1strglob)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT +){ return s3jni_strlike_glob(0, env, baG, baT, 0); } -JDECL(jint,1strlike)(JENV_CSELF, jbyteArray baG, jbyteArray baT, jint escChar){ +S3JniApi(sqlite3_strlike(),jint,1strlike)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar +){ return s3jni_strlike_glob(1, env, baG, baT, escChar); } -JDECL(jint,1shutdown)(JENV_CSELF){ +S3JniApi(sqlite3_shutdown(),jint,1shutdown)( + JniArgsEnvClass +){ s3jni_reset_auto_extension(env); MUTEX_ENV_ENTER; while( SJG.envCache.aHead ){ @@ -3435,7 +3581,9 @@ JDECL(jint,1shutdown)(JENV_CSELF){ return sqlite3_shutdown(); } -JDECL(jstring,1sql)(JENV_CSELF, jobject jpStmt){ +S3JniApi(sqlite3_sql(),jstring,1sql)( + JniArgsEnvClass, jobject jpStmt +){ sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); jstring rv = 0; if( pStmt ){ @@ -3447,10 +3595,12 @@ JDECL(jstring,1sql)(JENV_CSELF, jobject jpStmt){ return rv; } -JDECL(jint,1step)(JENV_CSELF,jobject jStmt){ +S3JniApi(sqlite3_step(),jint,1step)( + JniArgsEnvClass,jobject jStmt +){ int rc = SQLITE_MISUSE; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt); - if(pStmt){ + if( pStmt ){ rc = sqlite3_step(pStmt); } return rc; @@ -3464,10 +3614,10 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ jobject jPUnref = NULL /* potentially a local ref to jP */; int rc; int createStmt = 0; - switch(traceflag){ + switch( traceflag ){ case SQLITE_TRACE_STMT: jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); - if(!jX) return SQLITE_NOMEM; + if( !jX ) return SQLITE_NOMEM; /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ createStmt = 1; break; @@ -3476,7 +3626,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ (jlong)*((sqlite3_int64*)pX)); // hmm. ^^^ (*pX) really is zero. // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); - if(!jX) return SQLITE_NOMEM; + if( !jX ) return SQLITE_NOMEM; createStmt = 1; break; case SQLITE_TRACE_ROW: @@ -3491,7 +3641,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ } if( createStmt ){ jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); - if(!jP){ + if( !jP ){ UNREF_L(jX); return SQLITE_NOMEM; } @@ -3500,7 +3650,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ rc = (int)(*env)->CallIntMethod(env, ps->hooks.trace.jObj, ps->hooks.trace.midCallback, (jint)traceflag, jP, jX); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("sqlite3_trace_v2() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_trace_v2() callback threw."); @@ -3510,23 +3660,25 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ return rc; } -JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){ +S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( + JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer +){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; if( !traceMask || !jTracer ){ - if(ps){ + if( ps ){ S3JniHook_unref(env, &ps->hooks.trace, 0); } return (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); } - if(!ps) return SQLITE_NOMEM; + if( !ps ) return SQLITE_NOMEM; klazz = (*env)->GetObjectClass(env, jTracer); ps->hooks.trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(ILjava/lang/Object;Ljava/lang/Object;)I"); UNREF_L(klazz); - IFTHREW { - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionClear; return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on Tracer object."); } @@ -3534,54 +3686,72 @@ JDECL(jint,1trace_1v2)(JENV_CSELF,jobject jDb, jint traceMask, jobject jTracer){ return sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); } -JDECL(jobject,1update_1hook)(JENV_CSELF, jobject jDb, jobject jHook){ +S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)( + JniArgsEnvClass, jobject jDb, jobject jHook +){ return s3jni_updatepre_hook(env, 0, jDb, jHook); } -JDECL(jbyteArray,1value_1blob)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( + JniArgsEnvClass, jobject jpSVal +){ sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); int const nLen = sqlite3_value_bytes(sv); const jbyte * pBytes = sqlite3_value_blob(sv); jbyteArray const jba = pBytes ? (*env)->NewByteArray(env, (jsize)nLen) : NULL; - if(jba){ + if( jba ){ (*env)->SetByteArrayRegion(env, jba, 0, nLen, pBytes); } return jba; } -JDECL(jdouble,1value_1double)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( + JniArgsEnvClass, jobject jpSVal +){ return (jdouble) sqlite3_value_double(PtrGet_sqlite3_value(jpSVal)); } -JDECL(jobject,1value_1dup)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)( + JniArgsEnvClass, jobject jpSVal +){ sqlite3_value * const sv = sqlite3_value_dup(PtrGet_sqlite3_value(jpSVal)); return sv ? new_sqlite3_value_wrapper(env, sv) : 0; } -JDECL(void,1value_1free)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_free(),void,1value_1free)( + JniArgsEnvClass, jobject jpSVal +){ sqlite3_value_free(PtrGet_sqlite3_value(jpSVal)); } -JDECL(jint,1value_1int)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_int(),jint,1value_1int)( + JniArgsEnvClass, jobject jpSVal +){ return (jint) sqlite3_value_int(PtrGet_sqlite3_value(jpSVal)); } -JDECL(jlong,1value_1int64)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( + JniArgsEnvClass, jobject jpSVal +){ return (jlong) sqlite3_value_int64(PtrGet_sqlite3_value(jpSVal)); } -JDECL(jobject,1value_1java_1object)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( + JniArgsEnvClass, jobject jpSVal +){ ResultJavaVal * const rv = sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal), ResultJavaValuePtrStr); return rv ? rv->jObj : NULL; } -JDECL(jbyteArray,1value_1text_1utf8)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)( + JniArgsEnvClass, jobject jpSVal +){ sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); int const n = sqlite3_value_bytes(sv); const unsigned char * const p = sqlite3_value_text(sv); @@ -3593,7 +3763,7 @@ static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){ int const nLen = sqlite3_value_bytes16(sv); jbyteArray jba; const jbyte * pBytes; - switch(mode){ + switch( mode ){ case SQLITE_UTF16: pBytes = sqlite3_value_text16(sv); break; @@ -3610,25 +3780,19 @@ static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){ jba = pBytes ? (*env)->NewByteArray(env, (jsize)nLen) : NULL; - if(jba){ + if( jba ){ (*env)->SetByteArrayRegion(env, jba, 0, nLen, pBytes); } return jba; } -JDECL(jbyteArray,1value_1text16)(JENV_CSELF, jobject jpSVal){ +S3JniApi(sqlite3_value_text16(),jbyteArray,1value_1text16)( + JniArgsEnvClass, jobject jpSVal +){ return value_text16(SQLITE_UTF16, env, jpSVal); } -JDECL(jbyteArray,1value_1text16le)(JENV_CSELF, jobject jpSVal){ - return value_text16(SQLITE_UTF16LE, env, jpSVal); -} - -JDECL(jbyteArray,1value_1text16be)(JENV_CSELF, jobject jpSVal){ - return value_text16(SQLITE_UTF16BE, env, jpSVal); -} - -JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ +JniDecl(void,1do_1something_1for_1developer)(JniArgsEnvClass){ MARKER(("\nVarious bits of internal info:\n")); puts("FTS5 is " #ifdef SQLITE_ENABLE_FTS5 @@ -3685,22 +3849,22 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){ #ifdef SQLITE_ENABLE_FTS5 /* Creates a verbose JNI Fts5 function name. */ -#define JFuncNameFtsXA(Suffix) \ +#define JniFuncNameFtsXA(Suffix) \ Java_org_sqlite_jni_Fts5ExtensionApi_ ## Suffix -#define JFuncNameFtsApi(Suffix) \ +#define JniFuncNameFtsApi(Suffix) \ Java_org_sqlite_jni_fts5_1api_ ## Suffix -#define JFuncNameFtsTok(Suffix) \ +#define JniFuncNameFtsTok(Suffix) \ Java_org_sqlite_jni_fts5_tokenizer_ ## Suffix -#define JDECLFtsXA(ReturnType,Suffix) \ +#define JniDeclFtsXA(ReturnType,Suffix) \ JNIEXPORT ReturnType JNICALL \ - JFuncNameFtsXA(Suffix) -#define JDECLFtsApi(ReturnType,Suffix) \ + JniFuncNameFtsXA(Suffix) +#define JniDeclFtsApi(ReturnType,Suffix) \ JNIEXPORT ReturnType JNICALL \ - JFuncNameFtsApi(Suffix) -#define JDECLFtsTok(ReturnType,Suffix) \ + JniFuncNameFtsApi(Suffix) +#define JniDeclFtsTok(ReturnType,Suffix) \ JNIEXPORT ReturnType JNICALL \ - JFuncNameFtsTok(Suffix) + 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) @@ -3724,7 +3888,7 @@ typedef struct { static void Fts5JniAux_free(Fts5JniAux * const s){ LocalJniGetEnv; - if(env){ + if( env ){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ s3jni_call_xDestroy(env, s->jObj); UNREF_G(s->jObj); @@ -3735,12 +3899,12 @@ static void Fts5JniAux_free(Fts5JniAux * const s){ } static void Fts5JniAux_xDestroy(void *p){ - if(p) Fts5JniAux_free(p); + if( p ) Fts5JniAux_free(p); } static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux)); - if(s){ + if( s ){ jclass klazz; memset(s, 0, sizeof(Fts5JniAux)); s->jObj = REF_G(jObj); @@ -3751,9 +3915,9 @@ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ "Lorg/sqlite/jni/sqlite3_context;" "[Lorg/sqlite/jni/sqlite3_value;)V"); UNREF_L(klazz); - IFTHREW{ - EXCEPTION_REPORT; - EXCEPTION_CLEAR; + S3JniIfThrew{ + S3JniExceptionReport; + S3JniExceptionClear; Fts5JniAux_free(s); s = 0; } @@ -3809,11 +3973,11 @@ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ return pRet; } -JDECLFtsApi(jobject,getInstanceForDb)(JENV_CSELF,jobject jDb){ +JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jobject rv = 0; - if(!ps) return 0; - else if(ps->jFtsApi){ + if( !ps ) return 0; + else if( ps->jFtsApi ){ rv = ps->jFtsApi; }else{ fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb); @@ -3826,16 +3990,16 @@ JDECLFtsApi(jobject,getInstanceForDb)(JENV_CSELF,jobject jDb){ } -JDECLFtsXA(jobject,getInstance)(JENV_CSELF){ +JniDeclFtsXA(jobject,getInstance)(JniArgsEnvClass){ return s3jni_getFts5ExensionApi(env); } -JDECLFtsXA(jint,xColumnCount)(JENV_OSELF,jobject jCtx){ +JniDeclFtsXA(jint,xColumnCount)(JniArgsEnvObj,jobject jCtx){ Fts5ExtDecl; return (jint)fext->xColumnCount(PtrGet_Fts5Context(jCtx)); } -JDECLFtsXA(jint,xColumnSize)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOut32){ +JniDeclFtsXA(jint,xColumnSize)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOut32){ Fts5ExtDecl; int n1 = 0; int const rc = fext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1); @@ -3843,7 +4007,7 @@ JDECLFtsXA(jint,xColumnSize)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOut32) return rc; } -JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol, +JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol, jobject jOut){ Fts5ExtDecl; const char *pz = 0; @@ -3864,7 +4028,7 @@ JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol, return (jint)rc; } -JDECLFtsXA(jint,xColumnTotalSize)(JENV_OSELF,jobject jCtx, jint iCol, jobject jOut64){ +JniDeclFtsXA(jint,xColumnTotalSize)(JniArgsEnvObj,jobject jCtx, jint iCol, jobject jOut64){ Fts5ExtDecl; sqlite3_int64 nOut = 0; int const rc = fext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut); @@ -3893,12 +4057,12 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, jFXA = s3jni_getFts5ExensionApi(env); if( !jFXA ) goto error_oom; jpFts = new_Fts5Context_wrapper(env, pFts); - if(!jpFts) goto error_oom; + if( !jpFts ) goto error_oom; rc = udf_args(env, pCx, argc, argv, &jpCx, &jArgv); - if(rc) goto error_oom; + if( rc ) goto error_oom; (*env)->CallVoidMethod(env, pAux->jObj, pAux->jmid, jFXA, jpFts, jpCx, jArgv); - IFTHREW{ + S3JniIfThrew{ udf_report_exception(env, 1, pCx, pAux->zFuncName, "xFunction"); } UNREF_L(jpFts); @@ -3913,7 +4077,7 @@ error_oom: return; } -JDECLFtsApi(jint,xCreateFunction)(JENV_OSELF, jstring jName, +JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, jobject jUserData, jobject jFunc){ fts5_api * const pApi = PtrGet_fts5_api(jSelf); int rc; @@ -3951,9 +4115,9 @@ struct S3JniFts5AuxData { }; static void S3JniFts5AuxData_xDestroy(void *x){ - if(x){ + if( x ){ S3JniFts5AuxData * const p = x; - if(p->jObj){ + if( p->jObj ){ LocalJniGetEnv; s3jni_call_xDestroy(env, p->jObj); UNREF_G(p->jObj); @@ -3962,12 +4126,12 @@ static void S3JniFts5AuxData_xDestroy(void *x){ } } -JDECLFtsXA(jobject,xGetAuxdata)(JENV_OSELF,jobject jCtx, jboolean bClear){ +JniDeclFtsXA(jobject,xGetAuxdata)(JniArgsEnvObj,jobject jCtx, jboolean bClear){ Fts5ExtDecl; jobject rv = 0; S3JniFts5AuxData * const pAux = fext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear); - if(pAux){ - if(bClear){ + if( pAux ){ + if( bClear ){ if( pAux->jObj ){ rv = REF_L(pAux->jObj); UNREF_G(pAux->jObj); @@ -3981,7 +4145,7 @@ JDECLFtsXA(jobject,xGetAuxdata)(JENV_OSELF,jobject jCtx, jboolean bClear){ return rv; } -JDECLFtsXA(jint,xInst)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOutPhrase, +JniDeclFtsXA(jint,xInst)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOutPhrase, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; int n1 = 0, n2 = 2, n3 = 0; @@ -3994,7 +4158,7 @@ JDECLFtsXA(jint,xInst)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOutPhrase, return rc; } -JDECLFtsXA(jint,xInstCount)(JENV_OSELF,jobject jCtx, jobject jOut32){ +JniDeclFtsXA(jint,xInstCount)(JniArgsEnvObj,jobject jCtx, jobject jOut32){ Fts5ExtDecl; int nOut = 0; int const rc = fext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut); @@ -4002,7 +4166,7 @@ JDECLFtsXA(jint,xInstCount)(JENV_OSELF,jobject jCtx, jobject jOut32){ return (jint)rc; } -JDECLFtsXA(jint,xPhraseCount)(JENV_OSELF,jobject jCtx){ +JniDeclFtsXA(jint,xPhraseCount)(JniArgsEnvObj,jobject jCtx){ Fts5ExtDecl; return (jint)fext->xPhraseCount(PtrGet_Fts5Context(jCtx)); } @@ -4032,7 +4196,7 @@ static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter, EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); } -JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, +JniDeclFtsXA(jint,xPhraseFirst)(JniArgsEnvObj,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; @@ -4048,7 +4212,7 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase, return rc; } -JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, +JniDeclFtsXA(jint,xPhraseFirstColumn)(JniArgsEnvObj,jobject jCtx, jint iPhrase, jobject jIter, jobject jOutCol){ Fts5ExtDecl; Fts5PhraseIter iter; @@ -4062,7 +4226,7 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase, return rc; } -JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, +JniDeclFtsXA(void,xPhraseNext)(JniArgsEnvObj,jobject jCtx, jobject jIter, jobject jOutCol, jobject jOutOff){ Fts5ExtDecl; Fts5PhraseIter iter; @@ -4074,7 +4238,7 @@ JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter, s3jni_phraseIter_NToJ(env, &iter, jIter); } -JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter, +JniDeclFtsXA(void,xPhraseNextColumn)(JniArgsEnvObj,jobject jCtx, jobject jIter, jobject jOutCol){ Fts5ExtDecl; Fts5PhraseIter iter; @@ -4086,7 +4250,7 @@ JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter, } -JDECLFtsXA(jint,xPhraseSize)(JENV_OSELF,jobject jCtx, jint iPhrase){ +JniDeclFtsXA(jint,xPhraseSize)(JniArgsEnvObj,jobject jCtx, jint iPhrase){ Fts5ExtDecl; return (jint)fext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase); } @@ -4115,15 +4279,15 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, LocalJniGetEnv; int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, SJG.fts5.jFtsExt, s->jFcx); - IFTHREW{ + S3JniIfThrew{ EXCEPTION_WARN_CALLBACK_THREW("xQueryPhrase() callback"); - EXCEPTION_CLEAR; + S3JniExceptionClear; rc = SQLITE_ERROR; } return rc; } -JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, +JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase, jobject jCallback){ Fts5ExtDecl; S3JniEnv * const jc = S3JniGlobal_env_cache(env); @@ -4145,7 +4309,7 @@ JDECLFtsXA(jint,xQueryPhrase)(JENV_OSELF,jobject jFcx, jint iPhrase, } -JDECLFtsXA(jint,xRowCount)(JENV_OSELF,jobject jCtx, jobject jOut64){ +JniDeclFtsXA(jint,xRowCount)(JniArgsEnvObj,jobject jCtx, jobject jOut64){ Fts5ExtDecl; sqlite3_int64 nOut = 0; int const rc = fext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut); @@ -4153,18 +4317,18 @@ JDECLFtsXA(jint,xRowCount)(JENV_OSELF,jobject jCtx, jobject jOut64){ return (jint)rc; } -JDECLFtsXA(jlong,xRowid)(JENV_OSELF,jobject jCtx){ +JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){ Fts5ExtDecl; return (jlong)fext->xRowid(PtrGet_Fts5Context(jCtx)); } -JDECLFtsXA(int,xSetAuxdata)(JENV_OSELF,jobject jCtx, jobject jAux){ +JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; S3JniFts5AuxData * pAux; pAux = sqlite3_malloc(sizeof(*pAux)); - if(!pAux){ - if(jAux){ + if( !pAux ){ + if( jAux ){ /* Emulate how xSetAuxdata() behaves when it cannot alloc ** its auxdata wrapper. */ s3jni_call_xDestroy(env, jAux); @@ -4207,7 +4371,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, ** Proxy for Fts5ExtensionApi.xTokenize() and ** fts5_tokenizer.xTokenize() */ -static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, +static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; @@ -4226,9 +4390,9 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, s.fext = fext; s.midCallback = (*env)->GetMethodID(env, klazz, "xToken", "(I[BII)I"); UNREF_L(klazz); - IFTHREW { - EXCEPTION_REPORT; - EXCEPTION_CLEAR; + S3JniIfThrew { + S3JniExceptionReport; + S3JniExceptionClear; s3jni_jbytearray_release(jbaText, pText); return SQLITE_ERROR; } @@ -4247,7 +4411,7 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, }else{ (*env)->FatalError(env, "This cannot happen. Maintenance required."); } - if(s.tok.jba){ + if( s.tok.jba ){ assert( s.tok.zPrev ); UNREF_L(s.tok.jba); } @@ -4255,20 +4419,20 @@ static jint s3jni_fts5_xTokenize(JENV_OSELF, S3NphRef const *pRef, return (jint)rc; } -JDECLFtsXA(jint,xTokenize)(JENV_OSELF,jobject jFcx, jbyteArray jbaText, - jobject jCallback){ +JniDeclFtsXA(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jbyteArray jbaText, + jobject jCallback){ return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5ExtensionApi, 0, jFcx, jbaText, jCallback); } -JDECLFtsTok(jint,xTokenize)(JENV_OSELF,jobject jFcx, jint tokFlags, - jbyteArray jbaText, jobject jCallback){ +JniDeclFtsTok(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jint tokFlags, + jbyteArray jbaText, jobject jCallback){ return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5Tokenizer, tokFlags, jFcx, jbaText, jCallback); } -JDECLFtsXA(jobject,xUserData)(JENV_OSELF,jobject jFcx){ +JniDeclFtsXA(jobject,xUserData)(JniArgsEnvObj,jobject jFcx){ Fts5ExtDecl; Fts5JniAux * const pAux = fext->xUserData(PtrGet_Fts5Context(jFcx)); return pAux ? pAux->jUserData : 0; @@ -4446,7 +4610,7 @@ static int SQLTester_strnotglob(const char *zGlob, const char *z){ JNIEXPORT jint JNICALL Java_org_sqlite_jni_tester_SQLTester_strglob( - JENV_CSELF, jbyteArray baG, jbyteArray baT + JniArgsEnvClass, jbyteArray baG, jbyteArray baT ){ int rc = 0; jbyte * const pG = s3jni_jbytearray_bytes(baG); @@ -4472,7 +4636,7 @@ static int SQLTester_auto_extension(sqlite3 *pDb, const char **pzErr, } JNIEXPORT void JNICALL -Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){ +Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JniArgsEnvClass){ sqlite3_auto_extension( (void(*)(void))SQLTester_auto_extension ); } @@ -4489,7 +4653,7 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JENV_CSELF){ ** function be declared as synchronous. */ JNIEXPORT jboolean JNICALL -Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ +Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JniArgsEnvClass){ int rc; MUTEX_ENV_ENTER; rc = S3JniGlobal_env_uncache(env); @@ -4505,7 +4669,7 @@ Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JENV_CSELF){ ** sqlite3.c instead of sqlite3.h. */ JNIEXPORT void JNICALL -Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ +Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ enum JType { JTYPE_INT, JTYPE_BOOL @@ -4623,7 +4787,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JENV_CSELF){ fieldId = (*env)->GetStaticFieldID(env, jKlazz, pConfFlag->zName, zSig); EXCEPTION_IS_FATAL("Missing an expected static member of the SQLite3Jni class."); assert(fieldId); - switch(pConfFlag->jtype){ + switch( pConfFlag->jtype ){ case JTYPE_INT: (*env)->SetStaticIntField(env, jKlazz, fieldId, (jint)pConfFlag->value); break; diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 8052a6027e..8ec6184cd1 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1747,22 +1747,6 @@ JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text16 (JNIEnv *, jclass, jobject); -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_value_text16le - * Signature: (Lorg/sqlite/jni/sqlite3_value;)[B - */ -JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text16le - (JNIEnv *, jclass, jobject); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_value_text16be - * Signature: (Lorg/sqlite/jni/sqlite3_value;)[B - */ -JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text16be - (JNIEnv *, jclass, jobject); - /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_value_type diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index ef67890918..91a80ec431 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1116,12 +1116,13 @@ public final class SQLite3Jni { /** Binds the given text using C's sqlite3_result_text64() unless: - - text is null ==> sqlite3_result_null() + - text is null: translates to a call to sqlite3_result_null() - - text is too large ==> sqlite3_result_error_toobig() + - text is too large: translates to a call to + sqlite3_result_error_toobig() - - The `encoding` argument has an invalid value ==> - sqlite3_result_error_code() with SQLITE_FORMAT + - The `encoding` argument has an invalid value: translates to + sqlite3_result_error_code() with code SQLITE_FORMAT. If maxLength (in bytes, not characters) is larger than text.length, it is silently truncated to text.length. If it is @@ -1170,37 +1171,6 @@ public final class SQLite3Jni { } } - /** - Sets the current UDF result to the given bytes, which are assumed - be encoded in UTF-16LE. - */ - public static void sqlite3_result_text16le( - @NotNull sqlite3_context cx, @Nullable String text - ){ - if(null == text) sqlite3_result_null(cx); - else{ - final byte[] b = text.getBytes(StandardCharsets.UTF_16LE); - sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16LE); - } - } - - /** - Sets the current UDF result to the given bytes, which are assumed - be encoded in UTF-16BE. - */ - public static void sqlite3_result_text16be( - @NotNull sqlite3_context cx, @Nullable byte[] text - ){ - sqlite3_result_text64(cx, text, text.length, SQLITE_UTF16BE); - } - - public static void sqlite3_result_text16be( - @NotNull sqlite3_context cx, @NotNull String text - ){ - final byte[] b = text.getBytes(StandardCharsets.UTF_16BE); - sqlite3_result_text64(cx, b, b.length, SQLITE_UTF16BE); - } - public static native RollbackHook sqlite3_rollback_hook( @NotNull sqlite3 db, @Nullable RollbackHook hook ); @@ -1337,10 +1307,6 @@ public final class SQLite3Jni { public static native byte[] sqlite3_value_text16(@NotNull sqlite3_value v); - public static native byte[] sqlite3_value_text16le(@NotNull sqlite3_value v); - - public static native byte[] sqlite3_value_text16be(@NotNull sqlite3_value v); - public static native int sqlite3_value_type(@NotNull sqlite3_value v); public static native int sqlite3_value_numeric_type(@NotNull sqlite3_value v); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index cdbf860666..250c25bc06 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1447,7 +1447,7 @@ public class Tester1 implements Runnable { -v: emit some developer-mode info at the end. */ public static void main(String[] args) throws Exception { - Integer nThread = null; + Integer nThread = 1; boolean doSomethingForDev = false; Integer nRepeat = 1; boolean forceFail = false; @@ -1536,7 +1536,7 @@ public class Tester1 implements Runnable { outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ - if( nThread==null || nThread<=1 ){ + if( nThread<=1 ){ new Tester1(0).runTests(false); continue; } diff --git a/manifest b/manifest index 875bc7a4b2..414c6b82a4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Doc\sstyle\sfixes. -D 2023-08-24T15:42:08.168 +C Doc,\scode\sstyle,\sand\slegibility\scleanups. +D 2023-08-24T17:25:05.398 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -235,8 +235,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 2e17aae8debf0b0ee12010500eae7bd9557f8ad5554f0161c39a41f229e84e3e F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c d7d6d420f2a13d55828cee19ba17a37c4244532dafbc5822582d7fd52ae2aaf0 -F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2 +F ext/jni/src/c/sqlite3-jni.c 1da808c65c101d603bc4d8c755aff618c6ce9331b79ef431128ff076cdfca8f7 +F ext/jni/src/c/sqlite3-jni.h d1ee39fe20cb5ac189c5b4c3afa5ff47e259ccfed006eee629c2fdf9fc474856 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -257,8 +257,8 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c 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 956063c854c4f662183c41c65d0ab48b5e2127824b8053eeb05b9fc40f0d09e3 -F ext/jni/src/org/sqlite/jni/Tester1.java 9b6ec0ae299a56822e82e7dc2cf7ef1031ae87bcb595065bef84b7edac7114f5 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java eaf6a3d6814465c2a9e67d6b32a4af2b8efaaa69cf905f8bb81c05222bfaf602 +F ext/jni/src/org/sqlite/jni/Tester1.java 3fcc891398e412fdbd49b40140c1ded83991218d396016184237bf74e1bc18c5 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2094,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 53000cdae9b265752f8d8616679da621a571a6590c6eaeca23cf87a0e13aa4c4 -R 8df76b9c961752a2b50a8bb41ef16d24 +P 862f9828e078ae138c3533658c45e4c45155975794e752b9b3a71a693842f37a +R eb32cfcd718e24e179eb7f83fffb0fa5 U stephan -Z ce40bd6a5799196ea7241aee36380622 +Z 2f5c08343242711ee223d20d111fc13d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index fbb96c440f..d8198b164b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -862f9828e078ae138c3533658c45e4c45155975794e752b9b3a71a693842f37a \ No newline at end of file +cf185bcd25629d882a030b8b87048179a120ab1f84aa1d68b279c499dbdf0dba \ No newline at end of file From eef599dbd478ba234acf122a9aa75c146205d8a1 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 18:43:25 +0000 Subject: [PATCH 048/422] More code legibility and style improvements in the JNI pieces. Start work on a javadoc build. FossilOrigin-Name: 62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf --- ext/jni/GNUmakefile | 23 ++-- ext/jni/src/c/sqlite3-jni.c | 119 +++++++++--------- ext/jni/src/c/sqlite3-jni.h | 4 +- ext/jni/src/org/sqlite/jni/SQLFunction.java | 18 +-- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 79 ++++++------ ext/jni/src/org/sqlite/jni/Tester1.java | 2 +- ext/jni/src/org/sqlite/jni/package-info.java | 6 + .../src/org/sqlite/jni/tester/SQLTester.java | 2 +- manifest | 25 ++-- manifest.uuid | 2 +- 10 files changed, 154 insertions(+), 126 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/package-info.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index e3dfb53c1d..a6711043fc 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -6,9 +6,10 @@ JAVA_HOME ?= $(HOME)/jdk/current # e.g. /usr/lib/jvm/default-javajava-19-openjdk-amd64 JDK_HOME ?= $(JAVA_HOME) # ^^^ JDK_HOME is not as widely used as JAVA_HOME -bin.javac := $(JDK_HOME)/bin/javac -bin.java := $(JDK_HOME)/bin/java -bin.jar := $(JDK_HOME)/bin/jar +bin.jar := $(JDK_HOME)/bin/jar +bin.java := $(JDK_HOME)/bin/java +bin.javac := $(JDK_HOME)/bin/javac +bin.javadoc := $(JDK_HOME)/bin/javadoc ifeq (,$(wildcard $(JDK_HOME))) $(error set JDK_HOME to the top-most dir of your JDK installation.) endif @@ -17,18 +18,18 @@ $(MAKEFILE): package.jar := sqlite3-jni.jar -dir.top := ../.. -dir.tool := ../../tool -dir.jni := $(patsubst %/,%,$(dir $(MAKEFILE))) - +dir.top := ../.. +dir.tool := ../../tool +dir.jni := $(patsubst %/,%,$(dir $(MAKEFILE))) dir.src := $(dir.jni)/src dir.src.c := $(dir.src)/c dir.bld := $(dir.jni)/bld dir.bld.c := $(dir.bld) dir.src.jni := $(dir.src)/org/sqlite/jni dir.src.jni.tester := $(dir.src.jni)/tester +mkdir := mkdir -p $(dir.bld.c): - mkdir -p $@ + $(mkdir) $@ classpath := $(dir.src) CLEAN_FILES := $(package.jar) @@ -285,6 +286,12 @@ $(package.jar): $(CLASS_FILES) $(MAKEFILE) $(package.jar.in) jar: $(package.jar) +dir.doc := $(dir.jni)/doc +doc: $(JAVA_FILES.main) + $(bin.javadoc) -cp $(classpath) -d $(dir.doc) org.sqlite.jni + +######################################################################## +# Clean up... CLEAN_FILES += $(dir.bld.c)/* \ $(dir.src.jni)/*.class \ $(dir.src.jni.tester)/*.class \ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index def5f813fb..c62aa1765a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -297,7 +297,7 @@ enum { ** index field of those entries are the keys for this particular ** cache. */ - NphCache_SIZE = sizeof(S3NphRefs) / sizeof(S3NphRef) + S3Jni_NphCache_size = sizeof(S3NphRefs) / sizeof(S3NphRef) }; /* @@ -318,12 +318,16 @@ struct S3JniNphClass { ** by the sqlite3_context binding. */; }; -/** State for various hook callbacks. */ +/* +** State for binding C callbacks to Java methods. +*/ typedef struct S3JniHook S3JniHook; struct S3JniHook{ jobject jObj /* global ref to Java instance */; jmethodID midCallback /* callback method. Signature depends on ** jObj's type */; + /* We lookup the jObj.xDestroy() method as-needed for contexts which + ** have custom finalizers. */ }; /* @@ -433,10 +437,11 @@ struct S3JniGlobalType { ** JNIEnv when necessary. */ JavaVM * jvm; - /* Cache of Java refs/IDs for NativePointerHolder subclasses. + /* + ** Cache of Java refs/IDs for NativePointerHolder subclasses. ** Initialized on demand. */ - S3JniNphClass nph[NphCache_SIZE]; + S3JniNphClass nph[S3Jni_NphCache_size]; /* ** Cache of per-thread state. */ @@ -542,47 +547,47 @@ static void s3jni_incr( volatile unsigned int * const p ){ #endif /* Helpers for working with specific mutexes. */ -#define MUTEX_ENV_ASSERT_LOCKED \ +#define S3JniMutex_Env_assertLocked \ assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENV_ASSERT_LOCKER \ +#define S3JniMutex_Env_assertLocker \ assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENV_ASSERT_NOTLOCKER \ +#define S3JniMutex_Env_assertNotLocker \ assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define MUTEX_ENV_ENTER \ - MUTEX_ENV_ASSERT_NOTLOCKER; \ +#define S3JniMutex_Env_enter \ + S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ ++SJG.metrics.nMutexEnv; \ SJG.envCache.locker = env -#define MUTEX_ENV_LEAVE \ +#define S3JniMutex_Env_leave \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env));*/ \ - MUTEX_ENV_ASSERT_LOCKER; \ + S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) -#define MUTEX_EXT_ENTER \ +#define S3JniMutex_Ext_enter \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ ++SJG.metrics.nMutexAutoExt -#define MUTEX_EXT_LEAVE \ +#define S3JniMutex_Ext_leave \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_leave( SJG.autoExt.mutex ) -#define MUTEX_NPH_ENTER \ - MUTEX_ENV_ASSERT_NOTLOCKER; \ +#define S3JniMutex_Nph_enter \ + S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ ++SJG.metrics.nMutexEnv2; \ SJG.envCache.locker = env -#define MUTEX_NPH_LEAVE \ +#define S3JniMutex_Nph_leave \ /*MARKER(("Leaving NPH mutex @%p %s.\n", env));*/ \ - MUTEX_ENV_ASSERT_LOCKER; \ + S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) -#define MUTEX_PDB_ENTER \ +#define S3JniMutex_Pdb_enter \ /*MARKER(("Entering PerDb mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ ++SJG.metrics.nMutexPerDb; \ SJG.perDb.locker = env; -#define MUTEX_PDB_LEAVE \ +#define S3JniMutex_Pdb_leave \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) @@ -612,12 +617,12 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ */ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ struct S3JniEnv * row; - MUTEX_ENV_ENTER; + S3JniMutex_Env_enter; row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ s3jni_incr( &SJG.metrics.envCacheHits ); - MUTEX_ENV_LEAVE; + S3JniMutex_Env_leave; return row; } } @@ -637,7 +642,7 @@ static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ SJG.envCache.aHead = row; row->env = env; - MUTEX_ENV_LEAVE; + S3JniMutex_Env_leave; return row; } @@ -897,7 +902,7 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest */ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ if(s){ - MUTEX_PDB_ENTER; + S3JniMutex_Pdb_enter; assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); @@ -927,7 +932,7 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; - MUTEX_PDB_LEAVE; + S3JniMutex_Pdb_leave; } } @@ -938,7 +943,7 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ */ static int S3JniGlobal_env_uncache(JNIEnv * const env){ struct S3JniEnv * row; - MUTEX_ENV_ASSERT_LOCKED; + S3JniMutex_Env_assertLocked; row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ @@ -967,7 +972,7 @@ static int S3JniGlobal_env_uncache(JNIEnv * const env){ ** ** It is up to the caller to populate the other members of the ** returned object if needed, taking care to lock the population with -** MUTEX_NPH_ENTER/LEAVE. +** S3JniMutex_Nph_enter/LEAVE. ** ** This simple cache catches >99% of searches in the current ** (2023-07-31) tests. @@ -989,15 +994,17 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* cached as well. */ S3JniNphClass * const pNC = &SJG.nph[pRef->index]; + assert( (void*)pRef>=(void*)&S3NphRefs && (void*)pRef<(void*)(&S3NphRefs + 1) + && "pRef is out of range." ); if( !pNC->pRef ){ - MUTEX_NPH_ENTER; + S3JniMutex_Nph_enter; if( !pNC->pRef ){ pNC->pRef = pRef; pNC->klazz = (*env)->FindClass(env, pRef->zName); EXCEPTION_IS_FATAL("FindClass() unexpectedly threw"); pNC->klazz = REF_G(pNC->klazz); } - MUTEX_NPH_LEAVE; + S3JniMutex_Nph_leave; } return pNC; } @@ -1009,12 +1016,12 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); if( !pNC->fidValue ){ - MUTEX_NPH_ENTER; + S3JniMutex_Nph_enter; if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); } - MUTEX_NPH_LEAVE; + S3JniMutex_Nph_leave; } return pNC->fidValue; } @@ -1057,7 +1064,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; - MUTEX_PDB_ENTER; + S3JniMutex_Pdb_enter; if( SJG.perDb.aFree ){ rv = SJG.perDb.aFree; SJG.perDb.aFree = rv->pNext; @@ -1088,7 +1095,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->jDb = REF_G(jDb); rv->pDb = pDb; } - MUTEX_PDB_LEAVE; + S3JniMutex_Pdb_leave; return rv; } @@ -1106,7 +1113,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if( jDb || pDb ){ - MUTEX_PDB_ENTER; + S3JniMutex_Pdb_enter; s = SJG.perDb.aUsed; if( !pDb ){ assert( jDb ); @@ -1117,7 +1124,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ break; } } - MUTEX_PDB_LEAVE; + S3JniMutex_Pdb_leave; } return s; } @@ -1183,12 +1190,12 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx, S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context); if( !pNC->fidAggCtx ){ - MUTEX_NPH_ENTER; + S3JniMutex_Nph_enter; if( !pNC->fidAggCtx ){ pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, "aggregateContext", "J"); EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member."); } - MUTEX_NPH_LEAVE; + S3JniMutex_Nph_leave; } pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : sizeof(void*)); if( pAgg || isFinal ){ @@ -1218,12 +1225,12 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, jobject const jOut){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); if( !pNC->fidValue ){ - MUTEX_NPH_ENTER; + S3JniMutex_Nph_enter; if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "value", zTypeSig); EXCEPTION_IS_FATAL("setupOutputPointer() could not find OutputPointer.*.value"); } - MUTEX_NPH_LEAVE; + S3JniMutex_Nph_leave; } return pNC->fidValue; } @@ -1430,12 +1437,12 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const jobject rv = 0; S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); if( !pNC->midCtor ){ - MUTEX_NPH_ENTER; + S3JniMutex_Nph_enter; if( !pNC->midCtor ){ pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); EXCEPTION_IS_FATAL("Cannot find constructor for class."); } - MUTEX_NPH_LEAVE; + S3JniMutex_Nph_leave; } rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); EXCEPTION_IS_FATAL("No-arg constructor threw."); @@ -1855,14 +1862,14 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, ** local reference to it, to avoid a race condition with another ** thread manipulating the list during the call and invaliding ** what ax points to. */; - MUTEX_EXT_ENTER; + S3JniMutex_Ext_enter; if( i >= SJG.autoExt.nExt ){ go = 0; }else{ ax.jObj = REF_L(SJG.autoExt.pExt[i].jObj); ax.midFunc = SJG.autoExt.pExt[i].midFunc; } - MUTEX_EXT_LEAVE; + S3JniMutex_Ext_leave; if( ax.jObj ){ rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); UNREF_L(ax.jObj); @@ -1890,12 +1897,12 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( int rc = 0; if( !jAutoExt ) return SQLITE_MISUSE; - MUTEX_EXT_ENTER; + S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ /* Look for match or first empty slot. */ ax = &SJG.autoExt.pExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - MUTEX_EXT_LEAVE; + S3JniMutex_Ext_leave; return 0 /* this as a no-op. */; } } @@ -1931,7 +1938,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( ++SJG.autoExt.nExt; } } - MUTEX_EXT_LEAVE; + S3JniMutex_Ext_leave; return rc; } @@ -2082,7 +2089,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; int i; - MUTEX_EXT_ENTER; + S3JniMutex_Ext_enter; /* This algo mirrors the one in the core. */ for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ ax = &SJG.autoExt.pExt[i]; @@ -2098,7 +2105,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( break; } } - MUTEX_EXT_LEAVE; + S3JniMutex_Ext_leave; return rc; } @@ -3204,12 +3211,12 @@ S3JniApi(sqlite3_reset(),jint,1reset)( /* Clears all entries from S3JniGlobal.autoExt. */ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; - MUTEX_EXT_ENTER; + S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ S3JniAutoExtension_clear( env, &SJG.autoExt.pExt[i] ); } SJG.autoExt.nExt = 0; - MUTEX_EXT_LEAVE; + S3JniMutex_Ext_leave; } S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)( @@ -3571,11 +3578,11 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( JniArgsEnvClass ){ s3jni_reset_auto_extension(env); - MUTEX_ENV_ENTER; + S3JniMutex_Env_enter; while( SJG.envCache.aHead ){ S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); } - MUTEX_ENV_LEAVE; + S3JniMutex_Env_leave; /* Do not clear S3JniGlobal.jvm: it's legal to call sqlite3_initialize() again to restart the lib. */ return sqlite3_shutdown(); @@ -3812,7 +3819,7 @@ JniDecl(void,1do_1something_1for_1developer)(JniArgsEnvClass){ SO(S3JniDb); SO(S3NphRefs); printf("\t(^^^ %u NativePointerHolder subclasses)\n", - (unsigned)NphCache_SIZE); + (unsigned)S3Jni_NphCache_size); SO(S3JniGlobal); SO(S3JniAutoExtension); SO(S3JniUdf); @@ -3945,14 +3952,14 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ jobject pNPH = new_NativePointerHolder_object( env, &S3NphRefs.Fts5ExtensionApi, s3jni_ftsext() ); - MUTEX_ENV_ENTER; + S3JniMutex_Env_enter; if( pNPH ){ if( !SJG.fts5.jFtsExt ){ SJG.fts5.jFtsExt = REF_G(pNPH); } UNREF_L(pNPH); } - MUTEX_ENV_LEAVE; + S3JniMutex_Env_leave; } return SJG.fts5.jFtsExt; } @@ -4655,9 +4662,9 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JniArgsEnvClass){ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JniArgsEnvClass){ int rc; - MUTEX_ENV_ENTER; + S3JniMutex_Env_enter; rc = S3JniGlobal_env_uncache(env); - MUTEX_ENV_LEAVE; + S3JniMutex_Env_leave; return rc ? JNI_TRUE : JNI_FALSE; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 8ec6184cd1..b9f03983a3 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1789,10 +1789,10 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1subtype /* * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_do_something_for_developer + * Method: sqlite3_jni_internal_details * Signature: ()V */ -JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1do_1something_1for_1developer +JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1jni_1internal_1details (JNIEnv *, jclass); #ifdef __cplusplus diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index c8e87ff827..bc6f608736 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -19,6 +19,8 @@ package org.sqlite.jni; access to the callback functions needed in order to implement SQL functions in Java. +

+ This class is not used by itself, but is a marker base class. The three UDF types are modelled by the inner classes Scalar, Aggregate, and Window. Most simply, clients may subclass @@ -36,19 +38,19 @@ public abstract class SQLFunction { managing their accumulator state across calls to the UDF's callbacks. - If a given aggregate or window function is called multiple times +

If a given aggregate or window function is called multiple times in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., then the clients need some way of knowing which call is which so that they can map their state between their various UDF callbacks and reset it via xFinal(). This class takes care of such mappings. - This class works by mapping +

This class works by mapping sqlite3_context.getAggregateContext() to a single piece of state, of a client-defined type (the T part of this class), which persists across a "matching set" of the UDF's callbacks. - This class is a helper providing commonly-needed functionality - +

This class is a helper providing commonly-needed functionality - it is not required for use with aggregate or window functions. Client UDFs are free to perform such mappings using custom approaches. The provided Aggregate and Window classes @@ -69,7 +71,7 @@ public abstract class SQLFunction { without requiring that the client update the underlying map's entry. - T must be of a type which can be legally stored as a value in +

T must be of a type which can be legally stored as a value in java.util.HashMap. */ public ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ @@ -95,7 +97,9 @@ public abstract class SQLFunction { } } - //! Subclass for creating scalar functions. + /** + Subclass for creating scalar functions. + */ public static abstract class Scalar extends SQLFunction { /** @@ -151,7 +155,7 @@ public abstract class SQLFunction { argument, the context is set to the given initial value. On all other calls, the 2nd argument is ignored. - @see PerContextState#takeAggregateState() + @see SQLFunction.PerContextState#getAggregateState() */ protected final ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ return map.getAggregateState(cx, initialValue); @@ -161,7 +165,7 @@ public abstract class SQLFunction { To be called from the implementation's xFinal() method to fetch the final state of the UDF and remove its mapping. - @see PerContextState#takeAggregateState() + see SQLFunction.PerContextState#takeAggregateState() */ protected final T takeAggregateState(sqlite3_context cx){ return map.takeAggregateState(cx); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 91a80ec431..e5a8b6e243 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -33,7 +33,7 @@ import java.lang.annotation.ElementType; /** This annotation is for flagging parameters which may not legally be - null. Note that the C-style API does _not_ throw any + null. Note that the C-style API does not throw any NullPointerExceptions on its own because it has a no-throw policy in order to retain its C-style semantics. @@ -50,62 +50,64 @@ import java.lang.annotation.ElementType; This class contains the entire sqlite3 JNI API binding. For client-side use, a static import is recommended: - ``` + {@code import static org.sqlite.jni.SQLite3Jni.*; - ``` + } The C-side part can be found in sqlite3-jni.c. - Only functions which materially differ from their C counterparts +

Only functions which materially differ from their C counterparts are documented here. The C documetation is otherwise applicable here: - https://sqlite.org/c3ref/intro.html +

{link https://sqlite.org/c3ref/intro.html} - A handful of Java-specific APIs have been added. +

A handful of Java-specific APIs have been added. - ****************************************************************** - *** Warning regarding Java's Modified UTF-8 vs standard UTF-8: *** - ****************************************************************** +

Notes regarding Java's Modified UTF-8 vs standard UTF-8: - SQLite internally uses UTF-8 encoding, whereas Java natively uses +

SQLite internally uses UTF-8 encoding, whereas Java natively uses UTF-16. Java JNI has routines for converting to and from UTF-8, - _but_ JNI uses what its docs call modified UTF-8 (see links below) + but JNI uses what its docs call modified UTF-8 (see links below) Care must be taken when converting Java strings to or from standard UTF-8 to ensure that the proper conversion is performed. In short, Java's `String.getBytes(StandardCharsets.UTF_8)` performs the proper conversion in Java, and there are no JNI C APIs for that conversion (JNI's `NewStringUTF()` requires its input to be in MUTF-8). - The known consequences and limitations this discrepancy places on +

The known consequences and limitations this discrepancy places on the SQLite3 JNI binding include: - - Any functions which return state from a database take extra care - to perform proper conversion, at the cost of efficiency. +

    - - C functions which take C-style strings without a length argument - require special care when taking input from Java. In particular, - Java strings converted to byte arrays for encoding purposes are - not NUL-terminated, and conversion to a Java byte array must be - careful to add one. Functions which take a length do not require - this so long as the length is provided. Search the SQLite3Jni - class for "\0" for many examples. +
  • Any functions which return state from a database take extra care + to perform proper conversion, at the cost of efficiency.
  • - - Similarly, C-side code which deals with strings which might not be - NUL-terminated (e.g. while tokenizing in FTS5-related code) cannot - use JNI's new-string functions to return them to Java because none - of those APIs take a string-length argument. Such cases must - return byte arrays instead of strings. +
  • C functions which take C-style strings without a length argument + require special care when taking input from Java. In particular, + Java strings converted to byte arrays for encoding purposes are not + NUL-terminated, and conversion to a Java byte array must be careful + to add one. Functions which take a length do not require this so + long as the length is provided. Search the SQLite3Jni class for "\0" + for many examples. - Further reading: +
  • Similarly, C-side code which deals with strings which might not + be NUL-terminated (e.g. while tokenizing in FTS5-related code) + cannot use JNI's new-string functions to return them to Java because + none of those APIs take a string-length argument. Such cases must + return byte arrays instead of strings. - - https://stackoverflow.com/questions/57419723 - - https://stackoverflow.com/questions/7921016 - - https://itecnote.com/tecnote/java-getting-true-utf-8-characters-in-java-jni/ - - https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html#unicode - - https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8 +
+ +

Further reading: + +

https://stackoverflow.com/questions/57419723 +

https://stackoverflow.com/questions/7921016 +

https://itecnote.com/tecnote/java-getting-true-utf-8-characters-in-java-jni/ +

https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html#unicode +

https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8 */ public final class SQLite3Jni { @@ -124,7 +126,7 @@ public final class SQLite3Jni { This will clean up any cached per-JNIEnv info. Calling into the library will re-initialize the cache on demand. - This process does _not_ close any databases or finalize + This process does not close any databases or finalize any prepared statements because their ownership does not depend on a given thread. For proper library behavior, and to avoid C-side leaks, be sure to finalize all statements and close @@ -1194,7 +1196,7 @@ public final class SQLite3Jni { /** Internal impl of the public sqlite3_strglob() method. Neither argument - may be NULL and both _MUST_ be NUL-terminated. + may be NULL and both MUST be NUL-terminated. */ private static native int sqlite3_strglob( @NotNull byte[] glob, @NotNull byte[] txt @@ -1211,7 +1213,7 @@ public final class SQLite3Jni { /** Internal impl of the public sqlite3_strlike() method. Neither - argument may be NULL and both _MUST_ be NUL-terminated. + argument may be NULL and both MUST be NUL-terminated. */ private static native int sqlite3_strlike( @NotNull byte[] glob, @NotNull byte[] txt, int escChar @@ -1319,10 +1321,11 @@ public final class SQLite3Jni { /** This is NOT part of the public API. It exists solely as a place - to hook in arbitrary C-side code during development and testing - of this library. + for this code's developers to collect internal metrics and such. + It has no stable interface. It may go way or change behavior at + any time. */ - public static native void sqlite3_do_something_for_developer(); + public static native void sqlite3_jni_internal_details(); ////////////////////////////////////////////////////////////////////// // SQLITE_... constants follow... diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 250c25bc06..eb383fe8d2 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1575,7 +1575,7 @@ public class Tester1 implements Runnable { outln("\tAssertions checked: ",affirmCount); outln("\tDatabases opened: ",metrics.dbOpen); if( doSomethingForDev ){ - sqlite3_do_something_for_developer(); + sqlite3_jni_internal_details(); } sqlite3_shutdown(); int nMethods = 0; diff --git a/ext/jni/src/org/sqlite/jni/package-info.java b/ext/jni/src/org/sqlite/jni/package-info.java new file mode 100644 index 0000000000..2629d29282 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/package-info.java @@ -0,0 +1,6 @@ +/** + This package houses a JNI binding to the SQLite3 C API. + + The docs are in progress. +*/ +package org.sqlite.jni; diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index ef3b839bc1..e5107dca27 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -629,7 +629,7 @@ public class SQLTester { t.outln("Aborted ",t.nAbortedScript," script(s)."); } if( dumpInternals ){ - sqlite3_do_something_for_developer(); + sqlite3_jni_internal_details(); } } } diff --git a/manifest b/manifest index 414c6b82a4..ba8d4aea96 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Doc,\scode\sstyle,\sand\slegibility\scleanups. -D 2023-08-24T17:25:05.398 +C More\scode\slegibility\sand\sstyle\simprovements\sin\sthe\sJNI\spieces.\sStart\swork\son\sa\sjavadoc\sbuild. +D 2023-08-24T18:43:25.053 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 2e17aae8debf0b0ee12010500eae7bd9557f8ad5554f0161c39a41f229e84e3e +F ext/jni/GNUmakefile 6b3c0fd8d055c129735702d0b288589d25544dd404a00d46d9eb43770fe7f78f F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 1da808c65c101d603bc4d8c755aff618c6ce9331b79ef431128ff076cdfca8f7 -F ext/jni/src/c/sqlite3-jni.h d1ee39fe20cb5ac189c5b4c3afa5ff47e259ccfed006eee629c2fdf9fc474856 +F ext/jni/src/c/sqlite3-jni.c 8db2fcc05dd7749f9f4175e2654344feccac6abfd19fd9db0d116c6350e3b625 +F ext/jni/src/c/sqlite3-jni.h 2b81cfb83933cb18e5f690487f4556591d3329538809c847d00190aa4d69aa1d F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -255,10 +255,10 @@ F ext/jni/src/org/sqlite/jni/PreUpdateHook.java dec00a706b58c67989f0ff56c4f0a703 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/SQLFunction.java 5851698d96ee29171d68930ad758d0f5a253f7575f1feb890d82b2557a8d3ef5 F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java eaf6a3d6814465c2a9e67d6b32a4af2b8efaaa69cf905f8bb81c05222bfaf602 -F ext/jni/src/org/sqlite/jni/Tester1.java 3fcc891398e412fdbd49b40140c1ded83991218d396016184237bf74e1bc18c5 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d96f10a097c1d614b44353e85a65368d9aca565d5ae57fae0104811594fbdfba +F ext/jni/src/org/sqlite/jni/Tester1.java 76f308ad9bf0bd74374561c30c65564ed24583a465264b751d9e2333980149f1 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -266,11 +266,12 @@ F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b +F ext/jni/src/org/sqlite/jni/package-info.java 1a547913d681411d65c5fe0bca840f049abe5612740154a125545ea9e2481747 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc F ext/jni/src/org/sqlite/jni/sqlite3_context.java fe7797a696978f057528a57b7a11e7797ed41fd7afcf100c5ebb67055d9f706f F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 2835eb3dd1e14767ca49354c224150c70300d8013d6d51dd875f7d9380faa278 +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2094,8 +2095,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 862f9828e078ae138c3533658c45e4c45155975794e752b9b3a71a693842f37a -R eb32cfcd718e24e179eb7f83fffb0fa5 +P cf185bcd25629d882a030b8b87048179a120ab1f84aa1d68b279c499dbdf0dba +R 409ebb8571a40aa1108ef1efd5f91432 U stephan -Z 2f5c08343242711ee223d20d111fc13d +Z 85677113ed9046ae48bf181600ab6418 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d8198b164b..af25bbe444 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cf185bcd25629d882a030b8b87048179a120ab1f84aa1d68b279c499dbdf0dba \ No newline at end of file +62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf \ No newline at end of file From 36018803d6ced8fac948ea83dfd8c8da737d7bd9 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 24 Aug 2023 19:08:50 +0000 Subject: [PATCH 049/422] Add doc/testrunner.md, for documenting the testrunner.tcl script. FossilOrigin-Name: 9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23 --- doc/testrunner.md | 284 ++++++++++++++++++++++++++++++++++++++++++++ manifest | 15 +-- manifest.uuid | 2 +- test/testrunner.tcl | 11 +- 4 files changed, 300 insertions(+), 12 deletions(-) create mode 100644 doc/testrunner.md diff --git a/doc/testrunner.md b/doc/testrunner.md new file mode 100644 index 0000000000..d828fd76d8 --- /dev/null +++ b/doc/testrunner.md @@ -0,0 +1,284 @@ + + +# The testrunner.tcl Script + +# 1. Overview + +testrunner.tcl is a Tcl script used to run multiple SQLite tests using +multiple jobs. It supports the following types of tests: + + * Tcl test scripts. + + * Tests run with [make] commands. Specifically, at time of writing, + [make fuzztest], [make mptest], [make sourcetest] and [make threadtest]. + +testrunner.tcl pipes the output of all tests and builds run into log file +**testrunner.log**, created in the cwd directory. Searching this file for +"failed" is a good way to find the output of a failed test. + +testrunner.tcl also populates SQLite database **testrunner.db**. This database +contains details of all tests run, running and to be run. A useful query +might be: + +``` + SELECT * FROM script WHERE state='failed' +``` + +Running the command: + +``` + ./testfixture $(TESTDIR)/testrunner.tcl status +``` + +in the directory containing the testrunner.db database runs various queries +to produce a succinct report on the state of a running testrunner.tcl script. +Running: + +``` + watch ./testfixture $(TESTDIR)/testrunner.tcl status +``` + +in another terminal is a good way to keep an eye on a long running test. + +Sometimes testrunner.tcl uses the [testfixture] binary that it is run with +to run tests (see "Binary Tests" below). Sometimes it builds testfixture and +other binaries in specific configurations to test (see "Source Tests"). + +# 2. Binary Tests + +The commands described in this section all run various combinations of the Tcl +test scripts using the [testfixture] binary used to run the testrunner.tcl +script (i.e. they do not invoke the compiler to build new binaries, or the +[make] command to run tests that are not Tcl scripts). The procedure to run +these tests is therefore: + + 1. Build the "testfixture" (or "testfixture.exe" for windows) binary using + whatever method seems convenient. + + 2. Test the binary built in step 1 by running testrunner.tcl with it, + perhaps with various options. + +The following sub-sections describe the various options that can be +passed to testrunner.tcl to test binary testfixture builds. + +## 2.1. Organization of Tcl Tests + +Tcl tests are stored in files that match the pattern *\*.test*. They are +found in both the $TOP/test/ directory, and in the various sub-directories +of the $TOP/ext/ directory of the source tree. Not all *\*.test* files +contain Tcl tests - a handful are Tcl scripts designed to invoke other +*\*.test* files. + +The **veryquick** set of tests is a subset of all Tcl test scripts in the +source tree. In includes most tests, but excludes some that are very slow. +Almost all fault-injection tests (those that test the response of the library +to OOM or IO errors) are excluded. It is defined in source file +*test/permutations.test*. + +The **full** set of tests includes all Tcl test scripts in the source tree. +To run a "full" test is to run all Tcl test scripts that can be found in the +source tree. + +File *permutations.test* defines various test "permutations". A permutation +consists of: + + * A subset of Tcl test scripts, and + + * Runtime configuration to apply before running each test script + (e.g. enabling auto-vacuum, or disable lookaside). + +Running **all** tests is to run all tests in the full test set, plus a dozen +or so permutations. The specific permutations that are run as part of "all" +are defined in file *testrunner_data.tcl*. + +## 2.2. Commands to Run Tests + +To run the "veryquick" test set, use either of the following: + +``` + ./testfixture $TESTDIR/testrunner.tcl + ./testfixture $TESTDIR/testrunner.tcl veryquick +``` + +To run the "full" test suite: + +``` + ./testfixture $TESTDIR/testrunner.tcl full +``` + +To run the subset of the "full" test suite for which the test file name matches +a specified pattern (e.g. all tests that start with "fts5"), either of: + +``` + ./testfixture $TESTDIR/testrunner.tcl fts5% + ./testfixture $TESTDIR/testrunner.tcl 'fts5*' +``` + +To run "all" tests (full + permutations): + +``` + ./testfixture $TESTDIR/testrunner.tcl all +``` + + +## 2.3. Investigating Binary Test Failures + +If a test fails, testrunner.tcl reports name of the Tcl test script and, if +applicable, the name of the permutation, to stdout. This information can also +be retrieved from either *testrunner.log* or *testrunner.db*. + +If there is no permutation, the individual test script may be run with: + +``` + ./testfixture $PATH_TO_SCRIPT +``` + +Or, if the failure occured as part of a permutation: + +``` + ./testfixture $TESTDIR/testrunner.tcl $PERMUTATION $PATH_TO_SCRIPT +``` + +TODO: An example instead of "$PERMUTATION" and $PATH\_TO\_SCRIPT? + +# 3. Source Code Tests + +The commands described in this section invoke the C compiler to build +binaries from the source tree, then use those binaries to run Tcl and +other tests. The advantages of this are that: + + * it is possible to test multiple build configurations with a single + command, and + + * it ensures that tests are always run using binaries created with the + same set of compiler options. + +The testrunner.tcl commands described in this section may be run using +either a *testfixture* (or testfixture.exe) build, or with any other Tcl +shell that supports SQLite 3.31.1 or newer via "package require sqlite3". + +TODO: ./configure + Makefile.msc build systems. + +## Commands to Run SQLite Tests + +The **mdevtest** command is equivalent to running the veryquick tests and +the [make fuzztest] target once for each of two --enable-all builds - one +with debugging enabled and one without: + +``` + tclsh $TESTDIR/testrunner.tcl mdevtest +``` + +In other words, it is equivalent to running: + +``` + $TOP/configure --enable-all --enable-debug + make fuzztest + make testfixture + ./testfixture $TOP/test/testrunner.tcl veryquick + + # Then, after removing files created by the tests above: + $TOP/configure --enable-all OPTS="-O0" + make fuzztest + make testfixture + ./testfixture $TOP/test/testrunner.tcl veryquick +``` + +The **sdevtest** command is identical to the mdevtest command, except that the +second of the two builds is a sanitizer build. Specifically, this means that +OPTS="-fsanitize=address,undefined" is specified instead of OPTS="-O0": + +``` + tclsh $TESTDIR/testrunner.tcl sdevtest +``` + +The **release** command runs lots of tests under lots of builds. It runs +different combinations of builds and tests depending on whether it is run +on Linux, Windows or OSX. Refer to *testrunner\_data.tcl* for the details +of the specific tests run. + +``` + tclsh $TESTDIR/testrunner.tcl release +``` + +## Running ZipVFS Tests + +testrunner.tcl can build a zipvfs-enabled testfixture and use it to run +tests from the Zipvfs project with the following command: + +``` + tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS +``` + +This can be combined with any of "mdevtest", "sdevtest" or "release" to +test both SQLite and Zipvfs with a single command: + +``` + tclsh $TESTDIR/testrunner.tcl --zipvfs $PATH_TO_ZIPVFS mdevtest +``` + +## Investigating Source Code Test Failures + +Investigating a test failure that occurs during source code testing is a +two step process: + + 1. Recreating the build configuration in which the test failed, and + + 2. Re-running the actual test. + +To recreate a build configuration, use the testrunner.tcl **script** command +to create a build script. A build script is a bash script on Linux or OSX, or +a dos \*.bat file on windows. For example: + +``` + # Create a script that recreates build configuration "Device-One" on + # Linux or OSX: + tclsh $TESTDIR/testrunner.tcl script Device-One > make.sh + + # Create a script that recreates build configuration "Have-Not" on Windows: + tclsh $TESTDIR/testrunner.tcl script Have-Not > make.bat +``` + +The generated bash or \*.bat file script accepts a single argument - a makefile +target to build. This may be used either to run a [make] command test directly, +or else to build a testfixture (or testfixture.exe) binary with which to +run a Tcl test script, as described above. + + + +# 4. Controlling CPU Core Utilization + +When running either binary or source code tests, testrunner.tcl reports the +number of jobs it intends to use to stdout. e.g. + +``` + $ ./testfixture $TESTDIR/testrunner.tcl + splitting work across 16 jobs + ... more output ... +``` + +By default, testfixture.tcl attempts to set the number of jobs to the number +of real cores on the machine. This can be overridden using the "--jobs" (or -j) +switch: + +``` + $ ./testfixture $TESTDIR/testrunner.tcl --jobs 8 + splitting work across 8 jobs + ... more output ... +``` + +The number of jobs may also be changed while an instance of testrunner.tcl is +running by exucuting the following command from the directory containing the +testrunner.log and testrunner.db files: + +``` + $ ./testfixture $TESTDIR/testrunner.tcl njob $NEW_NUMBER_OF_JOBS +``` + + + + + + + + diff --git a/manifest b/manifest index ba8d4aea96..3d557c1b58 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\scode\slegibility\sand\sstyle\simprovements\sin\sthe\sJNI\spieces.\sStart\swork\son\sa\sjavadoc\sbuild. -D 2023-08-24T18:43:25.053 +C Add\sdoc/testrunner.md,\sfor\sdocumenting\sthe\stestrunner.tcl\sscript. +D 2023-08-24T19:08:50.024 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -41,6 +41,7 @@ F doc/compile-for-windows.md c52f2903f1cb11b2308798feecca2e44701b037b78f467a538a F doc/json-enhancements.md e356fc834781f1f1aa22ee300027a270b2c960122468499bf347bb123ce1ea4f F doc/lemon.html 44a53a1d2b42d7751f7b2f478efb23c978e258d794bfd172442307a755b9fa44 F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710 +F doc/testrunner.md 2434864be2219d4f0b6ffc99d0a2172d531c4ca4345340776f67ad4edd90dc90 F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56 F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a @@ -1606,7 +1607,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl a9fee4df57276bc9e446961b160068c269da5902cc8ffc3e8852d77626b7594c +F test/testrunner.tcl ccdfda84732cf8665bd8d3bfee79b80841e221459e5d00a632a3a5c758966e1f F test/testrunner_data.tcl c448693eb6fdbadb78cb26f6253d4f335666f9836f988afa575de960b666b19f F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2095,8 +2096,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 cf185bcd25629d882a030b8b87048179a120ab1f84aa1d68b279c499dbdf0dba -R 409ebb8571a40aa1108ef1efd5f91432 -U stephan -Z 85677113ed9046ae48bf181600ab6418 +P 62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf +R a33be9d18a7bfec02bf745dca74a3ae4 +U dan +Z b231c489ac6e506e7affd58c17184d19 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index af25bbe444..8eee5cd027 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf \ No newline at end of file +9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23 \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index 22e3b17bf4..d2f0fe7d53 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -651,11 +651,14 @@ proc r_get_next_job {iJob} { proc make_new_testset {} { global TRG - set tests [testset_patternlist $TRG(patternlist)] - + set tests [list] if {$TRG(zipvfs)!=""} { source [file join $TRG(zipvfs) test zipvfs_testrunner.tcl] - set tests [concat $tests [zipvfs_testrunner_testset]] + lappend tests {*}[zipvfs_testrunner_testset] + } + + if {$tests=="" || $TRG(patternlist)!=""} { + lappend tests {*}[testset_patternlist $TRG(patternlist)] } r_write_db { @@ -960,7 +963,7 @@ sqlite3 trdb $TRG(dbname) trdb timeout $TRG(timeout) set tm [lindex [time { make_new_testset }] 0] if {$TRG(nJob)>1} { - puts "splitting work across $TRG(nJob) cores" + puts "splitting work across $TRG(nJob) jobs" } puts "built testset in [expr $tm/1000]ms.." run_testset From 0f0bf3ff9eb35d3d1000dec6164b634e5478bb56 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 21:31:56 +0000 Subject: [PATCH 050/422] Do not pre-allocate sqlite3_aggregate_context() for Java UDFs, as it unduly complicates UDF initialization. FossilOrigin-Name: e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 75 +++++-------------- ext/jni/src/c/sqlite3-jni.h | 8 ++ ext/jni/src/org/sqlite/jni/SQLFunction.java | 27 ++++--- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 36 +++++++-- ext/jni/src/org/sqlite/jni/Tester1.java | 14 +++- .../src/org/sqlite/jni/sqlite3_context.java | 35 ++++++--- manifest | 26 +++---- manifest.uuid | 2 +- 9 files changed, 118 insertions(+), 107 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index a6711043fc..c4fa4b5c06 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -288,7 +288,7 @@ jar: $(package.jar) dir.doc := $(dir.jni)/doc doc: $(JAVA_FILES.main) - $(bin.javadoc) -cp $(classpath) -d $(dir.doc) org.sqlite.jni + $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet org.sqlite.jni ######################################################################## # Clean up... diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index c62aa1765a..13ccc4ec3f 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1164,49 +1164,6 @@ static int S3JniAutoExtension_init(JNIEnv *const env, return 0; } -/* -** Requires that jCx be a Java-side sqlite3_context wrapper for pCx. -** This function calls sqlite3_aggregate_context() to allocate a tiny -** sliver of memory, the address of which is set in -** jCx->aggregateContext. The memory is only used as a key for -** mapping client-side results of aggregate result sets across -** calls to the UDF's callbacks. -** -** isFinal must be 1 for xFinal() calls and 0 for all others, the -** difference being that the xFinal() invocation will not allocate -** new memory if it was not already, resulting in a value of 0 -** for jCx->aggregateContext. -** -** Returns 0 on success. Returns SQLITE_NOMEM on allocation error, -** noting that it will not allocate when isFinal is true. It returns -** SQLITE_ERROR if there's a serious internal error in dealing with -** the JNI state. -*/ -static int udf_setAggregateContext(JNIEnv * env, jobject jCx, - sqlite3_context * pCx, - int isFinal){ - void * pAgg; - int rc = 0; - S3JniNphClass * const pNC = - S3JniGlobal_nph_cache(env, &S3NphRefs.sqlite3_context); - if( !pNC->fidAggCtx ){ - S3JniMutex_Nph_enter; - if( !pNC->fidAggCtx ){ - pNC->fidAggCtx = (*env)->GetFieldID(env, pNC->klazz, "aggregateContext", "J"); - EXCEPTION_IS_FATAL("Cannot get sqlite3_contex.aggregateContext member."); - } - S3JniMutex_Nph_leave; - } - pAgg = sqlite3_aggregate_context(pCx, isFinal ? 0 : sizeof(void*)); - if( pAgg || isFinal ){ - (*env)->SetLongField(env, jCx, pNC->fidAggCtx, (jlong)pAgg); - }else{ - assert(!pAgg); - rc = SQLITE_NOMEM; - } - return rc; -} - /* ** Common init for OutputPointer_set_Int32() and friends. pRef must be ** a pointer from S3NphRefs. jOut must be an instance of that @@ -1628,6 +1585,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, (*env)->ExceptionDescribe( env ); S3JniExceptionClear; } + UNREF_L(ex); return rc; } @@ -1645,10 +1603,6 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); - if( rc ) return rc; - if( UDF_SCALAR != s->type ){ - rc = udf_setAggregateContext(env, args.jcx, pCx, 0); - } if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv); S3JniIfThrew{ @@ -1678,15 +1632,10 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, return SQLITE_NOMEM; } //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); - if( UDF_SCALAR != s->type ){ - rc = udf_setAggregateContext(env, jcx, cx, isFinal); - } - if( 0 == rc ){ - (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); - S3JniIfThrew{ - rc = udf_report_exception(env, isFinal, cx, s->zFuncName, - zFuncType); - } + (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); + S3JniIfThrew{ + rc = udf_report_exception(env, isFinal, cx, s->zFuncName, + zFuncType); } UNREF_L(jcx); return rc; @@ -1834,6 +1783,20 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type) #undef WRAP_MUTF8_VOID #undef WRAP_STR_STMT_INT + +S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( + JniArgsEnvClass, jobject jCx, jboolean initialize +){ + sqlite3_context * const pCx = PtrGet_sqlite3_context(jCx); + void * const p = pCx + ? sqlite3_aggregate_context(pCx, (int)(initialize + ? (int)sizeof(void*) + : 0)) + : 0; + return (jlong)p / sizeof(void*); +} + + /* Central auto-extension handler. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index b9f03983a3..e642241921 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -771,6 +771,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_init JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv (JNIEnv *, jclass); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_aggregate_context + * Signature: (Lorg/sqlite/jni/sqlite3_context;Z)J + */ +JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1aggregate_1context + (JNIEnv *, jclass, jobject, jboolean); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_auto_extension diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index bc6f608736..97a3f65a46 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -55,6 +55,9 @@ public abstract class SQLFunction { Client UDFs are free to perform such mappings using custom approaches. The provided Aggregate and Window classes use this. + +

T must be of a type which can be legally stored as a value in + java.util.HashMap. */ public static final class PerContextState { private final java.util.Map> map @@ -64,20 +67,20 @@ public abstract class SQLFunction { Should be called from a UDF's xStep(), xValue(), and xInverse() methods, passing it that method's first argument and an initial value for the persistent state. If there is currently no - mapping for cx.getAggregateContext() within the map, one is - created using the given initial value, else the existing one is - used and the 2nd argument is ignored. It returns a - ValueHolder which can be used to modify that state directly - without requiring that the client update the underlying map's - entry. + mapping for the given context within the map, one is created + using the given initial value, else the existing one is used + and the 2nd argument is ignored. It returns a ValueHolder + which can be used to modify that state directly without + requiring that the client update the underlying map's entry. -

T must be of a type which can be legally stored as a value in - java.util.HashMap. +

The caller is obligated to eventually call + takeAggregateState() to clear the mapping. */ public ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ - ValueHolder rc = map.get(cx.getAggregateContext()); - if(null == rc){ - map.put(cx.getAggregateContext(), rc = new ValueHolder<>(initialValue)); + final Long key = cx.getAggregateContext(true); + ValueHolder rc = null==key ? null : map.get(key); + if( null==rc ){ + map.put(key, rc = new ValueHolder<>(initialValue)); } return rc; } @@ -92,7 +95,7 @@ public abstract class SQLFunction { rows. */ public T takeAggregateState(sqlite3_context cx){ - final ValueHolder h = map.remove(cx.getAggregateContext()); + final ValueHolder h = map.remove(cx.getAggregateContext(false)); return null==h ? null : h.value; } } diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index e5a8b6e243..9a00fa86e5 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -126,19 +126,19 @@ public final class SQLite3Jni { This will clean up any cached per-JNIEnv info. Calling into the library will re-initialize the cache on demand. - This process does not close any databases or finalize +

This process does not close any databases or finalize any prepared statements because their ownership does not depend on a given thread. For proper library behavior, and to avoid C-side leaks, be sure to finalize all statements and close all databases before calling this function. - Calling this from the main application thread is not strictly +

Calling this from the main application thread is not strictly required but is "polite." Additional threads must call this before ending or they will leak cache entries in the C heap, which in turn may keep numerous Java-side global references active. - This routine returns false without side effects if the current +

This routine returns false without side effects if the current JNIEnv is not cached, else returns true, but this information is primarily for testing of the JNI bindings and is not information which client-level code should use to make any informed @@ -151,22 +151,42 @@ public final class SQLite3Jni { // alphabetized. The SQLITE_... values. on the other hand, are // grouped by category. + /** + Functions exactly like the native form except that (A) the + returned value is only intended for use as a lookup key in a + higher-level data structure and (B) the 2nd argument is a boolean + instead of an int. If passed true, it will attempt to allocate + enough memory to use as a UDF-call-local context key. If passed + false it will not allocate any memory. + +

It is only valid for the life of the current UDF method call + and must not be retained for later use. The return value 0 + indicates an allocation error unless initialize is false, in + which case it means that the given context was never passed to + this function with a true second argument so never had to + allocate. + +

For the JNI wrapping, the value of sz is provided for API + consistency but it is ignored unless it's 0. Results are + undefined if the value is negative. + */ + public static native long sqlite3_aggregate_context(sqlite3_context cx, boolean initialize); /** Functions almost as documented for the C API, with these exceptions: - - The callback interface is is shorter because of cross-language - differences. Specifically, 3rd argument to the C auto-extension - callback interface is unnecessary here. +

- The callback interface is is shorter because of + cross-language differences. Specifically, 3rd argument to the C + auto-extension callback interface is unnecessary here. - The C API docs do not specifically say so, if the list of +

The C API docs do not specifically say so, but if the list of auto-extensions is manipulated from an auto-extension, it is undefined which, if any, auto-extensions will subsequently execute for the current database. - See the AutoExtension class docs for more information. +

See the AutoExtension class docs for more information. */ public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index eb383fe8d2..89fa022cf5 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -723,7 +723,9 @@ public class Tester1 implements Runnable { SQLFunction func = new SQLFunction.Aggregate(){ @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ - this.getAggregateState(cx, 0).value += sqlite3_value_int(args[0]); + final ValueHolder agg = this.getAggregateState(cx, 0); + agg.value += sqlite3_value_int(args[0]); + affirm( agg == this.getAggregateState(cx, 0) ); } @Override public void xFinal(sqlite3_context cx){ @@ -740,15 +742,19 @@ public class Tester1 implements Runnable { int rc = sqlite3_create_function(db, "myfunc", 1, SQLITE_UTF8, func); affirm(0 == rc); sqlite3_stmt stmt = prepare(db, "select myfunc(a), myfunc(a+10) from t"); + affirm( null != stmt ); int n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ - final int v = sqlite3_column_int(stmt, 0); + int v = sqlite3_column_int(stmt, 0); affirm( 6 == v ); + int v2 = sqlite3_column_int(stmt, 1); + affirm( 30+v == v2 ); ++n; } + affirm( 1==n ); affirm(!xFinalNull.value); sqlite3_reset(stmt); - // Ensure that the accumulator is reset... + // Ensure that the accumulator is reset on subsequent calls... n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ final int v = sqlite3_column_int(stmt, 0); @@ -767,9 +773,9 @@ public class Tester1 implements Runnable { affirm( 6 == c0 ); affirm( 12 == c1 ); } + sqlite3_finalize(stmt); affirm( 1 == n ); affirm(!xFinalNull.value); - sqlite3_finalize(stmt); execSql(db, "SELECT myfunc(1) WHERE 0"); affirm(xFinalNull.value); diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_context.java b/ext/jni/src/org/sqlite/jni/sqlite3_context.java index d582df7838..ead3f372ea 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_context.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_context.java @@ -18,10 +18,7 @@ package org.sqlite.jni; SQL functions (a.k.a. UDFs). */ public final class sqlite3_context extends NativePointerHolder { - /** - Only set by the JNI layer. - */ - private long aggregateContext = 0; + private Long aggregateContext = null; /** getAggregateContext() corresponds to C's @@ -32,19 +29,29 @@ public final class sqlite3_context extends NativePointerHolder such that all calls into those callbacks can determine which "set" of those calls they belong to. - If this object is being used in the context of an aggregate or +

If the argument is true and the aggregate context has not yet + been set up, it will be initialized fetched on demand, else it + won't. The intent is that xStep(), xValue(), and xInverse() + methods pass true and xFinal() methods pass false. + +

This function treats numeric 0 as null, always returning null instead + of 0. + +

If this object is being used in the context of an aggregate or window UDF, this function returns a non-0 value which is distinct for each set of UDF callbacks from a single invocation of the UDF, otherwise it returns 0. The returned value is only only valid within the context of execution of a single SQL statement, - and may be re-used by future invocations of the UDF in different - SQL statements. + and must not be re-used by future invocations of the UDF in + different SQL statements. - Consider this SQL, where MYFUNC is a user-defined aggregate function: +

Consider this SQL, where MYFUNC is a user-defined aggregate function: + {code SELECT MYFUNC(A), MYFUNC(B) FROM T; + } - The xStep() and xFinal() methods of the callback need to be able +

The xStep() and xFinal() methods of the callback need to be able to differentiate between those two invocations in order to perform their work properly. The value returned by getAggregateContext() will be distinct for each of those @@ -52,14 +59,18 @@ public final class sqlite3_context extends NativePointerHolder key for mapping callback invocations to whatever client-defined state is needed by the UDF. - There is one case where this will return 0 in the context of an +

There is one case where this will return 0 in the context of an aggregate or window function: if the result set has no rows, the UDF's xFinal() will be called without any other x...() members having been called. In that one case, no aggregate context key will have been generated. xFinal() implementations need to be prepared to accept that condition as legal. */ - public long getAggregateContext(){ - return aggregateContext; + public synchronized Long getAggregateContext(boolean initIfNeeded){ + if( aggregateContext==null ){ + aggregateContext = SQLite3Jni.sqlite3_aggregate_context(this, initIfNeeded); + if( !initIfNeeded && null==aggregateContext ) aggregateContext = 0L; + } + return (null==aggregateContext || 0!=aggregateContext) ? aggregateContext : null; } } diff --git a/manifest b/manifest index 3d557c1b58..59c1110aa4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sdoc/testrunner.md,\sfor\sdocumenting\sthe\stestrunner.tcl\sscript. -D 2023-08-24T19:08:50.024 +C Do\snot\spre-allocate\ssqlite3_aggregate_context()\sfor\sJava\sUDFs,\sas\sit\sunduly\scomplicates\sUDF\sinitialization. +D 2023-08-24T21:31:56.676 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,11 +233,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 6b3c0fd8d055c129735702d0b288589d25544dd404a00d46d9eb43770fe7f78f +F ext/jni/GNUmakefile 6aeafa0ebcf0f0d834c814ae8b450b54135ea11a2a7868f90b6286ec1bf6020f F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 8db2fcc05dd7749f9f4175e2654344feccac6abfd19fd9db0d116c6350e3b625 -F ext/jni/src/c/sqlite3-jni.h 2b81cfb83933cb18e5f690487f4556591d3329538809c847d00190aa4d69aa1d +F ext/jni/src/c/sqlite3-jni.c 0d98ab3b117893904a06f0f8a350d68d4e911939b6aee4f0eb1ef707502ac23c +F ext/jni/src/c/sqlite3-jni.h 7e9f36434b919cd8b6aa66c61e3910e9f112e252f52d1ac8a9811c52710aefcb F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -256,10 +256,10 @@ F ext/jni/src/org/sqlite/jni/PreUpdateHook.java dec00a706b58c67989f0ff56c4f0a703 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 5851698d96ee29171d68930ad758d0f5a253f7575f1feb890d82b2557a8d3ef5 +F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d96f10a097c1d614b44353e85a65368d9aca565d5ae57fae0104811594fbdfba -F ext/jni/src/org/sqlite/jni/Tester1.java 76f308ad9bf0bd74374561c30c65564ed24583a465264b751d9e2333980149f1 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 866b320e6cb089ebe96c14763575df9e2e0d5e52a0ec87a52119ff17ec59ffbf +F ext/jni/src/org/sqlite/jni/Tester1.java 239eb0133547d4a23317a6dd5bc456172cfb1f2547fd15ba8408871c2776a721 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd9603 F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b F ext/jni/src/org/sqlite/jni/package-info.java 1a547913d681411d65c5fe0bca840f049abe5612740154a125545ea9e2481747 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc -F ext/jni/src/org/sqlite/jni/sqlite3_context.java fe7797a696978f057528a57b7a11e7797ed41fd7afcf100c5ebb67055d9f706f +F ext/jni/src/org/sqlite/jni/sqlite3_context.java 42df6769ab9c8de40d24f39f49152cdaa6e0c06d1468660a95dfee9ecaeb9a63 F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3 @@ -2096,8 +2096,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 62b404d62fd62f4d220838b59c9f38a71afa2d4a8c3af0a5c9495fa7020972cf -R a33be9d18a7bfec02bf745dca74a3ae4 -U dan -Z b231c489ac6e506e7affd58c17184d19 +P 9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23 +R 1c78bd3d04007fccae2264dff957535e +U stephan +Z 6a74bde3490bcf9bf06f9dd5ecb09697 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8eee5cd027..d99911cac2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23 \ No newline at end of file +e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5 \ No newline at end of file From 70dcc2822d38d29b0f9b220477ba9a022f148b25 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 21:45:30 +0000 Subject: [PATCH 051/422] Update some outdated JNI docs and account for a function renamed earlier this evening. FossilOrigin-Name: 3f684ef5018116f4be46a07779451c8983ac87a5db182477f71ee7bf28287a04 --- ext/jni/src/c/sqlite3-jni.c | 2 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 27 ++++++++++------------ manifest | 14 +++++------ manifest.uuid | 2 +- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 13ccc4ec3f..d475c15123 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -3762,7 +3762,7 @@ S3JniApi(sqlite3_value_text16(),jbyteArray,1value_1text16)( return value_text16(SQLITE_UTF16, env, jpSVal); } -JniDecl(void,1do_1something_1for_1developer)(JniArgsEnvClass){ +JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ MARKER(("\nVarious bits of internal info:\n")); puts("FTS5 is " #ifdef SQLITE_ENABLE_FTS5 diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 9a00fa86e5..21a93501e1 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -152,23 +152,20 @@ public final class SQLite3Jni { // grouped by category. /** - Functions exactly like the native form except that (A) the - returned value is only intended for use as a lookup key in a - higher-level data structure and (B) the 2nd argument is a boolean - instead of an int. If passed true, it will attempt to allocate - enough memory to use as a UDF-call-local context key. If passed - false it will not allocate any memory. + Functions exactly like the native form except that (A) the 2nd + argument is a boolean instead of an int and (B) the returned + value is not a pointer address and is only intended for use as a + per-UDF-call lookup key in a higher-level data structure. -

It is only valid for the life of the current UDF method call - and must not be retained for later use. The return value 0 - indicates an allocation error unless initialize is false, in - which case it means that the given context was never passed to - this function with a true second argument so never had to - allocate. +

Passing a true second argument is analogous to passing some + unspecified small, non-0 positive value to the C API and passing + false is equivalent to passing 0 to the C API. -

For the JNI wrapping, the value of sz is provided for API - consistency but it is ignored unless it's 0. Results are - undefined if the value is negative. +

Like the C API, it returns 0 if allocation fails or if + initialize is false and no prior aggregate context was allocated + for cx. If initialize is true then it returns 0 only on + allocation error. In all casses, 0 is considered the sentinel + "not a key" value. */ public static native long sqlite3_aggregate_context(sqlite3_context cx, boolean initialize); diff --git a/manifest b/manifest index 59c1110aa4..3ec95da27f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Do\snot\spre-allocate\ssqlite3_aggregate_context()\sfor\sJava\sUDFs,\sas\sit\sunduly\scomplicates\sUDF\sinitialization. -D 2023-08-24T21:31:56.676 +C Update\ssome\soutdated\sJNI\sdocs\sand\saccount\sfor\sa\sfunction\srenamed\searlier\sthis\sevening. +D 2023-08-24T21:45:30.722 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 6aeafa0ebcf0f0d834c814ae8b450b54135ea11a2a7868f90b6286ec1bf6020f F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 0d98ab3b117893904a06f0f8a350d68d4e911939b6aee4f0eb1ef707502ac23c +F ext/jni/src/c/sqlite3-jni.c c15bd15c895ec698a669c7058cb19e8bfccdcab88dc6345357f698fbfe9544aa F ext/jni/src/c/sqlite3-jni.h 7e9f36434b919cd8b6aa66c61e3910e9f112e252f52d1ac8a9811c52710aefcb F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 @@ -258,7 +258,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 866b320e6cb089ebe96c14763575df9e2e0d5e52a0ec87a52119ff17ec59ffbf +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1e791b79e96cc98a37659ea6f578ce595e1a38fb89b05e3560aa28462b283f6d F ext/jni/src/org/sqlite/jni/Tester1.java 239eb0133547d4a23317a6dd5bc456172cfb1f2547fd15ba8408871c2776a721 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -2096,8 +2096,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 9c69a28401c7273823f2c2b291fd417febeb278afb9ce085a4b944505ca13d23 -R 1c78bd3d04007fccae2264dff957535e +P e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5 +R 3d26ad0e6ed454d9e8adb86ee7670851 U stephan -Z 6a74bde3490bcf9bf06f9dd5ecb09697 +Z 11f2caacf45a97bd14e07afe2fb5856c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d99911cac2..f2fed92de8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5 \ No newline at end of file +3f684ef5018116f4be46a07779451c8983ac87a5db182477f71ee7bf28287a04 \ No newline at end of file From 3401736694774fd49db14244d15fdbce2eb5b9e4 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 24 Aug 2023 22:28:44 +0000 Subject: [PATCH 052/422] Re-frame the incongruous SQLite3Jni.uncacheThread() API as sqlite3_java_uncache_thread(). FossilOrigin-Name: 7232b033954fae40df3db43e489e0e5a703c03308f500a1ae36fd9d707632d7f --- ext/jni/src/c/sqlite3-jni.c | 101 ++++++++++++--------- ext/jni/src/c/sqlite3-jni.h | 4 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 25 ++--- ext/jni/src/org/sqlite/jni/Tester1.java | 4 +- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 86 insertions(+), 68 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d475c15123..410d42c818 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -547,48 +547,49 @@ static void s3jni_incr( volatile unsigned int * const p ){ #endif /* Helpers for working with specific mutexes. */ -#define S3JniMutex_Env_assertLocked \ +#define S3JniMutex_Env_assertLocked \ assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define S3JniMutex_Env_assertLocker \ +#define S3JniMutex_Env_assertLocker \ assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define S3JniMutex_Env_assertNotLocker \ +#define S3JniMutex_Env_assertNotLocker \ assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) -#define S3JniMutex_Env_enter \ - S3JniMutex_Env_assertNotLocker; \ +#define S3JniMutex_Env_enter \ + S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ ++SJG.metrics.nMutexEnv; \ SJG.envCache.locker = env -#define S3JniMutex_Env_leave \ +#define S3JniMutex_Env_leave \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env));*/ \ - S3JniMutex_Env_assertLocker; \ + S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) -#define S3JniMutex_Ext_enter \ +#define S3JniMutex_Ext_enter \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ ++SJG.metrics.nMutexAutoExt -#define S3JniMutex_Ext_leave \ +#define S3JniMutex_Ext_leave \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_leave( SJG.autoExt.mutex ) -#define S3JniMutex_Nph_enter \ - S3JniMutex_Env_assertNotLocker; \ +#define S3JniMutex_Nph_enter \ + S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ ++SJG.metrics.nMutexEnv2; \ SJG.envCache.locker = env -#define S3JniMutex_Nph_leave \ +#define S3JniMutex_Nph_leave \ /*MARKER(("Leaving NPH mutex @%p %s.\n", env));*/ \ S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) -#define S3JniMutex_Pdb_enter \ - /*MARKER(("Entering PerDb mutex@%p %s.\n", env));*/ \ +#define S3JniMutex_S3JniDb_enter \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ + assert( 0==SJG.perDb.locker ); \ ++SJG.metrics.nMutexPerDb; \ SJG.perDb.locker = env; -#define S3JniMutex_Pdb_leave \ +#define S3JniMutex_S3JniDb_leave \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ + assert( env == SJG.perDb.locker ); \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) @@ -890,7 +891,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ ** references. */ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDestroy){ - if(doXDestroy && s->jObj){ + if( doXDestroy && s->jObj ){ s3jni_call_xDestroy(env, s->jObj); } UNREF_G(s->jObj); @@ -900,9 +901,9 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest /* ** Clears s's state and moves it to the free-list. */ -static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ - if(s){ - S3JniMutex_Pdb_enter; +static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ + if( s ){ + assert( S3JniGlobal.perDb.locker == env ); assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); @@ -932,7 +933,13 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; - S3JniMutex_Pdb_leave; + } +} +static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ + if( s ){ + S3JniMutex_S3JniDb_enter; + S3JniDb_set_aside_unlocked(env, s); + S3JniMutex_S3JniDb_leave; } } @@ -1064,7 +1071,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ S3JniDb * rv; - S3JniMutex_Pdb_enter; + S3JniMutex_S3JniDb_enter; if( SJG.perDb.aFree ){ rv = SJG.perDb.aFree; SJG.perDb.aFree = rv->pNext; @@ -1095,7 +1102,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->jDb = REF_G(jDb); rv->pDb = pDb; } - S3JniMutex_Pdb_leave; + S3JniMutex_S3JniDb_leave; return rv; } @@ -1113,7 +1120,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if( jDb || pDb ){ - S3JniMutex_Pdb_enter; + S3JniMutex_S3JniDb_enter; s = SJG.perDb.aUsed; if( !pDb ){ assert( jDb ); @@ -1124,7 +1131,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ break; } } - S3JniMutex_Pdb_leave; + S3JniMutex_S3JniDb_leave; } return s; } @@ -2713,6 +2720,21 @@ S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( return rc ? JNI_TRUE : JNI_FALSE; } +/* +** Uncaches the current JNIEnv from the S3JniGlobal state, clearing any +** resources owned by that cache entry and making that slot available +** for re-use. It is important that the Java-side decl of this +** function be declared as synchronous. +*/ +JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){ + int rc; + S3JniMutex_Env_enter; + rc = S3JniGlobal_env_uncache(env); + S3JniMutex_Env_leave; + return rc ? JNI_TRUE : JNI_FALSE; +} + + S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( JniArgsEnvClass, jobject jpDb ){ @@ -3546,8 +3568,19 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); } S3JniMutex_Env_leave; - /* Do not clear S3JniGlobal.jvm: it's legal to call - sqlite3_initialize() again to restart the lib. */ +#if 0 + /* + ** Is this a good idea? We will get rid of the perDb list once + ** sqlite3 gets a per-db client state, at which point we won't have + ** a central list of databases to close. + */ + S3JniMutex_S3JniDb_enter; + while( SJG.perDb.pHead ){ + s3jni_close_db(env, SJG.perDb.pHead->jDb, 2); + } + S3JniMutex_S3JniDb_leave; +#endif + /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */ return sqlite3_shutdown(); } @@ -4615,22 +4648,6 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JniArgsEnvClass){ // End of SQLTester bindings. Start of lower-level bits. //////////////////////////////////////////////////////////////////////// - -/* -** Uncaches the current JNIEnv from the S3JniGlobal state, clearing any -** resources owned by that cache entry and making that slot available -** for re-use. It is important that the Java-side decl of this -** function be declared as synchronous. -*/ -JNIEXPORT jboolean JNICALL -Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv(JniArgsEnvClass){ - int rc; - S3JniMutex_Env_enter; - rc = S3JniGlobal_env_uncache(env); - S3JniMutex_Env_leave; - return rc ? JNI_TRUE : JNI_FALSE; -} - /* ** Called during static init of the SQLite3Jni class to sync certain ** compile-time constants to Java-space. diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index e642241921..d952cfa60a 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -765,10 +765,10 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_init /* * Class: org_sqlite_jni_SQLite3Jni - * Method: uncacheJniEnv + * Method: sqlite3_java_uncache_thread * Signature: ()Z */ -JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_uncacheJniEnv +JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1java_1uncache_1thread (JNIEnv *, jclass); /* diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 21a93501e1..b5cde41c1b 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -121,10 +121,9 @@ public final class SQLite3Jni { /** Each thread which uses the SQLite3 JNI APIs should call - uncacheJniEnv() when it is done with the library - either right - before it terminates or when it is finished using the SQLite API. - This will clean up any cached per-JNIEnv info. Calling into the - library will re-initialize the cache on demand. + sqlite3_jni_uncache_thread() when it is done with the library - + either right before it terminates or when it finishes using the + SQLite API. This will clean up any cached per-thread info.

This process does not close any databases or finalize any prepared statements because their ownership does not depend on @@ -133,10 +132,9 @@ public final class SQLite3Jni { all databases before calling this function.

Calling this from the main application thread is not strictly - required but is "polite." Additional threads must call this - before ending or they will leak cache entries in the C heap, - which in turn may keep numerous Java-side global references - active. + required. Additional threads must call this before ending or they + will leak cache entries in the C heap, which in turn may keep + numerous Java-side global references active.

This routine returns false without side effects if the current JNIEnv is not cached, else returns true, but this information is @@ -144,7 +142,7 @@ public final class SQLite3Jni { which client-level code should use to make any informed decisions. */ - public static synchronized native boolean uncacheJniEnv(); + public static native boolean sqlite3_java_uncache_thread(); ////////////////////////////////////////////////////////////////////// // Maintenance reminder: please keep the sqlite3_.... functions @@ -1154,9 +1152,12 @@ public final class SQLite3Jni { ); /** - Cleans up all per-JNIEnv and per-db state managed by the library, - as well as any registered auto-extensions, then calls the - C-native sqlite3_shutdown(). + Cleans up all stale per-thread state managed by the library, as + well as any registered auto-extensions, then calls the C-native + sqlite3_shutdown(). Calling this while database handles or + prepared statements are still active will leak resources. Trying + to use those objects after this routine is called invoked + undefined behavior. */ public static synchronized native int sqlite3_shutdown(); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 89fa022cf5..cc4d7dbd06 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1421,8 +1421,8 @@ public class Tester1 implements Runnable { listErrors.add(e); } }finally{ - affirm( SQLite3Jni.uncacheJniEnv() ); - affirm( !SQLite3Jni.uncacheJniEnv() ); + affirm( sqlite3_java_uncache_thread() ); + affirm( !sqlite3_java_uncache_thread() ); } } diff --git a/manifest b/manifest index 3ec95da27f..65636cb8a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\ssome\soutdated\sJNI\sdocs\sand\saccount\sfor\sa\sfunction\srenamed\searlier\sthis\sevening. -D 2023-08-24T21:45:30.722 +C Re-frame\sthe\sincongruous\sSQLite3Jni.uncacheThread()\sAPI\sas\ssqlite3_java_uncache_thread(). +D 2023-08-24T22:28:44.239 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 6aeafa0ebcf0f0d834c814ae8b450b54135ea11a2a7868f90b6286ec1bf6020f F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c c15bd15c895ec698a669c7058cb19e8bfccdcab88dc6345357f698fbfe9544aa -F ext/jni/src/c/sqlite3-jni.h 7e9f36434b919cd8b6aa66c61e3910e9f112e252f52d1ac8a9811c52710aefcb +F ext/jni/src/c/sqlite3-jni.c e4bdcd17e8f8e825f206e1c6ab5adf7f507d70b64b0f795c0cde141077fb68b2 +F ext/jni/src/c/sqlite3-jni.h 91c2eeee22d3594e6652d51edcce0cd94d258a768802fcfac13a78f900127b72 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c @@ -258,8 +258,8 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1e791b79e96cc98a37659ea6f578ce595e1a38fb89b05e3560aa28462b283f6d -F ext/jni/src/org/sqlite/jni/Tester1.java 239eb0133547d4a23317a6dd5bc456172cfb1f2547fd15ba8408871c2776a721 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5fddc34787b26a494dc9843ea2530cd319dc7a6bd369fd4e86dc713f20640fa6 +F ext/jni/src/org/sqlite/jni/Tester1.java e9b82c561ec8771b3e4ea537ebd7c16dd096928b6b8221967a4726104c7c6cb2 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d @@ -2096,8 +2096,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 e8308f0c6ec2d8999c8a2502fb130cb3501ba326f23f71f2cd8d452debae79b5 -R 3d26ad0e6ed454d9e8adb86ee7670851 +P 3f684ef5018116f4be46a07779451c8983ac87a5db182477f71ee7bf28287a04 +R ce1f562cc06444ed73ad1cad9371c48a U stephan -Z 11f2caacf45a97bd14e07afe2fb5856c +Z fbdf5ee5d7ecf00150898b00dcd91489 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f2fed92de8..33b1d73d8e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f684ef5018116f4be46a07779451c8983ac87a5db182477f71ee7bf28287a04 \ No newline at end of file +7232b033954fae40df3db43e489e0e5a703c03308f500a1ae36fd9d707632d7f \ No newline at end of file From 44b4b8260f24a8bb008bf078d00d48070e71220a Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 00:27:28 +0000 Subject: [PATCH 053/422] javadoc additions. FossilOrigin-Name: bedf33d403677d243a1505ce549166850ab55671700b143278db5feb84883ab3 --- ext/jni/GNUmakefile | 18 ++++- ext/jni/src/org/sqlite/jni/Authorizer.java | 8 +- .../org/sqlite/jni/NativePointerHolder.java | 2 +- ext/jni/src/org/sqlite/jni/NotNull.java | 21 ++++++ ext/jni/src/org/sqlite/jni/Nullable.java | 16 ++++ ext/jni/src/org/sqlite/jni/OutputPointer.java | 12 +-- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 73 +++++-------------- ext/jni/src/org/sqlite/jni/package-info.java | 2 + .../src/org/sqlite/jni/sqlite3_context.java | 4 +- manifest | 26 ++++--- manifest.uuid | 2 +- 11 files changed, 99 insertions(+), 85 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/NotNull.java create mode 100644 ext/jni/src/org/sqlite/jni/Nullable.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index c4fa4b5c06..043f929f2f 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -64,7 +64,10 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ CollationNeeded.java \ CommitHook.java \ NativePointerHolder.java \ + NotNull.java \ + Nullable.java \ OutputPointer.java \ + package-info.java \ ProgressHandler.java \ ResultCode.java \ RollbackHook.java \ @@ -286,10 +289,19 @@ $(package.jar): $(CLASS_FILES) $(MAKEFILE) $(package.jar.in) jar: $(package.jar) -dir.doc := $(dir.jni)/doc -doc: $(JAVA_FILES.main) +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 org.sqlite.jni + @echo "javadoc output is in $@" +.PHONY: doc javadoc docserve +.FORCE: doc +doc: $(doc.index) +javadoc: $(doc.index) +docserve: $(doc.index) + cd $(dir.doc) && althttpd -max-age 1 -page index.html ######################################################################## # Clean up... CLEAN_FILES += $(dir.bld.c)/* \ @@ -303,7 +315,7 @@ clean: -rm -f $(CLEAN_FILES) distclean: clean -rm -f $(DISTCLEAN_FILES) - -rm -fr $(dir.bld.c) + -rm -fr $(dir.bld.c) $(dir.doc) ######################################################################## # disttribution bundle rules... diff --git a/ext/jni/src/org/sqlite/jni/Authorizer.java b/ext/jni/src/org/sqlite/jni/Authorizer.java index 114c27fc63..b290b3af91 100644 --- a/ext/jni/src/org/sqlite/jni/Authorizer.java +++ b/ext/jni/src/org/sqlite/jni/Authorizer.java @@ -18,12 +18,8 @@ package org.sqlite.jni; */ public interface Authorizer { /** - Must functions as described for the sqlite3_set_authorizer() - callback, with one caveat: the string values passed here were - initially (at the C level) encoded in standard UTF-8. If they - contained any constructs which are not compatible with MUTF-8, - these strings will not have the expected values. For further - details, see the documentation for the SQLite3Jni class. + Must function as described for the sqlite3_set_authorizer() + callback. Must not throw. */ diff --git a/ext/jni/src/org/sqlite/jni/NativePointerHolder.java b/ext/jni/src/org/sqlite/jni/NativePointerHolder.java index 32aee978df..251eb7faad 100644 --- a/ext/jni/src/org/sqlite/jni/NativePointerHolder.java +++ b/ext/jni/src/org/sqlite/jni/NativePointerHolder.java @@ -23,7 +23,7 @@ package org.sqlite.jni; NativePointerHolder is not inadvertently passed to an incompatible function signature. - These objects do not _own_ the pointer they refer to. They are + These objects do not own the pointer they refer to. They are intended simply to communicate that pointer between C and Java. */ public class NativePointerHolder { diff --git a/ext/jni/src/org/sqlite/jni/NotNull.java b/ext/jni/src/org/sqlite/jni/NotNull.java new file mode 100644 index 0000000000..3222c357f6 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/NotNull.java @@ -0,0 +1,21 @@ +package org.sqlite.jni; + +/** + This annotation is for flagging parameters which may not legally be + null. Note that the C-style API does not throw any + NullPointerExceptions on its own because it has a no-throw policy + in order to retain its C-style semantics. + +

This annotation is informational only. No policy is in place to + programmatically ensure that NotNull is conformed to in client + code. + +

This annotation is solely for the use by the classes in this + package but is made public so that javadoc will link to it from the + annotated functions. It is not part of the public API and + client-level code must not rely on it. +*/ +@java.lang.annotation.Documented +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) +@java.lang.annotation.Target(java.lang.annotation.ElementType.PARAMETER) +public @interface NotNull{} diff --git a/ext/jni/src/org/sqlite/jni/Nullable.java b/ext/jni/src/org/sqlite/jni/Nullable.java new file mode 100644 index 0000000000..1dbb780d50 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/Nullable.java @@ -0,0 +1,16 @@ +package org.sqlite.jni; + +/** + This annotation is for flagging parameters which may legally be + null, noting that they may behave differently if passed null but + are prepared to expect null as a value. + +

This annotation is solely for the use by the classes in this + package but is made public so that javadoc will link to it from the + annotated functions. It is not part of the public API and + client-level code must not rely on it. +*/ +@java.lang.annotation.Documented +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) +@java.lang.annotation.Target(java.lang.annotation.ElementType.PARAMETER) +public @interface Nullable{} diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index bf61656dd5..fd25b0c5cf 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -16,13 +16,13 @@ package org.sqlite.jni; /** Helper classes for handling JNI output pointers. - We do not use a generic OutputPointer because working with those +

We do not use a generic OutputPointer because working with those from the native JNI code is unduly quirky due to a lack of autoboxing at that level. - The usage is similar for all of thes types: +

The usage is similar for all of thes types: - ``` +

{@code
    OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
    assert( null==out.get() );
    int rc = sqlite3_open(":memory:", out);
@@ -30,14 +30,14 @@ package org.sqlite.jni;
    assert( null!=out.get() );
    sqlite3 db = out.take();
    assert( null==out.get() );
-   ```
+   }
- With the minor exception that the primitive types permit direct +

With the minor exception that the primitive types permit direct access to the object's value via the `value` property, whereas the JNI-level opaque types do not permit client-level code to set that property. - Warning: do not share instances of these classes across +

Warning: do not share instances of these classes across threads. Doing so may lead to corrupting sqlite3-internal state. */ public final class OutputPointer { diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index b5cde41c1b..97c676cb5d 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -19,49 +19,22 @@ import java.lang.annotation.Target; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; -/** - This annotation is for flagging parameters which may legally be - null, noting that they may behave differently if passed null but - are prepared to expect null as a value. - - This annotation is solely for the reader's information. -*/ -@Documented -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.PARAMETER) -@interface Nullable{} - -/** - This annotation is for flagging parameters which may not legally be - null. Note that the C-style API does not throw any - NullPointerExceptions on its own because it has a no-throw policy - in order to retain its C-style semantics. - - This annotation is solely for the reader's information. No policy - is in place to programmatically ensure that NotNull is conformed to - in client code. -*/ -@Documented -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.PARAMETER) -@interface NotNull{} - /** This class contains the entire sqlite3 JNI API binding. For client-side use, a static import is recommended: - {@code +

{@code
   import static org.sqlite.jni.SQLite3Jni.*;
-  }
+  }
- The C-side part can be found in sqlite3-jni.c. +

The C-side part can be found in sqlite3-jni.c.

Only functions which materially differ from their C counterparts - are documented here. The C documetation is otherwise applicable - here: + are documented here. The C documentation is otherwise applicable + for these APIs: -

{link https://sqlite.org/c3ref/intro.html} +

https://sqlite.org/c3ref/intro.html

A handful of Java-specific APIs have been added. @@ -73,31 +46,22 @@ import java.lang.annotation.ElementType; but JNI uses what its docs call modified UTF-8 (see links below) Care must be taken when converting Java strings to or from standard UTF-8 to ensure that the proper conversion is performed. In short, - Java's `String.getBytes(StandardCharsets.UTF_8)` performs the proper + Java's {@code String.getBytes(StandardCharsets.UTF_8)} performs the proper conversion in Java, and there are no JNI C APIs for that conversion - (JNI's `NewStringUTF()` requires its input to be in MUTF-8). + (JNI's {@code NewStringUTF()} requires its input to be in MUTF-8).

The known consequences and limitations this discrepancy places on the SQLite3 JNI binding include:

    -
  • Any functions which return state from a database take extra care - to perform proper conversion, at the cost of efficiency.
  • -
  • C functions which take C-style strings without a length argument require special care when taking input from Java. In particular, Java strings converted to byte arrays for encoding purposes are not - NUL-terminated, and conversion to a Java byte array must be careful - to add one. Functions which take a length do not require this so - long as the length is provided. Search the SQLite3Jni class for "\0" - for many examples. - -
  • Similarly, C-side code which deals with strings which might not - be NUL-terminated (e.g. while tokenizing in FTS5-related code) - cannot use JNI's new-string functions to return them to Java because - none of those APIs take a string-length argument. Such cases must - return byte arrays instead of strings. + NUL-terminated, and conversion to a Java byte array must sometimes + be careful to add one. Functions which take a length do not require + this so long as the length is provided. Search the SQLite3Jni class + for "\0" for many examples.
@@ -996,8 +960,9 @@ public final class SQLite3Jni { /** Binds the SQL result to the given object, or - sqlite3_result_null() if o is null. Use - sqlite3_value_java_object() or sqlite3_column_java_object() to + {@link #sqlite3_result_null(sqlite3_context) sqlite3_result_null()} if {@code o} is null. Use + {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} or + {@link #sqlite3_column_java_object(sqlite3_stmt,int) sqlite3_column_java_object()} to fetch it. This is implemented in terms of sqlite3_result_pointer(), but @@ -1093,11 +1058,11 @@ public final class SQLite3Jni { /** Binds the given text using C's sqlite3_result_blob64() unless: - - blob is null ==> sqlite3_result_null() + - @param blob is null ==> sqlite3_result_null() - - blob is too large ==> sqlite3_result_error_toobig() + - @param blob is too large ==> sqlite3_result_error_toobig() - If maxLen is larger than blob.length, it is truncated to that + If @param maxLen is larger than blob.length, it is truncated to that value. If it is negative, results are undefined. */ private static native void sqlite3_result_blob64( @@ -1138,7 +1103,7 @@ public final class SQLite3Jni { - text is too large: translates to a call to sqlite3_result_error_toobig() - - The `encoding` argument has an invalid value: translates to + - The @param encoding argument has an invalid value: translates to sqlite3_result_error_code() with code SQLITE_FORMAT. If maxLength (in bytes, not characters) is larger than diff --git a/ext/jni/src/org/sqlite/jni/package-info.java b/ext/jni/src/org/sqlite/jni/package-info.java index 2629d29282..6de353e0b2 100644 --- a/ext/jni/src/org/sqlite/jni/package-info.java +++ b/ext/jni/src/org/sqlite/jni/package-info.java @@ -2,5 +2,7 @@ This package houses a JNI binding to the SQLite3 C API. The docs are in progress. + + The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}. */ package org.sqlite.jni; diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_context.java b/ext/jni/src/org/sqlite/jni/sqlite3_context.java index ead3f372ea..a3a3d48664 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_context.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_context.java @@ -47,9 +47,9 @@ public final class sqlite3_context extends NativePointerHolder

Consider this SQL, where MYFUNC is a user-defined aggregate function: - {code +

{@code
      SELECT MYFUNC(A), MYFUNC(B) FROM T;
-     }
+     }

The xStep() and xFinal() methods of the callback need to be able to differentiate between those two invocations in order to diff --git a/manifest b/manifest index 65636cb8a6..00e32259f3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Re-frame\sthe\sincongruous\sSQLite3Jni.uncacheThread()\sAPI\sas\ssqlite3_java_uncache_thread(). -D 2023-08-24T22:28:44.239 +C javadoc\sadditions. +D 2023-08-25T00:27:28.089 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,12 +233,12 @@ 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 6aeafa0ebcf0f0d834c814ae8b450b54135ea11a2a7868f90b6286ec1bf6020f +F ext/jni/GNUmakefile 642624b421936807eeed2fe7d0f9df898837ad0e4be4d9e32af06b0e5ef2c5b6 F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c e4bdcd17e8f8e825f206e1c6ab5adf7f507d70b64b0f795c0cde141077fb68b2 F ext/jni/src/c/sqlite3-jni.h 91c2eeee22d3594e6652d51edcce0cd94d258a768802fcfac13a78f900127b72 -F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 +F ext/jni/src/org/sqlite/jni/Authorizer.java e6cbc6605d4d254be892d5197dea6290180efb7c5dbb3060f8487563bb11bb65 F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1 @@ -250,15 +250,17 @@ F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 10cb2e0eb4dc5cf4241a7ccc0442a 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 -F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 8110d4cfb20884e8ed241de7420c615b040a9f9c441d9cff06f34833399244a8 -F ext/jni/src/org/sqlite/jni/OutputPointer.java bb09fee5ad51d10e58075de000f8c1a3622a6c4b6a390ef134b6add1bfb32ca1 +F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d +F ext/jni/src/org/sqlite/jni/NotNull.java a4016df436f454e8d6786dd8421484edd6fc604043cf7fd8ec94cf922ba61604 +F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 +F ext/jni/src/org/sqlite/jni/OutputPointer.java 8d7b2c865217d3b7a47dccaddc4a24748463b770eecca90873402a38c0b2d819 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 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 5fddc34787b26a494dc9843ea2530cd319dc7a6bd369fd4e86dc713f20640fa6 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java af2d1a673f48bed8bb39ad9f7fe79c3d904cb2c6c875254a0e8c7e7db6539725 F ext/jni/src/org/sqlite/jni/Tester1.java e9b82c561ec8771b3e4ea537ebd7c16dd096928b6b8221967a4726104c7c6cb2 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d @@ -267,9 +269,9 @@ F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b -F ext/jni/src/org/sqlite/jni/package-info.java 1a547913d681411d65c5fe0bca840f049abe5612740154a125545ea9e2481747 +F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc -F ext/jni/src/org/sqlite/jni/sqlite3_context.java 42df6769ab9c8de40d24f39f49152cdaa6e0c06d1468660a95dfee9ecaeb9a63 +F ext/jni/src/org/sqlite/jni/sqlite3_context.java dca23e54f368f8ea37c112c1d2f74dc9522d5da2fdf67d6fd6b2ec9603d8514c F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3 @@ -2096,8 +2098,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 3f684ef5018116f4be46a07779451c8983ac87a5db182477f71ee7bf28287a04 -R ce1f562cc06444ed73ad1cad9371c48a +P 7232b033954fae40df3db43e489e0e5a703c03308f500a1ae36fd9d707632d7f +R 2acb197dd02e88e8595f80978f7eae58 U stephan -Z fbdf5ee5d7ecf00150898b00dcd91489 +Z c7c0446afbe0f68b470086328d8c934a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 33b1d73d8e..760e3ea188 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7232b033954fae40df3db43e489e0e5a703c03308f500a1ae36fd9d707632d7f \ No newline at end of file +bedf33d403677d243a1505ce549166850ab55671700b143278db5feb84883ab3 \ No newline at end of file From 5276552083d58cadb33242ebc918b9bb10d56630 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 02:57:30 +0000 Subject: [PATCH 054/422] Replace all of the JNI XyzHook/Handler classes with snake_cased ones which follow unified naming conventions to make them easier to apply. FossilOrigin-Name: 76e62a381249b3b4262b22bdffe7fc2816c820115c9df266956ab8817b127aca --- ext/jni/GNUmakefile | 32 ++++--- ext/jni/README.md | 19 +--- ext/jni/src/c/sqlite3-jni.c | 44 +++++----- ext/jni/src/c/sqlite3-jni.h | 28 +++--- ext/jni/src/org/sqlite/jni/PreUpdateHook.java | 29 ------ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 50 +++++------ ext/jni/src/org/sqlite/jni/Tester1.java | 88 +++++++++---------- ext/jni/src/org/sqlite/jni/UpdateHook.java | 25 ------ ...thorizer.java => authorizer_callback.java} | 15 ++-- ...sion.java => auto_extension_callback.java} | 21 ++--- ...andler.java => busy_handler_callback.java} | 24 ++--- ...Collation.java => collation_callback.java} | 13 +-- ...ed.java => collation_needed_callback.java} | 10 +-- ...mitHook.java => commit_hook_callback.java} | 12 +-- ...QLLog.java => config_sqllog_callback.java} | 6 +- .../sqlite/jni/preupdate_hook_callback.java | 26 ++++++ ...er.java => progress_handler_callback.java} | 10 +-- ...kHook.java => rollback_hook_callback.java} | 12 +-- .../sqlite/jni/sqlite3_callback_proxy.java | 34 +++++++ .../sqlite/jni/sqlite3_xDestroy_callback.java | 28 ++++++ .../src/org/sqlite/jni/tester/SQLTester.java | 4 +- .../{Tracer.java => trace_v2_callback.java} | 42 ++++----- .../org/sqlite/jni/update_hook_callback.java | 25 ++++++ manifest | 50 ++++++----- manifest.uuid | 2 +- 25 files changed, 353 insertions(+), 296 deletions(-) delete mode 100644 ext/jni/src/org/sqlite/jni/PreUpdateHook.java delete mode 100644 ext/jni/src/org/sqlite/jni/UpdateHook.java rename ext/jni/src/org/sqlite/jni/{Authorizer.java => authorizer_callback.java} (58%) rename ext/jni/src/org/sqlite/jni/{AutoExtension.java => auto_extension_callback.java} (60%) rename ext/jni/src/org/sqlite/jni/{BusyHandler.java => busy_handler_callback.java} (65%) rename ext/jni/src/org/sqlite/jni/{Collation.java => collation_callback.java} (65%) rename ext/jni/src/org/sqlite/jni/{CollationNeeded.java => collation_needed_callback.java} (68%) rename ext/jni/src/org/sqlite/jni/{CommitHook.java => commit_hook_callback.java} (65%) rename ext/jni/src/org/sqlite/jni/{SQLLog.java => config_sqllog_callback.java} (81%) create mode 100644 ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java rename ext/jni/src/org/sqlite/jni/{ProgressHandler.java => progress_handler_callback.java} (70%) rename ext/jni/src/org/sqlite/jni/{RollbackHook.java => rollback_hook_callback.java} (65%) create mode 100644 ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java create mode 100644 ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java rename ext/jni/src/org/sqlite/jni/{Tracer.java => trace_v2_callback.java} (51%) create mode 100644 ext/jni/src/org/sqlite/jni/update_hook_callback.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 043f929f2f..5cd9c0dff0 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -59,29 +59,33 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ - BusyHandler.java \ - Collation.java \ - CollationNeeded.java \ - CommitHook.java \ + authorizer_callback.java \ + auto_extension_callback.java \ + busy_handler_callback.java \ + collation_callback.java \ + collation_needed_callback.java \ + commit_hook_callback.java \ + config_sqllog_callback.java \ + preupdate_hook_callback.java \ + progress_handler_callback.java \ + rollback_hook_callback.java \ + trace_v2_callback.java \ + update_hook_callback.java \ NativePointerHolder.java \ NotNull.java \ Nullable.java \ OutputPointer.java \ - package-info.java \ - ProgressHandler.java \ ResultCode.java \ - RollbackHook.java \ SQLFunction.java \ - SQLLog.java \ - sqlite3_context.java \ - sqlite3.java \ SQLite3Jni.java \ + Tester1.java \ + ValueHolder.java \ + package-info.java \ + sqlite3.java \ + sqlite3_context.java \ sqlite3_stmt.java \ sqlite3_value.java \ - Tester1.java \ - Tracer.java \ - UpdateHook.java \ - ValueHolder.java \ + sqlite3_xDestroy_callback.java \ ) ifeq (1,$(enable.fts5)) JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\ diff --git a/ext/jni/README.md b/ext/jni/README.md index 1c8ab4a9d8..d655a46b2b 100644 --- a/ext/jni/README.md +++ b/ext/jni/README.md @@ -184,17 +184,6 @@ necessarily suppress any exceptions in order to maintain the C-style semantics of the APIs. -Awkward Callback Names ------------------------------------------------------------------------- - -In places where the Java interface uses callbacks (see below), those -callbacks often have what might fairly be labeled as awkward names, -e.g. `sqlScalarFunction.xFunc()` and `preupdateHook.xPreUpdate()`. -Those names were chosen because they match the corresponding arguments -in the C-level API docs. If they were renamed to be more Java-esque, -documentation transparency would suffer. - - Unwieldy Constructs are Re-mapped ------------------------------------------------------------------------ @@ -246,18 +235,18 @@ follow that pattern use a slightly different Java interface: ```java int sqlite3_create_collation(sqlite3 db, String name, int eTextRep, - Collation collation); + SomeCallbackType collation); ``` -Where the `Collation` class has an abstract `xCompare()` method and +Where the `Collation` class has an abstract `call()` method and no-op `xDestroy()` method which can be overridden if needed, leading to a much more Java-esque usage: ```java -int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new Collation(){ +int rc = sqlite3_create_collation(db, "mycollation", SQLITE_UTF8, new SomeCallbackType(){ // Required comparison function: - @Override public int xCompare(byte[] lhs, byte[] rhs){ ... } + @Override public int call(byte[] lhs, byte[] rhs){ ... } // Optional finalizer function: @Override public void xDestroy(){ ... } diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 410d42c818..133a0edb88 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -519,7 +519,7 @@ struct S3JniGlobalType { } metrics; /** The list of bound auto-extensions (Java-side: - org.sqlite.jni.AutoExtension objects). + org.sqlite.jni.auto_extension objects). */ struct { S3JniAutoExtension *pExt /* Head of the auto-extension list */; @@ -925,7 +925,7 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ UNHOOK(preUpdate, 0); #endif UNHOOK(collation, 1); - UNHOOK(collationNeeded, 1); + UNHOOK(collationNeeded, 0); UNHOOK(busyHandler, 1); #undef UNHOOK UNREF_G(s->jDb); @@ -1158,12 +1158,12 @@ static int S3JniAutoExtension_init(JNIEnv *const env, jobject const jAutoExt){ jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); - ax->midFunc = (*env)->GetMethodID(env, klazz, "xEntryPoint", + ax->midFunc = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;)I"); UNREF_L(klazz); S3JniExceptionWarnIgnore; if( !ax->midFunc ){ - MARKER(("Error getting xEntryPoint(sqlite3) from AutoExtension object.")); + MARKER(("Error getting call(sqlite3) from AutoExtension object.\n")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } @@ -2025,7 +2025,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( S3JniHook_unref(env, pHook, 1); pHook->jObj = REF_G(jBusy); klazz = (*env)->GetObjectClass(env, jBusy); - pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(I)I"); + pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); UNREF_L(klazz); S3JniIfThrew { S3JniHook_unref(env, pHook, 0); @@ -2164,7 +2164,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( return 0; } klazz = (*env)->GetObjectClass(env, jHook); - xCallback = (*env)->GetMethodID(env, klazz, "xCollationNeeded", + xCallback = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); UNREF_L(klazz); S3JniIfThrew { @@ -2289,8 +2289,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, return pOld; } klazz = (*env)->GetObjectClass(env, jHook); - xCallback = (*env)->GetMethodID(env, klazz, - isCommit ? "xCommitHook" : "xRollbackHook", + xCallback = (*env)->GetMethodID(env, klazz, "call", isCommit ? "()I" : "()V"); UNREF_L(klazz); S3JniIfThrew { @@ -2400,7 +2399,7 @@ S3JniApi(sqlite3_config(/* for SQLLOG */), return 0; } klazz = (*env)->GetObjectClass(env, jLog); - hook->midCallback = (*env)->GetMethodID(env, klazz, "xSqllog", + hook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;" "Ljava/lang/String;" "I)V"); @@ -2444,7 +2443,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), if( !pHook ) return SQLITE_MISUSE; klazz = (*env)->GetObjectClass(env, oCollation); - pHook->midCallback = (*env)->GetMethodID(env, klazz, "xCompare", + pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); UNREF_L(klazz); S3JniIfThrew{ @@ -3055,13 +3054,13 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec } klazz = (*env)->GetObjectClass(env, jHook); xCallback = isPre - ? (*env)->GetMethodID(env, klazz, "xPreUpdate", + ? (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;" "I" "Ljava/lang/String;" "Ljava/lang/String;" "JJ)V") - : (*env)->GetMethodID(env, klazz, "xUpdateHook", + : (*env)->GetMethodID(env, klazz, "call", "(ILjava/lang/String;Ljava/lang/String;J)V"); UNREF_L(klazz); S3JniIfThrew { @@ -3167,7 +3166,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( return; } klazz = (*env)->GetObjectClass(env, jProgress); - xCallback = (*env)->GetMethodID(env, klazz, "xCallback", "()I"); + xCallback = (*env)->GetMethodID(env, klazz, "call", "()I"); UNREF_L(klazz); S3JniIfThrew { S3JniExceptionClear; @@ -3476,7 +3475,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( pHook->jObj = REF_G(jHook); klazz = (*env)->GetObjectClass(env, jHook); pHook->midCallback = (*env)->GetMethodID(env, klazz, - "xAuth", + "call", "(I" "Ljava/lang/String;" "Ljava/lang/String;" @@ -3667,25 +3666,26 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniHook * const pHook = ps ? &ps->hooks.trace : 0; jclass klazz; - if( !traceMask || !jTracer ){ - if( ps ){ - S3JniHook_unref(env, &ps->hooks.trace, 0); - } + if( !ps ) return SQLITE_MISUSE; + else if( !traceMask || !jTracer ){ + S3JniHook_unref(env, pHook, 0); return (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); } - if( !ps ) return SQLITE_NOMEM; klazz = (*env)->GetObjectClass(env, jTracer); - ps->hooks.trace.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", - "(ILjava/lang/Object;Ljava/lang/Object;)I"); + pHook->midCallback = (*env)->GetMethodID( + env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I" + ); UNREF_L(klazz); S3JniIfThrew { S3JniExceptionClear; + S3JniHook_unref(env, pHook, 0); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on Tracer object."); } - ps->hooks.trace.jObj = REF_G(jTracer); + pHook->jObj = REF_G(jTracer); return sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index d952cfa60a..0381118d53 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -782,7 +782,7 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1aggregate_1conte /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_auto_extension - * Signature: (Lorg/sqlite/jni/AutoExtension;)I + * Signature: (Lorg/sqlite/jni/auto_extension_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1auto_1extension (JNIEnv *, jclass, jobject); @@ -878,7 +878,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1zeroblob64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_busy_handler - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/BusyHandler;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/busy_handler_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1busy_1handler (JNIEnv *, jclass, jobject, jobject); @@ -894,7 +894,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1busy_1timeout /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_cancel_auto_extension - * Signature: (Lorg/sqlite/jni/AutoExtension;)Z + * Signature: (Lorg/sqlite/jni/auto_extension_callback;)Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1cancel_1auto_1extension (JNIEnv *, jclass, jobject); @@ -1062,7 +1062,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1value /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_collation_needed - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/CollationNeeded;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/collation_needed_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1collation_1needed (JNIEnv *, jclass, jobject, jobject); @@ -1078,7 +1078,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1context_1db_1h /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_commit_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/CommitHook;)Lorg/sqlite/jni/CommitHook; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/commit_hook_callback;)Lorg/sqlite/jni/commit_hook_callback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1commit_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1110,15 +1110,15 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__I /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_config - * Signature: (Lorg/sqlite/jni/SQLLog;)I + * Signature: (Lorg/sqlite/jni/config_sqllog_callback;)I */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqlite_jni_SQLLog_2 +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqlite_jni_config_1sqllog_1callback_2 (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_create_collation - * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/Collation;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/collation_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1create_1collation (JNIEnv *, jclass, jobject, jstring, jint, jobject); @@ -1350,7 +1350,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1depth /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_preupdate_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/PreUpdateHook;)Lorg/sqlite/jni/PreUpdateHook; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/preupdate_hook_callback;)Lorg/sqlite/jni/preupdate_hook_callback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1374,7 +1374,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1old /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_progress_handler - * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/ProgressHandler;)V + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/progress_handler_callback;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1progress_1handler (JNIEnv *, jclass, jobject, jint, jobject); @@ -1550,7 +1550,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_rollback_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHook;)Lorg/sqlite/jni/RollbackHook; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/rollback_hook_callback;)Lorg/sqlite/jni/rollback_hook_callback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1558,7 +1558,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_set_authorizer - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/Authorizer;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/authorizer_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1set_1authorizer (JNIEnv *, jclass, jobject, jobject); @@ -1646,7 +1646,7 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1total_1changes64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_trace_v2 - * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/Tracer;)I + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/trace_v2_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1trace_1v2 (JNIEnv *, jclass, jobject, jint, jobject); @@ -1654,7 +1654,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1trace_1v2 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_update_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/UpdateHook;)Lorg/sqlite/jni/UpdateHook; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/update_hook_callback;)Lorg/sqlite/jni/update_hook_callback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1update_1hook (JNIEnv *, jclass, jobject, jobject); diff --git a/ext/jni/src/org/sqlite/jni/PreUpdateHook.java b/ext/jni/src/org/sqlite/jni/PreUpdateHook.java deleted file mode 100644 index d5d82c72bc..0000000000 --- a/ext/jni/src/org/sqlite/jni/PreUpdateHook.java +++ /dev/null @@ -1,29 +0,0 @@ -/* -** 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 ); -} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 97c676cb5d..aaf91f5331 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -20,8 +20,9 @@ import java.lang.annotation.Documented; import java.lang.annotation.ElementType; /** - This class contains the entire sqlite3 JNI API binding. For - client-side use, a static import is recommended: + This class contains the entire C-style sqlite3 JNI API binding, + minus a few bits and pieces declared in other files. For client-side + use, a static import is recommended:

{@code
   import static org.sqlite.jni.SQLite3Jni.*;
@@ -36,8 +37,8 @@ import java.lang.annotation.ElementType;
 
   

https://sqlite.org/c3ref/intro.html -

A handful of Java-specific APIs have been added. - +

A handful of Java-specific APIs have been added which are documented + here.

Notes regarding Java's Modified UTF-8 vs standard UTF-8: @@ -147,7 +148,7 @@ public final class SQLite3Jni {

See the AutoExtension class docs for more information. */ - public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); + public static native int sqlite3_auto_extension(@NotNull auto_extension_callback callback); /** Results are undefined if data is not null and n<0 || n>=data.length. @@ -259,7 +260,7 @@ public final class SQLite3Jni { /** Requires that data be null or in UTF-16 encoding in platform byte order. Returns the result of the C-level sqlite3_bind_null() or - sqlite3_bind_text(). + sqlite3_bind_text16(). */ public static int sqlite3_bind_text16( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @@ -278,12 +279,12 @@ public final class SQLite3Jni { ); /** - As for the C-level function of the same name, with a BusyHandler + As for the C-level function of the same name, with a busy_handler_callback instance in place of a callback function. Pass it a null handler to clear the busy handler. */ public static native int sqlite3_busy_handler( - @NotNull sqlite3 db, @Nullable BusyHandler handler + @NotNull sqlite3 db, @Nullable busy_handler_callback handler ); public static native int sqlite3_busy_timeout( @@ -291,7 +292,7 @@ public final class SQLite3Jni { ); public static native boolean sqlite3_cancel_auto_extension( - @NotNull AutoExtension ax + @NotNull auto_extension_callback ax ); public static native int sqlite3_changes( @@ -457,7 +458,7 @@ public final class SQLite3Jni { Java's string type is compatible with that interface. */ public static native int sqlite3_collation_needed( - @NotNull sqlite3 db, @Nullable CollationNeeded callback + @NotNull sqlite3 db, @Nullable collation_needed_callback callback ); /** @@ -468,8 +469,8 @@ public final class SQLite3Jni { @NotNull sqlite3_context cx ); - public static native CommitHook sqlite3_commit_hook( - @NotNull sqlite3 db, @Nullable CommitHook hook + public static native commit_hook_callback sqlite3_commit_hook( + @NotNull sqlite3 db, @Nullable commit_hook_callback hook ); public static native String sqlite3_compileoption_get( @@ -503,11 +504,11 @@ public final class SQLite3Jni { ** If not built with SQLITE_ENABLE_SQLLOG defined, this returns ** SQLITE_MISUSE. */ - public static native int sqlite3_config( @Nullable SQLLog logger ); + public static native int sqlite3_config( @Nullable config_sqllog_callback logger ); public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, - @NotNull Collation col + @NotNull collation_callback col ); /** @@ -826,8 +827,9 @@ public final class SQLite3Jni { 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); + public static native preupdate_hook_callback sqlite3_preupdate_hook( + @NotNull sqlite3 db, @Nullable preupdate_hook_callback hook + ); /** If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, @@ -866,11 +868,9 @@ public final class SQLite3Jni { } public static native void sqlite3_progress_handler( - @NotNull sqlite3 db, int n, @Nullable ProgressHandler h + @NotNull sqlite3 db, int n, @Nullable progress_handler_callback h ); - //TODO??? void *sqlite3_preupdate_hook(...) and friends - public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt); /** @@ -1156,13 +1156,13 @@ public final class SQLite3Jni { } } - public static native RollbackHook sqlite3_rollback_hook( - @NotNull sqlite3 db, @Nullable RollbackHook hook + public static native rollback_hook_callback sqlite3_rollback_hook( + @NotNull sqlite3 db, @Nullable rollback_hook_callback hook ); //! Sets or unsets (if auth is null) the current authorizer. public static native int sqlite3_set_authorizer( - @NotNull sqlite3 db, @Nullable Authorizer auth + @NotNull sqlite3 db, @Nullable authorizer_callback auth ); public static native void sqlite3_set_last_insert_rowid( @@ -1229,11 +1229,11 @@ public final class SQLite3Jni { cannot be processed propertly (i.e. an internal error). */ public static native int sqlite3_trace_v2( - @NotNull sqlite3 db, int traceMask, @Nullable Tracer tracer + @NotNull sqlite3 db, int traceMask, @Nullable trace_v2_callback tracer ); - public static native UpdateHook sqlite3_update_hook( - sqlite3 db, UpdateHook hook + public static native update_hook_callback sqlite3_update_hook( + sqlite3 db, update_hook_callback hook ); public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index cc4d7dbd06..7587cad884 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -489,12 +489,12 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); final ValueHolder xDestroyCalled = new ValueHolder<>(false); - final Collation myCollation = new Collation() { + final collation_callback myCollation = new collation_callback() { private String myState = "this is local state. There is much like it, but this is mine."; @Override // Reverse-sorts its inputs... - public int xCompare(byte[] lhs, byte[] rhs){ + public int call(byte[] lhs, byte[] rhs){ int len = lhs.length > rhs.length ? rhs.length : lhs.length; int c = 0, i = 0; for(i = 0; i < len; ++i){ @@ -513,8 +513,9 @@ public class Tester1 implements Runnable { xDestroyCalled.value = true; } }; - final CollationNeeded collLoader = new CollationNeeded(){ - public int xCollationNeeded(sqlite3 dbArg, int eTextRep, String collationName){ + final collation_needed_callback collLoader = new collation_needed_callback(){ + @Override + public int call(sqlite3 dbArg, int eTextRep, String collationName){ affirm(dbArg == db/* as opposed to a temporary object*/); return sqlite3_create_collation(dbArg, "reversi", eTextRep, myCollation); } @@ -877,11 +878,11 @@ public class Tester1 implements Runnable { from Java to sqlite3 and back to Java. (At no small efficiency penalty.) */ final String nonBmpChar = "😃"; - sqlite3_trace_v2( + int rc = sqlite3_trace_v2( db, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE | SQLITE_TRACE_ROW | SQLITE_TRACE_CLOSE, - new Tracer(){ - public int xCallback(int traceFlag, Object pNative, Object x){ + new trace_v2_callback(){ + @Override public int call(int traceFlag, Object pNative, Object x){ ++counter.value; //outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName()); switch(traceFlag){ @@ -912,6 +913,7 @@ public class Tester1 implements Runnable { return 0; } }); + affirm( 0==rc ); execSql(db, "SELECT coalesce(null,null,'"+nonBmpChar+"'); "+ "SELECT 'w"+nonBmpChar+"orld'"); affirm( 6 == counter.value ); @@ -940,8 +942,8 @@ public class Tester1 implements Runnable { final ValueHolder xDestroyed = new ValueHolder<>(false); final ValueHolder xBusyCalled = new ValueHolder<>(0); - BusyHandler handler = new BusyHandler(){ - @Override public int xCallback(int n){ + busy_handler_callback handler = new busy_handler_callback(){ + @Override public int call(int n){ //outln("busy handler #"+n); return n > 2 ? 0 : ++xBusyCalled.value; } @@ -974,8 +976,8 @@ public class Tester1 implements Runnable { private void testProgress(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); - sqlite3_progress_handler(db, 1, new ProgressHandler(){ - public int xCallback(){ + sqlite3_progress_handler(db, 1, new progress_handler_callback(){ + @Override public int call(){ ++counter.value; return 0; } @@ -993,13 +995,13 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder hookResult = new ValueHolder<>(0); - final CommitHook theHook = new CommitHook(){ - public int xCommitHook(){ + final commit_hook_callback theHook = new commit_hook_callback(){ + @Override public int call(){ ++counter.value; return hookResult.value; } }; - CommitHook oldHook = sqlite3_commit_hook(db, theHook); + commit_hook_callback oldHook = sqlite3_commit_hook(db, theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 2 == counter.value ); @@ -1020,8 +1022,8 @@ public class Tester1 implements Runnable { execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;"); affirm( 4 == counter.value ); - final CommitHook newHook = new CommitHook(){ - public int xCommitHook(){return 0;} + final commit_hook_callback newHook = new commit_hook_callback(){ + @Override public int call(){return 0;} }; oldHook = sqlite3_commit_hook(db, newHook); affirm( null == oldHook ); @@ -1042,17 +1044,16 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); - final UpdateHook theHook = new UpdateHook(){ - @SuppressWarnings("unchecked") + final update_hook_callback theHook = new update_hook_callback(){ @Override - public void xUpdateHook(int opId, String dbName, String tableName, long rowId){ + public void call(int opId, String dbName, String tableName, long rowId){ ++counter.value; if( 0!=expectedOp.value ){ affirm( expectedOp.value == opId ); } } }; - UpdateHook oldHook = sqlite3_update_hook(db, theHook); + update_hook_callback oldHook = sqlite3_update_hook(db, theHook); affirm( null == oldHook ); expectedOp.value = SQLITE_INSERT; execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); @@ -1072,8 +1073,8 @@ public class Tester1 implements Runnable { oldHook = sqlite3_update_hook(db, null); affirm( null == oldHook ); - final UpdateHook newHook = new UpdateHook(){ - public void xUpdateHook(int opId, String dbName, String tableName, long rowId){ + final update_hook_callback newHook = new update_hook_callback(){ + @Override public void call(int opId, String dbName, String tableName, long rowId){ } }; oldHook = sqlite3_update_hook(db, newHook); @@ -1100,11 +1101,10 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); - final PreUpdateHook theHook = new PreUpdateHook(){ - @SuppressWarnings("unchecked") + final preupdate_hook_callback theHook = new preupdate_hook_callback(){ @Override - public void xPreUpdate(sqlite3 db, int opId, String dbName, String dbTable, - long iKey1, long iKey2 ){ + public void call(sqlite3 db, int opId, String dbName, String dbTable, + long iKey1, long iKey2 ){ ++counter.value; switch( opId ){ case SQLITE_UPDATE: @@ -1126,7 +1126,7 @@ public class Tester1 implements Runnable { } } }; - PreUpdateHook oldHook = sqlite3_preupdate_hook(db, theHook); + preupdate_hook_callback 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')"); @@ -1146,10 +1146,10 @@ public class Tester1 implements Runnable { oldHook = sqlite3_preupdate_hook(db, null); affirm( null == oldHook ); - final PreUpdateHook newHook = new PreUpdateHook(){ + final preupdate_hook_callback newHook = new preupdate_hook_callback(){ @Override - public void xPreUpdate(sqlite3 db, int opId, String dbName, - String tableName, long iKey1, long iKey2){ + public void call(sqlite3 db, int opId, String dbName, + String tableName, long iKey1, long iKey2){ } }; oldHook = sqlite3_preupdate_hook(db, newHook); @@ -1168,20 +1168,20 @@ public class Tester1 implements Runnable { private void testRollbackHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); - final RollbackHook theHook = new RollbackHook(){ - public void xRollbackHook(){ + final rollback_hook_callback theHook = new rollback_hook_callback(){ + @Override public void call(){ ++counter.value; } }; - RollbackHook oldHook = sqlite3_rollback_hook(db, theHook); + rollback_hook_callback oldHook = sqlite3_rollback_hook(db, theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 0 == counter.value ); execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;"); affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ ); - final RollbackHook newHook = new RollbackHook(){ - public void xRollbackHook(){return;} + final rollback_hook_callback newHook = new rollback_hook_callback(){ + @Override public void call(){return;} }; oldHook = sqlite3_rollback_hook(db, newHook); affirm( theHook == oldHook ); @@ -1237,8 +1237,8 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder authRc = new ValueHolder<>(0); - final Authorizer auth = new Authorizer(){ - public int xAuth(int op, String s0, String s1, String s2, String s3){ + final authorizer_callback auth = new authorizer_callback(){ + public int call(int op, String s0, String s1, String s2, String s3){ ++counter.value; //outln("xAuth(): "+s0+" "+s1+" "+s2+" "+s3); return authRc.value; @@ -1260,8 +1260,8 @@ public class Tester1 implements Runnable { private synchronized void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); - final AutoExtension ax = new AutoExtension(){ - public synchronized int xEntryPoint(sqlite3 db){ + final auto_extension_callback ax = new auto_extension_callback(){ + @Override public synchronized int call(sqlite3 db){ ++val.value; if( null!=toss.value ){ throw new RuntimeException(toss.value); @@ -1300,7 +1300,7 @@ public class Tester1 implements Runnable { rc = sqlite3_auto_extension( ax ); affirm( 0==rc ); Exception err = null; - toss.value = "Throwing from AutoExtension."; + toss.value = "Throwing from auto_extension."; try{ sqlite3_close(createNewDb()); }catch(Exception e){ @@ -1311,8 +1311,8 @@ public class Tester1 implements Runnable { toss.value = null; val.value = 0; - final AutoExtension ax2 = new AutoExtension(){ - public synchronized int xEntryPoint(sqlite3 db){ + final auto_extension_callback ax2 = new auto_extension_callback(){ + @Override public synchronized int call(sqlite3 db){ ++val.value; return 0; } @@ -1507,8 +1507,8 @@ public class Tester1 implements Runnable { if( sqlLog ){ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ - int rc = sqlite3_config( new SQLLog() { - @Override public void xSqllog(sqlite3 db, String msg, int op){ + int rc = sqlite3_config( new config_sqllog_callback() { + @Override public void call(sqlite3 db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; case 1: outln(db,": ",msg); break; diff --git a/ext/jni/src/org/sqlite/jni/UpdateHook.java b/ext/jni/src/org/sqlite/jni/UpdateHook.java deleted file mode 100644 index 171e2bdb41..0000000000 --- a/ext/jni/src/org/sqlite/jni/UpdateHook.java +++ /dev/null @@ -1,25 +0,0 @@ -/* -** 2023-07-22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** This file is part of the JNI bindings for the sqlite3 C API. -*/ -package org.sqlite.jni; - -/** - Callback proxy for use with sqlite3_update_hook(). -*/ -public interface UpdateHook { - /** - Works as documented for the sqlite3_update_hook() callback. - Must not throw. - */ - void xUpdateHook(int opId, String dbName, String tableName, long rowId); -} diff --git a/ext/jni/src/org/sqlite/jni/Authorizer.java b/ext/jni/src/org/sqlite/jni/authorizer_callback.java similarity index 58% rename from ext/jni/src/org/sqlite/jni/Authorizer.java rename to ext/jni/src/org/sqlite/jni/authorizer_callback.java index b290b3af91..6817c7e115 100644 --- a/ext/jni/src/org/sqlite/jni/Authorizer.java +++ b/ext/jni/src/org/sqlite/jni/authorizer_callback.java @@ -1,5 +1,5 @@ /* -** 2023-08-05 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,15 +14,16 @@ package org.sqlite.jni; /** - A callback for use with sqlite3_set_authorizer(). + Callback for use with sqlite3_set_authorizer(). */ -public interface Authorizer { +public interface authorizer_callback extends sqlite3_callback_proxy { /** - Must function as described for the sqlite3_set_authorizer() - callback. + Must function as described for the C-level + sqlite3_set_authorizer() callback. Must not throw. */ - int xAuth(int opId, @Nullable String s1, @Nullable String s2, - @Nullable String s3, @Nullable String s4); + int call(int opId, @Nullable String s1, @Nullable String s2, + @Nullable String s3, @Nullable String s4); + } diff --git a/ext/jni/src/org/sqlite/jni/AutoExtension.java b/ext/jni/src/org/sqlite/jni/auto_extension_callback.java similarity index 60% rename from ext/jni/src/org/sqlite/jni/AutoExtension.java rename to ext/jni/src/org/sqlite/jni/auto_extension_callback.java index 443345fde4..7bb1a757ef 100644 --- a/ext/jni/src/org/sqlite/jni/AutoExtension.java +++ b/ext/jni/src/org/sqlite/jni/auto_extension_callback.java @@ -1,5 +1,5 @@ /* -** 2023-08-05 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -17,24 +17,25 @@ package org.sqlite.jni; A callback for use with the sqlite3_auto_extension() family of APIs. */ -public interface AutoExtension { +public interface auto_extension_callback extends sqlite3_callback_proxy { /** - Must function as described for a sqlite3_auto_extension() - callback, with the caveat that the signature is shorter. + Must function as described for a C-level + sqlite3_auto_extension() callback, with the caveat that the + signature is shorter. - AutoExtensions may throw and the exception's error message - will be set as the db's error string. + This callback may throw and the exception's error message will + be set as the db's error string. Tips for implementations: - Opening a database from an auto-extension handler will lead to - an endless recursion of the auto-handler triggering itself - indirectly for each newly-opened database. + an endless recursion of the auto-handler triggering itself + indirectly for each newly-opened database. - If this routine is stateful, it may be useful to make the - overridden method synchronized. + overridden method synchronized. - Results are undefined if db is closed by an auto-extension. */ - int xEntryPoint(sqlite3 db); + int call(sqlite3 db); } diff --git a/ext/jni/src/org/sqlite/jni/BusyHandler.java b/ext/jni/src/org/sqlite/jni/busy_handler_callback.java similarity index 65% rename from ext/jni/src/org/sqlite/jni/BusyHandler.java rename to ext/jni/src/org/sqlite/jni/busy_handler_callback.java index 8ce729c904..60f08e531c 100644 --- a/ext/jni/src/org/sqlite/jni/BusyHandler.java +++ b/ext/jni/src/org/sqlite/jni/busy_handler_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -13,33 +13,35 @@ */ package org.sqlite.jni; + /** - Callback proxy for use with sqlite3_busy_handler(). + Callback for use with sqlite3_busy_handler() */ -public abstract class BusyHandler { +public abstract class busy_handler_callback + implements sqlite3_callback_proxy, sqlite3_xDestroy_callback { /** - Must function as documented for the sqlite3_busy_handler() - callback argument, minus the (void*) argument the C-level - function requires. + Must function as documented for the C-level + sqlite3_busy_handler() callback argument, minus the (void*) + argument the C-level function requires. Any exceptions thrown by this callback are suppressed in order to retain the C-style API semantics of the JNI bindings. */ - public abstract int xCallback(int n); + public abstract int call(int n); /** Optionally override to perform any cleanup when this busy handler is destroyed. It is destroyed when: - The associated db is passed to sqlite3_close() or - sqlite3_close_v2(). + sqlite3_close_v2(). - sqlite3_busy_handler() is called to replace the handler, - whether it's passed a null handler or any other instance of - this class. + whether it's passed a null handler or any other instance of + this class. - sqlite3_busy_timeout() is called, which implicitly installs - a busy handler. + a busy handler. */ public void xDestroy(){} } diff --git a/ext/jni/src/org/sqlite/jni/Collation.java b/ext/jni/src/org/sqlite/jni/collation_callback.java similarity index 65% rename from ext/jni/src/org/sqlite/jni/Collation.java rename to ext/jni/src/org/sqlite/jni/collation_callback.java index a05b8ef9ef..972ef30eb1 100644 --- a/ext/jni/src/org/sqlite/jni/Collation.java +++ b/ext/jni/src/org/sqlite/jni/collation_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,15 +14,18 @@ package org.sqlite.jni; /** + Callback for use with sqlite3_create_collation() */ -public abstract class Collation { +public abstract class collation_callback + implements sqlite3_callback_proxy, sqlite3_xDestroy_callback { /** Must compare the given byte arrays using memcmp() semantics. */ - public abstract int xCompare(byte[] lhs, byte[] rhs); + public abstract int call(byte[] lhs, byte[] rhs); + /** - Called by SQLite when the collation is destroyed. If a Collation + Called by SQLite when the collation is destroyed. If a collation requires custom cleanup, override this method. */ - public void xDestroy() {} + public void xDestroy(){} } diff --git a/ext/jni/src/org/sqlite/jni/CollationNeeded.java b/ext/jni/src/org/sqlite/jni/collation_needed_callback.java similarity index 68% rename from ext/jni/src/org/sqlite/jni/CollationNeeded.java rename to ext/jni/src/org/sqlite/jni/collation_needed_callback.java index 85214a1d27..5ee5fcfd91 100644 --- a/ext/jni/src/org/sqlite/jni/CollationNeeded.java +++ b/ext/jni/src/org/sqlite/jni/collation_needed_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-30 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,15 +14,15 @@ package org.sqlite.jni; /** - Callback proxy for use with sqlite3_collation_needed(). + Callback for use with sqlite3_collation_needed(). */ -public interface CollationNeeded { +public interface collation_needed_callback extends sqlite3_callback_proxy { /** Has the same semantics as the C-level sqlite3_create_collation() callback. - If it throws, the exception message is passed on to the db and +

If it throws, the exception message is passed on to the db and the exception is suppressed. */ - int xCollationNeeded(sqlite3 db, int eTextRep, String collationName); + int call(sqlite3 db, int eTextRep, String collationName); } diff --git a/ext/jni/src/org/sqlite/jni/CommitHook.java b/ext/jni/src/org/sqlite/jni/commit_hook_callback.java similarity index 65% rename from ext/jni/src/org/sqlite/jni/CommitHook.java rename to ext/jni/src/org/sqlite/jni/commit_hook_callback.java index eaa75a0040..54c0876c21 100644 --- a/ext/jni/src/org/sqlite/jni/CommitHook.java +++ b/ext/jni/src/org/sqlite/jni/commit_hook_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - Callback proxy for use with sqlite3_commit_hook(). + Callback for use with sqlite3_commit_hook() */ -public interface CommitHook { +public interface commit_hook_callback extends sqlite3_callback_proxy { /** - Works as documented for the sqlite3_commit_hook() callback. - Must not throw. + Works as documented for the C-level sqlite3_commit_hook() + callback. Must not throw. */ - int xCommitHook(); + int call(); } diff --git a/ext/jni/src/org/sqlite/jni/SQLLog.java b/ext/jni/src/org/sqlite/jni/config_sqllog_callback.java similarity index 81% rename from ext/jni/src/org/sqlite/jni/SQLLog.java rename to ext/jni/src/org/sqlite/jni/config_sqllog_callback.java index c1bc0aab61..6772de2022 100644 --- a/ext/jni/src/org/sqlite/jni/SQLLog.java +++ b/ext/jni/src/org/sqlite/jni/config_sqllog_callback.java @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - A callback for use with sqlite3_config(SQLLog). + A callback for use with sqlite3_config(). */ -public interface SQLLog { +public interface config_sqllog_callback { /** Must function as described for sqlite3_config(SQLITE_CONFIG_SQLLOG) callback, with the slight signature change. */ - void xSqllog(sqlite3 db, String msg, int msgType ); + void call(sqlite3 db, String msg, int msgType ); } diff --git a/ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java b/ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java new file mode 100644 index 0000000000..5c55fa2b5d --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java @@ -0,0 +1,26 @@ +/* +** 2023-08-25 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file is part of the JNI bindings for the sqlite3 C API. +*/ +package org.sqlite.jni; + +/** + Callback for use with sqlite3_preupdate_hook(). +*/ +public interface preupdate_hook_callback extends sqlite3_callback_proxy { + /** + Must function as described for the C-level sqlite3_preupdate_hook() + callback. Must not throw. + */ + void call(sqlite3 db, int op, String dbName, String dbTable, + long iKey1, long iKey2 ); +} diff --git a/ext/jni/src/org/sqlite/jni/ProgressHandler.java b/ext/jni/src/org/sqlite/jni/progress_handler_callback.java similarity index 70% rename from ext/jni/src/org/sqlite/jni/ProgressHandler.java rename to ext/jni/src/org/sqlite/jni/progress_handler_callback.java index c806eebca0..e1b6415f06 100644 --- a/ext/jni/src/org/sqlite/jni/ProgressHandler.java +++ b/ext/jni/src/org/sqlite/jni/progress_handler_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,14 +14,14 @@ package org.sqlite.jni; /** - Callback proxy for use with sqlite3_progress_handler(). + Callback for use with sqlite3_progress_handler() */ -public interface ProgressHandler { +public interface progress_handler_callback extends sqlite3_callback_proxy { /** - Works as documented for the sqlite3_progress_handler() callback. + Works as documented for the C-level sqlite3_progress_handler() callback. If it throws, the exception message is passed on to the db and the exception is suppressed. */ - int xCallback(); + int call(); } diff --git a/ext/jni/src/org/sqlite/jni/RollbackHook.java b/ext/jni/src/org/sqlite/jni/rollback_hook_callback.java similarity index 65% rename from ext/jni/src/org/sqlite/jni/RollbackHook.java rename to ext/jni/src/org/sqlite/jni/rollback_hook_callback.java index 4ce3cb93e3..224c26c477 100644 --- a/ext/jni/src/org/sqlite/jni/RollbackHook.java +++ b/ext/jni/src/org/sqlite/jni/rollback_hook_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - Callback proxy for use with sqlite3_rollback_hook(). + Callback for use with sqlite3_rollback_hook() */ -public interface RollbackHook { +public interface rollback_hook_callback extends sqlite3_callback_proxy { /** - Works as documented for the sqlite3_rollback_hook() callback. - Must not throw. + Works as documented for the C-level sqlite3_rollback_hook() + callback. Must not throw. */ - void xRollbackHook(); + void call(); } diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java b/ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java new file mode 100644 index 0000000000..6200948cce --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java @@ -0,0 +1,34 @@ +/* +** 2023-08-25 +** +** 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; +/** + This marker interface exists soley for use as a documentation and + class-grouping tool. It should be applied to interfaces or + classes which have a call() method implementing some specific + callback interface on behalf of the C library. + +

Callbacks of this style follow a common naming convention: + +

1) They almost all have the same class or interface name as the + C function they are proxying a callback for, minus the sqlite3_ + prefix, plus a _callback suffix. e.g. sqlite3_busy_handler()'s + callback is named busy_handler_callback. Exceptions are made where + that would potentially be ambiguous, e.g. config_sqllog_callback + instead of config_callback because the sqlite3_config() interface + may need to support more callback types in the future. + +

2) They all have a call() method but its signature is + callback-specific. +*/ +public interface sqlite3_callback_proxy {} diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java b/ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java new file mode 100644 index 0000000000..48822af9e9 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java @@ -0,0 +1,28 @@ +/* +** 2023-07-21 +** +** 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 declares JNI bindings for the sqlite3 C API. +*/ +package org.sqlite.jni; + +/** + Callback for a hook called by SQLite when certain client-provided + state are destroyed. It gets its name from the pervasive use of + the symbol name xDestroy() for this purpose in the C API + documentation. +*/ +public interface sqlite3_xDestroy_callback { + /** + Must perform any cleanup required by this object. Must not + throw. + */ + public void xDestroy(); +} diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index e5107dca27..90974b71c9 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -609,9 +609,9 @@ public class SQLTester { } t.addTestScript(a); } - final AutoExtension ax = new AutoExtension() { + final auto_extension_callback ax = new auto_extension_callback() { private final SQLTester tester = t; - public int xEntryPoint(sqlite3 db){ + @Override public int call(sqlite3 db){ final String init = tester.getDbInitSql(); if( !init.isEmpty() ){ tester.execSql(db, true, ResultBufferMode.NONE, null, init); diff --git a/ext/jni/src/org/sqlite/jni/Tracer.java b/ext/jni/src/org/sqlite/jni/trace_v2_callback.java similarity index 51% rename from ext/jni/src/org/sqlite/jni/Tracer.java rename to ext/jni/src/org/sqlite/jni/trace_v2_callback.java index fa62edbfa7..c7358f97fd 100644 --- a/ext/jni/src/org/sqlite/jni/Tracer.java +++ b/ext/jni/src/org/sqlite/jni/trace_v2_callback.java @@ -1,5 +1,5 @@ /* -** 2023-07-22 +** 2023-08-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -16,18 +16,14 @@ package org.sqlite.jni; /** Callback proxy for use with sqlite3_trace_v2(). */ -public interface Tracer { +public interface trace_v2_callback extends sqlite3_callback_proxy { /** - Achtung: this interface is subject to change because the current - approach to mapping the passed-in natives back to Java is - uncomfortably quirky. - Called by sqlite3 for various tracing operations, as per sqlite3_trace_v2(). Note that this interface elides the 2nd argument to the native trace callback, as that role is better filled by instance-local state. - The 2nd argument to this function, if non-0, will be a native +

The 2nd argument to this function, if non-0, will be a native pointer to either an sqlite3 or sqlite3_stmt object, depending on the first argument (see below). Client code can pass it to the sqlite3 resp. sqlite3_stmt constructor to create a wrapping @@ -36,27 +32,27 @@ public interface Tracer { each call is comparatively expensive, and the objects are probably only seldom useful. - The final argument to this function is the "X" argument +

The final argument to this function is the "X" argument documented for sqlite3_trace() and sqlite3_trace_v2(). Its type depends on value of the first argument: - - SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a string - containing the prepared SQL, with one caveat: JNI only provides - us with the ability to convert that string to MUTF-8, as - opposed to standard UTF-8, and is cannot be ruled out that that - difference may be significant for certain inputs. The - alternative would be that we first convert it to UTF-16 before - passing it on, but there's no readily-available way to do that - without calling back into the db to peform the conversion - (which would lead to further tracing). +

- SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a string + containing the prepared SQL, with one caveat: JNI only provides + us with the ability to convert that string to MUTF-8, as + opposed to standard UTF-8, and is cannot be ruled out that that + difference may be significant for certain inputs. The + alternative would be that we first convert it to UTF-16 before + passing it on, but there's no readily-available way to do that + without calling back into the db to peform the conversion + (which would lead to further tracing). - - SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long - holding an approximate number of nanoseconds the statement took - to run. +

- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long + holding an approximate number of nanoseconds the statement took + to run. - - SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null. +

- SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null. - - SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null. +

- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null. */ - int xCallback(int traceFlag, Object pNative, Object pX); + int call(int traceFlag, Object pNative, Object pX); } diff --git a/ext/jni/src/org/sqlite/jni/update_hook_callback.java b/ext/jni/src/org/sqlite/jni/update_hook_callback.java new file mode 100644 index 0000000000..9193b703a0 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/update_hook_callback.java @@ -0,0 +1,25 @@ +/* +** 2023-08-25 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file is part of the JNI bindings for the sqlite3 C API. +*/ +package org.sqlite.jni; + +/** + Callback for use with sqlite3_update_hook(). +*/ +public interface update_hook_callback extends sqlite3_callback_proxy { + /** + Must function as described for the C-level sqlite3_update_hook() + callback. Must not throw. + */ + void call(int opId, String dbName, String tableName, long rowId); +} diff --git a/manifest b/manifest index 00e32259f3..bffb59c5a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C javadoc\sadditions. -D 2023-08-25T00:27:28.089 +C Replace\sall\sof\sthe\sJNI\sXyzHook/Handler\sclasses\swith\ssnake_cased\sones\swhich\sfollow\sunified\snaming\sconventions\sto\smake\sthem\seasier\sto\sapply. +D 2023-08-25T02:57:30.049 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,17 +233,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 642624b421936807eeed2fe7d0f9df898837ad0e4be4d9e32af06b0e5ef2c5b6 -F ext/jni/README.md 9d3caa2e038bfe5e8356a9e8ff66f93ca0647ac278339eeea296f10017f5cf35 +F ext/jni/GNUmakefile fb73086e6f8ee40f3c79e32b8e0a27725b2680f9cf8ae41bde2556eb8e3fad2a +F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c e4bdcd17e8f8e825f206e1c6ab5adf7f507d70b64b0f795c0cde141077fb68b2 -F ext/jni/src/c/sqlite3-jni.h 91c2eeee22d3594e6652d51edcce0cd94d258a768802fcfac13a78f900127b72 -F ext/jni/src/org/sqlite/jni/Authorizer.java e6cbc6605d4d254be892d5197dea6290180efb7c5dbb3060f8487563bb11bb65 -F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6 -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 ad67843b6dd1c06b6b0a1dc72887b7c48e2a98042fcf6cacf14d42444037eab8 -F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a +F ext/jni/src/c/sqlite3-jni.c 2e9cabce55f0a4c0a56b29bbaa367c133959e94a390ec0129b4054832d0421f9 +F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 10cb2e0eb4dc5cf4241a7ccc0442a680f14a3ce6ecbb726552f2b5e026e521e0 @@ -254,28 +248,36 @@ F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a0 F ext/jni/src/org/sqlite/jni/NotNull.java a4016df436f454e8d6786dd8421484edd6fc604043cf7fd8ec94cf922ba61604 F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 F ext/jni/src/org/sqlite/jni/OutputPointer.java 8d7b2c865217d3b7a47dccaddc4a24748463b770eecca90873402a38c0b2d819 -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 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb -F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java af2d1a673f48bed8bb39ad9f7fe79c3d904cb2c6c875254a0e8c7e7db6539725 -F ext/jni/src/org/sqlite/jni/Tester1.java e9b82c561ec8771b3e4ea537ebd7c16dd096928b6b8221967a4726104c7c6cb2 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cb848377b214562c968934dc1749c5493d42254cbc825e44e2d2c34085c2ec5e +F ext/jni/src/org/sqlite/jni/Tester1.java 3bfbcbf0720f9b71e461eb016b8bc30289a7ceaab1aa5da13e319fd303bf19fd F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 -F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d -F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee +F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe w ext/jni/src/org/sqlite/jni/Authorizer.java +F ext/jni/src/org/sqlite/jni/auto_extension_callback.java c8754ffabe3b75bd8f209bf1451d6a180ec52e99b11c11b2e3642f1891eb2635 w ext/jni/src/org/sqlite/jni/AutoExtension.java +F ext/jni/src/org/sqlite/jni/busy_handler_callback.java c9b046631646a9c123f26f7b0056f274d1e85c02475981603271f6feefa9bfee w ext/jni/src/org/sqlite/jni/BusyHandler.java +F ext/jni/src/org/sqlite/jni/collation_callback.java 44ddecceafd1a099027a06bb53cbe825613255398990f58a57fcc9d9fb4c2ce2 w ext/jni/src/org/sqlite/jni/Collation.java +F ext/jni/src/org/sqlite/jni/collation_needed_callback.java 0d5cbac245db9ff22b67c92c06f2e31ed557cd018f1c4670ae970e6f16f22cee w ext/jni/src/org/sqlite/jni/CollationNeeded.java +F ext/jni/src/org/sqlite/jni/commit_hook_callback.java 88462783826026e61e522d9aae7a9b4cb0c30f7d56519e08a5de42213a0087bc w ext/jni/src/org/sqlite/jni/CommitHook.java +F ext/jni/src/org/sqlite/jni/config_sqllog_callback.java d8b9b4e0f9a522fd40a88b4f9f87308fff1be255523ad6cff8493bf3bbca2ec8 w ext/jni/src/org/sqlite/jni/SQLLog.java F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 +F ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java 2bcc61a9320a7af6be36e5a814d133dd610d8ead79622efd84e7444aabe25f6c w ext/jni/src/org/sqlite/jni/PreUpdateHook.java +F ext/jni/src/org/sqlite/jni/progress_handler_callback.java eae32bd36639b12552becf82a0481bb4c09d22655920007b62e49130ce97a850 w ext/jni/src/org/sqlite/jni/ProgressHandler.java +F ext/jni/src/org/sqlite/jni/rollback_hook_callback.java 25663dbad4f9da50019d0c68fc815d31155a04eaeb293f27fdc6f9b20a424760 w ext/jni/src/org/sqlite/jni/RollbackHook.java F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc +F ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java 4b3369faab47d787594c0544000dbac751d69641cac866f82f9be0e13c4b2ce5 F ext/jni/src/org/sqlite/jni/sqlite3_context.java dca23e54f368f8ea37c112c1d2f74dc9522d5da2fdf67d6fd6b2ec9603d8514c F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bc3d6797a2f6cb7d443a0b72af84e5a45e0416b96af52e432d28e123db1970c3 +F ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java 90470ef3e901e8f4863adacf361b0afcd5c7633166ed6c747630a30659224c20 +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java e560303ada834363b615e5863050d1488bf5c83f0627b966fb1a0a6a4355029f F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e +F ext/jni/src/org/sqlite/jni/trace_v2_callback.java b3365dbfa1c9b0d18541ae530055a8ff55fc5b8494d30c03a12eafd9c8cb4e8c w ext/jni/src/org/sqlite/jni/Tracer.java +F ext/jni/src/org/sqlite/jni/update_hook_callback.java 616dbc9f99bdfbde190af3d8a44a8ad418fdc5f8c63acb0a0d679bd063848da8 w ext/jni/src/org/sqlite/jni/UpdateHook.java F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 @@ -2098,8 +2100,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 7232b033954fae40df3db43e489e0e5a703c03308f500a1ae36fd9d707632d7f -R 2acb197dd02e88e8595f80978f7eae58 +P bedf33d403677d243a1505ce549166850ab55671700b143278db5feb84883ab3 +R 3d4d16fca03c716fac15f71f35ecf6fd U stephan -Z c7c0446afbe0f68b470086328d8c934a +Z 276c090c642360abc3be58550d893d5e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 760e3ea188..adc5380a67 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bedf33d403677d243a1505ce549166850ab55671700b143278db5feb84883ab3 \ No newline at end of file +76e62a381249b3b4262b22bdffe7fc2816c820115c9df266956ab8817b127aca \ No newline at end of file From aec1361a2b58ee9afcbf96e92b4e38c10b6d9e70 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 04:02:33 +0000 Subject: [PATCH 055/422] Javadoc and internal API cleanups. FossilOrigin-Name: 10a43cba9bb2c23b5f31b31fcac81be34acec1940cca47d0b8e5a5ccbf1aa8eb --- ext/jni/src/c/sqlite3-jni.c | 377 +++++++++--------- ext/jni/src/org/sqlite/jni/OutputPointer.java | 56 +-- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 2 +- .../src/org/sqlite/jni/trace_v2_callback.java | 25 +- manifest | 40 +- manifest.uuid | 2 +- 6 files changed, 245 insertions(+), 257 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 133a0edb88..757d813a04 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -188,13 +188,13 @@ #define S3JniExceptionIgnore S3JniIfThrew S3JniExceptionClear #define S3JniExceptionWarnIgnore \ S3JniIfThrew {S3JniExceptionReport; S3JniExceptionClear;}(void)0 -#define EXCEPTION_WARN_CALLBACK_THREW(STR) \ +#define S3JniExceptionWarnCallbackThrew(STR) \ MARKER(("WARNING: " STR " MUST NOT THROW.\n")); \ (*env)->ExceptionDescribe(env) /** To be used for cases where we're _really_ not expecting an exception, e.g. looking up well-defined Java class members. */ -#define EXCEPTION_IS_FATAL(MSG) S3JniIfThrew {\ +#define S3JniExceptionIsFatal(MSG) S3JniIfThrew {\ S3JniExceptionReport; S3JniExceptionClear; \ (*env)->FatalError(env, MSG); \ } @@ -211,19 +211,19 @@ #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ) #define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) /* Helpers for Java value reference management. */ -static inline jobject new_global_ref(JNIEnv * const env, jobject const v){ +static inline jobject s3jni_ref_global(JNIEnv * const env, jobject const v){ return v ? (*env)->NewGlobalRef(env, v) : NULL; } -static inline void delete_global_ref(JNIEnv * const env, jobject const v){ +static inline void s3jni_unref_global(JNIEnv * const env, jobject const v){ if( v ) (*env)->DeleteGlobalRef(env, v); } -static inline void delete_local_ref(JNIEnv * const env, jobject const v){ +static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ if( v ) (*env)->DeleteLocalRef(env, v); } -#define REF_G(VAR) new_global_ref(env, (VAR)) -#define REF_L(VAR) (*env)->NewLocalRef(env, VAR) -#define UNREF_G(VAR) delete_global_ref(env,(VAR)) -#define UNREF_L(VAR) delete_local_ref(env,(VAR)) +#define S3JniRefGlobal(VAR) s3jni_ref_global(env, (VAR)) +#define S3JniRefLocal(VAR) (*env)->NewLocalRef(env, (VAR)) +#define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR)) +#define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) /** Keys for use with S3JniGlobal_nph_cache(). @@ -611,12 +611,28 @@ static void * s3jni_malloc(JNIEnv * const env, size_t n){ } /* -** Fetches the S3JniGlobal.envCache row for the given env, allocing -** a row if needed. When a row is allocated, its state is initialized -** insofar as possible. Calls (*env)->FatalError() if allocation of -** an entry fails. That's hypothetically possible but "shouldn't happen." +** Returns the current JNIEnv object. Fails fatally if it cannot find +** the object. */ -static S3JniEnv * S3JniGlobal_env_cache(JNIEnv * const env){ +static JNIEnv * s3jni_env(void){ + JNIEnv * env = 0; + if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, + JNI_VERSION_1_8) ){ + fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); + abort(); + } + return env; +} +/* Declares local var env = s3jni_env(). */ +#define LocalJniGetEnv JNIEnv * const env = s3jni_env() + +/* +** Fetches the S3JniGlobal.envCache row for the given env, allocing a +** row if needed. When a row is allocated, its state is initialized +** 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){ struct S3JniEnv * row; S3JniMutex_Env_enter; row = SJG.envCache.aHead; @@ -689,22 +705,6 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, return jba; } -/* -** Returns the current JNIEnv object. Fails fatally if it cannot find -** it. -*/ -static JNIEnv * s3jni_get_env(void){ - JNIEnv * env = 0; - if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, - JNI_VERSION_1_8) ){ - fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); - abort(); - } - return env; -} -/* Declares local var env = s3jni_get_env(). */ -#define LocalJniGetEnv JNIEnv * const env = s3jni_get_env() - /* ** Uses the java.lang.String(byte[],Charset) constructor to create a ** new String from UTF-8 string z. n is the number of bytes to @@ -732,7 +732,7 @@ static jstring s3jni_utf8_to_jstring(JNIEnv * const env, if( jba ){ rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA, jba, SJG.g.oCharsetUtf8); - UNREF_L(jba); + S3JniUnrefLocal(jba); } } return rv; @@ -775,7 +775,7 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv); rv[nBa] = 0; } - UNREF_L(jba); + S3JniUnrefLocal(jba); return rv; } @@ -811,7 +811,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ char * zMsg; jclass const klazz = (*env)->GetObjectClass(env, jx); mid = (*env)->GetMethodID(env, klazz, "toString", "()Ljava/lang/String;"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew{ S3JniExceptionReport; S3JniExceptionClear; @@ -824,7 +824,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ return 0; } zMsg = s3jni_jstring_to_utf8(env, msg, 0); - UNREF_L(msg); + S3JniUnrefLocal(msg); return zMsg; } @@ -851,7 +851,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, zMsg = s3jni_exception_error_msg(env, ex); s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg); sqlite3_free(zMsg); - UNREF_L(ex); + S3JniUnrefLocal(ex); } return errCode; } @@ -868,12 +868,12 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ jclass const klazz = (*env)->GetObjectClass(env, jObj); jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); if( method ){ s3jni_incr( &SJG.metrics.nDestroy ); (*env)->CallVoidMethod(env, jObj, method); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("xDestroy() callback"); + S3JniExceptionWarnCallbackThrew("xDestroy() callback"); S3JniExceptionClear; } }else{ @@ -894,7 +894,7 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest if( doXDestroy && s->jObj ){ s3jni_call_xDestroy(env, s->jObj); } - UNREF_G(s->jObj); + S3JniUnrefGlobal(s->jObj); memset(s, 0, sizeof(*s)); } @@ -928,7 +928,7 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ UNHOOK(collationNeeded, 0); UNHOOK(busyHandler, 1); #undef UNHOOK - UNREF_G(s->jDb); + S3JniUnrefGlobal(s->jDb); memset(s, 0, sizeof(S3JniDb)); s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; @@ -948,7 +948,7 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ ** references the cache owns. Returns true if env was cached and false ** if it was not found in the cache. */ -static int S3JniGlobal_env_uncache(JNIEnv * const env){ +static int S3JniEnv_uncache(JNIEnv * const env){ struct S3JniEnv * row; S3JniMutex_Env_assertLocked; row = SJG.envCache.aHead; @@ -1008,8 +1008,8 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* if( !pNC->pRef ){ pNC->pRef = pRef; pNC->klazz = (*env)->FindClass(env, pRef->zName); - EXCEPTION_IS_FATAL("FindClass() unexpectedly threw"); - pNC->klazz = REF_G(pNC->klazz); + S3JniExceptionIsFatal("FindClass() unexpectedly threw"); + pNC->klazz = S3JniRefGlobal(pNC->klazz); } S3JniMutex_Nph_leave; } @@ -1026,7 +1026,7 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* S3JniMutex_Nph_enter; if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); - EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); + S3JniExceptionIsFatal("Code maintenance required: missing nativePointer field."); } S3JniMutex_Nph_leave; } @@ -1042,7 +1042,7 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, S3NphRef const* pRef){ (*env)->SetLongField(env, ppOut, NativePointerHolder_getField(env, pRef), (jlong)p); - EXCEPTION_IS_FATAL("Could not set NativePointerHolder.nativePointer."); + S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer."); } /* @@ -1055,7 +1055,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const void * const rv = (void*)(*env)->GetLongField( env, pObj, NativePointerHolder_getField(env, pRef) ); - EXCEPTION_IS_FATAL("Cannot fetch NativePointerHolder.nativePointer."); + S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); return rv; }else{ return 0; @@ -1099,7 +1099,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, assert(!rv->pNext->pPrev); rv->pNext->pPrev = rv; } - rv->jDb = REF_G(jDb); + rv->jDb = S3JniRefGlobal(jDb); rv->pDb = pDb; } S3JniMutex_S3JniDb_leave; @@ -1142,7 +1142,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ static void S3JniAutoExtension_clear(JNIEnv * const env, S3JniAutoExtension * const ax){ if( ax->jObj ){ - UNREF_G(ax->jObj); + S3JniUnrefGlobal(ax->jObj); memset(ax, 0, sizeof(*ax)); } } @@ -1160,14 +1160,14 @@ static int S3JniAutoExtension_init(JNIEnv *const env, ax->midFunc = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;)I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniExceptionWarnIgnore; if( !ax->midFunc ){ MARKER(("Error getting call(sqlite3) from AutoExtension object.\n")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } - ax->jObj = REF_G(jAutoExt); + ax->jObj = S3JniRefGlobal(jAutoExt); return 0; } @@ -1192,7 +1192,7 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, S3JniMutex_Nph_enter; if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "value", zTypeSig); - EXCEPTION_IS_FATAL("setupOutputPointer() could not find OutputPointer.*.value"); + S3JniExceptionIsFatal("setupOutputPointer() could not find OutputPointer.*.value"); } S3JniMutex_Nph_leave; } @@ -1208,7 +1208,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int env, &S3NphRefs.OutputPointer_Int32, "I", jOut ); (*env)->SetIntField(env, jOut, setter, (jint)v); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); } /* @@ -1220,7 +1220,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon env, &S3NphRefs.OutputPointer_Int64, "J", jOut ); (*env)->SetLongField(env, jOut, setter, v); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); } /* @@ -1233,7 +1233,7 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut ); (*env)->SetObjectField(env, jOut, setter, jDb); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3.value"); } /* @@ -1247,7 +1247,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu "Lorg/sqlite/jni/sqlite3_stmt;", jOut ); (*env)->SetObjectField(env, jOut, setter, jStmt); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_stmt.value"); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -1262,7 +1262,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO "Lorg/sqlite/jni/sqlite3_value;", jOut ); (*env)->SetObjectField(env, jOut, setter, jValue); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_value.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_value.value"); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -1278,7 +1278,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, env, &S3NphRefs.OutputPointer_ByteArray, "[B", jOut ); (*env)->SetObjectField(env, jOut, setter, v); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.ByteArray.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.ByteArray.value"); } #endif @@ -1292,7 +1292,7 @@ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;", jOut ); (*env)->SetObjectField(env, jOut, setter, v); - EXCEPTION_IS_FATAL("Cannot set OutputPointer.String.value"); + S3JniExceptionIsFatal("Cannot set OutputPointer.String.value"); } #endif /* SQLITE_ENABLE_FTS5 */ @@ -1321,7 +1321,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; if( !jbaRhs ){ - UNREF_L(jbaLhs); + S3JniUnrefLocal(jbaLhs); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; } @@ -1331,15 +1331,15 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, ps->hooks.collation.midCallback, jbaLhs, jbaRhs); S3JniExceptionIgnore; - UNREF_L(jbaLhs); - UNREF_L(jbaRhs); + S3JniUnrefLocal(jbaLhs); + S3JniUnrefLocal(jbaRhs); return (int)rc; } /* Collation finalizer for use by the sqlite3 internals. */ static void CollationState_xDestroy(void *pArg){ S3JniDb * const ps = pArg; - S3JniHook_unref( s3jni_get_env(), &ps->hooks.collation, 1 ); + S3JniHook_unref( s3jni_env(), &ps->hooks.collation, 1 ); } /* @@ -1365,7 +1365,7 @@ typedef struct { static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); if( rv ){ - rv->jObj = jObj ? REF_G(jObj) : 0; + rv->jObj = jObj ? S3JniRefGlobal(jObj) : 0; } return rv; } @@ -1378,7 +1378,7 @@ static void ResultJavaVal_finalizer(void *v){ if( v ){ ResultJavaVal * const rv = (ResultJavaVal*)v; LocalJniGetEnv; - UNREF_G(rv->jObj); + S3JniUnrefGlobal(rv->jObj); sqlite3_free(rv); } } @@ -1404,12 +1404,12 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const S3JniMutex_Nph_enter; if( !pNC->midCtor ){ pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); - EXCEPTION_IS_FATAL("Cannot find constructor for class."); + S3JniExceptionIsFatal("Cannot find constructor for class."); } S3JniMutex_Nph_leave; } rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); - EXCEPTION_IS_FATAL("No-arg constructor threw."); + S3JniExceptionIsFatal("No-arg constructor threw."); s3jni_oom_check(rv); if( rv ) NativePointerHolder_set(env, rv, pNative, pRef); return rv; @@ -1470,7 +1470,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ jclass const klazz = (*env)->GetObjectClass(env, jObj); memset(s, 0, sizeof(S3JniUdf)); - s->jObj = REF_G(jObj); + s->jObj = S3JniRefGlobal(jObj); #define FGET(FuncName,FuncType,Field) \ s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncType); \ if( !s->Field ) (*env)->ExceptionClear(env) @@ -1480,7 +1480,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ FGET("xValue", zFV, jmidxValue); FGET("xInverse", zFSI, jmidxInverse); #undef FGET - UNREF_L(klazz); + S3JniUnrefLocal(klazz); if( s->jmidxFunc ) s->type = UDF_SCALAR; else if( s->jmidxStep && s->jmidxFinal ){ s->type = s->jmidxValue ? UDF_WINDOW : UDF_AGGREGATE; @@ -1496,7 +1496,7 @@ static void S3JniUdf_free(S3JniUdf * s){ if( env ){ //MARKER(("UDF cleanup: %s\n", s->zFuncName)); s3jni_call_xDestroy(env, s->jObj); - UNREF_G(s->jObj); + S3JniUnrefGlobal(s->jObj); } sqlite3_free(s->zFuncName); sqlite3_free(s); @@ -1539,15 +1539,15 @@ static int udf_args(JNIEnv *env, jobject jsv = new_sqlite3_value_wrapper(env, argv[i]); if( !jsv ) goto error_oom; (*env)->SetObjectArrayElement(env, ja, i, jsv); - UNREF_L(jsv)/*array has a ref*/; + S3JniUnrefLocal(jsv)/*array has a ref*/; } *jCx = jcx; *jArgv = ja; return 0; error_oom: sqlite3_result_error_nomem(cx); - UNREF_L(jcx); - UNREF_L(ja); + S3JniUnrefLocal(jcx); + S3JniUnrefLocal(ja); return SQLITE_NOMEM; } @@ -1592,7 +1592,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, (*env)->ExceptionDescribe( env ); S3JniExceptionClear; } - UNREF_L(ex); + S3JniUnrefLocal(ex); return rc; } @@ -1617,8 +1617,8 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, s->zFuncName, zFuncType); } } - UNREF_L(args.jcx); - UNREF_L(args.jargv); + S3JniUnrefLocal(args.jcx); + S3JniUnrefLocal(args.jargv); return rc; } @@ -1644,7 +1644,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, rc = udf_report_exception(env, isFinal, cx, s->zFuncName, zFuncType); } - UNREF_L(jcx); + S3JniUnrefLocal(jcx); return rc; } @@ -1812,9 +1812,10 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, JNIEnv * env = 0; S3JniDb * ps; S3JniEnv * jc; + if( 0==SJG.autoExt.nExt ) return 0; - env = s3jni_get_env(); - jc = S3JniGlobal_env_cache(env); + env = s3jni_env(); + jc = S3JniEnv_get(env); ps = jc->pdbOpening; if( !ps ){ MARKER(("Unexpected arrival of null S3JniDb in auto-extension runner.\n")); @@ -1836,19 +1837,19 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, if( i >= SJG.autoExt.nExt ){ go = 0; }else{ - ax.jObj = REF_L(SJG.autoExt.pExt[i].jObj); + ax.jObj = S3JniRefLocal(SJG.autoExt.pExt[i].jObj); ax.midFunc = SJG.autoExt.pExt[i].midFunc; } S3JniMutex_Ext_leave; if( ax.jObj ){ rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); - UNREF_L(ax.jObj); + S3JniUnrefLocal(ax.jObj); S3JniIfThrew { jthrowable const ex = (*env)->ExceptionOccurred(env); char * zMsg; S3JniExceptionClear; zMsg = s3jni_exception_error_msg(env, ex); - UNREF_L(ex); + S3JniUnrefLocal(ex); *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); sqlite3_free(zMsg); if( !rc ) rc = SQLITE_ERROR; @@ -2001,7 +2002,7 @@ static int s3jni_busy_handler(void* pState, int n){ rc = (*env)->CallIntMethod(env, ps->hooks.busyHandler.jObj, ps->hooks.busyHandler.midCallback, (jint)n); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("sqlite3_busy_handler() callback"); + S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_busy_handler() callback threw."); } @@ -2023,10 +2024,10 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( } jclass klazz; S3JniHook_unref(env, pHook, 1); - pHook->jObj = REF_G(jBusy); + pHook->jObj = S3JniRefGlobal(jBusy); klazz = (*env)->GetObjectClass(env, jBusy); pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniHook_unref(env, pHook, 0); rc = SQLITE_ERROR; @@ -2137,7 +2138,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, S3JniIfThrew{ s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); } - UNREF_L(jName); + S3JniUnrefLocal(jName); } } @@ -2158,7 +2159,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( return 0; } if( !jHook ){ - UNREF_G(pOld); + S3JniUnrefGlobal(pOld); memset(pHook, 0, sizeof(S3JniHook)); sqlite3_collation_needed(ps->pDb, 0, 0); return 0; @@ -2166,15 +2167,15 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( klazz = (*env)->GetObjectClass(env, jHook); xCallback = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, "Cannot not find matching callback on " "collation-needed hook object."); }else{ pHook->midCallback = xCallback; - pHook->jObj = REF_G(jHook); - UNREF_G(pOld); + pHook->jObj = S3JniRefGlobal(jHook); + S3JniUnrefGlobal(pOld); rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); } return rc; @@ -2279,8 +2280,8 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, } if( !jHook ){ if( pOld ){ - jobject tmp = REF_L(pOld); - UNREF_G(pOld); + jobject tmp = S3JniRefLocal(pOld); + S3JniUnrefGlobal(pOld); pOld = tmp; } memset(pHook, 0, sizeof(S3JniHook)); @@ -2291,7 +2292,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, klazz = (*env)->GetObjectClass(env, jHook); xCallback = (*env)->GetMethodID(env, klazz, "call", isCommit ? "()I" : "()V"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionReport; S3JniExceptionClear; @@ -2300,12 +2301,12 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, "hook object."); }else{ pHook->midCallback = xCallback; - pHook->jObj = REF_G(jHook); + pHook->jObj = S3JniRefGlobal(jHook); if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps); else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps); if( pOld ){ - jobject tmp = REF_L(pOld); - UNREF_G(pOld); + jobject tmp = S3JniRefLocal(pOld); + S3JniUnrefGlobal(pOld); pOld = tmp; } } @@ -2357,7 +2358,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int S3JniHook * const hook = &SJG.hooks.sqllog; if( !ps || !hook->jObj ) return; - jArg0 = REF_L(ps->jDb); + jArg0 = S3JniRefLocal(ps->jDb); switch( op ){ case 0: /* db opened */ case 1: /* SQL executed */ @@ -2371,11 +2372,11 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int } (*env)->CallVoidMethod(env, hook->jObj, hook->midCallback, jArg0, jArg1, op); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("SQLITE_CONFIG_SQLLOG callback"); + S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback"); S3JniExceptionClear; } - UNREF_L(jArg0); - UNREF_L(jArg1); + S3JniUnrefLocal(jArg0); + S3JniUnrefLocal(jArg1); } //! Requirement of SQLITE_CONFIG_SQLLOG. void sqlite3_init_sqllog(void){ @@ -2403,13 +2404,13 @@ S3JniApi(sqlite3_config(/* for SQLLOG */), "(Lorg/sqlite/jni/sqlite3;" "Ljava/lang/String;" "I)V"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); if( !hook->midCallback ){ S3JniExceptionWarnIgnore; S3JniHook_unref(env, hook, 0); return SQLITE_ERROR; } - hook->jObj = REF_G(jLog); + hook->jObj = S3JniRefGlobal(jLog); rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); if( rc ){ S3JniHook_unref(env, hook, 0); @@ -2445,9 +2446,9 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), klazz = (*env)->GetObjectClass(env, oCollation); pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew{ - UNREF_L(klazz); + S3JniUnrefLocal(klazz); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); } @@ -2457,7 +2458,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), CollationState_xDestroy); s3jni_mutf8_release(name, zName); if( 0==rc ){ - pHook->jObj = REF_G(oCollation); + pHook->jObj = S3JniRefGlobal(oCollation); }else{ S3JniHook_unref(env, pHook, 1); } @@ -2728,7 +2729,7 @@ S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){ int rc; S3JniMutex_Env_enter; - rc = S3JniGlobal_env_uncache(env); + rc = S3JniEnv_uncache(env); S3JniMutex_Env_leave; return rc ? JNI_TRUE : JNI_FALSE; } @@ -2746,7 +2747,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, S3JniDb ** ps){ int rc = 0; jobject jDb = 0; - *jc = S3JniGlobal_env_cache(env); + *jc = S3JniEnv_get(env); if( !*jc ){ rc = SQLITE_NOMEM; goto end; @@ -2767,7 +2768,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, if( *ps ){ (*jc)->pdbOpening = *ps; }else{ - UNREF_L(jDb); + S3JniUnrefLocal(jDb); rc = SQLITE_NOMEM; } end: @@ -2897,11 +2898,11 @@ end: NativePointerHolder_set(env, jStmt, pStmt, &S3NphRefs.sqlite3_stmt); }else{ /* Happens for comments and whitespace. */ - UNREF_L(jStmt); + S3JniUnrefLocal(jStmt); jStmt = 0; } }else{ - UNREF_L(jStmt); + S3JniUnrefLocal(jStmt); jStmt = 0; } #if 0 @@ -2980,13 +2981,13 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, (jint)opId, jDbName, jTable, (jlong)iKey1); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("sqlite3_(pre)update_hook() callback"); + S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback"); s3jni_db_exception(env, ps, 0, "sqlite3_(pre)update_hook() callback threw"); } } - UNREF_L(jDbName); - UNREF_L(jTable); + S3JniUnrefLocal(jDbName); + S3JniUnrefLocal(jTable); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -3040,8 +3041,8 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec } if( !jHook ){ if( pOld ){ - jobject tmp = REF_L(pOld); - UNREF_G(pOld); + jobject tmp = S3JniRefLocal(pOld); + S3JniUnrefGlobal(pOld); pOld = tmp; } memset(pHook, 0, sizeof(S3JniHook)); @@ -3062,7 +3063,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec "JJ)V") : (*env)->GetMethodID(env, klazz, "call", "(ILjava/lang/String;Ljava/lang/String;J)V"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3070,15 +3071,15 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec "(pre)update hook object."); }else{ pHook->midCallback = xCallback; - pHook->jObj = REF_G(jHook); + pHook->jObj = S3JniRefGlobal(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); + jobject tmp = S3JniRefLocal(pOld); + S3JniUnrefGlobal(pOld); pOld = tmp; } } @@ -3110,7 +3111,7 @@ static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb, jobject pWrap = new_sqlite3_value_wrapper(env, pOut); if( pWrap ){ OutputPointer_set_sqlite3_value(env, jOut, pWrap); - UNREF_L(pWrap); + S3JniUnrefLocal(pWrap); }else{ rc = SQLITE_NOMEM; } @@ -3155,7 +3156,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( jmethodID xCallback; if( n<1 || !jProgress ){ if( ps ){ - UNREF_G(ps->hooks.progress.jObj); + S3JniUnrefGlobal(ps->hooks.progress.jObj); memset(&ps->hooks.progress, 0, sizeof(ps->hooks.progress)); } sqlite3_progress_handler(ps->pDb, 0, 0, 0); @@ -3167,16 +3168,16 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( } klazz = (*env)->GetObjectClass(env, jProgress); xCallback = (*env)->GetMethodID(env, klazz, "call", "()I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on " "ProgressHandler object."); }else{ - UNREF_G(ps->hooks.progress.jObj); + S3JniUnrefGlobal(ps->hooks.progress.jObj); ps->hooks.progress.midCallback = xCallback; - ps->hooks.progress.jObj = REF_G(jProgress); + ps->hooks.progress.jObj = S3JniRefGlobal(jProgress); sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); } } @@ -3445,10 +3446,10 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniIfThrew{ rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); } - UNREF_L(s0); - UNREF_L(s1); - UNREF_L(s2); - UNREF_L(s3); + S3JniUnrefLocal(s0); + S3JniUnrefLocal(s1); + S3JniUnrefLocal(s2); + S3JniUnrefLocal(s3); return rc; } @@ -3472,7 +3473,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( } S3JniHook_unref(env, pHook, 0); } - pHook->jObj = REF_G(jHook); + pHook->jObj = S3JniRefGlobal(jHook); klazz = (*env)->GetObjectClass(env, jHook); pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", @@ -3482,7 +3483,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( "Ljava/lang/String;" "Ljava/lang/String;" ")I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniHook_unref(env, pHook, 0); return s3jni_db_error(ps->pDb, SQLITE_ERROR, @@ -3564,7 +3565,7 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( s3jni_reset_auto_extension(env); S3JniMutex_Env_enter; while( SJG.envCache.aHead ){ - S3JniGlobal_env_uncache( SJG.envCache.aHead->env ); + S3JniEnv_uncache( SJG.envCache.aHead->env ); } S3JniMutex_Env_leave; #if 0 @@ -3615,13 +3616,11 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ jobject jP = NULL /* the tracer's P arg */; jobject jPUnref = NULL /* potentially a local ref to jP */; int rc; - int createStmt = 0; switch( traceflag ){ case SQLITE_TRACE_STMT: jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); if( !jX ) return SQLITE_NOMEM; /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ - createStmt = 1; break; case SQLITE_TRACE_PROFILE: jX = (*env)->NewObject(env, SJG.g.cLong, SJG.g.ctorLong1, @@ -3629,10 +3628,8 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ // hmm. ^^^ (*pX) really is zero. // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); if( !jX ) return SQLITE_NOMEM; - createStmt = 1; break; case SQLITE_TRACE_ROW: - createStmt = 1; break; case SQLITE_TRACE_CLOSE: jP = ps->jDb; @@ -3641,10 +3638,11 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ assert(!"cannot happen - unkown trace flag"); return SQLITE_ERROR; } - if( createStmt ){ + if( !jP ){ + /* Create a new temporary sqlite3_stmt wrapper */ jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); if( !jP ){ - UNREF_L(jX); + S3JniUnrefLocal(jX); return SQLITE_NOMEM; } } @@ -3653,12 +3651,11 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ ps->hooks.trace.midCallback, (jint)traceflag, jP, jX); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("sqlite3_trace_v2() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_trace_v2() callback threw."); } - UNREF_L(jPUnref); - UNREF_L(jX); + S3JniUnrefLocal(jPUnref); + S3JniUnrefLocal(jX); return rc; } @@ -3678,14 +3675,14 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( pHook->midCallback = (*env)->GetMethodID( env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I" ); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionClear; S3JniHook_unref(env, pHook, 0); return s3jni_db_error(ps->pDb, SQLITE_ERROR, "Cannot not find matching xCallback() on Tracer object."); } - pHook->jObj = REF_G(jTracer); + pHook->jObj = S3JniRefGlobal(jTracer); return sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); } @@ -3894,8 +3891,8 @@ static void Fts5JniAux_free(Fts5JniAux * const s){ if( env ){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ s3jni_call_xDestroy(env, s->jObj); - UNREF_G(s->jObj); - UNREF_G(s->jUserData); + S3JniUnrefGlobal(s->jObj); + S3JniUnrefGlobal(s->jUserData); } sqlite3_free(s->zFuncName); sqlite3_free(s); @@ -3910,14 +3907,14 @@ static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ if( s ){ jclass klazz; memset(s, 0, sizeof(Fts5JniAux)); - s->jObj = REF_G(jObj); + s->jObj = S3JniRefGlobal(jObj); klazz = (*env)->GetObjectClass(env, jObj); s->jmid = (*env)->GetMethodID(env, klazz, "xFunction", "(Lorg/sqlite/jni/Fts5ExtensionApi;" "Lorg/sqlite/jni/Fts5Context;" "Lorg/sqlite/jni/sqlite3_context;" "[Lorg/sqlite/jni/sqlite3_value;)V"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew{ S3JniExceptionReport; S3JniExceptionClear; @@ -3951,9 +3948,9 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ S3JniMutex_Env_enter; if( pNPH ){ if( !SJG.fts5.jFtsExt ){ - SJG.fts5.jFtsExt = REF_G(pNPH); + SJG.fts5.jFtsExt = S3JniRefGlobal(pNPH); } - UNREF_L(pNPH); + S3JniUnrefLocal(pNPH); } S3JniMutex_Env_leave; } @@ -3986,7 +3983,7 @@ JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){ fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb); if( pApi ){ rv = new_fts5_api_wrapper(env, pApi); - ps->jFtsApi = rv ? REF_G(rv) : 0; + ps->jFtsApi = rv ? S3JniRefGlobal(rv) : 0; } } return rv; @@ -4022,7 +4019,7 @@ JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol, if( pz ){ if( jstr ){ OutputPointer_set_String(env, jOut, jstr); - UNREF_L(jstr)/*jOut has a reference*/; + S3JniUnrefLocal(jstr)/*jOut has a reference*/; }else{ rc = SQLITE_NOMEM; } @@ -4068,14 +4065,14 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, S3JniIfThrew{ udf_report_exception(env, 1, pCx, pAux->zFuncName, "xFunction"); } - UNREF_L(jpFts); - UNREF_L(jpCx); - UNREF_L(jArgv); + S3JniUnrefLocal(jpFts); + S3JniUnrefLocal(jpCx); + S3JniUnrefLocal(jArgv); return; error_oom: assert( !jArgv ); assert( !jpCx ); - UNREF_L(jpFts); + S3JniUnrefLocal(jpFts); sqlite3_result_error_nomem(pCx); return; } @@ -4099,7 +4096,7 @@ JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, rc = SQLITE_NOMEM; } if( 0==rc ){ - pAux->jUserData = jUserData ? REF_G(jUserData) : 0; + pAux->jUserData = jUserData ? S3JniRefGlobal(jUserData) : 0; pAux->zFuncName = sqlite3_mprintf("%s", zName) /* OOM here is non-fatal. Ignore it. */; } @@ -4123,7 +4120,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){ if( p->jObj ){ LocalJniGetEnv; s3jni_call_xDestroy(env, p->jObj); - UNREF_G(p->jObj); + S3JniUnrefGlobal(p->jObj); } sqlite3_free(x); } @@ -4136,8 +4133,8 @@ JniDeclFtsXA(jobject,xGetAuxdata)(JniArgsEnvObj,jobject jCtx, jboolean bClear){ if( pAux ){ if( bClear ){ if( pAux->jObj ){ - rv = REF_L(pAux->jObj); - UNREF_G(pAux->jObj); + rv = S3JniRefLocal(pAux->jObj); + S3JniUnrefGlobal(pAux->jObj); } /* Note that we do not call xDestroy() in this case. */ sqlite3_free(pAux); @@ -4181,9 +4178,9 @@ static void s3jni_phraseIter_NToJ(JNIEnv *const env, S3JniGlobalType * const g = &S3JniGlobal; assert(g->fts5.jPhraseIter.fidA); (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidA, (jlong)pSrc->a); - EXCEPTION_IS_FATAL("Cannot set Fts5PhraseIter.a field."); + S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.a field."); (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidB, (jlong)pSrc->b); - EXCEPTION_IS_FATAL("Cannot set Fts5PhraseIter.b field."); + S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.b field."); } /* Copy the 'a' and 'b' fields from Fts5PhraseIter object jIter to pDest. */ @@ -4193,10 +4190,10 @@ static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter, assert(g->fts5.jPhraseIter.fidA); pDest->a = (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidA); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); + S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field."); pDest->b = (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidB); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); + S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field."); } JniDeclFtsXA(jint,xPhraseFirst)(JniArgsEnvObj,jobject jCtx, jint iPhrase, @@ -4283,7 +4280,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, SJG.fts5.jFtsExt, s->jFcx); S3JniIfThrew{ - EXCEPTION_WARN_CALLBACK_THREW("xQueryPhrase() callback"); + S3JniExceptionWarnCallbackThrew("xQueryPhrase() callback"); S3JniExceptionClear; rc = SQLITE_ERROR; } @@ -4293,7 +4290,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase, jobject jCallback){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniEnv_get(env); struct s3jni_xQueryPhraseState s; jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; @@ -4305,8 +4302,8 @@ JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase, s.midCallback = (*env)->GetMethodID(env, klazz, "xCallback", "(Lorg.sqlite.jni.Fts5ExtensionApi;" "Lorg.sqlite.jni.Fts5Context;)I"); - UNREF_L(klazz); - EXCEPTION_IS_FATAL("Could not extract xQueryPhraseCallback.xCallback method."); + S3JniUnrefLocal(klazz); + S3JniExceptionIsFatal("Could not extract xQueryPhraseCallback.xCallback method."); return (jint)fext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s, s3jni_xQueryPhrase); } @@ -4338,7 +4335,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ } return SQLITE_NOMEM; } - pAux->jObj = REF_G(jAux); + pAux->jObj = S3JniRefGlobal(jAux); rc = fext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux, S3JniFts5AuxData_xDestroy); return rc; @@ -4355,7 +4352,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, jba = s->tok.jba; }else{ if(s->tok.jba){ - UNREF_L(s->tok.jba); + S3JniUnrefLocal(s->tok.jba); } s->tok.zPrev = z; s->tok.nPrev = nZ; @@ -4378,7 +4375,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; - S3JniEnv * const jc = S3JniGlobal_env_cache(env); + S3JniEnv * const jc = S3JniEnv_get(env); struct s3jni_xQueryPhraseState s; int rc = 0; jbyte * const pText = jCallback ? s3jni_jbytearray_bytes(jbaText) : 0; @@ -4392,14 +4389,14 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, s.jFcx = jFcx; s.fext = fext; s.midCallback = (*env)->GetMethodID(env, klazz, "xToken", "(I[BII)I"); - UNREF_L(klazz); + S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionReport; S3JniExceptionClear; s3jni_jbytearray_release(jbaText, pText); return SQLITE_ERROR; } - s.tok.jba = REF_L(jbaText); + s.tok.jba = S3JniRefLocal(jbaText); s.tok.zPrev = (const char *)pText; s.tok.nPrev = (int)nText; if( pRef == &S3NphRefs.Fts5ExtensionApi ){ @@ -4416,7 +4413,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, } if( s.tok.jba ){ assert( s.tok.zPrev ); - UNREF_L(s.tok.jba); + S3JniUnrefLocal(s.tok.jba); } s3jni_jbytearray_release(jbaText, pText); return (jint)rc; @@ -4709,47 +4706,47 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ } /* Grab references to various global classes and objects... */ - SJG.g.cObj = REF_G((*env)->FindClass(env,"java/lang/Object")); - EXCEPTION_IS_FATAL("Error getting reference to Object class."); + SJG.g.cObj = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Object")); + S3JniExceptionIsFatal("Error getting reference to Object class."); - SJG.g.cLong = REF_G((*env)->FindClass(env,"java/lang/Long")); - EXCEPTION_IS_FATAL("Error getting reference to Long class."); + SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long")); + S3JniExceptionIsFatal("Error getting reference to Long class."); SJG.g.ctorLong1 = (*env)->GetMethodID(env, SJG.g.cLong, "", "(J)V"); - EXCEPTION_IS_FATAL("Error getting reference to Long constructor."); + S3JniExceptionIsFatal("Error getting reference to Long constructor."); - SJG.g.cString = REF_G((*env)->FindClass(env,"java/lang/String")); - EXCEPTION_IS_FATAL("Error getting reference to String class."); + SJG.g.cString = S3JniRefGlobal((*env)->FindClass(env,"java/lang/String")); + S3JniExceptionIsFatal("Error getting reference to String class."); SJG.g.ctorStringBA = (*env)->GetMethodID(env, SJG.g.cString, "", "([BLjava/nio/charset/Charset;)V"); - EXCEPTION_IS_FATAL("Error getting reference to String(byte[],Charset) ctor."); + S3JniExceptionIsFatal("Error getting reference to String(byte[],Charset) ctor."); SJG.g.stringGetBytes = (*env)->GetMethodID(env, SJG.g.cString, "getBytes", "(Ljava/nio/charset/Charset;)[B"); - EXCEPTION_IS_FATAL("Error getting reference to String.getBytes(Charset)."); + S3JniExceptionIsFatal("Error getting reference to String.getBytes(Charset)."); { /* StandardCharsets.UTF_8 */ jfieldID fUtf8; klazz = (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); - EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets class."); + S3JniExceptionIsFatal("Error getting reference to StandardCharsets class."); fUtf8 = (*env)->GetStaticFieldID(env, klazz, "UTF_8", "Ljava/nio/charset/Charset;"); - EXCEPTION_IS_FATAL("Error getting StandardCharsets.UTF_8 field."); + S3JniExceptionIsFatal("Error getting StandardCharsets.UTF_8 field."); SJG.g.oCharsetUtf8 = - REF_G((*env)->GetStaticObjectField(env, klazz, fUtf8)); - EXCEPTION_IS_FATAL("Error getting reference to StandardCharsets.UTF_8."); - UNREF_L(klazz); + S3JniRefGlobal((*env)->GetStaticObjectField(env, klazz, fUtf8)); + S3JniExceptionIsFatal("Error getting reference to StandardCharsets.UTF_8."); + S3JniUnrefLocal(klazz); } #ifdef SQLITE_ENABLE_FTS5 klazz = (*env)->FindClass(env, "org/sqlite/jni/Fts5PhraseIter"); - EXCEPTION_IS_FATAL("Error getting reference to org.sqlite.jni.Fts5PhraseIter."); + S3JniExceptionIsFatal("Error getting reference to org.sqlite.jni.Fts5PhraseIter."); SJG.fts5.jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J"); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.a field."); + S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field."); SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "b", "J"); - EXCEPTION_IS_FATAL("Cannot get Fts5PhraseIter.b field."); - UNREF_L(klazz); + S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field."); + S3JniUnrefLocal(klazz); #endif SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); @@ -4772,7 +4769,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ for( pConfFlag = &aLimits[0]; pConfFlag->zName; ++pConfFlag ){ char const * zSig = (JTYPE_BOOL == pConfFlag->jtype) ? "Z" : "I"; fieldId = (*env)->GetStaticFieldID(env, jKlazz, pConfFlag->zName, zSig); - EXCEPTION_IS_FATAL("Missing an expected static member of the SQLite3Jni class."); + S3JniExceptionIsFatal("Missing an expected static member of the SQLite3Jni class."); assert(fieldId); switch( pConfFlag->jtype ){ case JTYPE_INT: @@ -4783,6 +4780,6 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ pConfFlag->value ? JNI_TRUE : JNI_FALSE); break; } - EXCEPTION_IS_FATAL("Seting a static member of the SQLite3Jni class failed."); + S3JniExceptionIsFatal("Seting a static member of the SQLite3Jni class failed."); } } diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index fd25b0c5cf..8a59de574b 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -50,13 +50,13 @@ public final class OutputPointer { */ public static final class sqlite3 { private org.sqlite.jni.sqlite3 value; - //! Initializes with a null value. + /** Initializes with a null value. */ public sqlite3(){value = null;} - //! Sets the current value to null. + /** Sets the current value to null. */ public void clear(){value = null;} - //! Returns the current value. + /** Returns the current value. */ public final org.sqlite.jni.sqlite3 get(){return value;} - //! Equivalent to calling get() then clear(). + /** Equivalent to calling get() then clear(). */ public final org.sqlite.jni.sqlite3 take(){ final org.sqlite.jni.sqlite3 v = value; value = null; @@ -72,13 +72,13 @@ public final class OutputPointer { */ public static final class sqlite3_stmt { private org.sqlite.jni.sqlite3_stmt value; - //! Initializes with a null value. + /** Initializes with a null value. */ public sqlite3_stmt(){value = null;} - //! Sets the current value to null. + /** Sets the current value to null. */ public void clear(){value = null;} - //! Returns the current value. + /** Returns the current value. */ public final org.sqlite.jni.sqlite3_stmt get(){return value;} - //! Equivalent to calling get() then clear(). + /** Equivalent to calling get() then clear(). */ public final org.sqlite.jni.sqlite3_stmt take(){ final org.sqlite.jni.sqlite3_stmt v = value; value = null; @@ -94,13 +94,13 @@ public final class OutputPointer { */ public static final class sqlite3_value { private org.sqlite.jni.sqlite3_value value; - //! Initializes with a null value. + /** Initializes with a null value. */ public sqlite3_value(){value = null;} - //! Sets the current value to null. + /** Sets the current value to null. */ public void clear(){value = null;} - //! Returns the current value. + /** Returns the current value. */ public final org.sqlite.jni.sqlite3_value get(){return value;} - //! Equivalent to calling get() then clear(). + /** Equivalent to calling get() then clear(). */ public final org.sqlite.jni.sqlite3_value take(){ final org.sqlite.jni.sqlite3_value v = value; value = null; @@ -118,13 +118,13 @@ public final class OutputPointer { consistency with the higher-level types. */ public int value; - //! Initializes with the value 0. + /** Initializes with the value 0. */ public Int32(){this(0);} - //! Initializes with the value v. + /** Initializes with the value v. */ public Int32(int v){value = v;} - //! Returns the current value. + /** Returns the current value. */ public final int get(){return value;} - //! Sets the current value to v. + /** Sets the current value to v. */ public final void set(int v){value = v;} } @@ -138,13 +138,13 @@ public final class OutputPointer { consistency with the higher-level types. */ public long value; - //! Initializes with the value 0. + /** Initializes with the value 0. */ public Int64(){this(0);} - //! Initializes with the value v. + /** Initializes with the value v. */ public Int64(long v){value = v;} - //! Returns the current value. + /** Returns the current value. */ public final long get(){return value;} - //! Sets the current value. + /** Sets the current value. */ public final void set(long v){value = v;} } @@ -158,13 +158,13 @@ public final class OutputPointer { consistency with the higher-level types. */ public java.lang.String value; - //! Initializes with a null value. + /** Initializes with a null value. */ public String(){this(null);} - //! Initializes with the value v. + /** Initializes with the value v. */ public String(java.lang.String v){value = v;} - //! Returns the current value. + /** Returns the current value. */ public final java.lang.String get(){return value;} - //! Sets the current value. + /** Sets the current value. */ public final void set(java.lang.String v){value = v;} } @@ -178,13 +178,13 @@ public final class OutputPointer { consistency with the higher-level types. */ public byte[] value; - //! Initializes with the value null. + /** Initializes with the value null. */ public ByteArray(){this(null);} - //! Initializes with the value v. + /** Initializes with the value v. */ public ByteArray(byte[] v){value = v;} - //! Returns the current value. + /** Returns the current value. */ public final byte[] get(){return value;} - //! Sets the current value. + /** Sets the current value. */ public final void set(byte[] v){value = v;} } } diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index aaf91f5331..9146e61cca 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1270,7 +1270,7 @@ public final class SQLite3Jni { /** A variant of sqlite3_value_java_object() which returns the fetched object cast to T if the object is an instance of the - given Class. It returns null in all other cases. + given Class, else it returns null. */ @SuppressWarnings("unchecked") public static T sqlite3_value_java_casted(@NotNull sqlite3_value v, diff --git a/ext/jni/src/org/sqlite/jni/trace_v2_callback.java b/ext/jni/src/org/sqlite/jni/trace_v2_callback.java index c7358f97fd..e5afd6c663 100644 --- a/ext/jni/src/org/sqlite/jni/trace_v2_callback.java +++ b/ext/jni/src/org/sqlite/jni/trace_v2_callback.java @@ -23,28 +23,19 @@ public interface trace_v2_callback extends sqlite3_callback_proxy { argument to the native trace callback, as that role is better filled by instance-local state. -

The 2nd argument to this function, if non-0, will be a native - pointer to either an sqlite3 or sqlite3_stmt object, depending on - the first argument (see below). Client code can pass it to the - sqlite3 resp. sqlite3_stmt constructor to create a wrapping - object, if necessary. This API does not do so by default because - tracing can be called frequently, creating such a wrapper for - each call is comparatively expensive, and the objects are - probably only seldom useful. + These callbacks may throw, in which case their exceptions are + converted to C-level error information. + +

The 2nd argument to this function, if non-null, will be a an + sqlite3 or sqlite3_stmt object, depending on the first argument + (see below).

The final argument to this function is the "X" argument documented for sqlite3_trace() and sqlite3_trace_v2(). Its type depends on value of the first argument: -

- SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a string - containing the prepared SQL, with one caveat: JNI only provides - us with the ability to convert that string to MUTF-8, as - opposed to standard UTF-8, and is cannot be ruled out that that - difference may be significant for certain inputs. The - alternative would be that we first convert it to UTF-16 before - passing it on, but there's no readily-available way to do that - without calling back into the db to peform the conversion - (which would lead to further tracing). +

- SQLITE_TRACE_STMT: pNative is a sqlite3_stmt. pX is a String + containing the prepared SQL.

- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long holding an approximate number of nanoseconds the statement took diff --git a/manifest b/manifest index bffb59c5a6..28dde1fc3f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Replace\sall\sof\sthe\sJNI\sXyzHook/Handler\sclasses\swith\ssnake_cased\sones\swhich\sfollow\sunified\snaming\sconventions\sto\smake\sthem\seasier\sto\sapply. -D 2023-08-25T02:57:30.049 +C Javadoc\sand\sinternal\sAPI\scleanups. +D 2023-08-25T04:02:33.692 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile fb73086e6f8ee40f3c79e32b8e0a27725b2680f9cf8ae41bde2556eb8e3fad2a F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 2e9cabce55f0a4c0a56b29bbaa367c133959e94a390ec0129b4054832d0421f9 +F ext/jni/src/c/sqlite3-jni.c 2f9115f672f2bf2bca5ab58bdf8ef592099b7d8b8c44ffdfc5576399a18a0666 F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 @@ -247,27 +247,27 @@ F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b5 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d F ext/jni/src/org/sqlite/jni/NotNull.java a4016df436f454e8d6786dd8421484edd6fc604043cf7fd8ec94cf922ba61604 F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 -F ext/jni/src/org/sqlite/jni/OutputPointer.java 8d7b2c865217d3b7a47dccaddc4a24748463b770eecca90873402a38c0b2d819 +F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cb848377b214562c968934dc1749c5493d42254cbc825e44e2d2c34085c2ec5e +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 92d0711f004327728704cc7ff33d9923be6c98dad50515093c96a99f34e13f9d F ext/jni/src/org/sqlite/jni/Tester1.java 3bfbcbf0720f9b71e461eb016b8bc30289a7ceaab1aa5da13e319fd303bf19fd F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee -F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe w ext/jni/src/org/sqlite/jni/Authorizer.java -F ext/jni/src/org/sqlite/jni/auto_extension_callback.java c8754ffabe3b75bd8f209bf1451d6a180ec52e99b11c11b2e3642f1891eb2635 w ext/jni/src/org/sqlite/jni/AutoExtension.java -F ext/jni/src/org/sqlite/jni/busy_handler_callback.java c9b046631646a9c123f26f7b0056f274d1e85c02475981603271f6feefa9bfee w ext/jni/src/org/sqlite/jni/BusyHandler.java -F ext/jni/src/org/sqlite/jni/collation_callback.java 44ddecceafd1a099027a06bb53cbe825613255398990f58a57fcc9d9fb4c2ce2 w ext/jni/src/org/sqlite/jni/Collation.java -F ext/jni/src/org/sqlite/jni/collation_needed_callback.java 0d5cbac245db9ff22b67c92c06f2e31ed557cd018f1c4670ae970e6f16f22cee w ext/jni/src/org/sqlite/jni/CollationNeeded.java -F ext/jni/src/org/sqlite/jni/commit_hook_callback.java 88462783826026e61e522d9aae7a9b4cb0c30f7d56519e08a5de42213a0087bc w ext/jni/src/org/sqlite/jni/CommitHook.java -F ext/jni/src/org/sqlite/jni/config_sqllog_callback.java d8b9b4e0f9a522fd40a88b4f9f87308fff1be255523ad6cff8493bf3bbca2ec8 w ext/jni/src/org/sqlite/jni/SQLLog.java +F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe +F ext/jni/src/org/sqlite/jni/auto_extension_callback.java c8754ffabe3b75bd8f209bf1451d6a180ec52e99b11c11b2e3642f1891eb2635 +F ext/jni/src/org/sqlite/jni/busy_handler_callback.java c9b046631646a9c123f26f7b0056f274d1e85c02475981603271f6feefa9bfee +F ext/jni/src/org/sqlite/jni/collation_callback.java 44ddecceafd1a099027a06bb53cbe825613255398990f58a57fcc9d9fb4c2ce2 +F ext/jni/src/org/sqlite/jni/collation_needed_callback.java 0d5cbac245db9ff22b67c92c06f2e31ed557cd018f1c4670ae970e6f16f22cee +F ext/jni/src/org/sqlite/jni/commit_hook_callback.java 88462783826026e61e522d9aae7a9b4cb0c30f7d56519e08a5de42213a0087bc +F ext/jni/src/org/sqlite/jni/config_sqllog_callback.java d8b9b4e0f9a522fd40a88b4f9f87308fff1be255523ad6cff8493bf3bbca2ec8 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 -F ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java 2bcc61a9320a7af6be36e5a814d133dd610d8ead79622efd84e7444aabe25f6c w ext/jni/src/org/sqlite/jni/PreUpdateHook.java -F ext/jni/src/org/sqlite/jni/progress_handler_callback.java eae32bd36639b12552becf82a0481bb4c09d22655920007b62e49130ce97a850 w ext/jni/src/org/sqlite/jni/ProgressHandler.java -F ext/jni/src/org/sqlite/jni/rollback_hook_callback.java 25663dbad4f9da50019d0c68fc815d31155a04eaeb293f27fdc6f9b20a424760 w ext/jni/src/org/sqlite/jni/RollbackHook.java +F ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java 2bcc61a9320a7af6be36e5a814d133dd610d8ead79622efd84e7444aabe25f6c +F ext/jni/src/org/sqlite/jni/progress_handler_callback.java eae32bd36639b12552becf82a0481bb4c09d22655920007b62e49130ce97a850 +F ext/jni/src/org/sqlite/jni/rollback_hook_callback.java 25663dbad4f9da50019d0c68fc815d31155a04eaeb293f27fdc6f9b20a424760 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc F ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java 4b3369faab47d787594c0544000dbac751d69641cac866f82f9be0e13c4b2ce5 F ext/jni/src/org/sqlite/jni/sqlite3_context.java dca23e54f368f8ea37c112c1d2f74dc9522d5da2fdf67d6fd6b2ec9603d8514c @@ -276,8 +276,8 @@ F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e8597 F ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java 90470ef3e901e8f4863adacf361b0afcd5c7633166ed6c747630a30659224c20 F ext/jni/src/org/sqlite/jni/tester/SQLTester.java e560303ada834363b615e5863050d1488bf5c83f0627b966fb1a0a6a4355029f F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e -F ext/jni/src/org/sqlite/jni/trace_v2_callback.java b3365dbfa1c9b0d18541ae530055a8ff55fc5b8494d30c03a12eafd9c8cb4e8c w ext/jni/src/org/sqlite/jni/Tracer.java -F ext/jni/src/org/sqlite/jni/update_hook_callback.java 616dbc9f99bdfbde190af3d8a44a8ad418fdc5f8c63acb0a0d679bd063848da8 w ext/jni/src/org/sqlite/jni/UpdateHook.java +F ext/jni/src/org/sqlite/jni/trace_v2_callback.java ec89134d679a9f7aebd2187025b6c58ab86e733dfecd44365ea656ac2dfae36a +F ext/jni/src/org/sqlite/jni/update_hook_callback.java 616dbc9f99bdfbde190af3d8a44a8ad418fdc5f8c63acb0a0d679bd063848da8 F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 @@ -2100,8 +2100,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 bedf33d403677d243a1505ce549166850ab55671700b143278db5feb84883ab3 -R 3d4d16fca03c716fac15f71f35ecf6fd +P 76e62a381249b3b4262b22bdffe7fc2816c820115c9df266956ab8817b127aca +R 0c4026c894e1986e8dbcbf3bbc899b49 U stephan -Z 276c090c642360abc3be58550d893d5e +Z 4928add900897f47fe6d81602626ebaa # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index adc5380a67..025b1b0d2d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -76e62a381249b3b4262b22bdffe7fc2816c820115c9df266956ab8817b127aca \ No newline at end of file +10a43cba9bb2c23b5f31b31fcac81be34acec1940cca47d0b8e5a5ccbf1aa8eb \ No newline at end of file From 5ecb51c260d49edbac05d7f0197af3d07560a188 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 04:27:17 +0000 Subject: [PATCH 056/422] Only build in the JNI-side metrics tracking in SQLITE_DEBUG builds. FossilOrigin-Name: 646e7fc3b5ba81c207f013c9a06781986138379f20e787320a811ba3ed5489dc --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 42 ++++++++++++++++++++++++++----------- manifest | 14 ++++++------- manifest.uuid | 2 +- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 5cd9c0dff0..366c6329e9 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -224,7 +224,7 @@ sqlite3-jni.dll.cflags = \ # include path for client-level code. ######################################################################## ifeq (1,$(enable.tester)) - sqlite3-jni.dll.cflags += -DS3JNI_ENABLE_SQLTester + sqlite3-jni.dll.cflags += -DSQLITE_JNI_ENABLE_SQLTester endif $(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE) cat $(sqlite3-jni.h.in) > $@ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 757d813a04..0d2adff5aa 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -410,14 +410,24 @@ struct S3JniAutoExtension { jmethodID midFunc /* xEntryPoint() callback */; }; +#if !defined(SQLITE_JNI_OMIT_METRICS) && !defined(SQLITE_JNI_ENABLE_METRICS) +# ifdef SQLITE_DEBUG +# define SQLITE_JNI_ENABLE_METRICS +# endif +#endif + /* ** If true, modifying S3JniGlobal.metrics is protected by a mutex, ** else it isn't. */ #ifdef SQLITE_DEBUG -#define S3JNI_METRICS_MUTEX 1 +# define S3JNI_METRICS_MUTEX 1 #else -#define S3JNI_METRICS_MUTEX 0 +# define S3JNI_METRICS_MUTEX 0 +#endif +#ifndef SQLITE_JNI_ENABLE_METRICS +# undef S3JNI_METRICS_MUTEX +# define S3JNI_METRICS_MUTEX 0 #endif /* @@ -490,6 +500,7 @@ struct S3JniGlobalType { } jPhraseIter; } fts5; #endif +#ifdef SQLITE_JNI_ENABLE_METRICS /* Internal metrics. */ struct { volatile unsigned envCacheHits; @@ -517,6 +528,7 @@ struct S3JniGlobalType { sqlite3_mutex * mutex; #endif } metrics; +#endif /* SQLITE_JNI_ENABLE_METRICS */ /** The list of bound auto-extensions (Java-side: org.sqlite.jni.auto_extension objects). @@ -534,7 +546,9 @@ static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal /* Increments *p, possibly protected by a mutex. */ -#if S3JNI_METRICS_MUTEX +#ifndef SQLITE_JNI_ENABLE_METRICS +#define s3jni_incr(PTR) +#elif S3JNI_METRICS_MUTEX static void s3jni_incr( volatile unsigned int * const p ){ sqlite3_mutex * const m = SJG.metrics.mutex; sqlite3_mutex_enter(m); @@ -557,7 +571,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ - ++SJG.metrics.nMutexEnv; \ + s3jni_incr(&SJG.metrics.nMutexEnv); \ SJG.envCache.locker = env #define S3JniMutex_Env_leave \ /*MARKER(("Leaving ENV mutex @%p %s.\n", env));*/ \ @@ -567,7 +581,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Ext_enter \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ - ++SJG.metrics.nMutexAutoExt + s3jni_incr( &SJG.metrics.nMutexAutoExt ) #define S3JniMutex_Ext_leave \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_leave( SJG.autoExt.mutex ) @@ -575,7 +589,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ - ++SJG.metrics.nMutexEnv2; \ + s3jni_incr( &SJG.metrics.nMutexEnv2 ); \ SJG.envCache.locker = env #define S3JniMutex_Nph_leave \ /*MARKER(("Leaving NPH mutex @%p %s.\n", env));*/ \ @@ -585,7 +599,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_S3JniDb_enter \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ assert( 0==SJG.perDb.locker ); \ - ++SJG.metrics.nMutexPerDb; \ + s3jni_incr( &SJG.metrics.nMutexPerDb ); \ SJG.perDb.locker = env; #define S3JniMutex_S3JniDb_leave \ /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ @@ -829,7 +843,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ } /* -** Extracts the current JNI exception, sets ps->pDb's error message to +** Extracts env's current exception, sets ps->pDb's error message to ** its message string, and clears the exception. If errCode is non-0, ** it is used as-is, else SQLITE_ERROR is assumed. If there's a ** problem extracting the exception's message, it's treated as @@ -853,7 +867,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, sqlite3_free(zMsg); S3JniUnrefLocal(ex); } - return errCode; + return errCode; } /* @@ -3816,6 +3830,8 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SO(S3JniGlobal); SO(S3JniAutoExtension); SO(S3JniUdf); +#undef SO +#ifdef SQLITE_JNI_ENABLE_METRICS printf("Cache info:\n"); printf("\tJNIEnv cache: %u allocs, %u misses, %u hits\n", SJG.metrics.envCacheAllocs, @@ -3840,7 +3856,9 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ #undef UDF printf("xDestroy calls across all callback types: %u\n", SJG.metrics.nDestroy); -#undef SO +#else + puts("Built without SQLITE_JNI_ENABLE_METRICS."); +#endif } //////////////////////////////////////////////////////////////////////// @@ -4444,7 +4462,7 @@ JniDeclFtsXA(jobject,xUserData)(JniArgsEnvObj,jobject jFcx){ // End of the main API bindings. Start of SQLTester bits... //////////////////////////////////////////////////////////////////////// -#ifdef S3JNI_ENABLE_SQLTester +#ifdef SQLITE_JNI_ENABLE_SQLTester typedef struct SQLTesterJni SQLTesterJni; struct SQLTesterJni { sqlite3_int64 nDup; @@ -4640,7 +4658,7 @@ Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JniArgsEnvClass){ sqlite3_auto_extension( (void(*)(void))SQLTester_auto_extension ); } -#endif /* S3JNI_ENABLE_SQLTester */ +#endif /* SQLITE_JNI_ENABLE_SQLTester */ //////////////////////////////////////////////////////////////////////// // End of SQLTester bindings. Start of lower-level bits. //////////////////////////////////////////////////////////////////////// diff --git a/manifest b/manifest index 28dde1fc3f..5bde21390e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Javadoc\sand\sinternal\sAPI\scleanups. -D 2023-08-25T04:02:33.692 +C Only\sbuild\sin\sthe\sJNI-side\smetrics\stracking\sin\sSQLITE_DEBUG\sbuilds. +D 2023-08-25T04:27:17.217 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 fb73086e6f8ee40f3c79e32b8e0a27725b2680f9cf8ae41bde2556eb8e3fad2a +F ext/jni/GNUmakefile 2fe04e7a7534a069ea8448f42e95bb5f8fc279ea3c9883598acedc99bbc254a7 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 2f9115f672f2bf2bca5ab58bdf8ef592099b7d8b8c44ffdfc5576399a18a0666 +F ext/jni/src/c/sqlite3-jni.c 969e59cdf9ad2394dcfc7b7fd3e259ff27cacfbe4946ccfdb8de6554745a32cb F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 @@ -2100,8 +2100,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 76e62a381249b3b4262b22bdffe7fc2816c820115c9df266956ab8817b127aca -R 0c4026c894e1986e8dbcbf3bbc899b49 +P 10a43cba9bb2c23b5f31b31fcac81be34acec1940cca47d0b8e5a5ccbf1aa8eb +R e838efcc000e7b30329ed875c329638a U stephan -Z 4928add900897f47fe6d81602626ebaa +Z 2ac71c6aa5db1c4809a7fefefbd39191 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 025b1b0d2d..77b93bbcf9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -10a43cba9bb2c23b5f31b31fcac81be34acec1940cca47d0b8e5a5ccbf1aa8eb \ No newline at end of file +646e7fc3b5ba81c207f013c9a06781986138379f20e787320a811ba3ed5489dc \ No newline at end of file From 35f9b1719be55c7756a84753634e1f3b40df439d Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 25 Aug 2023 11:06:26 +0000 Subject: [PATCH 057/422] Remove incorrect (but harmless) dependency on source code file "sessionfuzz-data1.db" from main.mk and Makefile.in. FossilOrigin-Name: 17d56c0207f63614b34ef3594d06602ab7a6e85604f3589b30aa79316f1744ee --- Makefile.in | 4 ++-- main.mk | 4 ++-- manifest | 16 ++++++++-------- manifest.uuid | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Makefile.in b/Makefile.in index e938cec526..6043c869fb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1291,11 +1291,11 @@ fulltestonly: $(TESTPROGS) fuzztest ./testfixture$(TEXE) $(TOP)/test/full.test # Fuzz testing -fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db +fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) ./fuzzcheck$(TEXE) $(FUZZDATA) ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db -valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) $(TOP)/test/sessionfuzz-data1.db +valgrindfuzz: fuzzcheck$(TEXT) $(FUZZDATA) sessionfuzz$(TEXE) valgrind ./fuzzcheck$(TEXE) --cell-size-check --limit-mem 10M $(FUZZDATA) valgrind ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db diff --git a/main.mk b/main.mk index 75730b1a60..d8c372430e 100644 --- a/main.mk +++ b/main.mk @@ -917,11 +917,11 @@ fulltestonly: $(TESTPROGS) fuzztest queryplantest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/permutations.test queryplanner $(TESTOPTS) -fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db +fuzztest: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) ./fuzzcheck$(EXE) $(FUZZDATA) ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db -valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) $(TOP)/test/sessionfuzz-data1.db +valgrindfuzz: fuzzcheck$(EXE) $(FUZZDATA) sessionfuzz$(EXE) valgrind ./fuzzcheck$(EXE) --cell-size-check --limit-mem 10M $(FUZZDATA) valgrind ./sessionfuzz run $(TOP)/test/sessionfuzz-data1.db diff --git a/manifest b/manifest index 5bde21390e..347cb57fcb 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Only\sbuild\sin\sthe\sJNI-side\smetrics\stracking\sin\sSQLITE_DEBUG\sbuilds. -D 2023-08-25T04:27:17.217 +C Remove\sincorrect\s(but\sharmless)\sdependency\son\ssource\scode\sfile\s"sessionfuzz-data1.db"\sfrom\smain.mk\sand\sMakefile.in. +D 2023-08-25T11:06:26.590 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 1e9105ffed727b1557ce59f0941c5d271e276ec00bc9823f03d77a89e131b918 +F Makefile.in 577177569fa57e613b74f60d66b03cd16e4326439dd62fd5b9690f5b83c34bf0 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc 26c2d196391a285c279adb10fd6001774d9b243af94b700b681e4a49cd476684 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 @@ -612,7 +612,7 @@ F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0 -F main.mk e18c03071dbb9da424212ba2a1ea331ae0e8f6d7d51283b7ad610c52ea11229c +F main.mk 5536159f62058714c972b5bd325c8d5ecd5c9b00b385dd2e1cbc17da70a711b2 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421 @@ -2100,8 +2100,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 10a43cba9bb2c23b5f31b31fcac81be34acec1940cca47d0b8e5a5ccbf1aa8eb -R e838efcc000e7b30329ed875c329638a -U stephan -Z 2ac71c6aa5db1c4809a7fefefbd39191 +P 646e7fc3b5ba81c207f013c9a06781986138379f20e787320a811ba3ed5489dc +R b08c9d8a6a2483e5f535446bff4911b8 +U dan +Z 116a690cb7924a8fc2ec464347e1c482 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 77b93bbcf9..1b78f9a395 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -646e7fc3b5ba81c207f013c9a06781986138379f20e787320a811ba3ed5489dc \ No newline at end of file +17d56c0207f63614b34ef3594d06602ab7a6e85604f3589b30aa79316f1744ee \ No newline at end of file From 0199669fa121fd1c525066aabaff00b14a2d1b23 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 11:32:56 +0000 Subject: [PATCH 058/422] Code restructuring. Force SQLITE_THREADSAFE in JNI builds for the time being, as threadsafe==0 leads to as-yet-mysterious JNI-level reference errors. FossilOrigin-Name: 5a099caa2c21bec647f0a521e7f5d0d1cc2f96d388d3d6c53d5ec80947f33e8d --- ext/jni/src/c/sqlite3-jni.c | 96 +++++++++++++++------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 4 + ext/jni/src/org/sqlite/jni/Tester1.java | 29 +++++-- manifest | 18 ++-- manifest.uuid | 2 +- 5 files changed, 102 insertions(+), 47 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 0d2adff5aa..4c9f5e6e5b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -105,6 +105,14 @@ # define SQLITE_THREADSAFE 1 #endif +/* +** 2023-08-25: initial attempts at running with SQLITE_THREADSAFE=0 +** lead to as-yet-uninvestigated bad reference errors from JNI. +*/ +#if SQLITE_THREADSAFE==0 +# error "This code currently requires SQLITE_THREADSAFE!=0." +#endif + /**********************************************************************/ /* SQLITE_USE_... */ #ifndef SQLITE_USE_URI @@ -445,11 +453,12 @@ struct S3JniGlobalType { ** threads. Caching a copy of the JavaVM object enables any thread ** with access to the cached object to get access to its own ** JNIEnv when necessary. + ** */ JavaVM * jvm; /* - ** Cache of Java refs/IDs for NativePointerHolder subclasses. - ** Initialized on demand. + ** Cache of Java refs and method IDs for NativePointerHolder + ** subclasses. Initialized on demand. */ S3JniNphClass nph[S3Jni_NphCache_size]; /* @@ -459,24 +468,23 @@ struct S3JniGlobalType { 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 members. */; + first-time inits of nph[] entries. */; void const * locker /* env mutex is held on this object's behalf. Used only for sanity checking. */; } envCache; + /* + ** Per-db state. This can move into the core library once we can tie + ** client-defined state to db handles there. + */ struct { - S3JniDb * aUsed /* Linked list of in-use instances */; + S3JniDb * aHead /* Linked list of in-use instances */; S3JniDb * aFree /* Linked list of free instances */; - sqlite3_mutex * mutex /* mutex for aUsed and aFree */; + sqlite3_mutex * mutex /* mutex for aHead and aFree */; void const * locker /* perDb mutex is held on this object's behalf. Unlike envCache.locker, we cannot always have this set to the current JNIEnv object. Used only for sanity checking. */; } perDb; -#ifdef SQLITE_ENABLE_SQLLOG - struct { - S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */; - } hooks; -#endif /* ** Refs to global classes and methods. Obtained during static init ** and never released. @@ -490,6 +498,22 @@ struct S3JniGlobalType { jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; } g /* refs to global Java state */; + /** + The list of bound auto-extensions (Java-side: + org.sqlite.jni.auto_extension objects). + */ + struct { + S3JniAutoExtension *pExt /* The auto-extension list. It is + maintained such that all active + entries are in the first contiguous + nExt array elements. */; + int nAlloc /* number of entries allocated for pExt, + as distinct from the number of active + entries. */; + int nExt /* number of active entries in pExt, all in the + first nExt'th array elements. */; + sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; + } autoExt; #ifdef SQLITE_ENABLE_FTS5 struct { volatile jobject jFtsExt /* Global ref to Java singleton for the @@ -500,6 +524,11 @@ struct S3JniGlobalType { } jPhraseIter; } fts5; #endif +#ifdef SQLITE_ENABLE_SQLLOG + struct { + S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */; + } hooks; +#endif #ifdef SQLITE_JNI_ENABLE_METRICS /* Internal metrics. */ struct { @@ -529,18 +558,6 @@ struct S3JniGlobalType { #endif } metrics; #endif /* SQLITE_JNI_ENABLE_METRICS */ - /** - The list of bound auto-extensions (Java-side: - org.sqlite.jni.auto_extension objects). - */ - struct { - S3JniAutoExtension *pExt /* Head of the auto-extension list */; - int nAlloc /* number of entries allocated for pExt, - as distinct from the number of active - entries. */; - int nExt /* number of active entries in pExt. */; - sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; - } autoExt; }; static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal @@ -550,23 +567,24 @@ static S3JniGlobalType S3JniGlobal = {}; #define s3jni_incr(PTR) #elif S3JNI_METRICS_MUTEX static void s3jni_incr( volatile unsigned int * const p ){ - sqlite3_mutex * const m = SJG.metrics.mutex; - sqlite3_mutex_enter(m); + sqlite3_mutex_enter(SJG.metrics.mutex); ++SJG.metrics.nMetrics; ++(*p); - sqlite3_mutex_leave(m); + sqlite3_mutex_leave(SJG.metrics.mutex); } #else #define s3jni_incr(PTR) ++(*(PTR)) #endif /* Helpers for working with specific mutexes. */ +#if SQLITE_THREADSAFE #define S3JniMutex_Env_assertLocked \ assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define S3JniMutex_Env_assertLocker \ assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) #define S3JniMutex_Env_assertNotLocker \ assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) + #define S3JniMutex_Env_enter \ S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ @@ -606,6 +624,19 @@ static void s3jni_incr( volatile unsigned int * const p ){ assert( env == SJG.perDb.locker ); \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) +#else /* SQLITE_THREADSAFE==0 */ +#define S3JniMutex_Env_assertLocked +#define S3JniMutex_Env_assertLocker +#define S3JniMutex_Env_assertNotLocker +#define S3JniMutex_Env_enter +#define S3JniMutex_Env_leave +#define S3JniMutex_Ext_enter +#define S3JniMutex_Ext_leave +#define S3JniMutex_Nph_enter +#define S3JniMutex_Nph_leave +#define S3JniMutex_S3JniDb_enter +#define S3JniMutex_S3JniDb_leave +#endif #define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env) static inline void s3jni_oom(JNIEnv * const env){ @@ -917,15 +948,17 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest */ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ if( s ){ +#if SQLITE_THREADSAFE assert( S3JniGlobal.perDb.locker == env ); +#endif assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); if(s->pNext) s->pNext->pPrev = s->pPrev; if(s->pPrev) s->pPrev->pNext = s->pNext; - else if(SJG.perDb.aUsed == s){ + else if(SJG.perDb.aHead == s){ assert(!s->pPrev); - SJG.perDb.aUsed = s->pNext; + SJG.perDb.aHead = s->pNext; } sqlite3_free( s->zMainDbName ); #define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) @@ -1107,8 +1140,8 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, } } if( rv ){ - rv->pNext = SJG.perDb.aUsed; - SJG.perDb.aUsed = rv; + rv->pNext = SJG.perDb.aHead; + SJG.perDb.aHead = rv; if( rv->pNext ){ assert(!rv->pNext->pPrev); rv->pNext->pPrev = rv; @@ -1135,7 +1168,7 @@ static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; if( jDb || pDb ){ S3JniMutex_S3JniDb_enter; - s = SJG.perDb.aUsed; + s = SJG.perDb.aHead; if( !pDb ){ assert( jDb ); pDb = PtrGet_sqlite3(jDb); @@ -4707,6 +4740,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ {"SQLITE_MAX_TRIGGER_DEPTH", JTYPE_INT, SQLITE_MAX_TRIGGER_DEPTH}, {"SQLITE_LIMIT_WORKER_THREADS", JTYPE_INT, SQLITE_LIMIT_WORKER_THREADS}, {"SQLITE_MAX_WORKER_THREADS", JTYPE_INT, SQLITE_MAX_WORKER_THREADS}, + {"SQLITE_THREADSAFE", JTYPE_INT, SQLITE_THREADSAFE}, {0,0} }; jfieldID fieldId; @@ -4714,7 +4748,7 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ const ConfigFlagEntry * pConfFlag; if( 0==sqlite3_threadsafe() ){ - (*env)->FatalError(env, "sqlite3 was not built with SQLITE_THREADSAFE."); + (*env)->FatalError(env, "sqlite3 currently requires SQLITE_THREADSAFE!=0."); return; } memset(&S3JniGlobal, 0, sizeof(S3JniGlobal)); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 9146e61cca..5a9e3c0805 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1318,6 +1318,10 @@ public final class SQLite3Jni { public static final String SQLITE_VERSION = sqlite3_libversion(); public static final String SQLITE_SOURCE_ID = sqlite3_sourceid(); + // Initialized at static init time to the build-time value of + // SQLITE_THREADSAFE. + public static int SQLITE_THREADSAFE = -1; + // access public static final int SQLITE_ACCESS_EXISTS = 0; public static final int SQLITE_ACCESS_READWRITE = 1; diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 7587cad884..1c452f720c 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1530,12 +1530,29 @@ public class Tester1 implements Runnable { final long timeStart = System.currentTimeMillis(); int nLoop = 0; - affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), - "Could not switch to single-thread mode." ); - affirm( 0==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), - "Could not switch to multithread mode." ); - affirm( 0==sqlite3_config( SQLITE_CONFIG_SERIALIZED ), - "Could not switch to serialized threading mode." ); + switch( SQLITE_THREADSAFE ){ /* Sanity checking */ + case 0: + affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), + "Could not switch to single-thread mode." ); + affirm( 0!=sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), + "Could switch to multithread mode." ); + affirm( 0!=sqlite3_config( SQLITE_CONFIG_SERIALIZED ), + "Could not switch to serialized threading mode." ); + outln("This is a single-threaded build. Not using threads."); + nThread = 1; + break; + case 1: + case 2: + affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), + "Could not switch to single-thread mode." ); + affirm( 0==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), + "Could not switch to multithread mode." ); + affirm( 0==sqlite3_config( SQLITE_CONFIG_SERIALIZED ), + "Could not switch to serialized threading mode." ); + break; + default: + affirm( false, "Unhandled SQLITE_THREADSAFE value." ); + } outln("libversion_number: ", sqlite3_libversion_number(),"\n", sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); diff --git a/manifest b/manifest index 347cb57fcb..88ce877e9e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sincorrect\s(but\sharmless)\sdependency\son\ssource\scode\sfile\s"sessionfuzz-data1.db"\sfrom\smain.mk\sand\sMakefile.in. -D 2023-08-25T11:06:26.590 +C Code\srestructuring.\sForce\sSQLITE_THREADSAFE\sin\sJNI\sbuilds\sfor\sthe\stime\sbeing,\sas\sthreadsafe==0\sleads\sto\sas-yet-mysterious\sJNI-level\sreference\serrors. +D 2023-08-25T11:32:56.589 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 2fe04e7a7534a069ea8448f42e95bb5f8fc279ea3c9883598acedc99bbc254a7 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 969e59cdf9ad2394dcfc7b7fd3e259ff27cacfbe4946ccfdb8de6554745a32cb +F ext/jni/src/c/sqlite3-jni.c 2d13c93fbf83feb2725922cc63e3157284848a435a9ff5faa8949d9157e1490f F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 @@ -250,8 +250,8 @@ F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 92d0711f004327728704cc7ff33d9923be6c98dad50515093c96a99f34e13f9d -F ext/jni/src/org/sqlite/jni/Tester1.java 3bfbcbf0720f9b71e461eb016b8bc30289a7ceaab1aa5da13e319fd303bf19fd +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 582242f27aea55bce0d3b88dd63bea03e4cb49a8c40950ef99a7e19d9307dfb9 +F ext/jni/src/org/sqlite/jni/Tester1.java c2e3d18229e9443c3e6cf54a150cfa832219ba63a0b17f7bef102e5e4b2b2a8d F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe @@ -2100,8 +2100,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 646e7fc3b5ba81c207f013c9a06781986138379f20e787320a811ba3ed5489dc -R b08c9d8a6a2483e5f535446bff4911b8 -U dan -Z 116a690cb7924a8fc2ec464347e1c482 +P 17d56c0207f63614b34ef3594d06602ab7a6e85604f3589b30aa79316f1744ee +R 0d04424045f1de02bc280794aad48b2b +U stephan +Z a29af0815bfb2df6f8dd8b5fbb60db4d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 1b78f9a395..8c6286a678 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -17d56c0207f63614b34ef3594d06602ab7a6e85604f3589b30aa79316f1744ee \ No newline at end of file +5a099caa2c21bec647f0a521e7f5d0d1cc2f96d388d3d6c53d5ec80947f33e8d \ No newline at end of file From a899186c8639b31d06d6aa4d53d9d7ff5cf9364e Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 12:29:59 +0000 Subject: [PATCH 059/422] Re-enable SQLITE_THREADSAFE=0 in the JNI build and add multitest target which builds and runs the tests in all 3 threading modes. FossilOrigin-Name: 6b1c816f6aae9face07ed7ead969257cf915c21ff9cdad9a21b7cf0e7496e09e --- ext/jni/GNUmakefile | 16 +++++++++++++++- ext/jni/src/c/sqlite3-jni.c | 6 ++++-- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 11 +++++++++-- ext/jni/src/org/sqlite/jni/Tester1.java | 5 +++-- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 6 files changed, 41 insertions(+), 17 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 366c6329e9..a7d2319325 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -161,6 +161,7 @@ $(sqlite3.h): $(MAKE) -C $(dir.top) sqlite3.c $(sqlite3.c): $(sqlite3.h) +opt.threadsafe ?= 1 SQLITE_OPT = \ -DSQLITE_ENABLE_RTREE \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ @@ -174,7 +175,7 @@ SQLITE_OPT = \ -DSQLITE_OMIT_LOAD_EXTENSION \ -DSQLITE_OMIT_DEPRECATED \ -DSQLITE_OMIT_SHARED_CACHE \ - -DSQLITE_THREADSAFE=1 \ + -DSQLITE_THREADSAFE=$(opt.threadsafe) \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ @@ -280,6 +281,17 @@ endif tester-ext: tester-local tester: tester-ext tests: test tester + +######################################################################## +# Build each SQLITE_THREADMODE variant and run all tests against them. +multitest: clean + $(MAKE) opt.threadsafe=0 tests clean + $(MAKE) opt.threadsafe=1 tests clean + $(MAKE) opt.threadsafe=2 tests clean + + +######################################################################## +# jar bundle... package.jar.in := $(abspath $(dir.src)/jar.in) CLEAN_FILES += $(package.jar.in) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) @@ -293,6 +305,8 @@ $(package.jar): $(CLASS_FILES) $(MAKEFILE) $(package.jar.in) jar: $(package.jar) +######################################################################## +# javadoc... dir.doc := $(dir.jni)/javadoc doc.index := $(dir.doc)/index.html $(doc.index): $(JAVA_FILES.main) $(MAKEFILE) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 4c9f5e6e5b..45e02f79eb 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -109,7 +109,7 @@ ** 2023-08-25: initial attempts at running with SQLITE_THREADSAFE=0 ** lead to as-yet-uninvestigated bad reference errors from JNI. */ -#if SQLITE_THREADSAFE==0 +#if 0 && SQLITE_THREADSAFE==0 # error "This code currently requires SQLITE_THREADSAFE!=0." #endif @@ -2383,7 +2383,7 @@ S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( return rc; } -S3JniApi(sqlite3_config(/*for a small subset of options.*/), +S3JniApi(sqlite3_config() /*for a small subset of options.*/, jint,1config__I)(JniArgsEnvClass, jint n){ switch( n ){ case SQLITE_CONFIG_SINGLETHREAD: @@ -4747,10 +4747,12 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ jclass klazz; const ConfigFlagEntry * pConfFlag; +#if 0 if( 0==sqlite3_threadsafe() ){ (*env)->FatalError(env, "sqlite3 currently requires SQLITE_THREADSAFE!=0."); return; } +#endif memset(&S3JniGlobal, 0, sizeof(S3JniGlobal)); if( (*env)->GetJavaVM(env, &SJG.jvm) ){ (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible."); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 5a9e3c0805..a4e63cb1e4 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -32,14 +32,21 @@ import java.lang.annotation.ElementType;

Only functions which materially differ from their C counterparts - are documented here. The C documentation is otherwise applicable - for these APIs: + are documented here, and only those material differences are + documented. The C documentation is otherwise applicable for these + APIs:

https://sqlite.org/c3ref/intro.html

A handful of Java-specific APIs have been added which are documented here. +

Though most of the {@code SQLITE_abc...} C macros represented by + this class are defined as final, a few are necessarily non-final + because they cannot be set until static class-level initialization + is run. Modifying them at runtime has no effect on the library but + may confuse any client-level code which uses them. +

Notes regarding Java's Modified UTF-8 vs standard UTF-8:

SQLite internally uses UTF-8 encoding, whereas Java natively uses diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 1c452f720c..50cecf6511 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1532,7 +1532,7 @@ public class Tester1 implements Runnable { int nLoop = 0; switch( SQLITE_THREADSAFE ){ /* Sanity checking */ case 0: - affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), + affirm( 0!=sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), "Could not switch to single-thread mode." ); affirm( 0!=sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), "Could switch to multithread mode." ); @@ -1555,7 +1555,8 @@ public class Tester1 implements Runnable { } outln("libversion_number: ", sqlite3_libversion_number(),"\n", - sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); + sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n", + "SQLITE_THREADSAFE=",SQLITE_THREADSAFE); outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ diff --git a/manifest b/manifest index 88ce877e9e..b05d08c480 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Code\srestructuring.\sForce\sSQLITE_THREADSAFE\sin\sJNI\sbuilds\sfor\sthe\stime\sbeing,\sas\sthreadsafe==0\sleads\sto\sas-yet-mysterious\sJNI-level\sreference\serrors. -D 2023-08-25T11:32:56.589 +C Re-enable\sSQLITE_THREADSAFE=0\sin\sthe\sJNI\sbuild\sand\sadd\smultitest\starget\swhich\sbuilds\sand\sruns\sthe\stests\sin\sall\s3\sthreading\smodes. +D 2023-08-25T12:29:59.869 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 2fe04e7a7534a069ea8448f42e95bb5f8fc279ea3c9883598acedc99bbc254a7 +F ext/jni/GNUmakefile 8ad3d9c7dbcedd24fac64298b0558c029cd8b6683211400e403b2e661ce99755 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 2d13c93fbf83feb2725922cc63e3157284848a435a9ff5faa8949d9157e1490f +F ext/jni/src/c/sqlite3-jni.c 7a93269d26256e6adbdcb6bcdcbba8d1e39d21602e145e8d07d23979b4530219 F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 @@ -250,8 +250,8 @@ F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 582242f27aea55bce0d3b88dd63bea03e4cb49a8c40950ef99a7e19d9307dfb9 -F ext/jni/src/org/sqlite/jni/Tester1.java c2e3d18229e9443c3e6cf54a150cfa832219ba63a0b17f7bef102e5e4b2b2a8d +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26d757281a6ba588f9146f15f89ab14c8dbe5367ac40bd23d6be6f8eef578f18 +F ext/jni/src/org/sqlite/jni/Tester1.java 449091bf0c225774733657df7fdbb5f357692aed0cb6e327ef94474c81a2d834 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe @@ -2100,8 +2100,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 17d56c0207f63614b34ef3594d06602ab7a6e85604f3589b30aa79316f1744ee -R 0d04424045f1de02bc280794aad48b2b +P 5a099caa2c21bec647f0a521e7f5d0d1cc2f96d388d3d6c53d5ec80947f33e8d +R fc74e77446b77e2790329feddc6b9d23 U stephan -Z a29af0815bfb2df6f8dd8b5fbb60db4d +Z 1c734de9d457eba0b72974da78927307 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8c6286a678..8d12423ec6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5a099caa2c21bec647f0a521e7f5d0d1cc2f96d388d3d6c53d5ec80947f33e8d \ No newline at end of file +6b1c816f6aae9face07ed7ead969257cf915c21ff9cdad9a21b7cf0e7496e09e \ No newline at end of file From ad818380cc0f5be4e714ba6882dd65a71ef4c32c Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 13:23:45 +0000 Subject: [PATCH 060/422] More Java API renaming for consistency. FossilOrigin-Name: 4e457778de189d8393e88f399dc521c8a5ad4ddd80a5fd69295bcb104fac5cbf --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 2 +- ext/jni/src/c/sqlite3-jni.h | 4 +-- ext/jni/src/org/sqlite/jni/Fts5.java | 8 ++++-- .../src/org/sqlite/jni/Fts5ExtensionApi.java | 2 +- .../org/sqlite/jni/busy_handler_callback.java | 2 +- .../org/sqlite/jni/collation_callback.java | 2 +- .../src/org/sqlite/jni/fts5_tokenizer.java | 2 +- ...y_callback.java => xDestroy_callback.java} | 2 +- manifest | 28 +++++++++---------- manifest.uuid | 2 +- 11 files changed, 29 insertions(+), 27 deletions(-) rename ext/jni/src/org/sqlite/jni/{sqlite3_xDestroy_callback.java => xDestroy_callback.java} (94%) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index a7d2319325..70c0f9648c 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -85,7 +85,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ sqlite3_context.java \ sqlite3_stmt.java \ sqlite3_value.java \ - sqlite3_xDestroy_callback.java \ + xDestroy_callback.java \ ) ifeq (1,$(enable.fts5)) JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 45e02f79eb..5fd8de14a2 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -4439,7 +4439,7 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, s.jCallback = jCallback; s.jFcx = jFcx; s.fext = fext; - s.midCallback = (*env)->GetMethodID(env, klazz, "xToken", "(I[BII)I"); + s.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I[BII)I"); S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniExceptionReport; diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 0381118d53..83c066d35b 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1963,7 +1963,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_Fts5ExtensionApi_xSetAuxdata /* * Class: org_sqlite_jni_Fts5ExtensionApi * Method: xTokenize - * Signature: (Lorg/sqlite/jni/Fts5Context;[BLorg/sqlite/jni/Fts5/xTokenizeCallback;)I + * Signature: (Lorg/sqlite/jni/Fts5Context;[BLorg/sqlite/jni/Fts5/xTokenize_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_Fts5ExtensionApi_xTokenize (JNIEnv *, jobject, jobject, jbyteArray, jobject); @@ -2021,7 +2021,7 @@ extern "C" { /* * Class: org_sqlite_jni_fts5_tokenizer * Method: xTokenize - * Signature: (Lorg/sqlite/jni/Fts5Tokenizer;I[BLorg/sqlite/jni/Fts5/xTokenizeCallback;)I + * Signature: (Lorg/sqlite/jni/Fts5Tokenizer;I[BLorg/sqlite/jni/Fts5/xTokenize_callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_fts5_1tokenizer_xTokenize (JNIEnv *, jobject, jobject, jint, jbyteArray, jobject); diff --git a/ext/jni/src/org/sqlite/jni/Fts5.java b/ext/jni/src/org/sqlite/jni/Fts5.java index 443a69a409..3135db96d3 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5.java +++ b/ext/jni/src/org/sqlite/jni/Fts5.java @@ -25,9 +25,11 @@ public final class Fts5 { /* Not used */ private Fts5(){} - //! Callback type for use with xTokenize() variants - public static interface xTokenizeCallback { - int xToken(int tFlags, byte[] txt, int iStart, int iEnd); + /** + Callback type for use with xTokenize() variants + */ + public static interface xTokenize_callback { + int call(int tFlags, byte[] txt, int iStart, int iEnd); } public static final int FTS5_TOKENIZE_QUERY = 0x0001; diff --git a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java index 205f110f41..625f8af3ae 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java +++ b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java @@ -78,7 +78,7 @@ public final class Fts5ExtensionApi extends NativePointerHolder { public native int xTokenize(@NotNull Fts5Tokenizer t, int tokFlags, @NotNull byte pText[], - @NotNull Fts5.xTokenizeCallback callback); + @NotNull Fts5.xTokenize_callback callback); // int (*xTokenize)(Fts5Tokenizer*, diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java b/ext/jni/src/org/sqlite/jni/xDestroy_callback.java similarity index 94% rename from ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java rename to ext/jni/src/org/sqlite/jni/xDestroy_callback.java index 48822af9e9..92086cc796 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java +++ b/ext/jni/src/org/sqlite/jni/xDestroy_callback.java @@ -19,7 +19,7 @@ package org.sqlite.jni; the symbol name xDestroy() for this purpose in the C API documentation. */ -public interface sqlite3_xDestroy_callback { +public interface xDestroy_callback { /** Must perform any cleanup required by this object. Must not throw. diff --git a/manifest b/manifest index b05d08c480..9de96e0da2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Re-enable\sSQLITE_THREADSAFE=0\sin\sthe\sJNI\sbuild\sand\sadd\smultitest\starget\swhich\sbuilds\sand\sruns\sthe\stests\sin\sall\s3\sthreading\smodes. -D 2023-08-25T12:29:59.869 +C More\sJava\sAPI\srenaming\sfor\sconsistency. +D 2023-08-25T13:23:45.671 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,14 +233,14 @@ 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 8ad3d9c7dbcedd24fac64298b0558c029cd8b6683211400e403b2e661ce99755 +F ext/jni/GNUmakefile 6f2290fa521da420c4a5c2eed1692b7a5fc2d53dfa74b8786517d1adb6078bfe F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 7a93269d26256e6adbdcb6bcdcbba8d1e39d21602e145e8d07d23979b4530219 -F ext/jni/src/c/sqlite3-jni.h 3d8cdacce26d20fd967d67a2e8539d38fc2e9fe13598147399db4b2c303a89c8 -F ext/jni/src/org/sqlite/jni/Fts5.java a45cd890202d72c3bfe8aea69b57b02b6dd588361af81d8b921954c37940b2f7 +F ext/jni/src/c/sqlite3-jni.c 69cb5df6d5ed65ca3870982b0d5d59c99c6580538a8929c3d4a0cbe837eea660 +F ext/jni/src/c/sqlite3-jni.h 288a37d1921fd5f1772f82ecb766a64bf20df7da685479ca84d3b95d9bcacd66 +F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 -F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 10cb2e0eb4dc5cf4241a7ccc0442a680f14a3ce6ecbb726552f2b5e026e521e0 +F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java d475009d654a16060101da6bdfd7f118d49ff61d910faf5a1d1fdb0a1def75f1 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 @@ -256,14 +256,14 @@ F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb29 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe F ext/jni/src/org/sqlite/jni/auto_extension_callback.java c8754ffabe3b75bd8f209bf1451d6a180ec52e99b11c11b2e3642f1891eb2635 -F ext/jni/src/org/sqlite/jni/busy_handler_callback.java c9b046631646a9c123f26f7b0056f274d1e85c02475981603271f6feefa9bfee -F ext/jni/src/org/sqlite/jni/collation_callback.java 44ddecceafd1a099027a06bb53cbe825613255398990f58a57fcc9d9fb4c2ce2 +F ext/jni/src/org/sqlite/jni/busy_handler_callback.java b43fc35e0f245ff532a4ab8bda6736f06858a45a67a21dbac5d6c237c5188d2c +F ext/jni/src/org/sqlite/jni/collation_callback.java cc8ff046e01a3cda57d9072e84bb000aaf207db605e31928a63c8b87e8ea06bd F ext/jni/src/org/sqlite/jni/collation_needed_callback.java 0d5cbac245db9ff22b67c92c06f2e31ed557cd018f1c4670ae970e6f16f22cee F ext/jni/src/org/sqlite/jni/commit_hook_callback.java 88462783826026e61e522d9aae7a9b4cb0c30f7d56519e08a5de42213a0087bc F ext/jni/src/org/sqlite/jni/config_sqllog_callback.java d8b9b4e0f9a522fd40a88b4f9f87308fff1be255523ad6cff8493bf3bbca2ec8 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c -F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b +F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 F ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java 2bcc61a9320a7af6be36e5a814d133dd610d8ead79622efd84e7444aabe25f6c F ext/jni/src/org/sqlite/jni/progress_handler_callback.java eae32bd36639b12552becf82a0481bb4c09d22655920007b62e49130ce97a850 @@ -273,11 +273,11 @@ F ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java 4b3369faab47d787594c054 F ext/jni/src/org/sqlite/jni/sqlite3_context.java dca23e54f368f8ea37c112c1d2f74dc9522d5da2fdf67d6fd6b2ec9603d8514c F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java 90470ef3e901e8f4863adacf361b0afcd5c7633166ed6c747630a30659224c20 F ext/jni/src/org/sqlite/jni/tester/SQLTester.java e560303ada834363b615e5863050d1488bf5c83f0627b966fb1a0a6a4355029f F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/org/sqlite/jni/trace_v2_callback.java ec89134d679a9f7aebd2187025b6c58ab86e733dfecd44365ea656ac2dfae36a F ext/jni/src/org/sqlite/jni/update_hook_callback.java 616dbc9f99bdfbde190af3d8a44a8ad418fdc5f8c63acb0a0d679bd063848da8 +F ext/jni/src/org/sqlite/jni/xDestroy_callback.java db2ffef90eccb445c886ef21a88250c455fa530ca135def49d5afa6c79f7f115 w ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 @@ -2100,8 +2100,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 5a099caa2c21bec647f0a521e7f5d0d1cc2f96d388d3d6c53d5ec80947f33e8d -R fc74e77446b77e2790329feddc6b9d23 +P 6b1c816f6aae9face07ed7ead969257cf915c21ff9cdad9a21b7cf0e7496e09e +R e0e5881b251feaa8a0cfeca08d550ed6 U stephan -Z 1c734de9d457eba0b72974da78927307 +Z c7e4ee2c8d85ebfdfb957345bd33818f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8d12423ec6..11aa18bfe7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6b1c816f6aae9face07ed7ead969257cf915c21ff9cdad9a21b7cf0e7496e09e \ No newline at end of file +4e457778de189d8393e88f399dc521c8a5ad4ddd80a5fd69295bcb104fac5cbf \ No newline at end of file From a5f09eb29dab2ff08a55572e70a35dc2a66e7760 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 14:17:36 +0000 Subject: [PATCH 061/422] Hopefully the last major API rename in the JNI bits: rename all lower_camel_case classes which do not have direct counterparts in the C API to UpperCamelCase, as that's more idiomatic. Java types which reflect C-style types are unaffected. FossilOrigin-Name: 8ca528006533ac152e9b322e421cf5d339414c77332d9836a9ff429b793d0d2d --- ext/jni/GNUmakefile | 26 +++---- ext/jni/src/c/sqlite3-jni.c | 2 +- ext/jni/src/c/sqlite3-jni.h | 28 +++---- ..._callback.java => AuthorizerCallback.java} | 2 +- ...llback.java => AutoExtensionCallback.java} | 2 +- ...callback.java => BusyHandlerCallback.java} | 4 +- ...collation_callback.java => Collation.java} | 4 +- ...back.java => CollationNeededCallback.java} | 2 +- ..._callback.java => CommitHookCallback.java} | 2 +- ...allback.java => ConfigSqllogCallback.java} | 6 +- ext/jni/src/org/sqlite/jni/NotNull.java | 8 +- ...llback.java => PreupdateHookCallback.java} | 2 +- ...back.java => ProgressHandlerCallback.java} | 2 +- ...allback.java => RollbackHookCallback.java} | 2 +- ext/jni/src/org/sqlite/jni/SQLFunction.java | 6 +- ...k_proxy.java => SQLite3CallbackProxy.java} | 16 ++-- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 74 +++++++++---------- ext/jni/src/org/sqlite/jni/Tester1.java | 42 +++++------ ..._v2_callback.java => TraceV2Callback.java} | 2 +- ..._callback.java => UpdateHookCallback.java} | 2 +- ...oy_callback.java => XDestroyCallback.java} | 2 +- .../src/org/sqlite/jni/sqlite3_context.java | 23 +++--- .../src/org/sqlite/jni/tester/SQLTester.java | 2 +- manifest | 56 +++++++------- manifest.uuid | 2 +- 25 files changed, 162 insertions(+), 157 deletions(-) rename ext/jni/src/org/sqlite/jni/{authorizer_callback.java => AuthorizerCallback.java} (91%) rename ext/jni/src/org/sqlite/jni/{auto_extension_callback.java => AutoExtensionCallback.java} (94%) rename ext/jni/src/org/sqlite/jni/{busy_handler_callback.java => BusyHandlerCallback.java} (92%) rename ext/jni/src/org/sqlite/jni/{collation_callback.java => Collation.java} (89%) rename ext/jni/src/org/sqlite/jni/{collation_needed_callback.java => CollationNeededCallback.java} (91%) rename ext/jni/src/org/sqlite/jni/{commit_hook_callback.java => CommitHookCallback.java} (89%) rename ext/jni/src/org/sqlite/jni/{config_sqllog_callback.java => ConfigSqllogCallback.java} (74%) rename ext/jni/src/org/sqlite/jni/{preupdate_hook_callback.java => PreupdateHookCallback.java} (90%) rename ext/jni/src/org/sqlite/jni/{progress_handler_callback.java => ProgressHandlerCallback.java} (90%) rename ext/jni/src/org/sqlite/jni/{rollback_hook_callback.java => RollbackHookCallback.java} (89%) rename ext/jni/src/org/sqlite/jni/{sqlite3_callback_proxy.java => SQLite3CallbackProxy.java} (62%) rename ext/jni/src/org/sqlite/jni/{trace_v2_callback.java => TraceV2Callback.java} (96%) rename ext/jni/src/org/sqlite/jni/{update_hook_callback.java => UpdateHookCallback.java} (90%) rename ext/jni/src/org/sqlite/jni/{xDestroy_callback.java => XDestroyCallback.java} (95%) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 70c0f9648c..35381d1cc5 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -59,33 +59,33 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ - authorizer_callback.java \ - auto_extension_callback.java \ - busy_handler_callback.java \ - collation_callback.java \ - collation_needed_callback.java \ - commit_hook_callback.java \ - config_sqllog_callback.java \ - preupdate_hook_callback.java \ - progress_handler_callback.java \ - rollback_hook_callback.java \ - trace_v2_callback.java \ - update_hook_callback.java \ + AuthorizerCallback.java \ + AutoExtensionCallback.java \ + BusyHandlerCallback.java \ + Collation.java \ + CollationNeededCallback.java \ + CommitHookCallback.java \ + ConfigSqllogCallback.java \ NativePointerHolder.java \ NotNull.java \ Nullable.java \ OutputPointer.java \ + PreupdateHookCallback.java \ + ProgressHandlerCallback.java \ ResultCode.java \ + RollbackHookCallback.java \ SQLFunction.java \ SQLite3Jni.java \ Tester1.java \ + TraceV2Callback.java \ + UpdateHookCallback.java \ ValueHolder.java \ + XDestroyCallback.java \ package-info.java \ sqlite3.java \ sqlite3_context.java \ sqlite3_stmt.java \ sqlite3_value.java \ - xDestroy_callback.java \ ) ifeq (1,$(enable.fts5)) JAVA_FILES.main += $(patsubst %,$(dir.src.jni)/%,\ diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 5fd8de14a2..c3cf36f752 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1847,7 +1847,7 @@ S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( ? (int)sizeof(void*) : 0)) : 0; - return (jlong)p / sizeof(void*); + return (jlong)p; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 83c066d35b..0e8dd4bcc0 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -782,7 +782,7 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1aggregate_1conte /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_auto_extension - * Signature: (Lorg/sqlite/jni/auto_extension_callback;)I + * Signature: (Lorg/sqlite/jni/AutoExtensionCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1auto_1extension (JNIEnv *, jclass, jobject); @@ -878,7 +878,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1zeroblob64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_busy_handler - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/busy_handler_callback;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/BusyHandlerCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1busy_1handler (JNIEnv *, jclass, jobject, jobject); @@ -894,7 +894,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1busy_1timeout /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_cancel_auto_extension - * Signature: (Lorg/sqlite/jni/auto_extension_callback;)Z + * Signature: (Lorg/sqlite/jni/AutoExtensionCallback;)Z */ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1cancel_1auto_1extension (JNIEnv *, jclass, jobject); @@ -1062,7 +1062,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1value /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_collation_needed - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/collation_needed_callback;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/CollationNeededCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1collation_1needed (JNIEnv *, jclass, jobject, jobject); @@ -1078,7 +1078,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1context_1db_1h /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_commit_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/commit_hook_callback;)Lorg/sqlite/jni/commit_hook_callback; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/CommitHookCallback;)Lorg/sqlite/jni/CommitHookCallback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1commit_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1110,15 +1110,15 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__I /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_config - * Signature: (Lorg/sqlite/jni/config_sqllog_callback;)I + * Signature: (Lorg/sqlite/jni/ConfigSqllogCallback;)I */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqlite_jni_config_1sqllog_1callback_2 +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqlite_jni_ConfigSqllogCallback_2 (JNIEnv *, jclass, jobject); /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_create_collation - * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/collation_callback;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/Collation;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1create_1collation (JNIEnv *, jclass, jobject, jstring, jint, jobject); @@ -1350,7 +1350,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1depth /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_preupdate_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/preupdate_hook_callback;)Lorg/sqlite/jni/preupdate_hook_callback; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/PreupdateHookCallback;)Lorg/sqlite/jni/PreupdateHookCallback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1374,7 +1374,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1old /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_progress_handler - * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/progress_handler_callback;)V + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/ProgressHandlerCallback;)V */ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1progress_1handler (JNIEnv *, jclass, jobject, jint, jobject); @@ -1550,7 +1550,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_rollback_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/rollback_hook_callback;)Lorg/sqlite/jni/rollback_hook_callback; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHookCallback;)Lorg/sqlite/jni/RollbackHookCallback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook (JNIEnv *, jclass, jobject, jobject); @@ -1558,7 +1558,7 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_set_authorizer - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/authorizer_callback;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/AuthorizerCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1set_1authorizer (JNIEnv *, jclass, jobject, jobject); @@ -1646,7 +1646,7 @@ JNIEXPORT jlong JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1total_1changes64 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_trace_v2 - * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/trace_v2_callback;)I + * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/TraceV2Callback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1trace_1v2 (JNIEnv *, jclass, jobject, jint, jobject); @@ -1654,7 +1654,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1trace_1v2 /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_update_hook - * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/update_hook_callback;)Lorg/sqlite/jni/update_hook_callback; + * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/UpdateHookCallback;)Lorg/sqlite/jni/UpdateHookCallback; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1update_1hook (JNIEnv *, jclass, jobject, jobject); diff --git a/ext/jni/src/org/sqlite/jni/authorizer_callback.java b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java similarity index 91% rename from ext/jni/src/org/sqlite/jni/authorizer_callback.java rename to ext/jni/src/org/sqlite/jni/AuthorizerCallback.java index 6817c7e115..7eefe9f7db 100644 --- a/ext/jni/src/org/sqlite/jni/authorizer_callback.java +++ b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_set_authorizer(). */ -public interface authorizer_callback extends sqlite3_callback_proxy { +public interface AuthorizerCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_set_authorizer() callback. diff --git a/ext/jni/src/org/sqlite/jni/auto_extension_callback.java b/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java similarity index 94% rename from ext/jni/src/org/sqlite/jni/auto_extension_callback.java rename to ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java index 7bb1a757ef..11786c95e9 100644 --- a/ext/jni/src/org/sqlite/jni/auto_extension_callback.java +++ b/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java @@ -17,7 +17,7 @@ package org.sqlite.jni; A callback for use with the sqlite3_auto_extension() family of APIs. */ -public interface auto_extension_callback extends sqlite3_callback_proxy { +public interface AutoExtensionCallback extends SQLite3CallbackProxy { /** Must function as described for a C-level sqlite3_auto_extension() callback, with the caveat that the diff --git a/ext/jni/src/org/sqlite/jni/busy_handler_callback.java b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java similarity index 92% rename from ext/jni/src/org/sqlite/jni/busy_handler_callback.java rename to ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java index d636112bf7..e8740865e4 100644 --- a/ext/jni/src/org/sqlite/jni/busy_handler_callback.java +++ b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java @@ -17,8 +17,8 @@ package org.sqlite.jni; /** Callback for use with sqlite3_busy_handler() */ -public abstract class busy_handler_callback - implements sqlite3_callback_proxy, xDestroy_callback { +public abstract class BusyHandlerCallback + implements SQLite3CallbackProxy, XDestroyCallback { /** Must function as documented for the C-level sqlite3_busy_handler() callback argument, minus the (void*) diff --git a/ext/jni/src/org/sqlite/jni/collation_callback.java b/ext/jni/src/org/sqlite/jni/Collation.java similarity index 89% rename from ext/jni/src/org/sqlite/jni/collation_callback.java rename to ext/jni/src/org/sqlite/jni/Collation.java index db168ba480..cf64421531 100644 --- a/ext/jni/src/org/sqlite/jni/collation_callback.java +++ b/ext/jni/src/org/sqlite/jni/Collation.java @@ -16,8 +16,8 @@ package org.sqlite.jni; /** Callback for use with sqlite3_create_collation() */ -public abstract class collation_callback - implements sqlite3_callback_proxy, xDestroy_callback { +public abstract class Collation + implements SQLite3CallbackProxy, XDestroyCallback { /** Must compare the given byte arrays using memcmp() semantics. */ diff --git a/ext/jni/src/org/sqlite/jni/collation_needed_callback.java b/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java similarity index 91% rename from ext/jni/src/org/sqlite/jni/collation_needed_callback.java rename to ext/jni/src/org/sqlite/jni/CollationNeededCallback.java index 5ee5fcfd91..a8b17fe48b 100644 --- a/ext/jni/src/org/sqlite/jni/collation_needed_callback.java +++ b/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_collation_needed(). */ -public interface collation_needed_callback extends sqlite3_callback_proxy { +public interface CollationNeededCallback extends SQLite3CallbackProxy { /** Has the same semantics as the C-level sqlite3_create_collation() callback. diff --git a/ext/jni/src/org/sqlite/jni/commit_hook_callback.java b/ext/jni/src/org/sqlite/jni/CommitHookCallback.java similarity index 89% rename from ext/jni/src/org/sqlite/jni/commit_hook_callback.java rename to ext/jni/src/org/sqlite/jni/CommitHookCallback.java index 54c0876c21..7757a6ac0d 100644 --- a/ext/jni/src/org/sqlite/jni/commit_hook_callback.java +++ b/ext/jni/src/org/sqlite/jni/CommitHookCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_commit_hook() */ -public interface commit_hook_callback extends sqlite3_callback_proxy { +public interface CommitHookCallback extends SQLite3CallbackProxy { /** Works as documented for the C-level sqlite3_commit_hook() callback. Must not throw. diff --git a/ext/jni/src/org/sqlite/jni/config_sqllog_callback.java b/ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java similarity index 74% rename from ext/jni/src/org/sqlite/jni/config_sqllog_callback.java rename to ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java index 6772de2022..9bdd209a7a 100644 --- a/ext/jni/src/org/sqlite/jni/config_sqllog_callback.java +++ b/ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java @@ -16,10 +16,10 @@ package org.sqlite.jni; /** A callback for use with sqlite3_config(). */ -public interface config_sqllog_callback { +public interface ConfigSqllogCallback { /** - Must function as described for sqlite3_config(SQLITE_CONFIG_SQLLOG) - callback, with the slight signature change. + Must function as described for a C-level callback for + {@link SQLite3Jni#sqlite3_config(ConfigSqllogCallback)}, with the slight signature change. */ void call(sqlite3 db, String msg, int msgType ); } diff --git a/ext/jni/src/org/sqlite/jni/NotNull.java b/ext/jni/src/org/sqlite/jni/NotNull.java index 3222c357f6..831f084513 100644 --- a/ext/jni/src/org/sqlite/jni/NotNull.java +++ b/ext/jni/src/org/sqlite/jni/NotNull.java @@ -2,9 +2,11 @@ package org.sqlite.jni; /** This annotation is for flagging parameters which may not legally be - null. Note that the C-style API does not throw any - NullPointerExceptions on its own because it has a no-throw policy - in order to retain its C-style semantics. + null. Note that the C-style API does not throw any exceptions on + its own because it has a no-throw policy in order to retain its + C-style semantics, but it may trigger NullPointerExceptions (or + similar) if passed a null for a parameter flagged with this + annotation.

This annotation is informational only. No policy is in place to programmatically ensure that NotNull is conformed to in client diff --git a/ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java b/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java similarity index 90% rename from ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java rename to ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java index 5c55fa2b5d..f0b16ae926 100644 --- a/ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java +++ b/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_preupdate_hook(). */ -public interface preupdate_hook_callback extends sqlite3_callback_proxy { +public interface PreupdateHookCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_preupdate_hook() callback. Must not throw. diff --git a/ext/jni/src/org/sqlite/jni/progress_handler_callback.java b/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java similarity index 90% rename from ext/jni/src/org/sqlite/jni/progress_handler_callback.java rename to ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java index e1b6415f06..447bba6f38 100644 --- a/ext/jni/src/org/sqlite/jni/progress_handler_callback.java +++ b/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_progress_handler() */ -public interface progress_handler_callback extends sqlite3_callback_proxy { +public interface ProgressHandlerCallback extends SQLite3CallbackProxy { /** Works as documented for the C-level sqlite3_progress_handler() callback. diff --git a/ext/jni/src/org/sqlite/jni/rollback_hook_callback.java b/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java similarity index 89% rename from ext/jni/src/org/sqlite/jni/rollback_hook_callback.java rename to ext/jni/src/org/sqlite/jni/RollbackHookCallback.java index 224c26c477..7bf3879416 100644 --- a/ext/jni/src/org/sqlite/jni/rollback_hook_callback.java +++ b/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_rollback_hook() */ -public interface rollback_hook_callback extends sqlite3_callback_proxy { +public interface RollbackHookCallback extends SQLite3CallbackProxy { /** Works as documented for the C-level sqlite3_rollback_hook() callback. Must not throw. diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index 97a3f65a46..c8b15d8b68 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -38,6 +38,9 @@ public abstract class SQLFunction { managing their accumulator state across calls to the UDF's callbacks. +

T must be of a type which can be legally stored as a value in + java.util.HashMap. +

If a given aggregate or window function is called multiple times in a single SQL statement, e.g. SELECT MYFUNC(A), MYFUNC(B)..., then the clients need some way of knowing which call is which so @@ -55,9 +58,6 @@ public abstract class SQLFunction { Client UDFs are free to perform such mappings using custom approaches. The provided Aggregate and Window classes use this. - -

T must be of a type which can be legally stored as a value in - java.util.HashMap. */ public static final class PerContextState { private final java.util.Map> map diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java b/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java similarity index 62% rename from ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java rename to ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java index 6200948cce..137cddcc55 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java @@ -20,15 +20,15 @@ package org.sqlite.jni;

Callbacks of this style follow a common naming convention: -

1) They almost all have the same class or interface name as the - C function they are proxying a callback for, minus the sqlite3_ - prefix, plus a _callback suffix. e.g. sqlite3_busy_handler()'s - callback is named busy_handler_callback. Exceptions are made where - that would potentially be ambiguous, e.g. config_sqllog_callback - instead of config_callback because the sqlite3_config() interface - may need to support more callback types in the future. +

1) They use the UpperCamelCase form of the C function they're + proxying for, minus the sqlite3_ prefix, plus a Callback + suffix. e.g. sqlite3_busy_handler()'s callback is named + BusyHandlerCallback. Exceptions are made where that would + potentially be ambiguous, e.g. ConfigSqllogCallback instead of + config_callback because the sqlite3_config() interface may need to + support more callback types in the future.

2) They all have a call() method but its signature is callback-specific. */ -public interface sqlite3_callback_proxy {} +public interface SQLite3CallbackProxy {} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index a4e63cb1e4..2df9e5093b 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -155,7 +155,7 @@ public final class SQLite3Jni {

See the AutoExtension class docs for more information. */ - public static native int sqlite3_auto_extension(@NotNull auto_extension_callback callback); + public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback); /** Results are undefined if data is not null and n<0 || n>=data.length. @@ -286,12 +286,12 @@ public final class SQLite3Jni { ); /** - As for the C-level function of the same name, with a busy_handler_callback + As for the C-level function of the same name, with a BusyHandlerCallback instance in place of a callback function. Pass it a null handler to clear the busy handler. */ public static native int sqlite3_busy_handler( - @NotNull sqlite3 db, @Nullable busy_handler_callback handler + @NotNull sqlite3 db, @Nullable BusyHandlerCallback handler ); public static native int sqlite3_busy_timeout( @@ -299,7 +299,7 @@ public final class SQLite3Jni { ); public static native boolean sqlite3_cancel_auto_extension( - @NotNull auto_extension_callback ax + @NotNull AutoExtensionCallback ax ); public static native int sqlite3_changes( @@ -465,7 +465,7 @@ public final class SQLite3Jni { Java's string type is compatible with that interface. */ public static native int sqlite3_collation_needed( - @NotNull sqlite3 db, @Nullable collation_needed_callback callback + @NotNull sqlite3 db, @Nullable CollationNeededCallback callback ); /** @@ -476,8 +476,8 @@ public final class SQLite3Jni { @NotNull sqlite3_context cx ); - public static native commit_hook_callback sqlite3_commit_hook( - @NotNull sqlite3 db, @Nullable commit_hook_callback hook + public static native CommitHookCallback sqlite3_commit_hook( + @NotNull sqlite3 db, @Nullable CommitHookCallback hook ); public static native String sqlite3_compileoption_get( @@ -488,34 +488,34 @@ public final class SQLite3Jni { @NotNull String optName ); - /* - ** Works like in the C API with the exception that it only supports - ** the following subset of configution flags: - ** - ** - SQLITE_CONFIG_SINGLETHREAD - ** - SQLITE_CONFIG_MULTITHREAD - ** - SQLITE_CONFIG_SERIALIZED - ** - ** Others may be added in the future. It returns SQLITE_MISUSE if - ** given an argument it does not handle. + /** +

Works like in the C API with the exception that it only supports + the following subset of configution flags: + +

SQLITE_CONFIG_SINGLETHREAD + SQLITE_CONFIG_MULTITHREAD + SQLITE_CONFIG_SERIALIZED + +

Others may be added in the future. It returns SQLITE_MISUSE if + given an argument it does not handle. */ public static native int sqlite3_config(int op); - /* - ** If the native library was built with SQLITE_ENABLE_SQLLOG defined - ** then this acts as a proxy for C's - ** sqlite3_config(SQLITE_ENABLE_SQLLOG,...). This sets or clears the - ** logger. If installation of a logger fails, any previous logger is - ** retained. - ** - ** If not built with SQLITE_ENABLE_SQLLOG defined, this returns - ** SQLITE_MISUSE. + /** + If the native library was built with SQLITE_ENABLE_SQLLOG defined + then this acts as a proxy for C's + sqlite3_config(SQLITE_ENABLE_SQLLOG,...). This sets or clears the + logger. If installation of a logger fails, any previous logger is + retained. + + If not built with SQLITE_ENABLE_SQLLOG defined, this returns + SQLITE_MISUSE. */ - public static native int sqlite3_config( @Nullable config_sqllog_callback logger ); + public static native int sqlite3_config( @Nullable ConfigSqllogCallback logger ); public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, - @NotNull collation_callback col + @NotNull Collation col ); /** @@ -834,8 +834,8 @@ public final class SQLite3Jni { acts as a proxy for C's sqlite3_preupdate_hook(), else it returns null with no side effects. */ - public static native preupdate_hook_callback sqlite3_preupdate_hook( - @NotNull sqlite3 db, @Nullable preupdate_hook_callback hook + public static native PreupdateHookCallback sqlite3_preupdate_hook( + @NotNull sqlite3 db, @Nullable PreupdateHookCallback hook ); /** @@ -875,7 +875,7 @@ public final class SQLite3Jni { } public static native void sqlite3_progress_handler( - @NotNull sqlite3 db, int n, @Nullable progress_handler_callback h + @NotNull sqlite3 db, int n, @Nullable ProgressHandlerCallback h ); public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt); @@ -1163,13 +1163,13 @@ public final class SQLite3Jni { } } - public static native rollback_hook_callback sqlite3_rollback_hook( - @NotNull sqlite3 db, @Nullable rollback_hook_callback hook + public static native RollbackHookCallback sqlite3_rollback_hook( + @NotNull sqlite3 db, @Nullable RollbackHookCallback hook ); //! Sets or unsets (if auth is null) the current authorizer. public static native int sqlite3_set_authorizer( - @NotNull sqlite3 db, @Nullable authorizer_callback auth + @NotNull sqlite3 db, @Nullable AuthorizerCallback auth ); public static native void sqlite3_set_last_insert_rowid( @@ -1236,11 +1236,11 @@ public final class SQLite3Jni { cannot be processed propertly (i.e. an internal error). */ public static native int sqlite3_trace_v2( - @NotNull sqlite3 db, int traceMask, @Nullable trace_v2_callback tracer + @NotNull sqlite3 db, int traceMask, @Nullable TraceV2Callback tracer ); - public static native update_hook_callback sqlite3_update_hook( - sqlite3 db, update_hook_callback hook + public static native UpdateHookCallback sqlite3_update_hook( + sqlite3 db, UpdateHookCallback hook ); public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 50cecf6511..ccb8153758 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -489,7 +489,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); final ValueHolder xDestroyCalled = new ValueHolder<>(false); - final collation_callback myCollation = new collation_callback() { + final Collation myCollation = new Collation() { private String myState = "this is local state. There is much like it, but this is mine."; @Override @@ -513,7 +513,7 @@ public class Tester1 implements Runnable { xDestroyCalled.value = true; } }; - final collation_needed_callback collLoader = new collation_needed_callback(){ + final CollationNeededCallback collLoader = new CollationNeededCallback(){ @Override public int call(sqlite3 dbArg, int eTextRep, String collationName){ affirm(dbArg == db/* as opposed to a temporary object*/); @@ -881,7 +881,7 @@ public class Tester1 implements Runnable { int rc = sqlite3_trace_v2( db, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE | SQLITE_TRACE_ROW | SQLITE_TRACE_CLOSE, - new trace_v2_callback(){ + new TraceV2Callback(){ @Override public int call(int traceFlag, Object pNative, Object x){ ++counter.value; //outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName()); @@ -942,7 +942,7 @@ public class Tester1 implements Runnable { final ValueHolder xDestroyed = new ValueHolder<>(false); final ValueHolder xBusyCalled = new ValueHolder<>(0); - busy_handler_callback handler = new busy_handler_callback(){ + BusyHandlerCallback handler = new BusyHandlerCallback(){ @Override public int call(int n){ //outln("busy handler #"+n); return n > 2 ? 0 : ++xBusyCalled.value; @@ -976,7 +976,7 @@ public class Tester1 implements Runnable { private void testProgress(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); - sqlite3_progress_handler(db, 1, new progress_handler_callback(){ + sqlite3_progress_handler(db, 1, new ProgressHandlerCallback(){ @Override public int call(){ ++counter.value; return 0; @@ -995,13 +995,13 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder hookResult = new ValueHolder<>(0); - final commit_hook_callback theHook = new commit_hook_callback(){ + final CommitHookCallback theHook = new CommitHookCallback(){ @Override public int call(){ ++counter.value; return hookResult.value; } }; - commit_hook_callback oldHook = sqlite3_commit_hook(db, theHook); + CommitHookCallback oldHook = sqlite3_commit_hook(db, theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 2 == counter.value ); @@ -1022,7 +1022,7 @@ public class Tester1 implements Runnable { execSql(db, "BEGIN; update t set a='g' where a='f'; COMMIT;"); affirm( 4 == counter.value ); - final commit_hook_callback newHook = new commit_hook_callback(){ + final CommitHookCallback newHook = new CommitHookCallback(){ @Override public int call(){return 0;} }; oldHook = sqlite3_commit_hook(db, newHook); @@ -1044,7 +1044,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); - final update_hook_callback theHook = new update_hook_callback(){ + final UpdateHookCallback theHook = new UpdateHookCallback(){ @Override public void call(int opId, String dbName, String tableName, long rowId){ ++counter.value; @@ -1053,7 +1053,7 @@ public class Tester1 implements Runnable { } } }; - update_hook_callback oldHook = sqlite3_update_hook(db, theHook); + UpdateHookCallback oldHook = sqlite3_update_hook(db, theHook); affirm( null == oldHook ); expectedOp.value = SQLITE_INSERT; execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); @@ -1073,7 +1073,7 @@ public class Tester1 implements Runnable { oldHook = sqlite3_update_hook(db, null); affirm( null == oldHook ); - final update_hook_callback newHook = new update_hook_callback(){ + final UpdateHookCallback newHook = new UpdateHookCallback(){ @Override public void call(int opId, String dbName, String tableName, long rowId){ } }; @@ -1101,7 +1101,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder expectedOp = new ValueHolder<>(0); - final preupdate_hook_callback theHook = new preupdate_hook_callback(){ + final PreupdateHookCallback theHook = new PreupdateHookCallback(){ @Override public void call(sqlite3 db, int opId, String dbName, String dbTable, long iKey1, long iKey2 ){ @@ -1126,7 +1126,7 @@ public class Tester1 implements Runnable { } } }; - preupdate_hook_callback oldHook = sqlite3_preupdate_hook(db, theHook); + PreupdateHookCallback 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')"); @@ -1146,7 +1146,7 @@ public class Tester1 implements Runnable { oldHook = sqlite3_preupdate_hook(db, null); affirm( null == oldHook ); - final preupdate_hook_callback newHook = new preupdate_hook_callback(){ + final PreupdateHookCallback newHook = new PreupdateHookCallback(){ @Override public void call(sqlite3 db, int opId, String dbName, String tableName, long iKey1, long iKey2){ @@ -1168,19 +1168,19 @@ public class Tester1 implements Runnable { private void testRollbackHook(){ final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); - final rollback_hook_callback theHook = new rollback_hook_callback(){ + final RollbackHookCallback theHook = new RollbackHookCallback(){ @Override public void call(){ ++counter.value; } }; - rollback_hook_callback oldHook = sqlite3_rollback_hook(db, theHook); + RollbackHookCallback oldHook = sqlite3_rollback_hook(db, theHook); affirm( null == oldHook ); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); affirm( 0 == counter.value ); execSql(db, false, "BEGIN; SELECT 1; SELECT 2; ROLLBACK;"); affirm( 1 == counter.value /* contra to commit hook, is invoked if no changes are made */ ); - final rollback_hook_callback newHook = new rollback_hook_callback(){ + final RollbackHookCallback newHook = new RollbackHookCallback(){ @Override public void call(){return;} }; oldHook = sqlite3_rollback_hook(db, newHook); @@ -1237,7 +1237,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder counter = new ValueHolder<>(0); final ValueHolder authRc = new ValueHolder<>(0); - final authorizer_callback auth = new authorizer_callback(){ + final AuthorizerCallback auth = new AuthorizerCallback(){ public int call(int op, String s0, String s1, String s2, String s3){ ++counter.value; //outln("xAuth(): "+s0+" "+s1+" "+s2+" "+s3); @@ -1260,7 +1260,7 @@ public class Tester1 implements Runnable { private synchronized void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); - final auto_extension_callback ax = new auto_extension_callback(){ + final AutoExtensionCallback ax = new AutoExtensionCallback(){ @Override public synchronized int call(sqlite3 db){ ++val.value; if( null!=toss.value ){ @@ -1311,7 +1311,7 @@ public class Tester1 implements Runnable { toss.value = null; val.value = 0; - final auto_extension_callback ax2 = new auto_extension_callback(){ + final AutoExtensionCallback ax2 = new AutoExtensionCallback(){ @Override public synchronized int call(sqlite3 db){ ++val.value; return 0; @@ -1507,7 +1507,7 @@ public class Tester1 implements Runnable { if( sqlLog ){ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ - int rc = sqlite3_config( new config_sqllog_callback() { + int rc = sqlite3_config( new ConfigSqllogCallback() { @Override public void call(sqlite3 db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; diff --git a/ext/jni/src/org/sqlite/jni/trace_v2_callback.java b/ext/jni/src/org/sqlite/jni/TraceV2Callback.java similarity index 96% rename from ext/jni/src/org/sqlite/jni/trace_v2_callback.java rename to ext/jni/src/org/sqlite/jni/TraceV2Callback.java index e5afd6c663..453cf948db 100644 --- a/ext/jni/src/org/sqlite/jni/trace_v2_callback.java +++ b/ext/jni/src/org/sqlite/jni/TraceV2Callback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback proxy for use with sqlite3_trace_v2(). */ -public interface trace_v2_callback extends sqlite3_callback_proxy { +public interface TraceV2Callback extends SQLite3CallbackProxy { /** Called by sqlite3 for various tracing operations, as per sqlite3_trace_v2(). Note that this interface elides the 2nd diff --git a/ext/jni/src/org/sqlite/jni/update_hook_callback.java b/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java similarity index 90% rename from ext/jni/src/org/sqlite/jni/update_hook_callback.java rename to ext/jni/src/org/sqlite/jni/UpdateHookCallback.java index 9193b703a0..17c49ba27c 100644 --- a/ext/jni/src/org/sqlite/jni/update_hook_callback.java +++ b/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_update_hook(). */ -public interface update_hook_callback extends sqlite3_callback_proxy { +public interface UpdateHookCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_update_hook() callback. Must not throw. diff --git a/ext/jni/src/org/sqlite/jni/xDestroy_callback.java b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java similarity index 95% rename from ext/jni/src/org/sqlite/jni/xDestroy_callback.java rename to ext/jni/src/org/sqlite/jni/XDestroyCallback.java index 92086cc796..339508c088 100644 --- a/ext/jni/src/org/sqlite/jni/xDestroy_callback.java +++ b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java @@ -19,7 +19,7 @@ package org.sqlite.jni; the symbol name xDestroy() for this purpose in the C API documentation. */ -public interface xDestroy_callback { +public interface XDestroyCallback { /** Must perform any cleanup required by this object. Must not throw. diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_context.java b/ext/jni/src/org/sqlite/jni/sqlite3_context.java index a3a3d48664..b9f11d7336 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_context.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_context.java @@ -25,12 +25,15 @@ public final class sqlite3_context extends NativePointerHolder sqlite3_aggregate_context(), with a slightly different interface to account for cross-language differences. It serves the same purposes in a slightly different way: it provides a key which is - stable across invocations of "matching sets" of a UDF's callbacks, - such that all calls into those callbacks can determine which "set" - of those calls they belong to. + stable across invocations of a UDF's callbacks, such that all + calls into those callbacks can determine which "set" of those + calls they belong to. + +

Note that use of this method is not a requirement for proper use + of this class. sqlite3_aggregate_context() can also be used.

If the argument is true and the aggregate context has not yet - been set up, it will be initialized fetched on demand, else it + been set up, it will be initialized and fetched on demand, else it won't. The intent is that xStep(), xValue(), and xInverse() methods pass true and xFinal() methods pass false. @@ -59,12 +62,12 @@ public final class sqlite3_context extends NativePointerHolder key for mapping callback invocations to whatever client-defined state is needed by the UDF. -

There is one case where this will return 0 in the context of an - aggregate or window function: if the result set has no rows, - the UDF's xFinal() will be called without any other x...() members - having been called. In that one case, no aggregate context key will - have been generated. xFinal() implementations need to be prepared to - accept that condition as legal. +

There is one case where this will return null in the context + of an aggregate or window function: if the result set has no + rows, the UDF's xFinal() will be called without any other x...() + members having been called. In that one case, no aggregate + context key will have been generated. xFinal() implementations + need to be prepared to accept that condition as legal. */ public synchronized Long getAggregateContext(boolean initIfNeeded){ if( aggregateContext==null ){ diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index 90974b71c9..d8b84af795 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -609,7 +609,7 @@ public class SQLTester { } t.addTestScript(a); } - final auto_extension_callback ax = new auto_extension_callback() { + final AutoExtensionCallback ax = new AutoExtensionCallback() { private final SQLTester tester = t; @Override public int call(sqlite3 db){ final String init = tester.getDbInitSql(); diff --git a/manifest b/manifest index 9de96e0da2..2341f15205 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\sJava\sAPI\srenaming\sfor\sconsistency. -D 2023-08-25T13:23:45.671 +C Hopefully\sthe\slast\smajor\sAPI\srename\sin\sthe\sJNI\sbits:\srename\sall\slower_camel_case\sclasses\swhich\sdo\snot\shave\sdirect\scounterparts\sin\sthe\sC\sAPI\sto\sUpperCamelCase,\sas\sthat's\smore\sidiomatic.\sJava\stypes\swhich\sreflect\sC-style\stypes\sare\sunaffected. +D 2023-08-25T14:17:36.142 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,11 +233,18 @@ 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 6f2290fa521da420c4a5c2eed1692b7a5fc2d53dfa74b8786517d1adb6078bfe +F ext/jni/GNUmakefile e352de2c988d2bd0b2c356133b61710be19f7a97502442dcf8a5d5680d1e5fdb F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 69cb5df6d5ed65ca3870982b0d5d59c99c6580538a8929c3d4a0cbe837eea660 -F ext/jni/src/c/sqlite3-jni.h 288a37d1921fd5f1772f82ecb766a64bf20df7da685479ca84d3b95d9bcacd66 +F ext/jni/src/c/sqlite3-jni.c 29c10d96f81361b0d121e389320a1dd57958fd758e3790817542d2eb20c42bed +F ext/jni/src/c/sqlite3-jni.h 2f6184b6295fdb0e6b41e1ae8dfbd5da9fb4da9290684da769812062158bc70c +F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 w ext/jni/src/org/sqlite/jni/authorizer_callback.java +F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 w ext/jni/src/org/sqlite/jni/auto_extension_callback.java +F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 99248b76e9b124f60a25329309ac6a03c197b3e1a493e8f8ae2e577651ea58e5 w ext/jni/src/org/sqlite/jni/busy_handler_callback.java +F ext/jni/src/org/sqlite/jni/Collation.java b961e3abd8a50614852531b9bc313a1f2039a100acd653b3bd4173c4c55bd5eb w ext/jni/src/org/sqlite/jni/collation_callback.java +F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 w ext/jni/src/org/sqlite/jni/collation_needed_callback.java +F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f w ext/jni/src/org/sqlite/jni/commit_hook_callback.java +F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 w ext/jni/src/org/sqlite/jni/config_sqllog_callback.java F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java d475009d654a16060101da6bdfd7f118d49ff61d910faf5a1d1fdb0a1def75f1 @@ -245,39 +252,32 @@ 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 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d -F ext/jni/src/org/sqlite/jni/NotNull.java a4016df436f454e8d6786dd8421484edd6fc604043cf7fd8ec94cf922ba61604 +F ext/jni/src/org/sqlite/jni/NotNull.java 08fcfcee93cdf5dd3128a88f9a112c000c143f9e077fcb31e554822595c9d73e F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 +F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a964227db3ba6f953cc89b8be38a5ae6fea063818 w ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java +F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 w ext/jni/src/org/sqlite/jni/progress_handler_callback.java F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 -F ext/jni/src/org/sqlite/jni/SQLFunction.java 4d6291fa14fcca1a040609378f9f00a193145d79c3abbda98ba32c340904cbeb -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 26d757281a6ba588f9146f15f89ab14c8dbe5367ac40bd23d6be6f8eef578f18 -F ext/jni/src/org/sqlite/jni/Tester1.java 449091bf0c225774733657df7fdbb5f357692aed0cb6e327ef94474c81a2d834 +F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 w ext/jni/src/org/sqlite/jni/rollback_hook_callback.java +F ext/jni/src/org/sqlite/jni/SQLFunction.java f6842d587c6366d4ebd565b533a288efc978d56ab2d4f52f2e74b64c8e6a33f3 +F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 w ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 00aeec162b864cd62397f7e20941a3c911bf6856627f1b1a02cb12fd1e152449 +F ext/jni/src/org/sqlite/jni/Tester1.java 595c1b9467a081dde138e383f0ce5ac3519cb9b9b7c9b2e83d39bb192e9ee7eb F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 +F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c w ext/jni/src/org/sqlite/jni/trace_v2_callback.java +F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 w ext/jni/src/org/sqlite/jni/update_hook_callback.java F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee -F ext/jni/src/org/sqlite/jni/authorizer_callback.java 1d2d7fd584f917afa507820644d95504bcc9c5d7363a7afeb58de3b256851bfe -F ext/jni/src/org/sqlite/jni/auto_extension_callback.java c8754ffabe3b75bd8f209bf1451d6a180ec52e99b11c11b2e3642f1891eb2635 -F ext/jni/src/org/sqlite/jni/busy_handler_callback.java b43fc35e0f245ff532a4ab8bda6736f06858a45a67a21dbac5d6c237c5188d2c -F ext/jni/src/org/sqlite/jni/collation_callback.java cc8ff046e01a3cda57d9072e84bb000aaf207db605e31928a63c8b87e8ea06bd -F ext/jni/src/org/sqlite/jni/collation_needed_callback.java 0d5cbac245db9ff22b67c92c06f2e31ed557cd018f1c4670ae970e6f16f22cee -F ext/jni/src/org/sqlite/jni/commit_hook_callback.java 88462783826026e61e522d9aae7a9b4cb0c30f7d56519e08a5de42213a0087bc -F ext/jni/src/org/sqlite/jni/config_sqllog_callback.java d8b9b4e0f9a522fd40a88b4f9f87308fff1be255523ad6cff8493bf3bbca2ec8 +F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 w ext/jni/src/org/sqlite/jni/xDestroy_callback.java F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 -F ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java 2bcc61a9320a7af6be36e5a814d133dd610d8ead79622efd84e7444aabe25f6c -F ext/jni/src/org/sqlite/jni/progress_handler_callback.java eae32bd36639b12552becf82a0481bb4c09d22655920007b62e49130ce97a850 -F ext/jni/src/org/sqlite/jni/rollback_hook_callback.java 25663dbad4f9da50019d0c68fc815d31155a04eaeb293f27fdc6f9b20a424760 F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc -F ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java 4b3369faab47d787594c0544000dbac751d69641cac866f82f9be0e13c4b2ce5 -F ext/jni/src/org/sqlite/jni/sqlite3_context.java dca23e54f368f8ea37c112c1d2f74dc9522d5da2fdf67d6fd6b2ec9603d8514c +F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java e560303ada834363b615e5863050d1488bf5c83f0627b966fb1a0a6a4355029f +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 439d28ac92570e4d16a5361423656f3eae6916c6d4777afde490f77dbb3ec1bc F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e -F ext/jni/src/org/sqlite/jni/trace_v2_callback.java ec89134d679a9f7aebd2187025b6c58ab86e733dfecd44365ea656ac2dfae36a -F ext/jni/src/org/sqlite/jni/update_hook_callback.java 616dbc9f99bdfbde190af3d8a44a8ad418fdc5f8c63acb0a0d679bd063848da8 -F ext/jni/src/org/sqlite/jni/xDestroy_callback.java db2ffef90eccb445c886ef21a88250c455fa530ca135def49d5afa6c79f7f115 w ext/jni/src/org/sqlite/jni/sqlite3_xDestroy_callback.java F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 @@ -2100,8 +2100,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 6b1c816f6aae9face07ed7ead969257cf915c21ff9cdad9a21b7cf0e7496e09e -R e0e5881b251feaa8a0cfeca08d550ed6 +P 4e457778de189d8393e88f399dc521c8a5ad4ddd80a5fd69295bcb104fac5cbf +R a2db0a9911f347758d8a038cfb28c892 U stephan -Z c7e4ee2c8d85ebfdfb957345bd33818f +Z 97666aa13ffdfb8b58994782592ca3c3 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 11aa18bfe7..9f46b25f8e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e457778de189d8393e88f399dc521c8a5ad4ddd80a5fd69295bcb104fac5cbf \ No newline at end of file +8ca528006533ac152e9b322e421cf5d339414c77332d9836a9ff429b793d0d2d \ No newline at end of file From 9fed4aab7728f7b0ccc0ddf03fb4aa932053cec8 Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 16:26:16 +0000 Subject: [PATCH 062/422] Missed a straggler when renaming Java classes in [8ca528006533ac1]. Document threading limitations. FossilOrigin-Name: 5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.h | 2 +- ...{Collation.java => CollationCallback.java} | 2 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 8 +--- ext/jni/src/org/sqlite/jni/Tester1.java | 8 ++-- ext/jni/src/org/sqlite/jni/package-info.java | 39 ++++++++++++++- manifest | 48 +++++++++---------- manifest.uuid | 2 +- 8 files changed, 71 insertions(+), 40 deletions(-) rename ext/jni/src/org/sqlite/jni/{Collation.java => CollationCallback.java} (95%) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 35381d1cc5..2ee9389b81 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -62,7 +62,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ AuthorizerCallback.java \ AutoExtensionCallback.java \ BusyHandlerCallback.java \ - Collation.java \ + CollationCallback.java \ CollationNeededCallback.java \ CommitHookCallback.java \ ConfigSqllogCallback.java \ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 0e8dd4bcc0..7279b0376d 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1118,7 +1118,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1config__Lorg_sqli /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_create_collation - * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/Collation;)I + * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;ILorg/sqlite/jni/CollationCallback;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1create_1collation (JNIEnv *, jclass, jobject, jstring, jint, jobject); diff --git a/ext/jni/src/org/sqlite/jni/Collation.java b/ext/jni/src/org/sqlite/jni/CollationCallback.java similarity index 95% rename from ext/jni/src/org/sqlite/jni/Collation.java rename to ext/jni/src/org/sqlite/jni/CollationCallback.java index cf64421531..1e4d0396de 100644 --- a/ext/jni/src/org/sqlite/jni/Collation.java +++ b/ext/jni/src/org/sqlite/jni/CollationCallback.java @@ -16,7 +16,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_create_collation() */ -public abstract class Collation +public abstract class CollationCallback implements SQLite3CallbackProxy, XDestroyCallback { /** Must compare the given byte arrays using memcmp() semantics. diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 2df9e5093b..7b19c5dc72 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -468,10 +468,6 @@ public final class SQLite3Jni { @NotNull sqlite3 db, @Nullable CollationNeededCallback callback ); - /** - Returns the db handle passed to sqlite3_open() or - sqlite3_open_v2(), as opposed to a new wrapper object. - */ public static native sqlite3 sqlite3_context_db_handle( @NotNull sqlite3_context cx ); @@ -491,7 +487,7 @@ public final class SQLite3Jni { /**

Works like in the C API with the exception that it only supports the following subset of configution flags: - +

SQLITE_CONFIG_SINGLETHREAD SQLITE_CONFIG_MULTITHREAD SQLITE_CONFIG_SERIALIZED @@ -515,7 +511,7 @@ public final class SQLite3Jni { public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, - @NotNull Collation col + @NotNull CollationCallback col ); /** diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index ccb8153758..3af676394d 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -489,7 +489,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); final ValueHolder xDestroyCalled = new ValueHolder<>(false); - final Collation myCollation = new Collation() { + final CollationCallback myCollation = new CollationCallback() { private String myState = "this is local state. There is much like it, but this is mine."; @Override @@ -1532,11 +1532,11 @@ public class Tester1 implements Runnable { int nLoop = 0; switch( SQLITE_THREADSAFE ){ /* Sanity checking */ case 0: - affirm( 0!=sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), + affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), "Could not switch to single-thread mode." ); - affirm( 0!=sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), + affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ), "Could switch to multithread mode." ); - affirm( 0!=sqlite3_config( SQLITE_CONFIG_SERIALIZED ), + affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_SERIALIZED ), "Could not switch to serialized threading mode." ); outln("This is a single-threaded build. Not using threads."); nThread = 1; diff --git a/ext/jni/src/org/sqlite/jni/package-info.java b/ext/jni/src/org/sqlite/jni/package-info.java index 6de353e0b2..ed9086c4c2 100644 --- a/ext/jni/src/org/sqlite/jni/package-info.java +++ b/ext/jni/src/org/sqlite/jni/package-info.java @@ -1,8 +1,43 @@ /** This package houses a JNI binding to the SQLite3 C API. - The docs are in progress. +

The docs are in progress. - The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}. +

The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}. + +

Threading Considerations

+ +

This API is, if built with SQLITE_THREADSAFE set to 1 or 2, + thread-safe, insofar as the C API guarantees, with some addenda: + +

    + +
  • It is not legal to use Java-facing SQLite3 resource handles + (sqlite3, sqlite3_stmt, etc) from multiple threads concurrently, + nor to use any database-specific resources concurrently in a + thread separate from the one the database is currently in use + in. i.e. do not use a sqlite3_stmt in thread #2 when thread #1 is + using the database which prepared that handle. + +
    Violating this will eventually corrupt the JNI-level bindings + between Java's and C's view of the database. This is a limitation + of the JNI bindings, not the lower-level library. +
  • + +
  • It is legal to use a given handle, and database-specific + resources, across threads, so long as no two threads pass + resources owned by the same database into the library + concurrently. +
  • + +
+ +

Any number of threads may, of course, create and use any number + of database handles they wish. Care only needs to be taken when + those handles or their associated resources cross threads, or... + +

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 2341f15205..9640199ae7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Hopefully\sthe\slast\smajor\sAPI\srename\sin\sthe\sJNI\sbits:\srename\sall\slower_camel_case\sclasses\swhich\sdo\snot\shave\sdirect\scounterparts\sin\sthe\sC\sAPI\sto\sUpperCamelCase,\sas\sthat's\smore\sidiomatic.\sJava\stypes\swhich\sreflect\sC-style\stypes\sare\sunaffected. -D 2023-08-25T14:17:36.142 +C Missed\sa\sstraggler\swhen\srenaming\sJava\sclasses\sin\s[8ca528006533ac1].\sDocument\sthreading\slimitations. +D 2023-08-25T16:26:16.517 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,18 +233,18 @@ 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 e352de2c988d2bd0b2c356133b61710be19f7a97502442dcf8a5d5680d1e5fdb +F ext/jni/GNUmakefile 4fed859fb183595826e02213761b361c40d2f43959f0f119d322738892c29357 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 29c10d96f81361b0d121e389320a1dd57958fd758e3790817542d2eb20c42bed -F ext/jni/src/c/sqlite3-jni.h 2f6184b6295fdb0e6b41e1ae8dfbd5da9fb4da9290684da769812062158bc70c -F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 w ext/jni/src/org/sqlite/jni/authorizer_callback.java -F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 w ext/jni/src/org/sqlite/jni/auto_extension_callback.java -F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 99248b76e9b124f60a25329309ac6a03c197b3e1a493e8f8ae2e577651ea58e5 w ext/jni/src/org/sqlite/jni/busy_handler_callback.java -F ext/jni/src/org/sqlite/jni/Collation.java b961e3abd8a50614852531b9bc313a1f2039a100acd653b3bd4173c4c55bd5eb w ext/jni/src/org/sqlite/jni/collation_callback.java -F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 w ext/jni/src/org/sqlite/jni/collation_needed_callback.java -F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f w ext/jni/src/org/sqlite/jni/commit_hook_callback.java -F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 w ext/jni/src/org/sqlite/jni/config_sqllog_callback.java +F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 +F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 +F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 +F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 99248b76e9b124f60a25329309ac6a03c197b3e1a493e8f8ae2e577651ea58e5 +F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 w ext/jni/src/org/sqlite/jni/Collation.java +F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 +F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f +F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java d475009d654a16060101da6bdfd7f118d49ff61d910faf5a1d1fdb0a1def75f1 @@ -255,23 +255,23 @@ F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a0 F ext/jni/src/org/sqlite/jni/NotNull.java 08fcfcee93cdf5dd3128a88f9a112c000c143f9e077fcb31e554822595c9d73e F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 -F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a964227db3ba6f953cc89b8be38a5ae6fea063818 w ext/jni/src/org/sqlite/jni/preupdate_hook_callback.java -F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 w ext/jni/src/org/sqlite/jni/progress_handler_callback.java +F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a964227db3ba6f953cc89b8be38a5ae6fea063818 +F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 -F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 w ext/jni/src/org/sqlite/jni/rollback_hook_callback.java +F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java f6842d587c6366d4ebd565b533a288efc978d56ab2d4f52f2e74b64c8e6a33f3 -F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 w ext/jni/src/org/sqlite/jni/sqlite3_callback_proxy.java -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 00aeec162b864cd62397f7e20941a3c911bf6856627f1b1a02cb12fd1e152449 -F ext/jni/src/org/sqlite/jni/Tester1.java 595c1b9467a081dde138e383f0ce5ac3519cb9b9b7c9b2e83d39bb192e9ee7eb +F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 17aebe15820b525678839c5e12160a7ca77b1691e59c8fb7c08b05757bea9553 +F ext/jni/src/org/sqlite/jni/Tester1.java 882755ac7a437ecde721084a0839b524199fa9857a7878d16a20848a7187093f F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 -F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c w ext/jni/src/org/sqlite/jni/trace_v2_callback.java -F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 w ext/jni/src/org/sqlite/jni/update_hook_callback.java +F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c +F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee -F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 w ext/jni/src/org/sqlite/jni/xDestroy_callback.java +F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa -F ext/jni/src/org/sqlite/jni/package-info.java 5652d1bcaaf3ccb06d26c174e6d0b60571a545a0a3354dd8303960677be05e14 +F ext/jni/src/org/sqlite/jni/package-info.java c8f1c858ebcadd16ff047a74cf7a0556d4235386c47bc0a4d78c4a564bba03fe 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 @@ -2100,8 +2100,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 4e457778de189d8393e88f399dc521c8a5ad4ddd80a5fd69295bcb104fac5cbf -R a2db0a9911f347758d8a038cfb28c892 +P 8ca528006533ac152e9b322e421cf5d339414c77332d9836a9ff429b793d0d2d +R 5885805208dd5a9cb4303aef998be394 U stephan -Z 97666aa13ffdfb8b58994782592ca3c3 +Z 3728ae49651cf5a3340c3d5aa589c6ef # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9f46b25f8e..7f7db679d9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8ca528006533ac152e9b322e421cf5d339414c77332d9836a9ff429b793d0d2d \ No newline at end of file +5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 \ No newline at end of file From 25033b6b3b888e493a724007080001e45291601b Mon Sep 17 00:00:00 2001 From: stephan Date: Fri, 25 Aug 2023 16:43:51 +0000 Subject: [PATCH 063/422] Move the 3 Java SQLFunction subclasses from inner classes to package scope. FossilOrigin-Name: 21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 --- ext/jni/GNUmakefile | 7 ++ .../src/org/sqlite/jni/AggregateFunction.java | 72 ++++++++++++++ ext/jni/src/org/sqlite/jni/SQLFunction.java | 98 ------------------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 6 +- .../src/org/sqlite/jni/ScalarFunction.java | 33 +++++++ ext/jni/src/org/sqlite/jni/Tester1.java | 12 +-- .../src/org/sqlite/jni/WindowFunction.java | 39 ++++++++ manifest | 23 +++-- manifest.uuid | 2 +- 9 files changed, 174 insertions(+), 118 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/AggregateFunction.java create mode 100644 ext/jni/src/org/sqlite/jni/ScalarFunction.java create mode 100644 ext/jni/src/org/sqlite/jni/WindowFunction.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 2ee9389b81..7534166335 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -59,6 +59,7 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ + AggregateFunction.java \ AuthorizerCallback.java \ AutoExtensionCallback.java \ BusyHandlerCallback.java \ @@ -74,12 +75,14 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ ProgressHandlerCallback.java \ ResultCode.java \ RollbackHookCallback.java \ + ScalarFunction.java \ SQLFunction.java \ SQLite3Jni.java \ Tester1.java \ TraceV2Callback.java \ UpdateHookCallback.java \ ValueHolder.java \ + WindowFunction.java \ XDestroyCallback.java \ package-info.java \ sqlite3.java \ @@ -318,6 +321,10 @@ $(doc.index): $(JAVA_FILES.main) $(MAKEFILE) .FORCE: doc doc: $(doc.index) javadoc: $(doc.index) +# Force rebild of docs +redoc: + @rm -f $(doc.index)/index.html + @$(MAKE) doc docserve: $(doc.index) cd $(dir.doc) && althttpd -max-age 1 -page index.html ######################################################################## diff --git a/ext/jni/src/org/sqlite/jni/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/AggregateFunction.java new file mode 100644 index 0000000000..6b6c848763 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/AggregateFunction.java @@ -0,0 +1,72 @@ +/* +** 2023-08-25 +** +** 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; + + +/** + SQLFunction subclass for creating aggregate functions. Its T is + the data type of its "accumulator" state, an instance of which is + intended to be be managed using the getAggregateState() and + takeAggregateState() methods. +*/ +public abstract class AggregateFunction extends SQLFunction { + + /** + As for the xStep() argument of the C API's + sqlite3_create_function(). If this function throws, the + exception is not propagated and a warning might be emitted to a + debugging channel. + */ + public abstract void xStep(sqlite3_context cx, sqlite3_value[] args); + + /** + As for the xFinal() argument of the C API's sqlite3_create_function(). + If this function throws, it is translated into an sqlite3_result_error(). + */ + public abstract void xFinal(sqlite3_context cx); + + /** + Optionally override to be notified when the UDF is finalized by + SQLite. + */ + public void xDestroy() {} + + /** Per-invocation state for the UDF. */ + private final SQLFunction.PerContextState map = + new SQLFunction.PerContextState<>(); + + /** + To be called from the implementation's xStep() method, as well + as the xValue() and xInverse() methods of the Window + subclass, to fetch the current per-call UDF state. On the + first call to this method for any given sqlite3_context + argument, the context is set to the given initial value. On all other + calls, the 2nd argument is ignored. + + @see SQLFunction.PerContextState#getAggregateState() + */ + protected final ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ + return map.getAggregateState(cx, initialValue); + } + + /** + To be called from the implementation's xFinal() method to fetch + the final state of the UDF and remove its mapping. + + see SQLFunction.PerContextState#takeAggregateState() + */ + protected final T takeAggregateState(sqlite3_context cx){ + return map.takeAggregateState(cx); + } +} diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index c8b15d8b68..badbc3dc78 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -100,102 +100,4 @@ public abstract class SQLFunction { } } - /** - Subclass for creating scalar functions. - */ - public static abstract class Scalar extends SQLFunction { - - /** - As for the xFunc() argument of the C API's - sqlite3_create_function(). If this function throws, it is - translated into an sqlite3_result_error(). - */ - public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args); - - /** - Optionally override to be notified when the UDF is finalized by - SQLite. - */ - public void xDestroy() {} - } - - /** - SQLFunction Subclass for creating aggregate functions. Its T is - the data type of its "accumulator" state, an instance of which is - intended to be be managed using the getAggregateState() and - takeAggregateState() methods. - */ - public static abstract class Aggregate extends SQLFunction { - - /** - As for the xStep() argument of the C API's - sqlite3_create_function(). If this function throws, the - exception is not propagated and a warning might be emitted to a - debugging channel. - */ - public abstract void xStep(sqlite3_context cx, sqlite3_value[] args); - - /** - As for the xFinal() argument of the C API's sqlite3_create_function(). - If this function throws, it is translated into an sqlite3_result_error(). - */ - public abstract void xFinal(sqlite3_context cx); - - /** - Optionally override to be notified when the UDF is finalized by - SQLite. - */ - public void xDestroy() {} - - //! Per-invocation state for the UDF. - private final PerContextState map = new PerContextState<>(); - - /** - To be called from the implementation's xStep() method, as well - as the xValue() and xInverse() methods of the Window - subclass, to fetch the current per-call UDF state. On the - first call to this method for any given sqlite3_context - argument, the context is set to the given initial value. On all other - calls, the 2nd argument is ignored. - - @see SQLFunction.PerContextState#getAggregateState() - */ - protected final ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ - return map.getAggregateState(cx, initialValue); - } - - /** - To be called from the implementation's xFinal() method to fetch - the final state of the UDF and remove its mapping. - - see SQLFunction.PerContextState#takeAggregateState() - */ - protected final T takeAggregateState(sqlite3_context cx){ - return map.takeAggregateState(cx); - } - } - - /** - An SQLFunction subclass for creating window functions. Note that - Window inherits from Aggregate and each instance is - required to implement the inherited abstract methods from that - class. See Aggregate for information on managing the UDF's - invocation-specific state. - */ - public static abstract class Window extends Aggregate { - - /** - As for the xInverse() argument of the C API's - sqlite3_create_window_function(). If this function throws, the - exception is not propagated and a warning might be emitted - to a debugging channel. - */ - public abstract void xInverse(sqlite3_context cx, sqlite3_value[] args); - - /** - As for the xValue() argument of the C API's sqlite3_create_window_function(). - See xInverse() for the fate of any exceptions this throws. - */ - public abstract void xValue(sqlite3_context cx); - } } diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 7b19c5dc72..6c1ec796db 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -519,9 +519,9 @@ public final class SQLite3Jni { sqlite3_create_function_v2(), and sqlite3_create_window_function(). Which one it behaves like depends on which methods the final argument implements. See - SQLFunction's inner classes (Scalar, Aggregate, and Window) - for details. - */ + SQLFunction's subclasses (ScalarFunction, AggregateFunction, + and WindowFunction) for details. + */ public static native int sqlite3_create_function( @NotNull sqlite3 db, @NotNull String functionName, int nArg, int eTextRep, @NotNull SQLFunction func diff --git a/ext/jni/src/org/sqlite/jni/ScalarFunction.java b/ext/jni/src/org/sqlite/jni/ScalarFunction.java new file mode 100644 index 0000000000..9d75dbdb76 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/ScalarFunction.java @@ -0,0 +1,33 @@ +/* +** 2023-08-25 +** +** 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; + + +/** + SQLFunction subclass for creating scalar functions. +*/ +public abstract class ScalarFunction extends SQLFunction { + /** + As for the xFunc() argument of the C API's + sqlite3_create_function(). If this function throws, it is + translated into an sqlite3_result_error(). + */ + public abstract void xFunc(sqlite3_context cx, sqlite3_value[] args); + + /** + Optionally override to be notified when the UDF is finalized by + SQLite. + */ + public void xDestroy() {} +} diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 3af676394d..5d3712f2d6 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -612,7 +612,7 @@ public class Tester1 implements Runnable { // Each of the 3 subclasses requires a different set of // functions, all of which must be implemented. Anonymous // classes are a convenient way to implement these. - new SQLFunction.Scalar(){ + new ScalarFunction(){ public void xFunc(sqlite3_context cx, sqlite3_value[] args){ affirm(db == sqlite3_context_db_handle(cx)); int result = 0; @@ -648,7 +648,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); final ValueHolder xFuncAccum = new ValueHolder<>(0); - SQLFunction funcAgg = new SQLFunction.Aggregate(){ + SQLFunction funcAgg = new AggregateFunction(){ @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ /** Throwing from here should emit loud noise on stdout or stderr but the exception is supressed because we have no way to inform @@ -668,7 +668,7 @@ public class Tester1 implements Runnable { affirm( 0 != rc ); affirm( sqlite3_errmsg(db).indexOf("an xFinal") > 0 ); - SQLFunction funcSc = new SQLFunction.Scalar(){ + SQLFunction funcSc = new ScalarFunction(){ @Override public void xFunc(sqlite3_context cx, sqlite3_value[] args){ throw new RuntimeException("Throwing from an xFunc"); } @@ -689,7 +689,7 @@ public class Tester1 implements Runnable { private void testUdfJavaObject(){ final sqlite3 db = createNewDb(); final ValueHolder testResult = new ValueHolder<>(db); - final SQLFunction func = new SQLFunction.Scalar(){ + final SQLFunction func = new ScalarFunction(){ public void xFunc(sqlite3_context cx, sqlite3_value args[]){ sqlite3_result_java_object(cx, testResult.value); } @@ -721,7 +721,7 @@ public class Tester1 implements Runnable { // To confirm that xFinal() is called with no aggregate state // when the corresponding result set is empty. new ValueHolder<>(false); - SQLFunction func = new SQLFunction.Aggregate(){ + SQLFunction func = new AggregateFunction(){ @Override public void xStep(sqlite3_context cx, sqlite3_value[] args){ final ValueHolder agg = this.getAggregateState(cx, 0); @@ -787,7 +787,7 @@ public class Tester1 implements Runnable { final sqlite3 db = createNewDb(); /* Example window function, table, and results taken from: https://sqlite.org/windowfunctions.html#udfwinfunc */ - final SQLFunction func = new SQLFunction.Window(){ + final SQLFunction func = new WindowFunction(){ private void xStepInverse(sqlite3_context cx, int v){ this.getAggregateState(cx,0).value += v; diff --git a/ext/jni/src/org/sqlite/jni/WindowFunction.java b/ext/jni/src/org/sqlite/jni/WindowFunction.java new file mode 100644 index 0000000000..c083312f24 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/WindowFunction.java @@ -0,0 +1,39 @@ +/* +** 2023-08-25 +** +** 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; + + +/** + An SQLFunction subclass for creating window functions. Note that + Window inherits from Aggregate and each instance is + required to implement the inherited abstract methods from that + class. See Aggregate for information on managing the UDF's + invocation-specific state. +*/ +public abstract class WindowFunction extends AggregateFunction { + + /** + As for the xInverse() argument of the C API's + sqlite3_create_window_function(). If this function throws, the + exception is not propagated and a warning might be emitted + to a debugging channel. + */ + public abstract void xInverse(sqlite3_context cx, sqlite3_value[] args); + + /** + As for the xValue() argument of the C API's sqlite3_create_window_function(). + See xInverse() for the fate of any exceptions this throws. + */ + public abstract void xValue(sqlite3_context cx); +} diff --git a/manifest b/manifest index 9640199ae7..99bb002623 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Missed\sa\sstraggler\swhen\srenaming\sJava\sclasses\sin\s[8ca528006533ac1].\sDocument\sthreading\slimitations. -D 2023-08-25T16:26:16.517 +C Move\sthe\s3\sJava\sSQLFunction\ssubclasses\sfrom\sinner\sclasses\sto\spackage\sscope. +D 2023-08-25T16:43:51.353 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,15 +233,16 @@ 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 4fed859fb183595826e02213761b361c40d2f43959f0f119d322738892c29357 +F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 29c10d96f81361b0d121e389320a1dd57958fd758e3790817542d2eb20c42bed F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 +F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 99248b76e9b124f60a25329309ac6a03c197b3e1a493e8f8ae2e577651ea58e5 -F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 w ext/jni/src/org/sqlite/jni/Collation.java +F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 @@ -259,14 +260,16 @@ F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 -F ext/jni/src/org/sqlite/jni/SQLFunction.java f6842d587c6366d4ebd565b533a288efc978d56ab2d4f52f2e74b64c8e6a33f3 +F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 17aebe15820b525678839c5e12160a7ca77b1691e59c8fb7c08b05757bea9553 -F ext/jni/src/org/sqlite/jni/Tester1.java 882755ac7a437ecde721084a0839b524199fa9857a7878d16a20848a7187093f +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cb3040fcfe35199bb10b4bca2cc541ca383563f85c9b460412c3bd15f413ae23 +F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c +F ext/jni/src/org/sqlite/jni/Tester1.java 1a9c6c8252d854398e37f5e4f8d9f66de0fd4767e60e43af26f4fdc681fab8b9 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee +F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c @@ -2100,8 +2103,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 8ca528006533ac152e9b322e421cf5d339414c77332d9836a9ff429b793d0d2d -R 5885805208dd5a9cb4303aef998be394 +P 5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 +R 7f0497e9841cedff7936a9c02791fa84 U stephan -Z 3728ae49651cf5a3340c3d5aa589c6ef +Z 3ac21aead20aa0563125716a8691ad77 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7f7db679d9..9e2cd43c62 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 \ No newline at end of file +21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 \ No newline at end of file From 6f8f587d2f28e28ba9fb65e70b22ac3fa170f053 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 10:20:38 +0000 Subject: [PATCH 064/422] Improve threading support for all types of JNI-side callback hooks, making them safe to invoke if another thread is busy replacing them. FossilOrigin-Name: f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114 --- ext/jni/src/c/sqlite3-jni.c | 589 ++++++++++-------- .../src/org/sqlite/jni/XDestroyCallback.java | 3 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 353 insertions(+), 255 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index c3cf36f752..45813e63bb 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -337,6 +337,9 @@ struct S3JniHook{ /* We lookup the jObj.xDestroy() method as-needed for contexts which ** have custom finalizers. */ }; +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK +static const S3JniHook S3JniHook_empty = {0,0}; +#endif /* ** Per-(sqlite3*) state for various JNI bindings. This state is @@ -465,12 +468,12 @@ struct S3JniGlobalType { ** Cache of per-thread state. */ struct { - S3JniEnv * aHead /* Linked list of in-use instances */; - S3JniEnv * aFree /* Linked list of free instances */; + 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. */; - void const * locker /* env mutex is held on this object's behalf. - Used only for sanity checking. */; + void const * locker /* env mutex is held on this object's behalf. + Used only for sanity checking. */; } envCache; /* ** Per-db state. This can move into the core library once we can tie @@ -513,6 +516,8 @@ struct S3JniGlobalType { int nExt /* number of active entries in pExt, all in the first nExt'th array elements. */; sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; + const void * locker /* object on whose behalf the mutex is held. + Only for sanity checking in debug builds. */; } autoExt; #ifdef SQLITE_ENABLE_FTS5 struct { @@ -599,10 +604,14 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Ext_enter \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ + SJG.autoExt.locker = env; \ s3jni_incr( &SJG.metrics.nMutexAutoExt ) #define S3JniMutex_Ext_leave \ /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ + assert( env == SJG.autoExt.locker ); \ sqlite3_mutex_leave( SJG.autoExt.mutex ) +#define S3JniMutex_Ext_assertLocker \ + assert( env == SJG.autoExt.locker ) #define S3JniMutex_Nph_enter \ S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ @@ -630,6 +639,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Env_assertNotLocker #define S3JniMutex_Env_enter #define S3JniMutex_Env_leave +#define S3JniMutex_Ext_assertLocker #define S3JniMutex_Ext_enter #define S3JniMutex_Ext_leave #define S3JniMutex_Nph_enter @@ -669,7 +679,7 @@ static JNIEnv * s3jni_env(void){ return env; } /* Declares local var env = s3jni_env(). */ -#define LocalJniGetEnv JNIEnv * const env = s3jni_env() +#define S3JniDeclLocal_env JNIEnv * const env = s3jni_env() /* ** Fetches the S3JniGlobal.envCache row for the given env, allocing a @@ -943,6 +953,30 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest memset(s, 0, sizeof(*s)); } +/* +** Internal helper for many hook callback impls. Locks the S3JniDb +** mutex, makes a copy of src into dest, with one change if src->jObj +** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj +** instead of a copy of the prior GLOBAL ref. Then unlocks the +** mutex. If dest->jObj is not NULL when this returns then the caller +** is obligated to eventually free the new ref by passing dest->jObj +** to S3JniUnrefLocal(). The dest pointer must NOT be passed to +** S3JniHook_unref(), as that one assumes that dest->jObj is a GLOBAL +** ref. +** +** Background: when running a hook we need a call-local copy lest +** another thread modify the hook while we're running it. That copy +** has to haves its own Java reference, but it need only be +** call-local. +*/ +static void S3JniHook_copy( JNIEnv * const env, S3JniHook const * const src, + S3JniHook * const dest ){ + S3JniMutex_S3JniDb_enter; + *dest = *src; + if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj); + S3JniMutex_S3JniDb_leave; +} + /* ** Clears s's state and moves it to the free-list. */ @@ -962,18 +996,18 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ } sqlite3_free( s->zMainDbName ); #define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) - UNHOOK(trace, 0); - UNHOOK(progress, 0); - UNHOOK(commit, 0); - UNHOOK(rollback, 0); - UNHOOK(update, 0); UNHOOK(auth, 0); + UNHOOK(busyHandler, 1); + UNHOOK(collation, 1); + UNHOOK(collationNeeded, 0); + UNHOOK(commit, 0); + UNHOOK(progress, 0); + UNHOOK(rollback, 0); + UNHOOK(trace, 0); + UNHOOK(update, 0); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK UNHOOK(preUpdate, 0); #endif - UNHOOK(collation, 1); - UNHOOK(collationNeeded, 0); - UNHOOK(busyHandler, 1); #undef UNHOOK S3JniUnrefGlobal(s->jDb); memset(s, 0, sizeof(S3JniDb)); @@ -1205,6 +1239,8 @@ static int S3JniAutoExtension_init(JNIEnv *const env, jobject const jAutoExt){ jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); + S3JniMutex_Ext_assertLocker; + assert( env == SJG.autoExt.locker ); ax->midFunc = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;)I"); S3JniUnrefLocal(klazz); @@ -1232,8 +1268,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env, ** misbehave. */ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, - const char * const zTypeSig, - jobject const jOut){ + const char * const zTypeSig){ S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; @@ -1252,7 +1287,7 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, */ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_Int32, "I", jOut + env, &S3NphRefs.OutputPointer_Int32, "I" ); (*env)->SetIntField(env, jOut, setter, (jint)v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); @@ -1264,7 +1299,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int */ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_Int64, "J", jOut + env, &S3NphRefs.OutputPointer_Int64, "J" ); (*env)->SetLongField(env, jOut, setter, v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); @@ -1277,7 +1312,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlon static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;", jOut + env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;" ); (*env)->SetObjectField(env, jOut, setter, jDb); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3.value"); @@ -1291,7 +1326,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu jobject jStmt){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_sqlite3_stmt, - "Lorg/sqlite/jni/sqlite3_stmt;", jOut + "Lorg/sqlite/jni/sqlite3_stmt;" ); (*env)->SetObjectField(env, jOut, setter, jStmt); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_stmt.value"); @@ -1306,7 +1341,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO jobject jValue){ jfieldID const setter = setupOutputPointer( env, &S3NphRefs.OutputPointer_sqlite3_value, - "Lorg/sqlite/jni/sqlite3_value;", jOut + "Lorg/sqlite/jni/sqlite3_value;" ); (*env)->SetObjectField(env, jOut, setter, jValue); S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_value.value"); @@ -1320,9 +1355,9 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO ** to v. */ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, - jbyteArray const v){ + jbyteArray const v){ jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_ByteArray, "[B", jOut + env, &S3NphRefs.OutputPointer_ByteArray, "[B" ); (*env)->SetObjectField(env, jOut, setter, v); S3JniExceptionIsFatal("Cannot set OutputPointer.ByteArray.value"); @@ -1334,9 +1369,9 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, ** v. */ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, - jstring const v){ + jstring const v){ jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;", jOut + env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;" ); (*env)->SetObjectField(env, jOut, setter, v); S3JniExceptionIsFatal("Cannot set OutputPointer.String.value"); @@ -1358,35 +1393,45 @@ static int encodingTypeIsValid(int eTextRep){ } /* -** Proxy for Java-side Collation.xCompare() callbacks. +** Proxy for Java-side CollationCallback.xCompare() callbacks. */ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, int nRhs, const void *rhs){ S3JniDb * const ps = pArg; - LocalJniGetEnv; + S3JniDeclLocal_env; jint rc = 0; - jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); - jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; - if( !jbaRhs ){ + S3JniHook hook; + + S3JniHook_copy(env, &ps->hooks.collation, &hook ); + if( hook.jObj ){ + jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); + jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; + if( !jbaRhs ){ + S3JniUnrefLocal(jbaLhs); + s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + return 0; + } + (*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->hooks.collation.jObj, + ps->hooks.collation.midCallback, + jbaLhs, jbaRhs); + S3JniExceptionIgnore; S3JniUnrefLocal(jbaLhs); - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); - return 0; + S3JniUnrefLocal(jbaRhs); + S3JniUnrefLocal(hook.jObj); } - (*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->hooks.collation.jObj, - ps->hooks.collation.midCallback, - jbaLhs, jbaRhs); - S3JniExceptionIgnore; - S3JniUnrefLocal(jbaLhs); - S3JniUnrefLocal(jbaRhs); return (int)rc; } /* Collation finalizer for use by the sqlite3 internals. */ static void CollationState_xDestroy(void *pArg){ S3JniDb * const ps = pArg; + S3JniDeclLocal_env; + + S3JniMutex_S3JniDb_enter; S3JniHook_unref( s3jni_env(), &ps->hooks.collation, 1 ); + S3JniMutex_S3JniDb_leave; } /* @@ -1424,7 +1469,7 @@ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ static void ResultJavaVal_finalizer(void *v){ if( v ){ ResultJavaVal * const rv = (ResultJavaVal*)v; - LocalJniGetEnv; + S3JniDeclLocal_env; S3JniUnrefGlobal(rv->jObj); sqlite3_free(rv); } @@ -1539,7 +1584,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ } static void S3JniUdf_free(S3JniUdf * s){ - LocalJniGetEnv; + S3JniDeclLocal_env; if( env ){ //MARKER(("UDF cleanup: %s\n", s->zFuncName)); s3jni_call_xDestroy(env, s->jObj); @@ -1652,7 +1697,7 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, S3JniUdf * const s, jmethodID xMethodID, const char * const zFuncType){ - LocalJniGetEnv; + S3JniDeclLocal_env; udf_jargs args = {0,0}; int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); @@ -1676,7 +1721,7 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jmethodID xMethodID, const char *zFuncType){ - LocalJniGetEnv; + S3JniDeclLocal_env; jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; int const isFinal = 'F'==zFuncType[1]/*xFinal*/; @@ -1737,57 +1782,57 @@ static void udf_xInverse(sqlite3_context* cx, int argc, //////////////////////////////////////////////////////////////////////// /** Create a trivial JNI wrapper for (int CName(void)). */ -#define WRAP_INT_VOID(JniNameSuffix,CName) \ +#define WRAP_INT_VOID(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass){ \ - return (jint)CName(); \ + return (jint)CName(); \ } /** Create a trivial JNI wrapper for (int CName(int)). */ -#define WRAP_INT_INT(JniNameSuffix,CName) \ - JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jint arg){ \ - return (jint)CName((int)arg); \ +#define WRAP_INT_INT(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jint arg){ \ + return (jint)CName((int)arg); \ } /* ** Create a trivial JNI wrapper for (const mutf8_string * ** CName(void)). This is only valid for functions which are known to ** return ASCII or text which is equivalent in UTF-8 and MUTF-8. */ -#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ - JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ - return (*env)->NewStringUTF( env, CName() ); \ +#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ + return (*env)->NewStringUTF( env, CName() ); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ -#define WRAP_INT_STMT(JniNameSuffix,CName) \ - JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpStmt){ \ - jint const rc = (jint)CName(PtrGet_sqlite3_stmt(jpStmt)); \ - S3JniExceptionIgnore /* squelch -Xcheck:jni */; \ +#define WRAP_INT_STMT(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpStmt){ \ + jint const rc = (jint)CName(PtrGet_sqlite3_stmt(jpStmt)); \ + S3JniExceptionIgnore /* squelch -Xcheck:jni */; \ return rc; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */ -#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ +#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint n){ \ - return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \ + return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \ } /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ -#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ - JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \ - return s3jni_utf8_to_jstring(env, \ +#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \ + return s3jni_utf8_to_jstring(env, \ CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ - -1); \ + -1); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3*)). */ -#define WRAP_INT_DB(JniNameSuffix,CName) \ - JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ - return (jint)CName(PtrGet_sqlite3(pDb)); \ +#define WRAP_INT_DB(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ + return (jint)CName(PtrGet_sqlite3(pDb)); \ } /** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ -#define WRAP_INT64_DB(JniNameSuffix,CName) \ - JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ - return (jlong)CName(PtrGet_sqlite3(pDb)); \ +#define WRAP_INT64_DB(JniNameSuffix,CName) \ + JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \ + return (jlong)CName(PtrGet_sqlite3(pDb)); \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ -#define WRAP_INT_SVALUE(JniNameSuffix,CName) \ - JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpSValue){ \ - return (jint)CName(PtrGet_sqlite3_value(jpSValue)); \ +#define WRAP_INT_SVALUE(JniNameSuffix,CName) \ + JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpSValue){ \ + return (jint)CName(PtrGet_sqlite3_value(jpSValue)); \ } WRAP_INT_STMT(1bind_1parameter_1count, sqlite3_bind_parameter_count) @@ -2044,15 +2089,19 @@ S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob64)( static int s3jni_busy_handler(void* pState, int n){ S3JniDb * const ps = (S3JniDb *)pState; int rc = 0; - if( ps->hooks.busyHandler.jObj ){ - LocalJniGetEnv; - rc = (*env)->CallIntMethod(env, ps->hooks.busyHandler.jObj, - ps->hooks.busyHandler.midCallback, (jint)n); + S3JniDeclLocal_env; + S3JniHook hook; + + S3JniHook_copy(env, &ps->hooks.busyHandler, &hook ); + if( hook.jObj ){ + rc = (*env)->CallIntMethod(env, hook.jObj, + hook.midCallback, (jint)n); S3JniIfThrew{ S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback"); rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_busy_handler() callback threw."); } + S3JniUnrefLocal(hook.jObj); } return rc; } @@ -2061,12 +2110,14 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( JniArgsEnvClass, jobject jDb, jobject jBusy ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0; int rc = 0; - if( !ps ) return (jint)SQLITE_NOMEM; + if( !ps ) return (jint)SQLITE_MISUSE; + S3JniMutex_S3JniDb_enter; if( jBusy ){ - S3JniHook * const pHook = &ps->hooks.busyHandler; if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){ /* Same object - this is a no-op. */ + S3JniMutex_S3JniDb_leave; return 0; } jclass klazz; @@ -2079,15 +2130,16 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( S3JniHook_unref(env, pHook, 0); rc = SQLITE_ERROR; } - if( rc ){ - return rc; - } }else{ - S3JniHook_unref(env, &ps->hooks.busyHandler, 1); + S3JniHook_unref(env, pHook, 1); } - return jBusy - ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) - : sqlite3_busy_handler(ps->pDb, 0, 0); + if( 0==rc ){ + rc = jBusy + ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) + : sqlite3_busy_handler(ps->pDb, 0, 0); + } + S3JniMutex_S3JniDb_leave; + return rc; } S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( @@ -2172,20 +2224,26 @@ static unsigned int s3jni_utf16_strlen(void const * z){ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, int eTextRep, const void * z16Name){ S3JniDb * const ps = pState; - LocalJniGetEnv; - unsigned int const nName = s3jni_utf16_strlen(z16Name); - jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); - S3JniIfThrew{ - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); - S3JniExceptionClear; - }else{ - (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj, - ps->hooks.collationNeeded.midCallback, - ps->jDb, (jint)eTextRep, jName); + S3JniDeclLocal_env; + S3JniHook hook; + + S3JniHook_copy(env, &ps->hooks.collationNeeded, &hook ); + if( hook.jObj ){ + unsigned int const nName = s3jni_utf16_strlen(z16Name); + jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); S3JniIfThrew{ - s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); + s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + S3JniExceptionClear; + }else{ + (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj, + ps->hooks.collationNeeded.midCallback, + ps->jDb, (jint)eTextRep, jName); + S3JniIfThrew{ + s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); + } + S3JniUnrefLocal(jName); } - S3JniUnrefLocal(jName); + S3JniUnrefLocal(hook.jObj); } } @@ -2193,38 +2251,38 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( JniArgsEnvClass, jobject jDb, jobject jHook ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - jclass klazz; - jobject pOld = 0; - jmethodID xCallback; S3JniHook * const pHook = &ps->hooks.collationNeeded; - int rc; + int rc = 0; if( !ps ) return SQLITE_MISUSE; - pOld = pHook->jObj; - if( pOld && jHook && - (*env)->IsSameObject(env, pOld, jHook) ){ - return 0; - } - if( !jHook ){ - S3JniUnrefGlobal(pOld); - memset(pHook, 0, sizeof(S3JniHook)); + S3JniMutex_S3JniDb_enter; + if( pHook->jObj && jHook && + (*env)->IsSameObject(env, pHook->jObj, jHook) ){ + /* no-op */ + }else if( !jHook ){ + S3JniHook_unref(env, pHook, 0); sqlite3_collation_needed(ps->pDb, 0, 0); - return 0; - } - klazz = (*env)->GetObjectClass(env, jHook); - xCallback = (*env)->GetMethodID(env, klazz, "call", - "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"); - S3JniUnrefLocal(klazz); - S3JniIfThrew { - rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, - "Cannot not find matching callback on " - "collation-needed hook object."); }else{ - pHook->midCallback = xCallback; - pHook->jObj = S3JniRefGlobal(jHook); - S3JniUnrefGlobal(pOld); - rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); + jclass const klazz = (*env)->GetObjectClass(env, jHook); + jmethodID const xCallback = (*env)->GetMethodID( + env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I" + ); + S3JniUnrefLocal(klazz); + S3JniIfThrew { + rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, + "Cannot not find matching callback on " + "collation-needed hook object."); + }else{ + rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); + if( rc ){ + }else{ + S3JniHook_unref(env, pHook, 0); + pHook->midCallback = xCallback; + pHook->jObj = S3JniRefGlobal(jHook); + } + } } + S3JniMutex_S3JniDb_leave; return rc; } @@ -2281,21 +2339,30 @@ S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( S3JniApi(sqlite3_column_value(),jobject,1column_1value)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ - sqlite3_value * const sv = sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); + sqlite3_value * const sv = + sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); return new_sqlite3_value_wrapper(env, sv); } -static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ - LocalJniGetEnv; - int rc = isCommit - ? (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); - S3JniIfThrew{ - S3JniExceptionClear; - rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); +static int s3jni_commit_rollback_hook_impl(int isCommit, + S3JniDb * const ps){ + S3JniDeclLocal_env; + int rc = 0; + S3JniHook hook; + + S3JniHook_copy( env, + isCommit ? &ps->hooks.commit : &ps->hooks.rollback, + &hook); + if( hook.jObj ){ + rc = isCommit + ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) + : (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0); + S3JniIfThrew{ + S3JniExceptionClear; + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); + } + S3JniUnrefLocal(hook.jObj); } return rc; } @@ -2400,7 +2467,7 @@ S3JniApi(sqlite3_config() /*for a small subset of options.*/, static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){ jobject jArg0 = 0; jstring jArg1 = 0; - LocalJniGetEnv; + S3JniDeclLocal_env; S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); S3JniHook * const hook = &SJG.hooks.sqllog; @@ -2431,9 +2498,11 @@ void sqlite3_init_sqllog(void){ } #endif -S3JniApi(sqlite3_config(/* for SQLLOG */), +S3JniApi(sqlite3_config() /* for SQLLOG */, jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){ -#ifdef SQLITE_ENABLE_SQLLOG +#ifndef SQLITE_ENABLE_SQLLOG + return SQLITE_MISUSE; +#else S3JniHook tmpHook; S3JniHook * const hook = &tmpHook; S3JniHook * const hookOld = & SJG.hooks.sqllog; @@ -2466,9 +2535,6 @@ S3JniApi(sqlite3_config(/* for SQLLOG */), *hookOld = *hook; } return rc; -#else - MARKER(("Warning: built without SQLITE_ENABLE_SQLLOG.\n")); - return SQLITE_MISUSE; #endif } @@ -2482,32 +2548,35 @@ S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), jint,1create_1collation -)(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ +)(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, + jobject oCollation){ int rc; const char *zName; jclass klazz; S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); - S3JniHook * const pHook = ps ? &ps->hooks.collation : 0; + jmethodID midCallback; - if( !pHook ) return SQLITE_MISUSE; + if( !ps ) return SQLITE_MISUSE; klazz = (*env)->GetObjectClass(env, oCollation); - pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", - "([B[B)I"); + midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); S3JniUnrefLocal(klazz); S3JniIfThrew{ S3JniUnrefLocal(klazz); - return s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Could not get xCompare() method for object."); - } - zName = s3jni_jstring_to_mutf8(name); - rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, - ps, CollationState_xCompare, - CollationState_xDestroy); - s3jni_mutf8_release(name, zName); - if( 0==rc ){ - pHook->jObj = S3JniRefGlobal(oCollation); + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Could not get xCompare() method for object."); }else{ - S3JniHook_unref(env, pHook, 1); + zName = s3jni_jstring_to_mutf8(name); + rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, + ps, CollationState_xCompare, + CollationState_xDestroy); + s3jni_mutf8_release(name, zName); + if( 0==rc ){ + S3JniMutex_S3JniDb_enter; + S3JniHook_unref( env, &ps->hooks.collation, 1 ); + ps->hooks.collation.midCallback = midCallback; + ps->hooks.collation.jObj = S3JniRefGlobal(oCollation); + S3JniMutex_S3JniDb_leave; + } } return (jint)rc; } @@ -2564,7 +2633,7 @@ error_cleanup: return (jint)rc; } -S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/), +S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/, jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); @@ -2573,6 +2642,9 @@ S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/), switch( (ps && jStr) ? op : 0 ){ case SQLITE_DBCONFIG_MAINDBNAME: + S3JniMutex_S3JniDb_enter + /* Protect against a race in modifying/freeing + ps->zMainDbName. */; zStr = s3jni_jstring_to_utf8(env, jStr, 0); if( zStr ){ rc = sqlite3_db_config(ps->pDb, (int)op, zStr); @@ -2585,6 +2657,7 @@ S3JniApi(sqlite3_db_config(/*for MAINDBNAME*/), }else{ rc = SQLITE_NOMEM; } + S3JniMutex_S3JniDb_leave; break; default: rc = SQLITE_MISUSE; @@ -2909,8 +2982,8 @@ jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self, int rc = SQLITE_ERROR; assert(prepVersion==1 || prepVersion==2 || prepVersion==3); if( !pBuf ){ - rc = baSql ? SQLITE_MISUSE : SQLITE_NOMEM; - goto end; + rc = baSql ? SQLITE_NOMEM : SQLITE_MISUSE; + goto end; } jStmt = new_sqlite3_stmt_wrapper(env, 0); if( !jStmt ){ @@ -2995,37 +3068,38 @@ 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; + S3JniDeclLocal_env; jstring jDbName; jstring jTable; - S3JniHook * pHook; const int isPre = 0!=pDb; + S3JniHook hook; - pHook = isPre ? + S3JniHook_copy(env, isPre ? #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - &ps->hooks.preUpdate + &ps->hooks.preUpdate #else - 0 + &S3JniHook_empty #endif - : &ps->hooks.update; - - assert( pHook ); + : &ps->hooks.update, &hook); + if( !hook.jObj ){ + return; + } jDbName = s3jni_utf8_to_jstring(env, zDb, -1); jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0; S3JniIfThrew { S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); }else{ - assert( pHook->jObj ); - assert( pHook->midCallback ); + assert( hook.jObj ); + assert( hook.midCallback ); assert( ps->jDb ); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - if( isPre ) (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, + if( isPre ) (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, ps->jDb, (jint)opId, jDbName, jTable, (jlong)iKey1, (jlong)iKey2); else #endif - (*env)->CallVoidMethod(env, pHook->jObj, pHook->midCallback, + (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, (jint)opId, jDbName, jTable, (jlong)iKey1); S3JniIfThrew{ S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback"); @@ -3035,6 +3109,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, } S3JniUnrefLocal(jDbName); S3JniUnrefLocal(jTable); + S3JniUnrefLocal(hook.jObj); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -3070,21 +3145,23 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec jclass klazz; jobject pOld = 0; jmethodID xCallback; - S3JniHook * pHook = ps ? ( - isPre ? + S3JniHook * pHook; + + if( !ps ) return 0; + S3JniMutex_S3JniDb_enter; + pHook = isPre ? #ifdef SQLITE_ENABLE_PREUPDATE_HOOK &ps->hooks.preUpdate #else 0 #endif - : &ps->hooks.update) : 0; - + : &ps->hooks.update; if( !pHook ){ - return 0; + goto end; } pOld = pHook->jObj; if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){ - return pOld; + goto end; } if( !jHook ){ if( pOld ){ @@ -3098,7 +3175,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec else #endif sqlite3_update_hook(ps->pDb, 0, 0); - return pOld; + goto end; } klazz = (*env)->GetObjectClass(env, jHook); xCallback = isPre @@ -3130,6 +3207,8 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec pOld = tmp; } } +end: + S3JniMutex_S3JniDb_leave; return pOld; } @@ -3185,12 +3264,18 @@ S3JniApi(sqlite3_preupdate_old(),jint,1preupdate_1old)( /* 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->hooks.progress.jObj, - ps->hooks.progress.midCallback); - S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, rc, - "sqlite3_progress_handler() callback threw"); + int rc = 0; + S3JniDeclLocal_env; + S3JniHook hook; + + S3JniHook_copy( env, &ps->hooks.progress, &hook ); + if( hook.jObj ){ + rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback); + S3JniIfThrew{ + rc = s3jni_db_exception(env, ps, rc, + "sqlite3_progress_handler() callback threw"); + } + S3JniUnrefLocal(hook.jObj); } return rc; } @@ -3198,19 +3283,17 @@ static int s3jni_progress_handler_impl(void *pP){ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( JniArgsEnvClass,jobject jDb, jint n, jobject jProgress ){ - S3JniDb * ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jclass klazz; jmethodID xCallback; + S3JniHook * const pHook = ps ? &ps->hooks.progress : 0; + + if( !ps ) return; + S3JniMutex_S3JniDb_enter; if( n<1 || !jProgress ){ - if( ps ){ - S3JniUnrefGlobal(ps->hooks.progress.jObj); - memset(&ps->hooks.progress, 0, sizeof(ps->hooks.progress)); - } + S3JniHook_unref(env, pHook, 0); sqlite3_progress_handler(ps->pDb, 0, 0, 0); - return; - } - if( !ps ){ - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + S3JniMutex_S3JniDb_leave; return; } klazz = (*env)->GetObjectClass(env, jProgress); @@ -3222,22 +3305,19 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( "Cannot not find matching xCallback() on " "ProgressHandler object."); }else{ - S3JniUnrefGlobal(ps->hooks.progress.jObj); - ps->hooks.progress.midCallback = xCallback; - ps->hooks.progress.jObj = S3JniRefGlobal(jProgress); + S3JniUnrefGlobal(pHook->jObj); + pHook->midCallback = xCallback; + pHook->jObj = S3JniRefGlobal(jProgress); sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); } + S3JniMutex_S3JniDb_leave; } S3JniApi(sqlite3_reset(),jint,1reset)( JniArgsEnvClass, jobject jpStmt ){ - int rc = 0; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); - if( pStmt ){ - rc = sqlite3_reset(pStmt); - } - return rc; + return pStmt ? sqlite3_reset(pStmt) : SQLITE_MISUSE; } /* Clears all entries from S3JniGlobal.autoExt. */ @@ -3479,24 +3559,28 @@ S3JniApi(sqlite3_rollback_hook(),jobject,1rollback_1hook)( int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, const char*z2,const char*z3){ S3JniDb * const ps = pState; - LocalJniGetEnv; - S3JniHook const * const pHook = &ps->hooks.auth; - jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; - jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; - jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0; - jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0; - int rc; + S3JniDeclLocal_env; + S3JniHook hook; + int rc = 0; - assert( pHook->jObj ); - rc = (*env)->CallIntMethod(env, pHook->jObj, pHook->midCallback, (jint)op, - s0, s1, s3, s3); - S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); + S3JniHook_copy(env, &ps->hooks.auth, &hook ); + if( hook.jObj ){ + jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; + jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; + jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0; + jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0; + + rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op, + s0, s1, s3, s3); + S3JniIfThrew{ + rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); + } + S3JniUnrefLocal(s0); + S3JniUnrefLocal(s1); + S3JniUnrefLocal(s2); + S3JniUnrefLocal(s3); + S3JniUnrefLocal(hook.jObj); } - S3JniUnrefLocal(s0); - S3JniUnrefLocal(s1); - S3JniUnrefLocal(s2); - S3JniUnrefLocal(s3); return rc; } @@ -3505,17 +3589,19 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; + int rc = 0; if( !ps ) return SQLITE_MISUSE; - else if( !jHook ){ + S3JniMutex_S3JniDb_enter; + if( !jHook ){ S3JniHook_unref(env, pHook, 0); - return (jint)sqlite3_set_authorizer( ps->pDb, 0, 0 ); + rc = sqlite3_set_authorizer( ps->pDb, 0, 0 ); }else{ - int rc = 0; jclass klazz; if( pHook->jObj ){ if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){ /* Same object - this is a no-op. */ + S3JniMutex_S3JniDb_leave; return 0; } S3JniHook_unref(env, pHook, 0); @@ -3533,13 +3619,15 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( S3JniUnrefLocal(klazz); S3JniIfThrew { S3JniHook_unref(env, pHook, 0); - return s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Error setting up Java parts of authorizer hook."); + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Error setting up Java parts of authorizer hook."); + }else{ + rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); + if( rc ) S3JniHook_unref(env, pHook, 0); } - rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); - if( rc ) S3JniHook_unref(env, pHook, 0); - return rc; } + S3JniMutex_S3JniDb_leave; + return rc; } @@ -3658,15 +3746,21 @@ S3JniApi(sqlite3_step(),jint,1step)( static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ S3JniDb * const ps = (S3JniDb *)pC; - LocalJniGetEnv; + S3JniDeclLocal_env; jobject jX = NULL /* the tracer's X arg */; jobject jP = NULL /* the tracer's P arg */; jobject jPUnref = NULL /* potentially a local ref to jP */; - int rc; + int rc = 0; + S3JniHook hook; + + S3JniHook_copy( env, &ps->hooks.trace, &hook ); + if( !hook.jObj ){ + return 0; + } switch( traceflag ){ case SQLITE_TRACE_STMT: jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); - if( !jX ) return SQLITE_NOMEM; + if( !jX ) rc = SQLITE_NOMEM; /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ break; case SQLITE_TRACE_PROFILE: @@ -3674,7 +3768,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ (jlong)*((sqlite3_int64*)pX)); // hmm. ^^^ (*pX) really is zero. // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); - if( !jX ) return SQLITE_NOMEM; + if( !jX ) rc = SQLITE_NOMEM; break; case SQLITE_TRACE_ROW: break; @@ -3683,26 +3777,28 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ break; default: assert(!"cannot happen - unkown trace flag"); - return SQLITE_ERROR; + rc = SQLITE_ERROR; } - if( !jP ){ - /* Create a new temporary sqlite3_stmt wrapper */ - jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); + if( 0==rc ){ if( !jP ){ - S3JniUnrefLocal(jX); - return SQLITE_NOMEM; + /* Create a new temporary sqlite3_stmt wrapper */ + jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); + if( !jP ){ + S3JniUnrefLocal(jX); + rc = SQLITE_NOMEM; + } + } + assert(jP); + rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback, + (jint)traceflag, jP, jX); + S3JniIfThrew{ + rc = s3jni_db_exception(env, ps, SQLITE_ERROR, + "sqlite3_trace_v2() callback threw."); } - } - assert(jP); - rc = (int)(*env)->CallIntMethod(env, ps->hooks.trace.jObj, - ps->hooks.trace.midCallback, - (jint)traceflag, jP, jX); - S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, SQLITE_ERROR, - "sqlite3_trace_v2() callback threw."); } S3JniUnrefLocal(jPUnref); S3JniUnrefLocal(jX); + S3JniUnrefLocal(hook.jObj); return rc; } @@ -3938,7 +4034,7 @@ typedef struct { } Fts5JniAux; static void Fts5JniAux_free(Fts5JniAux * const s){ - LocalJniGetEnv; + S3JniDeclLocal_env; if( env ){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ s3jni_call_xDestroy(env, s->jObj); @@ -4102,7 +4198,7 @@ static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, jobject jpFts = 0; jobject jFXA; int rc; - LocalJniGetEnv; + S3JniDeclLocal_env; assert(pAux); jFXA = s3jni_getFts5ExensionApi(env); @@ -4169,7 +4265,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){ if( x ){ S3JniFts5AuxData * const p = x; if( p->jObj ){ - LocalJniGetEnv; + S3JniDeclLocal_env; s3jni_call_xDestroy(env, p->jObj); S3JniUnrefGlobal(p->jObj); } @@ -4327,7 +4423,7 @@ static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, guaranteed to be the same one passed to xQueryPhrase(). If it's not, we'll have to create a new wrapper object on every call. */ struct s3jni_xQueryPhraseState const * s = pData; - LocalJniGetEnv; + S3JniDeclLocal_env; int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, SJG.fts5.jFtsExt, s->jFcx); S3JniIfThrew{ @@ -4396,9 +4492,10 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, int nZ, int iStart, int iEnd){ int rc; - LocalJniGetEnv; + S3JniDeclLocal_env; struct s3jni_xQueryPhraseState * const s = p; jbyteArray jba; + if( s->tok.zPrev == z && s->tok.nPrev == nZ ){ jba = s->tok.jba; }else{ diff --git a/ext/jni/src/org/sqlite/jni/XDestroyCallback.java b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java index 339508c088..d1e4b1ee3c 100644 --- a/ext/jni/src/org/sqlite/jni/XDestroyCallback.java +++ b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java @@ -22,7 +22,8 @@ package org.sqlite.jni; public interface XDestroyCallback { /** Must perform any cleanup required by this object. Must not - throw. + throw. Must not call back into the sqlite3 API, else it might + invoke a deadlock. */ public void xDestroy(); } diff --git a/manifest b/manifest index 99bb002623..b2f4d0e247 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sthe\s3\sJava\sSQLFunction\ssubclasses\sfrom\sinner\sclasses\sto\spackage\sscope. -D 2023-08-25T16:43:51.353 +C Improve\sthreading\ssupport\sfor\sall\stypes\sof\sJNI-side\scallback\shooks,\smaking\sthem\ssafe\sto\sinvoke\sif\sanother\sthread\sis\sbusy\sreplacing\sthem. +D 2023-08-26T10:20:38.261 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 29c10d96f81361b0d121e389320a1dd57958fd758e3790817542d2eb20c42bed +F ext/jni/src/c/sqlite3-jni.c b34328504aa8a3e761e097ac3454a665dcb770e577d09f665191d98fe4a5a7b6 F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -270,7 +270,7 @@ F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 -F ext/jni/src/org/sqlite/jni/XDestroyCallback.java a43c6fad4d550c40d7ad2545565dd794df68aae855a7a6fe2d5f57ccbfc0e7d6 +F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471 F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa @@ -2103,8 +2103,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 5786b95f5d09b4462aff0fdeac37992a2b64c47b004d18960f51e4e6a5796106 -R 7f0497e9841cedff7936a9c02791fa84 +P 21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 +R b5acfb602720e59e7b14d5c62b0da1bf U stephan -Z 3ac21aead20aa0563125716a8691ad77 +Z 1a4a2d91841b9730ebb4db624f2ef288 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9e2cd43c62..e19b4b0c78 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 \ No newline at end of file +f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114 \ No newline at end of file From d9cf47e377366ff3d8ffc21321cf23eea75145d9 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 10:51:19 +0000 Subject: [PATCH 065/422] Remove the Java BusyHandler.xDestroy() method - it should not have had one. Eliminate the last of the potentially-significant MUTF-8 cases. FossilOrigin-Name: c852f1ebbde273c3d28fe5aff0bf73cfc06b41dd371a94d7520536dc7a1dbcc1 --- ext/jni/src/c/sqlite3-jni.c | 52 +++++++++++-------- .../org/sqlite/jni/BusyHandlerCallback.java | 19 +------ ext/jni/src/org/sqlite/jni/Tester1.java | 7 --- manifest | 16 +++--- manifest.uuid | 2 +- 5 files changed, 39 insertions(+), 57 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 45813e63bb..1446992c95 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -997,7 +997,7 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ sqlite3_free( s->zMainDbName ); #define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) UNHOOK(auth, 0); - UNHOOK(busyHandler, 1); + UNHOOK(busyHandler, 0); UNHOOK(collation, 1); UNHOOK(collationNeeded, 0); UNHOOK(commit, 0); @@ -2121,7 +2121,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( return 0; } jclass klazz; - S3JniHook_unref(env, pHook, 1); + S3JniHook_unref(env, pHook, 0); pHook->jObj = S3JniRefGlobal(jBusy); klazz = (*env)->GetObjectClass(env, jBusy); pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); @@ -2131,7 +2131,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( rc = SQLITE_ERROR; } }else{ - S3JniHook_unref(env, pHook, 1); + S3JniHook_unref(env, pHook, 0); } if( 0==rc ){ rc = jBusy @@ -2146,11 +2146,14 @@ S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( JniArgsEnvClass, jobject jDb, jint ms ){ S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + int rc = SQLITE_MISUSE; if( ps ){ - S3JniHook_unref(env, &ps->hooks.busyHandler, 1); - return sqlite3_busy_timeout(ps->pDb, (int)ms); + S3JniMutex_S3JniDb_enter; + S3JniHook_unref(env, &ps->hooks.busyHandler, 0); + rc = sqlite3_busy_timeout(ps->pDb, (int)ms); + S3JniMutex_S3JniDb_leave; } - return SQLITE_MISUSE; + return rc; } S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( @@ -2551,7 +2554,6 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), )(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ int rc; - const char *zName; jclass klazz; S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); jmethodID midCallback; @@ -2565,17 +2567,21 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); }else{ - zName = s3jni_jstring_to_mutf8(name); - rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, - ps, CollationState_xCompare, - CollationState_xDestroy); - s3jni_mutf8_release(name, zName); - if( 0==rc ){ + char * const zName = s3jni_jstring_to_utf8(env, name, 0); + if( zName ){ S3JniMutex_S3JniDb_enter; - S3JniHook_unref( env, &ps->hooks.collation, 1 ); - ps->hooks.collation.midCallback = midCallback; - ps->hooks.collation.jObj = S3JniRefGlobal(oCollation); + rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, + ps, CollationState_xCompare, + CollationState_xDestroy); + sqlite3_free(zName); + if( 0==rc ){ + S3JniHook_unref( env, &ps->hooks.collation, 1 ); + ps->hooks.collation.midCallback = midCallback; + ps->hooks.collation.jObj = S3JniRefGlobal(oCollation); + } S3JniMutex_S3JniDb_leave; + }else{ + rc = SQLITE_NOMEM; } } return (jint)rc; @@ -2627,9 +2633,9 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_w xFunc, xStep, xFinal, S3JniUdf_finalizer); } error_cleanup: + /* Reminder: on sqlite3_create_function() error, s will be + ** destroyed via create_function(). */ sqlite3_free(zFuncName); - /* on sqlite3_create_function() error, s will be destroyed via - ** create_function(), so we're not leaking s. */ return (jint)rc; } @@ -4228,11 +4234,11 @@ JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, jobject jUserData, jobject jFunc){ fts5_api * const pApi = PtrGet_fts5_api(jSelf); int rc; - char const * zName; + char * zName; Fts5JniAux * pAux; assert(pApi); - zName = s3jni_jstring_to_mutf8(jName); + zName = s3jni_jstring_to_utf8(env, jName, 0); if(!zName) return SQLITE_NOMEM; pAux = Fts5JniAux_alloc(env, jFunc); if( pAux ){ @@ -4244,10 +4250,10 @@ JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, } if( 0==rc ){ pAux->jUserData = jUserData ? S3JniRefGlobal(jUserData) : 0; - pAux->zFuncName = sqlite3_mprintf("%s", zName) - /* OOM here is non-fatal. Ignore it. */; + pAux->zFuncName = zName; + }else{ + sqlite3_free(zName); } - s3jni_mutf8_release(jName, zName); return (jint)rc; } diff --git a/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java index e8740865e4..77180e2c24 100644 --- a/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java +++ b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java @@ -17,8 +17,7 @@ package org.sqlite.jni; /** Callback for use with sqlite3_busy_handler() */ -public abstract class BusyHandlerCallback - implements SQLite3CallbackProxy, XDestroyCallback { +public abstract class BusyHandlerCallback implements SQLite3CallbackProxy { /** Must function as documented for the C-level sqlite3_busy_handler() callback argument, minus the (void*) @@ -28,20 +27,4 @@ public abstract class BusyHandlerCallback retain the C-style API semantics of the JNI bindings. */ public abstract int call(int n); - - /** - Optionally override to perform any cleanup when this busy - handler is destroyed. It is destroyed when: - - - The associated db is passed to sqlite3_close() or - sqlite3_close_v2(). - - - sqlite3_busy_handler() is called to replace the handler, - whether it's passed a null handler or any other instance of - this class. - - - sqlite3_busy_timeout() is called, which implicitly installs - a busy handler. - */ - public void xDestroy(){} } diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 5d3712f2d6..bd585528cc 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -940,31 +940,24 @@ public class Tester1 implements Runnable { rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); - final ValueHolder xDestroyed = new ValueHolder<>(false); final ValueHolder xBusyCalled = new ValueHolder<>(0); BusyHandlerCallback handler = new BusyHandlerCallback(){ @Override public int call(int n){ //outln("busy handler #"+n); return n > 2 ? 0 : ++xBusyCalled.value; } - @Override public void xDestroy(){ - xDestroyed.value = true; - } }; rc = sqlite3_busy_handler(db2, handler); affirm(0 == rc); // Force a locked condition... execSql(db1, "BEGIN EXCLUSIVE"); - affirm(!xDestroyed.value); rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt); affirm( SQLITE_BUSY == rc); assert( null == outStmt.get() ); affirm( 3 == xBusyCalled.value ); sqlite3_close_v2(db1); - affirm(!xDestroyed.value); sqlite3_close_v2(db2); - affirm(xDestroyed.value); try{ final java.io.File f = new java.io.File(dbName); f.delete(); diff --git a/manifest b/manifest index b2f4d0e247..db9eb67524 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sthreading\ssupport\sfor\sall\stypes\sof\sJNI-side\scallback\shooks,\smaking\sthem\ssafe\sto\sinvoke\sif\sanother\sthread\sis\sbusy\sreplacing\sthem. -D 2023-08-26T10:20:38.261 +C Remove\sthe\sJava\sBusyHandler.xDestroy()\smethod\s-\sit\sshould\snot\shave\shad\sone.\sEliminate\sthe\slast\sof\sthe\spotentially-significant\sMUTF-8\scases. +D 2023-08-26T10:51:19.217 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,12 +236,12 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c b34328504aa8a3e761e097ac3454a665dcb770e577d09f665191d98fe4a5a7b6 +F ext/jni/src/c/sqlite3-jni.c 86a9f9182e8c0b1f058196ac4017f3290b948bcfbea1bc55198bdd0ad10a2ea1 F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 -F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java 99248b76e9b124f60a25329309ac6a03c197b3e1a493e8f8ae2e577651ea58e5 +F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java efef1892e404f5780d81c46a7997cab874aff5db5131985dd3af319fc5e3adc7 F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cb3040fcfe35199bb10b4bca2cc541ca383563f85c9b460412c3bd15f413ae23 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java 1a9c6c8252d854398e37f5e4f8d9f66de0fd4767e60e43af26f4fdc681fab8b9 +F ext/jni/src/org/sqlite/jni/Tester1.java 2921142fff8cd5a09d1cee30853457926dc63e647df9a687265bb4e678bc9570 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 21fd47a68db9df1828f4cc4131d326a193b5751d56a40ae77ed0a78dc0621af1 -R b5acfb602720e59e7b14d5c62b0da1bf +P f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114 +R 0a5c5021d2a5ccca7e44db0ac1e86f8f U stephan -Z 1a4a2d91841b9730ebb4db624f2ef288 +Z 3f70be94b4b4f3d48d8c6e932937aea1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index e19b4b0c78..8ec48b7eeb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114 \ No newline at end of file +c852f1ebbde273c3d28fe5aff0bf73cfc06b41dd371a94d7520536dc7a1dbcc1 \ No newline at end of file From 3ff458d61e8c428e40fb4e5a26adbd35b4c8624b Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 11:57:34 +0000 Subject: [PATCH 066/422] JNI internal cleanups and docs. FossilOrigin-Name: b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6 --- ext/jni/src/c/sqlite3-jni.c | 173 ++++++++++++--------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 12 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 119 insertions(+), 82 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 1446992c95..d3927f6256 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -233,8 +233,8 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ #define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR)) #define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) -/** - Keys for use with S3JniGlobal_nph_cache(). +/* +** Key type for use with S3JniGlobal_nph_cache(). */ typedef struct S3NphRef S3NphRef; struct S3NphRef { @@ -242,10 +242,11 @@ struct S3NphRef { const char * const zName /* Full Java name of the class */; }; -/** - Keys for each concrete NativePointerHolder subclass. These are to - be used with S3JniGlobal_nph_cache() and friends. These are - initialized on-demand by S3JniGlobal_nph_cache(). +/* +** Cache keys for each concrete NativePointerHolder subclass and +** OutputPointer type. The members are to be used with +** S3JniGlobal_nph_cache() and friends, and each one's member->index +** corresponds to its index in the S3JniGlobal.nph[] array. */ static const struct { const S3NphRef sqlite3; @@ -289,21 +290,14 @@ static const struct { #undef NREF }; -/* Helpers for jstring and jbyteArray. */ -#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) -#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) -#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) -#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) - enum { /* ** Size of the NativePointerHolder cache. Need enough space for - ** (only) the library's NativePointerHolder types, a fixed count - ** known at build-time. If we add more than this a fatal error will - ** be triggered with a reminder to increase this. This value needs - ** to be exactly the number of entries in the S3NphRefs object. The - ** index field of those entries are the keys for this particular - ** cache. + ** (only) the library's NativePointerHolder and OutputPointer types, + ** a fixed count known at build-time. This value needs to be + ** exactly the number of S3NphRef entries in the S3NphRefs + ** object. The index field of those entries are the keys for this + ** particular cache. */ S3Jni_NphCache_size = sizeof(S3NphRefs) / sizeof(S3NphRef) }; @@ -315,15 +309,15 @@ enum { typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { volatile const S3NphRef * pRef /* Entry from S3NphRefs. */; - jclass klazz /* global ref to the concrete - ** NativePointerHolder subclass represented by - ** zClassName */; - volatile jmethodID midCtor /* klazz's no-arg constructor. Used by - ** new_NativePointerHolder_object(). */; - volatile jfieldID fidValue /* NativePointerHolder.nativePointer or - ** OutputPointer.T.value */; - volatile jfieldID fidAggCtx /* sqlite3_context::aggregateContext. Used only - ** by the sqlite3_context binding. */; + jclass klazz /* global ref to the concrete + ** NativePointerHolder subclass + ** represented by zClassName */; + volatile jmethodID midCtor /* klazz's no-arg constructor. Used by + ** new_NativePointerHolder_object(). */; + volatile jfieldID fidValue /* NativePointerHolder.nativePointer or + ** OutputPointer.T.value */; + volatile jfieldID fidAggCtx /* sqlite3_context.aggregateContext, used only + ** by the sqlite3_context binding. */; }; /* @@ -337,7 +331,7 @@ struct S3JniHook{ /* We lookup the jObj.xDestroy() method as-needed for contexts which ** have custom finalizers. */ }; -#ifndef SQLITE_ENABLE_PREUPDATE_HOOK +#if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) || defined(SQLITE_ENABLE_SQLLOG) static const S3JniHook S3JniHook_empty = {0,0}; #endif @@ -459,6 +453,7 @@ struct S3JniGlobalType { ** */ JavaVM * jvm; + sqlite3_mutex * mutex; /* ** Cache of Java refs and method IDs for NativePointerHolder ** subclasses. Initialized on demand. @@ -546,6 +541,7 @@ struct S3JniGlobalType { a S3JniNphClass operation. */; volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; + volatile unsigned nMutexGlobal /* number of times global mutex was entered. */; volatile unsigned nDestroy /* xDestroy() calls across all types */; volatile unsigned nPdbAlloc /* Number of S3JniDb alloced. */; volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */; @@ -601,6 +597,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) + #define S3JniMutex_Ext_enter \ /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ @@ -612,6 +609,15 @@ static void s3jni_incr( volatile unsigned int * const p ){ sqlite3_mutex_leave( SJG.autoExt.mutex ) #define S3JniMutex_Ext_assertLocker \ assert( env == SJG.autoExt.locker ) + +#define S3JniMutex_Global_enter \ + /*MARKER(("Entering GLOBAL mutex@%p %s.\n", env));*/ \ + sqlite3_mutex_enter( SJG.mutex ); \ + s3jni_incr(&SJG.metrics.nMutexGlobal); +#define S3JniMutex_Global_leave \ + /*MARKER(("Leaving GLOBAL mutex @%p %s.\n", env));*/ \ + sqlite3_mutex_leave( SJG.mutex ) + #define S3JniMutex_Nph_enter \ S3JniMutex_Env_assertNotLocker; \ /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ @@ -623,6 +629,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) + #define S3JniMutex_S3JniDb_enter \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ assert( 0==SJG.perDb.locker ); \ @@ -633,6 +640,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ assert( env == SJG.perDb.locker ); \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) + #else /* SQLITE_THREADSAFE==0 */ #define S3JniMutex_Env_assertLocked #define S3JniMutex_Env_assertLocker @@ -642,17 +650,29 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Ext_assertLocker #define S3JniMutex_Ext_enter #define S3JniMutex_Ext_leave +#define S3JniMutex_Global_enter +#define S3JniMutex_Global_leave #define S3JniMutex_Nph_enter #define S3JniMutex_Nph_leave #define S3JniMutex_S3JniDb_enter #define S3JniMutex_S3JniDb_leave #endif -#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env) +/* Helpers for jstring and jbyteArray. */ +#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) +#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) +#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) +#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) + + +/* Fail fatally with an OOM message. */ static inline void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; } +/* Fail fatally if !VAR. */ +#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env) + /* ** sqlite3_malloc() proxy which fails fatally on OOM. This should ** only be used for routines which manage global state and have no @@ -969,8 +989,8 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDest ** has to haves its own Java reference, but it need only be ** call-local. */ -static void S3JniHook_copy( JNIEnv * const env, S3JniHook const * const src, - S3JniHook * const dest ){ +static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, + S3JniHook * const dest ){ S3JniMutex_S3JniDb_enter; *dest = *src; if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj); @@ -1402,7 +1422,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, jint rc = 0; S3JniHook hook; - S3JniHook_copy(env, &ps->hooks.collation, &hook ); + S3JniHook_localdup(env, &ps->hooks.collation, &hook ); if( hook.jObj ){ jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; @@ -2092,7 +2112,7 @@ static int s3jni_busy_handler(void* pState, int n){ S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_copy(env, &ps->hooks.busyHandler, &hook ); + S3JniHook_localdup(env, &ps->hooks.busyHandler, &hook ); if( hook.jObj ){ rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)n); @@ -2230,7 +2250,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_copy(env, &ps->hooks.collationNeeded, &hook ); + S3JniHook_localdup(env, &ps->hooks.collationNeeded, &hook ); if( hook.jObj ){ unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); @@ -2354,7 +2374,7 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, int rc = 0; S3JniHook hook; - S3JniHook_copy( env, + S3JniHook_localdup( env, isCommit ? &ps->hooks.commit : &ps->hooks.rollback, &hook); if( hook.jObj ){ @@ -2472,9 +2492,12 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int jstring jArg1 = 0; S3JniDeclLocal_env; S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); - S3JniHook * const hook = &SJG.hooks.sqllog; + S3JniHook hook = S3JniHook_empty; - if( !ps || !hook->jObj ) return; + if( ps ){ + S3JniHook_localdup(env, &SJG.hooks.sqllog, &hook); + } + if( !hook.jObj ) return; jArg0 = S3JniRefLocal(ps->jDb); switch( op ){ case 0: /* db opened */ @@ -2487,11 +2510,12 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int (*env)->FatalError(env, "Unhandled 4th arg to SQLITE_CONFIG_SQLLOG."); break; } - (*env)->CallVoidMethod(env, hook->jObj, hook->midCallback, jArg0, jArg1, op); + (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, jArg0, jArg1, op); S3JniIfThrew{ S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback"); S3JniExceptionClear; } + S3JniUnrefLocal(hook.jObj); S3JniUnrefLocal(jArg0); S3JniUnrefLocal(jArg1); } @@ -2502,41 +2526,40 @@ void sqlite3_init_sqllog(void){ #endif S3JniApi(sqlite3_config() /* for SQLLOG */, - jint,1config__Lorg_sqlite_jni_SQLLog_2)(JniArgsEnvClass, jobject jLog){ + jint, 1config__Lorg_sqlite_jni_ConfigSqllogCallback_2)( + JniArgsEnvClass, jobject jLog + ){ #ifndef SQLITE_ENABLE_SQLLOG return SQLITE_MISUSE; #else - S3JniHook tmpHook; - S3JniHook * const hook = &tmpHook; - S3JniHook * const hookOld = & SJG.hooks.sqllog; - jclass klazz; + S3JniHook * const pHook = &SJG.hooks.sqllog; int rc = 0; + + S3JniMutex_Global_enter; if( !jLog ){ - S3JniHook_unref(env, hookOld, 0); - return 0; - } - if( hookOld->jObj && (*env)->IsSameObject(env, jLog, hookOld->jObj) ){ - return 0; - } - klazz = (*env)->GetObjectClass(env, jLog); - hook->midCallback = (*env)->GetMethodID(env, klazz, "call", - "(Lorg/sqlite/jni/sqlite3;" - "Ljava/lang/String;" - "I)V"); - S3JniUnrefLocal(klazz); - if( !hook->midCallback ){ - S3JniExceptionWarnIgnore; - S3JniHook_unref(env, hook, 0); - return SQLITE_ERROR; - } - hook->jObj = S3JniRefGlobal(jLog); - rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); - if( rc ){ - S3JniHook_unref(env, hook, 0); - }else{ - S3JniHook_unref(env, hookOld, 0); - *hookOld = *hook; + S3JniHook_unref(env, pHook, 0); + }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ + /* No-op */ + }else { + jclass const klazz = (*env)->GetObjectClass(env, jLog); + jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call", + "(Lorg/sqlite/jni/sqlite3;" + "Ljava/lang/String;" + "I)V"); + S3JniUnrefLocal(klazz); + if( midCallback ){ + rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); + if( 0==rc ){ + S3JniHook_unref(env, pHook, 0); + pHook->midCallback = midCallback; + pHook->jObj = S3JniRefGlobal(jLog); + } + }else{ + S3JniExceptionWarnIgnore; + rc = SQLITE_ERROR; + } } + S3JniMutex_Global_leave; return rc; #endif } @@ -3080,7 +3103,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, const int isPre = 0!=pDb; S3JniHook hook; - S3JniHook_copy(env, isPre ? + S3JniHook_localdup(env, isPre ? #ifdef SQLITE_ENABLE_PREUPDATE_HOOK &ps->hooks.preUpdate #else @@ -3274,7 +3297,7 @@ static int s3jni_progress_handler_impl(void *pP){ S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_copy( env, &ps->hooks.progress, &hook ); + S3JniHook_localdup( env, &ps->hooks.progress, &hook ); if( hook.jObj ){ rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback); S3JniIfThrew{ @@ -3569,7 +3592,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniHook hook; int rc = 0; - S3JniHook_copy(env, &ps->hooks.auth, &hook ); + S3JniHook_localdup(env, &ps->hooks.auth, &hook ); if( hook.jObj ){ jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; @@ -3759,7 +3782,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ int rc = 0; S3JniHook hook; - S3JniHook_copy( env, &ps->hooks.trace, &hook ); + S3JniHook_localdup( env, &ps->hooks.trace, &hook ); if( !hook.jObj ){ return 0; } @@ -3973,13 +3996,15 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SJG.metrics.envCacheMisses, SJG.metrics.envCacheHits); printf("Mutex entry:" + "\n\tglobal %u" "\n\tenv %u" "\n\tnph inits %u" "\n\tperDb %u" "\n\tautoExt %u list accesses" "\n\tmetrics %u\n", - SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, - SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt, + SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv, + SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, + SJG.metrics.nMutexAutoExt, SJG.metrics.nMetrics); printf("S3JniDb: %u alloced (*%u = %u bytes), %u recycled\n", SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb), @@ -4906,6 +4931,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ S3JniUnrefLocal(klazz); #endif + SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + s3jni_oom_check( SJG.mutex ); SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); s3jni_oom_check( SJG.envCache.mutex ); SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 6c1ec796db..86b20e70c3 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -494,6 +494,11 @@ public final class SQLite3Jni {

Others may be added in the future. It returns SQLITE_MISUSE if given an argument it does not handle. + +

Note that sqlite3_config() is not threadsafe with regards to + the rest of the library. This must not be called when any other + library APIs are being called. + */ public static native int sqlite3_config(int op); @@ -504,8 +509,13 @@ public final class SQLite3Jni { logger. If installation of a logger fails, any previous logger is retained. - If not built with SQLITE_ENABLE_SQLLOG defined, this returns +

If not built with SQLITE_ENABLE_SQLLOG defined, this returns SQLITE_MISUSE. + +

Note that sqlite3_config() is not threadsafe with regards to + the rest of the library. This must not be called when any other + library APIs are being called. + */ public static native int sqlite3_config( @Nullable ConfigSqllogCallback logger ); diff --git a/manifest b/manifest index db9eb67524..cd32317d2a 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sthe\sJava\sBusyHandler.xDestroy()\smethod\s-\sit\sshould\snot\shave\shad\sone.\sEliminate\sthe\slast\sof\sthe\spotentially-significant\sMUTF-8\scases. -D 2023-08-26T10:51:19.217 +C JNI\sinternal\scleanups\sand\sdocs. +D 2023-08-26T11:57:34.208 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 86a9f9182e8c0b1f058196ac4017f3290b948bcfbea1bc55198bdd0ad10a2ea1 +F ext/jni/src/c/sqlite3-jni.c e914d5ec2d7a80a2735d777b4309c1bb0adc721be63abed0b9927a3880008f9b F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java cb3040fcfe35199bb10b4bca2cc541ca383563f85c9b460412c3bd15f413ae23 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 4be23360d93011d80676bebb1f21f7da0fc4ab637a6d138c8c35bbb2f764b19d F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 2921142fff8cd5a09d1cee30853457926dc63e647df9a687265bb4e678bc9570 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 f2af7bbf493fe28d92fc9c77425f8bb9d48c02af9a5eabceb0365c705651e114 -R 0a5c5021d2a5ccca7e44db0ac1e86f8f +P c852f1ebbde273c3d28fe5aff0bf73cfc06b41dd371a94d7520536dc7a1dbcc1 +R e2cc30a4c5c5fdac12de199e721e1eae U stephan -Z 3f70be94b4b4f3d48d8c6e932937aea1 +Z afcbdc6516d9f37dfcef349abedaeb15 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8ec48b7eeb..d29f07c1cc 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c852f1ebbde273c3d28fe5aff0bf73cfc06b41dd371a94d7520536dc7a1dbcc1 \ No newline at end of file +b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6 \ No newline at end of file From acfbe5284e23e41eecdb80f5eec5d6c095f2235a Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 12:19:51 +0000 Subject: [PATCH 067/422] Add -sqllog flag to the JNI 'tests' target because it hits APIs which are otherwise not tested. FossilOrigin-Name: cc3153ed341f59262485c3541a8879c4e86520c8a10f4ce819344a88099e7d0e --- ext/jni/GNUmakefile | 15 +++++++++++---- ext/jni/src/org/sqlite/jni/Tester1.java | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 7534166335..9eb65186a3 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -243,12 +243,19 @@ test.flags ?= test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \ $(java.flags) -cp $(classpath) \ org.sqlite.jni.Tester1 -test-one: $(SQLite3Jni.class) $(sqlite3-jni.dll) +test.deps := $(SQLite3Jni.class) $(sqlite3-jni.dll) +test-one: $(test.deps) $(bin.java) $(test.main.flags) $(test.flags) -test: test-one - @echo "Again in multi-threaded mode:"; +test-sqllog: $(test.deps) + @echo "Testing with -sqllog..." + $(bin.java) $(test.main.flags) -sqllog +test-mt: $(test.deps) + @echo "Testing in multi-threaded mode:"; $(bin.java) $(test.main.flags) -t 5 -r 20 -shuffle $(test.flags) +test: test-one test-mt +tests: test test-sqllog + tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.flags ?= # --verbose .PHONY: tester tester-local tester-ext @@ -283,7 +290,7 @@ endif tester-ext: tester-local tester: tester-ext -tests: test tester +tests: tester ######################################################################## # Build each SQLITE_THREADMODE variant and run all tests against them. diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index bd585528cc..5f574045c2 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1504,7 +1504,7 @@ public class Tester1 implements Runnable { @Override public void call(sqlite3 db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; - case 1: outln(db,": ",msg); break; + case 1: outln("SQL ",db,": ",msg); break; case 2: outln("Closing db: ",db); break; } } diff --git a/manifest b/manifest index cd32317d2a..64134a970c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\sinternal\scleanups\sand\sdocs. -D 2023-08-26T11:57:34.208 +C Add\s-sqllog\sflag\sto\sthe\sJNI\s'tests'\starget\sbecause\sit\shits\sAPIs\swhich\sare\sotherwise\snot\stested. +D 2023-08-26T12:19:51.786 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,7 +233,7 @@ 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 b28f8b304ef97db8250857cb463aea1b329bfcb584a2902d4c1a490a831e2c9d +F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c e914d5ec2d7a80a2735d777b4309c1bb0adc721be63abed0b9927a3880008f9b @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 4be23360d93011d80676bebb1f21f7da0fc4ab637a6d138c8c35bbb2f764b19d F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java 2921142fff8cd5a09d1cee30853457926dc63e647df9a687265bb4e678bc9570 +F ext/jni/src/org/sqlite/jni/Tester1.java 929347853b4b6a1039d4878b629e2eaefa559ed4fee77af44f0667818cb0ce76 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 c852f1ebbde273c3d28fe5aff0bf73cfc06b41dd371a94d7520536dc7a1dbcc1 -R e2cc30a4c5c5fdac12de199e721e1eae +P b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6 +R bda1f076dda69c15cb8a680d0b1df2f4 U stephan -Z afcbdc6516d9f37dfcef349abedaeb15 +Z 305343d23fefa1e340e48a7e2b94b6db # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d29f07c1cc..4effb0c1d0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6 \ No newline at end of file +cc3153ed341f59262485c3541a8879c4e86520c8a10f4ce819344a88099e7d0e \ No newline at end of file From a7da592bd7e4cecc910aab069dbb3e6ea538e0c2 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 14:55:44 +0000 Subject: [PATCH 068/422] Code consolidation cleanups. FossilOrigin-Name: d6b5ecd28740c2c5d21797fce9fe137c8a83f702f22901720cc6e8b1b42af001 --- ext/jni/src/c/sqlite3-jni.c | 247 ++++++++++++++++++------------------ manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 133 insertions(+), 128 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d3927f6256..6badc3be31 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -234,18 +234,20 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ #define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) /* -** Key type for use with S3JniGlobal_nph_cache(). +** Key type for use with S3JniGlobal_nph(). */ typedef struct S3NphRef S3NphRef; struct S3NphRef { - const int index /* index into S3JniGlobal.nph[] */; - const char * const zName /* Full Java name of the class */; + const int index /* index into S3JniGlobal.nph[] */; + const char * const zName /* Full Java name of the class */; + const char * const zMember /* Name of member property */; + const char * const zTypeSig /* JNI type signature of zMember */; }; /* ** Cache keys for each concrete NativePointerHolder subclass and -** OutputPointer type. The members are to be used with -** S3JniGlobal_nph_cache() and friends, and each one's member->index +** OutputPointer.T type. The members are to be used with +** S3JniGlobal_nph() and friends, and each one's member->index ** corresponds to its index in the S3JniGlobal.nph[] array. */ static const struct { @@ -268,26 +270,36 @@ static const struct { const S3NphRef Fts5Tokenizer; #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$sqlite3"), - NREF(7, "OutputPointer$sqlite3_stmt"), - NREF(8, "OutputPointer$sqlite3_value"), +#define MkRef(INDEX, KLAZZ, MEMBER, SIG) \ + { INDEX, "org/sqlite/jni/" KLAZZ, MEMBER, SIG } +/* NativePointerHolder ref */ +#define RefN(INDEX, KLAZZ) MkRef(INDEX, KLAZZ, "nativePointer", "J") +/* OutputPointer.T ref */ +#define RefO(INDEX, KLAZZ, SIG) MkRef(INDEX, KLAZZ, "value", SIG) + RefN(0, "sqlite3"), + RefN(1, "sqlite3_stmt"), + RefN(2, "sqlite3_context"), + RefN(3, "sqlite3_value"), + RefO(4, "OutputPointer$Int32", "I"), + RefO(5, "OutputPointer$Int64", "J"), + RefO(6, "OutputPointer$sqlite3", + "Lorg/sqlite/jni/sqlite3;"), + RefO(7, "OutputPointer$sqlite3_stmt", + "Lorg/sqlite/jni/sqlite3_stmt;"), + RefO(8, "OutputPointer$sqlite3_value", + "Lorg/sqlite/jni/sqlite3_value;"), #ifdef SQLITE_ENABLE_FTS5 - NREF(9, "OutputPointer$String"), - NREF(10, "OutputPointer$ByteArray"), - NREF(11, "Fts5Context"), - NREF(12, "Fts5ExtensionApi"), - NREF(13, "fts5_api"), - NREF(14, "fts5_tokenizer"), - NREF(15, "Fts5Tokenizer") + RefO(9, "OutputPointer$String", "Ljava/lang/String;"), + RefO(10, "OutputPointer$ByteArray", "[B"), + RefN(11, "Fts5Context"), + RefN(12, "Fts5ExtensionApi"), + RefN(13, "fts5_api"), + RefN(14, "fts5_tokenizer"), + RefN(15, "Fts5Tokenizer") #endif -#undef NREF +#undef MkRef +#undef RefN +#undef RefO }; enum { @@ -296,8 +308,7 @@ enum { ** (only) the library's NativePointerHolder and OutputPointer types, ** a fixed count known at build-time. This value needs to be ** exactly the number of S3NphRef entries in the S3NphRefs - ** object. The index field of those entries are the keys for this - ** particular cache. + ** object. */ S3Jni_NphCache_size = sizeof(S3NphRefs) / sizeof(S3NphRef) }; @@ -1047,7 +1058,8 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ /* ** Uncache any state for the given JNIEnv, clearing all Java ** references the cache owns. Returns true if env was cached and false -** if it was not found in the cache. +** if it was not found in the cache. Ownership of the given object is +** passed over to this function, which makes it free for re-use. */ static int S3JniEnv_uncache(JNIEnv * const env){ struct S3JniEnv * row; @@ -1085,7 +1097,7 @@ static int S3JniEnv_uncache(JNIEnv * const env){ ** This simple cache catches >99% of searches in the current ** (2023-07-31) tests. */ -static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* pRef){ +static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef){ /** According to: @@ -1121,8 +1133,8 @@ static S3JniNphClass * S3JniGlobal_nph_cache(JNIEnv * const env, S3NphRef const* ** Returns the ID of the "nativePointer" field from the given ** NativePointerHolder class. */ -static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* pRef){ - S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); +static jfieldID NativePointerHolder_field(JNIEnv * const env, S3NphRef const* pRef){ + S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ @@ -1139,9 +1151,9 @@ static jfieldID NativePointerHolder_getField(JNIEnv * const env, S3NphRef const* ** zClassName must be a static string so we can use its address ** as a cache key. */ -static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, - S3NphRef const* pRef){ - (*env)->SetLongField(env, ppOut, NativePointerHolder_getField(env, 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); S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer."); } @@ -1154,7 +1166,7 @@ static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p, static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const* pRef){ if( pObj ){ void * const rv = (void*)(*env)->GetLongField( - env, pObj, NativePointerHolder_getField(env, pRef) + env, pObj, NativePointerHolder_field(env, pRef) ); S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); return rv; @@ -1177,11 +1189,9 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv = SJG.perDb.aFree; SJG.perDb.aFree = rv->pNext; assert(rv->pNext != rv); - assert(rv->pPrev != rv); - assert(rv->pPrev ? (rv->pPrev!=rv->pNext) : 1); + assert(!rv->pPrev); if( rv->pNext ){ assert(rv->pNext->pPrev == rv); - assert(rv->pPrev ? (rv->pNext == rv->pPrev->pNext) : 1); rv->pNext->pPrev = 0; rv->pNext = 0; } @@ -1208,7 +1218,8 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, } /* -** Returns the S3JniDb object for the given db. +** Returns the S3JniDb object for the given db. At most, one of jDb or +** pDb may be non-NULL. ** ** The 3rd argument should normally only be non-0 for routines which ** are called from the C library and pass a native db handle instead of @@ -1218,25 +1229,29 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, ** Returns NULL if jDb and pDb are both NULL or if there is no ** matching S3JniDb entry for pDb or the pointer fished out of jDb. */ -static S3JniDb * S3JniDb_for_db(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ +static S3JniDb * S3JniDb_get(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ S3JniDb * s = 0; - if( jDb || pDb ){ - S3JniMutex_S3JniDb_enter; - s = SJG.perDb.aHead; - if( !pDb ){ - assert( jDb ); - pDb = PtrGet_sqlite3(jDb); - } - for( ; pDb && s; s = s->pNext){ - if( s->pDb == pDb ){ - break; - } - } - S3JniMutex_S3JniDb_leave; + + if( 0==jDb && 0==pDb ) return 0; + assert( jDb ? !pDb : !!pDb ); + S3JniMutex_S3JniDb_enter; + if( jDb ){ + assert(!pDb); + pDb = PtrGet_sqlite3(jDb); } + s = SJG.perDb.aHead; + for( ; pDb && s; s = s->pNext){ + if( s->pDb == pDb ){ + break; + } + } + S3JniMutex_S3JniDb_leave; return s; } +#define S3JniDb_from_java(jObject) S3JniDb_get(env,(jObject),0) +#define S3JniDb_from_c(sqlite3Ptr) S3JniDb_get(env,0,(sqlite3Ptr)) + /* ** Unref any Java-side state in ax and zero out ax. */ @@ -1251,8 +1266,8 @@ static void S3JniAutoExtension_clear(JNIEnv * const env, /* ** Initializes a pre-allocated S3JniAutoExtension object. Returns ** non-0 if there is an error collecting the required state from -** jAutoExt (which must be an AutoExtension object). On error, it -** passes ax to S3JniAutoExtension_clear(). +** jAutoExt (which must be an AutoExtensionCallback object). On error, +** it passes ax to S3JniAutoExtension_clear(). */ static int S3JniAutoExtension_init(JNIEnv *const env, S3JniAutoExtension * const ax, @@ -1260,13 +1275,11 @@ static int S3JniAutoExtension_init(JNIEnv *const env, jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); S3JniMutex_Ext_assertLocker; - assert( env == SJG.autoExt.locker ); ax->midFunc = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;)I"); S3JniUnrefLocal(klazz); S3JniExceptionWarnIgnore; if( !ax->midFunc ){ - MARKER(("Error getting call(sqlite3) from AutoExtension object.\n")); S3JniAutoExtension_clear(env, ax); return SQLITE_ERROR; } @@ -1287,14 +1300,13 @@ static int S3JniAutoExtension_init(JNIEnv *const env, ** routine with the same pRef but different zTypeSig: it will ** misbehave. */ -static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, - const char * const zTypeSig){ - S3JniNphClass * const pNC = S3JniGlobal_nph_cache(env, pRef); +static jfieldID OutputPointer_field(JNIEnv * const env, S3NphRef const * pRef){ + S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ - pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "value", zTypeSig); - S3JniExceptionIsFatal("setupOutputPointer() could not find OutputPointer.*.value"); + pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); + S3JniExceptionIsFatal("OutputPointer_field() could not find OutputPointer.*.value"); } S3JniMutex_Nph_leave; } @@ -1305,11 +1317,12 @@ static jfieldID setupOutputPointer(JNIEnv * const env, S3NphRef const * pRef, ** Sets the value property of the OutputPointer.Int32 jOut object to ** v. */ -static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_Int32, "I" - ); - (*env)->SetIntField(env, jOut, setter, (jint)v); +static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, + int v){ + (*env)->SetIntField(env, jOut, + OutputPointer_field( + env, &S3NphRefs.OutputPointer_Int32 + ), (jint)v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); } @@ -1317,25 +1330,34 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int ** Sets the value property of the OutputPointer.Int64 jOut object to ** v. */ -static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_Int64, "J" - ); - (*env)->SetLongField(env, jOut, setter, v); +static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, + jlong v){ + (*env)->SetLongField(env, jOut, + OutputPointer_field( + env, &S3NphRefs.OutputPointer_Int64 + ), v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); } +/* +** Internal helper for OutputPointer_set_TYPE() where TYPE is an +** Object type. +*/ +static void OutputPointer_set_obj(JNIEnv * const env, + S3NphRef const * const pRef, + jobject const jOut, + jobject v){ + (*env)->SetObjectField(env, jOut, OutputPointer_field(env, pRef), v); + S3JniExceptionIsFatal("Cannot set OutputPointer.T.value"); +} + /* ** Sets the value property of the OutputPointer.sqlite3 jOut object to ** v. */ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_sqlite3, "Lorg/sqlite/jni/sqlite3;" - ); - (*env)->SetObjectField(env, jOut, setter, jDb); - S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3.value"); + OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3, jOut, jDb); } /* @@ -1344,12 +1366,7 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, */ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, jobject jStmt){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_sqlite3_stmt, - "Lorg/sqlite/jni/sqlite3_stmt;" - ); - (*env)->SetObjectField(env, jOut, setter, jStmt); - S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_stmt.value"); + OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3_stmt, jOut, jStmt); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -1359,12 +1376,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu */ 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;" - ); - (*env)->SetObjectField(env, jOut, setter, jValue); - S3JniExceptionIsFatal("Cannot set OutputPointer.sqlite3_value.value"); + OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3_value, jOut, jValue); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -1376,11 +1388,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO */ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, jbyteArray const v){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_ByteArray, "[B" - ); - (*env)->SetObjectField(env, jOut, setter, v); - S3JniExceptionIsFatal("Cannot set OutputPointer.ByteArray.value"); + OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_ByteArray, jOut, v); } #endif @@ -1390,11 +1398,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, */ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, jstring const v){ - jfieldID const setter = setupOutputPointer( - env, &S3NphRefs.OutputPointer_String, "Ljava/lang/String;" - ); - (*env)->SetObjectField(env, jOut, setter, v); - S3JniExceptionIsFatal("Cannot set OutputPointer.String.value"); + OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_String, jOut, v); } #endif /* SQLITE_ENABLE_FTS5 */ @@ -1511,7 +1515,7 @@ static void ResultJavaVal_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_cache(env, pRef); + S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef); if( !pNC->midCtor ){ S3JniMutex_Nph_enter; if( !pNC->midCtor ){ @@ -1523,7 +1527,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, rv, pNative, pRef); + if( rv ) NativePointerHolder_set(env, pRef, rv, pNative); return rv; } @@ -1938,7 +1942,7 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, assert( !ps->pDb && "it's still being opened" ); ps->pDb = pDb; assert( ps->jDb ); - NativePointerHolder_set(env, ps->jDb, pDb, &S3NphRefs.sqlite3); + NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb); for( i = 0; go && 0==rc; ++i ){ S3JniAutoExtension ax = {0,0} /* We need a copy of the auto-extension object, with our own @@ -2006,7 +2010,8 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( 0==rc ){ ax = &SJG.autoExt.pExt[SJG.autoExt.nExt]; rc = S3JniAutoExtension_init(env, ax, jAutoExt); - assert( rc ? 0==ax->jObj : 0!=ax->jObj ); + assert( rc ? (0==ax->jObj && 0==ax->midFunc) + : (0!=ax->jObj && 0!=ax->midFunc) ); } } if( 0==rc ){ @@ -2129,7 +2134,7 @@ static int s3jni_busy_handler(void* pState, int n){ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( JniArgsEnvClass, jobject jDb, jobject jBusy ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0; int rc = 0; if( !ps ) return (jint)SQLITE_MISUSE; @@ -2165,7 +2170,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( JniArgsEnvClass, jobject jDb, jint ms ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); int rc = SQLITE_MISUSE; if( ps ){ S3JniMutex_S3JniDb_enter; @@ -2208,13 +2213,13 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ int rc = 0; S3JniDb * ps = 0; assert(version == 1 || version == 2); - ps = S3JniDb_for_db(env, jDb, 0); + ps = S3JniDb_from_java(jDb); if( ps ){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ S3JniDb_set_aside(env, ps) /* MUST come after close() because of ps->trace. */; - NativePointerHolder_set(env, jDb, 0, &S3NphRefs.sqlite3); + NativePointerHolder_set(env, &S3NphRefs.sqlite3, jDb, 0); } } return (jint)rc; @@ -2273,7 +2278,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( JniArgsEnvClass, jobject jDb, jobject jHook ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); S3JniHook * const pHook = &ps->hooks.collationNeeded; int rc = 0; @@ -2400,7 +2405,7 @@ static void s3jni_rollback_hook_impl(void *pP){ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, jobject jDb, jobject jHook){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); jclass klazz; jobject pOld = 0; jmethodID xCallback; @@ -2491,7 +2496,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int jobject jArg0 = 0; jstring jArg1 = 0; S3JniDeclLocal_env; - S3JniDb * const ps = S3JniDb_for_db(env, 0, pDb); + S3JniDb * const ps = S3JniDb_from_c(pDb); S3JniHook hook = S3JniHook_empty; if( ps ){ @@ -2568,7 +2573,7 @@ S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( JniArgsEnvClass, jobject jpCx ){ sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); - S3JniDb * const ps = pDb ? S3JniDb_for_db(env, 0, pDb) : 0; + S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; return ps ? ps->jDb : 0; } @@ -2578,7 +2583,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), jobject oCollation){ int rc; jclass klazz; - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); jmethodID midCallback; if( !ps ) return SQLITE_MISUSE; @@ -2665,7 +2670,7 @@ error_cleanup: S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/, jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); int rc; char *zStr; @@ -2703,7 +2708,7 @@ S3JniApi( )( JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); int rc; switch( ps ? op : 0 ){ case SQLITE_DBCONFIG_ENABLE_FKEY: @@ -2754,7 +2759,7 @@ JniDecl(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPoint S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( JniArgsEnvClass, jobject jDb, jstring jDbName ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); char *zDbName; jstring jRv = 0; int nStr = 0; @@ -2840,7 +2845,7 @@ S3JniApi(sqlite3_finalize(),jint,1finalize)( sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ rc = sqlite3_finalize(pStmt); - NativePointerHolder_set(env, jpStmt, 0, &S3NphRefs.sqlite3_stmt); + NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jpStmt, 0); } return rc; } @@ -2943,7 +2948,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, assert(ps->jDb); if( 0==ps->pDb ){ ps->pDb = *ppDb; - NativePointerHolder_set(env, ps->jDb, *ppDb, &S3NphRefs.sqlite3); + NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, *ppDb); }else{ assert( ps->pDb == *ppDb /* set up via s3jni_run_java_auto_extensions() */); } @@ -3044,7 +3049,7 @@ end: OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0)); } if( pStmt ){ - NativePointerHolder_set(env, jStmt, pStmt, &S3NphRefs.sqlite3_stmt); + NativePointerHolder_set(env, &S3NphRefs.sqlite3_stmt, jStmt, pStmt); }else{ /* Happens for comments and whitespace. */ S3JniUnrefLocal(jStmt); @@ -3170,7 +3175,7 @@ S3JniApi(sqlite3_preupdate_depth(),int,1preupdate_1depth)( ** 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); + S3JniDb * const ps = S3JniDb_from_java(jDb); jclass klazz; jobject pOld = 0; jmethodID xCallback; @@ -3312,7 +3317,7 @@ static int s3jni_progress_handler_impl(void *pP){ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( JniArgsEnvClass,jobject jDb, jint n, jobject jProgress ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); jclass klazz; jmethodID xCallback; S3JniHook * const pHook = ps ? &ps->hooks.progress : 0; @@ -3616,7 +3621,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( JniArgsEnvClass,jobject jDb, jobject jHook ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; int rc = 0; @@ -3834,7 +3839,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer ){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); S3JniHook * const pHook = ps ? &ps->hooks.trace : 0; jclass klazz; @@ -4152,7 +4157,7 @@ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ } JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){ - S3JniDb * const ps = S3JniDb_for_db(env, jDb, 0); + S3JniDb * const ps = S3JniDb_from_java(jDb); jobject rv = 0; if( !ps ) return 0; else if( ps->jFtsApi ){ diff --git a/manifest b/manifest index 64134a970c..64d0d4a6b5 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\s-sqllog\sflag\sto\sthe\sJNI\s'tests'\starget\sbecause\sit\shits\sAPIs\swhich\sare\sotherwise\snot\stested. -D 2023-08-26T12:19:51.786 +C Code\sconsolidation\scleanups. +D 2023-08-26T14:55:44.725 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c e914d5ec2d7a80a2735d777b4309c1bb0adc721be63abed0b9927a3880008f9b +F ext/jni/src/c/sqlite3-jni.c 3003be176ac3326ba29f79679c7351593065e0e3ecf83b8f84345aea57ab4d21 F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 b7b26bfb4f86e0b8aaabab258ccb0713737ffd4ecd3156d6a83a4f9a1d25edf6 -R bda1f076dda69c15cb8a680d0b1df2f4 +P cc3153ed341f59262485c3541a8879c4e86520c8a10f4ce819344a88099e7d0e +R 2f6845a9eeb7c9c5a2de49d358b42470 U stephan -Z 305343d23fefa1e340e48a7e2b94b6db +Z 1a214861a65c5eead4ace1eeab3dd83d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4effb0c1d0..4d43f5d182 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cc3153ed341f59262485c3541a8879c4e86520c8a10f4ce819344a88099e7d0e \ No newline at end of file +d6b5ecd28740c2c5d21797fce9fe137c8a83f702f22901720cc6e8b1b42af001 \ No newline at end of file From fec793dd8aeecbd3e84f1edfd0012f04e7fdee53 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 16:29:48 +0000 Subject: [PATCH 069/422] Recycle per-UDF JNI state. FossilOrigin-Name: cf406528eb86d8d0d55a468b2c4ec32a11a4947f45c4bbabdde8742ae199ce1f --- ext/jni/src/c/sqlite3-jni.c | 124 +++++++++++++++++++++++------------- manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 86 insertions(+), 52 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 6badc3be31..980da3d9f2 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -426,6 +426,33 @@ struct S3JniAutoExtension { jmethodID midFunc /* xEntryPoint() callback */; }; +/* +** Type IDs for SQL function categories. +*/ +enum UDFType { + UDF_UNKNOWN_TYPE = 0/*for error propagation*/, + UDF_SCALAR, + UDF_AGGREGATE, + UDF_WINDOW +}; + +/** + State for binding Java-side UDFs. +*/ +typedef struct S3JniUdf S3JniUdf; +struct S3JniUdf { + jobject jObj /* SQLFunction instance */; + char * zFuncName /* Only for error reporting and debug logging */; + enum UDFType type; + /** Method IDs for the various UDF methods. */ + jmethodID jmidxFunc /* Java ID of xFunc method */; + jmethodID jmidxStep /* Java ID of xStep method */; + jmethodID jmidxFinal /* Java ID of xFinal method */; + jmethodID jmidxValue /* Java ID of xValue method */; + jmethodID jmidxInverse /* Java ID of xInverse method */; + S3JniUdf * pNext /* Next entry in free-list. */; +}; + #if !defined(SQLITE_JNI_OMIT_METRICS) && !defined(SQLITE_JNI_ENABLE_METRICS) # ifdef SQLITE_DEBUG # define SQLITE_JNI_ENABLE_METRICS @@ -464,10 +491,11 @@ struct S3JniGlobalType { ** */ JavaVM * jvm; + /* Global mutex. */ sqlite3_mutex * mutex; /* ** Cache of Java refs and method IDs for NativePointerHolder - ** subclasses. Initialized on demand. + ** subclasses and OutputPointer.T types. */ S3JniNphClass nph[S3Jni_NphCache_size]; /* @@ -494,6 +522,10 @@ struct S3JniGlobalType { always have this set to the current JNIEnv object. Used only for sanity checking. */; } perDb; + struct { + S3JniUdf * aFree /* Head of the free-item list. Guarded by global + mutex. */; + } udf; /* ** Refs to global classes and methods. Obtained during static init ** and never released. @@ -553,9 +585,13 @@ struct S3JniGlobalType { volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; volatile unsigned nMutexGlobal /* number of times global mutex was entered. */; + volatile unsigned nMutexUdf /* number of times global mutex was entered + for UDFs. */; volatile unsigned nDestroy /* xDestroy() calls across all types */; volatile unsigned nPdbAlloc /* Number of S3JniDb alloced. */; volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */; + volatile unsigned nUdfAlloc /* Number of S3JniUdf alloced. */; + volatile unsigned nUdfRecycled /* Number of S3JniUdf reused. */; struct { /* Number of calls for each type of UDF callback. */ volatile unsigned nFunc; @@ -1544,40 +1580,28 @@ static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_valu return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_value, sv); } -/* -** Type IDs for SQL function categories. -*/ -enum UDFType { - UDF_SCALAR = 1, - UDF_AGGREGATE, - UDF_WINDOW, - UDF_UNKNOWN_TYPE/*for error propagation*/ -}; - typedef void (*udf_xFunc_f)(sqlite3_context*,int,sqlite3_value**); typedef void (*udf_xStep_f)(sqlite3_context*,int,sqlite3_value**); typedef void (*udf_xFinal_f)(sqlite3_context*); /*typedef void (*udf_xValue_f)(sqlite3_context*);*/ /*typedef void (*udf_xInverse_f)(sqlite3_context*,int,sqlite3_value**);*/ -/** - State for binding Java-side UDFs. -*/ -typedef struct S3JniUdf S3JniUdf; -struct S3JniUdf { - jobject jObj /* SQLFunction instance */; - char * zFuncName /* Only for error reporting and debug logging */; - enum UDFType type; - /** Method IDs for the various UDF methods. */ - jmethodID jmidxFunc; - jmethodID jmidxStep; - jmethodID jmidxFinal; - jmethodID jmidxValue; - jmethodID jmidxInverse; -}; - static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ - S3JniUdf * const s = sqlite3_malloc(sizeof(S3JniUdf)); + S3JniUdf * s = 0; + + S3JniMutex_Global_enter; + s3jni_incr(&SJG.metrics.nMutexUdf); + if( SJG.udf.aFree ){ + s = SJG.udf.aFree; + SJG.udf.aFree = s->pNext; + s->pNext = 0; + s3jni_incr(&SJG.metrics.nUdfRecycled); + } + S3JniMutex_Global_leave; + if( !s ){ + s = sqlite3_malloc(sizeof(*s)); + s3jni_incr(&SJG.metrics.nUdfAlloc); + } if( s ){ const char * zFSI = /* signature for xFunc, xStep, xInverse */ "(Lorg/sqlite/jni/sqlite3_context;[Lorg/sqlite/jni/sqlite3_value;)V"; @@ -1585,9 +1609,9 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ "(Lorg/sqlite/jni/sqlite3_context;)V"; jclass const klazz = (*env)->GetObjectClass(env, jObj); - memset(s, 0, sizeof(S3JniUdf)); + memset(s, 0, sizeof(*s)); s->jObj = S3JniRefGlobal(jObj); -#define FGET(FuncName,FuncType,Field) \ +#define FGET(FuncName,FuncType,Field) \ s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncType); \ if( !s->Field ) (*env)->ExceptionClear(env) FGET("xFunc", zFSI, jmidxFunc); @@ -1607,20 +1631,24 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ return s; } + static void S3JniUdf_free(S3JniUdf * s){ S3JniDeclLocal_env; - if( env ){ - //MARKER(("UDF cleanup: %s\n", s->zFuncName)); - s3jni_call_xDestroy(env, s->jObj); - S3JniUnrefGlobal(s->jObj); - } + //MARKER(("UDF cleanup: %s\n", s->zFuncName)); + s3jni_call_xDestroy(env, s->jObj); + S3JniUnrefGlobal(s->jObj); sqlite3_free(s->zFuncName); - sqlite3_free(s); + assert( !s->pNext ); + memset(s, 0, sizeof(*s)); + S3JniMutex_Global_enter; + s->pNext = S3JniGlobal.udf.aFree; + S3JniGlobal.udf.aFree = s; + S3JniMutex_Global_leave; } static void S3JniUdf_finalizer(void * s){ //MARKER(("UDF finalizer @ %p\n", s)); - if( s ) S3JniUdf_free((S3JniUdf*)s); + S3JniUdf_free((S3JniUdf*)s); } /** @@ -2630,7 +2658,8 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_w } s = S3JniUdf_alloc(env, jFunctor); if( !s ) return SQLITE_NOMEM; - else if( UDF_UNKNOWN_TYPE==s->type ){ + + if( UDF_UNKNOWN_TYPE==s->type ){ rc = s3jni_db_error(pDb, SQLITE_MISUSE, "Cannot unambiguously determine function type."); S3JniUdf_free(s); @@ -4001,20 +4030,25 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SJG.metrics.envCacheMisses, SJG.metrics.envCacheHits); printf("Mutex entry:" - "\n\tglobal %u" - "\n\tenv %u" - "\n\tnph inits %u" - "\n\tperDb %u" - "\n\tautoExt %u list accesses" - "\n\tmetrics %u\n", + "\n\tglobal = %u" + "\n\tenv = %u" + "\n\tnph inits = %u" + "\n\tperDb = %u" + "\n\tautoExt list = %u" + "\n\tS3JniUdf free-list = %u" + "\n\tmetrics = %u\n", SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv, SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, - SJG.metrics.nMutexAutoExt, + SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf, SJG.metrics.nMetrics); printf("S3JniDb: %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", + SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf), + (unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)), + SJG.metrics.nUdfRecycled); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); diff --git a/manifest b/manifest index 64d0d4a6b5..7e8b84cc87 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Code\sconsolidation\scleanups. -D 2023-08-26T14:55:44.725 +C Recycle\sper-UDF\sJNI\sstate. +D 2023-08-26T16:29:48.809 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 3003be176ac3326ba29f79679c7351593065e0e3ecf83b8f84345aea57ab4d21 +F ext/jni/src/c/sqlite3-jni.c ac4020f987abc0f3970119d279d26b74de8fb9b1b49da4046dd3e74a40f9a819 F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 cc3153ed341f59262485c3541a8879c4e86520c8a10f4ce819344a88099e7d0e -R 2f6845a9eeb7c9c5a2de49d358b42470 +P d6b5ecd28740c2c5d21797fce9fe137c8a83f702f22901720cc6e8b1b42af001 +R b3a9fb323c6b84f48b5856006714684b U stephan -Z 1a214861a65c5eead4ace1eeab3dd83d +Z 9c33a91e895e4fc7c7f885475275408f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4d43f5d182..f3a58cea1e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d6b5ecd28740c2c5d21797fce9fe137c8a83f702f22901720cc6e8b1b42af001 \ No newline at end of file +cf406528eb86d8d0d55a468b2c4ec32a11a4947f45c4bbabdde8742ae199ce1f \ No newline at end of file From 2a0dc4895b54d4aca1b77f6078fd1736eaefc965 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 16:55:27 +0000 Subject: [PATCH 070/422] Correct a potential duplicate xDestroy() being triggered for Java-side collations. FossilOrigin-Name: 25331172f78544f7f23ad7821cbf065589f6d02706184d4c83fe3988452dac5d --- ext/jni/src/c/sqlite3-jni.c | 35 ++++++++++++++----------- ext/jni/src/org/sqlite/jni/Tester1.java | 8 +++--- manifest | 14 +++++----- manifest.uuid | 2 +- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 980da3d9f2..50f0ffb252 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -981,7 +981,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, /* ** Extracts the (void xDestroy()) method from jObj and applies it to ** jObj. If jObj is NULL, this is a no-op. The lack of an xDestroy() -** method is silently ignored and any exceptions thrown by xDestroy() +** method is silently ignored. Any exceptions thrown by xDestroy() ** trigger a warning to stdout or stderr and then the exception is ** suppressed. */ @@ -1012,11 +1012,14 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ ** cleared. It is legal to call this when the object has no Java ** references. */ -static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, int doXDestroy){ - if( doXDestroy && s->jObj ){ - s3jni_call_xDestroy(env, s->jObj); +static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, + int doXDestroy){ + if( s->jObj ){ + if( doXDestroy ){ + s3jni_call_xDestroy(env, s->jObj); + } + S3JniUnrefGlobal(s->jObj); } - S3JniUnrefGlobal(s->jObj); memset(s, 0, sizeof(*s)); } @@ -1062,18 +1065,18 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ SJG.perDb.aHead = s->pNext; } sqlite3_free( s->zMainDbName ); -#define UNHOOK(MEMBER,XDESTROY) S3JniHook_unref(env, &s->hooks.MEMBER, XDESTROY) - UNHOOK(auth, 0); - UNHOOK(busyHandler, 0); - UNHOOK(collation, 1); - UNHOOK(collationNeeded, 0); - UNHOOK(commit, 0); - UNHOOK(progress, 0); - UNHOOK(rollback, 0); - UNHOOK(trace, 0); - UNHOOK(update, 0); +#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0) + UNHOOK(auth); + UNHOOK(busyHandler); + UNHOOK(collation); + UNHOOK(collationNeeded); + UNHOOK(commit); + UNHOOK(progress); + UNHOOK(rollback); + UNHOOK(trace); + UNHOOK(update); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK - UNHOOK(preUpdate, 0); + UNHOOK(preUpdate); #endif #undef UNHOOK S3JniUnrefGlobal(s->jDb); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 5f574045c2..9ac1396f4b 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -488,7 +488,7 @@ public class Tester1 implements Runnable { private void testCollation(){ final sqlite3 db = createNewDb(); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')"); - final ValueHolder xDestroyCalled = new ValueHolder<>(false); + final ValueHolder xDestroyCalled = new ValueHolder<>(0); final CollationCallback myCollation = new CollationCallback() { private String myState = "this is local state. There is much like it, but this is mine."; @@ -510,7 +510,7 @@ public class Tester1 implements Runnable { @Override public void xDestroy() { // Just demonstrates that xDestroy is called. - xDestroyCalled.value = true; + ++xDestroyCalled.value; } }; final CollationNeededCallback collLoader = new CollationNeededCallback(){ @@ -552,12 +552,12 @@ public class Tester1 implements Runnable { } affirm(3 == counter); sqlite3_finalize(stmt); - affirm(!xDestroyCalled.value); + affirm( 0 == xDestroyCalled.value ); rc = sqlite3_collation_needed(db, null); affirm( 0 == rc ); sqlite3_close_v2(db); affirm( 0 == db.getNativePointer() ); - affirm(xDestroyCalled.value); + affirm( 1 == xDestroyCalled.value ); } @ManualTest /* because threading is meaningless here */ diff --git a/manifest b/manifest index 7e8b84cc87..81aaf952d4 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Recycle\sper-UDF\sJNI\sstate. -D 2023-08-26T16:29:48.809 +C Correct\sa\spotential\sduplicate\sxDestroy()\sbeing\striggered\sfor\sJava-side\scollations. +D 2023-08-26T16:55:27.685 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c ac4020f987abc0f3970119d279d26b74de8fb9b1b49da4046dd3e74a40f9a819 +F ext/jni/src/c/sqlite3-jni.c b2ddceed02600dfdaa3a0d876111b7ad2d4f247f8396f3c1b68bb9ea2e129a4a F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 4be23360d93011d80676bebb1f21f7da0fc4ab637a6d138c8c35bbb2f764b19d F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java 929347853b4b6a1039d4878b629e2eaefa559ed4fee77af44f0667818cb0ce76 +F ext/jni/src/org/sqlite/jni/Tester1.java f5205a22f10de63298ede691ed2938ab39084d1ff13d6f53a54c5100f798b933 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 d6b5ecd28740c2c5d21797fce9fe137c8a83f702f22901720cc6e8b1b42af001 -R b3a9fb323c6b84f48b5856006714684b +P cf406528eb86d8d0d55a468b2c4ec32a11a4947f45c4bbabdde8742ae199ce1f +R 5e283ad208dc4181e7491a5a1cc6002c U stephan -Z 9c33a91e895e4fc7c7f885475275408f +Z fd7fa811e5faceb76dda701b17b92b49 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f3a58cea1e..9422aa180e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cf406528eb86d8d0d55a468b2c4ec32a11a4947f45c4bbabdde8742ae199ce1f \ No newline at end of file +25331172f78544f7f23ad7821cbf065589f6d02706184d4c83fe3988452dac5d \ No newline at end of file From 3cb610c0106dd503bc85a85734ffd1427d3775d1 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 17:30:14 +0000 Subject: [PATCH 071/422] Bind sqlite3_db_handle() to JNI. FossilOrigin-Name: 1a30919bfbb686464e8020e684120d22dd2375b910a17446b75452fda96a7449 --- ext/jni/src/c/sqlite3-jni.c | 13 ++++++++++++- ext/jni/src/c/sqlite3-jni.h | 8 ++++++++ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 2 ++ ext/jni/src/org/sqlite/jni/Tester1.java | 6 ++++-- manifest | 18 +++++++++--------- manifest.uuid | 2 +- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 50f0ffb252..629126dd62 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2603,7 +2603,8 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( JniArgsEnvClass, jobject jpCx ){ - sqlite3 * const pDb = sqlite3_context_db_handle(PtrGet_sqlite3_context(jpCx)); + sqlite3_context * const pCx = PtrGet_sqlite3_context(jpCx); + sqlite3 * const pDb = pCx ? sqlite3_context_db_handle(pCx) : 0; S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; return ps ? ps->jDb : 0; } @@ -2810,6 +2811,16 @@ S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( return jRv; } +S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( + JniArgsEnvClass, jobject jpStmt +){ + sqlite3_stmt * const pStmt = PtrGet_sqlite3_context(jpStmt); + sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; + S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; + return ps ? ps->jDb : 0; +} + + S3JniApi(sqlite3_db_status(),jint,1db_1status)( JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent, jobject jOutHigh, jboolean reset diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 7279b0376d..c4fbf915be 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1147,6 +1147,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1data_1count JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename (JNIEnv *, jclass, jobject, jstring); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_db_handle + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;)Lorg/sqlite/jni/sqlite3; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1handle + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_db_config diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 86b20e70c3..b37265484b 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -545,6 +545,8 @@ public final class SQLite3Jni { @NotNull sqlite3 db, @NotNull String dbName ); + public static native sqlite3 sqlite3_db_handle( @NotNull sqlite3_stmt stmt ); + /** Overload for sqlite3_db_config() calls which take (int,int*) variadic arguments. Returns SQLITE_MISUSE if op is not one of the diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 9ac1396f4b..426cee598f 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -253,12 +253,14 @@ public class Tester1 implements Runnable { affirm(0 == rc); sqlite3_stmt stmt = outStmt.take(); affirm(0 != stmt.getNativePointer()); + affirm( db == sqlite3_db_handle(stmt) ); rc = sqlite3_step(stmt); if( SQLITE_DONE != rc ){ outln("step failed ??? ",rc, " ",sqlite3_errmsg(db)); } affirm(SQLITE_DONE == rc); sqlite3_finalize(stmt); + affirm( null == sqlite3_db_handle(stmt) ); affirm(0 == stmt.getNativePointer()); { /* Demonstrate how to use the "zTail" option of @@ -924,8 +926,8 @@ public class Tester1 implements Runnable { @ManualTest /* because threads inherently break this test */ private void testBusy(){ final String dbName = "_busy-handler.db"; - final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); - final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); + final OutputPointer.sqlite3 outDb = OutputPointer.sqlite3(); + final OutputPointer.sqlite3_stmt outStmt = OutputPointer.sqlite3_stmt(); int rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; diff --git a/manifest b/manifest index 81aaf952d4..f8be8f3203 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sa\spotential\sduplicate\sxDestroy()\sbeing\striggered\sfor\sJava-side\scollations. -D 2023-08-26T16:55:27.685 +C Bind\ssqlite3_db_handle()\sto\sJNI. +D 2023-08-26T17:30:14.460 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c b2ddceed02600dfdaa3a0d876111b7ad2d4f247f8396f3c1b68bb9ea2e129a4a -F ext/jni/src/c/sqlite3-jni.h 2745c4abd0933a4e8cc48989fffbad3936b4eaada64cce9ce11453dcd30e4184 +F ext/jni/src/c/sqlite3-jni.c 019f9b9342909af59982ee8c193a181202ef201b31c1f235e6958182c49e9d86 +F ext/jni/src/c/sqlite3-jni.h a4559d325925ca4d55ca75e2054adff607fdd35c2a94fecc9f2e8f919cababed F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 @@ -262,9 +262,9 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 4be23360d93011d80676bebb1f21f7da0fc4ab637a6d138c8c35bbb2f764b19d +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 9368994e8da4f8012466e4314f484818b8247c7417904fb04ffc87142c87aed5 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java f5205a22f10de63298ede691ed2938ab39084d1ff13d6f53a54c5100f798b933 +F ext/jni/src/org/sqlite/jni/Tester1.java 90c97ee08e8ad859695b79767869f609bb11b71ce7d6b119c766275d6cbfacf3 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 cf406528eb86d8d0d55a468b2c4ec32a11a4947f45c4bbabdde8742ae199ce1f -R 5e283ad208dc4181e7491a5a1cc6002c +P 25331172f78544f7f23ad7821cbf065589f6d02706184d4c83fe3988452dac5d +R edbf86b7a8a5043325b80898d6ff9011 U stephan -Z fd7fa811e5faceb76dda701b17b92b49 +Z 71902b3879f7020088c52617ebe652ae # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9422aa180e..9253a66eb1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -25331172f78544f7f23ad7821cbf065589f6d02706184d4c83fe3988452dac5d \ No newline at end of file +1a30919bfbb686464e8020e684120d22dd2375b910a17446b75452fda96a7449 \ No newline at end of file From 2a6835fe0c4c65c73138b2ec3ae9ae98fb09c15a Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 17:36:15 +0000 Subject: [PATCH 072/422] Two significant typo fixes for [1a30919bfbb686]. FossilOrigin-Name: 6a6ee6dd54ec1a1dc7fb7d2f331951525d394a8362aef911f62878ac3961898f --- ext/jni/src/c/sqlite3-jni.c | 2 +- ext/jni/src/org/sqlite/jni/Tester1.java | 4 ++-- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 629126dd62..fefd19223d 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2814,7 +2814,7 @@ S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( JniArgsEnvClass, jobject jpStmt ){ - sqlite3_stmt * const pStmt = PtrGet_sqlite3_context(jpStmt); + sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; return ps ? ps->jDb : 0; diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 426cee598f..1a4ee178c6 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -926,8 +926,8 @@ public class Tester1 implements Runnable { @ManualTest /* because threads inherently break this test */ private void testBusy(){ final String dbName = "_busy-handler.db"; - final OutputPointer.sqlite3 outDb = OutputPointer.sqlite3(); - final OutputPointer.sqlite3_stmt outStmt = OutputPointer.sqlite3_stmt(); + final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); + final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt(); int rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; diff --git a/manifest b/manifest index f8be8f3203..c6ef430101 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bind\ssqlite3_db_handle()\sto\sJNI. -D 2023-08-26T17:30:14.460 +C Two\ssignificant\stypo\sfixes\sfor\s[1a30919bfbb686]. +D 2023-08-26T17:36:15.621 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 019f9b9342909af59982ee8c193a181202ef201b31c1f235e6958182c49e9d86 +F ext/jni/src/c/sqlite3-jni.c 96d9681396fef07c83372ce949359bf069e04cd648279760b3bd65352a3cf889 F ext/jni/src/c/sqlite3-jni.h a4559d325925ca4d55ca75e2054adff607fdd35c2a94fecc9f2e8f919cababed F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 9368994e8da4f8012466e4314f484818b8247c7417904fb04ffc87142c87aed5 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java 90c97ee08e8ad859695b79767869f609bb11b71ce7d6b119c766275d6cbfacf3 +F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 25331172f78544f7f23ad7821cbf065589f6d02706184d4c83fe3988452dac5d -R edbf86b7a8a5043325b80898d6ff9011 +P 1a30919bfbb686464e8020e684120d22dd2375b910a17446b75452fda96a7449 +R daab7741306924c5da59534b151007f9 U stephan -Z 71902b3879f7020088c52617ebe652ae +Z 2446667f02ee59a9738709662543977d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9253a66eb1..a24f802dbd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1a30919bfbb686464e8020e684120d22dd2375b910a17446b75452fda96a7449 \ No newline at end of file +6a6ee6dd54ec1a1dc7fb7d2f331951525d394a8362aef911f62878ac3961898f \ No newline at end of file From 220b6145f5c895ebdfeb2ceaab3786317c9728cf Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 18:01:36 +0000 Subject: [PATCH 073/422] Re-order some out-of-order JNI APIs. Correct JNI sqlite3_open_v2() behavior in a particular OOM condition. FossilOrigin-Name: 49d3be002ce5e594027f47a3ba448f0c21ec68b416b8df997497753f53e3ca52 --- ext/jni/src/c/sqlite3-jni.c | 98 +++++++++++----------- ext/jni/src/c/sqlite3-jni.h | 32 +++---- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 12 +-- manifest | 16 ++-- manifest.uuid | 2 +- 5 files changed, 81 insertions(+), 79 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index fefd19223d..a056b0b534 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1220,8 +1220,7 @@ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, S3NphRef const ** OOM. pDb MUST, on success of the calling operation, subsequently be ** associated with jDb via NativePointerHolder_set(). */ -static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, - jobject jDb){ +static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ S3JniDb * rv; S3JniMutex_S3JniDb_enter; if( SJG.perDb.aFree ){ @@ -1250,7 +1249,6 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, sqlite3 *pDb, rv->pNext->pPrev = rv; } rv->jDb = S3JniRefGlobal(jDb); - rv->pDb = pDb; } S3JniMutex_S3JniDb_leave; return rv; @@ -2700,6 +2698,37 @@ error_cleanup: return (jint)rc; } +S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( + JniArgsEnvClass, jobject jDb, jstring jDbName +){ + S3JniDb * const ps = S3JniDb_from_java(jDb); + char *zDbName; + jstring jRv = 0; + int nStr = 0; + + if( !ps || !jDbName ){ + return 0; + } + zDbName = s3jni_jstring_to_utf8(env, jDbName, &nStr); + if( zDbName ){ + char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); + sqlite3_free(zDbName); + if( zRv ){ + jRv = s3jni_utf8_to_jstring(env, zRv, -1); + } + } + return jRv; +} + +S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( + JniArgsEnvClass, jobject jpStmt +){ + sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); + sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; + S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; + return ps ? ps->jDb : 0; +} + S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/, jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ @@ -2789,37 +2818,6 @@ JniDecl(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPoint ); } -S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( - JniArgsEnvClass, jobject jDb, jstring jDbName -){ - S3JniDb * const ps = S3JniDb_from_java(jDb); - char *zDbName; - jstring jRv = 0; - int nStr = 0; - - if( !ps || !jDbName ){ - return 0; - } - zDbName = s3jni_jstring_to_utf8(env, jDbName, &nStr); - if( zDbName ){ - char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); - sqlite3_free(zDbName); - if( zRv ){ - jRv = s3jni_utf8_to_jstring(env, zRv, -1); - } - } - return jRv; -} - -S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( - JniArgsEnvClass, jobject jpStmt -){ - sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); - sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; - S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; - return ps ? ps->jDb : 0; -} - S3JniApi(sqlite3_db_status(),jint,1db_1status)( JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent, @@ -2918,10 +2916,9 @@ S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( } /* -** Uncaches the current JNIEnv from the S3JniGlobal state, clearing any -** resources owned by that cache entry and making that slot available -** for re-use. It is important that the Java-side decl of this -** function be declared as synchronous. +** Uncaches the current JNIEnv from the S3JniGlobal state, clearing +** any resources owned by that cache entry and making that slot +** available for re-use. */ JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){ int rc; @@ -2938,7 +2935,7 @@ S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); } -/* Pre-open() code common to sqlite3_open(_v2)(). */ +/* Pre-open() code common to sqlite3_open[_v2](). */ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, S3JniDb ** ps){ @@ -2961,7 +2958,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, rc = SQLITE_NOMEM; goto end; } - *ps = S3JniDb_alloc(env, 0, jDb); + *ps = S3JniDb_alloc(env, jDb); if( *ps ){ (*jc)->pdbOpening = *ps; }else{ @@ -2981,6 +2978,9 @@ end: ** else ps is associated with *ppDb. If *ppDb is not NULL then ** ps->jDb is stored in jOut (an OutputPointer.sqlite3 instance). ** +** Must be called if s3jni_open_pre() succeeds and must not be called +** if it doesn't. +** ** Returns theRc. */ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, @@ -3031,16 +3031,18 @@ S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( S3JniEnv * jc = 0; char *zVfs = 0; int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); - if( 0==rc && strVfs ){ - zVfs = s3jni_jstring_to_utf8(env, strVfs, 0); - if( !zVfs ){ - rc = SQLITE_NOMEM; - } - } if( 0==rc ){ - rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs); + if( strVfs ){ + zVfs = s3jni_jstring_to_utf8(env, strVfs, 0); + if( !zVfs ){ + rc = SQLITE_NOMEM; + } + } + if( 0==rc ){ + rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs); + } + rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); } - rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); sqlite3_free(zVfs); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index c4fbf915be..49b4cba177 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1139,22 +1139,6 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1create_1function JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1data_1count (JNIEnv *, jclass, jobject); -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_db_filename - * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;)Ljava/lang/String; - */ -JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename - (JNIEnv *, jclass, jobject, jstring); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_db_handle - * Signature: (Lorg/sqlite/jni/sqlite3_stmt;)Lorg/sqlite/jni/sqlite3; - */ -JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1handle - (JNIEnv *, jclass, jobject); - /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_db_config @@ -1171,6 +1155,22 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 (JNIEnv *, jclass, jobject, jint, jstring); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_db_filename + * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename + (JNIEnv *, jclass, jobject, jstring); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_db_handle + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;)Lorg/sqlite/jni/sqlite3; + */ +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1handle + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_db_status diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index b37265484b..4c13286fe0 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -541,12 +541,6 @@ public final class SQLite3Jni { @NotNull sqlite3_stmt stmt ); - public static native String sqlite3_db_filename( - @NotNull sqlite3 db, @NotNull String dbName - ); - - public static native sqlite3 sqlite3_db_handle( @NotNull sqlite3_stmt stmt ); - /** Overload for sqlite3_db_config() calls which take (int,int*) variadic arguments. Returns SQLITE_MISUSE if op is not one of the @@ -567,6 +561,12 @@ public final class SQLite3Jni { @NotNull sqlite3 db, int op, @NotNull String val ); + public static native String sqlite3_db_filename( + @NotNull sqlite3 db, @NotNull String dbName + ); + + public static native sqlite3 sqlite3_db_handle( @NotNull sqlite3_stmt stmt ); + public static native int sqlite3_db_status( @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset diff --git a/manifest b/manifest index c6ef430101..99091cafcc 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Two\ssignificant\stypo\sfixes\sfor\s[1a30919bfbb686]. -D 2023-08-26T17:36:15.621 +C Re-order\ssome\sout-of-order\sJNI\sAPIs.\sCorrect\sJNI\ssqlite3_open_v2()\sbehavior\sin\sa\sparticular\sOOM\scondition. +D 2023-08-26T18:01:36.744 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 96d9681396fef07c83372ce949359bf069e04cd648279760b3bd65352a3cf889 -F ext/jni/src/c/sqlite3-jni.h a4559d325925ca4d55ca75e2054adff607fdd35c2a94fecc9f2e8f919cababed +F ext/jni/src/c/sqlite3-jni.c 2bbd0443b336a9c4d4cfea37a254592c96cf95f18f5aad0c60ff45968311bb61 +F ext/jni/src/c/sqlite3-jni.h 22c6c760a31ebfc3fe13d45d2a3a4dd7c8f9c6207aeba3fdc38137452cbf3a04 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 9368994e8da4f8012466e4314f484818b8247c7417904fb04ffc87142c87aed5 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 663600b216653850bfe4e2c00cfc8da12db94b3f36c4b79f6a7d1bde6ad0b484 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 1a30919bfbb686464e8020e684120d22dd2375b910a17446b75452fda96a7449 -R daab7741306924c5da59534b151007f9 +P 6a6ee6dd54ec1a1dc7fb7d2f331951525d394a8362aef911f62878ac3961898f +R 802391c6b76d7ea843bfc8720b7feccc U stephan -Z 2446667f02ee59a9738709662543977d +Z 04c34431ca2b1ac34ae8b832852ca4c0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a24f802dbd..8dd2e2bd90 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6a6ee6dd54ec1a1dc7fb7d2f331951525d394a8362aef911f62878ac3961898f \ No newline at end of file +49d3be002ce5e594027f47a3ba448f0c21ec68b416b8df997497753f53e3ca52 \ No newline at end of file From 6a1ebbdb909a973b431ca088a7e386b2508fbe8a Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 18:15:33 +0000 Subject: [PATCH 074/422] Remove a bunch of commented-out debug output. FossilOrigin-Name: b49488481e2952294960bb0ee971f6eca126c19d68ef92152894aa28393e6865 --- ext/jni/src/c/sqlite3-jni.c | 20 -------------------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index a056b0b534..93cb975af7 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -635,44 +635,36 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Env_enter \ S3JniMutex_Env_assertNotLocker; \ - /*MARKER(("Entering ENV mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ s3jni_incr(&SJG.metrics.nMutexEnv); \ SJG.envCache.locker = env #define S3JniMutex_Env_leave \ - /*MARKER(("Leaving ENV mutex @%p %s.\n", env));*/ \ S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) #define S3JniMutex_Ext_enter \ - /*MARKER(("Entering autoExt mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.autoExt.mutex ); \ SJG.autoExt.locker = env; \ s3jni_incr( &SJG.metrics.nMutexAutoExt ) #define S3JniMutex_Ext_leave \ - /*MARKER(("Leaving autoExt mutex@%p %s.\n", env));*/ \ assert( env == SJG.autoExt.locker ); \ sqlite3_mutex_leave( SJG.autoExt.mutex ) #define S3JniMutex_Ext_assertLocker \ assert( env == SJG.autoExt.locker ) #define S3JniMutex_Global_enter \ - /*MARKER(("Entering GLOBAL mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.mutex ); \ s3jni_incr(&SJG.metrics.nMutexGlobal); #define S3JniMutex_Global_leave \ - /*MARKER(("Leaving GLOBAL mutex @%p %s.\n", env));*/ \ sqlite3_mutex_leave( SJG.mutex ) #define S3JniMutex_Nph_enter \ S3JniMutex_Env_assertNotLocker; \ - /*MARKER(("Entering NPH mutex@%p %s.\n", env));*/ \ sqlite3_mutex_enter( SJG.envCache.mutex ); \ s3jni_incr( &SJG.metrics.nMutexEnv2 ); \ SJG.envCache.locker = env #define S3JniMutex_Nph_leave \ - /*MARKER(("Leaving NPH mutex @%p %s.\n", env));*/ \ S3JniMutex_Env_assertLocker; \ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) @@ -683,7 +675,6 @@ static void s3jni_incr( volatile unsigned int * const p ){ s3jni_incr( &SJG.metrics.nMutexPerDb ); \ SJG.perDb.locker = env; #define S3JniMutex_S3JniDb_leave \ - /*MARKER(("Leaving PerDb mutex@%p %s.\n", env));*/ \ assert( env == SJG.perDb.locker ); \ SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) @@ -1635,7 +1626,6 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ static void S3JniUdf_free(S3JniUdf * s){ S3JniDeclLocal_env; - //MARKER(("UDF cleanup: %s\n", s->zFuncName)); s3jni_call_xDestroy(env, s->jObj); S3JniUnrefGlobal(s->jObj); sqlite3_free(s->zFuncName); @@ -1648,7 +1638,6 @@ static void S3JniUdf_free(S3JniUdf * s){ } static void S3JniUdf_finalizer(void * s){ - //MARKER(("UDF finalizer @ %p\n", s)); S3JniUdf_free((S3JniUdf*)s); } @@ -1754,7 +1743,6 @@ static int udf_xFSI(sqlite3_context* const pCx, int argc, udf_jargs args = {0,0}; int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); - //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); if( 0 == rc ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv); S3JniIfThrew{ @@ -1778,12 +1766,10 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; int const isFinal = 'F'==zFuncType[1]/*xFinal*/; - //MARKER(("%s.%s() cx = %p\n", s->zFuncName, zFuncType, cx)); if( !jcx ){ if( isFinal ) sqlite3_result_error_nomem(cx); return SQLITE_NOMEM; } - //MARKER(("UDF::%s.%s()\n", s->zFuncName, zFuncType)); (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); S3JniIfThrew{ rc = udf_report_exception(env, isFinal, cx, s->zFuncName, @@ -3104,11 +3090,6 @@ end: S3JniUnrefLocal(jStmt); jStmt = 0; } -#if 0 - if( 0!=rc ){ - MARKER(("prepare rc = %d\n", rc)); - } -#endif OutputPointer_set_sqlite3_stmt(env, jOutStmt, jStmt); return (jint)rc; } @@ -3840,7 +3821,6 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ case SQLITE_TRACE_STMT: jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); if( !jX ) rc = SQLITE_NOMEM; - /*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/ break; case SQLITE_TRACE_PROFILE: jX = (*env)->NewObject(env, SJG.g.cLong, SJG.g.ctorLong1, diff --git a/manifest b/manifest index 99091cafcc..69941ee6c3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Re-order\ssome\sout-of-order\sJNI\sAPIs.\sCorrect\sJNI\ssqlite3_open_v2()\sbehavior\sin\sa\sparticular\sOOM\scondition. -D 2023-08-26T18:01:36.744 +C Remove\sa\sbunch\sof\scommented-out\sdebug\soutput. +D 2023-08-26T18:15:33.351 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 2bbd0443b336a9c4d4cfea37a254592c96cf95f18f5aad0c60ff45968311bb61 +F ext/jni/src/c/sqlite3-jni.c 5ccdea9487e66f0850aaa817ffe8523a84c4376235ae967c3700513665c0883a F ext/jni/src/c/sqlite3-jni.h 22c6c760a31ebfc3fe13d45d2a3a4dd7c8f9c6207aeba3fdc38137452cbf3a04 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 6a6ee6dd54ec1a1dc7fb7d2f331951525d394a8362aef911f62878ac3961898f -R 802391c6b76d7ea843bfc8720b7feccc +P 49d3be002ce5e594027f47a3ba448f0c21ec68b416b8df997497753f53e3ca52 +R a3f75122b9068beff86867201ce6d194 U stephan -Z 04c34431ca2b1ac34ae8b832852ca4c0 +Z 6e844ab70f4837c2de7f262b42dd7747 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8dd2e2bd90..ea4bb054c3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -49d3be002ce5e594027f47a3ba448f0c21ec68b416b8df997497753f53e3ca52 \ No newline at end of file +b49488481e2952294960bb0ee971f6eca126c19d68ef92152894aa28393e6865 \ No newline at end of file From 6428cd18d61279701eac1d39dbf0b4e8fccf90eb Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 19:34:49 +0000 Subject: [PATCH 075/422] Correct a string length misuse in JNI sqlite3_result_error() in an OOM case. Unrelated minor JNI cleanups. FossilOrigin-Name: 4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd --- ext/jni/src/c/sqlite3-jni.c | 61 ++++++++++------------ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 26 +++------ manifest | 14 ++--- manifest.uuid | 2 +- 4 files changed, 43 insertions(+), 60 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 93cb975af7..bc776ae166 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -669,14 +669,16 @@ static void s3jni_incr( volatile unsigned int * const p ){ SJG.envCache.locker = 0; \ sqlite3_mutex_leave( SJG.envCache.mutex ) +#define S3JniMutex_S3JniDb_assertLocker \ + assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) #define S3JniMutex_S3JniDb_enter \ sqlite3_mutex_enter( SJG.perDb.mutex ); \ - assert( 0==SJG.perDb.locker ); \ + assert( 0==SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \ s3jni_incr( &SJG.metrics.nMutexPerDb ); \ SJG.perDb.locker = env; #define S3JniMutex_S3JniDb_leave \ - assert( env == SJG.perDb.locker ); \ - SJG.perDb.locker = 0; \ + assert( env == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \ + SJG.perDb.locker = 0; \ sqlite3_mutex_leave( SJG.perDb.mutex ) #else /* SQLITE_THREADSAFE==0 */ @@ -1016,19 +1018,18 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, /* ** Internal helper for many hook callback impls. Locks the S3JniDb -** mutex, makes a copy of src into dest, with one change if src->jObj +** mutex, makes a copy of src into dest, with one change: if src->jObj ** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj -** instead of a copy of the prior GLOBAL ref. Then unlocks the +** instead of a copy of the prior GLOBAL ref. Then it unlocks the ** mutex. If dest->jObj is not NULL when this returns then the caller ** is obligated to eventually free the new ref by passing dest->jObj ** to S3JniUnrefLocal(). The dest pointer must NOT be passed to ** S3JniHook_unref(), as that one assumes that dest->jObj is a GLOBAL -** ref. +** ref (it's illegal to try to unref the wrong ref type).. ** ** Background: when running a hook we need a call-local copy lest ** another thread modify the hook while we're running it. That copy -** has to haves its own Java reference, but it need only be -** call-local. +** has to have its own Java reference, but it need only be call-local. */ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, S3JniHook * const dest ){ @@ -1039,13 +1040,12 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, } /* -** Clears s's state and moves it to the free-list. +** Clears s's state and moves it to the free-list. Requires that that the +** caller has locked S3JniGlobal.perDb.mutex. */ -static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ +static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ if( s ){ -#if SQLITE_THREADSAFE - assert( S3JniGlobal.perDb.locker == env ); -#endif + S3JniMutex_S3JniDb_enter; assert(s->pPrev != s); assert(s->pNext != s); assert(s->pPrev ? (s->pPrev!=s->pNext) : 1); @@ -1075,12 +1075,6 @@ static void S3JniDb_set_aside_unlocked(JNIEnv * env, S3JniDb * const s){ s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; - } -} -static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ - if( s ){ - S3JniMutex_S3JniDb_enter; - S3JniDb_set_aside_unlocked(env, s); S3JniMutex_S3JniDb_leave; } } @@ -1168,7 +1162,8 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env, S3NphRef const* pR if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ - pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, "nativePointer", "J"); + pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, + pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing nativePointer field."); } S3JniMutex_Nph_leave; @@ -1254,6 +1249,10 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ ** a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 ** object, from which the db is fished out. ** +** If the lockMutex argument is true then the S3JniDb mutex is locked +** before starting work, else the caller is required to have locked +** it. +** ** Returns NULL if jDb and pDb are both NULL or if there is no ** matching S3JniDb entry for pDb or the pointer fished out of jDb. */ @@ -2226,9 +2225,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; - S3JniDb * ps = 0; + S3JniDb * const ps = S3JniDb_from_java(jDb); assert(version == 1 || version == 2); - ps = S3JniDb_from_java(jDb); if( ps ){ rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ @@ -2282,6 +2280,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, ps->hooks.collationNeeded.midCallback, ps->jDb, (jint)eTextRep, jName); S3JniIfThrew{ + S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); } S3JniUnrefLocal(jName); @@ -2394,9 +2393,9 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, int rc = 0; S3JniHook hook; - S3JniHook_localdup( env, - isCommit ? &ps->hooks.commit : &ps->hooks.rollback, - &hook); + S3JniHook_localdup( env, isCommit + ? &ps->hooks.commit : &ps->hooks.rollback, + &hook); if( hook.jObj ){ rc = isCommit ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) @@ -3495,8 +3494,7 @@ S3JniApi(sqlite3_result_double(),void,1result_1double)( } S3JniApi(sqlite3_result_error(),void,1result_1error)( - JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, - int eTextRep + JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, int eTextRep ){ const char * zUnspecified = "Unspecified error."; jsize const baLen = (*env)->GetArrayLength(env, baMsg); @@ -3504,12 +3502,12 @@ S3JniApi(sqlite3_result_error(),void,1result_1error)( switch( pjBuf ? eTextRep : SQLITE_UTF8 ){ case SQLITE_UTF8: { const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; - sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); + int const n = pjBuf ? (int)baLen : (int)sqlite3Strlen30(zMsg); + sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, n); break; } case SQLITE_UTF16: { - const void *zMsg = pjBuf - ? (const void *)pjBuf : (const void *)zUnspecified; + const void *zMsg = pjBuf; sqlite3_result_error16(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); break; } @@ -3678,13 +3676,12 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( ")I"); S3JniUnrefLocal(klazz); S3JniIfThrew { - S3JniHook_unref(env, pHook, 0); rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "Error setting up Java parts of authorizer hook."); }else{ rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); - if( rc ) S3JniHook_unref(env, pHook, 0); } + if( rc ) S3JniHook_unref(env, pHook, 0); } S3JniMutex_S3JniDb_leave; return rc; diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 4c13286fe0..598fa2a016 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -587,7 +587,8 @@ public final class SQLite3Jni { public static native String sqlite3_errstr(int resultCode); /** - Note that the offset values assume UTF-8-encoded SQL. + Note that the returned byte offset values assume UTF-8-encoded + inputs, so won't always match character offsets in Java Strings. */ public static native int sqlite3_error_offset(@NotNull sqlite3 db); @@ -595,23 +596,8 @@ public final class SQLite3Jni { public static native int sqlite3_initialize(); - /** - Design note/FIXME: we have a problem vis-a-vis 'synchronized' - here: we specifically want other threads to be able to cancel a - long-running thread, but this routine requires access to C-side - global state which does not have a mutex. Making this function - synchronized would make it impossible for a long-running job to - be cancelled from another thread. - - The mutexing problem here is not within the core lib or Java, but - within the cached data held by the JNI binding. The cache holds - per-thread state, used by all but a tiny fraction of the JNI - binding layer, and access to that state needs to be - mutex-protected. - */ public static native void sqlite3_interrupt(@NotNull sqlite3 db); - //! See sqlite3_interrupt() for threading concerns. public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db); public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); @@ -907,7 +893,7 @@ public final class SQLite3Jni { a complaint about the invalid argument. */ private static native void sqlite3_result_error( - @NotNull sqlite3_context cx, @Nullable byte[] msg, + @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); @@ -920,12 +906,12 @@ public final class SQLite3Jni { public static void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = (msg+"\0").getBytes(StandardCharsets.UTF_8); + final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_8); sqlite3_result_error(cx, utf8, SQLITE_UTF8); } public static void sqlite3_result_error16( - @NotNull sqlite3_context cx, @Nullable byte[] utf16 + @NotNull sqlite3_context cx, @NotNull byte[] utf16 ){ sqlite3_result_error(cx, utf16, SQLITE_UTF16); } @@ -933,7 +919,7 @@ public final class SQLite3Jni { public static void sqlite3_result_error16( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = (msg+"\0").getBytes(StandardCharsets.UTF_16); + final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_16); sqlite3_result_error(cx, utf8, SQLITE_UTF16); } diff --git a/manifest b/manifest index 69941ee6c3..e504f7eaa7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sa\sbunch\sof\scommented-out\sdebug\soutput. -D 2023-08-26T18:15:33.351 +C Correct\sa\sstring\slength\smisuse\sin\sJNI\ssqlite3_result_error()\sin\san\sOOM\scase.\sUnrelated\sminor\sJNI\scleanups. +D 2023-08-26T19:34:49.574 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 5ccdea9487e66f0850aaa817ffe8523a84c4376235ae967c3700513665c0883a +F ext/jni/src/c/sqlite3-jni.c 14ac371890c91b15eb0f996fe5bcc9f30dde996983845a5e32a147826b81f72d F ext/jni/src/c/sqlite3-jni.h 22c6c760a31ebfc3fe13d45d2a3a4dd7c8f9c6207aeba3fdc38137452cbf3a04 F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 663600b216653850bfe4e2c00cfc8da12db94b3f36c4b79f6a7d1bde6ad0b484 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 77a8f965c87af3913839ee12666f6feed6c97fd981c55dadcad8f069aad89c00 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 49d3be002ce5e594027f47a3ba448f0c21ec68b416b8df997497753f53e3ca52 -R a3f75122b9068beff86867201ce6d194 +P b49488481e2952294960bb0ee971f6eca126c19d68ef92152894aa28393e6865 +R 9cb44f7d9077da136013e6794a7e1baf U stephan -Z 6e844ab70f4837c2de7f262b42dd7747 +Z b123384a6d9af143d63c67db0e5f0b49 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ea4bb054c3..90c4134155 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b49488481e2952294960bb0ee971f6eca126c19d68ef92152894aa28393e6865 \ No newline at end of file +4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd \ No newline at end of file From 506a8b52f393eb1500b16bd5e72371859bd1b14c Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 21:02:50 +0000 Subject: [PATCH 076/422] Whether or not OOM is always fatal in JNI is now a compile-time option. FossilOrigin-Name: 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 --- ext/jni/GNUmakefile | 3 +- ext/jni/src/c/sqlite3-jni.c | 98 +++++++++++++------ ext/jni/src/c/sqlite3-jni.h | 48 ++++----- .../src/org/sqlite/jni/AggregateFunction.java | 4 +- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 71 +++++++------- manifest | 20 ++-- manifest.uuid | 2 +- 7 files changed, 140 insertions(+), 106 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 9eb65186a3..5f7038f051 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -182,6 +182,7 @@ SQLITE_OPT = \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ + -DSQLITE_JNI_FATAL_OOM=0 \ -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG @@ -330,7 +331,7 @@ doc: $(doc.index) javadoc: $(doc.index) # Force rebild of docs redoc: - @rm -f $(doc.index)/index.html + @rm -f $(doc.index) @$(MAKE) doc docserve: $(doc.index) cd $(dir.doc) && althttpd -max-age 1 -page index.html diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index bc776ae166..0c290f9e23 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -74,6 +74,14 @@ //# define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION //#endif +/**********************************************************************/ +/* SQLITE_J... */ +#ifdef SQLITE_JNI_FATAL_OOM +#if !SQLITE_JNI_FATAL_OOM +#undef SQLITE_JNI_FATAL_OOM +#endif +#endif + /**********************************************************************/ /* SQLITE_M... */ #ifndef SQLITE_MAX_ALLOCATION_SIZE @@ -717,14 +725,39 @@ static inline void s3jni_oom(JNIEnv * const env){ ** sqlite3_malloc() proxy which fails fatally on OOM. This should ** only be used for routines which manage global state and have no ** recovery strategy for OOM. For sqlite3 API which can reasonably -** return SQLITE_NOMEM, sqlite3_malloc() should be used instead. +** return SQLITE_NOMEM, s3jni_malloc() should be used instead. */ -static void * s3jni_malloc(JNIEnv * const env, size_t n){ +static void * s3jni_malloc_or_die(JNIEnv * const env, size_t n){ void * const rv = sqlite3_malloc(n); if( n && !rv ) s3jni_oom(env); return rv; } +/* +** Works like sqlite3_malloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. +*/ +static void * s3jni_malloc(JNIEnv * const env, size_t n){ + void * const rv = sqlite3_malloc(n); +#ifdef SQLITE_JNI_FATAL_OOM + if( n && !rv ) s3jni_oom(env); +#endif + return rv; +} + +/* +** Works like sqlite3_realloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. +*/ +static void * s3jni_realloc(JNIEnv * const env, void * p, size_t n){ + void * const rv = sqlite3_realloc(p, (int)n); +#ifdef SQLITE_JNI_FATAL_OOM + if( n && !rv ) s3jni_oom(env); +#endif + return rv; +} + + /* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. @@ -765,7 +798,7 @@ static S3JniEnv * S3JniEnv_get(JNIEnv * const env){ SJG.envCache.aFree = row->pNext; if( row->pNext ) row->pNext->pPrev = 0; }else{ - row = s3jni_malloc(env, sizeof(S3JniEnv)); + row = s3jni_malloc_or_die(env, sizeof(*row)); s3jni_incr( &SJG.metrics.envCacheAllocs ); } memset(row, 0, sizeof(*row)); @@ -885,7 +918,7 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, } nBa = (*env)->GetArrayLength(env, jba); if( nLen ) *nLen = (int)nBa; - rv = sqlite3_malloc( nBa + 1 ); + rv = s3jni_malloc( env, nBa + 1 ); if( rv ){ (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv); rv[nBa] = 0; @@ -1506,7 +1539,7 @@ typedef struct { ** to ResultJavaVal_finalizer(). */ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ - ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); + ResultJavaVal * const rv = s3jni_malloc(env, sizeof(ResultJavaVal)); if( rv ){ rv->jObj = jObj ? S3JniRefGlobal(jObj) : 0; } @@ -1590,7 +1623,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ } S3JniMutex_Global_leave; if( !s ){ - s = sqlite3_malloc(sizeof(*s)); + s = s3jni_malloc(env, sizeof(*s)); s3jni_incr(&SJG.metrics.nUdfAlloc); } if( s ){ @@ -2012,8 +2045,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ unsigned n = 1 + SJG.autoExt.nAlloc; S3JniAutoExtension * const aNew = - sqlite3_realloc( SJG.autoExt.pExt, - n * sizeof(S3JniAutoExtension) ); + s3jni_realloc( env, SJG.autoExt.pExt, n * sizeof(*ax) ); if( !aNew ){ rc = SQLITE_NOMEM; }else{ @@ -3397,8 +3429,8 @@ S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)( } /* Impl for sqlite3_result_text/blob() and friends. */ -static void result_blob_text(int as64, - int eTextRep/*only for (asBlob=0)*/, +static void result_blob_text(int as64 /* true for text64/blob64() mode */, + int eTextRep /* 0 for blobs, else SQLITE_UTF... */, JNIEnv * const env, sqlite3_context *pCx, jbyteArray jBa, jlong nMax){ int const asBlob = 0==eTextRep; @@ -3425,8 +3457,7 @@ static void result_blob_text(int as64, } if( as64 ){ /* 64-bit... */ static const jsize nLimit64 = - SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary!*/ - /* jsize is int32, not int64! */; + SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary*/; if( nBa > nLimit64 ){ sqlite3_result_error_toobig(pCx); }else if( asBlob ){ @@ -3739,18 +3770,6 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, return rc; } -S3JniApi(sqlite3_strglob(),jint,1strglob)( - JniArgsEnvClass, jbyteArray baG, jbyteArray baT -){ - return s3jni_strlike_glob(0, env, baG, baT, 0); -} - -S3JniApi(sqlite3_strlike(),jint,1strlike)( - JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar -){ - return s3jni_strlike_glob(1, env, baG, baT, escChar); -} - S3JniApi(sqlite3_shutdown(),jint,1shutdown)( JniArgsEnvClass ){ @@ -3776,6 +3795,18 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( return sqlite3_shutdown(); } +S3JniApi(sqlite3_strglob(),jint,1strglob)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT +){ + return s3jni_strlike_glob(0, env, baG, baT, 0); +} + +S3JniApi(sqlite3_strlike(),jint,1strlike)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar +){ + return s3jni_strlike_glob(1, env, baG, baT, escChar); +} + S3JniApi(sqlite3_sql(),jstring,1sql)( JniArgsEnvClass, jobject jpStmt ){ @@ -4113,7 +4144,8 @@ static void Fts5JniAux_xDestroy(void *p){ } static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ - Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux)); + Fts5JniAux * s = s3jni_malloc(env, sizeof(Fts5JniAux)); + if( s ){ jclass klazz; memset(s, 0, sizeof(Fts5JniAux)); @@ -4146,9 +4178,9 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ return new_NativePointerHolder_object(env, &S3NphRefs.fts5_api, sv); } -/** - Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton - instance, or NULL on OOM. +/* +** Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton +** instance, or NULL on OOM. */ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ if( !SJG.fts5.jFtsExt ){ @@ -4168,8 +4200,8 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ } /* -** Return a pointer to the fts5_api instance for database connection -** db. If an error occurs, return NULL and leave an error in the +** Returns a pointer to the fts5_api instance for database connection +** db. If an error occurs, returns NULL and leaves an error in the ** database handle (accessible using sqlite3_errcode()/errmsg()). */ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ @@ -4536,7 +4568,8 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; S3JniFts5AuxData * pAux; - pAux = sqlite3_malloc(sizeof(*pAux)); + + pAux = s3jni_malloc(env, sizeof(*pAux)); if( !pAux ){ if( jAux ){ /* Emulate how xSetAuxdata() behaves when it cannot alloc @@ -4692,9 +4725,10 @@ static void SQLTester_dup_func( char *z; int n = sqlite3_value_bytes(argv[0]); SQLTesterJni * const p = (SQLTesterJni *)sqlite3_user_data(context); + S3JniDeclLocal_env; ++p->nDup; - if( n>0 && (pOut = sqlite3_malloc( (n+16)&~7 ))!=0 ){ + if( n>0 && (pOut = s3jni_malloc( env, (n+16)&~7 ))!=0 ){ pOut[0] = 0x2bbf4b7c; z = (char*)&pOut[1]; memcpy(z, sqlite3_value_text(argv[0]), n); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 49b4cba177..ed64a45033 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1531,30 +1531,6 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64 (JNIEnv *, jclass, jobject, jbyteArray, jlong, jint); -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_shutdown - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown - (JNIEnv *, jclass); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_status - * Signature: (ILorg/sqlite/jni/OutputPointer/Int32;Lorg/sqlite/jni/OutputPointer/Int32;Z)I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status - (JNIEnv *, jclass, jint, jobject, jobject, jboolean); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_status64 - * Signature: (ILorg/sqlite/jni/OutputPointer/Int64;Lorg/sqlite/jni/OutputPointer/Int64;Z)I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 - (JNIEnv *, jclass, jint, jobject, jobject, jboolean); - /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_rollback_hook @@ -1579,6 +1555,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1set_1authorizer JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1set_1last_1insert_1rowid (JNIEnv *, jclass, jobject, jlong); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_shutdown + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown + (JNIEnv *, jclass); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_sleep @@ -1603,6 +1587,22 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1sourceid JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1sql (JNIEnv *, jclass, jobject); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_status + * Signature: (ILorg/sqlite/jni/OutputPointer/Int32;Lorg/sqlite/jni/OutputPointer/Int32;Z)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status + (JNIEnv *, jclass, jint, jobject, jobject, jboolean); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_status64 + * Signature: (ILorg/sqlite/jni/OutputPointer/Int64;Lorg/sqlite/jni/OutputPointer/Int64;Z)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 + (JNIEnv *, jclass, jint, jobject, jobject, jboolean); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_step diff --git a/ext/jni/src/org/sqlite/jni/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/AggregateFunction.java index 6b6c848763..033e4dbca0 100644 --- a/ext/jni/src/org/sqlite/jni/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/AggregateFunction.java @@ -54,7 +54,7 @@ public abstract class AggregateFunction extends SQLFunction { argument, the context is set to the given initial value. On all other calls, the 2nd argument is ignored. - @see SQLFunction.PerContextState#getAggregateState() + @see SQLFunction.PerContextState#getAggregateState */ protected final ValueHolder getAggregateState(sqlite3_context cx, T initialValue){ return map.getAggregateState(cx, initialValue); @@ -64,7 +64,7 @@ public abstract class AggregateFunction extends SQLFunction { To be called from the implementation's xFinal() method to fetch the final state of the UDF and remove its mapping. - see SQLFunction.PerContextState#takeAggregateState() + see SQLFunction.PerContextState#takeAggregateState */ protected final T takeAggregateState(sqlite3_context cx){ return map.takeAggregateState(cx); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 598fa2a016..1dd5979c6c 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -893,8 +893,7 @@ public final class SQLite3Jni { a complaint about the invalid argument. */ private static native void sqlite3_result_error( - @NotNull sqlite3_context cx, @NotNull byte[] msg, - int eTextRep + @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); public static void sqlite3_result_error( @@ -919,22 +918,20 @@ public final class SQLite3Jni { public static void sqlite3_result_error16( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_16); - sqlite3_result_error(cx, utf8, SQLITE_UTF16); + final byte[] utf16 = msg.getBytes(StandardCharsets.UTF_16); + sqlite3_result_error(cx, utf16, SQLITE_UTF16); } + /** + Equivalent to passing e.getMessage() to + sqlite3_result_error(db,String). + */ public static void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull Exception e ){ sqlite3_result_error(cx, e.getMessage()); } - public static void sqlite3_result_error16( - @NotNull sqlite3_context cx, @NotNull Exception e - ){ - sqlite3_result_error16(cx, e.getMessage()); - } - public static native void sqlite3_result_error_toobig( @NotNull sqlite3_context cx ); @@ -961,7 +958,7 @@ public final class SQLite3Jni { /** Binds the SQL result to the given object, or - {@link #sqlite3_result_null(sqlite3_context) sqlite3_result_null()} if {@code o} is null. Use + {@link #sqlite3_result_null} if {@code o} is null. Use {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} or {@link #sqlite3_column_java_object(sqlite3_stmt,int) sqlite3_column_java_object()} to fetch it. @@ -1077,13 +1074,13 @@ public final class SQLite3Jni { } private static native void sqlite3_result_text( - @NotNull sqlite3_context cx, @Nullable byte[] text, int maxLen + @NotNull sqlite3_context cx, @Nullable byte[] utf8, int maxLen ); public static void sqlite3_result_text( - @NotNull sqlite3_context cx, @Nullable byte[] text + @NotNull sqlite3_context cx, @Nullable byte[] utf8 ){ - sqlite3_result_text(cx, text, null==text ? 0 : text.length); + sqlite3_result_text(cx, utf8, null==utf8 ? 0 : utf8.length); } public static void sqlite3_result_text( @@ -1117,34 +1114,14 @@ public final class SQLite3Jni { long maxLength, int encoding ); - /** - Cleans up all stale per-thread state managed by the library, as - well as any registered auto-extensions, then calls the C-native - sqlite3_shutdown(). Calling this while database handles or - prepared statements are still active will leak resources. Trying - to use those objects after this routine is called invoked - undefined behavior. - */ - public static synchronized native int sqlite3_shutdown(); - - public static native int sqlite3_status( - int op, @NotNull OutputPointer.Int32 pCurrent, - @NotNull OutputPointer.Int32 pHighwater, boolean reset - ); - - public static native int sqlite3_status64( - int op, @NotNull OutputPointer.Int64 pCurrent, - @NotNull OutputPointer.Int64 pHighwater, boolean reset - ); - /** Sets the current UDF result to the given bytes, which are assumed be encoded in UTF-16 using the platform's byte order. */ public static void sqlite3_result_text16( - @NotNull sqlite3_context cx, @Nullable byte[] text + @NotNull sqlite3_context cx, @Nullable byte[] utf16 ){ - sqlite3_result_text64(cx, text, text.length, SQLITE_UTF16); + sqlite3_result_text64(cx, utf16, utf16.length, SQLITE_UTF16); } public static void sqlite3_result_text16( @@ -1170,12 +1147,34 @@ public final class SQLite3Jni { @NotNull sqlite3 db, long rowid ); + + /** + Cleans up all stale per-thread state managed by the library, as + well as any registered auto-extensions, then calls the C-native + sqlite3_shutdown(). Calling this while database handles or + prepared statements are still active will leak resources. Trying + to use those objects after this routine is called invoked + undefined behavior. + */ + public static synchronized native int sqlite3_shutdown(); + public static native int sqlite3_sleep(int ms); public static native String sqlite3_sourceid(); public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt); + + public static native int sqlite3_status( + int op, @NotNull OutputPointer.Int32 pCurrent, + @NotNull OutputPointer.Int32 pHighwater, boolean reset + ); + + public static native int sqlite3_status64( + int op, @NotNull OutputPointer.Int64 pCurrent, + @NotNull OutputPointer.Int64 pHighwater, boolean reset + ); + public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); /** diff --git a/manifest b/manifest index e504f7eaa7..a6bb095c8e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sa\sstring\slength\smisuse\sin\sJNI\ssqlite3_result_error()\sin\san\sOOM\scase.\sUnrelated\sminor\sJNI\scleanups. -D 2023-08-26T19:34:49.574 +C Whether\sor\snot\sOOM\sis\salways\sfatal\sin\sJNI\sis\snow\sa\scompile-time\soption. +D 2023-08-26T21:02:50.002 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,12 +233,12 @@ 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 d9244b5addf58868343a74a94faa71f829e7f40c163486d053f4b4bbea173703 +F ext/jni/GNUmakefile 4e60cdca419ac6783719da98379480b6f04d5d1b5fa1408c46fcb0c32565c571 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 14ac371890c91b15eb0f996fe5bcc9f30dde996983845a5e32a147826b81f72d -F ext/jni/src/c/sqlite3-jni.h 22c6c760a31ebfc3fe13d45d2a3a4dd7c8f9c6207aeba3fdc38137452cbf3a04 -F ext/jni/src/org/sqlite/jni/AggregateFunction.java e0aac6ccae05702f8ee779820570866a2760aaa57a73135c57c8d3580bef52d5 +F ext/jni/src/c/sqlite3-jni.c d6665b309171af316f83a422b0a1c360f760493d9d8cfe239c8f38b639daac47 +F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 +F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java efef1892e404f5780d81c46a7997cab874aff5db5131985dd3af319fc5e3adc7 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 77a8f965c87af3913839ee12666f6feed6c97fd981c55dadcad8f069aad89c00 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d640493f52c04e03f9c0525f16dcec6b718638ace22101c0ccfc014883a15b0b F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 b49488481e2952294960bb0ee971f6eca126c19d68ef92152894aa28393e6865 -R 9cb44f7d9077da136013e6794a7e1baf +P 4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd +R 73a65c0a564c2b6325f39c30c9023371 U stephan -Z b123384a6d9af143d63c67db0e5f0b49 +Z c1a9d88eb5b30bcb5f8055b2c7e2b244 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 90c4134155..bdcc15bba5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd \ No newline at end of file +320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 \ No newline at end of file From 1f1169ad098ef2d08f593fcc99fba77e0258c1b0 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 26 Aug 2023 21:04:54 +0000 Subject: [PATCH 077/422] Updates to testrunner.tcl so that it runs "make fuzztest" using multiple jobs. FossilOrigin-Name: 7596ea7074e0ac73312586ed3d28cdacf97f54b8af73f804cbc8066c94d4b4ef --- Makefile.in | 6 + ext/fts5/test/fts5optimize2.test | 21 +- ext/fts5/test/fts5optimize3.test | 45 ++ manifest | 21 +- manifest.uuid | 2 +- test/testrunner.tcl | 714 ++++++++++++++++--------------- test/testrunner_data.tcl | 72 +++- 7 files changed, 503 insertions(+), 378 deletions(-) create mode 100644 ext/fts5/test/fts5optimize3.test diff --git a/Makefile.in b/Makefile.in index 6043c869fb..e3bf1c0e7a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1291,6 +1291,12 @@ fulltestonly: $(TESTPROGS) fuzztest ./testfixture$(TEXE) $(TOP)/test/full.test # Fuzz testing +# +# WARNING: When the "fuzztest" target is run by the testrunner.tcl script, +# it does not actually run this code. Instead, it schedules equivalent +# commands. Therefore, if this target is updated, then code in +# testrunner_data.tcl (search for "trd_fuzztest_data") must also be updated. +# fuzztest: fuzzcheck$(TEXE) $(FUZZDATA) sessionfuzz$(TEXE) ./fuzzcheck$(TEXE) $(FUZZDATA) ./sessionfuzz$(TEXE) run $(TOP)/test/sessionfuzz-data1.db diff --git a/ext/fts5/test/fts5optimize2.test b/ext/fts5/test/fts5optimize2.test index a0782ee790..b0b28874c3 100644 --- a/ext/fts5/test/fts5optimize2.test +++ b/ext/fts5/test/fts5optimize2.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# TESTRUNNER: slow +# TESTRUNNER: superslow # source [file join [file dirname [info script]] fts5_common.tcl] @@ -42,23 +42,4 @@ do_execsql_test 1.2 { SELECT count(*) FROM t1('mno') } $nLoop -do_execsql_test 2.0 { - CREATE VIRTUAL TABLE t2 USING fts5(x); - INSERT INTO t2(t2, rank) VALUES('pgsz', 32); -} - -do_test 2.1 { - for {set ii 0} {$ii < $nLoop} {incr ii} { - execsql { - INSERT INTO t2 VALUES('abc def ghi'); - INSERT INTO t2 VALUES('jkl mno pqr'); - INSERT INTO t2(t2, rank) VALUES('merge', -1); - } - } -} {} - -do_execsql_test 2.2 { - SELECT count(*) FROM t2('mno') -} $nLoop - finish_test diff --git a/ext/fts5/test/fts5optimize3.test b/ext/fts5/test/fts5optimize3.test new file mode 100644 index 0000000000..7b11b9402d --- /dev/null +++ b/ext/fts5/test/fts5optimize3.test @@ -0,0 +1,45 @@ +# 2023 Aug 27 +# +# 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. +# +#*********************************************************************** +# +# TESTRUNNER: superslow +# + +source [file join [file dirname [info script]] fts5_common.tcl] +set testprefix fts5optimize2 + +# If SQLITE_ENABLE_FTS5 is defined, omit this file. +ifcapable !fts5 { + finish_test + return +} + +set nLoop 2500 + +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t2 USING fts5(x); + INSERT INTO t2(t2, rank) VALUES('pgsz', 32); +} + +do_test 1.1 { + for {set ii 0} {$ii < $nLoop} {incr ii} { + execsql { + INSERT INTO t2 VALUES('abc def ghi'); + INSERT INTO t2 VALUES('jkl mno pqr'); + INSERT INTO t2(t2, rank) VALUES('merge', -1); + } + } +} {} + +do_execsql_test 1.2 { + SELECT count(*) FROM t2('mno') +} $nLoop + +finish_test diff --git a/manifest b/manifest index a6bb095c8e..afcba707c7 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Whether\sor\snot\sOOM\sis\salways\sfatal\sin\sJNI\sis\snow\sa\scompile-time\soption. -D 2023-08-26T21:02:50.002 +C Updates\sto\stestrunner.tcl\sso\sthat\sit\sruns\s"make\sfuzztest"\susing\smultiple\sjobs. +D 2023-08-26T21:04:54.521 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 577177569fa57e613b74f60d66b03cd16e4326439dd62fd5b9690f5b83c34bf0 +F Makefile.in 345a8599cf8ff015db534cedad7af70a1a6c36e295b85d720966c18af836ed30 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc 26c2d196391a285c279adb10fd6001774d9b243af94b700b681e4a49cd476684 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 @@ -185,7 +185,8 @@ F ext/fts5/test/fts5multiclient.test 5ff811c028d6108045ffef737f1e9f05028af2458e4 F ext/fts5/test/fts5near.test 211477940142d733ac04fad97cb24095513ab2507073a99c2765c3ddd2ef58bd F ext/fts5/test/fts5onepass.test f9b7d9b2c334900c6542a869760290e2ab5382af8fbd618834bf1fcc3e7b84da F ext/fts5/test/fts5optimize.test 36a752d24c818792032e4ff502936fc9cc5ef938721696396fdc79214b2717f1 -F ext/fts5/test/fts5optimize2.test c7c97693abe8a2cb572acfb1f252d78f03d3984094cfc5eb2285a76d8a702a92 +F ext/fts5/test/fts5optimize2.test 93e742c36b487d8874621360af5b1ce4d39b04fb9e71ce9bc34015c5fc811785 +F ext/fts5/test/fts5optimize3.test bf9c91bb927d0fb2b9a06318a217a0419183ac5913842e062c7e0b98ea5d0fca F ext/fts5/test/fts5phrase.test 13e5d8e9083077b3d9c74315b3c92ec723cc6eb37c8155e0bfe1bba00559f07b F ext/fts5/test/fts5plan.test b65cfcca9ddd6fdaa118c61e17aeec8e8433bc5b6bb307abd116514f79c49c5a F ext/fts5/test/fts5porter.test 8d08010c28527db66bc3feebd2b8767504aaeb9b101a986342fa7833d49d0d15 @@ -1614,8 +1615,8 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl ccdfda84732cf8665bd8d3bfee79b80841e221459e5d00a632a3a5c758966e1f -F test/testrunner_data.tcl c448693eb6fdbadb78cb26f6253d4f335666f9836f988afa575de960b666b19f +F test/testrunner.tcl 1a1a000d486b3f0dfb6c78b425c64bf400fa0c75d1058e7b3ce6e23047f5cdaa +F test/testrunner_data.tcl fdcc95d995fd1ef8bbaac1bc105988016213037038161bb555100439793ada18 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -2103,8 +2104,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 4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd -R 73a65c0a564c2b6325f39c30c9023371 -U stephan -Z c1a9d88eb5b30bcb5f8055b2c7e2b244 +P 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 +R 386688df896b64f3b42ccef0d10cea60 +U dan +Z 8aa57743124b4e556d29bb22c82a5c1a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bdcc15bba5..642b289373 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 \ No newline at end of file +7596ea7074e0ac73312586ed3d28cdacf97f54b8af73f804cbc8066c94d4b4ef \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index d2f0fe7d53..a1d14a7544 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -59,7 +59,6 @@ Usage: where SWITCHES are: --jobs NUMBER-OF-JOBS - --fuzztest --zipvfs ZIPVFS-SOURCE-DIR Interesting values for PERMUTATION are: @@ -83,11 +82,6 @@ If a PERMUTATION is specified and is followed by the path to a Tcl script instead of a list of patterns, then that single Tcl test script is run with the specified permutation. -The --fuzztest option is ignored if the PERMUTATION is "release". Otherwise, -if it is present, then "make -C

fuzztest" is run as part of the tests, -where is the directory containing the testfixture binary used to -run the script. - The "status" and "njob" commands are designed to be run from the same directory as a running testrunner.tcl script that is running tests. The "status" command prints a report describing the current state and progress @@ -157,19 +151,22 @@ set TRG(zipvfs) "" ;# -zipvfs option, if any switch -nocase -glob -- $tcl_platform(os) { *darwin* { - set TRG(platform) osx - set TRG(make) make.sh - set TRG(makecmd) "bash make.sh" + set TRG(platform) osx + set TRG(make) make.sh + set TRG(makecmd) "bash make.sh" + set TRG(testfixture) testfixture } *linux* { - set TRG(platform) linux - set TRG(make) make.sh - set TRG(makecmd) "bash make.sh" + set TRG(platform) linux + set TRG(make) make.sh + set TRG(makecmd) "bash make.sh" + set TRG(testfixture) testfixture } *win* { - set TRG(platform) win - set TRG(make) make.bat - set TRG(makecmd) make.bat + set TRG(platform) win + set TRG(make) make.bat + set TRG(makecmd) make.bat + set TRG(testfixture) testfixture.exe } default { error "cannot determine platform!" @@ -181,20 +178,30 @@ switch -nocase -glob -- $tcl_platform(os) { # The database schema used by the testrunner.db database. # set TRG(schema) { - DROP TABLE IF EXISTS script; + DROP TABLE IF EXISTS jobs; DROP TABLE IF EXISTS config; - CREATE TABLE script( - build TEXT DEFAULT '', - config TEXT, - filename TEXT, -- full path to test script - slow BOOLEAN, -- true if script is "slow" + /* + ** This table contains one row for each job that testrunner.tcl must run + ** before the entire test run is finished. + */ + CREATE TABLE jobs( + /* Fields populated when db is initialized */ + jobid INTEGER PRIMARY KEY, -- id to identify job + displaytype TEXT NOT NULL, -- Type of test (for one line report) + displayname TEXT NOT NULL, -- Human readable job name + build TEXT NOT NULL DEFAULT '', -- make.sh/make.bat file request, if any + dirname TEXT NOT NULL DEFAULT '', -- directory name, if required + cmd TEXT NOT NULL, -- shell command to run + depid INTEGER, -- identifier of dependency (or '') + copydir TEXT, -- copy files from here + priority INTEGER NOT NULL, -- higher priority jobs may run earlier + + /* Fields updated as jobs run */ + starttime INTEGER, + endtime INTEGER, state TEXT CHECK( state IN ('', 'ready', 'running', 'done', 'failed') ), - time INTEGER, -- Time in ms - output TEXT, -- full output of test script - priority INTEGER, - jobtype TEXT CHECK( jobtype IN ('script', 'build', 'make') ), - PRIMARY KEY(build, config, filename) + output TEXT ); CREATE TABLE config( @@ -202,8 +209,8 @@ set TRG(schema) { value ) WITHOUT ROWID; - CREATE INDEX i1 ON script(state, jobtype); - CREATE INDEX i2 ON script(state, priority); + CREATE INDEX i1 ON jobs(state, priority); + CREATE INDEX i2 ON jobs(depid); } #------------------------------------------------------------------------- @@ -303,39 +310,14 @@ if {[llength $argv]==1 && [string compare -nocase status [lindex $argv 0]]==0 } { - proc display_job {build config filename {tm ""}} { - if {$config=="build"} { - set fname "build: $filename" - set config "" - } elseif {$config=="make"} { - set fname "make: $filename" - set config "" - } else { - set fname [file normalize $filename] - if {[string first $::srcdir $fname]==0} { - set fname [string range $fname [string length $::srcdir]+1 end] - } - } - set dfname [format %-33s $fname] + proc display_job {jobdict {tm ""}} { + array set job $jobdict + + set dfname [format %-60s $job(displayname)] - set dbuild "" - set dconfig "" - set dparams "" set dtm "" - if {$build!=""} { set dbuild $build } - if {$config!="" && $config!="full"} { set dconfig $config } - if {$dbuild!="" || $dconfig!=""} { - append dparams "(" - if {$dbuild!=""} {append dparams "build=$dbuild"} - if {$dbuild!="" && $dconfig!=""} {append dparams " "} - if {$dconfig!=""} {append dparams "config=$dconfig"} - append dparams ")" - set dparams [format %-33s $dparams] - } - if {$tm!=""} { - set dtm "\[${tm}ms\]" - } - puts " $dfname $dparams $dtm" + if {$tm!=""} { set dtm "\[[expr {$tm-$job(starttime)}]ms\]" } + puts " $dfname $dtm" } sqlite3 mydb $TRG(dbname) @@ -355,7 +337,7 @@ if {[llength $argv]==1 set total 0 foreach s {"" ready running done failed} { set S($s) 0 } mydb eval { - SELECT state, count(*) AS cnt FROM script GROUP BY 1 + SELECT state, count(*) AS cnt FROM jobs GROUP BY 1 } { incr S($state) $cnt incr total $cnt @@ -375,19 +357,17 @@ if {[llength $argv]==1 if {$S(running)>0} { puts "Running: " mydb eval { - SELECT build, config, filename, time FROM script WHERE state='running' - ORDER BY time - } { - display_job $build $config $filename [expr $now-$time] + SELECT * FROM jobs WHERE state='running' ORDER BY starttime + } job { + display_job [array get job] $now } } if {$S(failed)>0} { puts "Failures: " mydb eval { - SELECT build, config, filename FROM script WHERE state='failed' - ORDER BY 3 - } { - display_job $build $config $filename + SELECT * FROM jobs WHERE state='failed' ORDER BY starttime + } job { + display_job [array get job] } } @@ -408,8 +388,6 @@ for {set ii 0} {$ii < [llength $argv]} {incr ii} { incr ii set TRG(nJob) [lindex $argv $ii] if {$isLast} { usage } - } elseif {($n>2 && [string match "$a*" --fuzztest]) || $a=="-f"} { - set TRG(fuzztest) 1 } elseif {($n>2 && [string match "$a*" --zipvfs]) || $a=="-z"} { incr ii set TRG(zipvfs) [lindex $argv $ii] @@ -492,108 +470,6 @@ proc build_to_dirname {bname} { } #------------------------------------------------------------------------- -# Return a list of tests to run. Each element of the list is itself a -# list of two elements - the name of a permuations.test configuration -# followed by the full path to a test script. i.e.: -# -# {BUILD CONFIG FILENAME} {BUILD CONFIG FILENAME} ... -# -proc testset_patternlist {patternlist} { - global TRG - - set testset [list] ;# return value - - set first [lindex $patternlist 0] - - if {$first=="sdevtest" || $first=="mdevtest"} { - set CONFIGS(sdevtest) {All-Debug All-Sanitize} - set CONFIGS(mdevtest) {All-Debug All-O0} - - set patternlist [lrange $patternlist 1 end] - - foreach b $CONFIGS($first) { - lappend testset [list $b build testfixture] - lappend testset [list $b make fuzztest] - testset_append testset $b veryquick $patternlist - } - } elseif {$first=="release"} { - set platform $::TRG(platform) - - set patternlist [lrange $patternlist 1 end] - foreach b [trd_builds $platform] { - foreach c [trd_configs $platform $b] { - testset_append testset $b $c $patternlist - } - - if {[llength $patternlist]==0 || $b=="User-Auth"} { - set target testfixture - } else { - set target coretestprogs - } - lappend testset [list $b build $target] - } - - if {[llength $patternlist]==0} { - foreach b [trd_builds $platform] { - foreach e [trd_extras $platform $b] { - lappend testset [list $b make $e] - } - } - } - - set TRG(fuzztest) 0 ;# ignore --fuzztest option in this case - - } elseif {$first=="all"} { - - set clist [trd_all_configs] - set patternlist [lrange $patternlist 1 end] - foreach c $clist { - testset_append testset "" $c $patternlist - } - - } elseif {[info exists ::testspec($first)]} { - set clist $first - testset_append testset "" $first [lrange $patternlist 1 end] - } elseif { [llength $patternlist]==0 } { - testset_append testset "" veryquick $patternlist - } else { - testset_append testset "" full $patternlist - } - if {$TRG(fuzztest)} { - if {$TRG(platform)=="win"} { error "todo" } - lappend testset [list "" make fuzztest] - } - - set testset -} - -proc testset_append {listvar build config patternlist} { - upvar $listvar lvar - - catch { array unset O } - array set O $::testspec($config) - - foreach f $O(-files) { - if {[llength $patternlist]>0} { - set bMatch 0 - foreach p $patternlist { - if {[string match $p [file tail $f]]} { - set bMatch 1 - break - } - } - if {$bMatch==0} continue - } - - if {[file pathtype $f]!="absolute"} { - set f [file join $::testdir $f] - } - lappend lvar [list $build $config $f] - } -} - -#-------------------------------------------------------------------------- - proc r_write_db {tcl} { trdb eval { BEGIN EXCLUSIVE } @@ -615,54 +491,299 @@ proc r_get_next_job {iJob} { set orderby "ORDER BY priority DESC" } + set ret [list] + r_write_db { - set f "" - set c "" - trdb eval " - SELECT build, config, filename - FROM script - WHERE state='ready' - $orderby LIMIT 1 - " { - set b $build - set c $config - set f $filename - } - if {$f!=""} { + set query " + SELECT * FROM jobs AS j WHERE state='ready' $orderby LIMIT 1 + " + trdb eval $query job { set tm [clock_milliseconds] set T($iJob) $tm - trdb eval { - UPDATE script SET state='running', time=$tm - WHERE (build, config, filename) = ($b, $c, $f) + set jobid $job(jobid) + + trdb eval { + UPDATE jobs SET starttime=$tm, state='running' WHERE jobid=$jobid } + + set ret [array get job] } } - if {$f==""} { return "" } - list $b $c $f + return $ret } #rename r_get_next_job r_get_next_job_r #proc r_get_next_job {iJob} { -# puts [time { set res [r_get_next_job_r $iJob] }] -# set res + #puts [time { set res [r_get_next_job_r $iJob] }] + #set res #} +# Usage: +# +# add_job OPTION ARG OPTION ARG... +# +# where available OPTIONS are: +# +# -displaytype +# -displayname +# -build +# -dirname +# -cmd +# -depid +# -copydir +# -priority +# +# Returns the jobid value for the new job. +# +proc add_job {args} { + + set options { + -displaytype -displayname -build -dirname + -cmd -depid -copydir -priority + } + + # Set default values of options. + set A(-dirname) "" + set A(-depid) "" + set A(-copydir) "" + set A(-priority) 0 + set A(-build) "" + + array set A $args + + # Check all required options are present. And that no extras are present. + foreach o $options { + if {[info exists A($o)]==0} { error "missing required option $o" } + } + foreach o [array names A] { + if {[lsearch -exact $options $o]<0} { error "unrecognized option: $o" } + } + + set state "" + if {$A(-depid)==""} { set state ready } + + trdb eval { + INSERT INTO jobs( + displaytype, displayname, build, dirname, cmd, depid, copydir, priority, + state + ) VALUES ( + $A(-displaytype), + $A(-displayname), + $A(-build), + $A(-dirname), + $A(-cmd), + $A(-depid), + $A(-copydir), + $A(-priority), + $state + ) + } + + trdb last_insert_rowid +} + +proc add_tcl_jobs {build config patternlist} { + global TRG + + set topdir [file dirname $::testdir] + set testrunner_tcl [file normalize [info script]] + + if {$build==""} { + set testfixture [info nameofexec] + } else { + set testfixture [file join [lindex $build 1] $TRG(testfixture)] + } + + # The ::testspec array is populated by permutations.test + foreach f [dict get $::testspec($config) -files] { + + if {[llength $patternlist]>0} { + set bMatch 0 + foreach p $patternlist { + if {[string match $p [file tail $f]]} { + set bMatch 1 + break + } + } + if {$bMatch==0} continue + } + + if {[file pathtype $f]!="absolute"} { set f [file join $::testdir $f] } + set f [file normalize $f] + + set displayname [string map [list $topdir/ {}] $f] + if {$config=="full" || $config=="veryquick"} { + set cmd "$testfixture $f" + } else { + set cmd "$testfixture $testrunner_tcl $config $f" + set displayname "config=$config $displayname" + } + if {$build!=""} { + set displayname "[lindex $build 2] $displayname" + } + + set lProp [trd_test_script_properties $f] + set priority 0 + if {[lsearch $lProp slow]>=0} { set priority 2 } + if {[lsearch $lProp superslow]>=0} { set priority 4 } + + add_job \ + -displaytype tcl \ + -displayname $displayname \ + -cmd $cmd \ + -depid [lindex $build 0] \ + -priority $priority + + } +} + +proc add_build_job {buildname target} { + global TRG + + set dirname "[string tolower [string map {- _} $buildname]]_$target" + set dirname "testrunner_bld_$dirname" + + set id [add_job \ + -displaytype bld \ + -displayname "Build $buildname ($target)" \ + -dirname $dirname \ + -build $buildname \ + -cmd "$TRG(makecmd) $target" \ + -priority 3 + ] + + list $id [file normalize $dirname] $buildname +} + +proc add_make_job {bld target} { + global TRG + + add_job \ + -displaytype make \ + -displayname "[lindex $bld 2] make $target" \ + -cmd "$TRG(makecmd) $target" \ + -copydir [lindex $bld 1] \ + -depid [lindex $bld 0] \ + -priority 1 +} + +proc add_fuzztest_jobs {buildname} { + + foreach {interpreter scripts} [trd_fuzztest_data] { + set subcmd [lrange $interpreter 1 end] + set interpreter [lindex $interpreter 0] + + set bld [add_build_job $buildname $interpreter] + foreach {depid dirname displayname} $bld {} + + foreach s $scripts { + + # Fuzz data files fuzzdata1.db and fuzzdata2.db are larger than + # the others. So ensure that these are run as a higher priority. + set tail [file tail $s] + if {$tail=="fuzzdata1.db" || $tail=="fuzzdata2.db"} { + set priority 5 + } else { + set priority 1 + } + + add_job \ + -displaytype fuzz \ + -displayname "$buildname $interpreter $tail" \ + -depid $depid \ + -cmd "[file join $dirname $interpreter] $subcmd $s" \ + -priority $priority + } + } +} + +proc add_zipvfs_jobs {} { + global TRG + source [file join $TRG(zipvfs) test zipvfs_testrunner.tcl] + + set bld [add_build_job Zipvfs $TRG(testfixture)] + foreach s [zipvfs_testrunner_files] { + set cmd "[file join [lindex $bld 1] $TRG(testfixture)] $s" + add_job \ + -displaytype tcl \ + -displayname "Zipvfs [file tail $s]" \ + -cmd $cmd \ + -depid [lindex $bld 0] + } + + set ::env(SQLITE_TEST_DIR) $::testdir +} + +proc add_jobs_from_cmdline {patternlist} { + global TRG + + if {$TRG(zipvfs)!=""} { + add_zipvfs_jobs + if {[llength $patternlist]==0} return + } + + if {[llength $patternlist]==0} { + set patternlist [list veryquick] + } + + set first [lindex $patternlist 0] + switch -- $first { + all { + set patternlist [lrange $patternlist 1 end] + set clist [trd_all_configs] + foreach c $clist { + add_tcl_jobs "" $c $patternlist + } + } + + mdevtest { + foreach b [list All-O0 All-Debug] { + set bld [add_build_job $b $TRG(testfixture)] + add_tcl_jobs $bld veryquick "" + add_fuzztest_jobs $b + } + } + + sdevtest { + foreach b [list All-Sanitize All-Debug] { + set bld [add_build_job $b $TRG(testfixture)] + add_tcl_jobs $bld veryquick "" + add_fuzztest_jobs $b + } + } + + release { + foreach b [trd_builds $TRG(platform)] { + set bld [add_build_job $b $TRG(testfixture)] + foreach c [trd_configs $TRG(platform) $b] { + add_tcl_jobs $bld $c "" + } + + foreach e [trd_extras $TRG(platform) $b] { + if {$e=="fuzztest"} { + add_fuzztest_jobs $b + } else { + add_make_job $bld $e + } + } + } + } + + default { + if {[info exists ::testspec($first)]} { + add_tcl_jobs "" $first [lrange $patternlist 1 end] + } else { + add_tcl_jobs "" full $patternlist + } + } + } +} + proc make_new_testset {} { global TRG - set tests [list] - if {$TRG(zipvfs)!=""} { - source [file join $TRG(zipvfs) test zipvfs_testrunner.tcl] - lappend tests {*}[zipvfs_testrunner_testset] - } - - if {$tests=="" || $TRG(patternlist)!=""} { - lappend tests {*}[testset_patternlist $TRG(patternlist)] - } - r_write_db { - trdb eval $TRG(schema) set nJob $TRG(nJob) set cmdline $TRG(cmdline) @@ -671,88 +792,44 @@ proc make_new_testset {} { trdb eval { REPLACE INTO config VALUES('cmdline', $cmdline ); } trdb eval { REPLACE INTO config VALUES('start', $tm ); } - foreach t $tests { - foreach {b c s} $t {} - set slow 0 - - if {$c!="make" && $c!="build"} { - set fd [open $s] - for {set ii 0} {$ii<100 && ![eof $fd]} {incr ii} { - set line [gets $fd] - if {[string match -nocase *testrunner:* $line]} { - regexp -nocase {.*testrunner:(.*)} $line -> properties - foreach p $properties { - if {$p=="slow"} { set slow 1 } - if {$p=="superslow"} { set slow 2 } - } - } - } - close $fd - } - - if {$c=="make" && $b==""} { - # --fuzztest option - set slow 1 - } - - if {$c=="veryquick"} { - set c "" - } - - set state ready - if {$b!="" && $c!="build"} { - set state "" - } - - set priority [expr {$slow*2}] - if {$c=="make"} { incr priority 3 } - if {$c=="build"} { incr priority 1 } - - if {$c=="make" || $c=="build"} { - set jobtype $c - } else { - set jobtype "script" - } - - trdb eval { - INSERT INTO script - (build, config, filename, slow, state, priority, jobtype) - VALUES ($b, $c, $s, $slow, $state, $priority, $jobtype) - } - } + add_jobs_from_cmdline $TRG(patternlist) } + } -proc script_input_ready {fd iJob b c f} { +proc script_input_ready {fd iJob jobid} { global TRG global O global T if {[eof $fd]} { + trdb eval { SELECT * FROM jobs WHERE jobid=$jobid } job {} + set ::done 1 fconfigure $fd -blocking 1 set state "done" set rc [catch { close $fd } msg] if {$rc} { - puts "FAILED: $b $c $f" + if {[info exists TRG(reportlength)]} { + puts -nonewline "[string repeat " " $TRG(reportlength)]\r" + } + puts "FAILED: $job(displayname)" set state "failed" } - set tm [expr [clock_milliseconds] - $T($iJob)] + set tm [clock_milliseconds] + set jobtm [expr {$tm - $job(starttime)}] - puts $TRG(log) "### $b ### $c ### $f ${tm}ms ($state)" + puts $TRG(log) "### $job(displayname) ${jobtm}ms ($state)" puts $TRG(log) [string trim $O($iJob)] r_write_db { set output $O($iJob) trdb eval { - UPDATE script SET output = $output, state=$state, time=$tm - WHERE (build, config, filename) = ($b, $c, $f) - } - if {$state=="done" && $c=="build"} { - trdb eval { - UPDATE script SET state = 'ready' WHERE (build, state)==($b, '') - } + UPDATE jobs + SET output=$output, state=$state, endtime=$tm + WHERE jobid=$jobid; + UPDATE jobs SET state='ready' WHERE depid=$jobid; } } @@ -783,79 +860,43 @@ proc launch_another_job {iJob} { set testfixture [info nameofexec] set script $TRG(info_script) - set dir [dirname $iJob] - create_or_clear_dir $dir - set O($iJob) "" - set job [r_get_next_job $iJob] - if {$job==""} { return 0 } + set jobdict [r_get_next_job $iJob] + if {$jobdict==""} { return 0 } + array set job $jobdict - foreach {b c f} $job {} + set dir $job(dirname) + if {$dir==""} { set dir [dirname $iJob] } + create_or_clear_dir $dir - if {$c=="build"} { - set testdir [file dirname $TRG(info_script)] - set srcdir [file dirname $testdir] - set builddir [build_to_dirname $b] - create_or_clear_dir $builddir - - if {$b=="Zipvfs"} { + if {$job(build)!=""} { + set srcdir [file dirname $::testdir] + if {$job(build)=="Zipvfs"} { set script [zipvfs_testrunner_script] } else { - set script [trd_buildscript $b $srcdir [expr {$TRG(platform)=="win"}]] + set bWin [expr {$TRG(platform)=="win"}] + set script [trd_buildscript $job(build) $srcdir $bWin] } - - set fd [open [file join $builddir $TRG(make)] w] + set fd [open [file join $dir $TRG(make)] w] puts $fd $script close $fd + } - puts "Launching build \"$b\" in directory $builddir..." - set target coretestprogs - if {$b=="User-Auth"} { set target testfixture } - - set cmd "$TRG(makecmd) $target" - set dir $builddir - - } elseif {$c=="make"} { - if {$b==""} { - if {$f!="fuzztest"} { error "corruption in testrunner.db!" } - # Special case - run [make fuzztest] - set makedir [file dirname $testfixture] - if {$TRG(platform)=="win"} { - error "how?" - } else { - set cmd [list make -C $makedir fuzztest] - } - } else { - set builddir [build_to_dirname $b] - copy_dir $builddir $dir - set cmd "$TRG(makecmd) $f" + if {$job(copydir)!=""} { + foreach f [glob -nocomplain [file join $job(copydir) *]] { + catch { file copy -force $f $dir } } - } else { - if {$b==""} { - set testfixture [info nameofexec] - } else { - set tail testfixture - if {$TRG(platform)=="win"} { set tail testfixture.exe } - set testfixture [file normalize [file join [build_to_dirname $b] $tail]] - } - - if {$c=="valgrind"} { - set testfixture "valgrind -v --error-exitcode=1 $testfixture" - set ::env(OMIT_MISUSE) 1 - } - set cmd [concat $testfixture [list $script $c $f]] } set pwd [pwd] cd $dir - set fd [open "|$cmd 2>@1" r] + set fd [open "|$job(cmd) 2>@1" r] cd $pwd set pid [pid $fd] fconfigure $fd -blocking false - fileevent $fd readable [list script_input_ready $fd $iJob $b $c $f] - unset -nocomplain ::env(OMIT_MISUSE) + fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)] return 1 } @@ -866,29 +907,20 @@ proc one_line_report {} { set tm [expr [clock_milliseconds] - $TRG(starttime)] set tm [format "%d" [expr int($tm/1000.0 + 0.5)]] - foreach s {ready running done failed} { - set v($s,build) 0 - set v($s,make) 0 - set v($s,script) 0 - } - r_write_db { - trdb eval { - SELECT state, jobtype, count(*) AS cnt - FROM script - GROUP BY state, jobtype + trdb eval { + SELECT displaytype, state, count(*) AS cnt + FROM jobs + GROUP BY 1, 2 } { - set v($state,$jobtype) $cnt - if {[info exists t($jobtype)]} { - incr t($jobtype) $cnt - } else { - set t($jobtype) $cnt - } + set v($state,$displaytype) $cnt + incr t($displaytype) $cnt } } set text "" - foreach j [array names t] { + foreach j [lsort [array names t]] { + foreach k {done failed running} { incr v($k,$j) 0 } set fin [expr $v(done,$j) + $v(failed,$j)] lappend text "$j ($fin/$t($j)) f=$v(failed,$j) r=$v(running,$j)" } @@ -898,7 +930,7 @@ proc one_line_report {} { } set report "${tm}s: [join $text { }]" set TRG(reportlength) [string length $report] - if {[string length $report]<80} { + if {[string length $report]<100} { puts -nonewline "$report\r" flush stdout } else { @@ -910,9 +942,8 @@ proc one_line_report {} { proc launch_some_jobs {} { global TRG - r_write_db { - set nJob [trdb one { SELECT value FROM config WHERE name='njob' }] - } + set nJob [trdb one { SELECT value FROM config WHERE name='njob' }] + while {[dirs_nHelper]<$nJob} { set iDir [dirs_allocDir] if {0==[launch_another_job $iDir]} { @@ -930,7 +961,6 @@ proc run_testset {} { set TRG(log) [open $TRG(logname) w] launch_some_jobs - # launch_another_job $ii one_line_report while {[dirs_nHelper]>0} { @@ -943,13 +973,13 @@ proc run_testset {} { r_write_db { set tm [clock_milliseconds] trdb eval { REPLACE INTO config VALUES('end', $tm ); } - set nErr [trdb one {SELECT count(*) FROM script WHERE state='failed'}] + set nErr [trdb one {SELECT count(*) FROM jobs WHERE state='failed'}] if {$nErr>0} { puts "$nErr failures:" trdb eval { - SELECT build, config, filename FROM script WHERE state='failed' + SELECT displayname FROM jobs WHERE state='failed' } { - puts "FAILED: $build $config $filename" + puts "FAILED: $displayname" } } } diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl index ce2ce01dd6..1fead5b4e2 100644 --- a/test/testrunner_data.tcl +++ b/test/testrunner_data.tcl @@ -370,15 +370,45 @@ proc trd_configs {platform bld} { proc trd_extras {platform bld} { trd_import + if {[info exists extra($platform.$bld)]==0} { return [list] } + return $extra($platform.$bld) +} - set elist [list] - if {[info exists extra($platform.$bld)]} { - set elist $extra($platform.$bld) +# Usage: +# +# trd_fuzztest_data +# +# This returns data used by testrunner.tcl to run commands equivalent +# to [make fuzztest]. The returned value is a list, which should be +# interpreted as a sequence of pairs. The first element of each pair +# is an interpreter name. The second element is a list of files. +# testrunner.tcl automatically creates one job to build each interpreter, +# and one to run each of the files with it once it has been built. +# +# In practice, the returned value looks like this: +# +# { +# {fuzzcheck {$testdir/fuzzdata1.db $testdir/fuzzdata2.db ...}} +# {{sessionfuzz run} $testdir/sessionfuzz-data1.db} +# } +# +# where $testdir is replaced by the full-path to the test-directory (the +# directory containing this file). "fuzzcheck" and "sessionfuzz" have .exe +# extensions on windows. +# +proc trd_fuzztest_data {} { + set EXE "" + if {$::tcl_platform(platform)=="windows"} { + set EXE ".exe" } - set elist + set lFuzzDb [glob [file join $::testdir fuzzdata*.db]] + set lSessionDb [glob [file join $::testdir sessionfuzz-data*.db]] + + return [list fuzzcheck$EXE $lFuzzDb "sessionfuzz$EXE run" $lSessionDb] } + proc trd_all_configs {} { trd_import set all_configs @@ -394,7 +424,7 @@ proc make_sh_script {srcdir opts cflags makeOpts configOpts} { set myopts "" if {[info exists ::env(OPTS)]} { append myopts "# From environment variable:\n" - append myopts "OPTS=$::env(OPTS)\n" + append myopts "OPTS=$::env(OPTS)\n\n" } foreach o [lsort $opts] { append myopts "OPTS=\"\$OPTS $o\"\n" @@ -560,4 +590,36 @@ proc trd_buildscript {config srcdir bMsvc} { return [make_script $build($config) $srcdir $bMsvc] } +# Usage: +# +# trd_test_script_properties PATH +# +# The argument must be a path to a Tcl test script. This function scans the +# first 100 lines of the script for lines that look like: +# +# TESTRUNNER: +# +# where is a list of identifiers, each of which defines a +# property of the test script. Example properties are "slow" or "superslow". +# +proc trd_test_script_properties {path} { + # Use this global array as a cache: + global trd_test_script_properties_cache + + if {![info exists trd_test_script_properties_cache($path)]} { + set fd [open $path] + set ret [list] + for {set line 0} {$line < 100 && ![eof $fd]} {incr line} { + set text [gets $fd] + if {[string match -nocase *testrunner:* $text]} { + regexp -nocase {.*testrunner:(.*)} $text -> properties + lappend ret {*}$properties + } + } + set trd_test_script_properties_cache($path) $ret + close $fd + } + + set trd_test_script_properties_cache($path) +} From 0deaea218daa79688ba14e010d9746f19dafadff Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 21:13:57 +0000 Subject: [PATCH 078/422] Eliminate a superfluous JNI-internal middle-man class. FossilOrigin-Name: daede0f801f59d6501a863c4688e4635b34171e98b56b8ab4432c779113f1997 --- ext/jni/src/c/sqlite3-jni.c | 44 +++++++------------------------------ manifest | 12 +++++----- manifest.uuid | 2 +- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 0c290f9e23..353ea552d5 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1518,44 +1518,17 @@ static void CollationState_xDestroy(void *pArg){ S3JniMutex_S3JniDb_leave; } -/* -** State for sqlite3_result_java_object() and -** sqlite3_value_java_object(). -** -** TODO: this middle-man struct is no longer necessary. Conider -** removing it and passing around jObj itself instead. OTOH, we might -** find more state to pack in here. -*/ -typedef struct { - jobject jObj; -} ResultJavaVal; - /* For use with sqlite3_result/value_pointer() */ #define ResultJavaValuePtrStr "org.sqlite.jni.ResultJavaVal" /* -** Allocate a new ResultJavaVal and assign it a new global ref of -** jObj. Caller owns the returned object and must eventually pass it -** to ResultJavaVal_finalizer(). +** If v is not NULL, it must be a jobject global reference. Its +** reference is relinquished and v is freed. */ -static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ - ResultJavaVal * const rv = s3jni_malloc(env, sizeof(ResultJavaVal)); - if( rv ){ - rv->jObj = jObj ? S3JniRefGlobal(jObj) : 0; - } - return rv; -} - -/* -** If v is not NULL, it must point to a a ResultJavaVal object. Its -** object reference is relinquished and v is freed. -*/ -static void ResultJavaVal_finalizer(void *v){ +static void ResultJavaValue_finalizer(void *v){ if( v ){ - ResultJavaVal * const rv = (ResultJavaVal*)v; S3JniDeclLocal_env; - S3JniUnrefGlobal(rv->jObj); - sqlite3_free(rv); + S3JniUnrefGlobal((jobject)v); } } @@ -3585,10 +3558,10 @@ S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( JniArgsEnvClass, jobject jpCx, jobject v ){ if( v ){ - ResultJavaVal * const rjv = ResultJavaVal_alloc(env, v); + jobject const rjv = S3JniRefGlobal(v); if( rjv ){ sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv, - ResultJavaValuePtrStr, ResultJavaVal_finalizer); + ResultJavaValuePtrStr, ResultJavaValue_finalizer); }else{ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } @@ -3974,9 +3947,8 @@ S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( JniArgsEnvClass, jobject jpSVal ){ - ResultJavaVal * const rv = sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal), - ResultJavaValuePtrStr); - return rv ? rv->jObj : NULL; + return sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal), + ResultJavaValuePtrStr); } S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)( diff --git a/manifest b/manifest index a6bb095c8e..29c3be6deb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Whether\sor\snot\sOOM\sis\salways\sfatal\sin\sJNI\sis\snow\sa\scompile-time\soption. -D 2023-08-26T21:02:50.002 +C Eliminate\sa\ssuperfluous\sJNI-internal\smiddle-man\sclass. +D 2023-08-26T21:13:57.847 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 4e60cdca419ac6783719da98379480b6f04d5d1b5fa1408c46fcb0c32565c571 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c d6665b309171af316f83a422b0a1c360f760493d9d8cfe239c8f38b639daac47 +F ext/jni/src/c/sqlite3-jni.c b5777fb1da4cd81bc91a051ebfa59ba28722536c4c98560350a7c219f9da265e F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 4252f56f3d8574b7b43306440726daf3b5f5500d5d9105784b2f82753e7c71dd -R 73a65c0a564c2b6325f39c30c9023371 +P 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 +R bd8a050119f034bd484ec7a8a853653c U stephan -Z c1a9d88eb5b30bcb5f8055b2c7e2b244 +Z 713c3c8622f9cf1b7dfe5fb5695c9634 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index bdcc15bba5..a04046908a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 \ No newline at end of file +daede0f801f59d6501a863c4688e4635b34171e98b56b8ab4432c779113f1997 \ No newline at end of file From c7e7c88873710118420ae3a6d91e31d45fa833c6 Mon Sep 17 00:00:00 2001 From: stephan Date: Sat, 26 Aug 2023 22:34:26 +0000 Subject: [PATCH 079/422] Apply the JNI OOM checks to memory returned by JDK APIs, as distinct from our APIs. FossilOrigin-Name: 1ff78582bfd934e0c76464b5f23ed9bf09a3491b145e0ca34acb6e59c4f53995 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 191 ++++++++++++++++++++---------------- manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 115 insertions(+), 94 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 5f7038f051..dd8df07bc4 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -182,7 +182,7 @@ SQLITE_OPT = \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ - -DSQLITE_JNI_FATAL_OOM=0 \ + -DSQLITE_JNI_FATAL_OOM=1 \ -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 353ea552d5..c210d298a0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -706,20 +706,39 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_S3JniDb_leave #endif -/* Helpers for jstring and jbyteArray. */ -#define s3jni_jstring_to_mutf8(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) -#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) -#define s3jni_jbytearray_bytes(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) -#define s3jni_jbytearray_release(ARG,VAR) if( VAR ) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) - - /* Fail fatally with an OOM message. */ static inline void s3jni_oom(JNIEnv * const env){ (*env)->FatalError(env, "Out of memory.") /* does not return */; } -/* Fail fatally if !VAR. */ -#define s3jni_oom_check(VAR) if( !(VAR) ) s3jni_oom(env) +/* Fail fatally if !EXPR. */ +#define s3jni_oom_fatal(EXPR) if( !(EXPR) ) s3jni_oom(env) +/* Maybe fail fatally if !EXPR. */ +#ifdef SQLITE_JNI_FATAL_OOM +#define s3jni_oom_check s3jni_oom_fatal +#else +#define s3jni_oom_check(EXPR) +#endif + +/* Helpers for jstring and jbyteArray. */ +static const char * s3jni__jstring_to_mutf8_bytes(JNIEnv * const env, jstring v ){ + const char *z = v ? (*env)->GetStringUTFChars(env, v, NULL) : 0; + s3jni_oom_check( v ? !!z : !z ); + return z; +} + +#define s3jni_jstring_to_mutf8(ARG) s3jni__jstring_to_mutf8_bytes(env, (ARG)) +#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) + +static jbyte * s3jni__jbytearray_bytes(JNIEnv * const env, jbyteArray jBA ){ + jbyte * const rv = jBA ? (*env)->GetByteArrayElements(env, jBA, NULL) : 0; + s3jni_oom_check( jBA ? !!rv : 1 ); + return rv; +} + +#define s3jni_jbytearray_bytes(jByteArray) s3jni__jbytearray_bytes(env, (jByteArray)) +#define s3jni_jbytearray_release(jByteArray,jBytes) \ + if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT) /* ** sqlite3_malloc() proxy which fails fatally on OOM. This should @@ -841,13 +860,17 @@ static int s3jni_db_error(sqlite3* const db, int err_code, } /* -** Creates a new jByteArray of length nP, copies p's contents into it, and -** returns that byte array (NULL on OOM). +** Creates a new jByteArray of length nP, copies p's contents into it, +** and returns that byte array (NULL on OOM unless fail-fast alloc +** errors are enabled). p may be NULL, in which case the array is +** created but no bytes are filled. */ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, - const unsigned char * const p, int nP){ + const void * const p, int nP){ jbyteArray jba = (*env)->NewByteArray(env, (jint)nP); - if( jba ){ + + s3jni_oom_check( jba ); + if( jba && p ){ (*env)->SetByteArrayRegion(env, jba, 0, (jint)nP, (const jbyte*)p); } return jba; @@ -876,13 +899,18 @@ static jstring s3jni_utf8_to_jstring(JNIEnv * const env, }else if( z ){ jbyteArray jba; if( n<0 ) n = sqlite3Strlen30(z); - jba = s3jni_new_jbyteArray(env, (unsigned const char *)z, (jsize)n); + jba = s3jni_new_jbyteArray(env, (unsigned const char *)z, n); if( jba ){ rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA, jba, SJG.g.oCharsetUtf8); + S3JniIfThrew{ + S3JniExceptionReport; + S3JniExceptionClear; + } S3JniUnrefLocal(jba); } } + s3jni_oom_check( rv ); return rv; } @@ -910,9 +938,11 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, if( !jstr ) return 0; jba = (*env)->CallObjectMethod(env, jstr, SJG.g.stringGetBytes, SJG.g.oCharsetUtf8); + if( (*env)->ExceptionCheck(env) || !jba /* order of these checks is significant for -Xlint:jni */ ) { S3JniExceptionReport; + s3jni_oom_check( jba ); if( nLen ) *nLen = 0; return 0; } @@ -935,9 +965,11 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, ** if !p or (*env)->NewString() fails. */ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){ - return p + jstring const rv = p ? (*env)->NewString(env, (const jchar *)p, (jsize)(nP/2)) : NULL; + s3jni_oom_check( p ? !!rv : 1 ); + return rv; } /* @@ -1488,15 +1520,14 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, S3JniHook_localdup(env, &ps->hooks.collation, &hook ); if( hook.jObj ){ - jbyteArray jbaLhs = (*env)->NewByteArray(env, (jint)nLhs); - jbyteArray jbaRhs = jbaLhs ? (*env)->NewByteArray(env, (jint)nRhs) : NULL; + jbyteArray jbaLhs = s3jni_new_jbyteArray(env, lhs, (jint)nLhs); + jbyteArray jbaRhs = jbaLhs + ? s3jni_new_jbyteArray(env, rhs, (jint)nRhs) : 0; if( !jbaRhs ){ S3JniUnrefLocal(jbaLhs); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); return 0; } - (*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->hooks.collation.jObj, ps->hooks.collation.midCallback, jbaLhs, jbaRhs); @@ -1673,6 +1704,7 @@ static int udf_args(JNIEnv *env, ja = (*env)->NewObjectArray(env, argc, SJG.g.cObj, NULL); + s3jni_oom_check( ja ); if( !ja ) goto error_oom; for(i = 0; i < argc; ++i){ jobject jsv = new_sqlite3_value_wrapper(env, argv[i]); @@ -1840,9 +1872,11 @@ static void udf_xInverse(sqlite3_context* cx, int argc, ** CName(void)). This is only valid for functions which are known to ** return ASCII or text which is equivalent in UTF-8 and MUTF-8. */ -#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ - JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ - return (*env)->NewStringUTF( env, CName() ); \ +#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ + jstring const rv = (*env)->NewStringUTF( env, CName() ); \ + s3jni_oom_check(rv); \ + return rv; \ } /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */ #define WRAP_INT_STMT(JniNameSuffix,CName) \ @@ -2053,9 +2087,16 @@ S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax ){ jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0; - int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, - pBuf, (int)nMax, SQLITE_TRANSIENT); - s3jni_jbytearray_release(baData,pBuf); + int rc; + if( pBuf ){ + rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, + pBuf, (int)nMax, SQLITE_TRANSIENT); + s3jni_jbytearray_release(baData, pBuf); + }else{ + rc = baData + ? SQLITE_NOMEM + : sqlite3_bind_null( PtrGet_sqlite3_stmt(jpStmt), ndx ); + } return (jint)rc; } @@ -2277,6 +2318,8 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, if( hook.jObj ){ unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); + + s3jni_oom_check( jName ); S3JniIfThrew{ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); S3JniExceptionClear; @@ -2339,12 +2382,8 @@ S3JniApi(sqlite3_column_blob(),jbyteArray,1column_1blob)( sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); void const * const p = sqlite3_column_blob(pStmt, (int)ndx); int const n = p ? sqlite3_column_bytes(pStmt, (int)ndx) : 0; - if( 0==p ) return NULL; - else{ - jbyteArray const jba = (*env)->NewByteArray(env, n); - (*env)->SetByteArrayRegion(env, jba, 0, n, (const jbyte *)p); - return jba; - } + + return p ? s3jni_new_jbyteArray(env, p, n) : 0; } S3JniApi(sqlite3_column_double(),jdouble,1column_1double)( @@ -2483,14 +2522,17 @@ S3JniApi(sqlite3_commit_hook(),jobject,1commit_1hook)( S3JniApi(sqlite3_compileoption_get(),jstring,1compileoption_1get)( JniArgsEnvClass, jint n ){ - return (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ) + jstring const rv = (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) ) /* We know these to be ASCII, so MUTF-8 is fine. */; + s3jni_oom_check(rv); + return rv; } S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( JniArgsEnvClass, jstring name ){ - const char *zUtf8 = s3jni_jstring_to_mutf8(name); + const char *zUtf8 = s3jni_jstring_to_mutf8(name) + /* We know these to be ASCII, so MUTF-8 is fine. */; const jboolean rc = 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; s3jni_mutf8_release(name, zUtf8); @@ -2840,9 +2882,11 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)( S3JniApi(sqlite3_errstr(),jstring,1errstr)( JniArgsEnvClass, jint rcCode ){ - return (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode)) + jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode)) /* We know these values to be plain ASCII, so pose no MUTF-8 ** incompatibility */; + s3jni_oom_check( rv ); + return rv; } S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( @@ -2852,7 +2896,7 @@ S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( jstring rv = 0; if( pStmt ){ char * zSql = sqlite3_expanded_sql(pStmt); - s3jni_oom_check(zSql); + s3jni_oom_fatal(zSql); if( zSql ){ rv = s3jni_utf8_to_jstring(env, zSql, -1); sqlite3_free(zSql); @@ -2862,8 +2906,7 @@ S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( } S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)( - JniArgsEnvClass, jobject jpDb, - jboolean onoff + JniArgsEnvClass, jobject jpDb, jboolean onoff ){ int const rc = sqlite3_extended_result_codes(PtrGet_sqlite3(jpDb), onoff ? 1 : 0); return rc ? JNI_TRUE : JNI_FALSE; @@ -3695,7 +3738,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)( JniArgsEnvClass, jobject jpDb, jlong rowId ){ - sqlite3_set_last_insert_rowid(PtrGet_sqlite3_context(jpDb), + sqlite3_set_last_insert_rowid(PtrGet_sqlite3(jpDb), (sqlite3_int64)rowId); } @@ -3731,7 +3774,7 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, jbyte * const pG = s3jni_jbytearray_bytes(baG); jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0; - s3jni_oom_check(pT); + s3jni_oom_fatal(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike @@ -3828,6 +3871,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ (jlong)*((sqlite3_int64*)pX)); // hmm. ^^^ (*pX) really is zero. // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); + s3jni_oom_check( jX ); if( !jX ) rc = SQLITE_NOMEM; break; case SQLITE_TRACE_ROW: @@ -3902,13 +3946,11 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); int const nLen = sqlite3_value_bytes(sv); const jbyte * pBytes = sqlite3_value_blob(sv); - jbyteArray const jba = pBytes - ? (*env)->NewByteArray(env, (jsize)nLen) + + s3jni_oom_check( nLen ? !!pBytes : 1 ); + return pBytes + ? s3jni_new_jbyteArray(env, pBytes, nLen) : NULL; - if( jba ){ - (*env)->SetByteArrayRegion(env, jba, 0, nLen, pBytes); - } - return jba; } @@ -3960,38 +4002,20 @@ S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)( return p ? s3jni_new_jbyteArray(env, p, n) : 0; } -static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){ - sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); - int const nLen = sqlite3_value_bytes16(sv); - jbyteArray jba; - const jbyte * pBytes; - switch( mode ){ - case SQLITE_UTF16: - pBytes = sqlite3_value_text16(sv); - break; - case SQLITE_UTF16LE: - pBytes = sqlite3_value_text16le(sv); - break; - case SQLITE_UTF16BE: - pBytes = sqlite3_value_text16be(sv); - break; - default: - assert(!"not possible"); - return NULL; - } - jba = pBytes - ? (*env)->NewByteArray(env, (jsize)nLen) - : NULL; - if( jba ){ - (*env)->SetByteArrayRegion(env, jba, 0, nLen, pBytes); - } - return jba; -} - S3JniApi(sqlite3_value_text16(),jbyteArray,1value_1text16)( JniArgsEnvClass, jobject jpSVal ){ - return value_text16(SQLITE_UTF16, env, jpSVal); + sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); + jbyteArray jba = 0; + if( sv ){ + int const nLen = sqlite3_value_bytes16(sv); + const jbyte * const pBytes = + nLen ? sqlite3_value_text16(sv) : 0; + + s3jni_oom_check( nLen ? !!pBytes : 1 ); + jba = s3jni_new_jbyteArray(env, pBytes, nLen); + } + return jba; } JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ @@ -4567,15 +4591,12 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, if( s->tok.zPrev == z && s->tok.nPrev == nZ ){ jba = s->tok.jba; }else{ - if(s->tok.jba){ - S3JniUnrefLocal(s->tok.jba); - } + S3JniUnrefLocal(s->tok.jba); s->tok.zPrev = z; s->tok.nPrev = nZ; - s->tok.jba = (*env)->NewByteArray(env, (jint)nZ); + s->tok.jba = s3jni_new_jbyteArray(env, z, nZ); if( !s->tok.jba ) return SQLITE_NOMEM; jba = s->tok.jba; - (*env)->SetByteArrayRegion(env, jba, 0, (jint)nZ, (const jbyte*)z); } rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, (jint)tFlags, jba, (jint)iStart, @@ -4833,7 +4854,7 @@ Java_org_sqlite_jni_tester_SQLTester_strglob( jbyte * const pG = s3jni_jbytearray_bytes(baG); jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0; - s3jni_oom_check(pT); + s3jni_oom_fatal(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = !SQLTester_strnotglob((const char *)pG, (const char *)pT); @@ -4970,17 +4991,17 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ #endif SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - s3jni_oom_check( SJG.mutex ); + s3jni_oom_fatal( SJG.mutex ); SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - s3jni_oom_check( SJG.envCache.mutex ); + s3jni_oom_fatal( SJG.envCache.mutex ); SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - s3jni_oom_check( SJG.perDb.mutex ); + s3jni_oom_fatal( SJG.perDb.mutex ); SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - s3jni_oom_check( SJG.autoExt.mutex ); + s3jni_oom_fatal( SJG.autoExt.mutex ); #if S3JNI_METRICS_MUTEX SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - s3jni_oom_check( SJG.metrics.mutex ); + s3jni_oom_fatal( SJG.metrics.mutex ); #endif sqlite3_shutdown() diff --git a/manifest b/manifest index 29c3be6deb..3e1f473740 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Eliminate\sa\ssuperfluous\sJNI-internal\smiddle-man\sclass. -D 2023-08-26T21:13:57.847 +C Apply\sthe\sJNI\sOOM\schecks\sto\smemory\sreturned\sby\sJDK\sAPIs,\sas\sdistinct\sfrom\sour\sAPIs. +D 2023-08-26T22:34:26.393 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 4e60cdca419ac6783719da98379480b6f04d5d1b5fa1408c46fcb0c32565c571 +F ext/jni/GNUmakefile c1893766d909a9748151a0d52a3c18eb6aa37d2aaba187eb5fe45df344f96d2a F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c b5777fb1da4cd81bc91a051ebfa59ba28722536c4c98560350a7c219f9da265e +F ext/jni/src/c/sqlite3-jni.c 9440a767fd1019cffec368b3542af0c0a8c7c6da1a7c6f285dc19e07f670ced8 F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 -R bd8a050119f034bd484ec7a8a853653c +P daede0f801f59d6501a863c4688e4635b34171e98b56b8ab4432c779113f1997 +R 6bd1f56d9b8c83901a03e2a9ce4434ac U stephan -Z 713c3c8622f9cf1b7dfe5fb5695c9634 +Z 362c439d994aeafc4f637309d1fc43bd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a04046908a..128decbd67 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -daede0f801f59d6501a863c4688e4635b34171e98b56b8ab4432c779113f1997 \ No newline at end of file +1ff78582bfd934e0c76464b5f23ed9bf09a3491b145e0ca34acb6e59c4f53995 \ No newline at end of file From 0f4bf3435aad7e9de0d76d1efcf3c0598177b84a Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 07:26:33 +0000 Subject: [PATCH 080/422] JNI code reorgs and simplify the failing-alloc interface a bit. FossilOrigin-Name: deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32 --- ext/jni/GNUmakefile | 2 +- ext/jni/src/c/sqlite3-jni.c | 198 ++++++++++++++++++++---------------- manifest | 14 +-- manifest.uuid | 2 +- 4 files changed, 118 insertions(+), 98 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index dd8df07bc4..5f7038f051 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -182,7 +182,7 @@ SQLITE_OPT = \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ - -DSQLITE_JNI_FATAL_OOM=1 \ + -DSQLITE_JNI_FATAL_OOM=0 \ -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index c210d298a0..d219039d30 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -216,19 +216,73 @@ } /* -** Helpers for extracting pointers from jobjects, noting that we rely -** on the corresponding Java interfaces having already done the -** type-checking. Don't use these in contexts where that's not the -** case. +** Declares local var env = s3jni_env(). All JNI calls involve a +** JNIEnv somewhere, always named env, and many of our macros assume +** env is in scope. */ -#define PtrGet_T(T,OBJ) NativePointerHolder_get(env, 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) -#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) +#define S3JniDeclLocal_env JNIEnv * const env = s3jni_env() + +/* Fail fatally with an OOM message. */ +static inline void s3jni_oom(JNIEnv * const env){ + (*env)->FatalError(env, "SQLite3 JNI is out of memory.") /* does not return */; +} + +/* +** sqlite3_malloc() proxy which fails fatally on OOM. This should +** only be used for routines which manage global state and have no +** recovery strategy for OOM. For sqlite3 API which can reasonably +** return SQLITE_NOMEM, s3jni_malloc() should be used instead. +*/ +static void * s3jni_malloc_or_die(JNIEnv * const env, size_t n){ + void * const rv = sqlite3_malloc(n); + if( n && !rv ) s3jni_oom(env); + return rv; +} + +/* +** Works like sqlite3_malloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. +*/ +#ifdef SQLITE_JNI_FATAL_OOM +#define s3jni_malloc(SIZE) s3jni_malloc_or_die(env, SIZE) +#else +#define s3jni_malloc(SIZE) sqlite3_malloc(((void)env,(SIZE))) +#endif + +/* +** Works like sqlite3_realloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. +*/ +#ifdef SQLITE_JNI_FATAL_OOM +static void * s3jni_realloc_or_die(JNIEnv * const env, void * p, size_t n){ + void * const rv = sqlite3_realloc(p, (int)n); + if( n && !rv ) s3jni_oom(env); + return rv; +} +#define s3jni_realloc(MEM,SIZE) s3jni_realloc_or_die(env, (MEM), (SIZE)) +#else +#define s3jni_realloc(MEM,SIZE) sqlite3_realloc((MEM), ((void)env, (SIZE))) +#endif + +/* Fail fatally if !EXPR. */ +#define s3jni_oom_fatal(EXPR) if( !(EXPR) ) s3jni_oom(env) +/* Maybe fail fatally if !EXPR. */ +#ifdef SQLITE_JNI_FATAL_OOM +#define s3jni_oom_check s3jni_oom_fatal +#else +#define s3jni_oom_check(EXPR) +#endif + /* Helpers for Java value reference management. */ -static inline jobject s3jni_ref_global(JNIEnv * const env, jobject const v){ - return v ? (*env)->NewGlobalRef(env, v) : NULL; +static jobject s3jni_ref_global(JNIEnv * const env, jobject const v){ + jobject const rv = v ? (*env)->NewGlobalRef(env, v) : NULL; + s3jni_oom_fatal( v ? !!rv : 1 ); + return rv; +} +static jobject s3jni_ref_local(JNIEnv * const env, jobject const v){ + jobject const rv = v ? (*env)->NewLocalRef(env, v) : NULL; + s3jni_oom_fatal( v ? !!rv : 1 ); + return rv; } static inline void s3jni_unref_global(JNIEnv * const env, jobject const v){ if( v ) (*env)->DeleteGlobalRef(env, v); @@ -237,7 +291,7 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ if( v ) (*env)->DeleteLocalRef(env, v); } #define S3JniRefGlobal(VAR) s3jni_ref_global(env, (VAR)) -#define S3JniRefLocal(VAR) (*env)->NewLocalRef(env, (VAR)) +#define S3JniRefLocal(VAR) s3jni_ref_local(env, (VAR)) #define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR)) #define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) @@ -472,7 +526,7 @@ struct S3JniUdf { ** else it isn't. */ #ifdef SQLITE_DEBUG -# define S3JNI_METRICS_MUTEX 1 +# define S3JNI_METRICS_MUTEX SQLITE_THREADSAFE #else # define S3JNI_METRICS_MUTEX 0 #endif @@ -552,16 +606,16 @@ struct S3JniGlobalType { org.sqlite.jni.auto_extension objects). */ struct { - S3JniAutoExtension *pExt /* The auto-extension list. It is + S3JniAutoExtension *aExt /* The auto-extension list. It is maintained such that all active entries are in the first contiguous nExt array elements. */; - int nAlloc /* number of entries allocated for pExt, + int nAlloc /* number of entries allocated for aExt, as distinct from the number of active entries. */; - int nExt /* number of active entries in pExt, all in the + int nExt /* number of active entries in aExt, all in the first nExt'th array elements. */; - sqlite3_mutex * mutex /* mutex for manipulation/traversal of pExt */; + sqlite3_mutex * mutex /* mutex for manipulation/traversal of aExt */; const void * locker /* object on whose behalf the mutex is held. Only for sanity checking in debug builds. */; } autoExt; @@ -618,6 +672,26 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal +/* +** Helpers for extracting pointers from jobjects, noting that we rely +** on the corresponding Java interfaces having already done the +** type-checking. OBJ must be a jobject referring to a +** NativePointerHolder, where T matches PtrGet_T. Don't use these +** in contexts where that's not the case. Note that these aren't +** type-safe in the strictest sense: +** +** sqlite3 * s = PtrGet_sqlite3_stmt(...) +** +** will work, despite the incorrect macro name, so long as the +** 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_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) +#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) + /* Increments *p, possibly protected by a mutex. */ #ifndef SQLITE_JNI_ENABLE_METRICS #define s3jni_incr(PTR) @@ -706,20 +780,6 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_S3JniDb_leave #endif -/* Fail fatally with an OOM message. */ -static inline void s3jni_oom(JNIEnv * const env){ - (*env)->FatalError(env, "Out of memory.") /* does not return */; -} - -/* Fail fatally if !EXPR. */ -#define s3jni_oom_fatal(EXPR) if( !(EXPR) ) s3jni_oom(env) -/* Maybe fail fatally if !EXPR. */ -#ifdef SQLITE_JNI_FATAL_OOM -#define s3jni_oom_check s3jni_oom_fatal -#else -#define s3jni_oom_check(EXPR) -#endif - /* Helpers for jstring and jbyteArray. */ static const char * s3jni__jstring_to_mutf8_bytes(JNIEnv * const env, jstring v ){ const char *z = v ? (*env)->GetStringUTFChars(env, v, NULL) : 0; @@ -740,43 +800,6 @@ static jbyte * s3jni__jbytearray_bytes(JNIEnv * const env, jbyteArray jBA ){ #define s3jni_jbytearray_release(jByteArray,jBytes) \ if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT) -/* -** sqlite3_malloc() proxy which fails fatally on OOM. This should -** only be used for routines which manage global state and have no -** recovery strategy for OOM. For sqlite3 API which can reasonably -** return SQLITE_NOMEM, s3jni_malloc() should be used instead. -*/ -static void * s3jni_malloc_or_die(JNIEnv * const env, size_t n){ - void * const rv = sqlite3_malloc(n); - if( n && !rv ) s3jni_oom(env); - return rv; -} - -/* -** Works like sqlite3_malloc() unless built with SQLITE_JNI_FATAL_OOM, -** in which case it calls s3jni_oom() on OOM. -*/ -static void * s3jni_malloc(JNIEnv * const env, size_t n){ - void * const rv = sqlite3_malloc(n); -#ifdef SQLITE_JNI_FATAL_OOM - if( n && !rv ) s3jni_oom(env); -#endif - return rv; -} - -/* -** Works like sqlite3_realloc() unless built with SQLITE_JNI_FATAL_OOM, -** in which case it calls s3jni_oom() on OOM. -*/ -static void * s3jni_realloc(JNIEnv * const env, void * p, size_t n){ - void * const rv = sqlite3_realloc(p, (int)n); -#ifdef SQLITE_JNI_FATAL_OOM - if( n && !rv ) s3jni_oom(env); -#endif - return rv; -} - - /* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. @@ -790,8 +813,6 @@ static JNIEnv * s3jni_env(void){ } return env; } -/* Declares local var env = s3jni_env(). */ -#define S3JniDeclLocal_env JNIEnv * const env = s3jni_env() /* ** Fetches the S3JniGlobal.envCache row for the given env, allocing a @@ -948,7 +969,7 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, } nBa = (*env)->GetArrayLength(env, jba); if( nLen ) *nLen = (int)nBa; - rv = s3jni_malloc( env, nBa + 1 ); + rv = s3jni_malloc( nBa + 1 ); if( rv ){ (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv); rv[nBa] = 0; @@ -1286,7 +1307,7 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ } s3jni_incr( &SJG.metrics.nPdbRecycled ); }else{ - rv = s3jni_malloc(env, sizeof(S3JniDb)); + rv = s3jni_malloc( sizeof(S3JniDb)); if( rv ){ memset(rv, 0, sizeof(S3JniDb)); s3jni_incr( &SJG.metrics.nPdbAlloc ); @@ -1545,7 +1566,7 @@ static void CollationState_xDestroy(void *pArg){ S3JniDeclLocal_env; S3JniMutex_S3JniDb_enter; - S3JniHook_unref( s3jni_env(), &ps->hooks.collation, 1 ); + S3JniHook_unref( env, &ps->hooks.collation, 1 ); S3JniMutex_S3JniDb_leave; } @@ -1627,7 +1648,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ } S3JniMutex_Global_leave; if( !s ){ - s = s3jni_malloc(env, sizeof(*s)); + s = s3jni_malloc( sizeof(*s)); s3jni_incr(&SJG.metrics.nUdfAlloc); } if( s ){ @@ -2007,8 +2028,8 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, if( i >= SJG.autoExt.nExt ){ go = 0; }else{ - ax.jObj = S3JniRefLocal(SJG.autoExt.pExt[i].jObj); - ax.midFunc = SJG.autoExt.pExt[i].midFunc; + ax.jObj = S3JniRefLocal(SJG.autoExt.aExt[i].jObj); + ax.midFunc = SJG.autoExt.aExt[i].midFunc; } S3JniMutex_Ext_leave; if( ax.jObj ){ @@ -2041,7 +2062,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ /* Look for match or first empty slot. */ - ax = &SJG.autoExt.pExt[i]; + ax = &SJG.autoExt.aExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniMutex_Ext_leave; return 0 /* this as a no-op. */; @@ -2052,16 +2073,16 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ unsigned n = 1 + SJG.autoExt.nAlloc; S3JniAutoExtension * const aNew = - s3jni_realloc( env, SJG.autoExt.pExt, n * sizeof(*ax) ); + s3jni_realloc( SJG.autoExt.aExt, n * sizeof(*ax) ); if( !aNew ){ rc = SQLITE_NOMEM; }else{ - SJG.autoExt.pExt = aNew; + SJG.autoExt.aExt = aNew; ++SJG.autoExt.nAlloc; } } if( 0==rc ){ - ax = &SJG.autoExt.pExt[SJG.autoExt.nExt]; + ax = &SJG.autoExt.aExt[SJG.autoExt.nExt]; rc = S3JniAutoExtension_init(env, ax, jAutoExt); assert( rc ? (0==ax->jObj && 0==ax->midFunc) : (0!=ax->jObj && 0!=ax->midFunc) ); @@ -2250,15 +2271,15 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( S3JniMutex_Ext_enter; /* This algo mirrors the one in the core. */ for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ - ax = &SJG.autoExt.pExt[i]; + ax = &SJG.autoExt.aExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ S3JniAutoExtension_clear(env, ax); /* Move final entry into this slot. */ --SJG.autoExt.nExt; - *ax = SJG.autoExt.pExt[SJG.autoExt.nExt]; - memset(&SJG.autoExt.pExt[SJG.autoExt.nExt], 0, + *ax = SJG.autoExt.aExt[SJG.autoExt.nExt]; + memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, sizeof(S3JniAutoExtension)); - assert(! SJG.autoExt.pExt[SJG.autoExt.nExt].jObj ); + assert(! SJG.autoExt.aExt[SJG.autoExt.nExt].jObj ); rc = JNI_TRUE; break; } @@ -2430,7 +2451,6 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)( return new_sqlite3_value_wrapper(env, sv); } - static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ S3JniDeclLocal_env; @@ -3432,7 +3452,7 @@ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ - S3JniAutoExtension_clear( env, &SJG.autoExt.pExt[i] ); + S3JniAutoExtension_clear( env, &SJG.autoExt.aExt[i] ); } SJG.autoExt.nExt = 0; S3JniMutex_Ext_leave; @@ -4140,7 +4160,7 @@ static void Fts5JniAux_xDestroy(void *p){ } static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ - Fts5JniAux * s = s3jni_malloc(env, sizeof(Fts5JniAux)); + Fts5JniAux * s = s3jni_malloc( sizeof(Fts5JniAux)); if( s ){ jclass klazz; @@ -4565,7 +4585,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ int rc; S3JniFts5AuxData * pAux; - pAux = s3jni_malloc(env, sizeof(*pAux)); + pAux = s3jni_malloc( sizeof(*pAux)); if( !pAux ){ if( jAux ){ /* Emulate how xSetAuxdata() behaves when it cannot alloc @@ -4721,7 +4741,7 @@ static void SQLTester_dup_func( S3JniDeclLocal_env; ++p->nDup; - if( n>0 && (pOut = s3jni_malloc( env, (n+16)&~7 ))!=0 ){ + if( n>0 && (pOut = s3jni_malloc( (n+16)&~7 ))!=0 ){ pOut[0] = 0x2bbf4b7c; z = (char*)&pOut[1]; memcpy(z, sqlite3_value_text(argv[0]), n); diff --git a/manifest b/manifest index 3e1f473740..04b1dafb47 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Apply\sthe\sJNI\sOOM\schecks\sto\smemory\sreturned\sby\sJDK\sAPIs,\sas\sdistinct\sfrom\sour\sAPIs. -D 2023-08-26T22:34:26.393 +C JNI\scode\sreorgs\sand\ssimplify\sthe\sfailing-alloc\sinterface\sa\sbit. +D 2023-08-27T07:26:33.055 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 c1893766d909a9748151a0d52a3c18eb6aa37d2aaba187eb5fe45df344f96d2a +F ext/jni/GNUmakefile 4e60cdca419ac6783719da98379480b6f04d5d1b5fa1408c46fcb0c32565c571 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 9440a767fd1019cffec368b3542af0c0a8c7c6da1a7c6f285dc19e07f670ced8 +F ext/jni/src/c/sqlite3-jni.c 98c483b32aaec515573d0cb1293010713954639f9279309a50cd4189eaf118c8 F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 daede0f801f59d6501a863c4688e4635b34171e98b56b8ab4432c779113f1997 -R 6bd1f56d9b8c83901a03e2a9ce4434ac +P 1ff78582bfd934e0c76464b5f23ed9bf09a3491b145e0ca34acb6e59c4f53995 +R 5568414903d2c4b26699319473e5e738 U stephan -Z 362c439d994aeafc4f637309d1fc43bd +Z 3cd78dd857d13aa12cd9cebc268e8f6f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 128decbd67..666e786af9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1ff78582bfd934e0c76464b5f23ed9bf09a3491b145e0ca34acb6e59c4f53995 \ No newline at end of file +deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32 \ No newline at end of file From 60aca33a8b8f8d8364ecaa6565128fc4c1f298fd Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 08:10:59 +0000 Subject: [PATCH 081/422] Factor out a superfluous JNI class. Doc and code style cleanups. FossilOrigin-Name: 0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b --- ext/jni/GNUmakefile | 5 +- ext/jni/src/c/sqlite3-jni.c | 113 +++++++++++++++++++----------------- manifest | 14 ++--- manifest.uuid | 2 +- 4 files changed, 70 insertions(+), 64 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 5f7038f051..f2252bc624 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -165,6 +165,7 @@ $(sqlite3.h): $(sqlite3.c): $(sqlite3.h) opt.threadsafe ?= 1 +opt.oom ?= 0 SQLITE_OPT = \ -DSQLITE_ENABLE_RTREE \ -DSQLITE_ENABLE_EXPLAIN_COMMENTS \ @@ -182,7 +183,7 @@ SQLITE_OPT = \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ - -DSQLITE_JNI_FATAL_OOM=0 \ + -DSQLITE_JNI_FATAL_OOM=$(opt.oom) \ -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG @@ -252,7 +253,7 @@ test-sqllog: $(test.deps) $(bin.java) $(test.main.flags) -sqllog test-mt: $(test.deps) @echo "Testing in multi-threaded mode:"; - $(bin.java) $(test.main.flags) -t 5 -r 20 -shuffle $(test.flags) + $(bin.java) $(test.main.flags) -t 11 -r 50 -shuffle $(test.flags) test: test-one test-mt tests: test test-sqllog diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index d219039d30..5adc2f626e 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -480,13 +480,12 @@ struct S3JniEnv { }; /* -** State for proxying sqlite3_auto_extension() in Java. +** State for proxying sqlite3_auto_extension() in Java. This was +** initially a separate class from S3JniHook and now the older name is +** retained for readability in the APIs which use this, as well as for +** its better code-searchability. */ -typedef struct S3JniAutoExtension S3JniAutoExtension; -struct S3JniAutoExtension { - jobject jObj /* Java object */; - jmethodID midFunc /* xEntryPoint() callback */; -}; +typedef S3JniHook S3JniAutoExtension; /* ** Type IDs for SQL function categories. @@ -1366,15 +1365,10 @@ static S3JniDb * S3JniDb_get(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ #define S3JniDb_from_c(sqlite3Ptr) S3JniDb_get(env,0,(sqlite3Ptr)) /* -** Unref any Java-side state in ax and zero out ax. +** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out +** AX. */ -static void S3JniAutoExtension_clear(JNIEnv * const env, - S3JniAutoExtension * const ax){ - if( ax->jObj ){ - S3JniUnrefGlobal(ax->jObj); - memset(ax, 0, sizeof(*ax)); - } -} +#define S3JniAutoExtension_clear(AX) S3JniHook_unref(env, AX, 0); /* ** Initializes a pre-allocated S3JniAutoExtension object. Returns @@ -1388,12 +1382,12 @@ static int S3JniAutoExtension_init(JNIEnv *const env, jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); S3JniMutex_Ext_assertLocker; - ax->midFunc = (*env)->GetMethodID(env, klazz, "call", - "(Lorg/sqlite/jni/sqlite3;)I"); + ax->midCallback = (*env)->GetMethodID(env, klazz, "call", + "(Lorg/sqlite/jni/sqlite3;)I"); S3JniUnrefLocal(klazz); S3JniExceptionWarnIgnore; - if( !ax->midFunc ){ - S3JniAutoExtension_clear(env, ax); + if( !ax->midCallback ){ + S3JniAutoExtension_clear(ax); return SQLITE_ERROR; } ax->jObj = S3JniRefGlobal(jAutoExt); @@ -1629,12 +1623,23 @@ static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_valu return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_value, sv); } +/* Helper typedefs for UDF callback types. */ typedef void (*udf_xFunc_f)(sqlite3_context*,int,sqlite3_value**); typedef void (*udf_xStep_f)(sqlite3_context*,int,sqlite3_value**); typedef void (*udf_xFinal_f)(sqlite3_context*); /*typedef void (*udf_xValue_f)(sqlite3_context*);*/ /*typedef void (*udf_xInverse_f)(sqlite3_context*,int,sqlite3_value**);*/ +/* +** Allocate a new S3JniUdf (User-defined Function) and associate it +** with the SQLFunction-type jObj. Returns NULL on OOM. If the +** returned object's type==UDF_UNKNOWN_TYPE then the type of UDF was +** not unambiguously detected based on which callback members it has, +** which falls into the category of user error. +** +** The caller must arrange for the returned object to eventually be +** passed to S3JniUdf_free(). +*/ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ S3JniUdf * s = 0; @@ -1660,15 +1665,18 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ memset(s, 0, sizeof(*s)); s->jObj = S3JniRefGlobal(jObj); -#define FGET(FuncName,FuncType,Field) \ - s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncType); \ + +#define FGET(FuncName,FuncSig,Field) \ + s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncSig); \ if( !s->Field ) (*env)->ExceptionClear(env) + FGET("xFunc", zFSI, jmidxFunc); FGET("xStep", zFSI, jmidxStep); FGET("xFinal", zFV, jmidxFinal); FGET("xValue", zFV, jmidxValue); FGET("xInverse", zFSI, jmidxInverse); #undef FGET + S3JniUnrefLocal(klazz); if( s->jmidxFunc ) s->type = UDF_SCALAR; else if( s->jmidxStep && s->jmidxFinal ){ @@ -1683,6 +1691,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ static void S3JniUdf_free(S3JniUdf * s){ S3JniDeclLocal_env; + s3jni_call_xDestroy(env, s->jObj); S3JniUnrefGlobal(s->jObj); sqlite3_free(s->zFuncName); @@ -1698,13 +1707,13 @@ static void S3JniUdf_finalizer(void * s){ S3JniUdf_free((S3JniUdf*)s); } -/** - Helper for processing args to UDF handlers - with signature (sqlite3_context*,int,sqlite3_value**). +/* +** Helper for processing args to UDF handlers with signature +** (sqlite3_context*,int,sqlite3_value**). */ typedef struct { - jobject jcx; - jobjectArray jargv; + jobject jcx /* sqlite3_context object */; + jobjectArray jargv /* sqlite3_value[] */; } udf_jargs; /** @@ -1778,10 +1787,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, rc = SQLITE_NOMEM; } }else{ - MARKER(("Client-defined SQL function %s.%s() threw. " - "It should not do that.\n", - zFuncName ? zFuncName : "", zFuncType)); - (*env)->ExceptionDescribe( env ); + S3JniExceptionWarnCallbackThrew("Client-defined SQL function"); S3JniExceptionClear; } S3JniUnrefLocal(ex); @@ -1793,10 +1799,8 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, ** UDF, calls it, and returns 0 on success. */ static int udf_xFSI(sqlite3_context* const pCx, int argc, - sqlite3_value** const argv, - S3JniUdf * const s, - jmethodID xMethodID, - const char * const zFuncType){ + sqlite3_value** const argv, S3JniUdf * const s, + jmethodID xMethodID, const char * const zFuncType){ S3JniDeclLocal_env; udf_jargs args = {0,0}; int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv); @@ -1824,16 +1828,17 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; int const isFinal = 'F'==zFuncType[1]/*xFinal*/; - if( !jcx ){ + if( jcx ){ + (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); + S3JniIfThrew{ + rc = udf_report_exception(env, isFinal, cx, s->zFuncName, + zFuncType); + } + S3JniUnrefLocal(jcx); + }else{ if( isFinal ) sqlite3_result_error_nomem(cx); - return SQLITE_NOMEM; + rc = SQLITE_NOMEM; } - (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); - S3JniIfThrew{ - rc = udf_report_exception(env, isFinal, cx, s->zFuncName, - zFuncType); - } - S3JniUnrefLocal(jcx); return rc; } @@ -1981,7 +1986,6 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type) #undef WRAP_MUTF8_VOID #undef WRAP_STR_STMT_INT - S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( JniArgsEnvClass, jobject jCx, jboolean initialize ){ @@ -1994,7 +1998,6 @@ S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( return (jlong)p; } - /* Central auto-extension handler. */ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, const struct sqlite3_api_routines *ignored){ @@ -2009,15 +2012,16 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, jc = S3JniEnv_get(env); ps = jc->pdbOpening; if( !ps ){ - MARKER(("Unexpected arrival of null S3JniDb in auto-extension runner.\n")); - *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in auto-extension runner."); + *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in " + "auto-extension runner."); return SQLITE_ERROR; } jc->pdbOpening = 0; assert( !ps->pDb && "it's still being opened" ); ps->pDb = pDb; assert( ps->jDb ); - NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb); + NativePointerHolder_set(env, &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} /* We need a copy of the auto-extension object, with our own @@ -2029,11 +2033,11 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, go = 0; }else{ ax.jObj = S3JniRefLocal(SJG.autoExt.aExt[i].jObj); - ax.midFunc = SJG.autoExt.aExt[i].midFunc; + ax.midCallback = SJG.autoExt.aExt[i].midCallback; } S3JniMutex_Ext_leave; if( ax.jObj ){ - rc = (*env)->CallIntMethod(env, ax.jObj, ax.midFunc, ps->jDb); + rc = (*env)->CallIntMethod(env, ax.jObj, ax.midCallback, ps->jDb); S3JniUnrefLocal(ax.jObj); S3JniIfThrew { jthrowable const ex = (*env)->ExceptionOccurred(env); @@ -2084,8 +2088,8 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( 0==rc ){ ax = &SJG.autoExt.aExt[SJG.autoExt.nExt]; rc = S3JniAutoExtension_init(env, ax, jAutoExt); - assert( rc ? (0==ax->jObj && 0==ax->midFunc) - : (0!=ax->jObj && 0!=ax->midFunc) ); + assert( rc ? (0==ax->jObj && 0==ax->midCallback) + : (0!=ax->jObj && 0!=ax->midCallback) ); } } if( 0==rc ){ @@ -2093,7 +2097,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( rc = sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); if( rc ){ assert( ax ); - S3JniAutoExtension_clear(env, ax); + S3JniAutoExtension_clear(ax); } } if( 0==rc ){ @@ -2273,7 +2277,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ ax = &SJG.autoExt.aExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ - S3JniAutoExtension_clear(env, ax); + S3JniAutoExtension_clear(ax); /* Move final entry into this slot. */ --SJG.autoExt.nExt; *ax = SJG.autoExt.aExt[SJG.autoExt.nExt]; @@ -3044,7 +3048,8 @@ 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(env, &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() */); } @@ -3452,7 +3457,7 @@ static void s3jni_reset_auto_extension(JNIEnv *env){ int i; S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ - S3JniAutoExtension_clear( env, &SJG.autoExt.aExt[i] ); + S3JniAutoExtension_clear( &SJG.autoExt.aExt[i] ); } SJG.autoExt.nExt = 0; S3JniMutex_Ext_leave; diff --git a/manifest b/manifest index 04b1dafb47..7aac56b14d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JNI\scode\sreorgs\sand\ssimplify\sthe\sfailing-alloc\sinterface\sa\sbit. -D 2023-08-27T07:26:33.055 +C Factor\sout\sa\ssuperfluous\sJNI\sclass.\sDoc\sand\scode\sstyle\scleanups. +D 2023-08-27T08:10:59.533 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 4e60cdca419ac6783719da98379480b6f04d5d1b5fa1408c46fcb0c32565c571 +F ext/jni/GNUmakefile 05a756bb7a579b7d6570cb590567e9f0d12270529a2e7e50523284e5a3684838 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 98c483b32aaec515573d0cb1293010713954639f9279309a50cd4189eaf118c8 +F ext/jni/src/c/sqlite3-jni.c 9a07c95d570c1a46fff1db5383d0d247ef946b8c010a8e392045e350bf22e28f F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -2103,8 +2103,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 1ff78582bfd934e0c76464b5f23ed9bf09a3491b145e0ca34acb6e59c4f53995 -R 5568414903d2c4b26699319473e5e738 +P deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32 +R 1286cf04d67bcef64c621ffe2a87fc44 U stephan -Z 3cd78dd857d13aa12cd9cebc268e8f6f +Z 113b1efbfce3141a69775192a9b6c79b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 666e786af9..b64486e811 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32 \ No newline at end of file +0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b \ No newline at end of file From 32a79760b534f097c28eb4b2b52acfa628d49884 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 09:12:50 +0000 Subject: [PATCH 082/422] Factor out an unnecessary struct member. JNI sqlite3_shutdown() now frees up the various object-recycling bins. Doc touchups. FossilOrigin-Name: bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d --- ext/jni/src/c/sqlite3-jni.c | 165 +++++++++++++-------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 7 +- manifest | 14 +- manifest.uuid | 2 +- 4 files changed, 119 insertions(+), 69 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 5adc2f626e..03b5ebe640 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -511,7 +511,7 @@ struct S3JniUdf { jmethodID jmidxFinal /* Java ID of xFinal method */; jmethodID jmidxValue /* Java ID of xValue method */; jmethodID jmidxInverse /* Java ID of xInverse method */; - S3JniUdf * pNext /* Next entry in free-list. */; + S3JniUdf * pNext /* Next entry in SJG.udf.aFree. */; }; #if !defined(SQLITE_JNI_OMIT_METRICS) && !defined(SQLITE_JNI_ENABLE_METRICS) @@ -584,25 +584,24 @@ struct S3JniGlobalType { object. Used only for sanity checking. */; } perDb; struct { - S3JniUdf * aFree /* Head of the free-item list. Guarded by global - mutex. */; + S3JniUdf * aFree /* Head of the free-item list. Guarded by global + mutex. */; } udf; /* ** Refs to global classes and methods. Obtained during static init ** and never released. */ struct { - jclass cObj /* global ref to java.lang.Object */; jclass cLong /* global ref to java.lang.Long */; jclass cString /* global ref to java.lang.String */; jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; jmethodID ctorLong1 /* the Long(long) constructor */; jmethodID ctorStringBA /* the String(byte[],Charset) constructor */; jmethodID stringGetBytes /* the String.getBytes(Charset) method */; - } g /* refs to global Java state */; - /** - The list of bound auto-extensions (Java-side: - org.sqlite.jni.auto_extension objects). + } g; + /* + ** The list of Java-side auto-extensions + ** (org.sqlite.jni.AutoExtensionCallback objects). */ struct { S3JniAutoExtension *aExt /* The auto-extension list. It is @@ -661,7 +660,8 @@ struct S3JniGlobalType { volatile unsigned nValue; volatile unsigned nInverse; } udf; - unsigned nMetrics /* Total number of mutex-locked metrics increments. */; + unsigned nMetrics /* Total number of mutex-locked + metrics increments. */; #if S3JNI_METRICS_MUTEX sqlite3_mutex * mutex; #endif @@ -1124,11 +1124,37 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, S3JniMutex_S3JniDb_leave; } +/* +** Clears all of s's state. Requires that that the caller has locked +** S3JniGlobal.perDb.mutex. Make sure to do anything needed with +** s->pNext and s->pPrev before calling this, as this clears them. +*/ +static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){ + S3JniMutex_S3JniDb_assertLocker; + sqlite3_free( s->zMainDbName ); +#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0) + UNHOOK(auth); + UNHOOK(busyHandler); + UNHOOK(collation); + UNHOOK(collationNeeded); + UNHOOK(commit); + UNHOOK(progress); + UNHOOK(rollback); + UNHOOK(trace); + UNHOOK(update); +#ifdef SQLITE_ENABLE_PREUPDATE_HOOK + UNHOOK(preUpdate); +#endif +#undef UNHOOK + S3JniUnrefGlobal(s->jDb); + memset(s, 0, sizeof(S3JniDb)); +} + /* ** Clears s's state and moves it to the free-list. Requires that that the ** caller has locked S3JniGlobal.perDb.mutex. */ -static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ +static void S3JniDb_set_aside(JNIEnv * const env, S3JniDb * const s){ if( s ){ S3JniMutex_S3JniDb_enter; assert(s->pPrev != s); @@ -1140,23 +1166,7 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ assert(!s->pPrev); SJG.perDb.aHead = s->pNext; } - sqlite3_free( s->zMainDbName ); -#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0) - UNHOOK(auth); - UNHOOK(busyHandler); - UNHOOK(collation); - UNHOOK(collationNeeded); - UNHOOK(commit); - UNHOOK(progress); - UNHOOK(rollback); - UNHOOK(trace); - UNHOOK(update); -#ifdef SQLITE_ENABLE_PREUPDATE_HOOK - UNHOOK(preUpdate); -#endif -#undef UNHOOK - S3JniUnrefGlobal(s->jDb); - memset(s, 0, sizeof(S3JniDb)); + S3JniDb_clear(env, s); s->pNext = SJG.perDb.aFree; if(s->pNext) s->pNext->pPrev = s; SJG.perDb.aFree = s; @@ -1169,6 +1179,8 @@ static void S3JniDb_set_aside(JNIEnv * env, S3JniDb * const s){ ** references the cache owns. Returns true if env was cached and false ** if it was not found in the cache. Ownership of the given object is ** passed over to this function, which makes it free for re-use. +** +** Requires that the Env mutex be locked. */ static int S3JniEnv_uncache(JNIEnv * const env){ struct S3JniEnv * row; @@ -1688,23 +1700,33 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ return s; } - -static void S3JniUdf_free(S3JniUdf * s){ - S3JniDeclLocal_env; - +/* +** Frees up all resources owned by s, clears its state, then either +** caches it for reuse (if cacheIt is true) or frees it. The former +** requires locking the global mutex, so it must not be held when this +** is called. +*/ +static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s, + int cacheIt){ + assert( !s->pNext ); s3jni_call_xDestroy(env, s->jObj); S3JniUnrefGlobal(s->jObj); sqlite3_free(s->zFuncName); assert( !s->pNext ); memset(s, 0, sizeof(*s)); - S3JniMutex_Global_enter; - s->pNext = S3JniGlobal.udf.aFree; - S3JniGlobal.udf.aFree = s; - S3JniMutex_Global_leave; + if( cacheIt ){ + S3JniMutex_Global_enter; + s->pNext = S3JniGlobal.udf.aFree; + S3JniGlobal.udf.aFree = s; + S3JniMutex_Global_leave; + }else{ + sqlite3_free( s ); + } } +/* Finalizer for sqlite3_create_function() and friends. */ static void S3JniUdf_finalizer(void * s){ - S3JniUdf_free((S3JniUdf*)s); + S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1); } /* @@ -1712,14 +1734,14 @@ static void S3JniUdf_finalizer(void * s){ ** (sqlite3_context*,int,sqlite3_value**). */ typedef struct { - jobject jcx /* sqlite3_context object */; + jobject jcx /* sqlite3_context */; jobjectArray jargv /* sqlite3_value[] */; } udf_jargs; -/** - Converts the given (cx, argc, argv) into arguments for the given - UDF, placing the result in the final argument. Returns 0 on - success, SQLITE_NOMEM on allocation error. +/* +** Converts the given (cx, argc, argv) into arguments for the given +** UDF, placing the result in the final argument. Returns 0 on +** success, SQLITE_NOMEM on allocation error. */ static int udf_args(JNIEnv *env, sqlite3_context * const cx, @@ -1728,19 +1750,19 @@ static int udf_args(JNIEnv *env, jobjectArray ja = 0; jobject jcx = new_sqlite3_context_wrapper(env, cx); jint i; + S3JniNphClass * const pNC = + S3JniGlobal_nph(env, &S3NphRefs.sqlite3_value); *jCx = 0; *jArgv = 0; if( !jcx ) goto error_oom; - ja = (*env)->NewObjectArray(env, argc, - SJG.g.cObj, - NULL); + ja = (*env)->NewObjectArray(env, argc, pNC->klazz, NULL); s3jni_oom_check( ja ); if( !ja ) goto error_oom; for(i = 0; i < argc; ++i){ jobject jsv = new_sqlite3_value_wrapper(env, argv[i]); if( !jsv ) goto error_oom; (*env)->SetObjectArrayElement(env, ja, i, jsv); - S3JniUnrefLocal(jsv)/*array has a ref*/; + S3JniUnrefLocal(jsv)/*ja has a ref*/; } *jCx = jcx; *jArgv = ja; @@ -2018,8 +2040,8 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, } jc->pdbOpening = 0; assert( !ps->pDb && "it's still being opened" ); - ps->pDb = pDb; assert( ps->jDb ); + ps->pDb = pDb; NativePointerHolder_set(env, &S3NphRefs.sqlite3, ps->jDb, pDb) /* As of here, the Java/C connection is complete */; for( i = 0; go && 0==rc; ++i ){ @@ -2057,7 +2079,6 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( JniArgsEnvClass, jobject jAutoExt ){ - static int once = 0; int i; S3JniAutoExtension * ax; int rc = 0; @@ -2065,16 +2086,18 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( !jAutoExt ) return SQLITE_MISUSE; S3JniMutex_Ext_enter; for( i = 0; i < SJG.autoExt.nExt; ++i ){ - /* Look for match or first empty slot. */ + /* Look for a match. */ ax = &SJG.autoExt.aExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ + /* same object, so this is a no-op. */ S3JniMutex_Ext_leave; - return 0 /* this as a no-op. */; + return 0; } } if( i == SJG.autoExt.nExt ){ assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc ); if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ + /* Allocate another slot. */ unsigned n = 1 + SJG.autoExt.nAlloc; S3JniAutoExtension * const aNew = s3jni_realloc( SJG.autoExt.aExt, n * sizeof(*ax) ); @@ -2093,8 +2116,14 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( } } if( 0==rc ){ + static int once = 0; if( 0==once && ++once ){ - rc = sqlite3_auto_extension( (void(*)(void))s3jni_run_java_auto_extensions ); + rc = sqlite3_auto_extension( + (void(*)(void))s3jni_run_java_auto_extensions + /* Reminder: the JNI binding of sqlite3_reset_auto_extension() + ** does not call the core-lib impl. It only clears Java-side + ** auto-extensions. */ + ); if( rc ){ assert( ax ); S3JniAutoExtension_clear(ax); @@ -2720,13 +2749,13 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_w if( UDF_UNKNOWN_TYPE==s->type ){ rc = s3jni_db_error(pDb, SQLITE_MISUSE, "Cannot unambiguously determine function type."); - S3JniUdf_free(s); + S3JniUdf_free(env, s, 1); goto error_cleanup; } zFuncName = s3jni_jstring_to_utf8(env,jFuncName,0); if( !zFuncName ){ rc = SQLITE_NOMEM; - S3JniUdf_free(s); + S3JniUdf_free(env, s, 1); goto error_cleanup; } if( UDF_WINDOW == s->type ){ @@ -3815,16 +3844,38 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( JniArgsEnvClass ){ s3jni_reset_auto_extension(env); + /* Free up env cache. */ S3JniMutex_Env_enter; while( SJG.envCache.aHead ){ S3JniEnv_uncache( SJG.envCache.aHead->env ); } S3JniMutex_Env_leave; + /* Free up S3JniUdf recycling bin. */ + S3JniMutex_Global_enter; + while( S3JniGlobal.udf.aFree ){ + S3JniUdf * const u = S3JniGlobal.udf.aFree; + S3JniGlobal.udf.aFree = u->pNext; + u->pNext = 0; + S3JniUdf_free(env, u, 0); + } + S3JniMutex_Global_leave; + /* Free up S3JniDb recycling bin. */ + S3JniMutex_S3JniDb_enter; + while( S3JniGlobal.perDb.aFree ){ + S3JniDb * const d = S3JniGlobal.perDb.aFree; + S3JniGlobal.perDb.aFree = d->pNext; + d->pNext = 0; + S3JniDb_clear(env, d); + sqlite3_free(d); + } + S3JniMutex_S3JniDb_leave; + #if 0 /* - ** Is this a good idea? We will get rid of the perDb list once - ** sqlite3 gets a per-db client state, at which point we won't have - ** a central list of databases to close. + ** Is automatically closing any still-open dbs a good idea? We will + ** get rid of the perDb list once sqlite3 gets a per-db client + ** state, at which point we won't have a central list of databases + ** to close. */ S3JniMutex_S3JniDb_enter; while( SJG.perDb.pHead ){ @@ -3832,6 +3883,7 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( } S3JniMutex_S3JniDb_leave; #endif + /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */ return sqlite3_shutdown(); } @@ -3857,7 +3909,6 @@ S3JniApi(sqlite3_sql(),jstring,1sql)( const char * zSql = 0; zSql = sqlite3_sql(pStmt); rv = s3jni_utf8_to_jstring(env, zSql, -1); - s3jni_oom_check(rv); } return rv; } @@ -4972,8 +5023,6 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ } /* Grab references to various global classes and objects... */ - SJG.g.cObj = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Object")); - S3JniExceptionIsFatal("Error getting reference to Object class."); SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long")); S3JniExceptionIsFatal("Error getting reference to Long class."); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 1dd5979c6c..008300b0d0 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1149,9 +1149,10 @@ public final class SQLite3Jni { /** - Cleans up all stale per-thread state managed by the library, as - well as any registered auto-extensions, then calls the C-native - sqlite3_shutdown(). Calling this while database handles or + In addition to calling the C-level sqlite3_shutdown(), the JNI + binding also cleans up all stale per-thread state managed by the + library, as well as any registered auto-extensions and free up + various bits of memory. Calling this while database handles or prepared statements are still active will leak resources. Trying to use those objects after this routine is called invoked undefined behavior. diff --git a/manifest b/manifest index 7aac56b14d..406ba837b1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\sout\sa\ssuperfluous\sJNI\sclass.\sDoc\sand\scode\sstyle\scleanups. -D 2023-08-27T08:10:59.533 +C Factor\sout\san\sunnecessary\sstruct\smember.\sJNI\ssqlite3_shutdown()\snow\sfrees\sup\sthe\svarious\sobject-recycling\sbins.\sDoc\stouchups. +D 2023-08-27T09:12:50.224 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 05a756bb7a579b7d6570cb590567e9f0d12270529a2e7e50523284e5a3684838 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 9a07c95d570c1a46fff1db5383d0d247ef946b8c010a8e392045e350bf22e28f +F ext/jni/src/c/sqlite3-jni.c b0b86b214477ee1604caf358fd1f232ee6649d327527063dddc22216026c20d2 F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java d640493f52c04e03f9c0525f16dcec6b718638ace22101c0ccfc014883a15b0b +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1ae75149383cd8b9fde175aa87855d18e425e32af2bc5e81bf56a95e92155195 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 deed5797de65a25896e991a441f0d05eb92662536296485920fb081e84ad5d32 -R 1286cf04d67bcef64c621ffe2a87fc44 +P 0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b +R e62c13b2db81e23df4398b126d6aa9e9 U stephan -Z 113b1efbfce3141a69775192a9b6c79b +Z aca6efed8b8717dbec6b53005671b2cd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b64486e811..439a3e96c4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b \ No newline at end of file +bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d \ No newline at end of file From 95f5d85d4a5a185f9b60d2ea4e226a9454110a4c Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 10:40:00 +0000 Subject: [PATCH 083/422] Make JNI sqlite3_trace_v2() thread-safe. Re-add a piece removed in [bae4d022aad9b] to work around a JVM crash which is unpredictably triggered by its substitute. Fix the THREADMODE=0 JNI build. Further internal API simplifications. FossilOrigin-Name: 3f9f7a9cb08b0687ad206605a5109306762df9ae8bdeab2d8d60bf9373c9ad32 --- ext/jni/GNUmakefile | 9 +- ext/jni/src/c/sqlite3-jni.c | 165 ++++++++++++--------- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 13 +- manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 115 insertions(+), 90 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index f2252bc624..32bd7b4b50 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -297,9 +297,12 @@ tests: tester ######################################################################## # Build each SQLITE_THREADMODE variant and run all tests against them. multitest: clean - $(MAKE) opt.threadsafe=0 tests clean - $(MAKE) opt.threadsafe=1 tests clean - $(MAKE) opt.threadsafe=2 tests clean + $(MAKE) opt.threadsafe=0 opt.oom=1 tests clean + $(MAKE) opt.threadsafe=0 opt.oom=0 tests clean + $(MAKE) opt.threadsafe=1 opt.oom=1 tests clean + $(MAKE) opt.threadsafe=1 opt.oom=0 tests clean + $(MAKE) opt.threadsafe=2 opt.oom=1 tests clean + $(MAKE) opt.threadsafe=2 opt.oom=0 tests clean ######################################################################## diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 03b5ebe640..c7d6a48e5b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -404,9 +404,8 @@ struct S3JniHook{ /* We lookup the jObj.xDestroy() method as-needed for contexts which ** have custom finalizers. */ }; -#if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) || defined(SQLITE_ENABLE_SQLLOG) +/* For clean bitwise-copy init of local instances. */ static const S3JniHook S3JniHook_empty = {0,0}; -#endif /* ** Per-(sqlite3*) state for various JNI bindings. This state is @@ -456,10 +455,10 @@ struct S3JniEnv { JNIEnv *env /* env in which this cache entry was created */; /* ** pdbOpening is used to coordinate the Java/DB connection of a - ** being-open()'d db in the face of auto-extensions. "The problem" - ** is that auto-extensions run before we can bind the C db to its - ** Java representation, but auto-extensions require that binding. We - ** handle this as follows: + ** being-open()'d db in the face of auto-extensions. + ** Auto-extensions run before we can bind the C db to its Java + ** representation, but auto-extensions require that binding to pass + ** on to their Java-side callbacks. We handle this as follows: ** ** - In the JNI side of sqlite3_open(), allocate the Java side of ** that connection and set pdbOpening to point to that @@ -497,8 +496,8 @@ enum UDFType { UDF_WINDOW }; -/** - State for binding Java-side UDFs. +/* +** State for binding Java-side UDFs. */ typedef struct S3JniUdf S3JniUdf; struct S3JniUdf { @@ -506,11 +505,11 @@ struct S3JniUdf { char * zFuncName /* Only for error reporting and debug logging */; enum UDFType type; /** Method IDs for the various UDF methods. */ - jmethodID jmidxFunc /* Java ID of xFunc method */; - jmethodID jmidxStep /* Java ID of xStep method */; - jmethodID jmidxFinal /* Java ID of xFinal method */; - jmethodID jmidxValue /* Java ID of xValue method */; - jmethodID jmidxInverse /* Java ID of xInverse method */; + jmethodID jmidxFunc /* xFunc method */; + jmethodID jmidxStep /* xStep method */; + jmethodID jmidxFinal /* xFinal method */; + jmethodID jmidxValue /* xValue method */; + jmethodID jmidxInverse /* xInverse method */; S3JniUdf * pNext /* Next entry in SJG.udf.aFree. */; }; @@ -592,6 +591,7 @@ struct S3JniGlobalType { ** and never released. */ struct { + jclass cObj /* global ref to java.lang.Object */; jclass cLong /* global ref to java.lang.Long */; jclass cString /* global ref to java.lang.String */; jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; @@ -729,10 +729,10 @@ static void s3jni_incr( volatile unsigned int * const p ){ SJG.autoExt.locker = env; \ s3jni_incr( &SJG.metrics.nMutexAutoExt ) #define S3JniMutex_Ext_leave \ - assert( env == SJG.autoExt.locker ); \ + assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ); \ sqlite3_mutex_leave( SJG.autoExt.mutex ) #define S3JniMutex_Ext_assertLocker \ - assert( env == SJG.autoExt.locker ) + assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ) #define S3JniMutex_Global_enter \ sqlite3_mutex_enter( SJG.mutex ); \ @@ -775,6 +775,7 @@ static void s3jni_incr( volatile unsigned int * const p ){ #define S3JniMutex_Global_leave #define S3JniMutex_Nph_enter #define S3JniMutex_Nph_leave +#define S3JniMutex_S3JniDb_assertLocker #define S3JniMutex_S3JniDb_enter #define S3JniMutex_S3JniDb_leave #endif @@ -1090,8 +1091,8 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ ** cleared. It is legal to call this when the object has no Java ** references. */ -static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, - int doXDestroy){ +static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s, + int doXDestroy){ if( s->jObj ){ if( doXDestroy ){ s3jni_call_xDestroy(env, s->jObj); @@ -1100,17 +1101,20 @@ static void S3JniHook_unref(JNIEnv * const env, S3JniHook * const s, } memset(s, 0, sizeof(*s)); } +#define S3JniHook_unref(H,X) S3JniHook_unref_impl(env, (H), (X)) /* ** Internal helper for many hook callback impls. Locks the S3JniDb ** mutex, makes a copy of src into dest, with one change: if src->jObj ** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj ** instead of a copy of the prior GLOBAL ref. Then it unlocks the -** mutex. If dest->jObj is not NULL when this returns then the caller -** is obligated to eventually free the new ref by passing dest->jObj -** to S3JniUnrefLocal(). The dest pointer must NOT be passed to -** S3JniHook_unref(), as that one assumes that dest->jObj is a GLOBAL -** ref (it's illegal to try to unref the wrong ref type).. +** mutex. +** +** If dest->jObj is not NULL when this returns then the caller is +** obligated to eventually free the new ref by passing *dest to +** S3JniHook_localundup(). The dest pointer must NOT be passed to +** S3JniHook_unref(), as that routine assumes that dest->jObj is a +** GLOBAL ref (it's illegal to try to unref the wrong ref type).. ** ** Background: when running a hook we need a call-local copy lest ** another thread modify the hook while we're running it. That copy @@ -1123,6 +1127,7 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj); S3JniMutex_S3JniDb_leave; } +#define S3JniHook_localundup(HOOK) S3JniUnrefLocal(HOOK.jObj) /* ** Clears all of s's state. Requires that that the caller has locked @@ -1132,7 +1137,7 @@ static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){ S3JniMutex_S3JniDb_assertLocker; sqlite3_free( s->zMainDbName ); -#define UNHOOK(MEMBER) S3JniHook_unref(env, &s->hooks.MEMBER, 0) +#define UNHOOK(MEMBER) S3JniHook_unref(&s->hooks.MEMBER, 0) UNHOOK(auth); UNHOOK(busyHandler); UNHOOK(collation); @@ -1380,7 +1385,7 @@ static S3JniDb * S3JniDb_get(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out ** AX. */ -#define S3JniAutoExtension_clear(AX) S3JniHook_unref(env, AX, 0); +#define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX, 0); /* ** Initializes a pre-allocated S3JniAutoExtension object. Returns @@ -1561,7 +1566,7 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, S3JniExceptionIgnore; S3JniUnrefLocal(jbaLhs); S3JniUnrefLocal(jbaRhs); - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } return (int)rc; } @@ -1572,7 +1577,7 @@ static void CollationState_xDestroy(void *pArg){ S3JniDeclLocal_env; S3JniMutex_S3JniDb_enter; - S3JniHook_unref( env, &ps->hooks.collation, 1 ); + S3JniHook_unref(&ps->hooks.collation, 1); S3JniMutex_S3JniDb_leave; } @@ -1750,12 +1755,14 @@ static int udf_args(JNIEnv *env, jobjectArray ja = 0; jobject jcx = new_sqlite3_context_wrapper(env, cx); jint i; - S3JniNphClass * const pNC = - S3JniGlobal_nph(env, &S3NphRefs.sqlite3_value); *jCx = 0; *jArgv = 0; if( !jcx ) goto error_oom; - ja = (*env)->NewObjectArray(env, argc, pNC->klazz, NULL); + ja = (*env)->NewObjectArray( + env, argc, SJG.g.cObj + /* S3JniGlobal_nph(env,&S3NphRefs.sqlite3_value)->klazz would be + more correct, but it unpredictably triggers an assert in the + JVM. */, NULL); s3jni_oom_check( ja ); if( !ja ) goto error_oom; for(i = 0; i < argc; ++i){ @@ -2240,7 +2247,7 @@ static int s3jni_busy_handler(void* pState, int n){ rc = s3jni_db_exception(env, ps, SQLITE_ERROR, "sqlite3_busy_handler() callback threw."); } - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } return rc; } @@ -2260,17 +2267,17 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( return 0; } jclass klazz; - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); pHook->jObj = S3JniRefGlobal(jBusy); klazz = (*env)->GetObjectClass(env, jBusy); pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); S3JniUnrefLocal(klazz); S3JniIfThrew { - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); rc = SQLITE_ERROR; } }else{ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); } if( 0==rc ){ rc = jBusy @@ -2288,7 +2295,7 @@ S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( int rc = SQLITE_MISUSE; if( ps ){ S3JniMutex_S3JniDb_enter; - S3JniHook_unref(env, &ps->hooks.busyHandler, 0); + S3JniHook_unref(&ps->hooks.busyHandler, 0); rc = sqlite3_busy_timeout(ps->pDb, (int)ms); S3JniMutex_S3JniDb_leave; } @@ -2387,7 +2394,7 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, } S3JniUnrefLocal(jName); } - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } } @@ -2404,7 +2411,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( (*env)->IsSameObject(env, pHook->jObj, jHook) ){ /* no-op */ }else if( !jHook ){ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); sqlite3_collation_needed(ps->pDb, 0, 0); }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); @@ -2420,7 +2427,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); if( rc ){ }else{ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); pHook->midCallback = xCallback; pHook->jObj = S3JniRefGlobal(jHook); } @@ -2501,7 +2508,7 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniExceptionClear; rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw."); } - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } return rc; } @@ -2634,7 +2641,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback"); S3JniExceptionClear; } - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); S3JniUnrefLocal(jArg0); S3JniUnrefLocal(jArg1); } @@ -2656,7 +2663,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, S3JniMutex_Global_enter; if( !jLog ){ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ /* No-op */ }else { @@ -2669,7 +2676,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, if( midCallback ){ rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); if( 0==rc ){ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); pHook->midCallback = midCallback; pHook->jObj = S3JniRefGlobal(jLog); } @@ -2718,7 +2725,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), CollationState_xDestroy); sqlite3_free(zName); if( 0==rc ){ - S3JniHook_unref( env, &ps->hooks.collation, 1 ); + S3JniHook_unref( &ps->hooks.collation, 1 ); ps->hooks.collation.midCallback = midCallback; ps->hooks.collation.jObj = S3JniRefGlobal(oCollation); } @@ -3080,7 +3087,8 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, NativePointerHolder_set(env, &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() */); + assert( ps->pDb==*ppDb + && "Set up via s3jni_run_java_auto_extensions()" ); } }else{ S3JniDb_set_aside(env, ps); @@ -3100,8 +3108,8 @@ S3JniApi(sqlite3_open(),jint,1open)( int rc; rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ - rc = sqlite3_open(zName, &pOut); - rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); + rc = s3jni_open_post(env, jc, ps, &pOut, jOut, + sqlite3_open(zName, &pOut)); assert(rc==0 ? pOut!=0 : 1); sqlite3_free(zName); } @@ -3270,7 +3278,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, } S3JniUnrefLocal(jDbName); S3JniUnrefLocal(jTable); - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -3436,7 +3444,7 @@ static int s3jni_progress_handler_impl(void *pP){ rc = s3jni_db_exception(env, ps, rc, "sqlite3_progress_handler() callback threw"); } - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } return rc; } @@ -3452,7 +3460,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( if( !ps ) return; S3JniMutex_S3JniDb_enter; if( n<1 || !jProgress ){ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); sqlite3_progress_handler(ps->pDb, 0, 0, 0); S3JniMutex_S3JniDb_leave; return; @@ -3738,7 +3746,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniUnrefLocal(s1); S3JniUnrefLocal(s2); S3JniUnrefLocal(s3); - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); } return rc; } @@ -3753,7 +3761,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( if( !ps ) return SQLITE_MISUSE; S3JniMutex_S3JniDb_enter; if( !jHook ){ - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); rc = sqlite3_set_authorizer( ps->pDb, 0, 0 ); }else{ jclass klazz; @@ -3763,7 +3771,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( S3JniMutex_S3JniDb_leave; return 0; } - S3JniHook_unref(env, pHook, 0); + S3JniHook_unref(pHook, 0); } pHook->jObj = S3JniRefGlobal(jHook); klazz = (*env)->GetObjectClass(env, jHook); @@ -3782,7 +3790,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( }else{ rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); } - if( rc ) S3JniHook_unref(env, pHook, 0); + if( rc ) S3JniHook_unref(pHook, 0); } S3JniMutex_S3JniDb_leave; return rc; @@ -3978,7 +3986,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ } S3JniUnrefLocal(jPUnref); S3JniUnrefLocal(jX); - S3JniUnrefLocal(hook.jObj); + S3JniHook_localundup(hook); return rc; } @@ -3986,27 +3994,40 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer ){ S3JniDb * const ps = S3JniDb_from_java(jDb); - S3JniHook * const pHook = ps ? &ps->hooks.trace : 0; - jclass klazz; + int rc; if( !ps ) return SQLITE_MISUSE; - else if( !traceMask || !jTracer ){ - S3JniHook_unref(env, pHook, 0); - return (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); + if( !traceMask || !jTracer ){ + S3JniMutex_S3JniDb_enter; + rc = (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); + S3JniHook_unref(&ps->hooks.trace, 0); + S3JniMutex_S3JniDb_leave; + }else{ + jclass const klazz = (*env)->GetObjectClass(env, jTracer); + S3JniHook hook = S3JniHook_empty; + hook.midCallback = (*env)->GetMethodID( + env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I" + ); + S3JniUnrefLocal(klazz); + S3JniIfThrew { + S3JniExceptionClear; + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Cannot not find matching call() on " + "TracerCallback object."); + }else{ + S3JniMutex_S3JniDb_enter; + hook.jObj = S3JniRefGlobal(jTracer); + rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); + if( 0==rc ){ + S3JniHook_unref(&ps->hooks.trace, 0); + ps->hooks.trace = hook; + }else{ + S3JniHook_unref(&hook, 0); + } + S3JniMutex_S3JniDb_leave; + } } - klazz = (*env)->GetObjectClass(env, jTracer); - pHook->midCallback = (*env)->GetMethodID( - env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I" - ); - S3JniUnrefLocal(klazz); - S3JniIfThrew { - S3JniExceptionClear; - S3JniHook_unref(env, pHook, 0); - return s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Cannot not find matching xCallback() on Tracer object."); - } - pHook->jObj = S3JniRefGlobal(jTracer); - return sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); + return rc; } S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)( @@ -5023,6 +5044,8 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ } /* Grab references to various global classes and objects... */ + SJG.g.cObj = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Object")); + S3JniExceptionIsFatal("Error getting reference to Object class."); SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long")); S3JniExceptionIsFatal("Error getting reference to Long class."); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 008300b0d0..f8bc36d8e7 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1151,7 +1151,7 @@ public final class SQLite3Jni { /** In addition to calling the C-level sqlite3_shutdown(), the JNI binding also cleans up all stale per-thread state managed by the - library, as well as any registered auto-extensions and free up + library, as well as any registered auto-extensions, and frees up various bits of memory. Calling this while database handles or prepared statements are still active will leak resources. Trying to use those objects after this routine is called invoked @@ -1179,8 +1179,8 @@ public final class SQLite3Jni { public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); /** - Internal impl of the public sqlite3_strglob() method. Neither argument - may be NULL and both MUST be NUL-terminated. + Internal impl of the public sqlite3_strglob() method. Neither + argument may be NULL and both MUST be NUL-terminated UTF-8. */ private static native int sqlite3_strglob( @NotNull byte[] glob, @NotNull byte[] txt @@ -1197,7 +1197,7 @@ public final class SQLite3Jni { /** Internal impl of the public sqlite3_strlike() method. Neither - argument may be NULL and both MUST be NUL-terminated. + argument may be NULL and both MUST be NUL-terminated UTF-8. */ private static native int sqlite3_strlike( @NotNull byte[] glob, @NotNull byte[] txt, int escChar @@ -1225,9 +1225,8 @@ public final class SQLite3Jni { arguments are encapsulated in the final argument to this function. Unlike the C API, which is documented as always returning 0, this - implementation returns SQLITE_NOMEM if allocation of per-db - mapping state fails and SQLITE_ERROR if the given callback object - cannot be processed propertly (i.e. an internal error). + implementation returns non-0 if initialization of the tracer + mapping state fails. */ public static native int sqlite3_trace_v2( @NotNull sqlite3 db, int traceMask, @Nullable TraceV2Callback tracer diff --git a/manifest b/manifest index 406ba837b1..c88153f399 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\sout\san\sunnecessary\sstruct\smember.\sJNI\ssqlite3_shutdown()\snow\sfrees\sup\sthe\svarious\sobject-recycling\sbins.\sDoc\stouchups. -D 2023-08-27T09:12:50.224 +C Make\sJNI\ssqlite3_trace_v2()\sthread-safe.\sRe-add\sa\spiece\sremoved\sin\s[bae4d022aad9b]\sto\swork\saround\sa\sJVM\scrash\swhich\sis\sunpredictably\striggered\sby\sits\ssubstitute.\sFix\sthe\sTHREADMODE=0\sJNI\sbuild.\sFurther\sinternal\sAPI\ssimplifications. +D 2023-08-27T10:40:00.984 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 05a756bb7a579b7d6570cb590567e9f0d12270529a2e7e50523284e5a3684838 +F ext/jni/GNUmakefile 527f7c72360ba081c9ad120a9a00834973dac0115c6272fad94963651ed15bab F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c b0b86b214477ee1604caf358fd1f232ee6649d327527063dddc22216026c20d2 +F ext/jni/src/c/sqlite3-jni.c 79360ee5b71ce2d90e712768456756e5d57c63bce4e8238c23417caabb790a92 F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 1ae75149383cd8b9fde175aa87855d18e425e32af2bc5e81bf56a95e92155195 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java e27b7b75f561a8a04b222b6306c59e65dcf7c1fc9408523da0d65c1ffb0e1590 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 0f37f27148dfa93ecc42381ad3455a9059285d1af2df027429044942dc4d861b -R e62c13b2db81e23df4398b126d6aa9e9 +P bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d +R 3800367f7606fbafc862156daf2d0dd5 U stephan -Z aca6efed8b8717dbec6b53005671b2cd +Z e92f2dfe5d2f02a04f8aac3861297f02 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 439a3e96c4..98d663acca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d \ No newline at end of file +3f9f7a9cb08b0687ad206605a5109306762df9ae8bdeab2d8d60bf9373c9ad32 \ No newline at end of file From 88bd53dfd0e38810ccac3a6bdd349bd71993c8f2 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 11:28:57 +0000 Subject: [PATCH 084/422] Correct the signature mismatch between JNI sqlite3_column/value_text16() and add related tests. FossilOrigin-Name: 77f6e70f17c0cb6d031f983c458c9ec2e88d92b4716397533a029af39da2d128 --- ext/jni/src/c/sqlite3-jni.c | 35 ++++++++++------------ ext/jni/src/c/sqlite3-jni.h | 8 ++--- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 6 ++-- ext/jni/src/org/sqlite/jni/Tester1.java | 8 +++-- manifest | 18 +++++------ manifest.uuid | 2 +- 6 files changed, 38 insertions(+), 39 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index c7d6a48e5b..39feafc8a7 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -169,9 +169,8 @@ Java_org_sqlite_jni_SQLite3Jni_sqlite3_ ## Suffix /* Prologue for JNI function declarations and definitions. */ -#define JniDecl(ReturnType,Suffix) \ - JNIEXPORT ReturnType JNICALL \ - JniFuncName(Suffix) +#define JniDecl(ReturnType,Suffix) \ + JNIEXPORT ReturnType JNICALL JniFuncName(Suffix) /* ** S3JniApi's intent is that CFunc be the C API func(s) the @@ -377,7 +376,9 @@ enum { /* ** Cache entry for NativePointerHolder subclasses and OutputPointer -** types. +** types. The pRef and klazz fields are set up the first time the +** entry is fetched using S3JniGlobal_nph(). The other fields are +** populated as needed by the routines which use them. */ typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { @@ -1745,8 +1746,9 @@ typedef struct { /* ** Converts the given (cx, argc, argv) into arguments for the given -** UDF, placing the result in the final argument. Returns 0 on -** success, SQLITE_NOMEM on allocation error. +** UDF, writing the result (Java wrappers for cx and argv) in the +** final 2 arguments. Returns 0 on success, SQLITE_NOMEM on allocation +** error. On error *jCx and *jArgv will be set to 0. */ static int udf_args(JNIEnv *env, sqlite3_context * const cx, @@ -1775,7 +1777,6 @@ static int udf_args(JNIEnv *env, *jArgv = ja; return 0; error_oom: - sqlite3_result_error_nomem(cx); S3JniUnrefLocal(jcx); S3JniUnrefLocal(ja); return SQLITE_NOMEM; @@ -1789,7 +1790,7 @@ error_oom: ** not do so. In either case, it clears the exception state. ** ** Returns SQLITE_NOMEM if an allocation fails, else SQLITE_ERROR. In -** the latter case it calls sqlite3_result_error_nomem(). +** the former case it calls sqlite3_result_error_nomem(). */ static int udf_report_exception(JNIEnv * const env, int translateToErr, sqlite3_context * cx, @@ -1816,7 +1817,7 @@ static int udf_report_exception(JNIEnv * const env, int translateToErr, rc = SQLITE_NOMEM; } }else{ - S3JniExceptionWarnCallbackThrew("Client-defined SQL function"); + S3JniExceptionWarnCallbackThrew("client-defined SQL function"); S3JniExceptionClear; } S3JniUnrefLocal(ex); @@ -1857,6 +1858,7 @@ static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, jobject jcx = new_sqlite3_context_wrapper(env, cx); int rc = 0; int const isFinal = 'F'==zFuncType[1]/*xFinal*/; + if( jcx ){ (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); S3JniIfThrew{ @@ -4099,20 +4101,13 @@ S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)( return p ? s3jni_new_jbyteArray(env, p, n) : 0; } -S3JniApi(sqlite3_value_text16(),jbyteArray,1value_1text16)( +S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)( JniArgsEnvClass, jobject jpSVal ){ sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); - jbyteArray jba = 0; - if( sv ){ - int const nLen = sqlite3_value_bytes16(sv); - const jbyte * const pBytes = - nLen ? sqlite3_value_text16(sv) : 0; - - s3jni_oom_check( nLen ? !!pBytes : 1 ); - jba = s3jni_new_jbyteArray(env, pBytes, nLen); - } - return jba; + const int n = sqlite3_value_bytes16(sv); + const void * const p = sqlite3_value_text16(sv); + return s3jni_text16_to_jstring(env, p, n); } JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index ed64a45033..a4fc6619c2 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1701,10 +1701,10 @@ JNIEXPORT jdouble JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1double /* * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_value_dupe + * Method: sqlite3_value_dup * Signature: (Lorg/sqlite/jni/sqlite3_value;)Lorg/sqlite/jni/sqlite3_value; */ -JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1dupe +JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1dup (JNIEnv *, jclass, jobject); /* @@ -1758,9 +1758,9 @@ JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_value_text16 - * Signature: (Lorg/sqlite/jni/sqlite3_value;)[B + * Signature: (Lorg/sqlite/jni/sqlite3_value;)Ljava/lang/String; */ -JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text16 +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text16 (JNIEnv *, jclass, jobject); /* diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index f8bc36d8e7..b53e921b65 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -367,7 +367,7 @@ public final class SQLite3Jni { Object rv = null; sqlite3_value v = sqlite3_column_value(stmt, ndx); if(null!=v){ - v = sqlite3_value_dupe(v) /* we need a "protected" value */; + v = sqlite3_value_dup(v) /* we need a "protected" value */; if(null!=v){ rv = sqlite3_value_java_object(v); sqlite3_value_free(v); @@ -1244,7 +1244,7 @@ public final class SQLite3Jni { public static native double sqlite3_value_double(@NotNull sqlite3_value v); - public static native sqlite3_value sqlite3_value_dupe( + public static native sqlite3_value sqlite3_value_dup( @NotNull sqlite3_value v ); @@ -1290,7 +1290,7 @@ public final class SQLite3Jni { return null==ba ? null : new String(ba, StandardCharsets.UTF_8); } - public static native byte[] sqlite3_value_text16(@NotNull sqlite3_value v); + public static native String sqlite3_value_text16(@NotNull sqlite3_value v); public static native int sqlite3_value_type(@NotNull sqlite3_value v); diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 1a4ee178c6..2647575ee5 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -435,9 +435,13 @@ public class Tester1 implements Runnable { StringBuilder sbuf = new StringBuilder(); n = 0; while( SQLITE_ROW == sqlite3_step(stmt) ){ - String txt = sqlite3_column_text16(stmt, 0); - //outln("txt = "+txt); + final sqlite3_value sv = sqlite3_value_dup(sqlite3_column_value(stmt,0)); + final String txt = sqlite3_column_text16(stmt, 0); sbuf.append( txt ); + affirm( txt.equals(sqlite3_column_text(stmt, 0)) ); + affirm( txt.equals(sqlite3_value_text(sv)) ); + affirm( txt.equals(sqlite3_value_text16(sv)) ); + sqlite3_value_free(sv); ++n; } sqlite3_finalize(stmt); diff --git a/manifest b/manifest index c88153f399..9806221478 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sJNI\ssqlite3_trace_v2()\sthread-safe.\sRe-add\sa\spiece\sremoved\sin\s[bae4d022aad9b]\sto\swork\saround\sa\sJVM\scrash\swhich\sis\sunpredictably\striggered\sby\sits\ssubstitute.\sFix\sthe\sTHREADMODE=0\sJNI\sbuild.\sFurther\sinternal\sAPI\ssimplifications. -D 2023-08-27T10:40:00.984 +C Correct\sthe\ssignature\smismatch\sbetween\sJNI\ssqlite3_column/value_text16()\sand\sadd\srelated\stests. +D 2023-08-27T11:28:57.220 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 527f7c72360ba081c9ad120a9a00834973dac0115c6272fad94963651ed15bab F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 79360ee5b71ce2d90e712768456756e5d57c63bce4e8238c23417caabb790a92 -F ext/jni/src/c/sqlite3-jni.h a410d05ca47a676b75ff7b8980e75ad604ea15f3c29965f88989703abc2eeaf6 +F ext/jni/src/c/sqlite3-jni.c fd9f52e536528ffaa4a98a8eebd0e15e438b3289e8f00947abcf011d5c8f9afb +F ext/jni/src/c/sqlite3-jni.h c035d576158137e620da870eef685e6d96ade54565817fe3988fd209514eace1 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 @@ -262,9 +262,9 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java e27b7b75f561a8a04b222b6306c59e65dcf7c1fc9408523da0d65c1ffb0e1590 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f45ac5e5fcfc03b8be6d3385a6a5a11fff40ba29735d6fde00b686d878017fe F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java 37b46dc15ac8fbeb916dcf1f7771023d2be025d05422d725d5891935eda506ac +F ext/jni/src/org/sqlite/jni/Tester1.java ec5622933b896679a1297db6ed70e0619149913c0043a063c3723ee4645f1c8a F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 bae4d022aad9bbeb78cb027ecad799af87afe331e697add44ec22297c873141d -R 3800367f7606fbafc862156daf2d0dd5 +P 3f9f7a9cb08b0687ad206605a5109306762df9ae8bdeab2d8d60bf9373c9ad32 +R e2ef8e56417f07ac504c27650de35a3d U stephan -Z e92f2dfe5d2f02a04f8aac3861297f02 +Z 3bfd1e616266221f5a840c6c97fab6d0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 98d663acca..4be733d091 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f9f7a9cb08b0687ad206605a5109306762df9ae8bdeab2d8d60bf9373c9ad32 \ No newline at end of file +77f6e70f17c0cb6d031f983c458c9ec2e88d92b4716397533a029af39da2d128 \ No newline at end of file From 5575d6421b24eaf0cd9fb39bf2e5b0baaf6c4429 Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 13:43:45 +0000 Subject: [PATCH 085/422] Remove JNI sqlite3_column_java_object(), as the protection rules of sqlite3_values makes it impossible to implement safely. Add JNI sqlite3_bind_java_object(). FossilOrigin-Name: 29bd4a23a4afd96b2cc06d2b91a4f30c0bbf2347af0b0d18f8d4cf8aafa63160 --- ext/jni/src/c/sqlite3-jni.c | 84 +++++++++++----- ext/jni/src/c/sqlite3-jni.h | 8 ++ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 109 ++++++++++----------- ext/jni/src/org/sqlite/jni/Tester1.java | 8 +- manifest | 18 ++-- manifest.uuid | 2 +- 6 files changed, 135 insertions(+), 94 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 39feafc8a7..a24ed25180 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1260,14 +1260,16 @@ static S3JniNphClass * S3JniGlobal_nph(JNIEnv * const env, S3NphRef const* pRef) ** Returns the ID of the "nativePointer" field from the given ** NativePointerHolder class. */ -static jfieldID NativePointerHolder_field(JNIEnv * const env, S3NphRef const* pRef){ +static jfieldID NativePointerHolder_field(JNIEnv * const env, + S3NphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph(env, pRef); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); - S3JniExceptionIsFatal("Code maintenance required: missing nativePointer field."); + S3JniExceptionIsFatal("Code maintenance required: missing " + "nativePointer field."); } S3JniMutex_Nph_leave; } @@ -1291,7 +1293,8 @@ static void NativePointerHolder_set(JNIEnv * env, S3NphRef const* pRef, ** 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) @@ -1344,29 +1347,17 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ } /* -** Returns the S3JniDb object for the given db. At most, one of jDb or -** pDb may be non-NULL. +** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3 +** object, or NULL if jDb is NULL, no pointer can be extracted +** from it, or no matching entry can be found. ** -** The 3rd argument should normally only be non-0 for routines which -** are called from the C library and pass a native db handle instead of -** a Java handle. In normal usage, the 2nd argument is a Java-side sqlite3 -** object, from which the db is fished out. -** -** If the lockMutex argument is true then the S3JniDb mutex is locked -** before starting work, else the caller is required to have locked -** it. -** -** Returns NULL if jDb and pDb are both NULL or if there is no -** matching S3JniDb entry for pDb or the pointer fished out of jDb. +** Requires locking the S3JniDb mutex. */ -static S3JniDb * S3JniDb_get(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ +static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){ S3JniDb * s = 0; - - if( 0==jDb && 0==pDb ) return 0; - assert( jDb ? !pDb : !!pDb ); + sqlite3 * pDb = 0; S3JniMutex_S3JniDb_enter; if( jDb ){ - assert(!pDb); pDb = PtrGet_sqlite3(jDb); } s = SJG.perDb.aHead; @@ -1379,8 +1370,28 @@ static S3JniDb * S3JniDb_get(JNIEnv * const env, jobject jDb, sqlite3 *pDb){ return s; } -#define S3JniDb_from_java(jObject) S3JniDb_get(env,(jObject),0) -#define S3JniDb_from_c(sqlite3Ptr) S3JniDb_get(env,0,(sqlite3Ptr)) +/* +** Returns the S3JniDb object for the sqlite3 object, or NULL if pDb +** is NULL, or no matching entry +** can be found. +** +** Requires locking the S3JniDb mutex. +*/ +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; + } + } + S3JniMutex_S3JniDb_leave; + return s; +} + +#define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject)) +#define S3JniDb_from_c(sqlite3Ptr) S3JniDb__from_c(env,(sqlite3Ptr)) /* ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out @@ -2181,6 +2192,29 @@ S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( return (jint)sqlite3_bind_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); } +/* +** Bind a new global ref to Object `val` using sqlite3_bind_pointer(). +*/ +S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( + JniArgsEnvClass, jobject jpStmt, jint ndx, jobject val +){ + sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); + int rc = 0; + + if(pStmt){ + jobject const rv = val ? S3JniRefGlobal(val) : 0; + if( rv ){ + rc = sqlite3_bind_pointer(pStmt, ndx, rv, ResultJavaValuePtrStr, + ResultJavaValue_finalizer); + }else if(val){ + rc = SQLITE_NOMEM; + } + }else{ + rc = SQLITE_MISUSE; + } + return rc; +} + S3JniApi(sqlite3_bind_null(),jint,1bind_1null)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ @@ -2337,7 +2371,9 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ S3JniDb * const ps = S3JniDb_from_java(jDb); assert(version == 1 || version == 2); if( ps ){ - rc = 1==version ? (jint)sqlite3_close(ps->pDb) : (jint)sqlite3_close_v2(ps->pDb); + rc = 1==version + ? (jint)sqlite3_close(ps->pDb) + : (jint)sqlite3_close_v2(ps->pDb); if( 0==rc ){ S3JniDb_set_aside(env, ps) /* MUST come after close() because of ps->trace. */; diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index a4fc6619c2..a588914238 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -819,6 +819,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1int JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1int64 (JNIEnv *, jclass, jobject, jint, jlong); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_bind_java_object + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;ILjava/lang/Object;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1java_1object + (JNIEnv *, jclass, jobject, jint, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_bind_null diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index b53e921b65..1ad48250e5 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -184,6 +184,15 @@ public final class SQLite3Jni { @NotNull sqlite3_stmt stmt, int ndx, long v ); + /** + Binds the given object at the given index. + + @see #sqlite3_result_java_object + */ + public static native int sqlite3_bind_java_object( + @NotNull sqlite3_stmt cx, int ndx, @Nullable Object o + ); + public static native int sqlite3_bind_null( @NotNull sqlite3_stmt stmt, int ndx ); @@ -210,7 +219,7 @@ public final class SQLite3Jni { Works like the C-level sqlite3_bind_text() but assumes SQLITE_TRANSIENT for the final C API parameter. - Results are undefined if data is not null and +

Results are undefined if data is not null and maxBytes>=data.length. If maxBytes is negative then results are undefined if data is not null and does not contain a NUL byte. */ @@ -358,35 +367,6 @@ public final class SQLite3Jni { @NotNull sqlite3_stmt stmt, int ndx ); - /** - Column counterpart of sqlite3_value_java_object(). - */ - public static Object sqlite3_column_java_object( - @NotNull sqlite3_stmt stmt, int ndx - ){ - Object rv = null; - sqlite3_value v = sqlite3_column_value(stmt, ndx); - if(null!=v){ - v = sqlite3_value_dup(v) /* we need a "protected" value */; - if(null!=v){ - rv = sqlite3_value_java_object(v); - sqlite3_value_free(v); - } - } - return rv; - } - - /** - Column counterpart of sqlite3_value_java_casted(). - */ - @SuppressWarnings("unchecked") - public static T sqlite3_column_java_casted( - @NotNull sqlite3_stmt stmt, int ndx, @NotNull Class type - ){ - final Object o = sqlite3_column_java_object(stmt, ndx); - return type.isInstance(o) ? (T)o : null; - } - public static native String sqlite3_column_origin_name( @NotNull sqlite3_stmt stmt, int ndx ); @@ -614,7 +594,7 @@ public final class SQLite3Jni { heed. Passing the object to sqlite3_close() or sqlite3_close_v2() will clear that pointer mapping. - Recall that even if opening fails, the output pointer might be +

Recall that even if opening fails, the output pointer might be non-null. Any error message about the failure will be in that object and it is up to the caller to sqlite3_close() that db handle. @@ -628,7 +608,7 @@ public final class SQLite3Jni { object might not have been successfully opened: use sqlite3_errcode() to check whether it is in an error state. - Ownership of the returned value is passed to the caller, who must eventually +

Ownership of the returned value is passed to the caller, who must eventually pass it to sqlite3_close() or sqlite3_close_v2(). */ public static sqlite3 sqlite3_open(@Nullable String filename){ @@ -659,10 +639,10 @@ public final class SQLite3Jni { retain functionally equivalent semantics and (B) overloading allows us to install several convenience forms. - All of them which take their SQL in the form of a byte[] require +

All of them which take their SQL in the form of a byte[] require that it be in UTF-8 encoding unless explicitly noted otherwise. - The forms which take a "tail" output pointer return (via that +

The forms which take a "tail" output pointer return (via that output object) the index into their SQL byte array at which the end of the first SQL statement processed by the call was found. That's fundamentally how the C APIs work but making use of @@ -959,19 +939,22 @@ 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()} or - {@link #sqlite3_column_java_object(sqlite3_stmt,int) sqlite3_column_java_object()} to + {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} to fetch it. - This is implemented in terms of 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), which we - cannot implement cross-language here unless, in the JNI layer, we - allocate such strings and store them somewhere for long-term use - (leaking them more likely than not). Even then, passing around a - pointer via Java like that has little practical use. +

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}. - Note that there is no sqlite3_bind_java_object() counterpart. +

Note that there is no sqlite3_column_java_object(), as the + C-level API has no sqlite3_column_pointer() to proxy. + + @see #sqlite3_value_java_object + @see #sqlite3_bind_java_object */ public static native void sqlite3_result_java_object( @NotNull sqlite3_context cx, @NotNull Object o @@ -1056,12 +1039,17 @@ public final class SQLite3Jni { /** Binds the given text using C's sqlite3_result_blob64() unless: - - @param blob is null ==> sqlite3_result_null() +

    - - @param blob is too large ==> sqlite3_result_error_toobig() +
  • @param blob is null: translates to sqlite3_result_null()
  • - If @param maxLen is larger than blob.length, it is truncated to that - value. If it is negative, results are undefined. +
  • @param blob is too large: translates to + sqlite3_result_error_toobig()
  • + +
+ + If @param maxLen is larger than blob.length, it is truncated to + that value. If it is negative, results are undefined. */ private static native void sqlite3_result_blob64( @NotNull sqlite3_context cx, @Nullable byte[] blob, long maxLen @@ -1096,17 +1084,21 @@ public final class SQLite3Jni { /** Binds the given text using C's sqlite3_result_text64() unless: - - text is null: translates to a call to sqlite3_result_null() +
    - - text is too large: translates to a call to - sqlite3_result_error_toobig() +
  • text is null: translates to a call to sqlite3_result_null()
  • - - The @param encoding argument has an invalid value: translates to - sqlite3_result_error_code() with code SQLITE_FORMAT. +
  • text is too large: translates to a call to + {@link #sqlite3_result_error_toobig}
  • + +
  • The @param encoding argument has an invalid value: translates to + {@link sqlite3_result_error_code} with code SQLITE_FORMAT.
  • + +
If maxLength (in bytes, not characters) is larger than text.length, it is silently truncated to text.length. If it is - negative, results are undefined. If text is null, the following + negative, results are undefined. If text is null, the subsequent arguments are ignored. */ private static native void sqlite3_result_text64( @@ -1224,7 +1216,7 @@ public final class SQLite3Jni { function is elided here because the roles of that functions' 3rd and 4th arguments are encapsulated in the final argument to this function. - Unlike the C API, which is documented as always returning 0, this +

Unlike the C API, which is documented as always returning 0, this implementation returns non-0 if initialization of the tracer mapping state fails. */ @@ -1257,10 +1249,11 @@ public final class SQLite3Jni { public static native long sqlite3_value_int64(@NotNull sqlite3_value v); /** - If the given value was set using sqlite3_result_java_value() then - this function returns that object, else it returns null. + If the given value was set using {@link + #sqlite3_result_java_object} then this function returns that + object, else it returns null. - It is up to the caller to inspect the object to determine its +

It is up to the caller to inspect the object to determine its type, and cast it if necessary. */ public static native Object sqlite3_value_java_object( diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 2647575ee5..a8e1712331 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -695,16 +695,20 @@ public class Tester1 implements Runnable { private void testUdfJavaObject(){ final sqlite3 db = createNewDb(); final ValueHolder testResult = new ValueHolder<>(db); + final ValueHolder boundObj = new ValueHolder<>(42); final SQLFunction func = new ScalarFunction(){ public void xFunc(sqlite3_context cx, sqlite3_value args[]){ sqlite3_result_java_object(cx, testResult.value); + affirm( sqlite3_value_java_object(args[0]) == boundObj ); } }; int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func); affirm(0 == rc); - final sqlite3_stmt stmt = prepare(db, "select myfunc()"); + sqlite3_stmt stmt = prepare(db, "select myfunc(?)"); affirm( 0 != stmt.getNativePointer() ); affirm( testResult.value == db ); + rc = sqlite3_bind_java_object(stmt, 1, boundObj); + affirm( 0==rc ); int n = 0; if( SQLITE_ROW == sqlite3_step(stmt) ){ final sqlite3_value v = sqlite3_column_value(stmt, 0); @@ -1346,7 +1350,7 @@ public class Tester1 implements Runnable { affirm( 8 == val.value ); } - @ManualTest /* because we only want to run this test manually */ + @ManualTest /* we really only want to run this test manually. */ private void testSleep(){ out("Sleeping briefly... "); sqlite3_sleep(600); diff --git a/manifest b/manifest index 9806221478..663f1823ab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sthe\ssignature\smismatch\sbetween\sJNI\ssqlite3_column/value_text16()\sand\sadd\srelated\stests. -D 2023-08-27T11:28:57.220 +C Remove\sJNI\ssqlite3_column_java_object(),\sas\sthe\sprotection\srules\sof\ssqlite3_values\smakes\sit\simpossible\sto\simplement\ssafely.\sAdd\sJNI\ssqlite3_bind_java_object(). +D 2023-08-27T13:43:45.514 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 527f7c72360ba081c9ad120a9a00834973dac0115c6272fad94963651ed15bab F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c fd9f52e536528ffaa4a98a8eebd0e15e438b3289e8f00947abcf011d5c8f9afb -F ext/jni/src/c/sqlite3-jni.h c035d576158137e620da870eef685e6d96ade54565817fe3988fd209514eace1 +F ext/jni/src/c/sqlite3-jni.c 0078fdd79d31db6184a961bdf79d4c9d1138e78f6705bce7e35e17531365fbdf +F ext/jni/src/c/sqlite3-jni.h 9c57a6e7efd466c4f96b190cea995353ff8897ed38fefb279b42913f352f73a6 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 @@ -262,9 +262,9 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2f45ac5e5fcfc03b8be6d3385a6a5a11fff40ba29735d6fde00b686d878017fe +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java b22b02b51eab5bab2e9068757c94c0978a7c2ccaf4c2ffa93b3b958c2f9e7dc9 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java ec5622933b896679a1297db6ed70e0619149913c0043a063c3723ee4645f1c8a +F ext/jni/src/org/sqlite/jni/Tester1.java ff13dc4babfa7a3f8bf91862c12a7f837db611bb66fcd82f7f8728c3e297a188 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 3f9f7a9cb08b0687ad206605a5109306762df9ae8bdeab2d8d60bf9373c9ad32 -R e2ef8e56417f07ac504c27650de35a3d +P 77f6e70f17c0cb6d031f983c458c9ec2e88d92b4716397533a029af39da2d128 +R 6ff41c97b17d551a7d85c6dd457042a1 U stephan -Z 3bfd1e616266221f5a840c6c97fab6d0 +Z 98f8ce6b232c9712186c8841f85ec5e1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4be733d091..04a026cc7d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -77f6e70f17c0cb6d031f983c458c9ec2e88d92b4716397533a029af39da2d128 \ No newline at end of file +29bd4a23a4afd96b2cc06d2b91a4f30c0bbf2347af0b0d18f8d4cf8aafa63160 \ No newline at end of file From 0639c5883606632cc47c685518306a43726d8e2e Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 14:47:45 +0000 Subject: [PATCH 086/422] Generic cleanups in Tester1.java. FossilOrigin-Name: fbc1b6310b01f4f79439bb6ce59b2aaabd126da7201f36d4fc5d7ef95118a494 --- ext/jni/src/org/sqlite/jni/Tester1.java | 31 +++++++++++++++---------- manifest | 12 +++++----- manifest.uuid | 2 +- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index a8e1712331..496a90a428 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -30,6 +30,13 @@ import java.util.concurrent.Future; @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) @interface ManualTest{} +/** + Annotation for Tester1 tests which mark those which must be skipped + in multi-threaded mode. +*/ +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) +@interface SingleThreadOnly{} public class Tester1 implements Runnable { //! True when running in multi-threaded mode. @@ -692,7 +699,9 @@ public class Tester1 implements Runnable { sqlite3_close_v2(db); } + @SingleThreadOnly private void testUdfJavaObject(){ + affirm( !mtMode ); final sqlite3 db = createNewDb(); final ValueHolder testResult = new ValueHolder<>(db); final ValueHolder boundObj = new ValueHolder<>(42); @@ -1096,7 +1105,7 @@ public class Tester1 implements Runnable { This test is functionally identical to testUpdateHook(), only with a different callback type. */ - private synchronized void testPreUpdateHook(){ + private void testPreUpdateHook(){ if( !sqlite3_compileoption_used("ENABLE_PREUPDATE_HOOK") ){ //outln("Skipping testPreUpdateHook(): no pre-update hook support."); return; @@ -1206,8 +1215,8 @@ public class Tester1 implements Runnable { it throws. */ @SuppressWarnings("unchecked") - @ManualTest /* because the Fts5 parts are not yet known to be - thread-safe */ + @SingleThreadOnly /* because the Fts5 parts are not yet known to be + thread-safe */ private void testFts5() throws Exception { if( !sqlite3_compileoption_used("ENABLE_FTS5") ){ //outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); @@ -1258,8 +1267,8 @@ public class Tester1 implements Runnable { sqlite3_close(db); } - @ManualTest/* because multiple threads legitimately make these - results unpredictable */ + @SingleThreadOnly /* because multiple threads legitimately make these + results unpredictable */ private synchronized void testAutoExtension(){ final ValueHolder val = new ValueHolder<>(0); final ValueHolder toss = new ValueHolder<>(null); @@ -1406,10 +1415,6 @@ public class Tester1 implements Runnable { } if( !fromThread ){ testBusy(); - if( !mtMode ){ - testAutoExtension() /* threads rightfully muck up these results */; - testFts5(); - } } synchronized( this.getClass() ){ ++nTestRuns; @@ -1501,7 +1506,9 @@ public class Tester1 implements Runnable { testMethods.add(m); } }else if( !m.isAnnotationPresent( ManualTest.class ) ){ - if( name.startsWith("test") ){ + if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ + outln("Skipping test in multi-thread mode: ",name,"()"); + }else if( name.startsWith("test") ){ testMethods.add(m); } } @@ -1563,6 +1570,8 @@ public class Tester1 implements Runnable { outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each."); if( takeNaps ) outln("Napping between tests is enabled."); for( int n = 0; n < nRepeat; ++n ){ + ++nLoop; + out((1==nLoop ? "" : " ")+nLoop); if( nThread<=1 ){ new Tester1(0).runTests(false); continue; @@ -1570,8 +1579,6 @@ public class Tester1 implements Runnable { Tester1.mtMode = true; final ExecutorService ex = Executors.newFixedThreadPool( nThread ); //final List> futures = new ArrayList<>(); - ++nLoop; - out((1==nLoop ? "" : " ")+nLoop); for( int i = 0; i < nThread; ++i ){ ex.submit( new Tester1(i), i ); } diff --git a/manifest b/manifest index 663f1823ab..e193cadd55 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\sJNI\ssqlite3_column_java_object(),\sas\sthe\sprotection\srules\sof\ssqlite3_values\smakes\sit\simpossible\sto\simplement\ssafely.\sAdd\sJNI\ssqlite3_bind_java_object(). -D 2023-08-27T13:43:45.514 +C Generic\scleanups\sin\sTester1.java. +D 2023-08-27T14:47:45.944 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -264,7 +264,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 F ext/jni/src/org/sqlite/jni/SQLite3Jni.java b22b02b51eab5bab2e9068757c94c0978a7c2ccaf4c2ffa93b3b958c2f9e7dc9 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c -F ext/jni/src/org/sqlite/jni/Tester1.java ff13dc4babfa7a3f8bf91862c12a7f837db611bb66fcd82f7f8728c3e297a188 +F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 @@ -2103,8 +2103,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 77f6e70f17c0cb6d031f983c458c9ec2e88d92b4716397533a029af39da2d128 -R 6ff41c97b17d551a7d85c6dd457042a1 +P 29bd4a23a4afd96b2cc06d2b91a4f30c0bbf2347af0b0d18f8d4cf8aafa63160 +R 63350485eb725e6c23f0f10ae456a835 U stephan -Z 98f8ce6b232c9712186c8841f85ec5e1 +Z f3b6506c5c2f32d6bb27133665c4317a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 04a026cc7d..049a1a5d4e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -29bd4a23a4afd96b2cc06d2b91a4f30c0bbf2347af0b0d18f8d4cf8aafa63160 \ No newline at end of file +fbc1b6310b01f4f79439bb6ce59b2aaabd126da7201f36d4fc5d7ef95118a494 \ No newline at end of file From 5020ddc243d239f4a1e85fbb45a99326e01748bd Mon Sep 17 00:00:00 2001 From: stephan Date: Sun, 27 Aug 2023 15:15:46 +0000 Subject: [PATCH 087/422] Reimplement JNI's sqlite3_value_text() as a native instead of a Java-side proxy. Unrelated minor simplifications. FossilOrigin-Name: 401dd6ee283a4e7da2e50175a1b58e6a83afb0ea2d3b6cf80c7c621e17bcce98 --- ext/jni/src/c/sqlite3-jni.c | 82 +++++++++++++--------- ext/jni/src/c/sqlite3-jni.h | 8 +++ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 15 ++-- manifest | 16 ++--- manifest.uuid | 2 +- 5 files changed, 75 insertions(+), 48 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index a24ed25180..0f18fa5a09 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -887,7 +887,7 @@ static int s3jni_db_error(sqlite3* const db, int err_code, ** errors are enabled). p may be NULL, in which case the array is ** created but no bytes are filled. */ -static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, +static jbyteArray s3jni__new_jbyteArray(JNIEnv * const env, const void * const p, int nP){ jbyteArray jba = (*env)->NewByteArray(env, (jint)nP); @@ -898,6 +898,9 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, return jba; } +#define s3jni_new_jbyteArray(P,n) s3jni__new_jbyteArray(env, P, n) + + /* ** Uses the java.lang.String(byte[],Charset) constructor to create a ** new String from UTF-8 string z. n is the number of bytes to @@ -910,8 +913,8 @@ static jbyteArray s3jni_new_jbyteArray(JNIEnv * const env, ** standard UTF-8 to a Java string, but JNI offers only algorithms for ** working with MUTF-8, not UTF-8. */ -static jstring s3jni_utf8_to_jstring(JNIEnv * const env, - const char * const z, int n){ +static jstring s3jni__utf8_to_jstring(JNIEnv * const env, + const char * const z, int n){ jstring rv = NULL; if( 0==n || (n<0 && z && !z[0]) ){ /* Fast-track the empty-string case via the MUTF-8 API. We could @@ -921,7 +924,7 @@ static jstring s3jni_utf8_to_jstring(JNIEnv * const env, }else if( z ){ jbyteArray jba; if( n<0 ) n = sqlite3Strlen30(z); - jba = s3jni_new_jbyteArray(env, (unsigned const char *)z, n); + jba = s3jni_new_jbyteArray((unsigned const char *)z, n); if( jba ){ rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA, jba, SJG.g.oCharsetUtf8); @@ -935,6 +938,7 @@ static jstring s3jni_utf8_to_jstring(JNIEnv * const env, s3jni_oom_check( rv ); return rv; } +#define s3jni_utf8_to_jstring(CStr,n) s3jni__utf8_to_jstring(env, CStr, n) /* ** Converts the given java.lang.String object into a NUL-terminated @@ -951,7 +955,7 @@ static jstring s3jni_utf8_to_jstring(JNIEnv * const env, ** The returned memory is allocated from sqlite3_malloc() and ** ownership is transferred to the caller. */ -static char * s3jni_jstring_to_utf8(JNIEnv * const env, +static char * s3jni__jstring_to_utf8(JNIEnv * const env, jstring jstr, int *nLen){ jbyteArray jba; jsize nBa; @@ -978,6 +982,7 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, S3JniUnrefLocal(jba); return rv; } +#define s3jni_jstring_to_utf8(JStr,n) s3jni__jstring_to_utf8(env, JStr, n) /* ** Expects to be passed a pointer from sqlite3_column_text16() or @@ -1025,7 +1030,7 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ S3JniExceptionClear; return 0; } - zMsg = s3jni_jstring_to_utf8(env, msg, 0); + zMsg = s3jni_jstring_to_utf8( msg, 0); S3JniUnrefLocal(msg); return zMsg; } @@ -1564,9 +1569,9 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, S3JniHook_localdup(env, &ps->hooks.collation, &hook ); if( hook.jObj ){ - jbyteArray jbaLhs = s3jni_new_jbyteArray(env, lhs, (jint)nLhs); + jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs); jbyteArray jbaRhs = jbaLhs - ? s3jni_new_jbyteArray(env, rhs, (jint)nRhs) : 0; + ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0; if( !jbaRhs ){ S3JniUnrefLocal(jbaLhs); s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); @@ -1961,7 +1966,7 @@ static void udf_xInverse(sqlite3_context* cx, int argc, /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */ #define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \ - return s3jni_utf8_to_jstring(env, \ + return s3jni_utf8_to_jstring( \ CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \ -1); \ } @@ -2482,7 +2487,7 @@ S3JniApi(sqlite3_column_blob(),jbyteArray,1column_1blob)( void const * const p = sqlite3_column_blob(pStmt, (int)ndx); int const n = p ? sqlite3_column_bytes(pStmt, (int)ndx) : 0; - return p ? s3jni_new_jbyteArray(env, p, n) : 0; + return p ? s3jni_new_jbyteArray(p, n) : 0; } S3JniApi(sqlite3_column_double(),jdouble,1column_1double)( @@ -2509,7 +2514,7 @@ S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text_1utf8)( sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); const int n = sqlite3_column_bytes(stmt, (int)ndx); const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - return p ? s3jni_new_jbyteArray(env, p, n) : NULL; + return p ? s3jni_new_jbyteArray(p, n) : NULL; } S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( @@ -2666,7 +2671,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int switch( op ){ case 0: /* db opened */ case 1: /* SQL executed */ - jArg1 = s3jni_utf8_to_jstring(env, z, -1); + jArg1 = s3jni_utf8_to_jstring( z, -1); break; case 2: /* db closed */ break; @@ -2755,7 +2760,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "Could not get xCompare() method for object."); }else{ - char * const zName = s3jni_jstring_to_utf8(env, name, 0); + char * const zName = s3jni_jstring_to_utf8( name, 0); if( zName ){ S3JniMutex_S3JniDb_enter; rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, @@ -2797,7 +2802,7 @@ S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_w S3JniUdf_free(env, s, 1); goto error_cleanup; } - zFuncName = s3jni_jstring_to_utf8(env,jFuncName,0); + zFuncName = s3jni_jstring_to_utf8(jFuncName,0); if( !zFuncName ){ rc = SQLITE_NOMEM; S3JniUdf_free(env, s, 1); @@ -2839,12 +2844,12 @@ S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( if( !ps || !jDbName ){ return 0; } - zDbName = s3jni_jstring_to_utf8(env, jDbName, &nStr); + zDbName = s3jni_jstring_to_utf8( jDbName, &nStr); if( zDbName ){ char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); sqlite3_free(zDbName); if( zRv ){ - jRv = s3jni_utf8_to_jstring(env, zRv, -1); + jRv = s3jni_utf8_to_jstring( zRv, -1); } } return jRv; @@ -2871,7 +2876,7 @@ S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/, S3JniMutex_S3JniDb_enter /* Protect against a race in modifying/freeing ps->zMainDbName. */; - zStr = s3jni_jstring_to_utf8(env, jStr, 0); + zStr = s3jni_jstring_to_utf8( jStr, 0); if( zStr ){ rc = sqlite3_db_config(ps->pDb, (int)op, zStr); if( rc ){ @@ -2974,7 +2979,7 @@ S3JniApi(sqlite3_errmsg(),jstring,1errmsg)( JniArgsEnvClass, jobject jpDb ){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - return pDb ? s3jni_utf8_to_jstring(env, sqlite3_errmsg(pDb), -1) : 0; + return pDb ? s3jni_utf8_to_jstring( sqlite3_errmsg(pDb), -1) : 0; } S3JniApi(sqlite3_errstr(),jstring,1errstr)( @@ -2996,7 +3001,7 @@ S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( char * zSql = sqlite3_expanded_sql(pStmt); s3jni_oom_fatal(zSql); if( zSql ){ - rv = s3jni_utf8_to_jstring(env, zSql, -1); + rv = s3jni_utf8_to_jstring( zSql, -1); sqlite3_free(zSql); } } @@ -3077,7 +3082,7 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, rc = SQLITE_NOMEM; goto end; } - *zDbName = jDbName ? s3jni_jstring_to_utf8(env, jDbName, 0) : 0; + *zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0) : 0; if( jDbName && !*zDbName ){ rc = SQLITE_NOMEM; goto end; @@ -3166,7 +3171,7 @@ S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ if( strVfs ){ - zVfs = s3jni_jstring_to_utf8(env, strVfs, 0); + zVfs = s3jni_jstring_to_utf8( strVfs, 0); if( !zVfs ){ rc = SQLITE_NOMEM; } @@ -3291,8 +3296,8 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, if( !hook.jObj ){ return; } - jDbName = s3jni_utf8_to_jstring(env, zDb, -1); - jTable = jDbName ? s3jni_utf8_to_jstring(env, zTable, -1) : 0; + jDbName = s3jni_utf8_to_jstring( zDb, -1); + jTable = jDbName ? s3jni_utf8_to_jstring( zTable, -1) : 0; S3JniIfThrew { S3JniExceptionClear; s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); @@ -3770,10 +3775,10 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniHook_localdup(env, &ps->hooks.auth, &hook ); if( hook.jObj ){ - jstring const s0 = z0 ? s3jni_utf8_to_jstring(env, z0, -1) : 0; - jstring const s1 = z1 ? s3jni_utf8_to_jstring(env, z1, -1) : 0; - jstring const s2 = z2 ? s3jni_utf8_to_jstring(env, z2, -1) : 0; - jstring const s3 = z3 ? s3jni_utf8_to_jstring(env, z3, -1) : 0; + jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0; + jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0; + jstring const s2 = z2 ? s3jni_utf8_to_jstring( z2, -1) : 0; + jstring const s3 = z3 ? s3jni_utf8_to_jstring( z3, -1) : 0; rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op, s0, s1, s3, s3); @@ -3954,7 +3959,7 @@ S3JniApi(sqlite3_sql(),jstring,1sql)( if( pStmt ){ const char * zSql = 0; zSql = sqlite3_sql(pStmt); - rv = s3jni_utf8_to_jstring(env, zSql, -1); + rv = s3jni_utf8_to_jstring( zSql, -1); } return rv; } @@ -3985,7 +3990,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ } switch( traceflag ){ case SQLITE_TRACE_STMT: - jX = s3jni_utf8_to_jstring(env, (const char *)pX, -1); + jX = s3jni_utf8_to_jstring( (const char *)pX, -1); if( !jX ) rc = SQLITE_NOMEM; break; case SQLITE_TRACE_PROFILE: @@ -4084,7 +4089,7 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( s3jni_oom_check( nLen ? !!pBytes : 1 ); return pBytes - ? s3jni_new_jbyteArray(env, pBytes, nLen) + ? s3jni_new_jbyteArray(pBytes, nLen) : NULL; } @@ -4134,7 +4139,16 @@ S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)( sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); int const n = sqlite3_value_bytes(sv); const unsigned char * const p = sqlite3_value_text(sv); - return p ? s3jni_new_jbyteArray(env, p, n) : 0; + return p ? s3jni_new_jbyteArray(p, n) : 0; +} + +S3JniApi(sqlite3_value_text(),jstring,1value_1text)( + JniArgsEnvClass, jobject jpSVal +){ + sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal); + int const n = sqlite3_value_bytes(sv); + const unsigned char * const p = sqlite3_value_text(sv); + return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; } S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)( @@ -4381,7 +4395,7 @@ JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol, int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol, &pz, &pn); if( 0==rc ){ - jstring jstr = pz ? s3jni_utf8_to_jstring(env, pz, pn) : 0; + jstring jstr = pz ? s3jni_utf8_to_jstring( pz, pn) : 0; if( pz ){ if( jstr ){ OutputPointer_set_String(env, jOut, jstr); @@ -4451,7 +4465,7 @@ JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, Fts5JniAux * pAux; assert(pApi); - zName = s3jni_jstring_to_utf8(env, jName, 0); + zName = s3jni_jstring_to_utf8( jName, 0); if(!zName) return SQLITE_NOMEM; pAux = Fts5JniAux_alloc(env, jFunc); if( pAux ){ @@ -4722,7 +4736,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, S3JniUnrefLocal(s->tok.jba); s->tok.zPrev = z; s->tok.nPrev = nZ; - s->tok.jba = s3jni_new_jbyteArray(env, z, nZ); + s->tok.jba = s3jni_new_jbyteArray(z, nZ); if( !s->tok.jba ) return SQLITE_NOMEM; jba = s->tok.jba; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index a588914238..7c44dfd758 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1763,6 +1763,14 @@ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1java_1o JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text_1utf8 (JNIEnv *, jclass, jobject); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_value_text + * Signature: (Lorg/sqlite/jni/sqlite3_value;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1value_1text + (JNIEnv *, jclass, jobject); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_value_text16 diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 1ad48250e5..df4657fd23 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -1274,15 +1274,20 @@ public final class SQLite3Jni { /** Returns the given value as UTF-8-encoded bytes, or null if the - underlying C API returns null for sqlite3_value_text(). + underlying C-level sqlite3_value_text() returns NULL. */ public static native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v); - public static String sqlite3_value_text(@NotNull sqlite3_value v){ - final byte[] ba = sqlite3_value_text_utf8(v); - return null==ba ? null : new String(ba, StandardCharsets.UTF_8); - } + public static native String sqlite3_value_text(@NotNull sqlite3_value v); + /** + In the Java layer, sqlite3_value_text() and + sqlite3_value_text16() are functionally equivalent, the + difference being only where the encoding to UTF-16 (if necessary) + takes place. This function does it via SQLite and + sqlite3_value_text() fetches UTF-8 (SQLite's default encoding) + and converts it to UTF-16 in Java. + */ public static native String sqlite3_value_text16(@NotNull sqlite3_value v); public static native int sqlite3_value_type(@NotNull sqlite3_value v); diff --git a/manifest b/manifest index e193cadd55..4654017df0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Generic\scleanups\sin\sTester1.java. -D 2023-08-27T14:47:45.944 +C Reimplement\sJNI's\ssqlite3_value_text()\sas\sa\snative\sinstead\sof\sa\sJava-side\sproxy.\sUnrelated\sminor\ssimplifications. +D 2023-08-27T15:15:46.747 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,8 +236,8 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 F ext/jni/GNUmakefile 527f7c72360ba081c9ad120a9a00834973dac0115c6272fad94963651ed15bab F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 0078fdd79d31db6184a961bdf79d4c9d1138e78f6705bce7e35e17531365fbdf -F ext/jni/src/c/sqlite3-jni.h 9c57a6e7efd466c4f96b190cea995353ff8897ed38fefb279b42913f352f73a6 +F ext/jni/src/c/sqlite3-jni.c 5d43a4a29d16f6a593091eb60371e166d56d0c019cb75a6b741bc41e3b946344 +F ext/jni/src/c/sqlite3-jni.h a43832a04347433e640f1b135f3a8b2a4293369a963659216c51d04daf934b43 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 @@ -262,7 +262,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java b22b02b51eab5bab2e9068757c94c0978a7c2ccaf4c2ffa93b3b958c2f9e7dc9 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java e51ebe108a2fd4758151258bcca8a2e419a6b55c69651e7cfb57d906aaa066fd F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -2103,8 +2103,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 29bd4a23a4afd96b2cc06d2b91a4f30c0bbf2347af0b0d18f8d4cf8aafa63160 -R 63350485eb725e6c23f0f10ae456a835 +P fbc1b6310b01f4f79439bb6ce59b2aaabd126da7201f36d4fc5d7ef95118a494 +R 5368adb6ae6cfb0703304e3a49d858b9 U stephan -Z f3b6506c5c2f32d6bb27133665c4317a +Z 2787d4cb0c304c511b23ae7b39d8dc7e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 049a1a5d4e..715ea8e496 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fbc1b6310b01f4f79439bb6ce59b2aaabd126da7201f36d4fc5d7ef95118a494 \ No newline at end of file +401dd6ee283a4e7da2e50175a1b58e6a83afb0ea2d3b6cf80c7c621e17bcce98 \ No newline at end of file From 56d3b213177c1f39f0bab617e63b937fad2df074 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 04:59:16 +0000 Subject: [PATCH 088/422] Resolve a makefile bug which causes sqlite3-worker1-bundler-friendly.mjs to be built incorrectly. Reported in [forum:a874e435cf4690c1|forum post a874e435cf4690c1]. FossilOrigin-Name: 9d68c7ef103b9b51ed35fb91e62351eb6c62ad63ab03ac456ff9787cc83f10ab --- ext/wasm/GNUmakefile | 4 ++-- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 2cd44ac55a..c0cab212d8 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -838,11 +838,11 @@ sqlite3-worker1-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-bundler-frien sqlite3-worker1-promiser-bundler-friendly.js := $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js $(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1.js))) $(eval $(call C-PP.FILTER,$(sqlite3-worker1.js.in),$(sqlite3-worker1-bundler-friendly.js),\ - $(c-pp.D.bundler-friendly))) + $(c-pp.D.sqlite3-bundler-friendly))) $(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),$(sqlite3-worker1-promiser.js))) $(eval $(call C-PP.FILTER,$(sqlite3-worker1-promiser.js.in),\ $(sqlite3-worker1-promiser-bundler-friendly.js),\ - $(c-pp.D.bundler-friendly))) + $(c-pp.D.sqlite3-bundler-friendly))) $(sqlite3-bundler-friendly.mjs): $(sqlite3-worker1-bundler-friendly.js) \ $(sqlite3-worker1-promiser-bundler-friendly.js) $(sqlite3.js) $(sqlite3.mjs): $(sqlite3-worker1.js) $(sqlite3-worker1-promiser.js) diff --git a/manifest b/manifest index 4654017df0..ce09f75dd8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Reimplement\sJNI's\ssqlite3_value_text()\sas\sa\snative\sinstead\sof\sa\sJava-side\sproxy.\sUnrelated\sminor\ssimplifications. -D 2023-08-27T15:15:46.747 +C Resolve\sa\smakefile\sbug\swhich\scauses\ssqlite3-worker1-bundler-friendly.mjs\sto\sbe\sbuilt\sincorrectly.\sReported\sin\s[forum:a874e435cf4690c1|forum\spost\sa874e435cf4690c1]. +D 2023-08-28T04:59:16.223 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -540,7 +540,7 @@ F ext/userauth/sqlite3userauth.h 7f3ea8c4686db8e40b0a0e7a8e0b00fac13aa7a3 F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile 594f5a6dc889f836ebdf0d4a35a9c39fbf56cd2827416cdd98b5244f51519901 +F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab @@ -2103,8 +2103,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 fbc1b6310b01f4f79439bb6ce59b2aaabd126da7201f36d4fc5d7ef95118a494 -R 5368adb6ae6cfb0703304e3a49d858b9 +P 401dd6ee283a4e7da2e50175a1b58e6a83afb0ea2d3b6cf80c7c621e17bcce98 +R fe522017118571ebafc6bc0118d325e6 U stephan -Z 2787d4cb0c304c511b23ae7b39d8dc7e +Z 8555bb227cc776c0f30cf33f6d19fa72 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 715ea8e496..d518768fa6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -401dd6ee283a4e7da2e50175a1b58e6a83afb0ea2d3b6cf80c7c621e17bcce98 \ No newline at end of file +9d68c7ef103b9b51ed35fb91e62351eb6c62ad63ab03ac456ff9787cc83f10ab \ No newline at end of file From b041f8ffdd8bcde211d366b73a12086f1ecc32d0 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 05:39:05 +0000 Subject: [PATCH 089/422] Move the JNI annotations into the annotation subpackage. FossilOrigin-Name: e4dedf90a92a069daef967dfe975469bf8ec7883c44c95e73345d4eded48e996 --- ext/jni/GNUmakefile | 4 ++-- .../src/org/sqlite/jni/Fts5ExtensionApi.java | 1 + ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 1 + .../sqlite/jni/{ => annotation}/NotNull.java | 0 .../sqlite/jni/{ => annotation}/Nullable.java | 0 manifest | 20 +++++++++---------- manifest.uuid | 2 +- 7 files changed, 15 insertions(+), 13 deletions(-) rename ext/jni/src/org/sqlite/jni/{ => annotation}/NotNull.java (100%) rename ext/jni/src/org/sqlite/jni/{ => annotation}/Nullable.java (100%) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 32bd7b4b50..05f05679c3 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -59,6 +59,8 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ + annotation/NotNull.java \ + annotation/Nullable.java \ AggregateFunction.java \ AuthorizerCallback.java \ AutoExtensionCallback.java \ @@ -68,8 +70,6 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ CommitHookCallback.java \ ConfigSqllogCallback.java \ NativePointerHolder.java \ - NotNull.java \ - Nullable.java \ OutputPointer.java \ PreupdateHookCallback.java \ ProgressHandlerCallback.java \ diff --git a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java index 625f8af3ae..ab2995f378 100644 --- a/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java +++ b/ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java @@ -13,6 +13,7 @@ */ package org.sqlite.jni; import java.nio.charset.StandardCharsets; +import org.sqlite.jni.annotation.*; /** ALMOST COMPLETELY UNTESTED. diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index df4657fd23..8777a9abd3 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -18,6 +18,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import org.sqlite.jni.annotation.*; /** This class contains the entire C-style sqlite3 JNI API binding, diff --git a/ext/jni/src/org/sqlite/jni/NotNull.java b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java similarity index 100% rename from ext/jni/src/org/sqlite/jni/NotNull.java rename to ext/jni/src/org/sqlite/jni/annotation/NotNull.java diff --git a/ext/jni/src/org/sqlite/jni/Nullable.java b/ext/jni/src/org/sqlite/jni/annotation/Nullable.java similarity index 100% rename from ext/jni/src/org/sqlite/jni/Nullable.java rename to ext/jni/src/org/sqlite/jni/annotation/Nullable.java diff --git a/manifest b/manifest index ce09f75dd8..b56a98f093 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Resolve\sa\smakefile\sbug\swhich\scauses\ssqlite3-worker1-bundler-friendly.mjs\sto\sbe\sbuilt\sincorrectly.\sReported\sin\s[forum:a874e435cf4690c1|forum\spost\sa874e435cf4690c1]. -D 2023-08-28T04:59:16.223 +C Move\sthe\sJNI\sannotations\sinto\sthe\sannotation\ssubpackage. +D 2023-08-28T05:39:05.491 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,7 +233,7 @@ 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 527f7c72360ba081c9ad120a9a00834973dac0115c6272fad94963651ed15bab +F ext/jni/GNUmakefile 121bc78adc561d8112900529da247c794395d2ea65f5f3be3bc9f7fc00f4cc69 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 5d43a4a29d16f6a593091eb60371e166d56d0c019cb75a6b741bc41e3b946344 @@ -248,13 +248,11 @@ F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a4 F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 -F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java d475009d654a16060101da6bdfd7f118d49ff61d910faf5a1d1fdb0a1def75f1 +F ext/jni/src/org/sqlite/jni/Fts5ExtensionApi.java 2fd11abb7c5403318181d69bb7b702a79cba7ab460105140f5161bea9bc505d1 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 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d -F ext/jni/src/org/sqlite/jni/NotNull.java 08fcfcee93cdf5dd3128a88f9a112c000c143f9e077fcb31e554822595c9d73e -F ext/jni/src/org/sqlite/jni/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a964227db3ba6f953cc89b8be38a5ae6fea063818 F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 @@ -262,7 +260,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java e51ebe108a2fd4758151258bcca8a2e419a6b55c69651e7cfb57d906aaa066fd +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2ba0affa14ed1963dff7ce7137bde7023529483c2035df85bc53d98db68323ce F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -271,6 +269,8 @@ F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b4 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471 +F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 08fcfcee93cdf5dd3128a88f9a112c000c143f9e077fcb31e554822595c9d73e w ext/jni/src/org/sqlite/jni/NotNull.java +F ext/jni/src/org/sqlite/jni/annotation/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 w ext/jni/src/org/sqlite/jni/Nullable.java F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c F ext/jni/src/org/sqlite/jni/fts5_tokenizer.java a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa @@ -2103,8 +2103,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 401dd6ee283a4e7da2e50175a1b58e6a83afb0ea2d3b6cf80c7c621e17bcce98 -R fe522017118571ebafc6bc0118d325e6 +P 9d68c7ef103b9b51ed35fb91e62351eb6c62ad63ab03ac456ff9787cc83f10ab +R 900001794b9eaaab3fc73ae240f52bf1 U stephan -Z 8555bb227cc776c0f30cf33f6d19fa72 +Z 30623e5b61f856149d3de4bbf6c1f46e # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d518768fa6..d4cfde474f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9d68c7ef103b9b51ed35fb91e62351eb6c62ad63ab03ac456ff9787cc83f10ab \ No newline at end of file +e4dedf90a92a069daef967dfe975469bf8ec7883c44c95e73345d4eded48e996 \ No newline at end of file From 23dfa67c1e2da7256b7bba9127f226caea55aa41 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 05:48:34 +0000 Subject: [PATCH 090/422] Correct a package renaming bug in the previous check-in and start applying the @Canonical annotation to functions to distinguish canonical API functions and Java-specific APIs. FossilOrigin-Name: 8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf --- ext/jni/GNUmakefile | 1 + .../org/sqlite/jni/AuthorizerCallback.java | 1 + ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 16 ++++++++++++ .../org/sqlite/jni/annotation/Canonical.java | 16 ++++++++++++ .../org/sqlite/jni/annotation/NotNull.java | 2 +- .../org/sqlite/jni/annotation/Nullable.java | 2 +- ext/jni/src/org/sqlite/jni/fts5_api.java | 1 + .../src/org/sqlite/jni/fts5_tokenizer.java | 1 + manifest | 25 ++++++++++--------- manifest.uuid | 2 +- 10 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/annotation/Canonical.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 05f05679c3..cd5150eb10 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -59,6 +59,7 @@ $(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile # Be explicit about which Java files to compile so that we can work on # in-progress files without requiring them to be in a compilable statae. JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ + annotation/Canonical.java \ annotation/NotNull.java \ annotation/Nullable.java \ AggregateFunction.java \ diff --git a/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java index 7eefe9f7db..eef7e5020c 100644 --- a/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java +++ b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java @@ -12,6 +12,7 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import org.sqlite.jni.annotation.*; /** Callback for use with sqlite3_set_authorizer(). diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 8777a9abd3..1e02b41be8 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -138,6 +138,7 @@ public final class SQLite3Jni { allocation error. In all casses, 0 is considered the sentinel "not a key" value. */ + @Canonical public static native long sqlite3_aggregate_context(sqlite3_context cx, boolean initialize); /** @@ -156,11 +157,13 @@ public final class SQLite3Jni {

See the AutoExtension class docs for more information. */ + @Canonical public static native int sqlite3_auto_extension(@NotNull AutoExtensionCallback callback); /** Results are undefined if data is not null and n<0 || n>=data.length. */ + @Canonical public static native int sqlite3_bind_blob( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n ); @@ -173,14 +176,17 @@ public final class SQLite3Jni { : sqlite3_bind_blob(stmt, ndx, data, data.length); } + @Canonical public static native int sqlite3_bind_double( @NotNull sqlite3_stmt stmt, int ndx, double v ); + @Canonical public static native int sqlite3_bind_int( @NotNull sqlite3_stmt stmt, int ndx, int v ); + @Canonical public static native int sqlite3_bind_int64( @NotNull sqlite3_stmt stmt, int ndx, long v ); @@ -194,10 +200,12 @@ public final class SQLite3Jni { @NotNull sqlite3_stmt cx, int ndx, @Nullable Object o ); + @Canonical public static native int sqlite3_bind_null( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native int sqlite3_bind_parameter_count( @NotNull sqlite3_stmt stmt ); @@ -205,10 +213,12 @@ public final class SQLite3Jni { /** Requires that paramName be a NUL-terminated UTF-8 string. */ + @Canonical public static native int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, byte[] paramName ); + @Canonical public static int sqlite3_bind_parameter_index( @NotNull sqlite3_stmt stmt, @NotNull String paramName ){ @@ -224,6 +234,7 @@ public final class SQLite3Jni { maxBytes>=data.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 ); @@ -257,6 +268,7 @@ public final class SQLite3Jni { signature but requires that its input be encoded in UTF-16 in platform byte order. */ + @Canonical private static native int sqlite3_bind_text16( @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes ); @@ -287,10 +299,12 @@ public final class SQLite3Jni { : sqlite3_bind_text16(stmt, ndx, data, data.length); } + @Canonical public static native int sqlite3_bind_zeroblob( @NotNull sqlite3_stmt stmt, int ndx, int n ); + @Canonical public static native int sqlite3_bind_zeroblob64( @NotNull sqlite3_stmt stmt, int ndx, long n ); @@ -300,10 +314,12 @@ public final class SQLite3Jni { instance in place of a callback function. Pass it a null handler to clear the busy handler. */ + @Canonical public static native int sqlite3_busy_handler( @NotNull sqlite3 db, @Nullable BusyHandlerCallback handler ); + @Canonical public static native int sqlite3_busy_timeout( @NotNull sqlite3 db, int ms ); diff --git a/ext/jni/src/org/sqlite/jni/annotation/Canonical.java b/ext/jni/src/org/sqlite/jni/annotation/Canonical.java new file mode 100644 index 0000000000..260fb2df5f --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/annotation/Canonical.java @@ -0,0 +1,16 @@ +package org.sqlite.jni.annotation; + +/** + This annotation is for marking functions as "canonical", meaning + that they exist in the C API. The intent is to distinguish them + from functions added specifically to the Java API. + +

Canonical functions, unless specifically documented, have the + same semantics as their counterparts in @{link + https://sqlite.org/c3ref/intro.html the C API documentation}, despite + their signatures perhaps differing. +*/ +@java.lang.annotation.Documented +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) +@java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) +public @interface Canonical{} diff --git a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java index 831f084513..58431e0d95 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java +++ b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java @@ -1,4 +1,4 @@ -package org.sqlite.jni; +package org.sqlite.jni.annotation; /** This annotation is for flagging parameters which may not legally be diff --git a/ext/jni/src/org/sqlite/jni/annotation/Nullable.java b/ext/jni/src/org/sqlite/jni/annotation/Nullable.java index 1dbb780d50..187ab263b0 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/Nullable.java +++ b/ext/jni/src/org/sqlite/jni/annotation/Nullable.java @@ -1,4 +1,4 @@ -package org.sqlite.jni; +package org.sqlite.jni.annotation; /** This annotation is for flagging parameters which may legally be diff --git a/ext/jni/src/org/sqlite/jni/fts5_api.java b/ext/jni/src/org/sqlite/jni/fts5_api.java index 43b3d62ded..92ca7c669a 100644 --- a/ext/jni/src/org/sqlite/jni/fts5_api.java +++ b/ext/jni/src/org/sqlite/jni/fts5_api.java @@ -12,6 +12,7 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import org.sqlite.jni.annotation.*; /** INCOMPLETE AND COMPLETELY UNTESTED. diff --git a/ext/jni/src/org/sqlite/jni/fts5_tokenizer.java b/ext/jni/src/org/sqlite/jni/fts5_tokenizer.java index fd86c2f518..053434e266 100644 --- a/ext/jni/src/org/sqlite/jni/fts5_tokenizer.java +++ b/ext/jni/src/org/sqlite/jni/fts5_tokenizer.java @@ -12,6 +12,7 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import org.sqlite.jni.annotation.*; /** INCOMPLETE AND COMPLETELY UNTESTED. diff --git a/manifest b/manifest index b56a98f093..47601b99ac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sthe\sJNI\sannotations\sinto\sthe\sannotation\ssubpackage. -D 2023-08-28T05:39:05.491 +C Correct\sa\spackage\srenaming\sbug\sin\sthe\sprevious\scheck-in\sand\sstart\sapplying\sthe\s@Canonical\sannotation\sto\sfunctions\sto\sdistinguish\scanonical\sAPI\sfunctions\sand\sJava-specific\sAPIs. +D 2023-08-28T05:48:34.397 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,13 +233,13 @@ 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 121bc78adc561d8112900529da247c794395d2ea65f5f3be3bc9f7fc00f4cc69 +F ext/jni/GNUmakefile e582f0e36b80c9588d52f520bf07c37ddbd63199029a97de0925014e73127972 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa F ext/jni/src/c/sqlite3-jni.c 5d43a4a29d16f6a593091eb60371e166d56d0c019cb75a6b741bc41e3b946344 F ext/jni/src/c/sqlite3-jni.h a43832a04347433e640f1b135f3a8b2a4293369a963659216c51d04daf934b43 F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef -F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java c374bb76409cce7a0bdba94877706b59ac6127fa5d9e6af3e8058c99ce99c030 +F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java 91442d9871423d01cb1750d63ccf991b3dadcf0a4efaf1d5bc49696469aeae2f F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java efef1892e404f5780d81c46a7997cab874aff5db5131985dd3af319fc5e3adc7 F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 @@ -260,7 +260,7 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2ba0affa14ed1963dff7ce7137bde7023529483c2035df85bc53d98db68323ce +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 953288346d99520bbc9ec94a28ec3a47262ef0c81fc7b11d82f8ba5143589518 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 @@ -269,11 +269,12 @@ F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b4 F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471 -F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 08fcfcee93cdf5dd3128a88f9a112c000c143f9e077fcb31e554822595c9d73e w ext/jni/src/org/sqlite/jni/NotNull.java -F ext/jni/src/org/sqlite/jni/annotation/Nullable.java b2f8755970e9dd0e917a505638d036ccc699c8422f1a69fe9d98c0804beaea17 w ext/jni/src/org/sqlite/jni/Nullable.java -F ext/jni/src/org/sqlite/jni/fts5_api.java 5198be71c162e3e0cb1f4962a7cdf0d7596e8af53f70c4af6db24aab8d53d9ba +F ext/jni/src/org/sqlite/jni/annotation/Canonical.java 87e59ab0ee5e454bc8b6a6e659996b2141041dd46e71638b42baf3360a990126 +F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 3a105502c56c0e4f1a826122db356be713cda975b3c17877d0805e45652f5937 +F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 38a2cf9b780484ca7950b76445f65133463afba6d593fd8d4a381a5937135b1c +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 a9cce7f9c52803f0d8ee7fb8e40c94e88e980dc24a170e6344b9e5ab0a4411fa +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/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c388ec3bc1de547f098a0217158fc F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad @@ -2103,8 +2104,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 9d68c7ef103b9b51ed35fb91e62351eb6c62ad63ab03ac456ff9787cc83f10ab -R 900001794b9eaaab3fc73ae240f52bf1 +P e4dedf90a92a069daef967dfe975469bf8ec7883c44c95e73345d4eded48e996 +R 2d3e956453c21ac2865e49e96e7183c2 U stephan -Z 30623e5b61f856149d3de4bbf6c1f46e +Z 2de3d392080a8d73c4c451d28e259756 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d4cfde474f..da3d87c080 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e4dedf90a92a069daef967dfe975469bf8ec7883c44c95e73345d4eded48e996 \ No newline at end of file +8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf \ No newline at end of file From 2597ec63857d85037b5a52951ceddf088fe336df Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 07:28:36 +0000 Subject: [PATCH 091/422] Lots of javadoc-related tweaks. FossilOrigin-Name: cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd --- ext/jni/GNUmakefile | 4 +- ext/jni/src/c/sqlite3-jni.c | 9 ++ ext/jni/src/c/sqlite3-jni.h | 8 + .../sqlite/jni/AbstractCollationCallback.java | 34 +++++ .../src/org/sqlite/jni/AggregateFunction.java | 8 +- .../org/sqlite/jni/AuthorizerCallback.java | 4 +- .../org/sqlite/jni/AutoExtensionCallback.java | 17 +-- .../org/sqlite/jni/BusyHandlerCallback.java | 10 +- .../src/org/sqlite/jni/CollationCallback.java | 16 +- .../sqlite/jni/CollationNeededCallback.java | 2 +- .../org/sqlite/jni/CommitHookCallback.java | 2 +- .../org/sqlite/jni/PreupdateHookCallback.java | 4 +- .../sqlite/jni/ProgressHandlerCallback.java | 4 +- .../org/sqlite/jni/RollbackHookCallback.java | 4 +- ext/jni/src/org/sqlite/jni/SQLFunction.java | 10 +- .../org/sqlite/jni/SQLite3CallbackProxy.java | 24 ++- ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 140 +++++++++++++++++- .../src/org/sqlite/jni/ScalarFunction.java | 6 +- .../src/org/sqlite/jni/TraceV2Callback.java | 7 +- .../org/sqlite/jni/UpdateHookCallback.java | 4 +- .../src/org/sqlite/jni/WindowFunction.java | 10 +- .../org/sqlite/jni/annotation/Canonical.java | 40 ++++- .../org/sqlite/jni/annotation/NotNull.java | 13 +- .../org/sqlite/jni/annotation/Nullable.java | 5 +- .../sqlite/jni/annotation/package-info.java | 4 + .../src/org/sqlite/jni/tester/SQLTester.java | 6 +- manifest | 60 ++++---- manifest.uuid | 2 +- 28 files changed, 342 insertions(+), 115 deletions(-) create mode 100644 ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java create mode 100644 ext/jni/src/org/sqlite/jni/annotation/package-info.java diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index cd5150eb10..3518518520 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -62,6 +62,8 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\ annotation/Canonical.java \ annotation/NotNull.java \ annotation/Nullable.java \ + annotation/package-info.java \ + AbstractCollationCallback.java \ AggregateFunction.java \ AuthorizerCallback.java \ AutoExtensionCallback.java \ @@ -327,7 +329,7 @@ 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 org.sqlite.jni + $(bin.javadoc) -cp $(classpath) -d $(dir.doc) -quiet -subpackages org.sqlite.jni @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 0f18fa5a09..471cc1503d 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -2517,6 +2517,15 @@ S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text_1utf8)( return p ? s3jni_new_jbyteArray(p, n) : NULL; } +S3JniApi(sqlite3_column_text(),jstring,1column_1text)( + JniArgsEnvClass, jobject jpStmt, jint ndx +){ + sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); + const int n = sqlite3_column_bytes(stmt, (int)ndx); + const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); + return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; +} + S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 7c44dfd758..40dd449901 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1043,6 +1043,14 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1table_ JNIEXPORT jbyteArray JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text_1utf8 (JNIEnv *, jclass, jobject, jint); +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_column_text + * Signature: (Lorg/sqlite/jni/sqlite3_stmt;I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1column_1text + (JNIEnv *, jclass, jobject, jint); + /* * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_column_text16 diff --git a/ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java b/ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java new file mode 100644 index 0000000000..63cac66a52 --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/AbstractCollationCallback.java @@ -0,0 +1,34 @@ +/* +** 2023-08-25 +** +** 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; +import org.sqlite.jni.annotation.NotNull; + +/** + An implementation of {@link CollationCallback} which provides a + no-op xDestroy() method. +*/ +public abstract class AbstractCollationCallback + implements CollationCallback, XDestroyCallback { + /** + Must compare the given byte arrays and return the result using + {@code memcmp()} semantics. + */ + public abstract int call(@NotNull byte[] lhs, @NotNull byte[] rhs); + + /** + Optionally override to be notified when the UDF is finalized by + SQLite. This implementation does nothing. + */ + public void xDestroy(){} +} diff --git a/ext/jni/src/org/sqlite/jni/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/AggregateFunction.java index 033e4dbca0..502cde12f8 100644 --- a/ext/jni/src/org/sqlite/jni/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/AggregateFunction.java @@ -15,12 +15,12 @@ package org.sqlite.jni; /** - SQLFunction subclass for creating aggregate functions. Its T is - the data type of its "accumulator" state, an instance of which is + A SQLFunction implementation for aggregate functions. Its T is the + data type of its "accumulator" state, an instance of which is intended to be be managed using the getAggregateState() and takeAggregateState() methods. */ -public abstract class AggregateFunction extends SQLFunction { +public abstract class AggregateFunction implements SQLFunction { /** As for the xStep() argument of the C API's @@ -48,7 +48,7 @@ public abstract class AggregateFunction extends SQLFunction { /** To be called from the implementation's xStep() method, as well - as the xValue() and xInverse() methods of the Window + as the xValue() and xInverse() methods of the {@link WindowFunction} subclass, to fetch the current per-call UDF state. On the first call to this method for any given sqlite3_context argument, the context is set to the given initial value. On all other diff --git a/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java index eef7e5020c..a9f15fc6c2 100644 --- a/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java +++ b/ext/jni/src/org/sqlite/jni/AuthorizerCallback.java @@ -15,14 +15,12 @@ package org.sqlite.jni; import org.sqlite.jni.annotation.*; /** - Callback for use with sqlite3_set_authorizer(). + Callback for use with {@link SQLite3Jni#sqlite3_set_authorizer}. */ public interface AuthorizerCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_set_authorizer() callback. - - Must not throw. */ int call(int opId, @Nullable String s1, @Nullable String s2, @Nullable String s3, @Nullable String s4); diff --git a/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java b/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java index 11786c95e9..1f8ace2fb8 100644 --- a/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java +++ b/ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java @@ -14,28 +14,27 @@ package org.sqlite.jni; /** - A callback for use with the sqlite3_auto_extension() family of - APIs. + Callback for use with the {@link SQLite3Jni#sqlite3_auto_extension} + family of APIs. */ public interface AutoExtensionCallback extends SQLite3CallbackProxy { /** Must function as described for a C-level - sqlite3_auto_extension() callback, with the caveat that the - signature is shorter. + sqlite3_auto_extension() callback. - This callback may throw and the exception's error message will +

This callback may throw and the exception's error message will be set as the db's error string. - Tips for implementations: +

Tips for implementations: - - Opening a database from an auto-extension handler will lead to +

- Opening a database from an auto-extension handler will lead to an endless recursion of the auto-handler triggering itself indirectly for each newly-opened database. - - If this routine is stateful, it may be useful to make the +

- If this routine is stateful, it may be useful to make the overridden method synchronized. - - Results are undefined if db is closed by an auto-extension. +

- Results are undefined if the given db is closed by an auto-extension. */ int call(sqlite3 db); } diff --git a/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java index 77180e2c24..db9295bb61 100644 --- a/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java +++ b/ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java @@ -13,18 +13,14 @@ */ package org.sqlite.jni; - /** - Callback for use with sqlite3_busy_handler() + Callback for use with {@link SQLite3Jni#sqlite3_busy_handler}. */ -public abstract class BusyHandlerCallback implements SQLite3CallbackProxy { +public interface BusyHandlerCallback extends SQLite3CallbackProxy { /** Must function as documented for the C-level sqlite3_busy_handler() callback argument, minus the (void*) argument the C-level function requires. - - Any exceptions thrown by this callback are suppressed in order to - retain the C-style API semantics of the JNI bindings. */ - public abstract int call(int n); + int call(int n); } diff --git a/ext/jni/src/org/sqlite/jni/CollationCallback.java b/ext/jni/src/org/sqlite/jni/CollationCallback.java index 1e4d0396de..481c6cd956 100644 --- a/ext/jni/src/org/sqlite/jni/CollationCallback.java +++ b/ext/jni/src/org/sqlite/jni/CollationCallback.java @@ -12,20 +12,24 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import org.sqlite.jni.annotation.NotNull; /** - Callback for use with sqlite3_create_collation() + Callback for use with {@link SQLite3Jni#sqlite3_create_collation}. + + @see AbstractCollationCallback */ -public abstract class CollationCallback - implements SQLite3CallbackProxy, XDestroyCallback { +public interface CollationCallback + extends SQLite3CallbackProxy, XDestroyCallback { /** - Must compare the given byte arrays using memcmp() semantics. + Must compare the given byte arrays and return the result using + {@code memcmp()} semantics. */ - public abstract int call(byte[] lhs, byte[] rhs); + int call(@NotNull byte[] lhs, @NotNull byte[] rhs); /** Called by SQLite when the collation is destroyed. If a collation requires custom cleanup, override this method. */ - public void xDestroy(){} + void xDestroy(); } diff --git a/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java index a8b17fe48b..e6c917a2c2 100644 --- a/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java +++ b/ext/jni/src/org/sqlite/jni/CollationNeededCallback.java @@ -14,7 +14,7 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_collation_needed(). + Callback for use with {@link SQLite3Jni#sqlite3_collation_needed}. */ public interface CollationNeededCallback extends SQLite3CallbackProxy { /** diff --git a/ext/jni/src/org/sqlite/jni/CommitHookCallback.java b/ext/jni/src/org/sqlite/jni/CommitHookCallback.java index 7757a6ac0d..253d0b8cfa 100644 --- a/ext/jni/src/org/sqlite/jni/CommitHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/CommitHookCallback.java @@ -14,7 +14,7 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_commit_hook() + Callback for use with {@link SQLite3Jni#sqlite3_commit_hook}. */ public interface CommitHookCallback extends SQLite3CallbackProxy { /** diff --git a/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java b/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java index f0b16ae926..b68dd4b6d4 100644 --- a/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_preupdate_hook(). + Callback for use with {@link SQLite3Jni#sqlite3_preupdate_hook}. */ public interface PreupdateHookCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_preupdate_hook() - callback. Must not throw. + callback. */ void call(sqlite3 db, int op, String dbName, String dbTable, long iKey1, long iKey2 ); diff --git a/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java b/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java index 447bba6f38..d15bf31a11 100644 --- a/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java +++ b/ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java @@ -14,13 +14,13 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_progress_handler() + Callback for use with {@link SQLite3Jni#sqlite3_progress_handler}. */ public interface ProgressHandlerCallback extends SQLite3CallbackProxy { /** Works as documented for the C-level sqlite3_progress_handler() callback. - If it throws, the exception message is passed on to the db and +

If it throws, the exception message is passed on to the db and the exception is suppressed. */ int call(); diff --git a/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java b/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java index 7bf3879416..3bf9f79a1a 100644 --- a/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/RollbackHookCallback.java @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_rollback_hook() + Callback for use with {@link SQLite3Jni#sqlite3_rollback_hook}. */ public interface RollbackHookCallback extends SQLite3CallbackProxy { /** Works as documented for the C-level sqlite3_rollback_hook() - callback. Must not throw. + callback. */ void call(); } diff --git a/ext/jni/src/org/sqlite/jni/SQLFunction.java b/ext/jni/src/org/sqlite/jni/SQLFunction.java index badbc3dc78..66119ebe55 100644 --- a/ext/jni/src/org/sqlite/jni/SQLFunction.java +++ b/ext/jni/src/org/sqlite/jni/SQLFunction.java @@ -31,7 +31,7 @@ package org.sqlite.jni; SQLFunction base class and the method names and signatures used by the UDF callback interfaces. */ -public abstract class SQLFunction { +public interface SQLFunction { /** PerContextState assists aggregate and window functions in @@ -53,11 +53,11 @@ public abstract class SQLFunction { state, of a client-defined type (the T part of this class), which persists across a "matching set" of the UDF's callbacks. -

This class is a helper providing commonly-needed functionality - - it is not required for use with aggregate or window functions. +

This class is a helper providing commonly-needed functionality + - it is not required for use with aggregate or window functions. Client UDFs are free to perform such mappings using custom - approaches. The provided Aggregate and Window classes - use this. + approaches. The provided {@link AggregateFunction} and {@link + WindowFunction} classes use this. */ public static final class PerContextState { private final java.util.Map> map diff --git a/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java b/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java index 137cddcc55..5052664937 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java @@ -18,17 +18,27 @@ package org.sqlite.jni; classes which have a call() method implementing some specific callback interface on behalf of the C library. +

Unless very explicitely documented otherwise, callbacks must + never throw. Any which do throw but should not might trigger debug + output regarding the error, but the exception will not be + propagated. For callback interfaces which support returning error + info to the core, the JNI binding will convert any exceptions to + C-level error information. For callback interfaces which do not + support, all exceptions will necessarily be suppressed in order to + retain the C-style no-throw semantics. +

Callbacks of this style follow a common naming convention:

1) They use the UpperCamelCase form of the C function they're - proxying for, minus the sqlite3_ prefix, plus a Callback - suffix. e.g. sqlite3_busy_handler()'s callback is named - BusyHandlerCallback. Exceptions are made where that would - potentially be ambiguous, e.g. ConfigSqllogCallback instead of - config_callback because the sqlite3_config() interface may need to - support more callback types in the future. + proxying for, minus the {@code sqlite3_} prefix, plus a {@code + Callback} suffix. e.g. {@code sqlite3_busy_handler()}'s callback is + named {@code BusyHandlerCallback}. Exceptions are made where that + would potentially be ambiguous, e.g. {@link ConfigSqllogCallback} + instead of {@code ConfigCallback} because the {@code + sqlite3_config()} interface may need to support more callback types + in the future. -

2) They all have a call() method but its signature is +

2) They all have a {@code call()} method but its signature is callback-specific. */ public interface SQLite3CallbackProxy {} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 1e02b41be8..6cad93d74b 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -324,70 +324,87 @@ public final class SQLite3Jni { @NotNull sqlite3 db, int ms ); + @Canonical public static native boolean sqlite3_cancel_auto_extension( @NotNull AutoExtensionCallback ax ); + @Canonical public static native int sqlite3_changes( @NotNull sqlite3 db ); + @Canonical public static native long sqlite3_changes64( @NotNull sqlite3 db ); + @Canonical public static native int sqlite3_clear_bindings( @NotNull sqlite3_stmt stmt ); + @Canonical public static native int sqlite3_close( @Nullable sqlite3 db ); + @Canonical public static native int sqlite3_close_v2( @Nullable sqlite3 db ); + @Canonical public static native byte[] sqlite3_column_blob( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native int sqlite3_column_bytes( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native int sqlite3_column_bytes16( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native int sqlite3_column_count( @NotNull sqlite3_stmt stmt ); + @Canonical public static native double sqlite3_column_double( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native int sqlite3_column_int( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native long sqlite3_column_int64( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native String sqlite3_column_name( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native String sqlite3_column_database_name( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native String sqlite3_column_origin_name( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native String sqlite3_column_table_name( @NotNull sqlite3_stmt stmt, int ndx ); @@ -396,18 +413,25 @@ public final class SQLite3Jni { Returns the given column's contents as UTF-8-encoded (not MUTF-8) text. Returns null if the C-level sqlite3_column_text() returns NULL. + + @see #sqlite3_column_text */ + @Canonical(cname="sqlite3_column_text") public static native byte[] sqlite3_column_text_utf8( @NotNull sqlite3_stmt stmt, int ndx ); - public static String sqlite3_column_text( - @NotNull sqlite3_stmt stmt, int ndx - ){ - final byte[] ba = sqlite3_column_text_utf8(stmt, ndx); - return ba==null ? null : new String(ba, StandardCharsets.UTF_8); - } + /** + Provides the same feature as the same-named C API but returns the + text in Java-native encoding rather than the C API's UTF-8. + @see #sqlite3_column_text16 + */ + public static native String sqlite3_column_text( + @NotNull sqlite3_stmt stmt, int ndx + ); + + @Canonical public static native String sqlite3_column_text16( @NotNull sqlite3_stmt stmt, int ndx ); @@ -449,10 +473,12 @@ public final class SQLite3Jni { // return rv; // } + @Canonical public static native int sqlite3_column_type( @NotNull sqlite3_stmt stmt, int ndx ); + @Canonical public static native sqlite3_value sqlite3_column_value( @NotNull sqlite3_stmt stmt, int ndx ); @@ -461,22 +487,27 @@ public final class SQLite3Jni { This functions like C's sqlite3_collation_needed16() because Java's string type is compatible with that interface. */ + @Canonical public static native int sqlite3_collation_needed( @NotNull sqlite3 db, @Nullable CollationNeededCallback callback ); + @Canonical public static native sqlite3 sqlite3_context_db_handle( @NotNull sqlite3_context cx ); + @Canonical public static native CommitHookCallback sqlite3_commit_hook( @NotNull sqlite3 db, @Nullable CommitHookCallback hook ); + @Canonical public static native String sqlite3_compileoption_get( int n ); + @Canonical public static native boolean sqlite3_compileoption_used( @NotNull String optName ); @@ -497,6 +528,9 @@ public final class SQLite3Jni { library APIs are being called. */ + @Canonical(comment="Option subset: "+ + "SQLITE_CONFIG_SINGLETHREAD, SQLITE_CONFIG_MULTITHREAD, "+ + "SQLITE_CONFIG_SERIALIZED") public static native int sqlite3_config(int op); /** @@ -514,8 +548,10 @@ public final class SQLite3Jni { library APIs are being called. */ + @Canonical(comment="Option subset: SQLITE_CONFIG_SQLLOG") public static native int sqlite3_config( @Nullable ConfigSqllogCallback logger ); + @Canonical public static native int sqlite3_create_collation( @NotNull sqlite3 db, @NotNull String name, int eTextRep, @NotNull CollationCallback col @@ -529,11 +565,13 @@ public final class SQLite3Jni { SQLFunction's subclasses (ScalarFunction, AggregateFunction, and WindowFunction) for details. */ + @Canonical public static native int sqlite3_create_function( @NotNull sqlite3 db, @NotNull String functionName, int nArg, int eTextRep, @NotNull SQLFunction func ); + @Canonical public static native int sqlite3_data_count( @NotNull sqlite3_stmt stmt ); @@ -543,6 +581,7 @@ public final class SQLite3Jni { variadic arguments. Returns SQLITE_MISUSE if op is not one of the SQLITE_DBCONFIG_... options which uses this call form. */ + @Canonical public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, int onOff, @Nullable OutputPointer.Int32 out ); @@ -554,53 +593,71 @@ public final class SQLite3Jni { SQLITE_DBCONFIG_MAINDBNAME, but that set of options may be extended in future versions. */ + @Canonical(comment="Supports only a subset of options.") public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, @NotNull String val ); + @Canonical public static native String sqlite3_db_filename( @NotNull sqlite3 db, @NotNull String dbName ); + @Canonical public static native sqlite3 sqlite3_db_handle( @NotNull sqlite3_stmt stmt ); + @Canonical public static native int sqlite3_db_status( @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset ); + @Canonical public static native int sqlite3_errcode(@NotNull sqlite3 db); + @Canonical public static native String sqlite3_expanded_sql(@NotNull sqlite3_stmt stmt); + @Canonical public static native int sqlite3_extended_errcode(@NotNull sqlite3 db); + @Canonical public static native boolean sqlite3_extended_result_codes( @NotNull sqlite3 db, boolean onoff ); + @Canonical public static native String sqlite3_errmsg(@NotNull sqlite3 db); + @Canonical public static native String sqlite3_errstr(int resultCode); /** Note that the returned byte offset values assume UTF-8-encoded inputs, so won't always match character offsets in Java Strings. */ + @Canonical public static native int sqlite3_error_offset(@NotNull sqlite3 db); + @Canonical public static native int sqlite3_finalize(@NotNull sqlite3_stmt stmt); + @Canonical public static native int sqlite3_initialize(); + @Canonical public static native void sqlite3_interrupt(@NotNull sqlite3 db); + @Canonical public static native boolean sqlite3_is_interrupted(@NotNull sqlite3 db); + @Canonical public static native long sqlite3_last_insert_rowid(@NotNull sqlite3 db); + @Canonical public static native String sqlite3_libversion(); + @Canonical public static native int sqlite3_libversion_number(); /** @@ -616,6 +673,7 @@ public final class SQLite3Jni { object and it is up to the caller to sqlite3_close() that db handle. */ + @Canonical public static native int sqlite3_open( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb ); @@ -634,6 +692,7 @@ public final class SQLite3Jni { return out.take(); }; + @Canonical public static native int sqlite3_open_v2( @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, int flags, @Nullable String zVfs @@ -670,6 +729,7 @@ public final class SQLite3Jni { necessary, however, and overloads are provided which gloss over that. */ + @Canonical private static native int sqlite3_prepare( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull OutputPointer.sqlite3_stmt outStmt, @@ -719,6 +779,7 @@ public final class SQLite3Jni { See sqlite3_prepare() for details about the slight API differences from the C API. */ + @Canonical private static native int sqlite3_prepare_v2( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull OutputPointer.sqlite3_stmt outStmt, @@ -760,6 +821,7 @@ public final class SQLite3Jni { return out.take(); } + @Canonical private static native int sqlite3_prepare_v3( @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt, @@ -806,18 +868,21 @@ public final class SQLite3Jni { acts as a proxy for C's sqlite3_preupdate_blobwrite(), else it returns SQLITE_MISUSE with no side effects. */ + @Canonical 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. */ + @Canonical 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. */ + @Canonical public static native int sqlite3_preupdate_depth(@NotNull sqlite3 db); /** @@ -825,6 +890,7 @@ public final class SQLite3Jni { acts as a proxy for C's sqlite3_preupdate_hook(), else it returns null with no side effects. */ + @Canonical public static native PreupdateHookCallback sqlite3_preupdate_hook( @NotNull sqlite3 db, @Nullable PreupdateHookCallback hook ); @@ -834,6 +900,7 @@ public final class SQLite3Jni { this acts as a proxy for C's sqlite3_preupdate_new(), else it returns SQLITE_MISUSE with no side effects. */ + @Canonical public static native int sqlite3_preupdate_new(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out); @@ -852,6 +919,7 @@ public final class SQLite3Jni { this acts as a proxy for C's sqlite3_preupdate_old(), else it returns SQLITE_MISUSE with no side effects. */ + @Canonical public static native int sqlite3_preupdate_old(@NotNull sqlite3 db, int col, @NotNull OutputPointer.sqlite3_value out); @@ -865,10 +933,12 @@ public final class SQLite3Jni { return out.take(); } + @Canonical public static native void sqlite3_progress_handler( @NotNull sqlite3 db, int n, @Nullable ProgressHandlerCallback h ); + @Canonical public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt); /** @@ -876,8 +946,10 @@ public final class SQLite3Jni { extensions are currently running. (The JNI-level list of extensions cannot be manipulated while it is being traversed.) */ + @Canonical public static native void sqlite3_reset_auto_extension(); + @Canonical public static native void sqlite3_result_double( @NotNull sqlite3_context cx, double v ); @@ -889,6 +961,7 @@ public final class SQLite3Jni { results in the C-level sqlite3_result_error() being called with a complaint about the invalid argument. */ + @Canonical private static native void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); @@ -929,26 +1002,32 @@ public final class SQLite3Jni { sqlite3_result_error(cx, e.getMessage()); } + @Canonical public static native void sqlite3_result_error_toobig( @NotNull sqlite3_context cx ); + @Canonical public static native void sqlite3_result_error_nomem( @NotNull sqlite3_context cx ); + @Canonical public static native void sqlite3_result_error_code( @NotNull sqlite3_context cx, int c ); + @Canonical public static native void sqlite3_result_null( @NotNull sqlite3_context cx ); + @Canonical public static native void sqlite3_result_int( @NotNull sqlite3_context cx, int v ); + @Canonical public static native void sqlite3_result_int64( @NotNull sqlite3_context cx, long v ); @@ -1031,18 +1110,22 @@ public final class SQLite3Jni { sqlite3_result_text(cx, v); } + @Canonical public static native void sqlite3_result_value( @NotNull sqlite3_context cx, @NotNull sqlite3_value v ); + @Canonical public static native void sqlite3_result_zeroblob( @NotNull sqlite3_context cx, int n ); + @Canonical public static native int sqlite3_result_zeroblob64( @NotNull sqlite3_context cx, long n ); + @Canonical private static native void sqlite3_result_blob( @NotNull sqlite3_context cx, @Nullable byte[] blob, int maxLen ); @@ -1068,6 +1151,7 @@ public final class SQLite3Jni { If @param maxLen is larger than blob.length, it is truncated to that value. If it is negative, results are undefined. */ + @Canonical private static native void sqlite3_result_blob64( @NotNull sqlite3_context cx, @Nullable byte[] blob, long maxLen ); @@ -1078,6 +1162,7 @@ public final class SQLite3Jni { sqlite3_result_blob64(cx, blob, (long)(null==blob ? 0 : blob.length)); } + @Canonical private static native void sqlite3_result_text( @NotNull sqlite3_context cx, @Nullable byte[] utf8, int maxLen ); @@ -1118,6 +1203,7 @@ public final class SQLite3Jni { negative, results are undefined. If text is null, the subsequent arguments are ignored. */ + @Canonical private static native void sqlite3_result_text64( @NotNull sqlite3_context cx, @Nullable byte[] text, long maxLength, int encoding @@ -1143,15 +1229,17 @@ public final class SQLite3Jni { } } + @Canonical public static native RollbackHookCallback sqlite3_rollback_hook( @NotNull sqlite3 db, @Nullable RollbackHookCallback hook ); - //! Sets or unsets (if auth is null) the current authorizer. + @Canonical public static native int sqlite3_set_authorizer( @NotNull sqlite3 db, @Nullable AuthorizerCallback auth ); + @Canonical public static native void sqlite3_set_last_insert_rowid( @NotNull sqlite3 db, long rowid ); @@ -1166,31 +1254,39 @@ public final class SQLite3Jni { to use those objects after this routine is called invoked undefined behavior. */ + @Canonical public static synchronized native int sqlite3_shutdown(); + @Canonical public static native int sqlite3_sleep(int ms); + @Canonical public static native String sqlite3_sourceid(); + @Canonical public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt); + @Canonical public static native int sqlite3_status( int op, @NotNull OutputPointer.Int32 pCurrent, @NotNull OutputPointer.Int32 pHighwater, boolean reset ); + @Canonical public static native int sqlite3_status64( int op, @NotNull OutputPointer.Int64 pCurrent, @NotNull OutputPointer.Int64 pHighwater, boolean reset ); + @Canonical public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); /** Internal impl of the public sqlite3_strglob() method. Neither argument may be NULL and both MUST be NUL-terminated UTF-8. */ + @Canonical private static native int sqlite3_strglob( @NotNull byte[] glob, @NotNull byte[] txt ); @@ -1208,6 +1304,7 @@ public final class SQLite3Jni { Internal impl of the public sqlite3_strlike() method. Neither argument may be NULL and both MUST be NUL-terminated UTF-8. */ + @Canonical private static native int sqlite3_strlike( @NotNull byte[] glob, @NotNull byte[] txt, int escChar ); @@ -1222,10 +1319,13 @@ public final class SQLite3Jni { ); } + @Canonical public static native int sqlite3_threadsafe(); + @Canonical public static native int sqlite3_total_changes(@NotNull sqlite3 db); + @Canonical public static native long sqlite3_total_changes64(@NotNull sqlite3 db); /** @@ -1237,32 +1337,43 @@ public final class SQLite3Jni { implementation returns non-0 if initialization of the tracer mapping state fails. */ + @Canonical public static native int sqlite3_trace_v2( @NotNull sqlite3 db, int traceMask, @Nullable TraceV2Callback tracer ); + @Canonical public static native UpdateHookCallback sqlite3_update_hook( sqlite3 db, UpdateHookCallback hook ); + @Canonical public static native byte[] sqlite3_value_blob(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_bytes(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_bytes16(@NotNull sqlite3_value v); + @Canonical public static native double sqlite3_value_double(@NotNull sqlite3_value v); + @Canonical public static native sqlite3_value sqlite3_value_dup( @NotNull sqlite3_value v ); + @Canonical public static native int sqlite3_value_encoding(@NotNull sqlite3_value v); + @Canonical public static native void sqlite3_value_free(@Nullable sqlite3_value v); + @Canonical public static native int sqlite3_value_int(@NotNull sqlite3_value v); + @Canonical public static native long sqlite3_value_int64(@NotNull sqlite3_value v); /** @@ -1293,8 +1404,17 @@ public final class SQLite3Jni { Returns the given value as UTF-8-encoded bytes, or null if the underlying C-level sqlite3_value_text() returns NULL. */ + @Canonical(cname="sqlite3_value_text", + comment="Renamed because its String-returning overload would "+ + "otherwise be ambiguous.") public static native byte[] sqlite3_value_text_utf8(@NotNull sqlite3_value v); + /** + Provides the same feature as the same-named C API but returns the + text in Java-native encoding rather than the C API's UTF-8. + + @see #sqlite3_value_text16 + */ public static native String sqlite3_value_text(@NotNull sqlite3_value v); /** @@ -1305,16 +1425,22 @@ public final class SQLite3Jni { sqlite3_value_text() fetches UTF-8 (SQLite's default encoding) and converts it to UTF-16 in Java. */ + @Canonical public static native String sqlite3_value_text16(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_type(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_numeric_type(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_nochange(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_frombind(@NotNull sqlite3_value v); + @Canonical public static native int sqlite3_value_subtype(@NotNull sqlite3_value v); /** diff --git a/ext/jni/src/org/sqlite/jni/ScalarFunction.java b/ext/jni/src/org/sqlite/jni/ScalarFunction.java index 9d75dbdb76..73fb58cda2 100644 --- a/ext/jni/src/org/sqlite/jni/ScalarFunction.java +++ b/ext/jni/src/org/sqlite/jni/ScalarFunction.java @@ -15,9 +15,9 @@ package org.sqlite.jni; /** - SQLFunction subclass for creating scalar functions. + A SQLFunction implementation for scalar functions. */ -public abstract class ScalarFunction extends SQLFunction { +public abstract class ScalarFunction implements SQLFunction { /** As for the xFunc() argument of the C API's sqlite3_create_function(). If this function throws, it is @@ -27,7 +27,7 @@ public abstract class ScalarFunction extends SQLFunction { /** Optionally override to be notified when the UDF is finalized by - SQLite. + SQLite. This implementation does nothing. */ public void xDestroy() {} } diff --git a/ext/jni/src/org/sqlite/jni/TraceV2Callback.java b/ext/jni/src/org/sqlite/jni/TraceV2Callback.java index 453cf948db..897aeefa9f 100644 --- a/ext/jni/src/org/sqlite/jni/TraceV2Callback.java +++ b/ext/jni/src/org/sqlite/jni/TraceV2Callback.java @@ -12,9 +12,10 @@ ** This file is part of the JNI bindings for the sqlite3 C API. */ package org.sqlite.jni; +import org.sqlite.jni.annotation.Nullable; /** - Callback proxy for use with sqlite3_trace_v2(). + Callback for use with {@link SQLite3Jni#sqlite3_trace_v2}. */ public interface TraceV2Callback extends SQLite3CallbackProxy { /** @@ -23,7 +24,7 @@ public interface TraceV2Callback extends SQLite3CallbackProxy { argument to the native trace callback, as that role is better filled by instance-local state. - These callbacks may throw, in which case their exceptions are +

These callbacks may throw, in which case their exceptions are converted to C-level error information.

The 2nd argument to this function, if non-null, will be a an @@ -45,5 +46,5 @@ public interface TraceV2Callback extends SQLite3CallbackProxy {

- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null. */ - int call(int traceFlag, Object pNative, Object pX); + int call(int traceFlag, Object pNative, @Nullable Object pX); } diff --git a/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java b/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java index 17c49ba27c..4fd0a63240 100644 --- a/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java +++ b/ext/jni/src/org/sqlite/jni/UpdateHookCallback.java @@ -14,12 +14,12 @@ package org.sqlite.jni; /** - Callback for use with sqlite3_update_hook(). + Callback for use with {@link SQLite3Jni#sqlite3_update_hook}. */ public interface UpdateHookCallback extends SQLite3CallbackProxy { /** Must function as described for the C-level sqlite3_update_hook() - callback. Must not throw. + callback. */ void call(int opId, String dbName, String tableName, long rowId); } diff --git a/ext/jni/src/org/sqlite/jni/WindowFunction.java b/ext/jni/src/org/sqlite/jni/WindowFunction.java index c083312f24..7f70177ac0 100644 --- a/ext/jni/src/org/sqlite/jni/WindowFunction.java +++ b/ext/jni/src/org/sqlite/jni/WindowFunction.java @@ -15,11 +15,11 @@ package org.sqlite.jni; /** - An SQLFunction subclass for creating window functions. Note that - Window inherits from Aggregate and each instance is - required to implement the inherited abstract methods from that - class. See Aggregate for information on managing the UDF's - invocation-specific state. + A SQLFunction implementation for window functions. Note that + WindowFunction inherits from {@link AggregateFunction} and each + instance is required to implement the inherited abstract methods + from that class. See {@link AggregateFunction} for information on + managing the UDF's invocation-specific state. */ public abstract class WindowFunction extends AggregateFunction { diff --git a/ext/jni/src/org/sqlite/jni/annotation/Canonical.java b/ext/jni/src/org/sqlite/jni/annotation/Canonical.java index 260fb2df5f..f329aee137 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/Canonical.java +++ b/ext/jni/src/org/sqlite/jni/annotation/Canonical.java @@ -2,15 +2,43 @@ package org.sqlite.jni.annotation; /** This annotation is for marking functions as "canonical", meaning - that they exist in the C API. The intent is to distinguish them - from functions added specifically to the Java API. + that they map directly to a function in the core sqlite3 C API. The + intent is to distinguish them from functions added specifically to + the Java API.

Canonical functions, unless specifically documented, have the - same semantics as their counterparts in @{link - https://sqlite.org/c3ref/intro.html the C API documentation}, despite - their signatures perhaps differing. + same semantics as their counterparts in + the C API documentation, + despite their signatures perhaps differing. The Java API adds a + number of overloads to simplify use, as well as a few Java-specific + functions, and those are never flagged as @Canonical. + +

In some cases, the canonical version of a function is private + and exposed to Java via public overloads. + +

In rare cases, the Java interface for a canonical function has a + different name than its C counterpart. For such cases, + (cname=the-C-side-name) is passed to this annotation and a + Java-side implementation with a slightly different signature is + added to with the canonical name. As of this writing, that applies + only to {@link org.sqlite.jni.SQLite3Jni#sqlite3_value_text_utf8} + and {@link org.sqlite.jni.SQLite3Jni#sqlite3_column_text_utf8}. + +

The comment property can be used to add a comment. */ @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) -public @interface Canonical{} +public @interface Canonical{ + /** + Java functions which directly map to a canonical function but + change its name for some reason should not the original name + in this property. + */ + String cname() default ""/*doesn't allow null*/; + /** + Brief comments about the binding, e.g. noting any major + semantic differences. + */ + String comment() default ""; +} diff --git a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java index 58431e0d95..99eae7370a 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/NotNull.java +++ b/ext/jni/src/org/sqlite/jni/annotation/NotNull.java @@ -2,11 +2,14 @@ package org.sqlite.jni.annotation; /** This annotation is for flagging parameters which may not legally be - null. Note that the C-style API does not throw any exceptions on - its own because it has a no-throw policy in order to retain its - C-style semantics, but it may trigger NullPointerExceptions (or - similar) if passed a null for a parameter flagged with this - annotation. + null. When used in the context of callback methods which are + called into from the C APIs, this annotation communicates that the + C API will never pass a null value to the callback. + +

Note that the C-style API does not throw any exceptions on its + own because it has a no-throw policy in order to retain its C-style + semantics, but it may trigger NullPointerExceptions (or similar) if + passed a null for a parameter flagged with this annotation.

This annotation is informational only. No policy is in place to programmatically ensure that NotNull is conformed to in client diff --git a/ext/jni/src/org/sqlite/jni/annotation/Nullable.java b/ext/jni/src/org/sqlite/jni/annotation/Nullable.java index 187ab263b0..7a011e33b1 100644 --- a/ext/jni/src/org/sqlite/jni/annotation/Nullable.java +++ b/ext/jni/src/org/sqlite/jni/annotation/Nullable.java @@ -3,7 +3,10 @@ package org.sqlite.jni.annotation; /** This annotation is for flagging parameters which may legally be null, noting that they may behave differently if passed null but - are prepared to expect null as a value. + are prepared to expect null as a value. When used in the context of + callback methods which are called into from the C APIs, this + annotation communicates that the C API may pass a null value to the + callback.

This annotation is solely for the use by the classes in this package but is made public so that javadoc will link to it from the diff --git a/ext/jni/src/org/sqlite/jni/annotation/package-info.java b/ext/jni/src/org/sqlite/jni/annotation/package-info.java new file mode 100644 index 0000000000..50db2a32bd --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/annotation/package-info.java @@ -0,0 +1,4 @@ +/** + This package houses annotations specific a JNI binding to the SQLite3 C API. +*/ +package org.sqlite.jni.annotation; diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index d8b84af795..e355fe7603 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -152,12 +152,12 @@ class Outer { /** This class provides an application which aims to implement the rudimentary SQL-driven test tool described in the accompanying - test-script-interpreter.md. + {@code test-script-interpreter.md}. - This is a work in progress. +

This is a work in progress. - An instance of this application provides a core set of services +

An instance of this application provides a core set of services which TestScript instances use for processing testing logic. TestScripts, in turn, delegate the concrete test work to Command objects, which the TestScript parses on their behalf. diff --git a/manifest b/manifest index 47601b99ac..3b290f812b 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sa\spackage\srenaming\sbug\sin\sthe\sprevious\scheck-in\sand\sstart\sapplying\sthe\s@Canonical\sannotation\sto\sfunctions\sto\sdistinguish\scanonical\sAPI\sfunctions\sand\sJava-specific\sAPIs. -D 2023-08-28T05:48:34.397 +C Lots\sof\sjavadoc-related\stweaks. +D 2023-08-28T07:28:36.868 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -233,18 +233,19 @@ 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 e582f0e36b80c9588d52f520bf07c37ddbd63199029a97de0925014e73127972 +F ext/jni/GNUmakefile 8bb7f82029eb7d6182f4af9c42f99abaf4cf476984f5aebd6dcf05d85da340c5 F ext/jni/README.md 1332b1fa27918bd5d9ca2d0d4f3ac3a6ab86b9e3699dc5bfe32904a027f3d2a9 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa -F ext/jni/src/c/sqlite3-jni.c 5d43a4a29d16f6a593091eb60371e166d56d0c019cb75a6b741bc41e3b946344 -F ext/jni/src/c/sqlite3-jni.h a43832a04347433e640f1b135f3a8b2a4293369a963659216c51d04daf934b43 -F ext/jni/src/org/sqlite/jni/AggregateFunction.java 0a5a74bea5ee12a99407e9432d0ca393525af912c2b0ca55c7ee5dbd019c00ef -F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java 91442d9871423d01cb1750d63ccf991b3dadcf0a4efaf1d5bc49696469aeae2f -F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 4290d8b0937b07d466b50e6ca4136cec037f3ce658277af0d0c2d371e5f4b459 -F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java efef1892e404f5780d81c46a7997cab874aff5db5131985dd3af319fc5e3adc7 -F ext/jni/src/org/sqlite/jni/CollationCallback.java 4391351e10f26ca61e9c461f969c12f36e0146e50a8c5b66ff549142bbf41f64 -F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java b2adbe0cb41b67bcb638885e00950abe0265e885326a96451f6ab114fb11ef59 -F ext/jni/src/org/sqlite/jni/CommitHookCallback.java c2b4deec20acf9c72ab487ba1a408c54cb5cc12c45baa46490b555b80bd3579f +F ext/jni/src/c/sqlite3-jni.c 064d3a14650221582a1d1621bd1109efb2a455f556387613cd583731eac28674 +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 +F ext/jni/src/org/sqlite/jni/AuthorizerCallback.java d00a2409ab76cae168927e2ca6a7ffbd0621a42547cce88768b4eeebc13827e0 +F ext/jni/src/org/sqlite/jni/AutoExtensionCallback.java 1470e14d09f10729d35568506c6e61318edfb17aa322802e386764fa6d582f14 +F ext/jni/src/org/sqlite/jni/BusyHandlerCallback.java cd12c26dafd3e6c097fc73f80d328aebac0f58b985f66a96ee567ddf8d195f30 +F ext/jni/src/org/sqlite/jni/CollationCallback.java 7d5b246f1a7c9d6b8e974d970bbbb2d05c6264e65448d7be6a85edbf703c823d +F ext/jni/src/org/sqlite/jni/CollationNeededCallback.java 1707b50146c6b805b79e84f89a57c8dbb0134e431799f041f0bec403eca5f841 +F ext/jni/src/org/sqlite/jni/CommitHookCallback.java e4de82c97560982e996e358958268e1e4e307b6115cd9aac0ff4f947d4380d90 F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4 F ext/jni/src/org/sqlite/jni/Fts5.java 3ebfbd5b95fdb9d7bc40306f2e682abd12e247d9224e92510b8dd103b4f96fe8 F ext/jni/src/org/sqlite/jni/Fts5Context.java 0a5a02047a6a1dd3e4a38b0e542a8dd2de365033ba30e6ae019a676305959890 @@ -254,24 +255,25 @@ F ext/jni/src/org/sqlite/jni/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d F ext/jni/src/org/sqlite/jni/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3 -F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 8925c58797a90caeb4a7728a964227db3ba6f953cc89b8be38a5ae6fea063818 -F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7c46660c6b07a765a3f053ae06a10d7ccb4966b49979143d605a3bfb4f14f806 +F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java 500c968b3893edbddf67e8eb773852c3a8ae58097a77bd22320ada6b1af06db1 +F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 0da841810319f5a9dc372d0f2348930d54fac1a4b53e4298884f44c720d67830 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86 -F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java be7f7a26d1102fb514d835e11198d51302af8053d97188bfb2e34c2133208568 -F ext/jni/src/org/sqlite/jni/SQLFunction.java d060f302b2cc4cf7a4f5a6b2d36458a2e6fc9648374b5d09c36a43665af41207 -F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java 13c4ea6f35871261eba63fa4117715515e0beecbdebfb879ec5b1f340ed36904 -F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 953288346d99520bbc9ec94a28ec3a47262ef0c81fc7b11d82f8ba5143589518 -F ext/jni/src/org/sqlite/jni/ScalarFunction.java 21301a947e49f0dd9c682dfe2cc8a6518226c837253dd791cd512f847eeca52c +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/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c F ext/jni/src/org/sqlite/jni/Tester1.java 8653c7b0b50116cf9bd8bf19b83b3e76896b75df09f5debe57a70c556d90203b F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 -F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 25a45e800b0c57f506c237d111bcfd09da584e936fee395d4bd802100ebeff8c -F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java f5eadfa44462c050658230884b41477274f34306accd85c8201a7afbc00d2429 +F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246 +F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee -F ext/jni/src/org/sqlite/jni/WindowFunction.java 3e24a0f2615f9a232b1ecbb3f243b05dd7c007dc43be238499af93a459fe8253 +F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246 F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471 -F ext/jni/src/org/sqlite/jni/annotation/Canonical.java 87e59ab0ee5e454bc8b6a6e659996b2141041dd46e71638b42baf3360a990126 -F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 3a105502c56c0e4f1a826122db356be713cda975b3c17877d0805e45652f5937 -F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 38a2cf9b780484ca7950b76445f65133463afba6d593fd8d4a381a5937135b1c +F ext/jni/src/org/sqlite/jni/annotation/Canonical.java e55b82c8259b617ff754ac493fd8b79602631d659b87a858b987540e4c4fdf56 +F ext/jni/src/org/sqlite/jni/annotation/NotNull.java d48ebd7ae6bbb78bd47d54431c85e1521c89b1d3864a2b6eafd9c0e1b2341457 +F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 6f962a98c9a5c6e9d21c50ae8716b16bdfdc934a191608cbb7e12ea588ddb6af +F ext/jni/src/org/sqlite/jni/annotation/package-info.java f66bfb621c6494e67c03ed38a9e26a3bd6af99b9f9f6ef79556bcec30a025a22 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 @@ -280,7 +282,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java 439d28ac92570e4d16a5361423656f3eae6916c6d4777afde490f77dbb3ec1bc +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bf350903abe04a9bed2d8a2a71692ed4291dbb4eece2d3329ed91d15b0321e6d F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -2104,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 e4dedf90a92a069daef967dfe975469bf8ec7883c44c95e73345d4eded48e996 -R 2d3e956453c21ac2865e49e96e7183c2 +P 8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf +R 9e09f148c76497a018ec611f2deafbbd U stephan -Z 2de3d392080a8d73c4c451d28e259756 +Z cceffc0e8c2aa755651a9db7f05913c5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index da3d87c080..ce51adc406 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8a016006805b08b72bfc4093c795d8cd8d7fe72f8ae234c175a8b7be3a841cbf \ No newline at end of file +cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd \ No newline at end of file From f0e9e59d8d3786db5347ad285b73a77e4d6c8bb1 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 11:10:13 +0000 Subject: [PATCH 092/422] Further JNI cleanups and javadoc additions. FossilOrigin-Name: c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 --- ext/jni/GNUmakefile | 3 +- ext/jni/src/c/sqlite3-jni.c | 188 +++++++++++++------ ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 140 +++++++++----- ext/jni/src/org/sqlite/jni/package-info.java | 6 + manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 236 insertions(+), 121 deletions(-) diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 3518518520..44a0d74011 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 471cc1503d..00138ba400 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 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 6cad93d74b..d32320b26f 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.*;

https://sqlite.org/c3ref/intro.html -

A handful of Java-specific APIs have been added which are documented - here. +

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.

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.

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. + +

Results are undefined if maxBytes>=sqlUtf8.length. + +

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. + +

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.

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).

Note that there is no sqlite3_column_java_object(), as the C-level API has no sqlite3_column_pointer() to proxy. @@ -1056,18 +1088,6 @@ public final class SQLite3Jni { @NotNull sqlite3_context cx, @NotNull Object o ); - public static void sqlite3_result_set( - @NotNull sqlite3_context cx, @NotNull Integer 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, @NotNull Boolean v ){ @@ -1080,18 +1100,6 @@ public final class SQLite3Jni { sqlite3_result_int(cx, v ? 1 : 0); } - public static void sqlite3_result_set( - @NotNull sqlite3_context cx, @NotNull Long v - ){ - sqlite3_result_int64(cx, v); - } - - public static void sqlite3_result_set( - @NotNull sqlite3_context cx, long v - ){ - sqlite3_result_int64(cx, v); - } - public static void sqlite3_result_set( @NotNull sqlite3_context cx, @NotNull Double v ){ @@ -1104,10 +1112,40 @@ public final class SQLite3Jni { sqlite3_result_double(cx, v); } + public static void sqlite3_result_set( + @NotNull sqlite3_context cx, @NotNull Integer 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, @NotNull Long v + ){ + sqlite3_result_int64(cx, v); + } + + public static void sqlite3_result_set( + @NotNull sqlite3_context cx, long v + ){ + sqlite3_result_int64(cx, v); + } + public static void sqlite3_result_set( @NotNull sqlite3_context cx, @Nullable String v ){ - sqlite3_result_text(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 byte[] blob + ){ + 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 ed9086c4c2..2ca997955a 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 @@

The primary interfaces are in {@link org.sqlite.jni.SQLite3Jni}. +

State of this API

+ +

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. +

Threading Considerations

This API is, if built with SQLITE_THREADSAFE set to 1 or 2, @@ -39,5 +44,6 @@

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 3b290f812b..48f883fc48 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 ce51adc406..efd91396c6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd \ No newline at end of file +c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 \ No newline at end of file From f98612b1c5a5f42a1bf9aa6f7d45c3e481ab8eed Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 28 Aug 2023 11:22:33 +0000 Subject: [PATCH 093/422] Fix the valgrind permutation on this branch. FossilOrigin-Name: ccbf06760e8377f9209ba3fd4895a25fe1a5ea688c384bf357e765b6a2b4c2f7 --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/testrunner.tcl | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index afcba707c7..d2716b35cf 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Updates\sto\stestrunner.tcl\sso\sthat\sit\sruns\s"make\sfuzztest"\susing\smultiple\sjobs. -D 2023-08-26T21:04:54.521 +C Fix\sthe\svalgrind\spermutation\son\sthis\sbranch. +D 2023-08-28T11:22:33.500 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1615,7 +1615,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 1a1a000d486b3f0dfb6c78b425c64bf400fa0c75d1058e7b3ce6e23047f5cdaa +F test/testrunner.tcl 80b2145f2b12b87dada28b7bbbd6d7fa4d977fd9edae9c8a467beaa9c39ffee3 F test/testrunner_data.tcl fdcc95d995fd1ef8bbaac1bc105988016213037038161bb555100439793ada18 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2104,8 +2104,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 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535 -R 386688df896b64f3b42ccef0d10cea60 +P 7596ea7074e0ac73312586ed3d28cdacf97f54b8af73f804cbc8066c94d4b4ef +R 79b21140462787dad9fdf55bfe6215a6 U dan -Z 8aa57743124b4e556d29bb22c82a5c1a +Z 5201645988ec9c127b26b5c606008acf # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 642b289373..7b9c91a1e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7596ea7074e0ac73312586ed3d28cdacf97f54b8af73f804cbc8066c94d4b4ef \ No newline at end of file +ccbf06760e8377f9209ba3fd4895a25fe1a5ea688c384bf357e765b6a2b4c2f7 \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index a1d14a7544..92e7ff1bcc 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -594,6 +594,9 @@ proc add_tcl_jobs {build config patternlist} { } else { set testfixture [file join [lindex $build 1] $TRG(testfixture)] } + if {[lindex $build 2]=="Valgrind"} { + set testfixture "valgrind -v --error-exitcode=1 $testfixture" + } # The ::testspec array is populated by permutations.test foreach f [dict get $::testspec($config) -files] { From 0d066bc8a626eb9a94ea289a78df7f825b9510d9 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 12:06:38 +0000 Subject: [PATCH 094/422] Resolve a condition which could cause an is-interrupted db to call its progress callback. FossilOrigin-Name: a0d0f1aafc6086726131dff5e6628f2771c20db3122a53bdbb82945ab5d326d1 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/util.c | 14 +++++++++----- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index 48f883fc48..68551f75a7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\sJNI\scleanups\sand\sjavadoc\sadditions. -D 2023-08-28T11:10:13.491 +C Resolve\sa\scondition\swhich\scould\scause\san\sis-interrupted\sdb\sto\scall\sits\sprogress\scallback. +D 2023-08-28T12:06:38.596 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -766,7 +766,7 @@ F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0 F src/update.c 6904814dd62a7a93bbb86d9f1419c7f134a9119582645854ab02b36b676d9f92 F src/upsert.c fa125a8d3410ce9a97b02cb50f7ae68a2476c405c76aa692d3acf6b8586e9242 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 -F src/util.c 278b81c3b33db1b5a5f3859adf8905c165b910080043061d44d3c5a25b4b406d +F src/util.c 81f6d47ecda50b87e87f86d0bf87aac213698b3eec0d95d4cbaea971794e2e25 F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104 F src/vdbe.c 346d848a0bf8128e3e3722c5406f4bde6c32d7093b93402c6f8e0718d19305c3 F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0 @@ -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 cfe06f90e2c0231efded98767ef3cc646d3a7daa34d77b62b7c04b5aae9448fd -R d8657bd6950439669ea385ca4259469a +P c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 +R 8d931a50e177b7d6e53f54c42fcaa391 U stephan -Z 33c0cfaa6b6aa55ee1261a4aa78cec86 +Z bab39be70eb99e3f7bbba492a0a8edda # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index efd91396c6..f47ed03299 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 \ No newline at end of file +a0d0f1aafc6086726131dff5e6628f2771c20db3122a53bdbb82945ab5d326d1 \ No newline at end of file diff --git a/src/util.c b/src/util.c index 97deb64cfd..7f8a33f9cc 100644 --- a/src/util.c +++ b/src/util.c @@ -202,12 +202,16 @@ void sqlite3ProgressCheck(Parse *p){ p->rc = SQLITE_INTERRUPT; } #ifndef SQLITE_OMIT_PROGRESS_CALLBACK - if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){ - if( db->xProgress(db->pProgressArg) ){ - p->nErr++; - p->rc = SQLITE_INTERRUPT; + if( db->xProgress ){ + if( p->rc==SQLITE_INTERRUPT ){ + p->nProgressSteps = 0; + }else if( (++p->nProgressSteps)>=db->nProgressOps ){ + if( db->xProgress(db->pProgressArg) ){ + p->nErr++; + p->rc = SQLITE_INTERRUPT; + } + p->nProgressSteps = 0; } - p->nProgressSteps = 0; } #endif } From bd953dfcadb9fe5eb3631ef131f7a250b063fa9c Mon Sep 17 00:00:00 2001 From: drh <> Date: Mon, 28 Aug 2023 12:20:18 +0000 Subject: [PATCH 095/422] Fix an issue with infinity handling by the SUM() function that goes back to the extended-precision SUM() enhancement of [check-in c63e26e705f5e967]. Problem reported by [forum:/forumpost/1c06ddcacc86032a|forum post 1c06ddcacc86032a]. FossilOrigin-Name: 77d3dcd283595c52f24c07fc59ba60c9133b71c440cf3f799cf48c907c6fae3e --- manifest | 16 ++++++++-------- manifest.uuid | 2 +- src/func.c | 4 +++- test/func.test | 8 ++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/manifest b/manifest index 68551f75a7..1878e12f38 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Resolve\sa\scondition\swhich\scould\scause\san\sis-interrupted\sdb\sto\scall\sits\sprogress\scallback. -D 2023-08-28T12:06:38.596 +C Fix\san\sissue\swith\sinfinity\shandling\sby\sthe\sSUM()\sfunction\sthat\sgoes\sback\nto\sthe\sextended-precision\sSUM()\senhancement\sof\n[check-in\sc63e26e705f5e967].\s\sProblem\sreported\sby\n[forum:/forumpost/1c06ddcacc86032a|forum\spost\s1c06ddcacc86032a]. +D 2023-08-28T12:20:18.225 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -651,7 +651,7 @@ F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 F src/expr.c 1affe0cc049683ef0ef3545d9b6901508556b0ef7e2892a344c3df6d7288d79d F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c dd1ecd1be6aaa67c9fa723f841e05e1536314c6aaa0509e25289b310f64dbb9c +F src/func.c f480d46974ecc84fefdd429377158981b974e0e33d656f1b0e919ba7c4bdd390 F src/global.c 29f56a330ed9d1b5cd9b79ac0ca36f97ac3afc730ff8bfa987b0db9e559d684d F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -1170,7 +1170,7 @@ F test/fts4umlaut.test fcaca4471de7e78c9d1f7e8976e3e8704d7d8ad979d57a739d00f3f75 F test/fts4unicode.test 82a9c16b68ba2f358a856226bb2ee02f81583797bc4744061c54401bf1a0f4c9 F test/fts4upfrom.test f25835162c989dffd5e2ef91ec24c4848cc9973093e2d492d1c7b32afac1b49d F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d -F test/func.test 2acd982669b2c15ceccc046385b458e985f90c9ca58b14d0ef75eec79c32f45b +F test/func.test 719076723b13a6bfcff7b00b7987b1d7b06876ceb71e868b3d13d571d9b42cae F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f F test/func3.test 600a632c305a88f3946d38f9a51efe145c989b2e13bd2b2a488db47fe76bab6a F test/func4.test 2285fb5792d593fef442358763f0fd9de806eda47dbc7a5934df57ffdc484c31 @@ -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 c393b1c9f5639c259406e587b3e08efdc1962203001823747681379ffc54fc10 -R 8d931a50e177b7d6e53f54c42fcaa391 -U stephan -Z bab39be70eb99e3f7bbba492a0a8edda +P a0d0f1aafc6086726131dff5e6628f2771c20db3122a53bdbb82945ab5d326d1 +R fb83093e6e2cb948f127cf0c2e1878f8 +U drh +Z 71db94e565d6eff1a32422e7510ceb2f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index f47ed03299..803fdb7513 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a0d0f1aafc6086726131dff5e6628f2771c20db3122a53bdbb82945ab5d326d1 \ No newline at end of file +77d3dcd283595c52f24c07fc59ba60c9133b71c440cf3f799cf48c907c6fae3e \ No newline at end of file diff --git a/src/func.c b/src/func.c index ca0075edc3..70fda85927 100644 --- a/src/func.c +++ b/src/func.c @@ -1816,8 +1816,10 @@ static void sumFinalize(sqlite3_context *context){ if( p->approx ){ if( p->ovrfl ){ sqlite3_result_error(context,"integer overflow",-1); - }else{ + }else if( !sqlite3IsNaN(p->rErr) ){ sqlite3_result_double(context, p->rSum+p->rErr); + }else{ + sqlite3_result_double(context, p->rSum); } }else{ sqlite3_result_int64(context, p->iSum); diff --git a/test/func.test b/test/func.test index 3d71060465..4743e109bd 100644 --- a/test/func.test +++ b/test/func.test @@ -1545,4 +1545,12 @@ do_catchsql_test func-37.120 { SELECT sum(x) FROM c; } {1 {integer overflow}} +# 2023-08-28 forum post https://sqlite.org/forum/forumpost/1c06ddcacc86032a +# Incorrect handling of infinity by SUM(). +# +do_execsql_test func-38.100 { + WITH t1(x) AS (VALUES(9e+999)) SELECT sum(x) FROM t1; + WITH t1(x) AS (VALUES(-9e+999)) SELECT sum(x) FROM t1; +} {Inf -Inf} + finish_test From 4e9b48cfc4bbb4d5909e1ce0b88e6635df6cc024 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 13:06:26 +0000 Subject: [PATCH 096/422] Resolve a JNI-side race condition. Removed a now-extraneous struct member. Internal API renaming for consistency. FossilOrigin-Name: f5274e00f17d58e075f90ae5c1d4b38933da315e51592171fa35bcbd67b40b2a --- ext/jni/src/c/sqlite3-jni.c | 188 ++++++++++++------------ ext/jni/src/org/sqlite/jni/Tester1.java | 40 ++--- manifest | 16 +- manifest.uuid | 2 +- 4 files changed, 127 insertions(+), 119 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 00138ba400..632db79e4a 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -297,8 +297,8 @@ static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ /* ** Key type for use with S3JniGlobal_nph(). */ -typedef struct S3NphRef S3NphRef; -struct S3NphRef { +typedef struct S3JniNphRef S3JniNphRef; +struct S3JniNphRef { const int index /* index into S3JniGlobal.nph[] */; const char * const zName /* Full Java name of the class */; const char * const zMember /* Name of member property */; @@ -312,25 +312,25 @@ struct S3NphRef { ** corresponds to its index in the S3JniGlobal.nph[] array. */ static const struct { - const S3NphRef sqlite3; - const S3NphRef sqlite3_stmt; - const S3NphRef sqlite3_context; - const S3NphRef sqlite3_value; - const S3NphRef OutputPointer_Int32; - const S3NphRef OutputPointer_Int64; - const S3NphRef OutputPointer_sqlite3; - const S3NphRef OutputPointer_sqlite3_stmt; - const S3NphRef OutputPointer_sqlite3_value; + const S3JniNphRef sqlite3; + const S3JniNphRef sqlite3_stmt; + const S3JniNphRef sqlite3_context; + const S3JniNphRef sqlite3_value; + const S3JniNphRef OutputPointer_Int32; + const S3JniNphRef OutputPointer_Int64; + const S3JniNphRef OutputPointer_sqlite3; + const S3JniNphRef OutputPointer_sqlite3_stmt; + const S3JniNphRef OutputPointer_sqlite3_value; #ifdef SQLITE_ENABLE_FTS5 - const S3NphRef OutputPointer_String; - const S3NphRef OutputPointer_ByteArray; - const S3NphRef Fts5Context; - const S3NphRef Fts5ExtensionApi; - const S3NphRef fts5_api; - const S3NphRef fts5_tokenizer; - const S3NphRef Fts5Tokenizer; + const S3JniNphRef OutputPointer_String; + const S3JniNphRef OutputPointer_ByteArray; + const S3JniNphRef Fts5Context; + const S3JniNphRef Fts5ExtensionApi; + const S3JniNphRef fts5_api; + const S3JniNphRef fts5_tokenizer; + const S3JniNphRef Fts5Tokenizer; #endif -} S3NphRefs = { +} S3JniNphRefs = { #define MkRef(INDEX, KLAZZ, MEMBER, SIG) \ { INDEX, "org/sqlite/jni/" KLAZZ, MEMBER, SIG } /* NativePointerHolder ref */ @@ -368,10 +368,10 @@ enum { ** Size of the NativePointerHolder cache. Need enough space for ** (only) the library's NativePointerHolder and OutputPointer types, ** a fixed count known at build-time. This value needs to be - ** exactly the number of S3NphRef entries in the S3NphRefs + ** exactly the number of S3JniNphRef entries in the S3JniNphRefs ** object. */ - S3Jni_NphCache_size = sizeof(S3NphRefs) / sizeof(S3NphRef) + S3Jni_NphCache_size = sizeof(S3JniNphRefs) / sizeof(S3JniNphRef) }; /* @@ -382,7 +382,7 @@ enum { */ typedef struct S3JniNphClass S3JniNphClass; struct S3JniNphClass { - volatile const S3NphRef * pRef /* Entry from S3NphRefs. */; + volatile const S3JniNphRef * pRef /* Entry from S3JniNphRefs. */; jclass klazz /* global ref to the concrete ** NativePointerHolder subclass ** represented by zClassName */; @@ -580,9 +580,7 @@ struct S3JniGlobalType { S3JniDb * aFree /* Linked list of free instances */; sqlite3_mutex * mutex /* mutex for aHead and aFree */; void const * locker /* perDb mutex is held on this object's - behalf. Unlike envCache.locker, we cannot - always have this set to the current JNIEnv - object. Used only for sanity checking. */; + behalf. Used only for sanity checking. */; } perDb; struct { S3JniUdf * aFree /* Head of the free-item list. Guarded by global @@ -593,7 +591,6 @@ struct S3JniGlobalType { ** and never released. */ struct { - jclass cObj /* global ref to java.lang.Object */; jclass cLong /* global ref to java.lang.Long */; jclass cString /* global ref to java.lang.String */; jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */; @@ -637,13 +634,13 @@ struct S3JniGlobalType { #ifdef SQLITE_JNI_ENABLE_METRICS /* Internal metrics. */ struct { - volatile unsigned envCacheHits; - volatile unsigned envCacheMisses; - volatile unsigned envCacheAllocs; + volatile unsigned nEnvHit; + volatile unsigned nEnvMiss; + volatile unsigned nEnvAlloc; + volatile unsigned nNphInit; volatile unsigned nMutexEnv /* number of times envCache.mutex was entered for a S3JniEnv operation. */; - volatile unsigned nMutexEnv2 /* number of times envCache.mutex was entered for - a S3JniNphClass operation. */; + volatile unsigned nMutexEnv2 /* number of times envCache.mutex was entered */; volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */; volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */; volatile unsigned nMutexGlobal /* number of times global mutex was entered. */; @@ -687,7 +684,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(OBJ, &S3NphRefs.T) +#define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3JniNphRefs.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) @@ -828,12 +825,12 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){ row = SJG.envCache.aHead; for( ; row; row = row->pNext ){ if( row->env == env ){ - s3jni_incr( &SJG.metrics.envCacheHits ); + s3jni_incr( &SJG.metrics.nEnvHit ); S3JniMutex_Env_leave; return row; } } - s3jni_incr( &SJG.metrics.envCacheMisses ); + s3jni_incr( &SJG.metrics.nEnvMiss ); row = SJG.envCache.aFree; if( row ){ assert(!row->pPrev); @@ -841,7 +838,7 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){ if( row->pNext ) row->pNext->pPrev = 0; }else{ row = s3jni_malloc_or_die(env, sizeof(*row)); - s3jni_incr( &SJG.metrics.envCacheAllocs ); + s3jni_incr( &SJG.metrics.nEnvAlloc ); } memset(row, 0, sizeof(*row)); row->pNext = SJG.envCache.aHead; @@ -1015,7 +1012,7 @@ static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, ** System.out.println(e.toString()); // java.lang.RuntimeException: Hi ** System.out.println(e.getMessage()); // Hi */ -static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx ){ +static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){ jmethodID mid; jstring msg; char * zMsg; @@ -1243,7 +1240,7 @@ static int S3JniEnv_uncache(JNIEnv * const env){ ** 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, S3JniNphRef const* pRef){ /** According to: @@ -1260,18 +1257,24 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3NphRef const* pRef cached as well. */ S3JniNphClass * const pNC = &SJG.nph[pRef->index]; - assert( (void*)pRef>=(void*)&S3NphRefs && (void*)pRef<(void*)(&S3NphRefs + 1) + assert( (void*)pRef>=(void*)&S3JniNphRefs && (void*)pRef<(void*)(&S3JniNphRefs + 1) && "pRef is out of range." ); + assert( pRef->index>=0 + && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) ); if( !pNC->pRef ){ S3JniMutex_Nph_enter; if( !pNC->pRef ){ - pNC->pRef = pRef; - pNC->klazz = (*env)->FindClass(env, pRef->zName); + jclass const klazz = (*env)->FindClass(env, pRef->zName); S3JniExceptionIsFatal("FindClass() unexpectedly threw"); - pNC->klazz = S3JniRefGlobal(pNC->klazz); + pNC->klazz = S3JniRefGlobal(klazz); + s3jni_incr( &SJG.metrics.nNphInit ); + pNC->pRef = pRef + /* Must come last to avoid a race condition where pNC->klass + can be NULL after this function returns. */; } S3JniMutex_Nph_leave; } + assert( pNC->klazz ); return pNC; } @@ -1282,11 +1285,13 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3NphRef const* pRef ** NativePointerHolder class. */ static jfieldID NativePointerHolder_field(JNIEnv * const env, - S3NphRef const* pRef){ + S3JniNphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); + assert( pNC->klazz ); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ + s3jni_incr( &SJG.metrics.nNphInit ); pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing " @@ -1302,7 +1307,7 @@ 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, S3JniNphRef const* pRef, jobject ppOut, const void * p){ jfieldID const fid = NativePointerHolder_field(env, pRef); S3JniMutex_Nph_enter; @@ -1320,7 +1325,7 @@ static void NativePointerHolder__set(JNIEnv * env, S3NphRef const* pRef, ** cache key. This is a no-op if pObj is NULL. */ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, - S3NphRef const* pRef){ + S3JniNphRef const* pRef){ if( pObj ){ jfieldID const fid = NativePointerHolder_field(env, pRef); void * rv; @@ -1467,7 +1472,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env, /* ** Common init for OutputPointer_set_Int32() and friends. pRef must be -** a pointer from S3NphRefs. jOut must be an instance of that +** a pointer from S3JniNphRefs. jOut must be an instance of that ** class. If necessary, this fetches the jfieldID for jOut's [value] ** property, which must be of the type represented by the JNI type ** signature zTypeSig, and stores it in pRef's S3JniGlobal.nph entry. @@ -1478,13 +1483,14 @@ static int S3JniAutoExtension_init(JNIEnv *const env, ** routine with the same pRef but different zTypeSig: it will ** misbehave. */ -static jfieldID OutputPointer_field(JNIEnv * const env, S3NphRef const * pRef){ +static jfieldID OutputPointer_field(JNIEnv * const env, S3JniNphRef const * pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); assert( pNC->klazz ); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; if( !pNC->fidValue ){ + s3jni_incr( &SJG.metrics.nNphInit ); pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("OutputPointer_field() could not find OutputPointer.*.value"); } @@ -1501,7 +1507,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ (*env)->SetIntField(env, jOut, OutputPointer_field( - env, &S3NphRefs.OutputPointer_Int32 + env, &S3JniNphRefs.OutputPointer_Int32 ), (jint)v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); } @@ -1514,7 +1520,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ (*env)->SetLongField(env, jOut, OutputPointer_field( - env, &S3NphRefs.OutputPointer_Int64 + env, &S3JniNphRefs.OutputPointer_Int64 ), v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); } @@ -1524,7 +1530,7 @@ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, ** Object type. */ static void OutputPointer_set_obj(JNIEnv * const env, - S3NphRef const * const pRef, + S3JniNphRef const * const pRef, jobject const jOut, jobject v){ (*env)->SetObjectField(env, jOut, OutputPointer_field(env, pRef), v); @@ -1537,7 +1543,7 @@ static void OutputPointer_set_obj(JNIEnv * const env, */ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, jobject jDb){ - OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3, jOut, jDb); + OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3, jOut, jDb); } /* @@ -1546,7 +1552,7 @@ static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut, */ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut, jobject jStmt){ - OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3_stmt, jOut, jStmt); + OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3_stmt, jOut, jStmt); } #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -1556,7 +1562,7 @@ static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOu */ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jOut, jobject jValue){ - OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_sqlite3_value, jOut, jValue); + OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3_value, jOut, jValue); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ @@ -1568,7 +1574,7 @@ static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jO */ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, jbyteArray const v){ - OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_ByteArray, jOut, v); + OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_ByteArray, jOut, v); } #endif @@ -1578,7 +1584,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, */ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, jstring const v){ - OutputPointer_set_obj(env, &S3NphRefs.OutputPointer_String, jOut, v); + OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_String, jOut, v); } #endif /* SQLITE_ENABLE_FTS5 */ @@ -1661,16 +1667,17 @@ static void ResultJavaValue_finalizer(void *v){ ** if Java fails to allocate, but the JNI docs are not entirely clear ** on that detail. ** -** Always use a static pointer from the S3NphRefs struct for the 2nd +** Always use a static pointer from the S3JniNphRefs struct for the 2nd ** argument so that we can use pRef->index as an O(1) cache key. */ -static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const * pRef, +static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef const * pRef, const void * pNative){ jobject rv = 0; S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); if( !pNC->midCtor ){ S3JniMutex_Nph_enter; if( !pNC->midCtor ){ + s3jni_incr( &SJG.metrics.nNphInit ); pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); S3JniExceptionIsFatal("Cannot find constructor for class."); } @@ -1684,16 +1691,16 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3NphRef const } static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3, sv); } static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_context, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_context, sv); } static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_stmt, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_stmt, sv); } static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.sqlite3_value, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_value, sv); } /* Helper typedefs for UDF callback types. */ @@ -1816,10 +1823,8 @@ static int udf_args(JNIEnv *env, *jArgv = 0; if( !jcx ) goto error_oom; ja = (*env)->NewObjectArray( - env, argc, SJG.g.cObj - /* S3JniGlobal_nph(&S3NphRefs.sqlite3_value)->klazz would be - more correct, but it unpredictably triggers an assert in the - JVM. */, NULL); + env, argc, S3JniGlobal_nph(&S3JniNphRefs.sqlite3_value)->klazz, + NULL); s3jni_oom_check( ja ); if( !ja ) goto error_oom; for(i = 0; i < argc; ++i){ @@ -2106,7 +2111,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(&S3NphRefs.sqlite3, ps->jDb, pDb) + NativePointerHolder_set(&S3JniNphRefs.sqlite3, ps->jDb, pDb) /* As of here, the Java/C connection is complete */; for( i = 0; go && 0==rc; ++i ){ S3JniAutoExtension ax = {0,0} @@ -2423,7 +2428,7 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ if( 0==rc ){ S3JniDb_set_aside(ps) /* MUST come after close() because of ps->trace. */; - NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0); + NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0); } } #else @@ -2446,7 +2451,7 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ if( 0==rc ){ S3JniDb__set_aside_unlocked(env,ps) /* MUST come after close() because of ps->trace. */; - NativePointerHolder_set(&S3NphRefs.sqlite3, jDb, 0); + NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0); } }else{ /* ps is from S3Global.perDb.aFree. */ @@ -3100,7 +3105,7 @@ S3JniApi(sqlite3_finalize(),jint,1finalize)( sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ rc = sqlite3_finalize(pStmt); - NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jpStmt, 0); + NativePointerHolder_set(&S3JniNphRefs.sqlite3_stmt, jpStmt, 0); } return rc; } @@ -3205,7 +3210,7 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, assert(ps->jDb); if( 0==ps->pDb ){ ps->pDb = *ppDb; - NativePointerHolder_set(&S3NphRefs.sqlite3, ps->jDb, *ppDb) + NativePointerHolder_set(&S3JniNphRefs.sqlite3, ps->jDb, *ppDb) /* As of here, the Java/C connection is complete */; }else{ assert( ps->pDb==*ppDb @@ -3310,7 +3315,7 @@ end: OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0)); } if( pStmt ){ - NativePointerHolder_set(&S3NphRefs.sqlite3_stmt, jStmt, pStmt); + NativePointerHolder_set(&S3JniNphRefs.sqlite3_stmt, jStmt, pStmt); }else{ /* Happens for comments and whitespace. */ S3JniUnrefLocal(jStmt); @@ -4256,7 +4261,7 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SO(S3JniEnv); SO(S3JniHook); SO(S3JniDb); - SO(S3NphRefs); + SO(S3JniNphRefs); printf("\t(^^^ %u NativePointerHolder subclasses)\n", (unsigned)S3Jni_NphCache_size); SO(S3JniGlobal); @@ -4266,19 +4271,20 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ #ifdef SQLITE_JNI_ENABLE_METRICS printf("Cache info:\n"); printf("\tJNIEnv cache: %u allocs, %u misses, %u hits\n", - SJG.metrics.envCacheAllocs, - SJG.metrics.envCacheMisses, - SJG.metrics.envCacheHits); + SJG.metrics.nEnvAlloc, SJG.metrics.nEnvMiss, + SJG.metrics.nEnvHit); printf("Mutex entry:" "\n\tglobal = %u" "\n\tenv = %u" - "\n\tnph = %u" + "\n\tnph = %u (%u for S3JniNphClass init, rest for " + "native pointer access)" "\n\tperDb = %u" "\n\tautoExt list = %u" "\n\tS3JniUdf free-list = %u" "\n\tmetrics = %u\n", SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv, - SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, + SJG.metrics.nMutexEnv2, SJG.metrics.nNphInit, + SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf, SJG.metrics.nMetrics); puts("Allocs:"); @@ -4290,6 +4296,9 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf), (unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)), SJG.metrics.nUdfRecycled); + printf("\tS3JniEnv: %u alloced (*%u = %u bytes)\n", + SJG.metrics.nEnvAlloc, (unsigned) sizeof(S3JniEnv), + (unsigned)(SJG.metrics.nEnvAlloc * sizeof(S3JniEnv))); puts("Java-side UDF calls:"); #define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); @@ -4324,10 +4333,10 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ JNIEXPORT ReturnType JNICALL \ JniFuncNameFtsTok(Suffix) -#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 PtrGet_fts5_api(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.fts5_api) +#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.fts5_tokenizer) +#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.Fts5Context) +#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.Fts5Tokenizer) #define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext() /** @@ -4389,10 +4398,10 @@ static inline Fts5ExtensionApi const * s3jni_ftsext(void){ } static inline jobject new_Fts5Context_wrapper(JNIEnv * const env, Fts5Context *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.Fts5Context, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.Fts5Context, sv); } static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ - return new_NativePointerHolder_object(env, &S3NphRefs.fts5_api, sv); + return new_NativePointerHolder_object(env, &S3JniNphRefs.fts5_api, sv); } /* @@ -4402,7 +4411,7 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ if( !SJG.fts5.jFtsExt ){ jobject pNPH = new_NativePointerHolder_object( - env, &S3NphRefs.Fts5ExtensionApi, s3jni_ftsext() + env, &S3JniNphRefs.Fts5ExtensionApi, s3jni_ftsext() ); S3JniMutex_Env_enter; if( pNPH ){ @@ -4829,7 +4838,7 @@ static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, ** Proxy for Fts5ExtensionApi.xTokenize() and ** fts5_tokenizer.xTokenize() */ -static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, +static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphRef const *pRef, jint tokFlags, jobject jFcx, jbyteArray jbaText, jobject jCallback){ Fts5ExtDecl; @@ -4857,11 +4866,11 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, s.tok.jba = S3JniRefLocal(jbaText); s.tok.zPrev = (const char *)pText; s.tok.nPrev = (int)nText; - if( pRef == &S3NphRefs.Fts5ExtensionApi ){ + if( pRef == &S3JniNphRefs.Fts5ExtensionApi ){ rc = fext->xTokenize(PtrGet_Fts5Context(jFcx), (const char *)pText, (int)nText, &s, s3jni_xTokenize_xToken); - }else if( pRef == &S3NphRefs.fts5_tokenizer ){ + }else if( pRef == &S3JniNphRefs.fts5_tokenizer ){ fts5_tokenizer * const pTok = PtrGet_fts5_tokenizer(jSelf); rc = pTok->xTokenize(PtrGet_Fts5Tokenizer(jFcx), &s, tokFlags, (const char *)pText, (int)nText, @@ -4879,13 +4888,13 @@ static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3NphRef const *pRef, JniDeclFtsXA(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jbyteArray jbaText, jobject jCallback){ - return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5ExtensionApi, + return s3jni_fts5_xTokenize(env, jSelf, &S3JniNphRefs.Fts5ExtensionApi, 0, jFcx, jbaText, jCallback); } JniDeclFtsTok(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jint tokFlags, jbyteArray jbaText, jobject jCallback){ - return s3jni_fts5_xTokenize(env, jSelf, &S3NphRefs.Fts5Tokenizer, + return s3jni_fts5_xTokenize(env, jSelf, &S3JniNphRefs.Fts5Tokenizer, tokFlags, jFcx, jbaText, jCallback); } @@ -5168,9 +5177,6 @@ Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){ } /* Grab references to various global classes and objects... */ - SJG.g.cObj = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Object")); - S3JniExceptionIsFatal("Error getting reference to Object class."); - SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long")); S3JniExceptionIsFatal("Error getting reference to Long class."); SJG.g.ctorLong1 = (*env)->GetMethodID(env, SJG.g.cLong, diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 496a90a428..3d60362f61 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1496,25 +1496,6 @@ public class Tester1 implements Runnable { } } - { - // Build list of tests to run from the methods named test*(). - testMethods = new ArrayList<>(); - for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ - final String name = m.getName(); - if( name.equals("testFail") ){ - if( forceFail ){ - testMethods.add(m); - } - }else if( !m.isAnnotationPresent( ManualTest.class ) ){ - if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ - outln("Skipping test in multi-thread mode: ",name,"()"); - }else if( name.startsWith("test") ){ - testMethods.add(m); - } - } - } - } - if( sqlLog ){ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ int rc = sqlite3_config( new ConfigSqllogCallback() { @@ -1538,6 +1519,27 @@ public class Tester1 implements Runnable { "you are very likely seeing the side effects of a known openjdk8 ", "bug. It is unsightly but does not affect the library."); + { + // Build list of tests to run from the methods named test*(). + testMethods = new ArrayList<>(); + out("Skipping tests in multi-thread mode:"); + for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){ + final String name = m.getName(); + if( name.equals("testFail") ){ + if( forceFail ){ + testMethods.add(m); + } + }else if( !m.isAnnotationPresent( ManualTest.class ) ){ + if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){ + out(" "+name+"()"); + }else if( name.startsWith("test") ){ + testMethods.add(m); + } + } + } + out("\n"); + } + final long timeStart = System.currentTimeMillis(); int nLoop = 0; switch( SQLITE_THREADSAFE ){ /* Sanity checking */ diff --git a/manifest b/manifest index 1878e12f38..70e5a02781 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\san\sissue\swith\sinfinity\shandling\sby\sthe\sSUM()\sfunction\sthat\sgoes\sback\nto\sthe\sextended-precision\sSUM()\senhancement\sof\n[check-in\sc63e26e705f5e967].\s\sProblem\sreported\sby\n[forum:/forumpost/1c06ddcacc86032a|forum\spost\s1c06ddcacc86032a]. -D 2023-08-28T12:20:18.225 +C Resolve\sa\sJNI-side\srace\scondition.\sRemoved\sa\snow-extraneous\sstruct\smember.\sInternal\sAPI\srenaming\sfor\sconsistency. +D 2023-08-28T13:06:26.423 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 c8f329d225c87c9af2c74508e6be48424a380502da7bca82fc7486dd91a7af9c +F ext/jni/src/c/sqlite3-jni.c 59c06b99420d61baf03004fc47093e786bde383ffe5d70e1458443e095a4fd78 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 @@ -263,7 +263,7 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185 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/Tester1.java a9558165dbb085494705525ef28e41d337d8348bf44259ce1f77cad72547bfdf F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d @@ -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 a0d0f1aafc6086726131dff5e6628f2771c20db3122a53bdbb82945ab5d326d1 -R fb83093e6e2cb948f127cf0c2e1878f8 -U drh -Z 71db94e565d6eff1a32422e7510ceb2f +P 77d3dcd283595c52f24c07fc59ba60c9133b71c440cf3f799cf48c907c6fae3e +R 09b3f39e435c1ed6b663c3c2289d1181 +U stephan +Z e20dad0d4d283548754a87d23449f696 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 803fdb7513..752861a24d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -77d3dcd283595c52f24c07fc59ba60c9133b71c440cf3f799cf48c907c6fae3e \ No newline at end of file +f5274e00f17d58e075f90ae5c1d4b38933da315e51592171fa35bcbd67b40b2a \ No newline at end of file From bdb149d063755ddb183de29e45676a688c3b6a1b Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 13:18:46 +0000 Subject: [PATCH 097/422] Move a metrics counter so that it can be used to indirectly witness the race condition fix from [f5274e00f17d58e0] by recording varying final values for that metric across consecutive multi-threaded test runs. FossilOrigin-Name: 54d2209d24547dbb05e7c12daa27211593c34de8005e8adba0989b8d219f5f3b --- ext/jni/src/c/sqlite3-jni.c | 9 ++++----- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 632db79e4a..88c0747a94 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1263,11 +1263,11 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) ); if( !pNC->pRef ){ S3JniMutex_Nph_enter; + s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->pRef ){ jclass const klazz = (*env)->FindClass(env, pRef->zName); S3JniExceptionIsFatal("FindClass() unexpectedly threw"); pNC->klazz = S3JniRefGlobal(klazz); - s3jni_incr( &SJG.metrics.nNphInit ); pNC->pRef = pRef /* Must come last to avoid a race condition where pNC->klass can be NULL after this function returns. */; @@ -1287,11 +1287,10 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p static jfieldID NativePointerHolder_field(JNIEnv * const env, S3JniNphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); - assert( pNC->klazz ); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; + s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->fidValue ){ - s3jni_incr( &SJG.metrics.nNphInit ); pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing " @@ -1489,8 +1488,8 @@ static jfieldID OutputPointer_field(JNIEnv * const env, S3JniNphRef const * pRef assert( pNC->klazz ); if( !pNC->fidValue ){ S3JniMutex_Nph_enter; + s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->fidValue ){ - s3jni_incr( &SJG.metrics.nNphInit ); pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("OutputPointer_field() could not find OutputPointer.*.value"); } @@ -1676,8 +1675,8 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef co S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); if( !pNC->midCtor ){ S3JniMutex_Nph_enter; + s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->midCtor ){ - s3jni_incr( &SJG.metrics.nNphInit ); pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); S3JniExceptionIsFatal("Cannot find constructor for class."); } diff --git a/manifest b/manifest index 70e5a02781..d3e128b126 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Resolve\sa\sJNI-side\srace\scondition.\sRemoved\sa\snow-extraneous\sstruct\smember.\sInternal\sAPI\srenaming\sfor\sconsistency. -D 2023-08-28T13:06:26.423 +C Move\sa\smetrics\scounter\sso\sthat\sit\scan\sbe\sused\sto\sindirectly\switness\sthe\srace\scondition\sfix\sfrom\s[f5274e00f17d58e0]\sby\srecording\svarying\sfinal\svalues\sfor\sthat\smetric\sacross\sconsecutive\smulti-threaded\stest\sruns. +D 2023-08-28T13:18:46.495 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 59c06b99420d61baf03004fc47093e786bde383ffe5d70e1458443e095a4fd78 +F ext/jni/src/c/sqlite3-jni.c 515464940aba09b6428f8a804660710aa365863d5a10699d5ac8321d83dddf64 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 @@ -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 77d3dcd283595c52f24c07fc59ba60c9133b71c440cf3f799cf48c907c6fae3e -R 09b3f39e435c1ed6b663c3c2289d1181 +P f5274e00f17d58e075f90ae5c1d4b38933da315e51592171fa35bcbd67b40b2a +R 969ab0bad5a3d98cbcd198512cf4e202 U stephan -Z e20dad0d4d283548754a87d23449f696 +Z 99163d5b88e11a26824bdaa0e0b1c0e2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 752861a24d..b69093b8ff 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f5274e00f17d58e075f90ae5c1d4b38933da315e51592171fa35bcbd67b40b2a \ No newline at end of file +54d2209d24547dbb05e7c12daa27211593c34de8005e8adba0989b8d219f5f3b \ No newline at end of file From 25e7d8af753d8e7bc1ce0a4b4be5996f5347effb Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 14:59:27 +0000 Subject: [PATCH 098/422] Duplicate code consolidation. FossilOrigin-Name: 71f239747c7934310dedf9fc0cbf84fbeeed53808234067147335c12396849a1 --- ext/jni/src/c/sqlite3-jni.c | 73 +++++++++++++------------------------ manifest | 12 +++--- manifest.uuid | 2 +- 3 files changed, 33 insertions(+), 54 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 88c0747a94..f18fa6f5dc 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1281,12 +1281,20 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p #define S3JniGlobal_nph(PREF) S3JniGlobal__nph(env, PREF) /* -** Returns the ID of the "nativePointer" field from the given -** NativePointerHolder class. +** Common code for accessor functions for NativePointerHolder and +** OutputPointer types. pRef must be a pointer from S3JniNphRefs. jOut +** must be an instance of that class (Java's type safety takes care of +** that requirement). If necessary, this fetches the jfieldID for +** jOut's pRef->zMember, which must be of the type represented by the +** JNI type signature pRef->zTypeSig, and stores it in +** S3JniGlobal.nph[pRef->index]. Fails fatally if the pRef->zMember +** property is not found, as that presents a serious internal misuse. +** +** Property lookups are cached on a per-pRef basis. */ -static jfieldID NativePointerHolder_field(JNIEnv * const env, - S3JniNphRef const* pRef){ +static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); + if( !pNC->fidValue ){ S3JniMutex_Nph_enter; s3jni_incr( &SJG.metrics.nNphInit ); @@ -1294,10 +1302,11 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env, pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); S3JniExceptionIsFatal("Code maintenance required: missing " - "nativePointer field."); + "required S3JniNphClass::fidValue."); } S3JniMutex_Nph_leave; } + assert( pNC->fidValue ); return pNC->fidValue; } @@ -1307,8 +1316,9 @@ static jfieldID NativePointerHolder_field(JNIEnv * const env, ** as a cache key. */ static void NativePointerHolder__set(JNIEnv * env, S3JniNphRef const* pRef, - jobject ppOut, const void * p){ - jfieldID const fid = NativePointerHolder_field(env, pRef); + jobject ppOut, const void * p){ + jfieldID const fid = s3jni_nphop_field(env, pRef); + S3JniMutex_Nph_enter; (*env)->SetLongField(env, ppOut, fid, (jlong)p); S3JniMutex_Nph_leave; @@ -1319,23 +1329,21 @@ static void NativePointerHolder__set(JNIEnv * env, S3JniNphRef const* pRef, 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. +** Fetches a native ptr value from NativePointerHolder object pObj, +** which must be of the native type described by pRef. This is a +** no-op if pObj is NULL. */ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, S3JniNphRef const* pRef){ + void * rv = 0; if( pObj ){ - jfieldID const fid = NativePointerHolder_field(env, pRef); - void * rv; + jfieldID const fid = s3jni_nphop_field(env, pRef); S3JniMutex_Nph_enter; rv = (void*)(*env)->GetLongField(env, pObj, fid); S3JniMutex_Nph_leave; S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); - return rv; - }else{ - return 0; } + return rv; } #define NativePointerHolder_get(JOBJ,NPHREF) \ @@ -1469,35 +1477,6 @@ static int S3JniAutoExtension_init(JNIEnv *const env, return 0; } -/* -** Common init for OutputPointer_set_Int32() and friends. pRef must be -** a pointer from S3JniNphRefs. jOut must be an instance of that -** class. If necessary, this fetches the jfieldID for jOut's [value] -** property, which must be of the type represented by the JNI type -** signature zTypeSig, and stores it in pRef's S3JniGlobal.nph entry. -** Fails fatally if the property is not found, as that presents a -** serious internal misuse. -** -** Property lookups are cached on a per-pRef basis. Do not use this -** routine with the same pRef but different zTypeSig: it will -** misbehave. -*/ -static jfieldID OutputPointer_field(JNIEnv * const env, S3JniNphRef const * pRef){ - S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); - - assert( pNC->klazz ); - if( !pNC->fidValue ){ - S3JniMutex_Nph_enter; - s3jni_incr( &SJG.metrics.nNphInit ); - if( !pNC->fidValue ){ - pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); - S3JniExceptionIsFatal("OutputPointer_field() could not find OutputPointer.*.value"); - } - S3JniMutex_Nph_leave; - } - return pNC->fidValue; -} - /* ** Sets the value property of the OutputPointer.Int32 jOut object to ** v. @@ -1505,7 +1484,7 @@ static jfieldID OutputPointer_field(JNIEnv * const env, S3JniNphRef const * pRef static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ (*env)->SetIntField(env, jOut, - OutputPointer_field( + s3jni_nphop_field( env, &S3JniNphRefs.OutputPointer_Int32 ), (jint)v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); @@ -1518,7 +1497,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ (*env)->SetLongField(env, jOut, - OutputPointer_field( + s3jni_nphop_field( env, &S3JniNphRefs.OutputPointer_Int64 ), v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); @@ -1532,7 +1511,7 @@ static void OutputPointer_set_obj(JNIEnv * const env, S3JniNphRef const * const pRef, jobject const jOut, jobject v){ - (*env)->SetObjectField(env, jOut, OutputPointer_field(env, pRef), v); + (*env)->SetObjectField(env, jOut, s3jni_nphop_field(env, pRef), v); S3JniExceptionIsFatal("Cannot set OutputPointer.T.value"); } diff --git a/manifest b/manifest index d3e128b126..5cf4f78850 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Move\sa\smetrics\scounter\sso\sthat\sit\scan\sbe\sused\sto\sindirectly\switness\sthe\srace\scondition\sfix\sfrom\s[f5274e00f17d58e0]\sby\srecording\svarying\sfinal\svalues\sfor\sthat\smetric\sacross\sconsecutive\smulti-threaded\stest\sruns. -D 2023-08-28T13:18:46.495 +C Duplicate\scode\sconsolidation. +D 2023-08-28T14:59:27.520 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 515464940aba09b6428f8a804660710aa365863d5a10699d5ac8321d83dddf64 +F ext/jni/src/c/sqlite3-jni.c 0b76d7947e474aa30f6636f42a4e00eb48ccad6f920f1d2a1ee2d9540709c578 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 @@ -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 f5274e00f17d58e075f90ae5c1d4b38933da315e51592171fa35bcbd67b40b2a -R 969ab0bad5a3d98cbcd198512cf4e202 +P 54d2209d24547dbb05e7c12daa27211593c34de8005e8adba0989b8d219f5f3b +R 4b2923bf6d2c403adcf8638cd038e3aa U stephan -Z 99163d5b88e11a26824bdaa0e0b1c0e2 +Z 68db36d58f6c1c3e233d49a0de3a9cc1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b69093b8ff..4a452b70e0 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -54d2209d24547dbb05e7c12daa27211593c34de8005e8adba0989b8d219f5f3b \ No newline at end of file +71f239747c7934310dedf9fc0cbf84fbeeed53808234067147335c12396849a1 \ No newline at end of file From 8d64c12a1013cda32813e76d2d493edb102c37ef Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 16:05:19 +0000 Subject: [PATCH 099/422] Update tool/mkctimec.tcl to account for [fe7365254d343e]. FossilOrigin-Name: 349aac7e8d513bc420e8948b84cf715e454443439fdcd5aff0f2b13815a9cbb3 --- manifest | 15 +++++++-------- manifest.uuid | 2 +- tool/mkctimec.tcl | 1 + 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index 0fad33fc3a..85ec886d53 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\sthe\s-DSQLITE_EXTRA_AUTOEXT=name\scompile-time\soption.\n[forum:/forumpost/00829394c74a670f|forum\sthread\s00829394c74a670f]. -D 2023-08-28T15:58:00.980 +C Update\stool/mkctimec.tcl\sto\saccount\sfor\s[fe7365254d343e]. +D 2023-08-28T16:05:19.091 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -2028,7 +2028,7 @@ F tool/max-limits.c cbb635fbb37ae4d05f240bfb5b5270bb63c54439 F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176 F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8 F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x -F tool/mkctimec.tcl aca4b83e49aecf10368cd5d11bc4847061041ade026db5bd8da17ef201f1403b x +F tool/mkctimec.tcl c7246946f847d3d6d022f5276650e0290e2aa648793be2fb8c3f206347baa356 x F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd F tool/mkmsvcmin.tcl 8897d515ef7f94772322db95a3b6fce6c614d84fe0bdd06ba5a1c786351d5a1d F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef @@ -2106,9 +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 71f239747c7934310dedf9fc0cbf84fbeeed53808234067147335c12396849a1 423e77277a61d7febf4c3fc737981fa22a82b5c774a8ada5375a01a0611535b2 -R 52dd63dfa31054f8cede16ca6a766cde -T +closed 423e77277a61d7febf4c3fc737981fa22a82b5c774a8ada5375a01a0611535b2 -U drh -Z 4533af2279533b84257be53cbe5f8c72 +P fe7365254d343ed03e11a4e9cad7f0e5d5182b9220c6fde6d30e434ebdaba2af +R b00a271c5a6f2785833f80aa9f660281 +U stephan +Z 28d128e867bc57e370acf90e968cbcdb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5db811ee13..9782c0a682 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -fe7365254d343ed03e11a4e9cad7f0e5d5182b9220c6fde6d30e434ebdaba2af \ No newline at end of file +349aac7e8d513bc420e8948b84cf715e454443439fdcd5aff0f2b13815a9cbb3 \ No newline at end of file diff --git a/tool/mkctimec.tcl b/tool/mkctimec.tcl index 317f30a35f..23726a722f 100755 --- a/tool/mkctimec.tcl +++ b/tool/mkctimec.tcl @@ -308,6 +308,7 @@ set value_options { SQLITE_ENABLE_8_3_NAMES SQLITE_ENABLE_CEROD SQLITE_ENABLE_LOCKING_STYLE + SQLITE_EXTRA_AUTOEXT SQLITE_EXTRA_INIT SQLITE_EXTRA_SHUTDOWN SQLITE_FTS3_MAX_EXPR_DEPTH From ece2d7f2be16329e34e6acb7913b42c70447881b Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 16:22:31 +0000 Subject: [PATCH 100/422] Remove a pair of what are arguably unnecessary mutex locks (and often hit). More JNI-internal cleanups. FossilOrigin-Name: ecf07a0144dc6402b1e0924b1775d99dc465b27aa86a2718cac60a9b4c974312 --- ext/jni/src/c/sqlite3-jni.c | 110 ++++++++++++++++++++---------------- manifest | 12 ++-- manifest.uuid | 2 +- 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index f18fa6f5dc..a78b07166b 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -637,7 +637,6 @@ struct S3JniGlobalType { volatile unsigned nEnvHit; volatile unsigned nEnvMiss; volatile unsigned nEnvAlloc; - volatile unsigned nNphInit; volatile unsigned nMutexEnv /* number of times envCache.mutex was entered for a S3JniEnv operation. */; volatile unsigned nMutexEnv2 /* number of times envCache.mutex was entered */; @@ -670,26 +669,6 @@ struct S3JniGlobalType { static S3JniGlobalType S3JniGlobal = {}; #define SJG S3JniGlobal -/* -** Helpers for extracting pointers from jobjects, noting that we rely -** on the corresponding Java interfaces having already done the -** type-checking. OBJ must be a jobject referring to a -** NativePointerHolder, where T matches PtrGet_T. Don't use these -** in contexts where that's not the case. Note that these aren't -** type-safe in the strictest sense: -** -** sqlite3 * s = PtrGet_sqlite3_stmt(...) -** -** will work, despite the incorrect macro name, so long as the -** argument is a Java sqlite3 object, as this operation only has void -** pointers to work with. -*/ -#define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3JniNphRefs.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) -#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) - /* Increments *p, possibly protected by a mutex. */ #ifndef SQLITE_JNI_ENABLE_METRICS #define s3jni_incr(PTR) @@ -1263,7 +1242,6 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) ); if( !pNC->pRef ){ S3JniMutex_Nph_enter; - s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->pRef ){ jclass const klazz = (*env)->FindClass(env, pRef->zName); S3JniExceptionIsFatal("FindClass() unexpectedly threw"); @@ -1297,7 +1275,6 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ if( !pNC->fidValue ){ S3JniMutex_Nph_enter; - s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->fidValue ){ pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, pRef->zMember, pRef->zTypeSig); @@ -1315,13 +1292,12 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ ** zClassName must be a static string so we can use its address ** as a cache key. */ -static void NativePointerHolder__set(JNIEnv * env, S3JniNphRef const* pRef, +static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef, jobject ppOut, const void * p){ jfieldID const fid = s3jni_nphop_field(env, pRef); - S3JniMutex_Nph_enter; + assert( ppOut ); (*env)->SetLongField(env, ppOut, fid, (jlong)p); - S3JniMutex_Nph_leave; S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer."); } @@ -1338,9 +1314,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, void * rv = 0; if( pObj ){ jfieldID const fid = s3jni_nphop_field(env, pRef); - S3JniMutex_Nph_enter; rv = (void*)(*env)->GetLongField(env, pObj, fid); - S3JniMutex_Nph_leave; S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); } return rv; @@ -1349,6 +1323,43 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, #define NativePointerHolder_get(JOBJ,NPHREF) \ NativePointerHolder__get(env, (JOBJ), (NPHREF)) +/* +** Helpers for extracting pointers from jobjects, noting that we rely +** on the corresponding Java interfaces having already done the +** type-checking. OBJ must be a jobject referring to a +** NativePointerHolder, where T matches PtrGet_T. Don't use these +** in contexts where that's not the case. Note that these aren't +** type-safe in the strictest sense: +** +** sqlite3 * s = PtrGet_sqlite3_stmt(...) +** +** will work, despite the incorrect macro name, so long as the +** argument is a Java sqlite3 object, as this operation only has void +** pointers to work with. +*/ +#define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3JniNphRefs.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) +#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) + +#if 0 +/* +** Enters the S3JniDb mutex and PtrGet_sqlite3()'s jObj. If that's +** NULL then it leaves the mutex, else the mutex is still entered +** when this returns and the caller is obligated to leave it. +*/ +static sqlite3* PtrGet__sqlite3_lock(JNIEnv * const env, jobject jObj){ + sqlite3 *rv; + S3JniMutex_S3JniDb_enter; + rv = PtrGet_sqlite3(jObj); + if( !rv ){ S3JniMutex_S3JniDb_leave; } + return rv; +} +#undef PtrGet_sqlite3 +#define PtrGet_sqlite3(JOBJ) PtrGet__sqlite3_lock(env, (JOBJ)) +#endif + /* ** Extracts the new S3JniDb instance from the free-list, or allocates ** one if needed, associats it with pDb, and returns. Returns NULL on @@ -1483,8 +1494,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env, */ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){ - (*env)->SetIntField(env, jOut, - s3jni_nphop_field( + (*env)->SetIntField(env, jOut, s3jni_nphop_field( env, &S3JniNphRefs.OutputPointer_Int32 ), (jint)v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); @@ -1496,8 +1506,7 @@ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, */ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){ - (*env)->SetLongField(env, jOut, - s3jni_nphop_field( + (*env)->SetLongField(env, jOut, s3jni_nphop_field( env, &S3JniNphRefs.OutputPointer_Int64 ), v); S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); @@ -1583,7 +1592,7 @@ static int encodingTypeIsValid(int eTextRep){ /* ** Proxy for Java-side CollationCallback.xCompare() callbacks. */ -static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, +static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, int nRhs, const void *rhs){ S3JniDb * const ps = pArg; S3JniDeclLocal_env; @@ -1611,8 +1620,8 @@ static int CollationState_xCompare(void *pArg, int nLhs, const void *lhs, return (int)rc; } -/* Collation finalizer for use by the sqlite3 internals. */ -static void CollationState_xDestroy(void *pArg){ +/* CollationCallback finalizer for use by the sqlite3 internals. */ +static void CollationCallback_xDestroy(void *pArg){ S3JniDb * const ps = pArg; S3JniDeclLocal_env; @@ -1654,7 +1663,6 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef co S3JniNphClass * const pNC = S3JniGlobal_nph(pRef); if( !pNC->midCtor ){ S3JniMutex_Nph_enter; - s3jni_incr( &SJG.metrics.nNphInit ); if( !pNC->midCtor ){ pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "", "()V"); S3JniExceptionIsFatal("Cannot find constructor for class."); @@ -2404,9 +2412,9 @@ 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 ){ + NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0); S3JniDb_set_aside(ps) /* MUST come after close() because of ps->trace. */; - NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0); } } #else @@ -2825,8 +2833,8 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), if( zName ){ S3JniMutex_S3JniDb_enter; rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, - ps, CollationState_xCompare, - CollationState_xDestroy); + ps, CollationCallback_xCompare, + CollationCallback_xDestroy); sqlite3_free(zName); if( 0==rc ){ S3JniHook_unref( &ps->hooks.collation, 1 ); @@ -3056,8 +3064,8 @@ S3JniApi(sqlite3_errstr(),jstring,1errstr)( S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( JniArgsEnvClass, jobject jpStmt ){ - sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); jstring rv = 0; + sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); if( pStmt ){ char * zSql = sqlite3_expanded_sql(pStmt); s3jni_oom_fatal(zSql); @@ -3072,7 +3080,8 @@ S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)( JniArgsEnvClass, jobject jpDb, jboolean onoff ){ - int const rc = sqlite3_extended_result_codes(PtrGet_sqlite3(jpDb), onoff ? 1 : 0); + sqlite3 * const pDb = PtrGet_sqlite3(jpDb); + int const rc = pDb ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) : 0; return rc ? JNI_TRUE : JNI_FALSE; } @@ -3098,7 +3107,9 @@ S3JniApi(sqlite3_interrupt(),void,1interrupt)( JniArgsEnvClass, jobject jpDb ){ sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - if( pDb ) sqlite3_interrupt(pDb); + if( pDb ){ + sqlite3_interrupt(pDb); + } } S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( @@ -3129,7 +3140,12 @@ JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){ S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( JniArgsEnvClass, jobject jpDb ){ - return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); + jlong rc = 0; + sqlite3 * const pDb = PtrGet_sqlite3(jpDb); + if( pDb ){ + rc = (jlong)sqlite3_last_insert_rowid(pDb); + } + return rc; } /* Pre-open() code common to sqlite3_open[_v2](). */ @@ -4254,15 +4270,13 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ printf("Mutex entry:" "\n\tglobal = %u" "\n\tenv = %u" - "\n\tnph = %u (%u for S3JniNphClass init, rest for " - "native pointer access)" + "\n\tnph = %u for S3JniNphClass init" "\n\tperDb = %u" "\n\tautoExt list = %u" - "\n\tS3JniUdf free-list = %u" + "\n\tS3JniUdf = %u (free-list)" "\n\tmetrics = %u\n", SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv, - SJG.metrics.nMutexEnv2, SJG.metrics.nNphInit, - SJG.metrics.nMutexPerDb, + SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf, SJG.metrics.nMetrics); puts("Allocs:"); diff --git a/manifest b/manifest index 85ec886d53..d123e5846f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\stool/mkctimec.tcl\sto\saccount\sfor\s[fe7365254d343e]. -D 2023-08-28T16:05:19.091 +C Remove\sa\spair\sof\swhat\sare\sarguably\sunnecessary\smutex\slocks\s(and\soften\shit).\sMore\sJNI-internal\scleanups. +D 2023-08-28T16:22:31.875 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -236,7 +236,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 0b76d7947e474aa30f6636f42a4e00eb48ccad6f920f1d2a1ee2d9540709c578 +F ext/jni/src/c/sqlite3-jni.c c92a764a728a3fcd34882defb7517c47af22c9f7fe184113015cdab9fe401c21 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 @@ -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 fe7365254d343ed03e11a4e9cad7f0e5d5182b9220c6fde6d30e434ebdaba2af -R b00a271c5a6f2785833f80aa9f660281 +P 349aac7e8d513bc420e8948b84cf715e454443439fdcd5aff0f2b13815a9cbb3 +R 3d489af3468d8a7b527ca6166e876cc6 U stephan -Z 28d128e867bc57e370acf90e968cbcdb +Z a77195f4742188b139cedf649b9d3b8f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 9782c0a682..b73fe40d28 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -349aac7e8d513bc420e8948b84cf715e454443439fdcd5aff0f2b13815a9cbb3 \ No newline at end of file +ecf07a0144dc6402b1e0924b1775d99dc465b27aa86a2718cac60a9b4c974312 \ No newline at end of file From d7def73c97db91b61c4757e349695ba298ef9d35 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 28 Aug 2023 16:28:00 +0000 Subject: [PATCH 101/422] Another fix for valgrind permutation. Remove the "copydir" table column - just add a [cp] or [copy] command to the test script where this functionality is required. FossilOrigin-Name: 448b9a575e66960a0885864f88b99b6ea15655c77a42eef956c09c8dc70e539a --- manifest | 12 ++++----- manifest.uuid | 2 +- test/testrunner.tcl | 59 +++++++++++++++++++++++++-------------------- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/manifest b/manifest index d2716b35cf..e558f78445 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\svalgrind\spermutation\son\sthis\sbranch. -D 2023-08-28T11:22:33.500 +C Another\sfix\sfor\svalgrind\spermutation.\sRemove\sthe\s"copydir"\stable\scolumn\s-\sjust\sadd\sa\s[cp]\sor\s[copy]\scommand\sto\sthe\stest\sscript\swhere\sthis\sfunctionality\sis\srequired. +D 2023-08-28T16:28:00.788 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1615,7 +1615,7 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 80b2145f2b12b87dada28b7bbbd6d7fa4d977fd9edae9c8a467beaa9c39ffee3 +F test/testrunner.tcl 139d4bfb5acd8b3bf45dbc3706c5fa4d226664d2540d5ccd3b4cf1034891fa01 F test/testrunner_data.tcl fdcc95d995fd1ef8bbaac1bc105988016213037038161bb555100439793ada18 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 @@ -2104,8 +2104,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 7596ea7074e0ac73312586ed3d28cdacf97f54b8af73f804cbc8066c94d4b4ef -R 79b21140462787dad9fdf55bfe6215a6 +P ccbf06760e8377f9209ba3fd4895a25fe1a5ea688c384bf357e765b6a2b4c2f7 +R ccaea0ec1aaf4f06cc5606ae7b37bb7e U dan -Z 5201645988ec9c127b26b5c606008acf +Z 7dad289e4c25c548365d88153d7b3fb1 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7b9c91a1e8..c89f16686a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ccbf06760e8377f9209ba3fd4895a25fe1a5ea688c384bf357e765b6a2b4c2f7 \ No newline at end of file +448b9a575e66960a0885864f88b99b6ea15655c77a42eef956c09c8dc70e539a \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index 92e7ff1bcc..8d1a739c23 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -155,18 +155,24 @@ switch -nocase -glob -- $tcl_platform(os) { set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture + set TRG(run) run.sh + set TRG(runcmd) "bash run.sh" } *linux* { set TRG(platform) linux set TRG(make) make.sh set TRG(makecmd) "bash make.sh" set TRG(testfixture) testfixture + set TRG(run) run.sh + set TRG(runcmd) "bash run.sh" } *win* { set TRG(platform) win set TRG(make) make.bat set TRG(makecmd) make.bat set TRG(testfixture) testfixture.exe + set TRG(run) run.bat + set TRG(runcmd) "run.bat" } default { error "cannot determine platform!" @@ -187,14 +193,13 @@ set TRG(schema) { */ CREATE TABLE jobs( /* Fields populated when db is initialized */ - jobid INTEGER PRIMARY KEY, -- id to identify job - displaytype TEXT NOT NULL, -- Type of test (for one line report) + jobid INTEGER PRIMARY KEY, -- id to identify job + displaytype TEXT NOT NULL, -- Type of test (for one line report) displayname TEXT NOT NULL, -- Human readable job name build TEXT NOT NULL DEFAULT '', -- make.sh/make.bat file request, if any dirname TEXT NOT NULL DEFAULT '', -- directory name, if required cmd TEXT NOT NULL, -- shell command to run depid INTEGER, -- identifier of dependency (or '') - copydir TEXT, -- copy files from here priority INTEGER NOT NULL, -- higher priority jobs may run earlier /* Fields updated as jobs run */ @@ -458,12 +463,6 @@ proc create_or_clear_dir {dir} { } } -proc copy_dir {from to} { - foreach f [glob -nocomplain [file join $from *]] { - catch { file copy -force $f $to } - } -} - proc build_to_dirname {bname} { set fold [string tolower [string map {- _} $bname]] return "testrunner_build_$fold" @@ -531,7 +530,6 @@ proc r_get_next_job {iJob} { # -dirname # -cmd # -depid -# -copydir # -priority # # Returns the jobid value for the new job. @@ -540,13 +538,12 @@ proc add_job {args} { set options { -displaytype -displayname -build -dirname - -cmd -depid -copydir -priority + -cmd -depid -priority } # Set default values of options. set A(-dirname) "" set A(-depid) "" - set A(-copydir) "" set A(-priority) 0 set A(-build) "" @@ -565,7 +562,7 @@ proc add_job {args} { trdb eval { INSERT INTO jobs( - displaytype, displayname, build, dirname, cmd, depid, copydir, priority, + displaytype, displayname, build, dirname, cmd, depid, priority, state ) VALUES ( $A(-displaytype), @@ -574,7 +571,6 @@ proc add_job {args} { $A(-dirname), $A(-cmd), $A(-depid), - $A(-copydir), $A(-priority), $state ) @@ -595,7 +591,8 @@ proc add_tcl_jobs {build config patternlist} { set testfixture [file join [lindex $build 1] $TRG(testfixture)] } if {[lindex $build 2]=="Valgrind"} { - set testfixture "valgrind -v --error-exitcode=1 $testfixture" + set setvar "export OMIT_MISUSE=1\n" + set testfixture "${setvar}valgrind -v --error-exitcode=1 $testfixture" } # The ::testspec array is populated by permutations.test @@ -662,11 +659,17 @@ proc add_build_job {buildname target} { proc add_make_job {bld target} { global TRG + if {$TRG(platform)=="win"} { + set cmd "copy [lindex $bld 1]\\* ." + } else { + set cmd "cp -r [lindex $bld 1]/* ." + } + append cmd "\n$TRG(makecmd) $target" + add_job \ -displaytype make \ -displayname "[lindex $bld 2] make $target" \ - -cmd "$TRG(makecmd) $target" \ - -copydir [lindex $bld 1] \ + -cmd $cmd \ -depid [lindex $bld 0] \ -priority 1 } @@ -808,6 +811,14 @@ proc script_input_ready {fd iJob jobid} { if {[eof $fd]} { trdb eval { SELECT * FROM jobs WHERE jobid=$jobid } job {} + # If this job specified a directory name, then delete the run.sh/run.bat + # file from it before continuing. This is because the contents of this + # directory might be copied by some other job, and we don't want to copy + # the run.sh file in this case. + if {$job(dirname)!=""} { + file delete -force [file join $job(dirname) $TRG(run)] + } + set ::done 1 fconfigure $fd -blocking 1 set state "done" @@ -816,7 +827,7 @@ proc script_input_ready {fd iJob jobid} { if {[info exists TRG(reportlength)]} { puts -nonewline "[string repeat " " $TRG(reportlength)]\r" } - puts "FAILED: $job(displayname)" + puts "FAILED: $job(displayname) ($iJob)" set state "failed" } @@ -886,17 +897,13 @@ proc launch_another_job {iJob} { close $fd } - if {$job(copydir)!=""} { - foreach f [glob -nocomplain [file join $job(copydir) *]] { - catch { file copy -force $f $dir } - } - } - set pwd [pwd] cd $dir - set fd [open "|$job(cmd) 2>@1" r] + set fd [open $TRG(run) w] + puts $fd $job(cmd) + close $fd + set fd [open "|$TRG(runcmd) 2>@1" r] cd $pwd - set pid [pid $fd] fconfigure $fd -blocking false fileevent $fd readable [list script_input_ready $fd $iJob $job(jobid)] From 9b0e9f908932d6d5a9c0cb80f081e2c7acf994d4 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 28 Aug 2023 18:15:14 +0000 Subject: [PATCH 102/422] Fixes for testrunner.tcl on windows. FossilOrigin-Name: 26ae090889640bfcf02a6dd5aa58044edd4d0708a11d8e005149f1a3c8495bba --- manifest | 14 +++++++------- manifest.uuid | 2 +- test/testrunner.tcl | 33 ++++++++++++++++++++++++++++++++- test/testrunner_data.tcl | 10 +++++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/manifest b/manifest index e558f78445..a24ea97ded 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Another\sfix\sfor\svalgrind\spermutation.\sRemove\sthe\s"copydir"\stable\scolumn\s-\sjust\sadd\sa\s[cp]\sor\s[copy]\scommand\sto\sthe\stest\sscript\swhere\sthis\sfunctionality\sis\srequired. -D 2023-08-28T16:28:00.788 +C Fixes\sfor\stestrunner.tcl\son\swindows. +D 2023-08-28T18:15:14.092 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1615,8 +1615,8 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163 F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede -F test/testrunner.tcl 139d4bfb5acd8b3bf45dbc3706c5fa4d226664d2540d5ccd3b4cf1034891fa01 -F test/testrunner_data.tcl fdcc95d995fd1ef8bbaac1bc105988016213037038161bb555100439793ada18 +F test/testrunner.tcl c88eae7d8ba9825d09f080ee2aa98b8e65c381bb56b4d427fb492625d2d4c36b +F test/testrunner_data.tcl 12b12a5661a8b6b005ec8ca6c5ad37f6847321c2cf5982d828aeea114bb1a579 F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -2104,8 +2104,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 ccbf06760e8377f9209ba3fd4895a25fe1a5ea688c384bf357e765b6a2b4c2f7 -R ccaea0ec1aaf4f06cc5606ae7b37bb7e +P 448b9a575e66960a0885864f88b99b6ea15655c77a42eef956c09c8dc70e539a +R 9124fb12ae32ed43e0e40441bac52bec U dan -Z 7dad289e4c25c548365d88153d7b3fb1 +Z 400d2ab7933216b29ca834eae65f3a8d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c89f16686a..6b4b39473b 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -448b9a575e66960a0885864f88b99b6ea15655c77a42eef956c09c8dc70e539a \ No newline at end of file +26ae090889640bfcf02a6dd5aa58044edd4d0708a11d8e005149f1a3c8495bba \ No newline at end of file diff --git a/test/testrunner.tcl b/test/testrunner.tcl index 8d1a739c23..9946d88649 100644 --- a/test/testrunner.tcl +++ b/test/testrunner.tcl @@ -190,6 +190,36 @@ set TRG(schema) { /* ** This table contains one row for each job that testrunner.tcl must run ** before the entire test run is finished. + ** + ** jobid: + ** Unique identifier for each job. Must be a +ve non-zero number. + ** + ** displaytype: + ** 3 or 4 letter mnemonic for the class of tests this belongs to e.g. + ** "fuzz", "tcl", "make" etc. + ** + ** displayname: + ** Name/description of job. For display purposes. + ** + ** build: + ** If the job requires a make.bat/make.sh make wrapper (i.e. to build + ** something), the name of the build configuration it uses. See + ** testrunner_data.tcl for a list of build configs. e.g. "Win32-MemDebug". + ** + ** dirname: + ** If the job should use a well-known directory name for its + ** sub-directory instead of an anonymous "testdir[1234...]" sub-dir + ** that is deleted after the job is finished. + ** + ** cmd: + ** Bash or batch script to run the job. + ** + ** depid: + ** The jobid value of a job that this job depends on. This job may not + ** be run before its depid job has finished successfully. + ** + ** priority: + ** Higher values run first. Sometimes. */ CREATE TABLE jobs( /* Fields populated when db is initialized */ @@ -660,7 +690,8 @@ proc add_make_job {bld target} { global TRG if {$TRG(platform)=="win"} { - set cmd "copy [lindex $bld 1]\\* ." + set path [string map {/ \\} [lindex $bld 1]] + set cmd "xcopy /S $path\\* ." } else { set cmd "cp -r [lindex $bld 1]/* ." } diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl index 1fead5b4e2..e39a9a2869 100644 --- a/test/testrunner_data.tcl +++ b/test/testrunner_data.tcl @@ -398,14 +398,14 @@ proc trd_extras {platform bld} { # proc trd_fuzztest_data {} { set EXE "" - if {$::tcl_platform(platform)=="windows"} { - set EXE ".exe" - } - set lFuzzDb [glob [file join $::testdir fuzzdata*.db]] set lSessionDb [glob [file join $::testdir sessionfuzz-data*.db]] - return [list fuzzcheck$EXE $lFuzzDb "sessionfuzz$EXE run" $lSessionDb] + if {$::tcl_platform(platform)=="windows"} { + return [list fuzzcheck.exe $lFuzzDb] + } + + return [list fuzzcheck $lFuzzDb sessionfuzz $lSessionDb] } From ecdc5b3e6f421de46780bab7b65e527df11d1531 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 28 Aug 2023 20:02:59 +0000 Subject: [PATCH 103/422] Fix the "sdevtest" testrunner.tcl command so that it correctly enables asan on windows. FossilOrigin-Name: 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b --- manifest | 12 ++++++------ manifest.uuid | 2 +- test/testrunner_data.tcl | 17 ++++++++++++----- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/manifest b/manifest index a24ea97ded..47a70ff455 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sfor\stestrunner.tcl\son\swindows. -D 2023-08-28T18:15:14.092 +C Fix\sthe\s"sdevtest"\stestrunner.tcl\scommand\sso\sthat\sit\scorrectly\senables\sasan\son\swindows. +D 2023-08-28T20:02:59.639 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -1616,7 +1616,7 @@ F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637 F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede F test/testrunner.tcl c88eae7d8ba9825d09f080ee2aa98b8e65c381bb56b4d427fb492625d2d4c36b -F test/testrunner_data.tcl 12b12a5661a8b6b005ec8ca6c5ad37f6847321c2cf5982d828aeea114bb1a579 +F test/testrunner_data.tcl 7d698b094b66b2a9849c6a592fe7083fd76b382257d49985b3487f2f5bf412ad F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899 F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 @@ -2104,8 +2104,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 448b9a575e66960a0885864f88b99b6ea15655c77a42eef956c09c8dc70e539a -R 9124fb12ae32ed43e0e40441bac52bec +P 26ae090889640bfcf02a6dd5aa58044edd4d0708a11d8e005149f1a3c8495bba +R 72ed41043ab228a8430556867561403b U dan -Z 400d2ab7933216b29ca834eae65f3a8d +Z 47e21353f9923e8c0acadea18c12278f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6b4b39473b..78bd4bf0d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -26ae090889640bfcf02a6dd5aa58044edd4d0708a11d8e005149f1a3c8495bba \ No newline at end of file +6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b \ No newline at end of file diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl index e39a9a2869..381bb3b461 100644 --- a/test/testrunner_data.tcl +++ b/test/testrunner_data.tcl @@ -539,7 +539,6 @@ proc make_script {cfg srcdir bMsvc} { default { error "Cannot translate $param for MSVC" } - } } @@ -547,11 +546,19 @@ proc make_script {cfg srcdir bMsvc} { } if {[string range $param 0 0]=="-"} { - if {$bMsvc && [regexp -- {^-O(\d+)$} $param -> level]} { - lappend makeOpts OPTIMIZATIONS=$level - } else { - lappend cflags $param + + if {$bMsvc} { + if {[regexp -- {^-O(\d+)$} $param -> level]} { + lappend makeOpts OPTIMIZATIONS=$level + continue + } + if {$param eq "-fsanitize=address,undefined"} { + lappend makeOpts ASAN=1 + continue + } } + + lappend cflags $param continue } From a08f73750375249268a599ca228cef618fbc51f8 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 20:21:56 +0000 Subject: [PATCH 104/422] Correct JNI mapping of collations to be 1-db-to-many-collations. FossilOrigin-Name: b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f --- ext/jni/src/c/sqlite3-jni.c | 511 ++++++++++-------- ext/jni/src/org/sqlite/jni/Tester1.java | 9 +- .../src/org/sqlite/jni/XDestroyCallback.java | 8 + manifest | 19 +- manifest.uuid | 2 +- 5 files changed, 298 insertions(+), 251 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index a78b07166b..dfa929ce90 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -429,7 +429,6 @@ struct S3JniDb { SQLITE_DBCONFIG_MAINDBNAME. */; struct { S3JniHook busyHandler; - S3JniHook collation; S3JniHook collationNeeded; S3JniHook commit; S3JniHook progress; @@ -1021,12 +1020,14 @@ static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){ ** problem extracting the exception's message, it's treated as ** non-fatal and zDfltMsg is used in its place. ** +** Locks the global S3JniDb mutex. +** ** This must only be called if a JNI exception is pending. ** ** Returns errCode unless it is 0, in which case SQLITE_ERROR is ** returned. */ -static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, +static int s3jni__db_exception(JNIEnv * const env, S3JniDb * const ps, int errCode, const char *zDfltMsg){ jthrowable const ex = (*env)->ExceptionOccurred(env); @@ -1034,13 +1035,17 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, if( ex ){ char * zMsg; S3JniExceptionClear; + S3JniMutex_S3JniDb_enter; zMsg = s3jni_exception_error_msg(env, ex); s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg); sqlite3_free(zMsg); S3JniUnrefLocal(ex); + S3JniMutex_S3JniDb_leave; } return errCode; } +#define s3jni_db_exception(JniDb,ERRCODE,DFLTMSG) \ + s3jni__db_exception(env, (JniDb), (ERRCODE), (DFLTMSG) ) /* ** Extracts the (void xDestroy()) method from jObj and applies it to @@ -1049,7 +1054,7 @@ static int s3jni_db_exception(JNIEnv * const env, S3JniDb * const ps, ** trigger a warning to stdout or stderr and then the exception is ** suppressed. */ -static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ +static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){ if( jObj ){ jclass const klazz = (*env)->GetObjectClass(env, jObj); jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); @@ -1068,25 +1073,7 @@ static void s3jni_call_xDestroy(JNIEnv * const env, jobject jObj){ } } } - -/* -** Removes any Java references from s and clears its state. If -** doXDestroy is true and s->jObj is not NULL, s->jObj's -** s is passed to s3jni_call_xDestroy() before any references are -** cleared. It is legal to call this when the object has no Java -** references. -*/ -static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s, - int doXDestroy){ - if( s->jObj ){ - if( doXDestroy ){ - s3jni_call_xDestroy(env, s->jObj); - } - S3JniUnrefGlobal(s->jObj); - } - memset(s, 0, sizeof(*s)); -} -#define S3JniHook_unref(H,X) S3JniHook_unref_impl(env, (H), (X)) +#define s3jni_call_xDestroy(JOBJ) s3jni__call_xDestroy(env, (JOBJ)) /* ** Internal helper for many hook callback impls. Locks the S3JniDb @@ -1105,15 +1092,36 @@ static void S3JniHook_unref_impl(JNIEnv * const env, S3JniHook * const s, ** another thread modify the hook while we're running it. That copy ** has to have its own Java reference, but it need only be call-local. */ -static void S3JniHook_localdup( JNIEnv * const env, S3JniHook const * const src, - S3JniHook * const dest ){ +static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src, + S3JniHook * const dest ){ S3JniMutex_S3JniDb_enter; *dest = *src; if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj); S3JniMutex_S3JniDb_leave; } +#define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest) #define S3JniHook_localundup(HOOK) S3JniUnrefLocal(HOOK.jObj) +/* +** Removes any Java references from s and clears its state. If +** doXDestroy is true and s->jObj is not NULL, s->jObj's +** s is passed to s3jni_call_xDestroy() before any references are +** cleared. It is legal to call this when the object has no Java +** references. +*/ +static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s, + int doXDestroy){ + if( s->jObj ){ + if( doXDestroy ){ + s3jni_call_xDestroy(s->jObj); + } + S3JniUnrefGlobal(s->jObj); + } + memset(s, 0, sizeof(*s)); +} +#define S3JniHook_unref(hook,doDestroy) \ + S3JniHook__unref(env, (hook), (doDestroy)) + /* ** Clears all of s's state. Requires that that the caller has locked ** S3JniGlobal.perDb.mutex. Make sure to do anything needed with @@ -1125,7 +1133,6 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){ #define UNHOOK(MEMBER) S3JniHook_unref(&s->hooks.MEMBER, 0) UNHOOK(auth); UNHOOK(busyHandler); - UNHOOK(collation); UNHOOK(collationNeeded); UNHOOK(commit); UNHOOK(progress); @@ -1163,10 +1170,11 @@ static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){ SJG.perDb.aFree = s; } } +#define S3JniDb_set_aside_unlocked(JniDb) S3JniDb__set_aside_unlocked(env, JniDb) static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){ S3JniMutex_S3JniDb_enter; - S3JniDb__set_aside_unlocked(env, s); + S3JniDb_set_aside_unlocked(s); S3JniMutex_S3JniDb_leave; } #define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB) @@ -1294,10 +1302,8 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){ */ static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef, jobject ppOut, const void * p){ - jfieldID const fid = s3jni_nphop_field(env, pRef); - assert( ppOut ); - (*env)->SetLongField(env, ppOut, fid, (jlong)p); + (*env)->SetLongField(env, ppOut, s3jni_nphop_field(env, pRef), (jlong)p); S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer."); } @@ -1313,8 +1319,7 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj, S3JniNphRef const* pRef){ void * rv = 0; if( pObj ){ - jfieldID const fid = s3jni_nphop_field(env, pRef); - rv = (void*)(*env)->GetLongField(env, pObj, fid); + rv = (void*)(*env)->GetLongField(env, pObj, s3jni_nphop_field(env, pRef)); S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); } return rv; @@ -1427,7 +1432,7 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){ /* An experiment */ //#define CLOSE_DB_LOCKED -#if defined(CLOSE_DB_LOCKED) +#if 1 || defined(CLOSE_DB_LOCKED) static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){ S3JniDb * s = 0; sqlite3 * pDb = 0; @@ -1436,7 +1441,9 @@ static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){ if( jDb ) pDb = PtrGet_sqlite3(jDb); S3JniDb_search; return s; + } +#define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB)) #endif /* @@ -1589,45 +1596,44 @@ static int encodingTypeIsValid(int eTextRep){ } } +/** + State for CollationCallbacks. +*/ +typedef S3JniHook S3JniCollationCallback; + /* ** Proxy for Java-side CollationCallback.xCompare() callbacks. */ static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, int nRhs, const void *rhs){ - S3JniDb * const ps = pArg; + S3JniCollationCallback * const pCC = pArg; S3JniDeclLocal_env; jint rc = 0; - S3JniHook hook; - - S3JniHook_localdup(env, &ps->hooks.collation, &hook ); - if( hook.jObj ){ + if( pCC->jObj ){ jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs); jbyteArray jbaRhs = jbaLhs ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0; if( !jbaRhs ){ S3JniUnrefLocal(jbaLhs); - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + /* We have no recovery strategy here. */ + s3jni_oom_check( jbaRhs ); return 0; } - rc = (*env)->CallIntMethod(env, ps->hooks.collation.jObj, - ps->hooks.collation.midCallback, + rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback, jbaLhs, jbaRhs); S3JniExceptionIgnore; S3JniUnrefLocal(jbaLhs); S3JniUnrefLocal(jbaRhs); - S3JniHook_localundup(hook); } return (int)rc; } /* CollationCallback finalizer for use by the sqlite3 internals. */ static void CollationCallback_xDestroy(void *pArg){ - S3JniDb * const ps = pArg; + S3JniCollationCallback * const pCC = pArg; S3JniDeclLocal_env; - - S3JniMutex_S3JniDb_enter; - S3JniHook_unref(&ps->hooks.collation, 1); - S3JniMutex_S3JniDb_leave; + S3JniHook_unref(pCC, 1); + sqlite3_free(pCC); } /* For use with sqlite3_result/value_pointer() */ @@ -1763,11 +1769,13 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s, int cacheIt){ assert( !s->pNext ); - s3jni_call_xDestroy(env, s->jObj); - S3JniUnrefGlobal(s->jObj); - sqlite3_free(s->zFuncName); - assert( !s->pNext ); - memset(s, 0, sizeof(*s)); + if( s->jObj ){ + s3jni_call_xDestroy(s->jObj); + S3JniUnrefGlobal(s->jObj); + sqlite3_free(s->zFuncName); + assert( !s->pNext ); + memset(s, 0, sizeof(*s)); + } if( cacheIt ){ S3JniMutex_Global_enter; s->pNext = S3JniGlobal.udf.aFree; @@ -2309,13 +2317,13 @@ static int s3jni_busy_handler(void* pState, int n){ S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_localdup(env, &ps->hooks.busyHandler, &hook ); + S3JniHook_localdup(&ps->hooks.busyHandler, &hook ); if( hook.jObj ){ rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)n); S3JniIfThrew{ S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback"); - rc = s3jni_db_exception(env, ps, SQLITE_ERROR, + rc = s3jni_db_exception(ps, SQLITE_ERROR, "sqlite3_busy_handler() callback threw."); } S3JniHook_localundup(hook); @@ -2328,33 +2336,42 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( ){ S3JniDb * const ps = S3JniDb_from_java(jDb); S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0; + S3JniHook hook = S3JniHook_empty; int rc = 0; + if( !ps ) return (jint)SQLITE_MISUSE; S3JniMutex_S3JniDb_enter; if( jBusy ){ if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){ /* Same object - this is a no-op. */ - S3JniMutex_S3JniDb_leave; - return 0; + }else{ + jclass const klazz = (*env)->GetObjectClass(env, jBusy); + hook.jObj = S3JniRefGlobal(jBusy); + hook.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); + S3JniUnrefLocal(klazz); + S3JniIfThrew { + rc = SQLITE_ERROR; + } } - jclass klazz; - S3JniHook_unref(pHook, 0); - pHook->jObj = S3JniRefGlobal(jBusy); - klazz = (*env)->GetObjectClass(env, jBusy); - pHook->midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); - S3JniUnrefLocal(klazz); - S3JniIfThrew { - S3JniHook_unref(pHook, 0); - rc = SQLITE_ERROR; - } - }else{ - S3JniHook_unref(pHook, 0); } if( 0==rc ){ - rc = jBusy - ? sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps) - : sqlite3_busy_handler(ps->pDb, 0, 0); + if( jBusy ){ + if( hook.jObj ){ /* Replace handler */ + rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps); + if( 0==rc ){ + S3JniHook_unref(pHook, 0); + *pHook = hook; + hook = S3JniHook_empty; + } + }/* else no-op */ + }else{ /* Clear handler */ + rc = sqlite3_busy_handler(ps->pDb, 0, 0); + if( 0==rc ){ + S3JniHook_unref(pHook, 0); + } + } } + S3JniHook_unref(&hook, 0); S3JniMutex_S3JniDb_leave; return rc; } @@ -2388,9 +2405,8 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( /* Move final entry into this slot. */ --SJG.autoExt.nExt; *ax = SJG.autoExt.aExt[SJG.autoExt.nExt]; - memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, - sizeof(S3JniAutoExtension)); - assert(! SJG.autoExt.aExt[SJG.autoExt.nExt].jObj ); + memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, sizeof(*ax)); + assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj ); rc = JNI_TRUE; break; } @@ -2403,9 +2419,9 @@ 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 ){ rc = 1==version @@ -2429,14 +2445,14 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){ if( 0!=jDb ){ S3JniDb * ps; S3JniMutex_S3JniDb_enter; - ps = S3JniDb__from_java_unlocked(env, jDb); + ps = S3JniDb_from_java_unlocked(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. */; + S3JniDb_set_aside_unlocked(ps) + /* MUST come after close() because of ps->hooks.trace. */; NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0); } }else{ @@ -2478,25 +2494,29 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_localdup(env, &ps->hooks.collationNeeded, &hook ); + S3JniHook_localdup(&ps->hooks.collationNeeded, &hook ); if( hook.jObj ){ unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); - s3jni_oom_check( jName ); S3JniIfThrew{ - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); S3JniExceptionClear; }else{ - (*env)->CallVoidMethod(env, ps->hooks.collationNeeded.jObj, - ps->hooks.collationNeeded.midCallback, - ps->jDb, (jint)eTextRep, jName); - S3JniIfThrew{ - S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); - s3jni_db_exception(env, ps, 0, "sqlite3_collation_needed() callback threw"); + jobject jDb; + S3JniMutex_S3JniDb_enter; + jDb = ps->jDb ? S3JniRefLocal(ps->jDb) : 0; + S3JniMutex_S3JniDb_leave; + if( jDb ){ + (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, + jDb, (jint)eTextRep, jName); + S3JniIfThrew{ + S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); + s3jni_db_exception(ps, 0, "sqlite3_collation_needed() callback threw"); + } + S3JniUnrefLocal(jDb); } - S3JniUnrefLocal(jName); } + S3JniUnrefLocal(jName); S3JniHook_localundup(hook); } } @@ -2504,18 +2524,25 @@ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( JniArgsEnvClass, jobject jDb, jobject jHook ){ - S3JniDb * const ps = S3JniDb_from_java(jDb); - S3JniHook * const pHook = &ps->hooks.collationNeeded; + S3JniDb * ps; + S3JniHook * pHook; int rc = 0; - if( !ps ) return SQLITE_MISUSE; S3JniMutex_S3JniDb_enter; + ps = S3JniDb_from_java_unlocked(jDb); + if( !ps ){ + S3JniMutex_S3JniDb_leave; + return SQLITE_MISUSE; + } + pHook = &ps->hooks.collationNeeded; if( pHook->jObj && jHook && (*env)->IsSameObject(env, pHook->jObj, jHook) ){ /* no-op */ }else if( !jHook ){ - S3JniHook_unref(pHook, 0); - sqlite3_collation_needed(ps->pDb, 0, 0); + rc = sqlite3_collation_needed(ps->pDb, 0, 0); + if( 0==rc ){ + S3JniHook_unref(pHook, 0); + } }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); jmethodID const xCallback = (*env)->GetMethodID( @@ -2523,13 +2550,12 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( ); S3JniUnrefLocal(klazz); S3JniIfThrew { - rc = s3jni_db_exception(env, ps, SQLITE_MISUSE, - "Cannot not find matching callback on " - "collation-needed hook object."); + rc = s3jni_db_exception(ps, SQLITE_MISUSE, + "Cannot not find matching call() in " + "CollationNeededCallback object."); }else{ rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); - if( rc ){ - }else{ + if( 0==rc ){ S3JniHook_unref(pHook, 0); pHook->midCallback = xCallback; pHook->jObj = S3JniRefGlobal(jHook); @@ -2603,15 +2629,17 @@ S3JniApi(sqlite3_column_value(),jobject,1column_1value)( return new_sqlite3_value_wrapper(env, sv); } -static int s3jni_commit_rollback_hook_impl(int isCommit, - S3JniDb * const ps){ +/* +** Impl for both commit hooks (if isCommit is true) or rollback hooks. +*/ +static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ S3JniDeclLocal_env; int rc = 0; S3JniHook hook; - S3JniHook_localdup( env, isCommit - ? &ps->hooks.commit : &ps->hooks.rollback, - &hook); + S3JniHook_localdup(isCommit + ? &ps->hooks.commit : &ps->hooks.rollback, + &hook); if( hook.jObj ){ rc = isCommit ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) @@ -2625,32 +2653,35 @@ static int s3jni_commit_rollback_hook_impl(int isCommit, return rc; } +/* C-to-Java commit hook wrapper. */ static int s3jni_commit_hook_impl(void *pP){ return s3jni_commit_rollback_hook_impl(1, pP); } +/* C-to-Java rollback hook wrapper. */ 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){ - S3JniDb * const ps = S3JniDb_from_java(jDb); - jclass klazz; + S3JniDb * ps; jobject pOld = 0; - jmethodID xCallback; - S3JniHook * const pHook = - isCommit ? &ps->hooks.commit : &ps->hooks.rollback; + S3JniHook * pHook; + + S3JniMutex_S3JniDb_enter; + ps = S3JniDb_from_java_unlocked(jDb); if( !ps ){ s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + S3JniMutex_S3JniDb_leave; return 0; } + pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback; pOld = pHook->jObj; if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){ - return pOld; - } - if( !jHook ){ + /* No-op. */ + }else if( !jHook ){ if( pOld ){ jobject tmp = S3JniRefLocal(pOld); S3JniUnrefGlobal(pOld); @@ -2659,29 +2690,30 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, memset(pHook, 0, sizeof(S3JniHook)); if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0); else sqlite3_rollback_hook(ps->pDb, 0, 0); - return pOld; - } - klazz = (*env)->GetObjectClass(env, jHook); - xCallback = (*env)->GetMethodID(env, klazz, "call", - isCommit ? "()I" : "()V"); - S3JniUnrefLocal(klazz); - S3JniIfThrew { - S3JniExceptionReport; - S3JniExceptionClear; - s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Cannot not find matching callback on " - "hook object."); }else{ - pHook->midCallback = xCallback; - pHook->jObj = S3JniRefGlobal(jHook); - if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps); - else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps); - if( pOld ){ - jobject tmp = S3JniRefLocal(pOld); - S3JniUnrefGlobal(pOld); - pOld = tmp; + jclass const klazz = (*env)->GetObjectClass(env, jHook); + jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", + isCommit ? "()I" : "()V"); + S3JniUnrefLocal(klazz); + S3JniIfThrew { + S3JniExceptionReport; + S3JniExceptionClear; + s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Cannot not find matching call() in" + "hook object."); + }else{ + pHook->midCallback = xCallback; + pHook->jObj = S3JniRefGlobal(jHook); + if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps); + else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps); + if( pOld ){ + jobject tmp = S3JniRefLocal(pOld); + S3JniUnrefGlobal(pOld); + pOld = tmp; + } } } + S3JniMutex_S3JniDb_leave; return pOld; } @@ -2733,7 +2765,7 @@ static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int S3JniHook hook = S3JniHook_empty; if( ps ){ - S3JniHook_localdup(env, &SJG.hooks.sqllog, &hook); + S3JniHook_localdup(&SJG.hooks.sqllog, &hook); } if( !hook.jObj ) return; jArg0 = S3JniRefLocal(ps->jDb); @@ -2775,7 +2807,10 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, S3JniMutex_Global_enter; if( !jLog ){ - S3JniHook_unref(pHook, 0); + rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); + if( 0==rc ){ + S3JniHook_unref(pHook, 0); + } }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ /* No-op */ }else { @@ -2825,26 +2860,28 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); S3JniUnrefLocal(klazz); S3JniIfThrew{ - S3JniUnrefLocal(klazz); rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Could not get xCompare() method for object."); + "Could not get call() method from " + "CollationCallback object."); }else{ char * const zName = s3jni_jstring_to_utf8( name, 0); - if( zName ){ - S3JniMutex_S3JniDb_enter; + S3JniCollationCallback * const pCC = + zName ? s3jni_malloc(sizeof(S3JniCollationCallback)) : 0; + if( pCC ){ + memset( pCC, 0, sizeof(*pCC) ); rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, - ps, CollationCallback_xCompare, + pCC, CollationCallback_xCompare, CollationCallback_xDestroy); - sqlite3_free(zName); if( 0==rc ){ - S3JniHook_unref( &ps->hooks.collation, 1 ); - ps->hooks.collation.midCallback = midCallback; - ps->hooks.collation.jObj = S3JniRefGlobal(oCollation); + pCC->midCallback = midCallback; + pCC->jObj = S3JniRefGlobal(oCollation); + }else{ + CollationCallback_xDestroy(pCC); } - S3JniMutex_S3JniDb_leave; }else{ rc = SQLITE_NOMEM; } + sqlite3_free(zName); } return (jint)rc; } @@ -3363,7 +3400,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, const int isPre = 0!=pDb; S3JniHook hook; - S3JniHook_localdup(env, isPre ? + S3JniHook_localdup(isPre ? #ifdef SQLITE_ENABLE_PREUPDATE_HOOK &ps->hooks.preUpdate #else @@ -3392,7 +3429,7 @@ static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, (jint)opId, jDbName, jTable, (jlong)iKey1); S3JniIfThrew{ S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback"); - s3jni_db_exception(env, ps, 0, + s3jni_db_exception(ps, 0, "sqlite3_(pre)update_hook() callback threw"); } } @@ -3516,19 +3553,21 @@ S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)( 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); - S3JniUnrefLocal(pWrap); - }else{ - rc = SQLITE_NOMEM; + int rc = SQLITE_MISUSE; + if( pDb ){ + sqlite3_value * pOut = 0; + 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); + S3JniUnrefLocal(pWrap); + }else{ + rc = SQLITE_NOMEM; + } } } return rc; @@ -3557,11 +3596,11 @@ static int s3jni_progress_handler_impl(void *pP){ S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_localdup( env, &ps->hooks.progress, &hook ); + S3JniHook_localdup(&ps->hooks.progress, &hook); if( hook.jObj ){ rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback); S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, rc, + rc = s3jni_db_exception(ps, rc, "sqlite3_progress_handler() callback threw"); } S3JniHook_localundup(hook); @@ -3573,8 +3612,6 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( JniArgsEnvClass,jobject jDb, jint n, jobject jProgress ){ S3JniDb * const ps = S3JniDb_from_java(jDb); - jclass klazz; - jmethodID xCallback; S3JniHook * const pHook = ps ? &ps->hooks.progress : 0; if( !ps ) return; @@ -3582,22 +3619,21 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( if( n<1 || !jProgress ){ S3JniHook_unref(pHook, 0); sqlite3_progress_handler(ps->pDb, 0, 0, 0); - S3JniMutex_S3JniDb_leave; - return; - } - klazz = (*env)->GetObjectClass(env, jProgress); - xCallback = (*env)->GetMethodID(env, klazz, "call", "()I"); - S3JniUnrefLocal(klazz); - S3JniIfThrew { - S3JniExceptionClear; - s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Cannot not find matching xCallback() on " - "ProgressHandler object."); }else{ - S3JniUnrefGlobal(pHook->jObj); - pHook->midCallback = xCallback; - pHook->jObj = S3JniRefGlobal(jProgress); - sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); + jclass const klazz = (*env)->GetObjectClass(env, jProgress); + jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", "()I"); + S3JniUnrefLocal(klazz); + S3JniIfThrew { + S3JniExceptionClear; + s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Cannot not find matching xCallback() on " + "ProgressHandler object."); + }else{ + S3JniUnrefGlobal(pHook->jObj); + pHook->midCallback = xCallback; + pHook->jObj = S3JniRefGlobal(jProgress); + sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); + } } S3JniMutex_S3JniDb_leave; } @@ -3752,7 +3788,7 @@ S3JniApi(sqlite3_result_error(),void,1result_1error)( S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)( JniArgsEnvClass, jobject jpCx, jint v ){ - sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), v ? (int)v : SQLITE_ERROR); + sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), (int)v); } S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)( @@ -3850,7 +3886,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, S3JniHook hook; int rc = 0; - S3JniHook_localdup(env, &ps->hooks.auth, &hook ); + S3JniHook_localdup(&ps->hooks.auth, &hook ); if( hook.jObj ){ jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0; jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0; @@ -3860,7 +3896,7 @@ int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op, s0, s1, s3, s3); S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, rc, "sqlite3_set_authorizer() callback"); + rc = s3jni_db_exception(ps, rc, "sqlite3_set_authorizer() callback"); } S3JniUnrefLocal(s0); S3JniUnrefLocal(s1); @@ -3924,6 +3960,52 @@ S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)( (sqlite3_int64)rowId); } +S3JniApi(sqlite3_shutdown(),jint,1shutdown)( + JniArgsEnvClass +){ + s3jni_reset_auto_extension(env); + /* Free up env cache. */ + S3JniMutex_Env_enter; + while( SJG.envCache.aHead ){ + S3JniEnv_uncache( SJG.envCache.aHead->env ); + } + S3JniMutex_Env_leave; + /* Free up S3JniUdf recycling bin. */ + S3JniMutex_Global_enter; + while( S3JniGlobal.udf.aFree ){ + S3JniUdf * const u = S3JniGlobal.udf.aFree; + S3JniGlobal.udf.aFree = u->pNext; + u->pNext = 0; + S3JniUdf_free(env, u, 0); + } + S3JniMutex_Global_leave; + /* Free up S3JniDb recycling bin. */ + S3JniMutex_S3JniDb_enter; + while( S3JniGlobal.perDb.aFree ){ + S3JniDb * const d = S3JniGlobal.perDb.aFree; + S3JniGlobal.perDb.aFree = d->pNext; + d->pNext = 0; + S3JniDb_clear(env, d); + sqlite3_free(d); + } + S3JniMutex_S3JniDb_leave; +#if 0 + /* + ** Is automatically closing any still-open dbs a good idea? We will + ** get rid of the perDb list once sqlite3 gets a per-db client + ** state, at which point we won't have a central list of databases + ** to close. + */ + S3JniMutex_S3JniDb_enter; + while( SJG.perDb.pHead ){ + s3jni_close_db(env, SJG.perDb.pHead->jDb, 2); + } + S3JniMutex_S3JniDb_leave; +#endif + /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */ + return sqlite3_shutdown(); +} + S3JniApi(sqlite3_status(),jint,1status)( JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, jboolean reset @@ -3968,54 +4050,6 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, return rc; } -S3JniApi(sqlite3_shutdown(),jint,1shutdown)( - JniArgsEnvClass -){ - s3jni_reset_auto_extension(env); - /* Free up env cache. */ - S3JniMutex_Env_enter; - while( SJG.envCache.aHead ){ - S3JniEnv_uncache( SJG.envCache.aHead->env ); - } - S3JniMutex_Env_leave; - /* Free up S3JniUdf recycling bin. */ - S3JniMutex_Global_enter; - while( S3JniGlobal.udf.aFree ){ - S3JniUdf * const u = S3JniGlobal.udf.aFree; - S3JniGlobal.udf.aFree = u->pNext; - u->pNext = 0; - S3JniUdf_free(env, u, 0); - } - S3JniMutex_Global_leave; - /* Free up S3JniDb recycling bin. */ - S3JniMutex_S3JniDb_enter; - while( S3JniGlobal.perDb.aFree ){ - S3JniDb * const d = S3JniGlobal.perDb.aFree; - S3JniGlobal.perDb.aFree = d->pNext; - d->pNext = 0; - S3JniDb_clear(env, d); - sqlite3_free(d); - } - S3JniMutex_S3JniDb_leave; - -#if 0 - /* - ** Is automatically closing any still-open dbs a good idea? We will - ** get rid of the perDb list once sqlite3 gets a per-db client - ** state, at which point we won't have a central list of databases - ** to close. - */ - S3JniMutex_S3JniDb_enter; - while( SJG.perDb.pHead ){ - s3jni_close_db(env, SJG.perDb.pHead->jDb, 2); - } - S3JniMutex_S3JniDb_leave; -#endif - - /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */ - return sqlite3_shutdown(); -} - S3JniApi(sqlite3_strglob(),jint,1strglob)( JniArgsEnvClass, jbyteArray baG, jbyteArray baT ){ @@ -4061,7 +4095,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ int rc = 0; S3JniHook hook; - S3JniHook_localdup( env, &ps->hooks.trace, &hook ); + S3JniHook_localdup(&ps->hooks.trace, &hook ); if( !hook.jObj ){ return 0; } @@ -4092,16 +4126,17 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ /* Create a new temporary sqlite3_stmt wrapper */ jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); if( !jP ){ - S3JniUnrefLocal(jX); rc = SQLITE_NOMEM; } } - assert(jP); - rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback, - (jint)traceflag, jP, jX); - S3JniIfThrew{ - rc = s3jni_db_exception(env, ps, SQLITE_ERROR, - "sqlite3_trace_v2() callback threw."); + if( 0==rc ){ + assert(jP); + rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback, + (jint)traceflag, jP, jX); + S3JniIfThrew{ + rc = s3jni_db_exception(ps, SQLITE_ERROR, + "sqlite3_trace_v2() callback threw."); + } } } S3JniUnrefLocal(jPUnref); @@ -4135,8 +4170,8 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( "Cannot not find matching call() on " "TracerCallback object."); }else{ - S3JniMutex_S3JniDb_enter; hook.jObj = S3JniRefGlobal(jTracer); + S3JniMutex_S3JniDb_enter; rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); if( 0==rc ){ S3JniHook_unref(&ps->hooks.trace, 0); @@ -4349,7 +4384,7 @@ static void Fts5JniAux_free(Fts5JniAux * const s){ S3JniDeclLocal_env; if( env ){ /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/ - s3jni_call_xDestroy(env, s->jObj); + s3jni_call_xDestroy(s->jObj); S3JniUnrefGlobal(s->jObj); S3JniUnrefGlobal(s->jUserData); } @@ -4579,7 +4614,7 @@ static void S3JniFts5AuxData_xDestroy(void *x){ S3JniFts5AuxData * const p = x; if( p->jObj ){ S3JniDeclLocal_env; - s3jni_call_xDestroy(env, p->jObj); + s3jni_call_xDestroy(p->jObj); S3JniUnrefGlobal(p->jObj); } sqlite3_free(x); @@ -4792,7 +4827,7 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ if( jAux ){ /* Emulate how xSetAuxdata() behaves when it cannot alloc ** its auxdata wrapper. */ - s3jni_call_xDestroy(env, jAux); + s3jni_call_xDestroy(jAux); } return SQLITE_NOMEM; } diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index 3d60362f61..e418de6096 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -1498,7 +1498,7 @@ public class Tester1 implements Runnable { if( sqlLog ){ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){ - int rc = sqlite3_config( new ConfigSqllogCallback() { + final ConfigSqllogCallback log = new ConfigSqllogCallback() { @Override public void call(sqlite3 db, String msg, int op){ switch(op){ case 0: outln("Opening db: ",db); break; @@ -1506,7 +1506,12 @@ public class Tester1 implements Runnable { case 2: outln("Closing db: ",db); break; } } - }); + }; + int rc = sqlite3_config( log ); + affirm( 0==rc ); + rc = sqlite3_config( null ); + affirm( 0==rc ); + rc = sqlite3_config( log ); affirm( 0==rc ); }else{ outln("WARNING: -sqllog is not active because library was built ", diff --git a/ext/jni/src/org/sqlite/jni/XDestroyCallback.java b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java index d1e4b1ee3c..4b547e6bc9 100644 --- a/ext/jni/src/org/sqlite/jni/XDestroyCallback.java +++ b/ext/jni/src/org/sqlite/jni/XDestroyCallback.java @@ -24,6 +24,14 @@ public interface XDestroyCallback { Must perform any cleanup required by this object. Must not throw. Must not call back into the sqlite3 API, else it might invoke a deadlock. + + WARNING: as a rule, it is never safe to register individual + instances with this interface multiple times in the + library. e.g., do not register the same CollationCallback with + multiple arities or names using sqlite3_create_collation(). If + this rule is violated, the library will eventually try to free + each individual reference, leading to memory corruption or a + crash via duplicate free(). */ public void xDestroy(); } diff --git a/manifest b/manifest index 7c36b33c28..ae37df779d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Updates\sto\stestrunner.tcl\sso\sthat\sit\sruns\sfuzztest\susing\smultiple\sjobs. -D 2023-08-28T20:14:19.097 +C Correct\sJNI\smapping\sof\scollations\sto\sbe\s1-db-to-many-collations. +D 2023-08-28T20:21:56.713 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 c92a764a728a3fcd34882defb7517c47af22c9f7fe184113015cdab9fe401c21 +F ext/jni/src/c/sqlite3-jni.c 3c16035f6d604c17e50a6477b7309ca8885c54cd8a9c85e8c36d2f8b918d71e7 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 @@ -264,13 +264,13 @@ F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b F ext/jni/src/org/sqlite/jni/SQLite3CallbackProxy.java c2748ab52856075b053a55b317988d95dc7fb4d3d42520f8c33573effe1cd185 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 a9558165dbb085494705525ef28e41d337d8348bf44259ce1f77cad72547bfdf +F ext/jni/src/org/sqlite/jni/Tester1.java 21d78aa59bfc5ce5ff242d4bb6f6d2255d162fba8be5859ab87c9201d61433f0 F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java 641926b05a772c2c05c842a81aa839053ba4a13b78ef04b402f5705d060c6246 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java be2bc96ff4f56b3c1fd18ae7dba9b207b25b6c123b8a5fd2f7aaf3cc208d8b7d F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246 -F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 95fb66353e62e4aca8d6ab60e8f14f9235bd10373c34db0a64f5f13f016f0471 +F ext/jni/src/org/sqlite/jni/XDestroyCallback.java 50c5ca124ef6c6b735a7e136e7a23a557be367e61b56d4aab5777a614ab46cc2 F ext/jni/src/org/sqlite/jni/annotation/Canonical.java e55b82c8259b617ff754ac493fd8b79602631d659b87a858b987540e4c4fdf56 F ext/jni/src/org/sqlite/jni/annotation/NotNull.java d48ebd7ae6bbb78bd47d54431c85e1521c89b1d3864a2b6eafd9c0e1b2341457 F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 6f962a98c9a5c6e9d21c50ae8716b16bdfdc934a191608cbb7e12ea588ddb6af @@ -2107,9 +2107,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 ecf07a0144dc6402b1e0924b1775d99dc465b27aa86a2718cac60a9b4c974312 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b -R 31bc738c1437151db584e22f2c509958 -T +closed 6463192369ff22eb15d4e34590ef55cd8a6f2501227835664816277806bf961b -U dan -Z 97aabfccc20038ea125aae98b8297d02 +P ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf +R 68323f68f37601aa48cb5f2c61fd0c0a +U stephan +Z 6560ea32a29f03de2be1f2fe7d94526d # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d7d1100a9c..3ef7700236 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf \ No newline at end of file +b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f \ No newline at end of file From fa23b4fc61ff45643e689849da00f7d62cf0cf55 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 21:27:32 +0000 Subject: [PATCH 105/422] Improve threadability of the JNI collation-related bindings and add infrastructure for similar cases. FossilOrigin-Name: f02dad66b965b9e3c504001e9603af8f74977f151bede9db369f88e86a4aeb00 --- ext/jni/src/c/sqlite3-jni.c | 249 ++++++++++++++++++++++-------------- manifest | 12 +- manifest.uuid | 2 +- 3 files changed, 162 insertions(+), 101 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index dfa929ce90..9eada125a1 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -404,6 +404,8 @@ struct S3JniHook{ ** jObj's type */; /* We lookup the jObj.xDestroy() method as-needed for contexts which ** have custom finalizers. */ + jobject jExtra /* Global ref to a per-hook-type value */; + S3JniHook * pNextFree /* Next entry in S3Global.hooks.aFree */; }; /* For clean bitwise-copy init of local instances. */ static const S3JniHook S3JniHook_empty = {0,0}; @@ -627,7 +629,9 @@ struct S3JniGlobalType { #endif #ifdef SQLITE_ENABLE_SQLLOG struct { - S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */; + S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */; + S3JniHook * aFree /* free-item list, for recycling. Guarded by + the global mutex. */; } hooks; #endif #ifdef SQLITE_JNI_ENABLE_METRICS @@ -649,6 +653,8 @@ struct S3JniGlobalType { volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */; volatile unsigned nUdfAlloc /* Number of S3JniUdf alloced. */; volatile unsigned nUdfRecycled /* Number of S3JniUdf reused. */; + volatile unsigned nHookAlloc /* Number of S3JniHook alloced. */; + volatile unsigned nHookRecycled /* Number of S3JniHook reused. */; struct { /* Number of calls for each type of UDF callback. */ volatile unsigned nFunc; @@ -1077,16 +1083,16 @@ static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){ /* ** Internal helper for many hook callback impls. Locks the S3JniDb -** mutex, makes a copy of src into dest, with one change: if src->jObj -** is not NULL then dest->jObj will be a new LOCAL ref to src->jObj -** instead of a copy of the prior GLOBAL ref. Then it unlocks the -** mutex. +** mutex, makes a copy of src into dest, with one differs: if +** src->jObj or src->jExtra are not NULL then dest will be a new LOCAL +** ref to it instead of a copy of the prior GLOBAL ref. Then it +** unlocks the mutex. ** ** If dest->jObj is not NULL when this returns then the caller is ** obligated to eventually free the new ref by passing *dest to ** S3JniHook_localundup(). The dest pointer must NOT be passed to -** S3JniHook_unref(), as that routine assumes that dest->jObj is a -** GLOBAL ref (it's illegal to try to unref the wrong ref type).. +** S3JniHook_unref(), as that routine assumes that dest->jObj/jExtra +** are GLOBAL refs (it's illegal to try to unref the wrong ref type). ** ** Background: when running a hook we need a call-local copy lest ** another thread modify the hook while we're running it. That copy @@ -1096,11 +1102,18 @@ static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src S3JniHook * const dest ){ S3JniMutex_S3JniDb_enter; *dest = *src; - if(dest->jObj) dest->jObj = S3JniRefLocal(dest->jObj); + if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj); + if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra); S3JniMutex_S3JniDb_leave; } #define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest) -#define S3JniHook_localundup(HOOK) S3JniUnrefLocal(HOOK.jObj) + +static void S3JniHook__localundup( JNIEnv * const env, S3JniHook * const h ){ + S3JniUnrefLocal(h->jObj); + S3JniUnrefLocal(h->jExtra); + memset(h, 0, sizeof(*h)); +} +#define S3JniHook_localundup(HOOK) S3JniHook__localundup(env, &(HOOK)) /* ** Removes any Java references from s and clears its state. If @@ -1110,18 +1123,61 @@ static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src ** references. */ static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s, - int doXDestroy){ + int doXDestroy){ if( s->jObj ){ if( doXDestroy ){ s3jni_call_xDestroy(s->jObj); } S3JniUnrefGlobal(s->jObj); + //S3JniUnrefGlobal(s->jExtra); } memset(s, 0, sizeof(*s)); } #define S3JniHook_unref(hook,doDestroy) \ S3JniHook__unref(env, (hook), (doDestroy)) +static S3JniHook *S3JniHook__alloc(JNIEnv * const env){ + S3JniHook * p = 0; + S3JniMutex_Global_enter; + if( SJG.hooks.aFree ){ + p = SJG.hooks.aFree; + SJG.hooks.aFree = p->pNextFree; + p->pNextFree = 0; + s3jni_incr(&SJG.metrics.nHookRecycled); + } + S3JniMutex_Global_leave; + if( 0==p ){ + p = s3jni_malloc(sizeof(S3JniHook)); + if( p ){ + s3jni_incr(&SJG.metrics.nHookAlloc); + } + } + if( p ){ + *p = S3JniHook_empty; + } + return p; +} + +#define S3JniHook_alloc() S3JniHook__alloc(env) + +/* +** The rightful fate of all results from S3JniHook_alloc(). doXDestroy +** is passed on as-is to S3JniHook_unref(). +*/ +static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p, + int doXDestroy){ + if(p){ + assert( !p->pNextFree ); + S3JniHook_unref(p, doXDestroy); + S3JniMutex_Global_enter; + p->pNextFree = SJG.hooks.aFree; + SJG.hooks.aFree = p; + S3JniMutex_Global_leave; + } +} +#define S3JniHook_free(hook,doXDestroy) S3JniHook__free(env, hook, doXDestroy) + + /* ** Clears all of s's state. Requires that that the caller has locked ** S3JniGlobal.perDb.mutex. Make sure to do anything needed with @@ -1596,46 +1652,6 @@ static int encodingTypeIsValid(int eTextRep){ } } -/** - State for CollationCallbacks. -*/ -typedef S3JniHook S3JniCollationCallback; - -/* -** Proxy for Java-side CollationCallback.xCompare() callbacks. -*/ -static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, - int nRhs, const void *rhs){ - S3JniCollationCallback * const pCC = pArg; - S3JniDeclLocal_env; - jint rc = 0; - if( pCC->jObj ){ - jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs); - jbyteArray jbaRhs = jbaLhs - ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0; - if( !jbaRhs ){ - S3JniUnrefLocal(jbaLhs); - /* We have no recovery strategy here. */ - s3jni_oom_check( jbaRhs ); - return 0; - } - rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback, - jbaLhs, jbaRhs); - S3JniExceptionIgnore; - S3JniUnrefLocal(jbaLhs); - S3JniUnrefLocal(jbaRhs); - } - return (int)rc; -} - -/* CollationCallback finalizer for use by the sqlite3 internals. */ -static void CollationCallback_xDestroy(void *pArg){ - S3JniCollationCallback * const pCC = pArg; - S3JniDeclLocal_env; - S3JniHook_unref(pCC, 1); - sqlite3_free(pCC); -} - /* For use with sqlite3_result/value_pointer() */ #define ResultJavaValuePtrStr "org.sqlite.jni.ResultJavaVal" @@ -2487,33 +2503,30 @@ static unsigned int s3jni_utf16_strlen(void const * z){ return i; } +/* Descriptive alias for use with sqlite3_create_collation(). */ +typedef S3JniHook S3JniCollationNeeded; + /* Central C-to-Java sqlite3_collation_needed16() hook impl. */ static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, int eTextRep, const void * z16Name){ - S3JniDb * const ps = pState; + S3JniCollationNeeded * const pHook = pState; S3JniDeclLocal_env; S3JniHook hook; - S3JniHook_localdup(&ps->hooks.collationNeeded, &hook ); + S3JniHook_localdup(pHook, &hook); if( hook.jObj ){ unsigned int const nName = s3jni_utf16_strlen(z16Name); jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); + s3jni_oom_check( jName ); + assert( hook.jExtra ); S3JniIfThrew{ S3JniExceptionClear; - }else{ - jobject jDb; - S3JniMutex_S3JniDb_enter; - jDb = ps->jDb ? S3JniRefLocal(ps->jDb) : 0; - S3JniMutex_S3JniDb_leave; - if( jDb ){ - (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, - jDb, (jint)eTextRep, jName); - S3JniIfThrew{ - S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); - s3jni_db_exception(ps, 0, "sqlite3_collation_needed() callback threw"); - } - S3JniUnrefLocal(jDb); + }else if( hook.jExtra ){ + (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, + hook.jExtra, (jint)eTextRep, jName); + S3JniIfThrew{ + S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); } } S3JniUnrefLocal(jName); @@ -2525,7 +2538,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( JniArgsEnvClass, jobject jDb, jobject jHook ){ S3JniDb * ps; - S3JniHook * pHook; + S3JniCollationNeeded * pHook; int rc = 0; S3JniMutex_S3JniDb_enter; @@ -2554,11 +2567,12 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( "Cannot not find matching call() in " "CollationNeededCallback object."); }else{ - rc = sqlite3_collation_needed16(ps->pDb, ps, s3jni_collation_needed_impl16); + rc = sqlite3_collation_needed16(ps->pDb, pHook, s3jni_collation_needed_impl16); if( 0==rc ){ S3JniHook_unref(pHook, 0); pHook->midCallback = xCallback; pHook->jObj = S3JniRefGlobal(jHook); + pHook->jExtra = S3JniRefGlobal(jDb); } } } @@ -2846,43 +2860,86 @@ S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( return ps ? ps->jDb : 0; } +/** + State for CollationCallbacks. +*/ +typedef S3JniHook S3JniCollationCallback; + +/* +** Proxy for Java-side CollationCallback.xCompare() callbacks. +*/ +static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, + int nRhs, const void *rhs){ + S3JniCollationCallback * const pCC = pArg; + S3JniDeclLocal_env; + jint rc = 0; + if( pCC->jObj ){ + jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs); + jbyteArray jbaRhs = jbaLhs + ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0; + if( !jbaRhs ){ + S3JniUnrefLocal(jbaLhs); + /* We have no recovery strategy here. */ + s3jni_oom_check( jbaRhs ); + return 0; + } + rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback, + jbaLhs, jbaRhs); + S3JniExceptionIgnore; + S3JniUnrefLocal(jbaLhs); + S3JniUnrefLocal(jbaRhs); + } + return (int)rc; +} + +/* CollationCallback finalizer for use by the sqlite3 internals. */ +static void CollationCallback_xDestroy(void *pArg){ + S3JniCollationCallback * const pCC = pArg; + S3JniDeclLocal_env; + S3JniHook_free(pCC, 1); +} + S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), jint,1create_1collation )(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ int rc; - jclass klazz; - S3JniDb * const ps = S3JniDb_from_java(jDb); - jmethodID midCallback; + S3JniDb * ps; - if( !ps ) return SQLITE_MISUSE; - klazz = (*env)->GetObjectClass(env, oCollation); - midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); - S3JniUnrefLocal(klazz); - S3JniIfThrew{ - rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, - "Could not get call() method from " - "CollationCallback object."); + S3JniMutex_S3JniDb_enter; + ps = S3JniDb_from_java_unlocked(jDb); + if( !ps ){ + rc = SQLITE_MISUSE; }else{ - char * const zName = s3jni_jstring_to_utf8( name, 0); - S3JniCollationCallback * const pCC = - zName ? s3jni_malloc(sizeof(S3JniCollationCallback)) : 0; - if( pCC ){ - memset( pCC, 0, sizeof(*pCC) ); - rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, - pCC, CollationCallback_xCompare, - CollationCallback_xDestroy); - if( 0==rc ){ - pCC->midCallback = midCallback; - pCC->jObj = S3JniRefGlobal(oCollation); - }else{ - CollationCallback_xDestroy(pCC); - } + jclass const klazz = (*env)->GetObjectClass(env, oCollation); + jmethodID const midCallback = + (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); + S3JniUnrefLocal(klazz); + S3JniIfThrew{ + rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, + "Could not get call() method from " + "CollationCallback object."); }else{ - rc = SQLITE_NOMEM; + char * const zName = s3jni_jstring_to_utf8( name, 0); + S3JniCollationCallback * const pCC = + zName ? S3JniHook_alloc() : 0; + if( pCC ){ + rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, + pCC, CollationCallback_xCompare, + CollationCallback_xDestroy); + if( 0==rc ){ + pCC->midCallback = midCallback; + pCC->jObj = S3JniRefGlobal(oCollation); + }else{ + CollationCallback_xDestroy(pCC); + } + }else{ + rc = SQLITE_NOMEM; + } + sqlite3_free(zName); } - sqlite3_free(zName); } + S3JniMutex_S3JniDb_leave; return (jint)rc; } @@ -4323,6 +4380,10 @@ JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf), (unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)), SJG.metrics.nUdfRecycled); + printf("\tS3JniHook: %u alloced (*%u = %u bytes), %u recycled\n", + SJG.metrics.nHookAlloc, (unsigned) sizeof(S3JniHook), + (unsigned)(SJG.metrics.nHookAlloc * sizeof(S3JniHook)), + SJG.metrics.nHookRecycled); printf("\tS3JniEnv: %u alloced (*%u = %u bytes)\n", SJG.metrics.nEnvAlloc, (unsigned) sizeof(S3JniEnv), (unsigned)(SJG.metrics.nEnvAlloc * sizeof(S3JniEnv))); diff --git a/manifest b/manifest index ae37df779d..b7c342d6f0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Correct\sJNI\smapping\sof\scollations\sto\sbe\s1-db-to-many-collations. -D 2023-08-28T20:21:56.713 +C Improve\sthreadability\sof\sthe\sJNI\scollation-related\sbindings\sand\sadd\sinfrastructure\sfor\ssimilar\scases. +D 2023-08-28T21:27:32.287 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 3c16035f6d604c17e50a6477b7309ca8885c54cd8a9c85e8c36d2f8b918d71e7 +F ext/jni/src/c/sqlite3-jni.c 48128298ecdd99ddd4f3068c73cc2548899c70cea8a1ddc9e651248b815854db 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 @@ -2107,8 +2107,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 ceeabe9f8b31a30c65147fd270b92d43c7842247548cee9de165113991f6c2cf -R 68323f68f37601aa48cb5f2c61fd0c0a +P b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f +R f4a0af7c14adbc7eb10004ae27d12901 U stephan -Z 6560ea32a29f03de2be1f2fe7d94526d +Z 0bf576aa532ec9693d521343b6887da2 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3ef7700236..a2681cfa81 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f \ No newline at end of file +f02dad66b965b9e3c504001e9603af8f74977f151bede9db369f88e86a4aeb00 \ No newline at end of file From 924c4545d3c64946b25c5b049415c12d695876ed Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 22:52:04 +0000 Subject: [PATCH 106/422] Further minor internal JNI simplifications. FossilOrigin-Name: 1808d12ee0d1f1e5ee49d48c699ca10c4f822989ac9b4ac08f2b861513ee5997 --- ext/jni/src/c/sqlite3-jni.c | 179 +++++++++++++++++++++--------------- manifest | 12 +-- manifest.uuid | 2 +- 3 files changed, 112 insertions(+), 81 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 9eada125a1..0625c96d18 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -405,10 +405,12 @@ struct S3JniHook{ /* We lookup the jObj.xDestroy() method as-needed for contexts which ** have custom finalizers. */ jobject jExtra /* Global ref to a per-hook-type value */; - S3JniHook * pNextFree /* Next entry in S3Global.hooks.aFree */; + int doXDestroy /* If true call jObj->xDestroy() when + this object is S3JniHook_unref()'d. */; + S3JniHook * pNext /* Next entry in S3Global.hooks.aFree */; }; /* For clean bitwise-copy init of local instances. */ -static const S3JniHook S3JniHook_empty = {0,0}; +static const S3JniHook S3JniHook_empty = {0,0,0,0,0}; /* ** Per-(sqlite3*) state for various JNI bindings. This state is @@ -1083,10 +1085,10 @@ static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){ /* ** Internal helper for many hook callback impls. Locks the S3JniDb -** mutex, makes a copy of src into dest, with one differs: if +** mutex, makes a copy of src into dest, with a some differences: (1) if ** src->jObj or src->jExtra are not NULL then dest will be a new LOCAL -** ref to it instead of a copy of the prior GLOBAL ref. Then it -** unlocks the mutex. +** ref to it instead of a copy of the prior GLOBAL ref. (2) dest->doXDestroy +** is always false. ** ** If dest->jObj is not NULL when this returns then the caller is ** obligated to eventually free the new ref by passing *dest to @@ -1104,6 +1106,7 @@ static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src *dest = *src; if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj); if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra); + dest->doXDestroy = 0; S3JniMutex_S3JniDb_leave; } #define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest) @@ -1111,38 +1114,42 @@ static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src static void S3JniHook__localundup( JNIEnv * const env, S3JniHook * const h ){ S3JniUnrefLocal(h->jObj); S3JniUnrefLocal(h->jExtra); - memset(h, 0, sizeof(*h)); + *h = S3JniHook_empty; } #define S3JniHook_localundup(HOOK) S3JniHook__localundup(env, &(HOOK)) /* ** Removes any Java references from s and clears its state. If -** doXDestroy is true and s->jObj is not NULL, s->jObj's -** s is passed to s3jni_call_xDestroy() before any references are +** doXDestroy is true and s->jObj is not NULL, s->jObj +** is passed to s3jni_call_xDestroy() before any references are ** cleared. It is legal to call this when the object has no Java -** references. +** references. s must not be NULL. */ -static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s, - int doXDestroy){ +static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s){ if( s->jObj ){ - if( doXDestroy ){ + if( s->doXDestroy ){ s3jni_call_xDestroy(s->jObj); } S3JniUnrefGlobal(s->jObj); - //S3JniUnrefGlobal(s->jExtra); + S3JniUnrefGlobal(s->jExtra); } - memset(s, 0, sizeof(*s)); + *s = S3JniHook_empty; } -#define S3JniHook_unref(hook,doDestroy) \ - S3JniHook__unref(env, (hook), (doDestroy)) +#define S3JniHook_unref(hook) \ + S3JniHook__unref(env, (hook)) +/* +** Allocates one blank S3JniHook object from the recycling bin, if +** available, else from the heap. Returns NULL or dies on OOM. Locks +** the global mutex. +*/ static S3JniHook *S3JniHook__alloc(JNIEnv * const env){ S3JniHook * p = 0; S3JniMutex_Global_enter; if( SJG.hooks.aFree ){ p = SJG.hooks.aFree; - SJG.hooks.aFree = p->pNextFree; - p->pNextFree = 0; + SJG.hooks.aFree = p->pNext; + p->pNext = 0; s3jni_incr(&SJG.metrics.nHookRecycled); } S3JniMutex_Global_leave; @@ -1157,26 +1164,37 @@ static S3JniHook *S3JniHook__alloc(JNIEnv * const env){ } return p; } - #define S3JniHook_alloc() S3JniHook__alloc(env) /* ** The rightful fate of all results from S3JniHook_alloc(). doXDestroy -** is passed on as-is to S3JniHook_unref(). +** is passed on as-is to S3JniHook_unref(). Locks the global mutex. */ -static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p, - int doXDestroy){ +static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p){ if(p){ - assert( !p->pNextFree ); - S3JniHook_unref(p, doXDestroy); + assert( !p->pNext ); + S3JniHook_unref(p); S3JniMutex_Global_enter; - p->pNextFree = SJG.hooks.aFree; + p->pNext = SJG.hooks.aFree; SJG.hooks.aFree = p; S3JniMutex_Global_leave; } } -#define S3JniHook_free(hook,doXDestroy) S3JniHook__free(env, hook, doXDestroy) +#define S3JniHook_free(hook) S3JniHook__free(env, hook) +#if 0 +/* S3JniHook__free() without the lock: caller must hold the global mutex */ +static void S3JniHook__free_unlocked(JNIEnv * const env, S3JniHook * const p){ + if(p){ + assert( !p->pNext ); + assert( p->pNext != SJG.hooks.aFree ); + S3JniHook_unref(p); + p->pNext = SJG.hooks.aFree; + SJG.hooks.aFree = p; + } +} +#define S3JniHook_free_unlocked(hook) S3JniHook__free_unlocked(env, hook) +#endif /* ** Clears all of s's state. Requires that that the caller has locked @@ -1186,7 +1204,8 @@ static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p, static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){ S3JniMutex_S3JniDb_assertLocker; sqlite3_free( s->zMainDbName ); -#define UNHOOK(MEMBER) S3JniHook_unref(&s->hooks.MEMBER, 0) +#define UNHOOK(MEMBER) \ + S3JniHook_unref(&s->hooks.MEMBER) UNHOOK(auth); UNHOOK(busyHandler); UNHOOK(collationNeeded); @@ -1525,7 +1544,7 @@ static S3JniDb * S3JniDb__from_c(JNIEnv * const env, sqlite3 *pDb){ ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out ** AX. */ -#define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX, 0); +#define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX); /* ** Initializes a pre-allocated S3JniAutoExtension object. Returns @@ -1539,6 +1558,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env, jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); S3JniMutex_Ext_assertLocker; + *ax = S3JniHook_empty; ax->midCallback = (*env)->GetMethodID(env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;)I"); S3JniUnrefLocal(klazz); @@ -2375,7 +2395,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( if( hook.jObj ){ /* Replace handler */ rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); *pHook = hook; hook = S3JniHook_empty; } @@ -2383,11 +2403,11 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( }else{ /* Clear handler */ rc = sqlite3_busy_handler(ps->pDb, 0, 0); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); } } } - S3JniHook_unref(&hook, 0); + S3JniHook_unref(&hook); S3JniMutex_S3JniDb_leave; return rc; } @@ -2399,7 +2419,7 @@ S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( int rc = SQLITE_MISUSE; if( ps ){ S3JniMutex_S3JniDb_enter; - S3JniHook_unref(&ps->hooks.busyHandler, 0); + S3JniHook_unref(&ps->hooks.busyHandler); rc = sqlite3_busy_timeout(ps->pDb, (int)ms); S3JniMutex_S3JniDb_leave; } @@ -2421,7 +2441,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( /* Move final entry into this slot. */ --SJG.autoExt.nExt; *ax = SJG.autoExt.aExt[SJG.autoExt.nExt]; - memset(&SJG.autoExt.aExt[SJG.autoExt.nExt], 0, sizeof(*ax)); + SJG.autoExt.aExt[SJG.autoExt.nExt] = S3JniHook_empty; assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj ); rc = JNI_TRUE; break; @@ -2503,7 +2523,7 @@ static unsigned int s3jni_utf16_strlen(void const * z){ return i; } -/* Descriptive alias for use with sqlite3_create_collation(). */ +/* Descriptive alias for use with sqlite3_collation_needed(). */ typedef S3JniHook S3JniCollationNeeded; /* Central C-to-Java sqlite3_collation_needed16() hook impl. */ @@ -2554,7 +2574,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( }else if( !jHook ){ rc = sqlite3_collation_needed(ps->pDb, 0, 0); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); } }else{ jclass const klazz = (*env)->GetObjectClass(env, jHook); @@ -2569,10 +2589,10 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( }else{ rc = sqlite3_collation_needed16(ps->pDb, pHook, s3jni_collation_needed_impl16); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); pHook->midCallback = xCallback; pHook->jObj = S3JniRefGlobal(jHook); - pHook->jExtra = S3JniRefGlobal(jDb); + pHook->jExtra = S3JniRefGlobal(ps->jDb); } } } @@ -2701,7 +2721,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, S3JniUnrefGlobal(pOld); pOld = tmp; } - memset(pHook, 0, sizeof(S3JniHook)); + *pHook = S3JniHook_empty; if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0); else sqlite3_rollback_hook(ps->pDb, 0, 0); }else{ @@ -2823,7 +2843,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, if( !jLog ){ rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); } }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ /* No-op */ @@ -2837,7 +2857,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */, if( midCallback ){ rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); if( 0==rc ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); pHook->midCallback = midCallback; pHook->jObj = S3JniRefGlobal(jLog); } @@ -2869,7 +2889,7 @@ typedef S3JniHook S3JniCollationCallback; ** Proxy for Java-side CollationCallback.xCompare() callbacks. */ static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, - int nRhs, const void *rhs){ + int nRhs, const void *rhs){ S3JniCollationCallback * const pCC = pArg; S3JniDeclLocal_env; jint rc = 0; @@ -2896,7 +2916,7 @@ static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, static void CollationCallback_xDestroy(void *pArg){ S3JniCollationCallback * const pCC = pArg; S3JniDeclLocal_env; - S3JniHook_free(pCC, 1); + S3JniHook_free(pCC); } S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), @@ -2930,6 +2950,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), if( 0==rc ){ pCC->midCallback = midCallback; pCC->jObj = S3JniRefGlobal(oCollation); + pCC->doXDestroy = 1; }else{ CollationCallback_xDestroy(pCC); } @@ -3552,7 +3573,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec S3JniUnrefGlobal(pOld); pOld = tmp; } - memset(pHook, 0, sizeof(S3JniHook)); + *pHook = S3JniHook_empty; #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( isPre ) sqlite3_preupdate_hook(ps->pDb, 0, 0); else @@ -3674,7 +3695,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( if( !ps ) return; S3JniMutex_S3JniDb_enter; if( n<1 || !jProgress ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); sqlite3_progress_handler(ps->pDb, 0, 0, 0); }else{ jclass const klazz = (*env)->GetObjectClass(env, jProgress); @@ -3974,7 +3995,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( if( !ps ) return SQLITE_MISUSE; S3JniMutex_S3JniDb_enter; if( !jHook ){ - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); rc = sqlite3_set_authorizer( ps->pDb, 0, 0 ); }else{ jclass klazz; @@ -3984,7 +4005,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( S3JniMutex_S3JniDb_leave; return 0; } - S3JniHook_unref(pHook, 0); + S3JniHook_unref(pHook); } pHook->jObj = S3JniRefGlobal(jHook); klazz = (*env)->GetObjectClass(env, jHook); @@ -4003,7 +4024,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( }else{ rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); } - if( rc ) S3JniHook_unref(pHook, 0); + if( rc ) S3JniHook_unref(pHook); } S3JniMutex_S3JniDb_leave; return rc; @@ -4021,31 +4042,41 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( JniArgsEnvClass ){ s3jni_reset_auto_extension(env); - /* Free up env cache. */ - S3JniMutex_Env_enter; - while( SJG.envCache.aHead ){ - S3JniEnv_uncache( SJG.envCache.aHead->env ); - } - S3JniMutex_Env_leave; - /* Free up S3JniUdf recycling bin. */ - S3JniMutex_Global_enter; - while( S3JniGlobal.udf.aFree ){ - S3JniUdf * const u = S3JniGlobal.udf.aFree; - S3JniGlobal.udf.aFree = u->pNext; - u->pNext = 0; - S3JniUdf_free(env, u, 0); - } - S3JniMutex_Global_leave; /* Free up S3JniDb recycling bin. */ - S3JniMutex_S3JniDb_enter; - while( S3JniGlobal.perDb.aFree ){ - S3JniDb * const d = S3JniGlobal.perDb.aFree; - S3JniGlobal.perDb.aFree = d->pNext; - d->pNext = 0; - S3JniDb_clear(env, d); - sqlite3_free(d); - } - S3JniMutex_S3JniDb_leave; + S3JniMutex_S3JniDb_enter; { + while( S3JniGlobal.perDb.aFree ){ + S3JniDb * const d = S3JniGlobal.perDb.aFree; + S3JniGlobal.perDb.aFree = d->pNext; + d->pNext = 0; + S3JniDb_clear(env, d); + sqlite3_free(d); + } + } S3JniMutex_S3JniDb_leave; + S3JniMutex_Global_enter; { + /* Free up S3JniUdf recycling bin. */ + while( S3JniGlobal.udf.aFree ){ + S3JniUdf * const u = S3JniGlobal.udf.aFree; + S3JniGlobal.udf.aFree = u->pNext; + u->pNext = 0; + S3JniUdf_free(env, u, 0); + } + /* Free up S3JniHook recycling bin. */ + while( S3JniGlobal.hooks.aFree ){ + S3JniHook * const u = S3JniGlobal.hooks.aFree; + S3JniGlobal.hooks.aFree = u->pNext; + u->pNext = 0; + assert( !u->doXDestroy ); + assert( !u->jObj ); + assert( !u->jExtra ); + sqlite3_free( u ); + } + } S3JniMutex_Global_leave; + /* Free up env cache. */ + S3JniMutex_Env_enter; { + while( SJG.envCache.aHead ){ + S3JniEnv_uncache( SJG.envCache.aHead->env ); + } + } S3JniMutex_Env_leave; #if 0 /* ** Is automatically closing any still-open dbs a good idea? We will @@ -4212,7 +4243,7 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( if( !traceMask || !jTracer ){ S3JniMutex_S3JniDb_enter; rc = (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); - S3JniHook_unref(&ps->hooks.trace, 0); + S3JniHook_unref(&ps->hooks.trace); S3JniMutex_S3JniDb_leave; }else{ jclass const klazz = (*env)->GetObjectClass(env, jTracer); @@ -4231,10 +4262,10 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( S3JniMutex_S3JniDb_enter; rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); if( 0==rc ){ - S3JniHook_unref(&ps->hooks.trace, 0); + S3JniHook_unref(&ps->hooks.trace); ps->hooks.trace = hook; }else{ - S3JniHook_unref(&hook, 0); + S3JniHook_unref(&hook); } S3JniMutex_S3JniDb_leave; } diff --git a/manifest b/manifest index b7c342d6f0..19184e9c0d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improve\sthreadability\sof\sthe\sJNI\scollation-related\sbindings\sand\sadd\sinfrastructure\sfor\ssimilar\scases. -D 2023-08-28T21:27:32.287 +C Further\sminor\sinternal\sJNI\ssimplifications. +D 2023-08-28T22:52:04.369 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 48128298ecdd99ddd4f3068c73cc2548899c70cea8a1ddc9e651248b815854db +F ext/jni/src/c/sqlite3-jni.c 6046370cddd31778e9002a3848e380d187a8a1b75666afbdcfb5906ef741b6f9 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 @@ -2107,8 +2107,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 b927b0f5a68684b0a9799396d153bf1e2306351e8039c2bacb3d5b2056a0634f -R f4a0af7c14adbc7eb10004ae27d12901 +P f02dad66b965b9e3c504001e9603af8f74977f151bede9db369f88e86a4aeb00 +R 939c02dc8fb9e092bdac6891a1151351 U stephan -Z 0bf576aa532ec9693d521343b6887da2 +Z c3a2989b14ceb26f9ee63232f5504607 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a2681cfa81..c4cfd033e2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f02dad66b965b9e3c504001e9603af8f74977f151bede9db369f88e86a4aeb00 \ No newline at end of file +1808d12ee0d1f1e5ee49d48c699ca10c4f822989ac9b4ac08f2b861513ee5997 \ No newline at end of file From 4e379078e7cde41438b6d260901487b5c5feb7d8 Mon Sep 17 00:00:00 2001 From: stephan Date: Mon, 28 Aug 2023 23:18:19 +0000 Subject: [PATCH 107/422] Factor out a superfluous struct member. FossilOrigin-Name: 76d3911c370b3dc02d119045003f87ad20a6efd9f7a44d4feb59b7c801ac8981 --- ext/jni/src/c/sqlite3-jni.c | 38 +++++++++++++++++-------------------- manifest | 12 ++++++------ manifest.uuid | 2 +- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 0625c96d18..b524d4e608 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -447,8 +447,8 @@ struct S3JniDb { #ifdef SQLITE_ENABLE_FTS5 jobject jFtsApi /* global ref to s3jni_fts5_api_from_db() */; #endif - S3JniDb * pNext /* Next entry in the available/free list */; - S3JniDb * pPrev /* Previous entry in the available/free list */; + S3JniDb * pNext /* Next entry in SJG.perDb.aFree or SJG.perDb.aHead */; + S3JniDb * pPrev /* Previous entry in SJG.perDb.aFree or SJG.perDb.aHead */; }; /* @@ -478,8 +478,8 @@ struct S3JniEnv { ** a NULL db, in which case free pdbOpening. */ S3JniDb * pdbOpening; - S3JniEnv * pPrev /* Previous entry in the linked list */; - S3JniEnv * pNext /* Next entry in the linked list */; + S3JniEnv * pNext /* Next entry in SJG.envCache.aHead or + SJG.envCache.aFree */; }; /* @@ -505,15 +505,15 @@ enum UDFType { */ typedef struct S3JniUdf S3JniUdf; struct S3JniUdf { - jobject jObj /* SQLFunction instance */; - char * zFuncName /* Only for error reporting and debug logging */; - enum UDFType type; + jobject jObj /* SQLFunction instance */; + char * zFuncName /* Only for error reporting and debug logging */; + enum UDFType type /* UDF type */; /** Method IDs for the various UDF methods. */ - jmethodID jmidxFunc /* xFunc method */; - jmethodID jmidxStep /* xStep method */; - jmethodID jmidxFinal /* xFinal method */; - jmethodID jmidxValue /* xValue method */; - jmethodID jmidxInverse /* xInverse method */; + jmethodID jmidxFunc /* xFunc method (scalar) */; + jmethodID jmidxStep /* xStep method (aggregate/window) */; + jmethodID jmidxFinal /* xFinal method (aggregate/window) */; + jmethodID jmidxValue /* xValue method (window) */; + jmethodID jmidxInverse /* xInverse method (window) */; S3JniUdf * pNext /* Next entry in SJG.udf.aFree. */; }; @@ -819,16 +819,13 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){ s3jni_incr( &SJG.metrics.nEnvMiss ); row = SJG.envCache.aFree; if( row ){ - assert(!row->pPrev); SJG.envCache.aFree = row->pNext; - if( row->pNext ) row->pNext->pPrev = 0; }else{ row = s3jni_malloc_or_die(env, sizeof(*row)); s3jni_incr( &SJG.metrics.nEnvAlloc ); } memset(row, 0, sizeof(*row)); row->pNext = SJG.envCache.aHead; - if( row->pNext ) row->pNext->pPrev = row; SJG.envCache.aHead = row; row->env = env; @@ -1264,9 +1261,10 @@ static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){ */ static int S3JniEnv_uncache(JNIEnv * const env){ struct S3JniEnv * row; + struct S3JniEnv * pPrev = 0; S3JniMutex_Env_assertLocked; row = SJG.envCache.aHead; - for( ; row; row = row->pNext ){ + for( ; row; pPrev = row, row = row->pNext ){ if( row->env == env ){ break; } @@ -1274,15 +1272,13 @@ static int S3JniEnv_uncache(JNIEnv * const env){ if( !row ){ return 0; } - if( row->pNext ) row->pNext->pPrev = row->pPrev; - if( row->pPrev ) row->pPrev->pNext = row->pNext; - if( SJG.envCache.aHead == row ){ - assert( !row->pPrev ); + if( pPrev) pPrev->pNext = row->pNext; + else{ + assert( SJG.envCache.aHead == row ); SJG.envCache.aHead = row->pNext; } memset(row, 0, sizeof(S3JniEnv)); row->pNext = SJG.envCache.aFree; - if( row->pNext ) row->pNext->pPrev = row; SJG.envCache.aFree = row; return 1; } diff --git a/manifest b/manifest index 19184e9c0d..9b137ef1a0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\sminor\sinternal\sJNI\ssimplifications. -D 2023-08-28T22:52:04.369 +C Factor\sout\sa\ssuperfluous\sstruct\smember. +D 2023-08-28T23:18:19.446 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 6046370cddd31778e9002a3848e380d187a8a1b75666afbdcfb5906ef741b6f9 +F ext/jni/src/c/sqlite3-jni.c 4b78ca613beb04515d27f3b539061438bee5af66ebf7ec1763b78f5b7a886e83 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 @@ -2107,8 +2107,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 f02dad66b965b9e3c504001e9603af8f74977f151bede9db369f88e86a4aeb00 -R 939c02dc8fb9e092bdac6891a1151351 +P 1808d12ee0d1f1e5ee49d48c699ca10c4f822989ac9b4ac08f2b861513ee5997 +R 5b95fe0a7229660967a4e5fdae0507cd U stephan -Z c3a2989b14ceb26f9ee63232f5504607 +Z f730905721414dc145d7e6f2fcf9a0e6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c4cfd033e2..6ef4ef08b2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1808d12ee0d1f1e5ee49d48c699ca10c4f822989ac9b4ac08f2b861513ee5997 \ No newline at end of file +76d3911c370b3dc02d119045003f87ad20a6efd9f7a44d4feb59b7c801ac8981 \ No newline at end of file From d10ed826eb0c8c8d0cbdf66a1e633e16796dd90e Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 29 Aug 2023 00:10:31 +0000 Subject: [PATCH 108/422] Remove some dead code. Add a skeleton SQLTester script for fts5. FossilOrigin-Name: 6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb --- ext/jni/src/c/sqlite3-jni.c | 9 +++------ ext/jni/src/tests/900-001-fts.test | 12 ++++++++++++ manifest | 13 +++++++------ manifest.uuid | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 ext/jni/src/tests/900-001-fts.test diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index b524d4e608..b426a26acc 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -1500,10 +1500,9 @@ static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){ S3JniMutex_S3JniDb_leave; return s; } +#define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject)) + -/* An experiment */ -//#define CLOSE_DB_LOCKED -#if 1 || defined(CLOSE_DB_LOCKED) static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){ S3JniDb * s = 0; sqlite3 * pDb = 0; @@ -1515,7 +1514,6 @@ static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){ } #define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB)) -#endif /* ** Returns the S3JniDb object for the sqlite3 object, or NULL if pDb @@ -1532,8 +1530,6 @@ static S3JniDb * S3JniDb__from_c(JNIEnv * const env, sqlite3 *pDb){ S3JniMutex_S3JniDb_leave; return s; } - -#define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject)) #define S3JniDb_from_c(sqlite3Ptr) S3JniDb__from_c(env,(sqlite3Ptr)) /* @@ -2451,6 +2447,7 @@ 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; +//#define CLOSE_DB_LOCKED /* An experiment */ #ifndef CLOSE_DB_LOCKED S3JniDb * const ps = S3JniDb_from_java(jDb); diff --git a/ext/jni/src/tests/900-001-fts.test b/ext/jni/src/tests/900-001-fts.test new file mode 100644 index 0000000000..65285e86b0 --- /dev/null +++ b/ext/jni/src/tests/900-001-fts.test @@ -0,0 +1,12 @@ +/* +** SCRIPT_MODULE_NAME: fts5-sanity-checks +** xREQUIRED_PROPERTIES: FTS5 +** +*/ + +--testcase 1.0 +CREATE VIRTUAL TABLE email USING fts5(sender, title, body); +insert into email values('fred','Help!','Dear Sir...'); +insert into email values('barney','Assistance','Dear Madam...'); +select * from email where email match 'assistance'; +--result barney Assistance {Dear Madam...} diff --git a/manifest b/manifest index 9b137ef1a0..b237cd5f65 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Factor\sout\sa\ssuperfluous\sstruct\smember. -D 2023-08-28T23:18:19.446 +C Remove\ssome\sdead\scode.\sAdd\sa\sskeleton\sSQLTester\sscript\sfor\sfts5. +D 2023-08-29T00:10:31.208 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -237,7 +237,7 @@ F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a3 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 4b78ca613beb04515d27f3b539061438bee5af66ebf7ec1763b78f5b7a886e83 +F ext/jni/src/c/sqlite3-jni.c 3d80af6bfa4af38dc50a919f97219a481410de1f6f885644b2f97cd64ab9b863 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 @@ -287,6 +287,7 @@ F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bf350903abe04a9bed2d8a2a71692 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 +F ext/jni/src/tests/900-001-fts.test bf0ce17a8d082773450e91f2388f5bbb2dfa316d0b676c313c637a91198090f0 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013 F ext/lsm1/lsm-test/README 87ea529d2abe615e856d4714bfe8bb185e6c2771b8612aa6298588b7b43e6f86 @@ -2107,8 +2108,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 1808d12ee0d1f1e5ee49d48c699ca10c4f822989ac9b4ac08f2b861513ee5997 -R 5b95fe0a7229660967a4e5fdae0507cd +P 76d3911c370b3dc02d119045003f87ad20a6efd9f7a44d4feb59b7c801ac8981 +R 7d288c901b3674a6a88e2405123881f5 U stephan -Z f730905721414dc145d7e6f2fcf9a0e6 +Z 6f3a62c9006357f22c26cb9d02ee32ca # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6ef4ef08b2..094b411e25 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -76d3911c370b3dc02d119045003f87ad20a6efd9f7a44d4feb59b7c801ac8981 \ No newline at end of file +6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb \ No newline at end of file From b44b8023782b00387bde7c00a0d83255284226a5 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 29 Aug 2023 10:50:11 +0000 Subject: [PATCH 109/422] Change a variable from "int" to "i64" to make it easier to prove that it cannot overflow. FossilOrigin-Name: 00a8b3a263f3537588063ce42fad6e21fa343dad850b086d0929ed1617eb44fc --- ext/fts5/fts5_index.c | 2 +- manifest | 14 +++++++------- manifest.uuid | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/fts5/fts5_index.c b/ext/fts5/fts5_index.c index 267489a7eb..1b3f0fef99 100644 --- a/ext/fts5/fts5_index.c +++ b/ext/fts5/fts5_index.c @@ -3272,7 +3272,7 @@ static Fts5Iter *fts5MultiIterAlloc( int nSeg ){ Fts5Iter *pNew; - int nSlot; /* Power of two >= nSeg */ + i64 nSlot; /* Power of two >= nSeg */ for(nSlot=2; nSlot Date: Tue, 29 Aug 2023 11:22:45 +0000 Subject: [PATCH 110/422] Init bits of a port of Java's SQLTester to JS. Far from complete. FossilOrigin-Name: 60eec5ceda80c64870713df8e9aeabeef933c007f2010792225a07d5ef36baef --- ext/wasm/SQLTester/SQLTester.mjs | 290 +++++++++++++++++++++++++++ ext/wasm/SQLTester/SQLTester.run.mjs | 8 + ext/wasm/SQLTester/index.html | 26 +++ manifest | 16 +- manifest.uuid | 2 +- 5 files changed, 336 insertions(+), 6 deletions(-) create mode 100644 ext/wasm/SQLTester/SQLTester.mjs create mode 100644 ext/wasm/SQLTester/SQLTester.run.mjs create mode 100644 ext/wasm/SQLTester/index.html diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs new file mode 100644 index 0000000000..c295bbd849 --- /dev/null +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -0,0 +1,290 @@ +/* +** 2023-08-29 +** +** 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 contains the main application entry pointer for the +** JS implementation of the SQLTester framework. +*/ + +// UNDER CONSTRUCTION. Still being ported from the Java impl. + +import sqlite3ApiInit from '/jswasm/sqlite3.mjs'; + +const sqlite3 = await sqlite3ApiInit(); + +const log = (...args)=>{ + console.log('SQLTester:',...args); +}; + +// Return a new enum entry value +const newE = ()=>Object.create(null); + +const newObj = (props)=>Object.assign(newE(), props); + +/** + Modes for how to escape (or not) column values and names from + SQLTester.execSql() to the result buffer output. +*/ +const ResultBufferMode = Object.assign(Object.create(null),{ + //! Do not append to result buffer + NONE: newE(), + //! Append output escaped. + ESCAPED: newE(), + //! Append output as-is + ASIS: newE() +}); + +/** + Modes to specify how to emit multi-row output from + SQLTester.execSql() to the result buffer. +*/ +const ResultRowMode = newObj({ + //! Keep all result rows on one line, space-separated. + ONLINE: newE(), + //! Add a newline between each result row. + NEWLINE: newE() +}); + +class SQLTesterException extends globalThis.Error { + constructor(...args){ + super(args.join(' ')); + } + isFatal() { return false; } +} + +SQLTesterException.toss = (...args)=>{ + throw new SQLTesterException(...args); +} + +class DbException extends SQLTesterException { + constructor(...args){ + super(...args); + //TODO... + //const db = args[0]; + //if( db instanceof sqlite3.oo1.DB ) + } + isFatal() { return true; } +} + +class TestScriptFailed extends SQLTesterException { + constructor(...args){ + super(...args); + } + isFatal() { return true; } +} + +class UnknownCommand extends SQLTesterException { + constructor(...args){ + super(...args); + } +} + +class IncompatibleDirective extends SQLTesterException { + constructor(...args){ + super(...args); + } +} + +const toss = (errType, ...args)=>{ + if( !(errType instanceof SQLTesterException)){ + args.unshift(errType); + errType = SQLTesterException; + } + throw new errType(...args); +}; + +const __utf8Decoder = new TextDecoder(); +const __utf8Encoder = new TextEncoder('utf-8'); +const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer) + ? function(){} : globalThis.SharedArrayBuffer; + +const Util = newObj({ + toss, + + unlink: function(fn){ + return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn); + }, + + argvToString: (list)=>list.join(" "), + + utf8Decode: function(arrayBuffer, begin, end){ + return __utf8Decoder.decode( + (arrayBuffer.buffer instanceof __SAB) + ? arrayBuffer.slice(begin, end) + : arrayBuffer.subarray(begin, end) + ); + }, + + utf8Encode: (str)=>__utf8Encoder.encode(str) +})/*Util*/; + +class Outer { + #lnBuf = []; + #verbosity = 0; + #logger = console.log.bind(console); + + out(...args){ + this.#lnBuf.append(...args); + return this; + } + outln(...args){ + this.#lnBuf.append(...args,'\n'); + this.logger(this.#lnBuf.join('')); + this.#lnBuf.length = 0; + return this; + } + + #verboseN(lvl, argv){ + if( this.#verbosity>=lvl ){ + const pre = this.getOutputPrefix ? this.getOutputPrefix() : ''; + this.outln('VERBOSE ',lvl,' ',pre,': ',...argv); + } + } + verbose1(...args){ return this.#verboseN(1,args); } + verbose2(...args){ return this.#verboseN(2,args); } + verbose3(...args){ return this.#verboseN(3,args); } + + verbosity(){ + let rc; + if(arguments.length){ + rc = this.#verbosity; + this.#verbosity = arguments[0]; + }else{ + rc = this.#verbosity; + } + return rc; + } + +}/*Outer*/ + +class SQLTester { + SQLTester(){} + + #aFiles = []; + #inputBuffer = []; + #outputBuffer = []; + #resultBuffer = []; + #nullView = "nil"; + #metrics = newObj({ + nTotalTest: 0, nTestFile: 0, nAbortedScript: 0 + }); + #emitColNames = false; + #keepGoing = false; + #aDb = []; + #db = newObj({ + list: [], + iCurrent: 0, + initialDbName: "test.db", + }); + +}/*SQLTester*/ + +class Command { + Command(){ + } + process(sqlTester,testScript,argv){ + SQLTesterException.toss("process() must be overridden"); + } + argcCheck(testScript,argv,min,max){ + const argc = argv.length-1; + if(argc=0 && argc>max)){ + if( min==max ){ + testScript.toss(argv[0]," requires exactly ",min," argument(s)"); + }else if(max>0){ + testScript.toss(argv[0]," requires ",min,"-",max," arguments."); + }else{ + testScript.toss(argv[0]," requires at least ",min," arguments."); + } + } + + } +} + +class Cursor { + src; + buffer = []; + pos = 0; + //! Current line number. Starts at 0 for internal reasons and will + // line up with 1-based reality once parsing starts. + lineNo = 0 /* yes, zero */; + //! Putback value for this.pos. + putbackPos = 0; + //! Putback line number + putbackLineNo = 0; + //! Peeked-to pos, used by peekLine() and consumePeeked(). + peekedPos = 0; + //! Peeked-to line number. + peekedLineNo = 0; + + //! Restore parsing state to the start of the stream. + rewind(){ + this.buffer.length = 0; + this.pos = this.lineNo = this.putbackPos = + this.putbackLineNo = this.peekedPos = this.peekedLineNo = 0; + } +} + +class TestScript { + #cursor = new Cursor(); + #verbosity = 0; + #moduleName = null; + #filename = null; + #testCaseName = null; + #outer = new Outer(); + #verboseN(lvl, argv){ + if( this.#verbosity>=lvl ){ + this.outln('VERBOSE ',lvl,': ',...argv); + } + } + + verbose1(...args){ return this.#verboseN(1,args); } + verbose2(...args){ return this.#verboseN(2,args); } + verbose3(...args){ return this.#verboseN(3,args); } + + TestScript(content){ + this.cursor.src = content; + this.outer.outputPrefix = ()=>this.getOutputPrefix(); + } + + verbosity(){ + let rc; + if(arguments.length){ + rc = this.#verbosity; + this.#verbosity = arguments[0]; + }else{ + rc = this.#verbosity; + } + return rc; + } + + getOutputPrefix() { + const rc = "["+(this.moduleName || this.filename)+"]"; + if( this.testCaseName ) rc += "["+this.testCaseName+"]"; + return rc + " line "+ this.cur.lineNo; + } + + toss(...args){ + Util.toss(this.getOutputPrefix()+":",TestScriptFailed,...args) + } + +}/*TestScript*/; + + +const namespace = newObj({ + SQLTester: new SQLTester(), + DbException, + IncompatibleDirective, + SQLTesterException, + TestScriptFailed, + UnknownCommand +}); + + +export {namespace as default}; diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs new file mode 100644 index 0000000000..0a0f8903b7 --- /dev/null +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -0,0 +1,8 @@ +import {default as ns} from './SQLTester.mjs'; + +const log = (...args)=>{ + console.log('SQLTester.run:',...args); +}; + + +log("SQLTester is ostensibly ready."); diff --git a/ext/wasm/SQLTester/index.html b/ext/wasm/SQLTester/index.html new file mode 100644 index 0000000000..e782f1367d --- /dev/null +++ b/ext/wasm/SQLTester/index.html @@ -0,0 +1,26 @@ + + + + + + + + + SQLTester + + +

SQLTester App. +

+

All stuff on this page happens in the dev console.

+
+
+ + + + diff --git a/manifest b/manifest index b237cd5f65..9e9e27578e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\ssome\sdead\scode.\sAdd\sa\sskeleton\sSQLTester\sscript\sfor\sfts5. -D 2023-08-29T00:10:31.208 +C Init\sbits\sof\sa\sport\sof\sJava's\sSQLTester\sto\sJS.\sFar\sfrom\scomplete. +D 2023-08-29T11:22:45.711 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,6 +548,9 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 +F ext/wasm/SQLTester/SQLTester.mjs a3d6ed049e578b2d0965273016f56293584823760df8ff850868c1f8aea17533 +F ext/wasm/SQLTester/SQLTester.run.mjs 156e174b98e7dd78bf6d1438d32e60d7a647f8b51d8246a3054e5fdfe6042478 +F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -2108,8 +2111,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 76d3911c370b3dc02d119045003f87ad20a6efd9f7a44d4feb59b7c801ac8981 -R 7d288c901b3674a6a88e2405123881f5 +P 6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb +R 94685fe29bdbe21cb1fdfbf40f131ffe +T *branch * js-tester +T *sym-js-tester * +T -sym-trunk * Cancelled\sby\sbranch. U stephan -Z 6f3a62c9006357f22c26cb9d02ee32ca +Z 952fd010d6a4d240388ad80f3eddac99 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 094b411e25..d92a1595ed 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb \ No newline at end of file +60eec5ceda80c64870713df8e9aeabeef933c007f2010792225a07d5ef36baef \ No newline at end of file From 0fc20a32c0fce51c34714363173c8a93771c1425 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 29 Aug 2023 13:28:36 +0000 Subject: [PATCH 111/422] Get the basic parsing pieces and command dispatching in place in the JS SQLTester. FossilOrigin-Name: 8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a --- ext/wasm/SQLTester/SQLTester.mjs | 320 +++++++++++++++++++++++---- ext/wasm/SQLTester/SQLTester.run.mjs | 22 +- manifest | 17 +- manifest.uuid | 2 +- 4 files changed, 299 insertions(+), 62 deletions(-) diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index c295bbd849..3af83e9e71 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -54,7 +54,7 @@ const ResultRowMode = newObj({ class SQLTesterException extends globalThis.Error { constructor(...args){ - super(args.join(' ')); + super(args.join('')); } isFatal() { return false; } } @@ -74,8 +74,8 @@ class DbException extends SQLTesterException { } class TestScriptFailed extends SQLTesterException { - constructor(...args){ - super(...args); + constructor(testScript, ...args){ + super(testScript.getPutputPrefix(),': ',...args); } isFatal() { return true; } } @@ -130,26 +130,39 @@ class Outer { #verbosity = 0; #logger = console.log.bind(console); + constructor(){ + } + out(...args){ - this.#lnBuf.append(...args); + if(!this.#lnBuf.length && this.getOutputPrefix ){ + this.#lnBuf.push(this.getOutputPrefix()); + } + this.#lnBuf.push(...args); return this; } outln(...args){ - this.#lnBuf.append(...args,'\n'); - this.logger(this.#lnBuf.join('')); + if(!this.#lnBuf.length && this.getOutputPrefix ){ + this.#lnBuf.push(this.getOutputPrefix()); + } + this.#lnBuf.push(...args,'\n'); + this.#logger(this.#lnBuf.join('')); this.#lnBuf.length = 0; return this; } - #verboseN(lvl, argv){ + setOutputPrefix( func ){ + this.getOutputPrefix = func; + return this; + } + + verboseN(lvl, argv){ if( this.#verbosity>=lvl ){ - const pre = this.getOutputPrefix ? this.getOutputPrefix() : ''; - this.outln('VERBOSE ',lvl,' ',pre,': ',...argv); + this.outln('VERBOSE ',lvl,': ',...argv); } } - verbose1(...args){ return this.#verboseN(1,args); } - verbose2(...args){ return this.#verboseN(2,args); } - verbose3(...args){ return this.#verboseN(3,args); } + verbose1(...args){ return this.verboseN(1,args); } + verbose2(...args){ return this.verboseN(2,args); } + verbose3(...args){ return this.verboseN(3,args); } verbosity(){ let rc; @@ -165,11 +178,10 @@ class Outer { }/*Outer*/ class SQLTester { - SQLTester(){} + #outer = new Outer().setOutputPrefix( ()=>'SQLTester: ' ); #aFiles = []; #inputBuffer = []; - #outputBuffer = []; #resultBuffer = []; #nullView = "nil"; #metrics = newObj({ @@ -184,14 +196,42 @@ class SQLTester { initialDbName: "test.db", }); + constructor(){ + } + + appendInput(line, addNL){ + this.#inputBuffer.push(line); + if( addNL ) this.#inputBuffer.push('\n'); + } + appendResult(line, addNL){ + this.#resultBuffer.push(line); + if( addNL ) this.#resultBuffer.push('\n'); + } + + clearInputBuffer(){ + this.#inputBuffer.length = 0; + return this.#inputBuffer; + } + clearResultBuffer(){ + this.#resultBuffer.length = 0; + return this.#resultBuffer; + } + + getInputText(){ return this.#inputBuffer.join(''); } + getResultText(){ return this.#resultBuffer.join(''); } + + verbosity(...args){ return this.#outer.verbosity(...args); } + }/*SQLTester*/ class Command { - Command(){ + constructor(){ } + process(sqlTester,testScript,argv){ SQLTesterException.toss("process() must be overridden"); } + argcCheck(testScript,argv,min,max){ const argc = argv.length-1; if(argc=0 && argc>max)){ @@ -203,13 +243,22 @@ class Command { testScript.toss(argv[0]," requires at least ",min," arguments."); } } + } +} +class TestCase extends Command { + + process(tester, script, argv){ + this.argcCheck(script, argv,1); + script.testCaseName(argv[1]); + tester.clearResultBuffer(); + tester.clearInputBuffer(); } } class Cursor { src; - buffer = []; + sb = []; pos = 0; //! Current line number. Starts at 0 for internal reasons and will // line up with 1-based reality once parsing starts. @@ -223,68 +272,241 @@ class Cursor { //! Peeked-to line number. peekedLineNo = 0; + constructor(){ + } + //! Restore parsing state to the start of the stream. rewind(){ - this.buffer.length = 0; - this.pos = this.lineNo = this.putbackPos = - this.putbackLineNo = this.peekedPos = this.peekedLineNo = 0; + this.sb.length = this.pos = this.lineNo + = this.putbackPos = this.putbackLineNo + = this.peekedPos = this.peekedLineNo = 0; } } +const Rx = newObj({ + requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/, + scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, + mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, + command: /^--(([a-z-]+)( .*)?)$/ +}); + class TestScript { #cursor = new Cursor(); - #verbosity = 0; #moduleName = null; #filename = null; #testCaseName = null; - #outer = new Outer(); - #verboseN(lvl, argv){ - if( this.#verbosity>=lvl ){ - this.outln('VERBOSE ',lvl,': ',...argv); - } - } + #outer = new Outer().setOutputPrefix( ()=>this.getOutputPrefix() ); - verbose1(...args){ return this.#verboseN(1,args); } - verbose2(...args){ return this.#verboseN(2,args); } - verbose3(...args){ return this.#verboseN(3,args); } - - TestScript(content){ - this.cursor.src = content; - this.outer.outputPrefix = ()=>this.getOutputPrefix(); - } - - verbosity(){ - let rc; - if(arguments.length){ - rc = this.#verbosity; - this.#verbosity = arguments[0]; + constructor(...args){ + let content, filename; + if( 2 == args.length ){ + filename = args[0]; + content = args[1]; }else{ - rc = this.#verbosity; + content = args[0]; } - return rc; + this.#filename = filename; + this.#cursor.src = content; + this.#outer.outputPrefix = ()=>this.getOutputPrefix(); + } + + testCaseName(){ + return (0==arguments.length) + ? this.#testCaseName : (this.#testCaseName = arguments[0]); } getOutputPrefix() { - const rc = "["+(this.moduleName || this.filename)+"]"; - if( this.testCaseName ) rc += "["+this.testCaseName+"]"; - return rc + " line "+ this.cur.lineNo; + let rc = "["+(this.#moduleName || this.#filename)+"]"; + if( this.#testCaseName ) rc += "["+this.#testCaseName+"]"; + return rc + " line "+ this.#cursor.lineNo +" "; + } + + reset(){ + this.#testCaseName = null; + this.#cursor.rewind(); + return this; } toss(...args){ - Util.toss(this.getOutputPrefix()+":",TestScriptFailed,...args) + throw new TestScriptFailed(this,...args); } + #checkForDirective(tester,line){ + //todo + } + + #getCommandArgv(line){ + const m = Rx.command.exec(line); + return m ? m[1].trim().split(/\s+/) : null; + } + + run(tester){ + this.reset(); + this.#outer.verbosity(tester.verbosity()); + let line, directive, argv = []; + while( null != (line = this.getLine()) ){ + this.verbose3("input line: ",line); + this.#checkForDirective(tester, line); + argv = this.#getCommandArgv(line); + if( argv ){ + this.#processCommand(tester, argv); + continue; + } + tester.appendInput(line,true); + } + return true; + } + + #processCommand(tester, argv){ + this.verbose1("running command: ",argv[0], " ", Util.argvToString(argv)); + if(this.#outer.verbosity()>1){ + const input = tester.getInputText(); + if( !!input ) this.verbose3("Input buffer = ",input); + } + CommandDispatcher.dispatch(tester, this, argv); + } + + getLine(){ + const cur = this.#cursor; + if( cur.pos==cur.src.byteLength ){ + return null/*EOF*/; + } + cur.putbackPos = cur.pos; + cur.putbackLineNo = cur.lineNo; + cur.sb.length = 0; + let b = 0, prevB = 0, i = cur.pos; + let doBreak = false; + let nChar = 0 /* number of bytes in the aChar char */; + const end = cur.src.byteLength; + for(; i < end && !doBreak; ++i){ + b = cur.src[i]; + switch( b ){ + case 13/*CR*/: continue; + case 10/*NL*/: + ++cur.lineNo; + if(cur.sb.length>0) doBreak = true; + // Else it's an empty string + break; + default:{ + /* Multi-byte chars need to be gathered up and appended at + one time so that we can get them as string objects. */ + nChar = 1; + switch( b & 0xF0 ){ + case 0xC0: nChar = 2; break; + case 0xE0: nChar = 3; break; + case 0xF0: nChar = 4; break; + default: + if( b > 127 ) this.toss("Invalid character (#"+b+")."); + break; + } + if( 1==nChar ){ + cur.sb.push(String.fromCharCode(b)); + }else{ + const aChar = [] /* multi-byte char buffer */; + for(let x = 0; (x < nChar) && (i+x < end); ++x) aChar[x] = cur.src[i+x]; + cur.sb.push( + Util.utf8Decode( new Uint8Array(aChar) ) + ); + i += nChar-1; + } + break; + } + } + } + cur.pos = i; + const rv = cur.sb.join(''); + if( i==cur.src.byteLength && 0==rv.length ){ + return null /* EOF */; + } + return rv; + }/*getLine()*/ + + /** + Fetches the next line then resets the cursor to its pre-call + state. consumePeeked() can be used to consume this peeked line + without having to re-parse it. + */ + peekLine(){ + const cur = this.#cursor; + const oldPos = cur.pos; + const oldPB = cur.putbackPos; + const oldPBL = cur.putbackLineNo; + const oldLine = cur.lineNo; + const rc = this.getLine(); + cur.peekedPos = cur.pos; + cur.peekedLineNo = cur.lineNo; + cur.pos = oldPos; + cur.lineNo = oldLine; + cur.putbackPos = oldPB; + cur.putbackLineNo = oldPBL; + return rc; + } + + + /** + Only valid after calling peekLine() and before calling getLine(). + This places the cursor to the position it would have been at had + the peekLine() had been fetched with getLine(). + */ + consumePeeked(){ + const cur = this.#cursor; + cur.pos = cur.peekedPos; + cur.lineNo = cur.peekedLineNo; + } + + /** + Restores the cursor to the position it had before the previous + call to getLine(). + */ + putbackLine(){ + const cur = this.#cursor; + cur.pos = cur.putbackPos; + cur.lineNo = cur.putbackLineNo; + } + + verbose1(...args){ return this.#outer.verboseN(1,args); } + verbose2(...args){ return this.#outer.verboseN(2,args); } + verbose3(...args){ return this.#outer.verboseN(3,args); } + verbosity(...args){ return this.#outer.verbosity(...args); } + }/*TestScript*/; +class CommandDispatcher { + static map = newObj(); + + static getCommandByName(name){ + let rv = CommandDispatcher.map[name]; + if( rv ) return rv; + switch(name){ + //todo: map name to Command instance + case "testcase": rv = new TestCase(); break; + } + if( rv ){ + CommandDispatcher.map[name] = rv; + } + return rv; + } + + static dispatch(tester, testScript, argv){ + const cmd = CommandDispatcher.getCommandByName(argv[0]); + if( !cmd ){ + toss(UnknownCommand,argv[0],' ',testScript.getOutputPrefix()); + } + cmd.process(tester, testScript, argv); + } +}/*CommandDispatcher*/ const namespace = newObj({ - SQLTester: new SQLTester(), + Command, DbException, IncompatibleDirective, + Outer, + SQLTester, SQLTesterException, + TestScript, TestScriptFailed, - UnknownCommand + UnknownCommand, + Util }); - export {namespace as default}; diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index 0a0f8903b7..4efd068e30 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -1,8 +1,26 @@ import {default as ns} from './SQLTester.mjs'; -const log = (...args)=>{ +const log = function f(...args){ console.log('SQLTester.run:',...args); + return f; }; +console.log("Loaded",ns); +const out = function f(...args){ return f.outer.out(...args) }; +out.outer = new ns.Outer(); +out.outer.getOutputPrefix = ()=>'SQLTester.run: '; +const outln = (...args)=>{ return out.outer.outln(...args) }; -log("SQLTester is ostensibly ready."); +log("ns =",ns); +out("Hi there. ").outln("SQLTester is ostensibly ready."); + +let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode(` +# comment line +select 1; +--testcase 0.0 +#--result 1 +`)); + +const sqt = new ns.SQLTester(); +sqt.verbosity(3); +ts.run(sqt); diff --git a/manifest b/manifest index 9e9e27578e..8e208a8ae1 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Init\sbits\sof\sa\sport\sof\sJava's\sSQLTester\sto\sJS.\sFar\sfrom\scomplete. -D 2023-08-29T11:22:45.711 +C Get\sthe\sbasic\sparsing\spieces\sand\scommand\sdispatching\sin\splace\sin\sthe\sJS\sSQLTester. +D 2023-08-29T13:28:36.476 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,8 +548,8 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs a3d6ed049e578b2d0965273016f56293584823760df8ff850868c1f8aea17533 -F ext/wasm/SQLTester/SQLTester.run.mjs 156e174b98e7dd78bf6d1438d32e60d7a647f8b51d8246a3054e5fdfe6042478 +F ext/wasm/SQLTester/SQLTester.mjs 2ea7d09f0c33e509aa3c4ca974be5705f59ddcd2173d4ff2721d7448c65be8bd +F ext/wasm/SQLTester/SQLTester.run.mjs 478f4d90951591decaa7e1e3fa1729f6ed0043ae4cb48b0a92056b9707d44185 F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -2111,11 +2111,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 6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb -R 94685fe29bdbe21cb1fdfbf40f131ffe -T *branch * js-tester -T *sym-js-tester * -T -sym-trunk * Cancelled\sby\sbranch. +P 60eec5ceda80c64870713df8e9aeabeef933c007f2010792225a07d5ef36baef +R 0bdc268c3b78f235b9f50e80fcfe1a69 U stephan -Z 952fd010d6a4d240388ad80f3eddac99 +Z 00dd200744de92467554b7f23df0cf79 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d92a1595ed..212a3786b4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -60eec5ceda80c64870713df8e9aeabeef933c007f2010792225a07d5ef36baef \ No newline at end of file +8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a \ No newline at end of file From e1e67abc5cf67f931aab1e471eda23d73f51d456 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 29 Aug 2023 15:24:41 +0000 Subject: [PATCH 112/422] Add support for the CONCAT() and CONCAT_WS() SQL functions, modeled after the PostgreSQL behavior. FossilOrigin-Name: 0b434ca7aa19eff4ad134a8c6f88f6a7ccab88864faa55e93579a2462d8ac3bc --- manifest | 15 +++++----- manifest.uuid | 2 +- src/func.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ test/func9.test | 40 +++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 test/func9.test diff --git a/manifest b/manifest index 34defa2299..073b8e1220 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sa\svariable\sfrom\s"int"\sto\s"i64"\sto\smake\sit\seasier\sto\sprove\sthat\sit\scannot\soverflow. -D 2023-08-29T10:50:11.066 +C Add\ssupport\sfor\sthe\sCONCAT()\sand\sCONCAT_WS()\sSQL\sfunctions,\smodeled\safter\nthe\sPostgreSQL\sbehavior. +D 2023-08-29T15:24:41.645 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -653,7 +653,7 @@ F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 F src/expr.c 1affe0cc049683ef0ef3545d9b6901508556b0ef7e2892a344c3df6d7288d79d F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007 F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36 -F src/func.c f480d46974ecc84fefdd429377158981b974e0e33d656f1b0e919ba7c4bdd390 +F src/func.c c96c7f55b97493ce278937e6ab6578a866b7e5d6dd486d58b220e9d17e88a750 F src/global.c 29f56a330ed9d1b5cd9b79ac0ca36f97ac3afc730ff8bfa987b0db9e559d684d F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220 F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51 @@ -1180,6 +1180,7 @@ F test/func5.test 863e6d1bd0013d09c17236f8a13ea34008dd857d87d85a13a673960e4c25d8 F test/func6.test 9cc9b1f43b435af34fe1416eb1e318c8920448ea7a6962f2121972f5215cb9b0 F test/func7.test adbfc910385a6ffd15dc47be3c619ef070c542fcb7488964badb17b2d9a4d080 F test/func8.test c4e2ecacf9f16e47a245e7a25fbabcc7e78f9c7c41a80f158527cdfdc6dd299d +F test/func9.test b32d313f679aa9698d52f39519d301c3941823cb72b4e23406c210eadd82c824 F test/fuzz-oss1.test 514dcabb24687818ea949fa6760229eaacad74ca70157743ef36d35bbe01ffb0 F test/fuzz.test 4608c1310cff4c3014a84bcced6278139743e080046e5f6784b0de7b069371d8 F test/fuzz2.test 76dc35b32b6d6f965259508508abce75a6c4d7e1 @@ -2108,8 +2109,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 6c83e31fa96f65b61377c0c801cc32b3c8ca27a0c8442f860364bec258c003cb -R 703966de64a45733a2ec15e9ba123f27 -U dan -Z cdc3674e70b09e1a4bfbc46bbf505365 +P 00a8b3a263f3537588063ce42fad6e21fa343dad850b086d0929ed1617eb44fc +R 73410e2beee6b3d9558e014cdb959353 +U drh +Z 75edcaca9693bb69bba026024423283c # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 4af2563b02..6cabfb5376 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00a8b3a263f3537588063ce42fad6e21fa343dad850b086d0929ed1617eb44fc \ No newline at end of file +0b434ca7aa19eff4ad134a8c6f88f6a7ccab88864faa55e93579a2462d8ac3bc \ No newline at end of file diff --git a/src/func.c b/src/func.c index 70fda85927..3332523486 100644 --- a/src/func.c +++ b/src/func.c @@ -1550,6 +1550,81 @@ static void trimFunc( sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT); } +/* The core implementation of the CONCAT(...) and CONCAT_WS(SEP,...) +** functions. +** +** Return a string value that is the concatenation of all non-null +** entries in argv[]. Use zSep as the separator. +*/ +static void concatFuncCore( + sqlite3_context *context, + int argc, + sqlite3_value **argv, + int nSep, + const char *zSep +){ + i64 j, k, n = 0; + int i; + char *z; + for(i=0; i0 ){ + const char *v = (const char*)sqlite3_value_text(argv[i]); + if( ALWAYS(v!=0) ){ + if( j>0 && nSep>0 ){ + memcpy(&z[j], zSep, nSep); + j += nSep; + } + memcpy(&z[j], v, k); + j += k; + } + } + } + z[j] = 0; + assert( j<=n ); + sqlite3_result_text64(context, z, n, sqlite3_free, SQLITE_UTF8); +} + +/* +** The CONCAT(...) function. Generate a string result that is the +** concatentation of all non-null arguments. +*/ +static void concatFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + concatFuncCore(context, argc, argv, 0, ""); +} + +/* +** The CONCAT_WS(separator, ...) function. +** +** Generate a string that is the concatenation of 2nd through the Nth +** argument. Use the first argument (which must be non-NULL) as the +** separator. +*/ +static void concatwsFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int nSep = sqlite3_value_bytes(argv[0]); + const char *zSep = (const char*)sqlite3_value_text(argv[0]); + if( zSep==0 ) return; + concatFuncCore(context, argc-1, argv+1, nSep, zSep); +} + #ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION /* @@ -2559,6 +2634,11 @@ void sqlite3RegisterBuiltinFunctions(void){ FUNCTION(hex, 1, 0, 0, hexFunc ), FUNCTION(unhex, 1, 0, 0, unhexFunc ), FUNCTION(unhex, 2, 0, 0, unhexFunc ), + FUNCTION(concat, -1, 0, 0, concatFunc ), + FUNCTION(concat, 0, 0, 0, 0 ), + FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ), + FUNCTION(concat_ws, 0, 0, 0, 0 ), + FUNCTION(concat_ws, 1, 0, 0, 0 ), INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ), VFUNCTION(random, 0, 0, 0, randomFunc ), VFUNCTION(randomblob, 1, 0, 0, randomBlob ), diff --git a/test/func9.test b/test/func9.test new file mode 100644 index 0000000000..6cf9fc31ec --- /dev/null +++ b/test/func9.test @@ -0,0 +1,40 @@ +# 2023-08-29 +# +# 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. +# +#************************************************************************* +# +# Test cases for SQL newer functions +# +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +do_execsql_test func9-100 { + SELECT concat('abc',123,null,'xyz'); +} {abc123xyz} +do_execsql_test func9-110 { + SELECT typeof(concat(null)); +} {text} +do_catchsql_test func9-120 { + SELECT concat(); +} {1 {wrong number of arguments to function concat()}} +do_execsql_test func9-130 { + SELECT concat_ws(',',1,2,3,4,5,6,7,8,NULL,9,10,11,12); +} {1,2,3,4,5,6,7,8,9,10,11,12} +do_execsql_test func9-140 { + SELECT concat_ws(NULL,1,2,3,4,5,6,7,8,NULL,9,10,11,12); +} {{}} +do_catchsql_test func9-150 { + SELECT concat_ws(); +} {1 {wrong number of arguments to function concat_ws()}} +do_catchsql_test func9-160 { + SELECT concat_ws(','); +} {1 {wrong number of arguments to function concat_ws()}} + + +finish_test From 9d6acd960fd0aceec73f2c492a2c2d67e8ad2924 Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 29 Aug 2023 15:38:54 +0000 Subject: [PATCH 113/422] Change the version number to 3.44.0 to begin the next development cycle. (Should have done this a few check-ins ago.) FossilOrigin-Name: 253fe4a45decdcc4180c5aeccaf4cd0d1325a0f6df0913b0e6721ea3e26b4fe2 --- VERSION | 2 +- configure | 18 +++++++++--------- manifest | 14 +++++++------- manifest.uuid | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/VERSION b/VERSION index a9184766ba..faf0dcbb0e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.43.0 +3.44.0 diff --git a/configure b/configure index 6b0c584a4a..e5aa019447 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for sqlite 3.43.0. +# Generated by GNU Autoconf 2.69 for sqlite 3.44.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -726,8 +726,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.43.0' -PACKAGE_STRING='sqlite 3.43.0' +PACKAGE_VERSION='3.44.0' +PACKAGE_STRING='sqlite 3.44.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1470,7 +1470,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.43.0 to adapt to many kinds of systems. +\`configure' configures sqlite 3.44.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1535,7 +1535,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.43.0:";; + short | recursive ) echo "Configuration of sqlite 3.44.0:";; esac cat <<\_ACEOF @@ -1665,7 +1665,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.43.0 +sqlite configure 3.44.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2084,7 +2084,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.43.0, which was +It was created by sqlite $as_me 3.44.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -12457,7 +12457,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.43.0, which was +This file was extended by sqlite $as_me 3.44.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -12523,7 +12523,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -sqlite config.status 3.43.0 +sqlite config.status 3.44.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/manifest b/manifest index 073b8e1220..7623764200 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\ssupport\sfor\sthe\sCONCAT()\sand\sCONCAT_WS()\sSQL\sfunctions,\smodeled\safter\nthe\sPostgreSQL\sbehavior. -D 2023-08-29T15:24:41.645 +C Change\sthe\sversion\snumber\sto\s3.44.0\sto\sbegin\sthe\snext\sdevelopment\ncycle.\s\s(Should\shave\sdone\sthis\sa\sfew\scheck-ins\sago.) +D 2023-08-29T15:38:54.461 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -7,7 +7,7 @@ F Makefile.in 345a8599cf8ff015db534cedad7af70a1a6c36e295b85d720966c18af836ed30 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 F Makefile.msc 26c2d196391a285c279adb10fd6001774d9b243af94b700b681e4a49cd476684 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 -F VERSION c6366dc72582d3144ce87b013cc35fe48d62f6d07d5be0c9716ea33c862144aa +F VERSION 4c09b629c03b8ae32317cb336a32f3aa3252841d6dcd51184cecc4278d08f21e F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2 F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90 @@ -33,7 +33,7 @@ F autoconf/tea/win/nmakehlp.c b01f822eabbe1ed2b64e70882d97d48402b42d2689a1ea0034 F autoconf/tea/win/rules.vc c511f222b80064096b705dbeb97060ee1d6b6d63 F config.guess 883205ddf25b46f10c181818bf42c09da9888884af96f79e1719264345053bd6 F config.sub c2d0260f17f3e4bc0b6808fccf1b291cb5e9126c14fc5890efc77b9fd0175559 -F configure 9dc3300339f4d6b3c3b108de60cc6ae6b3c547e25c7e6df280b4775db4de3a1b x +F configure 550c90a234b3d4ae80919e3c0a0bf0e6f34e4288eb613d7c71375714162a5038 x F configure.ac 4654d32ac0a0d0b48f1e1e79bdc3d777b723cf2f63c33eb1d7c4ed8b435938e8 F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad F doc/F2FS.txt c1d4a0ae9711cfe0e1d8b019d154f1c29e0d3abfe820787ba1e9ed7691160fcd @@ -2109,8 +2109,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 00a8b3a263f3537588063ce42fad6e21fa343dad850b086d0929ed1617eb44fc -R 73410e2beee6b3d9558e014cdb959353 +P 0b434ca7aa19eff4ad134a8c6f88f6a7ccab88864faa55e93579a2462d8ac3bc +R 36c15551b362fc3e22c9ee9fe92071f4 U drh -Z 75edcaca9693bb69bba026024423283c +Z bcbb66a4b150dee1334cf48c8a72dab0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 6cabfb5376..b1cf3eb395 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0b434ca7aa19eff4ad134a8c6f88f6a7ccab88864faa55e93579a2462d8ac3bc \ No newline at end of file +253fe4a45decdcc4180c5aeccaf4cd0d1325a0f6df0913b0e6721ea3e26b4fe2 \ No newline at end of file From 69a55ca17dc711a9b75eb738ab32336936d69fd7 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 29 Aug 2023 15:39:57 +0000 Subject: [PATCH 114/422] Get the JS SQLTester command handlers in place sans those which have to run SQL. FossilOrigin-Name: d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 --- .../src/org/sqlite/jni/tester/SQLTester.java | 6 +- ext/wasm/SQLTester/SQLTester.mjs | 377 ++++++++++++++++-- ext/wasm/SQLTester/SQLTester.run.mjs | 41 +- ext/wasm/api/sqlite3-api-prologue.js | 36 ++ manifest | 18 +- manifest.uuid | 2 +- 6 files changed, 424 insertions(+), 56 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index e355fe7603..f7550c8e46 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -336,7 +336,9 @@ public class SQLTester { } sqlite3 setCurrentDb(int n) throws Exception{ - return affirmDbId(n).aDb[n]; + affirmDbId(n); + iCurrentDb = n; + return this.aDb[n]; } sqlite3 getCurrentDb(){ return aDb[iCurrentDb]; } @@ -399,7 +401,7 @@ public class SQLTester { nullView = "nil"; emitColNames = false; iCurrentDb = 0; - dbInitSql.append("SELECT 1;"); + //dbInitSql.append("SELECT 1;"); } void setNullValue(String v){nullView = v;} diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 3af83e9e71..0461beb86f 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -64,11 +64,9 @@ SQLTesterException.toss = (...args)=>{ } class DbException extends SQLTesterException { - constructor(...args){ - super(...args); - //TODO... - //const db = args[0]; - //if( db instanceof sqlite3.oo1.DB ) + constructor(pDb, rc, closeDb){ + super("DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb)); + if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb); } isFatal() { return true; } } @@ -150,9 +148,14 @@ class Outer { return this; } - setOutputPrefix( func ){ - this.getOutputPrefix = func; - return this; + outputPrefix(){ + if( 0==arguments.length ){ + return (this.getOutputPrefix + ? (this.getOutputPrefix() ?? '') : ''); + }else{ + this.getOutputPrefix = arguments[0]; + return this; + } } verboseN(lvl, argv){ @@ -179,7 +182,7 @@ class Outer { class SQLTester { - #outer = new Outer().setOutputPrefix( ()=>'SQLTester: ' ); + #outer = new Outer().outputPrefix( ()=>'SQLTester: ' ); #aFiles = []; #inputBuffer = []; #resultBuffer = []; @@ -189,16 +192,34 @@ class SQLTester { }); #emitColNames = false; #keepGoing = false; - #aDb = []; #db = newObj({ - list: [], - iCurrent: 0, + list: new Array(7), + iCurrentDb: 0, initialDbName: "test.db", + initSql: ['select 1;'], + currentDb: function(){ + return this.list[this.iCurrentDb]; + } }); constructor(){ } + outln(...args){ return this.#outer.outln(...args); } + out(...args){ return this.#outer.out(...args); } + + reset(){ + this.clearInputBuffer(); + this.clearResultBuffer(); + this.#clearBuffer(this.#db.initSql); + this.closeAllDbs(); + this.nTest = 0; + this.nullView = "nil"; + this.emitColNames = false; + this.#db.iCurrentDb = 0; + this.#db.initSql.push("SELECT 1;"); + } + appendInput(line, addNL){ this.#inputBuffer.push(line); if( addNL ) this.#inputBuffer.push('\n'); @@ -208,20 +229,195 @@ class SQLTester { if( addNL ) this.#resultBuffer.push('\n'); } - clearInputBuffer(){ - this.#inputBuffer.length = 0; - return this.#inputBuffer; - } - clearResultBuffer(){ - this.#resultBuffer.length = 0; - return this.#resultBuffer; + #clearBuffer(buffer){ + buffer.length = 0; + return buffer; } + clearInputBuffer(){ return this.#clearBuffer(this.#inputBuffer); } + clearResultBuffer(){return this.#clearBuffer(this.#resultBuffer); } + getInputText(){ return this.#inputBuffer.join(''); } getResultText(){ return this.#resultBuffer.join(''); } + #takeBuffer(buffer){ + const s = buffer.join(''); + buffer.length = 0; + return s; + } + + takeInputBuffer(){ + return this.#takeBuffer(this.#inputBuffer); + } + takeResultBuffer(){ + return this.#takeBuffer(this.#resultBuffer); + } + verbosity(...args){ return this.#outer.verbosity(...args); } + nullValue(){ + if( 0==arguments.length ){ + return this.#nullView; + }else{ + this.#nullView = ''+arguments[0]; + } + } + + outputColumnNames(){ + if( 0==arguments.length ){ + return this.#emitColNames; + }else{ + this.#emitColNames = !!arguments[0]; + } + } + + currentDbId(){ + if( 0==arguments.length ){ + return this.#db.iCurrentDb; + }else{ + this.#affirmDbId(arguments[0]).#db.iCurrentDb = arguments[0]; + } + } + + #affirmDbId(id){ + if(id<0 || id>=this.#db.list.length){ + toss(SQLTesterException, "Database index ",id," is out of range."); + } + return this; + } + + currentDb(...args){ + if( 0!=args.length ){ + this.#affirmDbId(id).#db.iCurrentDb = id; + } + return this.#db.currentDb(); + } + + getDbById(id){ + return this.#affirmDbId(id).#db.list[id]; + } + + getCurrentDb(){ return this.#db.list[this.#db.iCurrentDb]; } + + + closeDb(id) { + if( 0==arguments.length ){ + id = this.#db.iCurrentDb; + } + const db = this.#affirmDbId(id).#db.list[id]; + if( db ){ + sqlite3.capi.sqlite3_close_v2(db); + this.#db.list[id] = null; + } + } + + closeAllDbs(){ + for(let i = 0; i 0){ + //this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString()); + rc = this.execSql(pDb, false, ResultBufferMode.NONE, + null, this.#db.initSql.join('')); + } + if( 0!=rc ){ + sqlite3.SQLite3Error.toss( + rc, + "sqlite3 result code",rc+":", + (pDb ? sqlite3.capi.sqlite3_errmsg(pDb) + : sqlite3.capi.sqlite3_errstr(rc)) + ); + } + return this.#db.list[this.#db.iCurrentDb] = pDb; + }catch(e){ + sqlite3.capi.sqlite3_close_v2(pDb); + throw e; + } + } + + #setupInitialDb(){ + if( !this.#db.list[0] ){ + Util.unlink(this.#db.initialDbName); + this.openDb(0, this.#db.initialDbName, true); + }else{ + this.#outer.outln("WARNING: setupInitialDb() unexpectedly ", + "triggered while it is opened."); + } + } + + /** + Returns v or some escaped form of v, as defined in the tester's + spec doc. + */ + #escapeSqlValue(v){ + if( !v ) return "{}"; + if( !Rx.special.test(v) ){ + return v /* no escaping needed */; + } + if( !Rx.squiggly.test(v) ){ + return "{"+v+"}"; + } + const sb = ["\""]; + const n = v.length; + for(let i = 0; i < n; ++i){ + const ch = v.charAt(i); + switch(ch){ + case '\\': sb.push("\\\\"); break; + case '"': sb.push("\\\""); break; + default:{ + //verbose("CHAR ",(int)ch," ",ch," octal=",String.format("\\%03o", (int)ch)); + const ccode = ch.charCodeAt(i); + if( ccode < 32 ) sb.push('\\',ccode.toString(8),'o'); + else sb.push(ch); + break; + } + } + } + sb.append("\""); + return sb.join(''); + } + + #appendDbErr(pDb, sb, rc){ + sb.push(sqlite3.capi.sqlite3_js_rc_str(rc), ' '); + const msg = this.#escapeSqlValue(sqlite3.capi.sqlite3_errmsg(pDb)); + if( '{' == msg.charAt(0) ){ + sb.push(msg); + }else{ + sb.push('{', msg, '}'); + } + } + + execSql(pDb, throwOnError, appendMode, lineMode, sql){ + sql = sqlite3.capi.sqlite3_js_sql_to_string(sql); + this.#outer.outln("execSql() is TODO. ",sql); + return 0; + } + }/*SQLTester*/ class Command { @@ -246,16 +442,6 @@ class Command { } } -class TestCase extends Command { - - process(tester, script, argv){ - this.argcCheck(script, argv,1); - script.testCaseName(argv[1]); - tester.clearResultBuffer(); - tester.clearInputBuffer(); - } -} - class Cursor { src; sb = []; @@ -287,7 +473,11 @@ const Rx = newObj({ requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/, scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, - command: /^--(([a-z-]+)( .*)?)$/ + command: /^--(([a-z-]+)( .*)?)$/, + //! "Special" characters - we have to escape output if it contains any. + special: /[\x00-\x20\x22\x5c\x7b\x7d]/, + //! Either of '{' or '}'. + squiggly: /[{}]/ }); class TestScript { @@ -295,7 +485,7 @@ class TestScript { #moduleName = null; #filename = null; #testCaseName = null; - #outer = new Outer().setOutputPrefix( ()=>this.getOutputPrefix() ); + #outer = new Outer().outputPrefix( ()=>this.getOutputPrefix()+': ' ); constructor(...args){ let content, filename; @@ -307,7 +497,6 @@ class TestScript { } this.#filename = filename; this.#cursor.src = content; - this.#outer.outputPrefix = ()=>this.getOutputPrefix(); } testCaseName(){ @@ -318,7 +507,7 @@ class TestScript { getOutputPrefix() { let rc = "["+(this.#moduleName || this.#filename)+"]"; if( this.#testCaseName ) rc += "["+this.#testCaseName+"]"; - return rc + " line "+ this.#cursor.lineNo +" "; + return rc + " line "+ this.#cursor.lineNo; } reset(){ @@ -471,6 +660,106 @@ class TestScript { }/*TestScript*/; +//! --close command +class CloseDbCommand extends Command { + process(t, ts, argv){ + this.argcCheck(ts,argv,0,1); + let id; + if(argv.length>1){ + const arg = argv[1]; + if("all".equals(arg)){ + t.closeAllDbs(); + return; + } + else{ + id = parseInt(arg); + } + }else{ + id = t.currentDbId(); + } + t.closeDb(id); + } +} + +//! --column-names command +class ColumnNamesCommand extends Command { + process( st, ts, argv ){ + this.argcCheck(ts,argv,1); + st.outputColumnNames( !!parseInt(argv[1]) ); + } +} + +//! --db command +class DbCommand extends Command { + process(t, ts, argv){ + this.argcCheck(ts,argv,1); + t.currentDbId( parseInt(argv[1]) ); + } +} + +//! --open command +class OpenDbCommand extends Command { + #createIfNeeded = false; + constructor(createIfNeeded=false){ + super(); + this.#createIfNeeded = createIfNeeded; + } + process(t, ts, argv){ + this.argcCheck(ts,argv,1); + t.openDb(argv[1], this.#createIfNeeded); + } +} + +//! --new command +class NewDbCommand extends OpenDbCommand { + constructor(){ super(true); } +} + +//! Placeholder dummy/no-op commands +class NoopCommand extends Command { + process(t, ts, argv){} +} + +//! --null command +class NullCommand extends Command { + process(st, ts, argv){ + this.argcCheck(ts,argv,1); + st.nullValue( argv[1] ); + } +} + +//! --print command +class PrintCommand extends Command { + process(st, ts, argv){ + st.out(ts.getOutputPrefix(),': '); + if( 1==argv.length ){ + st.out( st.getInputText() ); + }else{ + st.outln( Util.argvToString(argv) ); + } + } +} + + +//! --testcase command +class TestCaseCommand extends Command { + process(tester, script, argv){ + this.argcCheck(script, argv,1); + script.testCaseName(argv[1]); + tester.clearResultBuffer(); + tester.clearInputBuffer(); + } +} + + +//! --verbosity command +class VerbosityCommand extends Command { + process(t, ts, argv){ + t.argcCheck(ts,argv,1); + ts.verbosity( parseInt(argv[1]) ); + } +} + class CommandDispatcher { static map = newObj(); @@ -478,8 +767,23 @@ class CommandDispatcher { let rv = CommandDispatcher.map[name]; if( rv ) return rv; switch(name){ - //todo: map name to Command instance - case "testcase": rv = new TestCase(); break; + case "close": rv = new CloseDbCommand(); break; + case "column-names": rv = new ColumnNamesCommand(); break; + case "db": rv = new DbCommand(); break; + //case "glob": rv = new GlobCommand(); break; + //case "json": rv = new JsonCommand(); break; + //case "json-block": rv = new JsonBlockCommand(); break; + case "new": rv = new NewDbCommand(); break; + //case "notglob": rv = new NotGlobCommand(); break; + case "null": rv = new NullCommand(); break; + case "oom": rv = new NoopCommand(); break; + case "open": rv = new OpenDbCommand(); break; + case "print": rv = new PrintCommand(); break; + //case "result": rv = new ResultCommand(); break; + //case "run": rv = new RunCommand(); break; + //case "tableresult": rv = new TableResultCommand(); break; + case "testcase": rv = new TestCaseCommand(); break; + case "verbosity": rv = new VerbosityCommand(); break; } if( rv ){ CommandDispatcher.map[name] = rv; @@ -506,7 +810,8 @@ const namespace = newObj({ TestScript, TestScriptFailed, UnknownCommand, - Util + Util, + sqlite3 }); export {namespace as default}; diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index 4efd068e30..dc8eaa0c17 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -1,26 +1,51 @@ import {default as ns} from './SQLTester.mjs'; +globalThis.sqlite3 = ns.sqlite3; const log = function f(...args){ console.log('SQLTester.run:',...args); return f; }; -console.log("Loaded",ns); const out = function f(...args){ return f.outer.out(...args) }; out.outer = new ns.Outer(); out.outer.getOutputPrefix = ()=>'SQLTester.run: '; const outln = (...args)=>{ return out.outer.outln(...args) }; +const affirm = function(expr, msg){ + if( !expr ){ + throw new Error(arguments[1] + ? ("Assertion failed: "+arguments[1]) + : "Assertion failed"); + } +} + +console.log("Loaded",ns); + log("ns =",ns); out("Hi there. ").outln("SQLTester is ostensibly ready."); -let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode(` -# comment line -select 1; ---testcase 0.0 -#--result 1 +let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode( +`# comment line +--print Starting up... +--null NIL +--new :memory: +--testcase 0.0.1 +select '0.0.1'; +#--result 0.0.1 +--print done `)); const sqt = new ns.SQLTester(); -sqt.verbosity(3); -ts.run(sqt); +try{ + log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); + sqt.openDb('/foo.db', true); + log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); + sqt.verbosity(0); + affirm( 'NIL' !== sqt.nullValue() ); + ts.run(sqt); + affirm( 'NIL' === sqt.nullValue() ); +}finally{ + sqt.reset(); +} +log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); + diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 5abb13b990..35c856269d 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1135,7 +1135,23 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return 1===n ? wasm.pstack.alloc(safePtrSize ? 8 : wasm.ptrSizeof) : wasm.pstack.allocChunks(n, safePtrSize ? 8 : wasm.ptrSizeof); + }, + + /** + Records the current pstack position, calls the given function, + and restores the pstack regardless of whether the function + throws. Returns the result of the call or propagates an + exception on error. + + Added in 3.44. + */ + call: function(f){ + const stackPos = wasm.pstack.pointer; + try{ return f() }finally{ + wasm.pstack.restore(stackPos); + } } + })/*wasm.pstack*/; Object.defineProperties(wasm.pstack, { /** @@ -1543,6 +1559,26 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( } }; + /** + If v is a string, it is returned as-is. If it is-a Array, its + join("") result is returned. If is is a Uint8Array, Int8Array, + or ArrayBuffer, it is assumed to hold UTF-8-encoded text and is + decoded to a string. If it looks like a WASM pointer, + wasm.cstrToJs(sql) is returned. Else undefined is returned. + + The intent of this function is to convert SQL input text from a + variety of common forms to plain strings. + + Added in 3.44 + */ + capi.sqlite3_js_sql_to_string = (sql)=>{ + if('string' === typeof sql){ + return sql; + } + const x = flexibleString(v); + return x===v ? undefined : x; + } + if( util.isUIThread() ){ /* Features specific to the main window thread... */ diff --git a/manifest b/manifest index 8e208a8ae1..94540f2c28 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\sthe\sbasic\sparsing\spieces\sand\scommand\sdispatching\sin\splace\sin\sthe\sJS\sSQLTester. -D 2023-08-29T13:28:36.476 +C Get\sthe\sJS\sSQLTester\scommand\shandlers\sin\splace\ssans\sthose\swhich\shave\sto\srun\sSQL. +D 2023-08-29T15:39:57.155 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -283,7 +283,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java bf350903abe04a9bed2d8a2a71692ed4291dbb4eece2d3329ed91d15b0321e6d +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java c8a9f20694e66f4d7ed677cd6d1f0d829f802c347a1f413ac2446c62e4cba23d F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -548,8 +548,8 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs 2ea7d09f0c33e509aa3c4ca974be5705f59ddcd2173d4ff2721d7448c65be8bd -F ext/wasm/SQLTester/SQLTester.run.mjs 478f4d90951591decaa7e1e3fa1729f6ed0043ae4cb48b0a92056b9707d44185 +F ext/wasm/SQLTester/SQLTester.mjs 90fc3d2eb831afed237c18b78c22b8871d8f855a742715ebee571a60b9fcd98e +F ext/wasm/SQLTester/SQLTester.run.mjs 30a459ec400495cc52f1d693703f1629e141947a19eaf868a8e4c1fd3ef2a114 F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -563,7 +563,7 @@ F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057af F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e F ext/wasm/api/sqlite3-api-glue.js b65e546568f1dfb35205b9792feb5146a6323d71b55cda58e2ed30def6dd52f3 F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8 -F ext/wasm/api/sqlite3-api-prologue.js ef6f67c5ea718490806e5e17d2644b8b2f6e6ba5284d23dc1fbfd14d401c1ab5 +F ext/wasm/api/sqlite3-api-prologue.js 7fe51f06cd855634cb3765f830393f544fb532ead1cf95b5de3dd0befc81b92d F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379 @@ -2111,8 +2111,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 60eec5ceda80c64870713df8e9aeabeef933c007f2010792225a07d5ef36baef -R 0bdc268c3b78f235b9f50e80fcfe1a69 +P 8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a +R 8ed29d2cdb1f79e88ba5ccad29a3151d U stephan -Z 00dd200744de92467554b7f23df0cf79 +Z 988ed72426998a455381761a89d498de # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 212a3786b4..60048b2641 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a \ No newline at end of file +d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 \ No newline at end of file From a910f3f1c5b8d2e131949719b2985ed3f17e6e1d Mon Sep 17 00:00:00 2001 From: drh <> Date: Tue, 29 Aug 2023 18:28:08 +0000 Subject: [PATCH 115/422] New conversion letters added to strftime(): %e, %k, %I, %l, %R, %P, %p, %T, %u. FossilOrigin-Name: 058722b2d0b995195a8ce3effe9722ae1c18cb1e7a520b481030da0bd579fe41 --- manifest | 17 +++++++++-------- manifest.uuid | 2 +- src/date.c | 51 +++++++++++++++++++++++++++++++++++++++++-------- src/test1.c | 36 ++++++++++++++++++++++++++++++++++ test/date.test | 24 ++++++++++++++++++++--- test/date4.test | 38 ++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 20 deletions(-) create mode 100644 test/date4.test diff --git a/manifest b/manifest index 7623764200..9c82964290 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sversion\snumber\sto\s3.44.0\sto\sbegin\sthe\snext\sdevelopment\ncycle.\s\s(Should\shave\sdone\sthis\sa\sfew\scheck-ins\sago.) -D 2023-08-29T15:38:54.461 +C New\sconversion\sletters\sadded\sto\sstrftime():\s%e,\s%k,\s%I,\s%l,\s%R,\s%P,\s%p,\s%T,\s%u. +D 2023-08-29T18:28:08.008 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -646,7 +646,7 @@ F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4 F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c db847fac81837ff5e5028a5f7505147ac645ae676104adc5bc08e356f243de40 -F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94 +F src/date.c eebc54a00e888d3c56147779e9f361b77d62fd69ff2008c5373946aa1ba1d574 F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387 F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef F src/delete.c cb766727c78e715f9fb7ec8a7d03658ed2a3016343ca687acfcec9083cdca500 @@ -710,7 +710,7 @@ F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd -F src/test1.c ebba2473874a23add4a10881b90bd445cfa7f59f90749434938aec14239c6486 +F src/test1.c 57bd144d022ed1356ae5238110beb251e79b0db5cc1ec44ef5b2f44306adb75f F src/test2.c 54520d0565ef2b9bf0f8f1dcac43dc4d06baf4ffe13d10905f8d8c3ad3e4b9ab F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664 @@ -990,9 +990,10 @@ F test/ctime.test 340f362f41f92972bbd71f44e10569a5cc694062b692231bd08aa6fe6c1c47 F test/cursorhint.test 05cf0febe5c5f8a31f199401fd1c9322249e753950d55f26f9d5aca61408a270 F test/cursorhint2.test 6f3aa9cb19e7418967a10ec6905209bcbb5968054da855fc36c8beee9ae9c42f F test/dataversion1.test 6e5e86ac681f0782e766ebcb56c019ae001522d114e0e111e5ebf68ccf2a7bb8 -F test/date.test 1d44557f668298b10d3335b22ab8feb133267b67ec4d85538908fe4dfebd2611 +F test/date.test c0d17cdfd89395bc78087b131e3538d96f864b5029c335318011accc7c0d0934 F test/date2.test 7e12ec14aaf4d5e6294b4ba140445b0eca06ea50062a9c3a69c4ee13d0b6f8b1 F test/date3.test a1b77abf05c6772fe5ca2337cac1398892f2a41e62bce7e6be0f4a08a0e64ae5 +F test/date4.test db9e5760cf6f480fcf36bb7ca8e215880ff44354a31be6fb3d7e58f9d2e057e9 F test/dbdata.test 042f49acff3438f940eeba5868d3af080ae64ddf26ae78f80c92bec3ca7d8603 F test/dbfuzz.c 73047c920d6210e5912c87cdffd9a1c281d4252e F test/dbfuzz001.test 55e1a3504f8dea84155e09912fe3b1c3ad77e0b1a938ec42ca03b8e51b321e30 @@ -2109,8 +2110,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 0b434ca7aa19eff4ad134a8c6f88f6a7ccab88864faa55e93579a2462d8ac3bc -R 36c15551b362fc3e22c9ee9fe92071f4 +P 253fe4a45decdcc4180c5aeccaf4cd0d1325a0f6df0913b0e6721ea3e26b4fe2 +R ec116b42076aac24bc9f57b015b724bc U drh -Z bcbb66a4b150dee1334cf48c8a72dab0 +Z f129cd2d47b4d4fea322d97826168fb6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b1cf3eb395..0e0a56f3bd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -253fe4a45decdcc4180c5aeccaf4cd0d1325a0f6df0913b0e6721ea3e26b4fe2 \ No newline at end of file +058722b2d0b995195a8ce3effe9722ae1c18cb1e7a520b481030da0bd579fe41 \ No newline at end of file diff --git a/src/date.c b/src/date.c index 648cdeb882..f163085445 100644 --- a/src/date.c +++ b/src/date.c @@ -1270,13 +1270,16 @@ static void strftimeFunc( computeJD(&x); computeYMD_HMS(&x); for(i=j=0; zFmt[i]; i++){ + char cf; if( zFmt[i]!='%' ) continue; if( j12 ) h -= 12; + if( h==0 ) h = 12; + sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h); break; } case 'W': /* Fall thru */ @@ -1298,7 +1314,7 @@ static void strftimeFunc( y.D = 1; computeJD(&y); nDay = (int)((x.iJD-y.iJD+43200000)/86400000); - if( zFmt[i]=='W' ){ + if( cf=='W' ){ int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */ wd = (int)(((x.iJD+43200000)/86400000)%7); sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7); @@ -1319,6 +1335,19 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d",x.m); break; } + case 'p': /* Fall thru */ + case 'P': { + if( x.h>=12 ){ + sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2); + }else{ + sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2); + } + break; + } + case 'R': { + sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m); + break; + } case 's': { if( x.useSubsec ){ sqlite3_str_appendf(&sRes,"%.3f", @@ -1333,9 +1362,15 @@ static void strftimeFunc( sqlite3_str_appendf(&sRes,"%02d",(int)x.s); break; } + case 'T': { + sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s); + break; + } + case 'u': /* Fall thru */ case 'w': { - sqlite3_str_appendchar(&sRes, 1, - (char)(((x.iJD+129600000)/86400000) % 7) + '0'); + char c = (char)(((x.iJD+129600000)/86400000) % 7) + '0'; + if( c=='0' && cf=='u' ) c = '7'; + sqlite3_str_appendchar(&sRes, 1, c); break; } case 'Y': { diff --git a/src/test1.c b/src/test1.c index 4dcf7bc11f..145882f087 100644 --- a/src/test1.c +++ b/src/test1.c @@ -7579,6 +7579,41 @@ static int testLocaltime(const void *aliasT, void *aliasTM){ return t==959609760; /* Special case: 2000-05-29 14:16:00 fails */ } +/* +** TCLCMD: strftime FORMAT UNIXTIMESTAMP +** +** Access to the C-library strftime() routine, so that its results +** can be compared against SQLite's internal strftime() SQL function +** implementation. +*/ +static int SQLITE_TCLAPI strftime_cmd( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + Tcl_WideInt ts; + time_t t; + struct tm *pTm; + const char *zFmt; + size_t n; + char zBuf[1000]; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "FORMAT UNIXTIMESTAMP"); + return TCL_ERROR; + } + if( Tcl_GetWideIntFromObj(interp, objv[2], &ts) ) return TCL_ERROR; + zFmt = Tcl_GetString(objv[1]); + t = (time_t)ts; + pTm = gmtime(&t); + n = strftime(zBuf, sizeof(zBuf)-1, zFmt, pTm); + if( n>=0 && n Date: Tue, 29 Aug 2023 20:01:01 +0000 Subject: [PATCH 116/422] JS SQLTestRunner can now run the Java impl's core-most sanity tests, missing only support for directives. FossilOrigin-Name: 5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 --- .../src/org/sqlite/jni/tester/SQLTester.java | 35 +- ext/wasm/SQLTester/SQLTester.mjs | 352 ++++++++++++++++-- ext/wasm/SQLTester/SQLTester.run.mjs | 53 ++- ext/wasm/api/sqlite3-api-prologue.js | 8 +- ext/wasm/api/sqlite3-wasm.c | 112 ++++++ manifest | 20 +- manifest.uuid | 2 +- 7 files changed, 503 insertions(+), 79 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index f7550c8e46..b49c61957f 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -181,7 +181,7 @@ public class SQLTester { private int nTestFile = 0; //! Number of scripts which were aborted. private int nAbortedScript = 0; - //! Per-script test counter. + //! Incremented by test case handlers private int nTest = 0; //! True to enable column name output from execSql() private boolean emitColNames; @@ -582,6 +582,10 @@ public class SQLTester { } } }finally{ + sqlite3_reset(stmt + /* In order to trigger an exception in the + INSERT...RETURNING locking scenario: + https://sqlite.org/forum/forumpost/36f7a2e7494897df */); sqlite3_finalize(stmt); } if( 0!=rc && throwOnError ){ @@ -926,8 +930,8 @@ class RunCommand extends Command { final sqlite3 db = (1==argv.length) ? t.getCurrentDb() : t.getDbById( Integer.parseInt(argv[1]) ); final String sql = t.takeInputBuffer(); - int rc = t.execSql(db, false, ResultBufferMode.NONE, - ResultRowMode.ONELINE, sql); + final int rc = t.execSql(db, false, ResultBufferMode.NONE, + ResultRowMode.ONELINE, sql); if( 0!=rc && t.isVerbose() ){ String msg = sqlite3_errmsg(db); ts.verbose1(argv[0]," non-fatal command error #",rc,": ", @@ -950,8 +954,7 @@ class TableResultCommand extends Command { if( !body.endsWith("\n--end") ){ ts.toss(argv[0], " must be terminated with --end."); }else{ - int n = body.length(); - body = body.substring(0, n-6); + body = body.substring(0, body.length()-6); } final String[] globs = body.split("\\s*\\n\\s*"); if( globs.length < 1 ){ @@ -1240,14 +1243,15 @@ class TestScript { final int oldPB = cur.putbackPos; final int oldPBL = cur.putbackLineNo; final int oldLine = cur.lineNo; - final String rc = getLine(); - cur.peekedPos = cur.pos; - cur.peekedLineNo = cur.lineNo; - cur.pos = oldPos; - cur.lineNo = oldLine; - cur.putbackPos = oldPB; - cur.putbackLineNo = oldPBL; - return rc; + try{ return getLine(); } + finally{ + cur.peekedPos = cur.pos; + cur.peekedLineNo = cur.lineNo; + cur.pos = oldPos; + cur.lineNo = oldLine; + cur.putbackPos = oldPB; + cur.putbackLineNo = oldPBL; + } } /** @@ -1374,11 +1378,10 @@ class TestScript { String line; while( (null != (line = peekLine())) ){ checkForDirective(tester, line); - if( !isCommandLine(line, true) ){ + if( isCommandLine(line, true) ) break; + else { sb.append(line).append("\n"); consumePeeked(); - }else{ - break; } } line = sb.toString(); diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 0461beb86f..c7059ad1b7 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -73,7 +73,7 @@ class DbException extends SQLTesterException { class TestScriptFailed extends SQLTesterException { constructor(testScript, ...args){ - super(testScript.getPutputPrefix(),': ',...args); + super(testScript.getOutputPrefix(),': ',...args); } isFatal() { return true; } } @@ -103,6 +103,18 @@ const __utf8Encoder = new TextEncoder('utf-8'); const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer) ? function(){} : globalThis.SharedArrayBuffer; + +const Rx = newObj({ + requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/, + scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, + mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, + command: /^--(([a-z-]+)( .*)?)$/, + //! "Special" characters - we have to escape output if it contains any. + special: /[\x00-\x20\x22\x5c\x7b\x7d]/, + //! Either of '{' or '}'. + squiggly: /[{}]/ +}); + const Util = newObj({ toss, @@ -110,7 +122,11 @@ const Util = newObj({ return 0==sqlite3.wasm.sqlite3_wasm_vfs_unlink(0,fn); }, - argvToString: (list)=>list.join(" "), + argvToString: (list)=>{ + const m = [...list]; + m.shift(); + return m.join(" ") + }, utf8Decode: function(arrayBuffer, begin, end){ return __utf8Decoder.decode( @@ -120,7 +136,10 @@ const Util = newObj({ ); }, - utf8Encode: (str)=>__utf8Encoder.encode(str) + utf8Encode: (str)=>__utf8Encoder.encode(str), + + strglob: sqlite3.wasm.xWrap('sqlite3_wasm_SQLTester_strglob','int', + ['string','string']) })/*Util*/; class Outer { @@ -182,21 +201,39 @@ class Outer { class SQLTester { + //! Console output utility. #outer = new Outer().outputPrefix( ()=>'SQLTester: ' ); + //! List of input script files. #aFiles = []; + //! Test input buffer. #inputBuffer = []; + //! Test result buffer. #resultBuffer = []; + //! Output representation of SQL NULL. #nullView = "nil"; - #metrics = newObj({ - nTotalTest: 0, nTestFile: 0, nAbortedScript: 0 + metrics = newObj({ + //! Total tests run + nTotalTest: 0, + //! Total test script files run + nTestFile: 0, + //! Number of scripts which were aborted + nAbortedScript: 0, + //! Incremented by test case handlers + nTest: 0 }); #emitColNames = false; + //! True to keep going regardless of how a test fails. #keepGoing = false; #db = newObj({ + //! The list of available db handles. list: new Array(7), + //! Index into this.list of the current db. iCurrentDb: 0, + //! Name of the default db, re-created for each script. initialDbName: "test.db", + //! Buffer for REQUIRED_PROPERTIES pragmas. initSql: ['select 1;'], + //! (sqlite3*) to the current db. currentDb: function(){ return this.list[this.iCurrentDb]; } @@ -208,12 +245,17 @@ class SQLTester { outln(...args){ return this.#outer.outln(...args); } out(...args){ return this.#outer.out(...args); } + incrementTestCounter(){ + ++this.metrics.nTotalTest; + ++this.metrics.nTest; + } + reset(){ this.clearInputBuffer(); this.clearResultBuffer(); this.#clearBuffer(this.#db.initSql); this.closeAllDbs(); - this.nTest = 0; + this.metrics.nTest = 0; this.nullView = "nil"; this.emitColNames = false; this.#db.iCurrentDb = 0; @@ -365,7 +407,7 @@ class SQLTester { Util.unlink(this.#db.initialDbName); this.openDb(0, this.#db.initialDbName, true); }else{ - this.#outer.outln("WARNING: setupInitialDb() unexpectedly ", + this.#outer.outln("WARNING: setupInitialDb() was unexpectedly ", "triggered while it is opened."); } } @@ -405,17 +447,107 @@ class SQLTester { #appendDbErr(pDb, sb, rc){ sb.push(sqlite3.capi.sqlite3_js_rc_str(rc), ' '); const msg = this.#escapeSqlValue(sqlite3.capi.sqlite3_errmsg(pDb)); - if( '{' == msg.charAt(0) ){ + if( '{' === msg.charAt(0) ){ sb.push(msg); }else{ sb.push('{', msg, '}'); } } + #checkDbRc(pDb,rc){ + sqlite3.oo1.DB.checkRc(pDb, rc); + } + execSql(pDb, throwOnError, appendMode, lineMode, sql){ - sql = sqlite3.capi.sqlite3_js_sql_to_string(sql); - this.#outer.outln("execSql() is TODO. ",sql); - return 0; + if( !pDb && !this.#db.list[0] ){ + this.#setupInitialDb(); + } + if( !pDb ) pDb = this.#db.currentDb(); + const wasm = sqlite3.wasm, capi = sqlite3.capi; + sql = (sql instanceof Uint8Array) + ? sql + : new TextEncoder("utf-8").encode(capi.sqlite3_js_sql_to_string(sql)); + const self = this; + const sb = (ResultBufferMode.NONE===appendMode) ? null : this.#resultBuffer; + let rc = 0; + wasm.scopedAllocCall(function(){ + let sqlByteLen = sql.byteLength; + const ppStmt = wasm.scopedAlloc( + /* output (sqlite3_stmt**) arg and pzTail */ + (2 * wasm.ptrSizeof) + (sqlByteLen + 1/* SQL + NUL */) + ); + const pzTail = ppStmt + wasm.ptrSizeof /* final arg to sqlite3_prepare_v2() */; + let pSql = pzTail + wasm.ptrSizeof; + const pSqlEnd = pSql + sqlByteLen; + wasm.heap8().set(sql, pSql); + wasm.poke8(pSql + sqlByteLen, 0/*NUL terminator*/); + let pos = 0, n = 1, spacing = 0; + while( pSql && wasm.peek8(pSql) ){ + wasm.pokePtr([ppStmt, pzTail], 0); + rc = capi.sqlite3_prepare_v3( + pDb, pSql, sqlByteLen, 0, ppStmt, pzTail + ); + if( 0!==rc ){ + if(throwOnError){ + throw new DbException(pDb, rc); + }else if( sb ){ + self.#appendDbErr(db, sb, rc); + } + break; + } + const pStmt = wasm.peekPtr(ppStmt); + pSql = wasm.peekPtr(pzTail); + sqlByteLen = pSqlEnd - pSql; + if(!pStmt) continue /* only whitespace or comments */; + if( sb ){ + const nCol = capi.sqlite3_column_count(pStmt); + let colName, val; + while( capi.SQLITE_ROW === (rc = capi.sqlite3_step(pStmt)) ) { + for( let i=0; i < nCol; ++i ){ + if( spacing++ > 0 ) sb.push(' '); + if( self.#emitColNames ){ + colName = capi.sqlite3_column_name(pStmt, i); + switch(appendMode){ + case ResultBufferMode.ASIS: sb.push( colName ); break; + case ResultBufferMode.ESCAPED: + sb.push( self.#escapeSqlValue(colName) ); + break; + default: + self.toss("Unhandled ResultBufferMode."); + } + sb.push(' '); + } + val = capi.sqlite3_column_text(pStmt, i); + if( null===val ){ + sb.push( self.#nullView ); + continue; + } + switch(appendMode){ + case ResultBufferMode.ASIS: sb.push( val ); break; + case ResultBufferMode.ESCAPED: + sb.push( self.#escapeSqlValue(val) ); + break; + } + }/* column loop */ + }/* row loop */ + if( ResultRowMode.NEWLINE === lineMode ){ + spacing = 0; + sb.push('\n'); + } + }else{ // no output but possibly other side effects + while( capi.SQLITE_ROW === (rc = capi.sqlite3_step(pStmt)) ) {} + } + capi.sqlite3_finalize(pStmt); + if( capi.SQLITE_ROW===rc || capi.SQLITE_DONE===rc) rc = 0; + else if( rc!=0 ){ + if( sb ){ + self.#appendDbErr(db, sb, rc); + } + break; + } + }/* SQL script loop */; + })/*scopedAllocCall()*/; + return rc; } }/*SQLTester*/ @@ -469,17 +601,6 @@ class Cursor { } } -const Rx = newObj({ - requiredProperties: / REQUIRED_PROPERTIES:[ \t]*(\S.*)\s*$/, - scriptModuleName: / SCRIPT_MODULE_NAME:[ \t]*(\S+)\s*$/, - mixedModuleName: / ((MIXED_)?MODULE_NAME):[ \t]*(\S+)\s*$/, - command: /^--(([a-z-]+)( .*)?)$/, - //! "Special" characters - we have to escape output if it contains any. - special: /[\x00-\x20\x22\x5c\x7b\x7d]/, - //! Either of '{' or '}'. - squiggly: /[{}]/ -}); - class TestScript { #cursor = new Cursor(); #moduleName = null; @@ -529,6 +650,28 @@ class TestScript { return m ? m[1].trim().split(/\s+/) : null; } + + #isCommandLine(line, checkForImpl){ + let m = Rx.command.exec(line); + if( m && checkForImpl ){ + m = !!CommandDispatcher.getCommandByName(m[2]); + } + return !!m; + } + + fetchCommandBody(tester){ + const sb = []; + let line; + while( (null !== (line = this.peekLine())) ){ + this.#checkForDirective(tester, line); + if( this.#isCommandLine(line, true) ) break; + sb.push(line,"\n"); + this.consumePeeked(); + } + line = sb.join(''); + return !!line.trim() ? line : null; + } + run(tester){ this.reset(); this.#outer.verbosity(tester.verbosity()); @@ -621,14 +764,16 @@ class TestScript { const oldPB = cur.putbackPos; const oldPBL = cur.putbackLineNo; const oldLine = cur.lineNo; - const rc = this.getLine(); - cur.peekedPos = cur.pos; - cur.peekedLineNo = cur.lineNo; - cur.pos = oldPos; - cur.lineNo = oldLine; - cur.putbackPos = oldPB; - cur.putbackLineNo = oldPBL; - return rc; + try { + return this.getLine(); + }finally{ + cur.peekedPos = cur.pos; + cur.peekedLineNo = cur.lineNo; + cur.pos = oldPos; + cur.lineNo = oldLine; + cur.putbackPos = oldPB; + cur.putbackLineNo = oldPBL; + } } @@ -667,7 +812,7 @@ class CloseDbCommand extends Command { let id; if(argv.length>1){ const arg = argv[1]; - if("all".equals(arg)){ + if( "all" === arg ){ t.closeAllDbs(); return; } @@ -697,6 +842,36 @@ class DbCommand extends Command { } } +//! --glob command +class GlobCommand extends Command { + #negate = false; + constructor(negate=false){ + super(); + this.#negate = negate; + } + + process(t, ts, argv){ + this.argcCheck(ts,argv,1,-1); + t.incrementTestCounter(); + const sql = t.takeInputBuffer(); + let rc = t.execSql(null, true, ResultBufferMode.ESCAPED, + ResultRowMode.ONELINE, sql); + const result = t.getResultText(); + const sArgs = Util.argvToString(argv); + //t2.verbose2(argv[0]," rc = ",rc," result buffer:\n", result,"\nargs:\n",sArgs); + const glob = Util.argvToString(argv); + rc = Util.strglob(glob, result); + if( (this.#negate && 0===rc) || (!this.#negate && 0!==rc) ){ + ts.toss(argv[0], " mismatch: ", glob," vs input: ",result); + } + } +} + +//! --notglob command +class NotGlobCommand extends GlobCommand { + constructor(){super(true);} +} + //! --open command class OpenDbCommand extends Command { #createIfNeeded = false; @@ -740,6 +915,107 @@ class PrintCommand extends Command { } } +//! --result command +class ResultCommand extends Command { + #bufferMode; + constructor(resultBufferMode = ResultBufferMode.ESCAPED){ + super(); + this.#bufferMode = resultBufferMode; + } + process(t, ts, argv){ + this.argcCheck(ts,argv,0,-1); + t.incrementTestCounter(); + const sql = t.takeInputBuffer(); + //ts.verbose2(argv[0]," SQL =\n",sql); + t.execSql(null, false, this.#bufferMode, ResultRowMode.ONELINE, sql); + const result = t.getResultText().trim(); + const sArgs = argv.length>1 ? Util.argvToString(argv) : ""; + if( result !== sArgs ){ + t.outln(argv[0]," FAILED comparison. Result buffer:\n", + result,"\nExpected result:\n",sArgs); + ts.toss(argv[0]+" comparison failed."); + } + } +} + +//! --json command +class JsonCommand extends ResultCommand { + constructor(){ super(ResultBufferMode.ASIS); } +} + +//! --run command +class RunCommand extends Command { + process(t, ts, argv){ + this.argcCheck(ts,argv,0,1); + const pDb = (1==argv.length) + ? t.currentDb() : t.getDbById( parseInt(argv[1]) ); + const sql = t.takeInputBuffer(); + const rc = t.execSql(pDb, false, ResultBufferMode.NONE, + ResultRowMode.ONELINE, sql); + if( 0!==rc && t.verbosity()>0 ){ + const msg = sqlite3.capi.sqlite3_errmsg(pDb); + ts.verbose1(argv[0]," non-fatal command error #",rc,": ", + msg,"\nfor SQL:\n",sql); + } + } +} + +//! --tableresult command +class TableResultCommand extends Command { + #jsonMode; + constructor(jsonMode=false){ + super(); + this.#jsonMode = jsonMode; + } + process(t, ts, argv){ + this.argcCheck(ts,argv,0); + t.incrementTestCounter(); + let body = ts.fetchCommandBody(t); + log("TRC fetchCommandBody: ",body); + if( null===body ) ts.toss("Missing ",argv[0]," body."); + body = body.trim(); + if( !body.endsWith("\n--end") ){ + ts.toss(argv[0], " must be terminated with --end\\n"); + }else{ + body = body.substring(0, body.length-6); + log("TRC fetchCommandBody reshaped:",body); + } + const globs = body.split(/\s*\n\s*/); + if( globs.length < 1 ){ + ts.toss(argv[0], " requires 1 or more ", + (this.#jsonMode ? "json snippets" : "globs"),"."); + } + log("TRC fetchCommandBody globs:",globs); + const sql = t.takeInputBuffer(); + t.execSql(null, true, + this.#jsonMode ? ResultBufferMode.ASIS : ResultBufferMode.ESCAPED, + ResultRowMode.NEWLINE, sql); + const rbuf = t.getResultText().trim(); + const res = rbuf.split(/\r?\n/); + log("TRC fetchCommandBody rbuf, res:",rbuf, res); + if( res.length !== globs.length ){ + ts.toss(argv[0], " failure: input has ", res.length, + " row(s) but expecting ",globs.length); + } + for(let i = 0; i < res.length; ++i){ + const glob = globs[i].replaceAll(/\s+/g," ").trim(); + //ts.verbose2(argv[0]," <<",glob,">> vs <<",res[i],">>"); + if( this.#jsonMode ){ + if( glob!==res[i] ){ + ts.toss(argv[0], " json <<",glob, ">> does not match: <<", + res[i],">>"); + } + }else if( 0!=Util.strglob(glob, res[i]) ){ + ts.toss(argv[0], " glob <<",glob,">> does not match: <<",res[i],">>"); + } + } + } +} + +//! --json-block command +class JsonBlockCommand extends TableResultCommand { + constructor(){ super(true); } +} //! --testcase command class TestCaseCommand extends Command { @@ -770,18 +1046,18 @@ class CommandDispatcher { case "close": rv = new CloseDbCommand(); break; case "column-names": rv = new ColumnNamesCommand(); break; case "db": rv = new DbCommand(); break; - //case "glob": rv = new GlobCommand(); break; - //case "json": rv = new JsonCommand(); break; - //case "json-block": rv = new JsonBlockCommand(); break; + case "glob": rv = new GlobCommand(); break; + case "json": rv = new JsonCommand(); break; + case "json-block": rv = new JsonBlockCommand(); break; case "new": rv = new NewDbCommand(); break; - //case "notglob": rv = new NotGlobCommand(); break; + case "notglob": rv = new NotGlobCommand(); break; case "null": rv = new NullCommand(); break; case "oom": rv = new NoopCommand(); break; case "open": rv = new OpenDbCommand(); break; case "print": rv = new PrintCommand(); break; - //case "result": rv = new ResultCommand(); break; - //case "run": rv = new RunCommand(); break; - //case "tableresult": rv = new TableResultCommand(); break; + case "result": rv = new ResultCommand(); break; + case "run": rv = new RunCommand(); break; + case "tableresult": rv = new TableResultCommand(); break; case "testcase": rv = new TestCaseCommand(); break; case "verbosity": rv = new VerbosityCommand(); break; } diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index dc8eaa0c17..36d1ab5dcd 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -25,14 +25,46 @@ log("ns =",ns); out("Hi there. ").outln("SQLTester is ostensibly ready."); let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode( -`# comment line ---print Starting up... ---null NIL ---new :memory: ---testcase 0.0.1 -select '0.0.1'; -#--result 0.0.1 ---print done +` +--close all +--oom +--db 0 +--new my.db +--null zilch +--testcase 1.0 +SELECT 1, null; +--result 1 zilch +--glob *zil* +--notglob *ZIL* +SELECT 1, 2; +intentional error; +--run +--testcase json-1 +SELECT json_array(1,2,3) +--json [1,2,3] +--testcase tableresult-1 + select 1, 'a'; + select 2, 'b'; +--tableresult + # [a-z] + 2 b +--end +--testcase json-block-1 + select json_array(1,2,3); + select json_object('a',1,'b',2); +--json-block + [1,2,3] + {"a":1,"b":2} +--end +--testcase col-names-on +--column-names 1 + select 1 as 'a', 2 as 'b'; +--result a 1 b 2 +--testcase col-names-off +--column-names 0 + select 1 as 'a', 2 as 'b'; +--result 1 2 +--close `)); const sqt = new ns.SQLTester(); @@ -41,11 +73,12 @@ try{ sqt.openDb('/foo.db', true); log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); sqt.verbosity(0); - affirm( 'NIL' !== sqt.nullValue() ); + affirm( 'zilch' !== sqt.nullValue() ); ts.run(sqt); - affirm( 'NIL' === sqt.nullValue() ); + affirm( 'zilch' === sqt.nullValue() ); }finally{ sqt.reset(); } log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); +log( "Metrics:", sqt.metrics ); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 35c856269d..996f23298f 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -1139,15 +1139,15 @@ globalThis.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( /** Records the current pstack position, calls the given function, - and restores the pstack regardless of whether the function - throws. Returns the result of the call or propagates an - exception on error. + passing it the sqlite3 object, then restores the pstack + regardless of whether the function throws. Returns the result + of the call or propagates an exception on error. Added in 3.44. */ call: function(f){ const stackPos = wasm.pstack.pointer; - try{ return f() }finally{ + try{ return f(sqlite3) } finally{ wasm.pstack.restore(stackPos); } } diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index ff15e3b4fb..db77010d95 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -1801,6 +1801,118 @@ char * sqlite3_wasm_test_str_hello(int fail){ } return s; } + +/* +** For testing using SQLTester scripts. +** +** Return non-zero if string z matches glob pattern zGlob and zero if the +** pattern does not match. +** +** To repeat: +** +** zero == no match +** non-zero == match +** +** Globbing rules: +** +** '*' Matches any sequence of zero or more characters. +** +** '?' Matches exactly one character. +** +** [...] Matches one character from the enclosed list of +** characters. +** +** [^...] Matches one character not in the enclosed list. +** +** '#' Matches any sequence of one or more digits with an +** optional + or - sign in front, or a hexadecimal +** literal of the form 0x... +*/ +static int sqlite3_wasm_SQLTester_strnotglob(const char *zGlob, const char *z){ + int c, c2; + int invert; + int seen; + typedef int (*recurse_f)(const char *,const char *); + static const recurse_f recurse = sqlite3_wasm_SQLTester_strnotglob; + + while( (c = (*(zGlob++)))!=0 ){ + if( c=='*' ){ + while( (c=(*(zGlob++))) == '*' || c=='?' ){ + if( c=='?' && (*(z++))==0 ) return 0; + } + if( c==0 ){ + return 1; + }else if( c=='[' ){ + while( *z && recurse(zGlob-1,z)==0 ){ + z++; + } + return (*z)!=0; + } + while( (c2 = (*(z++)))!=0 ){ + while( c2!=c ){ + c2 = *(z++); + if( c2==0 ) return 0; + } + if( recurse(zGlob,z) ) return 1; + } + return 0; + }else if( c=='?' ){ + if( (*(z++))==0 ) return 0; + }else if( c=='[' ){ + int prior_c = 0; + seen = 0; + invert = 0; + c = *(z++); + if( c==0 ) return 0; + c2 = *(zGlob++); + if( c2=='^' ){ + invert = 1; + c2 = *(zGlob++); + } + if( c2==']' ){ + if( c==']' ) seen = 1; + c2 = *(zGlob++); + } + while( c2 && c2!=']' ){ + if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ + c2 = *(zGlob++); + if( c>=prior_c && c<=c2 ) seen = 1; + prior_c = 0; + }else{ + if( c==c2 ){ + seen = 1; + } + prior_c = c2; + } + c2 = *(zGlob++); + } + if( c2==0 || (seen ^ invert)==0 ) return 0; + }else if( c=='#' ){ + if( z[0]=='0' + && (z[1]=='x' || z[1]=='X') + && sqlite3Isxdigit(z[2]) + ){ + z += 3; + while( sqlite3Isxdigit(z[0]) ){ z++; } + }else{ + if( (z[0]=='-' || z[0]=='+') && sqlite3Isdigit(z[1]) ) z++; + if( !sqlite3Isdigit(z[0]) ) return 0; + z++; + while( sqlite3Isdigit(z[0]) ){ z++; } + } + }else{ + if( c!=(*(z++)) ) return 0; + } + } + return *z==0; +} + +SQLITE_WASM_EXPORT +int sqlite3_wasm_SQLTester_strglob(const char *zGlob, const char *z){ + return !sqlite3_wasm_SQLTester_strnotglob(zGlob, z); +} + + #endif /* SQLITE_WASM_TESTS */ #undef SQLITE_WASM_EXPORT diff --git a/manifest b/manifest index 94540f2c28..40829f5f94 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\sthe\sJS\sSQLTester\scommand\shandlers\sin\splace\ssans\sthose\swhich\shave\sto\srun\sSQL. -D 2023-08-29T15:39:57.155 +C JS\sSQLTestRunner\scan\snow\srun\sthe\sJava\simpl's\score-most\ssanity\stests,\smissing\sonly\ssupport\sfor\sdirectives. +D 2023-08-29T20:01:01.586 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -283,7 +283,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java c8a9f20694e66f4d7ed677cd6d1f0d829f802c347a1f413ac2446c62e4cba23d +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java a9f4b9e12109645b21fef15807973706dd958aad9fe1c835693fcb8e95abe949 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -548,8 +548,8 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs 90fc3d2eb831afed237c18b78c22b8871d8f855a742715ebee571a60b9fcd98e -F ext/wasm/SQLTester/SQLTester.run.mjs 30a459ec400495cc52f1d693703f1629e141947a19eaf868a8e4c1fd3ef2a114 +F ext/wasm/SQLTester/SQLTester.mjs 345736d970dc56e2c1041f8583fc602eedd8a64d455864f312db7d3208e640ea +F ext/wasm/SQLTester/SQLTester.run.mjs 2dfa1407f5f188dadafe6f21f7a6740b4f07d59c594781a01eedadec16b2ddfe F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -563,14 +563,14 @@ F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057af F ext/wasm/api/sqlite3-api-cleanup.js d235ad237df6954145404305040991c72ef8b1881715d2a650dda7b3c2576d0e F ext/wasm/api/sqlite3-api-glue.js b65e546568f1dfb35205b9792feb5146a6323d71b55cda58e2ed30def6dd52f3 F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8 -F ext/wasm/api/sqlite3-api-prologue.js 7fe51f06cd855634cb3765f830393f544fb532ead1cf95b5de3dd0befc81b92d +F ext/wasm/api/sqlite3-api-prologue.js 723908946bd624d367e4df7093e9a6c9725606dc526953ea601cad8d7ce88538 F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89 F ext/wasm/api/sqlite3-opfs-async-proxy.js 8cf8a897726f14071fae6be6648125162b256dfb4f96555b865dbb7a6b65e379 F ext/wasm/api/sqlite3-v-helper.js 7daa0eab0a513a25b05e9abae7b5beaaa39209b3ed12f86aeae9ef8d2719ed25 F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js 561463ac5380e4ccf1839a1922e6d7a5585660f32e3b9701a270b78cd35566cf F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js d9e62d42b86f7bb3143eb071628b24e2ba7dcc749e41a0e9d3e2451bfea1a6b6 -F ext/wasm/api/sqlite3-wasm.c 6773e949034369ddd2a1efdedc39b2808a10b7274b0769188905432e561feebe +F ext/wasm/api/sqlite3-wasm.c 65d60439671e24d50d9119ca805ac1c68fb36129e164377eb46f8d037bd88b07 F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75 F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 @@ -2111,8 +2111,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 8fcc2a553c1e26734902bbdee0c38183ee22b7b5c75f07405529bb79db34145a -R 8ed29d2cdb1f79e88ba5ccad29a3151d +P d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 +R 7266768b4057594984eb0965145c2068 U stephan -Z 988ed72426998a455381761a89d498de +Z 703bcb5450951150eb9347fe40faa521 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 60048b2641..47297d57d2 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 \ No newline at end of file +5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 \ No newline at end of file From 267c44771fec6758c371eb41d0ab99a9ea0c8452 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 29 Aug 2023 20:44:40 +0000 Subject: [PATCH 117/422] More fleshing out of JS SQLTester. FossilOrigin-Name: 8c503dfb9fa15389613a819fcc1792e23d3c05f99a9f450f82eac5125298726f --- .../src/org/sqlite/jni/tester/SQLTester.java | 1 - ext/wasm/SQLTester/SQLTester.mjs | 69 ++++++++++++++++--- ext/wasm/SQLTester/SQLTester.run.mjs | 23 ++++--- manifest | 16 ++--- manifest.uuid | 2 +- 5 files changed, 84 insertions(+), 27 deletions(-) diff --git a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java index b49c61957f..47d6b6f3ef 100644 --- a/ext/jni/src/org/sqlite/jni/tester/SQLTester.java +++ b/ext/jni/src/org/sqlite/jni/tester/SQLTester.java @@ -270,7 +270,6 @@ public class SQLTester { final long timeEnd = System.currentTimeMillis(); outln("🏁",(threw ? "❌" : "✅")," ",nTest," test(s) in ", (timeEnd-timeStart),"ms."); - //ts.getFilename()); } } final long tEnd = System.currentTimeMillis(); diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index c7059ad1b7..c8ab2fa5cc 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -203,8 +203,8 @@ class SQLTester { //! Console output utility. #outer = new Outer().outputPrefix( ()=>'SQLTester: ' ); - //! List of input script files. - #aFiles = []; + //! List of input scripts. + #aScripts = []; //! Test input buffer. #inputBuffer = []; //! Test result buffer. @@ -259,7 +259,7 @@ class SQLTester { this.nullView = "nil"; this.emitColNames = false; this.#db.iCurrentDb = 0; - this.#db.initSql.push("SELECT 1;"); + //this.#db.initSql.push("SELECT 1;"); } appendInput(line, addNL){ @@ -402,6 +402,54 @@ class SQLTester { } } + addTestScript(ts){ + if( 2===arguments.length ){ + ts = new TestScript(arguments[0], arguments[1]); + }else if(ts instanceof Uint8Array){ + ts = new TestScript('', ts); + }else if('string' === typeof arguments[1]){ + ts = new TestScript('', Util.utf8Encode(arguments[1])); + } + if( !(ts instanceof TestScript) ){ + Util.toss(SQLTesterException, "Invalid argument type for addTestScript()"); + } + this.#aScripts.push(ts); + return this; + } + + runTests(){ + const tStart = (new Date()).getTime(); + for(const ts of this.#aScripts){ + this.reset(); + ++this.metrics.nTestFile; + let threw = false; + const timeStart = (new Date()).getTime(); + try{ + ts.run(this); + }catch(e){ + if(e instanceof SQLTesterException){ + threw = true; + this.outln("🔥EXCEPTION: ",''+e); + ++this.metrics.nAbortedScript; + if( this.#keepGoing ){ + this.outln("Continuing anyway becaure of the keep-going option."); + } + else if( e.isFatal() ) throw e; + }else{ + throw e; + } + }finally{ + const timeEnd = (new Date()).getTime(); + this.outln("🏁", (threw ? "❌" : "✅"), " ", this.metrics.nTest, + " test(s) in ", (timeEnd-timeStart),"ms."); + } + } + const tEnd = (new Date()).getTime(); + this.outln("Total run-time: ",(tEnd-tStart),"ms"); + Util.unlink(this.#db.initialDbName); + return this; + } + #setupInitialDb(){ if( !this.#db.list[0] ){ Util.unlink(this.#db.initialDbName); @@ -466,7 +514,7 @@ class SQLTester { const wasm = sqlite3.wasm, capi = sqlite3.capi; sql = (sql instanceof Uint8Array) ? sql - : new TextEncoder("utf-8").encode(capi.sqlite3_js_sql_to_string(sql)); + : Util.utf8Encode(capi.sqlite3_js_sql_to_string(sql)); const self = this; const sb = (ResultBufferMode.NONE===appendMode) ? null : this.#resultBuffer; let rc = 0; @@ -616,6 +664,15 @@ class TestScript { }else{ content = args[0]; } + if(!(content instanceof Uint8Array)){ + if('string' === typeof content){ + content = Util.utf8Encode(content); + }else if(content instanceof ArrayBuffer){ + content = new Uint8Array(content); + }else{ + toss(Error, "Invalid content type for TestScript constructor."); + } + } this.#filename = filename; this.#cursor.src = content; } @@ -971,28 +1028,24 @@ class TableResultCommand extends Command { this.argcCheck(ts,argv,0); t.incrementTestCounter(); let body = ts.fetchCommandBody(t); - log("TRC fetchCommandBody: ",body); if( null===body ) ts.toss("Missing ",argv[0]," body."); body = body.trim(); if( !body.endsWith("\n--end") ){ ts.toss(argv[0], " must be terminated with --end\\n"); }else{ body = body.substring(0, body.length-6); - log("TRC fetchCommandBody reshaped:",body); } const globs = body.split(/\s*\n\s*/); if( globs.length < 1 ){ ts.toss(argv[0], " requires 1 or more ", (this.#jsonMode ? "json snippets" : "globs"),"."); } - log("TRC fetchCommandBody globs:",globs); const sql = t.takeInputBuffer(); t.execSql(null, true, this.#jsonMode ? ResultBufferMode.ASIS : ResultBufferMode.ESCAPED, ResultRowMode.NEWLINE, sql); const rbuf = t.getResultText().trim(); const res = rbuf.split(/\r?\n/); - log("TRC fetchCommandBody rbuf, res:",rbuf, res); if( res.length !== globs.length ){ ts.toss(argv[0], " failure: input has ", res.length, " row(s) but expecting ",globs.length); diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index 36d1ab5dcd..5136d58a28 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -22,10 +22,10 @@ const affirm = function(expr, msg){ console.log("Loaded",ns); log("ns =",ns); -out("Hi there. ").outln("SQLTester is ostensibly ready."); +outln("SQLTester is ready."); -let ts = new ns.TestScript('/foo.test', ns.Util.utf8Encode( -` +let ts = new ns.TestScript('/foo.test',` +--print Hello, world. --close all --oom --db 0 @@ -65,17 +65,22 @@ SELECT json_array(1,2,3) select 1 as 'a', 2 as 'b'; --result 1 2 --close -`)); +--print Until next time +`); const sqt = new ns.SQLTester(); try{ - log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); + affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); sqt.openDb('/foo.db', true); - log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); + affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); sqt.verbosity(0); - affirm( 'zilch' !== sqt.nullValue() ); - ts.run(sqt); - affirm( 'zilch' === sqt.nullValue() ); + if(false){ + affirm( 'zilch' !== sqt.nullValue() ); + ts.run(sqt); + affirm( 'zilch' === sqt.nullValue() ); + } + sqt.addTestScript(ts); + sqt.runTests(); }finally{ sqt.reset(); } diff --git a/manifest b/manifest index 40829f5f94..b7930866b8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C JS\sSQLTestRunner\scan\snow\srun\sthe\sJava\simpl's\score-most\ssanity\stests,\smissing\sonly\ssupport\sfor\sdirectives. -D 2023-08-29T20:01:01.586 +C More\sfleshing\sout\sof\sJS\sSQLTester. +D 2023-08-29T20:44:40.606 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -283,7 +283,7 @@ F ext/jni/src/org/sqlite/jni/sqlite3.java 62b1b81935ccf3393472d17cb883dc5ff39c38 F ext/jni/src/org/sqlite/jni/sqlite3_context.java 66ca95ce904044263a4aff684abe262d56f73e6b06bca6cf650761d79d7779ad F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 78e6d1b95ac600a9475e9db4623f69449322b0c93d1bd4e1616e76ed547ed9fc F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a -F ext/jni/src/org/sqlite/jni/tester/SQLTester.java a9f4b9e12109645b21fef15807973706dd958aad9fe1c835693fcb8e95abe949 +F ext/jni/src/org/sqlite/jni/tester/SQLTester.java e5a1a4b55ed940e61558be1292aa66563219f360c7c1f9d40d770307e6da3c07 F ext/jni/src/org/sqlite/jni/tester/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e F ext/jni/src/tests/000-000-sanity.test cfe6dc1b950751d6096e3f5695becaadcdaa048bfe9567209d6eb676e693366d F ext/jni/src/tests/000-001-ignored.test e17e874c6ab3c437f1293d88093cf06286083b65bf162317f91bbfd92f961b70 @@ -548,8 +548,8 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs 345736d970dc56e2c1041f8583fc602eedd8a64d455864f312db7d3208e640ea -F ext/wasm/SQLTester/SQLTester.run.mjs 2dfa1407f5f188dadafe6f21f7a6740b4f07d59c594781a01eedadec16b2ddfe +F ext/wasm/SQLTester/SQLTester.mjs ed6bc486d804829883d05a94cbc5ace1b468837fcaf687d87f17969a659100ae +F ext/wasm/SQLTester/SQLTester.run.mjs e053a4e94b22a97f5981a0ce927b77c542d68a564d723dbaeb7b299d817bb915 F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -2111,8 +2111,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 d21b1217964a53f33b7ba3958b34aa8560dff8ede33e66f54aa0afbab7099ec3 -R 7266768b4057594984eb0965145c2068 +P 5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 +R 3c7d31cab0e76e0f71d5ac9e71d46fd7 U stephan -Z 703bcb5450951150eb9347fe40faa521 +Z 3b4eebe6c916b635f59799d0c8f89ba5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 47297d57d2..8feab07178 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 \ No newline at end of file +8c503dfb9fa15389613a819fcc1792e23d3c05f99a9f450f82eac5125298726f \ No newline at end of file From 4f1387e9ab1b5aed8c1c901be23eb02af47854d6 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 29 Aug 2023 21:30:37 +0000 Subject: [PATCH 118/422] Add directives support to JS SQLTester comparable to the Java impl. This brings the two to feature parity. FossilOrigin-Name: 7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 --- ext/wasm/SQLTester/SQLTester.mjs | 72 ++++++++++++++++++++-------- ext/wasm/SQLTester/SQLTester.run.mjs | 15 ++++++ manifest | 14 +++--- manifest.uuid | 2 +- 4 files changed, 74 insertions(+), 29 deletions(-) diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index c8ab2fa5cc..aa02d7a5ec 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -53,8 +53,13 @@ const ResultRowMode = newObj({ }); class SQLTesterException extends globalThis.Error { - constructor(...args){ - super(args.join('')); + constructor(testScript, ...args){ + if(testScript){ + super( [testScript.getOutputPrefix()+": ", ...args].join('') ); + }else{ + super( args.join('') ); + } + this.name = 'SQLTesterException'; } isFatal() { return false; } } @@ -64,8 +69,9 @@ SQLTesterException.toss = (...args)=>{ } class DbException extends SQLTesterException { - constructor(pDb, rc, closeDb){ - super("DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb)); + constructor(testScript, pDb, rc, closeDb){ + super(testScript, "DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb)); + this.name = 'DbException'; if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb); } isFatal() { return true; } @@ -73,28 +79,27 @@ class DbException extends SQLTesterException { class TestScriptFailed extends SQLTesterException { constructor(testScript, ...args){ - super(testScript.getOutputPrefix(),': ',...args); + super(testScript,...args); + this.name = 'TestScriptFailed'; } isFatal() { return true; } } class UnknownCommand extends SQLTesterException { - constructor(...args){ - super(...args); + constructor(testScript, cmdName){ + super(testScript, cmdName); + this.name = 'UnknownCommand'; } } class IncompatibleDirective extends SQLTesterException { - constructor(...args){ - super(...args); + constructor(testScript, ...args){ + super(testScript,...args); + this.name = 'IncompatibleDirective'; } } const toss = (errType, ...args)=>{ - if( !(errType instanceof SQLTesterException)){ - args.unshift(errType); - errType = SQLTesterException; - } throw new errType(...args); }; @@ -429,7 +434,7 @@ class SQLTester { }catch(e){ if(e instanceof SQLTesterException){ threw = true; - this.outln("🔥EXCEPTION: ",''+e); + this.outln("🔥EXCEPTION: ",e); ++this.metrics.nAbortedScript; if( this.#keepGoing ){ this.outln("Continuing anyway becaure of the keep-going option."); @@ -537,7 +542,7 @@ class SQLTester { ); if( 0!==rc ){ if(throwOnError){ - throw new DbException(pDb, rc); + throw new DbException(self, pDb, rc); }else if( sb ){ self.#appendDbErr(db, sb, rc); } @@ -699,7 +704,32 @@ class TestScript { } #checkForDirective(tester,line){ - //todo + if(line.startsWith("#")){ + throw new IncompatibleDirective(this, "C-preprocessor input: "+line); + }else if(line.startsWith("---")){ + throw new IncompatibleDirective(this, "triple-dash: ",line); + } + let m = Rx.scriptModuleName.exec(line); + if( m ){ + this.#moduleName = m[1]; + return; + } + m = Rx.requiredProperties.exec(line); + if( m ){ + const rp = m[1]; + //if( ! checkRequiredProperties( tester, rp.split("\\s+") ) ){ + throw new IncompatibleDirective(this, "REQUIRED_PROPERTIES: "+rp); + //} + } + + m = Rx.mixedModuleName.exec(line); + if( m ){ + throw new IncompatibleDirective(this, m[1]+": "+m[3]); + } + if( line.indexOf("\n|")>=0 ){ + throw new IncompatibleDirective(this, "newline-pipe combination."); + } + } #getCommandArgv(line){ @@ -734,7 +764,7 @@ class TestScript { this.#outer.verbosity(tester.verbosity()); let line, directive, argv = []; while( null != (line = this.getLine()) ){ - this.verbose3("input line: ",line); + this.verbose3("run() input line: ",line); this.#checkForDirective(tester, line); argv = this.#getCommandArgv(line); if( argv ){ @@ -747,10 +777,10 @@ class TestScript { } #processCommand(tester, argv){ - this.verbose1("running command: ",argv[0], " ", Util.argvToString(argv)); + this.verbose2("processCommand(): ",argv[0], " ", Util.argvToString(argv)); if(this.#outer.verbosity()>1){ const input = tester.getInputText(); - if( !!input ) this.verbose3("Input buffer = ",input); + this.verbose3("processCommand() input buffer = ",input); } CommandDispatcher.dispatch(tester, this, argv); } @@ -1084,7 +1114,7 @@ class TestCaseCommand extends Command { //! --verbosity command class VerbosityCommand extends Command { process(t, ts, argv){ - t.argcCheck(ts,argv,1); + this.argcCheck(ts,argv,1); ts.verbosity( parseInt(argv[1]) ); } } @@ -1123,7 +1153,7 @@ class CommandDispatcher { static dispatch(tester, testScript, argv){ const cmd = CommandDispatcher.getCommandByName(argv[0]); if( !cmd ){ - toss(UnknownCommand,argv[0],' ',testScript.getOutputPrefix()); + toss(UnknownCommand,testScript,argv[0]); } cmd.process(tester, testScript, argv); } diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index 5136d58a28..e58db93450 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -25,6 +25,21 @@ log("ns =",ns); outln("SQLTester is ready."); let ts = new ns.TestScript('/foo.test',` +/* +** This is a comment. There are many like it but this one is mine. +** +** SCRIPT_MODULE_NAME: sanity-check +** xMIXED_MODULE_NAME: mixed-module +** xMODULE_NAME: module-name +** xREQUIRED_PROPERTIES: small fast reliable +** xREQUIRED_PROPERTIES: RECURSIVE_TRIGGERS +** xREQUIRED_PROPERTIES: TEMPSTORE_MEM TEMPSTORE_FILE +** +*/ +/* --verbosity 3 */ +/* ---must-fail */ +/* # must fail */ +/* --verbosity 0 */ --print Hello, world. --close all --oom diff --git a/manifest b/manifest index b7930866b8..c8e5581fda 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\sfleshing\sout\sof\sJS\sSQLTester. -D 2023-08-29T20:44:40.606 +C Add\sdirectives\ssupport\sto\sJS\sSQLTester\scomparable\sto\sthe\sJava\simpl.\sThis\sbrings\sthe\stwo\sto\sfeature\sparity. +D 2023-08-29T21:30:37.122 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,8 +548,8 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs ed6bc486d804829883d05a94cbc5ace1b468837fcaf687d87f17969a659100ae -F ext/wasm/SQLTester/SQLTester.run.mjs e053a4e94b22a97f5981a0ce927b77c542d68a564d723dbaeb7b299d817bb915 +F ext/wasm/SQLTester/SQLTester.mjs 40583ad88bbc32c602e9bfbcd7a90ea6bdfeed41994c1fcc0a7e9d0ffdcd9a5e +F ext/wasm/SQLTester/SQLTester.run.mjs de03763f8085a130e17706a8c475d7bab37f77eff4f5322b0cc33504733394d7 F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -2111,8 +2111,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 5e798369375ce1b0c9cdf831f835d931fbd562ff7b4db09a06d1bdca2ac1b975 -R 3c7d31cab0e76e0f71d5ac9e71d46fd7 +P 8c503dfb9fa15389613a819fcc1792e23d3c05f99a9f450f82eac5125298726f +R e03f3f1709d7ca32242a0e49e3b98e3f U stephan -Z 3b4eebe6c916b635f59799d0c8f89ba5 +Z ecc2ea98d4d7da8116d2d29b184f2709 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 8feab07178..cb91036e81 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8c503dfb9fa15389613a819fcc1792e23d3c05f99a9f450f82eac5125298726f \ No newline at end of file +7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 \ No newline at end of file From ac5e1f82ce5e4103bf31882252587f3cbb2a564b Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 30 Aug 2023 00:22:54 +0000 Subject: [PATCH 119/422] Add a mechanism with which to import external SQLTester scripts into the JS testing tool. FossilOrigin-Name: bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 --- ext/wasm/SQLTester/GNUmakefile | 55 +++++++++++++++++++ ext/wasm/SQLTester/SQLTester.mjs | 81 +++++++++++++++++++++++----- ext/wasm/SQLTester/SQLTester.run.mjs | 58 +++++++++++++------- ext/wasm/SQLTester/index.html | 13 ++--- ext/wasm/SQLTester/touint8array.c | 29 ++++++++++ manifest | 18 ++++--- manifest.uuid | 2 +- 7 files changed, 209 insertions(+), 47 deletions(-) create mode 100644 ext/wasm/SQLTester/GNUmakefile create mode 100644 ext/wasm/SQLTester/touint8array.c diff --git a/ext/wasm/SQLTester/GNUmakefile b/ext/wasm/SQLTester/GNUmakefile new file mode 100644 index 0000000000..63388084d9 --- /dev/null +++ b/ext/wasm/SQLTester/GNUmakefile @@ -0,0 +1,55 @@ +#!/this/is/make +# +# This makefile compiles SQLTester test files into something +# we can readily import into JavaScript. +all: + +SHELL := $(shell which bash 2>/dev/null) +MAKEFILE := $(lastword $(MAKEFILE_LIST)) +CLEAN_FILES := +DISTCLEAN_FILES := ./--dummy-- *~ + +test-list.mjs := test-list.mjs +test-list.mjs.gz := $(test-list.mjs).gz +CLEAN_FILES += $(test-list.mjs) + +tests.dir := $(firstword $(wildcard tests ../../jni/src/tests)) +$(info test script dir=$(tests.dir)) + +tests.all := $(wildcard $(tests.dir)/*.test) + +bin.touint8array := ./touint8array +$(bin.touint8array): $(bin.touint8array).c $(MAKEFILE) + $(CC) -o $@ $< +CLEAN_FILES += $(bin.touint8array) + +ifneq (,$(tests.all)) +$(test-list.mjs): $(bin.touint8array) $(tests.all) $(MAKEFILE) + @{\ + echo 'export default ['; \ + sep=''; \ + for f in $(sort $(tests.all)); do \ + echo -en $$sep'{"name": "'$${f##*/}'", "content":'; \ + $(bin.touint8array) < $$f; \ + echo -n '}'; \ + sep=',\n'; \ + done; \ + echo '];'; \ + } > $@ + @echo "Created $@" +$(test-list.mjs.gz): $(test-list.mjs) + gzip -c $< > $@ +CLEAAN_FILES += $(test-list.mjs.gz) +all: $(test-list.mjs.gz) +else + @echo "Cannot build $(test-list.mjs) for lack of input test files."; \ + echo "Symlink ./tests to a directory containing SQLTester-format "; \ + echo "test scripts named *.test, then try again"; \ + exit 1 +endif + +.PHONY: clean distclean +clean: + -rm -f $(CLEAN_FILES) +distclean: clean + -rm -f $(DISTCLEAN_FILES) diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index aa02d7a5ec..0ec82ca464 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -9,12 +9,13 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains the main application entry pointer for the -** JS implementation of the SQLTester framework. +** This file contains the main application entry pointer for the JS +** implementation of the SQLTester framework. This version is not well +** documented because the one it's a direct port of is documented: +** in the main SQLite3 source tree, see +** ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java. */ -// UNDER CONSTRUCTION. Still being ported from the Java impl. - import sqlite3ApiInit from '/jswasm/sqlite3.mjs'; const sqlite3 = await sqlite3ApiInit(); @@ -23,6 +24,43 @@ const log = (...args)=>{ console.log('SQLTester:',...args); }; +const tryInstallVfs = function f(vfsName){ + if(f.vfsName) return false; + const pVfs = sqlite3.capi.sqlite3_vfs_find(vfsName); + if(pVfs){ + log("Installing",'"'+vfsName+'"',"as default VFS."); + const rc = sqlite3.capi.sqlite3_vfs_register(pVfs, 1); + if(rc){ + sqlite3.SQLite3Error.toss(rc,"While trying to register",vfsName,"vfs."); + } + f.vfsName = vfsName; + } + return !!pVfs; +}; +tryInstallVfs.vfsName = undefined; + +if( 1 ){ + // Try OPFS storage, if available... + if(sqlite3.installOpfsSAHPoolVfs){ + await sqlite3.installOpfsSAHPoolVfs({ + clearOnInit: true, + initialCapacity: 15, + name: 'opfs-SQLTester' + }).then(pool=>{ + tryInstallVfs(pool.vfsName); + }).catch(e=>{ + log("OpfsSAHPool could not load:",e); + }); + } + if(sqlite3.oo1.OpfsDb){ + tryInstallVfs("opfs"); + } +} + +const wPost = (type,...payload)=>{ + postMessage({type, payload}); +}; + // Return a new enum entry value const newE = ()=>Object.create(null); @@ -215,7 +253,7 @@ class SQLTester { //! Test result buffer. #resultBuffer = []; //! Output representation of SQL NULL. - #nullView = "nil"; + #nullView; metrics = newObj({ //! Total tests run nTotalTest: 0, @@ -245,6 +283,7 @@ class SQLTester { }); constructor(){ + this.reset(); } outln(...args){ return this.#outer.outln(...args); } @@ -261,7 +300,7 @@ class SQLTester { this.#clearBuffer(this.#db.initSql); this.closeAllDbs(); this.metrics.nTest = 0; - this.nullView = "nil"; + this.#nullView = "nil"; this.emitColNames = false; this.#db.iCurrentDb = 0; //this.#db.initSql.push("SELECT 1;"); @@ -424,12 +463,19 @@ class SQLTester { runTests(){ const tStart = (new Date()).getTime(); + let isVerbose = this.verbosity(); for(const ts of this.#aScripts){ this.reset(); ++this.metrics.nTestFile; let threw = false; const timeStart = (new Date()).getTime(); + let msgTail = ''; try{ + if( isVerbose ){ + this.#outer.verbose1("Running ",ts.filename()); + }else{ + msgTail = ' '+ts.filename(); + } ts.run(this); }catch(e){ if(e instanceof SQLTesterException){ @@ -446,7 +492,7 @@ class SQLTester { }finally{ const timeEnd = (new Date()).getTime(); this.outln("🏁", (threw ? "❌" : "✅"), " ", this.metrics.nTest, - " test(s) in ", (timeEnd-timeStart),"ms."); + " test(s) in ", (timeEnd-timeStart),"ms.",msgTail); } } const tEnd = (new Date()).getTime(); @@ -544,7 +590,7 @@ class SQLTester { if(throwOnError){ throw new DbException(self, pDb, rc); }else if( sb ){ - self.#appendDbErr(db, sb, rc); + self.#appendDbErr(pDb, sb, rc); } break; } @@ -666,13 +712,20 @@ class TestScript { if( 2 == args.length ){ filename = args[0]; content = args[1]; - }else{ - content = args[0]; + }else if( 1 == args.length ){ + if(args[0] instanceof Object){ + const o = args[0]; + filename = o.name; + content = o.content; + }else{ + content = args[0]; + } } if(!(content instanceof Uint8Array)){ if('string' === typeof content){ content = Util.utf8Encode(content); - }else if(content instanceof ArrayBuffer){ + }else if((content instanceof ArrayBuffer) + ||(content instanceof Array)){ content = new Uint8Array(content); }else{ toss(Error, "Invalid content type for TestScript constructor."); @@ -686,6 +739,10 @@ class TestScript { return (0==arguments.length) ? this.#testCaseName : (this.#testCaseName = arguments[0]); } + filename(){ + return (0==arguments.length) + ? this.#filename : (this.#filename = arguments[0]); + } getOutputPrefix() { let rc = "["+(this.#moduleName || this.#filename)+"]"; @@ -1041,7 +1098,7 @@ class RunCommand extends Command { ResultRowMode.ONELINE, sql); if( 0!==rc && t.verbosity()>0 ){ const msg = sqlite3.capi.sqlite3_errmsg(pDb); - ts.verbose1(argv[0]," non-fatal command error #",rc,": ", + ts.verbose2(argv[0]," non-fatal command error #",rc,": ", msg,"\nfor SQL:\n",sql); } } diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index e58db93450..e6730253b6 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -1,4 +1,18 @@ +/* +** 2023-08-29 +** +** 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 contains a test application for SQLTester.js. +*/ import {default as ns} from './SQLTester.mjs'; +import {default as tests} from './test-list.mjs'; globalThis.sqlite3 = ns.sqlite3; const log = function f(...args){ @@ -19,16 +33,13 @@ const affirm = function(expr, msg){ } } -console.log("Loaded",ns); - -log("ns =",ns); -outln("SQLTester is ready."); +log("SQLTester is ready."); let ts = new ns.TestScript('/foo.test',` /* ** This is a comment. There are many like it but this one is mine. ** -** SCRIPT_MODULE_NAME: sanity-check +** SCRIPT_MODULE_NAME: sanity-check-0 ** xMIXED_MODULE_NAME: mixed-module ** xMODULE_NAME: module-name ** xREQUIRED_PROPERTIES: small fast reliable @@ -45,10 +56,10 @@ let ts = new ns.TestScript('/foo.test',` --oom --db 0 --new my.db ---null zilch +--null zilchy --testcase 1.0 SELECT 1, null; ---result 1 zilch +--result 1 zilchy --glob *zil* --notglob *ZIL* SELECT 1, 2; @@ -85,20 +96,27 @@ SELECT json_array(1,2,3) const sqt = new ns.SQLTester(); try{ - affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); - sqt.openDb('/foo.db', true); - affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); - sqt.verbosity(0); - if(false){ - affirm( 'zilch' !== sqt.nullValue() ); - ts.run(sqt); - affirm( 'zilch' === sqt.nullValue() ); + if( 0 ){ + affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); + sqt.openDb('/foo.db', true); + affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); + sqt.verbosity(0); + if(false){ + affirm( 'zilch' !== sqt.nullValue() ); + ts.run(sqt); + affirm( 'zilch' === sqt.nullValue() ); + } + sqt.addTestScript(ts); + sqt.runTests(); + }else{ + for(const t of tests){ + sqt.addTestScript( new ns.TestScript(t) ); + } + tests.length = 0; + sqt.verbosity(0); + sqt.runTests(); } - sqt.addTestScript(ts); - sqt.runTests(); }finally{ + log( "Metrics:", sqt.metrics ); sqt.reset(); } -log( 'sqt.getCurrentDb()', sqt.getCurrentDb() ); -log( "Metrics:", sqt.metrics ); - diff --git a/ext/wasm/SQLTester/index.html b/ext/wasm/SQLTester/index.html index e782f1367d..8ae3e27a36 100644 --- a/ext/wasm/SQLTester/index.html +++ b/ext/wasm/SQLTester/index.html @@ -14,13 +14,14 @@

All stuff on this page happens in the dev console.


- - + diff --git a/ext/wasm/SQLTester/touint8array.c b/ext/wasm/SQLTester/touint8array.c new file mode 100644 index 0000000000..b03ad42f03 --- /dev/null +++ b/ext/wasm/SQLTester/touint8array.c @@ -0,0 +1,29 @@ +/* +** 2023-08-29 +** +** 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 contains a tool for writing out the contents of stdin as +** a comma-separated list of numbers, one per byte. +*/ + +#include +int main(int argc, char const **argv){ + int i; + int rc = 0, colWidth = 30; + int ch; + printf("["); + for( i=0; EOF!=(ch = fgetc(stdin)); ++i ){ + if( 0!=i ) printf(","); + if( i && 0==(i%colWidth) ) puts(""); + printf("%d",ch); + } + printf("]"); + return rc; +} diff --git a/manifest b/manifest index c8e5581fda..7088c67f05 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sdirectives\ssupport\sto\sJS\sSQLTester\scomparable\sto\sthe\sJava\simpl.\sThis\sbrings\sthe\stwo\sto\sfeature\sparity. -D 2023-08-29T21:30:37.122 +C Add\sa\smechanism\swith\swhich\sto\simport\sexternal\sSQLTester\sscripts\sinto\sthe\sJS\stesting\stool. +D 2023-08-30T00:22:54.642 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,9 +548,11 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/SQLTester.mjs 40583ad88bbc32c602e9bfbcd7a90ea6bdfeed41994c1fcc0a7e9d0ffdcd9a5e -F ext/wasm/SQLTester/SQLTester.run.mjs de03763f8085a130e17706a8c475d7bab37f77eff4f5322b0cc33504733394d7 -F ext/wasm/SQLTester/index.html 88d87e3ccbc33e7ab3773a8e48c1172e876951c4be31d1307c3700671262cddf +F ext/wasm/SQLTester/GNUmakefile ba0430646d75a832d1647d6d204c999112d831f5e85d3ed99d8f663fea20fe19 +F ext/wasm/SQLTester/SQLTester.mjs 378868be0fcbbb92456aea10e3056f88b042e106b2c348aa51060e93726f6e10 +F ext/wasm/SQLTester/SQLTester.run.mjs 2695490e2092af0c6a9d1e4128edf830648687a54ec1f0ecd16bd1be083f3938 +F ext/wasm/SQLTester/index.html 317636557257608b103fa740c07f2d440d57b924ef2072e59c1372d4a4004c06 +F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 @@ -2111,8 +2113,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 8c503dfb9fa15389613a819fcc1792e23d3c05f99a9f450f82eac5125298726f -R e03f3f1709d7ca32242a0e49e3b98e3f +P 7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 +R 018b862c43a96a36d2df3223b087af85 U stephan -Z ecc2ea98d4d7da8116d2d29b184f2709 +Z 18f8112180ee272e1f7e50a87a0ce726 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index cb91036e81..7168a7b8ad 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 \ No newline at end of file +bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 \ No newline at end of file From e621556724c044aa09e7c4fdab4e8407df83d216 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 30 Aug 2023 11:54:43 +0000 Subject: [PATCH 120/422] Add a UI, of sorts, to the JS SQLTester. FossilOrigin-Name: 249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c --- ext/wasm/SQLTester/GNUmakefile | 2 +- ext/wasm/SQLTester/SQLTester.mjs | 165 ++++++++++++++++++--------- ext/wasm/SQLTester/SQLTester.run.mjs | 73 ++++++++---- ext/wasm/SQLTester/index.html | 99 +++++++++++++++- manifest | 18 +-- manifest.uuid | 2 +- 6 files changed, 267 insertions(+), 92 deletions(-) diff --git a/ext/wasm/SQLTester/GNUmakefile b/ext/wasm/SQLTester/GNUmakefile index 63388084d9..8fa1247138 100644 --- a/ext/wasm/SQLTester/GNUmakefile +++ b/ext/wasm/SQLTester/GNUmakefile @@ -39,7 +39,7 @@ $(test-list.mjs): $(bin.touint8array) $(tests.all) $(MAKEFILE) @echo "Created $@" $(test-list.mjs.gz): $(test-list.mjs) gzip -c $< > $@ -CLEAAN_FILES += $(test-list.mjs.gz) +CLEAN_FILES += $(test-list.mjs.gz) all: $(test-list.mjs.gz) else @echo "Cannot build $(test-list.mjs) for lack of input test files."; \ diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 0ec82ca464..895c646116 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -10,10 +10,11 @@ ** ************************************************************************* ** This file contains the main application entry pointer for the JS -** implementation of the SQLTester framework. This version is not well -** documented because the one it's a direct port of is documented: -** in the main SQLite3 source tree, see -** ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java. +** implementation of the SQLTester framework. +** +** This version is not well-documented because it's a direct port of +** the Java immplementation, which is documented: in the main SQLite3 +** source tree, see ext/jni/src/org/sqlite/jni/tester/SQLite3Tester.java. */ import sqlite3ApiInit from '/jswasm/sqlite3.mjs'; @@ -24,6 +25,12 @@ const log = (...args)=>{ console.log('SQLTester:',...args); }; +/** + Try to install vfsName as the new default VFS. Once this succeeds + (returns true) then it becomes a no-op on future calls. Throws if + vfs registration as the default VFS fails but has no side effects + if vfsName is not currently registered. +*/ const tryInstallVfs = function f(vfsName){ if(f.vfsName) return false; const pVfs = sqlite3.capi.sqlite3_vfs_find(vfsName); @@ -39,9 +46,13 @@ const tryInstallVfs = function f(vfsName){ }; tryInstallVfs.vfsName = undefined; -if( 1 ){ +if( 0 && globalThis.WorkerGlobalScope ){ // Try OPFS storage, if available... - if(sqlite3.installOpfsSAHPoolVfs){ + if( 0 && sqlite3.oo1.OpfsDb ){ + /* Really slow with these tests */ + tryInstallVfs("opfs"); + } + if( sqlite3.installOpfsSAHPoolVfs ){ await sqlite3.installOpfsSAHPoolVfs({ clearOnInit: true, initialCapacity: 15, @@ -52,14 +63,16 @@ if( 1 ){ log("OpfsSAHPool could not load:",e); }); } - if(sqlite3.oo1.OpfsDb){ - tryInstallVfs("opfs"); - } } -const wPost = (type,...payload)=>{ - postMessage({type, payload}); -}; +const wPost = (function(){ + return (('undefined'===typeof WorkerGlobalScope) + ? ()=>{} + : (type, payload)=>{ + postMessage({type, payload}); + }); +})(); +//log("WorkerGlobalScope",globalThis.WorkerGlobalScope); // Return a new enum entry value const newE = ()=>Object.create(null); @@ -99,6 +112,12 @@ class SQLTesterException extends globalThis.Error { } this.name = 'SQLTesterException'; } + /** + If this overrideable method returns false (the default) then + exceptions of that type are fatal to a whole test run, instead of + just the test which triggered it. If the the "keep going" flag + is set, this preference is ignored. + */ isFatal() { return false; } } @@ -107,7 +126,7 @@ SQLTesterException.toss = (...args)=>{ } class DbException extends SQLTesterException { - constructor(testScript, pDb, rc, closeDb){ + constructor(testScript, pDb, rc, closeDb=false){ super(testScript, "DB error #"+rc+": "+sqlite3.capi.sqlite3_errmsg(pDb)); this.name = 'DbException'; if( closeDb ) sqlite3.capi.sqlite3_close_v2(pDb); @@ -137,12 +156,14 @@ class IncompatibleDirective extends SQLTesterException { } } +//! For throwing where an expression is required. const toss = (errType, ...args)=>{ throw new errType(...args); }; const __utf8Decoder = new TextDecoder(); const __utf8Encoder = new TextEncoder('utf-8'); +//! Workaround for Util.utf8Decode() const __SAB = ('undefined'===typeof globalThis.SharedArrayBuffer) ? function(){} : globalThis.SharedArrayBuffer; @@ -190,26 +211,41 @@ class Outer { #verbosity = 0; #logger = console.log.bind(console); - constructor(){ + constructor(func){ + if(func) this.setFunc(func); + } + + logger(...args){ + if(args.length){ + this.#logger = args[0]; + return this; + } + return this.#logger; } out(...args){ - if(!this.#lnBuf.length && this.getOutputPrefix ){ + if( this.getOutputPrefix && !this.#lnBuf.length ){ this.#lnBuf.push(this.getOutputPrefix()); } this.#lnBuf.push(...args); return this; } - outln(...args){ - if(!this.#lnBuf.length && this.getOutputPrefix ){ + + #outlnImpl(vLevel, ...args){ + if( this.getOutputPrefix && !this.#lnBuf.length ){ this.#lnBuf.push(this.getOutputPrefix()); } this.#lnBuf.push(...args,'\n'); - this.#logger(this.#lnBuf.join('')); + const msg = this.#lnBuf.join(''); this.#lnBuf.length = 0; + this.#logger(msg); return this; } + outln(...args){ + return this.#outlnImpl(0,...args); + } + outputPrefix(){ if( 0==arguments.length ){ return (this.getOutputPrefix @@ -220,9 +256,9 @@ class Outer { } } - verboseN(lvl, argv){ + verboseN(lvl, args){ if( this.#verbosity>=lvl ){ - this.outln('VERBOSE ',lvl,': ',...argv); + this.#outlnImpl(lvl,'VERBOSE ',lvl,': ',...args); } } verbose1(...args){ return this.verboseN(1,args); } @@ -230,13 +266,8 @@ class Outer { verbose3(...args){ return this.verboseN(3,args); } verbosity(){ - let rc; - if(arguments.length){ - rc = this.#verbosity; - this.#verbosity = arguments[0]; - }else{ - rc = this.#verbosity; - } + const rc = this.#verbosity; + if(arguments.length) this.#verbosity = +arguments[0]; return rc; } @@ -261,7 +292,7 @@ class SQLTester { nTestFile: 0, //! Number of scripts which were aborted nAbortedScript: 0, - //! Incremented by test case handlers + //! Test-case count for to the current TestScript nTest: 0 }); #emitColNames = false; @@ -288,6 +319,24 @@ class SQLTester { outln(...args){ return this.#outer.outln(...args); } out(...args){ return this.#outer.out(...args); } + outer(...args){ + if(args.length){ + this.#outer = args[0]; + return this; + } + return this.#outer; + } + verbose1(...args){ return this.#outer.verboseN(1,args); } + verbose2(...args){ return this.#outer.verboseN(2,args); } + verbose3(...args){ return this.#outer.verboseN(3,args); } + verbosity(...args){ + const rc = this.#outer.verbosity(...args); + return args.length ? this : rc; + } + setLogger(func){ + this.#outer.logger(func); + return this; + } incrementTestCounter(){ ++this.metrics.nTotalTest; @@ -339,8 +388,6 @@ class SQLTester { return this.#takeBuffer(this.#resultBuffer); } - verbosity(...args){ return this.#outer.verbosity(...args); } - nullValue(){ if( 0==arguments.length ){ return this.#nullView; @@ -427,7 +474,7 @@ class SQLTester { pDb = wasm.peekPtr(ppOut); }); if( 0==rc && this.#db.initSql.length > 0){ - //this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString()); + this.#outer.verbose2("RUNNING DB INIT CODE: ",this.#db.initSql.toString()); rc = this.execSql(pDb, false, ResultBufferMode.NONE, null, this.#db.initSql.join('')); } @@ -464,6 +511,8 @@ class SQLTester { runTests(){ const tStart = (new Date()).getTime(); let isVerbose = this.verbosity(); + this.metrics.nAbortedScript = 0; + this.metrics.nTotalTest = 0; for(const ts of this.#aScripts){ this.reset(); ++this.metrics.nTestFile; @@ -471,11 +520,6 @@ class SQLTester { const timeStart = (new Date()).getTime(); let msgTail = ''; try{ - if( isVerbose ){ - this.#outer.verbose1("Running ",ts.filename()); - }else{ - msgTail = ' '+ts.filename(); - } ts.run(this); }catch(e){ if(e instanceof SQLTesterException){ @@ -483,21 +527,35 @@ class SQLTester { this.outln("🔥EXCEPTION: ",e); ++this.metrics.nAbortedScript; if( this.#keepGoing ){ - this.outln("Continuing anyway becaure of the keep-going option."); + this.outln("Continuing anyway because of the keep-going option."); + }else if( e.isFatal() ){ + throw e; } - else if( e.isFatal() ) throw e; }else{ throw e; } }finally{ const timeEnd = (new Date()).getTime(); - this.outln("🏁", (threw ? "❌" : "✅"), " ", this.metrics.nTest, - " test(s) in ", (timeEnd-timeStart),"ms.",msgTail); + this.out("🏁", (threw ? "❌" : "✅"), " ", + this.metrics.nTest, " test(s) in ", + (timeEnd-timeStart),"ms. "); + const mod = ts.moduleName(); + if( mod ){ + this.out( "[",mod,"] " ); + } + this.outln(ts.filename()); } } const tEnd = (new Date()).getTime(); - this.outln("Total run-time: ",(tEnd-tStart),"ms"); Util.unlink(this.#db.initialDbName); + this.outln("Took ",(tEnd-tStart),"ms. test count = ", + this.metrics.nTotalTest,", script count = ", + this.#aScripts.length,( + this.metrics.nAbortedScript + ? ", aborted scripts = "+this.metrics.nAbortedScript + : "" + ) + ); return this; } @@ -511,10 +569,6 @@ class SQLTester { } } - /** - Returns v or some escaped form of v, as defined in the tester's - spec doc. - */ #escapeSqlValue(v){ if( !v ) return "{}"; if( !Rx.special.test(v) ){ @@ -735,6 +789,11 @@ class TestScript { this.#cursor.src = content; } + moduleName(){ + return (0==arguments.length) + ? this.#moduleName : (this.#moduleName = arguments[0]); + } + testCaseName(){ return (0==arguments.length) ? this.#testCaseName : (this.#testCaseName = arguments[0]); @@ -760,6 +819,14 @@ class TestScript { throw new TestScriptFailed(this,...args); } + verbose1(...args){ return this.#outer.verboseN(1,args); } + verbose2(...args){ return this.#outer.verboseN(2,args); } + verbose3(...args){ return this.#outer.verboseN(3,args); } + verbosity(...args){ + const rc = this.#outer.verbosity(...args); + return args.length ? this : rc; + } + #checkForDirective(tester,line){ if(line.startsWith("#")){ throw new IncompatibleDirective(this, "C-preprocessor input: "+line); @@ -818,7 +885,8 @@ class TestScript { run(tester){ this.reset(); - this.#outer.verbosity(tester.verbosity()); + this.#outer.verbosity( tester.verbosity() ); + this.#outer.logger( tester.outer().logger() ); let line, directive, argv = []; while( null != (line = this.getLine()) ){ this.verbose3("run() input line: ",line); @@ -942,11 +1010,6 @@ class TestScript { cur.lineNo = cur.putbackLineNo; } - verbose1(...args){ return this.#outer.verboseN(1,args); } - verbose2(...args){ return this.#outer.verboseN(2,args); } - verbose3(...args){ return this.#outer.verboseN(3,args); } - verbosity(...args){ return this.#outer.verbosity(...args); } - }/*TestScript*/; //! --close command diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index e6730253b6..4a08904592 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -12,7 +12,7 @@ ** This file contains a test application for SQLTester.js. */ import {default as ns} from './SQLTester.mjs'; -import {default as tests} from './test-list.mjs'; +import {default as allTests} from './test-list.mjs'; globalThis.sqlite3 = ns.sqlite3; const log = function f(...args){ @@ -33,8 +33,6 @@ const affirm = function(expr, msg){ } } -log("SQLTester is ready."); - let ts = new ns.TestScript('/foo.test',` /* ** This is a comment. There are many like it but this one is mine. @@ -56,10 +54,10 @@ let ts = new ns.TestScript('/foo.test',` --oom --db 0 --new my.db ---null zilchy +--null zilch --testcase 1.0 SELECT 1, null; ---result 1 zilchy +--result 1 zilch --glob *zil* --notglob *ZIL* SELECT 1, 2; @@ -94,29 +92,54 @@ SELECT json_array(1,2,3) --print Until next time `); -const sqt = new ns.SQLTester(); -try{ - if( 0 ){ - affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); - sqt.openDb('/foo.db', true); - affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); - sqt.verbosity(0); - if(false){ +const sqt = new ns.SQLTester() + .setLogger(console.log.bind(console)) + .verbosity(1) + .addTestScript(ts); + +const runTests = function(){ + try{ + if( 0 ){ + affirm( !sqt.getCurrentDb(), 'sqt.getCurrentDb()' ); + sqt.openDb('/foo.db', true); + affirm( !!sqt.getCurrentDb(),'sqt.getCurrentDb()' ); affirm( 'zilch' !== sqt.nullValue() ); ts.run(sqt); affirm( 'zilch' === sqt.nullValue() ); + sqt.addTestScript(ts); + sqt.runTests(); + }else{ + for(const t of allTests){ + sqt.addTestScript( new ns.TestScript(t) ); + } + allTests.length = 0; + sqt.runTests(); } - sqt.addTestScript(ts); - sqt.runTests(); - }else{ - for(const t of tests){ - sqt.addTestScript( new ns.TestScript(t) ); - } - tests.length = 0; - sqt.verbosity(0); - sqt.runTests(); + }finally{ + //log( "Metrics:", sqt.metrics ); + sqt.reset(); } -}finally{ - log( "Metrics:", sqt.metrics ); - sqt.reset(); +}; + +if( globalThis.WorkerGlobalScope ){ + const wPost = (type,payload)=>globalThis.postMessage({type, payload}); + globalThis.onmessage = function({data}){ + switch(data.type){ + case 'run-tests':{ + try{ runTests(); } + finally{ wPost('tests-end'); } + break; + } + default: + log("unhandled onmessage: ",data); + break; + } + }; + sqt.setLogger((msg)=>{ + wPost('stdout', {message: msg}); + }); + wPost('is-ready'); + //globalThis.onmessage({data:{type:'run-tests'}}); +}else{ + runTests(); } diff --git a/ext/wasm/SQLTester/index.html b/ext/wasm/SQLTester/index.html index 8ae3e27a36..ebd828c645 100644 --- a/ext/wasm/SQLTester/index.html +++ b/ext/wasm/SQLTester/index.html @@ -8,19 +8,108 @@ SQLTester + -

SQLTester App. +

SQLTester for JS/WASM

+

This app reads in a build-time-defined set of SQLTester test + scripts and runs them through the test suite.

-

All stuff on this page happens in the dev console.

-
+
+ Options + + + + + +
diff --git a/manifest b/manifest index 7088c67f05..15a2c4be2d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\smechanism\swith\swhich\sto\simport\sexternal\sSQLTester\sscripts\sinto\sthe\sJS\stesting\stool. -D 2023-08-30T00:22:54.642 +C Add\sa\sUI,\sof\ssorts,\sto\sthe\sJS\sSQLTester. +D 2023-08-30T11:54:43.323 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -548,10 +548,10 @@ F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34ce F ext/wasm/GNUmakefile 0e362f3fc04eab6628cbe4f1e35f4ab4a200881f6b5f753b27fb45eabeddd9d2 F ext/wasm/README-dist.txt 6382cb9548076fca472fb3330bbdba3a55c1ea0b180ff9253f084f07ff383576 F ext/wasm/README.md a8a2962c3aebdf8d2104a9102e336c5554e78fc6072746e5daf9c61514e7d193 -F ext/wasm/SQLTester/GNUmakefile ba0430646d75a832d1647d6d204c999112d831f5e85d3ed99d8f663fea20fe19 -F ext/wasm/SQLTester/SQLTester.mjs 378868be0fcbbb92456aea10e3056f88b042e106b2c348aa51060e93726f6e10 -F ext/wasm/SQLTester/SQLTester.run.mjs 2695490e2092af0c6a9d1e4128edf830648687a54ec1f0ecd16bd1be083f3938 -F ext/wasm/SQLTester/index.html 317636557257608b103fa740c07f2d440d57b924ef2072e59c1372d4a4004c06 +F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff +F ext/wasm/SQLTester/SQLTester.mjs 4a8fc2194a67180772830f503129c93fe44887b9573f50a4e24868b4bbc817f4 +F ext/wasm/SQLTester/SQLTester.run.mjs a50a1f9314d22d68b62a2f21d8913de163fce1fa420229711d6749c2b4fff9d0 +F ext/wasm/SQLTester/index.html e5f18af71749d81d86e4649d8c6efc9b78361738cb8e5c5014ba0dd3dc3de6ac F ext/wasm/SQLTester/touint8array.c 2d5ece04ec1393a6a60c4bf96385bda5e1a10ad49f3038b96460fc5e5aa7e536 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api d6a5078f48a5301ed17b9a30331075d9b2506e1360c1f0dee0c7816c10acd9ab F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b @@ -2113,8 +2113,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 7cef4a8300826adbdcb3b205e134a4272b12b4aa7dbee97731ac12282a4a9f06 -R 018b862c43a96a36d2df3223b087af85 +P bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 +R 8240e3c6439a4fca46c74a72b36ea777 U stephan -Z 18f8112180ee272e1f7e50a87a0ce726 +Z cef4bceac8cb673301c00562148f0e5f # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 7168a7b8ad..a0ecd6b9de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bb08ba020ce1d86ca6aa92f43d5ae915f67d08fa73120e1f603d150e76166624 \ No newline at end of file +249e82b9917ea47c56ee1cbd3345a977d335fd3fc0d67a1ef157813ef4571c7c \ No newline at end of file From 8dd07389ac9ae965fa984451af807b719a867549 Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 30 Aug 2023 14:20:02 +0000 Subject: [PATCH 121/422] When a JS SQLTester script throws, report the exception details back to the UI regardless of whether it's fatal. FossilOrigin-Name: 273d3b05f630d399d42914e95c416b107b4746bbef129cfba9d00fd921666261 --- ext/wasm/SQLTester/SQLTester.mjs | 18 ++++++++++-------- ext/wasm/SQLTester/SQLTester.run.mjs | 4 +++- ext/wasm/SQLTester/index.html | 9 ++++++--- manifest | 17 ++++++++--------- manifest.uuid | 2 +- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/ext/wasm/SQLTester/SQLTester.mjs b/ext/wasm/SQLTester/SQLTester.mjs index 895c646116..8741b81524 100644 --- a/ext/wasm/SQLTester/SQLTester.mjs +++ b/ext/wasm/SQLTester/SQLTester.mjs @@ -147,6 +147,7 @@ class UnknownCommand extends SQLTesterException { super(testScript, cmdName); this.name = 'UnknownCommand'; } + isFatal() { return true; } } class IncompatibleDirective extends SQLTesterException { @@ -290,10 +291,10 @@ class SQLTester { nTotalTest: 0, //! Total test script files run nTestFile: 0, - //! Number of scripts which were aborted - nAbortedScript: 0, //! Test-case count for to the current TestScript - nTest: 0 + nTest: 0, + //! Names of scripts which were aborted. + failedScripts: [] }); #emitColNames = false; //! True to keep going regardless of how a test fails. @@ -511,8 +512,9 @@ class SQLTester { runTests(){ const tStart = (new Date()).getTime(); let isVerbose = this.verbosity(); - this.metrics.nAbortedScript = 0; + this.metrics.failedScripts.length = 0; this.metrics.nTotalTest = 0; + this.metrics.nTestFile = 0; for(const ts of this.#aScripts){ this.reset(); ++this.metrics.nTestFile; @@ -525,7 +527,7 @@ class SQLTester { if(e instanceof SQLTesterException){ threw = true; this.outln("🔥EXCEPTION: ",e); - ++this.metrics.nAbortedScript; + this.metrics.failedScripts.push({script: ts.filename(), message:e.toString()}); if( this.#keepGoing ){ this.outln("Continuing anyway because of the keep-going option."); }else if( e.isFatal() ){ @@ -548,11 +550,11 @@ class SQLTester { } const tEnd = (new Date()).getTime(); Util.unlink(this.#db.initialDbName); - this.outln("Took ",(tEnd-tStart),"ms. test count = ", + this.outln("Took ",(tEnd-tStart),"ms. Test count = ", this.metrics.nTotalTest,", script count = ", this.#aScripts.length,( - this.metrics.nAbortedScript - ? ", aborted scripts = "+this.metrics.nAbortedScript + this.metrics.failedScripts.length + ? ", failed scripts = "+this.metrics.failedScripts.length : "" ) ); diff --git a/ext/wasm/SQLTester/SQLTester.run.mjs b/ext/wasm/SQLTester/SQLTester.run.mjs index 4a08904592..0f91230fd0 100644 --- a/ext/wasm/SQLTester/SQLTester.run.mjs +++ b/ext/wasm/SQLTester/SQLTester.run.mjs @@ -63,6 +63,7 @@ SELECT 1, null; SELECT 1, 2; intentional error; --run +/* ---intentional-failure */ --testcase json-1 SELECT json_array(1,2,3) --json [1,2,3] @@ -96,6 +97,7 @@ const sqt = new ns.SQLTester() .setLogger(console.log.bind(console)) .verbosity(1) .addTestScript(ts); +sqt.outer().outputPrefix(''); const runTests = function(){ try{ @@ -127,7 +129,7 @@ if( globalThis.WorkerGlobalScope ){ switch(data.type){ case 'run-tests':{ try{ runTests(); } - finally{ wPost('tests-end'); } + finally{ wPost('tests-end', sqt.metrics); } break; } default: diff --git a/ext/wasm/SQLTester/index.html b/ext/wasm/SQLTester/index.html index ebd828c645..cd1423ac37 100644 --- a/ext/wasm/SQLTester/index.html +++ b/ext/wasm/SQLTester/index.html @@ -33,7 +33,7 @@ -
+
Test output will go here.