This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 8e06f4ad34b [improvement](decimal) improve overflow error message (#34689) (#36078) 8e06f4ad34b is described below commit 8e06f4ad34b33990fa6d4502e0d0a59b296778ab Author: Mingyu Chen <morning...@163.com> AuthorDate: Sun Jun 9 20:29:38 2024 +0800 [improvement](decimal) improve overflow error message (#34689) (#36078) bp #34689 Co-authored-by: TengJianPing <18241664+jackte...@users.noreply.github.com> --- be/src/vec/data_types/data_type_date.h | 6 + be/src/vec/data_types/data_type_date_time.cpp | 8 ++ be/src/vec/data_types/data_type_date_time.h | 1 + be/src/vec/data_types/data_type_decimal.cpp | 5 + be/src/vec/data_types/data_type_decimal.h | 131 +++++++++--------- be/src/vec/data_types/data_type_ipv4.cpp | 5 + be/src/vec/data_types/data_type_ipv4.h | 1 + be/src/vec/data_types/data_type_ipv6.cpp | 4 + be/src/vec/data_types/data_type_ipv6.h | 1 + be/src/vec/data_types/data_type_number_base.cpp | 13 ++ be/src/vec/data_types/data_type_number_base.h | 1 + be/src/vec/data_types/data_type_time.cpp | 6 + be/src/vec/data_types/data_type_time.h | 2 + be/src/vec/data_types/data_type_time_v2.cpp | 17 +++ be/src/vec/data_types/data_type_time_v2.h | 2 + be/src/vec/functions/function_binary_arithmetic.h | 156 +++++++++++++++------- be/src/vec/functions/function_cast.h | 10 +- be/src/vec/functions/multiply.cpp | 10 +- 18 files changed, 264 insertions(+), 115 deletions(-) diff --git a/be/src/vec/data_types/data_type_date.h b/be/src/vec/data_types/data_type_date.h index 1ae78678bdc..56f74124d90 100644 --- a/be/src/vec/data_types/data_type_date.h +++ b/be/src/vec/data_types/data_type_date.h @@ -60,6 +60,12 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(Int64 int_val) const { + doris::VecDateTimeValue value = binary_cast<Int64, doris::VecDateTimeValue>(int_val); + char buf[64]; + value.to_string(buf); + return buf; + } Status from_string(ReadBuffer& rb, IColumn* column) const override; static void cast_to_date(Int64& x); diff --git a/be/src/vec/data_types/data_type_date_time.cpp b/be/src/vec/data_types/data_type_date_time.cpp index 7985e4728c0..857251cd095 100644 --- a/be/src/vec/data_types/data_type_date_time.cpp +++ b/be/src/vec/data_types/data_type_date_time.cpp @@ -56,6 +56,14 @@ std::string DataTypeDateTime::to_string(const IColumn& column, size_t row_num) c return buf; } +std::string DataTypeDateTime::to_string(Int64 int_val) const { + doris::VecDateTimeValue value = binary_cast<Int64, doris::VecDateTimeValue>(int_val); + + char buf[64]; + value.to_string(buf); + // DateTime to_string the end is /0 + return buf; +} void DataTypeDateTime::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { auto result = check_column_const_set_readability(column, row_num); diff --git a/be/src/vec/data_types/data_type_date_time.h b/be/src/vec/data_types/data_type_date_time.h index 5633449bc43..3f604a541c1 100644 --- a/be/src/vec/data_types/data_type_date_time.h +++ b/be/src/vec/data_types/data_type_date_time.h @@ -84,6 +84,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; + std::string to_string(Int64 value) const; DataTypeSerDeSPtr get_serde(int nesting_level = 1) const override { return std::make_shared<DataTypeDateTimeSerDe>(nesting_level); diff --git a/be/src/vec/data_types/data_type_decimal.cpp b/be/src/vec/data_types/data_type_decimal.cpp index 6532a5f0b7a..e316903fe86 100644 --- a/be/src/vec/data_types/data_type_decimal.cpp +++ b/be/src/vec/data_types/data_type_decimal.cpp @@ -91,6 +91,11 @@ void DataTypeDecimal<T>::to_string(const IColumn& column, size_t row_num, } } +template <typename T> +std::string DataTypeDecimal<T>::to_string(const T& value) const { + return value.to_string(scale); +} + template <typename T> Status DataTypeDecimal<T>::from_string(ReadBuffer& rb, IColumn* column) const { auto& column_data = static_cast<ColumnType&>(*column).get_data(); diff --git a/be/src/vec/data_types/data_type_decimal.h b/be/src/vec/data_types/data_type_decimal.h index a2f4170f236..8b12010e159 100644 --- a/be/src/vec/data_types/data_type_decimal.h +++ b/be/src/vec/data_types/data_type_decimal.h @@ -253,6 +253,7 @@ public: std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(const T& value) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; DataTypeSerDeSPtr get_serde(int nesting_level = 1) const override { return std::make_shared<DataTypeDecimalSerDe<T>>(scale, precision, nesting_level); @@ -436,12 +437,16 @@ template <typename DataType> constexpr bool IsDataTypeDecimalOrNumber = IsDataTypeDecimal<DataType> || IsDataTypeNumber<DataType>; +#define THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(value, from_type_name, to_type_name) \ + throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, \ + "Arithmetic overflow when converting value {} from type {} to type {}", value, \ + from_type_name, to_type_name) // only for casting between other integral types and decimals -template <typename FromDataType, typename ToDataType, bool multiply_may_overflow, - bool narrow_integral, typename RealFrom, typename RealTo> +template <typename FromDataType, typename OrigFromDataType, typename ToDataType, + bool multiply_may_overflow, bool narrow_integral, typename RealFrom, typename RealTo> requires IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType> -void convert_to_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, UInt32 scale_to, - const typename ToDataType::FieldType& min_result, +void convert_to_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, UInt32 precicion_to, + UInt32 scale_to, const typename ToDataType::FieldType& min_result, const typename ToDataType::FieldType& max_result, size_t size) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; @@ -453,18 +458,21 @@ void convert_to_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, UI MaxFieldType multiplier = DataTypeDecimal<MaxFieldType>::get_scale_multiplier(scale_to - scale_from); MaxFieldType tmp; + ToDataType to_data_type(precicion_to, scale_to); + auto from_type_name = OrigFromDataType {}.get_name(); for (size_t i = 0; i < size; i++) { if constexpr (multiply_may_overflow) { if (common::mul_overflow(static_cast<MaxFieldType>(src[i]).value, multiplier.value, tmp.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, "Arithmetic overflow"); + auto value_str = OrigFromDataType {}.to_string(src[i]); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(value_str, from_type_name, + to_data_type.get_name()); } if constexpr (narrow_integral) { if (tmp.value < min_result.value || tmp.value > max_result.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - tmp.value, min_result.value, max_result.value); + auto value_str = OrigFromDataType {}.to_string(src[i]); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(value_str, from_type_name, + to_data_type.get_name()); } } dst[i].value = tmp.value; @@ -476,21 +484,20 @@ void convert_to_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, UI if constexpr (!multiply_may_overflow && narrow_integral) { for (size_t i = 0; i < size; i++) { if (dst[i].value < min_result.value || dst[i].value > max_result.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - dst[i].value, min_result.value, max_result.value); + auto value_str = OrigFromDataType {}.to_string(src[i]); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(value_str, from_type_name, + to_data_type.get_name()); } } } } // only for casting between other integral types and decimals -template <typename FromDataType, typename ToDataType, bool narrow_integral, typename RealFrom, - typename RealTo> +template <typename FromDataType, typename ToDataType, typename OrigToDataType, bool narrow_integral, + typename RealFrom, typename RealTo> requires IsDataTypeDecimal<FromDataType> && IsDataTypeDecimal<ToDataType> -void convert_from_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, - const typename ToDataType::FieldType& min_result, +void convert_from_decimals(RealTo* dst, const RealFrom* src, UInt32 precicion_from, + UInt32 scale_from, const typename ToDataType::FieldType& min_result, const typename ToDataType::FieldType& max_result, size_t size) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; @@ -499,20 +506,21 @@ void convert_from_decimals(RealTo* dst, const RealFrom* src, UInt32 scale_from, // from decimal to integer MaxFieldType multiplier = DataTypeDecimal<MaxFieldType>::get_scale_multiplier(scale_from); + FromDataType from_data_type(precicion_from, scale_from); for (size_t i = 0; i < size; i++) { auto tmp = static_cast<MaxFieldType>(src[i]).value / multiplier.value; if constexpr (narrow_integral) { if (tmp < min_result.value || tmp > max_result.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - tmp, min_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(from_data_type.to_string(src[i]), + from_data_type.get_name(), + OrigToDataType {}.get_name()); } } dst[i] = tmp; } } +// convert between decimal types template <typename FromDataType, typename ToDataType, bool multiply_may_overflow, bool narrow_integral> void convert_decimal_cols( @@ -532,6 +540,8 @@ void convert_decimal_cols( FromFieldType, ToFieldType>>; using MaxNativeType = typename MaxFieldType::NativeType; + FromDataType from_data_type(precision_from, scale_from); + ToDataType to_data_type(precision_to, scale_to); auto max_result = DataTypeDecimal<ToFieldType>::get_max_digits_number(precision_to); if (scale_to > scale_from) { const MaxNativeType multiplier = @@ -541,13 +551,14 @@ void convert_decimal_cols( if constexpr (multiply_may_overflow) { if (common::mul_overflow(static_cast<MaxNativeType>(vec_from[i].value), multiplier, res)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, "Arithmetic overflow"); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(from_data_type.to_string(vec_from[i]), + from_data_type.get_name(), + to_data_type.get_name()); } else { if (UNLIKELY(res > max_result.value || res < -max_result.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - res, -max_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION( + from_data_type.to_string(vec_from[i]), from_data_type.get_name(), + to_data_type.get_name()); } else { vec_to[i] = ToFieldType(res); } @@ -556,10 +567,9 @@ void convert_decimal_cols( res = vec_from[i].value * multiplier; if constexpr (narrow_integral) { if (UNLIKELY(res > max_result.value || res < -max_result.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - res, -max_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION( + from_data_type.to_string(vec_from[i]), from_data_type.get_name(), + to_data_type.get_name()); } } vec_to[i] = ToFieldType(res); @@ -570,10 +580,9 @@ void convert_decimal_cols( if constexpr (narrow_integral) { if (UNLIKELY(vec_from[i].value > max_result.value || vec_from[i].value < -max_result.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - vec_from[i].value, -max_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(from_data_type.to_string(vec_from[i]), + from_data_type.get_name(), + to_data_type.get_name()); } } vec_to[i] = ToFieldType(vec_from[i].value); @@ -587,10 +596,9 @@ void convert_decimal_cols( if constexpr (narrow_integral) { res = (vec_from[i].value + multiplier / 2) / multiplier; if (UNLIKELY(res > max_result.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - res, -max_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION( + from_data_type.to_string(vec_from[i]), from_data_type.get_name(), + to_data_type.get_name()); } vec_to[i] = ToFieldType(res); } else { @@ -600,10 +608,9 @@ void convert_decimal_cols( if constexpr (narrow_integral) { res = (vec_from[i].value - multiplier / 2) / multiplier; if (UNLIKELY(res < -max_result.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - res, -max_result.value, max_result.value); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION( + from_data_type.to_string(vec_from[i]), from_data_type.get_name(), + to_data_type.get_name()); } vec_to[i] = ToFieldType(res); } else { @@ -614,11 +621,12 @@ void convert_decimal_cols( } } +// convert from decimal to non-decimal template <typename FromDataType, typename ToDataType, bool narrow_integral> - requires IsDataTypeDecimal<FromDataType> + requires IsDataTypeDecimal<FromDataType> && (!IsDataTypeDecimal<ToDataType>) void convert_from_decimal(typename ToDataType::FieldType* dst, - const typename FromDataType::FieldType* src, UInt32 scale, - const typename ToDataType::FieldType& min_result, + const typename FromDataType::FieldType* src, UInt32 precision, + UInt32 scale, const typename ToDataType::FieldType& min_result, const typename ToDataType::FieldType& max_result, size_t size) { using FromFieldType = typename FromDataType::FieldType; using ToFieldType = typename ToDataType::FieldType; @@ -634,28 +642,31 @@ void convert_from_decimal(typename ToDataType::FieldType* dst, dst[i] = static_cast<ToFieldType>(src[i].value) / multiplier.value; } } + FromDataType from_data_type(precision, scale); if constexpr (narrow_integral) { for (size_t i = 0; i < size; i++) { if (dst[i] < min_result || dst[i] > max_result) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - dst[i], min_result, max_result); + THROW_DECIMAL_CONVERT_OVERFLOW_EXCEPTION(from_data_type.to_string(src[i]), + from_data_type.get_name(), + ToDataType {}.get_name()); } } } } else { - convert_from_decimals<FromDataType, FromDataType, narrow_integral>( - dst, src, scale, FromFieldType(min_result), FromFieldType(max_result), size); + convert_from_decimals<FromDataType, FromDataType, ToDataType, narrow_integral>( + dst, src, precision, scale, FromFieldType(min_result), FromFieldType(max_result), + size); } } +// convert from non-decimal to decimal template <typename FromDataType, typename ToDataType, bool multiply_may_overflow, bool narrow_integral> requires IsDataTypeDecimal<ToDataType> void convert_to_decimal(typename ToDataType::FieldType* dst, const typename FromDataType::FieldType* src, UInt32 from_scale, - UInt32 to_scale, const typename ToDataType::FieldType& min_result, + UInt32 to_precision, UInt32 to_scale, + const typename ToDataType::FieldType& min_result, const typename ToDataType::FieldType& max_result, size_t size) { using FromFieldType = typename FromDataType::FieldType; @@ -670,11 +681,11 @@ void convert_to_decimal(typename ToDataType::FieldType* dst, } FromFieldType tmp = src[i] * multiplier; if (tmp <= FromFieldType(min_result) || tmp >= FromFieldType(max_result)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow, convert failed from {}, " - "expected data is [{}, {}]", - FromFieldType(tmp), FromFieldType(min_result), - FromFieldType(max_result)); + ToDataType to_data_type(to_precision, to_scale); + throw Exception( + ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, + "Arithmetic overflow when converting value {} from type {} to type {}", + src[i], FromDataType {}.get_name(), to_data_type.get_name()); } } } @@ -687,9 +698,9 @@ void convert_to_decimal(typename ToDataType::FieldType* dst, std::conditional_t<std::is_same_v<FromFieldType, Int128>, Decimal128V2, std::conditional_t<std::is_same_v<FromFieldType, wide::Int256>, Decimal256, Decimal64>>; - convert_to_decimals<DataTypeDecimal<DecimalFrom>, ToDataType, multiply_may_overflow, - narrow_integral>(dst, src, from_scale, to_scale, min_result, max_result, - size); + convert_to_decimals<DataTypeDecimal<DecimalFrom>, FromDataType, ToDataType, + multiply_may_overflow, narrow_integral>( + dst, src, from_scale, to_precision, to_scale, min_result, max_result, size); } } diff --git a/be/src/vec/data_types/data_type_ipv4.cpp b/be/src/vec/data_types/data_type_ipv4.cpp index f7aaca5c73e..5b7c43af874 100644 --- a/be/src/vec/data_types/data_type_ipv4.cpp +++ b/be/src/vec/data_types/data_type_ipv4.cpp @@ -41,6 +41,11 @@ std::string DataTypeIPv4::to_string(const IColumn& column, size_t row_num) const return value.to_string(); } +std::string DataTypeIPv4::to_string(const IPv4& ipv4_val) const { + auto value = IPv4Value(ipv4_val); + return value.to_string(); +} + void DataTypeIPv4::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { std::string value = to_string(column, row_num); ostr.write(value.data(), value.size()); diff --git a/be/src/vec/data_types/data_type_ipv4.h b/be/src/vec/data_types/data_type_ipv4.h index 8874d8010ff..5d72d7a04e4 100644 --- a/be/src/vec/data_types/data_type_ipv4.h +++ b/be/src/vec/data_types/data_type_ipv4.h @@ -58,6 +58,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(const IPv4& value) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; Field get_field(const TExprNode& node) const override { return (IPv4)node.ipv4_literal.value; } diff --git a/be/src/vec/data_types/data_type_ipv6.cpp b/be/src/vec/data_types/data_type_ipv6.cpp index 78b8e8e07d7..11b8d250071 100755 --- a/be/src/vec/data_types/data_type_ipv6.cpp +++ b/be/src/vec/data_types/data_type_ipv6.cpp @@ -42,6 +42,10 @@ std::string DataTypeIPv6::to_string(const IColumn& column, size_t row_num) const return value.to_string(); } +std::string DataTypeIPv6::to_string(const IPv6& ipv6_val) const { + auto value = IPv6Value(ipv6_val); + return value.to_string(); +} void DataTypeIPv6::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { std::string value = to_string(column, row_num); ostr.write(value.data(), value.size()); diff --git a/be/src/vec/data_types/data_type_ipv6.h b/be/src/vec/data_types/data_type_ipv6.h index d43fc2d3340..34c97c4a1e9 100755 --- a/be/src/vec/data_types/data_type_ipv6.h +++ b/be/src/vec/data_types/data_type_ipv6.h @@ -57,6 +57,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(const IPv6& value) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; Field get_field(const TExprNode& node) const override { diff --git a/be/src/vec/data_types/data_type_number_base.cpp b/be/src/vec/data_types/data_type_number_base.cpp index 29032915999..78d6e81fb58 100644 --- a/be/src/vec/data_types/data_type_number_base.cpp +++ b/be/src/vec/data_types/data_type_number_base.cpp @@ -65,6 +65,19 @@ void DataTypeNumberBase<T>::to_string(const IColumn& column, size_t row_num, } } +template <typename T> +std::string DataTypeNumberBase<T>::to_string(const T& value) const { + if constexpr (std::is_same<T, int128_t>::value || std::is_same<T, uint128_t>::value || + std::is_same<T, UInt128>::value) { + return int128_to_string(value); + } else if constexpr (std::is_integral<T>::value) { + return std::to_string(value); + } else if constexpr (std::numeric_limits<T>::is_iec559) { + fmt::memory_buffer buffer; // only use in size-predictable type. + fmt::format_to(buffer, "{}", value); + return std::string(buffer.data(), buffer.size()); + } +} template <typename T> Status DataTypeNumberBase<T>::from_string(ReadBuffer& rb, IColumn* column) const { auto* column_data = static_cast<ColumnVector<T>*>(column); diff --git a/be/src/vec/data_types/data_type_number_base.h b/be/src/vec/data_types/data_type_number_base.h index 4ec30a3ef94..bc173520fa0 100644 --- a/be/src/vec/data_types/data_type_number_base.h +++ b/be/src/vec/data_types/data_type_number_base.h @@ -156,6 +156,7 @@ public: void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; std::string to_string(const IColumn& column, size_t row_num) const override; + std::string to_string(const T& value) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; bool is_null_literal() const override { return _is_null_literal; } void set_null_literal(bool flag) { _is_null_literal = flag; } diff --git a/be/src/vec/data_types/data_type_time.cpp b/be/src/vec/data_types/data_type_time.cpp index d9c2af440e9..4ed0e37da11 100644 --- a/be/src/vec/data_types/data_type_time.cpp +++ b/be/src/vec/data_types/data_type_time.cpp @@ -53,6 +53,9 @@ std::string DataTypeTime::to_string(const IColumn& column, size_t row_num) const return time_to_buffer_from_double(value); } +std::string DataTypeTime::to_string(double value) const { + return time_to_buffer_from_double(value); +} void DataTypeTime::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { std::string value = to_string(column, row_num); ostr.write(value.data(), value.size()); @@ -80,6 +83,9 @@ std::string DataTypeTimeV2::to_string(const IColumn& column, size_t row_num) con return timev2_to_buffer_from_double(value, _scale); } +std::string DataTypeTimeV2::to_string(double value) const { + return timev2_to_buffer_from_double(value, _scale); +} void DataTypeTimeV2::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { std::string value = to_string(column, row_num); ostr.write(value.data(), value.size()); diff --git a/be/src/vec/data_types/data_type_time.h b/be/src/vec/data_types/data_type_time.h index 27b1f931bce..c7f5068b9f4 100644 --- a/be/src/vec/data_types/data_type_time.h +++ b/be/src/vec/data_types/data_type_time.h @@ -53,6 +53,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; + std::string to_string(double int_val) const; TypeDescriptor get_type_as_type_descriptor() const override { return TypeDescriptor(TYPE_TIME); } @@ -85,6 +86,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; + std::string to_string(double int_val) const; TypeDescriptor get_type_as_type_descriptor() const override { return TypeDescriptor(TYPE_TIMEV2); } diff --git a/be/src/vec/data_types/data_type_time_v2.cpp b/be/src/vec/data_types/data_type_time_v2.cpp index c92201ee08d..bdd2dcf8612 100644 --- a/be/src/vec/data_types/data_type_time_v2.cpp +++ b/be/src/vec/data_types/data_type_time_v2.cpp @@ -60,6 +60,14 @@ std::string DataTypeDateV2::to_string(const IColumn& column, size_t row_num) con return std::string {buf}; } +std::string DataTypeDateV2::to_string(UInt32 int_val) const { + DateV2Value<DateV2ValueType> val = binary_cast<UInt32, DateV2Value<DateV2ValueType>>(int_val); + + char buf[64]; + val.to_string(buf); // DateTime to_string the end is /0 + return std::string {buf}; +} + void DataTypeDateV2::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { auto result = check_column_const_set_readability(column, row_num); ColumnPtr ptr = result.first; @@ -135,6 +143,15 @@ std::string DataTypeDateTimeV2::to_string(const IColumn& column, size_t row_num) return buf; // DateTime to_string the end is /0 } +std::string DataTypeDateTimeV2::to_string(UInt64 int_val) const { + DateV2Value<DateTimeV2ValueType> val = + binary_cast<UInt64, DateV2Value<DateTimeV2ValueType>>(int_val); + + char buf[64]; + val.to_string(buf, _scale); + return buf; // DateTime to_string the end is /0 +} + void DataTypeDateTimeV2::to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const { auto result = check_column_const_set_readability(column, row_num); diff --git a/be/src/vec/data_types/data_type_time_v2.h b/be/src/vec/data_types/data_type_time_v2.h index 6d26a8c0ff1..f15a06303c6 100644 --- a/be/src/vec/data_types/data_type_time_v2.h +++ b/be/src/vec/data_types/data_type_time_v2.h @@ -83,6 +83,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(UInt32 int_val) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; MutableColumnPtr create_column() const override; @@ -125,6 +126,7 @@ public: bool equals(const IDataType& rhs) const override; std::string to_string(const IColumn& column, size_t row_num) const override; void to_string(const IColumn& column, size_t row_num, BufferWritable& ostr) const override; + std::string to_string(UInt64 int_val) const; Status from_string(ReadBuffer& rb, IColumn* column) const override; DataTypeSerDeSPtr get_serde(int nesting_level = 1) const override { return std::make_shared<DataTypeDateTimeV2SerDe>(_scale, nesting_level); diff --git a/be/src/vec/functions/function_binary_arithmetic.h b/be/src/vec/functions/function_binary_arithmetic.h index a34709732cc..53d8141bc9e 100644 --- a/be/src/vec/functions/function_binary_arithmetic.h +++ b/be/src/vec/functions/function_binary_arithmetic.h @@ -220,12 +220,17 @@ struct BinaryOperationImpl { } }; +#define THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION(left_value, op_name, right_value, result_value, \ + result_type_name) \ + throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, \ + "Arithmetic overflow: {} {} {} = {}, result type: {}", left_value, op_name, \ + right_value, result_value, result_type_name) /// Binary operations for Decimals need scale args /// +|- scale one of args (which scale factor is not 1). ScaleR = oneof(Scale1, Scale2); /// * no agrs scale. ScaleR = Scale1 + Scale2; /// / first arg scale. ScaleR = Scale1 (scale_a = DecimalType<B>::get_scale()). -template <typename LeftDataType, typename RightDataType, - template <typename, typename> typename Operation, typename ResultType, +template <typename LeftDataType, typename RightDataType, typename ResultDataType, + template <typename, typename> typename Operation, typename Name, typename ResultType, bool is_to_null_type, bool check_overflow> struct DecimalBinaryOperation { using A = typename LeftDataType::FieldType; @@ -246,8 +251,9 @@ private: static void vector_vector(const typename Traits::ArrayA::value_type* __restrict a, const typename Traits::ArrayB::value_type* __restrict b, - typename ArrayC::value_type* c, size_t size, - const ResultType& max_result_number, + typename ArrayC::value_type* c, const LeftDataType& type_left, + const RightDataType& type_right, const ResultDataType& type_result, + size_t size, const ResultType& max_result_number, const ResultType& scale_diff_multiplier) { static_assert(OpTraits::is_plus_minus || OpTraits::is_multiply); if constexpr (OpTraits::is_multiply && IsDecimalV2<A> && IsDecimalV2<B> && @@ -259,7 +265,8 @@ private: [&](auto need_adjust_scale) { for (size_t i = 0; i < size; i++) { c[i] = typename ArrayC::value_type(apply<need_adjust_scale>( - a[i], b[i], max_result_number, scale_diff_multiplier)); + a[i], b[i], type_left, type_right, type_result, + max_result_number, scale_diff_multiplier)); } }, make_bool_variant(need_adjust_scale && check_overflow)); @@ -310,8 +317,9 @@ private: } static void vector_constant(const typename Traits::ArrayA::value_type* __restrict a, B b, - typename ArrayC::value_type* c, size_t size, - const ResultType& max_result_number, + typename ArrayC::value_type* c, const LeftDataType& type_left, + const RightDataType& type_right, const ResultDataType& type_result, + size_t size, const ResultType& max_result_number, const ResultType& scale_diff_multiplier) { static_assert(!OpTraits::is_division); @@ -320,7 +328,8 @@ private: [&](auto need_adjust_scale) { for (size_t i = 0; i < size; ++i) { c[i] = typename ArrayC::value_type(apply<need_adjust_scale>( - a[i], b, max_result_number, scale_diff_multiplier)); + a[i], b, type_left, type_right, type_result, max_result_number, + scale_diff_multiplier)); } }, make_bool_variant(need_adjust_scale)); @@ -343,15 +352,17 @@ private: } static void constant_vector(A a, const typename Traits::ArrayB::value_type* __restrict b, - typename ArrayC::value_type* c, size_t size, - const ResultType& max_result_number, + typename ArrayC::value_type* c, const LeftDataType& type_left, + const RightDataType& type_right, const ResultDataType& type_result, + size_t size, const ResultType& max_result_number, const ResultType& scale_diff_multiplier) { bool need_adjust_scale = scale_diff_multiplier.value > 1; std::visit( [&](auto need_adjust_scale) { for (size_t i = 0; i < size; ++i) { c[i] = typename ArrayC::value_type(apply<need_adjust_scale>( - a, b[i], max_result_number, scale_diff_multiplier)); + a, b[i], type_left, type_right, type_result, max_result_number, + scale_diff_multiplier)); } }, make_bool_variant(need_adjust_scale)); @@ -373,9 +384,13 @@ private: } } - static ResultType constant_constant(A a, B b, const ResultType& max_result_number, + static ResultType constant_constant(A a, B b, const LeftDataType& type_left, + const RightDataType& type_right, + const ResultDataType& type_result, + const ResultType& max_result_number, const ResultType& scale_diff_multiplier) { - return ResultType(apply<true>(a, b, max_result_number, scale_diff_multiplier)); + return ResultType(apply<true>(a, b, type_left, type_right, type_result, max_result_number, + scale_diff_multiplier)); } static ResultType constant_constant(A a, B b, UInt8& is_null, @@ -393,9 +408,12 @@ private: } public: - static ColumnPtr adapt_decimal_constant_constant(A a, B b, const ResultType& max_result_number, + static ColumnPtr adapt_decimal_constant_constant(A a, B b, const LeftDataType& type_left, + const RightDataType& type_right, + const ResultType& max_result_number, const ResultType& scale_diff_multiplier, DataTypePtr res_data_type) { + auto type_result = assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto column_result = ColumnDecimal<ResultType>::create( 1, assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type).get_scale()); @@ -410,15 +428,19 @@ public: return ColumnNullable::create(std::move(column_result), std::move(null_map)); } else { column_result->get_element(0) = - constant_constant(a, b, max_result_number, scale_diff_multiplier); + constant_constant(a, b, type_left, type_right, type_result, max_result_number, + scale_diff_multiplier); return column_result; } } static ColumnPtr adapt_decimal_vector_constant(ColumnPtr column_left, B b, + const LeftDataType& type_left, + const RightDataType& type_right, const ResultType& max_result_number, const ResultType& scale_diff_multiplier, DataTypePtr res_data_type) { + auto type_result = assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto column_left_ptr = check_and_get_column<typename Traits::ColumnVectorA>(column_left); auto column_result = ColumnDecimal<ResultType>::create( column_left->size(), @@ -436,15 +458,19 @@ public: return ColumnNullable::create(std::move(column_result), std::move(null_map)); } else { vector_constant(column_left_ptr->get_data().data(), b, column_result->get_data().data(), - column_left->size(), max_result_number, scale_diff_multiplier); + type_left, type_right, type_result, column_left->size(), + max_result_number, scale_diff_multiplier); return column_result; } } static ColumnPtr adapt_decimal_constant_vector(A a, ColumnPtr column_right, + const LeftDataType& type_left, + const RightDataType& type_right, const ResultType& max_result_number, const ResultType& scale_diff_multiplier, DataTypePtr res_data_type) { + auto type_result = assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto column_right_ptr = check_and_get_column<typename Traits::ColumnVectorB>(column_right); auto column_result = ColumnDecimal<ResultType>::create( column_right->size(), @@ -463,13 +489,15 @@ public: return ColumnNullable::create(std::move(column_result), std::move(null_map)); } else { constant_vector(a, column_right_ptr->get_data().data(), - column_result->get_data().data(), column_right->size(), - max_result_number, scale_diff_multiplier); + column_result->get_data().data(), type_left, type_right, type_result, + column_right->size(), max_result_number, scale_diff_multiplier); return column_result; } } static ColumnPtr adapt_decimal_vector_vector(ColumnPtr column_left, ColumnPtr column_right, + const LeftDataType& type_left, + const RightDataType& type_right, const ResultType& max_result_number, const ResultType& scale_diff_multiplier, DataTypePtr res_data_type) { @@ -494,8 +522,8 @@ public: return ColumnNullable::create(std::move(column_result), std::move(null_map)); } else { vector_vector(column_left_ptr->get_data().data(), column_right_ptr->get_data().data(), - column_result->get_data().data(), column_left->size(), max_result_number, - scale_diff_multiplier); + column_result->get_data().data(), type_left, type_right, type_result, + column_left->size(), max_result_number, scale_diff_multiplier); return column_result; } } @@ -504,6 +532,9 @@ private: /// there's implicit type conversion here template <bool need_adjust_scale> static ALWAYS_INLINE NativeResultType apply(NativeResultType a, NativeResultType b, + const LeftDataType& type_left, + const RightDataType& type_right, + const ResultDataType& type_result, const ResultType& max_result_number, const ResultType& scale_diff_multiplier) { static_assert(OpTraits::is_plus_minus || OpTraits::is_multiply); @@ -512,7 +543,10 @@ private: if constexpr (check_overflow) { auto res = Op::template apply(DecimalV2Value(a), DecimalV2Value(b)).value(); if (res > max_result_number.value || res < -max_result_number.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a).to_string(), Name::name, + DecimalV2Value(b).to_string(), DecimalV2Value(res).to_string(), + ResultDataType {}.get_name()); } return res; } else { @@ -524,8 +558,13 @@ private: // TODO handle overflow gracefully if (UNLIKELY(Op::template apply<NativeResultType>(a, b, res))) { if constexpr (OpTraits::is_plus_minus) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + auto result_str = + DataTypeDecimal<Decimal256> {BeConsts::MAX_DECIMAL256_PRECISION, + type_result.get_scale()} + .to_string(Decimal256(res)); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + type_left.to_string(A(a)), Name::name, type_right.to_string(B(b)), + result_str, type_result.get_name()); } // multiply if constexpr (std::is_same_v<NativeResultType, __int128>) { @@ -543,14 +582,24 @@ private: // check if final result is overflow if (res256 > wide::Int256(max_result_number.value) || res256 < wide::Int256(-max_result_number.value)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + auto result_str = + DataTypeDecimal<Decimal256> {BeConsts::MAX_DECIMAL256_PRECISION, + type_result.get_scale()} + .to_string(Decimal256(res256)); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + type_left.to_string(A(a)), Name::name, + type_right.to_string(B(b)), result_str, type_result.get_name()); } else { res = res256; } } else { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + auto result_str = + DataTypeDecimal<Decimal256> {BeConsts::MAX_DECIMAL256_PRECISION, + type_result.get_scale()} + .to_string(Decimal256(res)); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + type_left.to_string(A(a)), Name::name, type_right.to_string(B(b)), + result_str, type_result.get_name()); } } else { // round to final result precision @@ -564,8 +613,13 @@ private: } } if (res > max_result_number.value || res < -max_result_number.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + auto result_str = + DataTypeDecimal<Decimal256> {BeConsts::MAX_DECIMAL256_PRECISION, + type_result.get_scale()} + .to_string(Decimal256(res)); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + type_left.to_string(A(a)), Name::name, type_right.to_string(B(b)), + result_str, type_result.get_name()); } } return res; @@ -597,19 +651,25 @@ private: if constexpr (std::is_same_v<ANS_TYPE, DecimalV2Value>) { if (ans.value() > max_result_number.value || ans.value() < -max_result_number.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a).to_string(), Name::name, + DecimalV2Value(b).to_string(), DecimalV2Value(ans).to_string(), + ResultDataType {}.get_name()); } } else if constexpr (IsDecimalNumber<ANS_TYPE>) { if (ans.value > max_result_number.value || ans.value < -max_result_number.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a).to_string(), Name::name, + DecimalV2Value(b).to_string(), DecimalV2Value(ans).to_string(), + ResultDataType {}.get_name()); } } else { if (ans > max_result_number.value || ans < -max_result_number.value) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, - "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a).to_string(), Name::name, + DecimalV2Value(b).to_string(), DecimalV2Value(ans).to_string(), + ResultDataType {}.get_name()); } } } @@ -714,7 +774,7 @@ struct BinaryOperationTraits { }; template <typename LeftDataType, typename RightDataType, typename ExpectedResultDataType, - template <typename, typename> class Operation, bool is_to_null_type, + template <typename, typename> class Operation, typename Name, bool is_to_null_type, bool check_overflow_for_decimal> struct ConstOrVectorAdapter { static constexpr bool result_is_decimal = @@ -727,8 +787,8 @@ struct ConstOrVectorAdapter { using OperationImpl = std::conditional_t< IsDataTypeDecimal<ResultDataType>, - DecimalBinaryOperation<LeftDataType, RightDataType, Operation, ResultType, - is_to_null_type, check_overflow_for_decimal>, + DecimalBinaryOperation<LeftDataType, RightDataType, ResultDataType, Operation, Name, + ResultType, is_to_null_type, check_overflow_for_decimal>, BinaryOperationImpl<A, B, Operation<A, B>, is_to_null_type, ResultType>>; static ColumnPtr execute(ColumnPtr column_left, ColumnPtr column_right, @@ -785,8 +845,8 @@ private: column_result = OperationImpl::adapt_decimal_constant_constant( column_left_ptr->template get_value<A>(), - column_right_ptr->template get_value<B>(), max_and_multiplier.first, - max_and_multiplier.second, res_data_type); + column_right_ptr->template get_value<B>(), type_left, type_right, + max_and_multiplier.first, max_and_multiplier.second, res_data_type); } else { column_result = OperationImpl::adapt_normal_constant_constant( @@ -808,8 +868,8 @@ private: assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto max_and_multiplier = get_max_and_multiplier(type_left, type_right, type_result); return OperationImpl::adapt_decimal_vector_constant( - column_left->get_ptr(), column_right_ptr->template get_value<B>(), - max_and_multiplier.first, max_and_multiplier.second, res_data_type); + column_left->get_ptr(), column_right_ptr->template get_value<B>(), type_left, + type_right, max_and_multiplier.first, max_and_multiplier.second, res_data_type); } else { return OperationImpl::adapt_normal_vector_constant( column_left->get_ptr(), column_right_ptr->template get_value<B>()); @@ -827,8 +887,8 @@ private: assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto max_and_multiplier = get_max_and_multiplier(type_left, type_right, type_result); return OperationImpl::adapt_decimal_constant_vector( - column_left_ptr->template get_value<A>(), column_right->get_ptr(), - max_and_multiplier.first, max_and_multiplier.second, res_data_type); + column_left_ptr->template get_value<A>(), column_right->get_ptr(), type_left, + type_right, max_and_multiplier.first, max_and_multiplier.second, res_data_type); } else { return OperationImpl::adapt_normal_constant_vector( column_left_ptr->template get_value<A>(), column_right->get_ptr()); @@ -843,8 +903,8 @@ private: assert_cast<const DataTypeDecimal<ResultType>&>(*res_data_type); auto max_and_multiplier = get_max_and_multiplier(type_left, type_right, type_result); return OperationImpl::adapt_decimal_vector_vector( - column_left->get_ptr(), column_right->get_ptr(), max_and_multiplier.first, - max_and_multiplier.second, res_data_type); + column_left->get_ptr(), column_right->get_ptr(), type_left, type_right, + max_and_multiplier.first, max_and_multiplier.second, res_data_type); } else { return OperationImpl::adapt_normal_vector_vector(column_left->get_ptr(), column_right->get_ptr()); @@ -1004,7 +1064,7 @@ public: LeftDataType, RightDataType, std::conditional_t<IsDataTypeDecimal<ExpectedResultDataType>, ExpectedResultDataType, ResultDataType>, - Operation, is_to_null_type, + Operation, Name, is_to_null_type, true>::execute(block.get_by_position(arguments[0]).column, block.get_by_position(arguments[1]).column, left, right, @@ -1016,7 +1076,7 @@ public: LeftDataType, RightDataType, std::conditional_t<IsDataTypeDecimal<ExpectedResultDataType>, ExpectedResultDataType, ResultDataType>, - Operation, is_to_null_type, + Operation, Name, is_to_null_type, false>::execute(block.get_by_position(arguments[0]).column, block.get_by_position(arguments[1]).column, left, right, diff --git a/be/src/vec/functions/function_cast.h b/be/src/vec/functions/function_cast.h index 7f3f74217b4..952f4f57922 100644 --- a/be/src/vec/functions/function_cast.h +++ b/be/src/vec/functions/function_cast.h @@ -346,13 +346,13 @@ struct ConvertImpl { vec_from.size()); } else if constexpr (IsDataTypeDecimal<FromDataType>) { convert_from_decimal<FromDataType, ToDataType, narrow_integral>( - vec_to.data(), vec_from.data(), vec_from.get_scale(), - min_result, max_result, size); + vec_to.data(), vec_from.data(), from_precision, + vec_from.get_scale(), min_result, max_result, size); } else { convert_to_decimal<FromDataType, ToDataType, multiply_may_overflow, - narrow_integral>(vec_to.data(), vec_from.data(), - from_scale, to_scale, - min_result, max_result, size); + narrow_integral>( + vec_to.data(), vec_from.data(), from_scale, to_precision, + to_scale, min_result, max_result, size); } }, make_bool_variant(multiply_may_overflow), diff --git a/be/src/vec/functions/multiply.cpp b/be/src/vec/functions/multiply.cpp index 43da8bb56ff..f871a3a742c 100644 --- a/be/src/vec/functions/multiply.cpp +++ b/be/src/vec/functions/multiply.cpp @@ -76,11 +76,17 @@ struct MultiplyImpl { int128_t i128_mul_result; if (common::mul_overflow(DecimalV2Value(a[i]).value(), DecimalV2Value(b[i]).value(), i128_mul_result)) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a[i]).to_string(), "multiply", + DecimalV2Value(b[i]).to_string(), + DecimalV2Value(i128_mul_result).to_string(), "decimalv2"); } c[i] = (i128_mul_result - sgn[i]) / DecimalV2Value::ONE_BILLION + sgn[i]; if (c[i].value > max.value() || c[i].value < min.value()) { - throw Exception(ErrorCode::ARITHMETIC_OVERFLOW_ERRROR, "Arithmetic overflow"); + THROW_DECIMAL_BINARY_OP_OVERFLOW_EXCEPTION( + DecimalV2Value(a[i]).to_string(), "multiply", + DecimalV2Value(b[i]).to_string(), + DecimalV2Value(i128_mul_result).to_string(), "decimalv2"); } } else { c[i] = (DecimalV2Value(a[i]).value() * DecimalV2Value(b[i]).value() - sgn[i]) / --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org