/* Helpers for jstring and jbyteArray. */
#define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define JBA_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT)
/* Marker for code which needs(?) to be made thread-safe.
*/
#define FIXME_THREADING(REASON)
enum {
Size of the NativePointerHolder cache. Need enough space for
(only) the library's NativePointerHolder types, a fixed count
known at build-time. If we add more than this a fatal error will
/* Helpers for jstring and jbyteArray. */
#define JSTR_TOC(ARG) (*env)->GetStringUTFChars(env, ARG, NULL)
#define JSTR_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseStringUTFChars(env, ARG, VAR)
#define JBA_TOC(ARG) (*env)->GetByteArrayElements(env,ARG, NULL)
#define JBA_RELEASE(ARG,VAR) if(VAR) (*env)->ReleaseByteArrayElements(env, ARG, VAR, JNI_ABORT)
/* Marker for code which needs(?) to be made thread-safe.
REASON is a
terse reminder about why that function requires a mutex.
#define FIXME_THREADING(REASON)
enum {
Size of the NativePointerHolder cache. Need enough space for
(only) the library's NativePointerHolder types, a fixed count
known at build-time. If we add more than this a fatal error will
to v. */
static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_Int32, "I", jOut, &setter);
(*env)->SetIntField(env, jOut, setter, (jint)v);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value");
static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut,
jobject jDb){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3,
"Lorg/sqlite/jni/sqlite3;", jOut, &setter);
(*env)->SetObjectField(env, jOut, setter, jDb);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value");
static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut,
jobject jStmt){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3_stmt,
"Lorg/sqlite/jni/sqlite3_stmt;", jOut, &setter);
(*env)->SetObjectField(env, jOut, setter, jStmt);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value");
#ifdef SQLITE_ENABLE_FTS5
/* Sets the value property of the OutputPointer.Int64 jOut object
to v. */
static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_Int64, "J", jOut, &setter);
(*env)->SetLongField(env, jOut, setter, v);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value");
#if 0
/* Sets the value property of the OutputPointer.ByteArray jOut object
to v. */
static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
jbyteArray const v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_ByteArray, "[B",
to v. */
static void OutputPointer_set_Int32(JNIEnv * const env, jobject const jOut, int v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_Int32, "I", jOut, &setter);
(*env)->SetIntField(env, jOut, setter, (jint)v);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int32.value");
/* Sets the value property of the OutputPointer.Int64 jOut object
to v. */
static void OutputPointer_set_Int64(JNIEnv * const env, jobject const jOut, jlong v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_Int64, "J", jOut, &setter);
(*env)->SetLongField(env, jOut, setter, v);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.Int64.value");
static void OutputPointer_set_sqlite3(JNIEnv * const env, jobject const jOut,
jobject jDb){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3,
"Lorg/sqlite/jni/sqlite3;", jOut, &setter);
(*env)->SetObjectField(env, jOut, setter, jDb);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3.value");
static void OutputPointer_set_sqlite3_stmt(JNIEnv * const env, jobject const jOut,
jobject jStmt){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_sqlite3_stmt,
"Lorg/sqlite/jni/sqlite3_stmt;", jOut, &setter);
(*env)->SetObjectField(env, jOut, setter, jStmt);
EXCEPTION_IS_FATAL("Cannot set OutputPointer.sqlite3_stmt.value");
#ifdef SQLITE_ENABLE_FTS5
/* Sets the value property of the OutputPointer.ByteArray jOut object
to v. */
static void OutputPointer_set_ByteArray(JNIEnv * const env, jobject const jOut,
jbyteArray const v){
jfieldID setter = 0;
setupOutputPointer(env, S3JniClassNames.OutputPointer_ByteArray, "[B",
JDECL(void,1set_1last_1insert_1rowid)(JENV_CSELF, jobject jpDb, jlong rowId){
sqlite3_set_last_insert_rowid(PtrGet_sqlite3_context(jpDb),
(sqlite3_int64)rowId);
static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
jbyteArray baG, jbyteArray baT, jint escLike){
int rc = 0;
jbyte * const pG = JBA_TOC(baG);
jbyte * const pT = pG ? JBA_TOC(baT) : 0;
OOM_CHECK(pT);
JDECL(void,1set_1last_1insert_1rowid)(JENV_CSELF, jobject jpDb, jlong rowId){
sqlite3_set_last_insert_rowid(PtrGet_sqlite3_context(jpDb),
(sqlite3_int64)rowId);
FIXME_THREADING(nphCache)
JDECL(jint,1status)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh,
jboolean reset ){
int iCur = 0, iHigh = 0;
int rc = sqlite3_status( op, &iCur, &iHigh, reset );
if( 0==rc ){
OutputPointer_set_Int32(env, jOutCurrent, iCur);
OutputPointer_set_Int32(env, jOutHigh, iHigh);
return (jint)rc;
FIXME_THREADING(nphCache)
JDECL(jint,1status64)(JENV_CSELF, jint op, jobject jOutCurrent, jobject jOutHigh,
jboolean reset ){
sqlite3_int64 iCur = 0, iHigh = 0;
int rc = sqlite3_status64( op, &iCur, &iHigh, reset );
if( 0==rc ){
OutputPointer_set_Int64(env, jOutCurrent, iCur);
OutputPointer_set_Int64(env, jOutHigh, iHigh);
return (jint)rc;
static int s3jni_strlike_glob(int isLike, JNIEnv *const env,
jbyteArray baG, jbyteArray baT, jint escLike){
int rc = 0;
jbyte * const pG = JBA_TOC(baG);
jbyte * const pT = pG ? JBA_TOC(baT) : 0;
OOM_CHECK(pT);
Modified
ext/jni/src/c/sqlite3-jni.h
from
[8f9e0301]
to
[7a51d104]
.
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_result_text64
* Signature: (Lorg/sqlite/jni/sqlite3_context;[BJI)V
JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64
(JNIEnv *, jclass, jobject, jbyteArray, jlong, jint);
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_rollback_hook
* Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHook;)Lorg/sqlite/jni/RollbackHook;
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook
(JNIEnv *, jclass, jobject, jobject);
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_result_text64
* Signature: (Lorg/sqlite/jni/sqlite3_context;[BJI)V
JNIEXPORT void JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1result_1text64
(JNIEnv *, jclass, jobject, jbyteArray, jlong, jint);
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_status
* Signature: (ILorg/sqlite/jni/OutputPointer/Int32;Lorg/sqlite/jni/OutputPointer/Int32;Z)I
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status
(JNIEnv *, jclass, jint, jobject, jobject, jboolean);
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_status64
* Signature: (ILorg/sqlite/jni/OutputPointer/Int64;Lorg/sqlite/jni/OutputPointer/Int64;Z)I
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1status64
(JNIEnv *, jclass, jint, jobject, jobject, jboolean);
* Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_rollback_hook
* Signature: (Lorg/sqlite/jni/sqlite3;Lorg/sqlite/jni/RollbackHook;)Lorg/sqlite/jni/RollbackHook;
JNIEXPORT jobject JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1rollback_1hook
(JNIEnv *, jclass, jobject, jobject);
Modified
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
from
[23dec2c2]
to
[21af8894]
.
private static synchronized native void sqlite3_result_text64(
@NotNull sqlite3_context cx, @Nullable byte[] text,
long maxLength, int encoding
//
public static synchronized native int sqlite3_status(
//
int op, OutputPointer.Int32 pCurrent
, OutputPointer.Int32 pHighwater
,
//
boolean reset
//
);
//
public static synchronized native int sqlite3_status64(
//
int op, OutputPointer.Int64 pCurrent
, OutputPointer.Int64 pHighwater
,
//
boolean reset
//
);
Sets the current UDF result to the given bytes, which are assumed
be encoded in UTF-16 using the platform's byte order.
public static void sqlite3_result_text16(
@NotNull sqlite3_context cx, @Nullable byte[] text
private static synchronized native void sqlite3_result_text64(
@NotNull sqlite3_context cx, @Nullable byte[] text,
long maxLength, int encoding
public static synchronized native int sqlite3_status(
int op,
@NotNull
OutputPointer.Int32 pCurrent,
@NotNull OutputPointer.Int32 pHighwater,
boolean reset
public static synchronized native int sqlite3_status64(
int op,
@NotNull
OutputPointer.Int64 pCurrent,
@NotNull OutputPointer.Int64 pHighwater,
boolean reset
Sets the current UDF result to the given bytes, which are assumed
be encoded in UTF-16 using the platform's byte order.
public static void sqlite3_result_text16(
@NotNull sqlite3_context cx, @Nullable byte[] text
Modified
ext/jni/src/org/sqlite/jni/Tester1.java
from
[62ba3f54]
to
[c7bb4747]
.
int rc = sqlite3_prepare(db, sql, outStmt);
affirm( 0 == rc );
final sqlite3_stmt rv = outStmt.getValue();
outStmt.clear();
affirm( 0 != rv.getNativePointer() );
return rv;
private static void testOpenDb1(){
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
int rc = sqlite3_open(":memory:", out);
++metrics.dbOpen;
sqlite3 db = out.getValue();
affirm(0 == rc);
affirm(0 < db.getNativePointer());
sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, null)
/* This function has different mangled names in jdk8 vs jdk19,
and this call is here to ensure that the build fails
if it cannot find both names. */;
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
private static void testCompileOption(){
int i = 0;
String optName;
outln("compile options:");
for( ; null != (optName = sqlite3_compileoption_get(i)); ++i){
outln("\t"+optName+"\t (used="+
sqlite3_compileoption_used(optName)+")");
private static void testOpenDb2(){
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
int rc = sqlite3_open_v2(":memory:", out,
SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE, null);
int rc = sqlite3_prepare(db, sql, outStmt);
affirm( 0 == rc );
final sqlite3_stmt rv = outStmt.getValue();
outStmt.clear();
affirm( 0 != rv.getNativePointer() );
return rv;
private static void testCompileOption(){
int i = 0;
String optName;
outln("compile options:");
for( ; null != (optName = sqlite3_compileoption_get(i)); ++i){
outln("\t"+optName+"\t (used="+
sqlite3_compileoption_used(optName)+")");
private static void testOpenDb1(){
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
int rc = sqlite3_open(":memory:", out);
++metrics.dbOpen;
sqlite3 db = out.getValue();
affirm(0 == rc);
affirm(0 < db.getNativePointer());
sqlite3_db_config(db, SQLITE_DBCONFIG_DEFENSIVE, 1, null)
/* This function has different mangled names in jdk8 vs jdk19,
and this call is here to ensure that the build fails
if it cannot find both names. */;
sqlite3_close_v2(db);
affirm(0 == db.getNativePointer());
private static void testOpenDb2(){
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
int rc = sqlite3_open_v2(":memory:", out,
SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE, null);
https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html
Let's ensure that we can convert to standard UTF-8 in Java code
(noting that the JNI native API has no way to do this).
final byte[] ba = "a \0 b".getBytes(StandardCharsets.UTF_8);
affirm( 5 == ba.length /* as opposed to 6 in modified utf-8 */);
private static void testUdf1(){
final sqlite3 db = createNewDb();
// These ValueHolders are just to confirm that the func did what we want...
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html
Let's ensure that we can convert to standard UTF-8 in Java code
(noting that the JNI native API has no way to do this).
final byte[] ba = "a \0 b".getBytes(StandardCharsets.UTF_8);
affirm( 5 == ba.length /* as opposed to 6 in modified utf-8 */);
private static void testStatus(){
final OutputPointer.Int64 cur64 = new OutputPointer.Int64();
final OutputPointer.Int64 high64 = new OutputPointer.Int64();
final OutputPointer.Int32 cur32 = new OutputPointer.Int32();
final OutputPointer.Int32 high32 = new OutputPointer.Int32();
final sqlite3 db = createNewDb();
execSql(db, "create table t(a)");
sqlite3_close_v2(db);
int rc = sqlite3_status(SQLITE_STATUS_MEMORY_USED, cur32, high32, false);
affirm( 0 == rc );
affirm( cur32.getValue() > 0 );
affirm( high32.getValue() >= cur32.getValue() );
rc = sqlite3_status64(SQLITE_STATUS_MEMORY_USED, cur64, high64, false);
affirm( 0 == rc );
affirm( cur64.getValue() > 0 );
affirm( high64.getValue() >= cur64.getValue() );
private static void testUdf1(){
final sqlite3 db = createNewDb();
// These ValueHolders are just to confirm that the func did what we want...
final ValueHolder<Boolean> xDestroyCalled = new ValueHolder<>(false);
final ValueHolder<Integer> xFuncAccum = new ValueHolder<>(0);
testBindFetchInt64();
testBindFetchDouble();
testBindFetchText();
testBindFetchBlob();
testSql();
testCollation();
testToUtf8();
testUdf1();
testUdfJavaObject();
testUdfAggregate();
testUdfWindow();
testTrace();
testBusy();
testProgress();