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

Completely rework how the JNI sqlite3_open(_v2) and sqlite3_prepare(_vN)() bindings deal with output pointers to give the JNI side full control over the origin of db and stmt handles (necessary for solving chicken/egg situations in auto-extensions and prepare-time trace). Lots of adjacent internal API renaming.

FossilOrigin-Name: 644999caff9db79562d45520d94aaa24ee88c65e397b6fb9c20a4f0e7f84e1a5
This commit is contained in:
stephan
2023-08-06 21:29:13 +00:00
parent af90dcf324
commit d85f9bf6d5
9 changed files with 264 additions and 201 deletions

View File

@ -189,10 +189,10 @@
/** Helpers for extracting pointers from jobjects, noting that the /** Helpers for extracting pointers from jobjects, noting that the
corresponding Java interfaces have already done the type-checking. corresponding Java interfaces have already done the type-checking.
*/ */
#define PtrGet_sqlite3(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3) #define PtrGet_sqlite3(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3)
#define PtrGet_sqlite3_stmt(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_stmt) #define PtrGet_sqlite3_stmt(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_stmt)
#define PtrGet_sqlite3_value(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_value) #define PtrGet_sqlite3_value(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_value)
#define PtrGet_sqlite3_context(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_context) #define PtrGet_sqlite3_context(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_context)
/* Helpers for Java value reference management. */ /* Helpers for Java value reference management. */
static inline jobject new_global_ref(JNIEnv *env, jobject v){ static inline jobject new_global_ref(JNIEnv *env, jobject v){
return v ? (*env)->NewGlobalRef(env, v) : NULL; return v ? (*env)->NewGlobalRef(env, v) : NULL;
@ -215,6 +215,8 @@ static const struct {
const char * const OutputPointer_Int64; const char * const OutputPointer_Int64;
const char * const OutputPointer_String; const char * const OutputPointer_String;
const char * const OutputPointer_ByteArray; const char * const OutputPointer_ByteArray;
const char * const OutputPointer_sqlite3;
const char * const OutputPointer_sqlite3_stmt;
#ifdef SQLITE_ENABLE_FTS5 #ifdef SQLITE_ENABLE_FTS5
const char * const Fts5Context; const char * const Fts5Context;
const char * const Fts5ExtensionApi; const char * const Fts5ExtensionApi;
@ -231,6 +233,8 @@ static const struct {
"org/sqlite/jni/OutputPointer$Int64", "org/sqlite/jni/OutputPointer$Int64",
"org/sqlite/jni/OutputPointer$String", "org/sqlite/jni/OutputPointer$String",
"org/sqlite/jni/OutputPointer$ByteArray", "org/sqlite/jni/OutputPointer$ByteArray",
"org/sqlite/jni/OutputPointer$sqlite3",
"org/sqlite/jni/OutputPointer$sqlite3_stmt",
#ifdef SQLITE_ENABLE_FTS5 #ifdef SQLITE_ENABLE_FTS5
"org/sqlite/jni/Fts5Context", "org/sqlite/jni/Fts5Context",
"org/sqlite/jni/Fts5ExtensionApi", "org/sqlite/jni/Fts5ExtensionApi",
@ -296,7 +300,7 @@ static const struct {
#define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL) #define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR) #define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL) #define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define JBA_RELEASE(ARG,VAR) if(ARG) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT) #define JBA_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT)
/* Marker for code which needs(?) to be made thread-safe. */ /* Marker for code which needs(?) to be made thread-safe. */
#define FIXME_THREADING #define FIXME_THREADING
@ -948,7 +952,11 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv * const env, const char *
return freeSlot; return freeSlot;
} }
static jfieldID getNativePointerField(JNIEnv * const env, jclass klazz){ /**
Returns the ID of the "nativePointer" field from the given
NativePointerHolder<T> class.
*/
static jfieldID NativePointerHolder_getField(JNIEnv * const env, jclass klazz){
jfieldID rv = (*env)->GetFieldID(env, klazz, "nativePointer", "J"); jfieldID rv = (*env)->GetFieldID(env, klazz, "nativePointer", "J");
EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field."); EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field.");
return rv; return rv;
@ -959,7 +967,7 @@ static jfieldID getNativePointerField(JNIEnv * const env, jclass klazz){
zClassName must be a static string so we can use its address zClassName must be a static string so we can use its address
as a cache key. as a cache key.
*/ */
static void setNativePointer(JNIEnv * env, jobject ppOut, const void * p, static void NativePointerHolder_set(JNIEnv * env, jobject ppOut, const void * p,
const char *zClassName){ const char *zClassName){
jfieldID setter = 0; jfieldID setter = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName); struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
@ -970,7 +978,7 @@ static void setNativePointer(JNIEnv * env, jobject ppOut, const void * p,
}else{ }else{
jclass const klazz = jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, ppOut); cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, ppOut);
setter = getNativePointerField(env, klazz); setter = NativePointerHolder_getField(env, klazz);
if(cacheLine){ if(cacheLine){
assert(cacheLine->klazz); assert(cacheLine->klazz);
assert(!cacheLine->fidValue); assert(!cacheLine->fidValue);
@ -987,9 +995,8 @@ static void setNativePointer(JNIEnv * env, jobject ppOut, const void * p,
zClassName must be a static string so we can use its address as a zClassName must be a static string so we can use its address as a
cache key. cache key.
*/ */
static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassName){ static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zClassName){
if( 0==pObj ) return 0; if( pObj ){
else{
jfieldID getter = 0; jfieldID getter = 0;
void * rv = 0; void * rv = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName); struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
@ -998,7 +1005,7 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
}else{ }else{
jclass const klazz = jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, pObj); cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, pObj);
getter = getNativePointerField(env, klazz); getter = NativePointerHolder_getField(env, klazz);
if(cacheLine){ if(cacheLine){
assert(cacheLine->klazz); assert(cacheLine->klazz);
assert(zClassName == cacheLine->zClassName); assert(zClassName == cacheLine->zClassName);
@ -1008,6 +1015,8 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
rv = (void*)(*env)->GetLongField(env, pObj, getter); rv = (void*)(*env)->GetLongField(env, pObj, getter);
IFTHREW_REPORT; IFTHREW_REPORT;
return rv; return rv;
}else{
return 0;
} }
} }
@ -1015,7 +1024,7 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
Extracts the new PerDbStateJni instance from the free-list, or Extracts the new PerDbStateJni instance from the free-list, or
allocates one if needed, associats it with pDb, and returns. allocates one if needed, associats it with pDb, and returns.
Returns NULL on OOM. pDb MUST be associated with jDb via Returns NULL on OOM. pDb MUST be associated with jDb via
setNativePointer(). NativePointerHolder_set().
*/ */
static PerDbStateJni * PerDbStateJni_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){ static PerDbStateJni * PerDbStateJni_alloc(JNIEnv * const env, sqlite3 *pDb, jobject jDb){
PerDbStateJni * rv; PerDbStateJni * rv;
@ -1179,7 +1188,7 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx,
} }
/** /**
Common init for setOutputInt32() and friends. zClassName must be a Common init for OutputPointer_set_Int32() and friends. zClassName must be a
pointer from S3ClassNames. jOut must be an instance of that pointer from S3ClassNames. jOut must be an instance of that
class. Fetches the jfieldID for jOut's [value] property, which must class. Fetches the jfieldID for jOut's [value] property, which must
be of the type represented by the JNI type signature zTypeSig, and be of the type represented by the JNI type signature zTypeSig, and
@ -1213,17 +1222,35 @@ static void setupOutputPointer(JNIEnv * const env, const char *zClassName,
/* Sets the value property of the OutputPointer.Int32 jOut object /* Sets the value property of the OutputPointer.Int32 jOut object
to v. */ to v. */
static void setOutputInt32(JNIEnv * const env, jobject const jOut, int v){ static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){
jfieldID setter = 0; jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_Int32, "I", jOut, &setter); setupOutputPointer(env, S3ClassNames.OutputPointer_Int32, "I", jOut, &setter);
(*env)->SetIntField(env, jOut, setter, (jint)v); (*env)->SetIntField(env, jOut, setter, (jint)v);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value"); EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value");
} }
static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut,
jobject jDb){
jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_sqlite3,
"Lorg/sqlite/jni/sqlite3;", jOut, &setter);
(*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, S3ClassNames.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");
}
#ifdef SQLITE_ENABLE_FTS5 #ifdef SQLITE_ENABLE_FTS5
/* Sets the value property of the OutputPointer.Int64 jOut object /* Sets the value property of the OutputPointer.Int64 jOut object
to v. */ to v. */
static void setOutputInt64(JNIEnv * const env, jobject const jOut, jlong v){ static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){
jfieldID setter = 0; jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_Int64, "J", jOut, &setter); setupOutputPointer(env, S3ClassNames.OutputPointer_Int64, "J", jOut, &setter);
(*env)->SetLongField(env, jOut, setter, v); (*env)->SetLongField(env, jOut, setter, v);
@ -1232,7 +1259,7 @@ static void setOutputInt64(JNIEnv * const env, jobject const jOut, jlong v){
#if 0 #if 0
/* Sets the value property of the OutputPointer.ByteArray jOut object /* Sets the value property of the OutputPointer.ByteArray jOut object
to v. */ to v. */
static void setOutputByteArray(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
jbyteArray const v){ jbyteArray const v){
jfieldID setter = 0; jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_ByteArray, "[B", setupOutputPointer(env, S3ClassNames.OutputPointer_ByteArray, "[B",
@ -1243,7 +1270,7 @@ static void setOutputByteArray(JNIEnv * const env, jobject const jOut,
#endif #endif
/* Sets the value property of the OutputPointer.String jOut object /* Sets the value property of the OutputPointer.String jOut object
to v. */ to v. */
static void setOutputString(JNIEnv * const env, jobject const jOut, static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut,
jstring const v){ jstring const v){
jfieldID setter = 0; jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_String, setupOutputPointer(env, S3ClassNames.OutputPointer_String,
@ -1367,7 +1394,7 @@ static void ResultJavaVal_finalizer(void *v){
/** /**
Returns a new Java instance of the class named by zClassName, which Returns a new Java instance of the class named by zClassName, which
MUST be interface-compatible with NativePointerHolder and MUST have MUST be interface-compatible with NativePointerHolder and MUST have
a no-arg constructor. Its setNativePointer() method is passed a no-arg constructor. Its NativePointerHolder_set() method is passed
pNative. Hypothetically returns NULL if Java fails to allocate, but pNative. Hypothetically returns NULL if Java fails to allocate, but
the JNI docs are not entirely clear on that detail. the JNI docs are not entirely clear on that detail.
@ -1402,21 +1429,22 @@ static jobject new_NativePointerHolder_object(JNIEnv * const env, const char *zC
assert(ctor); assert(ctor);
rv = (*env)->NewObject(env, klazz, ctor); rv = (*env)->NewObject(env, klazz, ctor);
EXCEPTION_IS_FATAL("No-arg constructor threw."); EXCEPTION_IS_FATAL("No-arg constructor threw.");
if(rv) setNativePointer(env, rv, pNative, zClassName); if(rv) NativePointerHolder_set(env, rv, pNative, zClassName);
return rv; return rv;
} }
static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){ static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_value, sv); return new_NativePointerHolder_object(env, S3ClassNames.sqlite3, sv);
} }
static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){ static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_context, sv); return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_context, sv);
} }
static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){ static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_stmt, sv); return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_stmt, sv);
} }
static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_value, sv);
}
enum UDFType { enum UDFType {
UDF_SCALAR = 1, UDF_SCALAR = 1,
@ -1690,9 +1718,6 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
#if S3JNI_ENABLE_AUTOEXT #if S3JNI_ENABLE_AUTOEXT
/* auto-extension is very incomplete */ /* auto-extension is very incomplete */
static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3, sv);
}
/*static*/ int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr, /*static*/ int s3jni_auto_extension(sqlite3 *pDb, const char **pzErr,
const struct sqlite3_api_routines *pThunk){ const struct sqlite3_api_routines *pThunk){
S3JniAutoExtension const * pAX = S3Global.autoExt.pHead; S3JniAutoExtension const * pAX = S3Global.autoExt.pHead;
@ -1872,7 +1897,7 @@ static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
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(ps) PerDbStateJni_set_aside(ps) if(ps) PerDbStateJni_set_aside(ps)
/* MUST come after close() because of ps->trace. */; /* MUST come after close() because of ps->trace. */;
setNativePointer(env, jDb, 0, S3ClassNames.sqlite3); NativePointerHolder_set(env, jDb, 0, S3ClassNames.sqlite3);
return (jint)rc; return (jint)rc;
} }
@ -2177,7 +2202,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_
int pOut = 0; int pOut = 0;
rc = sqlite3_db_config( ps->pDb, (int)op, &pOut ); rc = sqlite3_db_config( ps->pDb, (int)op, &pOut );
if( 0==rc && jOut ){ if( 0==rc && jOut ){
setOutputInt32(env, jOut, pOut); OutputPointer_set_Int32(env, jOut, pOut);
} }
break; break;
} }
@ -2346,7 +2371,7 @@ JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){
JNIEnvCache * const jc = S3Global_env_cache(env); JNIEnvCache * const jc = S3Global_env_cache(env);
jobject const pPrev = stmt_set_current(jc, jpStmt); jobject const pPrev = stmt_set_current(jc, jpStmt);
rc = sqlite3_finalize(pStmt); rc = sqlite3_finalize(pStmt);
setNativePointer(env, jpStmt, 0, S3ClassNames.sqlite3_stmt); NativePointerHolder_set(env, jpStmt, 0, S3ClassNames.sqlite3_stmt);
(void)stmt_set_current(jc, pPrev); (void)stmt_set_current(jc, pPrev);
} }
return rc; return rc;
@ -2367,16 +2392,19 @@ JDECL(jlong,1last_1insert_1rowid)(JENV_CSELF, jobject jpDb){
is returned, else theRc is returned. In in case, *ppDb is stored in is returned, else theRc is returned. In in case, *ppDb is stored in
jDb's native pointer property (even if it's NULL). jDb's native pointer property (even if it's NULL).
*/ */
static int s3jni_open_post(JNIEnv * const env, sqlite3 **ppDb, jobject jDb, int theRc){ static int s3jni_open_post(JNIEnv * const env, sqlite3 **ppDb, jobject jOut, int theRc){
jobject jDb = 0;
if(*ppDb){ if(*ppDb){
PerDbStateJni * const s = PerDbStateJni_for_db(env, jDb, *ppDb, 1); jDb = new_sqlite3_wrapper(env, *ppDb);
PerDbStateJni * const s = jDb ? PerDbStateJni_for_db(env, jDb, *ppDb, 1) : 0;
if(!s && 0==theRc){ if(!s && 0==theRc){
UNREF_L(jDb);
sqlite3_close(*ppDb); sqlite3_close(*ppDb);
*ppDb = 0; *ppDb = 0;
theRc = SQLITE_NOMEM; theRc = SQLITE_NOMEM;
} }
} }
setNativePointer(env, jDb, *ppDb, S3ClassNames.sqlite3); OutputPointer_set_sqlite3(env, jOut, jDb);
return theRc; return theRc;
} }
@ -2408,53 +2436,72 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
/* Proxy for the sqlite3_prepare[_v2/3]() family. */ /* Proxy for the sqlite3_prepare[_v2/3]() family. */
static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass self, static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass self,
jobject jpDb, jbyteArray baSql, jobject jDb, jbyteArray baSql,
jint nMax, jint prepFlags, jint nMax, jint prepFlags,
jobject jOutStmt, jobject outTail){ jobject jOutStmt, jobject outTail){
sqlite3_stmt * pStmt = 0; sqlite3_stmt * pStmt = 0;
jobject jStmt = 0;
const char * zTail = 0; const char * zTail = 0;
jbyte * const pBuf = JBA_TOC(baSql); jbyte * const pBuf = JBA_TOC(baSql);
JNIEnvCache * const jc = S3Global_env_cache(env); JNIEnvCache * const jc = S3Global_env_cache(env);
jobject const pOldStmt = stmt_set_current(jc, jOutStmt); jobject const pOldStmt = stmt_set_current(jc, 0);
int rc = SQLITE_ERROR; int rc = SQLITE_ERROR;
assert(prepVersion==1 || prepVersion==2 || prepVersion==3); assert(prepVersion==1 || prepVersion==2 || prepVersion==3);
if( !pBuf ){
rc = baSql ? SQLITE_MISUSE : SQLITE_NOMEM;
goto end;
}
jStmt = new_sqlite3_stmt_wrapper(env, 0);
if( !jStmt ){
rc = SQLITE_NOMEM;
goto end;
}
switch( prepVersion ){ switch( prepVersion ){
case 1: rc = sqlite3_prepare(PtrGet_sqlite3(jpDb), (const char *)pBuf, case 1: rc = sqlite3_prepare(PtrGet_sqlite3(jDb), (const char *)pBuf,
(int)nMax, &pStmt, &zTail); (int)nMax, &pStmt, &zTail);
break; break;
case 2: rc = sqlite3_prepare_v2(PtrGet_sqlite3(jpDb), (const char *)pBuf, case 2: rc = sqlite3_prepare_v2(PtrGet_sqlite3(jDb), (const char *)pBuf,
(int)nMax, &pStmt, &zTail); (int)nMax, &pStmt, &zTail);
break; break;
case 3: rc = sqlite3_prepare_v3(PtrGet_sqlite3(jpDb), (const char *)pBuf, case 3: rc = sqlite3_prepare_v3(PtrGet_sqlite3(jDb), (const char *)pBuf,
(int)nMax, (unsigned int)prepFlags, (int)nMax, (unsigned int)prepFlags,
&pStmt, &zTail); &pStmt, &zTail);
break; break;
default: default:
assert(0 && "Invalid prepare() version"); assert(0 && "Invalid prepare() version");
} }
end:
JBA_RELEASE(baSql,pBuf); JBA_RELEASE(baSql,pBuf);
if( 0!=outTail ){ if( 0==rc ){
assert(zTail ? ((void*)zTail>=(void*)pBuf) : 1); if( 0!=outTail ){
assert(zTail ? (((int)((void*)zTail - (void*)pBuf)) >= 0) : 1); /* Noting that pBuf is deallocated now but its address is all we need. */
setOutputInt32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0)); 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));
}
NativePointerHolder_set(env, jStmt, pStmt, S3ClassNames.sqlite3_stmt);
}else{
UNREF_L(jStmt);
jStmt = 0;
} }
setNativePointer(env, jOutStmt, pStmt, S3ClassNames.sqlite3_stmt); OutputPointer_set_sqlite3_stmt(env, jOutStmt, jStmt);
//NativePointerHolder_set(env, jOutStmt, pStmt, S3ClassNames.sqlite3_stmt);
(void)stmt_set_current(jc, pOldStmt); (void)stmt_set_current(jc, pOldStmt);
return (jint)rc; return (jint)rc;
} }
JDECL(jint,1prepare)(JNIEnv * const env, jclass self, jobject jpDb, jbyteArray baSql, JDECL(jint,1prepare)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
jint nMax, jobject jOutStmt, jobject outTail){ jint nMax, jobject jOutStmt, jobject outTail){
return sqlite3_jni_prepare_v123(1, env, self, jpDb, baSql, nMax, 0, return sqlite3_jni_prepare_v123(1, env, self, jDb, baSql, nMax, 0,
jOutStmt, outTail); jOutStmt, outTail);
} }
JDECL(jint,1prepare_1v2)(JNIEnv * const env, jclass self, jobject jpDb, jbyteArray baSql, JDECL(jint,1prepare_1v2)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
jint nMax, jobject jOutStmt, jobject outTail){ jint nMax, jobject jOutStmt, jobject outTail){
return sqlite3_jni_prepare_v123(2, env, self, jpDb, baSql, nMax, 0, return sqlite3_jni_prepare_v123(2, env, self, jDb, baSql, nMax, 0,
jOutStmt, outTail); jOutStmt, outTail);
} }
JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jpDb, jbyteArray baSql, JDECL(jint,1prepare_1v3)(JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail){ jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail){
return sqlite3_jni_prepare_v123(3, env, self, jpDb, baSql, nMax, return sqlite3_jni_prepare_v123(3, env, self, jDb, baSql, nMax,
prepFlags, jOutStmt, outTail); prepFlags, jOutStmt, outTail);
} }
@ -2797,6 +2844,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
case SQLITE_TRACE_STMT: case SQLITE_TRACE_STMT:
jX = s3jni_utf8_to_jstring(jc, (const char *)pX, -1); jX = s3jni_utf8_to_jstring(jc, (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));*/
jP = jc->currentStmt; jP = jc->currentStmt;
break; break;
case SQLITE_TRACE_PROFILE: case SQLITE_TRACE_PROFILE:
@ -2806,15 +2854,13 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
// MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX))); // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX)));
jP = jc->currentStmt; jP = jc->currentStmt;
if(!jP){ if(!jP){
// This will be the case during prepare() b/c we don't have the
// pointer in time to wrap it before tracing is triggered.
jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP); jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
if(!jP){ if(!jP){
UNREF_L(jX); UNREF_L(jX);
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
MARKER(("WARNING: created new sqlite3_stmt wrapper for TRACE_PROFILE. stmt@%p\n"
"This means we have missed a route into the tracing API and it "
"needs the stmt_set_current() treatment which is littered around "
"a handful of other functions in this file.\n", pP));
} }
break; break;
case SQLITE_TRACE_ROW: case SQLITE_TRACE_ROW:
@ -3092,10 +3138,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){
JNIEXPORT ReturnType JNICALL \ JNIEXPORT ReturnType JNICALL \
JFuncNameFtsTok(Suffix) JFuncNameFtsTok(Suffix)
#define PtrGet_fts5_api(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_api) #define PtrGet_fts5_api(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.fts5_api)
#define PtrGet_fts5_tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_tokenizer) #define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.fts5_tokenizer)
#define PtrGet_Fts5Context(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Context) #define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.Fts5Context)
#define PtrGet_Fts5Tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Tokenizer) #define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.Fts5Tokenizer)
#define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext() #define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
/** /**
@ -3226,7 +3272,7 @@ JDECLFtsXA(jint,xColumnSize)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOut32)
Fts5ExtDecl; Fts5ExtDecl;
int n1 = 0; int n1 = 0;
int const rc = fext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1); int const rc = fext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1);
if( 0==rc ) setOutputInt32(env, jOut32, n1); if( 0==rc ) OutputPointer_set_Int32(env, jOut32, n1);
return rc; return rc;
} }
@ -3242,7 +3288,7 @@ JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol,
jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0; jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0;
if( pz ){ if( pz ){
if( jstr ){ if( jstr ){
setOutputString(env, jOut, jstr); OutputPointer_set_String(env, jOut, jstr);
UNREF_L(jstr)/*jOut has a reference*/; UNREF_L(jstr)/*jOut has a reference*/;
}else{ }else{
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
@ -3256,7 +3302,7 @@ JDECLFtsXA(jint,xColumnTotalSize)(JENV_OSELF,jobject jCtx, jint iCol, jobject jO
Fts5ExtDecl; Fts5ExtDecl;
sqlite3_int64 nOut = 0; sqlite3_int64 nOut = 0;
int const rc = fext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut); int const rc = fext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut);
if( 0==rc && jOut64 ) setOutputInt64(env, jOut64, (jlong)nOut); if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
return (jint)rc; return (jint)rc;
} }
@ -3372,9 +3418,9 @@ JDECLFtsXA(jint,xInst)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOutPhrase,
int n1 = 0, n2 = 2, n3 = 0; int n1 = 0, n2 = 2, n3 = 0;
int const rc = fext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3); int const rc = fext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3);
if( 0==rc ){ if( 0==rc ){
setOutputInt32(env, jOutPhrase, n1); OutputPointer_set_Int32(env, jOutPhrase, n1);
setOutputInt32(env, jOutCol, n2); OutputPointer_set_Int32(env, jOutCol, n2);
setOutputInt32(env, jOutOff, n3); OutputPointer_set_Int32(env, jOutOff, n3);
} }
return rc; return rc;
} }
@ -3383,7 +3429,7 @@ JDECLFtsXA(jint,xInstCount)(JENV_OSELF,jobject jCtx, jobject jOut32){
Fts5ExtDecl; Fts5ExtDecl;
int nOut = 0; int nOut = 0;
int const rc = fext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut); int const rc = fext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut);
if( 0==rc && jOut32 ) setOutputInt32(env, jOut32, nOut); if( 0==rc && jOut32 ) OutputPointer_set_Int32(env, jOut32, nOut);
return (jint)rc; return (jint)rc;
} }
@ -3442,8 +3488,8 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase,
rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase, rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase,
&iter, &iCol, &iOff); &iter, &iCol, &iOff);
if( 0==rc ){ if( 0==rc ){
setOutputInt32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutCol, iCol);
setOutputInt32(env, jOutOff, iOff); OutputPointer_set_Int32(env, jOutOff, iOff);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter); s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
} }
return rc; return rc;
@ -3459,7 +3505,7 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase,
rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase, rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase,
&iter, &iCol); &iter, &iCol);
if( 0==rc ){ if( 0==rc ){
setOutputInt32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutCol, iCol);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter); s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
} }
return rc; return rc;
@ -3475,8 +3521,8 @@ JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter,
s3jni_phraseIter_JToN(env, jc, jIter, &iter); s3jni_phraseIter_JToN(env, jc, jIter, &iter);
fext->xPhraseNext(PtrGet_Fts5Context(jCtx), fext->xPhraseNext(PtrGet_Fts5Context(jCtx),
&iter, &iCol, &iOff); &iter, &iCol, &iOff);
setOutputInt32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutCol, iCol);
setOutputInt32(env, jOutOff, iOff); OutputPointer_set_Int32(env, jOutOff, iOff);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter); s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
} }
@ -3489,7 +3535,7 @@ JDECLFtsXA(void,xPhraseNextColumn)(JENV_OSELF,jobject jCtx, jobject jIter,
if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/; if(!jc->jPhraseIter.klazz) return /*SQLITE_MISUSE*/;
s3jni_phraseIter_JToN(env, jc, jIter, &iter); s3jni_phraseIter_JToN(env, jc, jIter, &iter);
fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol); fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol);
setOutputInt32(env, jOutCol, iCol); OutputPointer_set_Int32(env, jOutCol, iCol);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter); s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
} }
@ -3562,7 +3608,7 @@ JDECLFtsXA(jint,xRowCount)(JENV_OSELF,jobject jCtx, jobject jOut64){
Fts5ExtDecl; Fts5ExtDecl;
sqlite3_int64 nOut = 0; sqlite3_int64 nOut = 0;
int const rc = fext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut); int const rc = fext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut);
if( 0==rc && jOut64 ) setOutputInt64(env, jOut64, (jlong)nOut); if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
return (jint)rc; return (jint)rc;
} }

