diff --git a/be/src/util/datetype_cast.hpp b/be/src/util/datetype_cast.hpp index 495631ea7e376c..5c187ded7b729c 100644 --- a/be/src/util/datetype_cast.hpp +++ b/be/src/util/datetype_cast.hpp @@ -29,8 +29,10 @@ /* * We use these function family to clarify our types of datelike type. for example: * DataTypeDate -------------------> ColumnDate -----------------------> Int64 - * | TypeToColumn ValueTypeOfColumn - * | TypeToValueType + * | | TypeToColumn ValueTypeOfColumn | + * | ↘--------------------------------------------------------------↗ + * | ::FieldType + * ↓ TypeToValueType * VecDateTimeValue */ namespace doris::date_cast { @@ -102,6 +104,7 @@ constexpr bool IsV1() { std::is_same_v); } +// only for datelike types. template constexpr bool IsV2() { return !IsV1(); diff --git a/be/src/vec/common/typeid_cast.h b/be/src/vec/common/typeid_cast.h index e135ef3309d2ec..3f81586a707c33 100644 --- a/be/src/vec/common/typeid_cast.h +++ b/be/src/vec/common/typeid_cast.h @@ -20,14 +20,11 @@ #pragma once -#include #include -#include #include #include "common/exception.h" #include "common/status.h" -#include "vec/common/demangle.h" /** Checks type by comparing typeid. * The exact match of the type is checked. That is, cast to the ancestor will be unsuccessful. diff --git a/be/src/vec/functions/array/function_array_range.cpp b/be/src/vec/functions/array/function_array_range.cpp index 0980587660b20a..ffb5987c744d1f 100644 --- a/be/src/vec/functions/array/function_array_range.cpp +++ b/be/src/vec/functions/array/function_array_range.cpp @@ -16,10 +16,10 @@ // under the License. #include -#include #include #include +#include #include #include @@ -41,11 +41,11 @@ #include "vec/data_types/data_type_date_time.h" #include "vec/data_types/data_type_nullable.h" #include "vec/data_types/data_type_number.h" +#include "vec/data_types/data_type_time_v2.h" #include "vec/functions/function.h" #include "vec/functions/function_date_or_datetime_computation.h" #include "vec/functions/simple_function_factory.h" #include "vec/runtime/vdatetime_value.h" -#include "vec/utils/util.hpp" namespace doris { class FunctionContext; @@ -229,10 +229,9 @@ struct RangeImplUtil { dest_nested_null_map.push_back(0); offset++; move++; - idx = doris::vectorized::date_time_add< - UNIT::value, DateV2Value, - DateV2Value, DateTimeV2>(idx, step_row, - is_null); + idx = doris::vectorized::date_time_add(idx, step_row, + is_null); } dest_offsets.push_back(offset); } diff --git a/be/src/vec/functions/function_date_or_datetime_computation.cpp b/be/src/vec/functions/function_date_or_datetime_computation.cpp index f6bf806ad46c1d..ece897d6dcbf7c 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.cpp +++ b/be/src/vec/functions/function_date_or_datetime_computation.cpp @@ -55,7 +55,7 @@ using FunctionWeeksDiff = using FunctionHoursDiff = FunctionDateOrDateTimeComputation>; using FunctionMinutesDiff = - FunctionDateOrDateTimeComputation>; + FunctionDateOrDateTimeComputation>; using FunctionSecondsDiff = FunctionDateOrDateTimeComputation>; @@ -68,6 +68,7 @@ struct NowFunctionName { static constexpr auto name = "now"; }; +//TODO: remove the inter-layer CurrentDateTimeImpl using FunctionNow = FunctionCurrentDateOrDateTime>; using FunctionNowWithPrecision = diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h b/be/src/vec/functions/function_date_or_datetime_computation.h index 224bf49179177c..8165f57881b839 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -17,13 +17,12 @@ #pragma once -#include -#include - #include #include +#include #include #include +#include #include #include @@ -32,7 +31,6 @@ #include "common/exception.h" #include "common/logging.h" #include "common/status.h" -#include "fmt/format.h" #include "runtime/runtime_state.h" #include "udf/udf.h" #include "util/binary_cast.hpp" @@ -45,12 +43,10 @@ #include "vec/columns/columns_number.h" #include "vec/common/assert_cast.h" #include "vec/common/pod_array_fwd.h" -#include "vec/common/typeid_cast.h" #include "vec/core/block.h" #include "vec/core/column_numbers.h" #include "vec/core/column_with_type_and_name.h" #include "vec/core/columns_with_type_and_name.h" -#include "vec/core/field.h" #include "vec/core/types.h" #include "vec/data_types/data_type.h" #include "vec/data_types/data_type_date.h" @@ -67,73 +63,57 @@ namespace doris::vectorized { -template -extern ResultType date_time_add(const Arg& t, Int64 delta, bool& is_null) { - auto ts_value = binary_cast(t); +/// because all these functions(xxx_add/xxx_sub) defined in FE use Integer as the second value +/// so Int32 as delta is enough. For upstream(FunctionDateOrDateTimeComputation) we also could use Int32. + +template +ReturnNativeType date_time_add(const InputNativeType& t, Int32 delta, bool& is_null) { + using DateValueType = date_cast::TypeToValueTypeV; + using ResultDateValueType = date_cast::TypeToValueTypeV; + // e.g.: for DatatypeDatetimeV2, cast from u64 to DateV2Value + auto ts_value = binary_cast(t); TimeInterval interval(unit, delta, false); - if constexpr (std::is_same_v || - std::is_same_v) { + if constexpr (std::is_same_v) { is_null = !(ts_value.template date_add_interval(interval)); - - return binary_cast(ts_value); + // here DateValueType = ResultDateValueType + return binary_cast(ts_value); } else { + // this is for HOUR/MINUTE/SECOND/MS_ADD for datev2. got datetimev2 but not datev2. so need this two-arg reload to assign. ResultDateValueType res; is_null = !(ts_value.template date_add_interval(interval, res)); - return binary_cast(res); + return binary_cast(res); } } -#define ADD_TIME_FUNCTION_IMPL(CLASS, NAME, UNIT) \ - template \ - struct CLASS { \ - using ReturnType = std::conditional_t< \ - date_cast::IsV1(), DataTypeDateTime, \ - std::conditional_t< \ - std::is_same_v, \ - std::conditional_t, \ - DataTypeDateTimeV2>>; \ - using ReturnNativeType = \ - date_cast::ValueTypeOfColumnV>; \ - using InputNativeType = date_cast::ValueTypeOfColumnV>; \ - static constexpr auto name = #NAME; \ - static constexpr auto is_nullable = true; \ - static inline ReturnNativeType execute(const InputNativeType& t, Int64 delta, \ - bool& is_null) { \ - if constexpr (std::is_same_v || \ - std::is_same_v) { \ - return date_time_add(t, delta, \ - is_null); \ - } else if constexpr (std::is_same_v) { \ - if constexpr (TimeUnit::UNIT == TimeUnit::HOUR || \ - TimeUnit::UNIT == TimeUnit::MINUTE || \ - TimeUnit::UNIT == TimeUnit::SECOND || \ - TimeUnit::UNIT == TimeUnit::SECOND_MICROSECOND) { \ - return date_time_add, \ - DateV2Value, ReturnNativeType>( \ - t, delta, is_null); \ - } else { \ - return date_time_add, \ - DateV2Value, ReturnNativeType>(t, delta, \ - is_null); \ - } \ - \ - } else { \ - return date_time_add, \ - DateV2Value, ReturnNativeType>(t, delta, \ - is_null); \ - } \ - } \ - \ - static DataTypes get_variadic_argument_types() { \ - return {std::make_shared(), std::make_shared()}; \ - } \ +#define ADD_TIME_FUNCTION_IMPL(CLASS, NAME, UNIT) \ + template \ + struct CLASS { \ + /* for V1 type all return Datetime. for V2 type, if unit <= hour, increase to DatetimeV2 */ \ + using ReturnType = std::conditional_t< \ + date_cast::IsV1(), DataTypeDateTime, \ + std::conditional_t< \ + std::is_same_v, \ + std::conditional_t, \ + DataTypeDateTimeV2>>; \ + using ReturnNativeType = ReturnType::FieldType; \ + using InputNativeType = ArgType::FieldType; \ + static constexpr auto name = #NAME; \ + static constexpr auto is_nullable = true; \ + static inline ReturnNativeType execute(const InputNativeType& t, Int32 delta, \ + bool& is_null) { \ + return date_time_add(t, delta, is_null); \ + } \ + \ + static DataTypes get_variadic_argument_types() { \ + return {std::make_shared(), std::make_shared()}; \ + } \ } ADD_TIME_FUNCTION_IMPL(AddMicrosecondsImpl, microseconds_add, MICROSECOND); @@ -146,46 +126,32 @@ ADD_TIME_FUNCTION_IMPL(AddWeeksImpl, weeks_add, WEEK); ADD_TIME_FUNCTION_IMPL(AddMonthsImpl, months_add, MONTH); ADD_TIME_FUNCTION_IMPL(AddYearsImpl, years_add, YEAR); -template +template struct AddQuartersImpl { using ReturnType = - std::conditional_t || - std::is_same_v, + std::conditional_t || + std::is_same_v, DataTypeDateTime, - std::conditional_t, + std::conditional_t, DataTypeDateV2, DataTypeDateTimeV2>>; - using InputNativeType = std::conditional_t< - std::is_same_v || std::is_same_v, - Int64, std::conditional_t, UInt32, UInt64>>; - using ReturnNativeType = std::conditional_t< - std::is_same_v || std::is_same_v, - Int64, std::conditional_t, UInt32, UInt64>>; + using InputNativeType = ArgType::FieldType; + using ReturnNativeType = ReturnType::FieldType; static constexpr auto name = "quarters_add"; static constexpr auto is_nullable = true; - static inline ReturnNativeType execute(const InputNativeType& t, Int64 delta, bool& is_null) { - if constexpr (std::is_same_v || - std::is_same_v) { - return date_time_add(t, delta, is_null); - } else if constexpr (std::is_same_v) { - return date_time_add, - DateV2Value, ReturnNativeType>(t, delta, is_null); - } else { - return date_time_add, - DateV2Value, ReturnNativeType>(t, delta, - is_null); - } + static inline ReturnNativeType execute(const InputNativeType& t, Int32 delta, bool& is_null) { + return date_time_add(t, 3 * delta, is_null); } - static DataTypes get_variadic_argument_types() { return {std::make_shared()}; } + static DataTypes get_variadic_argument_types() { return {std::make_shared()}; } }; template struct SubtractIntervalImpl { using ReturnType = typename Transform::ReturnType; using InputNativeType = typename Transform::InputNativeType; + using ReturnNativeType = typename Transform::ReturnNativeType; static constexpr auto is_nullable = true; - static inline Int64 execute(const InputNativeType& t, Int64 delta, bool& is_null) { + static inline ReturnNativeType execute(const InputNativeType& t, Int32 delta, bool& is_null) { return Transform::execute(t, -delta, is_null); } @@ -244,57 +210,49 @@ struct SubtractYearsImpl : SubtractIntervalImpl, DateType static constexpr auto name = "years_sub"; }; -#define DECLARE_DATE_FUNCTIONS(NAME, FN_NAME, RETURN_TYPE, STMT) \ - template \ - struct NAME { \ - using ArgType1 = std::conditional_t< \ - std::is_same_v, UInt32, \ - std::conditional_t, UInt64, Int64>>; \ - using ArgType2 = std::conditional_t< \ - std::is_same_v, UInt32, \ - std::conditional_t, UInt64, Int64>>; \ - using DateValueType1 = std::conditional_t< \ - std::is_same_v, DateV2Value, \ - std::conditional_t, \ - DateV2Value, VecDateTimeValue>>; \ - using DateValueType2 = std::conditional_t< \ - std::is_same_v, DateV2Value, \ - std::conditional_t, \ - DateV2Value, VecDateTimeValue>>; \ - using ReturnType = RETURN_TYPE; \ - static constexpr auto name = #FN_NAME; \ - static constexpr auto is_nullable = false; \ - static inline ReturnType::FieldType execute(const ArgType1& t0, const ArgType2& t1, \ - bool& is_null) { \ - const auto& ts0 = reinterpret_cast(t0); \ - const auto& ts1 = reinterpret_cast(t1); \ - is_null = !ts0.is_valid_date() || !ts1.is_valid_date(); \ - return STMT; \ - } \ - static DataTypes get_variadic_argument_types() { \ - return {std::make_shared(), std::make_shared()}; \ - } \ +#define DECLARE_DATE_FUNCTIONS(NAME, FN_NAME, RETURN_TYPE, STMT) \ + template \ + struct NAME { \ + using NativeType1 = DateType1::FieldType; \ + using NativeType2 = DateType2::FieldType; \ + using DateValueType1 = date_cast::TypeToValueTypeV; \ + using DateValueType2 = date_cast::TypeToValueTypeV; \ + using ReturnType = RETURN_TYPE; \ + \ + static constexpr auto name = #FN_NAME; \ + static constexpr auto is_nullable = false; \ + static inline ReturnType::FieldType execute(const NativeType1& t0, const NativeType2& t1, \ + bool& is_null) { \ + const auto& ts0 = reinterpret_cast(t0); \ + const auto& ts1 = reinterpret_cast(t1); \ + is_null = !ts0.is_valid_date() || !ts1.is_valid_date(); \ + return (STMT); \ + } \ + static DataTypes get_variadic_argument_types() { \ + return {std::make_shared(), std::make_shared()}; \ + } \ }; + DECLARE_DATE_FUNCTIONS(DateDiffImpl, datediff, DataTypeInt32, (ts0.daynr() - ts1.daynr())); // DECLARE_DATE_FUNCTIONS(TimeDiffImpl, timediff, DataTypeTime, ts0.second_diff(ts1)); -// Expands to +// Expands to below here because it use Time type which need some special deal. template struct TimeDiffImpl { - using DateValueType1 = date_cast::TypeToValueTypeV; - using DateValueType2 = date_cast::TypeToValueTypeV; - using ArgType1 = date_cast::ValueTypeOfColumnV>; - using ArgType2 = date_cast::ValueTypeOfColumnV>; + using NativeType1 = date_cast::TypeToValueTypeV; + using NativeType2 = date_cast::TypeToValueTypeV; + using ArgType1 = DateType1::FieldType; + using ArgType2 = DateType2::FieldType; static constexpr bool UsingTimev2 = date_cast::IsV2() || date_cast::IsV2(); - using ReturnType = DataTypeTimeV2; + using ReturnType = DataTypeTimeV2; // TimeV1Type also use double as native type. same as v2. static constexpr auto name = "timediff"; static constexpr int64_t limit_value = 3020399000000; // 838:59:59 convert to microsecond static inline ReturnType::FieldType execute(const ArgType1& t0, const ArgType2& t1, bool& is_null) { - const auto& ts0 = reinterpret_cast(t0); - const auto& ts1 = reinterpret_cast(t1); + const auto& ts0 = reinterpret_cast(t0); + const auto& ts1 = reinterpret_cast(t1); is_null = !ts0.is_valid_date() || !ts1.is_valid_date(); if constexpr (UsingTimev2) { // refer to https://dev.mysql.com/doc/refman/5.7/en/time.html @@ -318,381 +276,138 @@ struct TimeDiffImpl { #define TIME_DIFF_FUNCTION_IMPL(CLASS, NAME, UNIT) \ DECLARE_DATE_FUNCTIONS(CLASS, NAME, DataTypeInt64, datetime_diff(ts1, ts0)) +// all these functions implemented by datediff TIME_DIFF_FUNCTION_IMPL(YearsDiffImpl, years_diff, YEAR); TIME_DIFF_FUNCTION_IMPL(MonthsDiffImpl, months_diff, MONTH); TIME_DIFF_FUNCTION_IMPL(WeeksDiffImpl, weeks_diff, WEEK); TIME_DIFF_FUNCTION_IMPL(DaysDiffImpl, days_diff, DAY); TIME_DIFF_FUNCTION_IMPL(HoursDiffImpl, hours_diff, HOUR); -TIME_DIFF_FUNCTION_IMPL(MintueSDiffImpl, minutes_diff, MINUTE); +TIME_DIFF_FUNCTION_IMPL(MintuesDiffImpl, minutes_diff, MINUTE); TIME_DIFF_FUNCTION_IMPL(SecondsDiffImpl, seconds_diff, SECOND); TIME_DIFF_FUNCTION_IMPL(MilliSecondsDiffImpl, milliseconds_diff, MILLISECOND); TIME_DIFF_FUNCTION_IMPL(MicroSecondsDiffImpl, microseconds_diff, MICROSECOND); -#define TIME_FUNCTION_TWO_ARGS_IMPL(CLASS, NAME, FUNCTION, RETURN_TYPE) \ - template \ - struct CLASS { \ - using ArgType = std::conditional_t< \ - std::is_same_v, UInt32, \ - std::conditional_t, UInt64, Int64>>; \ - using DateValueType = std::conditional_t< \ - std::is_same_v, DateV2Value, \ - std::conditional_t, \ - DateV2Value, VecDateTimeValue>>; \ - using ReturnType = RETURN_TYPE; \ - static constexpr auto name = #NAME; \ - static constexpr auto is_nullable = false; \ - static inline ReturnType::FieldType execute(const ArgType& t0, const Int32 mode, \ - bool& is_null) { \ - const auto& ts0 = reinterpret_cast(t0); \ - is_null = !ts0.is_valid_date(); \ - return ts0.FUNCTION; \ - } \ - static DataTypes get_variadic_argument_types() { \ - return {std::make_shared(), std::make_shared()}; \ - } \ +#define TIME_FUNCTION_TWO_ARGS_IMPL(CLASS, NAME, FUNCTION, RETURN_TYPE) \ + template \ + struct CLASS { \ + using ArgType = DateType::FieldType; \ + using DateValueType = date_cast::TypeToValueTypeV; \ + using ReturnType = RETURN_TYPE; \ + \ + static constexpr auto name = #NAME; \ + static constexpr auto is_nullable = false; \ + static inline ReturnType::FieldType execute(const ArgType& t0, const Int32 mode, \ + bool& is_null) { \ + const auto& ts0 = reinterpret_cast(t0); \ + is_null = !ts0.is_valid_date(); \ + return ts0.FUNCTION; \ + } \ + static DataTypes get_variadic_argument_types() { \ + return {std::make_shared(), std::make_shared()}; \ + } \ } TIME_FUNCTION_TWO_ARGS_IMPL(ToYearWeekTwoArgsImpl, yearweek, year_week(mysql_week_mode(mode)), DataTypeInt32); TIME_FUNCTION_TWO_ARGS_IMPL(ToWeekTwoArgsImpl, week, week(mysql_week_mode(mode)), DataTypeInt8); -template +// only use for FunctionDateOrDateTimeComputation. FromTypes are NativeTypes. +template struct DateTimeOp { - // use for (DateTime, DateTime) -> other_type - static void vector_vector(const PaddedPODArray& vec_from0, - const PaddedPODArray& vec_from1, - PaddedPODArray& vec_to, NullMap& null_map) { - size_t size = vec_from0.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) { - // here reinterpret_cast is used to convert uint8& to bool&, - // otherwise it will be implicitly converted to bool, causing the rvalue to fail to match the lvalue. - // the same goes for the following. - vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], - reinterpret_cast(null_map[i])); - } - } - static void vector_vector(const PaddedPODArray& vec_from0, - const PaddedPODArray& vec_from1, - PaddedPODArray& vec_to) { - size_t size = vec_from0.size(); - vec_to.resize(size); - - bool invalid = true; - for (size_t i = 0; i < size; ++i) { - // here reinterpret_cast is used to convert uint8& to bool&, - // otherwise it will be implicitly converted to bool, causing the rvalue to fail to match the lvalue. - // the same goes for the following. - vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], invalid); - - if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, vec_from0[i], vec_from1[i]); - } + using NativeType0 = DataType0::FieldType; + using NativeType1 = DataType1::FieldType; + using ValueType0 = date_cast::TypeToValueTypeV; + // arg1 maybe just delta value(e.g. DataTypeInt32, not datelike type) + constexpr static bool CastType1 = std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; + + static void throw_out_of_bound(NativeType0 arg0, NativeType1 arg1) { + auto value0 = binary_cast(arg0); + char buf0[40]; + char* end0 = value0.to_string(buf0); + if constexpr (CastType1) { + auto value1 = binary_cast>(arg1); + char buf1[40]; + char* end1 = value1.to_string(buf1); + throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} of {}, {} out of range", + Transform::name, std::string_view {buf0, end0 - 1}, + std::string_view {buf1, end1 - 1}); // minus 1 to skip /0 + } else { + throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} of {}, {} out of range", + Transform::name, std::string_view {buf0, end0 - 1}, arg1); } } - // use for (DateTime, int32) -> other_type - static void vector_vector(const PaddedPODArray& vec_from0, - const PaddedPODArray& vec_from1, - PaddedPODArray& vec_to, NullMap& null_map) { - size_t size = vec_from0.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) - vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], - reinterpret_cast(null_map[i])); - } - static void vector_vector(const PaddedPODArray& vec_from0, - const PaddedPODArray& vec_from1, - PaddedPODArray& vec_to) { + // execute on the null value's nested value may cause false positive exception, so use nullmaps to skip them. + static void vector_vector(const PaddedPODArray& vec_from0, + const PaddedPODArray& vec_from1, + PaddedPODArray& vec_to, const NullMap* nullmap0, + const NullMap* nullmap1) { size_t size = vec_from0.size(); vec_to.resize(size); + bool invalid = false; - bool invalid = true; for (size_t i = 0; i < size; ++i) { + if ((nullmap0 && (*nullmap0)[i]) || (nullmap1 && (*nullmap1)[i])) [[unlikely]] { + continue; + } vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], invalid); if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, vec_from0[i], vec_from1[i]); + throw_out_of_bound(vec_from0[i], vec_from1[i]); } } } - // use for (DateTime, const DateTime) -> other_type - static void vector_constant(const PaddedPODArray& vec_from, - PaddedPODArray& vec_to, NullMap& null_map, Int128& delta) { - size_t size = vec_from.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) { - vec_to[i] = - Transform::execute(vec_from[i], delta, reinterpret_cast(null_map[i])); + static void vector_constant(const PaddedPODArray& vec_from, + PaddedPODArray& vec_to, const NativeType1& delta, + const NullMap* nullmap0, const NullMap* nullmap1) { + if (nullmap1 && (*nullmap1)[0]) [[unlikely]] { + return; } - } - static void vector_constant(const PaddedPODArray& vec_from, - PaddedPODArray& vec_to, Int128& delta) { size_t size = vec_from.size(); vec_to.resize(size); + bool invalid = false; - bool invalid = true; for (size_t i = 0; i < size; ++i) { - vec_to[i] = Transform::execute(vec_from[i], delta, invalid); - - if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, vec_from[i], delta); + if (nullmap0 && (*nullmap0)[i]) [[unlikely]] { + continue; } - } - } - - // use for (DateTime, const ColumnNumber) -> other_type - static void vector_constant(const PaddedPODArray& vec_from, - PaddedPODArray& vec_to, NullMap& null_map, Int64 delta) { - size_t size = vec_from.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) { - vec_to[i] = - Transform::execute(vec_from[i], delta, reinterpret_cast(null_map[i])); - } - } - static void vector_constant(const PaddedPODArray& vec_from, - PaddedPODArray& vec_to, Int64 delta) { - size_t size = vec_from.size(); - vec_to.resize(size); - bool invalid = true; - - for (size_t i = 0; i < size; ++i) { vec_to[i] = Transform::execute(vec_from[i], delta, invalid); if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, vec_from[i], delta); + throw_out_of_bound(vec_from[i], delta); } } } - // use for (const DateTime, ColumnNumber) -> other_type - static void constant_vector(const FromType1& from, PaddedPODArray& vec_to, - NullMap& null_map, const IColumn& delta) { - size_t size = delta.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) { - vec_to[i] = Transform::execute(from, delta.get_int(i), - reinterpret_cast(null_map[i])); + static void constant_vector(const NativeType0& from, PaddedPODArray& vec_to, + const PaddedPODArray& delta, const NullMap* nullmap0, + const NullMap* nullmap1) { + if (nullmap0 && (*nullmap0)[0]) [[unlikely]] { + return; } - } - static void constant_vector(const FromType1& from, PaddedPODArray& vec_to, - const IColumn& delta) { size_t size = delta.size(); vec_to.resize(size); - bool invalid = true; + bool invalid = false; for (size_t i = 0; i < size; ++i) { - vec_to[i] = Transform::execute(from, delta.get_int(i), invalid); - - if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, from, delta.get_int(i)); + if (nullmap1 && (*nullmap1)[i]) [[unlikely]] { + continue; } - } - } - - static void constant_vector(const FromType1& from, PaddedPODArray& vec_to, - NullMap& null_map, const PaddedPODArray& delta) { - size_t size = delta.size(); - vec_to.resize(size); - null_map.resize_fill(size, false); - - for (size_t i = 0; i < size; ++i) { - vec_to[i] = Transform::execute(from, delta[i], reinterpret_cast(null_map[i])); - } - } - - static void constant_vector(const FromType1& from, PaddedPODArray& vec_to, - const PaddedPODArray& delta) { - size_t size = delta.size(); - vec_to.resize(size); - bool invalid = true; - - for (size_t i = 0; i < size; ++i) { vec_to[i] = Transform::execute(from, delta[i], invalid); if (UNLIKELY(invalid)) { - throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} out of range", - Transform::name, from, delta[i]); - } - } - } -}; - -template -struct DateTimeAddIntervalImpl { - static Status execute(Block& block, const ColumnNumbers& arguments, uint32_t result, - size_t input_rows_count) { - using ToType = typename Transform::ReturnType::FieldType; - using Op = DateTimeOp; - - const ColumnPtr source_col = remove_nullable(block.get_by_position(arguments[0]).column); - const auto is_nullable = block.get_by_position(result).type->is_nullable(); - if (const auto* sources = check_and_get_column>(source_col.get())) { - auto col_to = ColumnVector::create(); - auto delta_column_ptr = remove_nullable(block.get_by_position(arguments[1]).column); - const IColumn& delta_column = *delta_column_ptr; - - if (is_nullable) { - auto null_map = ColumnUInt8::create(input_rows_count, 0); - if (const auto* delta_const_column = - typeid_cast(&delta_column)) { - if (delta_const_column->get_field().get_type() == Field::Types::Int128) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - null_map->get_data(), - delta_const_column->get_field().get()); - } else if (delta_const_column->get_field().get_type() == Field::Types::Int64) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - null_map->get_data(), - delta_const_column->get_field().get()); - } else if (delta_const_column->get_field().get_type() == Field::Types::UInt64) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - null_map->get_data(), - delta_const_column->get_field().get()); - } else { - Op::vector_constant(sources->get_data(), col_to->get_data(), - null_map->get_data(), - delta_const_column->get_field().get()); - } - } else { - if (const auto* delta_vec_column0 = - check_and_get_column>(delta_column)) { - Op::vector_vector(sources->get_data(), delta_vec_column0->get_data(), - col_to->get_data(), null_map->get_data()); - } else { - const auto* delta_vec_column1 = - check_and_get_column>(delta_column); - DCHECK(delta_vec_column1 != nullptr); - Op::vector_vector(sources->get_data(), delta_vec_column1->get_data(), - col_to->get_data(), null_map->get_data()); - } - } - if (const auto* nullable_col = check_and_get_column( - block.get_by_position(arguments[0]).column.get())) { - NullMap& result_null_map = assert_cast(*null_map).get_data(); - const NullMap& src_null_map = - assert_cast(nullable_col->get_null_map_column()) - .get_data(); - - VectorizedUtils::update_null_map(result_null_map, src_null_map); - } - if (const auto* nullable_col = check_and_get_column( - block.get_by_position(arguments[1]).column.get())) { - NullMap& result_null_map = assert_cast(*null_map).get_data(); - const NullMap& src_null_map = - assert_cast(nullable_col->get_null_map_column()) - .get_data(); - - VectorizedUtils::update_null_map(result_null_map, src_null_map); - } - block.get_by_position(result).column = - ColumnNullable::create(std::move(col_to), std::move(null_map)); - } else { - if (const auto* delta_const_column = - typeid_cast(&delta_column)) { - if (delta_const_column->get_field().get_type() == Field::Types::Int128) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - delta_const_column->get_field().get()); - } else if (delta_const_column->get_field().get_type() == Field::Types::Int64) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - delta_const_column->get_field().get()); - } else if (delta_const_column->get_field().get_type() == Field::Types::UInt64) { - Op::vector_constant(sources->get_data(), col_to->get_data(), - delta_const_column->get_field().get()); - } else { - Op::vector_constant(sources->get_data(), col_to->get_data(), - delta_const_column->get_field().get()); - } - } else { - if (const auto* delta_vec_column0 = - check_and_get_column>(delta_column)) { - Op::vector_vector(sources->get_data(), delta_vec_column0->get_data(), - col_to->get_data()); - } else { - const auto* delta_vec_column1 = - check_and_get_column>(delta_column); - DCHECK(delta_vec_column1 != nullptr); - Op::vector_vector(sources->get_data(), delta_vec_column1->get_data(), - col_to->get_data()); - } - } - block.replace_by_position(result, std::move(col_to)); + throw_out_of_bound(from, delta[i]); } - } else if (const auto* sources_const = - check_and_get_column_const>(source_col.get())) { - auto col_to = ColumnVector::create(); - if (is_nullable) { - auto null_map = ColumnUInt8::create(input_rows_count, 0); - auto not_nullable_column_ptr_arg1 = - remove_nullable(block.get_by_position(arguments[1]).column); - if (const auto* delta_vec_column = check_and_get_column>( - *not_nullable_column_ptr_arg1)) { - Op::constant_vector(sources_const->template get_value(), - col_to->get_data(), null_map->get_data(), - delta_vec_column->get_data()); - } else { - Op::constant_vector(sources_const->template get_value(), - col_to->get_data(), null_map->get_data(), - *not_nullable_column_ptr_arg1); - } - if (const auto* nullable_col = check_and_get_column( - block.get_by_position(arguments[0]).column.get())) { - NullMap& result_null_map = assert_cast(*null_map).get_data(); - const NullMap& src_null_map = - assert_cast(nullable_col->get_null_map_column()) - .get_data(); - - VectorizedUtils::update_null_map(result_null_map, src_null_map); - } - if (const auto* nullable_col = check_and_get_column( - block.get_by_position(arguments[1]).column.get())) { - NullMap& result_null_map = assert_cast(*null_map).get_data(); - const NullMap& src_null_map = - assert_cast(nullable_col->get_null_map_column()) - .get_data(); - - VectorizedUtils::update_null_map(result_null_map, src_null_map); - } - block.get_by_position(result).column = - ColumnNullable::create(std::move(col_to), std::move(null_map)); - } else { - if (const auto* delta_vec_column = check_and_get_column>( - *block.get_by_position(arguments[1]).column)) { - Op::constant_vector(sources_const->template get_value(), - col_to->get_data(), delta_vec_column->get_data()); - } else { - Op::constant_vector(sources_const->template get_value(), - col_to->get_data(), - *block.get_by_position(arguments[1]).column); - } - block.replace_by_position(result, std::move(col_to)); - } - } else { - return Status::RuntimeError( - "Illegal column {} of first argument and type {} of function {}", - block.get_by_position(arguments[0]).column->get_name(), - block.get_by_position(arguments[0]).type->get_name(), Transform::name); } - return Status::OK(); } }; +// Used for date(time) add/sub date(time)/integer. the input types are variadic and dispatch in execute. the return type is +// decided by Transform template class FunctionDateOrDateTimeComputation : public IFunction { public: @@ -708,41 +423,14 @@ class FunctionDateOrDateTimeComputation : public IFunction { size_t get_number_of_arguments() const override { return 0; } DataTypes get_variadic_argument_types_impl() const override { - if constexpr (has_variadic_argument) return Transform::get_variadic_argument_types(); + if constexpr (has_variadic_argument) { + return Transform::get_variadic_argument_types(); + } return {}; } bool use_default_implementation_for_nulls() const override { return false; } DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { - if (arguments.size() != 2 && arguments.size() != 3) { - throw doris::Exception(ErrorCode::INVALID_ARGUMENT, - "Number of arguments for function {} doesn't match: passed {} , " - "should be 2 or 3", - get_name(), arguments.size()); - } - - if (arguments.size() == 2) { - if (!is_date_or_datetime(remove_nullable(arguments[0].type)) && - !is_date_v2_or_datetime_v2(remove_nullable(arguments[0].type))) { - throw doris::Exception( - ErrorCode::INVALID_ARGUMENT, - "Illegal type {} of argument of function {}. Should be a date or a date " - "with time", - arguments[0].type->get_name(), get_name()); - } - } else { - if (!WhichDataType(remove_nullable(arguments[0].type)).is_date_time() || - !WhichDataType(remove_nullable(arguments[0].type)).is_date_time_v2() || - !WhichDataType(remove_nullable(arguments[2].type)).is_string()) { - throw doris::Exception( - ErrorCode::INVALID_ARGUMENT, - "Function {} supports 2 or 3 arguments. The 1st argument must be of type " - "Date or DateTime. The 2nd argument must be number. The 3rd argument " - "(optional) must be a constant string with timezone name. The timezone " - "argument is allowed only when the 1st argument has the type DateTime", - get_name()); - } - } RETURN_REAL_TYPE_FOR_DATEV2_FUNCTION(typename Transform::ReturnType); } @@ -753,48 +441,164 @@ class FunctionDateOrDateTimeComputation : public IFunction { WhichDataType which1(remove_nullable(first_arg_type)); WhichDataType which2(remove_nullable(second_arg_type)); + /// now dispatch with the two arguments' type. no need to consider return type because the same arguments decide a + /// unique return type which could be extracted from Transform. + + // for all `xxx_add/sub`, the second arg is int32. + // for `week/yearweek`, if it has the second arg, it's int32. + // in these situations, the first would be any datelike type. + if (which2.is_int32()) { + switch (which1.idx) { + case TypeIndex::Date: + return execute_inner(block, arguments, result, + input_rows_count); + break; + case TypeIndex::DateTime: + return execute_inner(block, arguments, result, + input_rows_count); + break; + case TypeIndex::DateV2: + return execute_inner(block, arguments, result, + input_rows_count); + break; + case TypeIndex::DateTimeV2: + return execute_inner(block, arguments, result, + input_rows_count); + break; + default: + return Status::InternalError("Illegal argument {} and {} of function {}", + block.get_by_position(arguments[0]).type->get_name(), + block.get_by_position(arguments[1]).type->get_name(), + get_name()); + } + } + // then consider datelike - datelike. everything is possible here as well. + // for `xxx_diff`, every combination of V2 is possible. but for V1 we only support Datetime - Datetime if (which1.is_date_v2() && which2.is_date_v2()) { - return DateTimeAddIntervalImpl::execute(block, arguments, - result, - input_rows_count); + return execute_inner(block, arguments, result, + input_rows_count); } else if (which1.is_date_time_v2() && which2.is_date_time_v2()) { - return DateTimeAddIntervalImpl< - DataTypeDateTimeV2::FieldType, Transform, - DataTypeDateTimeV2::FieldType>::execute(block, arguments, result, - input_rows_count); - } else if (which1.is_date_time() && which2.is_date_time()) { - return DateTimeAddIntervalImpl::execute(block, arguments, - result, - input_rows_count); + return execute_inner(block, arguments, result, + input_rows_count); } else if (which1.is_date_v2() && which2.is_date_time_v2()) { - return DateTimeAddIntervalImpl< - DataTypeDateV2::FieldType, Transform, - DataTypeDateTimeV2::FieldType>::execute(block, arguments, result, - input_rows_count); + return execute_inner(block, arguments, result, + input_rows_count); } else if (which1.is_date_time_v2() && which2.is_date_v2()) { - return DateTimeAddIntervalImpl::execute(block, arguments, - result, - input_rows_count); - } else if (which1.is_date()) { - return DateTimeAddIntervalImpl::execute( - block, arguments, result, input_rows_count); - } else if (which1.is_date_time()) { - return DateTimeAddIntervalImpl::execute( - block, arguments, result, input_rows_count); - } else if (which1.is_date_v2()) { - return DateTimeAddIntervalImpl::execute( - block, arguments, result, input_rows_count); - } else if (which1.is_date_time_v2()) { - return DateTimeAddIntervalImpl::execute( - block, arguments, result, input_rows_count); - } else { - return Status::RuntimeError("Illegal type {} of argument of function {}", - block.get_by_position(arguments[0]).type->get_name(), - get_name()); + return execute_inner(block, arguments, result, + input_rows_count); + } else if (which1.is_date_time() && which2.is_date_time()) { + return execute_inner(block, arguments, result, + input_rows_count); } + return Status::InternalError("Illegal argument {} and {} of function {}", + block.get_by_position(arguments[0]).type->get_name(), + block.get_by_position(arguments[1]).type->get_name(), + get_name()); + } + + template + static Status execute_inner(Block& block, const ColumnNumbers& arguments, uint32_t result, + size_t input_rows_count) { + using NativeType0 = DataType0::FieldType; + using NativeType1 = DataType1::FieldType; + using ResFieldType = typename Transform::ReturnType::FieldType; + using Op = DateTimeOp; + + auto get_null_map = [](const ColumnPtr& col) -> const NullMap* { + if (col->is_nullable()) { + return &static_cast(*col).get_null_map_data(); + } + // Const(Nullable) + if (const auto* const_col = check_and_get_column(col.get()); + const_col != nullptr && const_col->is_concrete_nullable()) { + return &static_cast(const_col->get_data_column()) + .get_null_map_data(); + } + return nullptr; + }; + + //ATTN: those null maps may be nullmap of ColumnConst(only 1 row) + // src column is always datelike type. + ColumnPtr& col0 = block.get_by_position(arguments[0]).column; + const NullMap* nullmap0 = get_null_map(col0); + // the second column may be delta column(xx_add/sub) or datelike column(xxx_diff) + ColumnPtr& col1 = block.get_by_position(arguments[1]).column; + const NullMap* nullmap1 = get_null_map(col1); + + // if null wrapped, extract nested column as src_nested_col + const ColumnPtr src_nested_col = remove_nullable(col0); + const auto result_nullable = block.get_by_position(result).type->is_nullable(); + auto res_col = ColumnVector::create(); + + // vector-const or vector-vector + if (const auto* sources = + check_and_get_column>(src_nested_col.get())) { + const ColumnPtr nest_col1 = remove_nullable(col1); + bool rconst = false; + // vector-const + if (const auto* nest_col1_const = check_and_get_column(*nest_col1)) { + rconst = true; + const auto col1_inside_const = assert_cast&>( + nest_col1_const->get_data_column()); + Op::vector_constant(sources->get_data(), res_col->get_data(), + col1_inside_const.get_data()[0], nullmap0, nullmap1); + } else { // vector-vector + const auto concrete_col1 = + assert_cast&>(*nest_col1); + Op::vector_vector(sources->get_data(), concrete_col1.get_data(), + res_col->get_data(), nullmap0, nullmap1); + } + + // update result nullmap with inputs + if (result_nullable) { + auto null_map = ColumnBool::create(input_rows_count, 0); + NullMap& result_null_map = assert_cast(*null_map).get_data(); + if (nullmap0) { + VectorizedUtils::update_null_map(result_null_map, *nullmap0); + } + if (nullmap1) { + VectorizedUtils::update_null_map(result_null_map, *nullmap1, rconst); + } + block.get_by_position(result).column = + ColumnNullable::create(std::move(res_col), std::move(null_map)); + } else { + block.replace_by_position(result, std::move(res_col)); + } + } else if (const auto* sources_const = + check_and_get_column_const>( + src_nested_col.get())) { + // const-vector + const auto col0_inside_const = + assert_cast&>(sources_const->get_data_column()); + const ColumnPtr nested_col1 = remove_nullable(col1); + const auto concrete_col1 = assert_cast&>(*nested_col1); + Op::constant_vector(col0_inside_const.get_data()[0], res_col->get_data(), + concrete_col1.get_data(), nullmap0, nullmap1); + + // update result nullmap with inputs + if (result_nullable) { + auto null_map = ColumnBool::create(input_rows_count, 0); + NullMap& result_null_map = assert_cast(*null_map).get_data(); + if (nullmap0) { + VectorizedUtils::update_null_map(result_null_map, *nullmap0, true); + } + if (nullmap1) { // no const-const here. default impl deal it. + VectorizedUtils::update_null_map(result_null_map, *nullmap1); + } + block.get_by_position(result).column = + ColumnNullable::create(std::move(res_col), std::move(null_map)); + } else { + block.replace_by_position(result, std::move(res_col)); + } + } else { // no const-const here. default impl deal it. + return Status::InternalError( + "Illegel columns for function {}:\n1: {} with type {}\n2: {} with type {}", + Transform::name, block.get_by_position(arguments[0]).name, + block.get_by_position(arguments[0]).type->get_name(), + block.get_by_position(arguments[1]).name, + block.get_by_position(arguments[1]).type->get_name()); + } + return Status::OK(); } }; @@ -1170,7 +974,9 @@ class CurrentDateFunctionBuilder : public FunctionBuilderImpl { FunctionBasePtr build_impl(const ColumnsWithTypeAndName& arguments, const DataTypePtr& return_type) const override { DataTypes data_types(arguments.size()); - for (size_t i = 0; i < arguments.size(); ++i) data_types[i] = arguments[i].type; + for (size_t i = 0; i < arguments.size(); ++i) { + data_types[i] = arguments[i].type; + } if (is_date_v2(return_type)) { auto function = FunctionCurrentDateOrDateTime< CurrentDateImpl>::create(); diff --git a/be/src/vec/functions/function_date_or_datetime_computation_v2.cpp b/be/src/vec/functions/function_date_or_datetime_computation_v2.cpp index ec9560456c131a..db43bf1818d38f 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation_v2.cpp +++ b/be/src/vec/functions/function_date_or_datetime_computation_v2.cpp @@ -95,14 +95,14 @@ using FunctionDatetimeV2SubYears = FUNCTION_DATEV2_WITH_TWO_ARGS(NAME, IMPL, DataTypeDateTimeV2, DataTypeDateV2) \ FUNCTION_DATEV2_WITH_TWO_ARGS(NAME, IMPL, DataTypeDateV2, DataTypeDateTimeV2) \ FUNCTION_DATEV2_WITH_TWO_ARGS(NAME, IMPL, DataTypeDateV2, DataTypeDateV2) - +// these diff functions accept all v2 types. but for v1 only datetime. ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2DateDiff, DateDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2TimeDiff, TimeDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2YearsDiff, YearsDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2MonthsDiff, MonthsDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2WeeksDiff, WeeksDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2HoursDiff, HoursDiffImpl) -ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2MinutesDiff, MintueSDiffImpl) +ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2MinutesDiff, MintuesDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2SecondsDiff, SecondsDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2DaysDiff, DaysDiffImpl) ALL_FUNCTION_DATEV2_WITH_TWO_ARGS(FunctionDatetimeV2MilliSecondsDiff, MilliSecondsDiffImpl) diff --git a/be/src/vec/functions/function_helpers.h b/be/src/vec/functions/function_helpers.h index 8c7eec28fe2f6f..818badeee4551b 100644 --- a/be/src/vec/functions/function_helpers.h +++ b/be/src/vec/functions/function_helpers.h @@ -20,10 +20,8 @@ #pragma once -#include - +#include #include -#include #include "vec/columns/column.h" #include "vec/columns/column_const.h" @@ -53,11 +51,15 @@ const Type* check_and_get_data_type(const IDataType* data_type) { template const ColumnConst* check_and_get_column_const(const IColumn* column) { - if (!column || !is_column_const(*column)) return {}; + if (!column || !is_column_const(*column)) { + return nullptr; + } - const ColumnConst* res = assert_cast(column); + const auto* res = assert_cast(column); - if (!check_column(&res->get_data_column())) return {}; + if (!check_column(&res->get_data_column())) { + return nullptr; + } return res; } @@ -66,7 +68,9 @@ template const Type* check_and_get_column_constData(const IColumn* column) { const ColumnConst* res = check_and_get_column_const(column); - if (!res) return {}; + if (!res) { + return nullptr; + } return static_cast(&res->get_data_column()); } diff --git a/be/test/vec/function/function_test_util.h b/be/test/vec/function/function_test_util.h index a3809bf8ec6a48..1c4c0906b80d3e 100644 --- a/be/test/vec/function/function_test_util.h +++ b/be/test/vec/function/function_test_util.h @@ -69,7 +69,7 @@ using Row = std::pair; using DataSet = std::vector; using InputTypeSet = std::vector; -// FIXME: should use exception or expected to deal null value.w +// FIXME: should use exception or expected to deal null value. int64_t str_to_date_time(std::string datetime_str, bool data_time = true); uint32_t str_to_date_v2(std::string datetime_str, std::string datetime_format); uint64_t str_to_datetime_v2(std::string datetime_str, std::string datetime_format); diff --git a/be/test/vec/function/function_time_test.cpp b/be/test/vec/function/function_time_test.cpp index a4299de3557608..ddfc722c7ab452 100644 --- a/be/test/vec/function/function_time_test.cpp +++ b/be/test/vec/function/function_time_test.cpp @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. +#include #include #include @@ -299,14 +300,22 @@ TEST(VTimestampFunctionsTest, years_add_test) { InputTypeSet input_types = {TypeIndex::DateTime, TypeIndex::Int32}; - DataSet data_set = { - {{std::string("2020-05-23 00:00:00"), 5}, str_to_date_time("2025-05-23 00:00:00")}, - {{std::string("2020-05-23 00:00:00"), -5}, str_to_date_time("2015-05-23 00:00:00")}, - {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23 00:00:00"), 8000}, Null()}, - {{Null(), 5}, Null()}}; + { + DataSet data_set = { + {{std::string("2020-05-23 00:00:00"), 5}, str_to_date_time("2025-05-23 00:00:00")}, + {{std::string("2020-05-23 00:00:00"), -5}, str_to_date_time("2015-05-23 00:00:00")}, + {{std::string(""), 5}, Null()}, + {{Null(), 5}, Null()}}; - static_cast(check_function(func_name, input_types, data_set)); + static_cast(check_function(func_name, input_types, data_set)); + } + + { + DataSet data_set = {{{std::string("2020-05-23 00:00:00"), 8000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } } TEST(VTimestampFunctionsTest, years_sub_test) { @@ -314,14 +323,22 @@ TEST(VTimestampFunctionsTest, years_sub_test) { InputTypeSet input_types = {TypeIndex::DateTime, TypeIndex::Int32}; - DataSet data_set = { - {{std::string("2020-05-23 00:00:00"), 5}, str_to_date_time("2015-05-23 00:00:00")}, - {{std::string("2020-05-23 00:00:00"), -5}, str_to_date_time("2025-05-23 00:00:00")}, - {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23 00:00:00"), 3000}, Null()}, - {{Null(), 5}, Null()}}; + { + DataSet data_set = { + {{std::string("2020-05-23 00:00:00"), 5}, str_to_date_time("2015-05-23 00:00:00")}, + {{std::string("2020-05-23 00:00:00"), -5}, str_to_date_time("2025-05-23 00:00:00")}, + {{std::string(""), 5}, Null()}, + {{Null(), 5}, Null()}}; - static_cast(check_function(func_name, input_types, data_set)); + static_cast(check_function(func_name, input_types, data_set)); + } + + { + DataSet data_set = {{{std::string("2020-05-23 00:00:00"), 3000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } } TEST(VTimestampFunctionsTest, months_add_test) { @@ -1043,11 +1060,18 @@ TEST(VTimestampFunctionsTest, years_add_v2_test) { {{std::string("2020-05-23"), 5}, str_to_date_v2("2025-05-23", "%Y-%m-%d")}, {{std::string("2020-05-23"), -5}, str_to_date_v2("2015-05-23", "%Y-%m-%d")}, {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23"), 8000}, Null()}, {{Null(), 5}, Null()}}; static_cast(check_function(func_name, input_types, data_set)); } + { + InputTypeSet input_types = {TypeIndex::DateV2, TypeIndex::Int32}; + + DataSet data_set = {{{std::string("2020-05-23"), 8000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } { InputTypeSet input_types = {TypeIndex::DateTimeV2, TypeIndex::Int32}; @@ -1057,12 +1081,19 @@ TEST(VTimestampFunctionsTest, years_add_v2_test) { {{std::string("2020-05-23 00:00:11.123"), -5}, str_to_datetime_v2("2015-05-23 00:00:11.123", "%Y-%m-%d %H:%i:%s.%f")}, {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23 00:00:11.123"), 8000}, Null()}, {{Null(), 5}, Null()}}; static_cast( check_function(func_name, input_types, data_set)); } + { + InputTypeSet input_types = {TypeIndex::DateTimeV2, TypeIndex::Int32}; + + DataSet data_set = {{{std::string("2020-05-23 00:00:11.123"), 8000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } } TEST(VTimestampFunctionsTest, years_sub_v2_test) { @@ -1075,11 +1106,19 @@ TEST(VTimestampFunctionsTest, years_sub_v2_test) { {{std::string("2020-05-23"), 5}, str_to_date_v2("2015-05-23", "%Y-%m-%d")}, {{std::string("2020-05-23"), -5}, str_to_date_v2("2025-05-23", "%Y-%m-%d")}, {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23"), 3000}, Null()}, {{Null(), 5}, Null()}}; static_cast(check_function(func_name, input_types, data_set)); } + { + InputTypeSet input_types = {TypeIndex::DateV2, TypeIndex::Int32}; + + DataSet data_set = {{{std::string("2020-05-23"), 3000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } + { InputTypeSet input_types = {TypeIndex::DateTimeV2, TypeIndex::Int32}; @@ -1088,12 +1127,19 @@ TEST(VTimestampFunctionsTest, years_sub_v2_test) { {{std::string("2020-05-23 00:00:11.123"), -5}, str_to_datetime_v2("2025-05-23 00:00:11.123", "%Y-%m-%d %H:%i:%s.%f")}, {{std::string(""), 5}, Null()}, - {{std::string("2020-05-23 00:00:11.123"), 3000}, Null()}, {{Null(), 5}, Null()}}; static_cast( check_function(func_name, input_types, data_set)); } + { + InputTypeSet input_types = {TypeIndex::DateTimeV2, TypeIndex::Int32}; + + DataSet data_set = {{{std::string("2020-05-23 00:00:11.123"), 3000}, Null()}}; + + EXPECT_ANY_THROW(static_cast( + check_function(func_name, input_types, data_set))); + } } TEST(VTimestampFunctionsTest, months_add_v2_test) { diff --git a/regression-test/suites/correctness/test_date_function_const.groovy b/regression-test/suites/correctness/test_date_function_const.groovy index d1ba4db4e68987..e9bf11bd24ebd6 100644 --- a/regression-test/suites/correctness/test_date_function_const.groovy +++ b/regression-test/suites/correctness/test_date_function_const.groovy @@ -61,6 +61,6 @@ suite("test_date_function_const") { test { sql """select date_add("1900-01-01 12:00:00.123456", interval 10000000000 month);""" - exception "Operation months_add 133705200962757184 1410065408 out of range" + exception "Operation months_add of 1900-01-01 12:00:00.123456, 1410065408 out of range" } } diff --git a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy index 282a28a903e4a0..53b7385b1535df 100644 --- a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy +++ b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy @@ -14,6 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + suite("test_date_or_datetime_computation_negative") { sql """ CREATE TABLE IF NOT EXISTS test_date_or_datetime_computation_negative ( `row_id` LARGEINT NOT NULL, @@ -50,8 +51,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 year) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 year), date_sub(dateV2_null, interval 1 year), date_sub(datetime_null, interval 1 year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_1 """SELECT date_sub(date_null, interval 1 year), date_sub(dateV2_null, interval 1 year), date_sub(datetime_null, interval 1 year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_sub(date, interval 1 month) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" @@ -65,8 +69,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 month) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 month), date_sub(dateV2_null, interval 1 month), date_sub(datetime_null, interval 1 month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_2 """SELECT date_sub(date_null, interval 1 month), date_sub(dateV2_null, interval 1 month), date_sub(datetime_null, interval 1 month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """ SELECT date_sub(date, interval 1 week) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" @@ -80,10 +87,12 @@ suite("test_date_or_datetime_computation_negative") { sql """ SELECT date_sub(datetime, interval 1 week) FROM test_date_or_datetime_computation_negative WHERE row_id=1; """ check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 week), date_sub(dateV2_null, interval 1 week), date_sub(datetime_null, interval 1 week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_3 """SELECT date_sub(date_null, interval 1 week), date_sub(dateV2_null, interval 1 week), date_sub(datetime_null, interval 1 week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" - test { sql """SELECT date_sub(date, interval 1 day) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> @@ -96,10 +105,12 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 day) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 day), date_sub(dateV2_null, interval 1 day), date_sub(datetime_null, interval 1 day) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_4 """SELECT date_sub(date_null, interval 1 day), date_sub(dateV2_null, interval 1 day), date_sub(datetime_null, interval 1 day) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" - test { sql """SELECT date_sub(date, interval 1 hour) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> @@ -112,8 +123,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 hour) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """ SELECT date_sub(date_null, interval 1 hour), date_sub(dateV2_null, interval 1 hour), date_sub(datetime_null, interval 1 hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_5 """ SELECT date_sub(date_null, interval 1 hour), date_sub(dateV2_null, interval 1 hour), date_sub(datetime_null, interval 1 hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_sub(date, interval 1 minute) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" @@ -127,8 +141,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 minute) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 minute), date_sub(dateV2_null, interval 1 minute), date_sub(datetime_null, interval 1 minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_6 """SELECT date_sub(date_null, interval 1 minute), date_sub(dateV2_null, interval 1 minute), date_sub(datetime_null, interval 1 minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_sub(date, interval 1 second) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" @@ -142,8 +159,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_sub(datetime, interval 1 second) FROM test_date_or_datetime_computation_negative WHERE row_id=1;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_sub(date_null, interval 1 second), date_sub(dateV2_null, interval 1 second), date_sub(datetime_null, interval 1 second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_7 """SELECT date_sub(date_null, interval 1 second), date_sub(dateV2_null, interval 1 second), date_sub(datetime_null, interval 1 second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { @@ -158,8 +178,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 year) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 year), date_add(dateV2_null, interval 1 year), date_add(datetime_null, interval 1 year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_8 """SELECT date_add(date_null, interval 1 year), date_add(dateV2_null, interval 1 year), date_add(datetime_null, interval 1 year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_add(date, interval 1 month) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" @@ -173,8 +196,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 month) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 month), date_add(dateV2_null, interval 1 month), date_add(datetime_null, interval 1 month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_9 """SELECT date_add(date_null, interval 1 month), date_add(dateV2_null, interval 1 month), date_add(datetime_null, interval 1 month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """ SELECT date_add(date, interval 1 week) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" @@ -188,10 +214,12 @@ suite("test_date_or_datetime_computation_negative") { sql """ SELECT date_add(datetime, interval 1 week) FROM test_date_or_datetime_computation_negative WHERE row_id=3; """ check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 week), date_add(dateV2_null, interval 1 week), date_add(datetime_null, interval 1 week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_10 """SELECT date_add(date_null, interval 1 week), date_add(dateV2_null, interval 1 week), date_add(datetime_null, interval 1 week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" - test { sql """SELECT date_add(date, interval 1 day) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> @@ -204,10 +232,12 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 day) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 day), date_add(dateV2_null, interval 1 day), date_add(datetime_null, interval 1 day) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_11 """SELECT date_add(date_null, interval 1 day), date_add(dateV2_null, interval 1 day), date_add(datetime_null, interval 1 day) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" - test { sql """SELECT date_add(date, interval 1 hour) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> @@ -220,8 +250,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 hour) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """ SELECT date_add(date_null, interval 1 hour), date_add(dateV2_null, interval 1 hour), date_add(datetime_null, interval 1 hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_12 """ SELECT date_add(date_null, interval 1 hour), date_add(dateV2_null, interval 1 hour), date_add(datetime_null, interval 1 hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_add(date, interval 1 minute) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" @@ -235,8 +268,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 minute) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 minute), date_add(dateV2_null, interval 1 minute), date_add(datetime_null, interval 1 minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_13 """SELECT date_add(date_null, interval 1 minute), date_add(dateV2_null, interval 1 minute), date_add(datetime_null, interval 1 minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" test { sql """SELECT date_add(date, interval 1 second) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" @@ -250,8 +286,11 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT date_add(datetime, interval 1 second) FROM test_date_or_datetime_computation_negative WHERE row_id=3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT date_add(date_null, interval 1 second), date_add(dateV2_null, interval 1 second), date_add(datetime_null, interval 1 second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_14 """SELECT date_add(date_null, interval 1 second), date_add(dateV2_null, interval 1 second), date_add(datetime_null, interval 1 second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" // TODO: // nagetive test for microseconds_add/milliseconds_add/seconds_add/minutes_add/hours_add/days_add/weeks_add/months_add/years_add @@ -268,8 +307,9 @@ suite("test_date_or_datetime_computation_negative") { sql """SELECT hours_add(datetime, 24) FROM test_date_or_datetime_computation_negative WHERE row_id = 3;""" check {result, exception, startTime, endTime -> assertTrue (exception != null)} + + sql """SELECT hours_add(date_null, 24), hours_add(dateV2_null, 24), hours_add(datetime_null, 24) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" + check {result, exception, startTime, endTime -> + assertTrue (exception != null)} } - qt_select_nullable_15 """SELECT hours_add(date_null, 24), hours_add(dateV2_null, 24), hours_add(datetime_null, 24) FROM test_date_or_datetime_computation_negative ORDER BY row_id;""" - - sql "DROP TABLE test_date_or_datetime_computation_negative" }