init push
This commit is contained in:
760
src/sql/engine/user_defined_function/ob_udf_util.cpp
Normal file
760
src/sql/engine/user_defined_function/ob_udf_util.cpp
Normal file
@ -0,0 +1,760 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OceanBase
|
||||
* OceanBase CE is licensed under Mulan PubL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PubL v2.
|
||||
* You may obtain a copy of Mulan PubL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPubL-2.0
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PubL v2 for more details.
|
||||
*/
|
||||
|
||||
#define USING_LOG_PREFIX SQL_ENG
|
||||
#include "ob_udf_util.h"
|
||||
#include "observer/mysql/obsm_utils.h"
|
||||
#include "lib/file/file_directory_utils.h"
|
||||
#include "sql/engine/expr/ob_expr_operator.h"
|
||||
#include "sql/engine/user_defined_function/ob_user_defined_function.h"
|
||||
#include "sql/session/ob_sql_session_info.h"
|
||||
|
||||
namespace oceanbase {
|
||||
namespace sql {
|
||||
using namespace common;
|
||||
using namespace obmysql;
|
||||
|
||||
const char* ObUdfUtil::load_function_postfix[5] = {
|
||||
"",
|
||||
"_init",
|
||||
"_deinit",
|
||||
"_clear",
|
||||
"_add",
|
||||
};
|
||||
|
||||
int ObUdfUtil::calc_udf_result_type(common::ObIAllocator& allocator, const ObUdfFunction* udf_func,
|
||||
const share::schema::ObUDFMeta& udf_meta, const common::ObIArray<common::ObString>& udf_attributes,
|
||||
const common::ObIArray<ObExprResType>& udf_attributes_types, ObExprResType& type)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
bool init_succ = false;
|
||||
// Based on the following facts:
|
||||
// 1. OB calculates the result type of expr when it is called ExprDeduceType,
|
||||
// and it is called in the resolver phase/rewrite phase/CG phase. In the CG phase,
|
||||
// the ObAccuracy of ObExprResType will be set to ObPostExprItem as the basis for
|
||||
// data regularization during calculation.
|
||||
// 2. The init function of the user UDF can assign values to the UDF_INIT structure,
|
||||
// set max_length and decimal, and specify the precision of the type. It is also
|
||||
// required to execute the init function in the earliest plan generation stage to
|
||||
// obtain max_length and decimal.
|
||||
// 3. During the execution of a physical Expr, the UDF_INIT and UDF_ARG structures
|
||||
// used by the udf function interface are used as member variables. Expr needs to
|
||||
// pass these two structures to the user-written function in a non-const manner.
|
||||
// For example, users can perform operations on the memory pointed to by ptr
|
||||
// in the UDF_INIT structure in their own udf function.
|
||||
// From the perspective of 2, UDF_INIT and UDF_ARG are static properties; from the perspective of 3,
|
||||
// they are more like executing ctx. Under this premise, Expr must use UDF_INIT/UDF_ARG as
|
||||
// the ctx component of the execution period, and must execute the init functions
|
||||
// to obtain accuracy, making it a necessary parameter in the logic stage. Then a better solution
|
||||
// is to first Call the init and deinit functions of udf once, specifically to obtain accuracy.
|
||||
// But there is another problem here, that is, if the init function is relatively obsolete or
|
||||
// blocked, the generation phase of this plan will be very slow.
|
||||
/* do the user defined init func */
|
||||
ObUdfFunction::ObUdfCtx udf_ctx;
|
||||
if (OB_FAIL(ObUdfUtil::init_udf_args(allocator, udf_attributes, udf_attributes_types, udf_ctx.udf_args_))) {
|
||||
LOG_WARN("failed to set udf args", K(ret));
|
||||
} else if (OB_FAIL(udf_func->process_init_func(udf_ctx))) {
|
||||
LOG_WARN("do agg init func failed", K(ret));
|
||||
} else {
|
||||
/* further infomation about scale, precision and length, just see the ob_resolver_utils.cpp */
|
||||
init_succ = true;
|
||||
type.set_default_collation_type();
|
||||
switch (udf_meta.ret_) {
|
||||
case share::schema::ObUDF::STRING: {
|
||||
/* for string and decimal, mysql use char* as result */
|
||||
type.set_varchar();
|
||||
const ObAccuracy& default_accuracy = ObAccuracy::DDL_DEFAULT_ACCURACY[ObVarcharType];
|
||||
/* OB_MAX_VARCHAR_LENGTH(1024*1024) is bigger than ObUdfUtil::MAX_FIELD_WIDTH(255*3 + 1) */
|
||||
if (udf_ctx.udf_init_.max_length > ObUdfUtil::UDF_MAX_FIELD_WIDTH) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the length is invalid", K(ret), K(udf_ctx.udf_init_.max_length));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.max_length != 0) {
|
||||
type.set_length((ObLength)udf_ctx.udf_init_.max_length);
|
||||
} else {
|
||||
type.set_length(default_accuracy.get_length());
|
||||
// TODO how about collation?
|
||||
/*
|
||||
* type.set_charset_type(charset_type);
|
||||
* type.set_collation_type(collation_type);
|
||||
* type.set_binary_collation(is_binary);
|
||||
* */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::UDFRetType::DECIMAL: {
|
||||
/* for string and decimal, mysql use char* as result */
|
||||
type.set_number();
|
||||
const ObAccuracy& default_accuracy = ObAccuracy::DDL_DEFAULT_ACCURACY[ObNumberType];
|
||||
/*
|
||||
* set scale
|
||||
* OB_MAX_DECIMAL_SCALE is 30, OB_MAX_DOUBLE_FLOAT_PRECISION is 53,
|
||||
* ObUdfUtil::DECIMAL_MAX_STR_LENGTH is 9*9 + 2, the same.
|
||||
* */
|
||||
if (udf_ctx.udf_init_.decimals > OB_MAX_DECIMAL_SCALE) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the decimal is invalid", K(ret), K(udf_ctx.udf_init_.decimals));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.decimals != 0) {
|
||||
type.set_scale((ObScale)udf_ctx.udf_init_.decimals);
|
||||
} else {
|
||||
type.set_scale(default_accuracy.get_scale());
|
||||
}
|
||||
/* set precision */
|
||||
if (OB_SUCC(ret)) {
|
||||
if (udf_ctx.udf_init_.max_length > OB_MAX_DECIMAL_PRECISION) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the max length is invalid", K(ret), K(udf_ctx.udf_init_.max_length));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.max_length != 0) {
|
||||
type.set_precision((ObPrecision)udf_ctx.udf_init_.max_length);
|
||||
} else {
|
||||
type.set_precision(default_accuracy.get_precision());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::UDFRetType::REAL: {
|
||||
// for real, mysql use double as result
|
||||
type.set_double();
|
||||
const ObAccuracy& default_accuracy = ObAccuracy::DDL_DEFAULT_ACCURACY[ObDoubleType];
|
||||
/* set scale */
|
||||
if (udf_ctx.udf_init_.decimals > OB_MAX_DOUBLE_FLOAT_SCALE) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the decimal is invalid", K(ret), K(udf_ctx.udf_init_.decimals));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.decimals != 0) {
|
||||
type.set_scale((ObScale)udf_ctx.udf_init_.decimals);
|
||||
} else {
|
||||
type.set_scale(default_accuracy.get_scale());
|
||||
}
|
||||
/* set precision */
|
||||
if (OB_SUCC(ret)) {
|
||||
if (udf_ctx.udf_init_.max_length > OB_MAX_DOUBLE_FLOAT_PRECISION) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the max length is invalid", K(ret), K(udf_ctx.udf_init_.max_length));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.max_length != 0) {
|
||||
type.set_precision((ObPrecision)udf_ctx.udf_init_.max_length);
|
||||
} else {
|
||||
type.set_precision(default_accuracy.get_precision());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::UDFRetType::INTEGER: {
|
||||
// for integer, mysql use long long as result
|
||||
type.set_int();
|
||||
const ObAccuracy& default_accuracy = ObAccuracy::DDL_DEFAULT_ACCURACY[ObIntType];
|
||||
if (udf_ctx.udf_init_.max_length > OB_MAX_INTEGER_DISPLAY_WIDTH) {
|
||||
ret = OB_CANT_INITIALIZE_UDF;
|
||||
LOG_WARN("the max length is invalid", K(ret), K(udf_ctx.udf_init_.max_length), K(udf_ctx.udf_init_.decimals));
|
||||
LOG_USER_ERROR(OB_CANT_INITIALIZE_UDF, udf_meta.name_.length(), udf_meta.name_.ptr());
|
||||
} else if (udf_ctx.udf_init_.max_length != 0) {
|
||||
type.set_precision((ObPrecision)udf_ctx.udf_init_.max_length);
|
||||
type.set_scale(0);
|
||||
} else {
|
||||
type.set_precision(default_accuracy.get_precision());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unhandled udf result type", K(ret));
|
||||
}
|
||||
/* do udf deinit function */
|
||||
if (init_succ) {
|
||||
IGNORE_RETURN udf_func->process_deinit_func(udf_ctx);
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("udf get result type", K(type));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::load_so(const common::ObString dl, ObUdfSoHandler& handler)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObUdfSoHandler handler_tmp = nullptr;
|
||||
// Since ObString does not store the last \0, this method is still used, otherwise dlopen will fail.
|
||||
int so_name_max_len = OB_MAX_UDF_NAME_LENGTH + ObUdfUtil::UDF_MAX_EXPAND_LENGTH;
|
||||
char so_name[OB_MAX_UDF_NAME_LENGTH + ObUdfUtil::UDF_MAX_EXPAND_LENGTH];
|
||||
MEMSET(so_name, 0, so_name_max_len);
|
||||
MEMCPY(so_name, dl.ptr(), dl.length());
|
||||
if (OB_ISNULL(handler_tmp = dlopen(so_name, RTLD_NOW))) {
|
||||
/* some error happened */
|
||||
ret = OB_CANT_OPEN_LIBRARY;
|
||||
char* error_msg = dlerror();
|
||||
LOG_WARN("failed to open dl", K(ret), K(error_msg));
|
||||
} else {
|
||||
/* we got the so handler success */
|
||||
handler = handler_tmp;
|
||||
LOG_DEBUG("udf get dll handler", K(handler));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ObUdfUtil::unload_so(ObUdfSoHandler& handler)
|
||||
{
|
||||
if (nullptr != handler) {
|
||||
dlclose(handler);
|
||||
}
|
||||
}
|
||||
|
||||
int ObUdfUtil::deep_copy_udf_args_cell(
|
||||
common::ObIAllocator& allocator, int64_t param_num, const common::ObObj* src_objs, common::ObObj*& dst_objs)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObObj* objs_tmp = nullptr;
|
||||
if (OB_ISNULL(src_objs) || param_num < 0) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("invalid input", K(ret), K(src_objs), K(param_num));
|
||||
} else if (param_num == 0) {
|
||||
} else if (OB_ISNULL(objs_tmp = (ObObj*)allocator.alloc(param_num * sizeof(ObObj)))) {
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
LOG_WARN("allocate failed", K(ret));
|
||||
}
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < param_num; ++i) {
|
||||
if (OB_FAIL(ob_write_obj(allocator, src_objs[i], objs_tmp[i]))) {
|
||||
LOG_WARN("deep copy obj failed", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
dst_objs = objs_tmp;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::init_udf_args(ObIAllocator& allocator, const common::ObIArray<common::ObString>& udf_attributes,
|
||||
const common::ObIArray<ObExprResType>& udf_attributes_types, ObUdfArgs& udf_args)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
if (udf_attributes.count() > INT64_MAX) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("too much args", K(ret));
|
||||
} else if (udf_attributes.count() != udf_attributes_types.count()) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unexpected input", K(ret), K(udf_attributes.count()), K(udf_attributes_types.count()));
|
||||
} else {
|
||||
udf_args.arg_count = (unsigned int)udf_attributes.count();
|
||||
udf_args.args = (char**)allocator.alloc(sizeof(char*) * udf_args.arg_count);
|
||||
udf_args.lengths = (unsigned long*)allocator.alloc(sizeof(unsigned long) * udf_args.arg_count);
|
||||
udf_args.arg_type = (enum UdfItemResult*)allocator.alloc(sizeof(enum UdfItemResult) * udf_args.arg_count);
|
||||
udf_args.maybe_null = (char*)allocator.alloc(sizeof(char) * udf_args.arg_count);
|
||||
udf_args.attributes = (char**)allocator.alloc(sizeof(char*) * udf_args.arg_count);
|
||||
udf_args.attribute_lengths = (unsigned long*)allocator.alloc(sizeof(unsigned long) * udf_args.arg_count);
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (udf_args.arg_count != 0 && (OB_ISNULL(udf_args.args) || OB_ISNULL(udf_args.lengths) ||
|
||||
OB_ISNULL(udf_args.arg_type) || OB_ISNULL(udf_args.maybe_null) ||
|
||||
OB_ISNULL(udf_args.attributes) || OB_ISNULL(udf_args.attribute_lengths))) {
|
||||
ret = OB_ALLOCATE_MEMORY_FAILED;
|
||||
LOG_WARN("allocate memory failed", K(ret), K(udf_args.arg_count));
|
||||
}
|
||||
}
|
||||
for (int64_t i = 0; OB_SUCC(ret) && i < udf_args.arg_count; ++i) {
|
||||
udf_args.lengths[i] = 0;
|
||||
udf_args.args[i] = nullptr;
|
||||
udf_args.attribute_lengths[i] = udf_attributes.at(i).length();
|
||||
if (OB_FAIL(convert_ob_type_to_udf_type(udf_attributes_types.at(i).get_type(), udf_args.arg_type[i]))) {
|
||||
LOG_WARN("failt to convert ob type to udf type",
|
||||
K(udf_attributes_types.at(i).get_type()),
|
||||
K(udf_attributes_types.at(i).get_calc_type()));
|
||||
} else if (udf_args.attribute_lengths[i] > 0) {
|
||||
udf_args.attributes[i] = (char*)allocator.alloc(udf_args.attribute_lengths[i]);
|
||||
IGNORE_RETURN MEMCPY(udf_args.attributes[i], udf_attributes.at(i).ptr(), udf_attributes.at(i).length());
|
||||
} else {
|
||||
udf_args.attributes[i] = nullptr;
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
udf_args.maybe_null[i] = udf_attributes_types.at(i).has_result_flag(OB_MYSQL_NOT_NULL_FLAG);
|
||||
}
|
||||
}
|
||||
IGNORE_RETURN print_udf_args_to_log(udf_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::init_const_args(ObIAllocator& allocator, const common::ObIArray<ObUdfConstArgs>& const_results,
|
||||
ObUdfArgs& udf_args, ObExprCtx& expr_ctx)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(allocator);
|
||||
ObNewRow empty_row;
|
||||
const auto& Objs = expr_ctx.phy_plan_ctx_->get_param_store();
|
||||
UNUSED(Objs);
|
||||
for (int64_t i = 0; i < const_results.count() && OB_SUCC(ret); ++i) {
|
||||
const ObUdfConstArgs& const_args = const_results.at(i);
|
||||
ObObj value;
|
||||
if (const_args.idx_in_udf_arg_ >= udf_args.arg_count || OB_ISNULL(const_args.sql_calc_)) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("invalid idx", K(ret), K(const_args.idx_in_udf_arg_), K(udf_args.arg_count));
|
||||
} else if (OB_FAIL(const_args.sql_calc_->calc(expr_ctx, empty_row, value))) {
|
||||
LOG_WARN("failed to calc sql expression", K(ret));
|
||||
} else {
|
||||
udf_args.args[const_args.idx_in_udf_arg_] = (char*)value.get_data_ptr();
|
||||
udf_args.lengths[const_args.idx_in_udf_arg_] = value.get_string_len();
|
||||
}
|
||||
LOG_DEBUG("calculable value", K(value));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::set_udf_args(
|
||||
common::ObExprCtx& expr_ctx, int64_t param_num, common::ObObj* src_objs, ObUdfArgs& udf_args)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
// MySQL needs various buffers to copy data to temporary memory for UDF to use.
|
||||
// Since we have already deep copyed all the passed ObObj in the previous section,
|
||||
// the memory is directly used by the UDF and there is no need to copy it again.
|
||||
// The difference is that the int/double area of mysql is continuous. If UDF is used
|
||||
// this hidden condition, it may cause core
|
||||
if (param_num != udf_args.arg_count) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("invalid input", K(ret), K(param_num), K(udf_args.arg_count));
|
||||
}
|
||||
for (uint64_t i = 0; i < udf_args.arg_count && OB_SUCC(ret); i++) {
|
||||
udf_args.args[i] = 0;
|
||||
switch (udf_args.arg_type[i]) {
|
||||
case STRING_RESULT:
|
||||
case DECIMAL_RESULT: {
|
||||
if (src_objs[i].is_null()) {
|
||||
udf_args.lengths[i] = 0;
|
||||
} else {
|
||||
if (!src_objs[i].is_string_type()) {
|
||||
EXPR_DEFINE_CAST_CTX(expr_ctx, CM_WARN_ON_FAIL);
|
||||
if (OB_FAIL(ObObjCaster::to_type(ObVarcharType, cast_ctx, src_objs[i], src_objs[i]))) {
|
||||
LOG_WARN("failed to cast", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
udf_args.args[i] = (char*)(src_objs[i].get_data_ptr());
|
||||
udf_args.lengths[i] = src_objs[i].get_string_len();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INT_RESULT:
|
||||
/* int, udf should not use lengths */
|
||||
if (OB_UNLIKELY(ObIntTC != src_objs[i].get_type_class())) {
|
||||
EXPR_DEFINE_CAST_CTX(expr_ctx, CM_WARN_ON_FAIL);
|
||||
if (OB_FAIL(ObObjCaster::to_type(ObIntType, cast_ctx, src_objs[i], src_objs[i]))) {
|
||||
LOG_WARN("failed to cast", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret) && !src_objs[i].is_null()) {
|
||||
int64_t num = src_objs[i].get_int();
|
||||
src_objs[i].set_int(num);
|
||||
udf_args.args[i] = (char*)src_objs[i].get_data_ptr();
|
||||
}
|
||||
break;
|
||||
case REAL_RESULT:
|
||||
/* double, udf should not use lengths */
|
||||
if (!src_objs[i].is_double()) {
|
||||
EXPR_DEFINE_CAST_CTX(expr_ctx, CM_WARN_ON_FAIL);
|
||||
if (OB_FAIL(ObObjCaster::to_type(ObDoubleType, cast_ctx, src_objs[i], src_objs[i]))) {
|
||||
LOG_WARN("failed to cast", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret) && !src_objs[i].is_null()) {
|
||||
udf_args.args[i] = (char*)src_objs[i].get_data_ptr();
|
||||
}
|
||||
break;
|
||||
case ROW_RESULT:
|
||||
default:
|
||||
// This case should never be chosen
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unexpected type", K(ret), K(udf_args.arg_type[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
IGNORE_RETURN ObUdfUtil::print_udf_args_to_log(udf_args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_udf_func(share::schema::ObUDF::UDFRetType ret_type, common::ObIAllocator& allocator,
|
||||
ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAny func_origin_, common::ObObj& result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
switch (ret_type) {
|
||||
case share::schema::ObUDF::STRING: {
|
||||
if (OB_FAIL(ObUdfUtil::process_str(allocator, udf_init, udf_args, func_origin_, result))) {
|
||||
LOG_WARN("failed to process str", K(ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::DECIMAL: {
|
||||
if (OB_FAIL(ObUdfUtil::process_dec(allocator, udf_init, udf_args, func_origin_, result))) {
|
||||
LOG_WARN("failed to process dec", K(ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::INTEGER: {
|
||||
if (OB_FAIL(ObUdfUtil::process_int(allocator, udf_init, udf_args, func_origin_, result))) {
|
||||
LOG_WARN("failed to process int", K(ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case share::schema::ObUDF::REAL: {
|
||||
if (OB_FAIL(ObUdfUtil::process_real(allocator, udf_init, udf_args, func_origin_, result))) {
|
||||
LOG_WARN("failed to process real", K(ret));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unexpected udf ret type", K(ret), K(ret_type));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_str(
|
||||
common::ObIAllocator& allocator, ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAny func, ObObj& result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
unsigned long res_length = UDF_MAX_FIELD_WIDTH;
|
||||
unsigned char is_null_tmp = 0;
|
||||
unsigned char error = 0;
|
||||
char* str_for_user = (char*)allocator.alloc(UDF_MAX_FIELD_WIDTH);
|
||||
IGNORE_RETURN MEMSET(str_for_user, 0, UDF_MAX_FIELD_WIDTH);
|
||||
ObUdfFuncString func_str = (ObUdfFuncString)func;
|
||||
char* res = func_str(&udf_init, &udf_args, str_for_user, &res_length, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret));
|
||||
} else if (nullptr == res || is_null_tmp) {
|
||||
/* set null to result */
|
||||
result.set_null();
|
||||
} else if (res_length >= UDF_MAX_FIELD_WIDTH) {
|
||||
/*
|
||||
* if res_length is bigger than MAX_FIELD_WIDTH, it must be some error happened.
|
||||
* */
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("the string is too large, maybe some error happen in user defined function", K(ret), K(res_length));
|
||||
} else {
|
||||
// result.set_char_value(res, (ObString::obstr_size_t)res_length);
|
||||
result.set_string(ObVarcharType, res, (ObString::obstr_size_t)res_length);
|
||||
result.set_default_collation_type();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_dec(
|
||||
common::ObIAllocator& allocator, ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAny func, ObObj& result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
unsigned long res_length = UDF_DECIMAL_MAX_STR_LENGTH;
|
||||
unsigned char is_null_tmp = 0;
|
||||
unsigned char error = 0;
|
||||
char* buf_for_user = (char*)allocator.alloc(UDF_DECIMAL_MAX_STR_LENGTH);
|
||||
IGNORE_RETURN MEMSET(buf_for_user, 0, UDF_DECIMAL_MAX_STR_LENGTH);
|
||||
ObUdfFuncString func_str = (ObUdfFuncString)func;
|
||||
|
||||
common::number::ObNumber num;
|
||||
ObPrecision res_precision = -1;
|
||||
ObScale res_scale = -1;
|
||||
|
||||
char* res = func_str(&udf_init, &udf_args, buf_for_user, &res_length, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret));
|
||||
} else if (nullptr == res || is_null_tmp) {
|
||||
/* set null to result */
|
||||
result.set_null();
|
||||
} else if (res_length >= UDF_DECIMAL_MAX_STR_LENGTH) {
|
||||
/*
|
||||
* if res_length is bigger than DECIMAL_MAX_STR_LENGTH, it must be some error happened.
|
||||
* */
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("the string is too large, maybe some error happen in user defined function", K(ret), K(res_length));
|
||||
} else if (OB_FAIL(num.from(res, (long int)res_length, allocator, &res_precision, &res_scale))) {
|
||||
LOG_WARN("fail to convert char* to decimal/obnumber", K(ret));
|
||||
} else {
|
||||
result.set_number(num);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_int(
|
||||
common::ObIAllocator& allocator, ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAny func, ObObj& result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(allocator);
|
||||
unsigned char is_null_tmp = 0;
|
||||
unsigned char error = 0;
|
||||
|
||||
ObUdfFuncLonglong func_int = (ObUdfFuncLonglong)func;
|
||||
long long int tmp = func_int(&udf_init, &udf_args, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret));
|
||||
} else if (is_null_tmp) {
|
||||
/* set null to result */
|
||||
result.set_null();
|
||||
} else {
|
||||
result.set_int(tmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_real(
|
||||
common::ObIAllocator& allocator, ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAny func, ObObj& result)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(allocator);
|
||||
unsigned char is_null_tmp = 0;
|
||||
unsigned char error = 0;
|
||||
|
||||
ObUdfFuncDouble func_double = (ObUdfFuncDouble)func;
|
||||
double tmp = func_double(&udf_init, &udf_args, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret));
|
||||
} else if (is_null_tmp) {
|
||||
/* set null to result */
|
||||
result.set_null();
|
||||
} else {
|
||||
result.set_double(tmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_add_func(ObUdfInit& udf_init, ObUdfArgs& udf_args, const ObUdfFuncAdd func)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
unsigned char is_null_tmp = 0;
|
||||
unsigned char error = 0;
|
||||
IGNORE_RETURN func(&udf_init, &udf_args, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret));
|
||||
}
|
||||
UNUSED(is_null_tmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::process_clear_func(ObUdfInit& udf_init, const ObUdfFuncClear func)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
unsigned char error = 0;
|
||||
unsigned char is_null_tmp = 0;
|
||||
IGNORE_RETURN func(&udf_init, &is_null_tmp, &error);
|
||||
if (error) {
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("error happened while do the user's code", K(ret), K(error), K(is_null_tmp));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ObUdfUtil::construct_udf_args(ObUdfArgs& args)
|
||||
{
|
||||
args.arg_count = 0;
|
||||
args.arg_type = nullptr;
|
||||
args.args = nullptr;
|
||||
args.attribute_lengths = nullptr;
|
||||
args.attributes = nullptr;
|
||||
args.extension = nullptr;
|
||||
args.maybe_null = nullptr;
|
||||
}
|
||||
|
||||
void ObUdfUtil::construct_udf_init(ObUdfInit& init)
|
||||
{
|
||||
init.const_item = 0;
|
||||
init.decimals = 0;
|
||||
init.extension = nullptr;
|
||||
init.max_length = 0;
|
||||
init.maybe_null = 0;
|
||||
init.ptr = nullptr;
|
||||
}
|
||||
|
||||
const char* ObUdfUtil::result_type_name(enum UdfItemResult result)
|
||||
{
|
||||
int64_t offset = 0;
|
||||
if (INVALID_RESULT == result) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = (int64_t)result;
|
||||
}
|
||||
static const char* result_string[7] = {
|
||||
"INVALID_RESULT",
|
||||
"STRING_RESULT",
|
||||
"REAL_RESULT",
|
||||
"INT_RESULT",
|
||||
"ROW_RESULT",
|
||||
"DECIMAL_RESULT",
|
||||
};
|
||||
return result_string[offset + 1];
|
||||
}
|
||||
|
||||
void ObUdfUtil::print_udf_args_to_log(const ObUdfArgs& args)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
ObSEArray<ObString, 16> args_strings;
|
||||
ObSEArray<ObString, 16> atts_strings;
|
||||
ObSEArray<int64_t, 16> maybe_nulls;
|
||||
ObSEArray<ObString, 16> arg_type_strings;
|
||||
for (int64_t i = 0; i < args.arg_count && OB_SUCC(ret); ++i) {
|
||||
if (args.lengths[i] > 0 && OB_FAIL(args_strings.push_back(ObString(args.lengths[i], args.args[i])))) {
|
||||
LOG_WARN("push back failed", K(ret));
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (args.attribute_lengths[i] > 0 &&
|
||||
OB_FAIL(atts_strings.push_back(ObString(args.attribute_lengths[i], args.attributes[i])))) {
|
||||
LOG_WARN("push back failed", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_FAIL(maybe_nulls.push_back(args.maybe_null[i] == 1 ? 1 : 0))) {
|
||||
LOG_WARN("push back failed", K(ret));
|
||||
}
|
||||
}
|
||||
if (OB_SUCC(ret)) {
|
||||
if (OB_FAIL(arg_type_strings.push_back(
|
||||
ObString(STRLEN(result_type_name(args.arg_type[i])), result_type_name(args.arg_type[i]))))) {
|
||||
LOG_WARN("push back failed", K(ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("UDF ARGS",
|
||||
K(ret),
|
||||
K(args.arg_count),
|
||||
K(args_strings),
|
||||
K(atts_strings),
|
||||
K(maybe_nulls),
|
||||
K(arg_type_strings),
|
||||
K(lbt()));
|
||||
}
|
||||
|
||||
void ObUdfUtil::print_udf_init_to_log(const ObUdfInit& init)
|
||||
{
|
||||
LOG_DEBUG("UDF INIT", K(init.maybe_null), K(init.decimals), K(init.max_length), K(init.ptr), K(init.const_item));
|
||||
}
|
||||
|
||||
void ObUdfUtil::assign_udf_args(const ObUdfArgs& src_args, ObUdfArgs& dst_args)
|
||||
{
|
||||
dst_args.arg_count = src_args.arg_count;
|
||||
dst_args.extension = src_args.extension;
|
||||
}
|
||||
|
||||
void ObUdfUtil::assign_udf_init(const ObUdfInit& src_init, ObUdfInit& dst_init)
|
||||
{
|
||||
dst_init.const_item = src_init.const_item;
|
||||
dst_init.decimals = src_init.decimals;
|
||||
dst_init.extension = src_init.extension;
|
||||
dst_init.max_length = src_init.max_length;
|
||||
dst_init.maybe_null = src_init.maybe_null;
|
||||
dst_init.ptr = src_init.ptr;
|
||||
}
|
||||
|
||||
int ObUdfUtil::is_udf_ctx_valid(const ObUdfArgs& udf_args, const ObUdfInit& udf_init)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
UNUSED(udf_init);
|
||||
if (udf_args.arg_type == nullptr || udf_args.args == nullptr || udf_args.lengths == nullptr ||
|
||||
udf_args.maybe_null == nullptr || udf_args.attributes == nullptr || udf_args.attribute_lengths == nullptr) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("some field is null",
|
||||
K(udf_args.arg_type),
|
||||
K(udf_args.args),
|
||||
K(udf_args.maybe_null),
|
||||
K(udf_args.attributes),
|
||||
K(udf_args.attribute_lengths),
|
||||
K(udf_args.arg_count));
|
||||
} else {
|
||||
for (int64_t i = 0; i < udf_args.arg_count && OB_SUCC(ret); ++i) {
|
||||
if (udf_args.args[i] == nullptr || udf_args.attributes[i] == nullptr) {
|
||||
ret = OB_INVALID_ARGUMENT;
|
||||
LOG_WARN("some field is null", K(ret), K(udf_args.args[i]), K(udf_args.attributes[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::convert_ob_type_to_udf_type(common::ObObjType ob_type, UdfItemResult& udf_type)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
// mysql 5.6 type.
|
||||
obmysql::EMySQLFieldType mysql_type;
|
||||
uint16_t flags;
|
||||
ObScale num_decimals;
|
||||
if (OB_FAIL(ObSMUtils::get_mysql_type(ob_type, mysql_type, flags, num_decimals))) {
|
||||
LOG_WARN("get mysql type failed", K(ret));
|
||||
} else if (OB_FAIL(convert_mysql_type_to_udf_type(mysql_type, udf_type))) {
|
||||
LOG_WARN("get udf type failed", K(ret));
|
||||
}
|
||||
LOG_DEBUG("udf type change", K(ob_type), K(mysql_type), K(udf_type));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ObUdfUtil::convert_mysql_type_to_udf_type(const obmysql::EMySQLFieldType& mysql_type, UdfItemResult& udf_type)
|
||||
{
|
||||
int ret = OB_SUCCESS;
|
||||
|
||||
// In the expression item of mysql, it seems that each category returns the hard-coded result type and field type.
|
||||
switch (mysql_type) {
|
||||
case EMySQLFieldType::MYSQL_TYPE_TINY:
|
||||
case EMySQLFieldType::MYSQL_TYPE_SHORT:
|
||||
case EMySQLFieldType::MYSQL_TYPE_INT24:
|
||||
case EMySQLFieldType::MYSQL_TYPE_LONG:
|
||||
case EMySQLFieldType::MYSQL_TYPE_LONGLONG:
|
||||
udf_type = INT_RESULT;
|
||||
break;
|
||||
case EMySQLFieldType::MYSQL_TYPE_DECIMAL:
|
||||
case EMySQLFieldType::MYSQL_TYPE_NEWDECIMAL:
|
||||
case EMySQLFieldType::MYSQL_TYPE_OB_NUMBER_FLOAT:
|
||||
udf_type = DECIMAL_RESULT;
|
||||
break;
|
||||
case EMySQLFieldType::MYSQL_TYPE_FLOAT:
|
||||
case EMySQLFieldType::MYSQL_TYPE_DOUBLE:
|
||||
udf_type = REAL_RESULT;
|
||||
break;
|
||||
case EMySQLFieldType::MYSQL_TYPE_VARCHAR:
|
||||
case EMySQLFieldType::MYSQL_TYPE_VAR_STRING:
|
||||
case EMySQLFieldType::MYSQL_TYPE_STRING:
|
||||
case EMySQLFieldType::MYSQL_TYPE_OB_NCHAR:
|
||||
case EMySQLFieldType::MYSQL_TYPE_OB_NVARCHAR2:
|
||||
case EMySQLFieldType::MYSQL_TYPE_OB_UROWID:
|
||||
udf_type = STRING_RESULT;
|
||||
break;
|
||||
case EMySQLFieldType::MYSQL_TYPE_YEAR:
|
||||
udf_type = INT_RESULT;
|
||||
break;
|
||||
case EMySQLFieldType::MYSQL_TYPE_TIMESTAMP:
|
||||
case EMySQLFieldType::MYSQL_TYPE_DATE:
|
||||
case EMySQLFieldType::MYSQL_TYPE_TIME:
|
||||
case EMySQLFieldType::MYSQL_TYPE_DATETIME:
|
||||
case EMySQLFieldType::MYSQL_TYPE_NEWDATE:
|
||||
case EMySQLFieldType::MYSQL_TYPE_BIT:
|
||||
// case MYSQL_TYPE_TIMESTAMP2:
|
||||
// case MYSQL_TYPE_DATETIME2:
|
||||
// case MYSQL_TYPE_TIME2:
|
||||
// case MYSQL_TYPE_JSON:
|
||||
case EMySQLFieldType::MYSQL_TYPE_ENUM:
|
||||
case EMySQLFieldType::MYSQL_TYPE_SET:
|
||||
case EMySQLFieldType::MYSQL_TYPE_GEOMETRY:
|
||||
case EMySQLFieldType::MYSQL_TYPE_NULL:
|
||||
case EMySQLFieldType::MYSQL_TYPE_TINY_BLOB:
|
||||
case EMySQLFieldType::MYSQL_TYPE_BLOB:
|
||||
case EMySQLFieldType::MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case EMySQLFieldType::MYSQL_TYPE_LONG_BLOB:
|
||||
udf_type = STRING_RESULT;
|
||||
break;
|
||||
default:
|
||||
udf_type = INVALID_RESULT;
|
||||
ret = OB_ERR_UNEXPECTED;
|
||||
LOG_WARN("unhandled mysql type", K(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace sql
|
||||
} // namespace oceanbase
|
||||
Reference in New Issue
Block a user