/** * Copyright (c) 2021 OceanBase * OceanBase Database Proxy(ODP) 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_COMMON_OB_OBJ_CAST_ #define OCEANBASE_COMMON_OB_OBJ_CAST_ #include "common/ob_object.h" #include "common/ob_accuracy.h" #include "common/ob_zerofill_info.h" #include "lib/timezone/ob_timezone_info.h" #include "lib/timezone/ob_time_convert.h" #include "lib/charset/ob_charset.h" namespace oceanbase { namespace common { #define DOUBLE_TRUE_VALUE_THRESHOLD (1e-50) #define OB_IS_DOUBLE_ZERO(d) (d < DOUBLE_TRUE_VALUE_THRESHOLD && d > -DOUBLE_TRUE_VALUE_THRESHOLD) #define OB_IS_DOUBLE_NOT_ZERO(d) (d >= DOUBLE_TRUE_VALUE_THRESHOLD || d <= -DOUBLE_TRUE_VALUE_THRESHOLD) #define CM_NONE (0ULL) #define CM_WARN_ON_FAIL (1ULL << 0) // treat fail as warn, otherwise // treat fail as error (set to ret). #define CM_NULL_ON_WARN (1ULL << 1) // return null value when warned, otherwise // return zero value. // only for non-numeric types. #define CM_NO_RANGE_CHECK (1ULL << 2) // no range check when cast to numeric type, otherwise // do range check. #define CM_NO_CAST_INT_UINT (1ULL << 3) // no cast between int and uint, otherwise // do cast between int and uint. #define CM_INSERT_UPDATE_SCOPE (1ULL << 62) // affect calculate values() function. return the insert values // otherwise return NULL; #define CM_INTERNAL_CALL (1ULL << 63) // is internal call, otherwise // is external call. typedef uint64_t ObCastMode; #define CM_IS_WARN_ON_FAIL(mode) (CM_WARN_ON_FAIL & (mode)) #define CM_IS_ERROR_ON_FAIL(mode) (!CM_IS_WARN_ON_FAIL(mode)) #define CM_SET_WARN_ON_FAIL(mode) (CM_WARN_ON_FAIL | (mode)) #define CM_IS_NULL_ON_WARN(mode) (CM_NULL_ON_WARN & (mode)) #define CM_IS_ZERO_ON_WARN(mode) (!CM_IS_NULL_ON_WARN(mode)) #define CM_SKIP_RANGE_CHECK(mode) (CM_NO_RANGE_CHECK & (mode)) #define CM_NEED_RANGE_CHECK(mode) (!CM_SKIP_RANGE_CHECK(mode)) #define CM_SKIP_CAST_INT_UINT(mode) (CM_NO_CAST_INT_UINT & (mode)) #define CM_NEED_CAST_INT_UINT(mode) (!CM_SKIP_CAST_INT_UINT(mode)) #define CM_UNSET_NO_CAST_INT_UINT(mode) (~CM_NO_CAST_INT_UINT & (mode)) #define CM_IS_INTERNAL_CALL(mode) (CM_INTERNAL_CALL & (mode)) #define CM_IS_EXTERNAL_CALL(mode) (!CM_IS_INTERNAL_CALL(mode)) struct ObObjCastParams { // add params when necessary DEFINE_ALLOCATOR_WRAPPER ObObjCastParams() : allocator_(NULL), allocator_v2_(NULL), tz_info_(NULL), cur_time_(0), cast_mode_(CM_NONE), warning_(OB_SUCCESS), zf_info_(NULL), connection_collation_(CS_TYPE_INVALID), res_accuracy_(NULL) {} ObObjCastParams(ObIAllocator *allocator_v2, const ObTimeZoneInfo *tz_info, ObCastMode cast_mode, ObCollationType connection_collation, ObAccuracy *res_accuracy = NULL) : allocator_(NULL), allocator_v2_(allocator_v2), tz_info_(tz_info), cur_time_(0), cast_mode_(cast_mode), warning_(OB_SUCCESS), zf_info_(NULL), connection_collation_(connection_collation), res_accuracy_(res_accuracy) {} ObObjCastParams(ObIAllocator *allocator_v2, const ObTimeZoneInfo *tz_info, int64_t cur_time, ObCastMode cast_mode, ObCollationType connection_collation, const ObZerofillInfo *zf_info = NULL, ObAccuracy *res_accuracy = NULL) : allocator_(NULL), allocator_v2_(allocator_v2), tz_info_(tz_info), cur_time_(cur_time), cast_mode_(cast_mode), warning_(OB_SUCCESS), zf_info_(zf_info), connection_collation_(connection_collation), res_accuracy_(res_accuracy) {} void *alloc(const int64_t size) const { void *ret = NULL; if (NULL != allocator_v2_) { ret = allocator_v2_->alloc(size); } else if (NULL != allocator_) { ret = allocator_->alloc(size); } return ret; } IAllocator *allocator_; ObIAllocator *allocator_v2_; const ObTimeZoneInfo *tz_info_; int64_t cur_time_; ObCastMode cast_mode_; int warning_; const ObZerofillInfo *zf_info_; ObCollationType connection_collation_; ObAccuracy *res_accuracy_; ObCollationType dest_collation_; }; /** * cast functions to do the real work * cast the input object to the type specified and store the result in out_obj * * @note in_obj and out_obj could be the same object, the cast function should consider and support it. * @param params [in] other cast params, e.g. allocator * @param in_obj [in] input object * @param out_tpye [in] output object tyoe * @param out_obj [out] output object * * @return ob errno */ typedef int (*ObObjCastFunc)(ObObjType expect_type, ObObjCastParams ¶ms, const ObObj &in_obj, ObObj &out_obj, const ObCastMode cast_mode); //////////////////////////////////////////////////////////////// class ObBufAllocator : public ObObjCastParams::IAllocator { public: ObBufAllocator() : buf_(NULL), buf_size_(0), used_buf_len_(const_cast(buf_size_)) {} ObBufAllocator(char *buf, const int64_t buf_size, int64_t &used_buf_len) : buf_(buf), buf_size_(buf_size), used_buf_len_(used_buf_len) {} void *alloc(const int64_t size) { char *ret = NULL; if (OB_LIKELY((NULL != buf_) && (used_buf_len_ + size) <= buf_size_)) { ret = buf_ + used_buf_len_; used_buf_len_ += size; } return ret; } private: // disallow copy DISALLOW_COPY_AND_ASSIGN(ObBufAllocator); private: char *const buf_; const int64_t buf_size_; int64_t &used_buf_len_; }; class ObStrAllocator : public ObObjCastParams::IAllocator { public: ObStrAllocator() : pos_(0), ba_() {} explicit ObStrAllocator(ObString &str) : pos_(0), ba_(str.ptr(), (0 != str.length()) ? str.length() : str.size(), pos_) {} void *alloc(const int64_t size) { return ba_.alloc(size); } private: // disallow copy DISALLOW_COPY_AND_ASSIGN(ObStrAllocator); private: int64_t pos_; ObBufAllocator ba_; }; class ObDummyAllocator : public ObObjCastParams::IAllocator { public: ObDummyAllocator() {} void *alloc(const int64_t size) { UNUSED(size); return NULL; } private: // disallow copy DISALLOW_COPY_AND_ASSIGN(ObDummyAllocator); }; enum AllocatorSource { FromNull = 0, FromBuffer = 1, FromString = 2, FromAllocator = 3, }; template class ObObjCaster { template struct AllocatorTraits { }; template struct AllocatorTraits { typedef ObDummyAllocator AllocatorType; }; template struct AllocatorTraits { typedef ObBufAllocator AllocatorType; }; template struct AllocatorTraits { typedef ObStrAllocator AllocatorType; }; template struct AllocatorTraits { typedef AllocatorTmpl_ AllocatorType; }; typedef typename AllocatorTraits::AllocatorType Allocator; public: ObObjCaster(char *buf, const int64_t buf_size, int64_t &used_buf_len) : ba_(buf, buf_size, used_buf_len), sa_(), da_(), allocator_(ba_) {} explicit ObObjCaster(ObString &str) : ba_(), sa_(str), da_(), allocator_(sa_) {} ObObjCaster() : ba_(), sa_(), da_(), allocator_(da_) {} explicit ObObjCaster(Allocator &allocator) : ba_(), sa_(), da_(), allocator_(allocator) {} ~ObObjCaster() {} public: int expr_obj_cast(const ObObjType orig_type, const ObObjType expect_type, const ObTimeZoneInfo *tz_info, const ObObj &in, ObObj &out); int obj_cast(const ObObj &orig_cell, const ObObjType expect_type, const ObTimeZoneInfo *tz_info, ObObj &casted_cell, const ObObj *&res_cell); public: int expr_obj_cast(const ObObjTypeClass orig_tc, ObObjTypeClass expect_tc, const ObObjType expect_type, const ObTimeZoneInfo *tz_info, const ObObj &in, ObObj &out); private: // disallow copy DISALLOW_COPY_AND_ASSIGN(ObObjCaster); private: ObBufAllocator ba_; ObStrAllocator sa_; ObDummyAllocator da_; Allocator &allocator_; }; template int ObObjCaster::expr_obj_cast(const ObObjType orig_type, ObObjType expect_type, const ObTimeZoneInfo *tz_info, const ObObj &in, ObObj &out) { const ObObjTypeClass orig_tc = ob_obj_type_class(orig_type); ObObjTypeClass expect_tc = ob_obj_type_class(expect_type); return expr_obj_cast(orig_tc, expect_tc, expect_type, tz_info, in, out); } template int ObObjCaster::expr_obj_cast(const ObObjTypeClass orig_tc, ObObjTypeClass expect_tc, // maybe we have only tc, no type, like relation promotion. ObObjType expect_type, const ObTimeZoneInfo *tz_info, const ObObj &in, ObObj &out) { int ret = OB_SUCCESS; if (OB_UNLIKELY(expect_tc >= ObMaxTC)) { expect_tc = ob_obj_type_class(expect_type); } else if (OB_UNLIKELY(expect_type >= ObMaxType)) { expect_type = ob_obj_default_type(expect_tc); } if (OB_UNLIKELY(in.is_null())) { out.set_null(); } else if (OB_UNLIKELY(ob_is_invalid_obj_tc(orig_tc) || ob_is_invalid_obj_tc(expect_tc) || ob_is_invalid_obj_type(expect_type) || expect_tc != ob_obj_type_class(expect_type))) { ret = OB_ERR_UNEXPECTED; COMMON_LOG(WARN, "invalid argument(s)", K(ret), K(orig_tc), K(expect_tc), K(expect_type)); } else { char varchar_buf[OB_CAST_TO_VARCHAR_MAX_LENGTH]; ObObj to; if (OB_UNLIKELY(ObStringTC == expect_tc)) { to.set_varchar(varchar_buf, OB_CAST_TO_VARCHAR_MAX_LENGTH); } ObObjCastParams::TAllocator ta(allocator_); ObObjCastParams params; params.allocator_ = &ta; params.tz_info_ = tz_info; params.cast_mode_ = CM_WARN_ON_FAIL; int warning = OB_SUCCESS; extern ObObjCastFunc OB_OBJ_CAST[ObMaxTC][ObMaxTC]; if (OB_FAIL(OB_OBJ_CAST[orig_tc][expect_tc](expect_type, params, in, to, warning))) { COMMON_LOG(WARN, "failed to type cast obj", K(ret), K(in), K(orig_tc), K(expect_tc)); } else if (OB_SUCCESS != warning) { ret = warning; COMMON_LOG(WARN, "failed to cast obj to expect type", K(ret), K(in), K(expect_type)); } out = to; if (ObStringTC == orig_tc && ObStringTC == expect_tc && in.get_collation_type() != CS_TYPE_INVALID) { out.set_collation_type(in.get_collation_type()); //set same collation type } else if (ObStringTC == expect_tc) { if (REACH_TIME_INTERVAL(10 * 1000 * 1000)) { BACKTRACE(WARN, true, "invalid collation type: %s", to_cstring(in)); } out.set_collation_type(ObCharset::get_default_collation(ObCharset::get_default_charset())); } } return ret; } template int ObObjCaster::obj_cast( const ObObj &orig_cell, const ObObjType expect_type, const ObTimeZoneInfo *tz_info, ObObj &casted_cell, const ObObj *&res_cell) { int ret = OB_SUCCESS; res_cell = NULL; if (OB_LIKELY(orig_cell.get_type() != expect_type)) { ret = expr_obj_cast(orig_cell.get_type(), expect_type, tz_info, orig_cell, casted_cell); res_cell = &casted_cell; // we need not see whether ret is not OB_SUCCESS, because without strict sql mode, // ER_WARN_DATA_OUT_OF_RANGE is allowed, and there is no following operation in // this func. } else { res_cell = &orig_cell; } return ret; } /** * cast obj to the type * utility functions which wrapper ObObjCaster * @param from [in] * @param to_type [in] expected type * @param to [out] * @param allocator [in] * * @return */ template int ob_obj_cast_to(const ObObj &from, const ObObjType to_type, ObObj &to, const ObTimeZoneInfo *tz_info, Allocator &allocator) { int ret = OB_SUCCESS; if (OB_UNLIKELY(to_type == from.get_type())) { if (&from != &to) { to = from; } } else { ObObjCaster caster(allocator); ret = caster.expr_obj_cast(from.get_type(), to_type, tz_info, from, to); } return ret; } template int ob_obj_cast_to(ObObj &obj, const ObObjType to_type, const ObTimeZoneInfo *tz_info, Allocator &allocator) { int ret = OB_SUCCESS; if (OB_LIKELY(to_type != obj.get_type())) { ObObjCaster caster(allocator); ret = caster.expr_obj_cast(obj.get_type(), to_type, tz_info, obj, obj); } return ret; } // whether the cast is supported bool cast_supported(const ObObjTypeClass orig_td, const ObObjTypeClass expect_td); int ob_obj_to_ob_time_with_date(const ObObj &obj, const ObTimeZoneInfo *tz_info, ObTime &ob_time); int ob_obj_to_ob_time_without_date(const ObObj &obj, const ObTimeZoneInfo *tz_info, ObTime &ob_time); //============================== typedef ObObjCastParams ObCastCtx; int obj_collation_check(const bool is_strict_mode, const ObCollationType cs_type, ObObj &obj); int obj_accuracy_check(ObCastCtx &cast_ctx, const ObAccuracy &accuracy, const ObCollationType cs_type, const ObObj &obj, ObObj &buf_obj, const ObObj *&res_obj); class ObObjCasterV2 { public: static int to_type(const ObObjType expect_type, ObCastCtx &cast_ctx, const ObObj &in_obj, ObObj &buf_obj, const ObObj *&out_obj); static int to_type(const ObObjType expect_type, ObCastCtx &cast_ctx, const ObObj &in_obj, ObObj &out_obj); static int to_type(const ObObjType expect_type, const ObCollationType expect_cs_type, ObCastCtx &cast_ctx, const ObObj &in_obj, ObObj &out_obj); static int is_cast_monotonic(ObObjType t1, ObObjType t2, bool &is_monotonic); static int is_order_consistent(const ObObjMeta &from, const ObObjMeta &to, bool &result); static int is_injection(const ObObjMeta &from, const ObObjMeta &to, bool &result); private: inline static int64_t get_idx_of_collate(ObCollationType cs_type) { int64_t idx = -1; if (CS_TYPE_UTF8MB4_GENERAL_CI == cs_type) { idx = 0; } else if (CS_TYPE_UTF8MB4_BIN == cs_type) { idx = 1; } else if (CS_TYPE_BINARY == cs_type) { idx = 2; } return idx; } private: static const bool CAST_MONOTONIC[ObMaxTC][ObMaxTC]; static const bool ORDER_CONSISTENT[ObMaxTC][ObMaxTC]; static const bool ORDER_CONSISTENT_WITH_BOTH_STRING[ObCharset::VALID_COLLATION_TYPES][ObCharset::VALID_COLLATION_TYPES][ObCharset::VALID_COLLATION_TYPES]; static const bool INJECTION[ObMaxTC][ObMaxTC]; static const bool INJECTION_WITH_BOTH_STRING[ObCharset::VALID_COLLATION_TYPES][ObCharset::VALID_COLLATION_TYPES][ObCharset::VALID_COLLATION_TYPES]; }; } // end namespace common } // end namespace oceanbase #endif //OCEANBASE_COMMON_OB_OBJ_CAST_