]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add update-hook support to JNI wrapper1.
authorstephan <stephan@noemail.net>
Sun, 5 Nov 2023 01:14:07 +0000 (01:14 +0000)
committerstephan <stephan@noemail.net>
Sun, 5 Nov 2023 01:14:07 +0000 (01:14 +0000)
FossilOrigin-Name: 6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618

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

index 5ce17e718aecc4658b8d688b9bcdc59fb5811bb9..cf9c4b6e7a9f5da1267f677c7c8a5caa6ba02de7 100644 (file)
@@ -18,8 +18,9 @@ package org.sqlite.jni.capi;
 */
 public interface RollbackHookCallback extends CallbackProxy {
   /**
-     Works as documented for the C-level sqlite3_rollback_hook()
-     callback.
+     Must function as documented for the C-level sqlite3_rollback_hook()
+     callback. If it throws, the exception is translated into
+     a db-level error.
   */
   void call();
 }
index 33d72a5dd2d7ed7b54d827a4d6127deee4542f41..e3d491f67e673a66053c554ecb1e7ef425004451 100644 (file)
@@ -19,7 +19,8 @@ package org.sqlite.jni.capi;
 public interface UpdateHookCallback extends CallbackProxy {
   /**
      Must function as described for the C-level sqlite3_update_hook()
-     callback.
+     callback. If it throws, the exception is translated into
+     a db-level error.
   */
   void call(int opId, String dbName, String tableName, long rowId);
 }
