Expose sqlite3_limit() to JNI and remove the all SQLITE_ macros from the Java interfa...
[sqlite.git] / ext / jni / src / c / sqlite3-jni.c
blob4151551a5a8997187cf47225eb40c3c44e629026
1 /*
2 ** 2023-07-21
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
12 ** This file implements the JNI bindings declared in
13 ** org.sqlite.jni.SQLiteJni (from which sqlite3-jni.h is generated).
17 ** If you found this comment by searching the code for
18 ** CallStaticObjectMethod then you're the victim of an OpenJDK bug:
20 ** https://bugs.openjdk.org/browse/JDK-8130659
22 ** It's known to happen with OpenJDK v8 but not with v19.
24 ** This code does not use JNI's CallStaticObjectMethod().
28 ** Define any SQLITE_... config defaults we want if they aren't
29 ** overridden by the builder. Please keep these alphabetized.
32 /**********************************************************************/
33 /* SQLITE_D... */
34 #ifndef SQLITE_DEFAULT_CACHE_SIZE
35 # define SQLITE_DEFAULT_CACHE_SIZE -16384
36 #endif
37 #if !defined(SQLITE_DEFAULT_PAGE_SIZE)
38 # define SQLITE_DEFAULT_PAGE_SIZE 8192
39 #endif
40 #ifndef SQLITE_DQS
41 # define SQLITE_DQS 0
42 #endif
44 /**********************************************************************/
45 /* SQLITE_ENABLE_... */
46 #ifndef SQLITE_ENABLE_BYTECODE_VTAB
47 # define SQLITE_ENABLE_BYTECODE_VTAB 1
48 #endif
49 #ifndef SQLITE_ENABLE_DBPAGE_VTAB
50 # define SQLITE_ENABLE_DBPAGE_VTAB 1
51 #endif
52 #ifndef SQLITE_ENABLE_DBSTAT_VTAB
53 # define SQLITE_ENABLE_DBSTAT_VTAB 1
54 #endif
55 #ifndef SQLITE_ENABLE_EXPLAIN_COMMENTS
56 # define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
57 #endif
58 #ifndef SQLITE_ENABLE_MATH_FUNCTIONS
59 # define SQLITE_ENABLE_MATH_FUNCTIONS 1
60 #endif
61 #ifndef SQLITE_ENABLE_OFFSET_SQL_FUNC
62 # define SQLITE_ENABLE_OFFSET_SQL_FUNC 1
63 #endif
64 #ifndef SQLITE_ENABLE_RTREE
65 # define SQLITE_ENABLE_RTREE 1
66 #endif
67 //#ifndef SQLITE_ENABLE_SESSION
68 //# define SQLITE_ENABLE_SESSION 1
69 //#endif
70 #ifndef SQLITE_ENABLE_STMTVTAB
71 # define SQLITE_ENABLE_STMTVTAB 1
72 #endif
73 //#ifndef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
74 //# define SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
75 //#endif
77 /**********************************************************************/
78 /* SQLITE_J... */
79 #ifdef SQLITE_JNI_FATAL_OOM
80 #if !SQLITE_JNI_FATAL_OOM
81 #undef SQLITE_JNI_FATAL_OOM
82 #endif
83 #endif
85 /**********************************************************************/
86 /* SQLITE_M... */
87 #ifndef SQLITE_MAX_ALLOCATION_SIZE
88 # define SQLITE_MAX_ALLOCATION_SIZE 0x1fffffff
89 #endif
91 /**********************************************************************/
92 /* SQLITE_O... */
93 #ifndef SQLITE_OMIT_DEPRECATED
94 # define SQLITE_OMIT_DEPRECATED 1
95 #endif
96 #ifndef SQLITE_OMIT_LOAD_EXTENSION
97 # define SQLITE_OMIT_LOAD_EXTENSION 1
98 #endif
99 #ifndef SQLITE_OMIT_SHARED_CACHE
100 # define SQLITE_OMIT_SHARED_CACHE 1
101 #endif
102 #ifdef SQLITE_OMIT_UTF16
103 /* UTF16 is required for java */
104 # undef SQLITE_OMIT_UTF16 1
105 #endif
107 /**********************************************************************/
108 /* SQLITE_T... */
109 #ifndef SQLITE_TEMP_STORE
110 # define SQLITE_TEMP_STORE 2
111 #endif
112 #ifndef SQLITE_THREADSAFE
113 # define SQLITE_THREADSAFE 1
114 #endif
116 /**********************************************************************/
117 /* SQLITE_USE_... */
118 #ifndef SQLITE_USE_URI
119 # define SQLITE_USE_URI 1
120 #endif
124 ** Which sqlite3.c we're using needs to be configurable to enable
125 ** building against a custom copy, e.g. the SEE variant. We have to
126 ** include sqlite3.c, as opposed to sqlite3.h, in order to get access
127 ** to some interal details like SQLITE_MAX_... and friends. This
128 ** increases the rebuild time considerably but we need this in order
129 ** to access some internal functionality and keep the to-Java-exported
130 ** values of SQLITE_MAX_... and SQLITE_LIMIT_... in sync with the C
131 ** build.
133 #ifndef SQLITE_C
134 # define SQLITE_C sqlite3.c
135 #endif
136 #define INC__STRINGIFY_(f) #f
137 #define INC__STRINGIFY(f) INC__STRINGIFY_(f)
138 #include INC__STRINGIFY(SQLITE_C)
139 #undef INC__STRINGIFY_
140 #undef INC__STRINGIFY
141 #undef SQLITE_C
144 ** End of the sqlite3 lib setup. What follows is JNI-specific.
147 #include "sqlite3-jni.h"
148 #include <assert.h>
149 #include <stdio.h> /* only for testing/debugging */
151 /* Only for debugging */
152 #define MARKER(pfexp) \
153 do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); \
154 printf pfexp; \
155 } while(0)
158 ** Creates a verbose JNI function name. Suffix must be
159 ** the JNI-mangled form of the function's name, minus the
160 ** prefix seen in this macro.
162 #define JniFuncName(Suffix) \
163 Java_org_sqlite_jni_SQLite3Jni_sqlite3_ ## Suffix
165 /* Prologue for JNI function declarations and definitions. */
166 #define JniDecl(ReturnType,Suffix) \
167 JNIEXPORT ReturnType JNICALL JniFuncName(Suffix)
170 ** S3JniApi's intent is that CFunc be the C API func(s) the
171 ** being-declared JNI function is wrapping, making it easier to find
172 ** that function's JNI-side entry point. The other args are for JniDecl.
174 #define S3JniApi(CFunc,ReturnType,Suffix) JniDecl(ReturnType,Suffix)
177 ** Shortcuts for the first 2 parameters to all JNI bindings.
179 ** The type of the jSelf arg differs, but no docs seem to mention
180 ** this: for static methods it's of type jclass and for non-static
181 ** it's jobject. jobject actually works for all funcs, in the sense
182 ** that it compiles and runs so long as we don't use jSelf (which is
183 ** only rarely needed in this code), but to be pedantically correct we
184 ** need the proper type in the signature.
186 ** https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#jni_interface_functions_and_pointers
188 #define JniArgsEnvObj JNIEnv * const env, jobject jSelf
189 #define JniArgsEnvClass JNIEnv * const env, jclass jKlazz
191 ** Helpers to account for -Xcheck:jni warnings about not having
192 ** checked for exceptions.
194 #define S3JniIfThrew if( (*env)->ExceptionCheck(env) )
195 #define S3JniExceptionClear (*env)->ExceptionClear(env)
196 #define S3JniExceptionReport (*env)->ExceptionDescribe(env)
197 #define S3JniExceptionIgnore S3JniIfThrew S3JniExceptionClear
198 #define S3JniExceptionWarnIgnore \
199 S3JniIfThrew {S3JniExceptionReport; S3JniExceptionClear;}(void)0
200 #define S3JniExceptionWarnCallbackThrew(STR) \
201 MARKER(("WARNING: " STR " MUST NOT THROW.\n")); \
202 (*env)->ExceptionDescribe(env)
204 /** To be used for cases where we're _really_ not expecting an
205 exception, e.g. looking up well-defined Java class members. */
206 #define S3JniExceptionIsFatal(MSG) S3JniIfThrew {\
207 S3JniExceptionReport; S3JniExceptionClear; \
208 (*env)->FatalError(env, MSG); \
212 ** Declares local var env = s3jni_env(). All JNI calls involve a
213 ** JNIEnv somewhere, always named env, and many of our macros assume
214 ** env is in scope. Where it's not, but should be, use this to make it
215 ** so.
217 #define S3JniDeclLocal_env JNIEnv * const env = s3jni_env()
219 /* Fail fatally with an OOM message. */
220 static inline void s3jni_oom(JNIEnv * const env){
221 (*env)->FatalError(env, "SQLite3 JNI is out of memory.") /* does not return */;
225 ** sqlite3_malloc() proxy which fails fatally on OOM. This should
226 ** only be used for routines which manage global state and have no
227 ** recovery strategy for OOM. For sqlite3 API which can reasonably
228 ** return SQLITE_NOMEM, s3jni_malloc() should be used instead.
230 static void * s3jni_malloc_or_die(JNIEnv * const env, size_t n){
231 void * const rv = sqlite3_malloc(n);
232 if( n && !rv ) s3jni_oom(env);
233 return rv;
237 ** Works like sqlite3_malloc() unless built with SQLITE_JNI_FATAL_OOM,
238 ** in which case it calls s3jni_oom() on OOM.
240 #ifdef SQLITE_JNI_FATAL_OOM
241 #define s3jni_malloc(SIZE) s3jni_malloc_or_die(env, SIZE)
242 #else
243 #define s3jni_malloc(SIZE) sqlite3_malloc(((void)env,(SIZE)))
244 #endif
247 ** Works like sqlite3_realloc() unless built with SQLITE_JNI_FATAL_OOM,
248 ** in which case it calls s3jni_oom() on OOM.
250 #ifdef SQLITE_JNI_FATAL_OOM
251 static void * s3jni_realloc_or_die(JNIEnv * const env, void * p, size_t n){
252 void * const rv = sqlite3_realloc(p, (int)n);
253 if( n && !rv ) s3jni_oom(env);
254 return rv;
256 #define s3jni_realloc(MEM,SIZE) s3jni_realloc_or_die(env, (MEM), (SIZE))
257 #else
258 #define s3jni_realloc(MEM,SIZE) sqlite3_realloc((MEM), ((void)env, (SIZE)))
259 #endif
261 /* Fail fatally if !EXPR. */
262 #define s3jni_oom_fatal(EXPR) if( !(EXPR) ) s3jni_oom(env)
263 /* Maybe fail fatally if !EXPR. */
264 #ifdef SQLITE_JNI_FATAL_OOM
265 #define s3jni_oom_check s3jni_oom_fatal
266 #else
267 #define s3jni_oom_check(EXPR)
268 #endif
269 //#define S3JniDb_oom(pDb,EXPR) ((EXPR) ? sqlite3OomFault(pDb) : 0)
271 /* Helpers for Java value reference management. */
272 static jobject s3jni_ref_global(JNIEnv * const env, jobject const v){
273 jobject const rv = v ? (*env)->NewGlobalRef(env, v) : NULL;
274 s3jni_oom_fatal( v ? !!rv : 1 );
275 return rv;
277 static jobject s3jni_ref_local(JNIEnv * const env, jobject const v){
278 jobject const rv = v ? (*env)->NewLocalRef(env, v) : NULL;
279 s3jni_oom_fatal( v ? !!rv : 1 );
280 return rv;
282 static inline void s3jni_unref_global(JNIEnv * const env, jobject const v){
283 if( v ) (*env)->DeleteGlobalRef(env, v);
285 static inline void s3jni_unref_local(JNIEnv * const env, jobject const v){
286 if( v ) (*env)->DeleteLocalRef(env, v);
288 #define S3JniRefGlobal(VAR) s3jni_ref_global(env, (VAR))
289 #define S3JniRefLocal(VAR) s3jni_ref_local(env, (VAR))
290 #define S3JniUnrefGlobal(VAR) s3jni_unref_global(env, (VAR))
291 #define S3JniUnrefLocal(VAR) s3jni_unref_local(env, (VAR))
294 ** Lookup key type for use with s3jni_nph().
296 typedef struct S3JniNphRef S3JniNphRef;
297 struct S3JniNphRef {
298 const int index /* index into S3JniGlobal.nph[] */;
299 const char * const zName /* Full Java name of the class */;
300 const char * const zMember /* Name of member property */;
301 const char * const zTypeSig /* JNI type signature of zMember */;
305 ** Cache keys for each concrete NativePointerHolder subclass and
306 ** OutputPointer.T type. The members are to be used with
307 ** s3jni_nph() and friends, and each one's member->index
308 ** corresponds to its index in the S3JniGlobal.nph[] array.
310 static const struct {
311 const S3JniNphRef sqlite3;
312 const S3JniNphRef sqlite3_stmt;
313 const S3JniNphRef sqlite3_context;
314 const S3JniNphRef sqlite3_value;
315 const S3JniNphRef OutputPointer_Bool;
316 const S3JniNphRef OutputPointer_Int32;
317 const S3JniNphRef OutputPointer_Int64;
318 const S3JniNphRef OutputPointer_sqlite3;
319 const S3JniNphRef OutputPointer_sqlite3_stmt;
320 const S3JniNphRef OutputPointer_sqlite3_value;
321 const S3JniNphRef OutputPointer_String;
322 #ifdef SQLITE_ENABLE_FTS5
323 const S3JniNphRef OutputPointer_ByteArray;
324 const S3JniNphRef Fts5Context;
325 const S3JniNphRef Fts5ExtensionApi;
326 const S3JniNphRef fts5_api;
327 const S3JniNphRef fts5_tokenizer;
328 const S3JniNphRef Fts5Tokenizer;
329 #endif
330 } S3JniNphRefs = {
331 #define MkRef(INDEX, KLAZZ, MEMBER, SIG) \
332 { INDEX, "org/sqlite/jni/" KLAZZ, MEMBER, SIG }
333 /* NativePointerHolder ref */
334 #define RefN(INDEX, KLAZZ) MkRef(INDEX, KLAZZ, "nativePointer", "J")
335 /* OutputPointer.T ref */
336 #define RefO(INDEX, KLAZZ, SIG) MkRef(INDEX, KLAZZ, "value", SIG)
337 RefN(0, "sqlite3"),
338 RefN(1, "sqlite3_stmt"),
339 RefN(2, "sqlite3_context"),
340 RefN(3, "sqlite3_value"),
341 RefO(4, "OutputPointer$Bool", "Z"),
342 RefO(5, "OutputPointer$Int32", "I"),
343 RefO(6, "OutputPointer$Int64", "J"),
344 RefO(7, "OutputPointer$sqlite3",
345 "Lorg/sqlite/jni/sqlite3;"),
346 RefO(8, "OutputPointer$sqlite3_stmt",
347 "Lorg/sqlite/jni/sqlite3_stmt;"),
348 RefO(9, "OutputPointer$sqlite3_value",
349 "Lorg/sqlite/jni/sqlite3_value;"),
350 RefO(10, "OutputPointer$String", "Ljava/lang/String;"),
351 #ifdef SQLITE_ENABLE_FTS5
352 RefO(11, "OutputPointer$ByteArray", "[B"),
353 RefN(12, "Fts5Context"),
354 RefN(13, "Fts5ExtensionApi"),
355 RefN(14, "fts5_api"),
356 RefN(15, "fts5_tokenizer"),
357 RefN(16, "Fts5Tokenizer")
358 #endif
359 #undef MkRef
360 #undef RefN
361 #undef RefO
364 enum {
366 ** Size of the NativePointerHolder cache. Need enough space for
367 ** (only) the library's NativePointerHolder and OutputPointer types,
368 ** a fixed count known at build-time. This value needs to be
369 ** exactly the number of S3JniNphRef entries in the S3JniNphRefs
370 ** object.
372 S3Jni_NphCache_size = sizeof(S3JniNphRefs) / sizeof(S3JniNphRef)
376 ** Cache entry for NativePointerHolder subclasses and OutputPointer
377 ** types. The pRef and klazz fields are set up the first time the
378 ** entry is fetched using s3jni_nph(). The other fields are
379 ** populated as needed by the routines which use them.
381 typedef struct S3JniNphClass S3JniNphClass;
382 struct S3JniNphClass {
383 volatile const S3JniNphRef * pRef /* Entry from S3JniNphRefs. */;
384 jclass klazz /* global ref to the concrete
385 ** NativePointerHolder subclass
386 ** represented by zClassName */;
387 volatile jmethodID midCtor /* klazz's no-arg constructor. Used by
388 ** new_NativePointerHolder_object(). */;
389 volatile jfieldID fidValue /* NativePointerHolder.nativePointer or
390 ** OutputPointer.T.value */;
391 volatile jfieldID fidAggCtx /* sqlite3_context.aggregateContext, used only
392 ** by the sqlite3_context binding. */;
396 ** State for binding C callbacks to Java methods.
398 typedef struct S3JniHook S3JniHook;
399 struct S3JniHook{
400 jobject jObj /* global ref to Java instance */;
401 jmethodID midCallback /* callback method. Signature depends on
402 ** jObj's type */;
403 /* We lookup the jObj.xDestroy() method as-needed for contexts which
404 ** have custom finalizers. */
405 jobject jExtra /* Global ref to a per-hook-type value */;
406 int doXDestroy /* If true call jObj->xDestroy() when
407 this object is S3JniHook_unref()'d. */;
408 S3JniHook * pNext /* Next entry in S3Global.hooks.aFree */;
410 /* For clean bitwise-copy init of local instances. */
411 static const S3JniHook S3JniHook_empty = {0,0,0,0,0};
414 ** Per-(sqlite3*) state for various JNI bindings. This state is
415 ** allocated as needed, cleaned up in sqlite3_close(_v2)(), and
416 ** recycled when possible.
418 ** Trivia: vars and parameters of this type are often named "ps"
419 ** because this class used to have a name for which that abbreviation
420 ** made sense.
422 typedef struct S3JniDb S3JniDb;
423 struct S3JniDb {
424 sqlite3 *pDb /* The associated db handle */;
425 jobject jDb /* A global ref of the output object which gets
426 returned from sqlite3_open(_v2)(). We need this in
427 order to have an object to pass to routines like
428 sqlite3_collation_needed()'s callback, or else we
429 have to dynamically create one for that purpose,
430 which would be fine except that it would be a
431 different instance (and maybe even a different
432 class) than the one the user may expect to
433 receive. */;
434 char * zMainDbName /* Holds the string allocated on behalf of
435 SQLITE_DBCONFIG_MAINDBNAME. */;
436 struct {
437 S3JniHook busyHandler;
438 S3JniHook collationNeeded;
439 S3JniHook commit;
440 S3JniHook progress;
441 S3JniHook rollback;
442 S3JniHook trace;
443 S3JniHook update;
444 S3JniHook auth;
445 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
446 S3JniHook preUpdate;
447 #endif
448 } hooks;
449 #ifdef SQLITE_ENABLE_FTS5
450 jobject jFtsApi /* global ref to s3jni_fts5_api_from_db() */;
451 #endif
452 S3JniDb * pNext /* Next entry in SJG.perDb.aFree */;
455 static const char * const S3JniDb_clientdata_key = "S3JniDb";
456 #define S3JniDb_from_clientdata(pDb) \
457 (pDb ? sqlite3_get_clientdata(pDb, S3JniDb_clientdata_key) : 0)
460 ** Cache for per-JNIEnv (i.e. per-thread) data.
462 ** Trivia: vars and parameters of this type are often named "jc"
463 ** because this class used to have a name for which that abbreviation
464 ** made sense.
466 typedef struct S3JniEnv S3JniEnv;
467 struct S3JniEnv {
468 JNIEnv *env /* env in which this cache entry was created */;
470 ** pdbOpening is used to coordinate the Java/DB connection of a
471 ** being-open()'d db in the face of auto-extensions.
472 ** Auto-extensions run before we can bind the C db to its Java
473 ** representation, but auto-extensions require that binding to pass
474 ** on to their Java-side callbacks. We handle this as follows:
476 ** - In the JNI side of sqlite3_open(), allocate the Java side of
477 ** that connection and set pdbOpening to point to that
478 ** object.
480 ** - Call sqlite3_open(), which triggers the auto-extension
481 ** handler. That handler uses pdbOpening to connect the native
482 ** db handle which it receives with pdbOpening.
484 ** - When sqlite3_open() returns, check whether pdbOpening->pDb is
485 ** NULL. If it isn't, auto-extension handling set it up. If it
486 ** is, complete the Java/C binding unless sqlite3_open() returns
487 ** a NULL db, in which case free pdbOpening.
489 S3JniDb * pdbOpening;
490 S3JniEnv * pNext /* Next entry in SJG.envCache.aHead or
491 SJG.envCache.aFree */;
495 ** State for proxying sqlite3_auto_extension() in Java. This was
496 ** initially a separate class from S3JniHook and now the older name is
497 ** retained for readability in the APIs which use this, as well as for
498 ** its better code-searchability.
500 typedef S3JniHook S3JniAutoExtension;
503 ** Type IDs for SQL function categories.
505 enum UDFType {
506 UDF_UNKNOWN_TYPE = 0/*for error propagation*/,
507 UDF_SCALAR,
508 UDF_AGGREGATE,
509 UDF_WINDOW
513 ** State for binding Java-side UDFs.
515 typedef struct S3JniUdf S3JniUdf;
516 struct S3JniUdf {
517 jobject jObj /* SQLFunction instance */;
518 char * zFuncName /* Only for error reporting and debug logging */;
519 enum UDFType type /* UDF type */;
520 /** Method IDs for the various UDF methods. */
521 jmethodID jmidxFunc /* xFunc method (scalar) */;
522 jmethodID jmidxStep /* xStep method (aggregate/window) */;
523 jmethodID jmidxFinal /* xFinal method (aggregate/window) */;
524 jmethodID jmidxValue /* xValue method (window) */;
525 jmethodID jmidxInverse /* xInverse method (window) */;
526 S3JniUdf * pNext /* Next entry in SJG.udf.aFree. */;
529 #if !defined(SQLITE_JNI_OMIT_METRICS) && !defined(SQLITE_JNI_ENABLE_METRICS)
530 # ifdef SQLITE_DEBUG
531 # define SQLITE_JNI_ENABLE_METRICS
532 # endif
533 #endif
536 ** If true, modifying S3JniGlobal.metrics is protected by a mutex,
537 ** else it isn't.
539 #ifdef SQLITE_DEBUG
540 # define S3JNI_METRICS_MUTEX SQLITE_THREADSAFE
541 #else
542 # define S3JNI_METRICS_MUTEX 0
543 #endif
544 #ifndef SQLITE_JNI_ENABLE_METRICS
545 # undef S3JNI_METRICS_MUTEX
546 # define S3JNI_METRICS_MUTEX 0
547 #endif
550 ** Global state, e.g. caches and metrics.
552 typedef struct S3JniGlobalType S3JniGlobalType;
553 struct S3JniGlobalType {
555 ** According to: https://developer.ibm.com/articles/j-jni/
557 ** > A thread can get a JNIEnv by calling GetEnv() using the JNI
558 ** invocation interface through a JavaVM object. The JavaVM object
559 ** itself can be obtained by calling the JNI GetJavaVM() method
560 ** using a JNIEnv object and can be cached and shared across
561 ** threads. Caching a copy of the JavaVM object enables any thread
562 ** with access to the cached object to get access to its own
563 ** JNIEnv when necessary.
566 JavaVM * jvm;
567 /* Global mutex. */
568 sqlite3_mutex * mutex;
570 ** Cache of Java refs and method IDs for NativePointerHolder
571 ** subclasses and OutputPointer.T types.
573 S3JniNphClass nph[S3Jni_NphCache_size];
575 ** Cache of per-thread state.
577 struct {
578 S3JniEnv * aHead /* Linked list of in-use instances */;
579 S3JniEnv * aFree /* Linked list of free instances */;
580 sqlite3_mutex * mutex /* mutex for aHead and aFree, first-time
581 inits of nph[] entries, and
582 NativePointerHolder_get/set(). */;
583 void const * locker /* env mutex is held on this object's behalf.
584 Used only for sanity checking. */;
585 } envCache;
587 ** Per-db state. This can move into the core library once we can tie
588 ** client-defined state to db handles there.
590 struct {
591 S3JniDb * aFree /* Linked list of free instances */;
592 sqlite3_mutex * mutex /* mutex for aHead and aFree */;
593 void const * locker /* perDb mutex is held on this object's
594 behalf. Used only for sanity checking. */;
595 } perDb;
596 struct {
597 S3JniUdf * aFree /* Head of the free-item list. Guarded by global
598 mutex. */;
599 } udf;
601 ** Refs to global classes and methods. Obtained during static init
602 ** and never released.
604 struct {
605 jclass cLong /* global ref to java.lang.Long */;
606 jclass cString /* global ref to java.lang.String */;
607 jobject oCharsetUtf8 /* global ref to StandardCharset.UTF_8 */;
608 jmethodID ctorLong1 /* the Long(long) constructor */;
609 jmethodID ctorStringBA /* the String(byte[],Charset) constructor */;
610 jmethodID stringGetBytes /* the String.getBytes(Charset) method */;
611 } g;
613 ** The list of Java-side auto-extensions
614 ** (org.sqlite.jni.AutoExtensionCallback objects).
616 struct {
617 S3JniAutoExtension *aExt /* The auto-extension list. It is
618 maintained such that all active
619 entries are in the first contiguous
620 nExt array elements. */;
621 int nAlloc /* number of entries allocated for aExt,
622 as distinct from the number of active
623 entries. */;
624 int nExt /* number of active entries in aExt, all in the
625 first nExt'th array elements. */;
626 sqlite3_mutex * mutex /* mutex for manipulation/traversal of aExt */;
627 const void * locker /* object on whose behalf the mutex is held.
628 Only for sanity checking in debug builds. */;
629 } autoExt;
630 #ifdef SQLITE_ENABLE_FTS5
631 struct {
632 volatile jobject jFtsExt /* Global ref to Java singleton for the
633 Fts5ExtensionApi instance. */;
634 struct {
635 jfieldID fidA /* Fts5Phrase::a member */;
636 jfieldID fidB /* Fts5Phrase::b member */;
637 } jPhraseIter;
638 } fts5;
639 #endif
640 #ifdef SQLITE_ENABLE_SQLLOG
641 struct {
642 S3JniHook sqllog /* sqlite3_config(SQLITE_CONFIG_SQLLOG) callback */;
643 S3JniHook * aFree /* free-item list, for recycling. Guarded by
644 the global mutex. */;
645 } hooks;
646 #endif
647 #ifdef SQLITE_JNI_ENABLE_METRICS
648 /* Internal metrics. */
649 struct {
650 volatile unsigned nEnvHit;
651 volatile unsigned nEnvMiss;
652 volatile unsigned nEnvAlloc;
653 volatile unsigned nMutexEnv /* number of times envCache.mutex was entered for
654 a S3JniEnv operation. */;
655 volatile unsigned nMutexEnv2 /* number of times envCache.mutex was entered */;
656 volatile unsigned nMutexPerDb /* number of times perDb.mutex was entered */;
657 volatile unsigned nMutexAutoExt /* number of times autoExt.mutex was entered */;
658 volatile unsigned nMutexGlobal /* number of times global mutex was entered. */;
659 volatile unsigned nMutexUdf /* number of times global mutex was entered
660 for UDFs. */;
661 volatile unsigned nDestroy /* xDestroy() calls across all types */;
662 volatile unsigned nPdbAlloc /* Number of S3JniDb alloced. */;
663 volatile unsigned nPdbRecycled /* Number of S3JniDb reused. */;
664 volatile unsigned nUdfAlloc /* Number of S3JniUdf alloced. */;
665 volatile unsigned nUdfRecycled /* Number of S3JniUdf reused. */;
666 volatile unsigned nHookAlloc /* Number of S3JniHook alloced. */;
667 volatile unsigned nHookRecycled /* Number of S3JniHook reused. */;
668 struct {
669 /* Number of calls for each type of UDF callback. */
670 volatile unsigned nFunc;
671 volatile unsigned nStep;
672 volatile unsigned nFinal;
673 volatile unsigned nValue;
674 volatile unsigned nInverse;
675 } udf;
676 unsigned nMetrics /* Total number of mutex-locked
677 metrics increments. */;
678 #if S3JNI_METRICS_MUTEX
679 sqlite3_mutex * mutex;
680 #endif
681 } metrics;
682 #endif /* SQLITE_JNI_ENABLE_METRICS */
684 static S3JniGlobalType S3JniGlobal = {};
685 #define SJG S3JniGlobal
687 /* Increments *p, possibly protected by a mutex. */
688 #ifndef SQLITE_JNI_ENABLE_METRICS
689 #define s3jni_incr(PTR)
690 #elif S3JNI_METRICS_MUTEX
691 static void s3jni_incr( volatile unsigned int * const p ){
692 sqlite3_mutex_enter(SJG.metrics.mutex);
693 ++SJG.metrics.nMetrics;
694 ++(*p);
695 sqlite3_mutex_leave(SJG.metrics.mutex);
697 #else
698 #define s3jni_incr(PTR) ++(*(PTR))
699 #endif
701 /* Helpers for working with specific mutexes. */
702 #if SQLITE_THREADSAFE
703 #define S3JniEnv_mutex_assertLocked \
704 assert( 0 != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
705 #define S3JniEnv_mutex_assertLocker \
706 assert( (env) == SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
707 #define S3JniEnv_mutex_assertNotLocker \
708 assert( (env) != SJG.envCache.locker && "Misuse of S3JniGlobal.envCache.mutex" )
710 #define S3JniEnv_mutex_enter \
711 S3JniEnv_mutex_assertNotLocker; \
712 sqlite3_mutex_enter( SJG.envCache.mutex ); \
713 s3jni_incr(&SJG.metrics.nMutexEnv); \
714 SJG.envCache.locker = env
715 #define S3JniEnv_mutex_leave \
716 S3JniEnv_mutex_assertLocker; \
717 SJG.envCache.locker = 0; \
718 sqlite3_mutex_leave( SJG.envCache.mutex )
720 #define S3JniAutoExt_mutex_enter \
721 sqlite3_mutex_enter( SJG.autoExt.mutex ); \
722 SJG.autoExt.locker = env; \
723 s3jni_incr( &SJG.metrics.nMutexAutoExt )
724 #define S3JniAutoExt_mutex_leave \
725 assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" ); \
726 sqlite3_mutex_leave( SJG.autoExt.mutex )
727 #define S3JniAutoExt_mutex_assertLocker \
728 assert( env == SJG.autoExt.locker && "Misuse of S3JniGlobal.autoExt.mutex" )
730 #define S3JniGlobal_mutex_enter \
731 sqlite3_mutex_enter( SJG.mutex ); \
732 s3jni_incr(&SJG.metrics.nMutexGlobal);
733 #define S3JniGlobal_mutex_leave \
734 sqlite3_mutex_leave( SJG.mutex )
736 #define S3JniNph_mutex_enter \
737 S3JniEnv_mutex_assertNotLocker; \
738 sqlite3_mutex_enter( SJG.envCache.mutex ); \
739 s3jni_incr( &SJG.metrics.nMutexEnv2 ); \
740 SJG.envCache.locker = env
741 #define S3JniNph_mutex_leave \
742 S3JniEnv_mutex_assertLocker; \
743 SJG.envCache.locker = 0; \
744 sqlite3_mutex_leave( SJG.envCache.mutex )
746 #define S3JniDb_mutex_assertLocker \
747 assert( (env) == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" )
748 #define S3JniDb_mutex_enter \
749 sqlite3_mutex_enter( SJG.perDb.mutex ); \
750 assert( 0==SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
751 s3jni_incr( &SJG.metrics.nMutexPerDb ); \
752 SJG.perDb.locker = env;
753 #define S3JniDb_mutex_leave \
754 assert( env == SJG.perDb.locker && "Misuse of S3JniGlobal.perDb.mutex" ); \
755 SJG.perDb.locker = 0; \
756 sqlite3_mutex_leave( SJG.perDb.mutex )
758 #else /* SQLITE_THREADSAFE==0 */
759 #define S3JniEnv_mutex_assertLocked
760 #define S3JniEnv_mutex_assertLocker
761 #define S3JniEnv_mutex_assertNotLocker
762 #define S3JniEnv_mutex_enter
763 #define S3JniEnv_mutex_leave
764 #define S3JniAutoExt_mutex_assertLocker
765 #define S3JniAutoExt_mutex_enter
766 #define S3JniAutoExt_mutex_leave
767 #define S3JniGlobal_mutex_enter
768 #define S3JniGlobal_mutex_leave
769 #define S3JniNph_mutex_enter
770 #define S3JniNph_mutex_leave
771 #define S3JniDb_mutex_assertLocker
772 #define S3JniDb_mutex_enter
773 #define S3JniDb_mutex_leave
774 #endif
776 /* Helpers for jstring and jbyteArray. */
777 static const char * s3jni__jstring_to_mutf8_bytes(JNIEnv * const env, jstring v ){
778 const char *z = v ? (*env)->GetStringUTFChars(env, v, NULL) : 0;
779 s3jni_oom_check( v ? !!z : !z );
780 return z;
783 #define s3jni_jstring_to_mutf8(ARG) s3jni__jstring_to_mutf8_bytes(env, (ARG))
784 #define s3jni_mutf8_release(ARG,VAR) if( VAR ) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
786 static jbyte * s3jni__jbytearray_bytes(JNIEnv * const env, jbyteArray jBA ){
787 jbyte * const rv = jBA ? (*env)->GetByteArrayElements(env, jBA, NULL) : 0;
788 s3jni_oom_check( jBA ? !!rv : 1 );
789 return rv;
792 #define s3jni_jbytearray_bytes(jByteArray) s3jni__jbytearray_bytes(env, (jByteArray))
793 #define s3jni_jbytearray_release(jByteArray,jBytes) \
794 if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_ABORT)
797 ** Returns the current JNIEnv object. Fails fatally if it cannot find
798 ** the object.
800 static JNIEnv * s3jni_env(void){
801 JNIEnv * env = 0;
802 if( (*SJG.jvm)->GetEnv(SJG.jvm, (void **)&env,
803 JNI_VERSION_1_8) ){
804 fprintf(stderr, "Fatal error: cannot get current JNIEnv.\n");
805 abort();
807 return env;
811 ** Fetches the S3JniGlobal.envCache row for the given env, allocing a
812 ** row if needed. When a row is allocated, its state is initialized
813 ** insofar as possible. Calls (*env)->FatalError() if allocation of an
814 ** entry fails. That's hypothetically possible but "shouldn't happen."
816 static S3JniEnv * S3JniEnv__get(JNIEnv * const env){
817 struct S3JniEnv * row;
818 S3JniEnv_mutex_enter;
819 row = SJG.envCache.aHead;
820 for( ; row; row = row->pNext ){
821 if( row->env == env ){
822 s3jni_incr( &SJG.metrics.nEnvHit );
823 S3JniEnv_mutex_leave;
824 return row;
827 s3jni_incr( &SJG.metrics.nEnvMiss );
828 row = SJG.envCache.aFree;
829 if( row ){
830 SJG.envCache.aFree = row->pNext;
831 }else{
832 row = s3jni_malloc_or_die(env, sizeof(*row));
833 s3jni_incr( &SJG.metrics.nEnvAlloc );
835 memset(row, 0, sizeof(*row));
836 row->pNext = SJG.envCache.aHead;
837 SJG.envCache.aHead = row;
838 row->env = env;
840 S3JniEnv_mutex_leave;
841 return row;
844 #define S3JniEnv_get() S3JniEnv__get(env)
847 ** This function is NOT part of the sqlite3 public API. It is strictly
848 ** for use by the sqlite project's own Java/JNI bindings.
850 ** For purposes of certain hand-crafted JNI function bindings, we
851 ** need a way of reporting errors which is consistent with the rest of
852 ** the C API, as opposed to throwing JS exceptions. To that end, this
853 ** internal-use-only function is a thin proxy around
854 ** sqlite3ErrorWithMessage(). The intent is that it only be used from
855 ** JNI bindings such as sqlite3_prepare_v2/v3(), and definitely not
856 ** from client code.
858 ** Returns err_code.
860 static int s3jni_db_error(sqlite3* const db, int err_code,
861 const char * const zMsg){
862 if( db!=0 ){
863 if( 0==zMsg ){
864 sqlite3Error(db, err_code);
865 }else{
866 const int nMsg = sqlite3Strlen30(zMsg);
867 sqlite3_mutex_enter(sqlite3_db_mutex(db));
868 sqlite3ErrorWithMsg(db, err_code, "%.*s", nMsg, zMsg);
869 sqlite3_mutex_leave(sqlite3_db_mutex(db));
872 return err_code;
876 ** Creates a new jByteArray of length nP, copies p's contents into it,
877 ** and returns that byte array (NULL on OOM unless fail-fast alloc
878 ** errors are enabled). p may be NULL, in which case the array is
879 ** created but no bytes are filled.
881 static jbyteArray s3jni__new_jbyteArray(JNIEnv * const env,
882 const void * const p, int nP){
883 jbyteArray jba = (*env)->NewByteArray(env, (jint)nP);
885 s3jni_oom_check( jba );
886 if( jba && p ){
887 (*env)->SetByteArrayRegion(env, jba, 0, (jint)nP, (const jbyte*)p);
889 return jba;
892 #define s3jni_new_jbyteArray(P,n) s3jni__new_jbyteArray(env, P, n)
896 ** Uses the java.lang.String(byte[],Charset) constructor to create a
897 ** new String from UTF-8 string z. n is the number of bytes to
898 ** copy. If n<0 then sqlite3Strlen30() is used to calculate it.
900 ** Returns NULL if z is NULL or on OOM, else returns a new jstring
901 ** owned by the caller.
903 ** Sidebar: this is a painfully inefficient way to convert from
904 ** standard UTF-8 to a Java string, but JNI offers only algorithms for
905 ** working with MUTF-8, not UTF-8.
907 static jstring s3jni__utf8_to_jstring(JNIEnv * const env,
908 const char * const z, int n){
909 jstring rv = NULL;
910 if( 0==n || (n<0 && z && !z[0]) ){
911 /* Fast-track the empty-string case via the MUTF-8 API. We could
912 hypothetically do this for any strings where n<4 and z is
913 NUL-terminated and none of z[0..3] are NUL bytes. */
914 rv = (*env)->NewStringUTF(env, "");
915 }else if( z ){
916 jbyteArray jba;
917 if( n<0 ) n = sqlite3Strlen30(z);
918 jba = s3jni_new_jbyteArray((unsigned const char *)z, n);
919 if( jba ){
920 rv = (*env)->NewObject(env, SJG.g.cString, SJG.g.ctorStringBA,
921 jba, SJG.g.oCharsetUtf8);
922 S3JniIfThrew{
923 S3JniExceptionReport;
924 S3JniExceptionClear;
926 S3JniUnrefLocal(jba);
929 s3jni_oom_check( rv );
930 return rv;
932 #define s3jni_utf8_to_jstring(CStr,n) s3jni__utf8_to_jstring(env, CStr, n)
935 ** Converts the given java.lang.String object into a NUL-terminated
936 ** UTF-8 C-string by calling jstr.getBytes(StandardCharset.UTF_8).
937 ** Returns NULL if jstr is NULL or on allocation error. If jstr is not
938 ** NULL and nLen is not NULL then nLen is set to the length of the
939 ** returned string, not including the terminating NUL. If jstr is not
940 ** NULL and it returns NULL, this indicates an allocation error. In
941 ** that case, if nLen is not NULL then it is either set to 0 (if
942 ** fetching of jstr's bytes fails to allocate) or set to what would
943 ** have been the length of the string had C-string allocation
944 ** succeeded.
946 ** The returned memory is allocated from sqlite3_malloc() and
947 ** ownership is transferred to the caller.
949 static char * s3jni__jstring_to_utf8(JNIEnv * const env,
950 jstring jstr, int *nLen){
951 jbyteArray jba;
952 jsize nBa;
953 char *rv;
955 if( !jstr ) return 0;
956 jba = (*env)->CallObjectMethod(env, jstr, SJG.g.stringGetBytes,
957 SJG.g.oCharsetUtf8);
959 if( (*env)->ExceptionCheck(env) || !jba
960 /* order of these checks is significant for -Xlint:jni */ ) {
961 S3JniExceptionReport;
962 s3jni_oom_check( jba );
963 if( nLen ) *nLen = 0;
964 return 0;
966 nBa = (*env)->GetArrayLength(env, jba);
967 if( nLen ) *nLen = (int)nBa;
968 rv = s3jni_malloc( nBa + 1 );
969 if( rv ){
970 (*env)->GetByteArrayRegion(env, jba, 0, nBa, (jbyte*)rv);
971 rv[nBa] = 0;
973 S3JniUnrefLocal(jba);
974 return rv;
976 #define s3jni_jstring_to_utf8(JStr,n) s3jni__jstring_to_utf8(env, JStr, n)
979 ** Expects to be passed a pointer from sqlite3_column_text16() or
980 ** sqlite3_value_text16() and a byte-length value from
981 ** sqlite3_column_bytes16() or sqlite3_value_bytes16(). It creates a
982 ** Java String of exactly half that character length, returning NULL
983 ** if !p or (*env)->NewString() fails.
985 static jstring s3jni_text16_to_jstring(JNIEnv * const env, const void * const p, int nP){
986 jstring const rv = p
987 ? (*env)->NewString(env, (const jchar *)p, (jsize)(nP/2))
988 : NULL;
989 s3jni_oom_check( p ? !!rv : 1 );
990 return rv;
994 ** Requires jx to be a Throwable. Calls its toString() method and
995 ** returns its value converted to a UTF-8 string. The caller owns the
996 ** returned string and must eventually sqlite3_free() it. Returns 0
997 ** if there is a problem fetching the info or on OOM.
999 ** Design note: we use toString() instead of getMessage() because the
1000 ** former includes the exception type's name:
1002 ** Exception e = new RuntimeException("Hi");
1003 ** System.out.println(e.toString()); // java.lang.RuntimeException: Hi
1004 ** System.out.println(e.getMessage()); // Hi
1006 static char * s3jni_exception_error_msg(JNIEnv * const env, jthrowable jx){
1007 jmethodID mid;
1008 jstring msg;
1009 char * zMsg;
1010 jclass const klazz = (*env)->GetObjectClass(env, jx);
1011 mid = (*env)->GetMethodID(env, klazz, "toString", "()Ljava/lang/String;");
1012 S3JniUnrefLocal(klazz);
1013 S3JniIfThrew{
1014 S3JniExceptionReport;
1015 S3JniExceptionClear;
1016 return 0;
1018 msg = (*env)->CallObjectMethod(env, jx, mid);
1019 S3JniIfThrew{
1020 S3JniExceptionReport;
1021 S3JniExceptionClear;
1022 return 0;
1024 zMsg = s3jni_jstring_to_utf8( msg, 0);
1025 S3JniUnrefLocal(msg);
1026 return zMsg;
1030 ** Extracts env's current exception, sets ps->pDb's error message to
1031 ** its message string, and clears the exception. If errCode is non-0,
1032 ** it is used as-is, else SQLITE_ERROR is assumed. If there's a
1033 ** problem extracting the exception's message, it's treated as
1034 ** non-fatal and zDfltMsg is used in its place.
1036 ** Locks the global S3JniDb mutex.
1038 ** This must only be called if a JNI exception is pending.
1040 ** Returns errCode unless it is 0, in which case SQLITE_ERROR is
1041 ** returned.
1043 static int s3jni__db_exception(JNIEnv * const env, S3JniDb * const ps,
1044 int errCode, const char *zDfltMsg){
1045 jthrowable const ex = (*env)->ExceptionOccurred(env);
1047 if( 0==errCode ) errCode = SQLITE_ERROR;
1048 if( ex ){
1049 char * zMsg;
1050 S3JniExceptionClear;
1051 S3JniDb_mutex_enter;
1052 zMsg = s3jni_exception_error_msg(env, ex);
1053 s3jni_db_error(ps->pDb, errCode, zMsg ? zMsg : zDfltMsg);
1054 sqlite3_free(zMsg);
1055 S3JniUnrefLocal(ex);
1056 S3JniDb_mutex_leave;
1058 return errCode;
1060 #define s3jni_db_exception(JniDb,ERRCODE,DFLTMSG) \
1061 s3jni__db_exception(env, (JniDb), (ERRCODE), (DFLTMSG) )
1064 ** Extracts the (void xDestroy()) method from jObj and applies it to
1065 ** jObj. If jObj is NULL, this is a no-op. The lack of an xDestroy()
1066 ** method is silently ignored. Any exceptions thrown by xDestroy()
1067 ** trigger a warning to stdout or stderr and then the exception is
1068 ** suppressed.
1070 static void s3jni__call_xDestroy(JNIEnv * const env, jobject jObj){
1071 if( jObj ){
1072 jclass const klazz = (*env)->GetObjectClass(env, jObj);
1073 jmethodID method = (*env)->GetMethodID(env, klazz, "xDestroy", "()V");
1075 S3JniUnrefLocal(klazz);
1076 if( method ){
1077 s3jni_incr( &SJG.metrics.nDestroy );
1078 (*env)->CallVoidMethod(env, jObj, method);
1079 S3JniIfThrew{
1080 S3JniExceptionWarnCallbackThrew("xDestroy() callback");
1081 S3JniExceptionClear;
1083 }else{
1084 /* Non-fatal. */
1085 S3JniExceptionClear;
1089 #define s3jni_call_xDestroy(JOBJ) s3jni__call_xDestroy(env, (JOBJ))
1092 ** Internal helper for many hook callback impls. Locks the S3JniDb
1093 ** mutex, makes a copy of src into dest, with a some differences: (1) if
1094 ** src->jObj or src->jExtra are not NULL then dest will be a new LOCAL
1095 ** ref to it instead of a copy of the prior GLOBAL ref. (2) dest->doXDestroy
1096 ** is always false.
1098 ** If dest->jObj is not NULL when this returns then the caller is
1099 ** obligated to eventually free the new ref by passing *dest to
1100 ** S3JniHook_localundup(). The dest pointer must NOT be passed to
1101 ** S3JniHook_unref(), as that routine assumes that dest->jObj/jExtra
1102 ** are GLOBAL refs (it's illegal to try to unref the wrong ref type).
1104 ** Background: when running a hook we need a call-local copy lest
1105 ** another thread modify the hook while we're running it. That copy
1106 ** has to have its own Java reference, but it need only be call-local.
1108 static void S3JniHook__localdup( JNIEnv * const env, S3JniHook const * const src,
1109 S3JniHook * const dest ){
1110 S3JniDb_mutex_enter;
1111 *dest = *src;
1112 if(src->jObj) dest->jObj = S3JniRefLocal(src->jObj);
1113 if(src->jExtra) dest->jExtra = S3JniRefLocal(src->jExtra);
1114 dest->doXDestroy = 0;
1115 S3JniDb_mutex_leave;
1117 #define S3JniHook_localdup(src,dest) S3JniHook__localdup(env,src,dest)
1119 static void S3JniHook__localundup( JNIEnv * const env, S3JniHook * const h ){
1120 S3JniUnrefLocal(h->jObj);
1121 S3JniUnrefLocal(h->jExtra);
1122 *h = S3JniHook_empty;
1124 #define S3JniHook_localundup(HOOK) S3JniHook__localundup(env, &(HOOK))
1127 ** Removes any Java references from s and clears its state. If
1128 ** doXDestroy is true and s->jObj is not NULL, s->jObj
1129 ** is passed to s3jni_call_xDestroy() before any references are
1130 ** cleared. It is legal to call this when the object has no Java
1131 ** references. s must not be NULL.
1133 static void S3JniHook__unref(JNIEnv * const env, S3JniHook * const s){
1134 if( s->jObj ){
1135 if( s->doXDestroy ){
1136 s3jni_call_xDestroy(s->jObj);
1138 S3JniUnrefGlobal(s->jObj);
1139 S3JniUnrefGlobal(s->jExtra);
1141 *s = S3JniHook_empty;
1143 #define S3JniHook_unref(hook) \
1144 S3JniHook__unref(env, (hook))
1147 ** Allocates one blank S3JniHook object from the recycling bin, if
1148 ** available, else from the heap. Returns NULL or dies on OOM. Locks
1149 ** the global mutex.
1151 static S3JniHook *S3JniHook__alloc(JNIEnv * const env){
1152 S3JniHook * p = 0;
1153 S3JniGlobal_mutex_enter;
1154 if( SJG.hooks.aFree ){
1155 p = SJG.hooks.aFree;
1156 SJG.hooks.aFree = p->pNext;
1157 p->pNext = 0;
1158 s3jni_incr(&SJG.metrics.nHookRecycled);
1160 S3JniGlobal_mutex_leave;
1161 if( 0==p ){
1162 p = s3jni_malloc(sizeof(S3JniHook));
1163 if( p ){
1164 s3jni_incr(&SJG.metrics.nHookAlloc);
1167 if( p ){
1168 *p = S3JniHook_empty;
1170 return p;
1172 #define S3JniHook_alloc() S3JniHook__alloc(env)
1175 ** The rightful fate of all results from S3JniHook_alloc(). doXDestroy
1176 ** is passed on as-is to S3JniHook_unref(). Locks the global mutex.
1178 static void S3JniHook__free(JNIEnv * const env, S3JniHook * const p){
1179 if(p){
1180 assert( !p->pNext );
1181 S3JniHook_unref(p);
1182 S3JniGlobal_mutex_enter;
1183 p->pNext = SJG.hooks.aFree;
1184 SJG.hooks.aFree = p;
1185 S3JniGlobal_mutex_leave;
1188 #define S3JniHook_free(hook) S3JniHook__free(env, hook)
1190 #if 0
1191 /* S3JniHook__free() without the lock: caller must hold the global mutex */
1192 static void S3JniHook__free_unlocked(JNIEnv * const env, S3JniHook * const p){
1193 if(p){
1194 assert( !p->pNext );
1195 assert( p->pNext != SJG.hooks.aFree );
1196 S3JniHook_unref(p);
1197 p->pNext = SJG.hooks.aFree;
1198 SJG.hooks.aFree = p;
1201 #define S3JniHook_free_unlocked(hook) S3JniHook__free_unlocked(env, hook)
1202 #endif
1205 ** Clears all of s's state. Requires that that the caller has locked
1206 ** S3JniGlobal.perDb.mutex. Make sure to do anything needed with
1207 ** s->pNext and s->pPrev before calling this, as this clears them.
1209 static void S3JniDb_clear(JNIEnv * const env, S3JniDb * const s){
1210 S3JniDb_mutex_assertLocker;
1211 sqlite3_free( s->zMainDbName );
1212 #define UNHOOK(MEMBER) \
1213 S3JniHook_unref(&s->hooks.MEMBER)
1214 UNHOOK(auth);
1215 UNHOOK(busyHandler);
1216 UNHOOK(collationNeeded);
1217 UNHOOK(commit);
1218 UNHOOK(progress);
1219 UNHOOK(rollback);
1220 UNHOOK(trace);
1221 UNHOOK(update);
1222 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
1223 UNHOOK(preUpdate);
1224 #endif
1225 #undef UNHOOK
1226 S3JniUnrefGlobal(s->jDb);
1227 memset(s, 0, sizeof(S3JniDb));
1231 ** Clears s's state and moves it to the free-list. Requires that
1232 ** S3JniGlobal.perDb.mutex is locked.
1234 static void S3JniDb__set_aside_unlocked(JNIEnv * const env, S3JniDb * const s){
1235 assert( s );
1236 S3JniDb_mutex_assertLocker;
1237 if( s ){
1238 S3JniDb_clear(env, s);
1239 s->pNext = SJG.perDb.aFree;
1240 SJG.perDb.aFree = s;
1243 #define S3JniDb_set_aside_unlocked(JniDb) S3JniDb__set_aside_unlocked(env, JniDb)
1245 static void S3JniDb__set_aside(JNIEnv * const env, S3JniDb * const s){
1246 S3JniDb_mutex_enter;
1247 S3JniDb_set_aside_unlocked(s);
1248 S3JniDb_mutex_leave;
1250 #define S3JniDb_set_aside(JNIDB) S3JniDb__set_aside(env, JNIDB)
1253 ** Uncache any state for the given JNIEnv, clearing all Java
1254 ** references the cache owns. Returns true if env was cached and false
1255 ** if it was not found in the cache. Ownership of the given object is
1256 ** passed over to this function, which makes it free for re-use.
1258 ** Requires that the Env mutex be locked.
1260 static int S3JniEnv_uncache(JNIEnv * const env){
1261 struct S3JniEnv * row;
1262 struct S3JniEnv * pPrev = 0;
1263 S3JniEnv_mutex_assertLocked;
1264 row = SJG.envCache.aHead;
1265 for( ; row; pPrev = row, row = row->pNext ){
1266 if( row->env == env ){
1267 break;
1270 if( !row ){
1271 return 0;
1273 if( pPrev) pPrev->pNext = row->pNext;
1274 else{
1275 assert( SJG.envCache.aHead == row );
1276 SJG.envCache.aHead = row->pNext;
1278 memset(row, 0, sizeof(S3JniEnv));
1279 row->pNext = SJG.envCache.aFree;
1280 SJG.envCache.aFree = row;
1281 return 1;
1285 ** Fetches the given nph-ref from cache the cache and returns the
1286 ** object with its klazz member set. This is an O(1) operation except
1287 ** on the first call for a given pRef, during which pRef->klazz and
1288 ** pRef->pRef are initialized thread-safely. In the latter case it's
1289 ** still effectively O(1), but with a much longer 1.
1291 ** It is up to the caller to populate the other members of the
1292 ** returned object if needed, taking care to lock the modification
1293 ** with S3JniNph_mutex_enter/leave.
1295 ** This simple cache catches >99% of searches in the current
1296 ** (2023-07-31) tests.
1298 static S3JniNphClass * s3jni__nph(JNIEnv * const env, S3JniNphRef const* pRef){
1300 According to:
1302 https://developer.ibm.com/articles/j-jni/
1304 > ... the IDs returned for a given class don't change for the
1305 lifetime of the JVM process. But the call to get the field or
1306 method can require significant work in the JVM, because
1307 fields and methods might have been inherited from
1308 superclasses, making the JVM walk up the class hierarchy to
1309 find them. Because the IDs are the same for a given class,
1310 you should look them up once and then reuse them. Similarly,
1311 looking up class objects can be expensive, so they should be
1312 cached as well.
1314 S3JniNphClass * const pNC = &SJG.nph[pRef->index];
1315 assert( (void*)pRef>=(void*)&S3JniNphRefs && (void*)pRef<(void*)(&S3JniNphRefs + 1)
1316 && "pRef is out of range." );
1317 assert( pRef->index>=0
1318 && (pRef->index < (sizeof(S3JniNphRefs) / sizeof(S3JniNphRef))) );
1319 if( !pNC->pRef ){
1320 S3JniNph_mutex_enter;
1321 if( !pNC->pRef ){
1322 jclass const klazz = (*env)->FindClass(env, pRef->zName);
1323 S3JniExceptionIsFatal("FindClass() unexpectedly threw");
1324 pNC->klazz = S3JniRefGlobal(klazz);
1325 pNC->pRef = pRef
1326 /* Must come last to avoid a race condition where pNC->klass
1327 can be NULL after this function returns. */;
1329 S3JniNph_mutex_leave;
1331 assert( pNC->klazz );
1332 return pNC;
1335 #define s3jni_nph(PRef) s3jni__nph(env, PRef)
1338 ** Common code for accessor functions for NativePointerHolder and
1339 ** OutputPointer types. pRef must be a pointer from S3JniNphRefs. jOut
1340 ** must be an instance of that class (Java's type safety takes care of
1341 ** that requirement). If necessary, this fetches the jfieldID for
1342 ** jOut's pRef->zMember, which must be of the type represented by the
1343 ** JNI type signature pRef->zTypeSig, and stores it in
1344 ** S3JniGlobal.nph[pRef->index]. Fails fatally if the pRef->zMember
1345 ** property is not found, as that presents a serious internal misuse.
1347 ** Property lookups are cached on a per-pRef basis.
1349 static jfieldID s3jni_nphop_field(JNIEnv * const env, S3JniNphRef const* pRef){
1350 S3JniNphClass * const pNC = s3jni_nph(pRef);
1352 if( !pNC->fidValue ){
1353 S3JniNph_mutex_enter;
1354 if( !pNC->fidValue ){
1355 pNC->fidValue = (*env)->GetFieldID(env, pNC->klazz,
1356 pRef->zMember, pRef->zTypeSig);
1357 S3JniExceptionIsFatal("Code maintenance required: missing "
1358 "required S3JniNphClass::fidValue.");
1360 S3JniNph_mutex_leave;
1362 assert( pNC->fidValue );
1363 return pNC->fidValue;
1367 ** Sets a native ptr value in NativePointerHolder object ppOut.
1368 ** zClassName must be a static string so we can use its address
1369 ** as a cache key.
1371 static void NativePointerHolder__set(JNIEnv * const env, S3JniNphRef const* pRef,
1372 jobject ppOut, const void * p){
1373 assert( ppOut );
1374 (*env)->SetLongField(env, ppOut, s3jni_nphop_field(env, pRef), (jlong)p);
1375 S3JniExceptionIsFatal("Could not set NativePointerHolder.nativePointer.");
1378 #define NativePointerHolder_set(PREF,PPOUT,P) \
1379 NativePointerHolder__set(env, PREF, PPOUT, P)
1382 ** Fetches a native ptr value from NativePointerHolder object pObj,
1383 ** which must be of the native type described by pRef. This is a
1384 ** no-op if pObj is NULL.
1386 static void * NativePointerHolder__get(JNIEnv * env, jobject pObj,
1387 S3JniNphRef const* pRef){
1388 void * rv = 0;
1389 if( pObj ){
1390 rv = (void*)(*env)->GetLongField(env, pObj, s3jni_nphop_field(env, pRef));
1391 S3JniExceptionIsFatal("Cannot fetch NativePointerHolder.nativePointer.");
1393 return rv;
1396 #define NativePointerHolder_get(JOBJ,NPHREF) \
1397 NativePointerHolder__get(env, (JOBJ), (NPHREF))
1400 ** Helpers for extracting pointers from jobjects, noting that we rely
1401 ** on the corresponding Java interfaces having already done the
1402 ** type-checking. OBJ must be a jobject referring to a
1403 ** NativePointerHolder<T>, where T matches PtrGet_T. Don't use these
1404 ** in contexts where that's not the case. Note that these aren't
1405 ** type-safe in the strictest sense:
1407 ** sqlite3 * s = PtrGet_sqlite3_stmt(...)
1409 ** will work, despite the incorrect macro name, so long as the
1410 ** argument is a Java sqlite3 object, as this operation only has void
1411 ** pointers to work with.
1413 #define PtrGet_T(T,OBJ) NativePointerHolder_get(OBJ, &S3JniNphRefs.T)
1414 #define PtrGet_sqlite3(OBJ) PtrGet_T(sqlite3, OBJ)
1415 #define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ)
1416 #define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ)
1417 #define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ)
1419 #if 0
1421 ** Enters the S3JniDb mutex and PtrGet_sqlite3()'s jObj. If that's
1422 ** NULL then it leaves the mutex, else the mutex is still entered
1423 ** when this returns and the caller is obligated to leave it.
1425 static sqlite3* PtrGet__sqlite3_lock(JNIEnv * const env, jobject jObj){
1426 sqlite3 *rv;
1427 S3JniDb_mutex_enter;
1428 rv = PtrGet_sqlite3(jObj);
1429 if( !rv ){ S3JniDb_mutex_leave; }
1430 return rv;
1432 #undef PtrGet_sqlite3
1433 #define PtrGet_sqlite3(JOBJ) PtrGet__sqlite3_lock(env, (JOBJ))
1434 #endif
1437 ** Extracts the new S3JniDb instance from the free-list, or allocates
1438 ** one if needed, associats it with pDb, and returns. Returns NULL on
1439 ** OOM. pDb MUST, on success of the calling operation, subsequently be
1440 ** associated with jDb via NativePointerHolder_set().
1442 static S3JniDb * S3JniDb_alloc(JNIEnv * const env, jobject jDb){
1443 S3JniDb * rv = 0;
1444 S3JniDb_mutex_enter;
1445 if( SJG.perDb.aFree ){
1446 rv = SJG.perDb.aFree;
1447 SJG.perDb.aFree = rv->pNext;
1448 rv->pNext = 0;
1449 s3jni_incr( &SJG.metrics.nPdbRecycled );
1451 S3JniDb_mutex_leave;
1452 if( 0==rv ){
1453 rv = s3jni_malloc( sizeof(S3JniDb));
1454 if( rv ){
1455 s3jni_incr( &SJG.metrics.nPdbAlloc );
1458 if( rv ){
1459 memset(rv, 0, sizeof(S3JniDb));
1460 rv->jDb = S3JniRefGlobal(jDb);
1462 return rv;
1466 ** Returns the S3JniDb object for the given org.sqlite.jni.sqlite3
1467 ** object, or NULL if jDb is NULL, no pointer can be extracted
1468 ** from it, or no matching entry can be found.
1470 static S3JniDb * S3JniDb__from_java(JNIEnv * const env, jobject jDb){
1471 sqlite3 * const pDb = jDb ? PtrGet_sqlite3(jDb) : 0;
1472 return pDb ? S3JniDb_from_clientdata(pDb) : 0;
1474 #define S3JniDb_from_java(jObject) S3JniDb__from_java(env,(jObject))
1477 ** S3JniDb finalizer for use with sqlite3_set_clientdata().
1479 static void S3JniDb_xDestroy(void *p){
1480 S3JniDeclLocal_env;
1481 S3JniDb * const ps = p;
1482 assert( !ps->pNext && "Else ps is already in the free-list.");
1483 S3JniDb_set_aside(ps);
1487 ** Evaluates to the S3JniDb object for the given sqlite3 object, or
1488 ** NULL if pDb is NULL or was not initialized via the JNI interfaces.
1490 #define S3JniDb_from_c(sqlite3Ptr) \
1491 ((sqlite3Ptr) ? S3JniDb_from_clientdata(sqlite3Ptr) : 0)
1494 ** Unref any Java-side state in (S3JniAutoExtension*) AX and zero out
1495 ** AX.
1497 #define S3JniAutoExtension_clear(AX) S3JniHook_unref(AX);
1500 ** Initializes a pre-allocated S3JniAutoExtension object. Returns
1501 ** non-0 if there is an error collecting the required state from
1502 ** jAutoExt (which must be an AutoExtensionCallback object). On error,
1503 ** it passes ax to S3JniAutoExtension_clear().
1505 static int S3JniAutoExtension_init(JNIEnv *const env,
1506 S3JniAutoExtension * const ax,
1507 jobject const jAutoExt){
1508 jclass const klazz = (*env)->GetObjectClass(env, jAutoExt);
1510 S3JniAutoExt_mutex_assertLocker;
1511 *ax = S3JniHook_empty;
1512 ax->midCallback = (*env)->GetMethodID(env, klazz, "call",
1513 "(Lorg/sqlite/jni/sqlite3;)I");
1514 S3JniUnrefLocal(klazz);
1515 S3JniExceptionWarnIgnore;
1516 if( !ax->midCallback ){
1517 S3JniAutoExtension_clear(ax);
1518 return SQLITE_ERROR;
1520 ax->jObj = S3JniRefGlobal(jAutoExt);
1521 return 0;
1525 ** Sets the value property of the OutputPointer.Bool jOut object to
1526 ** v.
1528 static void OutputPointer_set_Bool(JNIEnv * const env, jobject const jOut,
1529 int v){
1530 (*env)->SetBooleanField(env, jOut, s3jni_nphop_field(
1531 env, &S3JniNphRefs.OutputPointer_Bool
1532 ), v ? JNI_TRUE : JNI_FALSE );
1533 S3JniExceptionIsFatal("Cannot set OutputPointer.Bool.value");
1537 ** Sets the value property of the OutputPointer.Int32 jOut object to
1538 ** v.
1540 static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut,
1541 int v){
1542 (*env)->SetIntField(env, jOut, s3jni_nphop_field(
1543 env, &S3JniNphRefs.OutputPointer_Int32
1544 ), (jint)v);
1545 S3JniExceptionIsFatal("Cannot set OutputPointer.Int32.value");
1549 ** Sets the value property of the OutputPointer.Int64 jOut object to
1550 ** v.
1552 static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut,
1553 jlong v){
1554 (*env)->SetLongField(env, jOut, s3jni_nphop_field(
1555 env, &S3JniNphRefs.OutputPointer_Int64
1556 ), v);
1557 S3JniExceptionIsFatal("Cannot set OutputPointer.Int64.value");
1561 ** Internal helper for OutputPointer_set_TYPE() where TYPE is an
1562 ** Object type.
1564 static void OutputPointer_set_obj(JNIEnv * const env,
1565 S3JniNphRef const * const pRef,
1566 jobject const jOut,
1567 jobject v){
1568 (*env)->SetObjectField(env, jOut, s3jni_nphop_field(env, pRef), v);
1569 S3JniExceptionIsFatal("Cannot set OutputPointer.T.value");
1573 ** Sets the value property of the OutputPointer.sqlite3 jOut object to
1574 ** v.
1576 static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut,
1577 jobject jDb){
1578 OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3, jOut, jDb);
1582 ** Sets the value property of the OutputPointer.sqlite3_stmt jOut object to
1583 ** v.
1585 static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut,
1586 jobject jStmt){
1587 OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3_stmt, jOut, jStmt);
1590 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
1592 ** Sets the value property of the OutputPointer.sqlite3_value jOut object to
1593 ** v.
1595 static void OutputPointer_set_sqlite3_value(JNIEnv * const env, jobject const jOut,
1596 jobject jValue){
1597 OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_sqlite3_value, jOut, jValue);
1599 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
1601 #ifdef SQLITE_ENABLE_FTS5
1602 #if 0
1604 ** Sets the value property of the OutputPointer.ByteArray jOut object
1605 ** to v.
1607 static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
1608 jbyteArray const v){
1609 OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_ByteArray, jOut, v);
1611 #endif
1612 #endif /* SQLITE_ENABLE_FTS5 */
1615 ** Sets the value property of the OutputPointer.String jOut object to
1616 ** v.
1618 static void OutputPointer_set_String(JNIEnv * const env, jobject const jOut,
1619 jstring const v){
1620 OutputPointer_set_obj(env, &S3JniNphRefs.OutputPointer_String, jOut, v);
1624 ** Returns true if eTextRep is a valid sqlite3 encoding constant, else
1625 ** returns false.
1627 static int encodingTypeIsValid(int eTextRep){
1628 switch( eTextRep ){
1629 case SQLITE_UTF8: case SQLITE_UTF16:
1630 case SQLITE_UTF16LE: case SQLITE_UTF16BE:
1631 return 1;
1632 default:
1633 return 0;
1637 /* For use with sqlite3_result/value_pointer() */
1638 #define ResultJavaValuePtrStr "org.sqlite.jni.ResultJavaVal"
1641 ** If v is not NULL, it must be a jobject global reference. Its
1642 ** reference is relinquished and v is freed.
1644 static void ResultJavaValue_finalizer(void *v){
1645 if( v ){
1646 S3JniDeclLocal_env;
1647 S3JniUnrefGlobal((jobject)v);
1654 ** Returns a new Java instance of the class named by zClassName, which
1655 ** MUST be interface-compatible with NativePointerHolder and MUST have
1656 ** a no-arg constructor. The NativePointerHolder_set() method is
1657 ** passed the new Java object and pNative. Hypothetically returns NULL
1658 ** if Java fails to allocate, but the JNI docs are not entirely clear
1659 ** on that detail.
1661 ** Always use a static pointer from the S3JniNphRefs struct for the 2nd
1662 ** argument so that we can use pRef->index as an O(1) cache key.
1664 static jobject new_NativePointerHolder_object(JNIEnv * const env, S3JniNphRef const * pRef,
1665 const void * pNative){
1666 jobject rv = 0;
1667 S3JniNphClass * const pNC = s3jni_nph(pRef);
1668 if( !pNC->midCtor ){
1669 S3JniNph_mutex_enter;
1670 if( !pNC->midCtor ){
1671 pNC->midCtor = (*env)->GetMethodID(env, pNC->klazz, "<init>", "()V");
1672 S3JniExceptionIsFatal("Cannot find constructor for class.");
1674 S3JniNph_mutex_leave;
1676 rv = (*env)->NewObject(env, pNC->klazz, pNC->midCtor);
1677 S3JniExceptionIsFatal("No-arg constructor threw.");
1678 s3jni_oom_check(rv);
1679 if( rv ) NativePointerHolder_set(pRef, rv, pNative);
1680 return rv;
1683 static inline jobject new_sqlite3_wrapper(JNIEnv * const env, sqlite3 *sv){
1684 return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3, sv);
1686 static inline jobject new_sqlite3_context_wrapper(JNIEnv * const env, sqlite3_context *sv){
1687 return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_context, sv);
1689 static inline jobject new_sqlite3_stmt_wrapper(JNIEnv * const env, sqlite3_stmt *sv){
1690 return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_stmt, sv);
1692 static inline jobject new_sqlite3_value_wrapper(JNIEnv * const env, sqlite3_value *sv){
1693 return new_NativePointerHolder_object(env, &S3JniNphRefs.sqlite3_value, sv);
1696 /* Helper typedefs for UDF callback types. */
1697 typedef void (*udf_xFunc_f)(sqlite3_context*,int,sqlite3_value**);
1698 typedef void (*udf_xStep_f)(sqlite3_context*,int,sqlite3_value**);
1699 typedef void (*udf_xFinal_f)(sqlite3_context*);
1700 /*typedef void (*udf_xValue_f)(sqlite3_context*);*/
1701 /*typedef void (*udf_xInverse_f)(sqlite3_context*,int,sqlite3_value**);*/
1704 ** Allocate a new S3JniUdf (User-defined Function) and associate it
1705 ** with the SQLFunction-type jObj. Returns NULL on OOM. If the
1706 ** returned object's type==UDF_UNKNOWN_TYPE then the type of UDF was
1707 ** not unambiguously detected based on which callback members it has,
1708 ** which falls into the category of user error.
1710 ** The caller must arrange for the returned object to eventually be
1711 ** passed to S3JniUdf_free().
1713 static S3JniUdf * S3JniUdf_alloc(JNIEnv * const env, jobject jObj){
1714 S3JniUdf * s = 0;
1716 S3JniGlobal_mutex_enter;
1717 s3jni_incr(&SJG.metrics.nMutexUdf);
1718 if( SJG.udf.aFree ){
1719 s = SJG.udf.aFree;
1720 SJG.udf.aFree = s->pNext;
1721 s->pNext = 0;
1722 s3jni_incr(&SJG.metrics.nUdfRecycled);
1724 S3JniGlobal_mutex_leave;
1725 if( !s ){
1726 s = s3jni_malloc( sizeof(*s));
1727 s3jni_incr(&SJG.metrics.nUdfAlloc);
1729 if( s ){
1730 const char * zFSI = /* signature for xFunc, xStep, xInverse */
1731 "(Lorg/sqlite/jni/sqlite3_context;[Lorg/sqlite/jni/sqlite3_value;)V";
1732 const char * zFV = /* signature for xFinal, xValue */
1733 "(Lorg/sqlite/jni/sqlite3_context;)V";
1734 jclass const klazz = (*env)->GetObjectClass(env, jObj);
1736 memset(s, 0, sizeof(*s));
1737 s->jObj = S3JniRefGlobal(jObj);
1739 #define FGET(FuncName,FuncSig,Field) \
1740 s->Field = (*env)->GetMethodID(env, klazz, FuncName, FuncSig); \
1741 if( !s->Field ) (*env)->ExceptionClear(env)
1743 FGET("xFunc", zFSI, jmidxFunc);
1744 FGET("xStep", zFSI, jmidxStep);
1745 FGET("xFinal", zFV, jmidxFinal);
1746 FGET("xValue", zFV, jmidxValue);
1747 FGET("xInverse", zFSI, jmidxInverse);
1748 #undef FGET
1750 S3JniUnrefLocal(klazz);
1751 if( s->jmidxFunc ) s->type = UDF_SCALAR;
1752 else if( s->jmidxStep && s->jmidxFinal ){
1753 s->type = s->jmidxValue ? UDF_WINDOW : UDF_AGGREGATE;
1754 }else{
1755 s->type = UDF_UNKNOWN_TYPE;
1758 return s;
1762 ** Frees up all resources owned by s, clears its state, then either
1763 ** caches it for reuse (if cacheIt is true) or frees it. The former
1764 ** requires locking the global mutex, so it must not be held when this
1765 ** is called.
1767 static void S3JniUdf_free(JNIEnv * const env, S3JniUdf * const s,
1768 int cacheIt){
1769 assert( !s->pNext );
1770 if( s->jObj ){
1771 s3jni_call_xDestroy(s->jObj);
1772 S3JniUnrefGlobal(s->jObj);
1773 sqlite3_free(s->zFuncName);
1774 assert( !s->pNext );
1775 memset(s, 0, sizeof(*s));
1777 if( cacheIt ){
1778 S3JniGlobal_mutex_enter;
1779 s->pNext = S3JniGlobal.udf.aFree;
1780 S3JniGlobal.udf.aFree = s;
1781 S3JniGlobal_mutex_leave;
1782 }else{
1783 sqlite3_free( s );
1787 /* Finalizer for sqlite3_create_function() and friends. */
1788 static void S3JniUdf_finalizer(void * s){
1789 S3JniUdf_free(s3jni_env(), (S3JniUdf*)s, 1);
1793 ** Helper for processing args to UDF handlers with signature
1794 ** (sqlite3_context*,int,sqlite3_value**).
1796 typedef struct {
1797 jobject jcx /* sqlite3_context */;
1798 jobjectArray jargv /* sqlite3_value[] */;
1799 } udf_jargs;
1802 ** Converts the given (cx, argc, argv) into arguments for the given
1803 ** UDF, writing the result (Java wrappers for cx and argv) in the
1804 ** final 2 arguments. Returns 0 on success, SQLITE_NOMEM on allocation
1805 ** error. On error *jCx and *jArgv will be set to 0.
1807 static int udf_args(JNIEnv *env,
1808 sqlite3_context * const cx,
1809 int argc, sqlite3_value**argv,
1810 jobject * jCx, jobjectArray *jArgv){
1811 jobjectArray ja = 0;
1812 jobject jcx = new_sqlite3_context_wrapper(env, cx);
1813 jint i;
1814 *jCx = 0;
1815 *jArgv = 0;
1816 if( !jcx ) goto error_oom;
1817 ja = (*env)->NewObjectArray(
1818 env, argc, s3jni_nph(&S3JniNphRefs.sqlite3_value)->klazz,
1819 NULL);
1820 s3jni_oom_check( ja );
1821 if( !ja ) goto error_oom;
1822 for(i = 0; i < argc; ++i){
1823 jobject jsv = new_sqlite3_value_wrapper(env, argv[i]);
1824 if( !jsv ) goto error_oom;
1825 (*env)->SetObjectArrayElement(env, ja, i, jsv);
1826 S3JniUnrefLocal(jsv)/*ja has a ref*/;
1828 *jCx = jcx;
1829 *jArgv = ja;
1830 return 0;
1831 error_oom:
1832 S3JniUnrefLocal(jcx);
1833 S3JniUnrefLocal(ja);
1834 return SQLITE_NOMEM;
1838 ** Must be called immediately after a Java-side UDF callback throws.
1839 ** If translateToErr is true then it sets the exception's message in
1840 ** the result error using sqlite3_result_error(). If translateToErr is
1841 ** false then it emits a warning that the function threw but should
1842 ** not do so. In either case, it clears the exception state.
1844 ** Returns SQLITE_NOMEM if an allocation fails, else SQLITE_ERROR. In
1845 ** the former case it calls sqlite3_result_error_nomem().
1847 static int udf_report_exception(JNIEnv * const env, int translateToErr,
1848 sqlite3_context * cx,
1849 const char *zFuncName, const char *zFuncType ){
1850 jthrowable const ex = (*env)->ExceptionOccurred(env);
1851 int rc = SQLITE_ERROR;
1853 assert(ex && "This must only be called when a Java exception is pending.");
1854 if( translateToErr ){
1855 char * zMsg;
1856 char * z;
1858 S3JniExceptionClear;
1859 zMsg = s3jni_exception_error_msg(env, ex);
1860 z = sqlite3_mprintf("Client-defined SQL function %s.%s() threw: %s",
1861 zFuncName ? zFuncName : "<unnamed>", zFuncType,
1862 zMsg ? zMsg : "Unknown exception" );
1863 sqlite3_free(zMsg);
1864 if( z ){
1865 sqlite3_result_error(cx, z, -1);
1866 sqlite3_free(z);
1867 }else{
1868 sqlite3_result_error_nomem(cx);
1869 rc = SQLITE_NOMEM;
1871 }else{
1872 S3JniExceptionWarnCallbackThrew("client-defined SQL function");
1873 S3JniExceptionClear;
1875 S3JniUnrefLocal(ex);
1876 return rc;
1880 ** Sets up the state for calling a Java-side xFunc/xStep/xInverse()
1881 ** UDF, calls it, and returns 0 on success.
1883 static int udf_xFSI(sqlite3_context* const pCx, int argc,
1884 sqlite3_value** const argv, S3JniUdf * const s,
1885 jmethodID xMethodID, const char * const zFuncType){
1886 S3JniDeclLocal_env;
1887 udf_jargs args = {0,0};
1888 int rc = udf_args(env, pCx, argc, argv, &args.jcx, &args.jargv);
1890 if( 0 == rc ){
1891 (*env)->CallVoidMethod(env, s->jObj, xMethodID, args.jcx, args.jargv);
1892 S3JniIfThrew{
1893 rc = udf_report_exception(env, 'F'==zFuncType[1]/*xFunc*/, pCx,
1894 s->zFuncName, zFuncType);
1897 S3JniUnrefLocal(args.jcx);
1898 S3JniUnrefLocal(args.jargv);
1899 return rc;
1903 ** Sets up the state for calling a Java-side xFinal/xValue() UDF,
1904 ** calls it, and returns 0 on success.
1906 static int udf_xFV(sqlite3_context* cx, S3JniUdf * s,
1907 jmethodID xMethodID,
1908 const char *zFuncType){
1909 S3JniDeclLocal_env;
1910 jobject jcx = new_sqlite3_context_wrapper(env, cx);
1911 int rc = 0;
1912 int const isFinal = 'F'==zFuncType[1]/*xFinal*/;
1914 if( jcx ){
1915 (*env)->CallVoidMethod(env, s->jObj, xMethodID, jcx);
1916 S3JniIfThrew{
1917 rc = udf_report_exception(env, isFinal, cx, s->zFuncName,
1918 zFuncType);
1920 S3JniUnrefLocal(jcx);
1921 }else{
1922 if( isFinal ) sqlite3_result_error_nomem(cx);
1923 rc = SQLITE_NOMEM;
1925 return rc;
1928 /* Proxy for C-to-Java xFunc. */
1929 static void udf_xFunc(sqlite3_context* cx, int argc,
1930 sqlite3_value** argv){
1931 S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx);
1932 s3jni_incr( &SJG.metrics.udf.nFunc );
1933 udf_xFSI(cx, argc, argv, s, s->jmidxFunc, "xFunc");
1935 /* Proxy for C-to-Java xStep. */
1936 static void udf_xStep(sqlite3_context* cx, int argc,
1937 sqlite3_value** argv){
1938 S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx);
1939 s3jni_incr( &SJG.metrics.udf.nStep );
1940 udf_xFSI(cx, argc, argv, s, s->jmidxStep, "xStep");
1942 /* Proxy for C-to-Java xFinal. */
1943 static void udf_xFinal(sqlite3_context* cx){
1944 S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx);
1945 s3jni_incr( &SJG.metrics.udf.nFinal );
1946 udf_xFV(cx, s, s->jmidxFinal, "xFinal");
1948 /* Proxy for C-to-Java xValue. */
1949 static void udf_xValue(sqlite3_context* cx){
1950 S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx);
1951 s3jni_incr( &SJG.metrics.udf.nValue );
1952 udf_xFV(cx, s, s->jmidxValue, "xValue");
1954 /* Proxy for C-to-Java xInverse. */
1955 static void udf_xInverse(sqlite3_context* cx, int argc,
1956 sqlite3_value** argv){
1957 S3JniUdf * const s = (S3JniUdf*)sqlite3_user_data(cx);
1958 s3jni_incr( &SJG.metrics.udf.nInverse );
1959 udf_xFSI(cx, argc, argv, s, s->jmidxInverse, "xInverse");
1963 ////////////////////////////////////////////////////////////////////////
1964 // What follows is the JNI/C bindings. They are in alphabetical order
1965 // except for this macro-generated subset which are kept together
1966 // (alphabetized) here at the front...
1967 ////////////////////////////////////////////////////////////////////////
1969 /** Create a trivial JNI wrapper for (int CName(void)). */
1970 #define WRAP_INT_VOID(JniNameSuffix,CName) \
1971 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass){ \
1972 return (jint)CName(); \
1974 /** Create a trivial JNI wrapper for (int CName(int)). */
1975 #define WRAP_INT_INT(JniNameSuffix,CName) \
1976 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jint arg){ \
1977 return (jint)CName((int)arg); \
1980 ** Create a trivial JNI wrapper for (const mutf8_string *
1981 ** CName(void)). This is only valid for functions which are known to
1982 ** return ASCII or text which is equivalent in UTF-8 and MUTF-8.
1984 #define WRAP_MUTF8_VOID(JniNameSuffix,CName) \
1985 JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass){ \
1986 jstring const rv = (*env)->NewStringUTF( env, CName() ); \
1987 s3jni_oom_check(rv); \
1988 return rv; \
1990 /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*)). */
1991 #define WRAP_INT_STMT(JniNameSuffix,CName) \
1992 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpStmt){ \
1993 jint const rc = (jint)CName(PtrGet_sqlite3_stmt(jpStmt)); \
1994 S3JniExceptionIgnore /* squelch -Xcheck:jni */; \
1995 return rc; \
1997 /** Create a trivial JNI wrapper for (int CName(sqlite3_stmt*,int)). */
1998 #define WRAP_INT_STMT_INT(JniNameSuffix,CName) \
1999 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint n){ \
2000 return (jint)CName(PtrGet_sqlite3_stmt(pStmt), (int)n); \
2002 /** Create a trivial JNI wrapper for (boolish-int CName(sqlite3_stmt*)). */
2003 #define WRAP_BOOL_STMT(JniNameSuffix,CName) \
2004 JniDecl(jboolean,JniNameSuffix)(JniArgsEnvClass, jobject pStmt){ \
2005 return CName(PtrGet_sqlite3_stmt(pStmt)) ? JNI_TRUE : JNI_FALSE; \
2007 /** Create a trivial JNI wrapper for (jstring CName(sqlite3_stmt*,int)). */
2008 #define WRAP_STR_STMT_INT(JniNameSuffix,CName) \
2009 JniDecl(jstring,JniNameSuffix)(JniArgsEnvClass, jobject pStmt, jint ndx){ \
2010 return s3jni_utf8_to_jstring( \
2011 CName(PtrGet_sqlite3_stmt(pStmt), (int)ndx), \
2012 -1); \
2014 /** Create a trivial JNI wrapper for (int CName(sqlite3*)). */
2015 #define WRAP_INT_DB(JniNameSuffix,CName) \
2016 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \
2017 return (jint)CName(PtrGet_sqlite3(pDb)); \
2019 /** Create a trivial JNI wrapper for (int64 CName(sqlite3*)). */
2020 #define WRAP_INT64_DB(JniNameSuffix,CName) \
2021 JniDecl(jlong,JniNameSuffix)(JniArgsEnvClass, jobject pDb){ \
2022 return (jlong)CName(PtrGet_sqlite3(pDb)); \
2024 /** Create a trivial JNI wrapper for (int CName(sqlite3_value*)). */
2025 #define WRAP_INT_SVALUE(JniNameSuffix,CName) \
2026 JniDecl(jint,JniNameSuffix)(JniArgsEnvClass, jobject jpSValue){ \
2027 return (jint)CName(PtrGet_sqlite3_value(jpSValue)); \
2030 WRAP_INT_STMT(1bind_1parameter_1count, sqlite3_bind_parameter_count)
2031 WRAP_INT_DB(1changes, sqlite3_changes)
2032 WRAP_INT64_DB(1changes64, sqlite3_changes64)
2033 WRAP_INT_STMT(1clear_1bindings, sqlite3_clear_bindings)
2034 WRAP_INT_STMT_INT(1column_1bytes, sqlite3_column_bytes)
2035 WRAP_INT_STMT_INT(1column_1bytes16, sqlite3_column_bytes16)
2036 WRAP_INT_STMT(1column_1count, sqlite3_column_count)
2037 WRAP_STR_STMT_INT(1column_1decltype, sqlite3_column_decltype)
2038 WRAP_STR_STMT_INT(1column_1name, sqlite3_column_name)
2039 WRAP_STR_STMT_INT(1column_1database_1name, sqlite3_column_database_name)
2040 WRAP_STR_STMT_INT(1column_1origin_1name, sqlite3_column_origin_name)
2041 WRAP_STR_STMT_INT(1column_1table_1name, sqlite3_column_table_name)
2042 WRAP_INT_STMT_INT(1column_1type, sqlite3_column_type)
2043 WRAP_INT_STMT(1data_1count, sqlite3_data_count)
2044 WRAP_INT_DB(1error_1offset, sqlite3_error_offset)
2045 WRAP_INT_DB(1extended_1errcode, sqlite3_extended_errcode)
2046 WRAP_MUTF8_VOID(1libversion, sqlite3_libversion)
2047 WRAP_INT_VOID(1libversion_1number, sqlite3_libversion_number)
2048 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
2049 WRAP_INT_DB(1preupdate_1blobwrite, sqlite3_preupdate_blobwrite)
2050 WRAP_INT_DB(1preupdate_1count, sqlite3_preupdate_count)
2051 WRAP_INT_DB(1preupdate_1depth, sqlite3_preupdate_depth)
2052 #endif
2053 WRAP_INT_INT(1release_1memory, sqlite3_release_memory)
2054 WRAP_INT_INT(1sleep, sqlite3_sleep)
2055 WRAP_MUTF8_VOID(1sourceid, sqlite3_sourceid)
2056 WRAP_INT_STMT_INT(1stmt_1explain, sqlite3_stmt_explain)
2057 WRAP_INT_STMT(1stmt_1isexplain, sqlite3_stmt_isexplain)
2058 WRAP_BOOL_STMT(1stmt_1readonly, sqlite3_stmt_readonly)
2059 WRAP_INT_VOID(1threadsafe, sqlite3_threadsafe)
2060 WRAP_INT_DB(1total_1changes, sqlite3_total_changes)
2061 WRAP_INT64_DB(1total_1changes64, sqlite3_total_changes64)
2062 WRAP_INT_SVALUE(1value_1bytes, sqlite3_value_bytes)
2063 WRAP_INT_SVALUE(1value_1bytes16, sqlite3_value_bytes16)
2064 WRAP_INT_SVALUE(1value_1encoding, sqlite3_value_encoding)
2065 WRAP_INT_SVALUE(1value_1frombind, sqlite3_value_frombind)
2066 WRAP_INT_SVALUE(1value_1nochange, sqlite3_value_nochange)
2067 WRAP_INT_SVALUE(1value_1numeric_1type, sqlite3_value_numeric_type)
2068 WRAP_INT_SVALUE(1value_1subtype, sqlite3_value_subtype)
2069 WRAP_INT_SVALUE(1value_1type, sqlite3_value_type)
2071 #undef WRAP_INT64_DB
2072 #undef WRAP_INT_DB
2073 #undef WRAP_INT_INT
2074 #undef WRAP_INT_STMT
2075 #undef WRAP_INT_STMT_INT
2076 #undef WRAP_INT_SVALUE
2077 #undef WRAP_INT_VOID
2078 #undef WRAP_MUTF8_VOID
2079 #undef WRAP_STR_STMT_INT
2081 S3JniApi(sqlite3_aggregate_context(),jlong,1aggregate_1context)(
2082 JniArgsEnvClass, jobject jCx, jboolean initialize
2084 sqlite3_context * const pCx = PtrGet_sqlite3_context(jCx);
2085 void * const p = pCx
2086 ? sqlite3_aggregate_context(pCx, (int)(initialize
2087 ? (int)sizeof(void*)
2088 : 0))
2089 : 0;
2090 return (jlong)p;
2093 /* Central auto-extension handler. */
2094 static int s3jni_run_java_auto_extensions(sqlite3 *pDb, const char **pzErr,
2095 const struct sqlite3_api_routines *ignored){
2096 int rc = 0;
2097 unsigned i, go = 1;
2098 JNIEnv * env = 0;
2099 S3JniDb * ps;
2100 S3JniEnv * jc;
2102 if( 0==SJG.autoExt.nExt ) return 0;
2103 env = s3jni_env();
2104 jc = S3JniEnv_get();
2105 ps = jc->pdbOpening;
2106 if( !ps ){
2107 *pzErr = sqlite3_mprintf("Unexpected arrival of null S3JniDb in "
2108 "auto-extension runner.");
2109 return SQLITE_ERROR;
2111 jc->pdbOpening = 0;
2112 assert( !ps->pDb && "it's still being opened" );
2113 assert( ps->jDb );
2114 rc = sqlite3_set_clientdata(pDb, S3JniDb_clientdata_key,
2115 ps, 0/* we'll re-set this after open()
2116 completes. */);
2117 if( rc ){
2118 return rc;
2120 NativePointerHolder_set(&S3JniNphRefs.sqlite3, ps->jDb, pDb)
2121 /* As of here, the Java/C connection is complete except for the
2122 (temporary) lack of finalizer for the ps object. */;
2123 ps->pDb = pDb;
2124 for( i = 0; go && 0==rc; ++i ){
2125 S3JniAutoExtension ax = S3JniHook_empty
2126 /* We need a copy of the auto-extension object, with our own
2127 ** local reference to it, to avoid a race condition with another
2128 ** thread manipulating the list during the call and invaliding
2129 ** what ax references. */;
2130 S3JniAutoExt_mutex_enter;
2131 if( i >= SJG.autoExt.nExt ){
2132 go = 0;
2133 }else{
2134 S3JniHook_localdup(&SJG.autoExt.aExt[i], &ax);
2136 S3JniAutoExt_mutex_leave;
2137 if( ax.jObj ){
2138 rc = (*env)->CallIntMethod(env, ax.jObj, ax.midCallback, ps->jDb);
2139 S3JniHook_localundup(ax);
2140 S3JniIfThrew {
2141 jthrowable const ex = (*env)->ExceptionOccurred(env);
2142 char * zMsg;
2143 S3JniExceptionClear;
2144 zMsg = s3jni_exception_error_msg(env, ex);
2145 S3JniUnrefLocal(ex);
2146 *pzErr = sqlite3_mprintf("auto-extension threw: %s", zMsg);
2147 sqlite3_free(zMsg);
2148 rc = SQLITE_ERROR;
2152 return rc;
2155 S3JniApi(sqlite3_auto_extension(),jint,1auto_1extension)(
2156 JniArgsEnvClass, jobject jAutoExt
2158 int i;
2159 S3JniAutoExtension * ax;
2160 int rc = 0;
2162 if( !jAutoExt ) return SQLITE_MISUSE;
2163 S3JniAutoExt_mutex_enter;
2164 for( i = 0; i < SJG.autoExt.nExt; ++i ){
2165 /* Look for a match. */
2166 ax = &SJG.autoExt.aExt[i];
2167 if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){
2168 /* same object, so this is a no-op. */
2169 S3JniAutoExt_mutex_leave;
2170 return 0;
2173 if( i == SJG.autoExt.nExt ){
2174 assert( SJG.autoExt.nExt <= SJG.autoExt.nAlloc );
2175 if( SJG.autoExt.nExt == SJG.autoExt.nAlloc ){
2176 /* Allocate another slot. */
2177 unsigned n = 1 + SJG.autoExt.nAlloc;
2178 S3JniAutoExtension * const aNew =
2179 s3jni_realloc( SJG.autoExt.aExt, n * sizeof(*ax) );
2180 if( !aNew ){
2181 rc = SQLITE_NOMEM;
2182 }else{
2183 SJG.autoExt.aExt = aNew;
2184 ++SJG.autoExt.nAlloc;
2187 if( 0==rc ){
2188 ax = &SJG.autoExt.aExt[SJG.autoExt.nExt];
2189 rc = S3JniAutoExtension_init(env, ax, jAutoExt);
2190 assert( rc ? (0==ax->jObj && 0==ax->midCallback)
2191 : (0!=ax->jObj && 0!=ax->midCallback) );
2194 if( 0==rc ){
2195 static int once = 0;
2196 if( 0==once && ++once ){
2197 rc = sqlite3_auto_extension(
2198 (void(*)(void))s3jni_run_java_auto_extensions
2199 /* Reminder: the JNI binding of sqlite3_reset_auto_extension()
2200 ** does not call the core-lib impl. It only clears Java-side
2201 ** auto-extensions. */
2203 if( rc ){
2204 assert( ax );
2205 S3JniAutoExtension_clear(ax);
2208 if( 0==rc ){
2209 ++SJG.autoExt.nExt;
2212 S3JniAutoExt_mutex_leave;
2213 return rc;
2216 S3JniApi(sqlite3_bind_blob(),jint,1bind_1blob)(
2217 JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax
2219 jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0;
2220 int rc;
2221 if( pBuf ){
2222 rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
2223 pBuf, (int)nMax, SQLITE_TRANSIENT);
2224 s3jni_jbytearray_release(baData, pBuf);
2225 }else{
2226 rc = baData
2227 ? SQLITE_NOMEM
2228 : sqlite3_bind_null( PtrGet_sqlite3_stmt(jpStmt), ndx );
2230 return (jint)rc;
2233 S3JniApi(sqlite3_bind_double(),jint,1bind_1double)(
2234 JniArgsEnvClass, jobject jpStmt, jint ndx, jdouble val
2236 return (jint)sqlite3_bind_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (double)val);
2239 S3JniApi(sqlite3_bind_int(),jint,1bind_1int)(
2240 JniArgsEnvClass, jobject jpStmt, jint ndx, jint val
2242 return (jint)sqlite3_bind_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)val);
2245 S3JniApi(sqlite3_bind_int64(),jint,1bind_1int64)(
2246 JniArgsEnvClass, jobject jpStmt, jint ndx, jlong val
2248 return (jint)sqlite3_bind_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_int64)val);
2252 ** Bind a new global ref to Object `val` using sqlite3_bind_pointer().
2254 S3JniApi(sqlite3_bind_java_object(),jint,1bind_1java_1object)(
2255 JniArgsEnvClass, jobject jpStmt, jint ndx, jobject val
2257 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
2258 int rc = 0;
2260 if(pStmt){
2261 jobject const rv = val ? S3JniRefGlobal(val) : 0;
2262 if( rv ){
2263 rc = sqlite3_bind_pointer(pStmt, ndx, rv, ResultJavaValuePtrStr,
2264 ResultJavaValue_finalizer);
2265 }else if(val){
2266 rc = SQLITE_NOMEM;
2268 }else{
2269 rc = SQLITE_MISUSE;
2271 return rc;
2274 S3JniApi(sqlite3_bind_null(),jint,1bind_1null)(
2275 JniArgsEnvClass, jobject jpStmt, jint ndx
2277 return (jint)sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
2280 S3JniApi(sqlite3_bind_parameter_index(),jint,1bind_1parameter_1index)(
2281 JniArgsEnvClass, jobject jpStmt, jbyteArray jName
2283 int rc = 0;
2284 jbyte * const pBuf = s3jni_jbytearray_bytes(jName);
2285 if( pBuf ){
2286 rc = sqlite3_bind_parameter_index(PtrGet_sqlite3_stmt(jpStmt),
2287 (const char *)pBuf);
2288 s3jni_jbytearray_release(jName, pBuf);
2290 return rc;
2293 S3JniApi(sqlite3_bind_text(),jint,1bind_1text)(
2294 JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax
2296 jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0;
2297 int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
2298 (const char *)pBuf,
2299 (int)nMax, SQLITE_TRANSIENT);
2300 s3jni_jbytearray_release(baData, pBuf);
2301 return (jint)rc;
2304 S3JniApi(sqlite3_text16(),jint,1bind_1text16)(
2305 JniArgsEnvClass, jobject jpStmt, jint ndx, jbyteArray baData, jint nMax
2307 jbyte * const pBuf = baData ? s3jni_jbytearray_bytes(baData) : 0;
2308 int const rc = sqlite3_bind_text16(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
2309 pBuf, (int)nMax, SQLITE_TRANSIENT);
2310 s3jni_jbytearray_release(baData, pBuf);
2311 return (jint)rc;
2314 S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob)(
2315 JniArgsEnvClass, jobject jpStmt, jint ndx, jint n
2317 return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (int)n);
2320 S3JniApi(sqlite3_bind_zeroblob(),jint,1bind_1zeroblob64)(
2321 JniArgsEnvClass, jobject jpStmt, jint ndx, jlong n
2323 return (jint)sqlite3_bind_zeroblob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (sqlite3_uint64)n);
2326 /* Central C-to-Java busy handler proxy. */
2327 static int s3jni_busy_handler(void* pState, int n){
2328 S3JniDb * const ps = (S3JniDb *)pState;
2329 int rc = 0;
2330 S3JniDeclLocal_env;
2331 S3JniHook hook;
2333 S3JniHook_localdup(&ps->hooks.busyHandler, &hook );
2334 if( hook.jObj ){
2335 rc = (*env)->CallIntMethod(env, hook.jObj,
2336 hook.midCallback, (jint)n);
2337 S3JniIfThrew{
2338 S3JniExceptionWarnCallbackThrew("sqlite3_busy_handler() callback");
2339 rc = s3jni_db_exception(ps, SQLITE_ERROR,
2340 "sqlite3_busy_handler() callback threw.");
2342 S3JniHook_localundup(hook);
2344 return rc;
2347 S3JniApi(sqlite3_busy_handler(),jint,1busy_1handler)(
2348 JniArgsEnvClass, jobject jDb, jobject jBusy
2350 S3JniDb * const ps = S3JniDb_from_java(jDb);
2351 S3JniHook * const pHook = ps ? &ps->hooks.busyHandler : 0;
2352 S3JniHook hook = S3JniHook_empty;
2353 int rc = 0;
2355 if( !ps ) return (jint)SQLITE_MISUSE;
2356 S3JniDb_mutex_enter;
2357 if( jBusy ){
2358 if( pHook->jObj && (*env)->IsSameObject(env, pHook->jObj, jBusy) ){
2359 /* Same object - this is a no-op. */
2360 }else{
2361 jclass const klazz = (*env)->GetObjectClass(env, jBusy);
2362 hook.jObj = S3JniRefGlobal(jBusy);
2363 hook.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I)I");
2364 S3JniUnrefLocal(klazz);
2365 S3JniIfThrew {
2366 rc = SQLITE_ERROR;
2370 if( 0==rc ){
2371 if( jBusy ){
2372 if( hook.jObj ){ /* Replace handler */
2373 rc = sqlite3_busy_handler(ps->pDb, s3jni_busy_handler, ps);
2374 if( 0==rc ){
2375 S3JniHook_unref(pHook);
2376 *pHook = hook;
2377 hook = S3JniHook_empty;
2379 }/* else no-op */
2380 }else{ /* Clear handler */
2381 rc = sqlite3_busy_handler(ps->pDb, 0, 0);
2382 if( 0==rc ){
2383 S3JniHook_unref(pHook);
2387 S3JniHook_unref(&hook);
2388 S3JniDb_mutex_leave;
2389 return rc;
2392 S3JniApi(sqlite3_busy_timeout(),jint,1busy_1timeout)(
2393 JniArgsEnvClass, jobject jDb, jint ms
2395 S3JniDb * const ps = S3JniDb_from_java(jDb);
2396 int rc = SQLITE_MISUSE;
2397 if( ps ){
2398 S3JniDb_mutex_enter;
2399 S3JniHook_unref(&ps->hooks.busyHandler);
2400 rc = sqlite3_busy_timeout(ps->pDb, (int)ms);
2401 S3JniDb_mutex_leave;
2403 return rc;
2406 S3JniApi(sqlite3_cancel_auto_extension(),jboolean,1cancel_1auto_1extension)(
2407 JniArgsEnvClass, jobject jAutoExt
2409 S3JniAutoExtension * ax;
2410 jboolean rc = JNI_FALSE;
2411 int i;
2412 S3JniAutoExt_mutex_enter;
2413 /* This algo mirrors the one in the core. */
2414 for( i = SJG.autoExt.nExt-1; i >= 0; --i ){
2415 ax = &SJG.autoExt.aExt[i];
2416 if( ax->jObj && (*env)->IsSameObject(env, ax->jObj, jAutoExt) ){
2417 S3JniAutoExtension_clear(ax);
2418 /* Move final entry into this slot. */
2419 --SJG.autoExt.nExt;
2420 *ax = SJG.autoExt.aExt[SJG.autoExt.nExt];
2421 SJG.autoExt.aExt[SJG.autoExt.nExt] = S3JniHook_empty;
2422 assert( !SJG.autoExt.aExt[SJG.autoExt.nExt].jObj );
2423 rc = JNI_TRUE;
2424 break;
2427 S3JniAutoExt_mutex_leave;
2428 return rc;
2432 /* Wrapper for sqlite3_close(_v2)(). */
2433 static jint s3jni_close_db(JNIEnv * const env, jobject jDb, int version){
2434 int rc = 0;
2435 S3JniDb * const ps = S3JniDb_from_java(jDb);
2437 assert(version == 1 || version == 2);
2438 if( ps ){
2439 rc = 1==version
2440 ? (jint)sqlite3_close(ps->pDb)
2441 : (jint)sqlite3_close_v2(ps->pDb);
2442 if( 0==rc ){
2443 NativePointerHolder_set(&S3JniNphRefs.sqlite3, jDb, 0);
2446 return (jint)rc;
2449 S3JniApi(sqlite3_close_v2(),jint,1close_1v2)(
2450 JniArgsEnvClass, jobject pDb
2452 return s3jni_close_db(env, pDb, 2);
2455 S3JniApi(sqlite3_close(),jint,1close)(
2456 JniArgsEnvClass, jobject pDb
2458 return s3jni_close_db(env, pDb, 1);
2462 ** Assumes z is an array of unsigned short and returns the index in
2463 ** that array of the first element with the value 0.
2465 static unsigned int s3jni_utf16_strlen(void const * z){
2466 unsigned int i = 0;
2467 const unsigned short * p = z;
2468 while( p[i] ) ++i;
2469 return i;
2472 /* Descriptive alias for use with sqlite3_collation_needed(). */
2473 typedef S3JniHook S3JniCollationNeeded;
2475 /* Central C-to-Java sqlite3_collation_needed16() hook impl. */
2476 static void s3jni_collation_needed_impl16(void *pState, sqlite3 *pDb,
2477 int eTextRep, const void * z16Name){
2478 S3JniCollationNeeded * const pHook = pState;
2479 S3JniDeclLocal_env;
2480 S3JniHook hook;
2482 S3JniHook_localdup(pHook, &hook);
2483 if( hook.jObj ){
2484 unsigned int const nName = s3jni_utf16_strlen(z16Name);
2485 jstring jName = (*env)->NewString(env, (jchar const *)z16Name, nName);
2487 s3jni_oom_check( jName );
2488 assert( hook.jExtra );
2489 S3JniIfThrew{
2490 S3JniExceptionClear;
2491 }else if( hook.jExtra ){
2492 (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
2493 hook.jExtra, (jint)eTextRep, jName);
2494 S3JniIfThrew{
2495 S3JniExceptionWarnCallbackThrew("sqlite3_collation_needed() callback");
2498 S3JniUnrefLocal(jName);
2499 S3JniHook_localundup(hook);
2503 S3JniApi(sqlite3_collation_needed(),jint,1collation_1needed)(
2504 JniArgsEnvClass, jobject jDb, jobject jHook
2506 S3JniDb * ps;
2507 S3JniCollationNeeded * pHook;
2508 int rc = 0;
2510 S3JniDb_mutex_enter;
2511 ps = S3JniDb_from_java(jDb);
2512 if( !ps ){
2513 S3JniDb_mutex_leave;
2514 return SQLITE_MISUSE;
2516 pHook = &ps->hooks.collationNeeded;
2517 if( pHook->jObj && jHook &&
2518 (*env)->IsSameObject(env, pHook->jObj, jHook) ){
2519 /* no-op */
2520 }else if( !jHook ){
2521 rc = sqlite3_collation_needed(ps->pDb, 0, 0);
2522 if( 0==rc ){
2523 S3JniHook_unref(pHook);
2525 }else{
2526 jclass const klazz = (*env)->GetObjectClass(env, jHook);
2527 jmethodID const xCallback = (*env)->GetMethodID(
2528 env, klazz, "call", "(Lorg/sqlite/jni/sqlite3;ILjava/lang/String;)I"
2530 S3JniUnrefLocal(klazz);
2531 S3JniIfThrew {
2532 rc = s3jni_db_exception(ps, SQLITE_MISUSE,
2533 "Cannot not find matching call() in "
2534 "CollationNeededCallback object.");
2535 }else{
2536 rc = sqlite3_collation_needed16(ps->pDb, pHook, s3jni_collation_needed_impl16);
2537 if( 0==rc ){
2538 S3JniHook_unref(pHook);
2539 pHook->midCallback = xCallback;
2540 pHook->jObj = S3JniRefGlobal(jHook);
2541 pHook->jExtra = S3JniRefGlobal(ps->jDb);
2545 S3JniDb_mutex_leave;
2546 return rc;
2549 S3JniApi(sqlite3_column_blob(),jbyteArray,1column_1blob)(
2550 JniArgsEnvClass, jobject jpStmt, jint ndx
2552 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
2553 void const * const p = sqlite3_column_blob(pStmt, (int)ndx);
2554 int const n = p ? sqlite3_column_bytes(pStmt, (int)ndx) : 0;
2556 return p ? s3jni_new_jbyteArray(p, n) : 0;
2559 S3JniApi(sqlite3_column_double(),jdouble,1column_1double)(
2560 JniArgsEnvClass, jobject jpStmt, jint ndx
2562 return (jdouble)sqlite3_column_double(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
2565 S3JniApi(sqlite3_column_int(),jint,1column_1int)(
2566 JniArgsEnvClass, jobject jpStmt, jint ndx
2568 return (jint)sqlite3_column_int(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
2571 S3JniApi(sqlite3_column_int64(),jlong,1column_1int64)(
2572 JniArgsEnvClass, jobject jpStmt, jint ndx
2574 return (jlong)sqlite3_column_int64(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
2577 S3JniApi(sqlite3_column_text(),jbyteArray,1column_1text_1utf8)(
2578 JniArgsEnvClass, jobject jpStmt, jint ndx
2580 sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt);
2581 const int n = sqlite3_column_bytes(stmt, (int)ndx);
2582 const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx);
2583 return p ? s3jni_new_jbyteArray(p, n) : NULL;
2586 S3JniApi(sqlite3_column_text(),jstring,1column_1text)(
2587 JniArgsEnvClass, jobject jpStmt, jint ndx
2589 sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt);
2590 const int n = sqlite3_column_bytes(stmt, (int)ndx);
2591 const unsigned char * const p = sqlite3_column_text(stmt, (int)ndx);
2592 return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0;
2595 S3JniApi(sqlite3_column_text16(),jstring,1column_1text16)(
2596 JniArgsEnvClass, jobject jpStmt, jint ndx
2598 sqlite3_stmt * const stmt = PtrGet_sqlite3_stmt(jpStmt);
2599 const int n = sqlite3_column_bytes16(stmt, (int)ndx);
2600 const void * const p = sqlite3_column_text16(stmt, (int)ndx);
2601 return s3jni_text16_to_jstring(env, p, n);
2604 S3JniApi(sqlite3_column_value(),jobject,1column_1value)(
2605 JniArgsEnvClass, jobject jpStmt, jint ndx
2607 sqlite3_value * const sv =
2608 sqlite3_column_value(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
2609 return new_sqlite3_value_wrapper(env, sv);
2613 ** Impl for both commit hooks (if isCommit is true) or rollback hooks.
2615 static int s3jni_commit_rollback_hook_impl(int isCommit, S3JniDb * const ps){
2616 S3JniDeclLocal_env;
2617 int rc = 0;
2618 S3JniHook hook;
2620 S3JniHook_localdup(isCommit
2621 ? &ps->hooks.commit : &ps->hooks.rollback,
2622 &hook);
2623 if( hook.jObj ){
2624 rc = isCommit
2625 ? (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback)
2626 : (int)((*env)->CallVoidMethod(env, hook.jObj, hook.midCallback), 0);
2627 S3JniIfThrew{
2628 S3JniExceptionClear;
2629 rc = s3jni_db_error(ps->pDb, SQLITE_ERROR, "hook callback threw.");
2631 S3JniHook_localundup(hook);
2633 return rc;
2636 /* C-to-Java commit hook wrapper. */
2637 static int s3jni_commit_hook_impl(void *pP){
2638 return s3jni_commit_rollback_hook_impl(1, pP);
2641 /* C-to-Java rollback hook wrapper. */
2642 static void s3jni_rollback_hook_impl(void *pP){
2643 (void)s3jni_commit_rollback_hook_impl(0, pP);
2646 static jobject s3jni_commit_rollback_hook(int isCommit, JNIEnv * const env,
2647 jobject jDb, jobject jHook){
2648 S3JniDb * ps;
2649 jobject pOld = 0;
2650 S3JniHook * pHook;
2652 S3JniDb_mutex_enter;
2653 ps = S3JniDb_from_java(jDb);
2654 if( !ps ){
2655 s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
2656 S3JniDb_mutex_leave;
2657 return 0;
2659 pHook = isCommit ? &ps->hooks.commit : &ps->hooks.rollback;
2660 pOld = pHook->jObj;
2661 if( pOld && jHook &&
2662 (*env)->IsSameObject(env, pOld, jHook) ){
2663 /* No-op. */
2664 }else if( !jHook ){
2665 if( pOld ){
2666 jobject tmp = S3JniRefLocal(pOld);
2667 S3JniUnrefGlobal(pOld);
2668 pOld = tmp;
2670 *pHook = S3JniHook_empty;
2671 if( isCommit ) sqlite3_commit_hook(ps->pDb, 0, 0);
2672 else sqlite3_rollback_hook(ps->pDb, 0, 0);
2673 }else{
2674 jclass const klazz = (*env)->GetObjectClass(env, jHook);
2675 jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call",
2676 isCommit ? "()I" : "()V");
2677 S3JniUnrefLocal(klazz);
2678 S3JniIfThrew {
2679 S3JniExceptionReport;
2680 S3JniExceptionClear;
2681 s3jni_db_error(ps->pDb, SQLITE_ERROR,
2682 "Cannot not find matching call() in"
2683 "hook object.");
2684 }else{
2685 pHook->midCallback = xCallback;
2686 pHook->jObj = S3JniRefGlobal(jHook);
2687 if( isCommit ) sqlite3_commit_hook(ps->pDb, s3jni_commit_hook_impl, ps);
2688 else sqlite3_rollback_hook(ps->pDb, s3jni_rollback_hook_impl, ps);
2689 if( pOld ){
2690 jobject tmp = S3JniRefLocal(pOld);
2691 S3JniUnrefGlobal(pOld);
2692 pOld = tmp;
2696 S3JniDb_mutex_leave;
2697 return pOld;
2700 S3JniApi(sqlite3_commit_hook(),jobject,1commit_1hook)(
2701 JniArgsEnvClass,jobject jDb, jobject jHook
2703 return s3jni_commit_rollback_hook(1, env, jDb, jHook);
2706 S3JniApi(sqlite3_compileoption_get(),jstring,1compileoption_1get)(
2707 JniArgsEnvClass, jint n
2709 jstring const rv = (*env)->NewStringUTF( env, sqlite3_compileoption_get(n) )
2710 /* We know these to be ASCII, so MUTF-8 is fine. */;
2711 s3jni_oom_check(rv);
2712 return rv;
2715 S3JniApi(sqlite3_compileoption_used(),jboolean,1compileoption_1used)(
2716 JniArgsEnvClass, jstring name
2718 const char *zUtf8 = s3jni_jstring_to_mutf8(name)
2719 /* We know these to be ASCII, so MUTF-8 is fine. */;
2720 const jboolean rc =
2721 0==sqlite3_compileoption_used(zUtf8) ? JNI_FALSE : JNI_TRUE;
2722 s3jni_mutf8_release(name, zUtf8);
2723 return rc;
2726 S3JniApi(sqlite3_config() /*for a small subset of options.*/,
2727 jint,1config__I)(JniArgsEnvClass, jint n){
2728 switch( n ){
2729 case SQLITE_CONFIG_SINGLETHREAD:
2730 case SQLITE_CONFIG_MULTITHREAD:
2731 case SQLITE_CONFIG_SERIALIZED:
2732 return sqlite3_config( n );
2733 default:
2734 return SQLITE_MISUSE;
2738 #ifdef SQLITE_ENABLE_SQLLOG
2739 /* C-to-Java SQLITE_CONFIG_SQLLOG wrapper. */
2740 static void s3jni_config_sqllog(void *ignored, sqlite3 *pDb, const char *z, int op){
2741 jobject jArg0 = 0;
2742 jstring jArg1 = 0;
2743 S3JniDeclLocal_env;
2744 S3JniDb * const ps = S3JniDb_from_c(pDb);
2745 S3JniHook hook = S3JniHook_empty;
2747 if( ps ){
2748 S3JniHook_localdup(&SJG.hooks.sqllog, &hook);
2750 if( !hook.jObj ) return;
2751 jArg0 = S3JniRefLocal(ps->jDb);
2752 switch( op ){
2753 case 0: /* db opened */
2754 case 1: /* SQL executed */
2755 jArg1 = s3jni_utf8_to_jstring( z, -1);
2756 break;
2757 case 2: /* db closed */
2758 break;
2759 default:
2760 (*env)->FatalError(env, "Unhandled 4th arg to SQLITE_CONFIG_SQLLOG.");
2761 break;
2763 (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback, jArg0, jArg1, op);
2764 S3JniIfThrew{
2765 S3JniExceptionWarnCallbackThrew("SQLITE_CONFIG_SQLLOG callback");
2766 S3JniExceptionClear;
2768 S3JniHook_localundup(hook);
2769 S3JniUnrefLocal(jArg0);
2770 S3JniUnrefLocal(jArg1);
2772 //! Requirement of SQLITE_CONFIG_SQLLOG.
2773 void sqlite3_init_sqllog(void){
2774 sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
2776 #endif
2778 S3JniApi(sqlite3_config() /* for SQLLOG */,
2779 jint, 1config__Lorg_sqlite_jni_ConfigSqllogCallback_2)(
2780 JniArgsEnvClass, jobject jLog
2782 #ifndef SQLITE_ENABLE_SQLLOG
2783 return SQLITE_MISUSE;
2784 #else
2785 S3JniHook * const pHook = &SJG.hooks.sqllog;
2786 int rc = 0;
2788 S3JniGlobal_mutex_enter;
2789 if( !jLog ){
2790 rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
2791 if( 0==rc ){
2792 S3JniHook_unref(pHook);
2794 }else if( pHook->jObj && (*env)->IsSameObject(env, jLog, pHook->jObj) ){
2795 /* No-op */
2796 }else {
2797 jclass const klazz = (*env)->GetObjectClass(env, jLog);
2798 jmethodID const midCallback = (*env)->GetMethodID(env, klazz, "call",
2799 "(Lorg/sqlite/jni/sqlite3;"
2800 "Ljava/lang/String;"
2801 "I)V");
2802 S3JniUnrefLocal(klazz);
2803 if( midCallback ){
2804 rc = sqlite3_config( SQLITE_CONFIG_SQLLOG, s3jni_config_sqllog, 0 );
2805 if( 0==rc ){
2806 S3JniHook_unref(pHook);
2807 pHook->midCallback = midCallback;
2808 pHook->jObj = S3JniRefGlobal(jLog);
2810 }else{
2811 S3JniExceptionWarnIgnore;
2812 rc = SQLITE_ERROR;
2815 S3JniGlobal_mutex_leave;
2816 return rc;
2817 #endif
2820 S3JniApi(sqlite3_context_db_handle(),jobject,1context_1db_1handle)(
2821 JniArgsEnvClass, jobject jpCx
2823 sqlite3_context * const pCx = PtrGet_sqlite3_context(jpCx);
2824 sqlite3 * const pDb = pCx ? sqlite3_context_db_handle(pCx) : 0;
2825 S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0;
2826 return ps ? ps->jDb : 0;
2830 State for CollationCallbacks.
2832 typedef S3JniHook S3JniCollationCallback;
2835 ** Proxy for Java-side CollationCallback.xCompare() callbacks.
2837 static int CollationCallback_xCompare(void *pArg, int nLhs, const void *lhs,
2838 int nRhs, const void *rhs){
2839 S3JniCollationCallback * const pCC = pArg;
2840 S3JniDeclLocal_env;
2841 jint rc = 0;
2842 if( pCC->jObj ){
2843 jbyteArray jbaLhs = s3jni_new_jbyteArray(lhs, (jint)nLhs);
2844 jbyteArray jbaRhs = jbaLhs
2845 ? s3jni_new_jbyteArray(rhs, (jint)nRhs) : 0;
2846 if( !jbaRhs ){
2847 S3JniUnrefLocal(jbaLhs);
2848 /* We have no recovery strategy here. */
2849 s3jni_oom_check( jbaRhs );
2850 return 0;
2852 rc = (*env)->CallIntMethod(env, pCC->jObj, pCC->midCallback,
2853 jbaLhs, jbaRhs);
2854 S3JniExceptionIgnore;
2855 S3JniUnrefLocal(jbaLhs);
2856 S3JniUnrefLocal(jbaRhs);
2858 return (int)rc;
2861 /* CollationCallback finalizer for use by the sqlite3 internals. */
2862 static void CollationCallback_xDestroy(void *pArg){
2863 S3JniCollationCallback * const pCC = pArg;
2864 S3JniDeclLocal_env;
2865 S3JniHook_free(pCC);
2868 S3JniApi(sqlite3_create_collation() sqlite3_create_collation_v2(),
2869 jint,1create_1collation
2870 )(JniArgsEnvClass, jobject jDb, jstring name, jint eTextRep,
2871 jobject oCollation){
2872 int rc;
2873 S3JniDb * ps;
2875 S3JniDb_mutex_enter;
2876 ps = S3JniDb_from_java(jDb);
2877 if( !ps ){
2878 rc = SQLITE_MISUSE;
2879 }else{
2880 jclass const klazz = (*env)->GetObjectClass(env, oCollation);
2881 jmethodID const midCallback =
2882 (*env)->GetMethodID(env, klazz, "call", "([B[B)I");
2883 S3JniUnrefLocal(klazz);
2884 S3JniIfThrew{
2885 rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
2886 "Could not get call() method from "
2887 "CollationCallback object.");
2888 }else{
2889 char * const zName = s3jni_jstring_to_utf8( name, 0);
2890 S3JniCollationCallback * const pCC =
2891 zName ? S3JniHook_alloc() : 0;
2892 if( pCC ){
2893 rc = sqlite3_create_collation_v2(ps->pDb, zName, (int)eTextRep,
2894 pCC, CollationCallback_xCompare,
2895 CollationCallback_xDestroy);
2896 if( 0==rc ){
2897 pCC->midCallback = midCallback;
2898 pCC->jObj = S3JniRefGlobal(oCollation);
2899 pCC->doXDestroy = 1;
2900 }else{
2901 CollationCallback_xDestroy(pCC);
2903 }else{
2904 rc = SQLITE_NOMEM;
2906 sqlite3_free(zName);
2909 S3JniDb_mutex_leave;
2910 return (jint)rc;
2913 S3JniApi(sqlite3_create_function() sqlite3_create_function_v2() sqlite3_create_window_function(),
2914 jint,1create_1function
2915 )(JniArgsEnvClass, jobject jDb, jstring jFuncName, jint nArg,
2916 jint eTextRep, jobject jFunctor){
2917 S3JniUdf * s = 0;
2918 int rc;
2919 sqlite3 * const pDb = PtrGet_sqlite3(jDb);
2920 char * zFuncName = 0;
2922 if( !encodingTypeIsValid(eTextRep) ){
2923 return s3jni_db_error(pDb, SQLITE_FORMAT,
2924 "Invalid function encoding option.");
2926 s = S3JniUdf_alloc(env, jFunctor);
2927 if( !s ) return SQLITE_NOMEM;
2929 if( UDF_UNKNOWN_TYPE==s->type ){
2930 rc = s3jni_db_error(pDb, SQLITE_MISUSE,
2931 "Cannot unambiguously determine function type.");
2932 S3JniUdf_free(env, s, 1);
2933 goto error_cleanup;
2935 zFuncName = s3jni_jstring_to_utf8(jFuncName,0);
2936 if( !zFuncName ){
2937 rc = SQLITE_NOMEM;
2938 S3JniUdf_free(env, s, 1);
2939 goto error_cleanup;
2941 if( UDF_WINDOW == s->type ){
2942 rc = sqlite3_create_window_function(pDb, zFuncName, nArg, eTextRep, s,
2943 udf_xStep, udf_xFinal, udf_xValue,
2944 udf_xInverse, S3JniUdf_finalizer);
2945 }else{
2946 udf_xFunc_f xFunc = 0;
2947 udf_xStep_f xStep = 0;
2948 udf_xFinal_f xFinal = 0;
2949 if( UDF_SCALAR == s->type ){
2950 xFunc = udf_xFunc;
2951 }else{
2952 assert( UDF_AGGREGATE == s->type );
2953 xStep = udf_xStep;
2954 xFinal = udf_xFinal;
2956 rc = sqlite3_create_function_v2(pDb, zFuncName, nArg, eTextRep, s,
2957 xFunc, xStep, xFinal, S3JniUdf_finalizer);
2959 error_cleanup:
2960 /* Reminder: on sqlite3_create_function() error, s will be
2961 ** destroyed via create_function(). */
2962 sqlite3_free(zFuncName);
2963 return (jint)rc;
2966 S3JniApi(sqlite3_db_filename(),jstring,1db_1filename)(
2967 JniArgsEnvClass, jobject jDb, jstring jDbName
2969 S3JniDb * const ps = S3JniDb_from_java(jDb);
2970 char *zDbName;
2971 jstring jRv = 0;
2972 int nStr = 0;
2974 if( !ps || !jDbName ){
2975 return 0;
2977 zDbName = s3jni_jstring_to_utf8( jDbName, &nStr);
2978 if( zDbName ){
2979 char const * zRv = sqlite3_db_filename(ps->pDb, zDbName);
2980 sqlite3_free(zDbName);
2981 if( zRv ){
2982 jRv = s3jni_utf8_to_jstring( zRv, -1);
2985 return jRv;
2988 S3JniApi(sqlite3_db_handle(),jobject,1db_1handle)(
2989 JniArgsEnvClass, jobject jpStmt
2991 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
2992 sqlite3 * const pDb = pStmt ? sqlite3_db_handle(pStmt) : 0;
2993 S3JniDb * const ps = pDb ? S3JniDb_from_c(pDb) : 0;
2994 return ps ? ps->jDb : 0;
2997 S3JniApi(sqlite3_db_config() /*for MAINDBNAME*/,
2998 jint,1db_1config__Lorg_sqlite_jni_sqlite3_2ILjava_lang_String_2
2999 )(JniArgsEnvClass, jobject jDb, jint op, jstring jStr){
3000 S3JniDb * const ps = S3JniDb_from_java(jDb);
3001 int rc;
3002 char *zStr;
3004 switch( (ps && jStr) ? op : 0 ){
3005 case SQLITE_DBCONFIG_MAINDBNAME:
3006 S3JniDb_mutex_enter
3007 /* Protect against a race in modifying/freeing
3008 ps->zMainDbName. */;
3009 zStr = s3jni_jstring_to_utf8( jStr, 0);
3010 if( zStr ){
3011 rc = sqlite3_db_config(ps->pDb, (int)op, zStr);
3012 if( rc ){
3013 sqlite3_free( zStr );
3014 }else{
3015 sqlite3_free( ps->zMainDbName );
3016 ps->zMainDbName = zStr;
3018 }else{
3019 rc = SQLITE_NOMEM;
3021 S3JniDb_mutex_leave;
3022 break;
3023 default:
3024 rc = SQLITE_MISUSE;
3026 return rc;
3029 S3JniApi(
3030 sqlite3_db_config(),
3031 /* WARNING: openjdk v19 creates a different mangled name for this
3032 ** function than openjdk v8 does. We account for that by exporting
3033 ** both versions of the name. */
3034 jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2
3036 JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut
3038 S3JniDb * const ps = S3JniDb_from_java(jDb);
3039 int rc;
3040 switch( ps ? op : 0 ){
3041 case SQLITE_DBCONFIG_ENABLE_FKEY:
3042 case SQLITE_DBCONFIG_ENABLE_TRIGGER:
3043 case SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER:
3044 case SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION:
3045 case SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE:
3046 case SQLITE_DBCONFIG_ENABLE_QPSG:
3047 case SQLITE_DBCONFIG_TRIGGER_EQP:
3048 case SQLITE_DBCONFIG_RESET_DATABASE:
3049 case SQLITE_DBCONFIG_DEFENSIVE:
3050 case SQLITE_DBCONFIG_WRITABLE_SCHEMA:
3051 case SQLITE_DBCONFIG_LEGACY_ALTER_TABLE:
3052 case SQLITE_DBCONFIG_DQS_DML:
3053 case SQLITE_DBCONFIG_DQS_DDL:
3054 case SQLITE_DBCONFIG_ENABLE_VIEW:
3055 case SQLITE_DBCONFIG_LEGACY_FILE_FORMAT:
3056 case SQLITE_DBCONFIG_TRUSTED_SCHEMA:
3057 case SQLITE_DBCONFIG_STMT_SCANSTATUS:
3058 case SQLITE_DBCONFIG_REVERSE_SCANORDER: {
3059 int pOut = 0;
3060 rc = sqlite3_db_config( ps->pDb, (int)op, onOff, &pOut );
3061 if( 0==rc && jOut ){
3062 OutputPointer_set_Int32(env, jOut, pOut);
3064 break;
3066 default:
3067 rc = SQLITE_MISUSE;
3069 return (jint)rc;
3073 ** This is a workaround for openjdk v19 (and possibly others) encoding
3074 ** this function's name differently than JDK v8 does. If we do not
3075 ** install both names for this function then Java will not be able to
3076 ** find the function in both environments.
3078 JniDecl(jint,1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_00024Int32_2)(
3079 JniArgsEnvClass, jobject jDb, jint op, jint onOff, jobject jOut
3081 return JniFuncName(1db_1config__Lorg_sqlite_jni_sqlite3_2IILorg_sqlite_jni_OutputPointer_Int32_2)(
3082 env, jKlazz, jDb, op, onOff, jOut
3086 S3JniApi(sqlite3_db_release_memory(),int,1db_1release_1memory)(
3087 JniArgsEnvClass, jobject jDb
3089 sqlite3 * const pDb = PtrGet_sqlite3(jDb);
3090 return pDb ? sqlite3_db_release_memory(pDb) : SQLITE_MISUSE;
3093 S3JniApi(sqlite3_db_status(),jint,1db_1status)(
3094 JniArgsEnvClass, jobject jDb, jint op, jobject jOutCurrent,
3095 jobject jOutHigh, jboolean reset
3097 int iCur = 0, iHigh = 0;
3098 sqlite3 * const pDb = PtrGet_sqlite3(jDb);
3099 int rc = sqlite3_db_status( pDb, op, &iCur, &iHigh, reset );
3100 if( 0==rc ){
3101 OutputPointer_set_Int32(env, jOutCurrent, iCur);
3102 OutputPointer_set_Int32(env, jOutHigh, iHigh);
3104 return (jint)rc;
3107 S3JniApi(sqlite3_errcode(),jint,1errcode)(
3108 JniArgsEnvClass, jobject jpDb
3110 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3111 return pDb ? sqlite3_errcode(pDb) : SQLITE_MISUSE;
3114 S3JniApi(sqlite3_errmsg(),jstring,1errmsg)(
3115 JniArgsEnvClass, jobject jpDb
3117 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3118 return pDb ? s3jni_utf8_to_jstring( sqlite3_errmsg(pDb), -1) : 0;
3121 S3JniApi(sqlite3_errstr(),jstring,1errstr)(
3122 JniArgsEnvClass, jint rcCode
3124 jstring const rv = (*env)->NewStringUTF(env, sqlite3_errstr((int)rcCode))
3125 /* We know these values to be plain ASCII, so pose no MUTF-8
3126 ** incompatibility */;
3127 s3jni_oom_check( rv );
3128 return rv;
3131 S3JniApi(sqlite3_expanded_sql(),jstring,1expanded_1sql)(
3132 JniArgsEnvClass, jobject jpStmt
3134 jstring rv = 0;
3135 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
3136 if( pStmt ){
3137 char * zSql = sqlite3_expanded_sql(pStmt);
3138 s3jni_oom_fatal(zSql);
3139 if( zSql ){
3140 rv = s3jni_utf8_to_jstring( zSql, -1);
3141 sqlite3_free(zSql);
3144 return rv;
3147 S3JniApi(sqlite3_extended_result_codes(),jboolean,1extended_1result_1codes)(
3148 JniArgsEnvClass, jobject jpDb, jboolean onoff
3150 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3151 int const rc = pDb ? sqlite3_extended_result_codes(pDb, onoff ? 1 : 0) : 0;
3152 return rc ? JNI_TRUE : JNI_FALSE;
3155 S3JniApi(sqlite3_finalize(),jint,1finalize)(
3156 JniArgsEnvClass, jobject jpStmt
3158 int rc = 0;
3159 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
3160 if( pStmt ){
3161 rc = sqlite3_finalize(pStmt);
3162 NativePointerHolder_set(&S3JniNphRefs.sqlite3_stmt, jpStmt, 0);
3164 return rc;
3167 S3JniApi(sqlite3_initialize(),jint,1initialize)(
3168 JniArgsEnvClass
3170 return sqlite3_initialize();
3173 S3JniApi(sqlite3_interrupt(),void,1interrupt)(
3174 JniArgsEnvClass, jobject jpDb
3176 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3177 if( pDb ){
3178 sqlite3_interrupt(pDb);
3182 S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)(
3183 JniArgsEnvClass, jobject jpDb
3185 int rc = 0;
3186 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3187 if( pDb ){
3188 rc = sqlite3_is_interrupted(pDb);
3190 return rc ? JNI_TRUE : JNI_FALSE;
3194 ** Uncaches the current JNIEnv from the S3JniGlobal state, clearing
3195 ** any resources owned by that cache entry and making that slot
3196 ** available for re-use.
3198 JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){
3199 int rc;
3200 S3JniEnv_mutex_enter;
3201 rc = S3JniEnv_uncache(env);
3202 S3JniEnv_mutex_leave;
3203 return rc ? JNI_TRUE : JNI_FALSE;
3207 S3JniApi(sqlite3_last_insert_rowid(),jlong,1last_1insert_1rowid)(
3208 JniArgsEnvClass, jobject jpDb
3210 jlong rc = 0;
3211 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3212 if( pDb ){
3213 rc = (jlong)sqlite3_last_insert_rowid(pDb);
3215 return rc;
3218 S3JniApi(sqlite3_limit(),jint,1limit)(
3219 JniArgsEnvClass, jobject jpDb, jint id, jint newVal
3221 jint rc = 0;
3222 sqlite3 * const pDb = PtrGet_sqlite3(jpDb);
3223 if( pDb ){
3224 rc = sqlite3_limit( pDb, (int)id, (int)newVal );
3226 return rc;
3229 /* Pre-open() code common to sqlite3_open[_v2](). */
3230 static int s3jni_open_pre(JNIEnv * const env, S3JniEnv **jc,
3231 jstring jDbName, char **zDbName,
3232 S3JniDb ** ps){
3233 int rc = 0;
3234 jobject jDb = 0;
3235 *jc = S3JniEnv_get();
3236 if( !*jc ){
3237 rc = SQLITE_NOMEM;
3238 goto end;
3240 *zDbName = jDbName ? s3jni_jstring_to_utf8( jDbName, 0) : 0;
3241 if( jDbName && !*zDbName ){
3242 rc = SQLITE_NOMEM;
3243 goto end;
3245 jDb = new_sqlite3_wrapper(env, 0);
3246 if( !jDb ){
3247 sqlite3_free(*zDbName);
3248 *zDbName = 0;
3249 rc = SQLITE_NOMEM;
3250 goto end;
3252 *ps = S3JniDb_alloc(env, jDb);
3253 if( *ps ){
3254 (*jc)->pdbOpening = *ps;
3255 }else{
3256 S3JniUnrefLocal(jDb);
3257 rc = SQLITE_NOMEM;
3259 end:
3260 return rc;
3264 ** Post-open() code common to both the sqlite3_open() and
3265 ** sqlite3_open_v2() bindings. ps->jDb must be the
3266 ** org.sqlite.jni.sqlite3 object which will hold the db's native
3267 ** pointer. theRc must be the result code of the open() op. If
3268 ** *ppDb is NULL then ps is set aside and its state cleared,
3269 ** else ps is associated with *ppDb. If *ppDb is not NULL then
3270 ** ps->jDb is stored in jOut (an OutputPointer.sqlite3 instance).
3272 ** Must be called if s3jni_open_pre() succeeds and must not be called
3273 ** if it doesn't.
3275 ** Returns theRc.
3277 static int s3jni_open_post(JNIEnv * const env, S3JniEnv * const jc,
3278 S3JniDb * ps, sqlite3 **ppDb,
3279 jobject jOut, int theRc){
3280 int rc = 0;
3281 jc->pdbOpening = 0;
3282 if( *ppDb ){
3283 assert(ps->jDb);
3284 if( 0==ps->pDb ){
3285 ps->pDb = *ppDb;
3286 NativePointerHolder_set(&S3JniNphRefs.sqlite3, ps->jDb, *ppDb);
3287 }else{
3288 assert( ps->pDb==*ppDb
3289 && "Set up via s3jni_run_java_auto_extensions()" );
3291 rc = sqlite3_set_clientdata(ps->pDb, S3JniDb_clientdata_key,
3292 ps, S3JniDb_xDestroy)
3293 /* As of here, the Java/C connection is complete */;
3294 }else{
3295 S3JniDb_set_aside(ps);
3296 ps = 0;
3298 OutputPointer_set_sqlite3(env, jOut, ps ? ps->jDb : 0);
3299 return theRc ? theRc : rc;
3302 S3JniApi(sqlite3_open(),jint,1open)(
3303 JniArgsEnvClass, jstring strName, jobject jOut
3305 sqlite3 * pOut = 0;
3306 char *zName = 0;
3307 S3JniDb * ps = 0;
3308 S3JniEnv * jc = 0;
3309 int rc;
3310 rc = s3jni_open_pre(env, &jc, strName, &zName, &ps);
3311 if( 0==rc ){
3312 rc = s3jni_open_post(env, jc, ps, &pOut, jOut,
3313 sqlite3_open(zName, &pOut));
3314 assert(rc==0 ? pOut!=0 : 1);
3315 sqlite3_free(zName);
3317 return (jint)rc;
3320 S3JniApi(sqlite3_open_v2(),jint,1open_1v2)(
3321 JniArgsEnvClass, jstring strName,
3322 jobject jOut, jint flags, jstring strVfs
3324 sqlite3 * pOut = 0;
3325 char *zName = 0;
3326 S3JniDb * ps = 0;
3327 S3JniEnv * jc = 0;
3328 char *zVfs = 0;
3329 int rc = s3jni_open_pre(env, &jc, strName, &zName, &ps);
3330 if( 0==rc ){
3331 if( strVfs ){
3332 zVfs = s3jni_jstring_to_utf8( strVfs, 0);
3333 if( !zVfs ){
3334 rc = SQLITE_NOMEM;
3337 if( 0==rc ){
3338 rc = sqlite3_open_v2(zName, &pOut, (int)flags, zVfs);
3340 rc = s3jni_open_post(env, jc, ps, &pOut, jOut, rc);
3342 assert(rc==0 ? pOut!=0 : 1);
3343 sqlite3_free(zName);
3344 sqlite3_free(zVfs);
3345 return (jint)rc;
3348 /* Proxy for the sqlite3_prepare[_v2/3]() family. */
3349 jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self,
3350 jobject jDb, jbyteArray baSql,
3351 jint nMax, jint prepFlags,
3352 jobject jOutStmt, jobject outTail){
3353 sqlite3_stmt * pStmt = 0;
3354 jobject jStmt = 0;
3355 const char * zTail = 0;
3356 jbyte * const pBuf = s3jni_jbytearray_bytes(baSql);
3357 int rc = SQLITE_ERROR;
3358 assert(prepVersion==1 || prepVersion==2 || prepVersion==3);
3359 if( !pBuf ){
3360 rc = baSql ? SQLITE_NOMEM : SQLITE_MISUSE;
3361 goto end;
3363 jStmt = new_sqlite3_stmt_wrapper(env, 0);
3364 if( !jStmt ){
3365 rc = SQLITE_NOMEM;
3366 goto end;
3368 switch( prepVersion ){
3369 case 1: rc = sqlite3_prepare(PtrGet_sqlite3(jDb), (const char *)pBuf,
3370 (int)nMax, &pStmt, &zTail);
3371 break;
3372 case 2: rc = sqlite3_prepare_v2(PtrGet_sqlite3(jDb), (const char *)pBuf,
3373 (int)nMax, &pStmt, &zTail);
3374 break;
3375 case 3: rc = sqlite3_prepare_v3(PtrGet_sqlite3(jDb), (const char *)pBuf,
3376 (int)nMax, (unsigned int)prepFlags,
3377 &pStmt, &zTail);
3378 break;
3379 default:
3380 assert(0 && "Invalid prepare() version");
3382 end:
3383 s3jni_jbytearray_release(baSql,pBuf);
3384 if( 0==rc ){
3385 if( 0!=outTail ){
3386 /* Noting that pBuf is deallocated now but its address is all we need for
3387 ** what follows... */
3388 assert(zTail ? ((void*)zTail>=(void*)pBuf) : 1);
3389 assert(zTail ? (((int)((void*)zTail - (void*)pBuf)) >= 0) : 1);
3390 OutputPointer_set_Int32(env, outTail, (int)(zTail ? (zTail - (const char *)pBuf) : 0));
3392 if( pStmt ){
3393 NativePointerHolder_set(&S3JniNphRefs.sqlite3_stmt, jStmt, pStmt);
3394 }else{
3395 /* Happens for comments and whitespace. */
3396 S3JniUnrefLocal(jStmt);
3397 jStmt = 0;
3399 }else{
3400 S3JniUnrefLocal(jStmt);
3401 jStmt = 0;
3403 OutputPointer_set_sqlite3_stmt(env, jOutStmt, jStmt);
3404 return (jint)rc;
3406 S3JniApi(sqlite3_prepare(),jint,1prepare)(
3407 JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
3408 jint nMax, jobject jOutStmt, jobject outTail
3410 return sqlite3_jni_prepare_v123(1, env, self, jDb, baSql, nMax, 0,
3411 jOutStmt, outTail);
3413 S3JniApi(sqlite3_prepare_v2(),jint,1prepare_1v2)(
3414 JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
3415 jint nMax, jobject jOutStmt, jobject outTail
3417 return sqlite3_jni_prepare_v123(2, env, self, jDb, baSql, nMax, 0,
3418 jOutStmt, outTail);
3420 S3JniApi(sqlite3_prepare_v3(),jint,1prepare_1v3)(
3421 JNIEnv * const env, jclass self, jobject jDb, jbyteArray baSql,
3422 jint nMax, jint prepFlags, jobject jOutStmt, jobject outTail
3424 return sqlite3_jni_prepare_v123(3, env, self, jDb, baSql, nMax,
3425 prepFlags, jOutStmt, outTail);
3429 ** Impl for C-to-Java of the callbacks for both sqlite3_update_hook()
3430 ** and sqlite3_preupdate_hook(). The differences are that for
3431 ** update_hook():
3433 ** - pDb is NULL
3434 ** - iKey1 is the row ID
3435 ** - iKey2 is unused
3437 static void s3jni_updatepre_hook_impl(void * pState, sqlite3 *pDb, int opId,
3438 const char *zDb, const char *zTable,
3439 sqlite3_int64 iKey1, sqlite3_int64 iKey2){
3440 S3JniDb * const ps = pState;
3441 S3JniDeclLocal_env;
3442 jstring jDbName;
3443 jstring jTable;
3444 const int isPre = 0!=pDb;
3445 S3JniHook hook;
3447 S3JniHook_localdup(isPre ?
3448 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3449 &ps->hooks.preUpdate
3450 #else
3451 &S3JniHook_empty
3452 #endif
3453 : &ps->hooks.update, &hook);
3454 if( !hook.jObj ){
3455 return;
3457 jDbName = s3jni_utf8_to_jstring( zDb, -1);
3458 jTable = jDbName ? s3jni_utf8_to_jstring( zTable, -1) : 0;
3459 S3JniIfThrew {
3460 S3JniExceptionClear;
3461 s3jni_db_error(ps->pDb, SQLITE_NOMEM, 0);
3462 }else{
3463 assert( hook.jObj );
3464 assert( hook.midCallback );
3465 assert( ps->jDb );
3466 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3467 if( isPre ) (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
3468 ps->jDb, (jint)opId, jDbName, jTable,
3469 (jlong)iKey1, (jlong)iKey2);
3470 else
3471 #endif
3472 (*env)->CallVoidMethod(env, hook.jObj, hook.midCallback,
3473 (jint)opId, jDbName, jTable, (jlong)iKey1);
3474 S3JniIfThrew{
3475 S3JniExceptionWarnCallbackThrew("sqlite3_(pre)update_hook() callback");
3476 s3jni_db_exception(ps, 0,
3477 "sqlite3_(pre)update_hook() callback threw");
3480 S3JniUnrefLocal(jDbName);
3481 S3JniUnrefLocal(jTable);
3482 S3JniHook_localundup(hook);
3485 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3486 static void s3jni_preupdate_hook_impl(void * pState, sqlite3 *pDb, int opId,
3487 const char *zDb, const char *zTable,
3488 sqlite3_int64 iKey1, sqlite3_int64 iKey2){
3489 return s3jni_updatepre_hook_impl(pState, pDb, opId, zDb, zTable,
3490 iKey1, iKey2);
3492 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
3494 static void s3jni_update_hook_impl(void * pState, int opId, const char *zDb,
3495 const char *zTable, sqlite3_int64 nRowid){
3496 return s3jni_updatepre_hook_impl(pState, NULL, opId, zDb, zTable, nRowid, 0);
3499 #ifndef SQLITE_ENABLE_PREUPDATE_HOOK
3500 /* We need no-op impls for preupdate_{count,depth,blobwrite}() */
3501 S3JniApi(sqlite3_preupdate_blobwrite(),int,1preupdate_1blobwrite)(
3502 JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; }
3503 S3JniApi(sqlite3_preupdate_count(),int,1preupdate_1count)(
3504 JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; }
3505 S3JniApi(sqlite3_preupdate_depth(),int,1preupdate_1depth)(
3506 JniArgsEnvClass, jobject jDb){ return SQLITE_MISUSE; }
3507 #endif /* !SQLITE_ENABLE_PREUPDATE_HOOK */
3510 ** JNI wrapper for both sqlite3_update_hook() and
3511 ** sqlite3_preupdate_hook() (if isPre is true).
3513 static jobject s3jni_updatepre_hook(JNIEnv * env, int isPre, jobject jDb, jobject jHook){
3514 S3JniDb * const ps = S3JniDb_from_java(jDb);
3515 jclass klazz;
3516 jobject pOld = 0;
3517 jmethodID xCallback;
3518 S3JniHook * pHook;
3520 if( !ps ) return 0;
3521 S3JniDb_mutex_enter;
3522 pHook = isPre ?
3523 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3524 &ps->hooks.preUpdate
3525 #else
3527 #endif
3528 : &ps->hooks.update;
3529 if( !pHook ){
3530 goto end;
3532 pOld = pHook->jObj;
3533 if( pOld && jHook && (*env)->IsSameObject(env, pOld, jHook) ){
3534 goto end;
3536 if( !jHook ){
3537 if( pOld ){
3538 jobject tmp = S3JniRefLocal(pOld);
3539 S3JniUnrefGlobal(pOld);
3540 pOld = tmp;
3542 *pHook = S3JniHook_empty;
3543 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3544 if( isPre ) sqlite3_preupdate_hook(ps->pDb, 0, 0);
3545 else
3546 #endif
3547 sqlite3_update_hook(ps->pDb, 0, 0);
3548 goto end;
3550 klazz = (*env)->GetObjectClass(env, jHook);
3551 xCallback = isPre
3552 ? (*env)->GetMethodID(env, klazz, "call",
3553 "(Lorg/sqlite/jni/sqlite3;"
3555 "Ljava/lang/String;"
3556 "Ljava/lang/String;"
3557 "JJ)V")
3558 : (*env)->GetMethodID(env, klazz, "call",
3559 "(ILjava/lang/String;Ljava/lang/String;J)V");
3560 S3JniUnrefLocal(klazz);
3561 S3JniIfThrew {
3562 S3JniExceptionClear;
3563 s3jni_db_error(ps->pDb, SQLITE_ERROR,
3564 "Cannot not find matching callback on "
3565 "(pre)update hook object.");
3566 }else{
3567 pHook->midCallback = xCallback;
3568 pHook->jObj = S3JniRefGlobal(jHook);
3569 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3570 if( isPre ) sqlite3_preupdate_hook(ps->pDb, s3jni_preupdate_hook_impl, ps);
3571 else
3572 #endif
3573 sqlite3_update_hook(ps->pDb, s3jni_update_hook_impl, ps);
3574 if( pOld ){
3575 jobject tmp = S3JniRefLocal(pOld);
3576 S3JniUnrefGlobal(pOld);
3577 pOld = tmp;
3580 end:
3581 S3JniDb_mutex_leave;
3582 return pOld;
3586 S3JniApi(sqlite3_preupdate_hook(),jobject,1preupdate_1hook)(
3587 JniArgsEnvClass, jobject jDb, jobject jHook
3589 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3590 return s3jni_updatepre_hook(env, 1, jDb, jHook);
3591 #else
3592 return NULL;
3593 #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
3596 /* Impl for sqlite3_preupdate_{new,old}(). */
3597 static int s3jni_preupdate_newold(JNIEnv * const env, int isNew, jobject jDb,
3598 jint iCol, jobject jOut){
3599 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
3600 sqlite3 * const pDb = PtrGet_sqlite3(jDb);
3601 int rc = SQLITE_MISUSE;
3602 if( pDb ){
3603 sqlite3_value * pOut = 0;
3604 int (*fOrig)(sqlite3*,int,sqlite3_value**) =
3605 isNew ? sqlite3_preupdate_new : sqlite3_preupdate_old;
3606 rc = fOrig(pDb, (int)iCol, &pOut);
3607 if( 0==rc ){
3608 jobject pWrap = new_sqlite3_value_wrapper(env, pOut);
3609 if( pWrap ){
3610 OutputPointer_set_sqlite3_value(env, jOut, pWrap);
3611 S3JniUnrefLocal(pWrap);
3612 }else{
3613 rc = SQLITE_NOMEM;
3617 return rc;
3618 #else
3619 return SQLITE_MISUSE;
3620 #endif
3623 S3JniApi(sqlite3_preupdate_new(),jint,1preupdate_1new)(
3624 JniArgsEnvClass, jobject jDb, jint iCol, jobject jOut
3626 return s3jni_preupdate_newold(env, 1, jDb, iCol, jOut);
3629 S3JniApi(sqlite3_preupdate_old(),jint,1preupdate_1old)(
3630 JniArgsEnvClass, jobject jDb, jint iCol, jobject jOut
3632 return s3jni_preupdate_newold(env, 0, jDb, iCol, jOut);
3636 /* Central C-to-Java sqlite3_progress_handler() proxy. */
3637 static int s3jni_progress_handler_impl(void *pP){
3638 S3JniDb * const ps = (S3JniDb *)pP;
3639 int rc = 0;
3640 S3JniDeclLocal_env;
3641 S3JniHook hook;
3643 S3JniHook_localdup(&ps->hooks.progress, &hook);
3644 if( hook.jObj ){
3645 rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback);
3646 S3JniIfThrew{
3647 rc = s3jni_db_exception(ps, rc,
3648 "sqlite3_progress_handler() callback threw");
3650 S3JniHook_localundup(hook);
3652 return rc;
3655 S3JniApi(sqlite3_progress_handler(),void,1progress_1handler)(
3656 JniArgsEnvClass,jobject jDb, jint n, jobject jProgress
3658 S3JniDb * const ps = S3JniDb_from_java(jDb);
3659 S3JniHook * const pHook = ps ? &ps->hooks.progress : 0;
3661 if( !ps ) return;
3662 S3JniDb_mutex_enter;
3663 if( n<1 || !jProgress ){
3664 S3JniHook_unref(pHook);
3665 sqlite3_progress_handler(ps->pDb, 0, 0, 0);
3666 }else{
3667 jclass const klazz = (*env)->GetObjectClass(env, jProgress);
3668 jmethodID const xCallback = (*env)->GetMethodID(env, klazz, "call", "()I");
3669 S3JniUnrefLocal(klazz);
3670 S3JniIfThrew {
3671 S3JniExceptionClear;
3672 s3jni_db_error(ps->pDb, SQLITE_ERROR,
3673 "Cannot not find matching xCallback() on "
3674 "ProgressHandler object.");
3675 }else{
3676 S3JniUnrefGlobal(pHook->jObj);
3677 pHook->midCallback = xCallback;
3678 pHook->jObj = S3JniRefGlobal(jProgress);
3679 sqlite3_progress_handler(ps->pDb, (int)n, s3jni_progress_handler_impl, ps);
3682 S3JniDb_mutex_leave;
3685 S3JniApi(sqlite3_reset(),jint,1reset)(
3686 JniArgsEnvClass, jobject jpStmt
3688 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
3689 return pStmt ? sqlite3_reset(pStmt) : SQLITE_MISUSE;
3692 /* Clears all entries from S3JniGlobal.autoExt. */
3693 static void s3jni_reset_auto_extension(JNIEnv *env){
3694 int i;
3695 S3JniAutoExt_mutex_enter;
3696 for( i = 0; i < SJG.autoExt.nExt; ++i ){
3697 S3JniAutoExtension_clear( &SJG.autoExt.aExt[i] );
3699 SJG.autoExt.nExt = 0;
3700 S3JniAutoExt_mutex_leave;
3703 S3JniApi(sqlite3_reset_auto_extension(),void,1reset_1auto_1extension)(
3704 JniArgsEnvClass
3706 s3jni_reset_auto_extension(env);
3709 /* Impl for sqlite3_result_text/blob() and friends. */
3710 static void result_blob_text(int as64 /* true for text64/blob64() mode */,
3711 int eTextRep /* 0 for blobs, else SQLITE_UTF... */,
3712 JNIEnv * const env, sqlite3_context *pCx,
3713 jbyteArray jBa, jlong nMax){
3714 int const asBlob = 0==eTextRep;
3715 if( jBa ){
3716 jbyte * const pBuf = s3jni_jbytearray_bytes(jBa);
3717 jsize nBa = (*env)->GetArrayLength(env, jBa);
3718 if( nMax>=0 && nBa>(jsize)nMax ){
3719 nBa = (jsize)nMax;
3721 From the sqlite docs:
3723 > If the 3rd parameter to any of the sqlite3_result_text*
3724 interfaces other than sqlite3_result_text64() is negative,
3725 then SQLite computes the string length itself by searching
3726 the 2nd parameter for the first zero character.
3728 Note that the text64() interfaces take an unsigned value for
3729 the length, which Java does not support. This binding takes
3730 the approach of passing on negative values to the C API,
3731 which will, in turn fail with SQLITE_TOOBIG at some later
3732 point (recall that the sqlite3_result_xyz() family do not
3733 have result values).
3736 if( as64 ){ /* 64-bit... */
3737 static const jsize nLimit64 =
3738 SQLITE_MAX_ALLOCATION_SIZE/*only _kinda_ arbitrary*/;
3739 if( nBa > nLimit64 ){
3740 sqlite3_result_error_toobig(pCx);
3741 }else if( asBlob ){
3742 sqlite3_result_blob64(pCx, pBuf, (sqlite3_uint64)nBa,
3743 SQLITE_TRANSIENT);
3744 }else{ /* text64... */
3745 if( encodingTypeIsValid(eTextRep) ){
3746 sqlite3_result_text64(pCx, (const char *)pBuf,
3747 (sqlite3_uint64)nBa,
3748 SQLITE_TRANSIENT, eTextRep);
3749 }else{
3750 sqlite3_result_error_code(pCx, SQLITE_FORMAT);
3753 }else{ /* 32-bit... */
3754 static const jsize nLimit = SQLITE_MAX_ALLOCATION_SIZE;
3755 if( nBa > nLimit ){
3756 sqlite3_result_error_toobig(pCx);
3757 }else if( asBlob ){
3758 sqlite3_result_blob(pCx, pBuf, (int)nBa,
3759 SQLITE_TRANSIENT);
3760 }else{
3761 switch( eTextRep ){
3762 case SQLITE_UTF8:
3763 sqlite3_result_text(pCx, (const char *)pBuf, (int)nBa,
3764 SQLITE_TRANSIENT);
3765 break;
3766 case SQLITE_UTF16:
3767 sqlite3_result_text16(pCx, (const char *)pBuf, (int)nBa,
3768 SQLITE_TRANSIENT);
3769 break;
3770 case SQLITE_UTF16LE:
3771 sqlite3_result_text16le(pCx, (const char *)pBuf, (int)nBa,
3772 SQLITE_TRANSIENT);
3773 break;
3774 case SQLITE_UTF16BE:
3775 sqlite3_result_text16be(pCx, (const char *)pBuf, (int)nBa,
3776 SQLITE_TRANSIENT);
3777 break;
3780 s3jni_jbytearray_release(jBa, pBuf);
3782 }else{
3783 sqlite3_result_null(pCx);
3787 S3JniApi(sqlite3_result_blob(),void,1result_1blob)(
3788 JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax
3790 return result_blob_text(0, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax);
3793 S3JniApi(sqlite3_result_blob64(),void,1result_1blob64)(
3794 JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax
3796 return result_blob_text(1, 0, env, PtrGet_sqlite3_context(jpCx), jBa, nMax);
3799 S3JniApi(sqlite3_result_double(),void,1result_1double)(
3800 JniArgsEnvClass, jobject jpCx, jdouble v
3802 sqlite3_result_double(PtrGet_sqlite3_context(jpCx), v);
3805 S3JniApi(sqlite3_result_error(),void,1result_1error)(
3806 JniArgsEnvClass, jobject jpCx, jbyteArray baMsg, int eTextRep
3808 const char * zUnspecified = "Unspecified error.";
3809 jsize const baLen = (*env)->GetArrayLength(env, baMsg);
3810 jbyte * const pjBuf = baMsg ? s3jni_jbytearray_bytes(baMsg) : NULL;
3811 switch( pjBuf ? eTextRep : SQLITE_UTF8 ){
3812 case SQLITE_UTF8: {
3813 const char *zMsg = pjBuf ? (const char *)pjBuf : zUnspecified;
3814 int const n = pjBuf ? (int)baLen : (int)sqlite3Strlen30(zMsg);
3815 sqlite3_result_error(PtrGet_sqlite3_context(jpCx), zMsg, n);
3816 break;
3818 case SQLITE_UTF16: {
3819 const void *zMsg = pjBuf;
3820 sqlite3_result_error16(PtrGet_sqlite3_context(jpCx), zMsg, (int)baLen);
3821 break;
3823 default:
3824 sqlite3_result_error(PtrGet_sqlite3_context(jpCx),
3825 "Invalid encoding argument passed "
3826 "to sqlite3_result_error().", -1);
3827 break;
3829 s3jni_jbytearray_release(baMsg,pjBuf);
3832 S3JniApi(sqlite3_result_error_code(),void,1result_1error_1code)(
3833 JniArgsEnvClass, jobject jpCx, jint v
3835 sqlite3_result_error_code(PtrGet_sqlite3_context(jpCx), (int)v);
3838 S3JniApi(sqlite3_result_error_nomem(),void,1result_1error_1nomem)(
3839 JniArgsEnvClass, jobject jpCx
3841 sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx));
3844 S3JniApi(sqlite3_result_error_toobig(),void,1result_1error_1toobig)(
3845 JniArgsEnvClass, jobject jpCx
3847 sqlite3_result_error_toobig(PtrGet_sqlite3_context(jpCx));
3850 S3JniApi(sqlite3_result_int(),void,1result_1int)(
3851 JniArgsEnvClass, jobject jpCx, jint v
3853 sqlite3_result_int(PtrGet_sqlite3_context(jpCx), (int)v);
3856 S3JniApi(sqlite3_result_int64(),void,1result_1int64)(
3857 JniArgsEnvClass, jobject jpCx, jlong v
3859 sqlite3_result_int64(PtrGet_sqlite3_context(jpCx), (sqlite3_int64)v);
3862 S3JniApi(sqlite3_result_java_object(),void,1result_1java_1object)(
3863 JniArgsEnvClass, jobject jpCx, jobject v
3865 if( v ){
3866 jobject const rjv = S3JniRefGlobal(v);
3867 if( rjv ){
3868 sqlite3_result_pointer(PtrGet_sqlite3_context(jpCx), rjv,
3869 ResultJavaValuePtrStr, ResultJavaValue_finalizer);
3870 }else{
3871 sqlite3_result_error_nomem(PtrGet_sqlite3_context(jpCx));
3873 }else{
3874 sqlite3_result_null(PtrGet_sqlite3_context(jpCx));
3878 S3JniApi(sqlite3_result_null(),void,1result_1null)(
3879 JniArgsEnvClass, jobject jpCx
3881 sqlite3_result_null(PtrGet_sqlite3_context(jpCx));
3884 S3JniApi(sqlite3_result_text(),void,1result_1text)(
3885 JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jint nMax
3887 return result_blob_text(0, SQLITE_UTF8, env,
3888 PtrGet_sqlite3_context(jpCx), jBa, nMax);
3891 S3JniApi(sqlite3_result_text64(),void,1result_1text64)(
3892 JniArgsEnvClass, jobject jpCx, jbyteArray jBa, jlong nMax,
3893 jint eTextRep
3895 return result_blob_text(1, eTextRep, env,
3896 PtrGet_sqlite3_context(jpCx), jBa, nMax);
3899 S3JniApi(sqlite3_result_value(),void,1result_1value)(
3900 JniArgsEnvClass, jobject jpCx, jobject jpSVal
3902 sqlite3_result_value(PtrGet_sqlite3_context(jpCx),
3903 PtrGet_sqlite3_value(jpSVal));
3906 S3JniApi(sqlite3_result_zeroblob(),void,1result_1zeroblob)(
3907 JniArgsEnvClass, jobject jpCx, jint v
3909 sqlite3_result_zeroblob(PtrGet_sqlite3_context(jpCx), (int)v);
3912 S3JniApi(sqlite3_result_zeroblob64(),jint,1result_1zeroblob64)(
3913 JniArgsEnvClass, jobject jpCx, jlong v
3915 return (jint)sqlite3_result_zeroblob64(PtrGet_sqlite3_context(jpCx),
3916 (sqlite3_int64)v);
3919 S3JniApi(sqlite3_rollback_hook(),jobject,1rollback_1hook)(
3920 JniArgsEnvClass, jobject jDb, jobject jHook
3922 return s3jni_commit_rollback_hook(0, env, jDb, jHook);
3925 /* Callback for sqlite3_set_authorizer(). */
3926 int s3jni_xAuth(void* pState, int op,const char*z0, const char*z1,
3927 const char*z2,const char*z3){
3928 S3JniDb * const ps = pState;
3929 S3JniDeclLocal_env;
3930 S3JniHook hook;
3931 int rc = 0;
3933 S3JniHook_localdup(&ps->hooks.auth, &hook );
3934 if( hook.jObj ){
3935 jstring const s0 = z0 ? s3jni_utf8_to_jstring( z0, -1) : 0;
3936 jstring const s1 = z1 ? s3jni_utf8_to_jstring( z1, -1) : 0;
3937 jstring const s2 = z2 ? s3jni_utf8_to_jstring( z2, -1) : 0;
3938 jstring const s3 = z3 ? s3jni_utf8_to_jstring( z3, -1) : 0;
3940 rc = (*env)->CallIntMethod(env, hook.jObj, hook.midCallback, (jint)op,
3941 s0, s1, s3, s3);
3942 S3JniIfThrew{
3943 rc = s3jni_db_exception(ps, rc, "sqlite3_set_authorizer() callback");
3945 S3JniUnrefLocal(s0);
3946 S3JniUnrefLocal(s1);
3947 S3JniUnrefLocal(s2);
3948 S3JniUnrefLocal(s3);
3949 S3JniHook_localundup(hook);
3951 return rc;
3954 S3JniApi(sqlite3_set_authorizer(),jint,1set_1authorizer)(
3955 JniArgsEnvClass,jobject jDb, jobject jHook
3957 S3JniDb * const ps = S3JniDb_from_java(jDb);
3958 S3JniHook * const pHook = ps ? &ps->hooks.auth : 0;
3959 int rc = 0;
3961 if( !ps ) return SQLITE_MISUSE;
3962 S3JniDb_mutex_enter;
3963 if( !jHook ){
3964 S3JniHook_unref(pHook);
3965 rc = sqlite3_set_authorizer( ps->pDb, 0, 0 );
3966 }else{
3967 jclass klazz;
3968 if( pHook->jObj ){
3969 if( (*env)->IsSameObject(env, pHook->jObj, jHook) ){
3970 /* Same object - this is a no-op. */
3971 S3JniDb_mutex_leave;
3972 return 0;
3974 S3JniHook_unref(pHook);
3976 pHook->jObj = S3JniRefGlobal(jHook);
3977 klazz = (*env)->GetObjectClass(env, jHook);
3978 pHook->midCallback = (*env)->GetMethodID(env, klazz,
3979 "call",
3980 "(I"
3981 "Ljava/lang/String;"
3982 "Ljava/lang/String;"
3983 "Ljava/lang/String;"
3984 "Ljava/lang/String;"
3985 ")I");
3986 S3JniUnrefLocal(klazz);
3987 S3JniIfThrew {
3988 rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
3989 "Error setting up Java parts of authorizer hook.");
3990 }else{
3991 rc = sqlite3_set_authorizer(ps->pDb, s3jni_xAuth, ps);
3993 if( rc ) S3JniHook_unref(pHook);
3995 S3JniDb_mutex_leave;
3996 return rc;
4000 S3JniApi(sqlite3_set_last_insert_rowid(),void,1set_1last_1insert_1rowid)(
4001 JniArgsEnvClass, jobject jpDb, jlong rowId
4003 sqlite3_set_last_insert_rowid(PtrGet_sqlite3(jpDb),
4004 (sqlite3_int64)rowId);
4007 S3JniApi(sqlite3_shutdown(),jint,1shutdown)(
4008 JniArgsEnvClass
4010 s3jni_reset_auto_extension(env);
4011 /* Free up S3JniDb recycling bin. */
4012 S3JniDb_mutex_enter; {
4013 while( S3JniGlobal.perDb.aFree ){
4014 S3JniDb * const d = S3JniGlobal.perDb.aFree;
4015 S3JniGlobal.perDb.aFree = d->pNext;
4016 S3JniDb_clear(env, d);
4017 sqlite3_free(d);
4019 } S3JniDb_mutex_leave;
4020 S3JniGlobal_mutex_enter; {
4021 /* Free up S3JniUdf recycling bin. */
4022 while( S3JniGlobal.udf.aFree ){
4023 S3JniUdf * const u = S3JniGlobal.udf.aFree;
4024 S3JniGlobal.udf.aFree = u->pNext;
4025 u->pNext = 0;
4026 S3JniUdf_free(env, u, 0);
4028 /* Free up S3JniHook recycling bin. */
4029 while( S3JniGlobal.hooks.aFree ){
4030 S3JniHook * const u = S3JniGlobal.hooks.aFree;
4031 S3JniGlobal.hooks.aFree = u->pNext;
4032 u->pNext = 0;
4033 assert( !u->doXDestroy );
4034 assert( !u->jObj );
4035 assert( !u->jExtra );
4036 sqlite3_free( u );
4038 } S3JniGlobal_mutex_leave;
4039 /* Free up env cache. */
4040 S3JniEnv_mutex_enter; {
4041 while( SJG.envCache.aHead ){
4042 S3JniEnv_uncache( SJG.envCache.aHead->env );
4044 } S3JniEnv_mutex_leave;
4045 #if 0
4047 ** Is automatically closing any still-open dbs a good idea? We will
4048 ** get rid of the perDb list once sqlite3 gets a per-db client
4049 ** state, at which point we won't have a central list of databases
4050 ** to close.
4052 S3JniDb_mutex_enter;
4053 while( SJG.perDb.pHead ){
4054 s3jni_close_db(env, SJG.perDb.pHead->jDb, 2);
4056 S3JniDb_mutex_leave;
4057 #endif
4058 /* Do not clear S3JniGlobal.jvm: it's legal to restart the lib. */
4059 return sqlite3_shutdown();
4062 S3JniApi(sqlite3_status(),jint,1status)(
4063 JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
4064 jboolean reset
4066 int iCur = 0, iHigh = 0;
4067 int rc = sqlite3_status( op, &iCur, &iHigh, reset );
4068 if( 0==rc ){
4069 OutputPointer_set_Int32(env, jOutCurrent, iCur);
4070 OutputPointer_set_Int32(env, jOutHigh, iHigh);
4072 return (jint)rc;
4075 S3JniApi(sqlite3_status64(),jint,1status64)(
4076 JniArgsEnvClass, jint op, jobject jOutCurrent, jobject jOutHigh,
4077 jboolean reset
4079 sqlite3_int64 iCur = 0, iHigh = 0;
4080 int rc = sqlite3_status64( op, &iCur, &iHigh, reset );
4081 if( 0==rc ){
4082 OutputPointer_set_Int64(env, jOutCurrent, iCur);
4083 OutputPointer_set_Int64(env, jOutHigh, iHigh);
4085 return (jint)rc;
4088 static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
4089 jbyteArray baG, jbyteArray baT, jint escLike){
4090 int rc = 0;
4091 jbyte * const pG = s3jni_jbytearray_bytes(baG);
4092 jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0;
4094 s3jni_oom_fatal(pT);
4095 /* Note that we're relying on the byte arrays having been
4096 NUL-terminated on the Java side. */
4097 rc = isLike
4098 ? sqlite3_strlike((const char *)pG, (const char *)pT,
4099 (unsigned int)escLike)
4100 : sqlite3_strglob((const char *)pG, (const char *)pT);
4101 s3jni_jbytearray_release(baG, pG);
4102 s3jni_jbytearray_release(baT, pT);
4103 return rc;
4106 S3JniApi(sqlite3_strglob(),jint,1strglob)(
4107 JniArgsEnvClass, jbyteArray baG, jbyteArray baT
4109 return s3jni_strlike_glob(0, env, baG, baT, 0);
4112 S3JniApi(sqlite3_strlike(),jint,1strlike)(
4113 JniArgsEnvClass, jbyteArray baG, jbyteArray baT, jint escChar
4115 return s3jni_strlike_glob(1, env, baG, baT, escChar);
4118 S3JniApi(sqlite3_sql(),jstring,1sql)(
4119 JniArgsEnvClass, jobject jpStmt
4121 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jpStmt);
4122 jstring rv = 0;
4123 if( pStmt ){
4124 const char * zSql = 0;
4125 zSql = sqlite3_sql(pStmt);
4126 rv = s3jni_utf8_to_jstring( zSql, -1);
4128 return rv;
4131 S3JniApi(sqlite3_step(),jint,1step)(
4132 JniArgsEnvClass,jobject jStmt
4134 int rc = SQLITE_MISUSE;
4135 sqlite3_stmt * const pStmt = PtrGet_sqlite3_stmt(jStmt);
4136 if( pStmt ){
4137 rc = sqlite3_step(pStmt);
4139 return rc;
4142 S3JniApi(sqlite3_table_column_metadata(),int,1table_1column_1metadata)(
4143 JniArgsEnvClass, jobject jDb, jstring jDbName, jstring jTableName,
4144 jstring jColumnName, jobject jDataType, jobject jCollSeq, jobject jNotNull,
4145 jobject jPrimaryKey, jobject jAutoinc
4147 sqlite3 * const db = PtrGet_sqlite3(jDb);
4148 char * zDbName = 0, * zTableName = 0, * zColumnName = 0;
4149 const char * pzCollSeq = 0;
4150 const char * pzDataType = 0;
4151 int pNotNull = 0, pPrimaryKey = 0, pAutoinc = 0;
4152 int rc;
4154 if( !db || !jDbName || !jTableName || !jColumnName ) return SQLITE_MISUSE;
4155 zDbName = s3jni_jstring_to_utf8(jDbName,0);
4156 zTableName = zDbName ? s3jni_jstring_to_utf8(jTableName,0) : 0;
4157 zColumnName = zTableName ? s3jni_jstring_to_utf8(jColumnName,0) : 0;
4158 rc = zColumnName
4159 ? sqlite3_table_column_metadata(db, zDbName, zTableName,
4160 zColumnName, &pzDataType, &pzCollSeq,
4161 &pNotNull, &pPrimaryKey, &pAutoinc)
4162 : SQLITE_NOMEM;
4163 if( 0==rc ){
4164 jstring jseq = jCollSeq
4165 ? (pzCollSeq ? s3jni_utf8_to_jstring(pzCollSeq, -1) : 0)
4166 : 0;
4167 jstring jdtype = jDataType
4168 ? (pzDataType ? s3jni_utf8_to_jstring(pzDataType, -1) : 0)
4169 : 0;
4170 if( (jCollSeq && pzCollSeq && !jseq)
4171 || (jDataType && pzDataType && !jdtype) ){
4172 rc = SQLITE_NOMEM;
4173 }else{
4174 if( jNotNull ) OutputPointer_set_Bool(env, jNotNull, pNotNull);
4175 if( jPrimaryKey ) OutputPointer_set_Bool(env, jPrimaryKey, pPrimaryKey);
4176 if( jAutoinc ) OutputPointer_set_Bool(env, jAutoinc, pAutoinc);
4177 if( jCollSeq ) OutputPointer_set_String(env, jCollSeq, jseq);
4178 if( jDataType ) OutputPointer_set_String(env, jDataType, jdtype);
4180 S3JniUnrefLocal(jseq);
4181 S3JniUnrefLocal(jdtype);
4183 sqlite3_free(zDbName);
4184 sqlite3_free(zTableName);
4185 sqlite3_free(zColumnName);
4186 return rc;
4189 static int s3jni_trace_impl(unsigned traceflag, void *pC, void *pP, void *pX){
4190 S3JniDb * const ps = (S3JniDb *)pC;
4191 S3JniDeclLocal_env;
4192 jobject jX = NULL /* the tracer's X arg */;
4193 jobject jP = NULL /* the tracer's P arg */;
4194 jobject jPUnref = NULL /* potentially a local ref to jP */;
4195 int rc = 0;
4196 S3JniHook hook;
4198 S3JniHook_localdup(&ps->hooks.trace, &hook );
4199 if( !hook.jObj ){
4200 return 0;
4202 switch( traceflag ){
4203 case SQLITE_TRACE_STMT:
4204 jX = s3jni_utf8_to_jstring( (const char *)pX, -1);
4205 if( !jX ) rc = SQLITE_NOMEM;
4206 break;
4207 case SQLITE_TRACE_PROFILE:
4208 jX = (*env)->NewObject(env, SJG.g.cLong, SJG.g.ctorLong1,
4209 (jlong)*((sqlite3_int64*)pX));
4210 // hmm. ^^^ (*pX) really is zero.
4211 // MARKER(("profile time = %llu\n", *((sqlite3_int64*)pX)));
4212 s3jni_oom_check( jX );
4213 if( !jX ) rc = SQLITE_NOMEM;
4214 break;
4215 case SQLITE_TRACE_ROW:
4216 break;
4217 case SQLITE_TRACE_CLOSE:
4218 jP = ps->jDb;
4219 break;
4220 default:
4221 assert(!"cannot happen - unkown trace flag");
4222 rc = SQLITE_ERROR;
4224 if( 0==rc ){
4225 if( !jP ){
4226 /* Create a new temporary sqlite3_stmt wrapper */
4227 jP = jPUnref = new_sqlite3_stmt_wrapper(env, pP);
4228 if( !jP ){
4229 rc = SQLITE_NOMEM;
4232 if( 0==rc ){
4233 assert(jP);
4234 rc = (int)(*env)->CallIntMethod(env, hook.jObj, hook.midCallback,
4235 (jint)traceflag, jP, jX);
4236 S3JniIfThrew{
4237 rc = s3jni_db_exception(ps, SQLITE_ERROR,
4238 "sqlite3_trace_v2() callback threw.");
4242 S3JniUnrefLocal(jPUnref);
4243 S3JniUnrefLocal(jX);
4244 S3JniHook_localundup(hook);
4245 return rc;
4248 S3JniApi(sqlite3_trace_v2(),jint,1trace_1v2)(
4249 JniArgsEnvClass,jobject jDb, jint traceMask, jobject jTracer
4251 S3JniDb * const ps = S3JniDb_from_java(jDb);
4252 int rc;
4254 if( !ps ) return SQLITE_MISUSE;
4255 if( !traceMask || !jTracer ){
4256 S3JniDb_mutex_enter;
4257 rc = (jint)sqlite3_trace_v2(ps->pDb, 0, 0, 0);
4258 S3JniHook_unref(&ps->hooks.trace);
4259 S3JniDb_mutex_leave;
4260 }else{
4261 jclass const klazz = (*env)->GetObjectClass(env, jTracer);
4262 S3JniHook hook = S3JniHook_empty;
4263 hook.midCallback = (*env)->GetMethodID(
4264 env, klazz, "call", "(ILjava/lang/Object;Ljava/lang/Object;)I"
4266 S3JniUnrefLocal(klazz);
4267 S3JniIfThrew {
4268 S3JniExceptionClear;
4269 rc = s3jni_db_error(ps->pDb, SQLITE_ERROR,
4270 "Cannot not find matching call() on "
4271 "TracerCallback object.");
4272 }else{
4273 hook.jObj = S3JniRefGlobal(jTracer);
4274 S3JniDb_mutex_enter;
4275 rc = sqlite3_trace_v2(ps->pDb, (unsigned)traceMask, s3jni_trace_impl, ps);
4276 if( 0==rc ){
4277 S3JniHook_unref(&ps->hooks.trace);
4278 ps->hooks.trace = hook /* transfer ownership of reference */;
4279 }else{
4280 S3JniHook_unref(&hook);
4282 S3JniDb_mutex_leave;
4285 return rc;
4288 S3JniApi(sqlite3_txn_state(),jint,1txn_1state)(
4289 JniArgsEnvClass,jobject jDb, jstring jSchema
4291 sqlite3 * const pDb = PtrGet_sqlite3(jDb);
4292 int rc = SQLITE_MISUSE;
4293 if( pDb ){
4294 char * zSchema = jSchema
4295 ? s3jni_jstring_to_utf8(jSchema, 0)
4296 : 0;
4297 if( !jSchema || (zSchema && jSchema) ){
4298 rc = sqlite3_txn_state(pDb, zSchema);
4299 sqlite3_free(zSchema);
4300 }else{
4301 rc = SQLITE_NOMEM;
4304 return rc;
4307 S3JniApi(sqlite3_update_hook(),jobject,1update_1hook)(
4308 JniArgsEnvClass, jobject jDb, jobject jHook
4310 return s3jni_updatepre_hook(env, 0, jDb, jHook);
4314 S3JniApi(sqlite3_value_blob(),jbyteArray,1value_1blob)(
4315 JniArgsEnvClass, jobject jpSVal
4317 sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal);
4318 int const nLen = sqlite3_value_bytes(sv);
4319 const jbyte * pBytes = sqlite3_value_blob(sv);
4321 s3jni_oom_check( nLen ? !!pBytes : 1 );
4322 return pBytes
4323 ? s3jni_new_jbyteArray(pBytes, nLen)
4324 : NULL;
4328 S3JniApi(sqlite3_value_double(),jdouble,1value_1double)(
4329 JniArgsEnvClass, jobject jpSVal
4331 return (jdouble) sqlite3_value_double(PtrGet_sqlite3_value(jpSVal));
4335 S3JniApi(sqlite3_value_dup(),jobject,1value_1dup)(
4336 JniArgsEnvClass, jobject jpSVal
4338 sqlite3_value * const sv = sqlite3_value_dup(PtrGet_sqlite3_value(jpSVal));
4339 return sv ? new_sqlite3_value_wrapper(env, sv) : 0;
4342 S3JniApi(sqlite3_value_free(),void,1value_1free)(
4343 JniArgsEnvClass, jobject jpSVal
4345 sqlite3_value_free(PtrGet_sqlite3_value(jpSVal));
4348 S3JniApi(sqlite3_value_int(),jint,1value_1int)(
4349 JniArgsEnvClass, jobject jpSVal
4351 return (jint) sqlite3_value_int(PtrGet_sqlite3_value(jpSVal));
4354 S3JniApi(sqlite3_value_int64(),jlong,1value_1int64)(
4355 JniArgsEnvClass, jobject jpSVal
4357 return (jlong) sqlite3_value_int64(PtrGet_sqlite3_value(jpSVal));
4360 S3JniApi(sqlite3_value_java_object(),jobject,1value_1java_1object)(
4361 JniArgsEnvClass, jobject jpSVal
4363 return sqlite3_value_pointer(PtrGet_sqlite3_value(jpSVal),
4364 ResultJavaValuePtrStr);
4367 S3JniApi(sqlite3_value_text_utf8(),jbyteArray,1value_1text_1utf8)(
4368 JniArgsEnvClass, jobject jpSVal
4370 sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal);
4371 int const n = sqlite3_value_bytes(sv);
4372 const unsigned char * const p = sqlite3_value_text(sv);
4373 return p ? s3jni_new_jbyteArray(p, n) : 0;
4376 S3JniApi(sqlite3_value_text(),jstring,1value_1text)(
4377 JniArgsEnvClass, jobject jpSVal
4379 sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal);
4380 int const n = sqlite3_value_bytes(sv);
4381 const unsigned char * const p = sqlite3_value_text(sv);
4382 return p ? s3jni_utf8_to_jstring( (const char *)p, n) : 0;
4385 S3JniApi(sqlite3_value_text16(),jstring,1value_1text16)(
4386 JniArgsEnvClass, jobject jpSVal
4388 sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal);
4389 const int n = sqlite3_value_bytes16(sv);
4390 const void * const p = sqlite3_value_text16(sv);
4391 return s3jni_text16_to_jstring(env, p, n);
4394 JniDecl(void,1jni_1internal_1details)(JniArgsEnvClass){
4395 MARKER(("\nVarious bits of internal info:\n"));
4396 puts("FTS5 is "
4397 #ifdef SQLITE_ENABLE_FTS5
4398 "available"
4399 #else
4400 "unavailable"
4401 #endif
4404 puts("sizeofs:");
4405 #define SO(T) printf("\tsizeof(" #T ") = %u\n", (unsigned)sizeof(T))
4406 SO(void*);
4407 SO(jmethodID);
4408 SO(jfieldID);
4409 SO(S3JniEnv);
4410 SO(S3JniHook);
4411 SO(S3JniDb);
4412 SO(S3JniNphRefs);
4413 printf("\t(^^^ %u NativePointerHolder/OutputPointer.T types)\n",
4414 (unsigned)S3Jni_NphCache_size);
4415 SO(S3JniGlobal);
4416 SO(S3JniAutoExtension);
4417 SO(S3JniUdf);
4418 #undef SO
4419 #ifdef SQLITE_JNI_ENABLE_METRICS
4420 printf("Cache info:\n");
4421 printf("\tJNIEnv cache: %u allocs, %u misses, %u hits\n",
4422 SJG.metrics.nEnvAlloc, SJG.metrics.nEnvMiss,
4423 SJG.metrics.nEnvHit);
4424 printf("Mutex entry:"
4425 "\n\tglobal = %u"
4426 "\n\tenv = %u"
4427 "\n\tnph = %u for S3JniNphClass init"
4428 "\n\tperDb = %u"
4429 "\n\tautoExt list = %u"
4430 "\n\tS3JniUdf = %u (free-list)"
4431 "\n\tmetrics = %u\n",
4432 SJG.metrics.nMutexGlobal, SJG.metrics.nMutexEnv,
4433 SJG.metrics.nMutexEnv2, SJG.metrics.nMutexPerDb,
4434 SJG.metrics.nMutexAutoExt, SJG.metrics.nMutexUdf,
4435 SJG.metrics.nMetrics);
4436 puts("Allocs:");
4437 printf("\tS3JniDb: %u alloced (*%u = %u bytes), %u recycled\n",
4438 SJG.metrics.nPdbAlloc, (unsigned) sizeof(S3JniDb),
4439 (unsigned)(SJG.metrics.nPdbAlloc * sizeof(S3JniDb)),
4440 SJG.metrics.nPdbRecycled);
4441 printf("\tS3JniUdf: %u alloced (*%u = %u bytes), %u recycled\n",
4442 SJG.metrics.nUdfAlloc, (unsigned) sizeof(S3JniUdf),
4443 (unsigned)(SJG.metrics.nUdfAlloc * sizeof(S3JniUdf)),
4444 SJG.metrics.nUdfRecycled);
4445 printf("\tS3JniHook: %u alloced (*%u = %u bytes), %u recycled\n",
4446 SJG.metrics.nHookAlloc, (unsigned) sizeof(S3JniHook),
4447 (unsigned)(SJG.metrics.nHookAlloc * sizeof(S3JniHook)),
4448 SJG.metrics.nHookRecycled);
4449 printf("\tS3JniEnv: %u alloced (*%u = %u bytes)\n",
4450 SJG.metrics.nEnvAlloc, (unsigned) sizeof(S3JniEnv),
4451 (unsigned)(SJG.metrics.nEnvAlloc * sizeof(S3JniEnv)));
4452 puts("Java-side UDF calls:");
4453 #define UDF(T) printf("\t%-8s = %u\n", "x" #T, SJG.metrics.udf.n##T)
4454 UDF(Func); UDF(Step); UDF(Final); UDF(Value); UDF(Inverse);
4455 #undef UDF
4456 printf("xDestroy calls across all callback types: %u\n",
4457 SJG.metrics.nDestroy);
4458 #else
4459 puts("Built without SQLITE_JNI_ENABLE_METRICS.");
4460 #endif
4463 ////////////////////////////////////////////////////////////////////////
4464 // End of the sqlite3_... API bindings. Next up, FTS5...
4465 ////////////////////////////////////////////////////////////////////////
4466 #ifdef SQLITE_ENABLE_FTS5
4468 /* Creates a verbose JNI Fts5 function name. */
4469 #define JniFuncNameFtsXA(Suffix) \
4470 Java_org_sqlite_jni_Fts5ExtensionApi_ ## Suffix
4471 #define JniFuncNameFtsApi(Suffix) \
4472 Java_org_sqlite_jni_fts5_1api_ ## Suffix
4473 #define JniFuncNameFtsTok(Suffix) \
4474 Java_org_sqlite_jni_fts5_tokenizer_ ## Suffix
4476 #define JniDeclFtsXA(ReturnType,Suffix) \
4477 JNIEXPORT ReturnType JNICALL \
4478 JniFuncNameFtsXA(Suffix)
4479 #define JniDeclFtsApi(ReturnType,Suffix) \
4480 JNIEXPORT ReturnType JNICALL \
4481 JniFuncNameFtsApi(Suffix)
4482 #define JniDeclFtsTok(ReturnType,Suffix) \
4483 JNIEXPORT ReturnType JNICALL \
4484 JniFuncNameFtsTok(Suffix)
4486 #define PtrGet_fts5_api(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.fts5_api)
4487 #define PtrGet_fts5_tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.fts5_tokenizer)
4488 #define PtrGet_Fts5Context(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.Fts5Context)
4489 #define PtrGet_Fts5Tokenizer(OBJ) NativePointerHolder_get(OBJ,&S3JniNphRefs.Fts5Tokenizer)
4490 #define Fts5ExtDecl Fts5ExtensionApi const * const fext = s3jni_ftsext()
4493 State for binding Java-side FTS5 auxiliary functions.
4495 typedef struct {
4496 jobject jObj /* functor instance */;
4497 jobject jUserData /* 2nd arg to JNI binding of
4498 xCreateFunction(), ostensibly the 3rd arg
4499 to the lib-level xCreateFunction(), except
4500 that we necessarily use that slot for a
4501 Fts5JniAux instance. */;
4502 char * zFuncName /* Only for error reporting and debug logging */;
4503 jmethodID jmid /* callback member's method ID */;
4504 } Fts5JniAux;
4506 static void Fts5JniAux_free(Fts5JniAux * const s){
4507 S3JniDeclLocal_env;
4508 if( env ){
4509 /*MARKER(("FTS5 aux function cleanup: %s\n", s->zFuncName));*/
4510 s3jni_call_xDestroy(s->jObj);
4511 S3JniUnrefGlobal(s->jObj);
4512 S3JniUnrefGlobal(s->jUserData);
4514 sqlite3_free(s->zFuncName);
4515 sqlite3_free(s);
4518 static void Fts5JniAux_xDestroy(void *p){
4519 if( p ) Fts5JniAux_free(p);
4522 static Fts5JniAux * Fts5JniAux_alloc(JNIEnv * const env, jobject jObj){
4523 Fts5JniAux * s = s3jni_malloc( sizeof(Fts5JniAux));
4525 if( s ){
4526 jclass klazz;
4527 memset(s, 0, sizeof(Fts5JniAux));
4528 s->jObj = S3JniRefGlobal(jObj);
4529 klazz = (*env)->GetObjectClass(env, jObj);
4530 s->jmid = (*env)->GetMethodID(env, klazz, "xFunction",
4531 "(Lorg/sqlite/jni/Fts5ExtensionApi;"
4532 "Lorg/sqlite/jni/Fts5Context;"
4533 "Lorg/sqlite/jni/sqlite3_context;"
4534 "[Lorg/sqlite/jni/sqlite3_value;)V");
4535 S3JniUnrefLocal(klazz);
4536 S3JniIfThrew{
4537 S3JniExceptionReport;
4538 S3JniExceptionClear;
4539 Fts5JniAux_free(s);
4540 s = 0;
4543 return s;
4546 static inline Fts5ExtensionApi const * s3jni_ftsext(void){
4547 return &sFts5Api/*singleton from sqlite3.c*/;
4550 static inline jobject new_Fts5Context_wrapper(JNIEnv * const env, Fts5Context *sv){
4551 return new_NativePointerHolder_object(env, &S3JniNphRefs.Fts5Context, sv);
4553 static inline jobject new_fts5_api_wrapper(JNIEnv * const env, fts5_api *sv){
4554 return new_NativePointerHolder_object(env, &S3JniNphRefs.fts5_api, sv);
4558 ** Returns a per-JNIEnv global ref to the Fts5ExtensionApi singleton
4559 ** instance, or NULL on OOM.
4561 static jobject s3jni_getFts5ExensionApi(JNIEnv * const env){
4562 if( !SJG.fts5.jFtsExt ){
4563 jobject pNPH = new_NativePointerHolder_object(
4564 env, &S3JniNphRefs.Fts5ExtensionApi, s3jni_ftsext()
4566 S3JniEnv_mutex_enter;
4567 if( pNPH ){
4568 if( !SJG.fts5.jFtsExt ){
4569 SJG.fts5.jFtsExt = S3JniRefGlobal(pNPH);
4571 S3JniUnrefLocal(pNPH);
4573 S3JniEnv_mutex_leave;
4575 return SJG.fts5.jFtsExt;
4579 ** Returns a pointer to the fts5_api instance for database connection
4580 ** db. If an error occurs, returns NULL and leaves an error in the
4581 ** database handle (accessible using sqlite3_errcode()/errmsg()).
4583 static fts5_api *s3jni_fts5_api_from_db(sqlite3 *db){
4584 fts5_api *pRet = 0;
4585 sqlite3_stmt *pStmt = 0;
4586 if( SQLITE_OK==sqlite3_prepare(db, "SELECT fts5(?1)", -1, &pStmt, 0) ){
4587 sqlite3_bind_pointer(pStmt, 1, (void*)&pRet, "fts5_api_ptr", NULL);
4588 sqlite3_step(pStmt);
4590 sqlite3_finalize(pStmt);
4591 return pRet;
4594 JniDeclFtsApi(jobject,getInstanceForDb)(JniArgsEnvClass,jobject jDb){
4595 S3JniDb * const ps = S3JniDb_from_java(jDb);
4596 jobject rv = 0;
4597 if( !ps ) return 0;
4598 else if( ps->jFtsApi ){
4599 rv = ps->jFtsApi;
4600 }else{
4601 fts5_api * const pApi = s3jni_fts5_api_from_db(ps->pDb);
4602 if( pApi ){
4603 rv = new_fts5_api_wrapper(env, pApi);
4604 ps->jFtsApi = rv ? S3JniRefGlobal(rv) : 0;
4607 return rv;
4611 JniDeclFtsXA(jobject,getInstance)(JniArgsEnvClass){
4612 return s3jni_getFts5ExensionApi(env);
4615 JniDeclFtsXA(jint,xColumnCount)(JniArgsEnvObj,jobject jCtx){
4616 Fts5ExtDecl;
4617 return (jint)fext->xColumnCount(PtrGet_Fts5Context(jCtx));
4620 JniDeclFtsXA(jint,xColumnSize)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOut32){
4621 Fts5ExtDecl;
4622 int n1 = 0;
4623 int const rc = fext->xColumnSize(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1);
4624 if( 0==rc ) OutputPointer_set_Int32(env, jOut32, n1);
4625 return rc;
4628 JniDeclFtsXA(jint,xColumnText)(JniArgsEnvObj,jobject jCtx, jint iCol,
4629 jobject jOut){
4630 Fts5ExtDecl;
4631 const char *pz = 0;
4632 int pn = 0;
4633 int rc = fext->xColumnText(PtrGet_Fts5Context(jCtx), (int)iCol,
4634 &pz, &pn);
4635 if( 0==rc ){
4636 jstring jstr = pz ? s3jni_utf8_to_jstring( pz, pn) : 0;
4637 if( pz ){
4638 if( jstr ){
4639 OutputPointer_set_String(env, jOut, jstr);
4640 S3JniUnrefLocal(jstr)/*jOut has a reference*/;
4641 }else{
4642 rc = SQLITE_NOMEM;
4646 return (jint)rc;
4649 JniDeclFtsXA(jint,xColumnTotalSize)(JniArgsEnvObj,jobject jCtx, jint iCol, jobject jOut64){
4650 Fts5ExtDecl;
4651 sqlite3_int64 nOut = 0;
4652 int const rc = fext->xColumnTotalSize(PtrGet_Fts5Context(jCtx), (int)iCol, &nOut);
4653 if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
4654 return (jint)rc;
4658 ** Proxy for fts5_extension_function instances plugged in via
4659 ** fts5_api::xCreateFunction().
4661 static void s3jni_fts5_extension_function(Fts5ExtensionApi const *pApi,
4662 Fts5Context *pFts,
4663 sqlite3_context *pCx,
4664 int argc,
4665 sqlite3_value **argv){
4666 Fts5JniAux * const pAux = pApi->xUserData(pFts);
4667 jobject jpCx = 0;
4668 jobjectArray jArgv = 0;
4669 jobject jpFts = 0;
4670 jobject jFXA;
4671 int rc;
4672 S3JniDeclLocal_env;
4674 assert(pAux);
4675 jFXA = s3jni_getFts5ExensionApi(env);
4676 if( !jFXA ) goto error_oom;
4677 jpFts = new_Fts5Context_wrapper(env, pFts);
4678 if( !jpFts ) goto error_oom;
4679 rc = udf_args(env, pCx, argc, argv, &jpCx, &jArgv);
4680 if( rc ) goto error_oom;
4681 (*env)->CallVoidMethod(env, pAux->jObj, pAux->jmid,
4682 jFXA, jpFts, jpCx, jArgv);
4683 S3JniIfThrew{
4684 udf_report_exception(env, 1, pCx, pAux->zFuncName, "xFunction");
4686 S3JniUnrefLocal(jpFts);
4687 S3JniUnrefLocal(jpCx);
4688 S3JniUnrefLocal(jArgv);
4689 return;
4690 error_oom:
4691 assert( !jArgv );
4692 assert( !jpCx );
4693 S3JniUnrefLocal(jpFts);
4694 sqlite3_result_error_nomem(pCx);
4695 return;
4698 JniDeclFtsApi(jint,xCreateFunction)(JniArgsEnvObj, jstring jName,
4699 jobject jUserData, jobject jFunc){
4700 fts5_api * const pApi = PtrGet_fts5_api(jSelf);
4701 int rc;
4702 char * zName;
4703 Fts5JniAux * pAux;
4705 assert(pApi);
4706 zName = s3jni_jstring_to_utf8( jName, 0);
4707 if(!zName) return SQLITE_NOMEM;
4708 pAux = Fts5JniAux_alloc(env, jFunc);
4709 if( pAux ){
4710 rc = pApi->xCreateFunction(pApi, zName, pAux,
4711 s3jni_fts5_extension_function,
4712 Fts5JniAux_xDestroy);
4713 }else{
4714 rc = SQLITE_NOMEM;
4716 if( 0==rc ){
4717 pAux->jUserData = jUserData ? S3JniRefGlobal(jUserData) : 0;
4718 pAux->zFuncName = zName;
4719 }else{
4720 sqlite3_free(zName);
4722 return (jint)rc;
4726 typedef struct S3JniFts5AuxData S3JniFts5AuxData;
4728 ** TODO: this middle-man struct is no longer necessary. Conider
4729 ** removing it and passing around jObj itself instead.
4731 struct S3JniFts5AuxData {
4732 jobject jObj;
4735 static void S3JniFts5AuxData_xDestroy(void *x){
4736 if( x ){
4737 S3JniFts5AuxData * const p = x;
4738 if( p->jObj ){
4739 S3JniDeclLocal_env;
4740 s3jni_call_xDestroy(p->jObj);
4741 S3JniUnrefGlobal(p->jObj);
4743 sqlite3_free(x);
4747 JniDeclFtsXA(jobject,xGetAuxdata)(JniArgsEnvObj,jobject jCtx, jboolean bClear){
4748 Fts5ExtDecl;
4749 jobject rv = 0;
4750 S3JniFts5AuxData * const pAux = fext->xGetAuxdata(PtrGet_Fts5Context(jCtx), bClear);
4751 if( pAux ){
4752 if( bClear ){
4753 if( pAux->jObj ){
4754 rv = S3JniRefLocal(pAux->jObj);
4755 S3JniUnrefGlobal(pAux->jObj);
4757 /* Note that we do not call xDestroy() in this case. */
4758 sqlite3_free(pAux);
4759 }else{
4760 rv = pAux->jObj;
4763 return rv;
4766 JniDeclFtsXA(jint,xInst)(JniArgsEnvObj,jobject jCtx, jint iIdx, jobject jOutPhrase,
4767 jobject jOutCol, jobject jOutOff){
4768 Fts5ExtDecl;
4769 int n1 = 0, n2 = 2, n3 = 0;
4770 int const rc = fext->xInst(PtrGet_Fts5Context(jCtx), (int)iIdx, &n1, &n2, &n3);
4771 if( 0==rc ){
4772 OutputPointer_set_Int32(env, jOutPhrase, n1);
4773 OutputPointer_set_Int32(env, jOutCol, n2);
4774 OutputPointer_set_Int32(env, jOutOff, n3);
4776 return rc;
4779 JniDeclFtsXA(jint,xInstCount)(JniArgsEnvObj,jobject jCtx, jobject jOut32){
4780 Fts5ExtDecl;
4781 int nOut = 0;
4782 int const rc = fext->xInstCount(PtrGet_Fts5Context(jCtx), &nOut);
4783 if( 0==rc && jOut32 ) OutputPointer_set_Int32(env, jOut32, nOut);
4784 return (jint)rc;
4787 JniDeclFtsXA(jint,xPhraseCount)(JniArgsEnvObj,jobject jCtx){
4788 Fts5ExtDecl;
4789 return (jint)fext->xPhraseCount(PtrGet_Fts5Context(jCtx));
4792 /* Copy the 'a' and 'b' fields from pSrc to Fts5PhraseIter object jIter. */
4793 static void s3jni_phraseIter_NToJ(JNIEnv *const env,
4794 Fts5PhraseIter const * const pSrc,
4795 jobject jIter){
4796 S3JniGlobalType * const g = &S3JniGlobal;
4797 assert(g->fts5.jPhraseIter.fidA);
4798 (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidA, (jlong)pSrc->a);
4799 S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.a field.");
4800 (*env)->SetLongField(env, jIter, g->fts5.jPhraseIter.fidB, (jlong)pSrc->b);
4801 S3JniExceptionIsFatal("Cannot set Fts5PhraseIter.b field.");
4804 /* Copy the 'a' and 'b' fields from Fts5PhraseIter object jIter to pDest. */
4805 static void s3jni_phraseIter_JToN(JNIEnv *const env, jobject jIter,
4806 Fts5PhraseIter * const pDest){
4807 S3JniGlobalType * const g = &S3JniGlobal;
4808 assert(g->fts5.jPhraseIter.fidA);
4809 pDest->a =
4810 (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidA);
4811 S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field.");
4812 pDest->b =
4813 (const unsigned char *)(*env)->GetLongField(env, jIter, g->fts5.jPhraseIter.fidB);
4814 S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field.");
4817 JniDeclFtsXA(jint,xPhraseFirst)(JniArgsEnvObj,jobject jCtx, jint iPhrase,
4818 jobject jIter, jobject jOutCol,
4819 jobject jOutOff){
4820 Fts5ExtDecl;
4821 Fts5PhraseIter iter;
4822 int rc, iCol = 0, iOff = 0;
4823 rc = fext->xPhraseFirst(PtrGet_Fts5Context(jCtx), (int)iPhrase,
4824 &iter, &iCol, &iOff);
4825 if( 0==rc ){
4826 OutputPointer_set_Int32(env, jOutCol, iCol);
4827 OutputPointer_set_Int32(env, jOutOff, iOff);
4828 s3jni_phraseIter_NToJ(env, &iter, jIter);
4830 return rc;
4833 JniDeclFtsXA(jint,xPhraseFirstColumn)(JniArgsEnvObj,jobject jCtx, jint iPhrase,
4834 jobject jIter, jobject jOutCol){
4835 Fts5ExtDecl;
4836 Fts5PhraseIter iter;
4837 int rc, iCol = 0;
4838 rc = fext->xPhraseFirstColumn(PtrGet_Fts5Context(jCtx), (int)iPhrase,
4839 &iter, &iCol);
4840 if( 0==rc ){
4841 OutputPointer_set_Int32(env, jOutCol, iCol);
4842 s3jni_phraseIter_NToJ(env, &iter, jIter);
4844 return rc;
4847 JniDeclFtsXA(void,xPhraseNext)(JniArgsEnvObj,jobject jCtx, jobject jIter,
4848 jobject jOutCol, jobject jOutOff){
4849 Fts5ExtDecl;
4850 Fts5PhraseIter iter;
4851 int iCol = 0, iOff = 0;
4852 s3jni_phraseIter_JToN(env, jIter, &iter);
4853 fext->xPhraseNext(PtrGet_Fts5Context(jCtx), &iter, &iCol, &iOff);
4854 OutputPointer_set_Int32(env, jOutCol, iCol);
4855 OutputPointer_set_Int32(env, jOutOff, iOff);
4856 s3jni_phraseIter_NToJ(env, &iter, jIter);
4859 JniDeclFtsXA(void,xPhraseNextColumn)(JniArgsEnvObj,jobject jCtx, jobject jIter,
4860 jobject jOutCol){
4861 Fts5ExtDecl;
4862 Fts5PhraseIter iter;
4863 int iCol = 0;
4864 s3jni_phraseIter_JToN(env, jIter, &iter);
4865 fext->xPhraseNextColumn(PtrGet_Fts5Context(jCtx), &iter, &iCol);
4866 OutputPointer_set_Int32(env, jOutCol, iCol);
4867 s3jni_phraseIter_NToJ(env, &iter, jIter);
4871 JniDeclFtsXA(jint,xPhraseSize)(JniArgsEnvObj,jobject jCtx, jint iPhrase){
4872 Fts5ExtDecl;
4873 return (jint)fext->xPhraseSize(PtrGet_Fts5Context(jCtx), (int)iPhrase);
4876 /* State for use with xQueryPhrase() and xTokenize(). */
4877 struct s3jni_xQueryPhraseState {
4878 Fts5ExtensionApi const * fext;
4879 S3JniEnv const * jc;
4880 jmethodID midCallback;
4881 jobject jCallback;
4882 jobject jFcx;
4883 /* State for xTokenize() */
4884 struct {
4885 const char * zPrev;
4886 int nPrev;
4887 jbyteArray jba;
4888 } tok;
4891 static int s3jni_xQueryPhrase(const Fts5ExtensionApi *xapi,
4892 Fts5Context * pFcx, void *pData){
4893 /* TODO: confirm that the Fts5Context passed to this function is
4894 guaranteed to be the same one passed to xQueryPhrase(). If it's
4895 not, we'll have to create a new wrapper object on every call. */
4896 struct s3jni_xQueryPhraseState const * s = pData;
4897 S3JniDeclLocal_env;
4898 int rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback,
4899 SJG.fts5.jFtsExt, s->jFcx);
4900 S3JniIfThrew{
4901 S3JniExceptionWarnCallbackThrew("xQueryPhrase() callback");
4902 S3JniExceptionClear;
4903 rc = SQLITE_ERROR;
4905 return rc;
4908 JniDeclFtsXA(jint,xQueryPhrase)(JniArgsEnvObj,jobject jFcx, jint iPhrase,
4909 jobject jCallback){
4910 Fts5ExtDecl;
4911 S3JniEnv * const jc = S3JniEnv_get();
4912 struct s3jni_xQueryPhraseState s;
4913 jclass klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
4915 if( !klazz ) return SQLITE_MISUSE;
4916 s.jc = jc;
4917 s.jCallback = jCallback;
4918 s.jFcx = jFcx;
4919 s.fext = fext;
4920 s.midCallback = (*env)->GetMethodID(env, klazz, "xCallback",
4921 "(Lorg.sqlite.jni.Fts5ExtensionApi;"
4922 "Lorg.sqlite.jni.Fts5Context;)I");
4923 S3JniUnrefLocal(klazz);
4924 S3JniExceptionIsFatal("Could not extract xQueryPhraseCallback.xCallback method.");
4925 return (jint)fext->xQueryPhrase(PtrGet_Fts5Context(jFcx), iPhrase, &s,
4926 s3jni_xQueryPhrase);
4930 JniDeclFtsXA(jint,xRowCount)(JniArgsEnvObj,jobject jCtx, jobject jOut64){
4931 Fts5ExtDecl;
4932 sqlite3_int64 nOut = 0;
4933 int const rc = fext->xRowCount(PtrGet_Fts5Context(jCtx), &nOut);
4934 if( 0==rc && jOut64 ) OutputPointer_set_Int64(env, jOut64, (jlong)nOut);
4935 return (jint)rc;
4938 JniDeclFtsXA(jlong,xRowid)(JniArgsEnvObj,jobject jCtx){
4939 Fts5ExtDecl;
4940 return (jlong)fext->xRowid(PtrGet_Fts5Context(jCtx));
4943 JniDeclFtsXA(int,xSetAuxdata)(JniArgsEnvObj,jobject jCtx, jobject jAux){
4944 Fts5ExtDecl;
4945 int rc;
4946 S3JniFts5AuxData * pAux;
4948 pAux = s3jni_malloc( sizeof(*pAux));
4949 if( !pAux ){
4950 if( jAux ){
4951 /* Emulate how xSetAuxdata() behaves when it cannot alloc
4952 ** its auxdata wrapper. */
4953 s3jni_call_xDestroy(jAux);
4955 return SQLITE_NOMEM;
4957 pAux->jObj = S3JniRefGlobal(jAux);
4958 rc = fext->xSetAuxdata(PtrGet_Fts5Context(jCtx), pAux,
4959 S3JniFts5AuxData_xDestroy);
4960 return rc;
4963 /* xToken() impl for xTokenize(). */
4964 static int s3jni_xTokenize_xToken(void *p, int tFlags, const char* z,
4965 int nZ, int iStart, int iEnd){
4966 int rc;
4967 S3JniDeclLocal_env;
4968 struct s3jni_xQueryPhraseState * const s = p;
4969 jbyteArray jba;
4971 if( s->tok.zPrev == z && s->tok.nPrev == nZ ){
4972 jba = s->tok.jba;
4973 }else{
4974 S3JniUnrefLocal(s->tok.jba);
4975 s->tok.zPrev = z;
4976 s->tok.nPrev = nZ;
4977 s->tok.jba = s3jni_new_jbyteArray(z, nZ);
4978 if( !s->tok.jba ) return SQLITE_NOMEM;
4979 jba = s->tok.jba;
4981 rc = (int)(*env)->CallIntMethod(env, s->jCallback, s->midCallback,
4982 (jint)tFlags, jba, (jint)iStart,
4983 (jint)iEnd);
4984 return rc;
4988 ** Proxy for Fts5ExtensionApi.xTokenize() and
4989 ** fts5_tokenizer.xTokenize()
4991 static jint s3jni_fts5_xTokenize(JniArgsEnvObj, S3JniNphRef const *pRef,
4992 jint tokFlags, jobject jFcx,
4993 jbyteArray jbaText, jobject jCallback){
4994 Fts5ExtDecl;
4995 S3JniEnv * const jc = S3JniEnv_get();
4996 struct s3jni_xQueryPhraseState s;
4997 int rc = 0;
4998 jbyte * const pText = jCallback ? s3jni_jbytearray_bytes(jbaText) : 0;
4999 jsize nText = pText ? (*env)->GetArrayLength(env, jbaText) : 0;
5000 jclass const klazz = jCallback ? (*env)->GetObjectClass(env, jCallback) : NULL;
5002 if( !klazz ) return SQLITE_MISUSE;
5003 memset(&s, 0, sizeof(s));
5004 s.jc = jc;
5005 s.jCallback = jCallback;
5006 s.jFcx = jFcx;
5007 s.fext = fext;
5008 s.midCallback = (*env)->GetMethodID(env, klazz, "call", "(I[BII)I");
5009 S3JniUnrefLocal(klazz);
5010 S3JniIfThrew {
5011 S3JniExceptionReport;
5012 S3JniExceptionClear;
5013 s3jni_jbytearray_release(jbaText, pText);
5014 return SQLITE_ERROR;
5016 s.tok.jba = S3JniRefLocal(jbaText);
5017 s.tok.zPrev = (const char *)pText;
5018 s.tok.nPrev = (int)nText;
5019 if( pRef == &S3JniNphRefs.Fts5ExtensionApi ){
5020 rc = fext->xTokenize(PtrGet_Fts5Context(jFcx),
5021 (const char *)pText, (int)nText,
5022 &s, s3jni_xTokenize_xToken);
5023 }else if( pRef == &S3JniNphRefs.fts5_tokenizer ){
5024 fts5_tokenizer * const pTok = PtrGet_fts5_tokenizer(jSelf);
5025 rc = pTok->xTokenize(PtrGet_Fts5Tokenizer(jFcx), &s, tokFlags,
5026 (const char *)pText, (int)nText,
5027 s3jni_xTokenize_xToken);
5028 }else{
5029 (*env)->FatalError(env, "This cannot happen. Maintenance required.");
5031 if( s.tok.jba ){
5032 assert( s.tok.zPrev );
5033 S3JniUnrefLocal(s.tok.jba);
5035 s3jni_jbytearray_release(jbaText, pText);
5036 return (jint)rc;
5039 JniDeclFtsXA(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jbyteArray jbaText,
5040 jobject jCallback){
5041 return s3jni_fts5_xTokenize(env, jSelf, &S3JniNphRefs.Fts5ExtensionApi,
5042 0, jFcx, jbaText, jCallback);
5045 JniDeclFtsTok(jint,xTokenize)(JniArgsEnvObj,jobject jFcx, jint tokFlags,
5046 jbyteArray jbaText, jobject jCallback){
5047 return s3jni_fts5_xTokenize(env, jSelf, &S3JniNphRefs.Fts5Tokenizer,
5048 tokFlags, jFcx, jbaText, jCallback);
5052 JniDeclFtsXA(jobject,xUserData)(JniArgsEnvObj,jobject jFcx){
5053 Fts5ExtDecl;
5054 Fts5JniAux * const pAux = fext->xUserData(PtrGet_Fts5Context(jFcx));
5055 return pAux ? pAux->jUserData : 0;
5058 #endif /* SQLITE_ENABLE_FTS5 */
5060 ////////////////////////////////////////////////////////////////////////
5061 // End of the main API bindings. Start of SQLTester bits...
5062 ////////////////////////////////////////////////////////////////////////
5064 #ifdef SQLITE_JNI_ENABLE_SQLTester
5065 typedef struct SQLTesterJni SQLTesterJni;
5066 struct SQLTesterJni {
5067 sqlite3_int64 nDup;
5069 static SQLTesterJni SQLTester = {
5073 static void SQLTester_dup_destructor(void*pToFree){
5074 u64 *p = (u64*)pToFree;
5075 assert( p!=0 );
5076 p--;
5077 assert( p[0]==0x2bbf4b7c );
5078 p[0] = 0;
5079 p[1] = 0;
5080 sqlite3_free(p);
5084 ** Implementation of
5086 ** dup(TEXT)
5088 ** This SQL function simply makes a copy of its text argument. But it
5089 ** returns the result using a custom destructor, in order to provide
5090 ** tests for the use of Mem.xDel() in the SQLite VDBE.
5092 static void SQLTester_dup_func(
5093 sqlite3_context *context,
5094 int argc,
5095 sqlite3_value **argv
5097 u64 *pOut;
5098 char *z;
5099 int n = sqlite3_value_bytes(argv[0]);
5100 SQLTesterJni * const p = (SQLTesterJni *)sqlite3_user_data(context);
5101 S3JniDeclLocal_env;
5103 ++p->nDup;
5104 if( n>0 && (pOut = s3jni_malloc( (n+16)&~7 ))!=0 ){
5105 pOut[0] = 0x2bbf4b7c;
5106 z = (char*)&pOut[1];
5107 memcpy(z, sqlite3_value_text(argv[0]), n);
5108 z[n] = 0;
5109 sqlite3_result_text(context, z, n, SQLTester_dup_destructor);
5111 return;
5115 ** Return the number of calls to the dup() SQL function since the
5116 ** SQLTester context was opened or since the last dup_count() call.
5118 static void SQLTester_dup_count_func(
5119 sqlite3_context *context,
5120 int argc,
5121 sqlite3_value **argv
5123 SQLTesterJni * const p = (SQLTesterJni *)sqlite3_user_data(context);
5124 sqlite3_result_int64(context, p->nDup);
5125 p->nDup = 0;
5129 ** Return non-zero if string z matches glob pattern zGlob and zero if the
5130 ** pattern does not match.
5132 ** To repeat:
5134 ** zero == no match
5135 ** non-zero == match
5137 ** Globbing rules:
5139 ** '*' Matches any sequence of zero or more characters.
5141 ** '?' Matches exactly one character.
5143 ** [...] Matches one character from the enclosed list of
5144 ** characters.
5146 ** [^...] Matches one character not in the enclosed list.
5148 ** '#' Matches any sequence of one or more digits with an
5149 ** optional + or - sign in front, or a hexadecimal
5150 ** literal of the form 0x...
5152 static int SQLTester_strnotglob(const char *zGlob, const char *z){
5153 int c, c2;
5154 int invert;
5155 int seen;
5157 while( (c = (*(zGlob++)))!=0 ){
5158 if( c=='*' ){
5159 while( (c=(*(zGlob++))) == '*' || c=='?' ){
5160 if( c=='?' && (*(z++))==0 ) return 0;
5162 if( c==0 ){
5163 return 1;
5164 }else if( c=='[' ){
5165 while( *z && SQLTester_strnotglob(zGlob-1,z)==0 ){
5166 z++;
5168 return (*z)!=0;
5170 while( (c2 = (*(z++)))!=0 ){
5171 while( c2!=c ){
5172 c2 = *(z++);
5173 if( c2==0 ) return 0;
5175 if( SQLTester_strnotglob(zGlob,z) ) return 1;
5177 return 0;
5178 }else if( c=='?' ){
5179 if( (*(z++))==0 ) return 0;
5180 }else if( c=='[' ){
5181 int prior_c = 0;
5182 seen = 0;
5183 invert = 0;
5184 c = *(z++);
5185 if( c==0 ) return 0;
5186 c2 = *(zGlob++);
5187 if( c2=='^' ){
5188 invert = 1;
5189 c2 = *(zGlob++);
5191 if( c2==']' ){
5192 if( c==']' ) seen = 1;
5193 c2 = *(zGlob++);
5195 while( c2 && c2!=']' ){
5196 if( c2=='-' && zGlob[0]!=']' && zGlob[0]!=0 && prior_c>0 ){
5197 c2 = *(zGlob++);
5198 if( c>=prior_c && c<=c2 ) seen = 1;
5199 prior_c = 0;
5200 }else{
5201 if( c==c2 ){
5202 seen = 1;
5204 prior_c = c2;
5206 c2 = *(zGlob++);
5208 if( c2==0 || (seen ^ invert)==0 ) return 0;
5209 }else if( c=='#' ){
5210 if( z[0]=='0'
5211 && (z[1]=='x' || z[1]=='X')
5212 && sqlite3Isxdigit(z[2])
5214 z += 3;
5215 while( sqlite3Isxdigit(z[0]) ){ z++; }
5216 }else{
5217 if( (z[0]=='-' || z[0]=='+') && sqlite3Isdigit(z[1]) ) z++;
5218 if( !sqlite3Isdigit(z[0]) ) return 0;
5219 z++;
5220 while( sqlite3Isdigit(z[0]) ){ z++; }
5222 }else{
5223 if( c!=(*(z++)) ) return 0;
5226 return *z==0;
5229 JNIEXPORT jint JNICALL
5230 Java_org_sqlite_jni_tester_SQLTester_strglob(
5231 JniArgsEnvClass, jbyteArray baG, jbyteArray baT
5233 int rc = 0;
5234 jbyte * const pG = s3jni_jbytearray_bytes(baG);
5235 jbyte * const pT = pG ? s3jni_jbytearray_bytes(baT) : 0;
5237 s3jni_oom_fatal(pT);
5238 /* Note that we're relying on the byte arrays having been
5239 NUL-terminated on the Java side. */
5240 rc = !SQLTester_strnotglob((const char *)pG, (const char *)pT);
5241 s3jni_jbytearray_release(baG, pG);
5242 s3jni_jbytearray_release(baT, pT);
5243 return rc;
5247 static int SQLTester_auto_extension(sqlite3 *pDb, const char **pzErr,
5248 const struct sqlite3_api_routines *ignored){
5249 sqlite3_create_function(pDb, "dup", 1, SQLITE_UTF8, &SQLTester,
5250 SQLTester_dup_func, 0, 0);
5251 sqlite3_create_function(pDb, "dup_count", 0, SQLITE_UTF8, &SQLTester,
5252 SQLTester_dup_count_func, 0, 0);
5253 return 0;
5256 JNIEXPORT void JNICALL
5257 Java_org_sqlite_jni_tester_SQLTester_installCustomExtensions(JniArgsEnvClass){
5258 sqlite3_auto_extension( (void(*)(void))SQLTester_auto_extension );
5261 #endif /* SQLITE_JNI_ENABLE_SQLTester */
5262 ////////////////////////////////////////////////////////////////////////
5263 // End of SQLTester bindings. Start of lower-level bits.
5264 ////////////////////////////////////////////////////////////////////////
5267 ** Called during static init of the SQLite3Jni class to sync certain
5268 ** compile-time constants to Java-space.
5270 ** This routine is part of the reason why we have to #include
5271 ** sqlite3.c instead of sqlite3.h.
5273 JNIEXPORT void JNICALL
5274 Java_org_sqlite_jni_SQLite3Jni_init(JniArgsEnvClass){
5275 enum JType {
5276 JTYPE_INT,
5277 JTYPE_BOOL
5279 typedef struct {
5280 const char *zName;
5281 enum JType jtype;
5282 int value;
5283 } DefineEntry;
5284 const DefineEntry aLimits[] = {
5285 {0,0}
5287 jfieldID fieldId;
5288 jclass klazz;
5289 const DefineEntry * pDef;
5291 #if 0
5292 if( 0==sqlite3_threadsafe() ){
5293 (*env)->FatalError(env, "sqlite3 currently requires SQLITE_THREADSAFE!=0.");
5294 return;
5296 #endif
5297 memset(&S3JniGlobal, 0, sizeof(S3JniGlobal));
5298 if( (*env)->GetJavaVM(env, &SJG.jvm) ){
5299 (*env)->FatalError(env, "GetJavaVM() failure shouldn't be possible.");
5300 return;
5303 /* Grab references to various global classes and objects... */
5304 SJG.g.cLong = S3JniRefGlobal((*env)->FindClass(env,"java/lang/Long"));
5305 S3JniExceptionIsFatal("Error getting reference to Long class.");
5306 SJG.g.ctorLong1 = (*env)->GetMethodID(env, SJG.g.cLong,
5307 "<init>", "(J)V");
5308 S3JniExceptionIsFatal("Error getting reference to Long constructor.");
5310 SJG.g.cString = S3JniRefGlobal((*env)->FindClass(env,"java/lang/String"));
5311 S3JniExceptionIsFatal("Error getting reference to String class.");
5312 SJG.g.ctorStringBA =
5313 (*env)->GetMethodID(env, SJG.g.cString,
5314 "<init>", "([BLjava/nio/charset/Charset;)V");
5315 S3JniExceptionIsFatal("Error getting reference to String(byte[],Charset) ctor.");
5316 SJG.g.stringGetBytes =
5317 (*env)->GetMethodID(env, SJG.g.cString,
5318 "getBytes", "(Ljava/nio/charset/Charset;)[B");
5319 S3JniExceptionIsFatal("Error getting reference to String.getBytes(Charset).");
5321 { /* StandardCharsets.UTF_8 */
5322 jfieldID fUtf8;
5323 klazz = (*env)->FindClass(env,"java/nio/charset/StandardCharsets");
5324 S3JniExceptionIsFatal("Error getting reference to StandardCharsets class.");
5325 fUtf8 = (*env)->GetStaticFieldID(env, klazz, "UTF_8",
5326 "Ljava/nio/charset/Charset;");
5327 S3JniExceptionIsFatal("Error getting StandardCharsets.UTF_8 field.");
5328 SJG.g.oCharsetUtf8 =
5329 S3JniRefGlobal((*env)->GetStaticObjectField(env, klazz, fUtf8));
5330 S3JniExceptionIsFatal("Error getting reference to StandardCharsets.UTF_8.");
5331 S3JniUnrefLocal(klazz);
5334 #ifdef SQLITE_ENABLE_FTS5
5335 klazz = (*env)->FindClass(env, "org/sqlite/jni/Fts5PhraseIter");
5336 S3JniExceptionIsFatal("Error getting reference to org.sqlite.jni.Fts5PhraseIter.");
5337 SJG.fts5.jPhraseIter.fidA = (*env)->GetFieldID(env, klazz, "a", "J");
5338 S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.a field.");
5339 SJG.fts5.jPhraseIter.fidB = (*env)->GetFieldID(env, klazz, "b", "J");
5340 S3JniExceptionIsFatal("Cannot get Fts5PhraseIter.b field.");
5341 S3JniUnrefLocal(klazz);
5342 #endif
5344 SJG.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
5345 s3jni_oom_fatal( SJG.mutex );
5346 SJG.envCache.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
5347 s3jni_oom_fatal( SJG.envCache.mutex );
5348 SJG.perDb.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
5349 s3jni_oom_fatal( SJG.perDb.mutex );
5350 SJG.autoExt.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
5351 s3jni_oom_fatal( SJG.autoExt.mutex );
5353 #if S3JNI_METRICS_MUTEX
5354 SJG.metrics.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
5355 s3jni_oom_fatal( SJG.metrics.mutex );
5356 #endif
5358 sqlite3_shutdown()
5359 /* So that it becomes legal for Java-level code to call
5360 ** sqlite3_config(), if it's ever implemented. */;
5362 /* Set up static "consts" of the SQLite3Jni class. */
5363 for( pDef = &aLimits[0]; pDef->zName; ++pDef ){
5364 char const * zSig = (JTYPE_BOOL == pDef->jtype) ? "Z" : "I";
5365 fieldId = (*env)->GetStaticFieldID(env, jKlazz, pDef->zName, zSig);
5366 S3JniExceptionIsFatal("Missing an expected static member of the SQLite3Jni class.");
5367 assert(fieldId);
5368 switch( pDef->jtype ){
5369 case JTYPE_INT:
5370 (*env)->SetStaticIntField(env, jKlazz, fieldId, (jint)pDef->value);
5371 break;
5372 case JTYPE_BOOL:
5373 (*env)->SetStaticBooleanField(env, jKlazz, fieldId,
5374 pDef->value ? JNI_TRUE : JNI_FALSE);
5375 break;
5377 S3JniExceptionIsFatal("Seting a static member of the SQLite3Jni class failed.");