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:
75
ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java
Normal file
75
ext/jni/src/org/sqlite/jni/PrepareMultiCallback.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user