$(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
$(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE)
$(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \
- $(sqlite3-jni.c) -shared -o $@ -lpthread
+ $(sqlite3-jni.c) -shared -o $@
all: $(sqlite3-jni.dll)
.PHONY: test
-test.flags ?= -v
+test.flags ?=
+test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
+ $(java.flags) -cp $(classpath) \
+ org.sqlite.jni.Tester1
test: $(SQLite3Jni.class) $(sqlite3-jni.dll)
- $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
- $(java.flags) -cp $(classpath) \
- org.sqlite.jni.Tester1 $(test.flags)
-test-mt: $(SQLite3Jni.class) $(sqlite3-jni.dll)
- $(bin.java) -ea -Djava.library.path=$(dir.bld.c) \
- $(java.flags) -cp $(classpath) \
- org.sqlite.jni.Tester1 -t 7 -r 50 -shuffle $(test.flags)
+ $(bin.java) $(test.main.flags) $(test.flags)
+ @echo "Again in multi-threaded mode:"
+ $(bin.java) $(test.main.flags) -t 5 -r 20 -shuffle $(test.flags)
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
tester.flags ?= # --verbose
tester-ext: tester-local
tester: tester-ext
-tests: test test-mt tester
+tests: test tester
package.jar.in := $(abspath $(dir.src)/jar.in)
CLEAN_FILES += $(package.jar.in)
$(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main)
> **FOREWARNING:** this subproject is very much in development and
subject to any number of changes. Please do not rely on any
information about its API until this disclaimer is removed. The JNI
- bindgins released with version 3.43 are a "tech preview" and 3.44
+ bindings released with version 3.43 are a "tech preview" and 3.44
will be "final," at which point strong backward compatibility
guarantees will apply.
- Creation of high-level OO wrapper APIs. Clients are free to create
them off of the C-style API.
+
Hello World
-----------------------------------------------------------------------
```java
import org.sqlite.jni.*;
-import static org.sqlite.jni.SQLite3Jni;
+import static SQLite3Jni.*;
+
...
-OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
-int rc = sqlite3_open(":memory:", out);
-final sqlite3 db = out.take();
-if( 0 != rc ){
- if( null != db ){
- System.out.print("Error opening db: "+sqlite3_errmsg(db));
- sqlite3_close(db);
- }else{
- System.out.print("Error opening db: rc="+rc);
+
+final sqlite3 db = sqlite3_open(":memory:");
+try {
+ final int rc = sqlite3_errcode(db);
+ if( 0 != rc ){
+ if( null != db ){
+ System.out.print("Error opening db: "+sqlite3_errmsg(db));
+ }else{
+ System.out.print("Error opening db: rc="+rc);
+ }
+ ... handle error ...
}
- ... handle error ...
+ // ... else use the db ...
+}finally{
+ // ALWAYS close databases using sqlite3_close() or sqlite3_close_v2()
+ // when done with them. All of their active statement handles must
+ // first have been passed to sqlite3_finalize().
+ sqlite3_close_v2(db);
}
-
-... use db ...
-
-sqlite3_close_v2(db);
```
Building
$ make clean
```
-The jar distribution can be created with `make jar`.
+The jar distribution can be created with `make jar`, but note that it
+does not contain the binary DLL file. A different DLL is needed for
+each target platform.
+
<a id='1to1ish'></a>
One-to-One(-ish) Mapping to C
========================================================================
This JNI binding aims to provide as close to a 1-to-1 experience with
-the C API as cross-language semantics allow. Changes are necessarily
-made where cross-language semantics do not allow a 1-to-1, and
-judiciously made where a 1-to-1 mapping would be unduly cumbersome to
-use in Java.
+the C API as cross-language semantics allow. Interface changes are
+necessarily made where cross-language semantics do not allow a 1-to-1,
+and judiciously made where a 1-to-1 mapping would be unduly cumbersome
+to use in Java. In all cases, this binding makes every effort to
+provide semantics compatible with the C API documentation even if the
+interface to those semantics is slightly different. Any cases which
+deviate from those semantics (either removing or adding semantics) are
+clearly documented.
+
+Where it makes sense to do so for usability, Java-side overloads are
+provided which accept or return data in alternative forms or provide
+sensible default argument values. In all such cases they are thin
+proxies around the corresponding C APIs and do not introduce new
+semantics.
+
+In some very few cases, Java-specific capabilities have been added in
+new APIs, all of which have "_java" somewhere in their names.
+Examples include:
+
+- `sqlite3_result_java_object()`
+- `sqlite3_column_java_object()`
+- `sqlite3_column_java_casted()`
+- `sqlite3_value_java_object()`
+- `sqlite3_value_java_casted()`
+
+which, as one might surmise, collectively enable the passing of
+arbitrary Java objects from user-defined SQL functions through to the
+caller.
+
+
+Golden Rule: Garbage Collection Cannot Free SQLite Resources
+------------------------------------------------------------------------
+
+It is important that all databases and prepared statement handles get
+cleaned up by client code. A database cannot be closed if it has open
+statement handles. `sqlite3_close()` fails if the db cannot be closed
+whereas `sqlite3_close_v2()` recognizes that case and marks the db as
+a "zombie," pending finalization when the library detects that all
+pending statements have been closed. Be aware that Java garbage
+collection _cannot_ close a database or finalize a prepared statement.
+Those things require explicit API calls.
+
-Golden Rule: _Never_ Throw from Callbacks (Unless...)
+Golden Rule #2: _Never_ Throw from Callbacks (Unless...)
------------------------------------------------------------------------
+All routines in this API, barring explicitly documented exceptions,
+retain C-like semantics. For example, they are not permitted to throw
+or propagate exceptions and must return error information (if any) via
+result codes or `null`. The only cases where the C-style APIs may
+throw is through client-side misuse, e.g. passing in a null where it
+shouldn't be used. The APIs clearly mark function parameters which
+should not be null, but does not actively defend itself against such
+misuse. Some C-style APIs explicitly accept `null` as a no-op for
+usability's sake, and some of the JNI APIs deliberately return an
+error code, instead of segfaulting, when passed a `null`.
+
Client-defined callbacks _must never throw exceptions_ unless _very
explicitly documented_ as being throw-safe. Exceptions are generally
reserved for higher-level bindings which are constructed to
specifically deal with them and ensure that they do not leak C-level
-resources. In some cases, callback handlers (see below) are permitted
-to throw, in which cases they get translated to C-level result codes
-and/or messages.
+resources. In some cases, callback handlers are permitted to throw, in
+which cases they get translated to C-level result codes and/or
+messages. If a callback which is not permitted to throw throws, its
+exception may trigger debug output but will otherwise be suppressed.
+
+The reason some callbacks are permitted to throw and others not is
+because all such callbacks act as proxies for C function callback
+interfaces and some of those interfaces have no error-reporting
+mechanism. Those which are capable of propagating errors back through
+the library convert exceptions from callbacks into corresponding
+C-level error information. Those which cannot propagate errors
+necessarily suppress any exceptions in order to maintain the C-style
+semantics of the APIs.
Awkward Callback Names
Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and
`sqlite3_update_hook()`, use interfaces similar to those shown above.
-
+Despite the changes in signature, the JNI layer makes every effort to
+provide the same semantics as the C API documentation suggests.
echo "*** to configure it for your system. ***"; \
echo "************************************************************************"
$(CC) $(CFLAGS) $(SQLITE_OPT) \
- src/sqlite3-jni.c -lpthread -shared -o $@
+ src/sqlite3-jni.c -shared -o $@
@echo "Now try running it with: make test"
+test.flags = -Djava.library.path=. sqlite3-jni-*.jar
test: $(sqlite3-jni.dll)
- java -jar -Djava.library.path=. sqlite3-jni-*.jar
+ java -jar $(test.flags)
+ java -jar $(test.flags) -t 7 -r 10 -shuffle
clean:
-rm -f $(sqlite3-jni.dll)
} 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
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;
}
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,
}
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");
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
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.
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
);
@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
);
}
/**
- 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
){
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
){
: 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
);
/**
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
@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.
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,
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,
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
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;
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()
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;
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);
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);
}
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);
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);
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
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();
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.");
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);
affirm( 8 == val.value );
}
+ @ManualTest /* because we only want to run this test manually */
private void testSleep(){
out("Sleeping briefly... ");
sqlite3_sleep(600);
}
}
+ @ManualTest /* because we only want to run this test on demand */
private void testFail(){
affirm( false, "Intentional failure." );
}
testFts5();
}
}
+ synchronized( this.getClass() ){
+ ++nTestRuns;
+ }
}
public void run() {
CLI flags:
+ -q|-quiet: disables most test output.
+
-t|-thread N: runs the tests in N threads
concurrently. Default=1.
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("-")){
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);
}
{
// 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);
+ }
}
}
}
}
}
+ 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 ),
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 ){
Thread.currentThread().interrupt();
}
if( !listErrors.isEmpty() ){
+ quietMode = false;
outln("TEST ERRORS:");
Exception err = null;
for( Exception e : listErrors ){
}
}
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 ){
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); }
}
-C JNI\scleanups\sregarding\sbuilding\swith\scertain\sfeatures\sdisabled.
-D 2023-08-23T17:52:51.175
+C Add\smore\sJNI\sdocs,\stests,\sand\sa\shandful\sof\sJava-side\soverloads.
+D 2023-08-24T11:57:51.863
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
-F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5
-F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0
-F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3
-F ext/jni/src/c/sqlite3-jni.c 0ca96134d7fb3f313a7a49487f68a8d7a6d7545470c84532aa1ce63d2cdc432e
-F ext/jni/src/c/sqlite3-jni.h c5cb0348efe4e5f3d125a240e2437e8475de14a586c2f859e2acdcde4116244d
+F ext/jni/GNUmakefile 0a823c56f081294e7797dae303380ac989ebaa801bba970968342b7358f07aed
+F ext/jni/README.md 64bf1da0d562d051207ca1c5cfa52e8b7a69120533cc034a3da7670ef920cbef
+F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
+F ext/jni/src/c/sqlite3-jni.c e1e3cde4d08925282b5bc949f9ed8f613a6a2c6f60d0c697e79d59fb49f9fe4b
+F ext/jni/src/c/sqlite3-jni.h cc24d6742b29a52338ffd3b47caf923facb8ae77f9c2fc9c2de82673bf339ea2
F ext/jni/src/org/sqlite/jni/Authorizer.java 1308988f7f40579ea0e4deeaec3c6be971630566bd021c31367fe3f5140db892
-F ext/jni/src/org/sqlite/jni/AutoExtension.java 3b62c915e45ce73f63343ca9195ec63592244d616a1908b7587bdd45de1b97dd
+F ext/jni/src/org/sqlite/jni/AutoExtension.java bcc1849b2fccbe5e2d7ac9e9ac7f8d05a6d7088a8fedbaad90e39569745a61e6
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
F ext/jni/src/org/sqlite/jni/CollationNeeded.java ad67843b6dd1c06b6b0a1dc72887b7c48e2a98042fcf6cacf14d42444037eab8
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
F ext/jni/src/org/sqlite/jni/SQLFunction.java f697cf2a81c4119f2baf0682af689686f0466f1dd83dba00885f5603e693fe16
F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java f64554457fa30a048ef99374bfac3c4b986e3528353dce3086a98a858e3fe000
-F ext/jni/src/org/sqlite/jni/Tester1.java 69ea63a5b235f94f914dff6fe3ecd103ee0a8023b8737db071b46c0c75375e26
-F ext/jni/src/org/sqlite/jni/TesterFts5.java de095e3b701fba0c56d7b8b2993dc22bcbaa9de8f992904a93729ad729a91576
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 956063c854c4f662183c41c65d0ab48b5e2127824b8053eeb05b9fc40f0d09e3
+F ext/jni/src/org/sqlite/jni/Tester1.java b5a4bb2a969df053d5c138887f04039a79b36170372a2efdf5dfbd6ac90db4c9
+F ext/jni/src/org/sqlite/jni/TesterFts5.java 6f135c60e24c89e8eecb9fe61dde0f3bb2906de668ca6c9186bcf34bdaf94629
F ext/jni/src/org/sqlite/jni/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0
-R b911867dcaf18c3e131e156c82d306fe
+P a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18
+R cb79df5ad40cc86377d98f5a1329b589
U stephan
-Z bdef86836c9254e732e1b6d744febf17
+Z 39e8fbdcbc2ebaa58ed84b96f0374c06
# Remove this line to create a well-formed Fossil manifest.
-a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18
\ No newline at end of file
+d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0
\ No newline at end of file