/** * 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. */ #ifndef OCEANBASE_SQL_ENGINE_EXPR_NUMBER_FORMAT_MODELS_H_ #define OCEANBASE_SQL_ENGINE_EXPR_NUMBER_FORMAT_MODELS_H_ #include "sql/engine/expr/ob_expr_operator.h" #include "lib/container/ob_iarray.h" //see oracle doc Number Format Models: https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm namespace oceanbase { namespace sql { class ObNFMKeyWord; struct ObNFMElem { enum ElementType : int64_t { INVALID_TYPE = -1, NFM_COMMA, NFM_PERIOD, NFM_DOLLAR, NFM_ZERO, NFM_NINE, NFM_B, NFM_C, NFM_D, NFM_EEEE, NFM_G, NFM_L, NFM_MI, NFM_PR, NFM_RN, NFM_S, NFM_TME, NFM_TM9, NFM_TM, NFM_U, NFM_V, NFM_X, NFM_FM, MAX_TYPE_NUMBER }; enum ElementGroup : int64_t { INVALID_GROUP = -1, NUMBER_GROUP, // include 0, 9 GROUPING_GROUP, // include ',', '.' ISO_GROUPING_GROUP, // include D, G DOLLAR_GROUP, // include $ CURRENCY_GROUP, // include C, L, U EEEE_GROUP, // include EEEE ROMAN_GROUP, // include RN MULTI_GROUP, // include V HEX_GROUP, // include X SIGN_GROUP, // include MI, PR, S BLANK_GROUP, // include B FILLMODE_GROUP, // include FM TM_GROUP, // include TME, TM9, TM MAX_GROUP_NUMBER }; #define NFM_COMMA_FLAG (1 << 1) #define NFM_PERIOD_FLAG (1 << 2) #define NFM_DOLLAR_FLAG (1 << 3) #define NFM_ZERO_FLAG (1 << 4) #define NFM_NINE_FLAG (1 << 5) #define NFM_BLANK_FLAG (1 << 6) #define NFM_C_FLAG (1 << 7) #define NFM_D_FLAG (1 << 8) #define NFM_EEEE_FLAG (1 << 9) #define NFM_G_FLAG (1 << 10) #define NFM_L_FLAG (1 << 11) #define NFM_MI_FLAG (1 << 12) #define NFM_PR_FLAG (1 << 13) #define NFM_RN_FLAG (1 << 14) #define NFM_S_FLAG (1 << 15) #define NFM_TME_FLAG (1 << 16) #define NFM_TM_FLAG (1 << 17) #define NFM_U_FLAG (1 << 18) #define NFM_MULTI_FLAG (1 << 19) #define NFM_HEX_FLAG (1 << 20) #define NFM_FILLMODE_FLAG (1 << 21) static bool has_type(const int32_t elem_type, const int32_t flag) { return flag & elem_type; } static bool has_number_group(const int32_t flag) { return (flag & NFM_ZERO_FLAG) || (flag & NFM_NINE_FLAG); } static bool has_grouping_group(const int32_t flag) { return (flag & NFM_COMMA_FLAG) || (flag & NFM_PERIOD_FLAG); } static bool has_iso_grouping_group(const int32_t flag) { return (flag & NFM_D_FLAG) || (flag & NFM_G_FLAG); } static bool has_currency_group(const int32_t flag) { return (flag & NFM_C_FLAG) || (flag & NFM_L_FLAG) || (flag & NFM_U_FLAG); } static bool has_sign_group(const int32_t flag) { return (flag & NFM_MI_FLAG) || (flag & NFM_PR_FLAG) || (flag & NFM_S_FLAG); } static bool has_tm_group(const int32_t flag) { return (flag & NFM_TME_FLAG) || (flag & NFM_TM_FLAG); } static const ObNFMKeyWord NFM_KEYWORDS[MAX_TYPE_NUMBER]; static inline bool is_valid_type(const ElementType elem_type) { return elem_type > INVALID_TYPE && elem_type < MAX_TYPE_NUMBER; } static inline bool is_valid_group_type(const ElementGroup elem_group) { return elem_group > INVALID_GROUP && elem_group < MAX_GROUP_NUMBER; } enum ElemCaseMode { IGNORE_CASE, UPPER_CASE, LOWER_CASE }; ObNFMElem() : offset_(-1), is_last_elem_(false), keyword_(nullptr), prefix_type_(INVALID_TYPE), suffix_type_(INVALID_TYPE), case_mode_(IGNORE_CASE) {} common::ObString get_elem_type_name() const; TO_STRING_KV("elem_type", get_elem_type_name(), K_(keyword), K_(offset), K_(case_mode)); int32_t offset_; bool is_last_elem_; const ObNFMKeyWord *keyword_; ElementType prefix_type_; // type of prefix node ElementType suffix_type_; // type of suffix node ElemCaseMode case_mode_; }; struct ObNFMKeyWord { ObNFMKeyWord(const char *str, const ObNFMElem::ElementType type, const ObNFMElem::ElementGroup group, const int32_t output_len) : ptr_(str), len_(static_cast(strlen(str))), output_len_(output_len), elem_type_(type), elem_group_(group) {} ObNFMKeyWord(const char *str, int32_t len, const ObNFMElem::ElementType type, const ObNFMElem::ElementGroup group, const int32_t output_len) : ptr_(str), len_(len), output_len_(output_len), elem_type_(type), elem_group_(group) {} inline common::ObString to_obstring() const { return common::ObString(len_, ptr_); } const char *ptr_; int32_t len_; int32_t output_len_; ObNFMElem::ElementType elem_type_; ObNFMElem::ElementGroup elem_group_; TO_STRING_KV("value", common::ObString(len_, ptr_), K_(len), K_(elem_type), K_(elem_group), K_(output_len)); }; struct OBNFMDesc { enum ElemPos { INVALID_POS, FIRST_POS, MIDDLE_POS, LAST_POS }; OBNFMDesc() : digital_start_(-1), digital_end_(-1), decimal_pos_(INT32_MAX), pre_num_count_(0), post_num_count_(0), multi_(0), output_len_(0), zero_start_(-1), zero_end_(-1), last_separator_(-1), elem_flag_(0), upper_case_flag_(0), sign_appear_pos_(INVALID_POS), currency_appear_pos_(INVALID_POS), elem_x_count_(0), nls_currency_(), iso_grouping_() {} int32_t digital_start_; // offset of the first digit int32_t digital_end_; // offset of the last digit int32_t decimal_pos_; // offset of the decimal point int32_t pre_num_count_; // the number of digits before decimal point int32_t post_num_count_; // the number of digits after decimal point int32_t multi_; // the number of digits after 'V' int32_t output_len_; // output result length int32_t zero_start_; // offset of the first zero int32_t zero_end_; // offset of the last zero int32_t last_separator_; // offset of the last thousands separator int32_t elem_flag_; // bitmap marker element appears int32_t upper_case_flag_; // bitmap marker element case mode ElemPos sign_appear_pos_; // position the sign class appears ElemPos currency_appear_pos_; // position the currency class appears int32_t elem_x_count_; // number of 'X' common::ObString nls_currency_; common::ObString iso_grouping_; }; class ObNFMDescPrepare { public: static int fmt_desc_prepare(const common::ObSEArray &fmt_elem_list, OBNFMDesc &fmt_desc, bool need_check = true); private: static int check_conflict_group( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_comma_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_period_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_dollar_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_zero_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_nine_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_b_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_c_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_d_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_eeee_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_g_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_l_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_mi_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_pr_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_rn_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_s_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_tm_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_u_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_v_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_x_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); static int check_elem_fm_is_valid( const ObNFMElem *elem_item, const OBNFMDesc &fmt_desc); }; struct ObNFMObj { ObNFMObj() : obj_type_(common::ObNullType), num_(), float_(0), double_(0) {} ObNFMObj(const common::ObObjType &obj_type) : obj_type_(obj_type), num_(), float_(0), double_(0) {} const common::number::ObNumber& get_number() const {return num_;} float get_float() const {return float_;} double get_double() const {return double_;} void set_number(const common::number::ObNumber &num) {num_ = num;} void set_float(const float f) {float_ = f;} void set_double(const double d) {double_ = d;} common::ObObjType get_obj_type() const {return obj_type_;} void set_obj_type(const common::ObObjType obj_type) {obj_type_ = obj_type;} common::ObObjType obj_type_; common::number::ObNumber num_; float float_; double double_; }; class ObNFMBase { public: typedef common::ObSEArray BigNumber; typedef common::ObSEArray HexNumber; ObNFMBase() : fmt_str_(), fmt_desc_(), allocator_() {} ~ObNFMBase() { allocator_.reset(); } const common::ObSEArray &get_elem_list() const { return fmt_elem_list_; } const common::ObString &get_fmt_str() const { return fmt_str_; } int search_keyword(const char *cur_ch, const int32_t remain_len, const ObNFMKeyWord *&match_key, ObNFMElem::ElemCaseMode &case_mode) const; int parse_fmt(const char* fmt_str, const int32_t fmt_len, bool need_check = true); int fill_str(char *str, const int32_t str_len, const int32_t offset, const char fill_ch, const int32_t fill_len) const; int int_to_roman_str(const int64_t val, char *buf, const int64_t buf_len, int64_t &pos) const; int append_decimal_digit(BigNumber &num, const uint32_t decimal_digit) const; int decimal_to_hex(const common::ObString &num_str, char *buf, const int64_t buf_len, int64_t &pos) const; int build_hex_number(const char *str, const int32_t str_len, int32_t &offset, HexNumber &hex_num) const; int check_hex_str_valid(const char *str, const int32_t str_len, const int32_t offset) const; int 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 get_integer_part_len(const common::ObString &num_str, int32_t &integer_part_len) const; int get_decimal_part_len(const common::ObString &num_str, int32_t &decimal_part_len) const; int hex_to_num(const char c, int32_t &val) const; int get_nls_currency(const ObSQLSessionInfo &session); int get_iso_grouping(const ObSQLSessionInfo &session); int remove_leading_zero(char *buf, int64_t &offset); int process_fillmode(char *buf, const int64_t buf_len, int64_t &pos) const; int conv_num_to_nfm_obj(const common::ObObj &obj, common::ObExprCtx &expr_ctx, ObNFMObj &nfm_obj); int conv_num_to_nfm_obj(const common::ObObjMeta &obj_meta, const ObDatum &obj, ObNFMObj &nfm_obj, common::ObIAllocator &alloc); // the conversion range of roman numerals is 1~3999, so if it is not a valid int64, negative // number returns INT64_MIN, positive number returns INT64_MAX, will not affect the final result. int cast_obj_to_int(const ObNFMObj &nfm_obj, int64_t &res_val); int cast_obj_to_num_str(const ObNFMObj &nfm_obj, const int64_t scale, common::ObString &num_str); bool is_digit(const char c) const; bool is_zero(const common::ObString &num_str) const; static const char *const LOWER_RM1[9]; static const char *const LOWER_RM10[9]; static const char *const LOWER_RM100[9]; static const char *const UPPER_RM1[9]; static const char *const UPPER_RM10[9]; static const char *const UPPER_RM100[9]; protected: common::ObString fmt_str_; OBNFMDesc fmt_desc_; common::ObArenaAllocator allocator_; common::ObSEArray fmt_elem_list_; private: DISALLOW_COPY_AND_ASSIGN(ObNFMBase); }; class ObNFMToChar : public ObNFMBase { public: ObNFMToChar() {} int convert_num_to_fmt_str(const common::ObObj &obj, const char *fmt_str, const int32_t fmt_len, common::ObExprCtx &expr_ctx, common::ObString &res_str); int 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 calc_result_length(const common::ObObj &obj, int32_t &length); private: int process_fmt_conv(const ObSQLSessionInfo &session, const char *fmt_str, const int32_t fmt_len, const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos); int process_roman_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos); int process_hex_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos); int process_tm_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos); int process_tme_format(const ObNFMObj &nfm_obj, char *buf, const int64_t buf_len, int64_t &pos); int process_mul_format(const ObNFMObj &nfm_obj, common::ObString &num_str); int process_sci_format(const common::ObString &origin_str, const int32_t scale, common::ObString &num_str); int process_output_fmt(const common::ObString &num_str, const int32_t integer_part_len, char *buf, const int64_t buf_len, int64_t &pos); private: DISALLOW_COPY_AND_ASSIGN(ObNFMToChar); }; class ObNFMToNumber : public ObNFMBase { public: ObNFMToNumber() {} int 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 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); private: int 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 process_hex_format(const common::ObString &in_str, common::number::ObNumber &res_num, common::ObIAllocator &allocator); int process_output_fmt(const common::ObString &in_str, const int32_t integer_part_len, common::ObString &num_str); private: DISALLOW_COPY_AND_ASSIGN(ObNFMToNumber); }; } } #endif // OCEANBASE_SQL_ENGINE_EXPR_NUMBER_FORMAT_MODELS_H_