]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Export sqlite3_(db_)free_memory() and sqlite3_table_column_metadata() to JNI. Further...
authorstephan <stephan@noemail.net>
Thu, 31 Aug 2023 14:57:01 +0000 (14:57 +0000)
committerstephan <stephan@noemail.net>
Thu, 31 Aug 2023 14:57:01 +0000 (14:57 +0000)
FossilOrigin-Name: 7c86aa3400ed591d38c1828f366f4b5de97954c2b301919d3f06d9c2d3d7d1f2

ext/jni/src/c/sqlite3-jni.c
ext/jni/src/c/sqlite3-jni.h
ext/jni/src/org/sqlite/jni/OutputPointer.java
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
ext/jni/src/org/sqlite/jni/Tester1.java
manifest
manifest.uuid

index 20bc5cc986b7b4654dfbd73735c7300dd7dd5f67..4fe788a0072d431662db6c258186477e81b72274 100644 (file)
 /*
 ** 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.
+** env is in scope. Where it's not, but should be, use this to make it
+** so.
 */
 #define S3JniDeclLocal_env JNIEnv * const env = s3jni_env()
 
@@ -316,6 +317,7 @@ static const struct {
   const S3JniNphRef sqlite3_stmt;
   const S3JniNphRef sqlite3_context;
   const S3JniNphRef sqlite3_value;
+  const S3JniNphRef OutputPointer_Bool;
   const S3JniNphRef OutputPointer_Int32;
   const S3JniNphRef OutputPointer_Int64;
   const S3JniNphRef OutputPointer_sqlite3;
@@ -341,22 +343,23 @@ static const struct {
   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",
+  RefO(4,  "OutputPointer$Bool",  "Z"),
+  RefO(5,  "OutputPointer$Int32", "I"),
+  RefO(6,  "OutputPointer$Int64", "J"),
+  RefO(7,  "OutputPointer$sqlite3",
            "Lorg/sqlite/jni/sqlite3;"),
-  RefO(7,  "OutputPointer$sqlite3_stmt",
+  RefO(8,  "OutputPointer$sqlite3_stmt",
            "Lorg/sqlite/jni/sqlite3_stmt;"),
-  RefO(8,  "OutputPointer$sqlite3_value",
+  RefO(9,  "OutputPointer$sqlite3_value",
            "Lorg/sqlite/jni/sqlite3_value;"),
 #ifdef SQLITE_ENABLE_FTS5
-  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")
+  RefO(10, "OutputPointer$String", "Ljava/lang/String;"),
+  RefO(11, "OutputPointer$ByteArray", "[B"),
+  RefN(12, "Fts5Context"),
+  RefN(13, "Fts5ExtensionApi"),
+  RefN(14, "fts5_api"),
+  RefN(15, "fts5_tokenizer"),
+  RefN(16, "Fts5Tokenizer")
 #endif
 #undef MkRef
 #undef RefN
@@ -449,6 +452,9 @@ struct S3JniDb {
 #endif
   S3JniDb * pNext /* Next entry in SJG.perDb.aFree */;
 };
+#define S3JniDb_clientdata_key "S3JniDb"
+#define S3JniDb_from_clientdata(pDb)                                \
+  (pDb ? sqlite3_get_clientdata(pDb, S3JniDb_clientdata_key) : 0)
 
 /*
 ** Cache for per-JNIEnv (i.e. per-thread) data.
@@ -690,77 +696,77 @@ static void s3jni_incr( volatile unsigned int * const p ){
 
 /* Helpers for working with specific mutexes. */
 #if SQLITE_THREADSAFE
-#define S3JniMutex_Env_assertLocked \
+#define S3JniEnv_mutex_assertLocked \
   assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
-#define S3JniMutex_Env_assertLocker \
+#define S3JniEnv_mutex_assertLocker \
   assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
-#define S3JniMutex_Env_assertNotLocker \
+#define S3JniEnv_mutex_assertNotLocker \
   assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
 
-#define S3JniMutex_Env_enter                        \
-  S3JniMutex_Env_assertNotLocker;                   \
+#define S3JniEnv_mutex_enter                        \
+  S3JniEnv_mutex_assertNotLocker;                   \
   sqlite3_mutex_enter( SJG.envCache.mutex );        \
   s3jni_incr(&SJG.metrics.nMutexEnv);               \
   SJG.envCache.locker = env
-#define S3JniMutex_Env_leave                         \
-  S3JniMutex_Env_assertLocker;                       \
+#define S3JniEnv_mutex_leave                         \
+  S3JniEnv_mutex_assertLocker;                       \
   SJG.envCache.locker = 0;                           \
   sqlite3_mutex_leave( SJG.envCache.mutex )
 
-#define S3JniMutex_Ext_enter                            \
+#define S3JniAutoExt_mutex_enter                            \
   sqlite3_mutex_enter( SJG.autoExt.mutex );             \
   SJG.autoExt.locker = env;                             \
   s3jni_incr( &SJG.metrics.nMutexAutoExt )
-#define S3JniMutex_Ext_leave                            \
+#define S3JniAutoExt_mutex_leave                            \
   assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ); \
   sqlite3_mutex_leave( SJG.autoExt.mutex )
-#define S3JniMutex_Ext_assertLocker                     \
+#define S3JniAutoExt_mutex_assertLocker                     \
   assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" )
 
-#define S3JniMutex_Global_enter                        \
+#define S3JniGlobal_mutex_enter                        \
   sqlite3_mutex_enter( SJG.mutex );                    \
   s3jni_incr(&SJG.metrics.nMutexGlobal);
-#define S3JniMutex_Global_leave                         \
+#define S3JniGlobal_mutex_leave                         \
   sqlite3_mutex_leave( SJG.mutex )
 
-#define S3JniMutex_Nph_enter                        \
-  S3JniMutex_Env_assertNotLocker;                   \
+#define S3JniNph_mutex_enter                        \
+  S3JniEnv_mutex_assertNotLocker;                   \
   sqlite3_mutex_enter( SJG.envCache.mutex );        \
   s3jni_incr( &SJG.metrics.nMutexEnv2 );            \
   SJG.envCache.locker = env
-#define S3JniMutex_Nph_leave                         \
-  S3JniMutex_Env_assertLocker;                       \
+#define S3JniNph_mutex_leave                         \
+  S3JniEnv_mutex_assertLocker;                       \
   SJG.envCache.locker = 0;                           \
   sqlite3_mutex_leave( SJG.envCache.mutex )
 
-#define S3JniMutex_S3JniDb_assertLocker \
+#define S3JniDb_mutex_assertLocker \
   assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
-#define S3JniMutex_S3JniDb_enter                      \
+#define S3JniDb_mutex_enter                      \
   sqlite3_mutex_enter( SJG.perDb.mutex );             \
   assert( 0==SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
   s3jni_incr( &SJG.metrics.nMutexPerDb );             \
   SJG.perDb.locker = env;
-#define S3JniMutex_S3JniDb_leave                      \
+#define S3JniDb_mutex_leave                      \
   assert( env == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
   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_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_assertLocker
-#define S3JniMutex_S3JniDb_enter
-#define S3JniMutex_S3JniDb_leave
+#define S3JniEnv_mutex_assertLocked
+#define S3JniEnv_mutex_assertLocker
+#define S3JniEnv_mutex_assertNotLocker
+#define S3JniEnv_mutex_enter
+#define S3JniEnv_mutex_leave
+#define S3JniAutoExt_mutex_assertLocker
+#define S3JniAutoExt_mutex_enter
+#define S3JniAutoExt_mutex_leave
+#define S3JniGlobal_mutex_enter
+#define S3JniGlobal_mutex_leave
+#define S3JniNph_mutex_enter
+#define S3JniNph_mutex_leave
+#define S3JniDb_mutex_assertLocker
+#define S3JniDb_mutex_enter
+#define S3JniDb_mutex_leave
 #endif
 
 /* Helpers for jstring and jbyteArray. */
@@ -805,12 +811,12 @@ static JNIEnv * s3jni_env(void){
 */
 static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
   struct S3JniEnv * row;
-  S3JniMutex_Env_enter;
+  S3JniEnv_mutex_enter;
   row = SJG.envCache.aHead;
   for( ; row; row = row->pNext ){
     if( row->env == env ){
       s3jni_incr( &SJG.metrics.nEnvHit );
-      S3JniMutex_Env_leave;
+      S3JniEnv_mutex_leave;
       return row;
     }
   }
@@ -827,7 +833,7 @@ static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
   SJG.envCache.aHead = row;
   row->env = env;
 
-  S3JniMutex_Env_leave;
+  S3JniEnv_mutex_leave;
   return row;
 }
 
@@ -1038,12 +1044,12 @@ static int s3jni__db_exception(JNIEnv * const env, S3JniDb * const ps,
   if( ex ){
     char * zMsg;
     S3JniExceptionClear;
-    S3JniMutex_S3JniDb_enter;
+    S3JniDb_mutex_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;
+    S3JniDb_mutex_leave;
   }
   return errCode;
 }
@@ -1097,12 +1103,12 @@ static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){
 */
 static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src,
                                  S3JniHook * const dest ){
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   *dest = *src;
   if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj);
   if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra);
   dest->doXDestroy = 0;
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
 }
 #define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest)
 
@@ -1140,14 +1146,14 @@ static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s){
 */
 static S3JniHook *S3JniHook__alloc(JNIEnv  * const env){
   S3JniHook * p = 0;
-  S3JniMutex_Global_enter;
+  S3JniGlobal_mutex_enter;
   if( SJG.hooks.aFree ){
     p = SJG.hooks.aFree;
     SJG.hooks.aFree = p->pNext;
     p->pNext = 0;
     s3jni_incr(&SJG.metrics.nHookRecycled);
   }
-  S3JniMutex_Global_leave;
+  S3JniGlobal_mutex_leave;
   if( 0==p ){
     p = s3jni_malloc(sizeof(S3JniHook));
     if( p ){
@@ -1169,10 +1175,10 @@ static void S3JniHook__free(JNIEnv  * const env, S3JniHook * const p){
   if(p){
     assert( !p->pNext );
     S3JniHook_unref(p);
-    S3JniMutex_Global_enter;
+    S3JniGlobal_mutex_enter;
     p->pNext = SJG.hooks.aFree;
     SJG.hooks.aFree = p;
-    S3JniMutex_Global_leave;
+    S3JniGlobal_mutex_leave;
   }
 }
 #define S3JniHook_free(hook) S3JniHook__free(env, hook)
@@ -1197,7 +1203,7 @@ static void S3JniHook__free_unlocked(JNIEnv  * const env, S3JniHook * const p){
 ** 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;
+  S3JniDb_mutex_assertLocker;
   sqlite3_free( s->zMainDbName );
 #define UNHOOK(MEMBER) \
   S3JniHook_unref(&s->hooks.MEMBER)
@@ -1223,7 +1229,7 @@ static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
 */
 static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
   assert( s );
-  S3JniMutex_S3JniDb_assertLocker;
+  S3JniDb_mutex_assertLocker;
   if( s ){
     S3JniDb_clear(env, s);
     s->pNext = SJG.perDb.aFree;
@@ -1233,9 +1239,9 @@ static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const 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_mutex_enter;
   S3JniDb_set_aside_unlocked(s);
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
 }
 #define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
 
@@ -1250,7 +1256,7 @@ 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;
+  S3JniEnv_mutex_assertLocked;
   row = SJG.envCache.aHead;
   for( ; row; pPrev = row, row = row->pNext ){
     if( row->env == env ){
@@ -1281,7 +1287,7 @@ static int S3JniEnv_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 modification
-** with S3JniMutex_Nph_enter/leave.
+** with S3JniNph_mutex_enter/leave.
 **
 ** This simple cache catches >99% of searches in the current
 ** (2023-07-31) tests.
@@ -1308,7 +1314,7 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p
   assert( pRef->index>=0
           && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) );
   if( !pNC->pRef ){
-    S3JniMutex_Nph_enter;
+    S3JniNph_mutex_enter;
     if( !pNC->pRef ){
       jclass const klazz = (*env)->FindClass(env, pRef->zName);
       S3JniExceptionIsFatal("FindClass() unexpectedly threw");
@@ -1317,7 +1323,7 @@ static S3JniNphClass * S3JniGlobal__nph(JNIEnv * const env, S3JniNphRef const* p
         /* Must come last to avoid a race condition where pNC->klass
            can be NULL after this function returns. */;
     }
-    S3JniMutex_Nph_leave;
+    S3JniNph_mutex_leave;
   }
   assert( pNC->klazz );
   return pNC;
@@ -1341,14 +1347,14 @@ static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){
   S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
 
   if( !pNC->fidValue ){
-    S3JniMutex_Nph_enter;
+    S3JniNph_mutex_enter;
     if( !pNC->fidValue ){
       pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz,
                                          pRef->zMember, pRef->zTypeSig);
       S3JniExceptionIsFatal("Code maintenance required: missing "
                             "required S3JniNphClass::fidValue.");
     }
-    S3JniMutex_Nph_leave;
+    S3JniNph_mutex_leave;
   }
   assert( pNC->fidValue );
   return pNC->fidValue;
@@ -1415,9 +1421,9 @@ static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
 */
 static sqlite3* PtrGet__sqlite3_lock(JNIEnv * const env, jobject jObj){
   sqlite3 *rv;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   rv = PtrGet_sqlite3(jObj);
-  if( !rv ){ S3JniMutex_S3JniDb_leave; }
+  if( !rv ){ S3JniDb_mutex_leave; }
   return rv;
 }
 #undef PtrGet_sqlite3
@@ -1431,14 +1437,16 @@ static sqlite3* PtrGet__sqlite3_lock(JNIEnv * const env, jobject jObj){
 ** associated with jDb via NativePointerHolder_set().
 */
 static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
-  S3JniDb * rv;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb * rv = 0;
+  S3JniDb_mutex_enter;
   if( SJG.perDb.aFree ){
     rv = SJG.perDb.aFree;
     SJG.perDb.aFree = rv->pNext;
     rv->pNext = 0;
     s3jni_incr( &SJG.metrics.nPdbRecycled );
-  }else{
+  }
+  S3JniDb_mutex_leave;
+  if( 0==rv ){
     rv = s3jni_malloc( sizeof(S3JniDb));
     if( rv ){
       s3jni_incr( &SJG.metrics.nPdbAlloc );
@@ -1448,16 +1456,9 @@ static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
     memset(rv, 0, sizeof(S3JniDb));
     rv->jDb = S3JniRefGlobal(jDb);
   }
-  S3JniMutex_S3JniDb_leave;
   return rv;
 }
 
-#define S3JniDb_clientdata_key "S3JniDb"
-
-/* Short-lived code consolidator. */
-#define S3JniDb_search                                              \
-  s = pDb ? sqlite3_get_clientdata(pDb, S3JniDb_clientdata_key) : 0
-
 /*
 ** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3
 ** object, or NULL if jDb is NULL, no pointer can be extracted
@@ -1468,24 +1469,20 @@ 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;
+  S3JniDb_mutex_enter;
   if( jDb ) pDb = PtrGet_sqlite3(jDb);
-  S3JniDb_search;
-  S3JniMutex_S3JniDb_leave;
+  s = S3JniDb_from_clientdata(pDb);
+  S3JniDb_mutex_leave;
   return s;
 }
 #define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject))
 
 
 static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
-  S3JniDb * s = 0;
   sqlite3 * pDb = 0;
-
-  S3JniMutex_S3JniDb_assertLocker;
+  S3JniDb_mutex_assertLocker;
   if( jDb ) pDb = PtrGet_sqlite3(jDb);
-  S3JniDb_search;
-  return s;
+  return S3JniDb_from_clientdata(pDb);
 }
 #define S3JniDb_from_java_unlocked(JDB) S3JniDb__from_java_unlocked(env, (JDB))
 
@@ -1493,29 +1490,18 @@ static S3JniDb * S3JniDb__from_java_unlocked(JNIEnv * const env, jobject jDb){
 ** S3JniDb finalizer for use with sqlite3_set_clientdata().
 */
 static void S3JniDb_xDestroy(void *p){
-  S3JniDb * ps = p;
   S3JniDeclLocal_env;
-
+  S3JniDb * const ps = p;
   assert( !ps->pNext );
   S3JniDb_set_aside(ps);
 }
 
 /*
-** 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.
+** Evaluates to the S3JniDb object for the given sqlite3 object, or
+** NULL if pDb is NULL or was not initialized via the JNI interfaces.
 */
-static S3JniDb * S3JniDb__from_c(JNIEnv * const env, sqlite3 *pDb){
-  S3JniDb * s = 0;
-
-  S3JniMutex_S3JniDb_enter;
-  S3JniDb_search;
-  S3JniMutex_S3JniDb_leave;
-  return s;
-}
-#define S3JniDb_from_c(sqlite3Ptr) S3JniDb__from_c(env,(sqlite3Ptr))
+#define S3JniDb_from_c(sqlite3Ptr) \
+  ((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0)
 
 /*
 ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out
@@ -1534,7 +1520,7 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
                                    jobject const jAutoExt){
   jclass const klazz = (*env)->GetObjectClass(env, jAutoExt);
 
-  S3JniMutex_Ext_assertLocker;
+  S3JniAutoExt_mutex_assertLocker;
   *ax = S3JniHook_empty;
   ax->midCallback = (*env)->GetMethodID(env, klazz, "call",
                                         "(Lorg/sqlite/jni/sqlite3;)I");
@@ -1548,6 +1534,18 @@ static int S3JniAutoExtension_init(JNIEnv *const env,
   return 0;
 }
 
+/*
+** Sets the value property of the OutputPointer.Bool jOut object to
+** v.
+*/
+static void OutputPointer_set_Bool(JNIEnv * const env, jobject const jOut,
+                                    int v){
+  (*env)->SetBooleanField(env, jOut, s3jni_nphop_field(
+                            env, &S3JniNphRefs.OutputPointer_Bool
+                          ), v ? JNI_TRUE : JNI_FALSE );
+  S3JniExceptionIsFatal("Cannot set OutputPointer.Bool.value");
+}
+
 /*
 ** Sets the value property of the OutputPointer.Int32 jOut object to
 ** v.
@@ -1624,6 +1622,7 @@ static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
   OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_ByteArray, jOut, v);
 }
 #endif
+#endif /* SQLITE_ENABLE_FTS5 */
 
 /*
 ** Sets the value property of the OutputPointer.String jOut object to
@@ -1633,7 +1632,6 @@ static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut,
                                      jstring const v){
   OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_String, jOut, v);
 }
-#endif /* SQLITE_ENABLE_FTS5 */
 
 /*
 ** Returns true if eTextRep is a valid sqlite3 encoding constant, else
@@ -1681,12 +1679,12 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef co
   jobject rv = 0;
   S3JniNphClass * const pNC = S3JniGlobal_nph(pRef);
   if( !pNC->midCtor ){
-    S3JniMutex_Nph_enter;
+    S3JniNph_mutex_enter;
     if( !pNC->midCtor ){
       pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "<init>", "()V");
       S3JniExceptionIsFatal("Cannot find constructor for class.");
     }
-    S3JniMutex_Nph_leave;
+    S3JniNph_mutex_leave;
   }
   rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor);
   S3JniExceptionIsFatal("No-arg constructor threw.");
@@ -1728,7 +1726,7 @@ typedef void (*udf_xFinal_f)(sqlite3_context*);
 static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
   S3JniUdf * s = 0;
 
-  S3JniMutex_Global_enter;
+  S3JniGlobal_mutex_enter;
   s3jni_incr(&SJG.metrics.nMutexUdf);
   if( SJG.udf.aFree ){
     s = SJG.udf.aFree;
@@ -1736,7 +1734,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
     s->pNext = 0;
     s3jni_incr(&SJG.metrics.nUdfRecycled);
   }
-  S3JniMutex_Global_leave;
+  S3JniGlobal_mutex_leave;
   if( !s ){
     s = s3jni_malloc( sizeof(*s));
     s3jni_incr(&SJG.metrics.nUdfAlloc);
@@ -1790,10 +1788,10 @@ static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s,
     memset(s, 0, sizeof(*s));
   }
   if( cacheIt ){
-    S3JniMutex_Global_enter;
+    S3JniGlobal_mutex_enter;
     s->pNext = S3JniGlobal.udf.aFree;
     S3JniGlobal.udf.aFree = s;
-    S3JniMutex_Global_leave;
+    S3JniGlobal_mutex_leave;
   }else{
     sqlite3_free( s );
   }
@@ -2060,6 +2058,7 @@ 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(1release_1memory,         sqlite3_release_memory)
 WRAP_INT_INT(1sleep,                   sqlite3_sleep)
 WRAP_MUTF8_VOID(1sourceid,             sqlite3_sourceid)
 WRAP_INT_VOID(1threadsafe,             sqlite3_threadsafe)
@@ -2124,7 +2123,8 @@ static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
     return rc;
   }
   NativePointerHolder_set(&S3JniNphRefs.sqlite3, ps->jDb, pDb)
-    /* As of here, the Java/C connection is complete */;
+    /* As of here, the Java/C connection is complete except for the
+       (temporary) lack of finalizer for the ps object. */;
   ps->pDb = pDb;
   for( i = 0; go && 0==rc; ++i ){
     S3JniAutoExtension ax = {0,0}
@@ -2132,14 +2132,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. */;
-    S3JniMutex_Ext_enter;
+    S3JniAutoExt_mutex_enter;
     if( i >= SJG.autoExt.nExt ){
       go = 0;
     }else{
       ax.jObj = S3JniRefLocal(SJG.autoExt.aExt[i].jObj);
       ax.midCallback = SJG.autoExt.aExt[i].midCallback;
     }
-    S3JniMutex_Ext_leave;
+    S3JniAutoExt_mutex_leave;
     if( ax.jObj ){
       rc = (*env)->CallIntMethod(env, ax.jObj, ax.midCallback, ps->jDb);
       S3JniUnrefLocal(ax.jObj);
@@ -2166,13 +2166,13 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
   int rc = 0;
 
   if( !jAutoExt ) return SQLITE_MISUSE;
-  S3JniMutex_Ext_enter;
+  S3JniAutoExt_mutex_enter;
   for( i = 0; i < SJG.autoExt.nExt; ++i ){
     /* 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;
+      S3JniAutoExt_mutex_leave;
       return 0;
     }
   }
@@ -2215,7 +2215,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
       ++SJG.autoExt.nExt;
     }
   }
-  S3JniMutex_Ext_leave;
+  S3JniAutoExt_mutex_leave;
   return rc;
 }
 
@@ -2359,7 +2359,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
   int rc = 0;
 
   if( !ps ) return (jint)SQLITE_MISUSE;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   if( jBusy ){
     if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){
       /* Same object - this is a no-op. */
@@ -2391,7 +2391,7 @@ S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
     }
   }
   S3JniHook_unref(&hook);
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return rc;
 }
 
@@ -2401,10 +2401,10 @@ S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)(
   S3JniDb * const ps = S3JniDb_from_java(jDb);
   int rc = SQLITE_MISUSE;
   if( ps ){
-    S3JniMutex_S3JniDb_enter;
+    S3JniDb_mutex_enter;
     S3JniHook_unref(&ps->hooks.busyHandler);
     rc = sqlite3_busy_timeout(ps->pDb, (int)ms);
-    S3JniMutex_S3JniDb_leave;
+    S3JniDb_mutex_leave;
   }
   return rc;
 }
@@ -2415,7 +2415,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
   S3JniAutoExtension * ax;
   jboolean rc = JNI_FALSE;
   int i;
-  S3JniMutex_Ext_enter;
+  S3JniAutoExt_mutex_enter;
   /* This algo mirrors the one in the core. */
   for( i = SJG.autoExt.nExt-1; i >= 0; --i ){
     ax = &SJG.autoExt.aExt[i];
@@ -2430,7 +2430,7 @@ S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
       break;
     }
   }
-  S3JniMutex_Ext_leave;
+  S3JniAutoExt_mutex_leave;
   return rc;
 }
 
@@ -2513,10 +2513,10 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
   S3JniCollationNeeded * pHook;
   int rc = 0;
 
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   ps = S3JniDb_from_java_unlocked(jDb);
   if( !ps ){
-    S3JniMutex_S3JniDb_leave;
+    S3JniDb_mutex_leave;
     return SQLITE_MISUSE;
   }
   pHook = &ps->hooks.collationNeeded;
@@ -2548,7 +2548,7 @@ S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
       }
     }
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return rc;
 }
 
@@ -2655,11 +2655,11 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
   jobject pOld = 0;
   S3JniHook * pHook;
 
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   ps = S3JniDb_from_java_unlocked(jDb);
   if( !ps ){
     s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
-    S3JniMutex_S3JniDb_leave;
+    S3JniDb_mutex_leave;
     return 0;
   }
   pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
@@ -2699,7 +2699,7 @@ static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
       }
     }
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return pOld;
 }
 