View File

@ -1104,7 +1104,7 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
* Method: sqlite3_db_config * Method: sqlite3_db_config
* Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/OutputPointer/Int32;)I * Signature: (Lorg/sqlite/jni/sqlite3;ILorg/sqlite/jni/OutputPointer/Int32;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_00024Int32_2 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_Int32_2
(JNIEnv *, jclass, jobject, jint, jobject); (JNIEnv *, jclass, jobject, jint, jobject);
/* /*
@ -1206,7 +1206,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1libversion_1numbe
/* /*
* Class: org_sqlite_jni_SQLite3Jni * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_open * Method: sqlite3_open
* Signature: (Ljava/lang/String;Lorg/sqlite/jni/sqlite3;)I * Signature: (Ljava/lang/String;Lorg/sqlite/jni/OutputPointer/sqlite3;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open
(JNIEnv *, jclass, jstring, jobject); (JNIEnv *, jclass, jstring, jobject);
@ -1214,7 +1214,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open
/* /*
* Class: org_sqlite_jni_SQLite3Jni * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_open_v2 * Method: sqlite3_open_v2
* Signature: (Ljava/lang/String;Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I * Signature: (Ljava/lang/String;Lorg/sqlite/jni/OutputPointer/sqlite3;ILjava/lang/String;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open_1v2 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open_1v2
(JNIEnv *, jclass, jstring, jobject, jint, jstring); (JNIEnv *, jclass, jstring, jobject, jint, jstring);
@ -1222,7 +1222,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open_1v2
/* /*
* Class: org_sqlite_jni_SQLite3Jni * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_prepare * Method: sqlite3_prepare
* Signature: (Lorg/sqlite/jni/sqlite3;[BILorg/sqlite/jni/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I * Signature: (Lorg/sqlite/jni/sqlite3;[BILorg/sqlite/jni/OutputPointer/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare
(JNIEnv *, jclass, jobject, jbyteArray, jint, jobject, jobject); (JNIEnv *, jclass, jobject, jbyteArray, jint, jobject, jobject);
@ -1230,7 +1230,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare
/* /*
* Class: org_sqlite_jni_SQLite3Jni * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_prepare_v2 * Method: sqlite3_prepare_v2
* Signature: (Lorg/sqlite/jni/sqlite3;[BILorg/sqlite/jni/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I * Signature: (Lorg/sqlite/jni/sqlite3;[BILorg/sqlite/jni/OutputPointer/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v2 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v2
(JNIEnv *, jclass, jobject, jbyteArray, jint, jobject, jobject); (JNIEnv *, jclass, jobject, jbyteArray, jint, jobject, jobject);
@ -1238,7 +1238,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v2
/* /*
* Class: org_sqlite_jni_SQLite3Jni * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_prepare_v3 * Method: sqlite3_prepare_v3
* Signature: (Lorg/sqlite/jni/sqlite3;[BIILorg/sqlite/jni/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I * Signature: (Lorg/sqlite/jni/sqlite3;[BIILorg/sqlite/jni/OutputPointer/sqlite3_stmt;Lorg/sqlite/jni/OutputPointer/Int32;)I
*/ */
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v3 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1prepare_1v3
(JNIEnv *, jclass, jobject, jbyteArray, jint, jint, jobject, jobject); (JNIEnv *, jclass, jobject, jbyteArray, jint, jint, jobject, jobject);

View File

@ -22,6 +22,20 @@ package org.sqlite.jni;
autoboxing at that level. autoboxing at that level.
*/ */
public final class OutputPointer { public final class OutputPointer {
public static final class sqlite3 {
private org.sqlite.jni.sqlite3 value;
public sqlite3(){value = null;}
public void clear(){value = null;}
public final org.sqlite.jni.sqlite3 getValue(){return value;}
}
public static final class sqlite3_stmt {
private org.sqlite.jni.sqlite3_stmt value;
public sqlite3_stmt(){value = null;}
public void clear(){value = null;}
public final org.sqlite.jni.sqlite3_stmt getValue(){return value;}
}
public static final class Int32 { public static final class Int32 {
private int value; private int value;
public Int32(){this(0);} public Int32(){this(0);}

View File

@ -476,10 +476,10 @@ public final class SQLite3Jni {
pass to, e.g., the sqlite3_collation_needed() callback. pass to, e.g., the sqlite3_collation_needed() callback.
*/ */
public static native int sqlite3_open(@Nullable String filename, public static native int sqlite3_open(@Nullable String filename,
@NotNull sqlite3 ppDb); @NotNull OutputPointer.sqlite3 ppDb);
public static native int sqlite3_open_v2(@Nullable String filename, public static native int sqlite3_open_v2(@Nullable String filename,
@NotNull sqlite3 ppDb, @NotNull OutputPointer.sqlite3 ppDb,
int flags, @Nullable String zVfs); int flags, @Nullable String zVfs);
/** /**
@ -503,24 +503,24 @@ public final class SQLite3Jni {
*/ */
private static native int sqlite3_prepare(@NotNull sqlite3 db, private static native int sqlite3_prepare(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, int maxBytes, @NotNull byte[] sqlUtf8, int maxBytes,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset); @Nullable OutputPointer.Int32 pTailOffset);
public static int sqlite3_prepare(@NotNull sqlite3 db, public static int sqlite3_prepare(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset){ @Nullable OutputPointer.Int32 pTailOffset){
return sqlite3_prepare(db, sqlUtf8, sqlUtf8.length, outStmt, pTailOffset); return sqlite3_prepare(db, sqlUtf8, sqlUtf8.length, outStmt, pTailOffset);
} }
public static int sqlite3_prepare(@NotNull sqlite3 db, public static int sqlite3_prepare(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
@NotNull sqlite3_stmt outStmt){ @NotNull OutputPointer.sqlite3_stmt outStmt){
return sqlite3_prepare(db, sqlUtf8, sqlUtf8.length, outStmt, null); return sqlite3_prepare(db, sqlUtf8, sqlUtf8.length, outStmt, null);
} }
public static int sqlite3_prepare(@NotNull sqlite3 db, @NotNull String sql, public static int sqlite3_prepare(@NotNull sqlite3 db, @NotNull String sql,
@NotNull sqlite3_stmt outStmt){ @NotNull OutputPointer.sqlite3_stmt outStmt){
final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8); final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8);
return sqlite3_prepare(db, utf8, utf8.length, outStmt, null); return sqlite3_prepare(db, utf8, utf8.length, outStmt, null);
} }
@ -528,24 +528,24 @@ public final class SQLite3Jni {
private static native int sqlite3_prepare_v2(@NotNull sqlite3 db, private static native int sqlite3_prepare_v2(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
int maxBytes, int maxBytes,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset); @Nullable OutputPointer.Int32 pTailOffset);
public static int sqlite3_prepare_v2(@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, public static int sqlite3_prepare_v2(@NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset){ @Nullable OutputPointer.Int32 pTailOffset){
return sqlite3_prepare_v2(db, sqlUtf8, sqlUtf8.length, outStmt, pTailOffset); return sqlite3_prepare_v2(db, sqlUtf8, sqlUtf8.length, outStmt, pTailOffset);
} }
public static int sqlite3_prepare_v2(@NotNull sqlite3 db, public static int sqlite3_prepare_v2(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
@NotNull sqlite3_stmt outStmt){ @NotNull OutputPointer.sqlite3_stmt outStmt){
return sqlite3_prepare_v2(db, sqlUtf8, sqlUtf8.length, outStmt, null); return sqlite3_prepare_v2(db, sqlUtf8, sqlUtf8.length, outStmt, null);
} }
public static int sqlite3_prepare_v2(@NotNull sqlite3 db, public static int sqlite3_prepare_v2(@NotNull sqlite3 db,
@NotNull String sql, @NotNull String sql,
@NotNull sqlite3_stmt outStmt){ @NotNull OutputPointer.sqlite3_stmt outStmt){
final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8); final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8);
return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null); return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null);
} }
@ -553,12 +553,12 @@ public final class SQLite3Jni {
private static native int sqlite3_prepare_v3(@NotNull sqlite3 db, private static native int sqlite3_prepare_v3(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
int maxBytes, int prepFlags, int maxBytes, int prepFlags,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset); @Nullable OutputPointer.Int32 pTailOffset);
public static int sqlite3_prepare_v3(@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, public static int sqlite3_prepare_v3(@NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
int prepFlags, int prepFlags,
@NotNull sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@Nullable OutputPointer.Int32 pTailOffset){ @Nullable OutputPointer.Int32 pTailOffset){
return sqlite3_prepare_v3(db, sqlUtf8, sqlUtf8.length, prepFlags, outStmt, pTailOffset); return sqlite3_prepare_v3(db, sqlUtf8, sqlUtf8.length, prepFlags, outStmt, pTailOffset);
} }
@ -566,12 +566,13 @@ public final class SQLite3Jni {
public static int sqlite3_prepare_v3(@NotNull sqlite3 db, public static int sqlite3_prepare_v3(@NotNull sqlite3 db,
@NotNull byte[] sqlUtf8, @NotNull byte[] sqlUtf8,
int prepFlags, int prepFlags,
@NotNull sqlite3_stmt outStmt){ @NotNull OutputPointer.sqlite3_stmt outStmt){
return sqlite3_prepare_v3(db, sqlUtf8, sqlUtf8.length, prepFlags, outStmt, null); return sqlite3_prepare_v3(db, sqlUtf8, sqlUtf8.length, prepFlags, outStmt, null);
} }
public static int sqlite3_prepare_v3(@NotNull sqlite3 db, @NotNull String sql, public static int sqlite3_prepare_v3(@NotNull sqlite3 db, @NotNull String sql,
int prepFlags, @NotNull sqlite3_stmt outStmt){ int prepFlags,
@NotNull OutputPointer.sqlite3_stmt outStmt){
final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8); final byte[] utf8 = sql.getBytes(StandardCharsets.UTF_8);
return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null); return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null);
} }

