]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Bind the trace API to the JNI wrapper1 API and add a way to map the native-level...
authorstephan <stephan@noemail.net>
Sat, 4 Nov 2023 12:53:00 +0000 (12:53 +0000)
committerstephan <stephan@noemail.net>
Sat, 4 Nov 2023 12:53:00 +0000 (12:53 +0000)
FossilOrigin-Name: 702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3

ext/jni/src/org/sqlite/jni/capi/Tester1.java
ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java
ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java
manifest
manifest.uuid

index 46571856582c8a266c12885c4a0cd3c590833c60..a0445adb3a69a595c36cbf667a866a7517ec8198 100644 (file)
@@ -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")){
index 907755d99a9de8c9799718e4402490e8509dd1a2..685e8c326c6bd1a142e2e743b2bb76abe375315e 100644 (file)
@@ -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;
index d24551ebf1eabb00d288d735563018167d1356c4..b6f1011489cf25b39917c8b6b582e018def8584a 100644 (file)
@@ -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")){
index 18d8f5e182ce19170a3089ba3dbb22f704877e7c..ae3068b1249e7b6ef8e6ed330792ce1cef9d84f3 100644 (file)
--- a/manifest
+++ b/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.
index f69aa3731adb5c346646734bf00ba0ec640aff13..0e828aa55cce2f3525b583c1f707eb9f523a67c5 100644 (file)
@@ -1 +1 @@
-570635575cc5fbffe910ed992b58393e214117ef3b5370a66f115cd0ee202913
\ No newline at end of file
+702910e0d1cfc897a269b4fb36b255165958edf529ac9553ebc5155e404d4cd3
\ No newline at end of file