From: stephan Date: Wed, 13 Sep 2023 17:11:32 +0000 (+0000) Subject: Add JNI sqlite3_prepare_multi(), based on feedback. X-Git-Tag: version-3.44.0~206 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=181063d47736bcdbce189e8409a724f23581876e;p=thirdparty%2Fsqlite.git Add JNI sqlite3_prepare_multi(), based on feedback. FossilOrigin-Name: fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396 --- diff --git a/ext/jni/GNUmakefile b/ext/jni/GNUmakefile index 7c89e987ff..8f94952775 100644 --- a/ext/jni/GNUmakefile +++ b/ext/jni/GNUmakefile @@ -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 index 0000000000..aa3c7bfffc --- /dev/null +++ b/ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java @@ -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; + } + } +} diff --git a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java index e445013b83..a4bd83c064 100644 --- a/ext/jni/src/org/sqlite/jni/SQLite3Jni.java +++ b/ext/jni/src/org/sqlite/jni/SQLite3Jni.java @@ -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 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 diff --git a/ext/jni/src/org/sqlite/jni/Tester1.java b/ext/jni/src/org/sqlite/jni/Tester1.java index ccbb57cc02..dff97cc603 100644 --- a/ext/jni/src/org/sqlite/jni/Tester1.java +++ b/ext/jni/src/org/sqlite/jni/Tester1.java @@ -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 liStmt = new ArrayList(); + 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); } diff --git a/manifest b/manifest index 49e85b6254..f231d6b2d2 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 3e8ccff922..7c4032f1d8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2170312c8d7f076cbb8319227de3fac981432dae186bc1928cd217e41119b580 \ No newline at end of file +fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396 \ No newline at end of file