]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add JNI sqlite3_prepare_multi(), based on feedback.
authorstephan <stephan@noemail.net>
Wed, 13 Sep 2023 17:11:32 +0000 (17:11 +0000)
committerstephan <stephan@noemail.net>
Wed, 13 Sep 2023 17:11:32 +0000 (17:11 +0000)
FossilOrigin-Name: fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396

ext/jni/GNUmakefile
ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java [new file with mode: 0644]
ext/jni/src/org/sqlite/jni/SQLite3Jni.java
ext/jni/src/org/sqlite/jni/Tester1.java
manifest
manifest.uuid

index 7c89e987ff0152fa5f5daad491f2b856bfd22ed9..8f9495277554dde73305430fd3c1c5e7f316893b 100644 (file)
@@ -89,6 +89,7 @@ JAVA_FILES.main := $(patsubst %,$(dir.src.jni)/%,\
   ConfigSqllogCallback.java \
   NativePointerHolder.java \
   OutputPointer.java \
+  PrepareMultiCallback.java \
   PreupdateHookCallback.java \
   ProgressHandlerCallback.java \
   ResultCode.java \
diff --git a/ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java b/ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java
new file mode 100644 (file)
index 0000000..aa3c7bf
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+** 2023-09-13
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file is part of the JNI bindings for the sqlite3 C API.
+*/
+package org.sqlite.jni;
+
+/**
+   Callback for use with {@link SQLite3Jni#sqlite3_prepare_multi}.
+*/
+public interface PrepareMultiCallback extends CallbackProxy {
+
+  /**
+     Gets passed a which it may handle in arbitrary
+     ways, transfering ownership of it to this function.
+
+     sqlite3_prepare_multi() will _not_ finalize st - it is up
+     to the call() implementation how st is handled.
+
+     Must return 0 on success or an SQLITE_... code on error.
+
+     See the {@link Finalize} class for a wrapper which finalizes the
+     statement after calling a proxy PrepareMultiCallback.
+  */
+  int call(sqlite3_stmt st);
+
+  /**
+     A PrepareMultiCallback impl which wraps a separate impl and finalizes
+     any sqlite3_stmt passed to its callback.
+  */
+  public static final class Finalize implements PrepareMultiCallback {
+    private PrepareMultiCallback p;
+    public Finalize( PrepareMultiCallback p ){
+      this.p = p;
+    }
+    /**
+       Calls the call() method of the proxied callback and either returns its
+       result or propagates an exception. Either way, it passes its argument to
+       sqlite3_finalize().
+    */
+    @Override public int call(sqlite3_stmt st){
+      try {
+        return this.p.call(st);
+      }finally{
+        SQLite3Jni.sqlite3_finalize(st);
+      }
+    }
+  }
+
+  /**
+     A PrepareMultiCallback impl which steps entirely through a result set,
+     ignoring all non-error results.
+  */
+  public static final class StepAll implements PrepareMultiCallback {
+    public StepAll(){}
+    /**
+       Calls sqlite3_step() on st until it returns something other than
+       SQLITE_ROW. If the final result is SQLITE_DONE then 0 is returned,
+       else the result of the final step is returned.
+    */
+    @Override public int call(sqlite3_stmt st){
+      int rc = SQLite3Jni.SQLITE_DONE;
+      while( SQLite3Jni.SQLITE_ROW == (rc = SQLite3Jni.sqlite3_step(st)) ){}
+      return SQLite3Jni.SQLITE_DONE==rc ? 0 : rc;
+    }
+  }
+}
index e445013b830eb4f17fa7995a83439e960027c1c1..a4bd83c064be20d88cb6efa89477309ce1a4298f 100644 (file)
@@ -19,6 +19,7 @@ import java.lang.annotation.Target;
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import org.sqlite.jni.annotation.*;
+import java.util.Arrays;
 
 /**
   This class contains the entire C-style sqlite3 JNI API binding,
@@ -907,7 +908,6 @@ public final class SQLite3Jni {
     sqlite3_prepare(db, sql, out);
     return out.take();
   }
-
   /**
      @see #sqlite3_prepare
   */
@@ -1010,6 +1010,103 @@ public final class SQLite3Jni {
     return out.take();
   }
 
+  /**
+     A convenience wrapper around sqlite3_prepare_v3() which accepts
+     an arbitrary amount of input provided as a UTF-8-encoded byte
+     array.  It loops over the input bytes looking for
+     statements. Each one it finds is passed to p.call(), passing
+     ownership of it to that function. If p.call() returns 0, looping
+     continues, else the loop stops.
+
+     If p.call() throws, the exception is propagated.
+
+     How each statement is handled, including whether it is finalized
+     or not, is up to the callback object. e.g. the callback might
+     collect them for later use. If it does not collect them then it
+     must finalize them. See PrepareMultiCallback.Finalize for a
+     simple proxy which does that.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
+    int preFlags,
+    @NotNull PrepareMultiCallback p){
+    final OutputPointer.Int32 oTail = new OutputPointer.Int32();
+    int pos = 0, n = 1;
+    byte[] sqlChunk = sqlUtf8;
+    int rc = 0;
+    final OutputPointer.sqlite3_stmt outStmt = new OutputPointer.sqlite3_stmt();
+    while(0==rc && pos<sqlChunk.length){
+      sqlite3_stmt stmt = null;
+      if(pos > 0){
+        sqlChunk = Arrays.copyOfRange(sqlChunk, pos,
+                                      sqlChunk.length);
+      }
+      if( 0==sqlChunk.length ) break;
+      rc = sqlite3_prepare_v3(db, sqlChunk, preFlags, outStmt, oTail);
+      if( 0!=rc ) break;
+      pos = oTail.value;
+      stmt = outStmt.take();
+      if( null == stmt ){
+        // empty statement was parsed.
+        continue;
+      }
+      rc = p.call(stmt);
+    }
+    return rc;
+  }
+
+  /**
+     Convenience overload which accepts its SQL as a String and uses
+     no statement-preparation flags.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull byte[] sqlUtf8,
+    @NotNull PrepareMultiCallback p){
+    return sqlite3_prepare_multi(db, sqlUtf8, 0, p);
+  }
+
+  /**
+     Convenience overload which accepts its SQL as a String.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull String sql, int prepFlags,
+    @NotNull PrepareMultiCallback p){
+    return sqlite3_prepare_multi(
+      db, sql.getBytes(StandardCharsets.UTF_8), prepFlags, p
+    );
+  }
+
+  /**
+     Convenience overload which accepts its SQL as a String and uses
+     no statement-preparation flags.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull String sql,
+    @NotNull PrepareMultiCallback p){
+    return sqlite3_prepare_multi(db, sql, 0, p);
+  }
+
+  /**
+     Convenience overload which accepts its SQL as a String
+     array. They will be concatenated together as-is, with no
+     separator, and passed on to one of the other overloads.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull String[] sql, int prepFlags,
+    @NotNull PrepareMultiCallback p){
+    return sqlite3_prepare_multi(db, String.join("",sql), prepFlags, p);
+  }
+
+  /**
+     Convenience overload which uses no statement-preparation flags.
+  */
+  public static int sqlite3_prepare_multi(
+    @NotNull sqlite3 db, @NotNull String[] sql,
+    @NotNull PrepareMultiCallback p){
+    return sqlite3_prepare_multi(db, sql, 0, p);
+  }
+
+
   /**
      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
index ccbb57cc026ce4783130944826cfcf0b43004115..dff97cc60384d3b2ed4e61645ce0419436db8948 100644 (file)
@@ -1594,11 +1594,34 @@ public class Tester1 implements Runnable {
     sqlite3_close_v2(db);
   }
 
+  private void testPrepareMulti(){
+    final sqlite3 db = createNewDb();
+    final String[] sql = {
+      "create table t(a);",
+      "insert into t(a) values(1),(2),(3);",
+      "select a from t;"
+    };
+    final List<sqlite3_stmt> liStmt = new ArrayList<sqlite3_stmt>();
+    final PrepareMultiCallback proxy = new PrepareMultiCallback.StepAll();
+    PrepareMultiCallback m = new PrepareMultiCallback() {
+        @Override public int call(sqlite3_stmt st){
+          liStmt.add(st);
+          return proxy.call(st);
+        }
+      };
+    int rc = sqlite3_prepare_multi(db, sql, m);
+    affirm( 0==rc );
+    affirm( liStmt.size() == 3 );
+    for( sqlite3_stmt st : liStmt ){
+      sqlite3_finalize(st);
+    }
+    sqlite3_close_v2(db);
+  }
+
   /* Copy/paste/rename this to add new tests. */
   private void _testTemplate(){
     final sqlite3 db = createNewDb();
     sqlite3_stmt stmt = prepare(db,"SELECT 1");
-
     sqlite3_finalize(stmt);
     sqlite3_close_v2(db);
   }
index 49e85b6254c395fcc61771d4dbff4160a02b3d70..f231d6b2d28a20adcab2776be39bea8827dc3369 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Replace\san\sif()\scondition\sin\sfts5\sthat\sis\salways\strue\swith\san\sassert().
-D 2023-09-13T11:24:58.386
+C Add\sJNI\ssqlite3_prepare_multi(),\sbased\son\sfeedback.
+D 2023-09-13T17:11:32.386
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -235,7 +235,7 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
 F ext/icu/README.txt 7ab7ced8ae78e3a645b57e78570ff589d4c672b71370f5aa9e1cd7024f400fc9
 F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
 F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
-F ext/jni/GNUmakefile 3c82b1333ab4c8224787a490e80ea000a8f97bcb53bf9c9d11e095da1ae862f0
+F ext/jni/GNUmakefile 3a235b7bc27d238c826d0e67b389c5307dc6688a289d792d86ee46d7c4bd7754
 F ext/jni/README.md 9fceaeb17cecdc5d699dfc83c0cbc3a03fdb3b86bf676381894166c73375ee75
 F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
 F ext/jni/src/c/sqlite3-jni.c 24b620de024b1763c094dcfef978c78a1b417cb90210f6fe51b04b45e492496b
@@ -253,15 +253,16 @@ F ext/jni/src/org/sqlite/jni/ConfigLogCallback.java 636ed6b89ed03f15bc2a6f6f47bf
 F ext/jni/src/org/sqlite/jni/ConfigSqllogCallback.java e3656909eab7ed0f7e457c5b82df160ca22dd5e954c0a306ec1fca61b0d266b4
 F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 564087036449a16df148dcf0a067408bd251170bf23286c655f46b5f973e8b2d
 F ext/jni/src/org/sqlite/jni/OutputPointer.java 2f57c05672ddc9b38e3f8eed11759896cf0bf01107ffd24d5182b99f6e7254b6
+F ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java f522dd31dc76a09d033967658072c2a7aba1488e41c4d1798637c3bf1ff3f390
 F ext/jni/src/org/sqlite/jni/PreupdateHookCallback.java eccaed8dc9c6289f07ef3fc109891c6be1e7cc6c88723d90174b68706fc21cda
 F ext/jni/src/org/sqlite/jni/ProgressHandlerCallback.java 7b9ff2218129ece98ba60c57eeedcd8447e9e3b6e5d0f5e5d3eb0f0c5037d48d
 F ext/jni/src/org/sqlite/jni/ResultCode.java ba701f20213a5f259e94cfbfdd36eb7ac7ce7797f2c6c7fca2004ff12ce20f86
 F ext/jni/src/org/sqlite/jni/RollbackHookCallback.java d12352c0e22840de484ffa9b11ed5058bb0daca2e9f218055d3c54c947a273c4
 F ext/jni/src/org/sqlite/jni/SQLFunction.java 544a875d33fd160467d82e2397ac33157b29971d715a821a4fad3c899113ee8c
-F ext/jni/src/org/sqlite/jni/SQLite3Jni.java dab91a2a6f718476b3b3df646f9953a9314738937cc05caaa7298b9079a43006
+F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 62d2c4d537a08e57cf7520faf470767fa5482882ed87600ce2e4517f411c78e3
 F ext/jni/src/org/sqlite/jni/ScalarFunction.java 6d387bb499fbe3bc13c53315335233dbf6a0c711e8fa7c521683219b041c614c
 F ext/jni/src/org/sqlite/jni/TableColumnMetadata.java 54511b4297fa28dcb3f49b24035e34ced10e3fd44fd0e458e784f4d6b0096dab
-F ext/jni/src/org/sqlite/jni/Tester1.java ac20af46f909b454a0bd9dad19a212823f84ec8f8deeb53d09ef8a07583eeb2b
+F ext/jni/src/org/sqlite/jni/Tester1.java fb6edb189c2644b29806ba48825e6c816c17476bf2a4150cb7e4aee4c7978c34
 F ext/jni/src/org/sqlite/jni/TraceV2Callback.java beb0b064c1a5f8bfe585a324ed39a4e33edbe379a3fc60f1401661620d3ca7c0
 F ext/jni/src/org/sqlite/jni/UpdateHookCallback.java 8376f4a931f2d5612b295c003c9515ba933ee76d8f95610e89c339727376e36c
 F ext/jni/src/org/sqlite/jni/WindowFunction.java 488980f4dbb6bdd7067d6cb9c43e4075475e51c54d9b74a5834422654b126246
@@ -2119,8 +2120,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 6f7842f577a28df1f809cd4bae9e8eafa26f2b54a25a1362ebbdebf5026be57c
-R f3b4c841c08d4c6a9c1041284895fe73
-U dan
-Z 604f49d2d8fa4165afb87849a7893996
+P 2170312c8d7f076cbb8319227de3fac981432dae186bc1928cd217e41119b580
+R 3a804bafc3d00c98885f9a7a6ae4ef32
+U stephan
+Z 0f956dfaa1fe15e925b383a970a358db
 # Remove this line to create a well-formed Fossil manifest.
index 3e8ccff922aecf90dee6a46f0fdbe3349f77c64a..7c4032f1d8683be5b7607a691c91808fef4b2e9d 100644 (file)
@@ -1 +1 @@
-2170312c8d7f076cbb8319227de3fac981432dae186bc1928cd217e41119b580
\ No newline at end of file
+fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396
\ No newline at end of file