Skip to content

Commit 2f4ed44

Browse files
committed
Scalar functions: simplify JNI handling
This PR is a follow-up to #630 and #637. It removes JNI utilities specific to scalar functions in favour of more generic `GlobalRefHolder` utility. Testing: no functional changes, no new tests
1 parent c8d20e1 commit 2f4ed44

15 files changed

+305
-330
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,8 +597,8 @@ add_library(duckdb_java SHARED
597597
src/jni/config.cpp
598598
src/jni/duckdb_java.cpp
599599
src/jni/functions.cpp
600+
src/jni/holders.cpp
600601
src/jni/refs.cpp
601-
src/jni/scalar_functions.cpp
602602
src/jni/types.cpp
603603
src/jni/util.cpp
604604
${DUCKDB_SRC_FILES})

CMakeLists.txt.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ add_library(duckdb_java SHARED
115115
src/jni/config.cpp
116116
src/jni/duckdb_java.cpp
117117
src/jni/functions.cpp
118+
src/jni/holders.cpp
118119
src/jni/refs.cpp
119-
src/jni/scalar_functions.cpp
120120
src/jni/types.cpp
121121
src/jni/util.cpp
122122
${DUCKDB_SRC_FILES})

duckdb_java.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1return_1type
6060
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1varargs
6161
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1volatile
6262
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1special_1handling
63+
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1extra_1info
64+
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function
6365
Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1function
6466
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1error
6567
Java_org_duckdb_DuckDBBindings_duckdb_1create_1logical_1type
@@ -111,7 +113,6 @@ Java_org_duckdb_DuckDBBindings_duckdb_1appender_1column_1count
111113
Java_org_duckdb_DuckDBBindings_duckdb_1appender_1column_1type
112114
Java_org_duckdb_DuckDBBindings_duckdb_1append_1data_1chunk
113115
Java_org_duckdb_DuckDBBindings_duckdb_1append_1default_1to_1chunk
114-
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function
115116

116117
duckdb_adbc_init
117118
duckdb_add_aggregate_function_to_set

duckdb_java.exp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ _Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1return_1type
5757
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1varargs
5858
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1volatile
5959
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1special_1handling
60-
_Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1function
6160
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1error
6261
_Java_org_duckdb_DuckDBBindings_duckdb_1vector_1get_1string
6362
_Java_org_duckdb_DuckDBBindings_duckdb_1vector_1get_1string__Ljava_nio_ByteBuffer_2J
6463
_Java_org_duckdb_DuckDBBindings_duckdb_1create_1logical_1type
6564
_Java_org_duckdb_DuckDBBindings_duckdb_1create_1decimal_1type
65+
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1extra_1info
6666
_Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function
67+
_Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1function
6768
_Java_org_duckdb_DuckDBBindings_duckdb_1get_1type_1id
6869
_Java_org_duckdb_DuckDBBindings_duckdb_1decimal_1width
6970
_Java_org_duckdb_DuckDBBindings_duckdb_1decimal_1scale

