//! 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;
+ private static int listRunTests = 0;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
- if( listRunTests ){
+ if( (!fromThread && listRunTests>0) || listRunTests>1 ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
-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.
+ which are hard-coded. In multi-threaded mode, use this twice to
+ to emit the list run by each thread (which may differ from the initial
+ list, in particular if -shuffle is used).
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
- listRunTests = true;
+ ++listRunTests;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){
public static final int PREPARE_NORMALIZE = CApi.SQLITE_PREPARE_NORMALIZE;
public static final int PREPARE_NO_VTAB = CApi.SQLITE_PREPARE_NO_VTAB;
+ public static final int TRACE_STMT = CApi.SQLITE_TRACE_STMT;
+ public static final int TRACE_PROFILE = CApi.SQLITE_TRACE_PROFILE;
+ public static final int TRACE_ROW = CApi.SQLITE_TRACE_ROW;
+ public static final int TRACE_CLOSE = CApi.SQLITE_TRACE_CLOSE;
+ public static final int TRACE_ALL = TRACE_STMT | TRACE_PROFILE | TRACE_ROW | TRACE_CLOSE;
+
//! Used only by the open() factory functions.
private Sqlite(sqlite3 db){
this.db = db;
}
+ /** Maps org.sqlite.jni.capi.sqlite3 to Sqlite instances. */
+ private static final java.util.Map<org.sqlite.jni.capi.sqlite3, Sqlite> nativeToWrapper
+ = new java.util.HashMap<>();
+
+ /**
+ Returns the Sqlite object associated with the given sqlite3
+ object, or null if there is no such mapping.
+ */
+ static Sqlite fromNative(sqlite3 low){
+ synchronized(nativeToWrapper){
+ return nativeToWrapper.get(low);
+ }
+ }
+
/**
Returns a newly-opened db connection or throws SqliteException if
opening fails. All arguments are as documented for
n.close();
throw ex;
}
- return new Sqlite(n);
+ Sqlite rv = new Sqlite(n);
+ synchronized(nativeToWrapper){
+ nativeToWrapper.put(n, rv);
+ }
+ return rv;
}
public static Sqlite open(String filename, int flags){
@Override public void close(){
if(null!=this.db){
+ synchronized(nativeToWrapper){
+ nativeToWrapper.remove(this.db);
+ }
this.db.close();
this.db = null;
}
return rv;
}
+ public interface TraceCallback {
+ /**
+ Called by sqlite3 for various tracing operations, as per
+ sqlite3_trace_v2(). Note that this interface elides the 2nd
+ argument to the native trace callback, as that role is better
+ filled by instance-local state.
+
+ <p>These callbacks may throw, in which case their exceptions are
+ converted to C-level error information.
+
+ <p>The 2nd argument to this function, if non-null, will be a an
+ Sqlite or Sqlite.Stmt object, depending on the first argument
+ (see below).
+
+ <p>The final argument to this function is the "X" argument
+ documented for sqlite3_trace() and sqlite3_trace_v2(). Its type
+ depends on value of the first argument:
+
+ <p>- SQLITE_TRACE_STMT: pNative is a Sqlite.Stmt. pX is a String
+ containing the prepared SQL.
+
+ <p>- SQLITE_TRACE_PROFILE: pNative is a sqlite3_stmt. pX is a Long
+ holding an approximate number of nanoseconds the statement took
+ to run.
+
+ <p>- SQLITE_TRACE_ROW: pNative is a sqlite3_stmt. pX is null.
+
+ <p>- SQLITE_TRACE_CLOSE: pNative is a sqlite3. pX is null.
+ */
+ void call(int traceFlag, Object pNative, Object pX);
+ }
+
+ /**
+ Analog to sqlite3_trace_v2(). traceMask must be a mask of the
+ TRACE_... constants. Pass a null callback to remove tracing.
+
+ Throws on error.
+ */
+ public void trace(int traceMask, TraceCallback callback){
+ final Sqlite self = this;
+ final org.sqlite.jni.capi.TraceV2Callback tc =
+ (null==callback) ? null : new org.sqlite.jni.capi.TraceV2Callback(){
+ @SuppressWarnings("unchecked")
+ @Override public int call(int flag, Object pNative, Object pX){
+ switch(flag){
+ case TRACE_ROW:
+ case TRACE_PROFILE:
+ case TRACE_STMT:
+ callback.call(flag, Sqlite.Stmt.fromNative((sqlite3_stmt)pNative), pX);
+ break;
+ case TRACE_CLOSE:
+ callback.call(flag, self, pX);
+ break;
+ }
+ return 0;
+ }
+ };
+ checkRc( CApi.sqlite3_trace_v2(thisDb(), traceMask, tc) );
+ };
+
/**
Corresponds to the sqlite3_stmt class. Use Sqlite.prepare() to
create new instances.
*/
- public final class Stmt implements AutoCloseable {
+ public static final class Stmt implements AutoCloseable {
private Sqlite _db = null;
private sqlite3_stmt stmt = null;
/**
this._db = db;
this.stmt = stmt;
this.resultColCount = CApi.sqlite3_column_count(stmt);
+ synchronized(nativeToWrapper){
+ nativeToWrapper.put(this.stmt, this);
+ }
}
sqlite3_stmt nativeHandle(){
return stmt;
}
+ /** Maps org.sqlite.jni.capi.sqlite3_stmt to Stmt instances. */
+ private static final java.util.Map<org.sqlite.jni.capi.sqlite3_stmt, Stmt> nativeToWrapper
+ = new java.util.HashMap<>();
+
+ /**
+ Returns the Stmt object associated with the given sqlite3_stmt
+ object, or null if there is no such mapping.
+ */
+ static Stmt fromNative(sqlite3_stmt low){
+ synchronized(nativeToWrapper){
+ return nativeToWrapper.get(low);
+ }
+ }
+
/**
If this statement is still opened, its low-level handle is
returned, eelse an IllegalArgumentException is thrown.
public int finalizeStmt(){
int rc = 0;
if( null!=stmt ){
+ synchronized(nativeToWrapper){
+ nativeToWrapper.remove(this.stmt);
+ }
sqlite3_finalize(stmt);
stmt = null;
_db = null;
//! 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;
+ private static int listRunTests = 0;
//! True to squelch all out() and outln() output.
private static boolean quietMode = false;
//! Total number of runTests() calls.
db.close();
}
+
+ private void testTrace(){
+ final Sqlite db = openDb();
+ final ValueHolder<Integer> counter = new ValueHolder<>(0);
+ /* Ensure that characters outside of the UTF BMP survive the trip
+ from Java to sqlite3 and back to Java. (At no small efficiency
+ penalty.) */
+ final String nonBmpChar = "😃";
+ db.trace(
+ Sqlite.TRACE_ALL,
+ new Sqlite.TraceCallback(){
+ @Override public void call(int traceFlag, Object pNative, Object x){
+ ++counter.value;
+ //outln("TRACE "+traceFlag+" pNative = "+pNative.getClass().getName());
+ switch(traceFlag){
+ case Sqlite.TRACE_STMT:
+ affirm(pNative instanceof Sqlite.Stmt);
+ //outln("TRACE_STMT sql = "+x);
+ affirm(x instanceof String);
+ affirm( ((String)x).indexOf(nonBmpChar) > 0 );
+ break;
+ case Sqlite.TRACE_PROFILE:
+ affirm(pNative instanceof Sqlite.Stmt);
+ affirm(x instanceof Long);
+ //outln("TRACE_PROFILE time = "+x);
+ break;
+ case Sqlite.TRACE_ROW:
+ affirm(pNative instanceof Sqlite.Stmt);
+ affirm(null == x);
+ //outln("TRACE_ROW = "+sqlite3_column_text16((sqlite3_stmt)pNative, 0));
+ break;
+ case Sqlite.TRACE_CLOSE:
+ affirm(pNative instanceof Sqlite);
+ affirm(null == x);
+ break;
+ default:
+ affirm(false /*cannot happen*/);
+ break;
+ }
+ }
+ });
+ execSql(db, "SELECT coalesce(null,null,'"+nonBmpChar+"'); "+
+ "SELECT 'w"+nonBmpChar+"orld'");
+ affirm( 6 == counter.value );
+ db.close();
+ affirm( 7 == counter.value );
+ }
+
private void runTests(boolean fromThread) throws Exception {
List<java.lang.reflect.Method> mlist = testMethods;
affirm( null!=mlist );
mlist = new ArrayList<>( testMethods.subList(0, testMethods.size()) );
java.util.Collections.shuffle(mlist);
}
- if( listRunTests ){
+ if( (!fromThread && listRunTests>0) || listRunTests>1 ){
synchronized(this.getClass()){
if( !fromThread ){
out("Initial test"," list: ");
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.
+ which are hard-coded. In multi-threaded mode, use this twice to
+ to emit the list run by each thread (which may differ from the initial
+ list, in particular if -shuffle is used).
-fail: forces an exception to be thrown during the test run. Use
with -shuffle to make its appearance unpredictable.
}else if(arg.equals("shuffle")){
shuffle = true;
}else if(arg.equals("list-tests")){
- listRunTests = true;
+ ++listRunTests;
}else if(arg.equals("fail")){
forceFail = true;
}else if(arg.equals("sqllog")){
-C Back\sout\sthe\sALWAYS\sinserted\slate\syesterday.\s\sThe\sfuzzer\sdiscovered\sa\ncounter-example.
-D 2023-11-03T18:45:26.322
+C Bind\sthe\strace\sAPI\sto\sthe\sJNI\swrapper1\sAPI\sand\sadd\sa\sway\sto\smap\sthe\snative-level\sdb/stmt\stypes\sto\stheir\shigh-level\scounterparts\s(required\sfor\stranslating\scallbacks\ssuch\sas\stracers).
+D 2023-11-04T12:53:00.737
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1
F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f
-F ext/jni/src/org/sqlite/jni/capi/Tester1.java b6b2f3354ba68956a6bcd1c586b8eb25a0bd66eed2b58b340405e1129da15de9
+F ext/jni/src/org/sqlite/jni/capi/Tester1.java fba87e2c39ba186bb7add972d9e84b7f817f656452cf4f317679575bd5a738e7
F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723
F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java 2766b8526bbffc4f1045f70e79f1bc1b1efe1c3e95ca06cdb8a7391032dda3b4
F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950
F ext/jni/src/org/sqlite/jni/wrapper1/AggregateFunction.java bbe60ac7fd8718edb215a23dc901771bcedb1df3b46d9cf6caff6f419828587f
F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 0b01b9058ef6737c85b505c6aa2490fb1dc1d974fb39d88a93269fed09553f9f
-F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 1c95f5e0f872aeb9cdd174cbb2e254d158df1f8b2fee9f0e6ec82c348602a7bd
+F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12a9323a74e38e7c6229dc73c5b62bf50088a65310100f383469308549381907
F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java aa85b4b05fae240b14f3d332f9524a2f80c619fb03856be72b4adda866b63b72
-F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 9ab7e38e6741842f8e3b74cd3ecb4953e2f1957f5229bd32663df7331245ce95
+F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 83cfe6583c8df226eda985eed059f47efaefaca3951c618c286ffc8c63210ee8
F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java 1a1afbafbd7406ff67e7d6405541c6347517c731de535a97d7a3df1d4db835b4
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 8f5e9c192ff2820d8cfb076ab28f30697d10c22710583d6c7fd7019c4a0ea795
-Q -268b5984a4263bee245a9bb47ac927bde56cdf4af8795b851dada5622224076f
-R 4994e654abc4ea7988499736bdf83ad0
-U drh
-Z 5983c90319f7844a8c9e3e4e981877ce
+P 570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913
+R cb275d62547ff275bdfb4b29d97a5241
+U stephan
+Z 2824e678eb263c5ade76d9ba9fb58525
# Remove this line to create a well-formed Fossil manifest.
-570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913
\ No newline at end of file
+702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3
\ No newline at end of file