@@ -2791,7 +2791,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */,
   S3JniHook * const pHook = &SJG.hooks.sqllog;
   int rc = 0;
 
-  S3JniMutex_Global_enter;
+  S3JniGlobal_mutex_enter;
   if( !jLog ){
     rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
     if( 0==rc ){
@@ -2818,7 +2818,7 @@ S3JniApi(sqlite3_config() /* for SQLLOG */,
       rc = SQLITE_ERROR;
     }
   }
-  S3JniMutex_Global_leave;
+  S3JniGlobal_mutex_leave;
   return rc;
 #endif
 }
@@ -2878,7 +2878,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
   int rc;
   S3JniDb * ps;
 
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   ps = S3JniDb_from_java_unlocked(jDb);
   if( !ps ){
     rc = SQLITE_MISUSE;
@@ -2912,7 +2912,7 @@ S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
       sqlite3_free(zName);
     }
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return (jint)rc;
 }
 
@@ -3009,7 +3009,7 @@ S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/,
 
   switch( (ps && jStr) ? op : 0 ){
     case SQLITE_DBCONFIG_MAINDBNAME:
-      S3JniMutex_S3JniDb_enter
+      S3JniDb_mutex_enter
         /* Protect against a race in modifying/freeing
            ps->zMainDbName. */;
       zStr = s3jni_jstring_to_utf8( jStr, 0);
@@ -3024,7 +3024,7 @@ S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/,
       }else{
         rc = SQLITE_NOMEM;
       }
