1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Add JNI sqlite3_prepare_multi(), based on feedback.

FossilOrigin-Name: fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396
This commit is contained in:
stephan
2023-09-13 17:11:32 +00:00
parent fef1c11f92
commit 181063d477
6 changed files with 209 additions and 12 deletions

View 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 \

View File

@ -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;
}
}
}

View 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

View 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);
}

View File

@ -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.

View File

@ -1 +1 @@
2170312c8d7f076cbb8319227de3fac981432dae186bc1928cd217e41119b580
fa1c1534724b03debc83ae35c2fadab83faf4b4e62b91981fed103888de41396