|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_DEFAULT_CACHE_SIZE |
|
|
# define SQLITE_DEFAULT_CACHE_SIZE -16384 |
|
|
#endif |
|
|
#if !defined(SQLITE_DEFAULT_PAGE_SIZE) |
|
|
# define SQLITE_DEFAULT_PAGE_SIZE 8192 |
|
|
#endif |
|
|
#ifndef SQLITE_DQS |
|
|
# define SQLITE_DQS 0 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#undef SQLITE_ENABLE_API_ARMOR |
|
|
#define SQLITE_ENABLE_API_ARMOR 1 |
|
|
|
|
|
#ifndef SQLITE_ENABLE_BYTECODE_VTAB |
|
|
# define SQLITE_ENABLE_BYTECODE_VTAB 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_DBPAGE_VTAB |
|
|
# define SQLITE_ENABLE_DBPAGE_VTAB 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_DBSTAT_VTAB |
|
|
# define SQLITE_ENABLE_DBSTAT_VTAB 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS |
|
|
# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_MATH_FUNCTIONS |
|
|
# define SQLITE_ENABLE_MATH_FUNCTIONS 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC |
|
|
# define SQLITE_ENABLE_OFFSET_SQL_FUNC 1 |
|
|
#endif |
|
|
#ifndef SQLITE_ENABLE_RTREE |
|
|
# define SQLITE_ENABLE_RTREE 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_ENABLE_STMTVTAB |
|
|
# define SQLITE_ENABLE_STMTVTAB 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_JNI_FATAL_OOM |
|
|
#if !SQLITE_JNI_FATAL_OOM |
|
|
#undef SQLITE_JNI_FATAL_OOM |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_OMIT_DEPRECATED |
|
|
# define SQLITE_OMIT_DEPRECATED 1 |
|
|
#endif |
|
|
#ifndef SQLITE_OMIT_LOAD_EXTENSION |
|
|
# define SQLITE_OMIT_LOAD_EXTENSION 1 |
|
|
#endif |
|
|
#ifndef SQLITE_OMIT_SHARED_CACHE |
|
|
# define SQLITE_OMIT_SHARED_CACHE 1 |
|
|
#endif |
|
|
#ifdef SQLITE_OMIT_UTF16 |
|
|
|
|
|
# undef SQLITE_OMIT_UTF16 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_STRICT_SUBTYPE |
|
|
# define SQLITE_STRICT_SUBTYPE 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_TEMP_STORE |
|
|
# define SQLITE_TEMP_STORE 2 |
|
|
#endif |
|
|
#ifndef SQLITE_THREADSAFE |
|
|
# define SQLITE_THREADSAFE 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_USE_URI |
|
|
# define SQLITE_USE_URI 1 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef SQLITE_C |
|
|
# define SQLITE_C sqlite3.c |
|
|
#endif |
|
|
#define INC__STRINGIFY_(f) #f |
|
|
#define INC__STRINGIFY(f) INC__STRINGIFY_(f) |
|
|
#include INC__STRINGIFY(SQLITE_C) |
|
|
#undef INC__STRINGIFY_ |
|
|
#undef INC__STRINGIFY |
|
|
#undef SQLITE_C |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "sqlite3-jni.h" |
|
|
#include <assert.h> |
|
|
#include <stdio.h> |
|
|
#include <stdint.h> |
|
|
|
|
|
|
|
|
#define MARKER(pfexp) \ |
|
|
do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \ |
|
|
printf pfexp; \ |
|
|
} while(0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define JniFuncName(Suffix) \ |
|
|
Java_org_sqlite_jni_capi_CApi_sqlite3_ ## Suffix |
|
|
|
|
|
|
|
|
#define JniDecl(ReturnType,Suffix) \ |
|
|
JNIEXPORT ReturnType JNICALL JniFuncName(Suffix) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniApi(CFunc,ReturnType,Suffix) JniDecl(ReturnType,Suffix) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniCast_L2P(JLongAsPtr) (void*)((intptr_t)(JLongAsPtr)) |
|
|
#define S3JniCast_P2L(PTR) (jlong)((intptr_t)(PTR)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define JniArgsEnvObj JNIEnv * env, jobject jSelf |
|
|
#define JniArgsEnvClass JNIEnv * env, jclass jKlazz |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniIfThrew if( (*env)->ExceptionCheck(env) ) |
|
|
#define S3JniExceptionClear (*env)->ExceptionClear(env) |
|
|
#define S3JniExceptionReport (*env)->ExceptionDescribe(env) |
|
|
#define S3JniExceptionIgnore S3JniIfThrew S3JniExceptionClear |
|
|
#define S3JniExceptionWarnIgnore \ |
|
|
S3JniIfThrew {S3JniExceptionReport; S3JniExceptionClear;}(void)0 |
|
|
#define S3JniExceptionWarnCallbackThrew(STR) \ |
|
|
MARKER(("WARNING: " STR " MUST NOT THROW.\n")); \ |
|
|
(*env)->ExceptionDescribe(env) |
|
|
|
|
|
|
|
|
|
|
|
#define S3JniExceptionIsFatal(MSG) S3JniIfThrew {\ |
|
|
S3JniExceptionReport; S3JniExceptionClear; \ |
|
|
(*env)->FatalError(env, MSG); \ |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniDeclLocal_env JNIEnv * const env = s3jni_env() |
|
|
|
|
|
|
|
|
static inline void s3jni_oom(JNIEnv * const env){ |
|
|
(*env)->FatalError(env, "SQLite3 JNI is out of memory.") ; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_JNI_FATAL_OOM |
|
|
#define s3jni_malloc(SIZE) s3jni_malloc_or_die(env, SIZE) |
|
|
#else |
|
|
#define s3jni_malloc(SIZE) sqlite3_malloc(((void)env,(SIZE))) |
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_JNI_FATAL_OOM |
|
|
static void * s3jni_realloc_or_die(JNIEnv * const env, void * p, size_t n){ |
|
|
void * const rv = sqlite3_realloc(p, (int)n); |
|
|
if( n && !rv ) s3jni_oom(env); |
|
|
return rv; |
|
|
} |
|
|
#define s3jni_realloc(MEM,SIZE) s3jni_realloc_or_die(env, (MEM), (SIZE)) |
|
|
#else |
|
|
#define s3jni_realloc(MEM,SIZE) sqlite3_realloc((MEM), ((void)env, (SIZE))) |
|
|
#endif |
|
|
|
|
|
|
|
|
#define s3jni_oom_fatal(EXPR) if( !(EXPR) ) s3jni_oom(env) |
|
|
|
|
|
#ifdef SQLITE_JNI_FATAL_OOM |
|
|
#define s3jni_oom_check s3jni_oom_fatal |
|
|
#else |
|
|
#define s3jni_oom_check(EXPR) |
|
|
#endif |
|
|
|
|
|
|
|
|
#define s3jni_db_oom(pDb) (void)((pDb) ? ((pDb)->mallocFailed=1) : 0) |
|
|
|
|
|
|
|
|
static jobject s3jni_ref_global(JNIEnv * const env, jobject const v){ |
|
|
jobject const rv = v ? (*env)->NewGlobalRef(env, v) : NULL; |
|
|
s3jni_oom_fatal( v ? !!rv : 1 ); |
|
|
return rv; |
|
|
} |
|
|
static jobject s3jni_ref_local(JNIEnv * const env, jobject const v){ |
|
|
jobject const rv = v ? (*env)->NewLocalRef(env, v) : NULL; |
|
|
s3jni_oom_fatal( v ? !!rv : 1 ); |
|
|
return rv; |
|
|
} |
|
|
static inline void s3jni_unref_global(JNIEnv * const env, jobject const v){ |
|
|
if( v ) (*env)->DeleteGlobalRef(env, v); |
|
|
} |
|
|
static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){ |
|
|
if( v ) (*env)->DeleteLocalRef(env, v); |
|
|
} |
|
|
#define S3JniRefGlobal(VAR) s3jni_ref_global(env, (VAR)) |
|
|
#define S3JniRefLocal(VAR) s3jni_ref_local(env, (VAR)) |
|
|
#define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR)) |
|
|
#define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniNphOp S3JniNphOp; |
|
|
struct S3JniNphOp { |
|
|
const int index ; |
|
|
const char * const zName ; |
|
|
const char * const zMember ; |
|
|
const char * const zTypeSig ; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
jclass klazz; |
|
|
volatile jfieldID fidValue |
|
|
; |
|
|
volatile jmethodID midCtor |
|
|
; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const struct { |
|
|
const S3JniNphOp sqlite3; |
|
|
const S3JniNphOp sqlite3_backup; |
|
|
const S3JniNphOp sqlite3_blob; |
|
|
const S3JniNphOp sqlite3_context; |
|
|
const S3JniNphOp sqlite3_stmt; |
|
|
const S3JniNphOp sqlite3_value; |
|
|
const S3JniNphOp OutputPointer_Bool; |
|
|
const S3JniNphOp OutputPointer_Int32; |
|
|
const S3JniNphOp OutputPointer_Int64; |
|
|
const S3JniNphOp OutputPointer_sqlite3; |
|
|
const S3JniNphOp OutputPointer_sqlite3_blob; |
|
|
const S3JniNphOp OutputPointer_sqlite3_stmt; |
|
|
const S3JniNphOp OutputPointer_sqlite3_value; |
|
|
const S3JniNphOp OutputPointer_String; |
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
const S3JniNphOp OutputPointer_ByteArray; |
|
|
const S3JniNphOp Fts5Context; |
|
|
const S3JniNphOp Fts5ExtensionApi; |
|
|
const S3JniNphOp fts5_api; |
|
|
const S3JniNphOp fts5_tokenizer; |
|
|
const S3JniNphOp Fts5Tokenizer; |
|
|
#endif |
|
|
} S3JniNphOps = { |
|
|
#define MkRef(INDEX, KLAZZ, MEMBER, SIG) \ |
|
|
{ INDEX, "org/sqlite/jni/" KLAZZ, MEMBER, SIG } |
|
|
|
|
|
#define RefN(INDEX, KLAZZ) MkRef(INDEX, KLAZZ, "nativePointer", "J") |
|
|
|
|
|
#define RefO(INDEX, KLAZZ, SIG) MkRef(INDEX, KLAZZ, "value", SIG) |
|
|
RefN(0, "capi/sqlite3"), |
|
|
RefN(1, "capi/sqlite3_backup"), |
|
|
RefN(2, "capi/sqlite3_blob"), |
|
|
RefN(3, "capi/sqlite3_context"), |
|
|
RefN(4, "capi/sqlite3_stmt"), |
|
|
RefN(5, "capi/sqlite3_value"), |
|
|
RefO(6, "capi/OutputPointer$Bool", "Z"), |
|
|
RefO(7, "capi/OutputPointer$Int32", "I"), |
|
|
RefO(8, "capi/OutputPointer$Int64", "J"), |
|
|
RefO(9, "capi/OutputPointer$sqlite3", |
|
|
"Lorg/sqlite/jni/capi/sqlite3;"), |
|
|
RefO(10, "capi/OutputPointer$sqlite3_blob", |
|
|
"Lorg/sqlite/jni/capi/sqlite3_blob;"), |
|
|
RefO(11, "capi/OutputPointer$sqlite3_stmt", |
|
|
"Lorg/sqlite/jni/capi/sqlite3_stmt;"), |
|
|
RefO(12, "capi/OutputPointer$sqlite3_value", |
|
|
"Lorg/sqlite/jni/capi/sqlite3_value;"), |
|
|
RefO(13, "capi/OutputPointer$String", "Ljava/lang/String;"), |
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
RefO(14, "capi/OutputPointer$ByteArray", "[B"), |
|
|
RefN(15, "fts5/Fts5Context"), |
|
|
RefN(16, "fts5/Fts5ExtensionApi"), |
|
|
RefN(17, "fts5/fts5_api"), |
|
|
RefN(18, "fts5/fts5_tokenizer"), |
|
|
RefN(19, "fts5/Fts5Tokenizer") |
|
|
#endif |
|
|
#undef MkRef |
|
|
#undef RefN |
|
|
#undef RefO |
|
|
}; |
|
|
|
|
|
#define S3JniNph(T) &S3JniNphOps.T |
|
|
|
|
|
enum { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S3Jni_NphCache_size = sizeof(S3JniNphOps) / sizeof(S3JniNphOp) |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniHook S3JniHook; |
|
|
struct S3JniHook{ |
|
|
jobject jObj ; |
|
|
jmethodID midCallback |
|
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
jobject jExtra ; |
|
|
int doXDestroy |
|
|
; |
|
|
S3JniHook * pNext ; |
|
|
}; |
|
|
|
|
|
static const S3JniHook S3JniHook_empty = {0,0,0,0,0}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniDb S3JniDb; |
|
|
struct S3JniDb { |
|
|
sqlite3 *pDb ; |
|
|
jobject jDb |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
; |
|
|
char * zMainDbName |
|
|
; |
|
|
struct { |
|
|
S3JniHook busyHandler; |
|
|
S3JniHook collationNeeded; |
|
|
S3JniHook commit; |
|
|
S3JniHook progress; |
|
|
S3JniHook rollback; |
|
|
S3JniHook trace; |
|
|
S3JniHook update; |
|
|
S3JniHook auth; |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
S3JniHook preUpdate; |
|
|
#endif |
|
|
} hooks; |
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
|
|
|
struct { |
|
|
jobject jApi ; |
|
|
} fts; |
|
|
#endif |
|
|
S3JniDb * pNext ; |
|
|
}; |
|
|
|
|
|
static const char * const S3JniDb_clientdata_key = "S3JniDb"; |
|
|
#define S3JniDb_from_clientdata(pDb) \ |
|
|
(pDb ? sqlite3_get_clientdata(pDb, S3JniDb_clientdata_key) : 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniEnv S3JniEnv; |
|
|
struct S3JniEnv { |
|
|
JNIEnv *env ; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S3JniDb * pdbOpening; |
|
|
S3JniEnv * pNext |
|
|
; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef S3JniHook S3JniAutoExtension; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum UDFType { |
|
|
UDF_UNKNOWN_TYPE = 0, |
|
|
UDF_SCALAR, |
|
|
UDF_AGGREGATE, |
|
|
UDF_WINDOW |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniUdf S3JniUdf; |
|
|
struct S3JniUdf { |
|
|
jobject jObj ; |
|
|
char * zFuncName ; |
|
|
enum UDFType type /* UDF type */; |
|
|
|
|
|
jmethodID jmidxFunc ; |
|
|
jmethodID jmidxStep ; |
|
|
jmethodID jmidxFinal ; |
|
|
jmethodID jmidxValue ; |
|
|
jmethodID jmidxInverse ; |
|
|
S3JniUdf * pNext ; |
|
|
}; |
|
|
|
|
|
#if defined(SQLITE_JNI_ENABLE_METRICS) && 0==SQLITE_JNI_ENABLE_METRICS |
|
|
# undef SQLITE_JNI_ENABLE_METRICS |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_DEBUG |
|
|
# define S3JNI_METRICS_MUTEX SQLITE_THREADSAFE |
|
|
#else |
|
|
# define S3JNI_METRICS_MUTEX 0 |
|
|
#endif |
|
|
#ifndef SQLITE_JNI_ENABLE_METRICS |
|
|
# undef S3JNI_METRICS_MUTEX |
|
|
# define S3JNI_METRICS_MUTEX 0 |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct S3JniGlobalType S3JniGlobalType; |
|
|
struct S3JniGlobalType { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JavaVM * jvm; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sqlite3_mutex * mutex; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
S3JniNphOp list[S3Jni_NphCache_size]; |
|
|
sqlite3_mutex * mutex; |
|
|
volatile void const * locker; |
|
|
|
|
|
} nph; |
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
S3JniEnv * aHead ; |
|
|
S3JniEnv * aFree ; |
|
|
sqlite3_mutex * mutex ; |
|
|
volatile void const * locker |
|
|
|
|
|
; |
|
|
} envCache; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
S3JniDb * aFree ; |
|
|
sqlite3_mutex * mutex ; |
|
|
volatile void const * locker |
|
|
|
|
|
|
|
|
; |
|
|
} perDb; |
|
|
struct { |
|
|
S3JniUdf * aFree |
|
|
; |
|
|
} udf; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
jclass cLong ; |
|
|
jclass cString ; |
|
|
jobject oCharsetUtf8 ; |
|
|
jmethodID ctorLong1 ; |
|
|
jmethodID ctorStringBA ; |
|
|
jmethodID stringGetBytes ; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
jclass klazz ; |
|
|
jmethodID midAlloc ; |
|
|
jmethodID midLimit ; |
|
|
} byteBuffer; |
|
|
} g; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
S3JniAutoExtension *aExt |
|
|
|
|
|
|
|
|
; |
|
|
int nAlloc |
|
|
|
|
|
; |
|
|
int nExt |
|
|
; |
|
|
sqlite3_mutex * mutex ; |
|
|
volatile const void * locker |
|
|
|
|
|
; |
|
|
} autoExt; |
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
struct { |
|
|
volatile jobject jExt |
|
|
; |
|
|
struct { |
|
|
jfieldID fidA ; |
|
|
jfieldID fidB ; |
|
|
} jPhraseIter; |
|
|
} fts5; |
|
|
#endif |
|
|
struct { |
|
|
#ifdef SQLITE_ENABLE_SQLLOG |
|
|
S3JniHook sqllog ; |
|
|
#endif |
|
|
S3JniHook configlog ; |
|
|
S3JniHook * aFree ; |
|
|
sqlite3_mutex * mutex ; |
|
|
volatile const void * locker |
|
|
|
|
|
; |
|
|
} hook; |
|
|
#ifdef SQLITE_JNI_ENABLE_METRICS |
|
|
|
|
|
struct { |
|
|
volatile unsigned nEnvHit; |
|
|
volatile unsigned nEnvMiss; |
|
|
volatile unsigned nEnvAlloc; |
|
|
volatile unsigned nMutexEnv |
|
|
; |
|
|
volatile unsigned nMutexNph ; |
|
|
volatile unsigned nMutexHook ; |
|
|
volatile unsigned nMutexPerDb ; |
|
|
volatile unsigned nMutexAutoExt ; |
|
|
volatile unsigned nMutexGlobal ; |
|
|
volatile unsigned nMutexUdf |
|
|
; |
|
|
volatile unsigned nDestroy ; |
|
|
volatile unsigned nPdbAlloc ; |
|
|
volatile unsigned nPdbRecycled ; |
|
|
volatile unsigned nUdfAlloc ; |
|
|
volatile unsigned nUdfRecycled ; |
|
|
volatile unsigned nHookAlloc ; |
|
|
volatile unsigned nHookRecycled ; |
|
|
struct { |
|
|
|
|
|
volatile unsigned nFunc; |
|
|
volatile unsigned nStep; |
|
|
volatile unsigned nFinal; |
|
|
volatile unsigned nValue; |
|
|
volatile unsigned nInverse; |
|
|
} udf; |
|
|
unsigned nMetrics |
|
|
; |
|
|
#if S3JNI_METRICS_MUTEX |
|
|
sqlite3_mutex * mutex; |
|
|
#endif |
|
|
} metrics; |
|
|
#endif |
|
|
}; |
|
|
static S3JniGlobalType S3JniGlobal = {}; |
|
|
#define SJG S3JniGlobal |
|
|
|
|
|
|
|
|
#ifndef SQLITE_JNI_ENABLE_METRICS |
|
|
#define s3jni_incr(PTR) |
|
|
#elif S3JNI_METRICS_MUTEX |
|
|
static void s3jni_incr( volatile unsigned int * const p ){ |
|
|
sqlite3_mutex_enter(SJG.metrics.mutex); |
|
|
++SJG.metrics.nMetrics; |
|
|
++(*p); |
|
|
sqlite3_mutex_leave(SJG.metrics.mutex); |
|
|
} |
|
|
#else |
|
|
#define s3jni_incr(PTR) ++(*(PTR)) |
|
|
#endif |
|
|
|
|
|
|
|
|
#if SQLITE_THREADSAFE |
|
|
#define s3jni_mutex_enter2(M, Metric) \ |
|
|
sqlite3_mutex_enter( M ); \ |
|
|
s3jni_incr( &SJG.metrics.Metric ) |
|
|
#define s3jni_mutex_leave2(M) \ |
|
|
sqlite3_mutex_leave( M ) |
|
|
|
|
|
#define s3jni_mutex_enter(M, L, Metric) \ |
|
|
assert( (void*)env != (void*)L && "Invalid use of " #L); \ |
|
|
s3jni_mutex_enter2( M, Metric ); \ |
|
|
L = env |
|
|
#define s3jni_mutex_leave(M, L) \ |
|
|
assert( (void*)env == (void*)L && "Invalid use of " #L); \ |
|
|
L = 0; \ |
|
|
s3jni_mutex_leave2( M ) |
|
|
|
|
|
#define S3JniEnv_mutex_assertLocked \ |
|
|
assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) |
|
|
#define S3JniEnv_mutex_assertLocker \ |
|
|
assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) |
|
|
#define S3JniEnv_mutex_assertNotLocker \ |
|
|
assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" ) |
|
|
|
|
|
#define S3JniEnv_mutex_enter \ |
|
|
s3jni_mutex_enter( SJG.envCache.mutex, SJG.envCache.locker, nMutexEnv ) |
|
|
#define S3JniEnv_mutex_leave \ |
|
|
s3jni_mutex_leave( SJG.envCache.mutex, SJG.envCache.locker ) |
|
|
|
|
|
#define S3JniAutoExt_mutex_enter \ |
|
|
s3jni_mutex_enter( SJG.autoExt.mutex, SJG.autoExt.locker, nMutexAutoExt ) |
|
|
#define S3JniAutoExt_mutex_leave \ |
|
|
s3jni_mutex_leave( SJG.autoExt.mutex, SJG.autoExt.locker ) |
|
|
#define S3JniAutoExt_mutex_assertLocker \ |
|
|
assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ) |
|
|
|
|
|
#define S3JniGlobal_mutex_enter \ |
|
|
s3jni_mutex_enter2( SJG.mutex, nMutexGlobal ) |
|
|
#define S3JniGlobal_mutex_leave \ |
|
|
s3jni_mutex_leave2( SJG.mutex ) |
|
|
|
|
|
#define S3JniHook_mutex_enter \ |
|
|
s3jni_mutex_enter( SJG.hook.mutex, SJG.hook.locker, nMutexHook ) |
|
|
#define S3JniHook_mutex_leave \ |
|
|
s3jni_mutex_leave( SJG.hook.mutex, SJG.hook.locker ) |
|
|
|
|
|
#define S3JniNph_mutex_enter \ |
|
|
s3jni_mutex_enter( SJG.nph.mutex, SJG.nph.locker, nMutexNph ) |
|
|
#define S3JniNph_mutex_leave \ |
|
|
s3jni_mutex_leave( SJG.nph.mutex, SJG.nph.locker ) |
|
|
|
|
|
#define S3JniDb_mutex_assertLocker \ |
|
|
assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ) |
|
|
#define S3JniDb_mutex_enter \ |
|
|
s3jni_mutex_enter( SJG.perDb.mutex, SJG.perDb.locker, nMutexPerDb ) |
|
|
#define S3JniDb_mutex_leave \ |
|
|
s3jni_mutex_leave( SJG.perDb.mutex, SJG.perDb.locker ) |
|
|
|
|
|
#else |
|
|
#define S3JniAutoExt_mutex_assertLocker |
|
|
#define S3JniAutoExt_mutex_enter |
|
|
#define S3JniAutoExt_mutex_leave |
|
|
#define S3JniDb_mutex_assertLocker |
|
|
#define S3JniDb_mutex_enter |
|
|
#define S3JniDb_mutex_leave |
|
|
#define S3JniEnv_mutex_assertLocked |
|
|
#define S3JniEnv_mutex_assertLocker |
|
|
#define S3JniEnv_mutex_assertNotLocker |
|
|
#define S3JniEnv_mutex_enter |
|
|
#define S3JniEnv_mutex_leave |
|
|
#define S3JniGlobal_mutex_enter |
|
|
#define S3JniGlobal_mutex_leave |
|
|
#define S3JniHook_mutex_enter |
|
|
#define S3JniHook_mutex_leave |
|
|
#define S3JniNph_mutex_enter |
|
|
#define S3JniNph_mutex_leave |
|
|
#endif |
|
|
|
|
|
|
|
|
static const char * s3jni__jstring_to_mutf8(JNIEnv * const env, jstring v ){ |
|
|
const char *z = v ? (*env)->GetStringUTFChars(env, v, NULL) : 0; |
|
|
s3jni_oom_check( v ? !!z : !z ); |
|
|
return z; |
|
|
} |
|
|
|
|
|
#define s3jni_jstring_to_mutf8(ARG) s3jni__jstring_to_mutf8(env, (ARG)) |
|
|
#define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jbyte * s3jni__jbyteArray_bytes2(JNIEnv * const env, jbyteArray jBA, jsize * nBA ){ |
|
|
jbyte * const rv = jBA ? (*env)->GetByteArrayElements(env, jBA, NULL) : 0; |
|
|
s3jni_oom_check( jBA ? !!rv : 1 ); |
|
|
if( jBA && nBA ) *nBA = (*env)->GetArrayLength(env, jBA); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
#define s3jni_jbyteArray_bytes2(jByteArray,ptrToSz) \ |
|
|
s3jni__jbyteArray_bytes2(env, (jByteArray), (ptrToSz)) |
|
|
#define s3jni_jbyteArray_bytes(jByteArray) s3jni__jbyteArray_bytes2(env, (jByteArray), 0) |
|
|
#define s3jni_jbyteArray_release(jByteArray,jBytes) \ |
|
|
if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT) |
|
|
#define s3jni_jbyteArray_commit(jByteArray,jBytes) \ |
|
|
if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_COMMIT) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){ |
|
|
*pBuf = 0; |
|
|
*pN = 0; |
|
|
if( jbb ){ |
|
|
*pBuf = (*env)->GetDirectBufferAddress(env, jbb); |
|
|
if( *pBuf ){ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*pN = (*env)->CallIntMethod(env, jbb, SJG.g.byteBuffer.midLimit); |
|
|
S3JniExceptionIsFatal("Error calling ByteBuffer.limit() method."); |
|
|
} |
|
|
} |
|
|
} |
|
|
#define s3jni_get_nio_buffer(JOBJ,vpOut,jpOut) \ |
|
|
s3jni__get_nio_buffer(env,(JOBJ),(vpOut),(jpOut)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static JNIEnv * s3jni_env(void){ |
|
|
JNIEnv * env = 0; |
|
|
if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env, |
|
|
JNI_VERSION_1_8) ){ |
|
|
fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n"); |
|
|
abort(); |
|
|
} |
|
|
return env; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniEnv * S3JniEnv__get(JNIEnv * const env){ |
|
|
struct S3JniEnv * row; |
|
|
S3JniEnv_mutex_enter; |
|
|
row = SJG.envCache.aHead; |
|
|
for( ; row; row = row->pNext ){ |
|
|
if( row->env == env ){ |
|
|
s3jni_incr( &SJG.metrics.nEnvHit ); |
|
|
S3JniEnv_mutex_leave; |
|
|
return row; |
|
|
} |
|
|
} |
|
|
s3jni_incr( &SJG.metrics.nEnvMiss ); |
|
|
row = SJG.envCache.aFree; |
|
|
if( row ){ |
|
|
SJG.envCache.aFree = row->pNext; |
|
|
}else{ |
|
|
row = s3jni_malloc_or_die(env, sizeof(*row)); |
|
|
s3jni_incr( &SJG.metrics.nEnvAlloc ); |
|
|
} |
|
|
memset(row, 0, sizeof(*row)); |
|
|
row->pNext = SJG.envCache.aHead; |
|
|
SJG.envCache.aHead = row; |
|
|
row->env = env; |
|
|
|
|
|
S3JniEnv_mutex_leave; |
|
|
return row; |
|
|
} |
|
|
|
|
|
#define S3JniEnv_get() S3JniEnv__get(env) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_db_error(JNIEnv * env, sqlite3* const db, |
|
|
int err_code, const char * const zMsg){ |
|
|
if( db!=0 ){ |
|
|
int const rc = sqlite3_set_errmsg(db, err_code, zMsg); |
|
|
s3jni_oom_fatal(0==rc); |
|
|
if( rc && !err_code ) err_code=rc; |
|
|
} |
|
|
return err_code; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jbyteArray s3jni__new_jbyteArray(JNIEnv * const env, |
|
|
const void * const p, int nP){ |
|
|
jbyteArray jba = (*env)->NewByteArray(env, (jint)nP); |
|
|
|
|
|
s3jni_oom_check( jba ); |
|
|
if( jba && p ){ |
|
|
(*env)->SetByteArrayRegion(env, jba, 0, (jint)nP, (const jbyte*)p); |
|
|
} |
|
|
return jba; |
|
|
} |
|
|
|
|
|
#define s3jni_new_jbyteArray(P,n) s3jni__new_jbyteArray(env, P, n) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jstring s3jni__utf8_to_jstring(JNIEnv * const env, |
|
|
const char * const z, int n){ |
|
|
jstring rv = NULL; |
|
|
if( 0==n || (n<0 && z && !z[0]) ){ |
|
|
|
|
|
|
|
|
|
|
|
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 ){ |
|
|
rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA, |
|
|
jba, SJG.g.oCharsetUtf8); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
S3JniUnrefLocal(jba); |
|
|
} |
|
|
s3jni_oom_check( rv ); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
#define s3jni_utf8_to_jstring(CStr,n) s3jni__utf8_to_jstring(env, CStr, n) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char * s3jni__jstring_to_utf8(JNIEnv * const env, |
|
|
jstring jstr, int *nLen){ |
|
|
jbyteArray jba; |
|
|
jsize nBA; |
|
|
char *rv; |
|
|
|
|
|
if( !jstr ) return 0; |
|
|
jba = (*env)->CallObjectMethod(env, jstr, SJG.g.stringGetBytes, |
|
|
SJG.g.oCharsetUtf8); |
|
|
|
|
|
if( (*env)->ExceptionCheck(env) || !jba |
|
|
) { |
|
|
S3JniExceptionReport; |
|
|
s3jni_oom_check( jba ); |
|
|
if( nLen ) *nLen = 0; |
|
|
return 0; |
|
|
} |
|
|
nBA = (*env)->GetArrayLength(env, jba); |
|
|
if( nLen ) *nLen = (int)nBA; |
|
|
rv = s3jni_malloc( nBA + 1 ); |
|
|
if( rv ){ |
|
|
(*env)->GetByteArrayRegion(env, jba, 0, nBA, (jbyte*)rv); |
|
|
rv[nBA] = 0; |
|
|
} |
|
|
S3JniUnrefLocal(jba); |
|
|
return rv; |
|
|
} |
|
|
#define s3jni_jstring_to_utf8(JStr,n) s3jni__jstring_to_utf8(env, JStr, n) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){ |
|
|
jstring const rv = p |
|
|
? (*env)->NewString(env, (const jchar *)p, (jsize)(nP/2)) |
|
|
: NULL; |
|
|
s3jni_oom_check( p ? !!rv : 1 ); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject s3jni__new_ByteBuffer(JNIEnv * const env, int n){ |
|
|
jobject rv = 0; |
|
|
assert( SJG.g.byteBuffer.klazz ); |
|
|
assert( SJG.g.byteBuffer.midAlloc ); |
|
|
assert( n > 0 ); |
|
|
rv = (*env)->CallStaticObjectMethod(env, SJG.g.byteBuffer.klazz, |
|
|
SJG.g.byteBuffer.midAlloc, (jint)n); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
s3jni_oom_check( rv ); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject s3jni__blob_to_ByteBuffer(JNIEnv * const env, |
|
|
const void * p, int n){ |
|
|
jobject rv = NULL; |
|
|
assert( n >= 0 ); |
|
|
if( 0==n || !SJG.g.byteBuffer.klazz ){ |
|
|
return NULL; |
|
|
} |
|
|
rv = s3jni__new_ByteBuffer(env, n); |
|
|
if( rv ){ |
|
|
void * tgt = (*env)->GetDirectBufferAddress(env, rv); |
|
|
memcpy(tgt, p, (size_t)n); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){ |
|
|
jmethodID mid; |
|
|
jstring msg; |
|
|
char * zMsg; |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jx); |
|
|
mid = (*env)->GetMethodID(env, klazz, "toString", "()Ljava/lang/String;"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
return 0; |
|
|
} |
|
|
msg = (*env)->CallObjectMethod(env, jx, mid); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
return 0; |
|
|
} |
|
|
zMsg = s3jni_jstring_to_utf8( msg, 0); |
|
|
S3JniUnrefLocal(msg); |
|
|
return zMsg; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni__db_exception(JNIEnv * const env, sqlite3 * const pDb, |
|
|
int errCode, const char *zDfltMsg){ |
|
|
jthrowable const ex = (*env)->ExceptionOccurred(env); |
|
|
|
|
|
if( 0==errCode ) errCode = SQLITE_ERROR; |
|
|
if( ex ){ |
|
|
char * zMsg; |
|
|
S3JniExceptionClear; |
|
|
zMsg = s3jni_exception_error_msg(env, ex); |
|
|
s3jni_db_error(env, pDb, errCode, zMsg ? zMsg : zDfltMsg); |
|
|
sqlite3_free(zMsg); |
|
|
S3JniUnrefLocal(ex); |
|
|
}else if( zDfltMsg ){ |
|
|
s3jni_db_error(env, pDb, errCode, zDfltMsg); |
|
|
} |
|
|
return errCode; |
|
|
} |
|
|
#define s3jni_db_exception(pDb,ERRCODE,DFLTMSG) \ |
|
|
s3jni__db_exception(env, (pDb), (ERRCODE), (DFLTMSG) ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){ |
|
|
if( jObj ){ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jObj); |
|
|
jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V"); |
|
|
|
|
|
S3JniUnrefLocal(klazz); |
|
|
if( method ){ |
|
|
s3jni_incr( &SJG.metrics.nDestroy ); |
|
|
(*env)->CallVoidMethod(env, jObj, method); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("xDestroy() callback"); |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
}else{ |
|
|
|
|
|
S3JniExceptionClear; |
|
|
} |
|
|
} |
|
|
} |
|
|
#define s3jni_call_xDestroy(JOBJ) s3jni__call_xDestroy(env, (JOBJ)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src, |
|
|
S3JniHook * const dest ){ |
|
|
S3JniHook_mutex_enter; |
|
|
*dest = *src; |
|
|
if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj); |
|
|
if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra); |
|
|
dest->doXDestroy = 0; |
|
|
S3JniHook_mutex_leave; |
|
|
} |
|
|
#define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest) |
|
|
|
|
|
static void S3JniHook__localundup( JNIEnv * const env, S3JniHook * const h ){ |
|
|
S3JniUnrefLocal(h->jObj); |
|
|
S3JniUnrefLocal(h->jExtra); |
|
|
*h = S3JniHook_empty; |
|
|
} |
|
|
#define S3JniHook_localundup(HOOK) S3JniHook__localundup(env, &(HOOK)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s){ |
|
|
if( s->jObj ){ |
|
|
if( s->doXDestroy ){ |
|
|
s3jni_call_xDestroy(s->jObj); |
|
|
} |
|
|
S3JniUnrefGlobal(s->jObj); |
|
|
S3JniUnrefGlobal(s->jExtra); |
|
|
}else{ |
|
|
assert( !s->jExtra ); |
|
|
} |
|
|
*s = S3JniHook_empty; |
|
|
} |
|
|
#define S3JniHook_unref(hook) S3JniHook__unref(env, (hook)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniHook *S3JniHook__alloc(JNIEnv * const env){ |
|
|
S3JniHook * p = 0; |
|
|
S3JniHook_mutex_enter; |
|
|
if( SJG.hook.aFree ){ |
|
|
p = SJG.hook.aFree; |
|
|
SJG.hook.aFree = p->pNext; |
|
|
p->pNext = 0; |
|
|
s3jni_incr(&SJG.metrics.nHookRecycled); |
|
|
} |
|
|
S3JniHook_mutex_leave; |
|
|
if( 0==p ){ |
|
|
p = s3jni_malloc(sizeof(S3JniHook)); |
|
|
if( p ){ |
|
|
s3jni_incr(&SJG.metrics.nHookAlloc); |
|
|
} |
|
|
} |
|
|
if( p ){ |
|
|
*p = S3JniHook_empty; |
|
|
} |
|
|
return p; |
|
|
} |
|
|
#define S3JniHook_alloc() S3JniHook__alloc(env) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p){ |
|
|
if(p){ |
|
|
assert( !p->pNext ); |
|
|
S3JniHook_unref(p); |
|
|
S3JniHook_mutex_enter; |
|
|
p->pNext = SJG.hook.aFree; |
|
|
SJG.hook.aFree = p; |
|
|
S3JniHook_mutex_leave; |
|
|
} |
|
|
} |
|
|
#define S3JniHook_free(hook) S3JniHook__free(env, hook) |
|
|
|
|
|
#if 0 |
|
|
|
|
|
static void S3JniHook__free_unlocked(JNIEnv * const env, S3JniHook * const p){ |
|
|
if(p){ |
|
|
assert( !p->pNext ); |
|
|
assert( p->pNext != SJG.hook.aFree ); |
|
|
S3JniHook_unref(p); |
|
|
p->pNext = SJG.hook.aFree; |
|
|
SJG.hook.aFree = p; |
|
|
} |
|
|
} |
|
|
#define S3JniHook_free_unlocked(hook) S3JniHook__free_unlocked(env, hook) |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){ |
|
|
S3JniDb_mutex_assertLocker; |
|
|
sqlite3_free( s->zMainDbName ); |
|
|
#define UNHOOK(MEMBER) \ |
|
|
S3JniHook_unref(&s->hooks.MEMBER) |
|
|
UNHOOK(auth); |
|
|
UNHOOK(busyHandler); |
|
|
UNHOOK(collationNeeded); |
|
|
UNHOOK(commit); |
|
|
UNHOOK(progress); |
|
|
UNHOOK(rollback); |
|
|
UNHOOK(trace); |
|
|
UNHOOK(update); |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
UNHOOK(preUpdate); |
|
|
#endif |
|
|
#undef UNHOOK |
|
|
S3JniUnrefGlobal(s->jDb); |
|
|
memset(s, 0, sizeof(S3JniDb)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){ |
|
|
assert( s ); |
|
|
S3JniDb_mutex_assertLocker; |
|
|
if( s ){ |
|
|
S3JniDb_clear(env, s); |
|
|
s->pNext = SJG.perDb.aFree; |
|
|
SJG.perDb.aFree = s; |
|
|
} |
|
|
} |
|
|
#define S3JniDb_set_aside_unlocked(JniDb) S3JniDb__set_aside_unlocked(env, JniDb) |
|
|
|
|
|
static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){ |
|
|
S3JniDb_mutex_enter; |
|
|
S3JniDb_set_aside_unlocked(s); |
|
|
S3JniDb_mutex_leave; |
|
|
} |
|
|
#define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int S3JniEnv_uncache(JNIEnv * const env){ |
|
|
struct S3JniEnv * row; |
|
|
struct S3JniEnv * pPrev = 0; |
|
|
|
|
|
S3JniEnv_mutex_assertLocked; |
|
|
row = SJG.envCache.aHead; |
|
|
for( ; row; pPrev = row, row = row->pNext ){ |
|
|
if( row->env == env ){ |
|
|
break; |
|
|
} |
|
|
} |
|
|
if( !row ){ |
|
|
return 0; |
|
|
} |
|
|
if( pPrev) pPrev->pNext = row->pNext; |
|
|
else{ |
|
|
assert( SJG.envCache.aHead == row ); |
|
|
SJG.envCache.aHead = row->pNext; |
|
|
} |
|
|
memset(row, 0, sizeof(S3JniEnv)); |
|
|
row->pNext = SJG.envCache.aFree; |
|
|
SJG.envCache.aFree = row; |
|
|
return 1; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniNphOp * s3jni__nphop(JNIEnv * const env, S3JniNphOp const* pRef){ |
|
|
S3JniNphOp * const pNC = &SJG.nph.list[pRef->index]; |
|
|
|
|
|
assert( (void*)pRef>=(void*)&S3JniNphOps && (void*)pRef<(void*)(&S3JniNphOps + 1) |
|
|
&& "pRef is out of range" ); |
|
|
assert( pRef->index>=0 |
|
|
&& (pRef->index < (sizeof(S3JniNphOps) / sizeof(S3JniNphOp))) |
|
|
&& "pRef->index is out of range" ); |
|
|
if( !pNC->klazz ){ |
|
|
S3JniNph_mutex_enter; |
|
|
if( !pNC->klazz ){ |
|
|
jclass const klazz = (*env)->FindClass(env, pRef->zName); |
|
|
|
|
|
S3JniExceptionIsFatal("FindClass() unexpectedly threw"); |
|
|
pNC->klazz = S3JniRefGlobal(klazz); |
|
|
} |
|
|
S3JniNph_mutex_leave; |
|
|
} |
|
|
assert( pNC->klazz ); |
|
|
return pNC; |
|
|
} |
|
|
|
|
|
#define s3jni_nphop(PRef) s3jni__nphop(env, PRef) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphOp const* pRef){ |
|
|
S3JniNphOp * const pNC = s3jni_nphop(pRef); |
|
|
|
|
|
if( !pNC->fidValue ){ |
|
|
S3JniNph_mutex_enter; |
|
|
if( !pNC->fidValue ){ |
|
|
pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz, |
|
|
pRef->zMember, pRef->zTypeSig); |
|
|
S3JniExceptionIsFatal("Code maintenance required: missing " |
|
|
"required S3JniNphOp::fidValue."); |
|
|
} |
|
|
S3JniNph_mutex_leave; |
|
|
} |
|
|
assert( pNC->fidValue ); |
|
|
return pNC->fidValue; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void NativePointerHolder__set(JNIEnv * const env, S3JniNphOp const* pRef, |
|
|
jobject jNph, const void * p){ |
|
|
assert( jNph ); |
|
|
(*env)->SetLongField(env, jNph, s3jni_nphop_field(env, pRef), |
|
|
S3JniCast_P2L(p)); |
|
|
S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer."); |
|
|
} |
|
|
|
|
|
#define NativePointerHolder_set(PREF,JNPH,P) \ |
|
|
NativePointerHolder__set(env, PREF, JNPH, P) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void * NativePointerHolder__get(JNIEnv * env, jobject jNph, |
|
|
S3JniNphOp const* pRef){ |
|
|
void * rv = 0; |
|
|
if( jNph ){ |
|
|
rv = S3JniCast_L2P( |
|
|
(*env)->GetLongField(env, jNph, s3jni_nphop_field(env, pRef)) |
|
|
); |
|
|
S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer."); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
#define NativePointerHolder_get(JOBJ,NPHREF) \ |
|
|
NativePointerHolder__get(env, (JOBJ), (NPHREF)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define PtrGet_T(T,JOBJ) (T*)NativePointerHolder_get((JOBJ), S3JniNph(T)) |
|
|
#define PtrGet_sqlite3(JOBJ) PtrGet_T(sqlite3, (JOBJ)) |
|
|
#define PtrGet_sqlite3_backup(JOBJ) PtrGet_T(sqlite3_backup, (JOBJ)) |
|
|
#define PtrGet_sqlite3_blob(JOBJ) PtrGet_T(sqlite3_blob, (JOBJ)) |
|
|
#define PtrGet_sqlite3_context(JOBJ) PtrGet_T(sqlite3_context, (JOBJ)) |
|
|
#define PtrGet_sqlite3_stmt(JOBJ) PtrGet_T(sqlite3_stmt, (JOBJ)) |
|
|
#define PtrGet_sqlite3_value(JOBJ) PtrGet_T(sqlite3_value, (JOBJ)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)((JLongAsPtr))) |
|
|
#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,(JLongAsPtr)) |
|
|
#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,(JLongAsPtr)) |
|
|
#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,(JLongAsPtr)) |
|
|
#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,(JLongAsPtr)) |
|
|
#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,(JLongAsPtr)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){ |
|
|
S3JniDb * rv = 0; |
|
|
S3JniDb_mutex_enter; |
|
|
if( SJG.perDb.aFree ){ |
|
|
rv = SJG.perDb.aFree; |
|
|
SJG.perDb.aFree = rv->pNext; |
|
|
rv->pNext = 0; |
|
|
s3jni_incr( &SJG.metrics.nPdbRecycled ); |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
if( 0==rv ){ |
|
|
rv = s3jni_malloc(sizeof(S3JniDb)); |
|
|
if( rv ){ |
|
|
s3jni_incr( &SJG.metrics.nPdbAlloc ); |
|
|
} |
|
|
} |
|
|
if( rv ){ |
|
|
memset(rv, 0, sizeof(S3JniDb)); |
|
|
rv->jDb = S3JniRefGlobal(jDb); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){ |
|
|
sqlite3 * const pDb = jDb ? PtrGet_sqlite3(jDb) : 0; |
|
|
return pDb ? S3JniDb_from_clientdata(pDb) : 0; |
|
|
} |
|
|
#define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniDb_xDestroy(void *p){ |
|
|
S3JniDeclLocal_env; |
|
|
S3JniDb * const ps = p; |
|
|
assert( !ps->pNext && "Else ps is already in the free-list."); |
|
|
S3JniDb_set_aside(ps); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniDb_from_c(sqlite3Ptr) \ |
|
|
((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0) |
|
|
#define S3JniDb_from_jlong(sqlite3PtrAsLong) \ |
|
|
S3JniDb_from_c(LongPtrGet_T(sqlite3,sqlite3PtrAsLong)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int S3JniAutoExtension_init(JNIEnv *const env, |
|
|
S3JniAutoExtension * const ax, |
|
|
jobject const jAutoExt){ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jAutoExt); |
|
|
|
|
|
S3JniAutoExt_mutex_assertLocker; |
|
|
*ax = S3JniHook_empty; |
|
|
ax->midCallback = (*env)->GetMethodID(env, klazz, "call", |
|
|
"(Lorg/sqlite/jni/capi/sqlite3;)I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniExceptionWarnIgnore; |
|
|
if( !ax->midCallback ){ |
|
|
S3JniAutoExtension_clear(ax); |
|
|
return SQLITE_ERROR; |
|
|
} |
|
|
ax->jObj = S3JniRefGlobal(jAutoExt); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_Bool(JNIEnv * const env, jobject const jOut, |
|
|
int v){ |
|
|
(*env)->SetBooleanField(env, jOut, s3jni_nphop_field( |
|
|
env, S3JniNph(OutputPointer_Bool) |
|
|
), v ? JNI_TRUE : JNI_FALSE ); |
|
|
S3JniExceptionIsFatal("Cannot set OutputPointer.Bool.value"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, |
|
|
int v){ |
|
|
(*env)->SetIntField(env, jOut, s3jni_nphop_field( |
|
|
env, S3JniNph(OutputPointer_Int32) |
|
|
), (jint)v); |
|
|
S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, |
|
|
jlong v){ |
|
|
(*env)->SetLongField(env, jOut, s3jni_nphop_field( |
|
|
env, S3JniNph(OutputPointer_Int64) |
|
|
), v); |
|
|
S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_obj(JNIEnv * const env, |
|
|
S3JniNphOp const * const pRef, |
|
|
jobject const jOut, |
|
|
jobject v){ |
|
|
(*env)->SetObjectField(env, jOut, s3jni_nphop_field(env, pRef), v); |
|
|
S3JniExceptionIsFatal("Cannot set OutputPointer.T.value"); |
|
|
} |
|
|
|
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
#if 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut, |
|
|
jbyteArray const v){ |
|
|
OutputPointer_set_obj(env, S3JniNph(OutputPointer_ByteArray), jOut, v); |
|
|
} |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut, |
|
|
jstring const v){ |
|
|
OutputPointer_set_obj(env, S3JniNph(OutputPointer_String), jOut, v); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int encodingTypeIsValid(int eTextRep){ |
|
|
switch( eTextRep ){ |
|
|
case SQLITE_UTF8: case SQLITE_UTF16: |
|
|
case SQLITE_UTF16LE: case SQLITE_UTF16BE: |
|
|
return 1; |
|
|
default: |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static const char * const s3jni__value_jref_key = "org.sqlite.jni.capi.ResultJavaVal"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3Jni_jobject_finalizer(void *v){ |
|
|
if( v ){ |
|
|
S3JniDeclLocal_env; |
|
|
S3JniUnrefGlobal((jobject)v); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject NativePointerHolder_new(JNIEnv * const env, |
|
|
S3JniNphOp const * pRef, |
|
|
const void * pNative){ |
|
|
jobject rv = 0; |
|
|
S3JniNphOp * const pNC = s3jni_nphop(pRef); |
|
|
if( !pNC->midCtor ){ |
|
|
S3JniNph_mutex_enter; |
|
|
if( !pNC->midCtor ){ |
|
|
pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "<init>", "()V"); |
|
|
S3JniExceptionIsFatal("Cannot find constructor for class."); |
|
|
} |
|
|
S3JniNph_mutex_leave; |
|
|
} |
|
|
rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor); |
|
|
S3JniExceptionIsFatal("No-arg constructor threw."); |
|
|
s3jni_oom_check(rv); |
|
|
if( rv ) NativePointerHolder_set(pRef, rv, pNative); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
static inline jobject new_java_sqlite3(JNIEnv * const env, sqlite3 *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3), sv); |
|
|
} |
|
|
static inline jobject new_java_sqlite3_backup(JNIEnv * const env, sqlite3_backup *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3_backup), sv); |
|
|
} |
|
|
static inline jobject new_java_sqlite3_blob(JNIEnv * const env, sqlite3_blob *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3_blob), sv); |
|
|
} |
|
|
static inline jobject new_java_sqlite3_context(JNIEnv * const env, sqlite3_context *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3_context), sv); |
|
|
} |
|
|
static inline jobject new_java_sqlite3_stmt(JNIEnv * const env, sqlite3_stmt *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3_stmt), sv); |
|
|
} |
|
|
static inline jobject new_java_sqlite3_value(JNIEnv * const env, sqlite3_value *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(sqlite3_value), sv); |
|
|
} |
|
|
|
|
|
|
|
|
typedef void (*udf_xFunc_f)(sqlite3_context*,int,sqlite3_value**); |
|
|
typedef void (*udf_xStep_f)(sqlite3_context*,int,sqlite3_value**); |
|
|
typedef void (*udf_xFinal_f)(sqlite3_context*); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){ |
|
|
S3JniUdf * s = 0; |
|
|
|
|
|
S3JniGlobal_mutex_enter; |
|
|
s3jni_incr(&SJG.metrics.nMutexUdf); |
|
|
if( SJG.udf.aFree ){ |
|
|
s = SJG.udf.aFree; |
|
|
SJG.udf.aFree = s->pNext; |
|
|
s->pNext = 0; |
|
|
s3jni_incr(&SJG.metrics.nUdfRecycled); |
|
|
} |
|
|
S3JniGlobal_mutex_leave; |
|
|
if( !s ){ |
|
|
s = s3jni_malloc( sizeof(*s)); |
|
|
s3jni_incr(&SJG.metrics.nUdfAlloc); |
|
|
} |
|
|
if( s ){ |
|
|
const char * zFSI = |
|
|
"(Lorg/sqlite/jni/capi/sqlite3_context;[Lorg/sqlite/jni/capi/sqlite3_value;)V"; |
|
|
const char * zFV = |
|
|
"(Lorg/sqlite/jni/capi/sqlite3_context;)V"; |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jObj); |
|
|
|
|
|
memset(s, 0, sizeof(*s)); |
|
|
s->jObj = S3JniRefGlobal(jObj); |
|
|
|
|
|
#define FGET(FuncName,FuncSig,Field) \ |
|
|
s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncSig); \ |
|
|
if( !s->Field ) (*env)->ExceptionClear(env) |
|
|
|
|
|
FGET("xFunc", zFSI, jmidxFunc); |
|
|
FGET("xStep", zFSI, jmidxStep); |
|
|
FGET("xFinal", zFV, jmidxFinal); |
|
|
FGET("xValue", zFV, jmidxValue); |
|
|
FGET("xInverse", zFSI, jmidxInverse); |
|
|
#undef FGET |
|
|
|
|
|
S3JniUnrefLocal(klazz); |
|
|
if( s->jmidxFunc ) s->type = UDF_SCALAR; |
|
|
else if( s->jmidxStep && s->jmidxFinal ){ |
|
|
s->type = (s->jmidxValue && s->jmidxInverse) |
|
|
? UDF_WINDOW : UDF_AGGREGATE; |
|
|
}else{ |
|
|
s->type = UDF_UNKNOWN_TYPE; |
|
|
} |
|
|
} |
|
|
return s; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s, |
|
|
int cacheIt){ |
|
|
assert( !s->pNext ); |
|
|
if( s->jObj ){ |
|
|
s3jni_call_xDestroy(s->jObj); |
|
|
S3JniUnrefGlobal(s->jObj); |
|
|
sqlite3_free(s->zFuncName); |
|
|
assert( !s->pNext ); |
|
|
memset(s, 0, sizeof(*s)); |
|
|
} |
|
|
if( cacheIt ){ |
|
|
S3JniGlobal_mutex_enter; |
|
|
s->pNext = S3JniGlobal.udf.aFree; |
|
|
S3JniGlobal.udf.aFree = s; |
|
|
S3JniGlobal_mutex_leave; |
|
|
}else{ |
|
|
sqlite3_free( s ); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static void S3JniUdf_finalizer(void * s){ |
|
|
S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int udf_args(JNIEnv *env, |
|
|
sqlite3_context * const cx, |
|
|
int argc, sqlite3_value**argv, |
|
|
jobject * jCx, jobjectArray *jArgv){ |
|
|
jobjectArray ja = 0; |
|
|
jobject jcx = new_java_sqlite3_context(env, cx); |
|
|
jint i; |
|
|
*jCx = 0; |
|
|
*jArgv = 0; |
|
|
if( !jcx ) goto error_oom; |
|
|
ja = (*env)->NewObjectArray( |
|
|
env, argc, s3jni_nphop(S3JniNph(sqlite3_value))->klazz, |
|
|
NULL); |
|
|
s3jni_oom_check( ja ); |
|
|
if( !ja ) goto error_oom; |
|
|
for(i = 0; i < argc; ++i){ |
|
|
jobject jsv = new_java_sqlite3_value(env, argv[i]); |
|
|
if( !jsv ) goto error_oom; |
|
|
(*env)->SetObjectArrayElement(env, ja, i, jsv); |
|
|
S3JniUnrefLocal(jsv); |
|
|
} |
|
|
*jCx = jcx; |
|
|
*jArgv = ja; |
|
|
return 0; |
|
|
error_oom: |
|
|
S3JniUnrefLocal(jcx); |
|
|
S3JniUnrefLocal(ja); |
|
|
return SQLITE_NOMEM; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void udf_unargs(JNIEnv *env, jobject jCx, int argc, jobjectArray jArgv){ |
|
|
int i = 0; |
|
|
assert(jCx); |
|
|
NativePointerHolder_set(S3JniNph(sqlite3_context), jCx, 0); |
|
|
for( ; i < argc; ++i ){ |
|
|
jobject jsv = (*env)->GetObjectArrayElement(env, jArgv, i); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert(jsv && "Someone illegally modified a UDF argument array."); |
|
|
if( jsv ){ |
|
|
NativePointerHolder_set(S3JniNph(sqlite3_value), jsv, 0); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int udf_report_exception(JNIEnv * const env, int translateToErr, |
|
|
sqlite3_context * cx, |
|
|
const char *zFuncName, const char *zFuncType ){ |
|
|
jthrowable const ex = (*env)->ExceptionOccurred(env); |
|
|
int rc = SQLITE_ERROR; |
|
|
|
|
|
assert(ex && "This must only be called when a Java exception is pending."); |
|
|
if( translateToErr ){ |
|
|
char * zMsg; |
|
|
char * z; |
|
|
|
|
|
S3JniExceptionClear; |
|
|
zMsg = s3jni_exception_error_msg(env, ex); |
|
|
z = sqlite3_mprintf("Client-defined SQL function %s.%s() threw: %s", |
|
|
zFuncName ? zFuncName : "<unnamed>", zFuncType, |
|
|
zMsg ? zMsg : "Unknown exception" ); |
|
|
sqlite3_free(zMsg); |
|
|
if( z ){ |
|
|
sqlite3_result_error(cx, z, -1); |
|
|
sqlite3_free(z); |
|
|
}else{ |
|
|
sqlite3_result_error_nomem(cx); |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
}else{ |
|
|
S3JniExceptionWarnCallbackThrew("client-defined SQL function"); |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
S3JniUnrefLocal(ex); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int udf_xFSI(sqlite3_context* const pCx, int argc, |
|
|
sqlite3_value** const argv, S3JniUdf * const s, |
|
|
jmethodID xMethodID, const char * const zFuncType){ |
|
|
S3JniDeclLocal_env; |
|
|
jobject jcx = 0 ; |
|
|
jobjectArray jargv = 0 ; |
|
|
int rc = udf_args(env, pCx, argc, argv, &jcx, &jargv); |
|
|
if( 0 == rc ){ |
|
|
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx, jargv); |
|
|
S3JniIfThrew{ |
|
|
rc = udf_report_exception(env, 'F'==zFuncType[1], pCx, |
|
|
s->zFuncName, zFuncType); |
|
|
} |
|
|
udf_unargs(env, jcx, argc, jargv); |
|
|
} |
|
|
S3JniUnrefLocal(jcx); |
|
|
S3JniUnrefLocal(jargv); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int udf_xFV(sqlite3_context* cx, S3JniUdf * s, |
|
|
jmethodID xMethodID, |
|
|
const char *zFuncType){ |
|
|
S3JniDeclLocal_env; |
|
|
jobject jcx = new_java_sqlite3_context(env, cx); |
|
|
int rc = 0; |
|
|
int const isFinal = 'F'==zFuncType[1]; |
|
|
|
|
|
if( jcx ){ |
|
|
(*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx); |
|
|
S3JniIfThrew{ |
|
|
rc = udf_report_exception(env, isFinal, cx, s->zFuncName, |
|
|
zFuncType); |
|
|
} |
|
|
udf_unargs(env, jcx, 0, 0); |
|
|
S3JniUnrefLocal(jcx); |
|
|
}else{ |
|
|
if( isFinal ) sqlite3_result_error_nomem(cx); |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
static void udf_xFunc(sqlite3_context* cx, int argc, |
|
|
sqlite3_value** argv){ |
|
|
S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); |
|
|
s3jni_incr( &SJG.metrics.udf.nFunc ); |
|
|
udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc"); |
|
|
} |
|
|
|
|
|
static void udf_xStep(sqlite3_context* cx, int argc, |
|
|
sqlite3_value** argv){ |
|
|
S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); |
|
|
s3jni_incr( &SJG.metrics.udf.nStep ); |
|
|
udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep"); |
|
|
} |
|
|
|
|
|
static void udf_xFinal(sqlite3_context* cx){ |
|
|
S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); |
|
|
s3jni_incr( &SJG.metrics.udf.nFinal ); |
|
|
udf_xFV(cx, s, s->jmidxFinal, "xFinal"); |
|
|
} |
|
|
|
|
|
static void udf_xValue(sqlite3_context* cx){ |
|
|
S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); |
|
|
s3jni_incr( &SJG.metrics.udf.nValue ); |
|
|
udf_xFV(cx, s, s->jmidxValue, "xValue"); |
|
|
} |
|
|
|
|
|
static void udf_xInverse(sqlite3_context* cx, int argc, |
|
|
sqlite3_value** argv){ |
|
|
S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx); |
|
|
s3jni_incr( &SJG.metrics.udf.nInverse ); |
|
|
udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse"); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define WRAP_INT_VOID(JniNameSuffix,CName) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass){ \ |
|
|
return (jint)CName(); \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT_INT(JniNameSuffix,CName) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jint arg){ \ |
|
|
return (jint)CName((int)arg); \ |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define WRAP_MUTF8_VOID(JniNameSuffix,CName) \ |
|
|
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \ |
|
|
jstring const rv = (*env)->NewStringUTF( env, CName() ); \ |
|
|
s3jni_oom_check(rv); \ |
|
|
return rv; \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT_STMT(JniNameSuffix,CName) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt){ \ |
|
|
return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt)); \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT_STMT_INT(JniNameSuffix,CName) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint n){ \ |
|
|
return (jint)CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)n); \ |
|
|
} |
|
|
|
|
|
#define WRAP_BOOL_STMT(JniNameSuffix,CName) \ |
|
|
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jobject jStmt){ \ |
|
|
return CName(PtrGet_sqlite3_stmt(jStmt)) ? JNI_TRUE : JNI_FALSE; \ |
|
|
} |
|
|
|
|
|
#define WRAP_STR_STMT_INT(JniNameSuffix,CName) \ |
|
|
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpStmt, jint ndx){ \ |
|
|
return s3jni_utf8_to_jstring( \ |
|
|
CName(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx), \ |
|
|
-1); \ |
|
|
} |
|
|
|
|
|
#define WRAP_BOOL_DB(JniNameSuffix,CName) \ |
|
|
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ |
|
|
return CName(LongPtrGet_sqlite3(jpDb)) ? JNI_TRUE : JNI_FALSE; \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT_DB(JniNameSuffix,CName) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ |
|
|
return (jint)CName(LongPtrGet_sqlite3(jpDb)); \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT64_DB(JniNameSuffix,CName) \ |
|
|
JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jlong jpDb){ \ |
|
|
return (jlong)CName(LongPtrGet_sqlite3(jpDb)); \ |
|
|
} |
|
|
|
|
|
#define WRAP_STR_DB_INT(JniNameSuffix,CName) \ |
|
|
JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jlong jpDb, jint ndx){ \ |
|
|
return s3jni_utf8_to_jstring( \ |
|
|
CName(LongPtrGet_sqlite3(jpDb), (int)ndx), \ |
|
|
-1); \ |
|
|
} |
|
|
|
|
|
#define WRAP_INT_SVALUE(JniNameSuffix,CName,DfltOnNull) \ |
|
|
JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSValue); \ |
|
|
return (jint)(sv ? CName(sv): DfltOnNull); \ |
|
|
} |
|
|
|
|
|
#define WRAP_BOOL_SVALUE(JniNameSuffix,CName,DfltOnNull) \ |
|
|
JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jlong jpSValue){ \ |
|
|
sqlite3_value * const sv = LongPtrGet_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) |
|
|
WRAP_INT_STMT(1clear_1bindings, sqlite3_clear_bindings) |
|
|
WRAP_INT_STMT_INT(1column_1bytes, sqlite3_column_bytes) |
|
|
WRAP_INT_STMT_INT(1column_1bytes16, sqlite3_column_bytes16) |
|
|
WRAP_INT_STMT(1column_1count, sqlite3_column_count) |
|
|
WRAP_STR_STMT_INT(1column_1decltype, sqlite3_column_decltype) |
|
|
WRAP_STR_STMT_INT(1column_1name, sqlite3_column_name) |
|
|
#ifdef SQLITE_ENABLE_COLUMN_METADATA |
|
|
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) |
|
|
#endif |
|
|
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) |
|
|
WRAP_INT_VOID(1keyword_1count, sqlite3_keyword_count) |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
WRAP_INT_DB(1preupdate_1blobwrite, sqlite3_preupdate_blobwrite) |
|
|
WRAP_INT_DB(1preupdate_1count, sqlite3_preupdate_count) |
|
|
WRAP_INT_DB(1preupdate_1depth, sqlite3_preupdate_depth) |
|
|
#endif |
|
|
WRAP_INT_INT(1release_1memory, sqlite3_release_memory) |
|
|
WRAP_INT_INT(1sleep, sqlite3_sleep) |
|
|
WRAP_MUTF8_VOID(1sourceid, sqlite3_sourceid) |
|
|
WRAP_BOOL_STMT(1stmt_1busy, sqlite3_stmt_busy) |
|
|
WRAP_INT_STMT_INT(1stmt_1explain, sqlite3_stmt_explain) |
|
|
WRAP_INT_STMT(1stmt_1isexplain, sqlite3_stmt_isexplain) |
|
|
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_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 |
|
|
#undef WRAP_INT_DB |
|
|
#undef WRAP_INT_INT |
|
|
#undef WRAP_INT_STMT |
|
|
#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); |
|
|
void * const p = pCx |
|
|
? sqlite3_aggregate_context(pCx, (int)(initialize |
|
|
? (int)sizeof(void*) |
|
|
: 0)) |
|
|
: 0; |
|
|
return S3JniCast_P2L(p); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr, |
|
|
const struct sqlite3_api_routines *ignored){ |
|
|
int rc = 0; |
|
|
unsigned i, go = 1; |
|
|
JNIEnv * env = 0; |
|
|
S3JniDb * ps; |
|
|
S3JniEnv * jc; |
|
|
|
|
|
if( 0==SJG.autoExt.nExt ) return 0; |
|
|
env = s3jni_env(); |
|
|
jc = S3JniEnv_get(); |
|
|
S3JniDb_mutex_enter; |
|
|
ps = jc->pdbOpening ? jc->pdbOpening : S3JniDb_from_c(pDb); |
|
|
if( !ps ){ |
|
|
*pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in " |
|
|
"auto-extension runner."); |
|
|
S3JniDb_mutex_leave; |
|
|
return SQLITE_ERROR; |
|
|
} |
|
|
assert( ps->jDb ); |
|
|
if( !ps->pDb ){ |
|
|
assert( jc->pdbOpening == ps ); |
|
|
rc = sqlite3_set_clientdata(pDb, S3JniDb_clientdata_key, |
|
|
ps, 0 |
|
|
); |
|
|
if( rc ){ |
|
|
S3JniDb_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
} |
|
|
else{ |
|
|
assert( ps == jc->pdbOpening ); |
|
|
jc->pdbOpening = 0; |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
NativePointerHolder_set(S3JniNph(sqlite3), ps->jDb, pDb) |
|
|
|
|
|
; |
|
|
ps->pDb = pDb; |
|
|
for( i = 0; go && 0==rc; ++i ){ |
|
|
S3JniAutoExtension ax = S3JniHook_empty |
|
|
|
|
|
|
|
|
|
|
|
; |
|
|
S3JniAutoExt_mutex_enter; |
|
|
if( i >= SJG.autoExt.nExt ){ |
|
|
go = 0; |
|
|
}else{ |
|
|
S3JniHook_localdup(&SJG.autoExt.aExt[i], &ax); |
|
|
} |
|
|
S3JniAutoExt_mutex_leave; |
|
|
if( ax.jObj ){ |
|
|
rc = (*env)->CallIntMethod(env, ax.jObj, ax.midCallback, ps->jDb); |
|
|
S3JniHook_localundup(ax); |
|
|
S3JniIfThrew { |
|
|
jthrowable const ex = (*env)->ExceptionOccurred(env); |
|
|
char * zMsg; |
|
|
S3JniExceptionClear; |
|
|
zMsg = s3jni_exception_error_msg(env, ex); |
|
|
S3JniUnrefLocal(ex); |
|
|
*pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg); |
|
|
sqlite3_free(zMsg); |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)( |
|
|
JniArgsEnvClass, jobject jAutoExt |
|
|
){ |
|
|
int i; |
|
|
S3JniAutoExtension * ax = 0; |
|
|
int rc = 0; |
|
|
|
|
|
if( !jAutoExt ) return SQLITE_MISUSE; |
|
|
S3JniAutoExt_mutex_enter; |
|
|
for( i = 0; i < SJG.autoExt.nExt; ++i ){ |
|
|
|
|
|
ax = &SJG.autoExt.aExt[i]; |
|
|
if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ |
|
|
|
|
|
S3JniAutoExt_mutex_leave; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
if( i == SJG.autoExt.nExt ){ |
|
|
assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc ); |
|
|
if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){ |
|
|
|
|
|
unsigned n = 1 + SJG.autoExt.nAlloc; |
|
|
S3JniAutoExtension * const aNew = |
|
|
s3jni_realloc( SJG.autoExt.aExt, n * sizeof(*ax) ); |
|
|
if( !aNew ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
}else{ |
|
|
SJG.autoExt.aExt = aNew; |
|
|
++SJG.autoExt.nAlloc; |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
ax = &SJG.autoExt.aExt[SJG.autoExt.nExt]; |
|
|
rc = S3JniAutoExtension_init(env, ax, jAutoExt); |
|
|
assert( rc ? (0==ax->jObj && 0==ax->midCallback) |
|
|
: (0!=ax->jObj && 0!=ax->midCallback) ); |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
static int once = 0; |
|
|
if( 0==once && ++once ){ |
|
|
rc = sqlite3_auto_extension( |
|
|
(void(*)(void))s3jni_run_java_auto_extensions |
|
|
|
|
|
|
|
|
|
|
|
); |
|
|
if( rc ){ |
|
|
assert( ax ); |
|
|
S3JniAutoExtension_clear(ax); |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
++SJG.autoExt.nExt; |
|
|
} |
|
|
} |
|
|
S3JniAutoExt_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_backup_finish(),jint,1backup_1finish)( |
|
|
JniArgsEnvClass, jlong jpBack |
|
|
){ |
|
|
int rc = 0; |
|
|
if( jpBack!=0 ){ |
|
|
rc = sqlite3_backup_finish( LongPtrGet_sqlite3_backup(jpBack) ); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_backup_init(),jobject,1backup_1init)( |
|
|
JniArgsEnvClass, jlong jpDbDest, jstring jTDest, |
|
|
jlong jpDbSrc, jstring jTSrc |
|
|
){ |
|
|
sqlite3 * const pDest = LongPtrGet_sqlite3(jpDbDest); |
|
|
sqlite3 * const pSrc = LongPtrGet_sqlite3(jpDbSrc); |
|
|
char * const zDest = s3jni_jstring_to_utf8(jTDest, 0); |
|
|
char * const zSrc = s3jni_jstring_to_utf8(jTSrc, 0); |
|
|
jobject rv = 0; |
|
|
|
|
|
if( pDest && pSrc && zDest && zSrc ){ |
|
|
sqlite3_backup * const pB = |
|
|
sqlite3_backup_init(pDest, zDest, pSrc, zSrc); |
|
|
if( pB ){ |
|
|
rv = new_java_sqlite3_backup(env, pB); |
|
|
if( !rv ){ |
|
|
sqlite3_backup_finish( pB ); |
|
|
} |
|
|
} |
|
|
} |
|
|
sqlite3_free(zDest); |
|
|
sqlite3_free(zSrc); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_backup_pagecount(),jint,1backup_1pagecount)( |
|
|
JniArgsEnvClass, jlong jpBack |
|
|
){ |
|
|
return sqlite3_backup_pagecount(LongPtrGet_sqlite3_backup(jpBack)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_backup_remaining(),jint,1backup_1remaining)( |
|
|
JniArgsEnvClass, jlong jpBack |
|
|
){ |
|
|
return sqlite3_backup_remaining(LongPtrGet_sqlite3_backup(jpBack)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_backup_step(),jint,1backup_1step)( |
|
|
JniArgsEnvClass, jlong jpBack, jint nPage |
|
|
){ |
|
|
return sqlite3_backup_step(LongPtrGet_sqlite3_backup(jpBack), (int)nPage); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jbyteArray baData, jint nMax |
|
|
){ |
|
|
jsize nBA = 0; |
|
|
jbyte * const pBuf = baData ? s3jni_jbyteArray_bytes2(baData, &nBA) : 0; |
|
|
int rc; |
|
|
if( pBuf ){ |
|
|
if( nMax>nBA ){ |
|
|
nMax = nBA; |
|
|
} |
|
|
rc = sqlite3_bind_blob(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, |
|
|
pBuf, (int)nMax, SQLITE_TRANSIENT); |
|
|
s3jni_jbyteArray_release(baData, pBuf); |
|
|
}else{ |
|
|
rc = baData |
|
|
? SQLITE_NOMEM |
|
|
: sqlite3_bind_null( LongPtrGet_sqlite3_stmt(jpStmt), ndx ); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct S3JniNioArgs { |
|
|
jobject jBuf; |
|
|
jint iOffset; |
|
|
jint iHowMany; |
|
|
jint nBuf; |
|
|
void * p; |
|
|
void * pStart; |
|
|
int nOut; |
|
|
}; |
|
|
typedef struct S3JniNioArgs S3JniNioArgs; |
|
|
static const S3JniNioArgs S3JniNioArgs_empty = { |
|
|
0,0,0,0,0,0,0 |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_setup_nio_args( |
|
|
JNIEnv *env, S3JniNioArgs * pArgs, |
|
|
jobject jBuffer, jint iOffset, jint iHowMany |
|
|
){ |
|
|
jlong iEnd = 0; |
|
|
const int bAllowTruncate = iHowMany<0; |
|
|
*pArgs = S3JniNioArgs_empty; |
|
|
pArgs->jBuf = jBuffer; |
|
|
pArgs->iOffset = iOffset; |
|
|
pArgs->iHowMany = iHowMany; |
|
|
assert( SJG.g.byteBuffer.klazz ); |
|
|
if( pArgs->iOffset<0 ){ |
|
|
return SQLITE_ERROR |
|
|
|
|
|
|
|
|
; |
|
|
} |
|
|
s3jni_get_nio_buffer(pArgs->jBuf, &pArgs->p, &pArgs->nBuf); |
|
|
if( !pArgs->p ){ |
|
|
return SQLITE_MISUSE; |
|
|
}else if( pArgs->iOffset>=pArgs->nBuf ){ |
|
|
pArgs->pStart = 0; |
|
|
pArgs->nOut = 0; |
|
|
return 0; |
|
|
} |
|
|
assert( pArgs->nBuf > 0 ); |
|
|
assert( pArgs->iOffset < pArgs->nBuf ); |
|
|
iEnd = pArgs->iHowMany<0 |
|
|
? pArgs->nBuf - pArgs->iOffset |
|
|
: pArgs->iOffset + pArgs->iHowMany; |
|
|
if( iEnd>(jlong)pArgs->nBuf ){ |
|
|
if( bAllowTruncate ){ |
|
|
iEnd = pArgs->nBuf - pArgs->iOffset; |
|
|
}else{ |
|
|
return SQLITE_ERROR |
|
|
|
|
|
; |
|
|
} |
|
|
} |
|
|
if( iEnd - pArgs->iOffset > (jlong)SQLITE_MAX_LENGTH ){ |
|
|
return SQLITE_TOOBIG; |
|
|
} |
|
|
assert( pArgs->iOffset >= 0 ); |
|
|
assert( iEnd > pArgs->iOffset ); |
|
|
pArgs->pStart = pArgs->p + pArgs->iOffset; |
|
|
pArgs->nOut = (int)(iEnd - pArgs->iOffset); |
|
|
assert( pArgs->nOut > 0 ); |
|
|
assert( (pArgs->pStart + pArgs->nOut) <= (pArgs->p + pArgs->nBuf) ); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)( |
|
|
JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer, |
|
|
jint iOffset, jint iN |
|
|
){ |
|
|
sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt); |
|
|
S3JniNioArgs args; |
|
|
int rc; |
|
|
if( !pStmt || !SJG.g.byteBuffer.klazz ) return SQLITE_MISUSE; |
|
|
rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); |
|
|
if(rc){ |
|
|
return rc; |
|
|
}else if( !args.pStart || !args.nOut ){ |
|
|
return sqlite3_bind_null(pStmt, ndx); |
|
|
} |
|
|
return sqlite3_bind_blob( pStmt, (int)ndx, args.pStart, |
|
|
args.nOut, SQLITE_TRANSIENT ); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_double(),jint,1bind_1double)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val |
|
|
){ |
|
|
return (jint)sqlite3_bind_double(LongPtrGet_sqlite3_stmt(jpStmt), |
|
|
(int)ndx, (double)val); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_int(),jint,1bind_1int)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jint val |
|
|
){ |
|
|
return (jint)sqlite3_bind_int(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong val |
|
|
){ |
|
|
return (jint)sqlite3_bind_int64(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jobject val |
|
|
){ |
|
|
sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); |
|
|
int rc = SQLITE_MISUSE; |
|
|
|
|
|
if(pStmt){ |
|
|
jobject const rv = S3JniRefGlobal(val); |
|
|
if( rv ){ |
|
|
rc = sqlite3_bind_pointer(pStmt, ndx, rv, s3jni__value_jref_key, |
|
|
S3Jni_jobject_finalizer); |
|
|
}else if(val){ |
|
|
rc = SQLITE_NOMEM; |
|
|
}else{ |
|
|
rc = sqlite3_bind_null(pStmt, ndx); |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_null(),jint,1bind_1null)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx |
|
|
){ |
|
|
return (jint)sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_parameter_count(),jint,1bind_1parameter_1count)( |
|
|
JniArgsEnvClass, jlong jpStmt |
|
|
){ |
|
|
return (jint)sqlite3_bind_parameter_count(LongPtrGet_sqlite3_stmt(jpStmt)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)( |
|
|
JniArgsEnvClass, jlong jpStmt, jbyteArray jName |
|
|
){ |
|
|
int rc = 0; |
|
|
jbyte * const pBuf = s3jni_jbyteArray_bytes(jName); |
|
|
if( pBuf ){ |
|
|
rc = sqlite3_bind_parameter_index(LongPtrGet_sqlite3_stmt(jpStmt), |
|
|
(const char *)pBuf); |
|
|
s3jni_jbyteArray_release(jName, pBuf); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_parameter_name(),jstring,1bind_1parameter_1name)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx |
|
|
){ |
|
|
const char *z = |
|
|
sqlite3_bind_parameter_name(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx); |
|
|
return z ? s3jni_utf8_to_jstring(z, -1) : 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni__bind_text(int is16, JNIEnv *env, jlong jpStmt, jint ndx, |
|
|
jbyteArray baData, jint nMax){ |
|
|
jsize nBA = 0; |
|
|
jbyte * const pBuf = |
|
|
baData ? s3jni_jbyteArray_bytes2(baData, &nBA) : 0; |
|
|
int rc; |
|
|
if( pBuf ){ |
|
|
if( nMax>nBA ){ |
|
|
nMax = nBA; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rc = is16 |
|
|
? sqlite3_bind_text16(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, |
|
|
pBuf, (int)nMax, SQLITE_TRANSIENT) |
|
|
: sqlite3_bind_text(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx, |
|
|
(const char *)pBuf, |
|
|
(int)nMax, SQLITE_TRANSIENT); |
|
|
}else{ |
|
|
rc = baData |
|
|
? sqlite3_bind_null(LongPtrGet_sqlite3_stmt(jpStmt), (int)ndx) |
|
|
: SQLITE_NOMEM; |
|
|
} |
|
|
s3jni_jbyteArray_release(baData, pBuf); |
|
|
return (jint)rc; |
|
|
|
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_text(),jint,1bind_1text)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jbyteArray baData, jint nMax |
|
|
){ |
|
|
return s3jni__bind_text(0, env, jpStmt, ndx, baData, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_text16(),jint,1bind_1text16)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jbyteArray baData, jint nMax |
|
|
){ |
|
|
return s3jni__bind_text(1, env, jpStmt, ndx, baData, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_value(),jint,1bind_1value)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong jpValue |
|
|
){ |
|
|
int rc = 0; |
|
|
sqlite3_stmt * pStmt = LongPtrGet_sqlite3_stmt(jpStmt); |
|
|
if( pStmt ){ |
|
|
sqlite3_value *v = LongPtrGet_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(LongPtrGet_sqlite3_stmt(jpStmt), |
|
|
(int)ndx, (int)n); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_bind_zeroblob64(),jint,1bind_1zeroblob64)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx, jlong n |
|
|
){ |
|
|
return (jint)sqlite3_bind_zeroblob64(LongPtrGet_sqlite3_stmt(jpStmt), |
|
|
(int)ndx, (sqlite3_uint64)n); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_bytes(),jint,1blob_1bytes)( |
|
|
JniArgsEnvClass, jlong jpBlob |
|
|
){ |
|
|
return sqlite3_blob_bytes(LongPtrGet_sqlite3_blob(jpBlob)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_close(),jint,1blob_1close)( |
|
|
JniArgsEnvClass, jlong jpBlob |
|
|
){ |
|
|
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); |
|
|
return b ? (jint)sqlite3_blob_close(b) : SQLITE_MISUSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_open(),jint,1blob_1open)( |
|
|
JniArgsEnvClass, jlong jpDb, jstring jDbName, jstring jTbl, jstring jCol, |
|
|
jlong jRowId, jint flags, jobject jOut |
|
|
){ |
|
|
sqlite3 * const db = LongPtrGet_sqlite3(jpDb); |
|
|
sqlite3_blob * pBlob = 0; |
|
|
char * zDbName = 0, * zTableName = 0, * zColumnName = 0; |
|
|
int rc; |
|
|
|
|
|
if( !db || !jDbName || !jTbl || !jCol ) return SQLITE_MISUSE; |
|
|
zDbName = s3jni_jstring_to_utf8(jDbName,0); |
|
|
zTableName = zDbName ? s3jni_jstring_to_utf8(jTbl,0) : 0; |
|
|
zColumnName = zTableName ? s3jni_jstring_to_utf8(jCol,0) : 0; |
|
|
rc = zColumnName |
|
|
? sqlite3_blob_open(db, zDbName, zTableName, zColumnName, |
|
|
(sqlite3_int64)jRowId, (int)flags, &pBlob) |
|
|
: SQLITE_NOMEM; |
|
|
if( 0==rc ){ |
|
|
jobject rv = new_java_sqlite3_blob(env, pBlob); |
|
|
if( !rv ){ |
|
|
sqlite3_blob_close(pBlob); |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
OutputPointer_set_obj(env, S3JniNph(OutputPointer_sqlite3_blob), jOut, rv); |
|
|
} |
|
|
sqlite3_free(zDbName); |
|
|
sqlite3_free(zTableName); |
|
|
sqlite3_free(zColumnName); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_read(),jint,1blob_1read)( |
|
|
JniArgsEnvClass, jlong jpBlob, jbyteArray jTgt, jint iOffset |
|
|
){ |
|
|
jbyte * const pBa = s3jni_jbyteArray_bytes(jTgt); |
|
|
int rc = jTgt ? (pBa ? SQLITE_MISUSE : SQLITE_NOMEM) : SQLITE_MISUSE; |
|
|
if( pBa ){ |
|
|
jsize const nTgt = (*env)->GetArrayLength(env, jTgt); |
|
|
rc = sqlite3_blob_read(LongPtrGet_sqlite3_blob(jpBlob), pBa, |
|
|
(int)nTgt, (int)iOffset); |
|
|
if( 0==rc ){ |
|
|
s3jni_jbyteArray_commit(jTgt, pBa); |
|
|
}else{ |
|
|
s3jni_jbyteArray_release(jTgt, pBa); |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_read_nio_buffer(),jint,1blob_1read_1nio_1buffer)( |
|
|
JniArgsEnvClass, jlong jpBlob, jint iSrcOff, jobject jBB, jint iTgtOff, jint iHowMany |
|
|
){ |
|
|
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); |
|
|
S3JniNioArgs args; |
|
|
int rc; |
|
|
if( !b || !SJG.g.byteBuffer.klazz || iHowMany<0 ){ |
|
|
return SQLITE_MISUSE; |
|
|
}else if( iTgtOff<0 || iSrcOff<0 ){ |
|
|
return SQLITE_ERROR |
|
|
; |
|
|
}else if( 0==iHowMany ){ |
|
|
return 0; |
|
|
} |
|
|
rc = s3jni_setup_nio_args(env, &args, jBB, iTgtOff, iHowMany); |
|
|
if(rc){ |
|
|
return rc; |
|
|
}else if( !args.pStart || !args.nOut ){ |
|
|
return 0; |
|
|
} |
|
|
assert( args.iHowMany>0 ); |
|
|
return sqlite3_blob_read( b, args.pStart, (int)args.nOut, (int)iSrcOff ); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_reopen(),jint,1blob_1reopen)( |
|
|
JniArgsEnvClass, jlong jpBlob, jlong iNewRowId |
|
|
){ |
|
|
return (jint)sqlite3_blob_reopen(LongPtrGet_sqlite3_blob(jpBlob), |
|
|
(sqlite3_int64)iNewRowId); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_write(),jint,1blob_1write)( |
|
|
JniArgsEnvClass, jlong jpBlob, jbyteArray jBa, jint iOffset |
|
|
){ |
|
|
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); |
|
|
jbyte * const pBuf = b ? s3jni_jbyteArray_bytes(jBa) : 0; |
|
|
const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jBa) : 0; |
|
|
int rc = SQLITE_MISUSE; |
|
|
if(b && pBuf){ |
|
|
rc = sqlite3_blob_write( b, pBuf, (int)nBA, (int)iOffset ); |
|
|
} |
|
|
s3jni_jbyteArray_release(jBa, pBuf); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_blob_write_nio_buffer(),jint,1blob_1write_1nio_1buffer)( |
|
|
JniArgsEnvClass, jlong jpBlob, jint iTgtOff, jobject jBB, jint iSrcOff, jint iHowMany |
|
|
){ |
|
|
sqlite3_blob * const b = LongPtrGet_sqlite3_blob(jpBlob); |
|
|
S3JniNioArgs args; |
|
|
int rc; |
|
|
if( !b || !SJG.g.byteBuffer.klazz ){ |
|
|
return SQLITE_MISUSE; |
|
|
}else if( iTgtOff<0 || iSrcOff<0 ){ |
|
|
return SQLITE_ERROR |
|
|
; |
|
|
}else if( 0==iHowMany ){ |
|
|
return 0; |
|
|
} |
|
|
rc = s3jni_setup_nio_args(env, &args, jBB, iSrcOff, iHowMany); |
|
|
if(rc){ |
|
|
return rc; |
|
|
}else if( !args.pStart || !args.nOut ){ |
|
|
return 0; |
|
|
} |
|
|
return sqlite3_blob_write( b, args.pStart, (int)args.nOut, (int)iTgtOff ); |
|
|
} |
|
|
|
|
|
|
|
|
static int s3jni_busy_handler(void* pState, int n){ |
|
|
S3JniDb * const ps = (S3JniDb *)pState; |
|
|
int rc = 0; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(&ps->hooks.busyHandler, &hook); |
|
|
if( hook.jObj ){ |
|
|
rc = (*env)->CallIntMethod(env, hook.jObj, |
|
|
hook.midCallback, (jint)n); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback"); |
|
|
rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, |
|
|
"sqlite3_busy_handler() callback threw."); |
|
|
} |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jBusy |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_jlong(jpDb); |
|
|
S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0; |
|
|
S3JniHook hook = S3JniHook_empty; |
|
|
int rc = 0; |
|
|
|
|
|
if( !ps ) return (jint)SQLITE_MISUSE; |
|
|
S3JniDb_mutex_enter; |
|
|
if( jBusy ){ |
|
|
if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){ |
|
|
|
|
|
}else{ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jBusy); |
|
|
hook.jObj = S3JniRefGlobal(jBusy); |
|
|
hook.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
if( jBusy ){ |
|
|
if( hook.jObj ){ |
|
|
rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
*pHook = hook ; |
|
|
hook = S3JniHook_empty; |
|
|
} |
|
|
} |
|
|
}else{ |
|
|
rc = sqlite3_busy_handler(ps->pDb, 0, 0); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
} |
|
|
} |
|
|
} |
|
|
S3JniHook_unref(&hook); |
|
|
S3JniDb_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)( |
|
|
JniArgsEnvClass, jlong jpDb, jint ms |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_jlong(jpDb); |
|
|
int rc = SQLITE_MISUSE; |
|
|
if( ps ){ |
|
|
S3JniDb_mutex_enter; |
|
|
S3JniHook_unref(&ps->hooks.busyHandler); |
|
|
rc = sqlite3_busy_timeout(ps->pDb, (int)ms); |
|
|
S3JniDb_mutex_leave; |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)( |
|
|
JniArgsEnvClass, jobject jAutoExt |
|
|
){ |
|
|
S3JniAutoExtension * ax; |
|
|
jboolean rc = JNI_FALSE; |
|
|
int i; |
|
|
|
|
|
if( !jAutoExt ){ |
|
|
return rc; |
|
|
} |
|
|
S3JniAutoExt_mutex_enter; |
|
|
|
|
|
for( i = SJG.autoExt.nExt-1; i >= 0; --i ){ |
|
|
ax = &SJG.autoExt.aExt[i]; |
|
|
if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){ |
|
|
S3JniAutoExtension_clear(ax); |
|
|
|
|
|
--SJG.autoExt.nExt; |
|
|
*ax = SJG.autoExt.aExt[SJG.autoExt.nExt]; |
|
|
SJG.autoExt.aExt[SJG.autoExt.nExt] = S3JniHook_empty; |
|
|
assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj ); |
|
|
rc = JNI_TRUE; |
|
|
break; |
|
|
} |
|
|
} |
|
|
S3JniAutoExt_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
static jint s3jni_close_db(JNIEnv * const env, jlong jpDb, int version){ |
|
|
int rc = 0; |
|
|
S3JniDb * const ps = S3JniDb_from_jlong(jpDb); |
|
|
|
|
|
assert(version == 1 || version == 2); |
|
|
if( ps ){ |
|
|
rc = 1==version |
|
|
? (jint)sqlite3_close(ps->pDb) |
|
|
: (jint)sqlite3_close_v2(ps->pDb); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_close(),jint,1close)(JniArgsEnvClass, jlong pDb){ |
|
|
return s3jni_close_db(env, pDb, 1); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_close_v2(),jint,1close_1v2)(JniArgsEnvClass, jlong pDb){ |
|
|
return s3jni_close_db(env, pDb, 2); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int s3jni_utf16_strlen(void const * z){ |
|
|
unsigned int i = 0; |
|
|
const unsigned short * p = z; |
|
|
while( p[i] ) ++i; |
|
|
return i; |
|
|
} |
|
|
|
|
|
|
|
|
typedef S3JniHook S3JniCollationNeeded; |
|
|
|
|
|
|
|
|
static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb, |
|
|
int eTextRep, const void * z16Name){ |
|
|
S3JniCollationNeeded * const pHook = pState; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(pHook, &hook); |
|
|
if( hook.jObj ){ |
|
|
unsigned int const nName = s3jni_utf16_strlen(z16Name); |
|
|
jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName); |
|
|
|
|
|
s3jni_oom_check( jName ); |
|
|
assert( hook.jExtra ); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionClear; |
|
|
}else if( hook.jExtra ){ |
|
|
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, |
|
|
hook.jExtra, (jint)eTextRep, jName); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback"); |
|
|
} |
|
|
} |
|
|
S3JniUnrefLocal(jName); |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jHook |
|
|
){ |
|
|
S3JniDb * ps; |
|
|
S3JniCollationNeeded * pHook; |
|
|
int rc = 0; |
|
|
|
|
|
S3JniDb_mutex_enter; |
|
|
ps = S3JniDb_from_jlong(jpDb); |
|
|
if( !ps ){ |
|
|
S3JniDb_mutex_leave; |
|
|
return SQLITE_MISUSE; |
|
|
} |
|
|
pHook = &ps->hooks.collationNeeded; |
|
|
if( pHook->jObj && jHook && |
|
|
(*env)->IsSameObject(env, pHook->jObj, jHook) ){ |
|
|
|
|
|
}else if( !jHook ){ |
|
|
rc = sqlite3_collation_needed(ps->pDb, 0, 0); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
} |
|
|
}else{ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jHook); |
|
|
jmethodID const xCallback = (*env)->GetMethodID( |
|
|
env, klazz, "call", "(Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)V" |
|
|
); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
rc = s3jni_db_exception(ps->pDb, SQLITE_MISUSE, |
|
|
"Cannot not find matching call() in " |
|
|
"CollationNeededCallback object."); |
|
|
}else{ |
|
|
rc = sqlite3_collation_needed16(ps->pDb, pHook, |
|
|
s3jni_collation_needed_impl16); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
pHook->midCallback = xCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jHook); |
|
|
pHook->jExtra = S3JniRefGlobal(ps->jDb); |
|
|
} |
|
|
} |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_blob(),jbyteArray,1column_1blob)( |
|
|
JniArgsEnvClass, jobject jpStmt, jint ndx |
|
|
){ |
|
|
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); |
|
|
void const * const p = sqlite3_column_blob(pStmt, (int)ndx); |
|
|
int const n = p ? sqlite3_column_bytes(pStmt, (int)ndx) : 0; |
|
|
|
|
|
return p ? s3jni_new_jbyteArray(p, n) : 0; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_double(),jdouble,1column_1double)( |
|
|
JniArgsEnvClass, jobject jpStmt, jint ndx |
|
|
){ |
|
|
return (jdouble)sqlite3_column_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_int(),jint,1column_1int)( |
|
|
JniArgsEnvClass, jobject jpStmt, jint ndx |
|
|
){ |
|
|
return (jint)sqlite3_column_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)( |
|
|
JniArgsEnvClass, jobject jpStmt, jint ndx |
|
|
){ |
|
|
return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_java_object(),jobject,1column_1java_1object)( |
|
|
JniArgsEnvClass, jlong jpStmt, jint ndx |
|
|
){ |
|
|
sqlite3_stmt * const stmt = LongPtrGet_sqlite3_stmt(jpStmt); |
|
|
jobject rv = 0; |
|
|
if( stmt ){ |
|
|
sqlite3 * const db = sqlite3_db_handle(stmt); |
|
|
sqlite3_value * sv; |
|
|
sqlite3_mutex_enter(sqlite3_db_mutex(db)); |
|
|
sv = sqlite3_column_value(stmt, (int)ndx); |
|
|
if( sv ){ |
|
|
rv = S3JniRefLocal( |
|
|
sqlite3_value_pointer(sv, s3jni__value_jref_key) |
|
|
); |
|
|
} |
|
|
sqlite3_mutex_leave(sqlite3_db_mutex(db)); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_column_nio_buffer(),jobject,1column_1nio_1buffer)( |
|
|
JniArgsEnvClass, jobject jStmt, jint ndx |
|
|
){ |
|
|
sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jStmt); |
|
|
jobject rv = 0; |
|
|
if( stmt ){ |
|
|
const void * const p = sqlite3_column_blob(stmt, (int)ndx); |
|
|
if( p ){ |
|
|
const int n = sqlite3_column_bytes(stmt, (int)ndx); |
|
|
rv = s3jni__blob_to_ByteBuffer(env, p, n); |
|
|
} |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
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 = 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 |
|
|
|
|
|
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 = 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 = 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) |
|
|
; |
|
|
return new_java_sqlite3_value(env, sv); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){ |
|
|
S3JniDeclLocal_env; |
|
|
int rc = 0; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(isCommit |
|
|
? &ps->hooks.commit : &ps->hooks.rollback, |
|
|
&hook); |
|
|
if( hook.jObj ){ |
|
|
rc = isCommit |
|
|
? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback) |
|
|
: (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0); |
|
|
S3JniIfThrew{ |
|
|
rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, |
|
|
isCommit |
|
|
? "Commit hook callback threw" |
|
|
: "Rollback hook callback threw"); |
|
|
} |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
static int s3jni_commit_hook_impl(void *pP){ |
|
|
return s3jni_commit_rollback_hook_impl(1, pP); |
|
|
} |
|
|
|
|
|
|
|
|
static void s3jni_rollback_hook_impl(void *pP){ |
|
|
(void)s3jni_commit_rollback_hook_impl(0, pP); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env, |
|
|
jlong jpDb, jobject jHook){ |
|
|
S3JniDb * ps; |
|
|
jobject pOld = 0; |
|
|
S3JniHook * pHook; |
|
|
|
|
|
S3JniDb_mutex_enter; |
|
|
ps = S3JniDb_from_jlong(jpDb); |
|
|
if( !ps ){ |
|
|
s3jni_db_error(env, ps->pDb, SQLITE_MISUSE, 0); |
|
|
S3JniDb_mutex_leave; |
|
|
return 0; |
|
|
} |
|
|
pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback; |
|
|
pOld = pHook->jObj; |
|
|
if( pOld && jHook && |
|
|
(*env)->IsSameObject(env, pOld, jHook) ){ |
|
|
|
|
|
}else if( !jHook ){ |
|
|
if( pOld ){ |
|
|
jobject tmp = S3JniRefLocal(pOld); |
|
|
S3JniUnrefGlobal(pOld); |
|
|
pOld = tmp; |
|
|
} |
|
|
*pHook = S3JniHook_empty; |
|
|
if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0); |
|
|
else sqlite3_rollback_hook(ps->pDb, 0, 0); |
|
|
}else{ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jHook); |
|
|
jmethodID const xCallback = |
|
|
(*env)->GetMethodID(env, klazz, "call", |
|
|
isCommit ? "()I" : "()V"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Cannot not find matching call() method in" |
|
|
"hook object."); |
|
|
}else{ |
|
|
pHook->midCallback = xCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jHook); |
|
|
if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps); |
|
|
else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps); |
|
|
if( pOld ){ |
|
|
jobject tmp = S3JniRefLocal(pOld); |
|
|
S3JniUnrefGlobal(pOld); |
|
|
pOld = tmp; |
|
|
} |
|
|
} |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
return pOld; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_commit_hook(),jobject,1commit_1hook)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jHook |
|
|
){ |
|
|
return s3jni_commit_rollback_hook(1, env, jpDb, jHook); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_compileoption_get(),jstring,1compileoption_1get)( |
|
|
JniArgsEnvClass, jint n |
|
|
){ |
|
|
const char * z = sqlite3_compileoption_get(n); |
|
|
jstring const rv = z ? (*env)->NewStringUTF( env, z ) : 0; |
|
|
; |
|
|
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) |
|
|
|
|
|
; |
|
|
const jboolean rc = |
|
|
0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE; |
|
|
s3jni_mutf8_release(name, zUtf8); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_complete(),jint,1complete)( |
|
|
JniArgsEnvClass, jbyteArray jSql |
|
|
){ |
|
|
jbyte * const pBuf = s3jni_jbyteArray_bytes(jSql); |
|
|
const jsize nBA = pBuf ? (*env)->GetArrayLength(env, jSql) : 0; |
|
|
int rc; |
|
|
|
|
|
assert( (nBA>0 ? 0==pBuf[nBA-1] : (pBuf ? 0==*pBuf : 1)) |
|
|
&& "Byte array is not NUL-terminated." ); |
|
|
rc = (pBuf && 0==pBuf[(nBA ? nBA-1 : 0)]) |
|
|
? sqlite3_complete( (const char *)pBuf ) |
|
|
: (jSql ? SQLITE_NOMEM : SQLITE_MISUSE); |
|
|
s3jni_jbyteArray_release(jSql, pBuf); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_config() |
|
|
sqlite3_config__enable(), |
|
|
jint,1config_1_1enable)(JniArgsEnvClass, jint n){ |
|
|
switch( n ){ |
|
|
case SQLITE_CONFIG_SINGLETHREAD: |
|
|
case SQLITE_CONFIG_MULTITHREAD: |
|
|
case SQLITE_CONFIG_SERIALIZED: |
|
|
return sqlite3_config( n ); |
|
|
default: |
|
|
return SQLITE_MISUSE; |
|
|
} |
|
|
} |
|
|
|
|
|
static void s3jni_config_log(void *ignored, int errCode, const char *z){ |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook hook = S3JniHook_empty; |
|
|
|
|
|
S3JniHook_localdup(&SJG.hook.configlog, &hook); |
|
|
if( hook.jObj ){ |
|
|
jstring const jArg1 = z ? s3jni_utf8_to_jstring(z, -1) : 0; |
|
|
if( z ? !!jArg1 : 1 ){ |
|
|
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, errCode, jArg1); |
|
|
} |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_LOG callback"); |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
S3JniHook_localundup(hook); |
|
|
S3JniUnrefLocal(jArg1); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_config() |
|
|
sqlite3_config__config_log() , |
|
|
jint, 1config_1_1CONFIG_1LOG |
|
|
)(JniArgsEnvClass, jobject jLog){ |
|
|
S3JniHook * const pHook = &SJG.hook.configlog; |
|
|
int rc = 0; |
|
|
|
|
|
S3JniGlobal_mutex_enter; |
|
|
if( !jLog ){ |
|
|
rc = sqlite3_config( SQLITE_CONFIG_LOG, NULL, NULL ); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
} |
|
|
}else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ |
|
|
|
|
|
}else { |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jLog); |
|
|
jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call", |
|
|
"(ILjava/lang/String;)V"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
if( midCallback ){ |
|
|
rc = sqlite3_config( SQLITE_CONFIG_LOG, s3jni_config_log, NULL ); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
pHook->midCallback = midCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jLog); |
|
|
} |
|
|
}else{ |
|
|
S3JniExceptionWarnIgnore; |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
} |
|
|
S3JniGlobal_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
#ifdef SQLITE_ENABLE_SQLLOG |
|
|
|
|
|
static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){ |
|
|
jobject jArg0 = 0; |
|
|
jstring jArg1 = 0; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniDb * const ps = S3JniDb_from_c(pDb); |
|
|
S3JniHook hook = S3JniHook_empty; |
|
|
|
|
|
if( ps ){ |
|
|
S3JniHook_localdup(&SJG.hook.sqllog, &hook); |
|
|
} |
|
|
if( !hook.jObj ) return; |
|
|
jArg0 = S3JniRefLocal(ps->jDb); |
|
|
switch( op ){ |
|
|
case 0: |
|
|
case 1: |
|
|
jArg1 = s3jni_utf8_to_jstring( z, -1); |
|
|
break; |
|
|
case 2: |
|
|
break; |
|
|
default: |
|
|
(*env)->FatalError(env, "Unhandled 4th arg to SQLITE_CONFIG_SQLLOG."); |
|
|
break; |
|
|
} |
|
|
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, jArg0, jArg1, op); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback"); |
|
|
S3JniExceptionClear; |
|
|
} |
|
|
S3JniHook_localundup(hook); |
|
|
S3JniUnrefLocal(jArg0); |
|
|
S3JniUnrefLocal(jArg1); |
|
|
} |
|
|
|
|
|
void sqlite3_init_sqllog(void){ |
|
|
sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 ); |
|
|
} |
|
|
#endif |
|
|
|
|
|
S3JniApi(sqlite3_config() |
|
|
sqlite3_config__SQLLOG() , |
|
|
jint, 1config_1_1SQLLOG |
|
|
)(JniArgsEnvClass, jobject jLog){ |
|
|
#ifndef SQLITE_ENABLE_SQLLOG |
|
|
return SQLITE_MISUSE; |
|
|
#else |
|
|
S3JniHook * const pHook = &SJG.hook.sqllog; |
|
|
int rc = 0; |
|
|
|
|
|
S3JniGlobal_mutex_enter; |
|
|
if( !jLog ){ |
|
|
rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, NULL ); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
} |
|
|
}else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){ |
|
|
|
|
|
}else { |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jLog); |
|
|
jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call", |
|
|
"(Lorg/sqlite/jni/capi/sqlite3;" |
|
|
"Ljava/lang/String;" |
|
|
"I)V"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
if( midCallback ){ |
|
|
rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, NULL ); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(pHook); |
|
|
pHook->midCallback = midCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jLog); |
|
|
} |
|
|
}else{ |
|
|
S3JniExceptionWarnIgnore; |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
} |
|
|
S3JniGlobal_mutex_leave; |
|
|
return rc; |
|
|
#endif |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)( |
|
|
JniArgsEnvClass, jobject jpCx |
|
|
){ |
|
|
sqlite3_context * const pCx = PtrGet_sqlite3_context(jpCx); |
|
|
sqlite3 * const pDb = pCx ? sqlite3_context_db_handle(pCx) : 0; |
|
|
S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0; |
|
|
return ps ? ps->jDb : 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef S3JniHook S3JniCollationCallback; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs, |
|
|
int nRhs, const void *rhs){ |
|
|
S3JniCollationCallback * const pCC = pArg; |
|
|
S3JniDeclLocal_env; |
|
|
jint rc = 0; |
|
|
if( pCC->jObj ){ |
|
|
jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs); |
|
|
jbyteArray jbaRhs = jbaLhs |
|
|
? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0; |
|
|
if( !jbaRhs ){ |
|
|
S3JniUnrefLocal(jbaLhs); |
|
|
|
|
|
s3jni_oom_check( jbaRhs ); |
|
|
return 0; |
|
|
} |
|
|
rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback, |
|
|
jbaLhs, jbaRhs); |
|
|
S3JniExceptionIgnore; |
|
|
S3JniUnrefLocal(jbaLhs); |
|
|
S3JniUnrefLocal(jbaRhs); |
|
|
} |
|
|
return (int)rc; |
|
|
} |
|
|
|
|
|
|
|
|
static void CollationCallback_xDestroy(void *pArg){ |
|
|
S3JniCollationCallback * const pCC = pArg; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook_free(pCC); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(), |
|
|
jint,1create_1collation |
|
|
)(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"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew{ |
|
|
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Could not get call() method from " |
|
|
"CollationCallback object."); |
|
|
}else{ |
|
|
char * const zName = s3jni_jstring_to_utf8(name, 0); |
|
|
S3JniCollationCallback * const pCC = |
|
|
zName ? S3JniHook_alloc() : 0; |
|
|
if( pCC ){ |
|
|
rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep, |
|
|
pCC, CollationCallback_xCompare, |
|
|
CollationCallback_xDestroy); |
|
|
if( 0==rc ){ |
|
|
pCC->midCallback = midCallback; |
|
|
pCC->jObj = S3JniRefGlobal(oCollation); |
|
|
pCC->doXDestroy = 1; |
|
|
}else{ |
|
|
CollationCallback_xDestroy(pCC); |
|
|
} |
|
|
}else{ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
sqlite3_free(zName); |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() |
|
|
sqlite3_create_window_function(), |
|
|
jint,1create_1function |
|
|
)(JniArgsEnvClass, jobject jDb, jstring jFuncName, jint nArg, |
|
|
jint eTextRep, jobject jFunctor){ |
|
|
S3JniUdf * s = 0; |
|
|
int rc; |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jDb); |
|
|
char * zFuncName = 0; |
|
|
|
|
|
if( !pDb || !jFuncName ){ |
|
|
return SQLITE_MISUSE; |
|
|
}else if( !encodingTypeIsValid(eTextRep & 0x0f) ){ |
|
|
return s3jni_db_error(env, pDb, SQLITE_FORMAT, |
|
|
"Invalid function encoding option."); |
|
|
} |
|
|
s = S3JniUdf_alloc(env, jFunctor); |
|
|
if( !s ) return SQLITE_NOMEM; |
|
|
|
|
|
if( UDF_UNKNOWN_TYPE==s->type ){ |
|
|
rc = s3jni_db_error(env, pDb, SQLITE_MISUSE, |
|
|
"Cannot unambiguously determine function type."); |
|
|
S3JniUdf_free(env, s, 1); |
|
|
goto error_cleanup; |
|
|
} |
|
|
zFuncName = s3jni_jstring_to_utf8(jFuncName,0); |
|
|
if( !zFuncName ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
S3JniUdf_free(env, s, 1); |
|
|
goto error_cleanup; |
|
|
} |
|
|
s->zFuncName = zFuncName ; |
|
|
if( UDF_WINDOW == s->type ){ |
|
|
rc = sqlite3_create_window_function(pDb, zFuncName, nArg, eTextRep, s, |
|
|
udf_xStep, udf_xFinal, udf_xValue, |
|
|
udf_xInverse, S3JniUdf_finalizer); |
|
|
}else{ |
|
|
udf_xFunc_f xFunc = 0; |
|
|
udf_xStep_f xStep = 0; |
|
|
udf_xFinal_f xFinal = 0; |
|
|
if( UDF_SCALAR == s->type ){ |
|
|
xFunc = udf_xFunc; |
|
|
}else{ |
|
|
assert( UDF_AGGREGATE == s->type ); |
|
|
xStep = udf_xStep; |
|
|
xFinal = udf_xFinal; |
|
|
} |
|
|
rc = sqlite3_create_function_v2(pDb, zFuncName, nArg, eTextRep, s, |
|
|
xFunc, xStep, xFinal, S3JniUdf_finalizer); |
|
|
} |
|
|
error_cleanup: |
|
|
|
|
|
|
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_db_config() , |
|
|
jint,1db_1config__Lorg_sqlite_jni_capi_sqlite3_2ILjava_lang_String_2 |
|
|
)(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
int rc; |
|
|
char *zStr; |
|
|
|
|
|
switch( (ps && jStr) ? op : 0 ){ |
|
|
case SQLITE_DBCONFIG_MAINDBNAME: |
|
|
S3JniDb_mutex_enter |
|
|
|
|
|
; |
|
|
zStr = s3jni_jstring_to_utf8( jStr, 0); |
|
|
if( zStr ){ |
|
|
rc = sqlite3_db_config(ps->pDb, (int)op, zStr); |
|
|
if( rc ){ |
|
|
sqlite3_free( zStr ); |
|
|
}else{ |
|
|
sqlite3_free( ps->zMainDbName ); |
|
|
ps->zMainDbName = zStr; |
|
|
} |
|
|
}else{ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
break; |
|
|
case 0: |
|
|
default: |
|
|
rc = SQLITE_MISUSE; |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi( |
|
|
sqlite3_db_config(), |
|
|
|
|
|
|
|
|
|
|
|
jint,1db_1config__Lorg_sqlite_jni_capi_sqlite3_2IILorg_sqlite_jni_capi_OutputPointer_Int32_2 |
|
|
)( |
|
|
JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
int rc; |
|
|
switch( ps ? op : 0 ){ |
|
|
case SQLITE_DBCONFIG_ENABLE_FKEY: |
|
|
case SQLITE_DBCONFIG_ENABLE_TRIGGER: |
|
|
case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER: |
|
|
case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: |
|
|
case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: |
|
|
case SQLITE_DBCONFIG_ENABLE_QPSG: |
|
|
case SQLITE_DBCONFIG_TRIGGER_EQP: |
|
|
case SQLITE_DBCONFIG_RESET_DATABASE: |
|
|
case SQLITE_DBCONFIG_DEFENSIVE: |
|
|
case SQLITE_DBCONFIG_WRITABLE_SCHEMA: |
|
|
case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE: |
|
|
case SQLITE_DBCONFIG_DQS_DML: |
|
|
case SQLITE_DBCONFIG_DQS_DDL: |
|
|
case SQLITE_DBCONFIG_ENABLE_VIEW: |
|
|
case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT: |
|
|
case SQLITE_DBCONFIG_TRUSTED_SCHEMA: |
|
|
case SQLITE_DBCONFIG_STMT_SCANSTATUS: |
|
|
case SQLITE_DBCONFIG_REVERSE_SCANORDER: { |
|
|
int pOut = 0; |
|
|
rc = sqlite3_db_config( ps->pDb, (int)op, onOff, &pOut ); |
|
|
if( 0==rc && jOut ){ |
|
|
OutputPointer_set_Int32(env, jOut, pOut); |
|
|
} |
|
|
break; |
|
|
} |
|
|
default: |
|
|
rc = SQLITE_MISUSE; |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JniDecl(jint,1db_1config__Lorg_sqlite_jni_capi_sqlite3_2IILorg_sqlite_jni_capi_OutputPointer_00024Int32_2)( |
|
|
JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut |
|
|
){ |
|
|
return JniFuncName(1db_1config__Lorg_sqlite_jni_capi_sqlite3_2IILorg_sqlite_jni_capi_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(),jint,1db_1release_1memory)( |
|
|
JniArgsEnvClass, jobject jDb |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jDb); |
|
|
return pDb ? sqlite3_db_release_memory(pDb) : SQLITE_MISUSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_db_status(),jint,1db_1status)( |
|
|
JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent, |
|
|
jobject jOutHigh, jboolean reset |
|
|
){ |
|
|
int iCur = 0, iHigh = 0; |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jDb); |
|
|
int rc = sqlite3_db_status( pDb, op, &iCur, &iHigh, reset ); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int32(env, jOutCurrent, iCur); |
|
|
OutputPointer_set_Int32(env, jOutHigh, iHigh); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_errcode(),jint,1errcode)( |
|
|
JniArgsEnvClass, jobject jpDb |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
return pDb ? sqlite3_errcode(pDb) : SQLITE_MISUSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_errmsg(),jstring,1errmsg)( |
|
|
JniArgsEnvClass, jobject jpDb |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
return pDb ? s3jni_utf8_to_jstring( sqlite3_errmsg(pDb), -1) : 0 |
|
|
|
|
|
|
|
|
; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_set_errmsg(),jint,1set_1errmsg)( |
|
|
JniArgsEnvClass, jobject jpDb, jint errCode, jstring msg |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
const char *zUtf8; |
|
|
jint rc; |
|
|
if( !pDb ) return SQLITE_MISUSE; |
|
|
zUtf8 = msg ? s3jni_jstring_to_mutf8(msg) : NULL; |
|
|
rc = sqlite3_set_errmsg(pDb, (int)errCode, zUtf8); |
|
|
s3jni_mutf8_release(msg, zUtf8); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_errstr(),jstring,1errstr)( |
|
|
JniArgsEnvClass, jint rcCode |
|
|
){ |
|
|
jstring rv; |
|
|
const char * z = sqlite3_errstr((int)rcCode); |
|
|
if( !z ){ |
|
|
|
|
|
|
|
|
z = "unknown error"; |
|
|
} |
|
|
rv = (*env)->NewStringUTF(env, z) |
|
|
|
|
|
; |
|
|
s3jni_oom_check( rv ); |
|
|
return rv; |
|
|
} |
|
|
|
|
|
#ifndef SQLITE_ENABLE_NORMALIZE |
|
|
|
|
|
static const char * sqlite3_normalized_sql(sqlite3_stmt *s){ |
|
|
S3JniDeclLocal_env; |
|
|
(*env)->FatalError(env, "dummy sqlite3_normalized_sql() was " |
|
|
"impossibly called.") ; |
|
|
return 0; |
|
|
} |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jstring s3jni_xn_sql(int isExpanded, JNIEnv *env, jobject jpStmt){ |
|
|
jstring rv = 0; |
|
|
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); |
|
|
|
|
|
if( pStmt ){ |
|
|
char * zSql = isExpanded |
|
|
? sqlite3_expanded_sql(pStmt) |
|
|
: (char*)sqlite3_normalized_sql(pStmt); |
|
|
s3jni_oom_fatal(zSql); |
|
|
if( zSql ){ |
|
|
rv = s3jni_utf8_to_jstring(zSql, -1); |
|
|
if( isExpanded ) sqlite3_free(zSql); |
|
|
} |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)( |
|
|
JniArgsEnvClass, jobject jpStmt |
|
|
){ |
|
|
return s3jni_xn_sql(1, env, jpStmt); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_normalized_sql(),jstring,1normalized_1sql)( |
|
|
JniArgsEnvClass, jobject jpStmt |
|
|
){ |
|
|
#ifdef SQLITE_ENABLE_NORMALIZE |
|
|
return s3jni_xn_sql(0, env, jpStmt); |
|
|
#else |
|
|
return 0; |
|
|
#endif |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_extended_result_codes(),jint,1extended_1result_1codes)( |
|
|
JniArgsEnvClass, jobject jpDb, jboolean onoff |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
int const rc = pDb |
|
|
? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) |
|
|
: SQLITE_MISUSE; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_finalize(),jint,1finalize)( |
|
|
JniArgsEnvClass, jlong jpStmt |
|
|
){ |
|
|
return jpStmt |
|
|
? sqlite3_finalize(LongPtrGet_sqlite3_stmt(jpStmt)) |
|
|
: 0; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_get_auxdata(),jobject,1get_1auxdata)( |
|
|
JniArgsEnvClass, jobject jCx, jint n |
|
|
){ |
|
|
return sqlite3_get_auxdata(PtrGet_sqlite3_context(jCx), (int)n); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_initialize(),jint,1initialize)( |
|
|
JniArgsEnvClass |
|
|
){ |
|
|
return sqlite3_initialize(); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_interrupt(),void,1interrupt)( |
|
|
JniArgsEnvClass, jobject jpDb |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
if( pDb ){ |
|
|
sqlite3_interrupt(pDb); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)( |
|
|
JniArgsEnvClass, jobject jpDb |
|
|
){ |
|
|
int rc = 0; |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
if( pDb ){ |
|
|
rc = sqlite3_is_interrupted(pDb); |
|
|
} |
|
|
return rc ? JNI_TRUE : JNI_FALSE; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_java_uncache_thread(), jboolean, 1java_1uncache_1thread)( |
|
|
JniArgsEnvClass |
|
|
){ |
|
|
int rc; |
|
|
S3JniEnv_mutex_enter; |
|
|
rc = S3JniEnv_uncache(env); |
|
|
S3JniEnv_mutex_leave; |
|
|
return rc ? JNI_TRUE : JNI_FALSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_jni_db_error(), jint, 1jni_1db_1error)( |
|
|
JniArgsEnvClass, jobject jDb, jint jRc, jstring jStr |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
int rc = SQLITE_MISUSE; |
|
|
if( ps ){ |
|
|
char *zStr; |
|
|
zStr = jStr |
|
|
? s3jni_jstring_to_utf8( jStr, 0) |
|
|
: NULL; |
|
|
rc = s3jni_db_error(env, ps->pDb, (int)jRc, zStr ); |
|
|
sqlite3_free(zStr); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_jni_supports_nio(), jboolean,1jni_1supports_1nio)( |
|
|
JniArgsEnvClass |
|
|
){ |
|
|
return SJG.g.byteBuffer.klazz ? JNI_TRUE : JNI_FALSE; |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_keyword_check(),jboolean,1keyword_1check)( |
|
|
JniArgsEnvClass, jstring jWord |
|
|
){ |
|
|
int nWord = 0; |
|
|
char * zWord = s3jni_jstring_to_utf8(jWord, &nWord); |
|
|
int rc = 0; |
|
|
|
|
|
s3jni_oom_check(jWord ? !!zWord : 1); |
|
|
if( zWord && nWord ){ |
|
|
rc = sqlite3_keyword_check(zWord, nWord); |
|
|
} |
|
|
sqlite3_free(zWord); |
|
|
return rc ? JNI_TRUE : JNI_FALSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_keyword_name(),jstring,1keyword_1name)( |
|
|
JniArgsEnvClass, jint ndx |
|
|
){ |
|
|
const char * zWord = 0; |
|
|
int n = 0; |
|
|
jstring rv = 0; |
|
|
|
|
|
if( 0==sqlite3_keyword_name(ndx, &zWord, &n) ){ |
|
|
rv = s3jni_utf8_to_jstring(zWord, n); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)( |
|
|
JniArgsEnvClass, jobject jpDb |
|
|
){ |
|
|
return (jlong)sqlite3_last_insert_rowid(PtrGet_sqlite3(jpDb)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_limit(),jint,1limit)( |
|
|
JniArgsEnvClass, jobject jpDb, jint id, jint newVal |
|
|
){ |
|
|
jint rc = 0; |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jpDb); |
|
|
if( pDb ){ |
|
|
rc = sqlite3_limit( pDb, (int)id, (int)newVal ); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
*zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0) : 0; |
|
|
if( jDbName && !*zDbName ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
goto end; |
|
|
} |
|
|
jDb = new_java_sqlite3(env, 0); |
|
|
if( !jDb ){ |
|
|
sqlite3_free(*zDbName); |
|
|
*zDbName = 0; |
|
|
rc = SQLITE_NOMEM; |
|
|
goto end; |
|
|
} |
|
|
*ps = S3JniDb_alloc(env, jDb); |
|
|
if( *ps ){ |
|
|
(*jc)->pdbOpening = *ps; |
|
|
}else{ |
|
|
S3JniUnrefLocal(jDb); |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
end: |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc, |
|
|
S3JniDb * ps, sqlite3 **ppDb, |
|
|
jobject jOut, int theRc){ |
|
|
int rc = 0; |
|
|
jc->pdbOpening = 0; |
|
|
if( *ppDb ){ |
|
|
assert(ps->jDb); |
|
|
if( 0==ps->pDb ){ |
|
|
ps->pDb = *ppDb; |
|
|
NativePointerHolder_set(S3JniNph(sqlite3), ps->jDb, *ppDb); |
|
|
}else{ |
|
|
assert( ps->pDb==*ppDb |
|
|
&& "Set up via s3jni_run_java_auto_extensions()" ); |
|
|
} |
|
|
rc = sqlite3_set_clientdata(ps->pDb, S3JniDb_clientdata_key, |
|
|
ps, S3JniDb_xDestroy) |
|
|
; |
|
|
}else{ |
|
|
S3JniDb_set_aside(ps); |
|
|
ps = 0; |
|
|
} |
|
|
OutputPointer_set_obj(env, S3JniNph(OutputPointer_sqlite3), |
|
|
jOut, ps ? ps->jDb : 0); |
|
|
return theRc ? theRc : rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_open(),jint,1open)( |
|
|
JniArgsEnvClass, jstring strName, jobject jOut |
|
|
){ |
|
|
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); |
|
|
sqlite3_free(zName); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_open_v2(),jint,1open_1v2)( |
|
|
JniArgsEnvClass, jstring strName, |
|
|
jobject jOut, jint flags, jstring strVfs |
|
|
){ |
|
|
sqlite3 * pOut = 0; |
|
|
char *zName = 0; |
|
|
S3JniDb * ps = 0; |
|
|
S3JniEnv * jc = 0; |
|
|
char *zVfs = 0; |
|
|
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; |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs); |
|
|
} |
|
|
rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc); |
|
|
} |
|
|
assert(rc==0 ? pOut!=0 : 1); |
|
|
sqlite3_free(zName); |
|
|
sqlite3_free(zVfs); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
static jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, |
|
|
jclass self, |
|
|
jlong jpDb, jbyteArray baSql, |
|
|
jint nMax, jint prepFlags, |
|
|
jobject jOutStmt, jobject outTail){ |
|
|
sqlite3_stmt * pStmt = 0; |
|
|
jobject jStmt = 0; |
|
|
const char * zTail = 0; |
|
|
sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); |
|
|
jbyte * const pBuf = pDb ? s3jni_jbyteArray_bytes(baSql) : 0; |
|
|
int rc = SQLITE_ERROR; |
|
|
|
|
|
assert(prepVersion==1 || prepVersion==2 || prepVersion==3); |
|
|
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(pDb, (const char *)pBuf, |
|
|
(int)nMax, &pStmt, &zTail); |
|
|
break; |
|
|
case 2: rc = sqlite3_prepare_v2(pDb, (const char *)pBuf, |
|
|
(int)nMax, &pStmt, &zTail); |
|
|
break; |
|
|
case 3: rc = sqlite3_prepare_v3(pDb, (const char *)pBuf, |
|
|
(int)nMax, (unsigned int)prepFlags, |
|
|
&pStmt, &zTail); |
|
|
break; |
|
|
default: |
|
|
assert(!"Invalid prepare() version"); |
|
|
} |
|
|
end: |
|
|
s3jni_jbyteArray_release(baSql,pBuf); |
|
|
if( 0==rc ){ |
|
|
if( 0!=outTail ){ |
|
|
|
|
|
|
|
|
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) |
|
|
); |
|
|
} |
|
|
if( pStmt ){ |
|
|
NativePointerHolder_set(S3JniNph(sqlite3_stmt), jStmt, pStmt); |
|
|
}else{ |
|
|
|
|
|
S3JniUnrefLocal(jStmt); |
|
|
jStmt = 0; |
|
|
} |
|
|
}else{ |
|
|
S3JniUnrefLocal(jStmt); |
|
|
jStmt = 0; |
|
|
} |
|
|
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 |
|
|
){ |
|
|
return sqlite3_jni_prepare_v123(1, env, self, jpDb, baSql, nMax, 0, |
|
|
jOutStmt, outTail); |
|
|
} |
|
|
S3JniApi(sqlite3_prepare_v2(),jint,1prepare_1v2)( |
|
|
JNIEnv * const env, jclass self, jlong jpDb, jbyteArray baSql, |
|
|
jint nMax, jobject jOutStmt, jobject outTail |
|
|
){ |
|
|
return sqlite3_jni_prepare_v123(2, env, self, jpDb, baSql, nMax, 0, |
|
|
jOutStmt, outTail); |
|
|
} |
|
|
S3JniApi(sqlite3_prepare_v3(),jint,1prepare_1v3)( |
|
|
JNIEnv * const env, jclass self, jlong jpDb, jbyteArray baSql, |
|
|
jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail |
|
|
){ |
|
|
return sqlite3_jni_prepare_v123(3, env, self, jpDb, baSql, nMax, |
|
|
prepFlags, jOutStmt, outTail); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId, |
|
|
const char *zDb, const char *zTable, |
|
|
sqlite3_int64 iKey1, sqlite3_int64 iKey2){ |
|
|
S3JniDb * const ps = pState; |
|
|
S3JniDeclLocal_env; |
|
|
jstring jDbName; |
|
|
jstring jTable; |
|
|
const int isPre = 0!=pDb; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(isPre ? |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
&ps->hooks.preUpdate |
|
|
#else |
|
|
&S3JniHook_empty |
|
|
#endif |
|
|
: &ps->hooks.update, &hook); |
|
|
if( !hook.jObj ){ |
|
|
return; |
|
|
} |
|
|
jDbName = s3jni_utf8_to_jstring( zDb, -1); |
|
|
jTable = jDbName ? s3jni_utf8_to_jstring( zTable, -1) : 0; |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionClear; |
|
|
s3jni_db_error(env, ps->pDb, SQLITE_NOMEM, 0); |
|
|
}else{ |
|
|
assert( hook.jObj ); |
|
|
assert( hook.midCallback ); |
|
|
assert( ps->jDb ); |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
if( isPre ) (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, |
|
|
ps->jDb, (jint)opId, jDbName, jTable, |
|
|
(jlong)iKey1, (jlong)iKey2); |
|
|
else |
|
|
#endif |
|
|
(*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, |
|
|
(jint)opId, jDbName, jTable, (jlong)iKey1); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback"); |
|
|
s3jni_db_exception(ps->pDb, 0, |
|
|
"sqlite3_(pre)update_hook() callback threw"); |
|
|
} |
|
|
} |
|
|
S3JniUnrefLocal(jDbName); |
|
|
S3JniUnrefLocal(jTable); |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
|
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
static void s3jni_preupdate_hook_impl(void * pState, sqlite3 *pDb, int opId, |
|
|
const char *zDb, const char *zTable, |
|
|
sqlite3_int64 iKey1, sqlite3_int64 iKey2){ |
|
|
return s3jni_updatepre_hook_impl(pState, pDb, opId, zDb, zTable, |
|
|
iKey1, iKey2); |
|
|
} |
|
|
#endif |
|
|
|
|
|
static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb, |
|
|
const char *zTable, sqlite3_int64 nRowid){ |
|
|
return s3jni_updatepre_hook_impl(pState, NULL, opId, zDb, zTable, nRowid, 0); |
|
|
} |
|
|
|
|
|
#if !defined(SQLITE_ENABLE_PREUPDATE_HOOK) |
|
|
|
|
|
S3JniApi(sqlite3_preupdate_blobwrite(),jint,1preupdate_1blobwrite)( |
|
|
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } |
|
|
S3JniApi(sqlite3_preupdate_count(),jint,1preupdate_1count)( |
|
|
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } |
|
|
S3JniApi(sqlite3_preupdate_depth(),jint,1preupdate_1depth)( |
|
|
JniArgsEnvClass, jlong jDb){ return SQLITE_MISUSE; } |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jlong jpDb, jobject jHook){ |
|
|
S3JniDb * const ps = S3JniDb_from_jlong(jpDb); |
|
|
jclass klazz; |
|
|
jobject pOld = 0; |
|
|
jmethodID xCallback; |
|
|
S3JniHook * pHook; |
|
|
|
|
|
if( !ps ) return 0; |
|
|
S3JniDb_mutex_enter; |
|
|
pHook = isPre ? |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
&ps->hooks.preUpdate |
|
|
#else |
|
|
0 |
|
|
#endif |
|
|
: &ps->hooks.update; |
|
|
if( !pHook ){ |
|
|
goto end; |
|
|
} |
|
|
pOld = pHook->jObj; |
|
|
if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){ |
|
|
goto end; |
|
|
} |
|
|
if( !jHook ){ |
|
|
if( pOld ){ |
|
|
jobject tmp = S3JniRefLocal(pOld); |
|
|
S3JniUnrefGlobal(pOld); |
|
|
pOld = tmp; |
|
|
} |
|
|
*pHook = S3JniHook_empty; |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
if( isPre ) sqlite3_preupdate_hook(ps->pDb, 0, 0); |
|
|
else |
|
|
#endif |
|
|
sqlite3_update_hook(ps->pDb, 0, 0); |
|
|
goto end; |
|
|
} |
|
|
klazz = (*env)->GetObjectClass(env, jHook); |
|
|
xCallback = isPre |
|
|
? (*env)->GetMethodID(env, klazz, "call", |
|
|
"(Lorg/sqlite/jni/capi/sqlite3;" |
|
|
"I" |
|
|
"Ljava/lang/String;" |
|
|
"Ljava/lang/String;" |
|
|
"JJ)V") |
|
|
: (*env)->GetMethodID(env, klazz, "call", |
|
|
"(ILjava/lang/String;Ljava/lang/String;J)V"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionClear; |
|
|
s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Cannot not find matching callback on " |
|
|
"(pre)update hook object."); |
|
|
}else{ |
|
|
pHook->midCallback = xCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jHook); |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
if( isPre ) sqlite3_preupdate_hook(ps->pDb, s3jni_preupdate_hook_impl, ps); |
|
|
else |
|
|
#endif |
|
|
sqlite3_update_hook(ps->pDb, s3jni_update_hook_impl, ps); |
|
|
if( pOld ){ |
|
|
jobject tmp = S3JniRefLocal(pOld); |
|
|
S3JniUnrefGlobal(pOld); |
|
|
pOld = tmp; |
|
|
} |
|
|
} |
|
|
end: |
|
|
S3JniDb_mutex_leave; |
|
|
return pOld; |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jHook |
|
|
){ |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
return s3jni_updatepre_hook(env, 1, jpDb, jHook); |
|
|
#else |
|
|
return NULL; |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jlong jpDb, |
|
|
jint iCol, jobject jOut){ |
|
|
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
|
|
sqlite3 * const pDb = LongPtrGet_sqlite3(jpDb); |
|
|
int rc = SQLITE_MISUSE; |
|
|
if( pDb ){ |
|
|
sqlite3_value * pOut = 0; |
|
|
int (*fOrig)(sqlite3*,int,sqlite3_value**) = |
|
|
isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old; |
|
|
rc = fOrig(pDb, (int)iCol, &pOut); |
|
|
if( 0==rc ){ |
|
|
jobject pWrap = new_java_sqlite3_value(env, pOut); |
|
|
if( !pWrap ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
OutputPointer_set_obj(env, S3JniNph(OutputPointer_sqlite3_value), |
|
|
jOut, pWrap); |
|
|
S3JniUnrefLocal(pWrap); |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
#else |
|
|
return SQLITE_MISUSE; |
|
|
#endif |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_preupdate_new(),jint,1preupdate_1new)( |
|
|
JniArgsEnvClass, jlong jpDb, jint iCol, jobject jOut |
|
|
){ |
|
|
return s3jni_preupdate_newold(env, 1, jpDb, iCol, jOut); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_preupdate_old(),jint,1preupdate_1old)( |
|
|
JniArgsEnvClass, jlong jpDb, jint iCol, jobject jOut |
|
|
){ |
|
|
return s3jni_preupdate_newold(env, 0, jpDb, iCol, jOut); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int s3jni_progress_handler_impl(void *pP){ |
|
|
S3JniDb * const ps = (S3JniDb *)pP; |
|
|
int rc = 0; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(&ps->hooks.progress, &hook); |
|
|
if( hook.jObj ){ |
|
|
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback); |
|
|
S3JniIfThrew{ |
|
|
rc = s3jni_db_exception(ps->pDb, rc, |
|
|
"sqlite3_progress_handler() callback threw"); |
|
|
} |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)( |
|
|
JniArgsEnvClass,jobject jDb, jint n, jobject jProgress |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
S3JniHook * const pHook = ps ? &ps->hooks.progress : 0; |
|
|
|
|
|
if( !ps ) return; |
|
|
S3JniDb_mutex_enter; |
|
|
if( n<1 || !jProgress ){ |
|
|
S3JniHook_unref(pHook); |
|
|
sqlite3_progress_handler(ps->pDb, 0, 0, 0); |
|
|
}else{ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jProgress); |
|
|
jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", "()I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionClear; |
|
|
s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Cannot not find matching xCallback() on " |
|
|
"ProgressHandler object."); |
|
|
}else{ |
|
|
S3JniUnrefGlobal(pHook->jObj); |
|
|
pHook->midCallback = xCallback; |
|
|
pHook->jObj = S3JniRefGlobal(jProgress); |
|
|
sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps); |
|
|
} |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_randomness(),void,1randomness)( |
|
|
JniArgsEnvClass, jbyteArray jTgt |
|
|
){ |
|
|
jbyte * const jba = s3jni_jbyteArray_bytes(jTgt); |
|
|
if( jba ){ |
|
|
jsize const nTgt = (*env)->GetArrayLength(env, jTgt); |
|
|
sqlite3_randomness( (int)nTgt, jba ); |
|
|
s3jni_jbyteArray_commit(jTgt, jba); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_reset(),jint,1reset)( |
|
|
JniArgsEnvClass, jobject jpStmt |
|
|
){ |
|
|
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); |
|
|
return pStmt ? sqlite3_reset(pStmt) : SQLITE_MISUSE; |
|
|
} |
|
|
|
|
|
|
|
|
static void s3jni_reset_auto_extension(JNIEnv *env){ |
|
|
int i; |
|
|
S3JniAutoExt_mutex_enter; |
|
|
for( i = 0; i < SJG.autoExt.nExt; ++i ){ |
|
|
S3JniAutoExtension_clear( &SJG.autoExt.aExt[i] ); |
|
|
} |
|
|
SJG.autoExt.nExt = 0; |
|
|
S3JniAutoExt_mutex_leave; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)( |
|
|
JniArgsEnvClass |
|
|
){ |
|
|
s3jni_reset_auto_extension(env); |
|
|
} |
|
|
|
|
|
|
|
|
static void result_blob_text(int as64 , |
|
|
int eTextRep , |
|
|
JNIEnv * const env, sqlite3_context *pCx, |
|
|
jbyteArray jBa, jlong nMax){ |
|
|
int const asBlob = 0==eTextRep; |
|
|
if( !pCx ){ |
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
if( as64 ){ |
|
|
static const jsize nLimit64 = |
|
|
SQLITE_MAX_ALLOCATION_SIZE; |
|
|
if( nBA > nLimit64 ){ |
|
|
sqlite3_result_error_toobig(pCx); |
|
|
}else if( asBlob ){ |
|
|
sqlite3_result_blob64(pCx, pBuf, (sqlite3_uint64)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
}else{ |
|
|
if( encodingTypeIsValid(eTextRep) ){ |
|
|
sqlite3_result_text64(pCx, (const char *)pBuf, |
|
|
(sqlite3_uint64)nBA, |
|
|
SQLITE_TRANSIENT, eTextRep); |
|
|
}else{ |
|
|
sqlite3_result_error_code(pCx, SQLITE_FORMAT); |
|
|
} |
|
|
} |
|
|
}else{ |
|
|
static const jsize nLimit = SQLITE_MAX_ALLOCATION_SIZE; |
|
|
if( nBA > nLimit ){ |
|
|
sqlite3_result_error_toobig(pCx); |
|
|
}else if( asBlob ){ |
|
|
sqlite3_result_blob(pCx, pBuf, (int)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
}else{ |
|
|
switch( eTextRep ){ |
|
|
case SQLITE_UTF8: |
|
|
sqlite3_result_text(pCx, (const char *)pBuf, (int)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
break; |
|
|
case SQLITE_UTF16: |
|
|
sqlite3_result_text16(pCx, (const char *)pBuf, (int)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
break; |
|
|
case SQLITE_UTF16LE: |
|
|
sqlite3_result_text16le(pCx, (const char *)pBuf, (int)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
break; |
|
|
case SQLITE_UTF16BE: |
|
|
sqlite3_result_text16be(pCx, (const char *)pBuf, (int)nBA, |
|
|
SQLITE_TRANSIENT); |
|
|
break; |
|
|
} |
|
|
} |
|
|
s3jni_jbyteArray_release(jBa, pBuf); |
|
|
} |
|
|
}else{ |
|
|
sqlite3_result_null(pCx); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_blob(),void,1result_1blob)( |
|
|
JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax |
|
|
){ |
|
|
return result_blob_text(0, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_blob64(),void,1result_1blob64)( |
|
|
JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax |
|
|
){ |
|
|
return result_blob_text(1, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_double(),void,1result_1double)( |
|
|
JniArgsEnvClass, jobject jpCx, jdouble v |
|
|
){ |
|
|
sqlite3_result_double(PtrGet_sqlite3_context(jpCx), v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_error(),void,1result_1error)( |
|
|
JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, jint eTextRep |
|
|
){ |
|
|
const char * zUnspecified = "Unspecified error."; |
|
|
jsize const baLen = (*env)->GetArrayLength(env, baMsg); |
|
|
jbyte * const pjBuf = baMsg ? s3jni_jbyteArray_bytes(baMsg) : NULL; |
|
|
switch( pjBuf ? eTextRep : SQLITE_UTF8 ){ |
|
|
case SQLITE_UTF8: { |
|
|
const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified; |
|
|
int const n = pjBuf ? (int)baLen : (int)sqlite3Strlen30(zMsg); |
|
|
sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, n); |
|
|
break; |
|
|
} |
|
|
case SQLITE_UTF16: { |
|
|
const void *zMsg = pjBuf; |
|
|
sqlite3_result_error16(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen); |
|
|
break; |
|
|
} |
|
|
default: |
|
|
sqlite3_result_error(PtrGet_sqlite3_context(jpCx), |
|
|
"Invalid encoding argument passed " |
|
|
"to sqlite3_result_error().", -1); |
|
|
break; |
|
|
} |
|
|
s3jni_jbyteArray_release(baMsg,pjBuf); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)( |
|
|
JniArgsEnvClass, jobject jpCx, jint v |
|
|
){ |
|
|
sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), (int)v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)( |
|
|
JniArgsEnvClass, jobject jpCx |
|
|
){ |
|
|
sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_error_toobig(),void,1result_1error_1toobig)( |
|
|
JniArgsEnvClass, jobject jpCx |
|
|
){ |
|
|
sqlite3_result_error_toobig(PtrGet_sqlite3_context(jpCx)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_int(),void,1result_1int)( |
|
|
JniArgsEnvClass, jobject jpCx, jint v |
|
|
){ |
|
|
sqlite3_result_int(PtrGet_sqlite3_context(jpCx), (int)v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_int64(),void,1result_1int64)( |
|
|
JniArgsEnvClass, jobject jpCx, jlong v |
|
|
){ |
|
|
sqlite3_result_int64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)( |
|
|
JniArgsEnvClass, jobject jpCx, jobject v |
|
|
){ |
|
|
sqlite3_context * pCx = PtrGet_sqlite3_context(jpCx); |
|
|
if( !pCx ) return; |
|
|
else if( v ){ |
|
|
jobject const rjv = S3JniRefGlobal(v); |
|
|
if( rjv ){ |
|
|
sqlite3_result_pointer(pCx, rjv, |
|
|
s3jni__value_jref_key, S3Jni_jobject_finalizer); |
|
|
}else{ |
|
|
sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx)); |
|
|
} |
|
|
}else{ |
|
|
sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)( |
|
|
JniArgsEnvClass, jobject jpCtx, jobject jBuffer, |
|
|
jint iOffset, jint iN |
|
|
){ |
|
|
sqlite3_context * pCx = PtrGet_sqlite3_context(jpCtx); |
|
|
int rc; |
|
|
S3JniNioArgs args; |
|
|
if( !pCx ){ |
|
|
return; |
|
|
}else if( !SJG.g.byteBuffer.klazz ){ |
|
|
sqlite3_result_error( |
|
|
pCx, "This JVM does not support JNI access to ByteBuffers.", -1 |
|
|
); |
|
|
return; |
|
|
} |
|
|
rc = s3jni_setup_nio_args(env, &args, jBuffer, iOffset, iN); |
|
|
if(rc){ |
|
|
if( iOffset<0 ){ |
|
|
sqlite3_result_error(pCx, "Start index may not be negative.", -1); |
|
|
}else if( SQLITE_TOOBIG==rc ){ |
|
|
sqlite3_result_error_toobig(pCx); |
|
|
}else{ |
|
|
sqlite3_result_error( |
|
|
pCx, "Invalid arguments to sqlite3_result_nio_buffer().", -1 |
|
|
); |
|
|
} |
|
|
}else if( !args.pStart || !args.nOut ){ |
|
|
sqlite3_result_null(pCx); |
|
|
}else{ |
|
|
sqlite3_result_blob(pCx, args.pStart, args.nOut, SQLITE_TRANSIENT); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_result_null(),void,1result_1null)( |
|
|
JniArgsEnvClass, jobject jpCx |
|
|
){ |
|
|
sqlite3_result_null(PtrGet_sqlite3_context(jpCx)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_subtype(),void,1result_1subtype)( |
|
|
JniArgsEnvClass, jobject jpCx, jint v |
|
|
){ |
|
|
sqlite3_result_subtype(PtrGet_sqlite3_context(jpCx), (unsigned int)v); |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_result_text(),void,1result_1text)( |
|
|
JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax |
|
|
){ |
|
|
return result_blob_text(0, SQLITE_UTF8, env, |
|
|
PtrGet_sqlite3_context(jpCx), jBa, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_text64(),void,1result_1text64)( |
|
|
JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax, |
|
|
jint eTextRep |
|
|
){ |
|
|
return result_blob_text(1, eTextRep, env, |
|
|
PtrGet_sqlite3_context(jpCx), jBa, nMax); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_value(),void,1result_1value)( |
|
|
JniArgsEnvClass, jobject jpCx, jobject jpSVal |
|
|
){ |
|
|
sqlite3_result_value(PtrGet_sqlite3_context(jpCx), |
|
|
PtrGet_sqlite3_value(jpSVal)); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_zeroblob(),void,1result_1zeroblob)( |
|
|
JniArgsEnvClass, jobject jpCx, jint v |
|
|
){ |
|
|
sqlite3_result_zeroblob(PtrGet_sqlite3_context(jpCx), (int)v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_result_zeroblob64(),jint,1result_1zeroblob64)( |
|
|
JniArgsEnvClass, jobject jpCx, jlong v |
|
|
){ |
|
|
return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx), |
|
|
(sqlite3_int64)v); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_rollback_hook(),jobject,1rollback_1hook)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jHook |
|
|
){ |
|
|
return s3jni_commit_rollback_hook(0, env, jpDb, jHook); |
|
|
} |
|
|
|
|
|
|
|
|
int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1, |
|
|
const char*z2,const char*z3){ |
|
|
S3JniDb * const ps = pState; |
|
|
S3JniDeclLocal_env; |
|
|
S3JniHook hook; |
|
|
int rc = 0; |
|
|
|
|
|
S3JniHook_localdup(&ps->hooks.auth, &hook ); |
|
|
if( hook.jObj ){ |
|
|
jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0; |
|
|
jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0; |
|
|
jstring const s2 = z2 ? s3jni_utf8_to_jstring( z2, -1) : 0; |
|
|
jstring const s3 = z3 ? s3jni_utf8_to_jstring( z3, -1) : 0; |
|
|
|
|
|
rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op, |
|
|
s0, s1, s3, s3); |
|
|
S3JniIfThrew{ |
|
|
rc = s3jni_db_exception(ps->pDb, rc, "sqlite3_set_authorizer() callback"); |
|
|
} |
|
|
S3JniUnrefLocal(s0); |
|
|
S3JniUnrefLocal(s1); |
|
|
S3JniUnrefLocal(s2); |
|
|
S3JniUnrefLocal(s3); |
|
|
S3JniHook_localundup(hook); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)( |
|
|
JniArgsEnvClass,jobject jDb, jobject jHook |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
S3JniHook * const pHook = ps ? &ps->hooks.auth : 0; |
|
|
int rc = 0; |
|
|
|
|
|
if( !ps ) return SQLITE_MISUSE; |
|
|
S3JniDb_mutex_enter; |
|
|
if( !jHook ){ |
|
|
S3JniHook_unref(pHook); |
|
|
rc = sqlite3_set_authorizer( ps->pDb, 0, 0 ); |
|
|
}else{ |
|
|
jclass klazz; |
|
|
if( pHook->jObj ){ |
|
|
if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){ |
|
|
|
|
|
S3JniDb_mutex_leave; |
|
|
return 0; |
|
|
} |
|
|
S3JniHook_unref(pHook); |
|
|
} |
|
|
pHook->jObj = S3JniRefGlobal(jHook); |
|
|
klazz = (*env)->GetObjectClass(env, jHook); |
|
|
pHook->midCallback = (*env)->GetMethodID(env, klazz, |
|
|
"call", |
|
|
"(I" |
|
|
"Ljava/lang/String;" |
|
|
"Ljava/lang/String;" |
|
|
"Ljava/lang/String;" |
|
|
"Ljava/lang/String;" |
|
|
")I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Error setting up Java parts of " |
|
|
"authorizer hook."); |
|
|
}else{ |
|
|
rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps); |
|
|
} |
|
|
if( rc ) S3JniHook_unref(pHook); |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_set_auxdata(),void,1set_1auxdata)( |
|
|
JniArgsEnvClass, jobject jCx, jint n, jobject jAux |
|
|
){ |
|
|
sqlite3_set_auxdata(PtrGet_sqlite3_context(jCx), (int)n, |
|
|
S3JniRefGlobal(jAux), S3Jni_jobject_finalizer); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)( |
|
|
JniArgsEnvClass, jobject jpDb, jlong rowId |
|
|
){ |
|
|
sqlite3_set_last_insert_rowid(PtrGet_sqlite3(jpDb), |
|
|
(sqlite3_int64)rowId); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_shutdown(),jint,1shutdown)( |
|
|
JniArgsEnvClass |
|
|
){ |
|
|
s3jni_reset_auto_extension(env); |
|
|
#ifdef SQLITE_ENABLE_SQLLOG |
|
|
S3JniHook_unref(&SJG.hook.sqllog); |
|
|
#endif |
|
|
S3JniHook_unref(&SJG.hook.configlog); |
|
|
|
|
|
S3JniDb_mutex_enter; { |
|
|
while( S3JniGlobal.perDb.aFree ){ |
|
|
S3JniDb * const d = S3JniGlobal.perDb.aFree; |
|
|
S3JniGlobal.perDb.aFree = d->pNext; |
|
|
S3JniDb_clear(env, d); |
|
|
sqlite3_free(d); |
|
|
} |
|
|
} S3JniDb_mutex_leave; |
|
|
S3JniGlobal_mutex_enter; { |
|
|
|
|
|
while( S3JniGlobal.udf.aFree ){ |
|
|
S3JniUdf * const u = S3JniGlobal.udf.aFree; |
|
|
S3JniGlobal.udf.aFree = u->pNext; |
|
|
u->pNext = 0; |
|
|
S3JniUdf_free(env, u, 0); |
|
|
} |
|
|
} S3JniGlobal_mutex_leave; |
|
|
S3JniHook_mutex_enter; { |
|
|
|
|
|
while( S3JniGlobal.hook.aFree ){ |
|
|
S3JniHook * const u = S3JniGlobal.hook.aFree; |
|
|
S3JniGlobal.hook.aFree = u->pNext; |
|
|
u->pNext = 0; |
|
|
assert( !u->doXDestroy ); |
|
|
assert( !u->jObj ); |
|
|
assert( !u->jExtra ); |
|
|
sqlite3_free( u ); |
|
|
} |
|
|
} S3JniHook_mutex_leave; |
|
|
|
|
|
S3JniEnv_mutex_enter; { |
|
|
while( SJG.envCache.aHead ){ |
|
|
S3JniEnv_uncache( SJG.envCache.aHead->env ); |
|
|
} |
|
|
} S3JniEnv_mutex_leave; |
|
|
|
|
|
|
|
|
return sqlite3_shutdown(); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_status(),jint,1status)( |
|
|
JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, |
|
|
jboolean reset |
|
|
){ |
|
|
int iCur = 0, iHigh = 0; |
|
|
int rc = sqlite3_status( op, &iCur, &iHigh, reset ); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int32(env, jOutCurrent, iCur); |
|
|
OutputPointer_set_Int32(env, jOutHigh, iHigh); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_status64(),jint,1status64)( |
|
|
JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh, |
|
|
jboolean reset |
|
|
){ |
|
|
sqlite3_int64 iCur = 0, iHigh = 0; |
|
|
int rc = sqlite3_status64( op, &iCur, &iHigh, reset ); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int64(env, jOutCurrent, iCur); |
|
|
OutputPointer_set_Int64(env, jOutHigh, iHigh); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_stmt_status(),jint,1stmt_1status)( |
|
|
JniArgsEnvClass, jobject jStmt, jint op, jboolean reset |
|
|
){ |
|
|
return sqlite3_stmt_status(PtrGet_sqlite3_stmt(jStmt), |
|
|
(int)op, reset ? 1 : 0); |
|
|
} |
|
|
|
|
|
|
|
|
static int s3jni_strlike_glob(int isLike, JNIEnv *const env, |
|
|
jbyteArray baG, jbyteArray baT, jint escLike){ |
|
|
int rc = 0; |
|
|
jbyte * const pG = s3jni_jbyteArray_bytes(baG); |
|
|
jbyte * const pT = s3jni_jbyteArray_bytes(baT); |
|
|
|
|
|
|
|
|
|
|
|
rc = isLike |
|
|
? sqlite3_strlike((const char *)pG, (const char *)pT, |
|
|
(unsigned int)escLike) |
|
|
: sqlite3_strglob((const char *)pG, (const char *)pT); |
|
|
s3jni_jbyteArray_release(baG, pG); |
|
|
s3jni_jbyteArray_release(baT, pT); |
|
|
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_sql(),jstring,1sql)( |
|
|
JniArgsEnvClass, jobject jpStmt |
|
|
){ |
|
|
sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt); |
|
|
jstring rv = 0; |
|
|
if( pStmt ){ |
|
|
const char * zSql = 0; |
|
|
zSql = sqlite3_sql(pStmt); |
|
|
rv = s3jni_utf8_to_jstring( zSql, -1); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_step(),jint,1step)( |
|
|
JniArgsEnvClass, jlong jpStmt |
|
|
){ |
|
|
sqlite3_stmt * const pStmt = LongPtrGet_sqlite3_stmt(jpStmt); |
|
|
return pStmt ? (jint)sqlite3_step(pStmt) : (jint)SQLITE_MISUSE; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_table_column_metadata(),jint,1table_1column_1metadata)( |
|
|
JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName, |
|
|
jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull, |
|
|
jobject jPrimaryKey, jobject jAutoinc |
|
|
){ |
|
|
sqlite3 * const db = PtrGet_sqlite3(jDb); |
|
|
char * zDbName = 0, * zTableName = 0, * zColumnName = 0; |
|
|
const char * pzCollSeq = 0; |
|
|
const char * pzDataType = 0; |
|
|
int pNotNull = 0, pPrimaryKey = 0, pAutoinc = 0; |
|
|
int rc; |
|
|
|
|
|
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 && 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 ){ |
|
|
jstring jseq = jCollSeq |
|
|
? (pzCollSeq ? s3jni_utf8_to_jstring(pzCollSeq, -1) : 0) |
|
|
: 0; |
|
|
jstring jdtype = jDataType |
|
|
? (pzDataType ? s3jni_utf8_to_jstring(pzDataType, -1) : 0) |
|
|
: 0; |
|
|
if( (jCollSeq && pzCollSeq && !jseq) |
|
|
|| (jDataType && pzDataType && !jdtype) ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
}else{ |
|
|
if( jNotNull ) OutputPointer_set_Bool(env, jNotNull, pNotNull); |
|
|
if( jPrimaryKey ) OutputPointer_set_Bool(env, jPrimaryKey, pPrimaryKey); |
|
|
if( jAutoinc ) OutputPointer_set_Bool(env, jAutoinc, pAutoinc); |
|
|
if( jCollSeq ) OutputPointer_set_String(env, jCollSeq, jseq); |
|
|
if( jDataType ) OutputPointer_set_String(env, jDataType, jdtype); |
|
|
} |
|
|
S3JniUnrefLocal(jseq); |
|
|
S3JniUnrefLocal(jdtype); |
|
|
} |
|
|
sqlite3_free(zDbName); |
|
|
sqlite3_free(zTableName); |
|
|
sqlite3_free(zColumnName); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){ |
|
|
S3JniDb * const ps = (S3JniDb *)pC; |
|
|
S3JniDeclLocal_env; |
|
|
jobject jX = NULL ; |
|
|
jobject jP = NULL ; |
|
|
jobject jPUnref = NULL ; |
|
|
int rc = 0; |
|
|
S3JniHook hook; |
|
|
|
|
|
S3JniHook_localdup(&ps->hooks.trace, &hook ); |
|
|
if( !hook.jObj ){ |
|
|
return 0; |
|
|
} |
|
|
switch( traceflag ){ |
|
|
case SQLITE_TRACE_STMT: |
|
|
jX = s3jni_utf8_to_jstring( (const char *)pX, -1); |
|
|
if( !jX ) rc = SQLITE_NOMEM; |
|
|
break; |
|
|
case SQLITE_TRACE_PROFILE: |
|
|
jX = (*env)->NewObject(env, SJG.g.cLong, SJG.g.ctorLong1, |
|
|
(jlong)*((sqlite3_int64*)pX)); |
|
|
|
|
|
|
|
|
s3jni_oom_check( jX ); |
|
|
if( !jX ) rc = SQLITE_NOMEM; |
|
|
break; |
|
|
case SQLITE_TRACE_ROW: |
|
|
break; |
|
|
case SQLITE_TRACE_CLOSE: |
|
|
jP = jPUnref = S3JniRefLocal(ps->jDb); |
|
|
break; |
|
|
default: |
|
|
assert(!"cannot happen - unknown trace flag"); |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
if( 0==rc ){ |
|
|
if( !jP ){ |
|
|
|
|
|
jP = jPUnref = new_java_sqlite3_stmt(env, pP); |
|
|
if( !jP ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
} |
|
|
if( 0==rc ){ |
|
|
assert(jP); |
|
|
rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback, |
|
|
(jint)traceflag, jP, jX); |
|
|
S3JniIfThrew{ |
|
|
rc = s3jni_db_exception(ps->pDb, SQLITE_ERROR, |
|
|
"sqlite3_trace_v2() callback threw."); |
|
|
} |
|
|
} |
|
|
} |
|
|
S3JniUnrefLocal(jPUnref); |
|
|
S3JniUnrefLocal(jX); |
|
|
S3JniHook_localundup(hook); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)( |
|
|
JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer |
|
|
){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
int rc; |
|
|
|
|
|
if( !ps ) return SQLITE_MISUSE; |
|
|
if( !traceMask || !jTracer ){ |
|
|
S3JniDb_mutex_enter; |
|
|
rc = (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0); |
|
|
S3JniHook_unref(&ps->hooks.trace); |
|
|
S3JniDb_mutex_leave; |
|
|
}else{ |
|
|
jclass const klazz = (*env)->GetObjectClass(env, jTracer); |
|
|
S3JniHook hook = S3JniHook_empty; |
|
|
hook.midCallback = (*env)->GetMethodID( |
|
|
env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I" |
|
|
); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionClear; |
|
|
rc = s3jni_db_error(env, ps->pDb, SQLITE_ERROR, |
|
|
"Cannot not find matching call() on " |
|
|
"TracerCallback object."); |
|
|
}else{ |
|
|
hook.jObj = S3JniRefGlobal(jTracer); |
|
|
S3JniDb_mutex_enter; |
|
|
rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps); |
|
|
if( 0==rc ){ |
|
|
S3JniHook_unref(&ps->hooks.trace); |
|
|
ps->hooks.trace = hook ; |
|
|
}else{ |
|
|
S3JniHook_unref(&hook); |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_txn_state(),jint,1txn_1state)( |
|
|
JniArgsEnvClass,jobject jDb, jstring jSchema |
|
|
){ |
|
|
sqlite3 * const pDb = PtrGet_sqlite3(jDb); |
|
|
int rc = SQLITE_MISUSE; |
|
|
if( pDb ){ |
|
|
char * zSchema = jSchema |
|
|
? s3jni_jstring_to_utf8(jSchema, 0) |
|
|
: 0; |
|
|
if( !jSchema || (zSchema && jSchema) ){ |
|
|
rc = sqlite3_txn_state(pDb, zSchema); |
|
|
sqlite3_free(zSchema); |
|
|
}else{ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)( |
|
|
JniArgsEnvClass, jlong jpDb, jobject jHook |
|
|
){ |
|
|
return s3jni_updatepre_hook(env, 0, jpDb, jHook); |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
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(),jint,1value_1bytes)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
return sv ? sqlite3_value_bytes(sv) : 0; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_bytes16(),jint,1value_1bytes16)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
return sv ? sqlite3_value_bytes16(sv) : 0; |
|
|
} |
|
|
|
|
|
|
|
|
S3JniApi(sqlite3_value_double(),jdouble,1value_1double)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_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 = LongPtrGet_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 ) { |
|
|
|
|
|
sqlite3_value_free(sd); |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_free(),void,1value_1free)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
if( sv ){ |
|
|
sqlite3_value_free(sv); |
|
|
} |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_int(),jint,1value_1int)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
return (jint) (sv ? sqlite3_value_int(sv) : 0); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
return (jlong) (sv ? sqlite3_value_int64(sv) : 0LL); |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
return sv |
|
|
? sqlite3_value_pointer(sv, s3jni__value_jref_key) |
|
|
: 0; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_nio_buffer(),jobject,1value_1nio_1buffer)( |
|
|
JniArgsEnvClass, jobject jVal |
|
|
){ |
|
|
sqlite3_value * const sv = PtrGet_sqlite3_value(jVal); |
|
|
jobject rv = 0; |
|
|
if( sv ){ |
|
|
const void * const p = sqlite3_value_blob(sv); |
|
|
if( p ){ |
|
|
const int n = sqlite3_value_bytes(sv); |
|
|
rv = s3jni__blob_to_ByteBuffer(env, p, n); |
|
|
} |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
S3JniApi(sqlite3_value_text(),jbyteArray,1value_1text)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
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 |
|
|
|
|
|
S3JniApi(sqlite3_value_text(),jstring,1value_1text)( |
|
|
JniArgsEnvClass, jlong jpSVal |
|
|
){ |
|
|
sqlite3_value * const sv = LongPtrGet_sqlite3_value(jpSVal); |
|
|
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 = LongPtrGet_sqlite3_value(jpSVal); |
|
|
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 " |
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
"available" |
|
|
#else |
|
|
"unavailable" |
|
|
#endif |
|
|
"." |
|
|
); |
|
|
puts("sizeofs:"); |
|
|
#define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T)) |
|
|
SO(void*); |
|
|
SO(jmethodID); |
|
|
SO(jfieldID); |
|
|
SO(S3JniEnv); |
|
|
SO(S3JniHook); |
|
|
SO(S3JniDb); |
|
|
SO(S3JniNphOps); |
|
|
printf("\t(^^^ %u NativePointerHolder/OutputPointer.T types)\n", |
|
|
(unsigned)S3Jni_NphCache_size); |
|
|
SO(S3JniGlobal); |
|
|
SO(S3JniGlobal.nph); |
|
|
SO(S3JniGlobal.metrics); |
|
|
SO(S3JniAutoExtension); |
|
|
SO(S3JniUdf); |
|
|
#undef SO |
|
|
#ifdef SQLITE_JNI_ENABLE_METRICS |
|
|
printf("Cache info:\n"); |
|
|
printf("\tJNIEnv cache: %u allocs, %u misses, %u hits\n", |
|
|
SJG.metrics.nEnvAlloc, SJG.metrics.nEnvMiss, |
|
|
SJG.metrics.nEnvHit); |
|
|
printf("Mutex entry:" |
|
|
"\n\tglobal = %u" |
|
|
"\n\tenv = %u" |
|
|
"\n\tnph = %u for S3JniNphOp init" |
|
|
"\n\thook = %u" |
|
|
"\n\tperDb = %u" |
|
|
"\n\tautoExt list = %u" |
|
|
"\n\tS3JniUdf = %u (free-list)" |
|
|
"\n\tmetrics = %u\n", |
|
|
SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv, |
|
|
SJG.metrics.nMutexNph, SJG.metrics.nMutexHook, |
|
|
SJG.metrics.nMutexPerDb, SJG.metrics.nMutexAutoExt, |
|
|
SJG.metrics.nMutexUdf, SJG.metrics.nMetrics); |
|
|
puts("Allocs:"); |
|
|
printf("\tS3JniDb: %u alloced (*%u = %u bytes), %u recycled\n", |
|
|
SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb), |
|
|
(unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)), |
|
|
SJG.metrics.nPdbRecycled); |
|
|
printf("\tS3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n", |
|
|
SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf), |
|
|
(unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)), |
|
|
SJG.metrics.nUdfRecycled); |
|
|
printf("\tS3JniHook: %u alloced (*%u = %u bytes), %u recycled\n", |
|
|
SJG.metrics.nHookAlloc, (unsigned) sizeof(S3JniHook), |
|
|
(unsigned)(SJG.metrics.nHookAlloc * sizeof(S3JniHook)), |
|
|
SJG.metrics.nHookRecycled); |
|
|
printf("\tS3JniEnv: %u alloced (*%u = %u bytes)\n", |
|
|
SJG.metrics.nEnvAlloc, (unsigned) sizeof(S3JniEnv), |
|
|
(unsigned)(SJG.metrics.nEnvAlloc * sizeof(S3JniEnv))); |
|
|
puts("Java-side UDF calls:"); |
|
|
#define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T) |
|
|
UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse); |
|
|
#undef UDF |
|
|
printf("xDestroy calls across all callback types: %u\n", |
|
|
SJG.metrics.nDestroy); |
|
|
#else |
|
|
puts("Built without SQLITE_JNI_ENABLE_METRICS."); |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
|
|
|
|
|
|
#define JniFuncNameFtsXA(Suffix) \ |
|
|
Java_org_sqlite_jni_fts5_Fts5ExtensionApi_ ## Suffix |
|
|
#define JniFuncNameFtsApi(Suffix) \ |
|
|
Java_org_sqlite_jni_fts5_fts5_1api_ ## Suffix |
|
|
#define JniFuncNameFtsTok(Suffix) \ |
|
|
Java_org_sqlite_jni_fts5_fts5_tokenizer_ ## Suffix |
|
|
|
|
|
#define JniDeclFtsXA(ReturnType,Suffix) \ |
|
|
JNIEXPORT ReturnType JNICALL \ |
|
|
JniFuncNameFtsXA(Suffix) |
|
|
#define JniDeclFtsApi(ReturnType,Suffix) \ |
|
|
JNIEXPORT ReturnType JNICALL \ |
|
|
JniFuncNameFtsApi(Suffix) |
|
|
#define JniDeclFtsTok(ReturnType,Suffix) \ |
|
|
JNIEXPORT ReturnType JNICALL \ |
|
|
JniFuncNameFtsTok(Suffix) |
|
|
|
|
|
#define PtrGet_fts5_api(OBJ) NativePointerHolder_get(OBJ,S3JniNph(fts5_api)) |
|
|
#define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,S3JniNph(fts5_tokenizer)) |
|
|
#define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,S3JniNph(Fts5Context)) |
|
|
#define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,S3JniNph(Fts5Tokenizer)) |
|
|
#define s3jni_ftsext() &sFts5Api |
|
|
#define Fts5ExtDecl Fts5ExtensionApi const * const ext = s3jni_ftsext() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
jobject jObj ; |
|
|
jobject jUserData |
|
|
|
|
|
|
|
|
|
|
|
; |
|
|
char * zFuncName ; |
|
|
jmethodID jmid ; |
|
|
} Fts5JniAux; |
|
|
|
|
|
static void Fts5JniAux_free(Fts5JniAux * const s){ |
|
|
S3JniDeclLocal_env; |
|
|
if( env ){ |
|
|
|
|
|
s3jni_call_xDestroy(s->jObj); |
|
|
S3JniUnrefGlobal(s->jObj); |
|
|
S3JniUnrefGlobal(s->jUserData); |
|
|
} |
|
|
sqlite3_free(s->zFuncName); |
|
|
sqlite3_free(s); |
|
|
} |
|
|
|
|
|
static void Fts5JniAux_xDestroy(void *p){ |
|
|
if( p ) Fts5JniAux_free(p); |
|
|
} |
|
|
|
|
|
static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){ |
|
|
Fts5JniAux * s = s3jni_malloc( sizeof(Fts5JniAux)); |
|
|
|
|
|
if( s ){ |
|
|
jclass klazz; |
|
|
memset(s, 0, sizeof(Fts5JniAux)); |
|
|
s->jObj = S3JniRefGlobal(jObj); |
|
|
klazz = (*env)->GetObjectClass(env, jObj); |
|
|
s->jmid = (*env)->GetMethodID(env, klazz, "call", |
|
|
"(Lorg/sqlite/jni/fts5/Fts5ExtensionApi;" |
|
|
"Lorg/sqlite/jni/fts5/Fts5Context;" |
|
|
"Lorg/sqlite/jni/capi/sqlite3_context;" |
|
|
"[Lorg/sqlite/jni/capi/sqlite3_value;)V"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
Fts5JniAux_free(s); |
|
|
s = 0; |
|
|
} |
|
|
} |
|
|
return s; |
|
|
} |
|
|
|
|
|
static inline jobject new_java_Fts5Context(JNIEnv * const env, Fts5Context *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(Fts5Context), sv); |
|
|
} |
|
|
static inline jobject new_java_fts5_api(JNIEnv * const env, fts5_api *sv){ |
|
|
return NativePointerHolder_new(env, S3JniNph(fts5_api), sv); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jobject s3jni_getFts5ExtensionApi(JNIEnv * const env){ |
|
|
if( !SJG.fts5.jExt ){ |
|
|
S3JniGlobal_mutex_enter; |
|
|
if( !SJG.fts5.jExt ){ |
|
|
jobject const pNPH = NativePointerHolder_new( |
|
|
env, S3JniNph(Fts5ExtensionApi), s3jni_ftsext() |
|
|
); |
|
|
if( pNPH ){ |
|
|
SJG.fts5.jExt = S3JniRefGlobal(pNPH); |
|
|
S3JniUnrefLocal(pNPH); |
|
|
} |
|
|
} |
|
|
S3JniGlobal_mutex_leave; |
|
|
} |
|
|
return SJG.fts5.jExt; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){ |
|
|
fts5_api *pRet = 0; |
|
|
sqlite3_stmt *pStmt = 0; |
|
|
if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){ |
|
|
sqlite3_bind_pointer(pStmt, 1, (void*)&pRet, "fts5_api_ptr", NULL); |
|
|
sqlite3_step(pStmt); |
|
|
} |
|
|
sqlite3_finalize(pStmt); |
|
|
return pRet; |
|
|
} |
|
|
|
|
|
JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){ |
|
|
S3JniDb * const ps = S3JniDb_from_java(jDb); |
|
|
#if 0 |
|
|
jobject rv = 0; |
|
|
if( !ps ) return 0; |
|
|
else if( ps->fts.jApi ){ |
|
|
rv = ps->fts.jApi; |
|
|
}else{ |
|
|
fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb); |
|
|
if( pApi ){ |
|
|
rv = new_java_fts5_api(env, pApi); |
|
|
ps->fts.jApi = rv ? S3JniRefGlobal(rv) : 0; |
|
|
} |
|
|
} |
|
|
return rv; |
|
|
#else |
|
|
if( ps && !ps->fts.jApi ){ |
|
|
S3JniDb_mutex_enter; |
|
|
if( !ps->fts.jApi ){ |
|
|
fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb); |
|
|
if( pApi ){ |
|
|
jobject const rv = new_java_fts5_api(env, pApi); |
|
|
ps->fts.jApi = rv ? S3JniRefGlobal(rv) : 0; |
|
|
} |
|
|
} |
|
|
S3JniDb_mutex_leave; |
|
|
} |
|
|
return ps ? ps->fts.jApi : 0; |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
JniDeclFtsXA(jobject,getInstance)(JniArgsEnvClass){ |
|
|
return s3jni_getFts5ExtensionApi(env); |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xColumnCount)(JniArgsEnvObj,jobject jCtx){ |
|
|
Fts5ExtDecl; |
|
|
return (jint)ext->xColumnCount(PtrGet_Fts5Context(jCtx)); |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xColumnSize)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOut32){ |
|
|
Fts5ExtDecl; |
|
|
int n1 = 0; |
|
|
int const rc = ext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1); |
|
|
if( 0==rc ) OutputPointer_set_Int32(env, jOut32, n1); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol, |
|
|
jobject jOut){ |
|
|
Fts5ExtDecl; |
|
|
const char *pz = 0; |
|
|
int pn = 0; |
|
|
int rc = ext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol, |
|
|
&pz, &pn); |
|
|
if( 0==rc ){ |
|
|
jstring jstr = pz ? s3jni_utf8_to_jstring( pz, pn) : 0; |
|
|
if( pz ){ |
|
|
if( jstr ){ |
|
|
OutputPointer_set_String(env, jOut, jstr); |
|
|
S3JniUnrefLocal(jstr); |
|
|
}else{ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
} |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xColumnTotalSize)(JniArgsEnvObj,jobject jCtx, jint iCol, jobject jOut64){ |
|
|
Fts5ExtDecl; |
|
|
sqlite3_int64 nOut = 0; |
|
|
int const rc = ext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut); |
|
|
if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi, |
|
|
Fts5Context *pFts, |
|
|
sqlite3_context *pCx, |
|
|
int argc, |
|
|
sqlite3_value **argv){ |
|
|
Fts5JniAux * const pAux = pApi->xUserData(pFts); |
|
|
jobject jpCx = 0; |
|
|
jobjectArray jArgv = 0; |
|
|
jobject jpFts = 0; |
|
|
jobject jFXA; |
|
|
int rc; |
|
|
S3JniDeclLocal_env; |
|
|
|
|
|
assert(pAux); |
|
|
jFXA = s3jni_getFts5ExtensionApi(env); |
|
|
if( !jFXA ) goto error_oom; |
|
|
jpFts = new_java_Fts5Context(env, pFts); |
|
|
if( !jpFts ) goto error_oom; |
|
|
rc = udf_args(env, pCx, argc, argv, &jpCx, &jArgv); |
|
|
if( rc ) goto error_oom; |
|
|
(*env)->CallVoidMethod(env, pAux->jObj, pAux->jmid, |
|
|
jFXA, jpFts, jpCx, jArgv); |
|
|
S3JniIfThrew{ |
|
|
udf_report_exception(env, 1, pCx, pAux->zFuncName, "call"); |
|
|
} |
|
|
udf_unargs(env, jpCx, argc, jArgv); |
|
|
S3JniUnrefLocal(jpFts); |
|
|
S3JniUnrefLocal(jpCx); |
|
|
S3JniUnrefLocal(jArgv); |
|
|
return; |
|
|
error_oom: |
|
|
s3jni_db_oom( sqlite3_context_db_handle(pCx) ); |
|
|
assert( !jArgv ); |
|
|
assert( !jpCx ); |
|
|
S3JniUnrefLocal(jpFts); |
|
|
sqlite3_result_error_nomem(pCx); |
|
|
return; |
|
|
} |
|
|
|
|
|
JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName, |
|
|
jobject jUserData, jobject jFunc){ |
|
|
fts5_api * const pApi = PtrGet_fts5_api(jSelf); |
|
|
int rc; |
|
|
char * zName; |
|
|
Fts5JniAux * pAux; |
|
|
|
|
|
assert(pApi); |
|
|
zName = s3jni_jstring_to_utf8( jName, 0); |
|
|
if(!zName) return SQLITE_NOMEM; |
|
|
pAux = Fts5JniAux_alloc(env, jFunc); |
|
|
if( pAux ){ |
|
|
rc = pApi->xCreateFunction(pApi, zName, pAux, |
|
|
s3jni_fts5_extension_function, |
|
|
Fts5JniAux_xDestroy); |
|
|
}else{ |
|
|
rc = SQLITE_NOMEM; |
|
|
} |
|
|
if( 0==rc ){ |
|
|
pAux->jUserData = jUserData ? S3JniRefGlobal(jUserData) : 0; |
|
|
pAux->zFuncName = zName; |
|
|
}else{ |
|
|
sqlite3_free(zName); |
|
|
} |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
typedef struct S3JniFts5AuxData S3JniFts5AuxData; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct S3JniFts5AuxData { |
|
|
jobject jObj; |
|
|
}; |
|
|
|
|
|
static void S3JniFts5AuxData_xDestroy(void *x){ |
|
|
if( x ){ |
|
|
S3JniFts5AuxData * const p = x; |
|
|
if( p->jObj ){ |
|
|
S3JniDeclLocal_env; |
|
|
s3jni_call_xDestroy(p->jObj); |
|
|
S3JniUnrefGlobal(p->jObj); |
|
|
} |
|
|
sqlite3_free(x); |
|
|
} |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jobject,xGetAuxdata)(JniArgsEnvObj,jobject jCtx, jboolean bClear){ |
|
|
Fts5ExtDecl; |
|
|
jobject rv = 0; |
|
|
S3JniFts5AuxData * const pAux = ext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear); |
|
|
if( pAux ){ |
|
|
if( bClear ){ |
|
|
if( pAux->jObj ){ |
|
|
rv = S3JniRefLocal(pAux->jObj); |
|
|
S3JniUnrefGlobal(pAux->jObj); |
|
|
} |
|
|
|
|
|
sqlite3_free(pAux); |
|
|
}else{ |
|
|
rv = pAux->jObj; |
|
|
} |
|
|
} |
|
|
return rv; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xInst)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOutPhrase, |
|
|
jobject jOutCol, jobject jOutOff){ |
|
|
Fts5ExtDecl; |
|
|
int n1 = 0, n2 = 2, n3 = 0; |
|
|
int const rc = ext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int32(env, jOutPhrase, n1); |
|
|
OutputPointer_set_Int32(env, jOutCol, n2); |
|
|
OutputPointer_set_Int32(env, jOutOff, n3); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xInstCount)(JniArgsEnvObj,jobject jCtx, jobject jOut32){ |
|
|
Fts5ExtDecl; |
|
|
int nOut = 0; |
|
|
int const rc = ext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut); |
|
|
if( 0==rc && jOut32 ) OutputPointer_set_Int32(env, jOut32, nOut); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xPhraseCount)(JniArgsEnvObj,jobject jCtx){ |
|
|
Fts5ExtDecl; |
|
|
return (jint)ext->xPhraseCount(PtrGet_Fts5Context(jCtx)); |
|
|
} |
|
|
|
|
|
|
|
|
static void s3jni_phraseIter_NToJ(JNIEnv *const env, |
|
|
Fts5PhraseIter const * const pSrc, |
|
|
jobject jIter){ |
|
|
S3JniGlobalType * const g = &S3JniGlobal; |
|
|
assert(g->fts5.jPhraseIter.fidA); |
|
|
(*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidA, |
|
|
S3JniCast_P2L(pSrc->a)); |
|
|
S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.a field."); |
|
|
(*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidB, |
|
|
S3JniCast_P2L(pSrc->b)); |
|
|
S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.b field."); |
|
|
} |
|
|
|
|
|
|
|
|
static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter, |
|
|
Fts5PhraseIter * const pDest){ |
|
|
S3JniGlobalType * const g = &S3JniGlobal; |
|
|
assert(g->fts5.jPhraseIter.fidA); |
|
|
pDest->a = S3JniCast_L2P( |
|
|
(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidA) |
|
|
); |
|
|
S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field."); |
|
|
pDest->b = S3JniCast_L2P( |
|
|
(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidB) |
|
|
); |
|
|
S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field."); |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xPhraseFirst)(JniArgsEnvObj,jobject jCtx, jint iPhrase, |
|
|
jobject jIter, jobject jOutCol, |
|
|
jobject jOutOff){ |
|
|
Fts5ExtDecl; |
|
|
Fts5PhraseIter iter; |
|
|
int rc, iCol = 0, iOff = 0; |
|
|
rc = ext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase, |
|
|
&iter, &iCol, &iOff); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int32(env, jOutCol, iCol); |
|
|
OutputPointer_set_Int32(env, jOutOff, iOff); |
|
|
s3jni_phraseIter_NToJ(env, &iter, jIter); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xPhraseFirstColumn)(JniArgsEnvObj,jobject jCtx, jint iPhrase, |
|
|
jobject jIter, jobject jOutCol){ |
|
|
Fts5ExtDecl; |
|
|
Fts5PhraseIter iter; |
|
|
int rc, iCol = 0; |
|
|
rc = ext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase, |
|
|
&iter, &iCol); |
|
|
if( 0==rc ){ |
|
|
OutputPointer_set_Int32(env, jOutCol, iCol); |
|
|
s3jni_phraseIter_NToJ(env, &iter, jIter); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(void,xPhraseNext)(JniArgsEnvObj,jobject jCtx, jobject jIter, |
|
|
jobject jOutCol, jobject jOutOff){ |
|
|
Fts5ExtDecl; |
|
|
Fts5PhraseIter iter; |
|
|
int iCol = 0, iOff = 0; |
|
|
s3jni_phraseIter_JToN(env, jIter, &iter); |
|
|
ext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff); |
|
|
OutputPointer_set_Int32(env, jOutCol, iCol); |
|
|
OutputPointer_set_Int32(env, jOutOff, iOff); |
|
|
s3jni_phraseIter_NToJ(env, &iter, jIter); |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(void,xPhraseNextColumn)(JniArgsEnvObj,jobject jCtx, jobject jIter, |
|
|
jobject jOutCol){ |
|
|
Fts5ExtDecl; |
|
|
Fts5PhraseIter iter; |
|
|
int iCol = 0; |
|
|
s3jni_phraseIter_JToN(env, jIter, &iter); |
|
|
ext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol); |
|
|
OutputPointer_set_Int32(env, jOutCol, iCol); |
|
|
s3jni_phraseIter_NToJ(env, &iter, jIter); |
|
|
} |
|
|
|
|
|
|
|
|
JniDeclFtsXA(jint,xPhraseSize)(JniArgsEnvObj,jobject jCtx, jint iPhrase){ |
|
|
Fts5ExtDecl; |
|
|
return (jint)ext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase); |
|
|
} |
|
|
|
|
|
|
|
|
struct s3jni_xQueryPhraseState { |
|
|
Fts5ExtensionApi const * ext; |
|
|
jmethodID midCallback; |
|
|
jobject jCallback; |
|
|
jobject jFcx; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct { |
|
|
const char * zPrev; |
|
|
int nPrev; |
|
|
jbyteArray jba; |
|
|
} tok; |
|
|
}; |
|
|
|
|
|
static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi, |
|
|
Fts5Context * pFcx, void *pData){ |
|
|
struct s3jni_xQueryPhraseState * const s = pData; |
|
|
S3JniDeclLocal_env; |
|
|
|
|
|
if( !s->jFcx ){ |
|
|
s->jFcx = new_java_Fts5Context(env, pFcx); |
|
|
if( !s->jFcx ) return SQLITE_NOMEM; |
|
|
} |
|
|
int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, |
|
|
SJG.fts5.jExt, s->jFcx); |
|
|
S3JniIfThrew{ |
|
|
S3JniExceptionWarnCallbackThrew("xQueryPhrase() callback"); |
|
|
S3JniExceptionClear; |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase, |
|
|
jobject jCallback){ |
|
|
Fts5ExtDecl; |
|
|
int rc; |
|
|
struct s3jni_xQueryPhraseState s; |
|
|
jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; |
|
|
|
|
|
if( !klazz ) return SQLITE_MISUSE; |
|
|
s.jCallback = jCallback; |
|
|
s.jFcx = 0; |
|
|
s.ext = ext; |
|
|
s.midCallback = (*env)->GetMethodID(env, klazz, "call", |
|
|
"(Lorg/sqlite/jni/fts5/Fts5ExtensionApi;" |
|
|
"Lorg/sqlite/jni/fts5/Fts5Context;)I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniExceptionIsFatal("Could not extract xQueryPhraseCallback.call() method."); |
|
|
rc = ext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s, |
|
|
s3jni_xQueryPhrase); |
|
|
S3JniUnrefLocal(s.jFcx); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
|
|
|
JniDeclFtsXA(jint,xRowCount)(JniArgsEnvObj,jobject jCtx, jobject jOut64){ |
|
|
Fts5ExtDecl; |
|
|
sqlite3_int64 nOut = 0; |
|
|
int const rc = ext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut); |
|
|
if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){ |
|
|
Fts5ExtDecl; |
|
|
return (jlong)ext->xRowid(PtrGet_Fts5Context(jCtx)); |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){ |
|
|
Fts5ExtDecl; |
|
|
int rc; |
|
|
S3JniFts5AuxData * pAux; |
|
|
|
|
|
pAux = s3jni_malloc( sizeof(*pAux)); |
|
|
if( !pAux ){ |
|
|
if( jAux ){ |
|
|
|
|
|
|
|
|
s3jni_call_xDestroy(jAux); |
|
|
} |
|
|
return SQLITE_NOMEM; |
|
|
} |
|
|
pAux->jObj = S3JniRefGlobal(jAux); |
|
|
rc = ext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux, |
|
|
S3JniFts5AuxData_xDestroy); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z, |
|
|
int nZ, int iStart, int iEnd){ |
|
|
int rc; |
|
|
S3JniDeclLocal_env; |
|
|
struct s3jni_xQueryPhraseState * const s = p; |
|
|
jbyteArray jba; |
|
|
|
|
|
S3JniUnrefLocal(s->tok.jba); |
|
|
s->tok.zPrev = z; |
|
|
s->tok.nPrev = nZ; |
|
|
s->tok.jba = s3jni_new_jbyteArray(z, nZ); |
|
|
if( !s->tok.jba ) return SQLITE_NOMEM; |
|
|
jba = s->tok.jba; |
|
|
rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback, |
|
|
(jint)tFlags, jba, (jint)iStart, |
|
|
(jint)iEnd); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionWarnCallbackThrew("xTokenize() callback"); |
|
|
rc = SQLITE_ERROR; |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphOp const *pRef, |
|
|
jint tokFlags, jobject jFcx, |
|
|
jbyteArray jbaText, jobject jCallback){ |
|
|
Fts5ExtDecl; |
|
|
struct s3jni_xQueryPhraseState s; |
|
|
int rc = 0; |
|
|
jbyte * const pText = jCallback ? s3jni_jbyteArray_bytes(jbaText) : 0; |
|
|
jsize nText = pText ? (*env)->GetArrayLength(env, jbaText) : 0; |
|
|
jclass const klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL; |
|
|
|
|
|
if( !klazz ) return SQLITE_MISUSE; |
|
|
memset(&s, 0, sizeof(s)); |
|
|
s.jCallback = jCallback; |
|
|
s.jFcx = jFcx; |
|
|
s.ext = ext; |
|
|
s.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I[BII)I"); |
|
|
S3JniUnrefLocal(klazz); |
|
|
S3JniIfThrew { |
|
|
S3JniExceptionReport; |
|
|
S3JniExceptionClear; |
|
|
s3jni_jbyteArray_release(jbaText, pText); |
|
|
return SQLITE_ERROR; |
|
|
} |
|
|
s.tok.jba = S3JniRefLocal(jbaText); |
|
|
s.tok.zPrev = (const char *)pText; |
|
|
s.tok.nPrev = (int)nText; |
|
|
if( pRef == S3JniNph(Fts5ExtensionApi) ){ |
|
|
rc = ext->xTokenize(PtrGet_Fts5Context(jFcx), |
|
|
(const char *)pText, (int)nText, |
|
|
&s, s3jni_xTokenize_xToken); |
|
|
}else if( pRef == S3JniNph(fts5_tokenizer) ){ |
|
|
fts5_tokenizer * const pTok = PtrGet_fts5_tokenizer(jSelf); |
|
|
rc = pTok->xTokenize(PtrGet_Fts5Tokenizer(jFcx), &s, tokFlags, |
|
|
(const char *)pText, (int)nText, |
|
|
s3jni_xTokenize_xToken); |
|
|
}else{ |
|
|
(*env)->FatalError(env, "This cannot happen. Maintenance required."); |
|
|
} |
|
|
if( s.tok.jba ){ |
|
|
assert( s.tok.zPrev ); |
|
|
S3JniUnrefLocal(s.tok.jba); |
|
|
} |
|
|
s3jni_jbyteArray_release(jbaText, pText); |
|
|
return (jint)rc; |
|
|
} |
|
|
|
|
|
JniDeclFtsXA(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jbyteArray jbaText, |
|
|
jobject jCallback){ |
|
|
return s3jni_fts5_xTokenize(env, jSelf, S3JniNph(Fts5ExtensionApi), |
|
|
0, jFcx, jbaText, jCallback); |
|
|
} |
|
|
|
|
|
JniDeclFtsTok(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jint tokFlags, |
|
|
jbyteArray jbaText, jobject jCallback){ |
|
|
return s3jni_fts5_xTokenize(env, jSelf, S3JniNph(Fts5Tokenizer), |
|
|
tokFlags, jFcx, jbaText, jCallback); |
|
|
} |
|
|
|
|
|
|
|
|
JniDeclFtsXA(jobject,xUserData)(JniArgsEnvObj,jobject jFcx){ |
|
|
Fts5ExtDecl; |
|
|
Fts5JniAux * const pAux = ext->xUserData(PtrGet_Fts5Context(jFcx)); |
|
|
return pAux ? pAux->jUserData : 0; |
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SQLITE_JNI_ENABLE_SQLTester |
|
|
typedef struct SQLTesterJni SQLTesterJni; |
|
|
struct SQLTesterJni { |
|
|
sqlite3_int64 nDup; |
|
|
}; |
|
|
static SQLTesterJni SQLTester = { |
|
|
0 |
|
|
}; |
|
|
|
|
|
static void SQLTester_dup_destructor(void*pToFree){ |
|
|
u64 *p = (u64*)pToFree; |
|
|
assert( p!=0 ); |
|
|
p--; |
|
|
assert( p[0]==0x2bbf4b7c ); |
|
|
p[0] = 0; |
|
|
p[1] = 0; |
|
|
sqlite3_free(p); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SQLTester_dup_func( |
|
|
sqlite3_context *context, |
|
|
int argc, |
|
|
sqlite3_value **argv |
|
|
){ |
|
|
u64 *pOut; |
|
|
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 = s3jni_malloc( (n+16)&~7 ))!=0 ){ |
|
|
pOut[0] = 0x2bbf4b7c; |
|
|
z = (char*)&pOut[1]; |
|
|
memcpy(z, sqlite3_value_text(argv[0]), n); |
|
|
z[n] = 0; |
|
|
sqlite3_result_text(context, z, n, SQLTester_dup_destructor); |
|
|
} |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void SQLTester_dup_count_func( |
|
|
sqlite3_context *context, |
|
|
int argc, |
|
|
sqlite3_value **argv |
|
|
){ |
|
|
SQLTesterJni * const p = (SQLTesterJni *)sqlite3_user_data(context); |
|
|
sqlite3_result_int64(context, p->nDup); |
|
|
p->nDup = 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int SQLTester_strnotglob(const char *zGlob, const char *z){ |
|
|
int c, c2; |
|
|
int invert; |
|
|
int seen; |
|
|
|
|
|
while( (c = (*(zGlob++)))!=0 ){ |
|
|
if( c=='*' ){ |
|
|
while( (c=(*(zGlob++))) == '*' || c=='?' ){ |
|
|
if( c=='?' && (*(z++))==0 ) return 0; |
|
|
} |
|
|
if( c==0 ){ |
|
|
return 1; |
|
|
}else if( c=='[' ){ |
|
|
while( *z && SQLTester_strnotglob(zGlob-1,z)==0 ){ |
|
|
z++; |
|
|
} |
|
|
return (*z)!=0; |
|
|
} |
|
|
while( (c2 = (*(z++)))!=0 ){ |
|
|
while( c2!=c ){ |
|
|
c2 = *(z++); |
|
|
if( c2==0 ) return 0; |
|
|
} |
|
|
if( SQLTester_strnotglob(zGlob,z) ) return 1; |
|
|
} |
|
|
return 0; |
|
|
}else if( c=='?' ){ |
|
|
if( (*(z++))==0 ) return 0; |
|
|
}else if( c=='[' ){ |
|
|
int prior_c = 0; |
|
|
seen = 0; |
|
|
invert = 0; |
|
|
c = *(z++); |
|
|
if( c==0 ) return 0; |
|
|
c2 = *(zGlob++); |
|
|
if( c2=='^' ){ |
|
|
invert = 1; |
|
|
c2 = *(zGlob++); |
|
|
} |
|
|
if( c2==']' ){ |
|
|
if( c==']' ) seen = 1; |
|
|
c2 = *(zGlob++); |
|
|
} |
|
|
while( c2 && c2!=']' ){ |
|
|
if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){ |
|
|
c2 = *(zGlob++); |
|
|
if( c>=prior_c && c<=c2 ) seen = 1; |
|
|
prior_c = 0; |
|
|
}else{ |
|
|
if( c==c2 ){ |
|
|
seen = 1; |
|
|
} |
|
|
prior_c = c2; |
|
|
} |
|
|
c2 = *(zGlob++); |
|
|
} |
|
|
if( c2==0 || (seen ^ invert)==0 ) return 0; |
|
|
}else if( c=='#' ){ |
|
|
if( z[0]=='0' |
|
|
&& (z[1]=='x' || z[1]=='X') |
|
|
&& sqlite3Isxdigit(z[2]) |
|
|
){ |
|
|
z += 3; |
|
|
while( sqlite3Isxdigit(z[0]) ){ z++; } |
|
|
}else{ |
|
|
if( (z[0]=='-' || z[0]=='+') && sqlite3Isdigit(z[1]) ) z++; |
|
|
if( !sqlite3Isdigit(z[0]) ) return 0; |
|
|
z++; |
|
|
while( sqlite3Isdigit(z[0]) ){ z++; } |
|
|
} |
|
|
}else{ |
|
|
if( c!=(*(z++)) ) return 0; |
|
|
} |
|
|
} |
|
|
return *z==0; |
|
|
} |
|
|
|
|
|
JNIEXPORT jint JNICALL |
|
|
Java_org_sqlite_jni_capi_SQLTester_strglob( |
|
|
JniArgsEnvClass, jbyteArray baG, jbyteArray baT |
|
|
){ |
|
|
int rc = 0; |
|
|
jbyte * const pG = s3jni_jbyteArray_bytes(baG); |
|
|
jbyte * const pT = pG ? s3jni_jbyteArray_bytes(baT) : 0; |
|
|
|
|
|
s3jni_oom_fatal(pT); |
|
|
|
|
|
|
|
|
rc = !SQLTester_strnotglob((const char *)pG, (const char *)pT); |
|
|
s3jni_jbyteArray_release(baG, pG); |
|
|
s3jni_jbyteArray_release(baT, pT); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
static int SQLTester_auto_extension(sqlite3 *pDb, const char **pzErr, |
|
|
const struct sqlite3_api_routines *ignored){ |
|
|
sqlite3_create_function(pDb, "dup", 1, SQLITE_UTF8, &SQLTester, |
|
|
SQLTester_dup_func, 0, 0); |
|
|
sqlite3_create_function(pDb, "dup_count", 0, SQLITE_UTF8, &SQLTester, |
|
|
SQLTester_dup_count_func, 0, 0); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
JNIEXPORT void JNICALL |
|
|
Java_org_sqlite_jni_capi_SQLTester_installCustomExtensions(JniArgsEnvClass){ |
|
|
sqlite3_auto_extension( (void(*)(void))SQLTester_auto_extension ); |
|
|
} |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JNIEXPORT void JNICALL |
|
|
Java_org_sqlite_jni_capi_CApi_init(JniArgsEnvClass){ |
|
|
jclass klazz; |
|
|
|
|
|
memset(&S3JniGlobal, 0, sizeof(S3JniGlobal)); |
|
|
if( (*env)->GetJavaVM(env, &SJG.jvm) ){ |
|
|
(*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible."); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long")); |
|
|
S3JniExceptionIsFatal("Error getting reference to Long class."); |
|
|
SJG.g.ctorLong1 = (*env)->GetMethodID(env, SJG.g.cLong, |
|
|
"<init>", "(J)V"); |
|
|
S3JniExceptionIsFatal("Error getting reference to Long constructor."); |
|
|
|
|
|
SJG.g.cString = S3JniRefGlobal((*env)->FindClass(env,"java/lang/String")); |
|
|
S3JniExceptionIsFatal("Error getting reference to String class."); |
|
|
SJG.g.ctorStringBA = |
|
|
(*env)->GetMethodID(env, SJG.g.cString, |
|
|
"<init>", "([BLjava/nio/charset/Charset;)V"); |
|
|
S3JniExceptionIsFatal("Error getting reference to String(byte[],Charset) ctor."); |
|
|
SJG.g.stringGetBytes = |
|
|
(*env)->GetMethodID(env, SJG.g.cString, |
|
|
"getBytes", "(Ljava/nio/charset/Charset;)[B"); |
|
|
S3JniExceptionIsFatal("Error getting reference to String.getBytes(Charset)."); |
|
|
|
|
|
{ |
|
|
jfieldID fUtf8; |
|
|
klazz = (*env)->FindClass(env,"java/nio/charset/StandardCharsets"); |
|
|
S3JniExceptionIsFatal("Error getting reference to StandardCharsets class."); |
|
|
fUtf8 = (*env)->GetStaticFieldID(env, klazz, "UTF_8", |
|
|
"Ljava/nio/charset/Charset;"); |
|
|
S3JniExceptionIsFatal("Error getting StandardCharsets.UTF_8 field."); |
|
|
SJG.g.oCharsetUtf8 = |
|
|
S3JniRefGlobal((*env)->GetStaticObjectField(env, klazz, fUtf8)); |
|
|
S3JniExceptionIsFatal("Error getting reference to StandardCharsets.UTF_8."); |
|
|
S3JniUnrefLocal(klazz); |
|
|
} |
|
|
|
|
|
#ifdef SQLITE_ENABLE_FTS5 |
|
|
klazz = (*env)->FindClass(env, "org/sqlite/jni/fts5/Fts5PhraseIter"); |
|
|
S3JniExceptionIsFatal("Error getting reference to org.sqlite.jni.fts5.Fts5PhraseIter."); |
|
|
SJG.fts5.jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J"); |
|
|
S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field."); |
|
|
SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "b", "J"); |
|
|
S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field."); |
|
|
S3JniUnrefLocal(klazz); |
|
|
#endif |
|
|
|
|
|
SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.mutex ); |
|
|
SJG.hook.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.hook.mutex ); |
|
|
SJG.nph.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.nph.mutex ); |
|
|
SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.envCache.mutex ); |
|
|
SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.perDb.mutex ); |
|
|
SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.autoExt.mutex ); |
|
|
|
|
|
#if S3JNI_METRICS_MUTEX |
|
|
SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
|
|
s3jni_oom_fatal( SJG.metrics.mutex ); |
|
|
#endif |
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
unsigned char buf[16] = {0}; |
|
|
jobject bb = (*env)->NewDirectByteBuffer(env, buf, 16); |
|
|
if( bb ){ |
|
|
SJG.g.byteBuffer.klazz = S3JniRefGlobal((*env)->GetObjectClass(env, bb)); |
|
|
SJG.g.byteBuffer.midAlloc = (*env)->GetStaticMethodID( |
|
|
env, SJG.g.byteBuffer.klazz, "allocateDirect", "(I)Ljava/nio/ByteBuffer;" |
|
|
); |
|
|
S3JniExceptionIsFatal("Error getting ByteBuffer.allocateDirect() method."); |
|
|
SJG.g.byteBuffer.midLimit = (*env)->GetMethodID( |
|
|
env, SJG.g.byteBuffer.klazz, "limit", "()I" |
|
|
); |
|
|
S3JniExceptionIsFatal("Error getting ByteBuffer.limit() method."); |
|
|
S3JniUnrefLocal(bb); |
|
|
}else{ |
|
|
SJG.g.byteBuffer.klazz = 0; |
|
|
SJG.g.byteBuffer.midAlloc = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
sqlite3_shutdown() |
|
|
|
|
|
; |
|
|
} |
|
|
|