-      S3JniMutex_S3JniDb_leave;
+      S3JniDb_mutex_leave;
       break;
     default:
       rc = SQLITE_MISUSE;
@@ -3089,6 +3089,12 @@ JniDecl(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPoint
   );
 }
 
+S3JniApi(sqlite3_db_release_memory(),int,1db_1release_1memory)(
+  JniArgsEnvClass, jobject jDb
+){
+  sqlite3 * const pDb = PtrGet_sqlite3(jDb);
+  return pDb ? sqlite3_db_release_memory(pDb) : SQLITE_MISUSE;
+}
 
 S3JniApi(sqlite3_db_status(),jint,1db_1status)(
   JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent,
@@ -3197,9 +3203,9 @@ S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)(
 */
 JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){
   int rc;
-  S3JniMutex_Env_enter;
+  S3JniEnv_mutex_enter;
   rc = S3JniEnv_uncache(env);
-  S3JniMutex_Env_leave;
+  S3JniEnv_mutex_leave;
   return rc ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -3278,8 +3284,8 @@ static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc,
               && "Set up via s3jni_run_java_auto_extensions()" );
     }
     rc = sqlite3_set_clientdata(ps->pDb, S3JniDb_clientdata_key,
-                                ps, S3JniDb_xDestroy);
-    /* As of here, the Java/C connection is complete */
+                                ps, S3JniDb_xDestroy)
+      /* As of here, the Java/C connection is complete */;
   }else{
     S3JniDb_set_aside(ps);
     ps = 0;
@@ -3507,7 +3513,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec
   S3JniHook * pHook;
 
   if( !ps ) return 0;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   pHook = isPre ?
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
     &ps->hooks.preUpdate
@@ -3567,7 +3573,7 @@ static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobjec
     }
   }
 end:
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return pOld;
 }
 
