/** * 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 #define OB_MAX_WEIGHT OB_MAX_VARCHAR_LENGTH #include #include "sql/parser/ob_item_type.h" #include "sql/engine/expr/ob_expr_weight_string.h" #include "sql/engine/expr/ob_expr_operator.h" #include "share/object/ob_obj_cast.h" #include "lib/oblog/ob_log.h" #include "sql/engine/expr/ob_datum_cast.h" #include "ob_expr_util.h" using namespace oceanbase::common; namespace oceanbase { namespace sql { ObExprWeightString::ObExprWeightString(ObIAllocator &alloc) : ObStringExprOperator(alloc, T_FUN_SYS_WEIGHT_STRING, N_WEIGHT_STRING, MORE_THAN_ZERO) { } ObExprWeightString::~ObExprWeightString() { } int ObExprWeightString::calc_result_typeN(ObExprResType &type, ObExprResType *types_stack, int64_t param_num, ObExprTypeCtx &type_ctx) const { int ret = OB_SUCCESS; UNUSED(param_num); CK (OB_NOT_NULL(type_ctx.get_session())); if (NOT_ROW_DIMENSION != row_dimension_ || ObMaxType == types_stack[0].get_type()) { ret = OB_ERR_INVALID_TYPE_FOR_OP; } else { if (types_stack[0].get_type() > ObUNumberType ) { // 输入不是数字类型时 type_ctx.set_cast_mode(type_ctx.get_cast_mode() | CM_NULL_ON_WARN); types_stack[0].set_calc_type(ObVarcharType); } int max_length = OB_MAX_VARBINARY_LENGTH; // The maximum length of the result of WEIGHT_STRING() int result_length = types_stack[1].get_param().get_int(); int nweight = types_stack[2].get_param().get_int(); bool as_binary = types_stack[4].get_param().get_int(); ObCollationLevel coll_level = CS_LEVEL_INVALID; if (as_binary) { coll_level = CS_LEVEL_IMPLICIT; } else { coll_level = types_stack[0].get_collation_level(); } ObCollationType collation_type = types_stack[0].get_collation_type(); const ObCharsetInfo *cs = ObCharset::get_charset(collation_type); if (types_stack[0].get_type() == ObDateTimeType || types_stack[0].get_type() == ObTimestampType || types_stack[0].get_type() == ObDateType || types_stack[0].get_type() == ObTimeType ) { // 日期、时间等类型,max_lenght是输入的类型的长度 max_length = types_stack[0].get_length(); } else if (result_length > 0) { max_length = result_length; } else if (as_binary) { // as_binary的情况下,以nweight作为输出结果的max_length max_length = nweight; } else { // 输入为 char的情况下,使用cs->mbmaxlen计算max_length max_length = cs->mbmaxlen * max(nweight, types_stack[0].get_length()*cs->mbmaxlen); } ObObj aaa = types_stack[0].get_param(); // 推导结果 type.set_varchar(); type.set_collation_type(CS_TYPE_BINARY); type.set_collation_level(coll_level); type.set_length(max_length); } return ret; } int ObExprWeightString::calc_resultN(common::ObObj &result , const common::ObObj *objs_array, int64_t param_num,common::ObExprCtx &expr_ctx) const { int ret = OB_SUCCESS; if (OB_ISNULL(expr_ctx.calc_buf_)) { ret = OB_NOT_INIT; LOG_WARN("varchar buffer not init", K(ret)); } if (param_num == 5) { if (OB_UNLIKELY(objs_array[0].is_null()) || OB_UNLIKELY(objs_array[1].is_null()) || OB_UNLIKELY(objs_array[2].is_null()) || OB_UNLIKELY(objs_array[3].is_null()) || OB_UNLIKELY(objs_array[4].is_null()) ) { result.set_null(); } else { const ObString str = objs_array[0].get_string(); int result_length = objs_array[1].get_int(); int nweights = objs_array[2].get_int(); int flags = objs_array[3].get_int(); bool as_binary = objs_array[4].get_int(); ObCollationType collation_type = CS_TYPE_INVALID; if (as_binary) { collation_type = CS_TYPE_BINARY; } else { collation_type = objs_array[0].get_collation_type(); } const ObCharsetInfo *cs = ObCharset::get_charset(collation_type); flags = ob_strxfrm_flag_normalize(flags, cs->levels_for_order); // calc the length of result size_t frm_length = 0; size_t tmp_length = 0; if (result_length > 0) { tmp_length = result_length; } else { tmp_length = cs->coll->strnxfrmlen(cs, cs->mbmaxlen*max(str.length() , nweights)); } int is_valid_unicode_tmp = 1; char *out_buf; if (OB_ISNULL(out_buf = static_cast(expr_ctx.calc_buf_->alloc(tmp_length)))) { result.set_null(); ret = OB_ALLOCATE_MEMORY_FAILED; LOG_ERROR("alloc memory failed",K(ret), K(out_buf), K(tmp_length)); } else { frm_length = cs->coll->strnxfrm(cs, reinterpret_cast(out_buf), tmp_length, nweights ? nweights: tmp_length, reinterpret_cast(str.ptr()), str.length(), flags, &is_valid_unicode_tmp); result.set_varchar(out_buf,frm_length); } } } else { ret = OB_INVALID_ARGUMENT_NUM; result.set_null(); LOG_ERROR("invalid argument num",K(ret), K(param_num), K(objs_array)); } return ret; } int ObExprWeightString::eval_weight_string(const ObExpr &expr, ObEvalCtx &ctx, ObDatum &res_datum) { int ret = OB_SUCCESS; ObDatum *arg = NULL; ObDatum *result_length_arg = NULL; ObDatum *nweights_arg = NULL; ObDatum *flags_arg = NULL; ObDatum *as_binary_arg = NULL; if (OB_FAIL(expr.args_[0]->eval(ctx, arg)) || OB_FAIL(expr.args_[1]->eval(ctx, result_length_arg)) || OB_FAIL(expr.args_[2]->eval(ctx, nweights_arg)) || OB_FAIL(expr.args_[3]->eval(ctx, flags_arg)) || OB_FAIL(expr.args_[4]->eval(ctx, as_binary_arg))) { LOG_WARN("eval arg failed", K(ret), KP(arg), KP(result_length_arg), KP(nweights_arg), KP(flags_arg), KP(as_binary_arg)); } else if (arg->is_null() || arg->get_string() == NULL || expr.args_[0]->datum_meta_.type_ <= ObUNumberType ) { // The input string is NULL or numeric res_datum.set_null(); } else { const ObString str = arg->get_string(); int result_length = result_length_arg->get_int(); int nweights = nweights_arg->get_int(); int flags = flags_arg->get_int(); bool as_binary = as_binary_arg->get_int(); // Get the character set and collation information of the input string ObCollationType collation_type = CS_TYPE_INVALID; if (as_binary) { collation_type = CS_TYPE_BINARY; } else { collation_type = expr.args_[0]->datum_meta_.cs_type_; } const ObCharsetInfo *cs = ObCharset::get_charset(collation_type); flags = ob_strxfrm_flag_normalize(flags, cs->levels_for_order); // calc the length of result size_t frm_length = 0; size_t tmp_length = 0; if (result_length > 0) { tmp_length = result_length; } else { tmp_length = cs->coll->strnxfrmlen(cs, cs->mbmaxlen*max(str.length() , nweights)); } int is_valid_unicode_tmp = 1; char *out_buf = expr.get_str_res_mem(ctx, tmp_length); if (OB_ISNULL(out_buf)) { ret = OB_ALLOCATE_MEMORY_FAILED; } else { frm_length = cs->coll->strnxfrm(cs, reinterpret_cast(out_buf), tmp_length, nweights ? nweights: tmp_length, reinterpret_cast(str.ptr()), str.length(), flags, &is_valid_unicode_tmp); res_datum.set_string(out_buf,frm_length); } } return ret; } int ObExprWeightString::cg_expr(ObExprCGCtx &op_cg_ctx, const ObRawExpr &raw_expr, ObExpr &rt_expr) const { UNUSED(op_cg_ctx); UNUSED(raw_expr); rt_expr.eval_func_ = ObExprWeightString::eval_weight_string; return OB_SUCCESS; } uint64_t ObExprWeightString::ob_strxfrm_flag_normalize(uint64_t flags, uint64_t maximum) { /* If levels are omitted, then 1-maximum is assumed*/ if (!(flags & OB_STRXFRM_LEVEL_ALL)) { static uint64_t def_level_flags[]= {0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F }; uint64_t flag_pad= flags & (OB_STRXFRM_PAD_WITH_SPACE | OB_STRXFRM_PAD_TO_MAXLEN); flags= def_level_flags[maximum] | flag_pad; } else { uint64_t i = 0; uint64_t flag_lev= flags & OB_STRXFRM_LEVEL_ALL; uint64_t flag_dsc= (flags >> OB_STRXFRM_DESC_SHIFT) & OB_STRXFRM_LEVEL_ALL; uint64_t flag_rev= (flags >> OB_STRXFRM_REVERSE_SHIFT) & OB_STRXFRM_LEVEL_ALL; uint64_t flag_pad= flags & (OB_STRXFRM_PAD_WITH_SPACE | OB_STRXFRM_PAD_TO_MAXLEN); /* If any level number is greater than the maximum, it is treated as the maximum. */ maximum--; flags= 0; for (i= 0; i < OB_STRXFRM_NLEVELS; i++) { uint64_t src_bit= 1 << i; if (flag_lev & src_bit) { uint64_t dst_bit= 1 << std::min(i, maximum); flags|= dst_bit; flags|= (flag_dsc & dst_bit) << OB_STRXFRM_DESC_SHIFT; flags|= (flag_rev & dst_bit) << OB_STRXFRM_REVERSE_SHIFT; } } flags|= flag_pad; } return flags; } } }