View File

@ -22,6 +22,8 @@ public class Tester1 {
} }
static final Metrics metrics = new Metrics(); static final Metrics metrics = new Metrics();
private static final OutputPointer.sqlite3_stmt outStmt
= new OutputPointer.sqlite3_stmt();
public static <T> void out(T val){ public static <T> void out(T val){
System.out.print(val); System.out.print(val);
@ -53,11 +55,11 @@ public class Tester1 {
} }
public static sqlite3 createNewDb(){ public static sqlite3 createNewDb(){
sqlite3 db = new sqlite3(); final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
affirm(0 == db.getNativePointer()); int rc = sqlite3_open(":memory:", out);
int rc = sqlite3_open(":memory:", db);
++metrics.dbOpen; ++metrics.dbOpen;
affirm(0 == rc); affirm(0 == rc);
sqlite3 db = out.getValue();
affirm(0 != db.getNativePointer()); affirm(0 != db.getNativePointer());
rc = sqlite3_busy_timeout(db, 2000); rc = sqlite3_busy_timeout(db, 2000);
affirm( 0 == rc ); affirm( 0 == rc );
@ -69,52 +71,63 @@ public class Tester1 {
} }
public static int execSql(sqlite3 db, boolean throwOnError, String sql){ public static int execSql(sqlite3 db, boolean throwOnError, String sql){
OutputPointer.Int32 oTail = new OutputPointer.Int32(); OutputPointer.Int32 oTail = new OutputPointer.Int32();
final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8); final byte[] sqlUtf8 = sql.getBytes(StandardCharsets.UTF_8);
int pos = 0, n = 1; int pos = 0, n = 1;
byte[] sqlChunk = sqlUtf8; byte[] sqlChunk = sqlUtf8;
sqlite3_stmt stmt = new sqlite3_stmt(); int rc = 0;
int rc = 0; sqlite3_stmt stmt = null;
while(pos < sqlChunk.length){ while(pos < sqlChunk.length){
if(pos > 0){ if(pos > 0){
sqlChunk = Arrays.copyOfRange(sqlChunk, pos, sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
sqlChunk.length); sqlChunk.length);
} }
if( 0==sqlChunk.length ) break; if( 0==sqlChunk.length ) break;
rc = sqlite3_prepare_v2(db, sqlChunk, stmt, oTail); rc = sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
if(throwOnError) affirm(0 == rc); if(throwOnError) affirm(0 == rc);
else if( 0!=rc ) break; else if( 0!=rc ) break;
pos = oTail.getValue(); stmt = outStmt.getValue();
affirm(0 != stmt.getNativePointer()); pos = oTail.getValue();
while( SQLITE_ROW == (rc = sqlite3_step(stmt)) ){ affirm(0 != stmt.getNativePointer());
} while( SQLITE_ROW == (rc = sqlite3_step(stmt)) ){
sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer());
if(0!=rc && SQLITE_ROW!=rc && SQLITE_DONE!=rc){
if(throwOnError){
throw new RuntimeException("db op failed with rc="+rc);
}else{
break;
}
}
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
if(SQLITE_ROW==rc || SQLITE_DONE==rc) rc = 0; affirm(0 == stmt.getNativePointer());
return rc; if(0!=rc && SQLITE_ROW!=rc && SQLITE_DONE!=rc){
if(throwOnError){
throw new RuntimeException("db op failed with rc="+rc);
}else{
break;
}
}
}
sqlite3_finalize(stmt);
if(SQLITE_ROW==rc || SQLITE_DONE==rc) rc = 0;
return rc;
} }
public static void execSql(sqlite3 db, String sql){ public static void execSql(sqlite3 db, String sql){
execSql(db, true, sql); execSql(db, true, sql);
} }
public static sqlite3_stmt prepare(sqlite3 db, String sql){
outStmt.clear();
int rc = sqlite3_prepare(db, sql, outStmt);
affirm( 0 == rc );
final sqlite3_stmt rv = outStmt.getValue();
outStmt.clear();
affirm( 0 != rv.getNativePointer() );
return rv;
}
private static void testOpenDb1(){ private static void testOpenDb1(){
sqlite3 db = new sqlite3(); final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
affirm(0 == db.getNativePointer()); int rc = sqlite3_open(":memory:", out);
int rc = sqlite3_open(":memory:", db); ++metrics.dbOpen;
++metrics.dbOpen; sqlite3 db = out.getValue();
affirm(0 == rc); affirm(0 == rc);
affirm(0 < db.getNativePointer()); affirm(0 < db.getNativePointer());
sqlite3_close_v2(db); sqlite3_close_v2(db);
affirm(0 == db.getNativePointer()); affirm(0 == db.getNativePointer());
} }
@ -130,13 +143,13 @@ public class Tester1 {
} }
private static void testOpenDb2(){ private static void testOpenDb2(){
sqlite3 db = new sqlite3(); final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
affirm(0 == db.getNativePointer()); int rc = sqlite3_open_v2(":memory:", out,
int rc = sqlite3_open_v2(":memory:", db,
SQLITE_OPEN_READWRITE SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE, null); | SQLITE_OPEN_CREATE, null);
++metrics.dbOpen; ++metrics.dbOpen;
affirm(0 == rc); affirm(0 == rc);
sqlite3 db = out.getValue();
affirm(0 < db.getNativePointer()); affirm(0 < db.getNativePointer());
sqlite3_close_v2(db); sqlite3_close_v2(db);
affirm(0 == db.getNativePointer()); affirm(0 == db.getNativePointer());
@ -145,10 +158,9 @@ public class Tester1 {
private static void testPrepare123(){ private static void testPrepare123(){
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
int rc; int rc;
sqlite3_stmt stmt = new sqlite3_stmt(); rc = sqlite3_prepare(db, "CREATE TABLE t1(a);", outStmt);
affirm(0 == stmt.getNativePointer());
rc = sqlite3_prepare(db, "CREATE TABLE t1(a);", stmt);
affirm(0 == rc); affirm(0 == rc);
sqlite3_stmt stmt = outStmt.getValue();
affirm(0 != stmt.getNativePointer()); affirm(0 != stmt.getNativePointer());
rc = sqlite3_step(stmt); rc = sqlite3_step(stmt);
affirm(SQLITE_DONE == rc); affirm(SQLITE_DONE == rc);
@ -169,8 +181,9 @@ public class Tester1 {
} }
//outln("SQL chunk #"+n+" length = "+sqlChunk.length+", pos = "+pos); //outln("SQL chunk #"+n+" length = "+sqlChunk.length+", pos = "+pos);
if( 0==sqlChunk.length ) break; if( 0==sqlChunk.length ) break;
rc = sqlite3_prepare_v2(db, sqlChunk, stmt, oTail); rc = sqlite3_prepare_v2(db, sqlChunk, outStmt, oTail);
affirm(0 == rc); affirm(0 == rc);
stmt = outStmt.getValue();
pos = oTail.getValue(); pos = oTail.getValue();
/*outln("SQL tail pos = "+pos+". Chunk = "+ /*outln("SQL tail pos = "+pos+". Chunk = "+
(new String(Arrays.copyOfRange(sqlChunk,0,pos), (new String(Arrays.copyOfRange(sqlChunk,0,pos),
@ -192,8 +205,9 @@ public class Tester1 {
rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)", rc = sqlite3_prepare_v3(db, "INSERT INTO t2(a) VALUES(1),(2),(3)",
SQLITE_PREPARE_NORMALIZE, stmt); SQLITE_PREPARE_NORMALIZE, outStmt);
affirm(0 == rc); affirm(0 == rc);
stmt = outStmt.getValue();
affirm(0 != stmt.getNativePointer()); affirm(0 != stmt.getNativePointer());
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer() ); affirm(0 == stmt.getNativePointer() );
@ -204,9 +218,7 @@ public class Tester1 {
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)"); execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(:a);");
int rc = sqlite3_prepare(db, "INSERT INTO t(a) VALUES(:a);", stmt);
affirm(0 == rc);
affirm(1 == sqlite3_bind_parameter_count(stmt)); affirm(1 == sqlite3_bind_parameter_count(stmt));
final int paramNdx = sqlite3_bind_parameter_index(stmt, ":a"); final int paramNdx = sqlite3_bind_parameter_index(stmt, ":a");
affirm(1 == paramNdx); affirm(1 == paramNdx);
@ -216,6 +228,7 @@ public class Tester1 {
int changesT = sqlite3_total_changes(db); int changesT = sqlite3_total_changes(db);
long changes64 = sqlite3_changes64(db); long changes64 = sqlite3_changes64(db);
long changesT64 = sqlite3_total_changes64(db); long changesT64 = sqlite3_total_changes64(db);
int rc;
for(int i = 99; i < 102; ++i ){ for(int i = 99; i < 102; ++i ){
total1 += i; total1 += i;
rc = sqlite3_bind_int(stmt, paramNdx, i); rc = sqlite3_bind_int(stmt, paramNdx, i);
@ -233,8 +246,7 @@ public class Tester1 {
affirm(sqlite3_total_changes(db) > changesT); affirm(sqlite3_total_changes(db) > changesT);
affirm(sqlite3_changes64(db) > changes64); affirm(sqlite3_changes64(db) > changes64);
affirm(sqlite3_total_changes64(db) > changesT64); affirm(sqlite3_total_changes64(db) > changesT64);
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a DESC;", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm(0 == rc);
int total2 = 0; int total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int(stmt, 0); total2 += sqlite3_column_int(stmt, 0);
@ -252,8 +264,7 @@ public class Tester1 {
private static void testBindFetchInt64(){ private static void testBindFetchInt64(){
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)"); execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
int rc = sqlite3_prepare(db, "INSERT INTO t(a) VALUES(?);", stmt);
long total1 = 0; long total1 = 0;
for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){ for(long i = 0xffffffff; i < 0xffffffff + 3; ++i ){
total1 += i; total1 += i;
@ -262,8 +273,7 @@ public class Tester1 {
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a DESC;", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm(0 == rc);
long total2 = 0; long total2 = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
total2 += sqlite3_column_int64(stmt, 0); total2 += sqlite3_column_int64(stmt, 0);
@ -276,8 +286,7 @@ public class Tester1 {
private static void testBindFetchDouble(){ private static void testBindFetchDouble(){
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)"); execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
int rc = sqlite3_prepare(db, "INSERT INTO t(a) VALUES(?);", stmt);
double total1 = 0; double total1 = 0;
for(double i = 1.5; i < 5.0; i = i + 1.0 ){ for(double i = 1.5; i < 5.0; i = i + 1.0 ){
total1 += i; total1 += i;
@ -286,8 +295,7 @@ public class Tester1 {
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a DESC;", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm(0 == rc);
double total2 = 0; double total2 = 0;
int counter = 0; int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
@ -303,9 +311,9 @@ public class Tester1 {
private static void testBindFetchText(){ private static void testBindFetchText(){
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)"); execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
int rc = sqlite3_prepare(db, "INSERT INTO t(a) VALUES(?);", stmt);
String[] list1 = { "hell🤩", "w😃rld", "!" }; String[] list1 = { "hell🤩", "w😃rld", "!" };
int rc;
for( String e : list1 ){ for( String e : list1 ){
rc = sqlite3_bind_text(stmt, 1, e); rc = sqlite3_bind_text(stmt, 1, e);
affirm(0 == rc); affirm(0 == rc);
@ -314,8 +322,7 @@ public class Tester1 {
sqlite3_reset(stmt); sqlite3_reset(stmt);
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a DESC;", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm(0 == rc);
StringBuilder sbuf = new StringBuilder(); StringBuilder sbuf = new StringBuilder();
int n = 0; int n = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
@ -333,15 +340,14 @@ public class Tester1 {
private static void testBindFetchBlob(){ private static void testBindFetchBlob(){
sqlite3 db = createNewDb(); sqlite3 db = createNewDb();
execSql(db, "CREATE TABLE t(a)"); execSql(db, "CREATE TABLE t(a)");
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
int rc = sqlite3_prepare(db, "INSERT INTO t(a) VALUES(?);", stmt);
byte[] list1 = { 0x32, 0x33, 0x34 }; byte[] list1 = { 0x32, 0x33, 0x34 };
rc = sqlite3_bind_blob(stmt, 1, list1); int rc = sqlite3_bind_blob(stmt, 1, list1);
affirm( 0==rc );
rc = sqlite3_step(stmt); rc = sqlite3_step(stmt);
affirm(SQLITE_DONE == rc); affirm(SQLITE_DONE == rc);
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a DESC;", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
affirm(0 == rc);
int n = 0; int n = 0;
int total = 0; int total = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
@ -398,9 +404,7 @@ public class Tester1 {
affirm( 0 == rc ); affirm( 0 == rc );
rc = sqlite3_collation_needed(db, collLoader); rc = sqlite3_collation_needed(db, collLoader);
affirm( 0 == rc /* Installing the same object again is a no-op */); affirm( 0 == rc /* Installing the same object again is a no-op */);
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "SELECT a FROM t ORDER BY a COLLATE reversi");
rc = sqlite3_prepare(db, "SELECT a FROM t ORDER BY a COLLATE reversi", stmt);
affirm( 0 == rc );
int counter = 0; int counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
final String val = sqlite3_column_text(stmt, 0); final String val = sqlite3_column_text(stmt, 0);
@ -414,7 +418,7 @@ public class Tester1 {
} }
affirm(3 == counter); affirm(3 == counter);
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
sqlite3_prepare(db, "SELECT a FROM t ORDER BY a", stmt); stmt = prepare(db, "SELECT a FROM t ORDER BY a");
counter = 0; counter = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
final String val = sqlite3_column_text(stmt, 0); final String val = sqlite3_column_text(stmt, 0);
@ -479,9 +483,7 @@ public class Tester1 {
int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func); int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func);
affirm(0 == rc); affirm(0 == rc);
affirm(0 == xFuncAccum.value); affirm(0 == xFuncAccum.value);
final sqlite3_stmt stmt = new sqlite3_stmt(); final sqlite3_stmt stmt = prepare(db, "SELECT myfunc(1,2,3)");
rc = sqlite3_prepare(db, "SELECT myfunc(1,2,3)", stmt);
affirm( 0==rc );
int n = 0; int n = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
affirm( 6 == sqlite3_column_int(stmt, 0) ); affirm( 6 == sqlite3_column_int(stmt, 0) );
@ -498,15 +500,14 @@ public class Tester1 {
private static void testUdfJavaObject(){ private static void testUdfJavaObject(){
final sqlite3 db = createNewDb(); final sqlite3 db = createNewDb();
final ValueHolder<sqlite3> testResult = new ValueHolder<>(db); final ValueHolder<sqlite3> testResult = new ValueHolder<>(db);
SQLFunction func = new SQLFunction.Scalar(){ final SQLFunction func = new SQLFunction.Scalar(){
public void xFunc(sqlite3_context cx, sqlite3_value args[]){ public void xFunc(sqlite3_context cx, sqlite3_value args[]){
sqlite3_result_java_object(cx, testResult.value); sqlite3_result_java_object(cx, testResult.value);
} }
}; };
int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func); int rc = sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func);
affirm(0 == rc); affirm(0 == rc);
sqlite3_stmt stmt = new sqlite3_stmt(); final sqlite3_stmt stmt = prepare(db, "select myfunc()");
sqlite3_prepare(db, "select myfunc()", stmt);
affirm( 0 != stmt.getNativePointer() ); affirm( 0 != stmt.getNativePointer() );
affirm( testResult.value == db ); affirm( testResult.value == db );
int n = 0; int n = 0;
@ -550,9 +551,7 @@ public class Tester1 {
execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES(1),(2),(3)"); execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES(1),(2),(3)");
int rc = sqlite3_create_function(db, "myfunc", 1, SQLITE_UTF8, func); int rc = sqlite3_create_function(db, "myfunc", 1, SQLITE_UTF8, func);
affirm(0 == rc); affirm(0 == rc);
sqlite3_stmt stmt = new sqlite3_stmt(); sqlite3_stmt stmt = prepare(db, "select myfunc(a), myfunc(a+10) from t");
sqlite3_prepare(db, "select myfunc(a), myfunc(a+10) from t", stmt);
affirm( 0 != stmt.getNativePointer() );
int n = 0; int n = 0;
if( SQLITE_ROW == sqlite3_step(stmt) ){ if( SQLITE_ROW == sqlite3_step(stmt) ){
final int v = sqlite3_column_int(stmt, 0); final int v = sqlite3_column_int(stmt, 0);
@ -571,9 +570,7 @@ public class Tester1 {
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
affirm( 1==n ); affirm( 1==n );
rc = sqlite3_prepare(db, "select myfunc(a), myfunc(a+a) from t order by a", stmt = prepare(db, "select myfunc(a), myfunc(a+a) from t order by a");
stmt);
affirm( 0 == rc );
n = 0; n = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
final int c0 = sqlite3_column_int(stmt, 0); final int c0 = sqlite3_column_int(stmt, 0);
@ -624,12 +621,11 @@ public class Tester1 {
"CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES", "CREATE TEMP TABLE twin(x, y); INSERT INTO twin VALUES",
"('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)" "('a', 4),('b', 5),('c', 3),('d', 8),('e', 1)"
}); });
sqlite3_stmt stmt = new sqlite3_stmt(); final sqlite3_stmt stmt = prepare(db,
rc = sqlite3_prepare(db,
"SELECT x, winsumint(y) OVER ("+ "SELECT x, winsumint(y) OVER ("+
"ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+ "ORDER BY x ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"+
") AS sum_y "+ ") AS sum_y "+
"FROM twin ORDER BY x;", stmt); "FROM twin ORDER BY x;");
affirm( 0 == rc ); affirm( 0 == rc );
int n = 0; int n = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
@ -693,11 +689,12 @@ public class Tester1 {
new Tracer(){ new Tracer(){
public int xCallback(int traceFlag, Object pNative, Object x){ public int xCallback(int traceFlag, Object pNative, Object x){
++counter.value; ++counter.value;
//outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName());
switch(traceFlag){ switch(traceFlag){
case SQLITE_TRACE_STMT: case SQLITE_TRACE_STMT:
affirm(pNative instanceof sqlite3_stmt); affirm(pNative instanceof sqlite3_stmt);
affirm(x instanceof String);
//outln("TRACE_STMT sql = "+x); //outln("TRACE_STMT sql = "+x);
affirm(x instanceof String);
affirm( ((String)x).indexOf(nonBmpChar) > 0 ); affirm( ((String)x).indexOf(nonBmpChar) > 0 );
break; break;
case SQLITE_TRACE_PROFILE: case SQLITE_TRACE_PROFILE:
@ -730,16 +727,18 @@ public class Tester1 {
private static void testBusy(){ private static void testBusy(){
final String dbName = "_busy-handler.db"; final String dbName = "_busy-handler.db";
final sqlite3 db1 = new sqlite3(); final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
final sqlite3 db2 = new sqlite3();
int rc = sqlite3_open(dbName, db1); int rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen; ++metrics.dbOpen;
affirm( 0 == rc ); affirm( 0 == rc );
final sqlite3 db1 = outDb.getValue();
execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)"); execSql(db1, "CREATE TABLE IF NOT EXISTS t(a)");
rc = sqlite3_open(dbName, db2); rc = sqlite3_open(dbName, outDb);
++metrics.dbOpen; ++metrics.dbOpen;
affirm( 0 == rc ); affirm( 0 == rc );
affirm( outDb.getValue() != db1 );
final sqlite3 db2 = outDb.getValue();
rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo");
affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) );
@ -760,11 +759,10 @@ public class Tester1 {
// Force a locked condition... // Force a locked condition...
execSql(db1, "BEGIN EXCLUSIVE"); execSql(db1, "BEGIN EXCLUSIVE");
affirm(!xDestroyed.value); affirm(!xDestroyed.value);
sqlite3_stmt stmt = new sqlite3_stmt(); rc = sqlite3_prepare_v2(db2, "SELECT * from t", outStmt);
rc = sqlite3_prepare(db2, "SELECT * from t", stmt);
affirm( SQLITE_BUSY == rc); affirm( SQLITE_BUSY == rc);
assert( null == outStmt.getValue() );
affirm( 3 == xBusyCalled.value ); affirm( 3 == xBusyCalled.value );
sqlite3_finalize(stmt);
sqlite3_close_v2(db1); sqlite3_close_v2(db1);
affirm(!xDestroyed.value); affirm(!xDestroyed.value);
sqlite3_close_v2(db2); sqlite3_close_v2(db2);

View File

@ -20,4 +20,6 @@ package org.sqlite.jni;
and C via JNI. and C via JNI.
*/ */
public final class sqlite3 extends NativePointerHolder<sqlite3> { public final class sqlite3 extends NativePointerHolder<sqlite3> {
// Only invoked from JNI
private sqlite3(){}
} }

View File

@ -14,4 +14,6 @@
package org.sqlite.jni; package org.sqlite.jni;
public final class sqlite3_value extends NativePointerHolder<sqlite3_value> { public final class sqlite3_value extends NativePointerHolder<sqlite3_value> {
//! Invoked only from JNI.
private sqlite3_value(){}
} }

View File

@ -1,5 +1,5 @@
C Start\sadding\sJNI-side\ssupport\sfor\sauto\sextensions,\sbut\shit\sa\sbrick\swall\swhich\srequires\sslightly\sawkward\ssemantics\schanges\sin\sthe\sJNI\sbindings\sfor\ssqlite3_open(_v2)()\sto\sresolve,\sso\sstash\sthis\s#if'd\sout\sfor\sthe\stime\sbeing. C Completely\srework\show\sthe\sJNI\ssqlite3_open(_v2)\sand\ssqlite3_prepare(_vN)()\sbindings\sdeal\swith\soutput\spointers\sto\sgive\sthe\sJNI\sside\sfull\scontrol\sover\sthe\sorigin\sof\sdb\sand\sstmt\shandles\s(necessary\sfor\ssolving\schicken/egg\ssituations\sin\sauto-extensions\sand\sprepare-time\strace).\sLots\sof\sadjacent\sinternal\sAPI\srenaming.
D 2023-08-06T20:01:30.439 D 2023-08-06T21:29:13.410
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -232,8 +232,8 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 61d9bbc179a49523a142928455b3297779b9c40f25783ecf1538279e426cbc99 F ext/jni/GNUmakefile 61d9bbc179a49523a142928455b3297779b9c40f25783ecf1538279e426cbc99
F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d
F ext/jni/src/c/sqlite3-jni.c e335541e3eac0e337cc22f0b78a040e73c477d41128845ffe4ba8029fc077994 F ext/jni/src/c/sqlite3-jni.c 22f463b0bf4e79ccbc0dcd157e8c419e1a6ab1a88afbd565db818aec2802241e
F ext/jni/src/c/sqlite3-jni.h 2ef601ab7cef00047ef0907e873f8f7bc4bfa6ee510b0435e070eb8ee7b6c6f0 F ext/jni/src/c/sqlite3-jni.h 2108bb9434fe873e08d6388bce102a474b5c6b00c2ea47d8aee257aca2de2a67
F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892 F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c 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/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
@ -246,12 +246,12 @@ 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/Fts5PhraseIter.java 6642beda341c0b1b46af4e2d7f6f9ab03a7aede43277b2c92859176d6bce3be9
F ext/jni/src/org/sqlite/jni/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060 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 9c5d901cce4f7e57c3d623f4e2476f9f79a8eed6e51b2a603f37866018e040ee
F ext/jni/src/org/sqlite/jni/OutputPointer.java fcece068415b804aa7843534addb39059ea2b923a9f5dbe91f4f78f066c77991 F ext/jni/src/org/sqlite/jni/OutputPointer.java 053ea7dbc1234dd70b8948009a52a3f1090403a6fe2ab7b7885b6f08ed26deea
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 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 09ce81c1c637e31c3a830d4c859cce95d65f5e02ff45f8bd1985b3479381bc46
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2ee2d3522ab6bec9337653233f5fb50619120cc5b12ce5deb59035ca2502cdcd F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 3edf79fb7a3cb5eaeeaaa074f51521d6d9a962b0dd1ca88074be4fd075258fd8
F ext/jni/src/org/sqlite/jni/Tester1.java 04c43f3ec93b362fc1c66430a3125067285259cd4fa5afccdb2fa66b691db8d0 F ext/jni/src/org/sqlite/jni/Tester1.java 7ea111e9d52042889f2360e7addfefed4174c1a17dfe6efccf64aaa9a65749cf
F ext/jni/src/org/sqlite/jni/TesterFts5.java cf2d687baafffdeba219b77cf611fd47a0556248820ea794ae3e8259bfbdc5ee F ext/jni/src/org/sqlite/jni/TesterFts5.java cf2d687baafffdeba219b77cf611fd47a0556248820ea794ae3e8259bfbdc5ee
F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d 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/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
@ -259,10 +259,10 @@ F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71
F ext/jni/src/org/sqlite/jni/fts5_api.java 8c6b32455d7f85ee3f7f3e71c148bb3c2106f1d5484017daddfd560dd69d4f66 F ext/jni/src/org/sqlite/jni/fts5_api.java 8c6b32455d7f85ee3f7f3e71c148bb3c2106f1d5484017daddfd560dd69d4f66
F ext/jni/src/org/sqlite/jni/fts5_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c 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 e530b36e6437fcc500e95d5d75fbffe272bdea20d2fac6be2e1336c578fba98b
F ext/jni/src/org/sqlite/jni/sqlite3.java 600c3ddc1ac28ee8f58669fb435fd0d21f2972c652039361fde907d4fe44eb58 F ext/jni/src/org/sqlite/jni/sqlite3.java ff3729426704626a6019d97bfee512a83f253cf43ffeffbd45b238718154df36
F ext/jni/src/org/sqlite/jni/sqlite3_context.java d26573fc7b309228cb49786e9078597d96232257defa955a3425d10897bca810 F ext/jni/src/org/sqlite/jni/sqlite3_context.java d26573fc7b309228cb49786e9078597d96232257defa955a3425d10897bca810
F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 72a0698aeb50a183ad146cd29ee04952abb8c36021f6122656aa5ec20469f6f7 F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 72a0698aeb50a183ad146cd29ee04952abb8c36021f6122656aa5ec20469f6f7
F ext/jni/src/org/sqlite/jni/sqlite3_value.java fd045a09458e0a1b9328b085015b5ca5cc9024e7f91ee299f95da9dfd9a865a7 F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9 F ext/lsm1/Makefile a553b728bba6c11201b795188c5708915cc4290f02b7df6ba7e8c4c943fd5cd9
F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013 F ext/lsm1/Makefile.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013
F ext/lsm1/lsm-test/README 87ea529d2abe615e856d4714bfe8bb185e6c2771b8612aa6298588b7b43e6f86 F ext/lsm1/lsm-test/README 87ea529d2abe615e856d4714bfe8bb185e6c2771b8612aa6298588b7b43e6f86
@ -2082,8 +2082,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 5f56b007704f2aad4cbc6f0ccd1e1f1c974865971f99451352714ee7e077c284 P 77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c
R 55ff516d4ad7789048494bf17442d9b9 R 218b9f82589d0175238ee5cd53ac1842
U stephan U stephan
Z 7da38e0ddc75e18237df446c4596f88f Z f6066ff3c416a47c98afc4ffb5e0f6a5
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c 644999caff9db79562d45520d94aaa24ee88c65e397b6fb9c20a4f0e7f84e1a5