diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index 6a490404f7..87f530d8a0 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -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 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; } diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index edb75ae04e..287df65558 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -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); diff --git a/ext/jni/src/org/sqlite/jni/OutputPointer.java b/ext/jni/src/org/sqlite/jni/OutputPointer.java index c4fbcb116e..065e133202 100644 --- a/ext/jni/src/org/sqlite/jni/OutputPointer.java +++ b/ext/jni/src/org/sqlite/jni/OutputPointer.java @@ -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);} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index d8f67cd1f9..68bd4e246a 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -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); } diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index d25d879173..e96038d4d8 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -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 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 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); diff --git a/ext/jni/src/org/sqlite/jni/sqlite3.java b/ext/jni/src/org/sqlite/jni/sqlite3.java index 99d06af829..8f4ecb772b 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3.java @@ -20,4 +20,6 @@ package org.sqlite.jni; and C via JNI. */ public final class sqlite3 extends NativePointerHolder { + // Only invoked from JNI + private sqlite3(){} } diff --git a/ext/jni/src/org/sqlite/jni/sqlite3_value.java b/ext/jni/src/org/sqlite/jni/sqlite3_value.java index 8aa8c77bbd..2cfb32ff1a 100644 --- a/ext/jni/src/org/sqlite/jni/sqlite3_value.java +++ b/ext/jni/src/org/sqlite/jni/sqlite3_value.java @@ -14,4 +14,6 @@ package org.sqlite.jni; public final class sqlite3_value extends NativePointerHolder { + //! Invoked only from JNI. + private sqlite3_value(){} } diff --git a/manifest b/manifest index dcdf8f77f9..e68cb542c1 100644 --- a/manifest +++ b/manifest @@ -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. -D 2023-08-06T20:01:30.439 +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-06T21:29:13.410 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -232,8 +232,8 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/jni/GNUmakefile 61d9bbc179a49523a142928455b3297779b9c40f25783ecf1538279e426cbc99 F ext/jni/README.md e965674505e105626127ad45e628e4d19fcd379cdafc4d23c814c1ac2c55681d -F ext/jni/src/c/sqlite3-jni.c e335541e3eac0e337cc22f0b78a040e73c477d41128845ffe4ba8029fc077994 -F ext/jni/src/c/sqlite3-jni.h 2ef601ab7cef00047ef0907e873f8f7bc4bfa6ee510b0435e070eb8ee7b6c6f0 +F ext/jni/src/c/sqlite3-jni.c 22f463b0bf4e79ccbc0dcd157e8c419e1a6ab1a88afbd565db818aec2802241e +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/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c 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/Fts5Tokenizer.java 91489893596b6528c0df5cd7180bd5b55809c26e2b797fb321dfcdbc1298c060 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/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 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/Tester1.java 04c43f3ec93b362fc1c66430a3125067285259cd4fa5afccdb2fa66b691db8d0 +F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 3edf79fb7a3cb5eaeeaaa074f51521d6d9a962b0dd1ca88074be4fd075258fd8 +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/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d 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_extension_function.java ac825035d7d83fc7fd960347abfa6803e1614334a21533302041823ad5fc894c 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_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.msc f8c878b467232226de288da320e1ac71c131f5ec91e08b21f502303347260013 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.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5f56b007704f2aad4cbc6f0ccd1e1f1c974865971f99451352714ee7e077c284 -R 55ff516d4ad7789048494bf17442d9b9 +P 77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c +R 218b9f82589d0175238ee5cd53ac1842 U stephan -Z 7da38e0ddc75e18237df446c4596f88f +Z f6066ff3c416a47c98afc4ffb5e0f6a5 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b8a927308b..101fba0f15 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -77a32d238e80fe1d237768d88780043a7bd2b3543e6672536254782cbea0039c \ No newline at end of file +644999caff9db79562d45520d94aaa24ee88c65e397b6fb9c20a4f0e7f84e1a5 \ No newline at end of file