From 3e718cd2a2fccdc938577beee9c514d07d6191e4 Mon Sep 17 00:00:00 2001 From: Cai-Yao <729673078@qq.com> Date: Thu, 20 Feb 2025 12:45:40 +0000 Subject: [PATCH] fix bug in cast decimalint to int --- .../expr/vector_cast/cast_impl_helper.ipp | 88 +++++++++++++++---- .../engine/expr/vector_cast/cast_to_date.ipp | 3 +- .../engine/expr/vector_cast/cast_to_int.ipp | 31 +++++-- 3 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/sql/engine/expr/vector_cast/cast_impl_helper.ipp b/src/sql/engine/expr/vector_cast/cast_impl_helper.ipp index 75af9327b7..4f6966786f 100644 --- a/src/sql/engine/expr/vector_cast/cast_impl_helper.ipp +++ b/src/sql/engine/expr/vector_cast/cast_impl_helper.ipp @@ -162,41 +162,62 @@ public: /// 3. that is, when dividing, scale_factor should be uint64_t. ///@param [in] scale >= 0, negative scale for decimalint is illegal template::type> - static inline int64_t scale_to_integer(T &lhs, + static inline int64_t scale_to_integer(const T &lhs, scale_type &scale_factor, int16_t scale, + int &err, bool round_up = true) { - uint64_t sf = static_cast(scale_factor); - int64_t q = lhs; + T q = lhs; + int64_t sf = static_cast(scale_factor); + err = 0; if (OB_LIKELY(sf > 1)) { bool is_neg = lhs < 0; if (is_neg) { q = -lhs; } - int64_t r = q % sf; + T r = q % sf; q = q / sf; round_up &= (r >= (sf>>1)); if (round_up) { q = q + 1; } if (is_neg) { q = -q; } } - return q; + int64_t out_val = 0; + if (OB_UNLIKELY(q > INT64_MAX)) { + out_val = INT64_MAX; + err = OB_ERRNO_ERANGE; + } else if (OB_UNLIKELY(q < INT64_MIN)) { + out_val = INT64_MIN; + err = OB_ERRNO_ERANGE; + } else { + out_val = q; + } + return out_val; } template static inline int64_t scale_to_integer(const wide::ObWideInteger &lhs, const scale_type &scale_factor, int16_t scale, + int &err, bool round_up = true) { int64_t q = 0; int64_t decint = static_cast(lhs); if (OB_UNLIKELY(scale == 0)) { - q = (lhs < INT64_MIN) ? INT64_MIN : (lhs > INT64_MAX ? INT64_MAX : decint); + if (OB_UNLIKELY(lhs > INT64_MAX)) { + q = INT64_MAX; + err = OB_ERRNO_ERANGE; + } else if (OB_UNLIKELY(lhs < INT64_MIN)) { + q = INT64_MIN; + err = OB_ERRNO_ERANGE; + } else { + q = decint; + } } else if (OB_LIKELY(INT64_MIN <= lhs && lhs <= INT64_MAX)) { uint64_t sf = pow10(scale); - q = scale_to_integer(decint, sf, scale, round_up); + q = scale_to_integer(decint, sf, scale, err, round_up); } else { static const uint64_t constexpr DIGITS10_BASE = 10000000000000000000ULL; // 10^19 if (scale > 19) { wide::ObWideInteger num = lhs / DIGITS10_BASE; scale_type sf = get_scale_factor(scale - 19); - q = scale_to_integer(num, sf, scale - 19, round_up); + q = scale_to_integer(num, sf, scale - 19, err, round_up); } else { bool is_neg = (lhs < 0); wide::ObWideInteger num = (is_neg ? -lhs : lhs); @@ -208,7 +229,15 @@ public: if (round_up) { num = num + 1; } if (is_neg) { num = -num; } q = static_cast(num); - q = (num < INT64_MIN) ? INT64_MIN : (num > INT64_MAX ? INT64_MAX : q); + if (OB_UNLIKELY(num > INT64_MAX)) { + q = INT64_MAX; + err = OB_ERRNO_ERANGE; + } else if (OB_UNLIKELY(num < INT64_MIN)) { + q = INT64_MIN; + err = OB_ERRNO_ERANGE; + } else { + q = static_cast(num); + } } } return q; @@ -218,36 +247,51 @@ public: static inline uint64_t scale_to_unsigned_integer(T &lhs, scale_type &scale_factor, int16_t scale, + int &err, bool round_up = true) { - uint64_t sf = static_cast(scale_factor); - uint64_t q = lhs; + T q = lhs; + uint64_t sf = static_cast(scale_factor); + err = 0; if (OB_LIKELY(sf > 1)) { int64_t r = q % sf; q = q / sf; round_up &= (r >= (sf>>1)); if (round_up) { q = q + 1; } } - return q; + uint64_t out_val = 0; + if (OB_UNLIKELY(q > UINT64_MAX)) { + out_val = INT64_MAX; + err = OB_ERRNO_ERANGE; + } else { + out_val = q; + } + return out_val; } template static inline uint64_t scale_to_unsigned_integer(const wide::ObWideInteger &lhs, const scale_type &scale_factor, int16_t scale, + int &err, bool round_up = true) { uint64_t q = 0; uint64_t decint = static_cast(lhs); if (OB_UNLIKELY(scale == 0)) { - q = (lhs > UINT64_MAX ? UINT64_MAX : decint); + if (OB_UNLIKELY(lhs > UINT64_MAX)) { + q = UINT64_MAX; + err = OB_ERRNO_ERANGE; + } else { + q = decint; + } } else if (OB_LIKELY(lhs <= UINT64_MAX)) { uint64_t sf = pow10(scale); - q = scale_to_unsigned_integer(decint, sf, scale, round_up); + q = scale_to_unsigned_integer(decint, sf, scale, err, round_up); } else { static const uint64_t constexpr DIGITS10_BASE = 10000000000000000000ULL; // 10^19 wide::ObWideInteger num; if (scale > 19) { num = lhs / DIGITS10_BASE; scale_type sf = get_scale_factor(scale - 19); - q = scale_to_unsigned_integer(num, sf, scale - 19, round_up); + q = scale_to_unsigned_integer(num, sf, scale - 19, err, round_up); } else { wide::ObWideInteger remain; uint64_t sf = pow10(scale); @@ -255,7 +299,12 @@ public: divide(lhs, sf, num, remain); round_up &= (remain >= (sf >> 1)); if (round_up) { num = num + 1; } - q = (num > UINT64_MAX ? UINT64_MAX : static_cast(num)); + if (OB_UNLIKELY(num > UINT64_MAX)) { + q = UINT64_MAX; + err = OB_ERRNO_ERANGE; + } else { + q = static_cast(num); + } } } return q; @@ -303,6 +352,7 @@ public: int64_t &int_part, int64_t &dec_part) { int ret = OB_SUCCESS; + int err = 0; if (scale < 6) { // 20010528.1430 -> 20010528, 143000 000 if (OB_UNLIKELY(decint > INT64_MAX)) { ret = OB_INVALID_DATE_VALUE; @@ -315,14 +365,14 @@ public: } } else { // 20010528.1430307654321 -> 20010528, 143031 000 calc_type sf = get_scale_factor(scale); - int_part = CastHelperImpl::scale_to_integer(decint, sf, scale, false); + int_part = CastHelperImpl::scale_to_integer(decint, sf, scale, err, false); calc_type sf_minus6 = get_scale_factor(scale - 6); if (scale < 19) { int64_t remain = decint - sf * int_part; - dec_part = CastHelperImpl::scale_to_integer(remain, sf_minus6, scale - 6); + dec_part = CastHelperImpl::scale_to_integer(remain, sf_minus6, scale - 6, err); } else { calc_type remain = decint - sf * int_part; - dec_part = CastHelperImpl::scale_to_integer(remain, sf_minus6, scale - 6); + dec_part = CastHelperImpl::scale_to_integer(remain, sf_minus6, scale - 6, err); } dec_part *= 1000; } diff --git a/src/sql/engine/expr/vector_cast/cast_to_date.ipp b/src/sql/engine/expr/vector_cast/cast_to_date.ipp index 1215b6db58..77b29736fb 100644 --- a/src/sql/engine/expr/vector_cast/cast_to_date.ipp +++ b/src/sql/engine/expr/vector_cast/cast_to_date.ipp @@ -192,7 +192,8 @@ struct ToDateCastImpl ret = OB_INVALID_DATE_VALUE; SQL_LOG(WARN, "invalid date value", K(ret), K(lhs)); } else { - uint64_t uint64 = CastHelperImpl::scale_to_unsigned_integer(lhs, sf_, in_scale_, false); + int err = 0; + uint64_t uint64 = CastHelperImpl::scale_to_unsigned_integer(lhs, sf_, in_scale_, err, false); int64_t int64 = uint64; if (OB_UNLIKELY(uint64 > INT64_MAX)) { int64 = INT64_MAX; diff --git a/src/sql/engine/expr/vector_cast/cast_to_int.ipp b/src/sql/engine/expr/vector_cast/cast_to_int.ipp index ff07f904f8..ed154017ba 100644 --- a/src/sql/engine/expr/vector_cast/cast_to_int.ipp +++ b/src/sql/engine/expr/vector_cast/cast_to_int.ipp @@ -486,7 +486,7 @@ struct ToIntegerCastImpl } else { OUT_TYPE out_val = int_val; bool need_range_check = std::is_same::value - ? out_type_ < ObInt32Type : out_type_ < ObUInt32Type; + ? out_type_ < ObIntType : out_type_ < ObUInt64Type; if (need_range_check && CAST_FAIL(numeric_range_check(out_val, type_min_val_, type_max_val_, out_val))) { SQL_LOG(WARN, "int_range_check failed", K(ret)); @@ -534,9 +534,15 @@ struct ToIntegerCastImpl { int ret = OB_SUCCESS; IN_TYPE lhs = *reinterpret_cast(arg_vec_->get_payload(idx)); - int64_t out_val = CastHelperImpl::scale_to_integer(lhs, sf_, in_scale_); int warning = OB_SUCCESS; - if (out_type_ < ObIntType && CAST_FAIL(int_range_check(out_type_, out_val, out_val))) { + int err = 0; + int64_t out_val = CastHelperImpl::scale_to_integer(lhs, sf_, in_scale_, err); + if (OB_ERRNO_ERANGE == err && (INT64_MIN == out_val || INT64_MAX == out_val)) { + ret = OB_DATA_OUT_OF_RANGE; + } + if (CAST_FAIL(ret)) { + SQL_LOG(WARN, "decimalint int failed", K(ret)); + } else if (out_type_ < ObIntType && CAST_FAIL(int_range_check(out_type_, out_val, out_val))) { SQL_LOG(WARN, "int_range_check failed", K(ret), K(expr.extra_)); } else { res_vec_->set_int(idx, out_val); @@ -577,11 +583,22 @@ struct ToIntegerCastImpl OB_INLINE int operator() (const ObExpr &expr, int idx) { int ret = OB_SUCCESS; - IN_TYPE lhs = *reinterpret_cast(arg_vec_->get_payload(idx)); - uint64_t out_val = (lhs < 0) ? 0 : - CastHelperImpl::scale_to_unsigned_integer(lhs, sf_, in_scale_); int warning = OB_SUCCESS; - if ((out_type_ < ObUInt64Type && CM_NEED_RANGE_CHECK(expr.extra_)) + int err = 0; + IN_TYPE lhs = *reinterpret_cast(arg_vec_->get_payload(idx)); + uint64_t out_val = 0; + if (OB_UNLIKELY(lhs < 0)) { + out_val = 0; + ret = OB_DATA_OUT_OF_RANGE; + } else { + out_val = CastHelperImpl::scale_to_unsigned_integer(lhs, sf_, in_scale_, err); + } + if (OB_ERRNO_ERANGE == err && (0 == out_val || UINT64_MAX == out_val)) { + ret = OB_DATA_OUT_OF_RANGE; + } + if (CAST_FAIL(ret)) { + SQL_LOG(WARN, "decimalint uint failed", K(ret)); + } else if ((out_type_ < ObUInt64Type && CM_NEED_RANGE_CHECK(expr.extra_)) && CAST_FAIL(uint_upper_check(out_type_, out_val))) { SQL_LOG(WARN, "int_range_check failed", K(ret), K(expr.extra_)); } else {