ResultCode.java \
RollbackHookCallback.java \
ScalarFunction.java \
+ Sqlite.java \
+ SqliteException.java \
SQLFunction.java \
CallbackProxy.java \
CApi.java \
)
JAVA_FILES.unittest := $(patsubst %,$(dir.src.jni)/%,\
Tester1.java \
+ Tester2.java \
)
ifeq (1,$(enable.fts5))
JAVA_FILES.unittest += $(patsubst %,$(dir.src.jni)/%,\
all: $(package.dll)
.PHONY: test test-one
-test.flags ?=
-test.main.flags = -ea -Djava.library.path=$(dir.bld.c) \
- $(java.flags) -cp $(classpath) \
- org.sqlite.jni.Tester1
+Tester1.flags ?=
+Tester2.flags ?=
+test.flags.jvm = -ea -Djava.library.path=$(dir.bld.c) \
+ $(java.flags) -cp $(classpath)
test.deps := $(CLASS_FILES) $(package.dll)
test-one: $(test.deps)
- $(bin.java) $(test.main.flags) $(test.flags)
+ $(bin.java) $(test.flags.jvm) org.sqlite.jni.Tester1 $(Tester1.flags)
+# $(bin.java) $(test.flags.jvm) org.sqlite.jni.Tester2 $(Tester2.flags)
test-sqllog: $(test.deps)
@echo "Testing with -sqllog..."
- $(bin.java) $(test.main.flags) -sqllog
+ $(bin.java) $(test.flags.jvm) -sqllog
test-mt: $(test.deps)
@echo "Testing in multi-threaded mode:";
- $(bin.java) $(test.main.flags) -t 7 -r 50 -shuffle $(test.flags)
+ $(bin.java) $(test.flags.jvm) org.sqlite.jni.Tester1 \
+ -t 7 -r 50 -shuffle $(Tester1.flags)
+ $(bin.java) $(test.flags.jvm) org.sqlite.jni.Tester2 \
+ -t 7 -r 50 -shuffle $(Tester2.flags)
test: test-one test-mt
tests: test test-sqllog
--- /dev/null
+/*
+** 2023-10-09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file is part of the JNI bindings for the sqlite3 C API.
+*/
+package org.sqlite.jni;
+import static org.sqlite.jni.CApi.*;
+
+/**
+ This class represents a database connection, analog to the C-side
+ sqlite3 class but with added argument validation, exceptions, and
+ similar "smoothing of sharp edges" to make the API safe to use from
+ Java. It also acts as a namespace for other types for which
+ individual instances are tied to a specific database connection.
+*/
+public final class Sqlite implements AutoCloseable {
+ private sqlite3 db = null;
+
+ //! Used only by the open() factory functions.
+ private Sqlite(sqlite3 db){
+ this.db = db;
+ }
+
+ public static Sqlite open(String filename, int flags, String zVfs){
+ final OutputPointer.sqlite3 out = new OutputPointer.sqlite3();
+ final int rc = sqlite3_open_v2(filename, out, flags, zVfs);
+ final sqlite3 n = out.take();
+ if( 0!=rc ){
+ if( null==n ) throw new SqliteException(rc);
+ else throw new SqliteException(n);
+ }
+ return new Sqlite(n);
+ }
+
+ public static Sqlite open(String filename, int flags){
+ return open(filename, flags, null);
+ }
+
+ public static Sqlite open(String filename){
+ return open(filename, 0, null);
+ }
+
+ @Override public void close(){
+ if(null!=this.db){
+ this.db.close();
+ this.db = null;
+ }
+ }
+
+ sqlite3 dbHandle(){ return this.db; }
+
+}
--- /dev/null
+/*
+** 2023-10-09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file is part of the JNI bindings for the sqlite3 C API.
+*/
+package org.sqlite.jni;
+import static org.sqlite.jni.CApi.*;
+
+/**
+ A wrapper for communicating C-level (sqlite3*) instances with
+ Java. These wrappers do not own their associated pointer, they
+ simply provide a type-safe way to communicate it between Java
+ and C via JNI.
+*/
+public final class SqliteException extends java.lang.RuntimeException {
+
+ public SqliteException(String msg){
+ super(msg);
+ }
+
+ public SqliteException(int sqlite3ResultCode){
+ super(sqlite3_errstr(sqlite3ResultCode));
+ }
+
+ public SqliteException(sqlite3 db){
+ super(sqlite3_errmsg(db));
+ db.close();
+ }
+
+ public SqliteException(Sqlite db){
+ this(db.dbHandle());
+ }
+
+}
sqlite3_shutdown();
int nMethods = 0;
int nNatives = 0;
- int nCanonical = 0;
final java.lang.reflect.Method[] declaredMethods =
CApi.class.getDeclaredMethods();
for(java.lang.reflect.Method m : declaredMethods){
final String name = m.getName();
if(name.startsWith("sqlite3_")){
++nMethods;
- if( m.isAnnotationPresent( org.sqlite.jni.annotation.Canonical.class ) ){
- ++nCanonical;
- }
if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
++nNatives;
}
}
}
}
- outln("\tSQLite3Jni.sqlite3_*() methods: "+
+ outln("\tCApi.sqlite3_*() methods: "+
nMethods+" total, with "+
nNatives+" native, "+
- (nMethods - nNatives)+" Java, ",
- nCanonical," @Canonical"
+ (nMethods - nNatives)+" Java"
);
outln("\tTotal test time = "
+(timeEnd - timeStart)+"ms");
--- /dev/null
+/*
+** 2023-10-09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains a set of tests for the sqlite3 JNI bindings.
+*/
+package org.sqlite.jni;
+import static org.sqlite.jni.CApi.*;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class Tester2 implements Runnable {
+ //! True when running in multi-threaded mode.
+ private static boolean mtMode = false;
+ //! True to sleep briefly between tests.
+ private static boolean takeNaps = false;
+ //! True to shuffle the order of the tests.
+ 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()
+ private static List<Exception> listErrors = new ArrayList<>();
+ private static final class Metrics {
+ //! Number of times createNewDb() (or equivalent) is invoked.
+ volatile int dbOpen = 0;
+ }
+
+ //! Instance ID.
+ private Integer tId;
+
+ Tester2(Integer id){
+ tId = id;
+ }
+
+ static final Metrics metrics = new Metrics();
+
+ public static synchronized void outln(){
+ if( !quietMode ){
+ System.out.println("");
+ }
+ }
+
+ public static synchronized void outPrefix(){
+ if( !quietMode ){
+ System.out.print(Thread.currentThread().getName()+": ");
+ }
+ }
+
+ public static synchronized void outln(Object val){
+ if( !quietMode ){
+ outPrefix();
+ System.out.println(val);
+ }
+ }
+
+ public static synchronized void out(Object val){
+ if( !quietMode ){
+ System.out.print(val);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static synchronized void out(Object... vals){
+ if( !quietMode ){
+ outPrefix();
+ for(Object v : vals) out(v);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static synchronized void outln(Object... vals){
+ if( !quietMode ){
+ out(vals); out("\n");
+ }
+ }
+
+ static volatile int affirmCount = 0;
+ public static synchronized int affirm(Boolean v, String comment){
+ ++affirmCount;
+ if( false ) assert( v /* prefer assert over exception if it's enabled because
+ the JNI layer sometimes has to suppress exceptions,
+ so they might be squelched on their way back to the
+ top. */);
+ if( !v ) throw new RuntimeException(comment);
+ return affirmCount;
+ }
+
+ public static void affirm(Boolean v){
+ affirm(v, "Affirmation failed.");
+ }
+
+ @SingleThreadOnly /* because it's thread-agnostic */
+ private void test1(){
+ affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER);
+ }
+
+ /* Copy/paste/rename this to add new tests. */
+ private void _testTemplate(){
+ //final sqlite3 db = createNewDb();
+ //sqlite3_stmt stmt = prepare(db,"SELECT 1");
+ //sqlite3_finalize(stmt);
+ //sqlite3_close_v2(db);
+ }
+
+ private void nap() throws InterruptedException {
+ if( takeNaps ){
+ Thread.sleep(java.util.concurrent.ThreadLocalRandom.current().nextInt(3, 17), 0);
+ }
+ }
+
+ @ManualTest /* because we only want to run this test on demand */
+ private void testFail(){
+ affirm( false, "Intentional failure." );
+ }
+
+ private void runTests(boolean fromThread) throws Exception {
+ List<java.lang.reflect.Method> mlist = testMethods;
+ affirm( null!=mlist );
+ if( shuffle ){
+ mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
+ java.util.Collections.shuffle(mlist);
+ }
+ if( listRunTests ){
+ synchronized(this.getClass()){
+ if( !fromThread ){
+ out("Initial test"," list: ");
+ for(java.lang.reflect.Method m : testMethods){
+ out(m.getName()+" ");
+ }
+ outln();
+ outln("(That list excludes some which are hard-coded to run.)");
+ }
+ out("Running"," tests: ");
+ for(java.lang.reflect.Method m : mlist){
+ out(m.getName()+" ");
+ }
+ outln();
+ }
+ }
+ for(java.lang.reflect.Method m : mlist){
+ nap();
+ try{
+ m.invoke(this);
+ }catch(java.lang.reflect.InvocationTargetException e){
+ outln("FAILURE: ",m.getName(),"(): ", e.getCause());
+ throw e;
+ }
+ }
+ synchronized( this.getClass() ){
+ ++nTestRuns;
+ }
+ }
+
+ public void run() {
+ try {
+ runTests(0!=this.tId);
+ }catch(Exception e){
+ synchronized( listErrors ){
+ listErrors.add(e);
+ }
+ }finally{
+ affirm( sqlite3_java_uncache_thread() );
+ affirm( !sqlite3_java_uncache_thread() );
+ }
+ }
+
+ /**
+ Runs the basic sqlite3 JNI binding sanity-check suite.
+
+ CLI flags:
+
+ -q|-quiet: disables most test output.
+
+ -t|-thread N: runs the tests in N threads
+ concurrently. Default=1.
+
+ -r|-repeat N: repeats the tests in a loop N times, each one
+ consisting of the -thread value's threads.
+
+ -shuffle: randomizes the order of most of the test functions.
+
+ -naps: sleep small random intervals between tests in order to add
+ some chaos for cross-thread contention.
+
+ -list-tests: outputs the list of tests being run, minus some
+ which are hard-coded. This is noisy in multi-threaded mode.
+
+ -fail: forces an exception to be thrown during the test run. Use
+ with -shuffle to make its appearance unpredictable.
+
+ -v: emit some developer-mode info at the end.
+ */
+ public static void main(String[] args) throws Exception {
+ Integer nThread = 1;
+ boolean doSomethingForDev = false;
+ Integer nRepeat = 1;
+ boolean forceFail = false;
+ boolean sqlLog = false;
+ boolean configLog = false;
+ boolean squelchTestOutput = false;
+ for( int i = 0; i < args.length; ){
+ String arg = args[i++];
+ if(arg.startsWith("-")){
+ arg = arg.replaceFirst("-+","");
+ if(arg.equals("v")){
+ doSomethingForDev = true;
+ //listBoundMethods();
+ }else if(arg.equals("t") || arg.equals("thread")){
+ nThread = Integer.parseInt(args[i++]);
+ }else if(arg.equals("r") || arg.equals("repeat")){
+ nRepeat = Integer.parseInt(args[i++]);
+ }else if(arg.equals("shuffle")){
+ shuffle = true;
+ }else if(arg.equals("list-tests")){
+ listRunTests = true;
+ }else if(arg.equals("fail")){
+ forceFail = true;
+ }else if(arg.equals("sqllog")){
+ sqlLog = true;
+ }else if(arg.equals("configlog")){
+ configLog = 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);
+ }
+ }
+ }
+
+ if( sqlLog ){
+ if( sqlite3_compileoption_used("ENABLE_SQLLOG") ){
+ final ConfigSqllogCallback log = new ConfigSqllogCallback() {
+ @Override public void call(sqlite3 db, String msg, int op){
+ switch(op){
+ case 0: outln("Opening db: ",db); break;
+ case 1: outln("SQL ",db,": ",msg); break;
+ case 2: outln("Closing db: ",db); break;
+ }
+ }
+ };
+ int rc = sqlite3_config( log );
+ affirm( 0==rc );
+ rc = sqlite3_config( (ConfigSqllogCallback)null );
+ affirm( 0==rc );
+ rc = sqlite3_config( log );
+ affirm( 0==rc );
+ }else{
+ outln("WARNING: -sqllog is not active because library was built ",
+ "without SQLITE_ENABLE_SQLLOG.");
+ }
+ }
+ if( configLog ){
+ final ConfigLogCallback log = new ConfigLogCallback() {
+ @Override public void call(int code, String msg){
+ outln("ConfigLogCallback: ",ResultCode.getEntryForInt(code),": ", msg);
+ };
+ };
+ int rc = sqlite3_config( log );
+ affirm( 0==rc );
+ rc = sqlite3_config( (ConfigLogCallback)null );
+ affirm( 0==rc );
+ rc = sqlite3_config( log );
+ affirm( 0==rc );
+ }
+
+ 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.");
+
+ {
+ // Build list of tests to run from the methods named test*().
+ testMethods = new ArrayList<>();
+ int nSkipped = 0;
+ for(final java.lang.reflect.Method m : Tester2.class.getDeclaredMethods()){
+ final String name = m.getName();
+ if( name.equals("testFail") ){
+ if( forceFail ){
+ testMethods.add(m);
+ }
+ }else if( !m.isAnnotationPresent( ManualTest.class ) ){
+ if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
+ if( 0==nSkipped++ ){
+ out("Skipping tests in multi-thread mode:");
+ }
+ out(" "+name+"()");
+ }else if( name.startsWith("test") ){
+ testMethods.add(m);
+ }
+ }
+ }
+ if( nSkipped>0 ) out("\n");
+ }
+
+ final long timeStart = System.currentTimeMillis();
+ int nLoop = 0;
+ switch( sqlite3_threadsafe() ){ /* Sanity checking */
+ case 0:
+ affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ),
+ "Could not switch to single-thread mode." );
+ affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ),
+ "Could switch to multithread mode." );
+ affirm( SQLITE_ERROR==sqlite3_config( SQLITE_CONFIG_SERIALIZED ),
+ "Could not switch to serialized threading mode." );
+ outln("This is a single-threaded build. Not using threads.");
+ nThread = 1;
+ break;
+ case 1:
+ case 2:
+ affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ),
+ "Could not switch to single-thread mode." );
+ affirm( 0==sqlite3_config( SQLITE_CONFIG_MULTITHREAD ),
+ "Could not switch to multithread mode." );
+ affirm( 0==sqlite3_config( SQLITE_CONFIG_SERIALIZED ),
+ "Could not switch to serialized threading mode." );
+ break;
+ default:
+ affirm( false, "Unhandled SQLITE_THREADSAFE value." );
+ }
+ outln("libversion_number: ",
+ sqlite3_libversion_number(),"\n",
+ sqlite3_libversion(),"\n",SQLITE_SOURCE_ID,"\n",
+ "SQLITE_THREADSAFE=",sqlite3_threadsafe());
+ final boolean showLoopCount = (nRepeat>1 && nThread>1);
+ if( showLoopCount ){
+ 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 ){
+ ++nLoop;
+ if( showLoopCount ) out((1==nLoop ? "" : " ")+nLoop);
+ if( nThread<=1 ){
+ new Tester2(0).runTests(false);
+ continue;
+ }
+ Tester2.mtMode = true;
+ final ExecutorService ex = Executors.newFixedThreadPool( nThread );
+ for( int i = 0; i < nThread; ++i ){
+ ex.submit( new Tester2(i), i );
+ }
+ ex.shutdown();
+ try{
+ ex.awaitTermination(nThread*200, java.util.concurrent.TimeUnit.MILLISECONDS);
+ ex.shutdownNow();
+ }catch (InterruptedException ie){
+ ex.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
+ if( !listErrors.isEmpty() ){
+ quietMode = false;
+ outln("TEST ERRORS:");
+ Exception err = null;
+ for( Exception e : listErrors ){
+ e.printStackTrace();
+ if( null==err ) err = e;
+ }
+ if( null!=err ) throw err;
+ }
+ }
+ if( showLoopCount ) outln();
+ quietMode = false;
+
+ final long timeEnd = System.currentTimeMillis();
+ outln("Tests done. Metrics across ",nTestRuns," total iteration(s):");
+ outln("\tAssertions checked: ",affirmCount);
+ outln("\tDatabases opened: ",metrics.dbOpen);
+ if( doSomethingForDev ){
+ sqlite3_jni_internal_details();
+ }
+ affirm( 0==sqlite3_release_memory(1) );
+ sqlite3_shutdown();
+ int nMethods = 0;
+ int nNatives = 0;
+ int nCanonical = 0;
+ final java.lang.reflect.Method[] declaredMethods =
+ CApi.class.getDeclaredMethods();
+ for(java.lang.reflect.Method m : declaredMethods){
+ final int mod = m.getModifiers();
+ if( 0!=(mod & java.lang.reflect.Modifier.STATIC) ){
+ final String name = m.getName();
+ if(name.startsWith("sqlite3_")){
+ ++nMethods;
+ if( 0!=(mod & java.lang.reflect.Modifier.NATIVE) ){
+ ++nNatives;
+ }
+ }
+ }
+ }
+ outln("\tCApi.sqlite3_*() methods: "+
+ nMethods+" total, with "+
+ nNatives+" native, "+
+ (nMethods - nNatives)+" Java"
+ );
+ outln("\tTotal test time = "
+ +(timeEnd - timeStart)+"ms");
+ }
+}
*/
public static native Fts5ExtensionApi getInstance();
- @Canonical
public native int xColumnCount(@NotNull Fts5Context fcx);
- @Canonical
public native int xColumnSize(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.Int32 pnToken);
- @Canonical
public native int xColumnText(@NotNull Fts5Context cx, int iCol,
@NotNull OutputPointer.String txt);
- @Canonical
public native int xColumnTotalSize(@NotNull Fts5Context fcx, int iCol,
@NotNull OutputPointer.Int64 pnToken);
- @Canonical
public native Object xGetAuxdata(@NotNull Fts5Context cx, boolean clearIt);
- @Canonical
public native int xInst(@NotNull Fts5Context cx, int iIdx,
@NotNull OutputPointer.Int32 piPhrase,
@NotNull OutputPointer.Int32 piCol,
@NotNull OutputPointer.Int32 piOff);
- @Canonical
public native int xInstCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int32 pnInst);
- @Canonical
public native int xPhraseCount(@NotNull Fts5Context fcx);
- @Canonical
public native int xPhraseFirst(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
- @Canonical
public native int xPhraseFirstColumn(@NotNull Fts5Context cx, int iPhrase,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
- @Canonical
public native void xPhraseNext(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol,
@NotNull OutputPointer.Int32 iOff);
- @Canonical
public native void xPhraseNextColumn(@NotNull Fts5Context cx,
@NotNull Fts5PhraseIter iter,
@NotNull OutputPointer.Int32 iCol);
- @Canonical
public native int xPhraseSize(@NotNull Fts5Context fcx, int iPhrase);
- @Canonical
public native int xQueryPhrase(@NotNull Fts5Context cx, int iPhrase,
@NotNull XQueryPhraseCallback callback);
- @Canonical
public native int xRowCount(@NotNull Fts5Context fcx,
@NotNull OutputPointer.Int64 nRow);
- @Canonical
public native long xRowid(@NotNull Fts5Context cx);
/* Note that the JNI binding lacks the C version's xDelete()
callback argument. Instead, if pAux has an xDestroy() method, it
pAux held by the JNI layer will be relinquished regardless of
whether pAux has an xDestroy() method. */
- @Canonical
public native int xSetAuxdata(@NotNull Fts5Context cx, @Nullable Object pAux);
- @Canonical
public native int xTokenize(@NotNull Fts5Context cx, @NotNull byte[] pText,
@NotNull XTokenizeCallback callback);
- @Canonical
public native Object xUserData(Fts5Context cx);
//^^^ returns the pointer passed as the 3rd arg to the C-level
// fts5_api::xCreateFunction().
*/
public static synchronized native fts5_api getInstanceForDb(@NotNull sqlite3 db);
- @Canonical
public synchronized native int xCreateFunction(@NotNull String name,
@Nullable Object userData,
@NotNull fts5_extension_function xFunction);
;
}
- @Override protected void finalize(){
- //System.out.println(this+".finalize()");
- CApi.sqlite3_close_v2(this.clearNativePointer());
- }
-
@Override public void close(){
CApi.sqlite3_close_v2(this.clearNativePointer());
}
// Only invoked from JNI.
private sqlite3_stmt(){}
- //For as-yet-unknown reasons, this triggers a JVM crash.
- //@Override protected void finalize(){
- // CApi.sqlite3_finalize(this.clearNativePointer());
- //}
-
@Override public void close(){
CApi.sqlite3_finalize(this.clearNativePointer());
}
-C Minor\sJNI\sdoc\sand\spublic/private\scleanups.
-D 2023-10-09T10:44:10.717
+C Add\sJNI\sSqlite\sand\sSqliteException\sclasses.\sAdd\sTester2.java\sas\sthe\smain\stest\sapp\sfor\sthe\shigh-level\sAPI.
+D 2023-10-09T11:46:32.020
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 8c942ede995d4e42f79c838c03b913dde62f573b443fe8f308dae547bcf04d0b
+F ext/jni/GNUmakefile 8c44e22bad18ecc266dd8c521f215e95dc3741d9e337c51b175029abaedcfb35
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c fb8f178d27df828e3c797b4427a0a20545b44f5147ce38d09ce9b465be5a840b
F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c
F ext/jni/src/org/sqlite/jni/SQLTester.java d246c67f93e2fa2603bd106dbb3246ea725c987dffd6e5d42214ae262f750c68
F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
+F ext/jni/src/org/sqlite/jni/Sqlite.java 04c1efa37eda02e5695a890479f2de5cc54abdbec61042611419f0141e05f83b
+F ext/jni/src/org/sqlite/jni/SqliteException.java cabf37b09f519b6b8aebecd100e7f66fffc1d244d3e46f62c2b12120b9e2a33c
F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab
-F ext/jni/src/org/sqlite/jni/Tester1.java 9326bf645accee7b4a509b7a4db1fa2ba46efaf131e0d53816a6ad109056cd97
+F ext/jni/src/org/sqlite/jni/Tester1.java f7b85fe24cf6c3e43bdf7e390617657e8137359f804d76921829c2a8c41b6df1
+F ext/jni/src/org/sqlite/jni/Tester2.java 75aa079e2baf8f73d95299da092e611656be0f6e12fe2fa051fdd984657857e2
F ext/jni/src/org/sqlite/jni/TesterFts5.java d60fe9944a81156b3b5325dd1b0e8e92a1547468f39fd1266d06f7bb6a95fa70
F ext/jni/src/org/sqlite/jni/TraceV2Callback.java f157edd9c72e7d2243c169061487cd7bb51a0d50f3ac976dbcbbacf748ab1fc2
F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 959d4677a857c9079c6e96ddd10918b946d68359af6252b6f284379069ea3d27
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
F ext/jni/src/org/sqlite/jni/fts5/Fts5.java e94681023785f1eff5399f0ddc82f46b035977d350f14838db659236ebdf6b41
F ext/jni/src/org/sqlite/jni/fts5/Fts5Context.java 7058da97059b8e156c17561a47ecd7faa0fc3e2d8c2588b9a28dbff8d06202dd
-F ext/jni/src/org/sqlite/jni/fts5/Fts5ExtensionApi.java e2680721bd83129d0d650ba845b44d7634a9489a90a56c5ce3c54508bf470743
+F ext/jni/src/org/sqlite/jni/fts5/Fts5ExtensionApi.java c8e06475a6172a7cd61b2bad9cfb18b6f059ffdd2935e62856f95785a14fe0e5
F ext/jni/src/org/sqlite/jni/fts5/Fts5PhraseIter.java 2a7f3d76a1206e6a43d4c4ed9609b294d5431cc7d8fb875d8419f76efa6e56dc
F ext/jni/src/org/sqlite/jni/fts5/Fts5Tokenizer.java cc9a53846a168a215238af224c31cef0e8379780e36e8a5e743b00c08145cf19
F ext/jni/src/org/sqlite/jni/fts5/XTokenizeCallback.java 1efd1220ea328a32f2d2a1b16c735864159e929480f71daad4de9d5944839167
-F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java e2ad9bc06a9d307e0a6221c11645783898906455a92b1f7d5ec9b9ff1af1b8ea
+F ext/jni/src/org/sqlite/jni/fts5/fts5_api.java 90f09477331c371a8abe0a6504cfe094bc075b29a800be9d72a2c92a7bb49db1
F ext/jni/src/org/sqlite/jni/fts5/fts5_extension_function.java 1fe0f5692c1d67475d12b067f0469949073446f18c56eba5ee5da6ddd06db9b9
F ext/jni/src/org/sqlite/jni/fts5/fts5_tokenizer.java ea993738b851038c16d98576abd0db3d6028a231f075a394fb8a78c7834d0f6c
F ext/jni/src/org/sqlite/jni/package-info.java 7d465cbdf9050761db0db6d0c542afaaad7dc67f61510860592159c48bfc40e8
-F ext/jni/src/org/sqlite/jni/sqlite3.java 3247101956b66c0a59333d3ddd3550eadf24b37c1ab15c322efbed5b221d0f08
+F ext/jni/src/org/sqlite/jni/sqlite3.java 4fa76f9c618264ed17ab613570076002c0b78717261b263350cd92d6d6b01242
F ext/jni/src/org/sqlite/jni/sqlite3_backup.java 42db8b2f9cd8e9e16217273890e5d4afbb102603d7130a2cb1651f1c69c1cfa4
F ext/jni/src/org/sqlite/jni/sqlite3_blob.java 7c341bca1856475fc3bf3697251e0cf1d737ddcb099c65d90afdc164aaddcc51
F ext/jni/src/org/sqlite/jni/sqlite3_context.java ba8da75eaaeb557c986af3fb4dbc69501cf2b083ca33497f2c0c70dbc0a53f2c
-F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java 38e5e88e79cde51a1dbeacfdbe7857b93ca105972f47c1198a51f0eaa48886f5
+F ext/jni/src/org/sqlite/jni/sqlite3_stmt.java fa0703004721c49d6d08a0c1e99439fadb8cebaebf42b81ee3f427d7f950d1eb
F ext/jni/src/org/sqlite/jni/sqlite3_value.java 3d1d4903e267bc0bc81d57d21f5e85978eff389a1a6ed46726dbe75f85e6914a
F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe59004a1485311c50a13edbf18c79a6ff9160030e
F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P ca216b4486aff7d206ebfc3a5e84d48919c282425d4313396bc19313ffca8a0e
-R af6221100b1b0f5eb240f10a6b3978ab
+P c49d36ece283274963ce2e5a4db1e8f586dffa22e47f4adb93c625f918c3fd5d
+R fe6b079fd83fb48b0f0881df379b853e
U stephan
-Z ff4bc4025115006080a68d26c19680b4
+Z 8f2bcd17cd435607663eb211630fda5f
# Remove this line to create a well-formed Fossil manifest.
-c49d36ece283274963ce2e5a4db1e8f586dffa22e47f4adb93c625f918c3fd5d
\ No newline at end of file
+6acf52be7abce8dcf434c5ebf0d5e88859b033e6418077846247ecf00ccf9381
\ No newline at end of file