Index: ext/jni/src/c/sqlite3-jni.c ================================================================== --- ext/jni/src/c/sqlite3-jni.c +++ ext/jni/src/c/sqlite3-jni.c @@ -907,11 +907,11 @@ ** This function is NOT part of the sqlite3 public API. It is strictly ** for use by the sqlite project's own Java/JNI bindings. ** ** For purposes of certain hand-crafted JNI function bindings, we ** need a way of reporting errors which is consistent with the rest of -** the C API, as opposed to throwing JS exceptions. To that end, this +** the C API, as opposed to throwing Java exceptions. To that end, this ** internal-use-only function is a thin proxy around ** sqlite3ErrorWithMessage(). The intent is that it only be used from ** JNI bindings such as sqlite3_prepare_v2/v3(), and definitely not ** from client code. ** @@ -970,10 +970,11 @@ if( 0==n || (n<0 && z && !z[0]) ){ /* Fast-track the empty-string case via the MUTF-8 API. We could hypothetically do this for any strings where n<4 and z is NUL-terminated and none of z[0..3] are NUL bytes. */ rv = (*env)->NewStringUTF(env, ""); + s3jni_oom_check( rv ); }else if( z ){ jbyteArray jba; if( n<0 ) n = sqlite3Strlen30(z); jba = s3jni_new_jbyteArray((unsigned const char *)z, n); if( jba ){ @@ -983,12 +984,12 @@ S3JniExceptionReport; S3JniExceptionClear; } S3JniUnrefLocal(jba); } + s3jni_oom_check( rv ); } - s3jni_oom_check( rv ); return rv; } #define s3jni_utf8_to_jstring(CStr,n) s3jni__utf8_to_jstring(env, CStr, n) /* @@ -1466,16 +1467,18 @@ #define PtrGet_sqlite3_blob(OBJ) PtrGet_T(sqlite3_blob, OBJ) #define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ) #define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ) #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ) /* -** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 -** struct type name and Y to be a native pointer to such an object in -** the form of a jlong value. The jlong is simply cast to (X*). This +** S3JniLongPtr_T(X,Y) expects X to be an unqualified sqlite3 struct +** type name and Y to be a native pointer to such an object in the +** form of a jlong value. The jlong is simply cast to (X*). This ** approach is, as of 2023-09-27, supplanting the former approach. We ** now do the native pointer extraction in the Java side, rather than -** the C side, because it's reportedly significantly faster. +** the C side, because it's reportedly significantly faster. The +** intptr_t part here is necessary for compatibility with (at least) +** ARM32. */ #define S3JniLongPtr_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr)) #define S3JniLongPtr_sqlite3(JLongAsPtr) S3JniLongPtr_T(sqlite3,JLongAsPtr) #define S3JniLongPtr_sqlite3_backup(JLongAsPtr) S3JniLongPtr_T(sqlite3_backup,JLongAsPtr) #define S3JniLongPtr_sqlite3_blob(JLongAsPtr) S3JniLongPtr_T(sqlite3_blob,JLongAsPtr) @@ -2052,19 +2055,28 @@ /** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */ #define WRAP_INT64_DB(JniNameSuffix,CName) \ JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ return (jlong)CName(S3JniLongPtr_sqlite3(jpDb)); \ } +/** Create a trivial JNI wrapper for (jstring CName(sqlite3*,int)). */ +#define WRAP_STR_DB_INT(JniNameSuffix,CName) \ + JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpDb, jint ndx){ \ + return s3jni_utf8_to_jstring( \ + CName(S3JniLongPtr_sqlite3(jpDb), (int)ndx), \ + -1); \ + } /** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */ -#define WRAP_INT_SVALUE(JniNameSuffix,CName) \ +#define WRAP_INT_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ - return (jint)CName(S3JniLongPtr_sqlite3_value(jpSValue)); \ + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \ + return (jint)(sv ? CName(sv): DfltOnNull); \ } /** Create a trivial JNI wrapper for (boolean CName(sqlite3_value*)). */ -#define WRAP_BOOL_SVALUE(JniNameSuffix,CName) \ +#define WRAP_BOOL_SVALUE(JniNameSuffix,CName,DfltOnNull) \ JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ - return (jint)CName(S3JniLongPtr_sqlite3_value(jpSValue)) \ + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSValue); \ + return (jint)(sv ? CName(sv) : DfltOnNull) \ ? JNI_TRUE : JNI_FALSE; \ } WRAP_INT_DB(1changes, sqlite3_changes) WRAP_INT64_DB(1changes64, sqlite3_changes64) @@ -2077,10 +2089,11 @@ WRAP_STR_STMT_INT(1column_1database_1name, sqlite3_column_database_name) WRAP_STR_STMT_INT(1column_1origin_1name, sqlite3_column_origin_name) WRAP_STR_STMT_INT(1column_1table_1name, sqlite3_column_table_name) WRAP_INT_STMT_INT(1column_1type, sqlite3_column_type) WRAP_INT_STMT(1data_1count, sqlite3_data_count) +WRAP_STR_DB_INT(1db_1name, sqlite3_db_name) WRAP_INT_DB(1error_1offset, sqlite3_error_offset) WRAP_INT_DB(1extended_1errcode, sqlite3_extended_errcode) WRAP_BOOL_DB(1get_1autocommit, sqlite3_get_autocommit) WRAP_MUTF8_VOID(1libversion, sqlite3_libversion) WRAP_INT_VOID(1libversion_1number, sqlite3_libversion_number) @@ -2099,18 +2112,16 @@ WRAP_BOOL_STMT(1stmt_1readonly, sqlite3_stmt_readonly) WRAP_INT_DB(1system_1errno, sqlite3_system_errno) WRAP_INT_VOID(1threadsafe, sqlite3_threadsafe) WRAP_INT_DB(1total_1changes, sqlite3_total_changes) WRAP_INT64_DB(1total_1changes64, sqlite3_total_changes64) -WRAP_INT_SVALUE(1value_1bytes, sqlite3_value_bytes) -WRAP_INT_SVALUE(1value_1bytes16, sqlite3_value_bytes16) -WRAP_INT_SVALUE(1value_1encoding, sqlite3_value_encoding) -WRAP_BOOL_SVALUE(1value_1frombind, sqlite3_value_frombind) -WRAP_INT_SVALUE(1value_1nochange, sqlite3_value_nochange) -WRAP_INT_SVALUE(1value_1numeric_1type, sqlite3_value_numeric_type) -WRAP_INT_SVALUE(1value_1subtype, sqlite3_value_subtype) -WRAP_INT_SVALUE(1value_1type, sqlite3_value_type) +WRAP_INT_SVALUE(1value_1encoding, sqlite3_value_encoding,SQLITE_UTF8) +WRAP_BOOL_SVALUE(1value_1frombind, sqlite3_value_frombind,0) +WRAP_INT_SVALUE(1value_1nochange, sqlite3_value_nochange,0) +WRAP_INT_SVALUE(1value_1numeric_1type, sqlite3_value_numeric_type,SQLITE_NULL) +WRAP_INT_SVALUE(1value_1subtype, sqlite3_value_subtype,0) +WRAP_INT_SVALUE(1value_1type, sqlite3_value_type,SQLITE_NULL) #undef WRAP_BOOL_DB #undef WRAP_BOOL_STMT #undef WRAP_BOOL_SVALUE #undef WRAP_INT64_DB @@ -2120,10 +2131,11 @@ #undef WRAP_INT_STMT_INT #undef WRAP_INT_SVALUE #undef WRAP_INT_VOID #undef WRAP_MUTF8_VOID #undef WRAP_STR_STMT_INT +#undef WRAP_STR_DB_INT S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)( JniArgsEnvClass, jobject jCx, jboolean initialize ){ sqlite3_context * const pCx = PtrGet_sqlite3_context(jCx); @@ -2364,11 +2376,11 @@ ){ sqlite3_stmt * const pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt); int rc = SQLITE_MISUSE; if(pStmt){ - jobject const rv = val ? S3JniRefGlobal(val) : 0; + jobject const rv = S3JniRefGlobal(val); if( rv ){ rc = sqlite3_bind_pointer(pStmt, ndx, rv, ResultJavaValuePtrStr, S3Jni_jobject_finalizer); }else if(val){ rc = SQLITE_NOMEM; @@ -2430,10 +2442,28 @@ int const rc = sqlite3_bind_text16(S3JniLongPtr_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax, SQLITE_TRANSIENT); s3jni_jbyteArray_release(baData, pBuf); return (jint)rc; } + +S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( + JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue +){ + int rc = 0; + sqlite3_stmt * pStmt = S3JniLongPtr_sqlite3_stmt(jpStmt); + if( pStmt ){ + sqlite3_value *v = S3JniLongPtr_sqlite3_value(jpValue); + if( v ){ + rc = sqlite3_bind_value(pStmt, (int)ndx, v); + }else{ + rc = sqlite3_bind_null(pStmt, (int)ndx); + } + }else{ + rc = SQLITE_MISUSE; + } + return (jint)rc; +} S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)( JniArgsEnvClass, jlong jpStmt, jint ndx, jint n ){ return (jint)sqlite3_bind_zeroblob(S3JniLongPtr_sqlite3_stmt(jpStmt), @@ -2614,10 +2644,14 @@ JniArgsEnvClass, jobject jAutoExt ){ S3JniAutoExtension * ax; jboolean rc = JNI_FALSE; int i; + + if( !jAutoExt ){ + return rc; + } S3JniAutoExt_mutex_enter; /* This algo corresponds to the one in the core. */ for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ ax = &SJG.autoExt.aExt[i]; if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ @@ -2776,41 +2810,42 @@ S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - const int n = sqlite3_column_bytes(stmt, (int)ndx); + const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; return p ? s3jni_new_jbyteArray(p, n) : NULL; } #if 0 // this impl might prove useful. S3JniApi(sqlite3_column_text(),jstring,1column_1text)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx); - const int n = sqlite3_column_bytes(stmt, (int)ndx); + const unsigned char * const p = stmt ? sqlite3_column_text(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes(stmt, (int)ndx) : 0; return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; } #endif S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt); - const void * const p = sqlite3_column_text16(stmt, (int)ndx); - const int n = sqlite3_column_bytes16(stmt, (int)ndx); + const void * const p = stmt ? sqlite3_column_text16(stmt, (int)ndx) : 0; + const int n = p ? sqlite3_column_bytes16(stmt, (int)ndx) : 0; return s3jni_text16_to_jstring(env, p, n); } S3JniApi(sqlite3_column_value(),jobject,1column_1value)( JniArgsEnvClass, jobject jpStmt, jint ndx ){ sqlite3_value * const sv = - sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); + sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx) + /* reminder: returns an SQL NULL if jpStmt==NULL */; return new_java_sqlite3_value(env, sv); } /* ** Impl for commit hooks (if isCommit is true) or rollback hooks. @@ -2856,11 +2891,11 @@ S3JniHook * pHook; /* ps->hooks.commit|rollback */ S3JniDb_mutex_enter; ps = S3JniDb_from_jlong(jpDb); if( !ps ){ - s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0); + s3jni_db_error(ps->pDb, SQLITE_MISUSE, 0); S3JniDb_mutex_leave; return 0; } pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback; pOld = pHook->jObj; @@ -2916,10 +2951,22 @@ jstring const rv = z ? (*env)->NewStringUTF( env, z ) : 0; /* We know these to be ASCII, so MUTF-8 is fine. */; s3jni_oom_check(z ? !!rv : 1); return rv; } + +S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( + JniArgsEnvClass, jstring name +){ + const char *zUtf8 = s3jni_jstring_to_mutf8(name) + /* We know these to be ASCII, so MUTF-8 is fine (and + hypothetically faster to convert). */; + const jboolean rc = + 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; + s3jni_mutf8_release(name, zUtf8); + return rc; +} S3JniApi(sqlite3_complete(),int,1complete)( JniArgsEnvClass, jbyteArray jSql ){ jbyte * const pBuf = s3jni_jbyteArray_bytes(jSql); @@ -2933,22 +2980,10 @@ : (jSql ? SQLITE_NOMEM : SQLITE_MISUSE); s3jni_jbyteArray_release(jSql, pBuf); return rc; } -S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)( - JniArgsEnvClass, jstring name -){ - const char *zUtf8 = s3jni_jstring_to_mutf8(name) - /* We know these to be ASCII, so MUTF-8 is fine (and - hypothetically faster to convert). */; - const jboolean rc = - 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; - s3jni_mutf8_release(name, zUtf8); - return rc; -} - S3JniApi(sqlite3_config() /*for a small subset of options.*/, jint,1config__I)(JniArgsEnvClass, jint n){ switch( n ){ case SQLITE_CONFIG_SINGLETHREAD: case SQLITE_CONFIG_MULTITHREAD: @@ -3149,10 +3184,13 @@ )(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep, jobject oCollation){ int rc; S3JniDb * ps; + if( !jDb || !name || !encodingTypeIsValid(eTextRep) ){ + return (jint)SQLITE_MISUSE; + } S3JniDb_mutex_enter; ps = S3JniDb_from_java(jDb); jclass const klazz = (*env)->GetObjectClass(env, oCollation); jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call", "([B[B)I"); @@ -3239,40 +3277,10 @@ /* Reminder: on sqlite3_create_function() error, s will be ** destroyed via create_function(). */ return (jint)rc; } -S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( - JniArgsEnvClass, jobject jDb, jstring jDbName -){ - S3JniDb * const ps = S3JniDb_from_java(jDb); - char *zDbName; - jstring jRv = 0; - int nStr = 0; - - if( !ps || !jDbName ){ - return 0; - } - zDbName = s3jni_jstring_to_utf8( jDbName, &nStr); - if( zDbName ){ - char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); - sqlite3_free(zDbName); - if( zRv ){ - jRv = s3jni_utf8_to_jstring( zRv, -1); - } - } - return jRv; -} - -S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( - JniArgsEnvClass, jobject jpStmt -){ - sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); - sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; - S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; - return ps ? ps->jDb : 0; -} S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/, jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ S3JniDb * const ps = S3JniDb_from_java(jDb); @@ -3296,10 +3304,11 @@ }else{ rc = SQLITE_NOMEM; } S3JniDb_mutex_leave; break; + case 0: default: rc = SQLITE_MISUSE; } return rc; } @@ -3339,10 +3348,11 @@ if( 0==rc && jOut ){ OutputPointer_set_Int32(env, jOut, pOut); } break; } + case 0: default: rc = SQLITE_MISUSE; } return (jint)rc; } @@ -3358,10 +3368,52 @@ ){ return JniFuncName(1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)( env, jKlazz, jDb, op, onOff, jOut ); } + +S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)( + JniArgsEnvClass, jobject jDb, jstring jDbName +){ + S3JniDb * const ps = S3JniDb_from_java(jDb); + char *zDbName; + jstring jRv = 0; + int nStr = 0; + + if( !ps || !jDbName ){ + return 0; + } + zDbName = s3jni_jstring_to_utf8( jDbName, &nStr); + if( zDbName ){ + char const * zRv = sqlite3_db_filename(ps->pDb, zDbName); + sqlite3_free(zDbName); + if( zRv ){ + jRv = s3jni_utf8_to_jstring( zRv, -1); + } + } + return jRv; +} + +S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)( + JniArgsEnvClass, jobject jpStmt +){ + sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); + sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0; + S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; + return ps ? ps->jDb : 0; +} + +S3JniApi(sqlite3_db_readonly(),jint,1db_1readonly)( + JniArgsEnvClass, jobject jDb, jstring jDbName +){ + int rc = 0; + S3JniDb * const ps = S3JniDb_from_java(jDb); + char *zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0 ) : 0; + rc = sqlite3_db_readonly(ps ? ps->pDb : 0, zDbName); + sqlite3_free(zDbName); + return (jint)rc; +} S3JniApi(sqlite3_db_release_memory(),int,1db_1release_1memory)( JniArgsEnvClass, jobject jDb ){ sqlite3 * const pDb = PtrGet_sqlite3(jDb); @@ -3547,16 +3599,11 @@ S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( JniArgsEnvClass, jobject jpDb ){ - jlong rc = 0; - sqlite3 * const pDb = PtrGet_sqlite3(jpDb); - if( pDb ){ - rc = (jlong)sqlite3_last_insert_rowid(pDb); - } - return rc; + return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); } S3JniApi(sqlite3_limit(),jint,1limit)( JniArgsEnvClass, jobject jpDb, jint id, jint newVal ){ @@ -3572,10 +3619,11 @@ static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc, jstring jDbName, char **zDbName, S3JniDb ** ps){ int rc = 0; jobject jDb = 0; + *jc = S3JniEnv_get(); if( !*jc ){ rc = SQLITE_NOMEM; goto end; } @@ -3648,10 +3696,12 @@ sqlite3 * pOut = 0; char *zName = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; int rc; + + if( 0==jOut ) return SQLITE_MISUSE; rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ rc = s3jni_open_post(env, jc, ps, &pOut, jOut, sqlite3_open(zName, &pOut)); assert(rc==0 ? pOut!=0 : 1); @@ -3660,18 +3710,21 @@ return (jint)rc; } S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( JniArgsEnvClass, jstring strName, - jobject jOut, jint flags, jstring strVfs + jobject jOut, jint flags, jstring strVfs ){ sqlite3 * pOut = 0; char *zName = 0; S3JniDb * ps = 0; S3JniEnv * jc = 0; char *zVfs = 0; - int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); + int rc; + + if( 0==jOut ) return SQLITE_MISUSE; + rc = s3jni_open_pre(env, &jc, strName, &zName, &ps); if( 0==rc ){ if( strVfs ){ zVfs = s3jni_jstring_to_utf8( strVfs, 0); if( !zVfs ){ rc = SQLITE_NOMEM; @@ -3694,30 +3747,35 @@ jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail){ sqlite3_stmt * pStmt = 0; jobject jStmt = 0; const char * zTail = 0; - jbyte * const pBuf = s3jni_jbyteArray_bytes(baSql); + sqlite3 * const pDb = S3JniLongPtr_sqlite3(jpDb); + jbyte * const pBuf = pDb ? s3jni_jbyteArray_bytes(baSql) : 0; int rc = SQLITE_ERROR; + assert(prepVersion==1 || prepVersion==2 || prepVersion==3); - if( !pBuf ){ + if( !pDb || !jOutStmt ){ + rc = SQLITE_MISUSE; + goto end; + }else if( !pBuf ){ rc = baSql ? SQLITE_NOMEM : SQLITE_MISUSE; goto end; } jStmt = new_java_sqlite3_stmt(env, 0); if( !jStmt ){ rc = SQLITE_NOMEM; goto end; } switch( prepVersion ){ - case 1: rc = sqlite3_prepare(S3JniLongPtr_sqlite3(jpDb), (const char *)pBuf, + case 1: rc = sqlite3_prepare(pDb, (const char *)pBuf, (int)nMax, &pStmt, &zTail); break; - case 2: rc = sqlite3_prepare_v2(S3JniLongPtr_sqlite3(jpDb), (const char *)pBuf, + case 2: rc = sqlite3_prepare_v2(pDb, (const char *)pBuf, (int)nMax, &pStmt, &zTail); break; - case 3: rc = sqlite3_prepare_v3(S3JniLongPtr_sqlite3(jpDb), (const char *)pBuf, + case 3: rc = sqlite3_prepare_v3(pDb, (const char *)pBuf, (int)nMax, (unsigned int)prepFlags, &pStmt, &zTail); break; default: assert(!"Invalid prepare() version"); @@ -3743,12 +3801,14 @@ } }else{ S3JniUnrefLocal(jStmt); jStmt = 0; } - OutputPointer_set_obj(env, S3JniNph(OutputPointer_sqlite3_stmt), - jOutStmt, jStmt); + if( jOutStmt ){ + OutputPointer_set_obj(env, S3JniNph(OutputPointer_sqlite3_stmt), + jOutStmt, jStmt); + } return (jint)rc; } S3JniApi(sqlite3_prepare(),jint,1prepare)( JNIEnv * const env, jclass self, jlong jpDb, jbyteArray baSql, jint nMax, jobject jOutStmt, jobject outTail @@ -4068,11 +4128,14 @@ 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; - if( jBa ){ + if( !pCx ){ + /* We should arguably emit a warning here. But where to log it? */ + return; + }else if( jBa ){ jbyte * const pBuf = s3jni_jbyteArray_bytes(jBa); jsize nBa = (*env)->GetArrayLength(env, jBa); if( nMax>=0 && nBa>(jsize)nMax ){ nBa = (jsize)nMax; /** @@ -4084,11 +4147,11 @@ the 2nd parameter for the first zero character. Note that the text64() interfaces take an unsigned value for the length, which Java does not support. This binding takes the approach of passing on negative values to the C API, - which will, in turn fail with SQLITE_TOOBIG at some later + which will in turn fail with SQLITE_TOOBIG at some later point (recall that the sqlite3_result_xyz() family do not have result values). */ } if( as64 ){ /* 64-bit... */ @@ -4218,14 +4281,16 @@ } S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( JniArgsEnvClass, jobject jpCx, jobject v ){ - if( v ){ + sqlite3_context * pCx = PtrGet_sqlite3_context(jpCx); + if( !pCx ) return; + else if( v ){ jobject const rjv = S3JniRefGlobal(v); if( rjv ){ - sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv, + sqlite3_result_pointer(pCx, rjv, ResultJavaValuePtrStr, S3Jni_jobject_finalizer); }else{ sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); } }else{ @@ -4467,11 +4532,10 @@ jbyteArray baG, jbyteArray baT, jint escLike){ int rc = 0; jbyte * const pG = s3jni_jbyteArray_bytes(baG); jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0; - s3jni_oom_fatal(pT); /* Note that we're relying on the byte arrays having been NUL-terminated on the Java side. */ rc = isLike ? sqlite3_strlike((const char *)pG, (const char *)pT, (unsigned int)escLike) @@ -4507,16 +4571,12 @@ } S3JniApi(sqlite3_step(),jint,1step)( JniArgsEnvClass,jobject jStmt ){ - int rc = SQLITE_MISUSE; sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt); - if( pStmt ){ - rc = sqlite3_step(pStmt); - } - return rc; + return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; } S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)( JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName, jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull, @@ -4527,15 +4587,16 @@ const char * pzCollSeq = 0; const char * pzDataType = 0; int pNotNull = 0, pPrimaryKey = 0, pAutoinc = 0; int rc; - if( !db || !jDbName || !jTableName || !jColumnName ) return SQLITE_MISUSE; + if( !db || !jDbName || !jTableName ) return SQLITE_MISUSE; zDbName = s3jni_jstring_to_utf8(jDbName,0); zTableName = zDbName ? s3jni_jstring_to_utf8(jTableName,0) : 0; - zColumnName = zTableName ? s3jni_jstring_to_utf8(jColumnName,0) : 0; - rc = zColumnName + zColumnName = (zTableName && jColumnName) + ? s3jni_jstring_to_utf8(jColumnName,0) : 0; + rc = zTableName ? sqlite3_table_column_metadata(db, zDbName, zTableName, zColumnName, &pzDataType, &pzCollSeq, &pNotNull, &pPrimaryKey, &pAutoinc) : SQLITE_NOMEM; if( 0==rc ){ @@ -4691,87 +4752,115 @@ S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); - const jbyte * pBytes = sqlite3_value_blob(sv); - int const nLen = sqlite3_value_bytes(sv); + const jbyte * pBytes = sv ? sqlite3_value_blob(sv) : 0; + int const nLen = pBytes ? sqlite3_value_bytes(sv) : 0; s3jni_oom_check( nLen ? !!pBytes : 1 ); return pBytes ? s3jni_new_jbyteArray(pBytes, nLen) : NULL; } +S3JniApi(sqlite3_value_bytes(),int,1value_1bytes)( + JniArgsEnvClass, jlong jpSVal +){ + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return sv ? sqlite3_value_bytes(sv) : 0; +} + +S3JniApi(sqlite3_value_bytes16(),int,1value_1bytes16)( + JniArgsEnvClass, jlong jpSVal +){ + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return sv ? sqlite3_value_bytes16(sv) : 0; +} + S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( JniArgsEnvClass, jlong jpSVal ){ - return (jdouble) sqlite3_value_double(S3JniLongPtr_sqlite3_value(jpSVal)); + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return (jdouble) (sv ? sqlite3_value_double(sv) : 0.0); } S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value * const sv = sqlite3_value_dup(S3JniLongPtr_sqlite3_value(jpSVal)); - return sv ? new_java_sqlite3_value(env, sv) : 0; + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + sqlite3_value * const sd = sv ? sqlite3_value_dup(sv) : 0; + jobject rv = sd ? new_java_sqlite3_value(env, sd) : 0; + if( sd && !rv ) { + /* OOM */ + sqlite3_value_free(sd); + } + return rv; } S3JniApi(sqlite3_value_free(),void,1value_1free)( JniArgsEnvClass, jlong jpSVal ){ - sqlite3_value_free(S3JniLongPtr_sqlite3_value(jpSVal)); + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + if( sv ){ + sqlite3_value_free(sv); + } } S3JniApi(sqlite3_value_int(),jint,1value_1int)( JniArgsEnvClass, jlong jpSVal ){ - return (jint) sqlite3_value_int(S3JniLongPtr_sqlite3_value(jpSVal)); + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return (jint) (sv ? sqlite3_value_int(sv) : 0); } S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( JniArgsEnvClass, jlong jpSVal ){ - return (jlong) sqlite3_value_int64(S3JniLongPtr_sqlite3_value(jpSVal)); + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return (jlong) (sv ? sqlite3_value_int64(sv) : 0LL); } S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( JniArgsEnvClass, jlong jpSVal ){ - return sqlite3_value_pointer(S3JniLongPtr_sqlite3_value(jpSVal), - ResultJavaValuePtrStr); + sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); + return sv + ? sqlite3_value_pointer(sv, ResultJavaValuePtrStr) + : 0; } S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); - const unsigned char * const p = sqlite3_value_text(sv); - int const n = sqlite3_value_bytes(sv); + const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; + int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_new_jbyteArray(p, n) : 0; } #if 0 // this impl might prove useful. S3JniApi(sqlite3_value_text(),jstring,1value_1text)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); - const unsigned char * const p = sqlite3_value_text(sv); - int const n = sqlite3_value_bytes(sv); + const unsigned char * const p = sv ? sqlite3_value_text(sv) : 0; + int const n = p ? sqlite3_value_bytes(sv) : 0; return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0; } #endif S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)( JniArgsEnvClass, jlong jpSVal ){ sqlite3_value * const sv = S3JniLongPtr_sqlite3_value(jpSVal); - const int n = sqlite3_value_bytes16(sv); - const void * const p = sqlite3_value_text16(sv); - return s3jni_text16_to_jstring(env, p, n); + const int n = sv ? sqlite3_value_bytes16(sv) : 0; + const void * const p = sv ? sqlite3_value_text16(sv) : 0; + return p ? s3jni_text16_to_jstring(env, p, n) : 0; } JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){ MARKER(("\nVarious bits of internal info:\n")); puts("FTS5 is " Index: ext/jni/src/c/sqlite3-jni.h ================================================================== --- ext/jni/src/c/sqlite3-jni.h +++ ext/jni/src/c/sqlite3-jni.h @@ -937,10 +937,18 @@ * Signature: (JI[BI)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_CApi_sqlite3_1bind_1text16 (JNIEnv *, jclass, jlong, jint, jbyteArray, jint); +/* + * Class: org_sqlite_jni_CApi + * Method: sqlite3_bind_value + * Signature: (JIJ)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_CApi_sqlite3_1bind_1value + (JNIEnv *, jclass, jlong, jint, jlong); + /* * Class: org_sqlite_jni_CApi * Method: sqlite3_bind_zeroblob * Signature: (JII)I */ @@ -1305,10 +1313,18 @@ * Signature: (Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I */ JNIEXPORT jint JNICALL Java_org_sqlite_jni_CApi_sqlite3_1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2 (JNIEnv *, jclass, jobject, jint, jstring); +/* + * Class: org_sqlite_jni_CApi + * Method: sqlite3_db_name + * Signature: (JI)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_sqlite_jni_CApi_sqlite3_1db_1name + (JNIEnv *, jclass, jlong, jint); + /* * Class: org_sqlite_jni_CApi * Method: sqlite3_db_filename * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;)Ljava/lang/String; */ @@ -1321,10 +1337,18 @@ * Signature: (Lorg/sqlite/jni/sqlite3_stmt;)Lorg/sqlite/jni/sqlite3; */ JNIEXPORT jobject JNICALL Java_org_sqlite_jni_CApi_sqlite3_1db_1handle (JNIEnv *, jclass, jobject); +/* + * Class: org_sqlite_jni_CApi + * Method: sqlite3_db_readonly + * Signature: (Lorg/sqlite/jni/sqlite3;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_org_sqlite_jni_CApi_sqlite3_1db_1readonly + (JNIEnv *, jclass, jobject, jstring); + /* * Class: org_sqlite_jni_CApi * Method: sqlite3_db_release_memory * Signature: (Lorg/sqlite/jni/sqlite3;)I */ Index: ext/jni/src/org/sqlite/jni/CApi.java ================================================================== --- ext/jni/src/org/sqlite/jni/CApi.java +++ ext/jni/src/org/sqlite/jni/CApi.java @@ -397,10 +397,21 @@ ){ return (null == data) ? sqlite3_bind_null(stmt.getNativePointer(), ndx) : sqlite3_bind_text16(stmt.getNativePointer(), ndx, data, data.length); } + + static native int sqlite3_bind_value(@NotNull long ptrToStmt, int ndx, long ptrToValue); + + /** + Functions like the C-level sqlite3_bind_value(), or + sqlite3_bind_null() if val is null. + */ + public static int sqlite3_bind_value(@NotNull sqlite3_stmt stmt, int ndx, sqlite3_value val){ + return sqlite3_bind_value(stmt.getNativePointer(), ndx, + null==val ? 0L : val.getNativePointer()); + } static native int sqlite3_bind_zeroblob(@NotNull long ptrToStmt, int ndx, int n); public static int sqlite3_bind_zeroblob(@NotNull sqlite3_stmt stmt, int ndx, int n){ return sqlite3_bind_zeroblob(stmt.getNativePointer(), ndx, n); @@ -808,16 +819,25 @@ extended in future versions. */ public static native int sqlite3_db_config( @NotNull sqlite3 db, int op, @NotNull String val ); + + private static native String sqlite3_db_name(@NotNull long ptrToDb, int ndx); + + public static String sqlite3_db_name(@NotNull sqlite3 db, int ndx){ + return null==db ? null : sqlite3_db_name(db.getNativePointer(), ndx); + } + public static native String sqlite3_db_filename( @NotNull sqlite3 db, @NotNull String dbName ); public static native sqlite3 sqlite3_db_handle(@NotNull sqlite3_stmt stmt); + + public static native int sqlite3_db_readonly(@NotNull sqlite3 db, String dbName); public static native int sqlite3_db_release_memory(sqlite3 db); public static native int sqlite3_db_status( @NotNull sqlite3 db, int op, @NotNull OutputPointer.Int32 pCurrent, Index: ext/jni/src/org/sqlite/jni/Tester1.java ================================================================== --- ext/jni/src/org/sqlite/jni/Tester1.java +++ ext/jni/src/org/sqlite/jni/Tester1.java @@ -253,10 +253,16 @@ sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, null) /* This function has different mangled names in jdk8 vs jdk19, and this call is here to ensure that the build fails if it cannot find both names. */; + affirm( 0==sqlite3_db_readonly(db,"main") ); + affirm( 0==sqlite3_db_readonly(db,null) ); + affirm( 0>sqlite3_db_readonly(db,"nope") ); + affirm( 0>sqlite3_db_readonly(null,null) ); + affirm( 0==sqlite3_last_insert_rowid(null) ); + // These interrupt checks are only to make sure that the JNI binding // has the proper exported symbol names. They don't actually test // anything useful. affirm( !sqlite3_is_interrupted(db) ); sqlite3_interrupt(db); @@ -1026,12 +1032,15 @@ rc = sqlite3_open(dbName, outDb); ++metrics.dbOpen; affirm( 0 == rc ); affirm( outDb.get() != db1 ); final sqlite3 db2 = outDb.get(); + + affirm( "main".equals( sqlite3_db_name(db1, 0) ) ); rc = sqlite3_db_config(db1, SQLITE_DBCONFIG_MAINDBNAME, "foo"); affirm( sqlite3_db_filename(db1, "foo").endsWith(dbName) ); + affirm( "foo".equals( sqlite3_db_name(db1, 0) ) ); final ValueHolder xBusyCalled = new ValueHolder<>(0); BusyHandlerCallback handler = new BusyHandlerCallback(){ @Override public int call(int n){ //outln("busy handler #"+n); @@ -1454,20 +1463,30 @@ affirm( !bAutoinc.value ); affirm( bNotNull.value ); affirm( "noCase".equals(zCollSeq.value) ); affirm( "duck".equals(zDataType.value) ); - final TableColumnMetadata m = + TableColumnMetadata m = sqlite3_table_column_metadata(db, "main", "t", "a"); affirm( null != m ); affirm( bPrimaryKey.value == m.isPrimaryKey() ); affirm( bAutoinc.value == m.isAutoincrement() ); affirm( bNotNull.value == m.isNotNull() ); affirm( zCollSeq.value.equals(m.getCollation()) ); affirm( zDataType.value.equals(m.getDataType()) ); affirm( null == sqlite3_table_column_metadata(db, "nope", "t", "a") ); + affirm( null == sqlite3_table_column_metadata(db, "main", "nope", "a") ); + + m = sqlite3_table_column_metadata(db, "main", "t", null) + /* Check only for existence of table */; + affirm( null != m ); + affirm( m.isPrimaryKey() ); + affirm( !m.isAutoincrement() ); + affirm( !m.isNotNull() ); + affirm( "BINARY".equalsIgnoreCase(m.getCollation()) ); + affirm( "INTEGER".equalsIgnoreCase(m.getDataType()) ); sqlite3_close_v2(db); } private void testTxnState(){ Index: src/loadext.c ================================================================== --- src/loadext.c +++ src/loadext.c @@ -728,10 +728,13 @@ /* ** Enable or disable extension loading. Extension loading is disabled by ** default so as not to open security holes in older applications. */ int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); if( onoff ){ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; }else{ db->flags &= ~(u64)(SQLITE_LoadExtension|SQLITE_LoadExtFunc); @@ -749,11 +752,11 @@ ** This list is shared across threads. The SQLITE_MUTEX_STATIC_MAIN ** mutex must be held while accessing this list. */ typedef struct sqlite3AutoExtList sqlite3AutoExtList; static SQLITE_WSD struct sqlite3AutoExtList { - u32 nExt; /* Number of entries in aExt[] */ + u32 nExt; /* Number of entries in aExt[] */ void (**aExt)(void); /* Pointers to the extension init functions */ } sqlite3Autoext = { 0, 0 }; /* The "wsdAutoext" macro will resolve to the autoextension ** state vector. If writable static data is unsupported on the target, @@ -777,10 +780,13 @@ */ int sqlite3_auto_extension( void (*xInit)(void) ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return SQLITE_MISUSE_BKPT; +#endif #ifndef SQLITE_OMIT_AUTOINIT rc = sqlite3_initialize(); if( rc ){ return rc; }else @@ -829,10 +835,13 @@ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); #endif int i; int n = 0; wsdAutoextInit; +#ifdef SQLITE_ENABLE_API_ARMOR + if( xInit==0 ) return 0; +#endif sqlite3_mutex_enter(mutex); for(i=(int)wsdAutoext.nExt-1; i>=0; i--){ if( wsdAutoext.aExt[i]==xInit ){ wsdAutoext.nExt--; wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt]; Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -952,10 +952,14 @@ ** Configuration settings for an individual database connection */ int sqlite3_db_config(sqlite3 *db, int op, ...){ va_list ap; int rc; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_MAINDBNAME: { /* IMP: R-06824-28531 */ @@ -2363,10 +2367,16 @@ void(*xCallback)( /* Callback function */ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64), void *pArg /* First callback argument */ ){ void *pRet; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || xCallback==0 ){ + return; + } +#endif sqlite3_mutex_enter(db->mutex); pRet = db->pPreUpdateArg; db->xPreUpdateCallback = xCallback; db->pPreUpdateArg = pArg; sqlite3_mutex_leave(db->mutex); Index: src/notify.c ================================================================== --- src/notify.c +++ src/notify.c @@ -150,10 +150,13 @@ void (*xNotify)(void **, int), void *pArg ){ int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(db->mutex); enterMutex(); if( xNotify==0 ){ removeFromBlockedList(db); Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -403,29 +403,37 @@ } } static int invokeValueDestructor( const void *p, /* Value to destroy */ void (*xDel)(void*), /* The destructor */ - sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */ + sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */ ){ assert( xDel!=SQLITE_DYNAMIC ); if( xDel==0 ){ /* noop */ }else if( xDel==SQLITE_TRANSIENT ){ /* noop */ }else{ xDel((void*)p); } - sqlite3_result_error_toobig(pCtx); + if( pCtx!=0 ){ + sqlite3_result_error_toobig(pCtx); + } return SQLITE_TOOBIG; } void sqlite3_result_blob( sqlite3_context *pCtx, const void *z, int n, void (*xDel)(void *) ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 || n<0 ){ + invokeValueDestructor(z, xDel, pCtx); + return; + } +#endif assert( n>=0 ); assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, 0, xDel); } void sqlite3_result_blob64( @@ -432,60 +440,95 @@ sqlite3_context *pCtx, const void *z, sqlite3_uint64 n, void (*xDel)(void *) ){ - assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif + assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); if( n>0x7fffffff ){ (void)invokeValueDestructor(z, xDel, pCtx); }else{ setResultStrOrError(pCtx, z, (int)n, 0, xDel); } } void sqlite3_result_double(sqlite3_context *pCtx, double rVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetDouble(pCtx->pOut, rVal); } void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT); } #ifndef SQLITE_OMIT_UTF16 void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_ERROR; sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT); } #endif void sqlite3_result_int(sqlite3_context *pCtx, int iVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal); } void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetInt64(pCtx->pOut, iVal); } void sqlite3_result_null(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); } void sqlite3_result_pointer( sqlite3_context *pCtx, void *pPtr, const char *zPType, void (*xDestructor)(void*) ){ - Mem *pOut = pCtx->pOut; + Mem *pOut; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(pPtr, xDestructor, 0); + return; + } +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); sqlite3VdbeMemRelease(pOut); pOut->flags = MEM_Null; sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor); } void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){ - Mem *pOut = pCtx->pOut; + Mem *pOut; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); pOut->eSubtype = eSubtype & 0xff; pOut->flags |= MEM_Subtype; } void sqlite3_result_text( @@ -492,10 +535,16 @@ sqlite3_context *pCtx, const char *z, int n, void (*xDel)(void *) ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel); } void sqlite3_result_text64( sqlite3_context *pCtx, @@ -502,10 +551,16 @@ const char *z, sqlite3_uint64 n, void (*xDel)(void *), unsigned char enc ){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ){ + invokeValueDestructor(z, xDel, 0); + return; + } +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); assert( xDel!=SQLITE_DYNAMIC ); if( enc!=SQLITE_UTF8 ){ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE; n &= ~(u64)1; @@ -545,11 +600,20 @@ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); setResultStrOrError(pCtx, z, n & ~(u64)1, SQLITE_UTF16LE, xDel); } #endif /* SQLITE_OMIT_UTF16 */ void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){ - Mem *pOut = pCtx->pOut; + Mem *pOut; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; + if( pValue==0 ){ + sqlite3_result_null(pCtx); + return; + } +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemCopy(pOut, pValue); sqlite3VdbeChangeEncoding(pOut, pCtx->enc); if( sqlite3VdbeMemTooBig(pOut) ){ sqlite3_result_error_toobig(pCtx); @@ -557,11 +621,16 @@ } void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){ sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0); } int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){ - Mem *pOut = pCtx->pOut; + Mem *pOut; + +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return SQLITE_MISUSE_BKPT; +#endif + pOut = pCtx->pOut; assert( sqlite3_mutex_held(pOut->db->mutex) ); if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){ sqlite3_result_error_toobig(pCtx); return SQLITE_TOOBIG; } @@ -571,10 +640,13 @@ #else return sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n); #endif } void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif pCtx->isError = errCode ? errCode : -1; #ifdef SQLITE_DEBUG if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode; #endif if( pCtx->pOut->flags & MEM_Null ){ @@ -583,18 +655,24 @@ } } /* Force an SQLITE_TOOBIG error. */ void sqlite3_result_error_toobig(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); pCtx->isError = SQLITE_TOOBIG; sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1, SQLITE_UTF8, SQLITE_STATIC); } /* An SQLITE_NOMEM error. */ void sqlite3_result_error_nomem(sqlite3_context *pCtx){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); sqlite3VdbeMemSetNull(pCtx->pOut); pCtx->isError = SQLITE_NOMEM_BKPT; sqlite3OomFault(pCtx->pOut->db); } @@ -843,11 +921,15 @@ /* ** Extract the user data from a sqlite3_context structure and return a ** pointer to it. */ void *sqlite3_user_data(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#else assert( p && p->pFunc ); +#endif return p->pFunc->pUserData; } /* ** Extract the user data from a sqlite3_context structure and return a @@ -858,11 +940,15 @@ ** parameter) of the sqlite3_create_function() and ** sqlite3_create_function16() routines that originally registered the ** application defined function. */ sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#else assert( p && p->pOut ); +#endif return p->pOut->db; } /* ** If this routine is invoked from within an xColumn method of a virtual @@ -877,11 +963,15 @@ ** Virtual table implements might use this routine to optimize their ** performance by substituting a NULL result, or some other light-weight ** value, as a signal to the xUpdate routine that the column is unchanged. */ int sqlite3_vtab_nochange(sqlite3_context *p){ +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 0; +#else assert( p ); +#endif return sqlite3_value_nochange(p->pOut); } /* ** The destructor function for a ValueList object. This needs to be @@ -1036,10 +1126,13 @@ ** single prepared statement. The iArg values must match. */ void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){ AuxData *pAuxData; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return 0; +#endif assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #if SQLITE_ENABLE_STAT4 if( pCtx->pVdbe==0 ) return 0; #else assert( pCtx->pVdbe!=0 ); @@ -1068,12 +1161,16 @@ int iArg, void *pAux, void (*xDelete)(void*) ){ AuxData *pAuxData; - Vdbe *pVdbe = pCtx->pVdbe; + Vdbe *pVdbe; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pCtx==0 ) return; +#endif + pVdbe= pCtx->pVdbe; assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) ); #ifdef SQLITE_ENABLE_STAT4 if( pVdbe==0 ) goto failed; #else assert( pVdbe!=0 ); @@ -1735,10 +1832,13 @@ return rc; } int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){ int rc; Vdbe *p = (Vdbe *)pStmt; +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(p->db->mutex); if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){ rc = SQLITE_TOOBIG; }else{ assert( (n & 0x7FFFFFFF)==n ); @@ -1861,10 +1961,13 @@ ** Set the explain mode for a statement. */ int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){ Vdbe *v = (Vdbe*)pStmt; int rc; +#ifdef SQLITE_ENABLE_API_ARMOR + if( pStmt==0 ) return SQLITE_MISUSE_BKPT; +#endif sqlite3_mutex_enter(v->db->mutex); if( ((int)v->explain)==eMode ){ rc = SQLITE_OK; }else if( eMode<0 || eMode>2 ){ rc = SQLITE_ERROR; @@ -2027,14 +2130,20 @@ /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or deleted. */ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; Mem *pMem; int rc = SQLITE_OK; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; /* Test that this call is being made from within an SQLITE_DELETE or ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */ if( !p || p->op==SQLITE_INSERT ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_old_out; @@ -2091,11 +2200,16 @@ /* ** This function is called from within a pre-update callback to retrieve ** the number of columns in the row being updated, deleted or inserted. */ int sqlite3_preupdate_count(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->keyinfo.nKeyField : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -2109,11 +2223,16 @@ ** ** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL ** or SET DEFAULT action is considered a trigger. */ int sqlite3_preupdate_depth(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->v->nFrame : 0); } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -2120,11 +2239,16 @@ /* ** This function is designed to be called from within a pre-update callback ** only. */ int sqlite3_preupdate_blobwrite(sqlite3 *db){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; +#ifdef SQLITE_ENABLE_API_ARMOR + p = db!=0 ? db->pPreUpdate : 0; +#else + p = db->pPreUpdate; +#endif return (p ? p->iBlobWrite : -1); } #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK @@ -2131,14 +2255,20 @@ /* ** This function is called from within a pre-update callback to retrieve ** a field of the row currently being updated or inserted. */ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){ - PreUpdate *p = db->pPreUpdate; + PreUpdate *p; int rc = SQLITE_OK; Mem *pMem; +#ifdef SQLITE_ENABLE_API_ARMOR + if( db==0 || ppValue==0 ){ + return SQLITE_MISUSE_BKPT; + } +#endif + p = db->pPreUpdate; if( !p || p->op==SQLITE_DELETE ){ rc = SQLITE_MISUSE_BKPT; goto preupdate_new_out; } if( p->pPk && p->op!=SQLITE_UPDATE ){ @@ -2213,15 +2343,20 @@ int iScanStatusOp, /* Which metric to return */ int flags, void *pOut /* OUT: Write the answer here */ ){ Vdbe *p = (Vdbe*)pStmt; - VdbeOp *aOp = p->aOp; - int nOp = p->nOp; + VdbeOp *aOp; + int nOp; ScanStatus *pScan = 0; int idx; +#ifdef SQLITE_ENABLE_API_ARMOR + if( p==0 ) return 1; +#endif + aOp = p->aOp; + nOp = p->nOp; if( p->pFrame ){ VdbeFrame *pFrame; for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); aOp = pFrame->aOp; nOp = pFrame->nOp; @@ -2364,12 +2499,12 @@ ** Zero all counters associated with the sqlite3_stmt_scanstatus() data. */ void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe*)pStmt; int ii; - for(ii=0; iinOp; ii++){ + for(ii=0; p!=0 && iinOp; ii++){ Op *pOp = &p->aOp[ii]; pOp->nExec = 0; pOp->nCycle = 0; } } #endif /* SQLITE_ENABLE_STMT_SCANSTATUS */ Index: src/vdbeblob.c ================================================================== --- src/vdbeblob.c +++ src/vdbeblob.c @@ -140,11 +140,11 @@ return SQLITE_MISUSE_BKPT; } #endif *ppBlob = 0; #ifdef SQLITE_ENABLE_API_ARMOR - if( !sqlite3SafetyCheckOk(db) || zTable==0 ){ + if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){ return SQLITE_MISUSE_BKPT; } #endif wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */