@@ -40,6 +40,7 @@ static jclass J_Float;
4040static jclass J_Double;
4141static jclass J_String;
4242static jclass J_Timestamp;
43+ static jmethodID J_Timestamp_valueOf;
4344static jclass J_TimestampTZ;
4445static jclass J_Decimal;
4546static jclass J_ByteArray;
@@ -70,11 +71,22 @@ static jfieldID J_DuckVector_varlen;
7071static jclass J_DuckArray;
7172static jmethodID J_DuckArray_init;
7273
74+ static jclass J_Struct;
75+ static jmethodID J_Struct_getSQLTypeName;
76+ static jmethodID J_Struct_getAttributes;
77+
78+ static jclass J_Array;
79+ static jmethodID J_Array_getBaseTypeName;
80+ static jmethodID J_Array_getArray;
81+
7382static jclass J_DuckStruct;
7483static jmethodID J_DuckStruct_init;
7584
7685static jclass J_ByteBuffer;
7786
87+ static jclass J_DuckMap;
88+ static jmethodID J_DuckMap_getSQLTypeName;
89+
7890static jmethodID J_Map_entrySet;
7991static jmethodID J_Set_iterator;
8092static jmethodID J_Iterator_hasNext;
@@ -89,6 +101,7 @@ static jmethodID J_UUID_getLeastSignificantBits;
89101static jclass J_DuckDBDate;
90102static jmethodID J_DuckDBDate_getDaysSinceEpoch;
91103
104+ static jclass J_Object;
92105static jmethodID J_Object_toString;
93106
94107static jclass J_DuckDBTime;
@@ -163,9 +176,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
163176 tmpLocalRef = env->FindClass (" java/lang/String" );
164177 J_String = (jclass)env->NewGlobalRef (tmpLocalRef);
165178 env->DeleteLocalRef (tmpLocalRef);
179+
166180 tmpLocalRef = env->FindClass (" org/duckdb/DuckDBTimestamp" );
167181 J_Timestamp = (jclass)env->NewGlobalRef (tmpLocalRef);
168182 env->DeleteLocalRef (tmpLocalRef);
183+ J_Timestamp_valueOf = env->GetStaticMethodID (J_Timestamp, " valueOf" , " (Ljava/lang/Object;)Ljava/lang/Object;" );
184+
169185 tmpLocalRef = env->FindClass (" org/duckdb/DuckDBTimestampTZ" );
170186 J_TimestampTZ = (jclass)env->NewGlobalRef (tmpLocalRef);
171187 env->DeleteLocalRef (tmpLocalRef);
@@ -183,6 +199,11 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
183199 J_ByteArray = (jclass)env->NewGlobalRef (tmpLocalRef);
184200 env->DeleteLocalRef (tmpLocalRef);
185201
202+ J_DuckMap = GetClassRef (env, " org/duckdb/user/DuckDBMap" );
203+ D_ASSERT (J_DuckMap);
204+ J_DuckMap_getSQLTypeName = env->GetMethodID (J_DuckMap, " getSQLTypeName" , " ()Ljava/lang/String;" );
205+ D_ASSERT (J_DuckMap_getSQLTypeName);
206+
186207 tmpLocalRef = env->FindClass (" java/util/Map" );
187208 J_Map_entrySet = env->GetMethodID (tmpLocalRef, " entrySet" , " ()Ljava/util/Set;" );
188209 env->DeleteLocalRef (tmpLocalRef);
@@ -209,13 +230,21 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
209230 D_ASSERT (J_DuckArray_init);
210231 env->DeleteLocalRef (tmpLocalRef);
211232
212- tmpLocalRef = env->FindClass (" org/duckdb/DuckDBStruct" );
213- D_ASSERT (tmpLocalRef);
214- J_DuckStruct = (jclass)env->NewGlobalRef (tmpLocalRef);
233+ J_DuckStruct = GetClassRef (env, " org/duckdb/DuckDBStruct" );
215234 J_DuckStruct_init =
216235 env->GetMethodID (J_DuckStruct, " <init>" , " ([Ljava/lang/String;[Lorg/duckdb/DuckDBVector;ILjava/lang/String;)V" );
217236 D_ASSERT (J_DuckStruct_init);
218- env->DeleteLocalRef (tmpLocalRef);
237+
238+ J_Struct = GetClassRef (env, " java/sql/Struct" );
239+ J_Struct_getSQLTypeName = env->GetMethodID (J_Struct, " getSQLTypeName" , " ()Ljava/lang/String;" );
240+ J_Struct_getAttributes = env->GetMethodID (J_Struct, " getAttributes" , " ()[Ljava/lang/Object;" );
241+
242+ J_Array = GetClassRef (env, " java/sql/Array" );
243+ J_Array_getArray = env->GetMethodID (J_Array, " getArray" , " ()Ljava/lang/Object;" );
244+ J_Array_getBaseTypeName = env->GetMethodID (J_Array, " getBaseTypeName" , " ()Ljava/lang/String;" );
245+
246+ J_Object = GetClassRef (env, " java/lang/Object" );
247+ J_Object_toString = env->GetMethodID (J_Object, " toString" , " ()Ljava/lang/String;" );
219248
220249 tmpLocalRef = env->FindClass (" java/util/Map$Entry" );
221250 J_Entry_getKey = env->GetMethodID (tmpLocalRef, " getKey" , " ()Ljava/lang/Object;" );
@@ -559,11 +588,120 @@ struct ResultHolder {
559588 duckdb::unique_ptr<DataChunk> chunk;
560589};
561590
591+ Value ToValue (JNIEnv *env, jobject param, duckdb::shared_ptr<ClientContext> context) {
592+ param = env->CallStaticObjectMethod (J_Timestamp, J_Timestamp_valueOf, param);
593+
594+ if (param == nullptr ) {
595+ return (Value ());
596+ } else if (env->IsInstanceOf (param, J_Bool)) {
597+ return (Value::BOOLEAN (env->CallBooleanMethod (param, J_Bool_booleanValue)));
598+ } else if (env->IsInstanceOf (param, J_Byte)) {
599+ return (Value::TINYINT (env->CallByteMethod (param, J_Byte_byteValue)));
600+ } else if (env->IsInstanceOf (param, J_Short)) {
601+ return (Value::SMALLINT (env->CallShortMethod (param, J_Short_shortValue)));
602+ } else if (env->IsInstanceOf (param, J_Int)) {
603+ return (Value::INTEGER (env->CallIntMethod (param, J_Int_intValue)));
604+ } else if (env->IsInstanceOf (param, J_Long)) {
605+ return (Value::BIGINT (env->CallLongMethod (param, J_Long_longValue)));
606+ } else if (env->IsInstanceOf (param, J_TimestampTZ)) { // Check for subclass before superclass!
607+ return (
608+ Value::TIMESTAMPTZ ((timestamp_t )env->CallLongMethod (param, J_TimestampTZ_getMicrosEpoch)));
609+ } else if (env->IsInstanceOf (param, J_DuckDBDate)) {
610+ return (
611+ Value::DATE ((date_t )env->CallLongMethod (param, J_DuckDBDate_getDaysSinceEpoch)));
612+
613+ } else if (env->IsInstanceOf (param, J_DuckDBTime)) {
614+ return (Value::TIME ((dtime_t )env->CallLongMethod (param, J_Timestamp_getMicrosEpoch)));
615+ } else if (env->IsInstanceOf (param, J_Timestamp)) {
616+ return (
617+ Value::TIMESTAMP ((timestamp_t )env->CallLongMethod (param, J_Timestamp_getMicrosEpoch)));
618+ } else if (env->IsInstanceOf (param, J_Float)) {
619+ return (Value::FLOAT (env->CallFloatMethod (param, J_Float_floatValue)));
620+ } else if (env->IsInstanceOf (param, J_Double)) {
621+ return (Value::DOUBLE (env->CallDoubleMethod (param, J_Double_doubleValue)));
622+ } else if (env->IsInstanceOf (param, J_Decimal)) {
623+ Value val = create_value_from_bigdecimal (env, param);
624+ return (val);
625+ } else if (env->IsInstanceOf (param, J_String)) {
626+ auto param_string = jstring_to_string (env, (jstring)param);
627+ return (Value (param_string));
628+ } else if (env->IsInstanceOf (param, J_ByteArray)) {
629+ return (Value::BLOB_RAW (byte_array_to_string (env, (jbyteArray)param)));
630+ } else if (env->IsInstanceOf (param, J_UUID)) {
631+ auto most_significant = (jlong)env->CallObjectMethod (param, J_UUID_getMostSignificantBits);
632+ auto least_significant = (jlong)env->CallObjectMethod (param, J_UUID_getLeastSignificantBits);
633+ return (Value::UUID (hugeint_t (most_significant, least_significant)));
634+ } else if (env->IsInstanceOf (param, J_DuckMap)) {
635+ auto typeName = jstring_to_string (env, (jstring)env->CallObjectMethod (param, J_DuckMap_getSQLTypeName));
636+
637+ LogicalType type;
638+ context->RunFunctionInTransaction ([&]() { type = TransformStringToLogicalType (typeName, *context); });
639+
640+ auto entrySet = env->CallObjectMethod (param, J_Map_entrySet);
641+ auto iterator = env->CallObjectMethod (entrySet, J_Set_iterator);
642+ duckdb::vector<Value> entries;
643+ while (env->CallBooleanMethod (iterator, J_Iterator_hasNext)) {
644+ auto entry = env->CallObjectMethod (iterator, J_Iterator_next);
645+
646+ auto key = env->CallObjectMethod (entry, J_Entry_getKey);
647+ auto value = env->CallObjectMethod (entry, J_Entry_getValue);
648+ D_ASSERT (key);
649+ D_ASSERT (value);
650+
651+ entries.push_back (Value::STRUCT ({{" key" , ToValue (env, key, context)}, {" value" , ToValue (env, value, context)}}));
652+ }
653+
654+ return (Value::MAP (ListType::GetChildType (type), entries));
655+
656+ } else if (env->IsInstanceOf (param, J_Struct)) {
657+ auto typeName = jstring_to_string (env, (jstring)env->CallObjectMethod (param, J_Struct_getSQLTypeName));
658+
659+ LogicalType type;
660+ context->RunFunctionInTransaction ([&]() { type = TransformStringToLogicalType (typeName, *context); });
661+
662+ auto jvalues = (jobjectArray)env->CallObjectMethod (param, J_Struct_getAttributes);
663+
664+ int size = env->GetArrayLength (jvalues);
665+
666+ child_list_t <Value> values;
667+
668+ for (int i = 0 ; i < size; i++) {
669+ auto name = StructType::GetChildName (type, i);
670+
671+ auto value = env->GetObjectArrayElement (jvalues, i);
672+
673+ values.emplace_back (name, ToValue (env, value, context));
674+ }
675+
676+ return (Value::STRUCT (std::move (values)));
677+ } else if (env->IsInstanceOf (param, J_Array)) {
678+ auto typeName = jstring_to_string (env, (jstring)env->CallObjectMethod (param, J_Array_getBaseTypeName));
679+ auto jvalues = (jobjectArray)env->CallObjectMethod (param, J_Array_getArray);
680+ int size = env->GetArrayLength (jvalues);
681+
682+ LogicalType type;
683+ context->RunFunctionInTransaction ([&]() { type = TransformStringToLogicalType (typeName, *context); });
684+
685+ duckdb::vector<Value> values;
686+ for (int i = 0 ; i < size; i++) {
687+ auto value = env->GetObjectArrayElement (jvalues, i);
688+
689+ values.emplace_back (ToValue (env, value, context));
690+ }
691+
692+ return (Value::LIST (type, values));
693+
694+ } else {
695+ throw InvalidInputException (" Unsupported parameter type" );
696+ }
697+ }
698+
562699jobject _duckdb_jdbc_execute (JNIEnv *env, jclass, jobject stmt_ref_buf, jobjectArray params) {
563700 auto stmt_ref = (StatementHolder *)env->GetDirectBufferAddress (stmt_ref_buf);
564701 if (!stmt_ref) {
565702 throw InvalidInputException (" Invalid statement" );
566703 }
704+
567705 auto res_ref = make_uniq<ResultHolder>();
568706 duckdb::vector<Value> duckdb_params;
569707
@@ -572,64 +710,12 @@ jobject _duckdb_jdbc_execute(JNIEnv *env, jclass, jobject stmt_ref_buf, jobjectA
572710 throw InvalidInputException (" Parameter count mismatch" );
573711 }
574712
713+ auto &context = stmt_ref->stmt ->context ;
714+
575715 if (param_len > 0 ) {
576716 for (idx_t i = 0 ; i < param_len; i++) {
577717 auto param = env->GetObjectArrayElement (params, i);
578- if (param == nullptr ) {
579- duckdb_params.push_back (Value ());
580- continue ;
581- } else if (env->IsInstanceOf (param, J_Bool)) {
582- duckdb_params.push_back (Value::BOOLEAN (env->CallBooleanMethod (param, J_Bool_booleanValue)));
583- continue ;
584- } else if (env->IsInstanceOf (param, J_Byte)) {
585- duckdb_params.push_back (Value::TINYINT (env->CallByteMethod (param, J_Byte_byteValue)));
586- continue ;
587- } else if (env->IsInstanceOf (param, J_Short)) {
588- duckdb_params.push_back (Value::SMALLINT (env->CallShortMethod (param, J_Short_shortValue)));
589- continue ;
590- } else if (env->IsInstanceOf (param, J_Int)) {
591- duckdb_params.push_back (Value::INTEGER (env->CallIntMethod (param, J_Int_intValue)));
592- continue ;
593- } else if (env->IsInstanceOf (param, J_Long)) {
594- duckdb_params.push_back (Value::BIGINT (env->CallLongMethod (param, J_Long_longValue)));
595- continue ;
596- } else if (env->IsInstanceOf (param, J_TimestampTZ)) { // Check for subclass before superclass!
597- duckdb_params.push_back (
598- Value::TIMESTAMPTZ ((timestamp_t )env->CallLongMethod (param, J_TimestampTZ_getMicrosEpoch)));
599- continue ;
600- } else if (env->IsInstanceOf (param, J_DuckDBDate)) {
601- duckdb_params.push_back (
602- Value::DATE ((date_t )env->CallLongMethod (param, J_DuckDBDate_getDaysSinceEpoch)));
603-
604- } else if (env->IsInstanceOf (param, J_DuckDBTime)) {
605- duckdb_params.push_back (Value::TIME ((dtime_t )env->CallLongMethod (param, J_Timestamp_getMicrosEpoch)));
606-
607- } else if (env->IsInstanceOf (param, J_Timestamp)) {
608- duckdb_params.push_back (
609- Value::TIMESTAMP ((timestamp_t )env->CallLongMethod (param, J_Timestamp_getMicrosEpoch)));
610- continue ;
611- } else if (env->IsInstanceOf (param, J_Float)) {
612- duckdb_params.push_back (Value::FLOAT (env->CallFloatMethod (param, J_Float_floatValue)));
613- continue ;
614- } else if (env->IsInstanceOf (param, J_Double)) {
615- duckdb_params.push_back (Value::DOUBLE (env->CallDoubleMethod (param, J_Double_doubleValue)));
616- continue ;
617- } else if (env->IsInstanceOf (param, J_Decimal)) {
618- Value val = create_value_from_bigdecimal (env, param);
619- duckdb_params.push_back (val);
620- continue ;
621- } else if (env->IsInstanceOf (param, J_String)) {
622- auto param_string = jstring_to_string (env, (jstring)param);
623- duckdb_params.push_back (Value (param_string));
624- } else if (env->IsInstanceOf (param, J_ByteArray)) {
625- duckdb_params.push_back (Value::BLOB_RAW (byte_array_to_string (env, (jbyteArray)param)));
626- } else if (env->IsInstanceOf (param, J_UUID)) {
627- auto most_significant = (jlong)env->CallObjectMethod (param, J_UUID_getMostSignificantBits);
628- auto least_significant = (jlong)env->CallObjectMethod (param, J_UUID_getLeastSignificantBits);
629- duckdb_params.push_back (Value::UUID (hugeint_t (most_significant, least_significant)));
630- } else {
631- throw InvalidInputException (" Unsupported parameter type" );
632- }
718+ duckdb_params.push_back (ToValue (env, param, context));
633719 }
634720 }
635721
0 commit comments