1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-01 06:27:03 +03:00

Add more JNI docs, tests, and a handful of Java-side overloads.

FossilOrigin-Name: d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0
This commit is contained in:
stephan
2023-08-24 11:57:51 +00:00
parent 8cafdfa916
commit bfdc7ab5a7
11 changed files with 395 additions and 138 deletions

View File

@ -223,19 +223,18 @@ $(sqlite3-jni.h): $(sqlite3-jni.h.in) $(MAKEFILE)
$(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h) $(sqlite3-jni.dll): $(sqlite3-jni.h) $(sqlite3.c) $(sqlite3.h)
$(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE) $(sqlite3-jni.dll): $(dir.bld.c) $(sqlite3-jni.c) $(SQLite3Jni.java) $(MAKEFILE)
$(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \ $(CC) $(sqlite3-jni.dll.cflags) $(SQLITE_OPT) \
$(sqlite3-jni.c) -shared -o $@ -lpthread $(sqlite3-jni.c) -shared -o $@
all: $(sqlite3-jni.dll) all: $(sqlite3-jni.dll)
.PHONY: test .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) test: $(SQLite3Jni.class) $(sqlite3-jni.dll)
$(bin.java) -ea -Djava.library.path=$(dir.bld.c) \ $(bin.java) $(test.main.flags) $(test.flags)
$(java.flags) -cp $(classpath) \ @echo "Again in multi-threaded mode:"
org.sqlite.jni.Tester1 $(test.flags) $(bin.java) $(test.main.flags) -t 5 -r 20 -shuffle $(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)
tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test)) tester.scripts := $(sort $(wildcard $(dir.src)/tests/*.test))
tester.flags ?= # --verbose tester.flags ?= # --verbose
@ -271,7 +270,7 @@ endif
tester-ext: tester-local tester-ext: tester-local
tester: tester-ext tester: tester-ext
tests: test test-mt tester tests: test tester
package.jar.in := $(abspath $(dir.src)/jar.in) package.jar.in := $(abspath $(dir.src)/jar.in)
CLEAN_FILES += $(package.jar.in) CLEAN_FILES += $(package.jar.in)
$(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main) $(package.jar.in): $(MAKEFILE) $(CLASS_FILES.main)

View File

@ -16,7 +16,7 @@ Technical support is available in the forum:
> **FOREWARNING:** this subproject is very much in development and > **FOREWARNING:** this subproject is very much in development and
subject to any number of changes. Please do not rely on any subject to any number of changes. Please do not rely on any
information about its API until this disclaimer is removed. The JNI 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 will be "final," at which point strong backward compatibility
guarantees will apply. guarantees will apply.
@ -43,29 +43,34 @@ Non-goals:
- Creation of high-level OO wrapper APIs. Clients are free to create - Creation of high-level OO wrapper APIs. Clients are free to create
them off of the C-style API. them off of the C-style API.
Hello World Hello World
----------------------------------------------------------------------- -----------------------------------------------------------------------
```java ```java
import org.sqlite.jni.*; 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 = sqlite3_open(":memory:");
final sqlite3 db = out.take(); try {
if( 0 != rc ){ final int rc = sqlite3_errcode(db);
if( null != db ){ if( 0 != rc ){
System.out.print("Error opening db: "+sqlite3_errmsg(db)); if( null != db ){
sqlite3_close(db); System.out.print("Error opening db: "+sqlite3_errmsg(db));
}else{ }else{
System.out.print("Error opening db: rc="+rc); 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 Building
@ -86,28 +91,90 @@ $ make test
$ make clean $ 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> <a id='1to1ish'></a>
One-to-One(-ish) Mapping to C One-to-One(-ish) Mapping to C
======================================================================== ========================================================================
This JNI binding aims to provide as close to a 1-to-1 experience with 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 the C API as cross-language semantics allow. Interface changes are
made where cross-language semantics do not allow a 1-to-1, and necessarily made where cross-language semantics do not allow a 1-to-1,
judiciously made where a 1-to-1 mapping would be unduly cumbersome to and judiciously made where a 1-to-1 mapping would be unduly cumbersome
use in Java. 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.
Golden Rule: _Never_ Throw from Callbacks (Unless...) 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 #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 Client-defined callbacks _must never throw exceptions_ unless _very
explicitly documented_ as being throw-safe. Exceptions are generally explicitly documented_ as being throw-safe. Exceptions are generally
reserved for higher-level bindings which are constructed to reserved for higher-level bindings which are constructed to
specifically deal with them and ensure that they do not leak C-level specifically deal with them and ensure that they do not leak C-level
resources. In some cases, callback handlers (see below) are permitted resources. In some cases, callback handlers are permitted to throw, in
to throw, in which cases they get translated to C-level result codes which cases they get translated to C-level result codes and/or
and/or messages. 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 Awkward Callback Names
@ -246,4 +313,5 @@ in-flux nature of this API.
Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and Various APIs which accept callbacks, e.g. `sqlite3_trace_v2()` and
`sqlite3_update_hook()`, use interfaces similar to those shown above. `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.

View File

@ -46,11 +46,13 @@ $(sqlite3-jni.dll):
echo "*** to configure it for your system. ***"; \ echo "*** to configure it for your system. ***"; \
echo "************************************************************************" echo "************************************************************************"
$(CC) $(CFLAGS) $(SQLITE_OPT) \ $(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" @echo "Now try running it with: make test"
test.flags = -Djava.library.path=. sqlite3-jni-*.jar
test: $(sqlite3-jni.dll) 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: clean:
-rm -f $(sqlite3-jni.dll) -rm -f $(sqlite3-jni.dll)

View File

@ -1429,7 +1429,7 @@ typedef struct {
} ResultJavaVal; } ResultJavaVal;
/* For use with sqlite3_result/value_pointer() */ /* 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 ** 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, JDECL(jint,1bind_1blob)(JENV_CSELF, jobject jpStmt,
jint ndx, jbyteArray baData, jint nMax){ jint ndx, jbyteArray baData, jint nMax){
int rc; jbyte * const pBuf = baData ? JBA_TOC(baData) : 0;
if(!baData){ int const rc = sqlite3_bind_blob(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
rc = sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), ndx); pBuf, (int)nMax, SQLITE_TRANSIENT);
}else{ JBA_RELEASE(baData,pBuf);
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);
}
return (jint)rc; 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, JDECL(jint,1bind_1text)(JENV_CSELF, jobject jpStmt,
jint ndx, jbyteArray baData, jint nMax){ jint ndx, jbyteArray baData, jint nMax){
if(baData){ jbyte * const pBuf = baData ? JBA_TOC(baData) : 0;
jbyte * const pBuf = JBA_TOC(baData); int const rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx,
int rc = sqlite3_bind_text(PtrGet_sqlite3_stmt(jpStmt), (int)ndx, (const char *)pBuf, (const char *)pBuf,
(int)nMax, SQLITE_TRANSIENT); (int)nMax, SQLITE_TRANSIENT);
JBA_RELEASE(baData, pBuf); JBA_RELEASE(baData, pBuf);
return (jint)rc; return (jint)rc;
}else{ }
return sqlite3_bind_null(PtrGet_sqlite3_stmt(jpStmt), (int)ndx);
} 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, 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){ 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; jbyteArray jba;
const jbyte * pBytes; const jbyte * pBytes;
switch(mode){ switch(mode){
case SQLITE_UTF16: case SQLITE_UTF16:
pBytes = sqlite3_value_text16(PtrGet_sqlite3_value(jpSVal)); pBytes = sqlite3_value_text16(sv);
break; break;
case SQLITE_UTF16LE: case SQLITE_UTF16LE:
pBytes = sqlite3_value_text16le(PtrGet_sqlite3_value(jpSVal)); pBytes = sqlite3_value_text16le(sv);
break; break;
case SQLITE_UTF16BE: case SQLITE_UTF16BE:
pBytes = sqlite3_value_text16be(PtrGet_sqlite3_value(jpSVal)); pBytes = sqlite3_value_text16be(sv);
break; break;
default: default:
assert(!"not possible"); assert(!"not possible");

View File

@ -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 JNIEXPORT jint JNICALL Java_org_sqlite_jni_SQLite3Jni_sqlite3_1bind_1text
(JNIEnv *, jclass, jobject, jint, jbyteArray, jint); (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 * Class: org_sqlite_jni_SQLite3Jni
* Method: sqlite3_bind_zeroblob * Method: sqlite3_bind_zeroblob

View File

@ -14,24 +14,24 @@
package org.sqlite.jni; 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 { public interface AutoExtension {
/** /**
Must function as described for a sqlite3_auto_extension() 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 AutoExtensions may throw and the exception's error message
rule, AutoExtensions may throw and the exception's error message
will be set as the db's error string. 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 - Opening a database from an auto-extension handler will lead to
an endless recursion of the auto-handler triggering itself an endless recursion of the auto-handler triggering itself
indirectly for each newly-opened database. 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. overridden method synchronized.
- Results are undefined if db is closed by an auto-extension. - Results are undefined if db is closed by an auto-extension.

View File

@ -154,32 +154,35 @@ public final class SQLite3Jni {
Functions almost as documented for the C API, with these Functions almost as documented for the C API, with these
exceptions: exceptions:
- The callback interface is more limited because of - The callback interface is is shorter because of cross-language
cross-language differences. Specifically, auto-extensions do differences. Specifically, 3rd argument to the C auto-extension
not have access to the sqlite3_api object which native callback interface is unnecessary here.
auto-extensions do.
- If the list of auto-extensions is manipulated from an
auto-extension, it is undefined which, if any, auto-extensions The C API docs do not specifically say so, if the list of
will subsequently execute for the current database (it depends auto-extensions is manipulated from an auto-extension, it is
on multiple factors). undefined which, if any, auto-extensions will subsequently
execute for the current database.
See the AutoExtension class docs for more information. See the AutoExtension class docs for more information.
*/ */
public static native int sqlite3_auto_extension(@NotNull AutoExtension callback); 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( public static int sqlite3_bind_blob(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data
){ ){
return (null == data) return (null==data)
? sqlite3_bind_null(stmt, ndx) ? sqlite3_bind_null(stmt, ndx)
: sqlite3_bind_blob(stmt, ndx, data, data.length); : 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( public static native int sqlite3_bind_double(
@NotNull sqlite3_stmt stmt, int ndx, double v @NotNull sqlite3_stmt stmt, int ndx, double v
); );
@ -200,13 +203,10 @@ public final class SQLite3Jni {
@NotNull sqlite3_stmt stmt @NotNull sqlite3_stmt stmt
); );
/** /**
A level of indirection required to ensure that the input to the Requires that paramName be a NUL-terminated UTF-8 string.
C-level function of the same name is 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 @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 Works like the C-level sqlite3_bind_text() but assumes
SQLITE_TRANSIENT for the final parameter and (B) behaves like SQLITE_TRANSIENT for the final C API parameter.
sqlite3_bind_null() if the data argument is null.
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( private static native int sqlite3_bind_text(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int maxBytes @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( public static int sqlite3_bind_text(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable String data @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); 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( public static int sqlite3_bind_text(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data @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); : 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( public static native int sqlite3_bind_zeroblob(
@NotNull sqlite3_stmt stmt, int ndx, int n @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 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 instance in place of a callback function. Pass it a null handler
to clear the busy handler. Calling this multiple times with the to clear the busy handler.
same object is a no-op on the second and subsequent calls.
*/ */
public static native int sqlite3_busy_handler( public static native int sqlite3_busy_handler(
@NotNull sqlite3 db, @Nullable BusyHandler handler @NotNull sqlite3 db, @Nullable BusyHandler handler
@ -595,15 +640,41 @@ public final class SQLite3Jni {
@Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb @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( public static native int sqlite3_open_v2(
@Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb, @Nullable String filename, @NotNull OutputPointer.sqlite3 ppDb,
int flags, @Nullable String zVfs 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 The sqlite3_prepare() family of functions require slightly
different signatures than their native counterparts, but different signatures than their native counterparts, but (A) they
overloading allows us to install several convenience forms. 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 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. 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); 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( private static native int sqlite3_prepare_v2(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
@NotNull OutputPointer.sqlite3_stmt outStmt, @NotNull OutputPointer.sqlite3_stmt outStmt,
@ -677,6 +768,18 @@ public final class SQLite3Jni {
return sqlite3_prepare_v2(db, utf8, utf8.length, outStmt, null); 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( private static native int sqlite3_prepare_v3(
@NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes, @NotNull sqlite3 db, @NotNull byte[] sqlUtf8, int maxBytes,
int prepFlags, @NotNull OutputPointer.sqlite3_stmt outStmt, 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); 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 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 acts as a proxy for C's sqlite3_preupdate_blobwrite(), else it returns

View File

@ -21,6 +21,16 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; 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 { public class Tester1 implements Runnable {
//! True when running in multi-threaded mode. //! True when running in multi-threaded mode.
private static boolean mtMode = false; private static boolean mtMode = false;
@ -30,6 +40,10 @@ public class Tester1 implements Runnable {
private static boolean shuffle = false; private static boolean shuffle = false;
//! True to dump the list of to-run tests to stdout. //! True to dump the list of to-run tests to stdout.
private static boolean listRunTests = false; 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. //! List of test*() methods to run.
private static List<java.lang.reflect.Method> testMethods = null; private static List<java.lang.reflect.Method> testMethods = null;
//! List of exceptions collected by run() //! List of exceptions collected by run()
@ -48,27 +62,37 @@ public class Tester1 implements Runnable {
static final Metrics metrics = new Metrics(); static final Metrics metrics = new Metrics();
public synchronized static void outln(){ public synchronized static void outln(){
System.out.println(""); if( !quietMode ){
System.out.println("");
}
} }
public synchronized static void outln(Object val){ public synchronized static void outln(Object val){
System.out.print(Thread.currentThread().getName()+": "); if( !quietMode ){
System.out.println(val); System.out.print(Thread.currentThread().getName()+": ");
System.out.println(val);
}
} }
public synchronized static void out(Object val){ public synchronized static void out(Object val){
System.out.print(val); if( !quietMode ){
System.out.print(val);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized static void out(Object... vals){ public synchronized static void out(Object... vals){
System.out.print(Thread.currentThread().getName()+": "); if( !quietMode ){
for(Object v : vals) out(v); System.out.print(Thread.currentThread().getName()+": ");
for(Object v : vals) out(v);
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized static void outln(Object... vals){ public synchronized static void outln(Object... vals){
out(vals); out("\n"); if( !quietMode ){
out(vals); out("\n");
}
} }
static volatile int affirmCount = 0; static volatile int affirmCount = 0;
@ -85,6 +109,7 @@ public class Tester1 implements Runnable {
affirm(v, "Affirmation failed."); affirm(v, "Affirmation failed.");
} }
@ManualTest /* because testing this for threading is pointless */
private void test1(){ private void test1(){
affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); affirm(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER);
affirm(SQLITE_MAX_LENGTH > 0); affirm(SQLITE_MAX_LENGTH > 0);
@ -280,6 +305,16 @@ public class Tester1 implements Runnable {
affirm(0 != stmt.getNativePointer()); affirm(0 != stmt.getNativePointer());
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
affirm(0 == stmt.getNativePointer() ); 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_close_v2(db);
} }
@ -383,8 +418,11 @@ public class Tester1 implements Runnable {
sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);"); sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
String[] list1 = { "hell🤩", "w😃rld", "!" }; String[] list1 = { "hell🤩", "w😃rld", "!" };
int rc; int rc;
int n = 0;
for( String e : list1 ){ 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); affirm(0 == rc);
rc = sqlite3_step(stmt); rc = sqlite3_step(stmt);
affirm(SQLITE_DONE==rc); affirm(SQLITE_DONE==rc);
@ -393,7 +431,7 @@ public class Tester1 implements Runnable {
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;"); stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
StringBuilder sbuf = new StringBuilder(); StringBuilder sbuf = new StringBuilder();
int n = 0; n = 0;
while( SQLITE_ROW == sqlite3_step(stmt) ){ while( SQLITE_ROW == sqlite3_step(stmt) ){
String txt = sqlite3_column_text16(stmt, 0); String txt = sqlite3_column_text16(stmt, 0);
//outln("txt = "+txt); //outln("txt = "+txt);
@ -521,6 +559,7 @@ public class Tester1 implements Runnable {
affirm(xDestroyCalled.value); affirm(xDestroyCalled.value);
} }
@ManualTest /* because threading is meaningless here */
private void testToUtf8(){ private void testToUtf8(){
/** /**
https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html 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 ); affirm( 7 == counter.value );
} }
@ManualTest /* because threads inherently break this test */
private void testBusy(){ private void testBusy(){
final String dbName = "_busy-handler.db"; final String dbName = "_busy-handler.db";
final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3(); final OutputPointer.sqlite3 outDb = new OutputPointer.sqlite3();
@ -1156,6 +1196,8 @@ public class Tester1 implements Runnable {
it throws. it throws.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ManualTest /* because the Fts5 parts are not yet known to be
thread-safe */
private void testFts5() throws Exception { private void testFts5() throws Exception {
if( !sqlite3_compileoption_used("ENABLE_FTS5") ){ if( !sqlite3_compileoption_used("ENABLE_FTS5") ){
//outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests."); //outln("SQLITE_ENABLE_FTS5 is not set. Skipping FTS5 tests.");
@ -1206,6 +1248,8 @@ public class Tester1 implements Runnable {
sqlite3_close(db); sqlite3_close(db);
} }
@ManualTest/* because multiple threads legitimately make these
results unpredictable */
private synchronized void testAutoExtension(){ private synchronized void testAutoExtension(){
final ValueHolder<Integer> val = new ValueHolder<>(0); final ValueHolder<Integer> val = new ValueHolder<>(0);
final ValueHolder<String> toss = new ValueHolder<>(null); final ValueHolder<String> toss = new ValueHolder<>(null);
@ -1296,6 +1340,7 @@ public class Tester1 implements Runnable {
affirm( 8 == val.value ); affirm( 8 == val.value );
} }
@ManualTest /* because we only want to run this test manually */
private void testSleep(){ private void testSleep(){
out("Sleeping briefly... "); out("Sleeping briefly... ");
sqlite3_sleep(600); 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(){ private void testFail(){
affirm( false, "Intentional failure." ); affirm( false, "Intentional failure." );
} }
@ -1355,6 +1401,9 @@ public class Tester1 implements Runnable {
testFts5(); testFts5();
} }
} }
synchronized( this.getClass() ){
++nTestRuns;
}
} }
public void run() { public void run() {
@ -1375,6 +1424,8 @@ public class Tester1 implements Runnable {
CLI flags: CLI flags:
-q|-quiet: disables most test output.
-t|-thread N: runs the tests in N threads -t|-thread N: runs the tests in N threads
concurrently. Default=1. concurrently. Default=1.
@ -1400,6 +1451,7 @@ public class Tester1 implements Runnable {
Integer nRepeat = 1; Integer nRepeat = 1;
boolean forceFail = false; boolean forceFail = false;
boolean sqlLog = false; boolean sqlLog = false;
boolean squelchTestOutput = false;
for( int i = 0; i < args.length; ){ for( int i = 0; i < args.length; ){
String arg = args[i++]; String arg = args[i++];
if(arg.startsWith("-")){ if(arg.startsWith("-")){
@ -1421,6 +1473,8 @@ public class Tester1 implements Runnable {
sqlLog = true; sqlLog = true;
}else if(arg.equals("naps")){ }else if(arg.equals("naps")){
takeNaps = true; takeNaps = true;
}else if(arg.equals("q") || arg.equals("quiet")){
squelchTestOutput = true;
}else{ }else{
throw new IllegalArgumentException("Unhandled flag:"+arg); 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*(). // Build list of tests to run from the methods named test*().
testMethods = new ArrayList<>(); testMethods = new ArrayList<>();
final List<String> excludes = new ArrayList<>(); for(final java.lang.reflect.Method m : Tester1.class.getDeclaredMethods()){
// 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()){
final String name = m.getName(); final String name = m.getName();
if( name.startsWith("test") && excludes.indexOf(name)<0 ){ if( name.equals("testFail") ){
testMethods.add(m); 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(); final long timeStart = System.currentTimeMillis();
int nLoop = 0; int nLoop = 0;
affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ), affirm( 0==sqlite3_config( SQLITE_CONFIG_SINGLETHREAD ),
@ -1476,7 +1532,7 @@ public class Tester1 implements Runnable {
outln("libversion_number: ", outln("libversion_number: ",
sqlite3_libversion_number(),"\n", sqlite3_libversion_number(),"\n",
sqlite3_libversion(),"\n",SQLITE_SOURCE_ID); 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."); if( takeNaps ) outln("Napping between tests is enabled.");
for( int n = 0; n < nRepeat; ++n ){ for( int n = 0; n < nRepeat; ++n ){
if( nThread==null || nThread<=1 ){ if( nThread==null || nThread<=1 ){
@ -1500,6 +1556,7 @@ public class Tester1 implements Runnable {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
if( !listErrors.isEmpty() ){ if( !listErrors.isEmpty() ){
quietMode = false;
outln("TEST ERRORS:"); outln("TEST ERRORS:");
Exception err = null; Exception err = null;
for( Exception e : listErrors ){ for( Exception e : listErrors ){
@ -1510,9 +1567,10 @@ public class Tester1 implements Runnable {
} }
} }
outln(); outln();
quietMode = false;
final long timeEnd = System.currentTimeMillis(); final long timeEnd = System.currentTimeMillis();
outln("Tests done. Metrics:"); outln("Tests done. Metrics across ",nTestRuns," total iteration(s):");
outln("\tAssertions checked: ",affirmCount); outln("\tAssertions checked: ",affirmCount);
outln("\tDatabases opened: ",metrics.dbOpen); outln("\tDatabases opened: ",metrics.dbOpen);
if( doSomethingForDev ){ if( doSomethingForDev ){

View File

@ -72,13 +72,18 @@ public class TesterFts5 {
affirm( xDestroyCalled.value ); affirm( xDestroyCalled.value );
} }
public TesterFts5(){ public TesterFts5(boolean verbose){
final long timeStart = System.currentTimeMillis(); if(verbose){
final int oldAffirmCount = Tester1.affirmCount; final long timeStart = System.currentTimeMillis();
test1(); final int oldAffirmCount = Tester1.affirmCount;
final int affirmCount = Tester1.affirmCount - oldAffirmCount; test1();
final long timeEnd = System.currentTimeMillis(); final int affirmCount = Tester1.affirmCount - oldAffirmCount;
outln("FTS5 Tests done. Assertions checked = ",affirmCount, final long timeEnd = System.currentTimeMillis();
", Total time = ",(timeEnd - timeStart),"ms"); outln("FTS5 Tests done. Assertions checked = ",affirmCount,
", Total time = ",(timeEnd - timeStart),"ms");
}else{
test1();
}
} }
public TesterFts5(){ this(false); }
} }

View File

@ -1,5 +1,5 @@
C JNI\scleanups\sregarding\sbuilding\swith\scertain\sfeatures\sdisabled. C Add\smore\sJNI\sdocs,\stests,\sand\sa\shandful\sof\sJava-side\soverloads.
D 2023-08-23T17:52:51.175 D 2023-08-24T11:57:51.863
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -232,13 +232,13 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 14b7c3abd1ae8693203b08b0e06bb359f8924ad2243f15953e9c6e456ae317b5 F ext/jni/GNUmakefile 0a823c56f081294e7797dae303380ac989ebaa801bba970968342b7358f07aed
F ext/jni/README.md 1693e865d366f5ebaa756732ea0d4b786515caf3cfbcd4dcb8758274373913b0 F ext/jni/README.md 64bf1da0d562d051207ca1c5cfa52e8b7a69120533cc034a3da7670ef920cbef
F ext/jni/jar-dist.make 9a03d10dbb5a74c724bfec4b76fd9e4c9865cbbc858d731cb48f38ac897d73a3 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
F ext/jni/src/c/sqlite3-jni.c 0ca96134d7fb3f313a7a49487f68a8d7a6d7545470c84532aa1ce63d2cdc432e F ext/jni/src/c/sqlite3-jni.c e1e3cde4d08925282b5bc949f9ed8f613a6a2c6f60d0c697e79d59fb49f9fe4b
F ext/jni/src/c/sqlite3-jni.h c5cb0348efe4e5f3d125a240e2437e8475de14a586c2f859e2acdcde4116244d 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/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/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1 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/CollationNeeded.java ad67843b6dd1c06b6b0a1dc72887b7c48e2a98042fcf6cacf14d42444037eab8
@ -257,9 +257,9 @@ F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7c
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564 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/SQLFunction.java f697cf2a81c4119f2baf0682af689686f0466f1dd83dba00885f5603e693fe16
F ext/jni/src/org/sqlite/jni/SQLLog.java c60610b35208416940822e834d61f08fbbe5d6e06b374b541b49e41fd56c9798 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/SQLite3Jni.java 956063c854c4f662183c41c65d0ab48b5e2127824b8053eeb05b9fc40f0d09e3
F ext/jni/src/org/sqlite/jni/Tester1.java 69ea63a5b235f94f914dff6fe3ecd103ee0a8023b8737db071b46c0c75375e26 F ext/jni/src/org/sqlite/jni/Tester1.java b5a4bb2a969df053d5c138887f04039a79b36170372a2efdf5dfbd6ac90db4c9
F ext/jni/src/org/sqlite/jni/TesterFts5.java de095e3b701fba0c56d7b8b2993dc22bcbaa9de8f992904a93729ad729a91576 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/Tracer.java a5cece9f947b0af27669b8baec300b6dd7ff859c3e6a6e4a1bd8b50f9714775d
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
@ -2094,8 +2094,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P d67255f7251cc5d1d27d77d4c84ff216e2da71202db989718189a6b4beff1cd0 P a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18
R b911867dcaf18c3e131e156c82d306fe R cb79df5ad40cc86377d98f5a1329b589
U stephan U stephan
Z bdef86836c9254e732e1b6d744febf17 Z 39e8fbdcbc2ebaa58ed84b96f0374c06
# Remove this line to create a well-formed Fossil manifest. # Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
a9e6d5158b8a4a6b8554a5f8f0a35785ee450d42ea877275dc27085e89716c18 d19a431facbde6a6b960664674753ee85d2c051a76109ce7db0b079c65fbdea0