From d5367cc0d1c4835f21b637f53daf18af553db9b4 Mon Sep 17 00:00:00 2001
From: David Li
Date: Fri, 12 Jun 2026 15:03:12 +0900
Subject: [PATCH] fix(java/driver/jni): check for pending exceptions more
thoroughly
After JNI calls that can leave a pending Java exception (GetArrayLength,
GetObjectArrayElement, GetIntArrayRegion, GetByteArrayRegion, NewByteArray,
SetByteArrayRegion) and are followed by further JNI or native ADBC work,
check env->ExceptionCheck() and return the function's existing error
default, letting the pending exception propagate to Java rather than
performing more work or raising a second exception.
Generated-by: Claude Opus 4.8
---
java/driver/jni/src/main/cpp/jni_wrapper.cc | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/java/driver/jni/src/main/cpp/jni_wrapper.cc b/java/driver/jni/src/main/cpp/jni_wrapper.cc
index 6af1a247c3..4cec83d50f 100644
--- a/java/driver/jni/src/main/cpp/jni_wrapper.cc
+++ b/java/driver/jni/src/main/cpp/jni_wrapper.cc
@@ -337,6 +337,7 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_openDatabase(
error_guard.error);
const jsize num_params = env->GetArrayLength(parameters);
+ if (env->ExceptionCheck()) return nullptr;
if (num_params % 2 != 0) {
throw AdbcException{
.code = ADBC_STATUS_INVALID_ARGUMENT,
@@ -346,8 +347,10 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_openDatabase(
for (jsize i = 0; i < num_params; i += 2) {
// N.B. assuming String because Java side is typed as String[]
auto key = reinterpret_cast(env->GetObjectArrayElement(parameters, i));
+ if (env->ExceptionCheck()) return nullptr;
auto value =
reinterpret_cast(env->GetObjectArrayElement(parameters, i + 1));
+ if (env->ExceptionCheck()) return nullptr;
JniStringView key_str(env, key);
JniStringView value_str(env, value);
@@ -728,8 +731,10 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_statementGetOptionBytes(
return nullptr;
}
jbyteArray result = env->NewByteArray(static_cast(length));
+ if (result == nullptr || env->ExceptionCheck()) return nullptr;
env->SetByteArrayRegion(result, 0, static_cast(length),
reinterpret_cast(buf.data()));
+ if (env->ExceptionCheck()) return nullptr;
return result;
}
@@ -807,9 +812,11 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_statementSetOptionBytes(
try {
JniStringView key_str(env, key);
jsize value_length = env->GetArrayLength(value);
+ if (env->ExceptionCheck()) return;
std::vector value_buf(static_cast(value_length));
env->GetByteArrayRegion(value, 0, value_length,
reinterpret_cast(value_buf.data()));
+ if (env->ExceptionCheck()) return;
CHECK_ADBC_ERROR(AdbcStatementSetOptionBytes(stmt, key_str.value, value_buf.data(),
value_buf.size(), &error_guard.error),
error_guard.error);
@@ -905,11 +912,13 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetObjects(
const char** c_table_types = nullptr;
if (table_types != nullptr) {
jsize len = env->GetArrayLength(table_types);
+ if (env->ExceptionCheck()) return nullptr;
table_type_strings.reserve(len);
table_type_ptrs.reserve(len + 1);
for (jsize i = 0; i < len; i++) {
auto element =
reinterpret_cast(env->GetObjectArrayElement(table_types, i));
+ if (env->ExceptionCheck()) return nullptr;
table_type_strings.push_back(GetJniString(env, element));
table_type_ptrs.push_back(table_type_strings.back().c_str());
}
@@ -948,9 +957,11 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetInfo(
std::vector info_codes_vec;
if (info_codes != nullptr) {
jsize len = env->GetArrayLength(info_codes);
+ if (env->ExceptionCheck()) return nullptr;
info_codes_vec.resize(len);
env->GetIntArrayRegion(info_codes, 0, len,
reinterpret_cast(info_codes_vec.data()));
+ if (env->ExceptionCheck()) return nullptr;
c_info_codes = info_codes_vec.data();
info_codes_length = static_cast(len);
}
@@ -1041,8 +1052,10 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionGetOptionBytes(
return nullptr;
}
jbyteArray result = env->NewByteArray(static_cast(length));
+ if (result == nullptr || env->ExceptionCheck()) return nullptr;
env->SetByteArrayRegion(result, 0, static_cast(length),
reinterpret_cast(buf.data()));
+ if (env->ExceptionCheck()) return nullptr;
return result;
}
@@ -1120,9 +1133,11 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_connectionSetOptionBytes(
try {
JniStringView key_str(env, key);
jsize value_length = env->GetArrayLength(value);
+ if (env->ExceptionCheck()) return;
std::vector value_buf(static_cast(value_length));
env->GetByteArrayRegion(value, 0, value_length,
reinterpret_cast(value_buf.data()));
+ if (env->ExceptionCheck()) return;
CHECK_ADBC_ERROR(AdbcConnectionSetOptionBytes(conn, key_str.value, value_buf.data(),
value_buf.size(), &error_guard.error),
error_guard.error);
@@ -1303,8 +1318,10 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_databaseGetOptionBytes(
return nullptr;
}
jbyteArray result = env->NewByteArray(static_cast(length));
+ if (result == nullptr || env->ExceptionCheck()) return nullptr;
env->SetByteArrayRegion(result, 0, static_cast(length),
reinterpret_cast(buf.data()));
+ if (env->ExceptionCheck()) return nullptr;
return result;
}
@@ -1382,9 +1399,11 @@ Java_org_apache_arrow_adbc_driver_jni_impl_NativeAdbc_databaseSetOptionBytes(
try {
JniStringView key_str(env, key);
jsize value_length = env->GetArrayLength(value);
+ if (env->ExceptionCheck()) return;
std::vector value_buf(static_cast(value_length));
env->GetByteArrayRegion(value, 0, value_length,
reinterpret_cast(value_buf.data()));
+ if (env->ExceptionCheck()) return;
CHECK_ADBC_ERROR(AdbcDatabaseSetOptionBytes(db, key_str.value, value_buf.data(),
value_buf.size(), &error_guard.error),
error_guard.error);