Coverage Report

Created: 2026-08-07 05:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/exprs/function/function_java_udf.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "exprs/function/function_java_udf.h"
19
20
#include <bthread/bthread.h>
21
22
#include <future>
23
#include <memory>
24
#include <string>
25
26
#include "common/exception.h"
27
#include "core/block/block.h"
28
#include "format/jni/jni_data_bridge.h"
29
#include "jni.h"
30
#include "runtime/exec_env.h"
31
#include "runtime/user_function_cache.h"
32
#include "util/jni-util.h"
33
#include "util/threadpool.h"
34
35
const char* EXECUTOR_CLASS = "org/apache/doris/udf/UdfExecutor";
36
const char* EXECUTOR_CTOR_SIGNATURE = "([B)V";
37
const char* EXECUTOR_EVALUATE_SIGNATURE = "(Ljava/util/Map;Ljava/util/Map;)J";
38
const char* EXECUTOR_CLOSE_SIGNATURE = "()V";
39
40
namespace doris {
41
42
JavaFunctionCall::JavaFunctionCall(const TFunction& fn, const DataTypes& argument_types,
43
                                   const DataTypePtr& return_type)
44
473
        : fn_(fn), _argument_types(argument_types), _return_type(return_type) {}
45
46
4.65k
Status JavaFunctionCall::open(FunctionContext* context, FunctionContext::FunctionStateScope scope) {
47
4.65k
    JNIEnv* env = nullptr;
48
4.65k
    RETURN_IF_ERROR(Jni::Env::Get(&env));
49
50
4.65k
    if (scope == FunctionContext::FunctionStateScope::THREAD_LOCAL) {
51
4.19k
        SCOPED_TIMER(context->get_udf_execute_timer());
52
4.19k
        std::shared_ptr<JniContext> jni_ctx = std::make_shared<JniContext>();
53
4.19k
        context->set_function_state(FunctionContext::THREAD_LOCAL, jni_ctx);
54
55
4.19k
        {
56
4.19k
            std::string local_location;
57
4.19k
            auto function_cache = UserFunctionCache::instance();
58
4.19k
            TJavaUdfExecutorCtorParams ctor_params;
59
4.19k
            ctor_params.__set_fn(fn_);
60
            // get jar path if both file path location and checksum are null
61
4.19k
            if (!fn_.hdfs_location.empty() && !fn_.checksum.empty()) {
62
4.18k
                RETURN_IF_ERROR(function_cache->get_jarpath(fn_.id, fn_.hdfs_location, fn_.checksum,
63
4.18k
                                                            &local_location));
64
4.18k
                ctor_params.__set_location(local_location);
65
4.18k
            }
66
67
4.19k
            RETURN_IF_ERROR(Jni::Util::find_class(env, EXECUTOR_CLASS, &jni_ctx->executor_cl));
68
69
4.19k
            RETURN_IF_ERROR(jni_ctx->executor_cl.get_method(env, "<init>", EXECUTOR_CTOR_SIGNATURE,
70
4.19k
                                                            &jni_ctx->executor_ctor_id));
71
4.19k
            RETURN_IF_ERROR(jni_ctx->executor_cl.get_method(
72
4.19k
                    env, "evaluate", EXECUTOR_EVALUATE_SIGNATURE, &jni_ctx->executor_evaluate_id));
73
4.19k
            RETURN_IF_ERROR(jni_ctx->executor_cl.get_method(env, "close", EXECUTOR_CLOSE_SIGNATURE,
74
4.19k
                                                            &jni_ctx->executor_close_id));
75
4.19k
            Jni::LocalArray ctor_params_bytes;
76
4.19k
            RETURN_IF_ERROR(Jni::Util::SerializeThriftMsg(env, &ctor_params, &ctor_params_bytes));
77
4.19k
            RETURN_IF_ERROR(jni_ctx->executor_cl.new_object(env, jni_ctx->executor_ctor_id)
78
4.19k
                                    .with_arg(ctor_params_bytes)
79
4.19k
                                    .call(&jni_ctx->executor));
80
4.19k
        }
81
4.19k
        jni_ctx->open_successes = true;
82
4.19k
    }
83
4.65k
    return Status::OK();
84
4.65k
}
85
86
Status JavaFunctionCall::execute_impl(FunctionContext* context, Block& block,
87
                                      const ColumnNumbers& arguments, uint32_t result,
88
2.18k
                                      size_t num_rows) const {
89
2.18k
    JNIEnv* env = nullptr;
90
2.18k
    RETURN_IF_ERROR(Jni::Env::Get(&env));
91
2.18k
    auto* jni_ctx = reinterpret_cast<JniContext*>(
92
2.18k
            context->get_function_state(FunctionContext::THREAD_LOCAL));
93
2.18k
    SCOPED_TIMER(context->get_udf_execute_timer());
94
2.18k
    std::unique_ptr<long[]> input_table;
95
2.18k
    RETURN_IF_ERROR(JniDataBridge::to_java_table(&block, num_rows, arguments, input_table));
96
2.18k
    auto input_table_schema = JniDataBridge::parse_table_schema(&block, arguments, true);
97
2.18k
    std::map<String, String> input_params = {
98
2.18k
            {"meta_address", std::to_string((long)input_table.get())},
99
2.18k
            {"required_fields", input_table_schema.first},
100
2.18k
            {"columns_types", input_table_schema.second}};
101
2.18k
    Jni::LocalObject input_map;
102
103
2.18k
    RETURN_IF_ERROR(Jni::Util::convert_to_java_map(env, input_params, &input_map));
104
2.18k
    auto output_table_schema = JniDataBridge::parse_table_schema(&block, {result}, true);
105
2.18k
    std::string output_nullable =
106
2.18k
            block.get_by_position(result).type->is_nullable() ? "true" : "false";
107
2.18k
    std::map<String, String> output_params = {{"is_nullable", output_nullable},
108
2.18k
                                              {"required_fields", output_table_schema.first},
109
2.18k
                                              {"columns_types", output_table_schema.second}};
110
2.18k
    Jni::LocalObject output_map;
111
2.18k
    RETURN_IF_ERROR(Jni::Util::convert_to_java_map(env, output_params, &output_map));
112
2.18k
    long output_address = 0;
113
2.18k
    RETURN_IF_ERROR(jni_ctx->executor.call_long_method(env, jni_ctx->executor_evaluate_id)
114
2.18k
                            .with_arg(input_map)
115
2.18k
                            .with_arg(output_map)
116
2.18k
                            .call(&output_address));
117
118
2.18k
    return JniDataBridge::fill_block(&block, {result}, output_address);
119
2.18k
}
120
121
Status JavaFunctionCall::close(FunctionContext* context,
122
4.68k
                               FunctionContext::FunctionStateScope scope) {
123
4.68k
    auto close_func = [context]() {
124
4.67k
        auto* jni_ctx = reinterpret_cast<JniContext*>(
125
4.67k
                context->get_function_state(FunctionContext::THREAD_LOCAL));
126
        // JNIContext own some resource and its release method depend on JavaFunctionCall
127
        // has to release the resource before JavaFunctionCall is deconstructed.
128
4.68k
        if (jni_ctx) {
129
4.68k
            RETURN_IF_ERROR(jni_ctx->close());
130
4.68k
        }
131
4.67k
        return Status::OK();
132
4.67k
    };
133
134
4.68k
    if (bthread_self() == 0) {
135
4.68k
        return close_func();
136
4.68k
    } else {
137
0
        DorisMetrics::instance()->udf_close_bthread_count->increment(1);
138
        // Use the close_workers pthread pool to execute the close function
139
0
        auto task = std::make_shared<std::packaged_task<Status()>>(std::move(close_func));
140
0
        auto task_future = task->get_future();
141
0
        RETURN_IF_ERROR(ExecEnv::GetInstance()->udf_close_workers_pool()->submit_func(
142
0
                [task]() { (*task)(); }));
143
0
        RETURN_IF_ERROR(task_future.get());
144
0
        return Status::OK();
145
0
    }
146
4.68k
}
147
} // namespace doris