diff options
author | stephan <stephan@noemail.net> | 2023-08-26 21:02:50 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2023-08-26 21:02:50 +0000 |
commit | 506a8b52f393eb1500b16bd5e72371859bd1b14c (patch) | |
tree | a38ae32d5878cf3ceff85dd1a10f62a563145a5b /ext | |
parent | 6428cd18d61279701eac1d39dbf0b4e8fccf90eb (diff) | |
download | sqlite-506a8b52f393eb1500b16bd5e72371859bd1b14c.tar.gz sqlite-506a8b52f393eb1500b16bd5e72371859bd1b14c.zip |
Whether or not OOM is always fatal in JNI is now a compile-time option.
FossilOrigin-Name: 320a34c080d8bc1feae1578697923dfa7c4144b78de36f704c24cc4a4ce9d535
Diffstat (limited to 'ext')
-rw-r--r-- | ext/jni/GNUmakefile | 3 | ||||
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.c | 96 | ||||
-rw-r--r-- | ext/jni/src/c/sqlite3-jni.h | 48 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/AggregateFunction.java | 4 | ||||
-rw-r--r-- | ext/jni/src/org/sqlite/jni/SQLite3Jni.java | 71 |
5 files changed, 128 insertions, 94 deletions
diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 9eb65186a..5f7038f05 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -182,6 +182,7 @@ SQLITE_OPT = \ -DSQLITE_TEMP_STORE=2 \ -DSQLITE_USE_URI=1 \ -DSQLITE_C=$(sqlite3.c) \ + -DSQLITE_JNI_FATAL_OOM=0 \ -DSQLITE_DEBUG SQLITE_OPT += -g -DDEBUG -UNDEBUG @@ -330,7 +331,7 @@ doc: $(doc.index) javadoc: $(doc.index) # Force rebild of docs redoc: - @rm -f $(doc.index)/index.html + @rm -f $(doc.index) @$(MAKE) doc docserve: $(doc.index) cd $(dir.doc) && althttpd -max-age 1 -page index.html diff --git a/ext/jni/src/c/sqlite3-jni.c b/ext/jni/src/c/sqlite3-jni.c index bc776ae16..0c290f9e2 100644 --- a/ext/jni/src/c/sqlite3-jni.c +++ b/ext/jni/src/c/sqlite3-jni.c @@ -75,6 +75,14 @@ //#endif /**********************************************************************/ +/* SQLITE_J... */ +#ifdef SQLITE_JNI_FATAL_OOM +#if !SQLITE_JNI_FATAL_OOM +#undef SQLITE_JNI_FATAL_OOM +#endif +#endif + +/**********************************************************************/ /* SQLITE_M... */ #ifndef SQLITE_MAX_ALLOCATION_SIZE # define SQLITE_MAX_ALLOCATION_SIZE 0x1fffffff @@ -717,15 +725,40 @@ static inline void s3jni_oom(JNIEnv * const env){ ** sqlite3_malloc() proxy which fails fatally on OOM. This should ** only be used for routines which manage global state and have no ** recovery strategy for OOM. For sqlite3 API which can reasonably -** return SQLITE_NOMEM, sqlite3_malloc() should be used instead. +** return SQLITE_NOMEM, s3jni_malloc() should be used instead. +*/ +static void * s3jni_malloc_or_die(JNIEnv * const env, size_t n){ + void * const rv = sqlite3_malloc(n); + if( n && !rv ) s3jni_oom(env); + return rv; +} + +/* +** Works like sqlite3_malloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. */ static void * s3jni_malloc(JNIEnv * const env, size_t n){ void * const rv = sqlite3_malloc(n); +#ifdef SQLITE_JNI_FATAL_OOM if( n && !rv ) s3jni_oom(env); +#endif return rv; } /* +** Works like sqlite3_realloc() unless built with SQLITE_JNI_FATAL_OOM, +** in which case it calls s3jni_oom() on OOM. +*/ +static void * s3jni_realloc(JNIEnv * const env, void * p, size_t n){ + void * const rv = sqlite3_realloc(p, (int)n); +#ifdef SQLITE_JNI_FATAL_OOM + if( n && !rv ) s3jni_oom(env); +#endif + return rv; +} + + +/* ** Returns the current JNIEnv object. Fails fatally if it cannot find ** the object. */ @@ -765,7 +798,7 @@ static S3JniEnv * S3JniEnv_get(JNIEnv * const env){ SJG.envCache.aFree = row->pNext; if( row->pNext ) row->pNext->pPrev = 0; }else{ - row = s3jni_malloc(env, sizeof(S3JniEnv)); + row = s3jni_malloc_or_die(env, sizeof(*row)); s3jni_incr( &SJG.metrics.envCacheAllocs ); } memset(row, 0, sizeof(*row)); @@ -885,7 +918,7 @@ static char * s3jni_jstring_to_utf8(JNIEnv * const env, } nBa = (*env)->GetArrayLength(env, jba); if( nLen ) *nLen = (int)nBa; - rv = sqlite3_malloc( nBa + 1 ); + rv = s3jni_malloc( env, nBa + 1 ); if( rv ){ (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv); rv[nBa] = 0; @@ -1506,7 +1539,7 @@ typedef struct { ** to ResultJavaVal_finalizer(). */ static ResultJavaVal * ResultJavaVal_alloc(JNIEnv * const env, jobject jObj){ - ResultJavaVal * rv = sqlite3_malloc(sizeof(ResultJavaVal)); + ResultJavaVal * const rv = s3jni_malloc(env, sizeof(ResultJavaVal)); if( rv ){ rv->jObj = jObj ? S3JniRefGlobal(jObj) : 0; } @@ -1590,7 +1623,7 @@ static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ } S3JniMutex_Global_leave; if( !s ){ - s = sqlite3_malloc(sizeof(*s)); + s = s3jni_malloc(env, sizeof(*s)); s3jni_incr(&SJG.metrics.nUdfAlloc); } if( s ){ @@ -2012,8 +2045,7 @@ S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ unsigned n = 1 + SJG.autoExt.nAlloc; S3JniAutoExtension * const aNew = - sqlite3_realloc( SJG.autoExt.pExt, - n * sizeof(S3JniAutoExtension) ); + s3jni_realloc( env, SJG.autoExt.pExt, n * sizeof(*ax) ); if( !aNew ){ rc = SQLITE_NOMEM; }else{ @@ -3397,8 +3429,8 @@ S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)( } /* Impl for sqlite3_result_text/blob() and friends. */ -static void result_blob_text(int as64, - int eTextRep/*only for (asBlob=0)*/, +static void result_blob_text(int as64 /* true for text64/blob64() mode */, + int eTextRep /* 0 for blobs, else SQLITE_UTF... */, JNIEnv * const env, sqlite3_context *pCx, jbyteArray jBa, jlong nMax){ int const asBlob = 0==eTextRep; @@ -3425,8 +3457,7 @@ static void result_blob_text(int as64, } if( as64 ){ /* 64-bit... */ static const jsize nLimit64 = - SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary!*/ - /* jsize is int32, not int64! */; + SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary*/; if( nBa > nLimit64 ){ sqlite3_result_error_toobig(pCx); }else if( asBlob ){ @@ -3739,18 +3770,6 @@ static int s3jni_strlike_glob(int isLike, JNIEnv *const env, return rc; } -S3JniApi(sqlite3_strglob(),jint,1strglob)( - JniArgsEnvClass, jbyteArray baG, jbyteArray baT -){ - return s3jni_strlike_glob(0, env, baG, baT, 0); -} - -S3JniApi(sqlite3_strlike(),jint,1strlike)( - JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar -){ - return s3jni_strlike_glob(1, env, baG, baT, escChar); -} - S3JniApi(sqlite3_shutdown(),jint,1shutdown)( JniArgsEnvClass ){ @@ -3776,6 +3795,18 @@ S3JniApi(sqlite3_shutdown(),jint,1shutdown)( return sqlite3_shutdown(); } +S3JniApi(sqlite3_strglob(),jint,1strglob)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT +){ + return s3jni_strlike_glob(0, env, baG, baT, 0); +} + +S3JniApi(sqlite3_strlike(),jint,1strlike)( + JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar +){ + return s3jni_strlike_glob(1, env, baG, baT, escChar); +} + S3JniApi(sqlite3_sql(),jstring,1sql)( JniArgsEnvClass, jobject jpStmt ){ @@ -4113,7 +4144,8 @@ static void Fts5JniAux_xDestroy(void *p){ } static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ - Fts5JniAux * s = sqlite3_malloc(sizeof(Fts5JniAux)); + Fts5JniAux * s = s3jni_malloc(env, sizeof(Fts5JniAux)); + if( s ){ jclass klazz; memset(s, 0, sizeof(Fts5JniAux)); @@ -4146,9 +4178,9 @@ static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){ return new_NativePointerHolder_object(env, &S3NphRefs.fts5_api, sv); } -/** - Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton - instance, or NULL on OOM. +/* +** Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton +** instance, or NULL on OOM. */ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ if( !SJG.fts5.jFtsExt ){ @@ -4168,8 +4200,8 @@ static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){ } /* -** Return a pointer to the fts5_api instance for database connection -** db. If an error occurs, return NULL and leave an error in the +** Returns a pointer to the fts5_api instance for database connection +** db. If an error occurs, returns NULL and leaves an error in the ** database handle (accessible using sqlite3_errcode()/errmsg()). */ static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ @@ -4536,7 +4568,8 @@ JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ Fts5ExtDecl; int rc; S3JniFts5AuxData * pAux; - pAux = sqlite3_malloc(sizeof(*pAux)); + + pAux = s3jni_malloc(env, sizeof(*pAux)); if( !pAux ){ if( jAux ){ /* Emulate how xSetAuxdata() behaves when it cannot alloc @@ -4692,9 +4725,10 @@ static void SQLTester_dup_func( char *z; int n = sqlite3_value_bytes(argv[0]); SQLTesterJni * const p = (SQLTesterJni *)sqlite3_user_data(context); + S3JniDeclLocal_env; ++p->nDup; - if( n>0 && (pOut = sqlite3_malloc( (n+16)&~7 ))!=0 ){ + if( n>0 && (pOut = s3jni_malloc( env, (n+16)&~7 ))!=0 ){ pOut[0] = 0x2bbf4b7c; z = (char*)&pOut[1]; memcpy(z, sqlite3_value_text(argv[0]), n); diff --git a/ext/jni/src/c/sqlite3-jni.h b/ext/jni/src/c/sqlite3-jni.h index 49b4cba17..ed64a4503 100644 --- a/ext/jni/src/c/sqlite3-jni.h +++ b/ext/jni/src/c/sqlite3-jni.h @@ -1533,30 +1533,6 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64 /* * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_shutdown - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown - (JNIEnv *, jclass); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_status - * Signature: (ILorg/sqlite/jni/OutputPointer/Int32;Lorg/sqlite/jni/OutputPointer/Int32;Z)I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status - (JNIEnv *, jclass, jint, jobject, jobject, jboolean); - -/* - * Class: org_sqlite_jni_SQLite3Jni - * Method: sqlite3_status64 - * Signature: (ILorg/sqlite/jni/OutputPointer/Int64;Lorg/sqlite/jni/OutputPointer/Int64;Z)I - */ -JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 - (JNIEnv *, jclass, jint, jobject, jobject, jboolean); - -/* - * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_rollback_hook * Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHookCallback;)Lorg/sqlite/jni/RollbackHookCallback; */ @@ -1581,6 +1557,14 @@ JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1set_1last_1insert /* * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_shutdown + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1shutdown + (JNIEnv *, jclass); + +/* + * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_sleep * Signature: (I)I */ @@ -1605,6 +1589,22 @@ JNIEXPORT jstring JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1sql /* * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_status + * Signature: (ILorg/sqlite/jni/OutputPointer/Int32;Lorg/sqlite/jni/OutputPointer/Int32;Z)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status + (JNIEnv *, jclass, jint, jobject, jobject, jboolean); + +/* + * Class: org_sqlite_jni_SQLite3Jni + * Method: sqlite3_status64 + * Signature: (ILorg/sqlite/jni/OutputPointer/Int64;Lorg/sqlite/jni/OutputPointer/Int64;Z)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64 + (JNIEnv *, jclass, jint, jobject, jobject, jboolean); + +/* + * Class: org_sqlite_jni_SQLite3Jni * Method: sqlite3_step * Signature: (Lorg/sqlite/jni/sqlite3_stmt;)I */ diff --git a/ext/jni/src/org/sqlite/jni/AggregateFunction.java b/ext/jni/src/org/sqlite/jni/AggregateFunction.java index 6b6c84876..033e4dbca 100644 --- a/ext/jni/src/org/sqlite/jni/AggregateFunction.java +++ b/ext/jni/src/org/sqlite/jni/AggregateFunction.java @@ -54,7 +54,7 @@ public abstract class AggregateFunction<T> extends SQLFunction { argument, the context is set to the given initial value. On all other calls, the 2nd argument is ignored. - @see SQLFunction.PerContextState#getAggregateState() + @see SQLFunction.PerContextState#getAggregateState */ protected final ValueHolder<T> getAggregateState(sqlite3_context cx, T initialValue){ return map.getAggregateState(cx, initialValue); @@ -64,7 +64,7 @@ public abstract class AggregateFunction<T> extends SQLFunction { To be called from the implementation's xFinal() method to fetch the final state of the UDF and remove its mapping. - see SQLFunction.PerContextState#takeAggregateState() + see SQLFunction.PerContextState#takeAggregateState */ protected final T takeAggregateState(sqlite3_context cx){ return map.takeAggregateState(cx); diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index 598fa2a01..1dd5979c6 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -893,8 +893,7 @@ public final class SQLite3Jni { a complaint about the invalid argument. */ private static native void sqlite3_result_error( - @NotNull sqlite3_context cx, @NotNull byte[] msg, - int eTextRep + @NotNull sqlite3_context cx, @NotNull byte[] msg, int eTextRep ); public static void sqlite3_result_error( @@ -919,22 +918,20 @@ public final class SQLite3Jni { public static void sqlite3_result_error16( @NotNull sqlite3_context cx, @NotNull String msg ){ - final byte[] utf8 = msg.getBytes(StandardCharsets.UTF_16); - sqlite3_result_error(cx, utf8, SQLITE_UTF16); + final byte[] utf16 = msg.getBytes(StandardCharsets.UTF_16); + sqlite3_result_error(cx, utf16, SQLITE_UTF16); } + /** + Equivalent to passing e.getMessage() to + sqlite3_result_error(db,String). + */ public static void sqlite3_result_error( @NotNull sqlite3_context cx, @NotNull Exception e ){ sqlite3_result_error(cx, e.getMessage()); } - public static void sqlite3_result_error16( - @NotNull sqlite3_context cx, @NotNull Exception e - ){ - sqlite3_result_error16(cx, e.getMessage()); - } - public static native void sqlite3_result_error_toobig( @NotNull sqlite3_context cx ); @@ -961,7 +958,7 @@ public final class SQLite3Jni { /** Binds the SQL result to the given object, or - {@link #sqlite3_result_null(sqlite3_context) sqlite3_result_null()} if {@code o} is null. Use + {@link #sqlite3_result_null} if {@code o} is null. Use {@link #sqlite3_value_java_object(sqlite3_value) sqlite3_value_java_object()} or {@link #sqlite3_column_java_object(sqlite3_stmt,int) sqlite3_column_java_object()} to fetch it. @@ -1077,13 +1074,13 @@ public final class SQLite3Jni { } private static native void sqlite3_result_text( - @NotNull sqlite3_context cx, @Nullable byte[] text, int maxLen + @NotNull sqlite3_context cx, @Nullable byte[] utf8, int maxLen ); public static void sqlite3_result_text( - @NotNull sqlite3_context cx, @Nullable byte[] text + @NotNull sqlite3_context cx, @Nullable byte[] utf8 ){ - sqlite3_result_text(cx, text, null==text ? 0 : text.length); + sqlite3_result_text(cx, utf8, null==utf8 ? 0 : utf8.length); } public static void sqlite3_result_text( @@ -1118,33 +1115,13 @@ public final class SQLite3Jni { ); /** - Cleans up all stale per-thread state managed by the library, as - well as any registered auto-extensions, then calls the C-native - sqlite3_shutdown(). Calling this while database handles or - prepared statements are still active will leak resources. Trying - to use those objects after this routine is called invoked - undefined behavior. - */ - public static synchronized native int sqlite3_shutdown(); - - public static native int sqlite3_status( - int op, @NotNull OutputPointer.Int32 pCurrent, - @NotNull OutputPointer.Int32 pHighwater, boolean reset - ); - - public static native int sqlite3_status64( - int op, @NotNull OutputPointer.Int64 pCurrent, - @NotNull OutputPointer.Int64 pHighwater, boolean reset - ); - - /** Sets the current UDF result to the given bytes, which are assumed be encoded in UTF-16 using the platform's byte order. */ public static void sqlite3_result_text16( - @NotNull sqlite3_context cx, @Nullable byte[] text + @NotNull sqlite3_context cx, @Nullable byte[] utf16 ){ - sqlite3_result_text64(cx, text, text.length, SQLITE_UTF16); + sqlite3_result_text64(cx, utf16, utf16.length, SQLITE_UTF16); } public static void sqlite3_result_text16( @@ -1170,12 +1147,34 @@ public final class SQLite3Jni { @NotNull sqlite3 db, long rowid ); + + /** + Cleans up all stale per-thread state managed by the library, as + well as any registered auto-extensions, then calls the C-native + sqlite3_shutdown(). Calling this while database handles or + prepared statements are still active will leak resources. Trying + to use those objects after this routine is called invoked + undefined behavior. + */ + public static synchronized native int sqlite3_shutdown(); + public static native int sqlite3_sleep(int ms); public static native String sqlite3_sourceid(); public static native String sqlite3_sql(@NotNull sqlite3_stmt stmt); + + public static native int sqlite3_status( + int op, @NotNull OutputPointer.Int32 pCurrent, + @NotNull OutputPointer.Int32 pHighwater, boolean reset + ); + + public static native int sqlite3_status64( + int op, @NotNull OutputPointer.Int64 pCurrent, + @NotNull OutputPointer.Int64 pHighwater, boolean reset + ); + public static native int sqlite3_step(@NotNull sqlite3_stmt stmt); /** |