/** * 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_number_format_models.h" #include "lib/charset/ob_charset.h" #include "sql/engine/ob_exec_context.h" #include "sql/engine/expr/ob_expr_util.h" #include "share/system_variable/ob_nls_system_variable.h" namespace oceanbase { namespace sql { const int64_t MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS= 256; const ObNFMKeyWord ObNFMElem::NFM_KEYWORDS[MAX_TYPE_NUMBER] = { {",", NFM_COMMA, GROUPING_GROUP, 1}, {".", NFM_PERIOD, GROUPING_GROUP, 1}, {"$", NFM_DOLLAR, DOLLAR_GROUP, 1}, {"0", NFM_ZERO, NUMBER_GROUP, 1}, {"9", NFM_NINE, NUMBER_GROUP, 1}, {"B", NFM_B, BLANK_GROUP, 0}, {"C", NFM_C, CURRENCY_GROUP, 7}, {"D", NFM_D, ISO_GROUPING_GROUP, 1}, {"EEEE", NFM_EEEE, EEEE_GROUP, 5}, {"G", NFM_G, ISO_GROUPING_GROUP, 1}, {"L", NFM_L, CURRENCY_GROUP, 10}, {"MI", NFM_MI, SIGN_GROUP, 1}, {"PR", NFM_PR, SIGN_GROUP, 2}, {"RN", NFM_RN, ROMAN_GROUP, 15}, {"S", NFM_S, SIGN_GROUP, 1}, {"TME", NFM_TME, TM_GROUP, 64}, {"TM9", NFM_TM9, TM_GROUP, 64}, {"TM", NFM_TM, TM_GROUP, 64}, {"U", NFM_U, CURRENCY_GROUP, 10}, {"V", NFM_V, MULTI_GROUP, 0}, {"X", NFM_X, HEX_GROUP, 1}, {"FM", NFM_FM, FILLMODE_GROUP, 0} }; ObString ObNFMElem::get_elem_type_name() const { ObString result; if (OB_ISNULL(keyword_) || is_valid_type(keyword_->elem_type_) || offset_ < 0) { result = ObString("INVALID ELEMENT TYPE"); } else { result = ObNFMElem::NFM_KEYWORDS[keyword_->elem_type_].to_obstring(); } return result; } int ObNFMDescPrepare::check_conflict_group(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; if (OB_ISNULL(elem_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid elem item", K(ret)); } else { const ObNFMElem::ElementGroup elem_group = elem_item->keyword_->elem_group_; switch(elem_group) { case ObNFMElem::NUMBER_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::GROUPING_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_iso_grouping_group(fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::ISO_GROUPING_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_grouping_group(fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::DOLLAR_GROUP: if (ObNFMElem::has_type(NFM_DOLLAR_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_currency_group(fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::CURRENCY_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_DOLLAR_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_currency_group(fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::EEEE_GROUP: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::ROMAN_GROUP: if (ObNFMElem::has_type(~NFM_FILLMODE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::MULTI_GROUP: if (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::HEX_GROUP: if (ObNFMElem::has_type((~NFM_HEX_FLAG) & (~NFM_ZERO_FLAG) & (~NFM_NINE_FLAG) & (~NFM_FILLMODE_FLAG) & (~NFM_COMMA_FLAG) & (~NFM_G_FLAG), fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::SIGN_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_sign_group(fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::BLANK_GROUP: if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_tm_group(fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::TM_GROUP: if (ObNFMElem::has_type(~NFM_FILLMODE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret), K(elem_group)); } break; case ObNFMElem::FILLMODE_GROUP: // do nothing break; default : ret = OB_ERR_UNEXPECTED; LOG_WARN("unknown group type", K(ret), K(elem_group)); } } return ret; } int ObNFMDescPrepare::check_elem_comma_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear in the integral part of a number // can't appear before the number // can't appear at the same time as element 'EEEE' // can't appear after the element 'V' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (-1 == fmt_desc.digital_start_ || elem_item->offset_ >= fmt_desc.decimal_pos_ || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem comma is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_period_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can appear only once // can't appear at the same time as element 'V' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem period is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_dollar_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can appear only once // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_DOLLAR_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem dollar is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_zero_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear after element 'EEEE' or element 'X' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem zero is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_nine_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear after element 'EEEE' or element 'X' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem nine is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_b_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can appear only once // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_BLANK_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem blank is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_c_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can appear only once // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_currency_group(fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem currency is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_d_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear at the same time as element 'V' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem d is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_eeee_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear at the same time as thousands separator // when element 'V' appears in front of all numbers, element 'EEEE' can't appear behind if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_) || (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_) && fmt_desc.multi_ == fmt_desc.pre_num_count_) || ObNFMElem::has_type(NFM_COMMA_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem eeee is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_g_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear in the integral part of a number // can't appear before the number // can't appear after the element 'V' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (-1 == fmt_desc.digital_start_ || elem_item->offset_ >= fmt_desc.decimal_pos_ || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem g is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_l_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_C_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem l is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_mi_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear at the end of the fmt string if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_MI_FLAG, fmt_desc.elem_flag_) || !elem_item->is_last_elem_) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem mi is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_pr_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear at the end of the fmt string if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_PR_FLAG, fmt_desc.elem_flag_) || !elem_item->is_last_elem_) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem pr is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_rn_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem rn is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_s_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear at the front or the end of the fmt string // but 'FM' + 'S' is an exception if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc.elem_flag_) || (0 !=elem_item->offset_ && (!elem_item->is_last_elem_ && elem_item->prefix_type_ != ObNFMElem::NFM_FM))) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem s is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_tm_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_TM_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem tm is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_u_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_U_FLAG, fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem u is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_v_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can't appear at the same time as element '.' or element 'D' // can't appear after the element 'EEEE' if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_) || fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem multi is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_x_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::NFM_NINE == elem_item->prefix_type_) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem multi is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::check_elem_fm_is_valid(const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc) { int ret = OB_SUCCESS; // can only appear at the front of the fmt string if (OB_FAIL(check_conflict_group(elem_item, fmt_desc))) { LOG_WARN("check conflict group failed", K(ret)); } else if (ObNFMElem::has_type(NFM_FILLMODE_FLAG, fmt_desc.elem_flag_) || elem_item->offset_ != 0) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem hex is invalid", K(ret)); } return ret; } int ObNFMDescPrepare::fmt_desc_prepare(const common::ObSEArray &fmt_elem_list, OBNFMDesc &fmt_desc, bool need_check/*true*/) { int ret = OB_SUCCESS; for (int32_t i = 0; OB_SUCC(ret) && i < fmt_elem_list.count(); ++i) { const ObNFMElem *elem_item = fmt_elem_list.at(i); if (OB_ISNULL(elem_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid elem item", K(ret)); } else { switch (elem_item->keyword_->elem_type_) { case ObNFMElem::NFM_COMMA: if (need_check && OB_FAIL(check_elem_comma_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem comma is valid", K(ret)); } else { fmt_desc.last_separator_ = elem_item->offset_; fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_COMMA_FLAG; } break; case ObNFMElem::NFM_PERIOD: if (need_check && OB_FAIL(check_elem_period_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem period is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_PERIOD_FLAG; fmt_desc.decimal_pos_ = elem_item->offset_; } break; case ObNFMElem::NFM_DOLLAR: if (need_check && OB_FAIL(check_elem_dollar_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem dollar is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_DOLLAR_FLAG; } break; case ObNFMElem::NFM_ZERO: if (need_check && OB_FAIL(check_elem_zero_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem zero is valid", K(ret)); } else { if (-1 == fmt_desc.digital_start_) { fmt_desc.digital_start_ = elem_item->offset_; fmt_desc.zero_start_ = elem_item->offset_; } else if (-1 == fmt_desc.zero_start_) { fmt_desc.zero_start_ = elem_item->offset_; } fmt_desc.zero_end_ = elem_item->offset_; fmt_desc.digital_end_ = elem_item->offset_; // whether the decimal point appears if (INT32_MAX == fmt_desc.decimal_pos_) { ++fmt_desc.pre_num_count_; } else { ++fmt_desc.post_num_count_; } // if element 'V' appears, count the number of digits after element 'V' if (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_)) { ++fmt_desc.multi_; } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_ZERO_FLAG; } break; case ObNFMElem::NFM_NINE: if (need_check && OB_FAIL(check_elem_nine_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem nine is valid", K(ret)); } else { if (-1 == fmt_desc.digital_start_) { fmt_desc.digital_start_ = elem_item->offset_; } fmt_desc.digital_end_ = elem_item->offset_; // whether the decimal point appears if (INT32_MAX == fmt_desc.decimal_pos_) { ++fmt_desc.pre_num_count_; } else { ++fmt_desc.post_num_count_; } // if element 'V' appears, count the number of digits after element 'V' if (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_)) { ++fmt_desc.multi_; } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_NINE_FLAG; } break; case ObNFMElem::NFM_B: if (need_check && OB_FAIL(check_elem_b_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem blank is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_BLANK_FLAG; } break; case ObNFMElem::NFM_C: if (need_check && OB_FAIL(check_elem_c_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem c is valid", K(ret)); } else { // if the iso currency ('C', 'L', 'U') does not appear in the first or last position of // the fmt string, it will also be treated as a decimal point // but element ('FM', 'S') + element ('C', 'L', 'U') is exception // element ('C', 'L', 'U') + element ('MI', 'PR', 'S') is exception if (0 == elem_item->offset_ || (elem_item->prefix_type_ == ObNFMElem::NFM_FM || elem_item->prefix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::FIRST_POS; } else if (elem_item->is_last_elem_ || (elem_item->suffix_type_ == ObNFMElem::NFM_MI || elem_item->suffix_type_ == ObNFMElem::NFM_PR || elem_item->suffix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::LAST_POS; } else { // if decimal point has appeared, fmt is invalid if (need_check && (fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_grouping_group(fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_))) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem currency is invalid", K(ret)); } else { fmt_desc.decimal_pos_ = elem_item->offset_; fmt_desc.currency_appear_pos_ = OBNFMDesc::MIDDLE_POS; } } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_C_FLAG; } break; case ObNFMElem::NFM_D: if (need_check && OB_FAIL(check_elem_d_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem d is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_D_FLAG; fmt_desc.decimal_pos_ = elem_item->offset_; } break; case ObNFMElem::NFM_EEEE: if (need_check && OB_FAIL(check_elem_eeee_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem eeee is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_EEEE_FLAG; } break; case ObNFMElem::NFM_G: if (need_check && OB_FAIL(check_elem_g_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem g is valid", K(ret)); } else { fmt_desc.last_separator_ = elem_item->offset_; fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_G_FLAG; } break; case ObNFMElem::NFM_L: if (need_check && OB_FAIL(check_elem_l_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem l is valid", K(ret)); } else { // if the iso currency ('C', 'L', 'U') does not appear in the first or last position of // the fmt string, it will also be treated as a decimal point // but element ('FM', 'S') + element ('C', 'L', 'U') is exception // element ('C', 'L', 'U') + element ('MI', 'PR', 'S') is exception if (0 == elem_item->offset_ || (elem_item->prefix_type_ == ObNFMElem::NFM_FM || elem_item->prefix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::FIRST_POS; } else if (elem_item->is_last_elem_ || (elem_item->suffix_type_ == ObNFMElem::NFM_MI || elem_item->suffix_type_ == ObNFMElem::NFM_PR || elem_item->suffix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::LAST_POS; } else { // if decimal point has appeared, fmt is invalid if (need_check && (fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_grouping_group(fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_))) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem currency is invalid", K(ret)); } else { fmt_desc.decimal_pos_ = elem_item->offset_; fmt_desc.currency_appear_pos_ = OBNFMDesc::MIDDLE_POS; } } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_L_FLAG; } break; case ObNFMElem::NFM_MI: if (need_check && OB_FAIL(check_elem_mi_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem mi is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_MI_FLAG; } break; case ObNFMElem::NFM_PR: if (need_check && OB_FAIL(check_elem_pr_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem pr is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_PR_FLAG; } break; case ObNFMElem::NFM_RN: if (need_check && OB_FAIL(check_elem_rn_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem rn is valid", K(ret)); } else { if (elem_item->case_mode_ == ObNFMElem::UPPER_CASE) { fmt_desc.upper_case_flag_ |= NFM_RN_FLAG; } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_RN_FLAG; } break; case ObNFMElem::NFM_S: if (need_check && OB_FAIL(check_elem_s_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem s is valid", K(ret)); } else { if (elem_item->is_last_elem_) { fmt_desc.sign_appear_pos_ = OBNFMDesc::LAST_POS; } else { fmt_desc.sign_appear_pos_ = OBNFMDesc::FIRST_POS; } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_S_FLAG; } break; case ObNFMElem::NFM_TME: if (need_check && OB_FAIL(check_elem_tm_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem tme is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_TME_FLAG; } break; case ObNFMElem::NFM_TM: case ObNFMElem::NFM_TM9: if (need_check && OB_FAIL(check_elem_tm_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem tm9 is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_TM_FLAG; } break; case ObNFMElem::NFM_U: if (need_check && OB_FAIL(check_elem_u_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem u is valid", K(ret)); } else { // if the iso currency ('C', 'L', 'U') does not appear in the first or last position of // the fmt string, it will also be treated as a decimal point // but element ('FM', 'S') + element ('C', 'L', 'U') is exception // element ('C', 'L', 'U') + element ('MI', 'PR', 'S') is exception if (0 == elem_item->offset_ || (elem_item->prefix_type_ == ObNFMElem::NFM_FM || elem_item->prefix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::FIRST_POS; } else if (elem_item->is_last_elem_ || (elem_item->suffix_type_ == ObNFMElem::NFM_MI || elem_item->suffix_type_ == ObNFMElem::NFM_PR || elem_item->suffix_type_ == ObNFMElem::NFM_S)) { fmt_desc.currency_appear_pos_ = OBNFMDesc::LAST_POS; } else { // if decimal point has appeared, fmt is invalid if (need_check && (fmt_desc.decimal_pos_ != INT32_MAX || ObNFMElem::has_grouping_group(fmt_desc.elem_flag_) || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc.elem_flag_))) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("check elem currency is invalid", K(ret)); } else { fmt_desc.decimal_pos_ = elem_item->offset_; fmt_desc.currency_appear_pos_ = OBNFMDesc::MIDDLE_POS; } } fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_U_FLAG; } break; case ObNFMElem::NFM_V: if (need_check && OB_FAIL(check_elem_v_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem v is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_MULTI_FLAG; } break; case ObNFMElem::NFM_X: if (0 == fmt_desc.elem_x_count_ && elem_item->case_mode_ == ObNFMElem::UPPER_CASE) { fmt_desc.upper_case_flag_ |= NFM_HEX_FLAG; } if (need_check && OB_FAIL(check_elem_x_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem hex is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_HEX_FLAG; fmt_desc.elem_x_count_++; } break; case ObNFMElem::NFM_FM: if (need_check && OB_FAIL(check_elem_fm_is_valid(elem_item, fmt_desc))) { LOG_WARN("check elem fm is valid", K(ret)); } else { fmt_desc.output_len_ += elem_item->keyword_->output_len_; fmt_desc.elem_flag_ |= NFM_FILLMODE_FLAG; } break; default : ret = OB_ERR_UNEXPECTED; LOG_WARN("unknown elem type", K(ret), K(elem_item->keyword_->elem_type_)); break; } } } if (OB_SUCC(ret)) { if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc.elem_flag_)) { fmt_desc.output_len_ = fmt_desc.output_len_ - fmt_desc.pre_num_count_ + 1; } if (!ObNFMElem::has_sign_group(fmt_desc.elem_flag_) && !ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc.elem_flag_)) { // if no sign or element 'RN' appears, add a sign character fmt_desc.output_len_ += 1; } } return ret; } const char *const ObNFMBase::LOWER_RM1[9] = {"i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"}; const char *const ObNFMBase::LOWER_RM10[9] = {"x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"}; const char *const ObNFMBase::LOWER_RM100[9] = {"c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"}; const char *const ObNFMBase::UPPER_RM1[9] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; const char *const ObNFMBase::UPPER_RM10[9] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; const char *const ObNFMBase::UPPER_RM100[9] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; int ObNFMBase::search_keyword(const char *cur_ch, const int32_t remain_len, const ObNFMKeyWord *&match_key, ObNFMElem::ElemCaseMode &case_mode) const { int ret = OB_SUCCESS; if (OB_ISNULL(cur_ch) || remain_len <= 0) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid fmt str", K(ret), K(cur_ch), K(remain_len)); } else { // if the first character of an element is lowercase, it is interpreted as lowercase // otherwise, it is interpreted as uppercase if ((*cur_ch >= 'a') && (*cur_ch <= 'z')) { case_mode = ObNFMElem::LOWER_CASE; } else if ((*cur_ch >= 'A') && (*cur_ch <= 'Z')) { case_mode = ObNFMElem::UPPER_CASE; } else { case_mode = ObNFMElem::IGNORE_CASE; } int32_t index = 0; for (index = 0; index < ObNFMElem::MAX_TYPE_NUMBER; ++index) { const ObNFMKeyWord &keyword = ObNFMElem::NFM_KEYWORDS[index]; if (remain_len >= keyword.len_ && (0 == strncasecmp(cur_ch, keyword.ptr_, keyword.len_))) { match_key = &keyword; break; } } if (ObNFMElem::MAX_TYPE_NUMBER == index) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid fmt character ", K(ret), K(cur_ch), K(remain_len)); } } return ret; } int ObNFMBase::parse_fmt(const char* fmt_str, const int32_t fmt_len, bool need_check/*true*/) { int ret = OB_SUCCESS; if (OB_ISNULL(fmt_str) || fmt_len <= 0) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("invalid fmt str", K(ret), K(fmt_str), K(fmt_len)); } else { fmt_str_.assign_ptr(fmt_str, fmt_len); int32_t remain_len = fmt_len; int32_t index = 0; const char* cur_ch = fmt_str; const char* fmt_end = fmt_str + fmt_len; ObNFMElem *last_elem = NULL; ObNFMElem::ElementType prefix_type = ObNFMElem::INVALID_TYPE; for (index = 0; OB_SUCC(ret) && remain_len > 0 && cur_ch < fmt_end; ++index) { char *elem_buf = NULL; const ObNFMKeyWord *match_keyword = NULL; ObNFMElem::ElemCaseMode case_mode = ObNFMElem::IGNORE_CASE; if (OB_FAIL(search_keyword(cur_ch, remain_len, match_keyword, case_mode))) { LOG_WARN("fail to search match keyword", K(ret), K(cur_ch), K(remain_len)); } else if (OB_ISNULL(elem_buf = static_cast(allocator_.alloc(sizeof(ObNFMElem))))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory"); } else { ObNFMElem *cur_elem = new(elem_buf) ObNFMElem; cur_elem->keyword_ = match_keyword; cur_elem->offset_ = index; cur_elem->case_mode_ = case_mode; cur_elem->prefix_type_ = prefix_type; if (OB_NOT_NULL(last_elem)) { last_elem->suffix_type_ = cur_elem->keyword_->elem_type_; } prefix_type = cur_elem->keyword_->elem_type_; last_elem = cur_elem; cur_ch += cur_elem->keyword_->len_; remain_len -= cur_elem->keyword_->len_; if (remain_len <= 0) { cur_elem->is_last_elem_ = true; } if (OB_FAIL(fmt_elem_list_.push_back(cur_elem))) { LOG_WARN("fail to push back elem to fmt_elem_list", K(ret)); } } } // fill format desc if (OB_SUCC(ret) && OB_FAIL(ObNFMDescPrepare::fmt_desc_prepare(fmt_elem_list_, fmt_desc_, need_check))) { LOG_WARN("fail to prepare format desc", K(ret)); } } return ret; } int ObNFMBase::fill_str(char *str, const int32_t str_len, const int32_t offset, const char fill_ch, const int32_t fill_len) const { int ret = OB_SUCCESS; if (OB_ISNULL(str) || (str_len - offset < fill_len)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("fail to fill str", K(ret), K(str), K(str_len), K(offset), K(fill_len)); } else { MEMSET(str + offset, fill_ch, fill_len); } return ret; } int ObNFMBase::append_decimal_digit(BigNumber &num, const uint32_t decimal_digit) const { int ret = OB_SUCCESS; uint64_t carry = decimal_digit; for (size_t i = 0; i < num.count(); ++i) { uint64_t product = num.at(i) * (uint64_t)10 + carry; num[i] = static_cast(product); carry = product >> 32; } if (carry > 0 && OB_FAIL(num.push_back(carry))) { LOG_WARN("fail to push digit", K(ret)); } return ret; } int ObNFMBase::decimal_to_hex(const ObString &origin_str, char *buf, const int64_t buf_len, int64_t &pos) const { int ret = OB_SUCCESS; BigNumber num; const char *ptr = origin_str.ptr(); int32_t num_pos = 0; pos = 0; if (origin_str.empty()) { ret = OB_ERR_UNEXPECTED; LOG_WARN("origin_str is empty", K(ret)); } else { // if is negative, fill it with '#' if ('-' == ptr[num_pos] && !is_zero(origin_str)) { if (OB_FAIL(fill_str(buf, buf_len, 0, '#', fmt_desc_.output_len_))) { LOG_WARN("fail to fill str", K(ret)); } pos = fmt_desc_.output_len_; } else if (ObNFMElem::has_type((~NFM_HEX_FLAG) & (~NFM_ZERO_FLAG) & (~NFM_NINE_FLAG) & (~NFM_FILLMODE_FLAG), fmt_desc_.elem_flag_)) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("incompatible with other formats", K(ret)); } else { while (OB_SUCC(ret) && num_pos < origin_str.length()) { if (is_digit(ptr[num_pos]) && OB_FAIL(append_decimal_digit(num, ptr[num_pos] - '0'))) { LOG_WARN("fail to append decimal digit", K(ret)); } ++num_pos; } if (num.empty()) { *buf = '0'; pos = 1; } else { uint32_t last_digit = num.at(num.count() - 1); if (fmt_desc_.upper_case_flag_ & NFM_HEX_FLAG) { if (OB_FAIL(databuff_printf(buf, buf_len, pos, "%X", last_digit))) { LOG_WARN("fail to print hex", K(ret)); } } else { if (OB_FAIL(databuff_printf(buf, buf_len, pos, "%x", last_digit))) { LOG_WARN("fail to print hex", K(ret)); } } for (ssize_t i = num.count() - 2; OB_SUCC(ret) && i >= 0; --i) { if (fmt_desc_.upper_case_flag_ & NFM_HEX_FLAG) { ret = databuff_printf(buf, buf_len, pos, "%08X", num.at(i)); } else { ret = databuff_printf(buf, buf_len, pos, "%08x", num.at(i)); } } } if (OB_SUCC(ret)) { // if the length of the fmt is less than the length of the result, fill it with '#' int32_t valid_len = fmt_desc_.elem_x_count_ + fmt_desc_.pre_num_count_; if (pos > valid_len) { if (OB_FAIL(fill_str(buf, buf_len, 0, '#', fmt_desc_.output_len_))) { LOG_WARN("fail to fill str", K(ret)); } pos = fmt_desc_.output_len_; } else { ObString hex_str(pos, buf); LOG_DEBUG("decimal_to_hex", K(hex_str)); if (pos < valid_len && ObNFMElem::has_type(NFM_ZERO_FLAG, fmt_desc_.elem_flag_)) { // if the length of the fmt is greater than the length of the result string // and the fmt model contains leading zero, the return result needs to be // supplemented with '0' // eg: to_char(100, '00x') --> '064' int64_t fill_zero_count = valid_len - pos; MEMMOVE(buf + fill_zero_count, buf, pos); if (OB_FAIL(fill_str(buf, buf_len, 0, '0', fill_zero_count))) { LOG_WARN("fail to fill str", K(ret)); } else { pos += fill_zero_count; } } // process fill mode if (OB_SUCC(ret) && OB_FAIL(process_fillmode(buf, buf_len, pos))) { LOG_WARN("fail to process fillmode", K(ret)); } } } } } return ret; } int ObNFMBase::check_hex_str_valid(const char *str, const int32_t str_len, const int32_t offset) const { int ret = OB_SUCCESS; int32_t hex_str_len = 0; int32_t fmt_len = fmt_desc_.elem_x_count_ + fmt_desc_.pre_num_count_; for (int32_t i = offset; OB_SUCC(ret) && i < str_len; i++) { if ((str[i] >= '0' && str[i] <= '9') || (str[i] >= 'a' && str[i] <= 'f') || (str[i] >= 'A' && str[i] <= 'F')) { ++hex_str_len; } else { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("invalid hex character", K(ret)); } } if (OB_SUCC(ret) && hex_str_len > fmt_len) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("overflowed digit format", K(ret), K(hex_str_len), K(fmt_len)); } return ret; } // we'll represent an arbitrary-precision non-negative hexadecimal string as a vector of uint64 // every 15 hexadecimal strings are converted into a uint64 number // eg: 12345123456789abcdef --> '12345' and '123456789abcdef' // '12345' and '123456789abcdef' --> [81985529216486900, 74565] int ObNFMBase::build_hex_number(const char *str, const int32_t str_len, int32_t &offset, HexNumber &nums) const { int ret = OB_SUCCESS; int32_t digit = 0; uint64_t num = 0; int32_t index = 0; const int32_t batch_size = 15; for (int32_t i = str_len - 1; OB_SUCC(ret) && i >= offset; i--) { ++index; if (OB_FAIL(hex_to_num(str[i], digit))) { LOG_WARN("invalid hex character", K(ret)); } else { uint64_t power_val = pow(16, index - 1); num = digit * power_val + num; if (index % batch_size == 0) { nums.push_back(num); index = 0; num = 0; } } } if (OB_SUCC(ret) && index % batch_size != 0) { nums.push_back(num); } return ret; } int ObNFMBase::int_to_roman_str(const int64_t num, char *buf, const int64_t buf_len, int64_t &pos) const { int ret = OB_SUCCESS; if ((num < 1 || num > 3999)) { if (OB_FAIL(fill_str(buf, buf_len, 0, '#', fmt_desc_.output_len_))) { LOG_WARN("fail to fill str", K(ret)); } pos = fmt_desc_.output_len_; } else { int64_t str_len = 0; char num_str[12] = {0}; str_len = snprintf(num_str, sizeof(num_str), "%d", static_cast(num)); for (char *p = num_str; OB_SUCC(ret) && *p != '\0'; p++, --str_len) { int32_t val = *p - 49; if (val < 0) { continue; } if (str_len > 3) { while (OB_SUCC(ret) && val-- != -1) { if (fmt_desc_.upper_case_flag_ & NFM_RN_FLAG) { ret = databuff_printf(buf, buf_len, pos, "%s", "M"); } else { ret = databuff_printf(buf, buf_len, pos, "%s", "m"); } } } else { if (str_len == 3) { if (fmt_desc_.upper_case_flag_ & NFM_RN_FLAG) { ret = databuff_printf(buf, buf_len, pos, "%s", UPPER_RM100[val]); } else { ret = databuff_printf(buf, buf_len, pos, "%s", LOWER_RM100[val]); } } else if (str_len == 2) { if (fmt_desc_.upper_case_flag_ & NFM_RN_FLAG) { ret = databuff_printf(buf, buf_len, pos, "%s", UPPER_RM10[val]); } else { ret = databuff_printf(buf, buf_len, pos, "%s", LOWER_RM10[val]); } } else if (str_len == 1) { if (fmt_desc_.upper_case_flag_ & NFM_RN_FLAG) { ret = databuff_printf(buf, buf_len, pos, "%s", UPPER_RM1[val]); } else { ret = databuff_printf(buf, buf_len, pos, "%s", LOWER_RM1[val]); } } } } if (OB_SUCC(ret) && OB_FAIL(process_fillmode(buf, buf_len, pos))) { LOG_WARN("fail to process fillmode", K(ret)); } } return ret; } int ObNFMBase::get_integer_part_len(const common::ObString &num_str, int32_t &integer_part_len) const { int ret = OB_SUCCESS; const char *ptr = num_str.ptr(); integer_part_len = 0; bool digit_appear = false; for (int32_t i = 0; i < num_str.length(); ++i) { if (ptr[i] >= '0' && ptr[i] <= '9') { integer_part_len++; digit_appear = true; } else if ('.' == ptr[i] || 'E' == ptr[i]) { // if it's scientific notation // eg: to_number('1E+00', '9BEEEE') --> integer_part_len = 1 break; } else if (digit_appear && ptr[i] != ',') { // to_number('USD123.123', 'c999.999') // to_number('23USD00', '99C99') --> integer_part_len = 2 break; } } return ret; } int ObNFMBase::get_decimal_part_len(const common::ObString &num_str, int32_t &decimal_part_len) const { int ret = OB_SUCCESS; const char *ptr = num_str.ptr(); decimal_part_len = 0; bool dc_appear = false; for (int32_t i = 0; i < num_str.length(); ++i) { if ('.' == ptr[i]) { if (dc_appear) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("decimal already appear", K(ret)); } else { dc_appear = true; } } else if ('E' == ptr[i]) { // if it's scientific notation // eg: to_number('1.23E+03', '9.99EEEE') --> decimal_part_len = 2 break; } else if (ptr[i] < '0' || ptr[i] > '9') { if (dc_appear) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("decimal already appear", K(ret)); } else { dc_appear = true; } } else if (dc_appear && (ptr[i] >= '0' && ptr[i] <= '9')) { ++decimal_part_len; } } return ret; } bool ObNFMBase::is_digit(const char c) const { return c >= '0' && c <= '9'; } bool ObNFMBase::is_zero(const common::ObString &num_str) const { const char *ptr = num_str.ptr(); for (int32_t i = 0; i < num_str.length(); i++) { if (ptr[i] != '-' && ptr[i] != '.' && ptr[i] != '0') { return false; } } return true; } int ObNFMBase::hex_to_num(const char c, int32_t &val) const { int ret = OB_SUCCESS; if (c >= '0' && c <= '9') { val = c - '0'; } else if (c >= 'a' && c <= 'f') { val = c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { val = c - 'A' + 10; } else { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("invalid hex character", K(ret)); } return ret; } int ObNFMBase::process_fillmode(char *buf, const int64_t buf_len, int64_t &pos) const { int ret = OB_SUCCESS; if (OB_ISNULL(buf) || buf_len < 0 || fmt_desc_.output_len_ < pos) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(buf), K(buf_len)); } else { int32_t space_size = fmt_desc_.output_len_ - pos; if (!ObNFMElem::has_type(NFM_TME_FLAG, fmt_desc_.elem_flag_) && !ObNFMElem::has_type(NFM_TM_FLAG, fmt_desc_.elem_flag_) && !ObNFMElem::has_type(NFM_FILLMODE_FLAG, fmt_desc_.elem_flag_)) { // if is not fill mode. needs to be moved to the end to fill leading ' ' if (space_size) { MEMMOVE(buf + space_size, buf, pos); for (int32_t i = 0; i < space_size; ++i) { buf[i] = ' '; } } pos = fmt_desc_.output_len_; } } return ret; } int ObNFMBase::get_nls_currency(const ObSQLSessionInfo &session) { int ret = OB_SUCCESS; if (ObNFMElem::has_type(NFM_C_FLAG, fmt_desc_.elem_flag_)) { fmt_desc_.nls_currency_ = session.get_iso_nls_currency(); } else if (ObNFMElem::has_type(NFM_L_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_NLS_CURRENCY, fmt_desc_.nls_currency_))) { LOG_WARN("fail to get sys variable", K(ret)); } } else if (ObNFMElem::has_type(NFM_U_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_NLS_DUAL_CURRENCY, fmt_desc_.nls_currency_))) { LOG_WARN("fail to get sys variable", K(ret)); } } return ret; } int ObNFMBase::get_iso_grouping(const ObSQLSessionInfo &session) { int ret = OB_SUCCESS; if (OB_FAIL(session.get_sys_variable(share::SYS_VAR_NLS_NUMERIC_CHARACTERS, fmt_desc_.iso_grouping_))) { LOG_WARN("fail to get sys variable", K(ret)); } return ret; } int ObNFMBase::conv_num_to_nfm_obj(const common::ObObj &obj, common::ObExprCtx &expr_ctx, ObNFMObj &nfm_obj) { int ret = OB_SUCCESS; number::ObNumber num; if (OB_ISNULL(expr_ctx.calc_buf_)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument.", K(ret), K(expr_ctx.calc_buf_)); } else { EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE); if (obj.is_float() || obj.is_ufloat() || obj.is_double() || obj.is_udouble()) { if (obj.is_double() || obj.is_udouble()) { nfm_obj.set_obj_type(ObDoubleType); nfm_obj.set_double(obj.get_double()); } else { nfm_obj.set_obj_type(ObFloatType); nfm_obj.set_float(obj.get_float()); } } else { EXPR_GET_NUMBER_V2(obj, num); if (OB_FAIL(ret)) { LOG_WARN("failed to cast obj as number", K(ret)); } else { nfm_obj.set_obj_type(ObNumberType); nfm_obj.set_number(num); } } } return ret; } int ObNFMBase::conv_num_to_nfm_obj(const common::ObObjMeta &obj_meta, const ObDatum &obj, ObNFMObj &nfm_obj, common::ObIAllocator &alloc) { int ret = OB_SUCCESS; const bool is_float = obj_meta.is_float(); const bool is_double = obj_meta.is_double(); const bool is_ufloat = obj_meta.is_ufloat(); const bool is_udouble = obj_meta.is_udouble(); const bool is_int_tc = obj_meta.is_integer_type(); if (is_float || is_ufloat || is_double || is_udouble) { if (is_float || is_ufloat) { nfm_obj.set_obj_type(ObFloatType); nfm_obj.set_float(obj.get_float()); } else { nfm_obj.set_obj_type(ObDoubleType); nfm_obj.set_double(obj.get_double()); } } else { number::ObNumber num; if (is_int_tc) { // int tc, to support PLS_INTERGER type if (OB_FAIL(num.from(obj.get_int(), alloc))) { LOG_WARN("fail to int_number", K(ret)); } } else { // number num.assign(obj.get_number().desc_.desc_, const_cast(&(obj.get_number().digits_[0]))); } if (OB_SUCC(ret)) { nfm_obj.set_obj_type(ObNumberType); nfm_obj.set_number(num); } } return ret; } int ObNFMBase::cast_obj_to_int(const ObNFMObj &nfm_obj, int64_t &res_val) { int ret = OB_SUCCESS; ObObjType obj_type = nfm_obj.get_obj_type(); int32_t scale = 0; if (ObFloatType == obj_type) { res_val = static_cast(ObExprUtil::round_double(nfm_obj.get_float(), scale)); } else if (ObDoubleType == obj_type) { res_val = static_cast(ObExprUtil::round_double(nfm_obj.get_double(), scale)); } else { number::ObNumber num = nfm_obj.get_number(); number::ObNumber nmb; ObNumStackOnceAlloc tmp_alloc; if (OB_FAIL(nmb.from(num, tmp_alloc))) { LOG_WARN("copy number failed.", K(ret), K(num)); } else if (OB_FAIL(nmb.round(scale))) { LOG_WARN("round failed.", K(ret), K(num.format()), K(scale)); } else if (!nmb.is_valid_int64(res_val)) { // if it is not a valid int64, negative number returns INT64_MIN // positive number returns INT64_MAX if (nmb.is_negative()) { res_val = INT64_MIN; } else { res_val = INT64_MAX; } } } LOG_DEBUG("cast_obj_to_int", K(ret), K(res_val)); return ret; } int ObNFMBase::remove_leading_zero(char *buf, int64_t &offset) { int ret = OB_SUCCESS; if (OB_ISNULL(buf)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid buf"); } else { bool is_negative = false; int32_t pos = 0; if (offset > 0 && '-' == buf[0]) { is_negative = true; pos++; } int32_t leading_zero = 0; for (; pos < offset; ++pos) { if ('0' == buf[pos]) { ++leading_zero; } else { break; } } if (leading_zero > 0) { if (is_negative) { MEMMOVE(buf + 1, buf + leading_zero + 1, offset - leading_zero); } else { MEMMOVE(buf, buf + leading_zero, offset - leading_zero); } offset -= leading_zero; } } return ret; } int ObNFMBase::cast_obj_to_num_str(const ObNFMObj &nfm_obj, const int64_t scale, common::ObString &num_str) { int ret = OB_SUCCESS; bool is_negative = false; int64_t num_str_len = 0; number::ObNumber nmb; char *num_str_buf = NULL; const int64_t alloc_size = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; ObObjType obj_type = nfm_obj.get_obj_type(); if (OB_ISNULL(num_str_buf = static_cast( allocator_.alloc(alloc_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else { if (ObFloatType == obj_type || ObDoubleType == obj_type) { if (ObFloatType == obj_type) { num_str_len = ob_gcvt_strict(nfm_obj.get_float(), OB_GCVT_ARG_FLOAT, alloc_size, num_str_buf, NULL, lib::is_oracle_mode(), TRUE, FALSE); } else if (ObDoubleType == obj_type) { num_str_len = ob_gcvt_strict(nfm_obj.get_double(), OB_GCVT_ARG_DOUBLE, alloc_size, num_str_buf, NULL, lib::is_oracle_mode(), TRUE, FALSE); } if (OB_FAIL(nmb.from(num_str_buf, num_str_len, allocator_))) { LOG_WARN("number from str failed", K(ret)); } num_str_len = 0; } else { number::ObNumber num = nfm_obj.get_number(); if (OB_FAIL(nmb.from(num, allocator_))) { LOG_WARN("copy number failed.", K(ret), K(num)); } } } if (OB_SUCC(ret)) { if (OB_FAIL(nmb.format_v2(num_str_buf, alloc_size, num_str_len, scale, false))) { LOG_WARN("fail to convert number to string", K(ret)); } else { is_negative = nmb.is_negative(); num_str.assign_ptr(num_str_buf, static_cast(num_str_len)); // is if "-0", special treatment for compatible with oracle // eg: to_char(-0.4000, '0000') --> -0000 if (is_zero(num_str)) { int32_t pos = 0; if (is_negative) { num_str_buf[pos++] = '-'; } if (fmt_desc_.pre_num_count_ != 0 && 0 == fmt_desc_.post_num_count_) { num_str_buf[pos++] = '0'; } num_str_buf[pos++] = '.'; if (fmt_desc_.post_num_count_ != 0) { num_str_buf[pos++] = '0'; } num_str.assign_ptr(num_str_buf, pos); } else if (OB_FAIL(remove_leading_zero(num_str_buf, num_str_len))) { LOG_WARN("fail to remove leading zero", K(ret)); } else { num_str.assign_ptr(num_str_buf, static_cast(num_str_len)); } LOG_DEBUG("cast_obj_to_num_str", K(num_str)); } } return ret; } int ObNFMBase::num_str_to_sci(const common::ObString &num_str, const int32_t scale, char *buf, const int64_t len, int64_t &pos, bool is_tm) const { int ret = OB_SUCCESS; const char *ptr = num_str.ptr(); int64_t str_len = num_str.length(); int64_t origin = pos; const int64_t SCI_NUMBER_LENGTH = 40; if (OB_UNLIKELY(pos > len || len < 0 || pos < 0 || NULL == buf)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid value", K(ret), K(pos), K(len), KP(buf)); } else if (is_zero(num_str)) { if (OB_FAIL(databuff_printf(buf, len, pos, "%.*s", num_str.length(), num_str.ptr()))) { LOG_WARN("fail to fill num str", K(ret), K(len), K(num_str)); } else if (OB_FAIL(databuff_printf(buf, len, pos, "%s", "E+00"))) { LOG_WARN("fail to fill num str", K(ret)); } } else { int64_t raw_pos = 0; //the string of the exponent part char pow_str[6]; int64_t pow_size = 0; int64_t pow_index = 0; bool pre_dot = false; bool is_negative = false; int64_t width_count = 0; pow_str[pow_index++] = 'E'; pow_str[pow_index++] = '+'; // handle the negative sign or decimal point at the beginning of num str if ('-' == ptr[raw_pos]) { raw_pos++; buf[pos++] = '-'; is_negative = true; } if ('.' == ptr[raw_pos]) { raw_pos++; pre_dot = true; pow_str[pow_index - 1] = '-'; } int64_t zero_count = 0; // first non-zero number found while ('0' == ptr[raw_pos] && raw_pos < str_len) { raw_pos++; zero_count++; } buf[pos++] = ptr[raw_pos++]; if (scale != 0) { buf[pos++] = '.'; } // determine the index part and the number part according to // whether there is a decimal point in the front if (pre_dot) { pow_size = zero_count + 1; if (pow_size >= 0 && pow_size <= 99) { width_count = 2; } else { width_count = 3; } if (scale > 0) { for (int32_t count = 0; count < scale && pos < SCI_NUMBER_LENGTH - width_count - pow_index + origin; ++count) { if (raw_pos >= str_len) { buf[pos++] = '0'; } else { buf[pos++] = ptr[raw_pos++]; } } } else if (scale < 0) { while (raw_pos < str_len && pos < SCI_NUMBER_LENGTH - width_count - pow_index + origin) { buf[pos++] = ptr[raw_pos++]; } } } else if (!pre_dot && 0 == zero_count) { int64_t index_count = 0; // if numbers greater than 10, always need to traverse the number // array to know the final exponent. when a decimal point is encountered // it will not be traversed. the index value will affect the number of bytes // in the index part for (int64_t i = raw_pos; i < str_len && ptr[i] != '.'; ++i) { index_count++; } if (index_count >= 0 && index_count <= 99) { width_count = 2; } else { width_count = 3; } if (scale > 0) { for (int32_t count = 0; count < scale && pos < SCI_NUMBER_LENGTH - pow_index - width_count + origin;) { if (raw_pos >= str_len) { buf[pos++] = '0'; ++count; } else if ('.' == ptr[raw_pos]) { raw_pos++; } else { buf[pos++] = ptr[raw_pos++]; ++count; } } } else if (scale < 0) { while (raw_pos < str_len && pos < SCI_NUMBER_LENGTH - pow_index - width_count + origin) { if ('.' == ptr[raw_pos]) { raw_pos++; } else { buf[pos++] = ptr[raw_pos++]; } } } pow_size = index_count; } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("the number raw str is unexpected", K(ret)); } // round the last digit and process the carry if (!is_tm && OB_SUCC(ret)) { int64_t carry = 0; int64_t carry_pos = pos; int64_t digit_start_pos = is_negative ? origin + 1 : origin; if ('.' == ptr[raw_pos]) { ++raw_pos; } if (raw_pos < str_len && ptr[raw_pos] >= '5' && ptr[raw_pos] <= '9') { carry = 1; carry_pos--; while (carry && carry_pos >= digit_start_pos && OB_SUCC(ret)) { if (buf[carry_pos] >= '0' && buf[carry_pos] <= '8') { buf[carry_pos] = (char)((int)buf[carry_pos] + carry); carry = 0; carry_pos--; } else if ('9' == buf[carry_pos]) { carry = 1; buf[carry_pos--] = '0'; } else if ('.' == buf[carry_pos]) { carry_pos--; } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("It's unexpected to round the number sci", K(ret)); } } if (1 == carry && digit_start_pos - 1 == carry_pos && OB_SUCC(ret)) { bool decimal_appear = false; for (int64_t i = pos - 1; i >= digit_start_pos + 1; --i) { if (buf[i - 1] != '.') { buf[i] = buf[i - 1]; } else { decimal_appear = true; } } buf[digit_start_pos] = '1'; if (decimal_appear) { buf[digit_start_pos + 1] = '.'; } ++pow_size; } } } if (OB_SUCC(ret) && is_tm) { int32_t offset = 0; // 1.0000000000000000000000000000000000E+65 --> 1E+65 for (offset = pos - 1; offset > origin; offset--) { if (buf[offset] != '0') { break; } } pos = offset + 1; if ('.' == buf[offset]) { --pos; } } // print exponent if (OB_SUCC(ret)) { if (OB_FAIL(databuff_printf(pow_str, sizeof(pow_str), pow_index, "%02ld", pow_size))) { LOG_WARN("fail to generate pow str", K(ret)); } else { for (int i = 0; i < pow_index; ++i) { buf[pos++] = pow_str[i]; } } } } return ret; } int ObNFMToChar::process_mul_format(const ObNFMObj &nfm_obj, common::ObString &num_str) { int ret = OB_SUCCESS; number::ObNumber num_val; number::ObNumber base_num; number::ObNumber power_num; number::ObNumber origin_num; const int64_t base_val = 10; const int64_t scale = 0; int64_t origin_str_len = 0; char *origin_str_buf = NULL; int64_t exponent = fmt_desc_.multi_; const int64_t alloc_size = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; if (OB_ISNULL(origin_str_buf = static_cast( allocator_.alloc(alloc_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else { ObObjType obj_type = nfm_obj.get_obj_type(); int64_t power_val = pow(10, exponent); if (ObFloatType == obj_type) { origin_str_len = ob_fcvt(ObExprUtil::round_double(nfm_obj.get_float() * power_val, scale), scale, alloc_size, origin_str_buf, NULL); } else if (ObDoubleType == obj_type) { origin_str_len = ob_fcvt(ObExprUtil::round_double(nfm_obj.get_double() * power_val, scale), scale, alloc_size, origin_str_buf, NULL); } else if (ObNumberType == obj_type) { num_val = nfm_obj.get_number(); if (OB_FAIL(base_num.from(base_val, allocator_))) { LOG_WARN("fail to cast int to number", K(ret)); } else if (OB_FAIL(base_num.power(exponent, power_num, allocator_))) { LOG_WARN("power calc failed", K(ret)); } else if (OB_FAIL(num_val.mul_v2(power_num, origin_num, allocator_))) { LOG_WARN("fail to mul number", K(ret)); } else if (OB_FAIL(origin_num.round(scale))) { LOG_WARN("round failed", K(ret), K(origin_num), K(scale)); } else if (OB_FAIL(origin_num.format_v2(origin_str_buf, alloc_size, origin_str_len, scale, false))) { LOG_WARN("fail to convert number to string", K(ret)); } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid obj type", K(ret), K(obj_type)); } if (OB_SUCC(ret)) { num_str.assign_ptr(origin_str_buf, static_cast(origin_str_len)); LOG_DEBUG("obj_to_multi_num_str", K(num_str)); } } return ret; } int ObNFMToChar::process_roman_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos) { int ret = OB_SUCCESS; int64_t val; if (OB_FAIL(cast_obj_to_int(nfm_obj, val))) { LOG_WARN("fail to cast obj to int", K(ret)); } else if (OB_FAIL(int_to_roman_str(val, buf, buf_len, pos))) { LOG_WARN("fail to convert int to roman str", K(ret)); } return ret; } int ObNFMToChar::process_hex_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos) { int ret = OB_SUCCESS; int64_t scale = 0; ObString origin_str; if (OB_FAIL(cast_obj_to_num_str(nfm_obj, scale, origin_str))) { LOG_WARN("fail to cast obj to num str", K(ret)); } else if (OB_FAIL(decimal_to_hex(origin_str, buf, buf_len, pos))) { LOG_WARN("fail to convert decimal to hex str", K(ret)); } return ret; } int ObNFMToChar::process_tm_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos) { int ret = OB_SUCCESS; int64_t num_str_len = 0; char *num_str_buf = NULL; ObString num_str; const int32_t scale = -1; const int64_t alloc_size = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; if (OB_ISNULL(num_str_buf = static_cast( allocator_.alloc(alloc_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else { ObObjType obj_type = nfm_obj.get_obj_type(); if (ObFloatType == obj_type) { num_str_len = ob_gcvt_opt(nfm_obj.get_float(), OB_GCVT_ARG_FLOAT, static_cast(alloc_size), num_str_buf, NULL, lib::is_oracle_mode(), TRUE); } else if (ObDoubleType == obj_type) { num_str_len = ob_gcvt_opt(nfm_obj.get_double(), OB_GCVT_ARG_DOUBLE, static_cast(alloc_size), num_str_buf, NULL, lib::is_oracle_mode(), TRUE); } else if (ObNumberType == obj_type) { number::ObNumber num = nfm_obj.get_number(); number::ObNumber nmb; if (OB_FAIL(nmb.from(num, allocator_))) { LOG_WARN("copy number failed.", K(ret), K(num)); } else if (OB_FAIL(nmb.format_v2(num_str_buf, alloc_size, num_str_len, scale, false))) { LOG_WARN("fail to format", K(ret), K(nmb)); } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid obj type", K(ret), K(obj_type)); } if (OB_SUCC(ret)) { num_str.assign_ptr(num_str_buf, static_cast(num_str_len)); LOG_DEBUG("process_tme_format", K(ret), K(num_str_buf), K(num_str_len)); if (num_str_len <= 64) { MEMCPY(buf, num_str_buf, num_str_len); pos += num_str_len; } else if (num_str_len > 64) { if (OB_FAIL(num_str_to_sci(num_str, scale, buf, buf_len, pos, true))) { LOG_WARN("failed to convert num to sci str", K(ret)); } } } if (OB_SUCC(ret) && OB_FAIL(process_fillmode(buf, buf_len, pos))) { LOG_WARN("fail to process fillmode", K(ret)); } } return ret; } int ObNFMToChar::process_tme_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos) { int ret = OB_SUCCESS; int64_t num_str_len = 0; char *num_str_buf = NULL; ObString num_str; const int32_t scale = -1; const int64_t alloc_size = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; if (OB_ISNULL(num_str_buf = static_cast( allocator_.alloc(alloc_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else { ObObjType obj_type = nfm_obj.get_obj_type(); if (ObFloatType == obj_type) { num_str_len = ob_gcvt_opt(nfm_obj.get_float(), OB_GCVT_ARG_FLOAT, static_cast(alloc_size), num_str_buf, NULL, lib::is_oracle_mode(), TRUE); } else if (ObDoubleType == obj_type) { num_str_len = ob_gcvt_opt(nfm_obj.get_double(), OB_GCVT_ARG_DOUBLE, static_cast(alloc_size), num_str_buf, NULL, lib::is_oracle_mode(), TRUE); } else if (ObNumberType == obj_type) { number::ObNumber num = nfm_obj.get_number(); number::ObNumber nmb; if (OB_FAIL(nmb.from(num, allocator_))) { LOG_WARN("copy number failed.", K(ret), K(num)); } else if (OB_FAIL(nmb.format_v2(num_str_buf, alloc_size, num_str_len, scale, false))) { LOG_WARN("fail to format", K(ret), K(nmb)); } } else { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid obj type", K(ret), K(obj_type)); } if (OB_SUCC(ret)) { num_str.assign_ptr(num_str_buf, static_cast(num_str_len)); LOG_DEBUG("process_tme_format", K(ret), K(num_str_buf), K(num_str_len)); if (OB_FAIL(num_str_to_sci(num_str, scale, buf, buf_len, pos, true))) { LOG_WARN("failed to convert num to sci str", K(ret)); } else if (OB_FAIL(process_fillmode(buf, buf_len, pos))) { LOG_WARN("fail to process fillmode", K(ret)); } } } return ret; } int ObNFMToChar::process_sci_format(const common::ObString &origin_str, const int32_t scale, common::ObString &num_str) { int ret = OB_SUCCESS; int64_t sci_str_len = 0; char *sci_str_buf = NULL; const int64_t alloc_size = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; if (fmt_desc_.pre_num_count_ < 1) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid number fmt model", K_(fmt_str)); } else if (OB_ISNULL(sci_str_buf = static_cast( allocator_.alloc(alloc_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else { if (OB_FAIL(num_str_to_sci(origin_str, scale, sci_str_buf, alloc_size, sci_str_len, false))) { LOG_WARN("failed to convert num to sci str", K(ret)); } else { num_str.assign_ptr(sci_str_buf, sci_str_len); } } return ret; } int ObNFMToChar::process_output_fmt(const common::ObString &str, const int32_t integer_part_len, char *buf, const int64_t buf_len, int64_t &pos) { int ret = OB_SUCCESS; const char *num_str = str.ptr(); int32_t num_str_len = str.length(); int32_t num_str_pos = 0; pos = 0; bool is_negative = false; if ('-' == num_str[num_str_pos]) { num_str_pos++; is_negative = true; } // processing leading positive or negative sign element if (ObNFMElem::has_sign_group(fmt_desc_.elem_flag_)) { if (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc_.elem_flag_) && OBNFMDesc::FIRST_POS == fmt_desc_.sign_appear_pos_) { if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '-'); } else { ret = databuff_printf(buf, buf_len, pos, "%c", '+'); } } else if (ObNFMElem::has_type(NFM_PR_FLAG, fmt_desc_.elem_flag_)) { if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '<'); } } } else { // if there is no leading positive or negative sign element // fill in '-' before negative if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '-'); } } // processing element '$' if (OB_SUCC(ret) && ObNFMElem::has_type(NFM_DOLLAR_FLAG, fmt_desc_.elem_flag_)) { ret = databuff_printf(buf, buf_len, pos, "%c", '$'); } if (OB_SUCC(ret)) { int32_t need_skip_num = fmt_desc_.pre_num_count_ - integer_part_len; bool digit_appear = false; int32_t traversed_num_count = 0; for (int32_t i = 0; OB_SUCC(ret) && i < fmt_elem_list_.count(); ++i) { const ObNFMElem *elem_item = fmt_elem_list_.at(i); if (OB_ISNULL(elem_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid elem item", K(ret)); } else { switch (elem_item->keyword_->elem_type_) { case ObNFMElem::NFM_COMMA: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (!digit_appear || traversed_num_count > integer_part_len) { // skip, do nothing } else { ret = databuff_printf(buf, buf_len, pos, "%c", ','); } } else { if (!digit_appear || (need_skip_num && (fmt_desc_.zero_start_ < 0 || fmt_desc_.zero_start_ > i))) { // skip, do nothing } else { ret = databuff_printf(buf, buf_len, pos, "%c", ','); } } break; case ObNFMElem::NFM_PERIOD: ret = databuff_printf(buf, buf_len, pos, "%c", '.'); if ('.' == num_str[num_str_pos]) { num_str_pos++; } break; case ObNFMElem::NFM_ZERO: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (traversed_num_count >= integer_part_len) { // skip, do nothing } else if (num_str_pos >= num_str_len || !is_digit(num_str[num_str_pos])) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid number", K(ret), K(str)); } else { digit_appear = true; ret = databuff_printf(buf, buf_len, pos, "%c", num_str[num_str_pos++]); } } else { if (need_skip_num) { // need to fill leading '0' ret = databuff_printf(buf, buf_len, pos, "%c", '0'); need_skip_num--; digit_appear = true; } else if (num_str_pos >= num_str_len || !is_digit(num_str[num_str_pos])) { // need to fill '0' at last // eg: to_char(123456789, 'FM999999999.00000') --> 123456789.00000 ret = databuff_printf(buf, buf_len, pos, "%c", '0'); digit_appear = true; } else { ret = databuff_printf(buf, buf_len, pos, "%c", num_str[num_str_pos++]); digit_appear = true; } } ++traversed_num_count; break; case ObNFMElem::NFM_NINE: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (traversed_num_count >= integer_part_len) { // skip, do nothing } else if (num_str_pos >= num_str_len || !is_digit(num_str[num_str_pos])) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid number", K(ret), K(str)); } else { digit_appear = true; ret = databuff_printf(buf, buf_len, pos, "%c", num_str[num_str_pos++]); } } else { if (need_skip_num) { // if element '0' has appeared, the following numbers need to fill '0' // eg: to_char(123.123, '9099999.999') --> 000123.123 if (fmt_desc_.zero_start_ >= 0 && fmt_desc_.zero_start_ < i) { digit_appear = true; ret = databuff_printf(buf, buf_len, pos, "%c", '0'); } need_skip_num--; } else if (num_str_pos >= num_str_len || !is_digit(num_str[num_str_pos])) { // eg: to_char(10000, 'FM99,999.99') --> 10,000. // eg: to_char(10000, '99,999.99') --> 10,000.00 if (!ObNFMElem::has_type(NFM_FILLMODE_FLAG, fmt_desc_.elem_flag_) || (fmt_desc_.zero_end_ >= 0 && fmt_desc_.zero_end_ > i)) { digit_appear = true; ret = databuff_printf(buf, buf_len, pos, "%c", '0'); } } else { digit_appear = true; ret = databuff_printf(buf, buf_len, pos, "%c", num_str[num_str_pos++]); } } ++traversed_num_count; break; case ObNFMElem::NFM_D: if (fmt_desc_.iso_grouping_.length() < 2) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid iso grouping", K(ret), K(fmt_desc_.iso_grouping_)); } else { ret = databuff_printf(buf, buf_len, pos, "%c", fmt_desc_.iso_grouping_.ptr()[0]); // eg: to_char(123, 'FM999.9999') --> 123. if (fmt_desc_.iso_grouping_.ptr()[0] == num_str[num_str_pos]) { num_str_pos++; } } break; case ObNFMElem::NFM_G: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (!digit_appear || traversed_num_count > integer_part_len) { // skip, do nothing } else { if (fmt_desc_.iso_grouping_.length() < 2) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid iso grouping", K(ret), K(fmt_desc_.iso_grouping_)); } else { ret = databuff_printf(buf, buf_len, pos, "%c", fmt_desc_.iso_grouping_.ptr()[1]); } } } else { if (!digit_appear || (need_skip_num && (fmt_desc_.zero_start_ < 0 || fmt_desc_.zero_start_ > i))) { // skip, do nothing } else { if (fmt_desc_.iso_grouping_.length() < 2) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid iso grouping", K(ret), K(fmt_desc_.iso_grouping_)); } else { ret = databuff_printf(buf, buf_len, pos, "%c", fmt_desc_.iso_grouping_.ptr()[1]); } } } break; case ObNFMElem::NFM_C: case ObNFMElem::NFM_L: case ObNFMElem::NFM_U: ret = databuff_printf(buf, buf_len, pos, "%.*s", fmt_desc_.nls_currency_.length(), fmt_desc_.nls_currency_.ptr()); if (OBNFMDesc::MIDDLE_POS == fmt_desc_.currency_appear_pos_) { // if the element('C', 'L', 'U') appears in the middle, it will be regarded as the // decimal point, so the iso currency symbol is used to replace the decimal point if (num_str_pos < num_str_len && '.' == num_str[num_str_pos]) { num_str_pos++; } } break; case ObNFMElem::NFM_EEEE: while (OB_SUCC(ret) && num_str_pos < num_str_len) { buf[pos++] = num_str[num_str_pos]; num_str_pos++; } break; default: break; } } } } // processing the last positive or negative sign element if (OB_SUCC(ret) && ObNFMElem::has_sign_group(fmt_desc_.elem_flag_)) { if (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc_.elem_flag_) && OBNFMDesc::LAST_POS == fmt_desc_.sign_appear_pos_) { if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '-'); } else { ret = databuff_printf(buf, buf_len, pos, "%c", '+'); } } else if (ObNFMElem::has_type(NFM_PR_FLAG, fmt_desc_.elem_flag_)) { if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '>'); } else { ret = databuff_printf(buf, buf_len, pos, "%c", ' '); } } else if (ObNFMElem::has_type(NFM_MI_FLAG, fmt_desc_.elem_flag_)) { if (is_negative) { ret = databuff_printf(buf, buf_len, pos, "%c", '-'); } else { ret = databuff_printf(buf, buf_len, pos, "%c", ' '); } } } // processing result is zero and has element 'B' if (OB_SUCC(ret) && ObNFMElem::has_type(NFM_BLANK_FLAG, fmt_desc_.elem_flag_) && is_zero(str)) { if (ObNFMElem::has_type(NFM_FILLMODE_FLAG, fmt_desc_.elem_flag_)) { fmt_desc_.output_len_ = 0; } else { if (OB_FAIL(fill_str(buf, buf_len, 0, ' ', fmt_desc_.output_len_))) { LOG_WARN("fail to fill str", K(ret)); } } pos = fmt_desc_.output_len_; } if (pos > fmt_desc_.output_len_) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("res str is larger than output len", K(ret), K(pos), K(fmt_desc_.output_len_)); } if (OB_SUCC(ret) && OB_FAIL(process_fillmode(buf, buf_len, pos))) { LOG_WARN("fail to process fillmode", K(ret)); } return ret; } int ObNFMToChar::process_fmt_conv(const ObSQLSessionInfo &session, const char *fmt_str, const int32_t fmt_len, const ObNFMObj &nfm_obj, char *res_buf, const int64_t res_buf_len, int64_t &offset) { int ret = OB_SUCCESS; ObString num_str; bool is_overflow = false; int32_t integer_part_len = 0; if (OB_FAIL(parse_fmt(fmt_str, fmt_len))) { LOG_WARN("fail to parse fmt model", K(ret), K(fmt_str), K(fmt_len)); } else { // processing calculation conversion element if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_roman_format(nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process roman fmt", K(ret)); } } else if (ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_hex_format(nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process hex fmt", K(ret)); } } else if (ObNFMElem::has_type(NFM_TM_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_tm_format(nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process tm fmt", K(ret)); } } else if (ObNFMElem::has_type(NFM_TME_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_tme_format(nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process tme fmt", K(ret)); } } else { // the number of digits after the decimal point means how many decimal places are reserved // eg: to_char(123.123, '999.99') --> 123.12 // the value is rounded to two decimal places const int32_t scale = fmt_desc_.post_num_count_; if (ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_mul_format(nfm_obj, num_str))) { LOG_WARN("fail to process mul format", K(ret)); } } else { if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(cast_obj_to_num_str(nfm_obj, -1, num_str))) { LOG_WARN("fail to cast obj to num str", K(ret)); } } else { if (OB_FAIL(cast_obj_to_num_str(nfm_obj, scale, num_str))) { LOG_WARN("fail to cast obj to num str", K(ret)); } } } if (OB_SUCC(ret)) { if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_sci_format(num_str, scale, num_str))) { LOG_WARN("fail to process sci fmt", K(ret)); } } if (OB_SUCC(ret)) { // if the length of the integer part exceeds the number of the integer part in fmt str // fill with '#' // eg: to_char(123.12, '99.99') --> ###### if (OB_FAIL(get_integer_part_len(num_str, integer_part_len))) { LOG_WARN("fail to get num str pre num count", K(ret)); } else if (!is_zero(num_str) && integer_part_len > fmt_desc_.pre_num_count_) { is_overflow = true; if (OB_FAIL(fill_str(res_buf, res_buf_len, 0, '#', fmt_desc_.output_len_))) { LOG_WARN("fail to fill str", K(ret)); } else { offset = fmt_desc_.output_len_; } } } } if (!is_overflow) { if (OB_SUCC(ret) && ObNFMElem::has_currency_group(fmt_desc_.elem_flag_) && OB_FAIL(get_nls_currency(session))) { LOG_WARN("fail to get nls currency", K(ret)); } if (OB_SUCC(ret) && ObNFMElem::has_iso_grouping_group(fmt_desc_.elem_flag_) && OB_FAIL(get_iso_grouping(session))) { LOG_WARN("fail to get iso grouping", K(ret)); } if (OB_SUCC(ret) && OB_FAIL(process_output_fmt(num_str, integer_part_len, res_buf, res_buf_len, offset))) { LOG_WARN("fail to processor fmt print", K(ret)); } } } } return ret; } int ObNFMToChar::convert_num_to_fmt_str(const ObObj &obj, const char *fmt_str, const int32_t fmt_len, ObExprCtx &expr_ctx, ObString &res_str) { int ret = OB_SUCCESS; int64_t offset = 0; char *res_buf = NULL; ObNFMObj nfm_obj; ObSQLSessionInfo *session = expr_ctx.my_session_; const int64_t res_buf_len = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; if (OB_ISNULL(fmt_str) || OB_ISNULL(expr_ctx.calc_buf_) || OB_ISNULL(session)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(fmt_str), K(expr_ctx.calc_buf_), K(session)); } else if (fmt_len >= MAX_FMT_STR_LEN) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid fmt string", K(ret)); } else if (OB_ISNULL(res_buf = static_cast( expr_ctx.calc_buf_->alloc(res_buf_len)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (OB_FAIL(conv_num_to_nfm_obj(obj, expr_ctx, nfm_obj))) { LOG_WARN("fail to conv obj to nfm obj", K(ret)); } else if (OB_FAIL(process_fmt_conv(*session, fmt_str, fmt_len, nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process fmt conversion", K(ret), K(fmt_str), K(fmt_len)); } else { res_str.assign_ptr(res_buf, offset); } return ret; } int ObNFMToChar::calc_result_length(const common::ObObj &obj, int32_t &length) { int ret = OB_SUCCESS; bool need_check = false; ObExprCtx expr_ctx; expr_ctx.calc_buf_ = &allocator_; EXPR_DEFINE_CAST_CTX(expr_ctx, CM_NONE); ObString fmt_str; EXPR_GET_VARCHAR_V2(obj, fmt_str); if (OB_FAIL(ret)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid input format. need varchar.", K(ret), K(obj)); } else if (OB_FAIL(parse_fmt(fmt_str.ptr(), fmt_str.length(), need_check))) { LOG_WARN("fail to parse fmt model", K(ret), K(fmt_str)); } else { length = fmt_desc_.output_len_; } return ret; } int ObNFMToChar::convert_num_to_fmt_str(const common::ObObjMeta &obj_meta, const common::ObDatum &obj, common::ObIAllocator &alloc, const char *fmt_str, const int32_t fmt_len, ObEvalCtx &ctx, common::ObString &res_str) { int ret = OB_SUCCESS; int64_t offset = 0; char *res_buf = NULL; ObNFMObj nfm_obj; const int64_t res_buf_len = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; ObSQLSessionInfo *session = ctx.exec_ctx_.get_my_session(); if (OB_ISNULL(fmt_str) || OB_ISNULL(session)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(fmt_str), K(session)); } else if (fmt_len >= MAX_FMT_STR_LEN) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid fmt string", K(ret)); } else if (OB_ISNULL(res_buf = static_cast(alloc.alloc(res_buf_len)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (OB_FAIL(conv_num_to_nfm_obj(obj_meta, obj, nfm_obj, alloc))) { LOG_WARN("fail to conv obj to nfm obj", K(ret)); } else if (OB_FAIL(process_fmt_conv(*session, fmt_str, fmt_len, nfm_obj, res_buf, res_buf_len, offset))) { LOG_WARN("fail to process fmt conversion", K(ret), K(fmt_str), K(fmt_len)); } else { res_str.assign_ptr(res_buf, offset); } return ret; } int ObNFMToNumber::process_hex_format(const common::ObString &in_str, number::ObNumber &res_num, common::ObIAllocator &allocator) { int ret = OB_SUCCESS; const char *str = in_str.ptr(); int32_t str_len = in_str.length(); int32_t str_pos = 0; HexNumber nums; // remove leading spaces // eg: to_number(' fff', 'xxx') --> 4095 // eg: to_number('fff ', 'xxxx') --> invalid number while (str_pos < str_len && isspace(str[str_pos])) { ++str_pos; } if (ObNFMElem::has_type(NFM_ZERO_FLAG, fmt_desc_.elem_flag_)) { if (str_len - str_pos != fmt_desc_.pre_num_count_ + fmt_desc_.elem_x_count_) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str)); } } else { if (str_len - str_pos > fmt_desc_.elem_x_count_) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str)); } } if (OB_FAIL(ret)) { } else if ('-' == str[str_pos]) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("not support negative number", K(ret), K(in_str)); } else if (ObNFMElem::has_type((~NFM_HEX_FLAG) & (~NFM_ZERO_FLAG) & (~NFM_NINE_FLAG) & (~NFM_FILLMODE_FLAG), fmt_desc_.elem_flag_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("incompatible with other formats", K(ret), K(in_str)); } else if (OB_FAIL(check_hex_str_valid(str, str_len, str_pos))) { LOG_WARN("invalid hex character", K(ret), K(in_str)); } else if (OB_FAIL(build_hex_number(str, str_len, str_pos, nums))) { LOG_WARN("fail to build hex number", K(ret), K(in_str)); } else { int32_t hex_num_size = nums.count(); if (1 == hex_num_size) { if (OB_FAIL(res_num.from(nums.at(0), allocator))) { LOG_WARN("failed to cast obj as number", K(ret)); } } else { const uint64_t base = 16; const uint64_t exponent = 15; number::ObNumber base_num; number::ObNumber power_num; number::ObNumber res; if (base_num.from(base, allocator)) { LOG_WARN("fail to cast uint64 to number", K(ret), K(base)); } else if (base_num.power(exponent, power_num, allocator)) { LOG_WARN("power calc failed", K(ret)); } else { for (int32_t i = 0; OB_SUCC(ret) && i < hex_num_size; i++) { uint64_t val = nums.at(i); number::ObNumber num; if (num.from(val, allocator)) { LOG_WARN("fail to cast uint64 to number", K(ret), K(val)); } else { for (int32_t j = i; OB_SUCC(ret) && j > 0; j--) { if (num.mul_v2(power_num, num, allocator)) { LOG_WARN("fail to mul number", K(ret)); } } if (OB_SUCC(ret) && OB_FAIL(res.add(num, res, allocator))) { LOG_WARN("fail to add number", K(ret)); } } } if (OB_SUCC(ret)) { res_num = res; } } } } return ret; } int ObNFMToNumber::process_output_fmt(const ObString &in_str, const int32_t integer_part_len, common::ObString &num_str) { int ret = OB_SUCCESS; const char *str = in_str.ptr(); int32_t str_len = in_str.length(); int32_t str_pos = 0; int64_t offset = 0; bool is_negative = false; char *buf = NULL; const int64_t buf_len = MAX_TO_CHAR_BUFFER_SIZE_IN_FORMAT_MODELS; // skip leading spaces while (str_pos < str_len && ' ' == str[str_pos]) { ++str_pos; } // reserve the positive and negative sign position ++offset; // eg: to_number(' ', 'B') --> 0 if (str_pos == str_len && (!ObNFMElem::has_type(NFM_BLANK_FLAG, fmt_desc_.elem_flag_) || str_len != fmt_elem_list_.count())) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("invalid number", K(ret), K(in_str)); } else if (OB_ISNULL(buf = static_cast( allocator_.alloc(buf_len)))) { ret = OB_ALLOCATE_MEMORY_FAILED; LOG_WARN("fail to alloc memory", K(ret)); } else if (ObNFMElem::has_sign_group(fmt_desc_.elem_flag_)) { // processing leading positive or negative sign element if (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc_.elem_flag_) && OBNFMDesc::FIRST_POS == fmt_desc_.sign_appear_pos_) { if (str_pos >= str_len) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } else if ('-' == str[str_pos]) { is_negative = true; ++str_pos; } else if ('+' == str[str_pos]) { ++str_pos; } else { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } } else if (ObNFMElem::has_type(NFM_PR_FLAG, fmt_desc_.elem_flag_)) { if (str_pos < str_len) { if ('<' == str[str_pos]) { is_negative = true; ++str_pos; } } } } else { if (str_pos < str_len) { if ('-' == str[str_pos]) { is_negative = true; ++str_pos; } } } // processing element '$' if (OB_SUCC(ret) && ObNFMElem::has_type(NFM_DOLLAR_FLAG, fmt_desc_.elem_flag_)) { if (str_pos >= str_len || str[str_pos++] != '$' || (0 == fmt_desc_.pre_num_count_ && 0 == fmt_desc_.post_num_count_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } } // processing the last positive or negative sign element if (OB_SUCC(ret) && ObNFMElem::has_sign_group(fmt_desc_.elem_flag_)) { if (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc_.elem_flag_) && OBNFMDesc::LAST_POS == fmt_desc_.sign_appear_pos_) { if ('-' == str[str_len - 1]) { is_negative = true; --str_len; } else if ('+' == str[str_len - 1]) { --str_len; } else { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } } else if (ObNFMElem::has_type(NFM_PR_FLAG, fmt_desc_.elem_flag_)) { if (is_negative) { if (str[str_len - 1] != '>') { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } --str_len; } } else if (ObNFMElem::has_type(NFM_MI_FLAG, fmt_desc_.elem_flag_)) { if (is_negative) { if ('-' == str[str_len - 1]) { is_negative = true; --str_len; } else { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } } else { if (' ' == str[str_len - 1]) { --str_len; } else if ('-' == str[str_len - 1]) { is_negative = true; --str_len; } else if (!ObNFMElem::has_type(NFM_FILLMODE_FLAG, fmt_desc_.elem_flag_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str)); } } } } if (OB_SUCC(ret)) { // need to skip leading and trailing redundant numbers in the fmt string // eg: to_number('123.123', '999999999.999999999') // pre_need_skip_num = 6; const int32_t pre_need_skip_num = fmt_desc_.pre_num_count_ - integer_part_len; int32_t traversed_num_count = 0; for (int32_t i = 0; OB_SUCC(ret) && i < fmt_elem_list_.count(); ++i) { const ObNFMElem *elem_item = fmt_elem_list_.at(i); if (OB_ISNULL(elem_item)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("invalid elem item", K(ret)); } else { ObNFMElem::ElementType elem_type = elem_item->keyword_->elem_type_; // skip thousands separator // eg: to_number('123,12,12,', '99999999999999') --> 1231212 // eg: to_number('1,,,.235', '9GGD999') --> 1.235 if (elem_type != ObNFMElem::NFM_COMMA && elem_type != ObNFMElem::NFM_G) { if (traversed_num_count > 0 && traversed_num_count <= fmt_desc_.pre_num_count_ && fmt_desc_.last_separator_ < i) { while (str_pos < str_len && ((',' == str[str_pos]) || (ObNFMElem::has_iso_grouping_group(fmt_desc_.elem_flag_) && str[str_pos] == fmt_desc_.iso_grouping_.ptr()[1]))) { ++str_pos; } } } switch (elem_type) { case ObNFMElem::NFM_COMMA: if (traversed_num_count <= pre_need_skip_num && ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else if (traversed_num_count <= pre_need_skip_num && (fmt_desc_.zero_start_ < 0 || fmt_desc_.zero_start_ > i)) { // skip, do nothing } else if (str_pos >= str_len || str[str_pos] != ',') { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str),K(str_pos), K(i)); } else { ++str_pos; } break; case ObNFMElem::NFM_PERIOD: // ignore decimal point // eg: to_number(1265, '0000.000') if (str_pos >= str_len) { // do nothing } else { if (str[str_pos] != '.') { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos]); ++str_pos; } } break; case ObNFMElem::NFM_ZERO: // if there is fractional part and the number of decimal parts exceeds the precision // the following numbers need to be skipped // eg: to_number('123.123', '999999999.999999999') if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (fmt_desc_.pre_num_count_ - traversed_num_count > 1 || (fmt_desc_.decimal_pos_ != INT32_MAX && fmt_desc_.decimal_pos_ < i && str_pos >= str_len)) { // do nothing } else if (str_pos >= str_len || !is_digit(str[str_pos])) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos++]); } } else { if (fmt_desc_.decimal_pos_ != INT32_MAX && fmt_desc_.decimal_pos_ < i && str_pos >= str_len) { // do nothing } else if (str_pos >= str_len || !is_digit(str[str_pos])) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { if (traversed_num_count < pre_need_skip_num) { str_pos++; } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos++]); } } } ++traversed_num_count; break; case ObNFMElem::NFM_NINE: if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (fmt_desc_.pre_num_count_ - traversed_num_count > 1 || (fmt_desc_.decimal_pos_ != INT32_MAX && fmt_desc_.decimal_pos_ < i && str_pos >= str_len)) { // do nothing } else { if (str_pos >= str_len || !is_digit(str[str_pos])) { // do nothing } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos++]); } } } else { // if there is fractional part and the number of decimal parts exceeds the precision // the following numbers need to be skipped // eg: to_number('123.123', '999999999.999999999') if (fmt_desc_.decimal_pos_ != INT32_MAX && fmt_desc_.decimal_pos_ < i && str_pos >= str_len) { // do nothing } else if (traversed_num_count < pre_need_skip_num) { // if element '0' has appeared, the following numbers need to fill '0' // eg: to_char(123.123, '9099999.999') --> 000123.123 // eg: to_number('000123.123', '9099999.999') --> 123.123 if ((fmt_desc_.zero_start_ >= 0 && fmt_desc_.zero_start_ < i)) { if (str_pos >= str_len || !is_digit(str[str_pos])) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ++str_pos; } } } else { if (str_pos >= str_len || !is_digit(str[str_pos])) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos++]); } } } ++traversed_num_count; break; case ObNFMElem::NFM_D: // ignore decimal point // eg: to_number(1265, '0000.000') if (str_pos >= str_len) { // do nothing } else { if (str[str_pos] != fmt_desc_.iso_grouping_.ptr()[0]) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ret = databuff_printf(buf, buf_len, offset, "%c", str[str_pos]); ++str_pos; } } break; case ObNFMElem::NFM_G: if (traversed_num_count <= pre_need_skip_num && ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else if (traversed_num_count <= pre_need_skip_num && (fmt_desc_.zero_start_ < 0 || fmt_desc_.zero_start_ > i)) { // skip, do nothing } else { if (str_pos >= str_len || str[str_pos] != fmt_desc_.iso_grouping_.ptr()[1]) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { ++str_pos; } } break; case ObNFMElem::NFM_C: case ObNFMElem::NFM_L: case ObNFMElem::NFM_U: if (OBNFMDesc::MIDDLE_POS == fmt_desc_.currency_appear_pos_) { if (str_pos >= str_len) { // do nothing } else { if ((str[str_pos] != '.' && (str_len - str_pos < fmt_desc_.nls_currency_.length() || (0 != strncasecmp(str + str_pos, fmt_desc_.nls_currency_.ptr(), fmt_desc_.nls_currency_.length())))) || (ObNFMElem::has_type(NFM_S_FLAG, fmt_desc_.elem_flag_) && OBNFMDesc::LAST_POS == fmt_desc_.sign_appear_pos_)) { // eg: to_number('23$00+', '99L99S') --> error 1722 ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { if (str[str_pos] == '.') { ++str_pos; } else { str_pos += fmt_desc_.nls_currency_.length(); } // if the element('C', 'L', 'U') appears in the middle, it will be regarded as the // decimal point, so use decimal point to replace the iso currency ret = databuff_printf(buf, buf_len, offset, "%c", '.'); } } } else { if (str_pos >= str_len || str_len - str_pos < fmt_desc_.nls_currency_.length() || (0 != strncasecmp(str + str_pos, fmt_desc_.nls_currency_.ptr(), fmt_desc_.nls_currency_.length()))) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } else { str_pos += fmt_desc_.nls_currency_.length(); } } break; case ObNFMElem::NFM_EEEE: if (str_pos < str_len && str[str_pos] != 'e' && str[str_pos] != 'E') { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(in_str), K_(fmt_str), K(str_pos), K(i)); } // eg: to_number('1.2E+03 ', '9.9EEEEMI') while (str_pos < str_len && str[str_pos] != ' ') { buf[offset++] = str[str_pos++]; } break; default: break; } } } } // eg: to_number('123.', '999') --> 123 // eg: to_number('123,', '999') --> 123 for (; OB_SUCC(ret) && str_pos < str_len; ++str_pos) { if (str[str_pos] != ',' && str[str_pos] != '.') { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("fmt does not match", K(ret), K(str_pos), K(str_len), K(in_str), K_(fmt_str)); } } if (OB_SUCC(ret)) { if (is_negative) { buf[0] = '-'; } else { buf[0] = '+'; } // if string is empty, set num_str to '+0' or '-0' as a valid number. if (offset == 1) { buf[offset++] = '0'; } num_str.assign(buf, offset); } return ret; } int ObNFMToNumber::process_fmt_conv(const ObSQLSessionInfo &session, const common::ObString &in_str, const common::ObString &in_fmt_str, common::ObIAllocator &alloc, common::number::ObNumber &res_num) { int ret = OB_SUCCESS; int32_t integer_part_len = 0; ObPrecision res_precision = -1; ObScale res_scale = -1; ObString num_str; if (OB_FAIL(parse_fmt(in_fmt_str.ptr(), in_fmt_str.length()))) { LOG_WARN("fail to parse format model", K(ret), K(in_fmt_str)); } else if (ObNFMElem::has_type(NFM_RN_FLAG, fmt_desc_.elem_flag_) || ObNFMElem::has_type(NFM_TM_FLAG, fmt_desc_.elem_flag_) || ObNFMElem::has_type(NFM_TME_FLAG, fmt_desc_.elem_flag_) || ObNFMElem::has_type(NFM_MULTI_FLAG, fmt_desc_.elem_flag_)) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("not support elem type", K(ret)); } else if (OB_FAIL(get_integer_part_len(in_str, integer_part_len))) { LOG_WARN("fail to get integer part len", K(ret)); } else if (ObNFMElem::has_type(NFM_HEX_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(process_hex_format(in_str, res_num, alloc))) { LOG_WARN("fail to process hex format", K(ret)); } } else { // here are two cases, return invalid number // eg: to_number('1.23E+03', '9.9EEEE') invalid number // eg: to_number('123.12', '99.99') invalid number if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (fmt_desc_.pre_num_count_ <= 0) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("fmt does not match", K(ret), K(in_str), K(in_fmt_str)); } } else { if (integer_part_len > fmt_desc_.pre_num_count_) { ret = OB_ERR_CAST_VARCHAR_TO_NUMBER; LOG_WARN("overflowed digit format", K(ret), K(integer_part_len), K(fmt_desc_.pre_num_count_), K(fmt_desc_.post_num_count_)); } } if (OB_FAIL(ret)) { } else if (ObNFMElem::has_currency_group(fmt_desc_.elem_flag_) && OB_FAIL(get_nls_currency(session))) { LOG_WARN("fail to get nls currency", K(ret)); } else if (ObNFMElem::has_iso_grouping_group(fmt_desc_.elem_flag_) && OB_FAIL(get_iso_grouping(session))) { LOG_WARN("fail to get iso grouping", K(ret)); } else if (OB_FAIL(process_output_fmt(in_str, integer_part_len, num_str))) { LOG_WARN("fail to process output fmt", K(ret)); } else { if (ObNFMElem::has_type(NFM_EEEE_FLAG, fmt_desc_.elem_flag_)) { if (OB_FAIL(res_num.from_sci_opt(num_str.ptr(), num_str.length(), alloc, &res_precision, &res_scale))) { LOG_WARN("fail to calc function to_number with", K(ret), K(num_str)); } } else { if (OB_FAIL(res_num.from(num_str.ptr(), num_str.length(), alloc, NULL, &res_precision, &res_scale))) { LOG_WARN("fail to calc function to_number with", K(ret), K(num_str)); } } } } return ret; } int ObNFMToNumber::convert_char_to_num(const common::ObString &in_str, const common::ObString &in_fmt_str, common::ObExprCtx &expr_ctx, common::number::ObNumber &res_num) { int ret = OB_SUCCESS; ObSQLSessionInfo *session = expr_ctx.my_session_; if (OB_ISNULL(expr_ctx.calc_buf_) || OB_ISNULL(session)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid argument", K(ret), K(expr_ctx.calc_buf_), K(session)); } else if (in_fmt_str.length() >= MAX_FMT_STR_LEN) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid format string", K(ret)); } else if (OB_FAIL(process_fmt_conv(*session, in_str, in_fmt_str, *(expr_ctx.calc_buf_), res_num))) { LOG_WARN("fail to parse format model", K(ret), K(in_fmt_str)); } return ret; } int ObNFMToNumber::convert_char_to_num(const common::ObString &in_str, const common::ObString &in_fmt_str, common::ObIAllocator &alloc, ObEvalCtx &ctx, common::number::ObNumber &res_num) { int ret = OB_SUCCESS; ObSQLSessionInfo *session = ctx.exec_ctx_.get_my_session(); if (OB_ISNULL(session)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("Invalid argument", K(ret), K(session)); } else if (in_fmt_str.length() >= MAX_FMT_STR_LEN) { ret = OB_ERR_INVALID_NUMBER_FORMAT_MODEL; LOG_WARN("invalid format string", K(ret)); } else if (OB_FAIL(process_fmt_conv(*session, in_str, in_fmt_str, alloc, res_num))) { LOG_WARN("fail to parse format model", K(ret), K(in_fmt_str)); } return ret; } } }