@@ -3648,7 +3654,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
   S3JniHook * const pHook = ps ? &ps->hooks.progress : 0;
 
   if( !ps ) return;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   if( n<1 || !jProgress ){
     S3JniHook_unref(pHook);
     sqlite3_progress_handler(ps->pDb, 0, 0, 0);
@@ -3668,7 +3674,7 @@ S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
       sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
     }
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
 }
 
 S3JniApi(sqlite3_reset(),jint,1reset)(
@@ -3681,12 +3687,12 @@ S3JniApi(sqlite3_reset(),jint,1reset)(
 /* Clears all entries from S3JniGlobal.autoExt. */
 static void s3jni_reset_auto_extension(JNIEnv *env){
   int i;
-  S3JniMutex_Ext_enter;
+  S3JniAutoExt_mutex_enter;
   for( i = 0; i < SJG.autoExt.nExt; ++i ){
     S3JniAutoExtension_clear( &SJG.autoExt.aExt[i] );
   }
   SJG.autoExt.nExt = 0;
-  S3JniMutex_Ext_leave;
+  S3JniAutoExt_mutex_leave;
 }
 
 S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)(
@@ -3948,7 +3954,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
   int rc = 0;
 
   if( !ps ) return SQLITE_MISUSE;
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   if( !jHook ){
     S3JniHook_unref(pHook);
     rc = sqlite3_set_authorizer( ps->pDb, 0, 0 );
@@ -3957,7 +3963,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
     if( pHook->jObj ){
       if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){
       /* Same object - this is a no-op. */
-        S3JniMutex_S3JniDb_leave;
+        S3JniDb_mutex_leave;
         return 0;
       }
       S3JniHook_unref(pHook);
@@ -3981,7 +3987,7 @@ S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
     }
     if( rc ) S3JniHook_unref(pHook);
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
   return rc;
 }
 
@@ -3998,15 +4004,15 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
 ){
   s3jni_reset_auto_extension(env);
   /* Free up S3JniDb recycling bin. */
-  S3JniMutex_S3JniDb_enter; {
+  S3JniDb_mutex_enter; {
     while( S3JniGlobal.perDb.aFree ){
       S3JniDb * const d = S3JniGlobal.perDb.aFree;
       S3JniGlobal.perDb.aFree = d->pNext;
       S3JniDb_clear(env, d);
       sqlite3_free(d);
     }
-  } S3JniMutex_S3JniDb_leave;
-  S3JniMutex_Global_enter; {
+  } S3JniDb_mutex_leave;
+  S3JniGlobal_mutex_enter; {
     /* Free up S3JniUdf recycling bin. */
     while( S3JniGlobal.udf.aFree ){
       S3JniUdf * const u = S3JniGlobal.udf.aFree;
@@ -4024,13 +4030,13 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
       assert( !u->jExtra );
       sqlite3_free( u );
     }
-  } S3JniMutex_Global_leave;
+  } S3JniGlobal_mutex_leave;
   /* Free up env cache. */
-  S3JniMutex_Env_enter; {
+  S3JniEnv_mutex_enter; {
     while( SJG.envCache.aHead ){
       S3JniEnv_uncache( SJG.envCache.aHead->env );
     }
-  } S3JniMutex_Env_leave;
+  } S3JniEnv_mutex_leave;
 #if 0
   /*
   ** Is automatically closing any still-open dbs a good idea? We will
@@ -4038,11 +4044,11 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
   ** state, at which point we won't have a central list of databases
   ** to close.
   */
-  S3JniMutex_S3JniDb_enter;
+  S3JniDb_mutex_enter;
   while( SJG.perDb.pHead ){
     s3jni_close_db(env, SJG.perDb.pHead->jDb, 2);
   }
-  S3JniMutex_S3JniDb_leave;
+  S3JniDb_mutex_leave;
 #endif
   /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
   return sqlite3_shutdown();
@@ -4128,6 +4134,53 @@ S3JniApi(sqlite3_step(),jint,1step)(
   return rc;
 }
 
+S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)(
+  JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName,
+  jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull,
+  jobject jPrimaryKey, jobject jAutoinc
+){
+  sqlite3 * const db = PtrGet_sqlite3(jDb);
+  char * zDbName = 0, * zTableName = 0, * zColumnName = 0;
+  const char * pzCollSeq = 0;
+  const char * pzDataType = 0;
+  int pNotNull = 0, pPrimaryKey = 0, pAutoinc = 0;
+  int rc;
+
+  if( !db || !jDbName || !jTableName || !jColumnName ) return SQLITE_MISUSE;
+  zDbName = s3jni_jstring_to_utf8(jDbName,0);
+  zTableName = zDbName ? s3jni_jstring_to_utf8(jTableName,0) : 0;
+  zColumnName = zTableName ? s3jni_jstring_to_utf8(jColumnName,0) : 0;
+  rc = zColumnName
+    ? sqlite3_table_column_metadata(db, zDbName, zTableName,
+                                    zColumnName, &pzDataType, &pzCollSeq,
+                                    &pNotNull, &pPrimaryKey, &pAutoinc)
+    : SQLITE_NOMEM;
+  if( 0==rc ){
+    jstring jseq = jCollSeq
+      ? (pzCollSeq ? s3jni_utf8_to_jstring(pzCollSeq, -1) : 0)
+      : 0;
+    jstring jdtype = jDataType
+      ? (pzDataType ? s3jni_utf8_to_jstring(pzDataType, -1) : 0)
+      : 0;
+    if( (jCollSeq && pzCollSeq && !jseq)
+        || (jDataType && pzDataType && !jdtype) ){
+      rc = SQLITE_NOMEM;
+    }else{
+      if( jNotNull ) OutputPointer_set_Bool(env, jNotNull, pNotNull);
+      if( jPrimaryKey ) OutputPointer_set_Bool(env, jPrimaryKey, pPrimaryKey);
+      if( jAutoinc ) OutputPointer_set_Bool(env, jAutoinc, pAutoinc);
+      if( jCollSeq ) OutputPointer_set_String(env, jCollSeq, jseq);
+      if( jDataType ) OutputPointer_set_String(env, jDataType, jdtype);
+    }
+    S3JniUnrefLocal(jseq);
+    S3JniUnrefLocal(jdtype);
+  }
+  sqlite3_free(zDbName);
+  sqlite3_free(zTableName);
+  sqlite3_free(zColumnName);
+  return rc;
+}
+
 static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
   S3JniDb * const ps = (S3JniDb *)pC;
   S3JniDeclLocal_env;
