|
25 | 25 | #include "exprs/function_context.h" |
26 | 26 | #include "fmt/core.h" |
27 | 27 | #include "jni.h" |
| 28 | +#include "types/date_value.h" |
28 | 29 | #include "types/logical_type.h" |
| 30 | +#include "types/timestamp_value.h" |
29 | 31 | #include "udf/java/java_native_method.h" |
30 | 32 | #include "udf/java/type_traits.h" |
31 | 33 | #include "udf/java/utils.h" |
@@ -184,6 +186,16 @@ void JVMFunctionHelper::_init() { |
184 | 186 | CHECK(_jarrays_class); |
185 | 187 | CHECK(_exception_util_class); |
186 | 188 |
|
| 189 | + _local_date_class = JNI_FIND_CLASS("java/time/LocalDate"); |
| 190 | + CHECK(_local_date_class); |
| 191 | + _local_date_of_epoch_day = _env->GetStaticMethodID(_local_date_class, "ofEpochDay", "(J)Ljava/time/LocalDate;"); |
| 192 | + CHECK(_local_date_of_epoch_day); |
| 193 | + _local_date_to_epoch_day = _env->GetMethodID(_local_date_class, "toEpochDay", "()J"); |
| 194 | + CHECK(_local_date_to_epoch_day); |
| 195 | + |
| 196 | + _local_datetime_class = JNI_FIND_CLASS("java/time/LocalDateTime"); |
| 197 | + CHECK(_local_datetime_class); |
| 198 | + |
187 | 199 | ADD_NUMBERIC_CLASS(boolean, Boolean, Z); |
188 | 200 | ADD_NUMBERIC_CLASS(byte, Byte, B); |
189 | 201 | ADD_NUMBERIC_CLASS(short, Short, S); |
@@ -251,6 +263,15 @@ void JVMFunctionHelper::_init() { |
251 | 263 | _method_map.emplace(TYPE_ARRAY_METHOD_ID, tmp); |
252 | 264 | INIT_HELPER_METHOD(tmp, "createBoxedMapArray", CREATE_BOXED_MAP_SIGNATURE); |
253 | 265 | _method_map.emplace(TYPE_MAP_METHOD_ID, tmp); |
| 266 | + INIT_HELPER_METHOD(tmp, "createBoxedLocalDateArray", CREATE_BOXED_PRIMI_SIGNATURE); |
| 267 | + _method_map.emplace(JNIPrimTypeId<DateValue>::id, tmp); |
| 268 | + INIT_HELPER_METHOD(tmp, "createBoxedLocalDateTimeArray", CREATE_BOXED_PRIMI_SIGNATURE); |
| 269 | + _method_map.emplace(JNIPrimTypeId<TimestampValue>::id, tmp); |
| 270 | + |
| 271 | + // LocalDateTime <-> packed int64 conversion stays in UDFHelper because the |
| 272 | + // packing is StarRocks-specific. |
| 273 | + INIT_HELPER_METHOD(_local_datetime_from_packed, "localDateTimeFromPackedTimestamp", "(J)Ljava/time/LocalDateTime;"); |
| 274 | + INIT_HELPER_METHOD(_local_datetime_to_packed, "packedTimestampFromLocalDateTime", "(Ljava/time/LocalDateTime;)J"); |
254 | 275 |
|
255 | 276 | // init bytebuffer |
256 | 277 | _direct_buffer_class = JNI_FIND_CLASS("java/nio/ByteBuffer"); |
@@ -537,6 +558,65 @@ jobject JVMFunctionHelper::newString(const char* data, size_t size) { |
537 | 558 | return nstr; |
538 | 559 | } |
539 | 560 |
|
| 561 | +<<<<<<< HEAD |
| 562 | +======= |
| 563 | +jobject JVMFunctionHelper::newBigDecimal(const std::string& s) { |
| 564 | + jobject jstr = newString(s.data(), s.size()); |
| 565 | + if (jstr == nullptr) { |
| 566 | + return nullptr; |
| 567 | + } |
| 568 | + LOCAL_REF_GUARD(jstr); |
| 569 | + jobject bd = _env->NewObject(_big_decimal_class, _big_decimal_ctor_string, jstr); |
| 570 | + RETURN_IF_JNI_EXCEPTION(_env, "newBigDecimal: NewObject failed", nullptr); |
| 571 | + return bd; |
| 572 | +} |
| 573 | + |
| 574 | +jobject JVMFunctionHelper::newBigDecimal(int64_t unscaled, int scale) { |
| 575 | + jobject bd = _env->CallStaticObjectMethod(_big_decimal_class, _big_decimal_value_of_ll, |
| 576 | + static_cast<jlong>(unscaled), static_cast<jint>(scale)); |
| 577 | + RETURN_IF_JNI_EXCEPTION(_env, "newBigDecimal(long, int): CallStatic failed", nullptr); |
| 578 | + return bd; |
| 579 | +} |
| 580 | + |
| 581 | +jlong JVMFunctionHelper::unscaled_long(jobject big_decimal, int precision, int scale) { |
| 582 | + return _env->CallStaticLongMethod(_udf_helper_class, _bd_unscaled_long, big_decimal, static_cast<jint>(precision), |
| 583 | + static_cast<jint>(scale)); |
| 584 | +} |
| 585 | + |
| 586 | +jbyteArray JVMFunctionHelper::unscaled_le_bytes(jobject big_decimal, int precision, int scale, int byte_width) { |
| 587 | + return (jbyteArray)_env->CallStaticObjectMethod(_udf_helper_class, _bd_unscaled_le_bytes, big_decimal, |
| 588 | + static_cast<jint>(precision), static_cast<jint>(scale), |
| 589 | + static_cast<jint>(byte_width)); |
| 590 | +} |
| 591 | + |
| 592 | +// DateValue stores days as Julian day; LocalDate exposes them as days-since-1970-01-01. |
| 593 | +// 2440588 is the Julian day number of the Unix epoch. |
| 594 | +static constexpr jlong UNIX_EPOCH_JULIAN_DAYS = 2440588; |
| 595 | + |
| 596 | +jobject JVMFunctionHelper::newLocalDate(int32_t julian) { |
| 597 | + jobject ld = _env->CallStaticObjectMethod(_local_date_class, _local_date_of_epoch_day, |
| 598 | + static_cast<jlong>(julian) - UNIX_EPOCH_JULIAN_DAYS); |
| 599 | + RETURN_IF_JNI_EXCEPTION(_env, "newLocalDate: ofEpochDay failed", nullptr); |
| 600 | + return ld; |
| 601 | +} |
| 602 | + |
| 603 | +int32_t JVMFunctionHelper::valLocalDate(jobject obj) { |
| 604 | + jlong epoch_day = _env->CallLongMethod(obj, _local_date_to_epoch_day); |
| 605 | + return static_cast<int32_t>(epoch_day + UNIX_EPOCH_JULIAN_DAYS); |
| 606 | +} |
| 607 | + |
| 608 | +jobject JVMFunctionHelper::newLocalDateTime(int64_t packed_timestamp) { |
| 609 | + jobject ldt = _env->CallStaticObjectMethod(_udf_helper_class, _local_datetime_from_packed, |
| 610 | + static_cast<jlong>(packed_timestamp)); |
| 611 | + RETURN_IF_JNI_EXCEPTION(_env, "newLocalDateTime: localDateTimeFromPackedTimestamp failed", nullptr); |
| 612 | + return ldt; |
| 613 | +} |
| 614 | + |
| 615 | +int64_t JVMFunctionHelper::valLocalDateTime(jobject obj) { |
| 616 | + return _env->CallStaticLongMethod(_udf_helper_class, _local_datetime_to_packed, obj); |
| 617 | +} |
| 618 | + |
| 619 | +>>>>>>> add5deeffc ([Enhancement] support DATE and DATETIME types in Java UDF (#72337)) |
540 | 620 | Slice JVMFunctionHelper::sliceVal(jstring jstr, std::string* buffer) { |
541 | 621 | const size_t utf_length = _env->GetStringUTFLength(jstr); |
542 | 622 | buffer->resize(utf_length); |
@@ -974,6 +1054,23 @@ Status ClassAnalyzer::get_udaf_method_desc(const std::string& sign, std::vector< |
974 | 1054 | desc->emplace_back(MethodTypeDescriptor{TYPE_ARRAY, true}); |
975 | 1055 | } else if (type == "java/util/Map") { |
976 | 1056 | desc->emplace_back(MethodTypeDescriptor{TYPE_MAP, true}); |
| 1057 | +<<<<<<< HEAD |
| 1058 | +======= |
| 1059 | + } else if (type == "java/math/BigDecimal") { |
| 1060 | + // Structural placeholder only: `.type` is not dispatched on at runtime for |
| 1061 | + // BigDecimal params. The actual DECIMAL precision/scale is resolved from |
| 1062 | + // ctx->get_arg_type() / ctx->get_return_type() at the call site. |
| 1063 | + desc->emplace_back(MethodTypeDescriptor{TYPE_DECIMAL128, true}); |
| 1064 | + } else if (type == "java/time/LocalDate") { |
| 1065 | + desc->emplace_back(MethodTypeDescriptor{TYPE_DATE, true}); |
| 1066 | + } else if (type == "java/time/LocalDateTime") { |
| 1067 | + desc->emplace_back(MethodTypeDescriptor{TYPE_DATETIME, true}); |
| 1068 | + } else { |
| 1069 | + // Unrecognized object class. Surface as TYPE_UNKNOWN so get_method_desc() |
| 1070 | + // validation produces a clear error instead of leaving method_desc short |
| 1071 | + // and crashing on a later method_desc[0].is_box access. |
| 1072 | + desc->emplace_back(MethodTypeDescriptor{TYPE_UNKNOWN, true}); |
| 1073 | +>>>>>>> add5deeffc ([Enhancement] support DATE and DATETIME types in Java UDF (#72337)) |
977 | 1074 | } |
978 | 1075 | continue; |
979 | 1076 | } |
|
0 commit comments