1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Export sqlite3_(db_)free_memory() and sqlite3_table_column_metadata() to JNI. Further internals renaming for consistency and legibility.

FossilOrigin-Name: 7c86aa3400ed591d38c1828f366f4b5de97954c2b301919d3f06d9c2d3d7d1f2
This commit is contained in:
stephan
2023-08-31 14:57:01 +00:00
parent 3823208d5b
commit 0c2ba994d2
7 changed files with 319 additions and 177 deletions

View File

@ -217,7 +217,8 @@
/*
** 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;
}

View 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

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

View 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();

View 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;