@@ -4195,10 +4248,10 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
 
   if( !ps ) return SQLITE_MISUSE;
   if( !traceMask || !jTracer ){
-    S3JniMutex_S3JniDb_enter;
+    S3JniDb_mutex_enter;
     rc = (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0);
     S3JniHook_unref(&ps->hooks.trace);
-    S3JniMutex_S3JniDb_leave;
+    S3JniDb_mutex_leave;
   }else{
     jclass const klazz = (*env)->GetObjectClass(env, jTracer);
     S3JniHook hook = S3JniHook_empty;
@@ -4213,7 +4266,7 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
                           "TracerCallback object.");
     }else{
       hook.jObj = S3JniRefGlobal(jTracer);
-      S3JniMutex_S3JniDb_enter;
+      S3JniDb_mutex_enter;
       rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps);
       if( 0==rc ){
         S3JniHook_unref(&ps->hooks.trace);
@@ -4221,7 +4274,7 @@ S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
       }else{
         S3JniHook_unref(&hook);
       }
-      S3JniMutex_S3JniDb_leave;
+      S3JniDb_mutex_leave;
     }
   }
   return rc;
@@ -4486,14 +4539,14 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
     jobject pNPH = new_NativePointerHolder_object(
       env, &S3JniNphRefs.Fts5ExtensionApi, s3jni_ftsext()
     );