duckdb_java.map

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ DUCKDB_JAVA {
5959
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1varargs;
6060
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1volatile;
6161
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1special_1handling;
62+
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1extra_1info;
63+
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function;
6264
Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1function;
6365
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1error;
6466
Java_org_duckdb_DuckDBBindings_duckdb_1vector_1get_1string;
@@ -110,7 +112,6 @@ DUCKDB_JAVA {
110112
Java_org_duckdb_DuckDBBindings_duckdb_1appender_1column_1type;
111113
Java_org_duckdb_DuckDBBindings_duckdb_1append_1data_1chunk;
112114
Java_org_duckdb_DuckDBBindings_duckdb_1append_1default_1to_1chunk;
113-
Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function;
114115

115116
duckdb_adbc_init;
116117
duckdb_add_aggregate_function_to_set;

src/jni/bindings_scalar_function.cpp

Lines changed: 121 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#include "functions.hpp"
33
#include "holders.hpp"
44
#include "refs.hpp"
5-
#include "scalar_functions.hpp"
65
#include "util.hpp"
76

87
static duckdb_scalar_function scalar_function_buf_to_scalar_function(JNIEnv *env, jobject scalar_function_buf) {
@@ -36,10 +35,20 @@ static duckdb_function_info function_info_buf_to_function_info(JNIEnv *env, jobj
3635
return function_info;
3736
}
3837

38+
/*
39+
* Class: org_duckdb_DuckDBBindings
40+
* Method: duckdb_create_scalar_function
41+
* Signature: ()Ljava/nio/ByteBuffer;
42+
*/
3943
JNIEXPORT jobject JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1create_1scalar_1function(JNIEnv *env, jclass) {
4044
return make_ptr_buf(env, duckdb_create_scalar_function());
4145
}
4246

47+
/*
48+
* Class: org_duckdb_DuckDBBindings
49+
* Method: duckdb_destroy_scalar_function
50+
* Signature: (Ljava/nio/ByteBuffer;)V
51+
*/
4352
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1scalar_1function(JNIEnv *env, jclass,
4453
jobject scalar_function) {
4554
auto function = scalar_function_buf_to_scalar_function(env, scalar_function);
@@ -49,6 +58,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1destroy_1scalar_1f
4958
duckdb_destroy_scalar_function(&function);
5059
}
5160

61+
/*
62+
* Class: org_duckdb_DuckDBBindings
63+
* Method: duckdb_scalar_function_set_name
64+
* Signature: (Ljava/nio/ByteBuffer;[B)V
65+
*/
5266
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1name(JNIEnv *env, jclass,
5367
jobject scalar_function,
5468
jbyteArray name) {
@@ -67,6 +81,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
6781
duckdb_scalar_function_set_name(function, function_name.c_str());
6882
}
6983

84+
/*
85+
* Class: org_duckdb_DuckDBBindings
86+
* Method: duckdb_scalar_function_add_parameter
87+
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V
88+
*/
7089
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1add_1parameter(JNIEnv *env, jclass,
7190
jobject scalar_function,
7291
jobject logical_type) {
@@ -81,6 +100,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
81100
duckdb_scalar_function_add_parameter(function, type);
82101
}
83102

103+
/*
104+
* Class: org_duckdb_DuckDBBindings
105+
* Method: duckdb_scalar_function_set_return_type
106+
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V
107+
*/
84108
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1return_1type(
85109
JNIEnv *env, jclass, jobject scalar_function, jobject logical_type) {
86110
auto function = scalar_function_buf_to_scalar_function(env, scalar_function);
@@ -94,6 +118,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
94118
duckdb_scalar_function_set_return_type(function, type);
95119
}
96120

121+
/*
122+
* Class: org_duckdb_DuckDBBindings
123+
* Method: duckdb_scalar_function_set_varargs
124+
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)V
125+
*/
97126
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1varargs(JNIEnv *env, jclass,
98127
jobject scalar_function,
99128
jobject logical_type) {
@@ -108,6 +137,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
108137
duckdb_scalar_function_set_varargs(function, type);
109138
}
110139

140+
/*
141+
* Class: org_duckdb_DuckDBBindings
142+
* Method: duckdb_scalar_function_set_volatile
143+
* Signature: (Ljava/nio/ByteBuffer;)V
144+
*/
111145
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1volatile(JNIEnv *env, jclass,
112146
jobject scalar_function) {
113147
auto function = scalar_function_buf_to_scalar_function(env, scalar_function);
@@ -117,6 +151,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
117151
duckdb_scalar_function_set_volatile(function);
118152
}
119153

154+
/*
155+
* Class: org_duckdb_DuckDBBindings
156+
* Method: duckdb_scalar_function_set_special_handling
157+
* Signature: (Ljava/nio/ByteBuffer;)V
158+
*/
120159
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1special_1handling(
121160
JNIEnv *env, jclass, jobject scalar_function) {
122161
auto function = scalar_function_buf_to_scalar_function(env, scalar_function);
@@ -126,6 +165,11 @@ JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1
126165
duckdb_scalar_function_set_special_handling(function);
127166
}
128167

168+
/*
169+
* Class: org_duckdb_DuckDBBindings
170+
* Method: duckdb_register_scalar_function
171+
* Signature: (Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I
172+
*/
129173
JNIEXPORT jint JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1function(JNIEnv *env, jclass,
130174
jobject connection,
131175
jobject scalar_function) {
@@ -140,16 +184,85 @@ JNIEXPORT jint JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1register_1scalar_1
140184
return static_cast<jint>(duckdb_register_scalar_function(conn, function));
141185
}
142186

143-
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function(
144-
JNIEnv *env, jclass, jobject scalar_function_buf, jobject function_j) {
145-
try {
146-
scalar_function_set_function(env, scalar_function_buf, function_j);
147-
} catch (const std::exception &e) {
148-
duckdb::ErrorData error(e);
149-
ThrowJNI(env, error.Message().c_str());
187+
/*
188+
* Class: org_duckdb_DuckDBBindings
189+
* Method: duckdb_scalar_function_set_extra_info
190+
* Signature: (Ljava/nio/ByteBuffer;Ljava/lang/Object;)V
191+
*/
192+
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1extra_1info(
193+
JNIEnv *env, jclass, jobject scalar_function, jobject callback) {
194+
195+
if (callback == nullptr) {
196+
env->ThrowNew(J_SQLException, "Specified callback must be not null");
197+
return;
198+
}
199+
200+
auto sf = scalar_function_buf_to_scalar_function(env, scalar_function);
201+
if (env->ExceptionCheck()) {
202+
return;
203+
}
204+
205+
auto callback_holder = std::unique_ptr<GlobalRefHolder>(new GlobalRefHolder(env, callback));
206+
if (callback_holder->vm == nullptr) {
207+
env->ThrowNew(J_SQLException, "Unable to create a global reference to the specified scalar function callback");
208+
return;
150209
}
210+
211+
duckdb_scalar_function_set_extra_info(sf, callback_holder.release(), GlobalRefHolder::destroy);
212+
}
213+
214+
/*
215+
* Class: org_duckdb_DuckDBBindings
216+
* Method: duckdb_scalar_function_set_function
217+
* Signature: (Ljava/nio/ByteBuffer;)V
218+
*/
219+
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1function(JNIEnv *env, jclass,
220+
jobject scalar_function) {
221+
auto sf = scalar_function_buf_to_scalar_function(env, scalar_function);
222+
if (env->ExceptionCheck()) {
223+
return;
224+
}
225+
226+
duckdb_scalar_function_set_function(
227+
sf, [](duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output) {
228+
auto callback_holder = reinterpret_cast<GlobalRefHolder *>(duckdb_scalar_function_get_extra_info(info));
229+
AttachedJNIEnv attached = callback_holder->attach_current_thread();
230+
if (attached.env == nullptr) {
231+
duckdb_scalar_function_set_error(info, "Unable to attach JNI environment");
232+
return;
233+
}
234+
jobject info_buf = make_ptr_buf(attached.env, info);
235+
if (info_buf == nullptr) {
236+
duckdb_scalar_function_set_error(info, "Unable to create function info buffer");
237+
return;
238+
}
239+
LocalRefHolder info_buf_holder(attached.env, info_buf);
240+
jobject input_buf = make_ptr_buf(attached.env, input);
241+
if (input_buf == nullptr) {
242+
duckdb_scalar_function_set_error(info, "Unable to create input buffer");
243+
return;
244+
}
245+
LocalRefHolder input_buf_holder(attached.env, input_buf);
246+
jobject output_buf = make_ptr_buf(attached.env, output);
247+
if (output_buf == nullptr) {
248+
duckdb_scalar_function_set_error(info, "Unable to create output buffer");
249+
return;
250+
}
251+
LocalRefHolder output_buf_holder(attached.env, output_buf);
252+
253+
attached.env->CallVoidMethod(callback_holder->global_ref, J_DuckDBScalarFunctionWrapper_execute, info_buf,
254+
input_buf, output_buf);
255+
if (attached.env->ExceptionCheck()) {
256+
duckdb_scalar_function_set_error(info, "Java callback system error");
257+
}
258+
});
151259
}
152260

261+
/*
262+
* Class: org_duckdb_DuckDBBindings
263+
* Method: duckdb_scalar_function_set_error
264+
* Signature: (Ljava/nio/ByteBuffer;[B)V
265+
*/
153266
JNIEXPORT void JNICALL Java_org_duckdb_DuckDBBindings_duckdb_1scalar_1function_1set_1error(JNIEnv *env, jclass,
154267
jobject function_info_buf,
155268
jbyteArray error) {

src/jni/holders.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "holders.hpp"
2+
3+
ConnectionHolder *get_connection_ref(JNIEnv *env, jobject conn_ref_buf) {
4+
if (!conn_ref_buf) {
5+
throw duckdb::ConnectionException("Invalid connection buffer ref");
6+
}
7+
auto conn_holder = reinterpret_cast<ConnectionHolder *>(env->GetDirectBufferAddress(conn_ref_buf));
8+
if (!conn_holder) {
9+
throw duckdb::ConnectionException("Invalid connection buffer");
10+
}
11+
return conn_holder;
12+
}
13+
14+
/**
15+
* Throws a SQLException and returns nullptr if a valid Connection can't be retrieved from the buffer.
16+
*/
17+
duckdb::Connection *get_connection(JNIEnv *env, jobject conn_ref_buf) {
18+
auto conn_holder = get_connection_ref(env, conn_ref_buf);
19+
auto conn_ref = conn_holder->connection.get();
20+
if (!conn_ref || !conn_ref->context) {
21+
throw duckdb::ConnectionException("Invalid connection");
22+
}
23+
24+
return conn_ref;
25+
}
26+
27+
duckdb_connection conn_ref_buf_to_conn(JNIEnv *env, jobject conn_ref_buf) {
28+
if (conn_ref_buf == nullptr) {
29+
env->ThrowNew(J_SQLException, "Invalid connection buffer");
30+
return nullptr;
31+
}
32+
auto conn_holder = reinterpret_cast<ConnectionHolder *>(env->GetDirectBufferAddress(conn_ref_buf));
33+
if (conn_holder == nullptr) {
34+
env->ThrowNew(J_SQLException, "Invalid connection holder");
35+
return nullptr;
36+
}
37+
auto conn_ref = conn_holder->connection.get();
38+
if (conn_ref == nullptr || conn_ref->context == nullptr) {
39+
env->ThrowNew(J_SQLException, "Invalid connection");
40+
return nullptr;
41+
}
42+
43+
return reinterpret_cast<duckdb_connection>(conn_ref);
44+
}
45+
46+
AttachedJNIEnv::AttachedJNIEnv() {
47+
}
48+
49+
AttachedJNIEnv::AttachedJNIEnv(JavaVM *vm_in, JNIEnv *env_in, bool need_to_detach_in)
50+
: vm(vm_in), env(env_in), need_to_detach(need_to_detach_in) {
51+
}
52+
53+
AttachedJNIEnv::~AttachedJNIEnv() noexcept {
54+
if (vm == nullptr) {
55+
return;
56+
}
57+
if (need_to_detach) {
58+
vm->DetachCurrentThread();
59+
}
60+
}
61+
62+
GlobalRefHolder::GlobalRefHolder(JNIEnv *env, jobject local_ref) {
63+
if (env->GetJavaVM(&this->vm) != JNI_OK || this->vm == nullptr) {
64+
this->vm = nullptr;
65+
return;
66+
}
67+
if (local_ref != nullptr) {
68+
this->global_ref = env->NewGlobalRef(local_ref);
69+
if (this->global_ref == nullptr) {
70+
this->vm = nullptr;
71+
}
72+
}
73+
}
74+
75+
GlobalRefHolder::~GlobalRefHolder() noexcept {
76+
if (global_ref == nullptr) {
77+
return;
78+
}
79+
AttachedJNIEnv attached = attach_current_thread();
80+
if (attached.env == nullptr) {
81+
return;
82+
}
83+
attached.env->DeleteGlobalRef(global_ref);
84+
}
85+
86+
AttachedJNIEnv GlobalRefHolder::attach_current_thread() {
87+
if (vm == nullptr) {
88+
return AttachedJNIEnv();
89+
}
90+
JNIEnv *env = nullptr;
91+
auto env_status = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
92+
if (env_status != JNI_OK && env_status != JNI_EDETACHED) {
93+
return AttachedJNIEnv();
94+
}
95+
bool need_to_detach = false;
96+
if (env_status == JNI_EDETACHED) {
97+
auto attach_status = vm->AttachCurrentThread(reinterpret_cast<void **>(&env), nullptr);
98+
if (attach_status != JNI_OK || env == nullptr) {
99+
return AttachedJNIEnv();
100+
}
101+
need_to_detach = true;
102+
}
103+
104+
return AttachedJNIEnv(vm, env, need_to_detach);
105+
}
106+
107+
void GlobalRefHolder::destroy(void *holder_in) noexcept {
108+
auto holder = reinterpret_cast<GlobalRefHolder *>(holder_in);
109+
delete holder;
110+
}
111+
112+
LocalRefHolder::LocalRefHolder(JNIEnv *env_in, jobject local_ref_in) : env(env_in), local_ref(local_ref_in) {
113+
}
114+
115+
LocalRefHolder::~LocalRefHolder() noexcept {
116+
if (env == nullptr || local_ref == nullptr) {
117+
return;
118+
}
119+
env->DeleteLocalRef(local_ref);
120+
}

0 commit comments

Comments
 (0)