mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Add more JNI docs, tests, and a handful of Java-side overloads.
FossilOrigin-Name: d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0
This commit is contained in:
@ -1429,7 +1429,7 @@ typedef struct {
|
||||
} ResultJavaVal;
|
||||
|
||||
/* For use with sqlite3_result/value_pointer() */
|
||||
#define ResultJavaValuePtrStr "ResultJavaVal"
|
||||
#define ResultJavaValuePtrStr "org.sqlite.jni.ResultJavaVal"
|
||||
|
||||
/*
|
||||
** Allocate a new ResultJavaVal and assign it a new global ref of
|
||||
@ -1915,15 +1915,10 @@ JDECL(jint,1auto_1extension)(JENV_CSELF, jobject jAutoExt){
|
||||
|
||||
JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt,
|
||||
jint ndx, jbyteArray baData, jint nMax){
|
||||
int rc;
|
||||
if(!baData){
|
||||
rc = sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), ndx);
|
||||
}else{
|
||||
jbyte * const pBuf = JBA_TOC(baData);
|
||||
rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, pBuf, (int)nMax,
|
||||
SQLITE_TRANSIENT);
|
||||
JBA_RELEASE(baData,pBuf);
|
||||
}
|
||||
jbyte * const pBuf = baData ? JBA_TOC(baData) : 0;
|
||||
int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
|
||||
pBuf, (int)nMax, SQLITE_TRANSIENT);
|
||||
JBA_RELEASE(baData,pBuf);
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
@ -1960,15 +1955,21 @@ JDECL(jint,1bind_1parameter_1index)(JENV_CSELF, jobject jpStmt, jbyteArray jName
|
||||
|
||||
JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt,
|
||||
jint ndx, jbyteArray baData, jint nMax){
|
||||
if(baData){
|
||||
jbyte * const pBuf = JBA_TOC(baData);
|
||||
int rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf,
|
||||
(int)nMax, SQLITE_TRANSIENT);
|
||||
JBA_RELEASE(baData, pBuf);
|
||||
return (jint)rc;
|
||||
}else{
|
||||
return sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
|
||||
}
|
||||
jbyte * const pBuf = baData ? JBA_TOC(baData) : 0;
|
||||
int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
|
||||
(const char *)pBuf,
|
||||
(int)nMax, SQLITE_TRANSIENT);
|
||||
JBA_RELEASE(baData, pBuf);
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
JDECL(jint,1bind_1text16)(JENV_CSELF, jobject jpStmt,
|
||||
jint ndx, jbyteArray baData, jint nMax){
|
||||
jbyte * const pBuf = baData ? JBA_TOC(baData) : 0;
|
||||
int const rc = sqlite3_bind_text16(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
|
||||
pBuf, (int)nMax, SQLITE_TRANSIENT);
|
||||
JBA_RELEASE(baData, pBuf);
|
||||
return (jint)rc;
|
||||
}
|
||||
|
||||
JDECL(jint,1bind_1zeroblob)(JENV_CSELF, jobject jpStmt,
|
||||
@ -3595,18 +3596,19 @@ JDECL(jbyteArray,1value_1text_1utf8)(JENV_CSELF, jobject jpSVal){
|
||||
}
|
||||
|
||||
static jbyteArray value_text16(int mode, JNIEnv * const env, jobject jpSVal){
|
||||
int const nLen = sqlite3_value_bytes16(PtrGet_sqlite3_value(jpSVal));
|
||||
sqlite3_value * const sv = PtrGet_sqlite3_value(jpSVal);
|
||||
int const nLen = sqlite3_value_bytes16(sv);
|
||||
jbyteArray jba;
|
||||
const jbyte * pBytes;
|
||||
switch(mode){
|
||||
case SQLITE_UTF16:
|
||||
pBytes = sqlite3_value_text16(PtrGet_sqlite3_value(jpSVal));
|
||||
pBytes = sqlite3_value_text16(sv);
|
||||
break;
|
||||
case SQLITE_UTF16LE:
|
||||
pBytes = sqlite3_value_text16le(PtrGet_sqlite3_value(jpSVal));
|
||||
pBytes = sqlite3_value_text16le(sv);
|
||||
break;
|
||||
case SQLITE_UTF16BE:
|
||||
pBytes = sqlite3_value_text16be(PtrGet_sqlite3_value(jpSVal));
|
||||
pBytes = sqlite3_value_text16be(sv);
|
||||
break;
|
||||
default:
|
||||
assert(!"not possible");
|
||||
|
@ -843,6 +843,14 @@ JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1parameter_1
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1text
|
||||
(JNIEnv *, jclass, jobject, jint, jbyteArray, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_SQLite3Jni
|
||||
* Method: sqlite3_bind_text16
|
||||
* Signature: (Lorg/sqlite/jni/sqlite3_stmt;I[BI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1text16
|
||||
(JNIEnv *, jclass, jobject, jint, jbyteArray, jint);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_SQLite3Jni
|
||||
* Method: sqlite3_bind_zeroblob
|
||||
|
@ -14,24 +14,24 @@
|
||||
package org.sqlite.jni;
|
||||
|
||||
/**
|
||||
A callback for use with sqlite3_auto_extension().
|
||||
A callback for use with the sqlite3_auto_extension() family of
|
||||
APIs.
|
||||
*/
|
||||
public interface AutoExtension {
|
||||
/**
|
||||
Must function as described for a sqlite3_auto_extension()
|
||||
callback, with the caveat that the signature is more limited.
|
||||
callback, with the caveat that the signature is shorter.
|
||||
|
||||
As an exception (as it were) to the callbacks-must-not-throw
|
||||
rule, AutoExtensions may throw and the exception's error message
|
||||
AutoExtensions may throw and the exception's error message
|
||||
will be set as the db's error string.
|
||||
|
||||
Hints for implementations:
|
||||
Tips for implementations:
|
||||
|
||||
- Opening a database from an auto-extension handler will lead to
|
||||
an endless recursion of the auto-handler triggering itself
|
||||
indirectly for each newly-opened database.
|
||||
|
||||
- If this routine is stateful, it is a good idea to make the
|
||||
- If this routine is stateful, it may be useful to make the
|
||||
overridden method synchronized.
|
||||
|
||||
- Results are undefined if db is closed by an auto-extension.
|
||||
|
@ -154,32 +154,35 @@ public final class SQLite3Jni {
|
||||
Functions almost as documented for the C API, with these
|
||||
exceptions:
|
||||
|
||||
- The callback interface is more limited because of
|
||||
cross-language differences. Specifically, auto-extensions do
|
||||
not have access to the sqlite3_api object which native
|
||||
auto-extensions do.
|
||||
- The callback interface is is shorter because of cross-language
|
||||
differences. Specifically, 3rd argument to the C auto-extension
|
||||
callback interface is unnecessary here.
|
||||
|
||||
- If the list of auto-extensions is manipulated from an
|
||||
auto-extension, it is undefined which, if any, auto-extensions
|
||||
will subsequently execute for the current database (it depends
|
||||
on multiple factors).
|
||||
|
||||
The C API docs do not specifically say so, if the list of
|
||||
auto-extensions is manipulated from an auto-extension, it is
|
||||
undefined which, if any, auto-extensions will subsequently
|
||||
execute for the current database.
|
||||
|
||||
See the AutoExtension class docs for more information.
|
||||
*/
|
||||
public static native int sqlite3_auto_extension(@NotNull AutoExtension callback);
|
||||
|
||||
/**
|
||||
Results are undefined if data is not null and n<0 || n>=data.length.
|
||||
*/
|
||||
public static native int sqlite3_bind_blob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n
|
||||
);
|
||||
|
||||
public static int sqlite3_bind_blob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
|
||||
){
|
||||
return (null == data)
|
||||
return (null==data)
|
||||
? sqlite3_bind_null(stmt, ndx)
|
||||
: sqlite3_bind_blob(stmt, ndx, data, data.length);
|
||||
}
|
||||
|
||||
private static native int sqlite3_bind_blob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n
|
||||
);
|
||||
|
||||
public static native int sqlite3_bind_double(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, double v
|
||||
);
|
||||
@ -200,13 +203,10 @@ public final class SQLite3Jni {
|
||||
@NotNull sqlite3_stmt stmt
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
A level of indirection required to ensure that the input to the
|
||||
C-level function of the same name is a NUL-terminated UTF-8
|
||||
string.
|
||||
Requires that paramName be a NUL-terminated UTF-8 string.
|
||||
*/
|
||||
private static native int sqlite3_bind_parameter_index(
|
||||
public static native int sqlite3_bind_parameter_index(
|
||||
@NotNull sqlite3_stmt stmt, byte[] paramName
|
||||
);
|
||||
|
||||
@ -218,14 +218,22 @@ public final class SQLite3Jni {
|
||||
}
|
||||
|
||||
/**
|
||||
Works like the C-level sqlite3_bind_text() but (A) assumes
|
||||
SQLITE_TRANSIENT for the final parameter and (B) behaves like
|
||||
sqlite3_bind_null() if the data argument is null.
|
||||
Works like the C-level sqlite3_bind_text() but assumes
|
||||
SQLITE_TRANSIENT for the final C API parameter.
|
||||
|
||||
Results are undefined if data is not null and
|
||||
maxBytes>=data.length. If maxBytes is negative then results are
|
||||
undefined if data is not null and does not contain a NUL byte.
|
||||
*/
|
||||
private static native int sqlite3_bind_text(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
|
||||
);
|
||||
|
||||
/**
|
||||
Converts data, if not null, to a UTF-8-encoded byte array and
|
||||
binds it as such, returning the result of the C-level
|
||||
sqlite3_bind_null() or sqlite3_bind_text().
|
||||
*/
|
||||
public static int sqlite3_bind_text(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable String data
|
||||
){
|
||||
@ -234,6 +242,9 @@ public final class SQLite3Jni {
|
||||
return sqlite3_bind_text(stmt, ndx, utf8, utf8.length);
|
||||
}
|
||||
|
||||
/**
|
||||
Requires that data be null or in UTF-8 encoding.
|
||||
*/
|
||||
public static int sqlite3_bind_text(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
|
||||
){
|
||||
@ -242,6 +253,41 @@ public final class SQLite3Jni {
|
||||
: sqlite3_bind_text(stmt, ndx, data, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
Identical to the sqlite3_bind_text() overload with the same
|
||||
signature but requires that its input be encoded in UTF-16 in
|
||||
platform byte order.
|
||||
*/
|
||||
private static native int sqlite3_bind_text16(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes
|
||||
);
|
||||
|
||||
/**
|
||||
Converts its string argument to UTF-16 and binds it as such, returning
|
||||
the result of the C-side function of the same name. The 3rd argument
|
||||
may be null.
|
||||
*/
|
||||
public static int sqlite3_bind_text16(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable String data
|
||||
){
|
||||
if(null == data) return sqlite3_bind_null(stmt, ndx);
|
||||
final byte[] bytes = data.getBytes(StandardCharsets.UTF_16);
|
||||
return sqlite3_bind_text16(stmt, ndx, bytes, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
Requires that data be null or in UTF-16 encoding in platform byte
|
||||
order. Returns the result of the C-level sqlite3_bind_null() or
|
||||
sqlite3_bind_text().
|
||||
*/
|
||||
public static int sqlite3_bind_text16(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
|
||||
){
|
||||
return (null == data)
|
||||
? sqlite3_bind_null(stmt, ndx)
|
||||
: sqlite3_bind_text16(stmt, ndx, data, data.length);
|
||||
}
|
||||
|
||||
public static native int sqlite3_bind_zeroblob(
|
||||
@NotNull sqlite3_stmt stmt, int ndx, int n
|
||||
);
|
||||
@ -253,8 +299,7 @@ public final class SQLite3Jni {
|
||||
/**
|
||||
As for the C-level function of the same name, with a BusyHandler
|
||||
instance in place of a callback function. Pass it a null handler
|
||||
to clear the busy handler. Calling this multiple times with the
|
||||
same object is a no-op on the second and subsequent calls.
|
||||
to clear the busy handler.
|
||||
*/
|
||||
public static native int sqlite3_busy_handler(
|
||||
@NotNull sqlite3 db, @Nullable BusyHandler handler
|
||||
@ -595,15 +640,41 @@ public final class SQLite3Jni {
|
||||
@Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb
|
||||
);
|
||||
|
||||
/**
|
||||
Convenience overload which returns its db handle directly. The returned
|
||||
object might not have been successfully opened: use sqlite3_errcode() to
|
||||
check whether it is in an error state.
|
||||
|
||||
Ownership of the returned value is passed to the caller, who must eventually
|
||||
pass it to sqlite3_close() or sqlite3_close_v2().
|
||||
*/
|
||||
public static sqlite3 sqlite3_open(@Nullable String filename){
|
||||
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
sqlite3_open(filename, out);
|
||||
return out.take();
|
||||
};
|
||||
|
||||
public static native int sqlite3_open_v2(
|
||||
@Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb,
|
||||
int flags, @Nullable String zVfs
|
||||
);
|
||||
|
||||
/**
|
||||
Has the same semantics as the sqlite3-returning sqlite3_open()
|
||||
but uses sqlite3_open_v2() instead of sqlite3_open().
|
||||
*/
|
||||
public static sqlite3 sqlite3_open_v2(@Nullable String filename, int flags,
|
||||
@Nullable String zVfs){
|
||||
final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
|
||||
sqlite3_open_v2(filename, out, flags, zVfs);
|
||||
return out.take();
|
||||
};
|
||||
|
||||
/**
|
||||
The sqlite3_prepare() family of functions require slightly
|
||||
different signatures than their native counterparts, but
|
||||
overloading allows us to install several convenience forms.
|
||||
different signatures than their native counterparts, but (A) they
|
||||
retain functionally equivalent semantics and (B) overloading
|
||||
allows us to install several convenience forms.
|
||||
|
||||
All of them which take their SQL in the form of a byte[] require
|
||||
that it be in UTF-8 encoding unless explicitly noted otherwise.
|
||||
@ -648,6 +719,26 @@ public final class SQLite3Jni {
|
||||
return sqlite3_prepare(db, utf8, utf8.length, outStmt, null);
|
||||
}
|
||||
|
||||
/**
|
||||
Convenience overload which returns its statement handle directly,
|
||||
or null on error or when reading only whitespace or
|
||||
comments. sqlite3_errcode() can be used to determine whether
|
||||
there was an error or the input was empty. Ownership of the
|
||||
returned object is passed to the caller, who must eventually pass
|
||||
it to sqlite3_finalize().
|
||||
*/
|
||||
public static sqlite3_stmt sqlite3_prepare(
|
||||
@NotNull sqlite3 db, @NotNull String sql
|
||||
){
|
||||
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
|
||||
sqlite3_prepare(db, sql, out);
|
||||
return out.take();
|
||||
}
|
||||
|
||||
/**
|
||||
See sqlite3_prepare() for details about the slight API differences
|
||||
from the C API.
|
||||
*/
|
||||
private static native int sqlite3_prepare_v2(
|
||||
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
|
||||
@NotNull OutputPointer.sqlite3_stmt outStmt,
|
||||
@ -677,6 +768,18 @@ public final class SQLite3Jni {
|
||||
return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null);
|
||||
}
|
||||
|
||||
/**
|
||||
Works identically to the sqlite3_stmt-returning sqlite3_prepare()
|
||||
but uses sqlite3_prepare_v2().
|
||||
*/
|
||||
public static sqlite3_stmt sqlite3_prepare_v2(
|
||||
@NotNull sqlite3 db, @NotNull String sql
|
||||
){
|
||||
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
|
||||
sqlite3_prepare_v2(db, sql, out);
|
||||
return out.take();
|
||||
}
|
||||
|
||||
private static native int sqlite3_prepare_v3(
|
||||
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
|
||||
int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt,
|
||||
@ -706,6 +809,18 @@ public final class SQLite3Jni {
|
||||
return sqlite3_prepare_v3(db, utf8, utf8.length, prepFlags, outStmt, null);
|
||||
}
|
||||
|
||||
/**
|
||||
Works identically to the sqlite3_stmt-returning sqlite3_prepare()
|
||||
but uses sqlite3_prepare_v3().
|
||||
*/
|
||||
public static sqlite3_stmt sqlite3_prepare_v3(
|
||||
@NotNull sqlite3 db, @NotNull String sql, int prepFlags
|
||||
){
|
||||
final OutputPointer.sqlite3_stmt out = new OutputPointer.sqlite3_stmt();
|
||||
sqlite3_prepare_v3(db, sql, prepFlags, out);
|
||||
return out.take();
|
||||
}
|
||||
|
||||
/**
|
||||
If the C API was built with SQLITE_ENABLE_PREUPDATE_HOOK defined, this
|
||||
acts as a proxy for C's sqlite3_preupdate_blobwrite(), else it returns
|
||||
|
@ -21,6 +21,16 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
An annotation for Tester1 tests which we do not want to run in
|
||||
reflection-driven test mode because either they are not suitable
|
||||
for multi-threaded threaded mode or we have to control their execution
|
||||
order.
|
||||
*/
|
||||
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
|
||||
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
|
||||
@interface ManualTest{}
|
||||
|
||||
public class Tester1 implements Runnable {
|
||||
//! True when running in multi-threaded mode.
|
||||
private static boolean mtMode = false;
|
||||
@ -30,6 +40,10 @@ public class Tester1 implements Runnable {
|
||||
private static boolean shuffle = false;
|
||||
//! True to dump the list of to-run tests to stdout.
|
||||
private static boolean listRunTests = false;
|
||||
//! True to squelch all out() and outln() output.
|
||||
private static boolean quietMode = false;
|
||||
//! Total number of runTests() calls.
|
||||
private static int nTestRuns = 0;
|
||||
//! List of test*() methods to run.
|
||||
private static List<java.lang.reflect.Method> testMethods = null;
|
||||
//! List of exceptions collected by run()
|
||||
@ -48,27 +62,37 @@ public class Tester1 implements Runnable {
|
||||
static final Metrics metrics = new Metrics();
|
||||
|
||||
public synchronized static void outln(){
|
||||
System.out.println("");
|
||||
if( !quietMode ){
|
||||
System.out.println("");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void outln(Object val){
|
||||
System.out.print(Thread.currentThread().getName()+": ");
|
||||
System.out.println(val);
|
||||
if( !quietMode ){
|
||||
System.out.print(Thread.currentThread().getName()+": ");
|
||||
System.out.println(val);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void out(Object val){
|
||||
System.out.print(val);
|
||||
if( !quietMode ){
|
||||
System.out.print(val);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized static void out(Object... vals){
|
||||
System.out.print(Thread.currentThread().getName()+": ");
|
||||
for(Object v : vals) out(v);
|
||||
if( !quietMode ){
|
||||
System.out.print(Thread.currentThread().getName()+": ");
|
||||
for(Object v : vals) out(v);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized static void outln(Object... vals){
|
||||
out(vals); out("\n");
|
||||
if( !quietMode ){
|
||||
out(vals); out("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static volatile int affirmCount = 0;
|
||||
@ -85,6 +109,7 @@ public class Tester1 implements Runnable {
|
||||
affirm(v, "Affirmation failed.");
|
||||
}
|
||||
|
||||
@ManualTest /* because testing this for threading is pointless */
|
||||
private void test1(){
|
||||
affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER);
|
||||
affirm(SQLITE_MAX_LENGTH > 0);
|
||||
@ -280,6 +305,16 @@ public class Tester1 implements Runnable {
|
||||
affirm(0 != stmt.getNativePointer());
|
||||
sqlite3_finalize(stmt);
|
||||
affirm(0 == stmt.getNativePointer() );
|
||||
|
||||
affirm( 0==sqlite3_errcode(db) );
|
||||
stmt = sqlite3_prepare(db, "intentional error");
|
||||
affirm( null==stmt );
|
||||
affirm( 0!=sqlite3_errcode(db) );
|
||||
affirm( 0==sqlite3_errmsg(db).indexOf("near \"intentional\"") );
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = sqlite3_prepare(db, "/* empty input*/\n-- comments only");
|
||||
affirm( null==stmt );
|
||||
affirm( 0==sqlite3_errcode(db) );
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
@ -383,8 +418,11 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
|
||||
String[] list1 = { "hell🤩", "w😃rld", "!" };
|
||||
int rc;
|
||||
int n = 0;
|
||||
for( String e : list1 ){
|
||||
rc = sqlite3_bind_text(stmt, 1, e);
|
||||
rc = (0==n)
|
||||
? sqlite3_bind_text(stmt, 1, e)
|
||||
: sqlite3_bind_text16(stmt, 1, e);
|
||||
affirm(0 == rc);
|
||||
rc = sqlite3_step(stmt);
|
||||
affirm(SQLITE_DONE==rc);
|
||||
@ -393,7 +431,7 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
|
||||
StringBuilder sbuf = new StringBuilder();
|
||||
int n = 0;
|
||||
n = 0;
|
||||
while( SQLITE_ROW == sqlite3_step(stmt) ){
|
||||
String txt = sqlite3_column_text16(stmt, 0);
|
||||
//outln("txt = "+txt);
|
||||
@ -521,6 +559,7 @@ public class Tester1 implements Runnable {
|
||||
affirm(xDestroyCalled.value);
|
||||
}
|
||||
|
||||
@ManualTest /* because threading is meaningless here */
|
||||
private void testToUtf8(){
|
||||
/**
|
||||
https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html
|
||||
@ -873,6 +912,7 @@ public class Tester1 implements Runnable {
|
||||
affirm( 7 == counter.value );
|
||||
}
|
||||
|
||||
@ManualTest /* because threads inherently break this test */
|
||||
private void testBusy(){
|
||||
final String dbName = "_busy-handler.db";
|
||||
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
|
||||
@ -1156,6 +1196,8 @@ public class Tester1 implements Runnable {
|
||||
it throws.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@ManualTest /* because the Fts5 parts are not yet known to be
|
||||
thread-safe */
|
||||
private void testFts5() throws Exception {
|
||||
if( !sqlite3_compileoption_used("ENABLE_FTS5") ){
|
||||
//outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests.");
|
||||
@ -1206,6 +1248,8 @@ public class Tester1 implements Runnable {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
@ManualTest/* because multiple threads legitimately make these
|
||||
results unpredictable */
|
||||
private synchronized void testAutoExtension(){
|
||||
final ValueHolder<Integer> val = new ValueHolder<>(0);
|
||||
final ValueHolder<String> toss = new ValueHolder<>(null);
|
||||
@ -1296,6 +1340,7 @@ public class Tester1 implements Runnable {
|
||||
affirm( 8 == val.value );
|
||||
}
|
||||
|
||||
@ManualTest /* because we only want to run this test manually */
|
||||
private void testSleep(){
|
||||
out("Sleeping briefly... ");
|
||||
sqlite3_sleep(600);
|
||||
@ -1308,6 +1353,7 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
@ManualTest /* because we only want to run this test on demand */
|
||||
private void testFail(){
|
||||
affirm( false, "Intentional failure." );
|
||||
}
|
||||
@ -1355,6 +1401,9 @@ public class Tester1 implements Runnable {
|
||||
testFts5();
|
||||
}
|
||||
}
|
||||
synchronized( this.getClass() ){
|
||||
++nTestRuns;
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
@ -1375,6 +1424,8 @@ public class Tester1 implements Runnable {
|
||||
|
||||
CLI flags:
|
||||
|
||||
-q|-quiet: disables most test output.
|
||||
|
||||
-t|-thread N: runs the tests in N threads
|
||||
concurrently. Default=1.
|
||||
|
||||
@ -1400,6 +1451,7 @@ public class Tester1 implements Runnable {
|
||||
Integer nRepeat = 1;
|
||||
boolean forceFail = false;
|
||||
boolean sqlLog = false;
|
||||
boolean squelchTestOutput = false;
|
||||
for( int i = 0; i < args.length; ){
|
||||
String arg = args[i++];
|
||||
if(arg.startsWith("-")){
|
||||
@ -1421,6 +1473,8 @@ public class Tester1 implements Runnable {
|
||||
sqlLog = true;
|
||||
}else if(arg.equals("naps")){
|
||||
takeNaps = true;
|
||||
}else if(arg.equals("q") || arg.equals("quiet")){
|
||||
squelchTestOutput = true;
|
||||
}else{
|
||||
throw new IllegalArgumentException("Unhandled flag:"+arg);
|
||||
}
|
||||
@ -1430,19 +1484,16 @@ public class Tester1 implements Runnable {
|
||||
{
|
||||
// Build list of tests to run from the methods named test*().
|
||||
testMethods = new ArrayList<>();
|
||||
final List<String> excludes = new ArrayList<>();
|
||||
// Tests we want to control the order of:
|
||||
if( !forceFail ) excludes.add("testFail");
|
||||
excludes.add("test1");
|
||||
excludes.add("testAutoExtension");
|
||||
excludes.add("testBusy");
|
||||
excludes.add("testFts5");
|
||||
excludes.add("testSleep");
|
||||
excludes.add("testToUtf8");
|
||||
for(java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){
|
||||
for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){
|
||||
final String name = m.getName();
|
||||
if( name.startsWith("test") && excludes.indexOf(name)<0 ){
|
||||
testMethods.add(m);
|
||||
if( name.equals("testFail") ){
|
||||
if( forceFail ){
|
||||
testMethods.add(m);
|
||||
}
|
||||
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
|
||||
if( name.startsWith("test") ){
|
||||
testMethods.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1465,6 +1516,11 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
quietMode = squelchTestOutput;
|
||||
outln("If you just saw warning messages regarding CallStaticObjectMethod, ",
|
||||
"you are very likely seeing the side effects of a known openjdk8 ",
|
||||
"bug. It is unsightly but does not affect the library.");
|
||||
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
int nLoop = 0;
|
||||
affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ),
|
||||
@ -1476,7 +1532,7 @@ public class Tester1 implements Runnable {
|
||||
outln("libversion_number: ",
|
||||
sqlite3_libversion_number(),"\n",
|
||||
sqlite3_libversion(),"\n",SQLITE_SOURCE_ID);
|
||||
outln("Running ",nRepeat," loop(s) over ",nThread," thread(s).");
|
||||
outln("Running ",nRepeat," loop(s) with ",nThread," thread(s) each.");
|
||||
if( takeNaps ) outln("Napping between tests is enabled.");
|
||||
for( int n = 0; n < nRepeat; ++n ){
|
||||
if( nThread==null || nThread<=1 ){
|
||||
@ -1500,6 +1556,7 @@ public class Tester1 implements Runnable {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if( !listErrors.isEmpty() ){
|
||||
quietMode = false;
|
||||
outln("TEST ERRORS:");
|
||||
Exception err = null;
|
||||
for( Exception e : listErrors ){
|
||||
@ -1510,9 +1567,10 @@ public class Tester1 implements Runnable {
|
||||
}
|
||||
}
|
||||
outln();
|
||||
quietMode = false;
|
||||
|
||||
final long timeEnd = System.currentTimeMillis();
|
||||
outln("Tests done. Metrics:");
|
||||
outln("Tests done. Metrics across ",nTestRuns," total iteration(s):");
|
||||
outln("\tAssertions checked: ",affirmCount);
|
||||
outln("\tDatabases opened: ",metrics.dbOpen);
|
||||
if( doSomethingForDev ){
|
||||
|
@ -72,13 +72,18 @@ public class TesterFts5 {
|
||||
affirm( xDestroyCalled.value );
|
||||
}
|
||||
|
||||
public TesterFts5(){
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
final int oldAffirmCount = Tester1.affirmCount;
|
||||
test1();
|
||||
final int affirmCount = Tester1.affirmCount - oldAffirmCount;
|
||||
final long timeEnd = System.currentTimeMillis();
|
||||
outln("FTS5 Tests done. Assertions checked = ",affirmCount,
|
||||
", Total time = ",(timeEnd - timeStart),"ms");
|
||||
public TesterFts5(boolean verbose){
|
||||
if(verbose){
|
||||
final long timeStart = System.currentTimeMillis();
|
||||
final int oldAffirmCount = Tester1.affirmCount;
|
||||
test1();
|
||||
final int affirmCount = Tester1.affirmCount - oldAffirmCount;
|
||||
final long timeEnd = System.currentTimeMillis();
|
||||
outln("FTS5 Tests done. Assertions checked = ",affirmCount,
|
||||
", Total time = ",(timeEnd - timeStart),"ms");
|
||||
}else{
|
||||
test1();
|
||||
}
|
||||
}
|
||||
public TesterFts5(){ this(false); }
|
||||
}
|
||||
|
Reference in New Issue
Block a user