-    S3JniMutex_Env_enter;
+    S3JniEnv_mutex_enter;
     if( pNPH ){
       if( !SJG.fts5.jFtsExt ){
         SJG.fts5.jFtsExt = S3JniRefGlobal(pNPH);
       }
       S3JniUnrefLocal(pNPH);
     }
-    S3JniMutex_Env_leave;
+    S3JniEnv_mutex_leave;
   }
   return SJG.fts5.jFtsExt;
 }
index 40dd44990102aa3385c3171265ffa313ecc0acfb..a5454d54c73f2287ba02e7e67fb1bd2111f1a70b 100644 (file)
@@ -1187,6 +1187,14 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
 JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1handle
   (JNIEnv *, jclass, jobject);
 
+/*
+ * Class:     org_sqlite_jni_SQLite3Jni
+ * Method:    sqlite3_db_release_memory
+ * Signature: (Lorg/sqlite/jni/sqlite3;)I
+ */
+JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1release_1memory
+  (JNIEnv *, jclass, jobject);
+
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_db_status
@@ -1403,6 +1411,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1preupdate_1old
 JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1progress_1handler
   (JNIEnv *, jclass, jobject, jint, jobject);
 
+/*
+ * Class:     org_sqlite_jni_SQLite3Jni
+ * Method:    sqlite3_release_memory
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1release_1memory
+  (JNIEnv *, jclass, jint);
+
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_reset
@@ -1643,6 +1659,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1strglob
 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1strlike
   (JNIEnv *, jclass, jbyteArray, jbyteArray, jint);
 
+/*
+ * Class:     org_sqlite_jni_SQLite3Jni
+ * Method:    sqlite3_table_column_metadata
+ * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/sqlite/jni/OutputPointer/String;Lorg/sqlite/jni/OutputPointer/String;Lorg/sqlite/jni/OutputPointer/Bool;Lorg/sqlite/jni/OutputPointer/Bool;Lorg/sqlite/jni/OutputPointer/Bool;)I
+ */
+JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1table_1column_1metadata
+  (JNIEnv *, jclass, jobject, jstring, jstring, jstring, jobject, jobject, jobject, jobject, jobject);
+
 /*
  * Class:     org_sqlite_jni_SQLite3Jni
  * Method:    sqlite3_threadsafe
index 8a59de574b56b55401e140691f16c62385b26ef3..f27d9777441e304ff9b2ff55d933f881b604c98e 100644 (file)
@@ -108,6 +108,26 @@ public final class OutputPointer {
     }
   }
 
+  /**
+     Output pointer for use with native routines which return booleans
+     via integer output pointers.
+  */
+  public static final class Bool {
+    /**
+       This is public for ease of use. Accessors are provided for
+       consistency with the higher-level types.
+    */
+    public boolean value;
+    /** Initializes with the value 0. */
+    public Bool(){this(false);}
+    /** Initializes with the value v. */
+    public Bool(boolean v){value = v;}
+    /** Returns the current value. */
+    public final boolean get(){return value;}
+    /** Sets the current value to v. */
+    public final void set(boolean v){value = v;}
+  }
+
   /**
      Output pointer for use with native routines which return integers via
      output pointers.
index d32320b26fa6c2f1ea45596635f2cae0e67d998a..2c6ed2d7995328792859777b2359bdfb58b051fb 100644 (file)
@@ -612,6 +612,9 @@ public final class SQLite3Jni {
   @Canonical
   public static native sqlite3 sqlite3_db_handle( @NotNull sqlite3_stmt stmt );
 
+  @Canonical
+  public static native int sqlite3_db_release_memory(sqlite3 db);
+
   @Canonical
   public static native int sqlite3_db_status(
     @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent,
@@ -972,6 +975,9 @@ public final class SQLite3Jni {
     @NotNull sqlite3 db, int n, @Nullable ProgressHandlerCallback h
   );
 
+  @Canonical
+  public static native int sqlite3_release_memory(int n);
+
   @Canonical
   public static native int sqlite3_reset(@NotNull sqlite3_stmt stmt);
 
@@ -1304,7 +1310,6 @@ public final class SQLite3Jni {
   @Canonical
   public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt);
 
-
   @Canonical
   public static native int sqlite3_status(
     int op, @NotNull OutputPointer.Int32 pCurrent,
@@ -1357,6 +1362,17 @@ public final class SQLite3Jni {
     );
   }
 
+  @Canonical
+  public static native int sqlite3_table_column_metadata(
+    @NotNull sqlite3 db, @NotNull String zDbName,
+    @NotNull String zTableName, @NotNull String zColumnName,
+    @Nullable OutputPointer.String pzDataType,
+    @Nullable OutputPointer.String pzCollSeq,
+    @Nullable OutputPointer.Bool pNotNull,
+    @Nullable OutputPointer.Bool pPrimaryKey,
+    @Nullable OutputPointer.Bool pAutoinc
+  );
+
   @Canonical
   public static native int sqlite3_threadsafe();
 
index e418de60966affe5ad95f195e8d1cd2124308ecf..bbd61dfbd319351f5317bc93e0dc064a5d87292e 100644 (file)
@@ -731,6 +731,7 @@ public class Tester1 implements Runnable {
     }
     sqlite3_finalize(stmt);
     affirm( 1 == n );
+    affirm( 0==sqlite3_db_release_memory(db) );
     sqlite3_close_v2(db);
   }
 
@@ -1359,6 +1360,30 @@ public class Tester1 implements Runnable {
     affirm( 8 == val.value );
   }
 
+
+  private void testColumnMetadata(){
+    sqlite3 db = createNewDb();
+    execSql(db, new String[] {
+        "CREATE TABLE t(a duck primary key not null collate noCase); ",
+        "INSERT INTO t(a) VALUES(1),(2),(3);"
+      });
+    OutputPointer.Bool bNotNull = new OutputPointer.Bool();
+    OutputPointer.Bool bPrimaryKey = new OutputPointer.Bool();
+    OutputPointer.Bool bAutoinc = new OutputPointer.Bool();
+    OutputPointer.String zCollSeq = new OutputPointer.String();
+    OutputPointer.String zDataType = new OutputPointer.String();
+    int rc = sqlite3_table_column_metadata(
+      db, "main", "t", "a", zDataType, zCollSeq,
+      bNotNull, bPrimaryKey, bAutoinc);
+    affirm( 0==rc );
+    affirm( bPrimaryKey.value );
+    affirm( !bAutoinc.value );
+    affirm( bNotNull.value );
+    affirm( "noCase".equals(zCollSeq.value) );
+    affirm( "duck".equals(zDataType.value) );
+    sqlite3_close_v2(db);
+  }
+
   @ManualTest /* we really only want to run this test manually. */
   private void testSleep(){
     out("Sleeping briefly... ");
@@ -1618,6 +1643,7 @@ public class Tester1 implements Runnable {
     if( doSomethingForDev ){
       sqlite3_jni_internal_details();
     }
+    affirm( 0==sqlite3_release_memory(1) );
     sqlite3_shutdown();
     int nMethods = 0;
     int nNatives = 0;
index 0b380a8a0e4a10368858e31f24a2d507a2e7b37a..4eefbd21f57394de460499bd67ec79fe6f1d397a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Use\smutexes\sto\smake\ssqlite3_set_clientdata()\sand\ssqlite3_get_clientdata()\nthreadsafe.
-D 2023-08-30T18:51:26.770
+C Export\ssqlite3_(db_)free_memory()\sand\ssqlite3_table_column_metadata()\sto\sJNI.\sFurther\sinternals\srenaming\sfor\sconsistency\sand\slegibility.
+D 2023-08-31T14:57:01.532
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -237,8 +237,8 @@ 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 aaec2851258a7d9c9907d8e864a17e055676ec0adb64f335d979fa19674a0cab
-F ext/jni/src/c/sqlite3-jni.h 12e1a5ef5ee1795dc22577c285b4518dfd8aa4af45757f6cb81a555d967bf201
+F ext/jni/src/c/sqlite3-jni.c 2364ccb4445c5f45a4d4b80d28455fe0c547f5529e5bd8610b4ba3811c82cc78
+F ext/jni/src/c/sqlite3-jni.h d9ea1662cefe7d8a2737cdbc55799a7e407cbc2239769f486d8eb33b68b9f133
 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
@@ -255,16 +255,16 @@ 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/OutputPointer.java 4ae06135decef35eb04498daa2868939d91a294e948747c580ef9ce31563a6b3
+F ext/jni/src/org/sqlite/jni/OutputPointer.java 1f2319976fff206f5056eafc8a4f48d43abe09d5d1b5287ba9145a95d847cbb7
 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 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 440d64e8c4cff53bd3c0cc676381212489198302d7f1aaa535712c2d7163cc69
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 548ab462e161118b50a614a8bbc2fc2abd507f9b0632267fc78bdd8926ad0807
 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
-F ext/jni/src/org/sqlite/jni/Tester1.java 21d78aa59bfc5ce5ff242d4bb6f6d2255d162fba8be5859ab87c9201d61433f0
+F ext/jni/src/org/sqlite/jni/Tester1.java 05750f0ea53057de146fce8d06b7314009732de06f1e4843c87aa28ba940d6f7
 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
@@ -2115,8 +2115,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 e7c11d34ee2eebdca4d9db1496bbb4152e4c62745c083ad5e0337733e8d1254e
-R 3f04e2f3da46f99baea85816871f54e6
-U drh
-Z 4a72dec53887e83eac398e1a9fd59cf2
+P 443ea20ddb0f3bf5d77ef59cd4678f0e32d7da328002bb44d6fc080a53a37e29
+R d454ebcc01c88272e67e43cac0f5ee2a
+T *branch * jni-client-data
+T *sym-jni-client-data *
+T -sym-db-client-data * Cancelled\sby\sbranch.
+U stephan
+Z c9e44a2d4b476765181f0fdc6d845bb0
 # Remove this line to create a well-formed Fossil manifest.
index 14a82559d31e55eb190ae4fbf778ce4db580e418..016324c8ae7fbdd0b276d0eb35a0853a2c1e91fb 100644 (file)
@@ -1 +1 @@
-443ea20ddb0f3bf5d77ef59cd4678f0e32d7da328002bb44d6fc080a53a37e29
\ No newline at end of file
+7c86aa3400ed591d38c1828f366f4b5de97954c2b301919d3f06d9c2d3d7d1f2
\ No newline at end of file