mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
JNI: change sqlite3_prepare_multi()'s exception-handling semantics to be more C-like and, to support that, add the package-private sqlite3_jni_db_error() method to set the db error state from package-level Java code.
FossilOrigin-Name: 46656b354311ec0a36832af1c4ccb3b6a244aa55cfb3681e25c3f42b13b387dd
This commit is contained in:
@ -3879,7 +3879,9 @@ S3JniApi(sqlite3_is_interrupted(),jboolean,1is_1interrupted)(
|
||||
** any resources owned by that cache entry and making that slot
|
||||
** available for re-use.
|
||||
*/
|
||||
JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){
|
||||
S3JniApi(sqlite3_java_uncache_thread(), jboolean, 1java_1uncache_1thread)(
|
||||
JniArgsEnvClass
|
||||
){
|
||||
int rc;
|
||||
S3JniEnv_mutex_enter;
|
||||
rc = S3JniEnv_uncache(env);
|
||||
@ -3887,7 +3889,25 @@ JniDecl(jboolean,1java_1uncache_1thread)(JniArgsEnvClass){
|
||||
return rc ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
JniDecl(jboolean,1jni_1supports_1nio)(JniArgsEnvClass){
|
||||
S3JniApi(sqlite3_jni_db_error(), jint, 1jni_1db_1error)(
|
||||
JniArgsEnvClass, jobject jDb, jint jRc, jstring jStr
|
||||
){
|
||||
S3JniDb * const ps = S3JniDb_from_java(jDb);
|
||||
int rc = SQLITE_MISUSE;
|
||||
if( ps ){
|
||||
char *zStr;
|
||||
zStr = jStr
|
||||
? s3jni_jstring_to_utf8( jStr, 0)
|
||||
: NULL;
|
||||
rc = s3jni_db_error( ps->pDb, (int)jRc, zStr );
|
||||
sqlite3_free(zStr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
S3JniApi(sqlite3_jni_supports_nio(), jboolean,1jni_1supports_1nio)(
|
||||
JniArgsEnvClass
|
||||
){
|
||||
return SJG.g.cByteBuffer ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
||||
@ -4065,10 +4085,11 @@ S3JniApi(sqlite3_open_v2(),jint,1open_1v2)(
|
||||
}
|
||||
|
||||
/* Proxy for the sqlite3_prepare[_v2/3]() family. */
|
||||
jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env, jclass self,
|
||||
jlong jpDb, jbyteArray baSql,
|
||||
jint nMax, jint prepFlags,
|
||||
jobject jOutStmt, jobject outTail){
|
||||
static jint sqlite3_jni_prepare_v123( int prepVersion, JNIEnv * const env,
|
||||
jclass self,
|
||||
jlong jpDb, jbyteArray baSql,
|
||||
jint nMax, jint prepFlags,
|
||||
jobject jOutStmt, jobject outTail){
|
||||
sqlite3_stmt * pStmt = 0;
|
||||
jobject jStmt = 0;
|
||||
const char * zTail = 0;
|
||||
|
@ -785,6 +785,14 @@ JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1java_1uncache_
|
||||
JNIEXPORT jboolean JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1supports_1nio
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_jni_db_error
|
||||
* Signature: (Lorg/sqlite/jni/capi/sqlite3;ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1jni_1db_1error
|
||||
(JNIEnv *, jclass, jobject, jint, jstring);
|
||||
|
||||
/*
|
||||
* Class: org_sqlite_jni_capi_CApi
|
||||
* Method: sqlite3_aggregate_context
|
||||
|
@ -131,6 +131,25 @@ public final class CApi {
|
||||
*/
|
||||
public static native boolean sqlite3_jni_supports_nio();
|
||||
|
||||
/**
|
||||
For internal use only. Sets the given db's error code and
|
||||
(optionally) string. If rc is 0, it defaults to SQLITE_ERROR.
|
||||
|
||||
On success it returns rc. On error it may return a more serious
|
||||
code, such as SQLITE_NOMEM. Returns SQLITE_MISUSE if db is null.
|
||||
*/
|
||||
static native int sqlite3_jni_db_error(@NotNull sqlite3 db,
|
||||
int rc, @Nullable String msg);
|
||||
|
||||
/**
|
||||
Convenience overload which uses e.toString() as the error
|
||||
message.
|
||||
*/
|
||||
static int sqlite3_jni_db_error(@NotNull sqlite3 db,
|
||||
int rc, @NotNull Exception e){
|
||||
return sqlite3_jni_db_error(db, rc, e.toString());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Maintenance reminder: please keep the sqlite3_.... functions
|
||||
// alphabetized. The SQLITE_... values. on the other hand, are
|
||||
@ -1324,9 +1343,13 @@ public final class CApi {
|
||||
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.
|
||||
continues, else the loop stops and p.call()'s result code is
|
||||
returned. If preparation of any given segment fails, looping
|
||||
stops and that result code is returned.
|
||||
|
||||
<p>If p.call() throws, the exception is propagated.
|
||||
<p>If p.call() throws, the exception is converted to a db-level
|
||||
error and a non-0 code is returned, in order to retain the
|
||||
C-style error semantics of the API.
|
||||
|
||||
<p>How each statement is handled, including whether it is finalized
|
||||
or not, is up to the callback object. e.g. the callback might
|
||||
@ -1358,7 +1381,11 @@ public final class CApi {
|
||||
// empty statement (whitespace/comments)
|
||||
continue;
|
||||
}
|
||||
rc = p.call(stmt);
|
||||
try{
|
||||
rc = p.call(stmt);
|
||||
}catch(Exception e){
|
||||
rc = sqlite3_jni_db_error( db, SQLITE_ERROR, e );
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
@ -25,7 +25,10 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
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.
|
||||
Must return 0 on success or an SQLITE_... code on error. If it
|
||||
throws, sqlite3_prepare_multi() will transform the exception into
|
||||
a db-level error in order to retain the C-style error semantics
|
||||
of the API.
|
||||
|
||||
See the {@link Finalize} class for a wrapper which finalizes the
|
||||
statement after calling a proxy PrepareMultiCallback.
|
||||
@ -37,7 +40,7 @@ public interface PrepareMultiCallback extends CallbackProxy {
|
||||
any sqlite3_stmt passed to its callback.
|
||||
*/
|
||||
public static final class Finalize implements PrepareMultiCallback {
|
||||
private PrepareMultiCallback p;
|
||||
private final PrepareMultiCallback p;
|
||||
/**
|
||||
p is the proxy to call() when this.call() is called.
|
||||
*/
|
||||
|
@ -1790,9 +1790,13 @@ public class Tester1 implements Runnable {
|
||||
};
|
||||
final List<sqlite3_stmt> liStmt = new ArrayList<sqlite3_stmt>();
|
||||
final PrepareMultiCallback proxy = new PrepareMultiCallback.StepAll();
|
||||
final ValueHolder<String> toss = new ValueHolder<>(null);
|
||||
PrepareMultiCallback m = new PrepareMultiCallback() {
|
||||
@Override public int call(sqlite3_stmt st){
|
||||
liStmt.add(st);
|
||||
if( null!=toss.value ){
|
||||
throw new RuntimeException(toss.value);
|
||||
}
|
||||
return proxy.call(st);
|
||||
}
|
||||
};
|
||||
@ -1802,6 +1806,10 @@ public class Tester1 implements Runnable {
|
||||
for( sqlite3_stmt st : liStmt ){
|
||||
sqlite3_finalize(st);
|
||||
}
|
||||
toss.value = "This is an exception.";
|
||||
rc = sqlite3_prepare_multi(db, "SELECT 1", m);
|
||||
affirm( SQLITE_ERROR==rc );
|
||||
affirm( sqlite3_errmsg(db).indexOf(toss.value)>0 );
|
||||
sqlite3_close_v2(db);
|
||||
}
|
||||
|
||||
|
@ -605,6 +605,14 @@ public final class Sqlite implements AutoCloseable {
|
||||
prepareMulti( sql, 0, visitor );
|
||||
}
|
||||
|
||||
/**
|
||||
Equivallent to prepareMulti(X,prepFlags,visitor), where X is
|
||||
sql.getBytes(StandardCharsets.UTF_8).
|
||||
*/
|
||||
public void prepareMulti(String sql, int prepFlags, PrepareMulti visitor){
|
||||
prepareMulti(sql.getBytes(StandardCharsets.UTF_8), prepFlags, visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
A variant of prepare() which can handle multiple SQL statements
|
||||
in a single input string. For each statement in the given string,
|
||||
@ -646,14 +654,6 @@ public final class Sqlite implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Equivallent to prepareMulti(X,prepFlags,visitor), where X is
|
||||
sql.getBytes(StandardCharsets.UTF_8).
|
||||
*/
|
||||
public void prepareMulti(String sql, int prepFlags, PrepareMulti visitor){
|
||||
prepareMulti(sql.getBytes(StandardCharsets.UTF_8), prepFlags, visitor);
|
||||
}
|
||||
|
||||
public void createFunction(String name, int nArg, int eTextRep, ScalarFunction f){
|
||||
int rc = CApi.sqlite3_create_function(thisDb(), name, nArg, eTextRep,
|
||||
new SqlFunction.ScalarAdapter(f));
|
||||
|
Reference in New Issue
Block a user