/** * 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. */ #include "ob_obj2str_helper.h" #include "ob_log_timezone_info_getter.h" #include "lib/timezone/ob_timezone_info.h" #include "lib/string/ob_sql_string.h" #include "sql/engine/expr/ob_datum_cast.h" // padding_char_for_cast #include "lib/alloc/ob_malloc_allocator.h" #include "sql/engine/expr/ob_expr_uuid.h" #include "sql/engine/expr/ob_expr_operator.h" #include "sql/engine/expr/ob_expr_res_type_map.h" #include "ob_log_utils.h" // _M_ using namespace oceanbase::common; namespace oceanbase { namespace liboblog { const char* ObObj2strHelper::EMPTY_STRING = ""; ObObj2strHelper::ObObj2strHelper() : inited_(false), timezone_info_getter_(NULL), hbase_util_(NULL), enable_hbase_mode_(false), enable_convert_timestamp_to_unix_timestamp_(false), enable_backup_mode_(false), tenant_mgr_(NULL) { } ObObj2strHelper::~ObObj2strHelper() { destroy(); } int ObObj2strHelper::init(IObLogTimeZoneInfoGetter &timezone_info_getter, ObLogHbaseUtil &hbase_util, const bool enable_hbase_mode, const bool enable_convert_timestamp_to_unix_timestamp, const bool enable_backup_mode, IObLogTenantMgr &tenant_mgr) { int ret = OB_SUCCESS; if (inited_) { ret = OB_INIT_TWICE; } else if (OB_FAIL(init_ob_charset_utils())) { OBLOG_LOG(ERROR, "failed to init ob charset util!", KR(ret)); } else { timezone_info_getter_ = &timezone_info_getter; hbase_util_ = &hbase_util; enable_hbase_mode_ = enable_hbase_mode; enable_convert_timestamp_to_unix_timestamp_ = enable_convert_timestamp_to_unix_timestamp; enable_backup_mode_ = enable_backup_mode; tenant_mgr_ = &tenant_mgr; inited_ = true; } return ret; } int ObObj2strHelper::init_ob_charset_utils() { int ret = common::OB_SUCCESS; lib::ObMallocAllocator *allocator = NULL; const lib::ObMemAttr attr(common::OB_SYS_TENANT_ID, ObModIds::OB_NUMBER); if (OB_FAIL(sql::ObExprTRDateFormat::init())) { OBLOG_LOG(ERROR, "failed to init vars in oracle trunc", KR(ret)); } else if (OB_FAIL(sql::ObExprUuid::init())) { OBLOG_LOG(ERROR, "failed to init vars in uuid", KR(ret)); } else if (OB_ISNULL(allocator = lib::ObMallocAllocator::get_instance())) { ret = OB_ERR_UNEXPECTED; OBLOG_LOG(ERROR, "allocator is null", KR(ret)); } else if (OB_FAIL(common::ObNumberConstValue::init(*allocator, attr))) { OBLOG_LOG(ERROR, "failed to init ObNumberConstValue", KR(ret)); } else if (OB_FAIL(sql::ARITH_RESULT_TYPE_ORACLE.init())) { OBLOG_LOG(ERROR, "failed to init ORACLE_ARITH_RESULT_TYPE", KR(ret)); } else if (OB_FAIL(ObCharsetUtils::init(*allocator))) { OBLOG_LOG(ERROR, "fail to init ObCharsetUtils", KR(ret)); } return ret; } void ObObj2strHelper::destroy() { inited_ = false; timezone_info_getter_ = NULL; hbase_util_ = NULL; enable_hbase_mode_ = false; enable_convert_timestamp_to_unix_timestamp_ = false; enable_backup_mode_ = false; tenant_mgr_ = NULL; } //extended_type_info used for enum/set int ObObj2strHelper::obj2str(const uint64_t tenant_id, const uint64_t table_id, const uint64_t column_id, const common::ObObj &obj, common::ObString &str, common::ObIAllocator &allocator, const bool string_deep_copy, const common::ObIArray &extended_type_info, const common::ObAccuracy &accuracy, const common::ObCollationType &collation_type) { int ret = OB_SUCCESS; ObObjType obj_type = obj.get_type(); common::ObObjTypeClass obj_tc = common::ob_obj_type_class(obj_type); ObWorker::CompatMode compat_mode = THIS_WORKER.get_compatibility_mode(); // Configure allowed conversions: mysql timestamp column -> UTC integer time if (ObTimestampType == obj_type && enable_convert_timestamp_to_unix_timestamp_) { if (OB_FAIL(convert_mysql_timestamp_to_utc_(obj, str, allocator))) { OBLOG_LOG(ERROR, "convert_mysql_timestamp_to_utc_ fail", KR(ret), K(table_id), K(column_id), K(obj), K(obj_type), K(str)); } } else if (common::ObNullTC == obj_tc) { str.assign_ptr(NULL, 0); } else if (common::ObExtendTC == obj_tc) { static const int64_t MAX_EXT_PRINT_LEN = 1 << 10; char BUFFER[MAX_EXT_PRINT_LEN]; int64_t pos = 0; char *ptr = NULL; if (OB_FAIL(obj.print_sql_literal(BUFFER, sizeof(BUFFER), pos)) || pos <= 0) { OBLOG_LOG(ERROR, "obj print_sql_literal fail", KR(ret), K(obj), K(MAX_EXT_PRINT_LEN), K(pos)); ret = common::OB_SUCCESS == ret ? common::OB_ERR_UNEXPECTED : ret; } else if (NULL == (ptr = (char *)allocator.alloc(pos))) { OBLOG_LOG(ERROR, "allocate memory fail", "size", pos); ret = common::OB_ALLOCATE_MEMORY_FAILED; } else { (void)MEMCPY(ptr, BUFFER, pos); str.assign_ptr(ptr, (int32_t)pos); OBLOG_LOG(DEBUG, "obj2str cast extend type", K(obj), "cast_str", str); } // This should be before is_string_type, because for char/nchar it is also ObStringTC, so is_string_type=true } else if (need_padding_(compat_mode, obj)) { if (OB_FAIL(convert_char_obj_to_padding_obj_(compat_mode, obj, accuracy, collation_type, allocator, str))) { OBLOG_LOG(ERROR, "convert_char_obj_to_padding_obj_ fail", KR(ret), K(obj), K(accuracy), K(collation_type), K(str), K(compat_mode), "compat_mode_str", print_compat_mode(compat_mode)); } } else if (obj.is_string_type()) { if (string_deep_copy) { // need deep-copy void *dst_buf = NULL; ObString src_str = obj.get_string(); int64_t str_len = obj.get_val_len(); if (str_len > 0) { if (OB_ISNULL(dst_buf = allocator.alloc(str_len))) { OBLOG_LOG(ERROR, "allocate memory fail", K(str_len)); ret = OB_ALLOCATE_MEMORY_FAILED; } else { MEMCPY(dst_buf, src_str.ptr(), src_str.length()); } } if (OB_SUCCESS == ret) { str.assign_ptr(static_cast(dst_buf), static_cast(str_len)); } } else { // No deep copy required, direct reference to original string memory if (OB_FAIL(obj.get_string(str))) { OBLOG_LOG(ERROR, "get_string from ObObj fail", KR(ret), K(obj)); } else { // success } } if (OB_SUCC(ret)) { // For a varchar with a default value of '', str_len=0, the empty string should be synchronised and not output as NULL if (0 == obj.get_val_len()) { str.assign_ptr(EMPTY_STRING, static_cast(obj.get_val_len())); } } } else { common::ObObj tmp_inner_obj; const common::ObObj *in_obj = &obj; ObObjMeta inner_meta; inner_meta.set_collation_level(CS_LEVEL_NUMERIC); inner_meta.set_collation_type(CS_TYPE_BINARY); if (obj.is_enum() || obj.is_set()) { if (OB_FAIL(ObObjCaster::enumset_to_inner(inner_meta, obj, tmp_inner_obj, allocator, extended_type_info))) { OBLOG_LOG(ERROR, "fail to enumset_to_inner", KR(ret)); } else { in_obj = &tmp_inner_obj; } } if (OB_SUCC(ret)) { common::ObObj str_obj; common::ObObjType target_type = common::ObMaxType; //liboblog need use_standard_format ObTimeZoneInfoWrap *tz_info_wrap = nullptr; const common::ObTimeZoneInfo *tz_info = nullptr; if (OB_FAIL(tenant_mgr_->get_tenant_tz_wrap(tenant_id, tz_info_wrap))) { OBLOG_LOG(ERROR, "get_tenant_tz_wrap failed", KR(ret), K(tenant_id)); } else if (OB_ISNULL(tz_info_wrap)) { ret = OB_ERR_UNEXPECTED; OBLOG_LOG(ERROR, "tenant not exist", KR(ret), K(tenant_id)); } else { tz_info = tz_info_wrap->get_time_zone_info(); const ObDataTypeCastParams dtc_params(tz_info); ObObjCastParams cast_param(&allocator, &dtc_params, CM_NONE, collation_type); cast_param.format_number_with_limit_ = false;//here need no limit format number for liboblog if (in_obj->is_bit()) { target_type = common::ObUInt64Type; } else { target_type = common::ObVarcharType; } if (OB_FAIL(ObObjCaster::to_type(target_type, cast_param, *in_obj, str_obj))) { OBLOG_LOG(ERROR, "cast obj to varchar type fail", KR(ret), KPC(in_obj), K(target_type)); if (OB_ERR_INVALID_TIMEZONE_REGION_ID == ret) { // Refresh timezone until successful and convert again ret = OB_SUCCESS; if (OB_FAIL(convert_timestamp_with_timezone_data_util_succ_(target_type, cast_param, *in_obj, str_obj, str, tenant_id))) { OBLOG_LOG(ERROR, "convert_timestamp_with_timezone_data_util_succ_ fail", KR(ret), KPC(in_obj), K(target_type)); } } } else { if (in_obj->is_bit()) { if (OB_FAIL(convert_bit_obj_to_decimal_str_(obj, str_obj, str, allocator))) { OBLOG_LOG(ERROR, "convert_bit_obj_to_decimal_str_ fail", KR(ret), K(obj), K(target_type)); } } else { if (OB_FAIL(str_obj.get_string(str))) { OBLOG_LOG(ERROR, "get_string from ObObj fail", KR(ret), K(str_obj)); } else { // For a varchar with a default value of '', str_len=0, the empty string should be synchronised and not output as NULL if ((obj.is_enum() || obj.is_set()) && 0 == str_obj.get_val_len()) { str.assign_ptr(EMPTY_STRING, static_cast(str_obj.get_val_len())); } } } } } // 1. hbase table T column timestamp type should be converted to positive if it is negative // 2. not converted in backup mode if (OB_SUCC(ret)) { bool is_hbase_table_T_column = false; if (obj.is_int() && enable_hbase_mode_ && ! enable_backup_mode_) { if (OB_ISNULL(hbase_util_)) { OBLOG_LOG(ERROR, "hbase_util_ is null", K(hbase_util_)); ret = OB_ERR_UNEXPECTED; } else if (OB_FAIL(hbase_util_->judge_hbase_T_column(table_id, column_id, is_hbase_table_T_column))) { OBLOG_LOG(ERROR, "hbase_util_ judge_hbase_T_column fail", KR(ret), K(table_id), K(column_id), K(is_hbase_table_T_column)); } else if (! is_hbase_table_T_column) { // do nothing } else { if (OB_FAIL(convert_hbase_bit_obj_to_positive_bit_str_(obj, str_obj, str, allocator))) { OBLOG_LOG(ERROR, "convert_hbase_bit_obj_to_positive_bit_str_ fail", KR(ret), K(obj), K(target_type)); } } } OBLOG_LOG(DEBUG, "[HBASE]", KR(ret), K(obj), K(obj_type), K(enable_hbase_mode_), K(enable_backup_mode_), K(str_obj), K(is_hbase_table_T_column), K(table_id)); } } // OB_SUCC(ret) } // If it is a LOB, larger than 2M, do not print the contents, print the address and length // Avoid printing the log taking too long if (str.length() > 2 * _M_) { OBLOG_LOG(DEBUG, "obj2str", KR(ret), K(obj_type), K(obj.get_scale()), K(obj.get_meta()), K(obj_tc), K(accuracy), K(collation_type), KP(obj.get_string().ptr()), K(obj.get_string().length()), KP(str.ptr()), K(str.length())); } else { OBLOG_LOG(DEBUG, "obj2str", KR(ret), K(obj_type), K(obj.get_scale()), K(obj.get_meta()), K(obj_tc), K(accuracy), K(collation_type), K(obj), K(str), K(str.length())); } return ret; } int ObObj2strHelper::convert_timestamp_with_timezone_data_util_succ_(const common::ObObjType &target_type, common::ObObjCastParams &cast_param, const common::ObObj &in_obj, common::ObObj &str_obj, common::ObString &str, const uint64_t tenant_id) { int ret = OB_SUCCESS; bool done = false; ObTZInfoMap *tz_info_map = NULL; if (OB_ISNULL(timezone_info_getter_)) { OBLOG_LOG(ERROR, "timezone_info_getter_ is null", K(timezone_info_getter_)); ret = OB_ERR_UNEXPECTED; } else if (OB_FAIL(tenant_mgr_->get_tenant_tz_map(tenant_id, tz_info_map))) { OBLOG_LOG(ERROR, "get_tenant_tz_map failed", KR(ret), K(tenant_id)); } else { while (! done && OB_SUCCESS == ret) { if (OB_FAIL(timezone_info_getter_->fetch_tenant_timezone_info_util_succ(tenant_id, tz_info_map))) { OBLOG_LOG(ERROR, "fetch_tenant_timezone_info_util_succ fail", KR(ret), K(tenant_id)); } else if (OB_FAIL(ObObjCaster::to_type(target_type, cast_param, in_obj, str_obj))) { if (OB_ERR_INVALID_TIMEZONE_REGION_ID == ret) { OBLOG_LOG(WARN, "cast obj to varchar type fail, try again", KR(ret), K(in_obj), K(target_type)); } else { OBLOG_LOG(ERROR, "cast obj to varchar type fail", KR(ret), K(in_obj), K(target_type)); } } else { done = true; if (OB_FAIL(str_obj.get_string(str))) { OBLOG_LOG(ERROR, "get_string from ObObj fail", KR(ret), K(str_obj)); } } if (OB_ERR_INVALID_TIMEZONE_REGION_ID == ret) { ret = OB_SUCCESS; usleep(10L * 1000L); } } } return ret; } // bit type output decimal string int ObObj2strHelper::convert_bit_obj_to_decimal_str_(const common::ObObj &obj, const common::ObObj &str_obj, common::ObString &str, common::ObIAllocator &allocator) { int ret = OB_SUCCESS; uint64_t value = 0; if (OB_FAIL(str_obj.get_uint64(value))) { OBLOG_LOG(ERROR, "get_uint64 from ObObj fail", KR(ret), K(obj), K(str_obj), K(value)); } else { char buf[MAX_BIT_DECIMAL_STR_LENGTH]; int64_t pos = 0; char *ptr = NULL; if (OB_FAIL(common::databuff_printf(buf, MAX_BIT_DECIMAL_STR_LENGTH, pos, "%lu", value))) { OBLOG_LOG(ERROR, "databuff_printf fail", K(pos), K(value)); } else if (OB_ISNULL(ptr = (char *)allocator.alloc(pos))) { OBLOG_LOG(ERROR, "allocate memory fail", "size", pos); ret = common::OB_ALLOCATE_MEMORY_FAILED; } else { (void)MEMCPY(ptr, buf, pos); str.assign_ptr(ptr, (int32_t)pos); OBLOG_LOG(DEBUG, "obj2str cast bit type", K(obj), "cast_str", str, K(value)); } } return ret; } int ObObj2strHelper::convert_hbase_bit_obj_to_positive_bit_str_(const common::ObObj &obj, const common::ObObj ¤t_str_obj, common::ObString &str, common::ObIAllocator &allocator) { int ret = OB_SUCCESS; UNUSED(allocator); ObString current_str; const char *current_str_ptr = NULL; ObString::obstr_size_t current_str_len = 0; if (OB_FAIL(current_str_obj.get_string(current_str))) { OBLOG_LOG(ERROR, "get_string from ObObj fail", KR(ret), K(obj), K(current_str_obj), K(current_str)); } else if (OB_ISNULL(current_str_ptr = current_str.ptr())) { OBLOG_LOG(ERROR, "current_str_ptr is null", K(obj), K(current_str_obj), K(current_str), K(current_str_ptr)); ret = OB_ERR_UNEXPECTED; } else { current_str_len = current_str.length(); OBLOG_LOG(DEBUG, "[HBASE]", K(obj), K(current_str_obj), K(current_str), K(current_str_ptr), K(current_str_len)); if ('-' == current_str_ptr[0]) { str.assign_ptr(current_str_ptr + 1, (int32_t)(current_str_len - 1)); } } return ret; } int ObObj2strHelper::convert_mysql_timestamp_to_utc_(const common::ObObj &obj, common::ObString &str, common::ObIAllocator &allocator) { int ret = OB_SUCCESS; const int64_t utc_time = obj.get_timestamp(); char buf[MAX_TIMESTAMP_UTC_LONG_STR_LENGTH]; int64_t pos = 0; char *ptr = NULL; // external output of utc integer time, compatible with mysql, splitting seconds and microseconds with a decimal point // Microsecond precision length of 6 const int64_t usec_mod_val = 1000000; if (OB_FAIL(common::databuff_printf(buf, MAX_TIMESTAMP_UTC_LONG_STR_LENGTH, pos, "%ld.%06ld", utc_time / usec_mod_val, utc_time % usec_mod_val))) { OBLOG_LOG(ERROR, "databuff_printf fail", K(pos), K(utc_time), K(usec_mod_val)); } else if (OB_ISNULL(ptr = (char *)allocator.alloc(pos))) { OBLOG_LOG(ERROR, "allocate memory fail", "size", pos); ret = common::OB_ALLOCATE_MEMORY_FAILED; } else { (void)MEMCPY(ptr, buf, pos); str.assign_ptr(ptr, (int32_t)pos); OBLOG_LOG(DEBUG, "obj2str cast timestamp type to utc long", K(obj), "cast_str", str, K(utc_time)); } return ret; } bool ObObj2strHelper::need_padding_(const ObWorker::CompatMode &compat_mode, const common::ObObj &obj) const { bool bool_ret = false; bool_ret = (ObWorker::CompatMode::ORACLE == compat_mode) && (obj.is_char() || obj.is_nchar()); return bool_ret; } int ObObj2strHelper::convert_char_obj_to_padding_obj_(const ObWorker::CompatMode &compat_mode, const common::ObObj &obj, const common::ObAccuracy &accuracy, const common::ObCollationType &collation_type, common::ObIAllocator &allocator, common::ObString &str) { int ret = OB_SUCCESS; int32_t char_len = 0; if (OB_FAIL(obj.get_string(str))) { OBLOG_LOG(ERROR, "get_string from ObObj fail", KR(ret), K(obj)); } else if (OB_FAIL(obj.get_char_length(accuracy, char_len, ObWorker::CompatMode::ORACLE == compat_mode))) { OBLOG_LOG(ERROR, "obj get_char_length fail", KR(ret), K(accuracy), K(char_len)); } else { // The calculation of padding here needs to be based on char_len, not str.length // e.g. nchar, 'a', str,length=2, not 1 const int64_t padding_cnt = accuracy.get_length() - char_len; // need pad if (padding_cnt > 0) { ObString padding_res; if (OB_FAIL(sql::padding_char_for_cast(padding_cnt, collation_type, allocator, padding_res))) { OBLOG_LOG(ERROR, "padding_char_for_cast fail", KR(ret), K(obj), K(accuracy), K(collation_type), K(padding_res)); } else { int64_t all_size = padding_res.length() + str.length(); char *res_ptr = static_cast(allocator.alloc(all_size)); if (OB_ISNULL(res_ptr)) { OBLOG_LOG(ERROR, "allocate memory failed", KR(ret)); ret = OB_ALLOCATE_MEMORY_FAILED; } else { MEMMOVE(res_ptr, str.ptr(), str.length()); MEMMOVE(res_ptr + str.length(), padding_res.ptr(), padding_res.length()); str.assign_ptr(res_ptr, static_cast(all_size)); OBLOG_LOG(DEBUG, "obj2str cast char/nchar type", K(obj), "cast_str", str, "cast_str_len", str.length(), K(padding_cnt), K(padding_res), "padding_res_len", padding_res.length(), K(accuracy), K(collation_type), K(char_len)); } } } // if (padding_cnt > 0) } return ret; } } // namespace liboblog } // namespace oceanbase