1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +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
corresponding Java interfaces have already done the type-checking.
*/
#define PtrGet_sqlite3(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3)
#define PtrGet_sqlite3_stmt(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_stmt)
#define PtrGet_sqlite3_value(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_value)
#define PtrGet_sqlite3_context(OBJ) getNativePointer(env,OBJ,S3ClassNames.sqlite3_context)
#define PtrGet_sqlite3(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3)
#define PtrGet_sqlite3_stmt(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_stmt)
#define PtrGet_sqlite3_value(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_value)
#define PtrGet_sqlite3_context(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.sqlite3_context)
/* Helpers for Java value reference management. */
static inline jobject new_global_ref(JNIEnv *env, jobject v){
return v ? (*env)->NewGlobalRef(env, v) : NULL;
@ -215,6 +215,8 @@ static const struct {
const char * const OutputPointer_Int64;
const char * const OutputPointer_String;
const char * const OutputPointer_ByteArray;
const char * const OutputPointer_sqlite3;
const char * const OutputPointer_sqlite3_stmt;
#ifdef SQLITE_ENABLE_FTS5
const char * const Fts5Context;
const char * const Fts5ExtensionApi;
@ -231,6 +233,8 @@ static const struct {
"org/sqlite/jni/OutputPointer$Int64",
"org/sqlite/jni/OutputPointer$String",
"org/sqlite/jni/OutputPointer$ByteArray",
"org/sqlite/jni/OutputPointer$sqlite3",
"org/sqlite/jni/OutputPointer$sqlite3_stmt",
#ifdef SQLITE_ENABLE_FTS5
"org/sqlite/jni/Fts5Context",
"org/sqlite/jni/Fts5ExtensionApi",
@ -296,7 +300,7 @@ static const struct {
#define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define JBA_RELEASE(ARG,VAR) if(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. */
#define FIXME_THREADING
@ -948,7 +952,11 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv * const env, const char *
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");
EXCEPTION_IS_FATAL("Code maintenance required: missing nativePointer field.");
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
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){
jfieldID setter = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
@ -970,7 +978,7 @@ static void setNativePointer(JNIEnv * env, jobject ppOut, const void * p,
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, ppOut);
setter = getNativePointerField(env, klazz);
setter = NativePointerHolder_getField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
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
cache key.
*/
static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassName){
if( 0==pObj ) return 0;
else{
static void * NativePointerHolder_get(JNIEnv * env, jobject pObj, const char *zClassName){
if( pObj ){
jfieldID getter = 0;
void * rv = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
@ -998,7 +1005,7 @@ static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassNam
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, pObj);
getter = getNativePointerField(env, klazz);
getter = NativePointerHolder_getField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
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);
IFTHREW_REPORT;
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
allocates one if needed, associats it with pDb, and returns.
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){
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
class. Fetches the jfieldID for jOut's [value] property, which must
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
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;
setupOutputPointer(env, S3ClassNames.OutputPointer_Int32, "I", jOut, &setter);
(*env)->SetIntField(env, jOut, setter, (jint)v);
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
/* Sets the value property of the OutputPointer.Int64 jOut object
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;
setupOutputPointer(env, S3ClassNames.OutputPointer_Int64, "J", jOut, &setter);
(*env)->SetLongField(env, jOut, setter, v);
@ -1232,7 +1259,7 @@ static void setOutputInt64(JNIEnv * const env, jobject const jOut, jlong v){
#if 0
/* Sets the value property of the OutputPointer.ByteArray jOut object
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){
jfieldID setter = 0;
setupOutputPointer(env, S3ClassNames.OutputPointer_ByteArray, "[B",
@ -1243,7 +1270,7 @@ static void setOutputByteArray(JNIEnv * const env, jobject const jOut,
#endif
/* Sets the value property of the OutputPointer.String jOut object
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){
jfieldID setter = 0;
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
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
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);
rv = (*env)->NewObject(env, klazz, ctor);
EXCEPTION_IS_FATAL("No-arg constructor threw.");
if(rv) setNativePointer(env, rv, pNative, zClassName);
if(rv) NativePointerHolder_set(env, rv, pNative, zClassName);
return rv;
}
static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3_value, sv);
static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
return new_NativePointerHolder_object(env, S3ClassNames.sqlite3, sv);
}
static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, 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){
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 {
UDF_SCALAR = 1,
@ -1690,9 +1718,6 @@ WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
#if S3JNI_ENABLE_AUTOEXT
/* 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,
const struct sqlite3_api_routines *pThunk){
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);
if(ps) PerDbStateJni_set_aside(ps)
/* MUST come after close() because of ps->trace. */;
setNativePointer(env, jDb, 0, S3ClassNames.sqlite3);
NativePointerHolder_set(env, jDb, 0, S3ClassNames.sqlite3);
return (jint)rc;
}
@ -2177,7 +2202,7 @@ JDECL(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILorg_sqlite_jni_OutputPointer_
int pOut = 0;
rc = sqlite3_db_config( ps->pDb, (int)op, &pOut );
if( 0==rc && jOut ){
setOutputInt32(env, jOut, pOut);
OutputPointer_set_Int32(env, jOut, pOut);
}
break;
}
@ -2346,7 +2371,7 @@ JDECL(jint,1finalize)(JENV_CSELF, jobject jpStmt){
JNIEnvCache * const jc = S3Global_env_cache(env);
jobject const pPrev = stmt_set_current(jc, jpStmt);
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);
}
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
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){
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){
UNREF_L(jDb);
sqlite3_close(*ppDb);
*ppDb = 0;
theRc = SQLITE_NOMEM;
}
}
setNativePointer(env, jDb, *ppDb, S3ClassNames.sqlite3);
OutputPointer_set_sqlite3(env, jOut, jDb);
return theRc;
}
@ -2408,53 +2436,72 @@ JDECL(jint,1open_1v2)(JENV_CSELF, jstring strName,
/* Proxy for the sqlite3_prepare[_v2/3]() family. */
static jint sqlite3_jni_prepare_v123(int prepVersion, JNIEnv * const env, jclass self,
jobject jpDb, jbyteArray baSql,
jobject jDb, jbyteArray baSql,
jint nMax, jint prepFlags,
jobject jOutStmt, jobject outTail){
sqlite3_stmt * pStmt = 0;
jobject jStmt = 0;
const char * zTail = 0;
jbyte * const pBuf = JBA_TOC(baSql);
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;
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 ){
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);
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);
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,
&pStmt, &zTail);
break;
default:
assert(0 && "Invalid prepare() version");
}
end:
JBA_RELEASE(baSql,pBuf);
if( 0!=outTail ){
assert(zTail ? ((void*)zTail>=(void*)pBuf) : 1);
assert(zTail ? (((int)((void*)zTail - (void*)pBuf)) >= 0) : 1);
setOutputInt32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0));
if( 0==rc ){
if( 0!=outTail ){
/* Noting that pBuf is deallocated now but its address is all we need. */
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);
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){
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);
}
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){
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);
}
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){
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);
}
@ -2797,6 +2844,7 @@ static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
case SQLITE_TRACE_STMT:
jX = s3jni_utf8_to_jstring(jc, (const char *)pX, -1);
if(!jX) return SQLITE_NOMEM;
/*MARKER(("TRACE_STMT@%p SQL=%p / %s\n", pP, jX, (const char *)pX));*/
jP = jc->currentStmt;
break;
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)));
jP = jc->currentStmt;
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);
if(!jP){
UNREF_L(jX);
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;
case SQLITE_TRACE_ROW:
@ -3092,10 +3138,10 @@ JDECL(void,1do_1something_1for_1developer)(JENV_CSELF){
JNIEXPORT ReturnType JNICALL \
JFuncNameFtsTok(Suffix)
#define PtrGet_fts5_api(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_api)
#define PtrGet_fts5_tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.fts5_tokenizer)
#define PtrGet_Fts5Context(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Context)
#define PtrGet_Fts5Tokenizer(OBJ) getNativePointer(env,OBJ,S3ClassNames.Fts5Tokenizer)
#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.fts5_api)
#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.fts5_tokenizer)
#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.Fts5Context)
#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(env,OBJ,S3ClassNames.Fts5Tokenizer)
#define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
/**
@ -3226,7 +3272,7 @@ JDECLFtsXA(jint,xColumnSize)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOut32)
Fts5ExtDecl;
int n1 = 0;
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;
}
@ -3242,7 +3288,7 @@ JDECLFtsXA(jint,xColumnText)(JENV_OSELF,jobject jCtx, jint iCol,
jstring jstr = pz ? s3jni_utf8_to_jstring(jc, pz, pn) : 0;
if( pz ){
if( jstr ){
setOutputString(env, jOut, jstr);
OutputPointer_set_String(env, jOut, jstr);
UNREF_L(jstr)/*jOut has a reference*/;
}else{
rc = SQLITE_NOMEM;
@ -3256,7 +3302,7 @@ JDECLFtsXA(jint,xColumnTotalSize)(JENV_OSELF,jobject jCtx, jint iCol, jobject jO
Fts5ExtDecl;
sqlite3_int64 nOut = 0;
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;
}
@ -3372,9 +3418,9 @@ JDECLFtsXA(jint,xInst)(JENV_OSELF,jobject jCtx, jint iIdx, jobject jOutPhrase,
int n1 = 0, n2 = 2, n3 = 0;
int const rc = fext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3);
if( 0==rc ){
setOutputInt32(env, jOutPhrase, n1);
setOutputInt32(env, jOutCol, n2);
setOutputInt32(env, jOutOff, n3);
OutputPointer_set_Int32(env, jOutPhrase, n1);
OutputPointer_set_Int32(env, jOutCol, n2);
OutputPointer_set_Int32(env, jOutOff, n3);
}
return rc;
}
@ -3383,7 +3429,7 @@ JDECLFtsXA(jint,xInstCount)(JENV_OSELF,jobject jCtx, jobject jOut32){
Fts5ExtDecl;
int nOut = 0;
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;
}
@ -3442,8 +3488,8 @@ JDECLFtsXA(jint,xPhraseFirst)(JENV_OSELF,jobject jCtx, jint iPhrase,
rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase,
&iter, &iCol, &iOff);
if( 0==rc ){
setOutputInt32(env, jOutCol, iCol);
setOutputInt32(env, jOutOff, iOff);
OutputPointer_set_Int32(env, jOutCol, iCol);
OutputPointer_set_Int32(env, jOutOff, iOff);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
}
return rc;
@ -3459,7 +3505,7 @@ JDECLFtsXA(jint,xPhraseFirstColumn)(JENV_OSELF,jobject jCtx, jint iPhrase,
rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase,
&iter, &iCol);
if( 0==rc ){
setOutputInt32(env, jOutCol, iCol);
OutputPointer_set_Int32(env, jOutCol, iCol);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
}
return rc;
@ -3475,8 +3521,8 @@ JDECLFtsXA(void,xPhraseNext)(JENV_OSELF,jobject jCtx, jobject jIter,
s3jni_phraseIter_JToN(env, jc, jIter, &iter);
fext->xPhraseNext(PtrGet_Fts5Context(jCtx),
&iter, &iCol, &iOff);
setOutputInt32(env, jOutCol, iCol);
setOutputInt32(env, jOutOff, iOff);
OutputPointer_set_Int32(env, jOutCol, iCol);
OutputPointer_set_Int32(env, jOutOff, iOff);
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*/;
s3jni_phraseIter_JToN(env, jc, jIter, &iter);
fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol);
setOutputInt32(env, jOutCol, iCol);
OutputPointer_set_Int32(env, jOutCol, iCol);
s3jni_phraseIter_NToJ(env, jc, &iter, jIter);
}
@ -3562,7 +3608,7 @@ JDECLFtsXA(jint,xRowCount)(JENV_OSELF,jobject jCtx, jobject jOut64){
Fts5ExtDecl;
sqlite3_int64 nOut = 0;
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;
}

View File

@ -1104,7 +1104,7 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1db_1filename
* Method: sqlite3_db_config
* 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);
/*
@ -1206,7 +1206,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1libversion_1numbe
/*
* Class: org_sqlite_jni_SQLite3Jni
* 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
(JNIEnv *, jclass, jstring, jobject);
@ -1214,7 +1214,7 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1open
/*
* Class: org_sqlite_jni_SQLite3Jni
* 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
(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
* 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
(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
* 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
(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
* 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
(JNIEnv *, jclass, jobject, jbyteArray, jint, jint, jobject, jobject);

View File

@ -22,6 +22,20 @@ package org.sqlite.jni;
autoboxing at that level.
*/
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 {
private int value;
public Int32(){this(0);}

View File

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

View File

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

View File

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