index dcd475ea682b3175c5a77c55e920ebe0fdf611bd..324f65ae2926b990a7f969eb7158e123e682c2b1 100644 (file)
@@ -112,6 +112,42 @@ public final class Sqlite implements AutoCloseable  {
   public static final int BUSY = CApi.SQLITE_BUSY;
   public static final int LOCKED = CApi.SQLITE_LOCKED;
 
+  public static final int DENY = CApi.SQLITE_DENY;
+  public static final int IGNORE = CApi.SQLITE_IGNORE;
+  public static final int CREATE_INDEX = CApi.SQLITE_CREATE_INDEX;
+  public static final int CREATE_TABLE = CApi.SQLITE_CREATE_TABLE;
+  public static final int CREATE_TEMP_INDEX = CApi.SQLITE_CREATE_TEMP_INDEX;
+  public static final int CREATE_TEMP_TABLE = CApi.SQLITE_CREATE_TEMP_TABLE;
+  public static final int CREATE_TEMP_TRIGGER = CApi.SQLITE_CREATE_TEMP_TRIGGER;
+  public static final int CREATE_TEMP_VIEW = CApi.SQLITE_CREATE_TEMP_VIEW;
+  public static final int CREATE_TRIGGER = CApi.SQLITE_CREATE_TRIGGER;
+  public static final int CREATE_VIEW = CApi.SQLITE_CREATE_VIEW;
+  public static final int DELETE = CApi.SQLITE_DELETE;
+  public static final int DROP_INDEX = CApi.SQLITE_DROP_INDEX;
+  public static final int DROP_TABLE = CApi.SQLITE_DROP_TABLE;
+  public static final int DROP_TEMP_INDEX = CApi.SQLITE_DROP_TEMP_INDEX;
+  public static final int DROP_TEMP_TABLE = CApi.SQLITE_DROP_TEMP_TABLE;
+  public static final int DROP_TEMP_TRIGGER = CApi.SQLITE_DROP_TEMP_TRIGGER;
+  public static final int DROP_TEMP_VIEW = CApi.SQLITE_DROP_TEMP_VIEW;
+  public static final int DROP_TRIGGER = CApi.SQLITE_DROP_TRIGGER;
+  public static final int DROP_VIEW = CApi.SQLITE_DROP_VIEW;
+  public static final int INSERT = CApi.SQLITE_INSERT;
+  public static final int PRAGMA = CApi.SQLITE_PRAGMA;
+  public static final int READ = CApi.SQLITE_READ;
+  public static final int SELECT = CApi.SQLITE_SELECT;
+  public static final int TRANSACTION = CApi.SQLITE_TRANSACTION;
+  public static final int UPDATE = CApi.SQLITE_UPDATE;
+  public static final int ATTACH = CApi.SQLITE_ATTACH;
+  public static final int DETACH = CApi.SQLITE_DETACH;
+  public static final int ALTER_TABLE = CApi.SQLITE_ALTER_TABLE;
+  public static final int REINDEX = CApi.SQLITE_REINDEX;
+  public static final int ANALYZE = CApi.SQLITE_ANALYZE;
+  public static final int CREATE_VTABLE = CApi.SQLITE_CREATE_VTABLE;
+  public static final int DROP_VTABLE = CApi.SQLITE_DROP_VTABLE;
+  public static final int FUNCTION = CApi.SQLITE_FUNCTION;
+  public static final int SAVEPOINT = CApi.SQLITE_SAVEPOINT;
+  public static final int RECURSIVE = CApi.SQLITE_RECURSIVE;
+
   //! Used only by the open() factory functions.
   private Sqlite(sqlite3 db){
     this.db = db;
@@ -1353,5 +1389,53 @@ public final class Sqlite implements AutoCloseable  {
       : null;
   }
 
+  public interface UpdateHook {
+    /**
+       Must function as described for the C-level sqlite3_update_hook()
+       callback.
+    */
+    void call(int opId, String dbName, String tableName, long rowId);
+  }
+
+  /**
+     A level of indirection to permit setUpdateHook() to have similar
+     semantics as the C API, returning the previous hook. The caveat
+     is that if the low-level API is used to install a hook, it will
+     have a different hook type than Sqlite.UpdateHook so
+     setUpdateHook() will return null instead of that object.
+  */
+  private static class UpdateHookProxy
+    implements org.sqlite.jni.capi.UpdateHookCallback {
+    final UpdateHook updateHook;
+    UpdateHookProxy(UpdateHook ch){
+      this.updateHook = ch;
+    }
+    @Override public void call(int opId, String dbName, String tableName, long rowId){
+      updateHook.call(opId, dbName, tableName, rowId);
+    }
+  }
+
+  /**
+     Analog to sqlite3_update_hook(). Returns the previous hook, if
+     any (else null). Throws if this db is closed.
+
+     Minor caveat: if a update hook is set on this object's underlying
+     db handle using the lower-level SQLite API, this function may
+     return null when replacing it, despite there being a hook,
+     because it will have a different callback type. So long as the
+     handle is only manipulated via the high-level API, this caveat
+     does not apply.
+  */
+  UpdateHook setUpdateHook( UpdateHook c ){
+    UpdateHookProxy chp = null;
+    if( null!=c ){
+      chp = new UpdateHookProxy(c);
+    }
+    final org.sqlite.jni.capi.UpdateHookCallback rv =
+      CApi.sqlite3_update_hook(thisDb(), chp);
+    return (rv instanceof UpdateHookProxy)
+      ? ((UpdateHookProxy)rv).updateHook
+      : null;
+  }
 
 }
index 56ea35ef1f40e3278b5c2dc928be01b7bd0d5c62..fd8fb69b7a192ec26bdb4dcd087353df3c4f56b9 100644 (file)
@@ -133,6 +133,9 @@ public class Tester2 implements Runnable {
      Executes all SQL statements in the given string. If throwOnError
      is true then it will throw for any prepare/step errors, else it
      will return the corresponding non-0 result code.
+
+     TODO: reimplement this in the high-level API once it has the
+     multi-prepare capability.
   */
   public static int execSql(Sqlite dbw, boolean throwOnError, String sql){
     final sqlite3 db = dbw.nativeHandle();
@@ -163,7 +166,7 @@ public class Tester2 implements Runnable {
       }
       CApi.sqlite3_finalize(stmt);
       affirm(0 == stmt.getNativePointer());
-      if(CApi.SQLITE_DONE!=rc){
+      if(Sqlite.DONE!=rc){
         break;
       }
     }
@@ -181,7 +184,7 @@ public class Tester2 implements Runnable {
 
   @SingleThreadOnly /* because it's thread-agnostic */
   private void test1(){
-    affirm(CApi.sqlite3_libversion_number() == CApi.SQLITE_VERSION_NUMBER);
+    affirm(Sqlite.libVersionNumber() == CApi.SQLITE_VERSION_NUMBER);
   }
 
   /* Copy/paste/rename this to add new tests. */
@@ -818,6 +821,55 @@ public class Tester2 implements Runnable {
     db.close();
   }
 
+  private void testUpdateHook(){
+    final Sqlite db = openDb();
+    final ValueHolder<Integer> counter = new ValueHolder<>(0);
+    final ValueHolder<Integer> expectedOp = new ValueHolder<>(0);
+    final Sqlite.UpdateHook theHook = new Sqlite.UpdateHook(){
+        @Override
+        public void call(int opId, String dbName, String tableName, long rowId){
+          ++counter.value;
+          if( 0!=expectedOp.value ){
+            affirm( expectedOp.value == opId );
+          }
+        }
+      };
+    Sqlite.UpdateHook oldHook = db.setUpdateHook(theHook);
+    affirm( null == oldHook );
+    expectedOp.value = Sqlite.INSERT;
+    execSql(db, "CREATE TABLE t(a); INSERT INTO t(a) VALUES('a'),('b'),('c')");
+    affirm( 3 == counter.value );
+    expectedOp.value = Sqlite.UPDATE;
+    execSql(db, "update t set a='d' where a='c';");
+    affirm( 4 == counter.value );
+    oldHook = db.setUpdateHook(theHook);
+    affirm( theHook == oldHook );
+    expectedOp.value = Sqlite.DELETE;
+    execSql(db, "DELETE FROM t where a='d'");
+    affirm( 5 == counter.value );
+    oldHook = db.setUpdateHook(null);
+    affirm( theHook == oldHook );
+    execSql(db, "update t set a='e' where a='b';");
+    affirm( 5 == counter.value );
+    oldHook = db.setUpdateHook(null);
+    affirm( null == oldHook );
+
+    final Sqlite.UpdateHook newHook = new Sqlite.UpdateHook(){
+        @Override public void call(int opId, String dbName, String tableName, long rowId){
+        }
+      };
+    oldHook = db.setUpdateHook(newHook);
+    affirm( null == oldHook );
+    execSql(db, "update t set a='h' where a='a'");
+    affirm( 5 == counter.value );
+    oldHook = db.setUpdateHook(theHook);
+    affirm( newHook == oldHook );
+    expectedOp.value = Sqlite.UPDATE;
+    execSql(db, "update t set a='i' where a='h'");
+    affirm( 6 == counter.value );
+    db.close();
+  }
+
   private void runTests(boolean fromThread) throws Exception {
     List<java.lang.reflect.Method> mlist = testMethods;
     affirm( null!=mlist );
index 68dcbace48d300e10cfbf3445f7e71f7f9caa0c2..3ee436df8796092e4043aa0c8747da7ac0452e76 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\scommit/rollback\shook\ssupport\sto\sJNI\swrapper1.
-D 2023-11-05T00:48:43.424
+C Add\supdate-hook\ssupport\sto\sJNI\swrapper1.
+D 2023-11-05T01:14:07.152
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -264,14 +264,14 @@ F ext/jni/src/org/sqlite/jni/capi/PrepareMultiCallback.java aca8f9fa72e3b6602bc9
 F ext/jni/src/org/sqlite/jni/capi/PreupdateHookCallback.java 819d938e26208adde17ca4b7ddde1d8cd6915b6ab7b708249a9787beca6bd6b6
 F ext/jni/src/org/sqlite/jni/capi/ProgressHandlerCallback.java 01bc0c238eed2d5f93c73522cb7849a445cc9098c2ed1e78248fa20ed1cfde5b
 F ext/jni/src/org/sqlite/jni/capi/ResultCode.java 8141171f1bcf9f46eef303b9d3c5dc2537a25ad1628f3638398d8a60cacefa7f
-F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java 105e324d09c207100485e7667ad172e64322c62426bb49b547e9b0dc9c33f5f0
+F ext/jni/src/org/sqlite/jni/capi/RollbackHookCallback.java e172210a2080e851ebb694c70e9f0bf89284237795e38710a7f5f1b61e3f6787
 F ext/jni/src/org/sqlite/jni/capi/SQLFunction.java 0d1e9afc9ff8a2adb94a155b72385155fa3b8011a5cca0bb3c28468c7131c1a5
 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 909ebd23111762c878116ebacf73a848c8323fb46e8128eb3d99a85d48905444
 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/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56
 F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950
 F ext/jni/src/org/sqlite/jni/capi/WindowFunction.java caf4396f91b2567904cf94bc538a069fd62260d975bd037d15a02a890ed1ef9e
 F ext/jni/src/org/sqlite/jni/capi/XDestroyCallback.java f3abb8dd7381f53ebba909437090caf68200f06717b8a7d6aa96fa3e8133117d
@@ -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 d5c108b02afd3c63c9e5e53f71f85273c1bfdc461ae526e0a0bb2b25e4df6483
 F ext/jni/src/org/sqlite/jni/wrapper1/ScalarFunction.java 43c43adfb7866098aadaaca1620028a6ec82d5193149970019b1cce9eb59fb03
 F ext/jni/src/org/sqlite/jni/wrapper1/SqlFunction.java 2833afdb9af5c1949bb35f4c926a5351fba9d1cdf0996864caa7b47827a346c7
-F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 782bb185ffc629cdabbd624565a52ed9b4b1b2d773b8b1d84476d91cbf94827d
+F ext/jni/src/org/sqlite/jni/wrapper1/Sqlite.java 12f55248d500c0cf4148757adb442303831632edaa738a41d82708643ba42418
 F ext/jni/src/org/sqlite/jni/wrapper1/SqliteException.java 929a1e2ab4e135fbbae7f0d2d609f77cfbbc60bbec7ba789ce23d9c73bc6156e
-F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java ab1236dd65b4f90db729c88c71382b14aa179095f8b8e4b50835125bd0072f9e
+F ext/jni/src/org/sqlite/jni/wrapper1/Tester2.java 4cb3bebcd44f4289fa8075477583b6c508832288f7b18d6101d2ec23309ee4cf
 F ext/jni/src/org/sqlite/jni/wrapper1/ValueHolder.java 7b89a7391f771692c5b83b0a5b86266abe8d59f1c77d7a0eccc9b79f259d79af
 F ext/jni/src/org/sqlite/jni/wrapper1/WindowFunction.java c7d1452f9ff26175b3c19bbf273116cc2846610af68e01756d755f037fe7319f
 F ext/jni/src/tests/000-000-sanity.test c3427a0e0ac84d7cbe4c95fdc1cd4b61f9ddcf43443408f3000139478c4dc745
@@ -2142,8 +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 dcf579ab2de4a3d3a437cde59b2fd60f1729c0bde31df1865117e6a5ea4bab20
-R f0002b14be80bf0bcecc7fa19a1e63af
+P ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8
+R 100ace4e28cdce95fe3bed6faf77a591
 U stephan
-Z 507dddf857173269602bfa9033ce1fa7
+Z dbd53c8956790cde47b0f616fa6677f5
 # Remove this line to create a well-formed Fossil manifest.
index ba526bfc7b61405fc85ef91106b79e2170f26a78..e37ae0477c04084605b475ff5f0a2aa5b83c7e03 100644 (file)
@@ -1 +1 @@
-ff3d44fe42528d96533d22c7807472df89bca18f1def23b018e2f407318143f8
\ No newline at end of file
+6c584cf27179d16deee84e9699493cf29bebef123fa2a7493aad0324bead1618
\ No newline at end of file