Bind the trace API to the JNI wrapper1 API and add a way to map the native-level db/stmt types to their high-level counterparts (required for translating callbacks such as tracers).
FossilOrigin-Name: 702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3
This commit is contained in:
parent
5189ef98d3
commit
aad6808efc
@ -46,7 +46,7 @@ public class Tester1 implements Runnable {
|
||||
//! 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.
|
||||
@ -1701,7 +1701,7 @@ public class Tester1 implements Runnable {
|
||||
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: ");
|
||||
@ -1763,8 +1763,11 @@ public class Tester1 implements Runnable {
|
||||
-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.
|
||||
@ -1793,7 +1796,7 @@ public class Tester1 implements Runnable {
|
||||
}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")){
|
||||
|
@ -61,11 +61,31 @@ public final class Sqlite implements AutoCloseable {
|
||||
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
|
||||
@ -84,7 +104,11 @@ public final class Sqlite implements AutoCloseable {
|
||||
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){
|
||||
@ -124,6 +148,9 @@ public final class Sqlite implements AutoCloseable {
|
||||
|
||||
@Override public void close(){
|
||||
if(null!=this.db){
|
||||
synchronized(nativeToWrapper){
|
||||
nativeToWrapper.remove(this.db);
|
||||
}
|
||||
this.db.close();
|
||||
this.db = null;
|
||||
}
|
||||
@ -467,11 +494,71 @@ public final class Sqlite implements AutoCloseable {
|
||||
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;
|
||||
/**
|
||||
@ -489,12 +576,29 @@ public final class Sqlite implements AutoCloseable {
|
||||
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.
|
||||
@ -527,6 +631,9 @@ public final class Sqlite implements AutoCloseable {
|
||||
public int finalizeStmt(){
|
||||
int rc = 0;
|
||||
if( null!=stmt ){
|
||||
synchronized(nativeToWrapper){
|
||||
nativeToWrapper.remove(this.stmt);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = null;
|
||||
_db = null;
|
||||
|
@ -46,7 +46,7 @@ public class Tester2 implements Runnable {
|
||||
//! 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.
|
||||
@ -444,6 +444,54 @@ public class Tester2 implements Runnable {
|
||||
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 );
|
||||
@ -451,7 +499,7 @@ public class Tester2 implements Runnable {
|
||||
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: ");
|
||||
@ -514,7 +562,9 @@ public class Tester2 implements Runnable {
|
||||
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.
|
||||
@ -543,7 +593,7 @@ public class Tester2 implements Runnable {
|
||||
}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")){
|
||||
|
19
manifest
19
manifest
@ -1,5 +1,5 @@
|
||||
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
|
||||
@ -269,7 +269,7 @@ F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385
|
||||
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
|
||||
@ -296,9 +296,9 @@ F ext/jni/src/org/sqlite/jni/test-script-interpreter.md f9f25126127045d051e918fe
|
||||
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
|
||||
@ -2142,9 +2142,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
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.
|
||||
|
@ -1 +1 @@
|
||||
570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913
|
||||
702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3
|
Loading…
Reference in New Issue
Block a user