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 5cf5158a1eb59d..25febd3efdbec8 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -64,6 +64,7 @@ namespace doris::vectorized { #include "common/compile_check_avoid_begin.h" + /// because all these functions(xxx_add/xxx_sub) defined in FE use Integer as the second value /// so Int64 as delta is needed to support large values. For upstream(FunctionDateOrDateTimeComputation) we use Int64. @@ -113,7 +114,6 @@ auto date_time_add(const typename PrimitiveTypeTraits::DataType::FieldT std::make_shared::DataType>()}; \ } \ } - ADD_TIME_FUNCTION_IMPL(AddMicrosecondsImpl, microseconds_add, MICROSECOND); ADD_TIME_FUNCTION_IMPL(AddMillisecondsImpl, milliseconds_add, MILLISECOND); ADD_TIME_FUNCTION_IMPL(AddSecondsImpl, seconds_add, SECOND); @@ -1246,5 +1246,119 @@ class FunctionTime : public IFunction { return Status::OK(); } }; + +struct AddTimeImpl { + static constexpr auto name = "add_time"; + static bool is_negative() { return false; } +}; + +struct SubTimeImpl { + static constexpr auto name = "sub_time"; + static bool is_negative() { return true; } +}; + +template +class FunctionAddTime : public IFunction { +public: + static constexpr auto name = Impl::name; + static constexpr PrimitiveType ReturnType = PType; + static constexpr PrimitiveType ArgType1 = PType; + static constexpr PrimitiveType ArgType2 = TYPE_TIMEV2; + using ColumnType1 = typename PrimitiveTypeTraits::ColumnType; + using ColumnType2 = typename PrimitiveTypeTraits::ColumnType; + using InputType1 = typename PrimitiveTypeTraits::DataType::FieldType; + using InputType2 = typename PrimitiveTypeTraits::DataType::FieldType; + using ReturnNativeType = InputType1; + using ReturnDataType = typename PrimitiveTypeTraits::DataType; + + String get_name() const override { return name; } + size_t get_number_of_arguments() const override { return 2; } + DataTypes get_variadic_argument_types_impl() const override { + return {std::make_shared::DataType>(), + std::make_shared::DataType>()}; + } + DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { + return std::make_shared(); + } + + ReturnNativeType compute(const InputType1& arg1, const InputType2& arg2) const { + if constexpr (PType == TYPE_DATETIMEV2) { + DateV2Value dtv1 = + binary_cast>(arg1); + auto tv2 = static_cast(arg2); + TimeInterval interval(TimeUnit::MICROSECOND, tv2, Impl::is_negative()); + bool out_range = dtv1.template date_add_interval(interval); + if (UNLIKELY(!out_range)) { + throw Exception(ErrorCode::INVALID_ARGUMENT, + "datetime value is out of range in function {}", name); + } + return binary_cast, ReturnNativeType>(dtv1); + } else if constexpr (PType == TYPE_TIMEV2) { + auto tv1 = static_cast(arg1); + auto tv2 = static_cast(arg2); + double res = TimeValue::limit_with_bound(Impl::is_negative() ? tv1 - tv2 : tv1 + tv2); + return res; + } else { + throw Exception(ErrorCode::FATAL_ERROR, "not support type for function {}", name); + } + } + + static FunctionPtr create() { return std::make_shared(); } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + uint32_t result, size_t input_rows_count) const override { + DCHECK_EQ(arguments.size(), 2); + const auto& [left_col, left_const] = + unpack_if_const(block.get_by_position(arguments[0]).column); + const auto& [right_col, right_const] = + unpack_if_const(block.get_by_position(arguments[1]).column); + ColumnPtr nest_col1 = remove_nullable(left_col); + ColumnPtr nest_col2 = remove_nullable(right_col); + auto res = ColumnVector::create(input_rows_count, 0); + + if (left_const) { + execute_constant_vector(assert_cast(*nest_col1).get_element(0), + assert_cast(*nest_col2).get_data(), + res->get_data(), input_rows_count); + } else if (right_const) { + execute_vector_constant(assert_cast(*nest_col1).get_data(), + assert_cast(*nest_col2).get_element(0), + res->get_data(), input_rows_count); + } else { + execute_vector_vector(assert_cast(*nest_col1).get_data(), + assert_cast(*nest_col2).get_data(), + res->get_data(), input_rows_count); + } + + block.replace_by_position(result, std::move(res)); + return Status::OK(); + } + void execute_vector_vector(const PaddedPODArray& left_col, + const PaddedPODArray& right_col, + PaddedPODArray& res_data, + size_t input_rows_count) const { + for (size_t i = 0; i < input_rows_count; ++i) { + res_data[i] = compute(left_col[i], right_col[i]); + } + } + + void execute_vector_constant(const PaddedPODArray& left_col, + const InputType2 right_value, + PaddedPODArray& res_data, + size_t input_rows_count) const { + for (size_t i = 0; i < input_rows_count; ++i) { + res_data[i] = compute(left_col[i], right_value); + } + } + + void execute_constant_vector(const InputType1 left_value, + const PaddedPODArray& right_col, + PaddedPODArray& res_data, + size_t input_rows_count) const { + for (size_t i = 0; i < input_rows_count; ++i) { + res_data[i] = compute(left_value, right_col[i]); + } + } +}; #include "common/compile_check_avoid_end.h" } // namespace doris::vectorized 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 ee6b92aa45c8b6..53eb2ea9410b14 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 @@ -21,6 +21,10 @@ namespace doris::vectorized { +using FunctionAddTimeV2 = FunctionAddTime; +using FunctionSubTimeV2 = FunctionAddTime; +using FunctionDateTimeAddTimeV2 = FunctionAddTime; +using FunctionDateTimeSubTimeV2 = FunctionAddTime; using FunctionAddDaysV2 = FunctionDateOrDateTimeComputation>; using FunctionAddWeeksV2 = FunctionDateOrDateTimeComputation>; using FunctionAddMonthsV2 = FunctionDateOrDateTimeComputation>; @@ -157,6 +161,10 @@ void register_function_date_time_computation_v2(SimpleFunctionFactory& factory) factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function(); + factory.register_function(); + factory.register_function(); + factory.register_function(); } } // namespace doris::vectorized diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java index c9b3f441a3a4fc..01245fe80af72b 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/ScalarType.java @@ -923,6 +923,9 @@ public boolean matchesType(Type t) { if (isDatetimeV2() && scalarType.isDatetimeV2()) { return true; } + if (isTimeV2() && scalarType.isTimeV2()) { + return true; + } if (isVariantType() && scalarType.isVariantType()) { return true; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java index 01ae64db3c1a1e..a55717cb78a9be 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java @@ -33,6 +33,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Abs; import org.apache.doris.nereids.trees.expressions.functions.scalar.Acos; import org.apache.doris.nereids.trees.expressions.functions.scalar.Acosh; +import org.apache.doris.nereids.trees.expressions.functions.scalar.AddTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.AesDecrypt; import org.apache.doris.nereids.trees.expressions.functions.scalar.AesEncrypt; import org.apache.doris.nereids.trees.expressions.functions.scalar.AppendTrailingCharIfAbsent; @@ -460,6 +461,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace; +import org.apache.doris.nereids.trees.expressions.functions.scalar.SubTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubstringIndex; import org.apache.doris.nereids.trees.expressions.functions.scalar.Tan; @@ -542,6 +544,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(Abs.class, "abs"), scalar(Acos.class, "acos"), scalar(Acosh.class, "acosh"), + scalar(AddTime.class, "add_time"), scalar(AesDecrypt.class, "aes_decrypt"), scalar(AesEncrypt.class, "aes_encrypt"), scalar(AppendTrailingCharIfAbsent.class, "append_trailing_char_if_absent"), @@ -970,7 +973,8 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(SubBitmap.class, "sub_bitmap"), scalar(SubReplace.class, "sub_replace"), scalar(Substring.class, "substr", "substring", "mid"), - scalar(SubstringIndex.class, "substring_index"), + scalar(SubTime.class, "sub_time"), + scalar(SubstringIndex.class, "substring_index"), scalar(Tan.class, "tan"), scalar(Tanh.class, "tanh"), scalar(Time.class, "time"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureForTimeArithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureForTimeArithmetic.java new file mode 100644 index 00000000000000..980ab09ea10339 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureForTimeArithmetic.java @@ -0,0 +1,119 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.annotation.Developing; +import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral; +import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal; +import org.apache.doris.nereids.types.TimeV2Type; + +/** + * use for literal in time arithmetic, such as time_add('12:34:56', INTERVAL 1 second). + * if the first argument is string like literal and could cast to legal time literal, + * then use TimeType signature rather than DatetimeV2 signature(usually in SIGNATURES). + * but won't add any signatures to SIGNATURES. normally there should be a TimeType signature in SIGNATURES. + */ +@Developing +public interface ComputeSignatureForTimeArithmetic extends ComputeSignature { + + @Override + default FunctionSignature computeSignature(FunctionSignature signature) { + if (child(0) instanceof StringLikeLiteral) { + try { + String s = ((StringLikeLiteral) child(0)).getStringValue().trim(); + if (isTimeFormat(s)) { + new TimeV2Literal(s); // check legality + TimeV2Type t1 = TimeV2Type.forTypeFromString(s); + TimeV2Type t2 = TimeV2Type.INSTANCE; + if (child(1) instanceof StringLikeLiteral) { + String s2 = ((StringLikeLiteral) child(1)).getStringValue().trim(); + new TimeV2Literal(s2); // check legality + t2 = TimeV2Type.forTypeFromString(s2); + } + int maxScale = Math.max(t1.getScale(), t2.getScale()); + TimeV2Type resultType = TimeV2Type.of(maxScale); + return FunctionSignature.ret(resultType).args(resultType, resultType); + } + } catch (Exception e) { + // string like literal cannot cast to a legal time literal + } + } + // Call the parent implementation for non-time formats + return ComputeSignature.super.computeSignature(signature); + } + + /** + * Check if the string is in a valid time format. + */ + default boolean isTimeFormat(String s) { + if (s == null || s.isEmpty()) { + return false; + } + + s = s.trim(); + if (s.startsWith("+") || s.startsWith("-")) { + return true; + } + + if (s.contains("-") || s.contains("/") || s.contains(" ") || s.contains("T")) { + return false; + } + + if (s.contains(":")) { + return isColonTimeFormat(s); + } else { + return isNumericTimeFormat(s); + } + } + + /** + * Check if the string is in HH:MM[:SS[.FFFFFF]] format. + */ + default boolean isColonTimeFormat(String s) { + String[] parts = s.split("\\.", 2); + String timePart = parts[0]; + + /* + * Split time part, first part is two length we define it is time type, such as + * 12:12:12 + * datetime such as 2023:12:12 12:12:12 length is 19,time part length is <= 15 + */ + String[] timeFields = timePart.split(":"); + if ((timeFields[0].length() == 2 && timeFields[1].length() <= 15) || timeFields[0].length() == 3 + || timePart.length() < 8) { + return true; + } + + return false; + } + + /** + * Check if the string is in numeric format: continuous digits [ .FFFFFF ] + */ + default boolean isNumericTimeFormat(String s) { + String[] parts = s.split("\\.", 2); + String numberPart = parts[0]; + + int length = numberPart.length(); + if (length <= 7) { + return true; + } + return false; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/AddTime.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/AddTime.java new file mode 100644 index 00000000000000..9a4e23de4f57a1 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/AddTime.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.ComputeSignatureForTimeArithmetic; +import org.apache.doris.nereids.trees.expressions.functions.DateAddSubMonotonic; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DateTimeV2Type; +import org.apache.doris.nereids.types.TimeV2Type; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'add_time'. + */ +public class AddTime extends ScalarFunction implements BinaryExpression, ExplicitlyCastableSignature, + ComputeSignatureForTimeArithmetic, PropagateNullable, DateAddSubMonotonic { + + private static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args(DateTimeV2Type.SYSTEM_DEFAULT, + TimeV2Type.INSTANCE), + FunctionSignature.ret(TimeV2Type.INSTANCE).args(TimeV2Type.INSTANCE, TimeV2Type.INSTANCE)); + + public AddTime(Expression arg0, Expression arg1) { + super("add_time", arg0, arg1); + } + + /** constructor for withChildren and reuse signature */ + private AddTime(ScalarFunctionParams functionParams) { + super(functionParams); + } + + @Override + public AddTime withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new AddTime(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitAddTime(this, context); + } + + @Override + public Expression withConstantArgs(Expression literal) { + return new AddTime(literal, child(1)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SubTime.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SubTime.java new file mode 100644 index 00000000000000..35455c7cfa49dd --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SubTime.java @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions.scalar; + +import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.functions.ComputeSignatureForTimeArithmetic; +import org.apache.doris.nereids.trees.expressions.functions.DateAddSubMonotonic; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DateTimeV2Type; +import org.apache.doris.nereids.types.TimeV2Type; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'sub_time'. + */ +public class SubTime extends ScalarFunction implements BinaryExpression, ExplicitlyCastableSignature, + ComputeSignatureForTimeArithmetic, PropagateNullable, DateAddSubMonotonic { + + private static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args(DateTimeV2Type.SYSTEM_DEFAULT, + TimeV2Type.INSTANCE), + FunctionSignature.ret(TimeV2Type.INSTANCE).args(TimeV2Type.INSTANCE, TimeV2Type.INSTANCE)); + + public SubTime(Expression arg0, Expression arg1) { + super("sub_time", arg0, arg1); + } + + /** constructor for withChildren and reuse signature */ + private SubTime(ScalarFunctionParams functionParams) { + super(functionParams); + } + + @Override + public SubTime withChildren(List children) { + Preconditions.checkArgument(children.size() == 2); + return new SubTime(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitSubTime(this, context); + } + + @Override + public Expression withConstantArgs(Expression literal) { + return new SubTime(literal, child(1)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsAdd.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsAdd.java index 627269a5a0d35c..8c4e25f0b1c14d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsAdd.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/YearsAdd.java @@ -35,7 +35,7 @@ import java.util.List; /** - * ScalarFunction 'days_add'. + * ScalarFunction 'years_add'. */ public class YearsAdd extends ScalarFunction implements BinaryExpression, ExplicitlyCastableSignature, ComputeSignatureForDateArithmetic, PropagateNullableOnDateOrTimeLikeV2Args, DateAddSubMonotonic { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java index 275a76bcc48889..18104b2d4a2ca2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java @@ -35,6 +35,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Abs; import org.apache.doris.nereids.trees.expressions.functions.scalar.Acos; import org.apache.doris.nereids.trees.expressions.functions.scalar.Acosh; +import org.apache.doris.nereids.trees.expressions.functions.scalar.AddTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.AesDecrypt; import org.apache.doris.nereids.trees.expressions.functions.scalar.AesEncrypt; import org.apache.doris.nereids.trees.expressions.functions.scalar.AppendTrailingCharIfAbsent; @@ -461,6 +462,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace; +import org.apache.doris.nereids.trees.expressions.functions.scalar.SubTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring; import org.apache.doris.nereids.trees.expressions.functions.scalar.SubstringIndex; import org.apache.doris.nereids.trees.expressions.functions.scalar.Tan; @@ -548,6 +550,10 @@ default R visitAcosh(Acosh acosh, C context) { return visitScalarFunction(acosh, context); } + default R visitAddTime(AddTime addTime, C context) { + return visitScalarFunction(addTime, context); + } + default R visitAesDecrypt(AesDecrypt aesDecrypt, C context) { return visitScalarFunction(aesDecrypt, context); } @@ -2197,6 +2203,10 @@ default R visitSubReplace(SubReplace subReplace, C context) { return visitScalarFunction(subReplace, context); } + default R visitSubTime(SubTime subTime, C context) { + return visitScalarFunction(subTime, context); + } + default R visitSubstring(Substring substring, C context) { return visitScalarFunction(substring, context); } diff --git a/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out index 347216879a5137..fd5329f2b198d8 100644 --- a/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out +++ b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.out @@ -41,3 +41,91 @@ -- !sql_diff14 -- 86400000000 +-- !sql_addtime1 -- +2023-10-14T22:35:22 + +-- !sql_addtime2 -- +2023-10-14T22:35:22.243456 + +-- !sql_addtime3 -- +2025-01-03T12:46:34.123456 +2023-01-20T11:06:07.123456 +2025-07-05T18:20:52.123456 + +-- !sql_addtime4 -- +838:59:59.000000 + +-- !sql_addtime5 -- +-503:36:23.123456 + +-- !sql_addtime6 -- +2024-12-29T22:46:08.123456 +2023-01-16T08:30:44 +2025-06-30T03:10:34.999999 + +-- !sql_addtime7 -- +14:34:56.123 +25:59:59.123 +-10:34:55.877 + +-- !sql_addtime8 -- +2024-12-29T11:41:12.123456 +2023-01-15T10:00:45 +2025-06-30T17:15:30.999999 + +-- !sql_subtime1 -- +2023-10-13T01:24:38 + +-- !sql_subtime2 -- +2023-10-13T01:24:37.996544 + +-- !sql_subtime3 -- +2024-12-28T11:35:49.876544 +2023-01-14T09:55:22.876544 +2025-06-29T17:10:07.876544 + +-- !sql_subtime4 -- +-838:59:59 + +-- !sql_subtime5 -- +503:36:23.123456 + +-- !sql_subtime6 -- +2024-12-28T21:36:16.123456 +2023-01-14T08:30:46 +2025-07-01T04:20:26.999999 + +-- !sql_subtime7 -- +10:34:55.877 +21:59:58.877 +-14:34:56.123 + +-- !sql_subtime8 -- +2024-12-29T08:41:12.123456 +2023-01-15T07:00:45 +2025-06-30T14:15:30.999999 + +-- !sql_addtime9 -- +44:22:32 + +-- !sql_addtime10 -- +44:22:32 + +-- !sql_addtime11 -- +44:22:32 + +-- !sql_addtime12 -- +2022-12-22T22:10:10 + +-- !sql_subtime9 -- +00:02:12 + +-- !sql_subtime10 -- +00:02:12 + +-- !sql_subtime11 -- +-44:22:32 + +-- !sql_subtime12 -- +2022-12-21T01:49:50 + diff --git a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy index 58a04714139a99..0ebb2524cab20c 100644 --- a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy +++ b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function_v2.groovy @@ -27,7 +27,9 @@ suite("test_date_function_v2") { CREATE TABLE IF NOT EXISTS ${tableName} ( `id` INT, `name` varchar(32), - `dt` varchar(32) + `dt` varchar(32), + `time_str` varchar(32), + `datetime_val` datetime(6) ) ENGINE=OLAP UNIQUE KEY(`id`) DISTRIBUTED BY HASH(`id`) BUCKETS 1 @@ -35,7 +37,11 @@ suite("test_date_function_v2") { "replication_allocation" = "tag.location.default: 1" ) """ - sql """ insert into ${tableName} values (3, 'Carl','2024-12-29 10:11:12') """ + sql """ insert into ${tableName} values + (1, 'Carl', '2024-12-29 10:11:12', '12:34:56', '2024-12-29 10:11:12.123456'), + (2, 'Bob', '2023-01-15 08:30:45', '23:59:59', '2023-01-15 08:30:45.000000'), + (3, 'Alice', '2025-06-30 15:45:30', '-12:34:56', '2025-06-30 15:45:30.999999') + """ def result1 = try_sql """ select cast(str_to_date(dt, '%Y-%m-%d %H:%i:%s') as string) from ${tableName}; """ @@ -86,4 +92,55 @@ suite("test_date_function_v2") { testFoldConst("select microseconds_diff('2023-10-15 00:00:00', '2023-10-14 00:00:00.1');") qt_sql_diff14 "select microseconds_diff('2023-10-15 00:00:00', '2023-10-14 00:00:00');" testFoldConst("select microseconds_diff('2023-10-15 00:00:00', '2023-10-14 00:00:00');") + + qt_sql_addtime1 "select add_time('2023-10-14 00:00:00', '22:35:22');" + testFoldConst("select add_time('2023-10-14 00:00:00', '22:35:22');") + qt_sql_addtime2 "select add_time('2023-10-14 00:00:00.12', '22:35:22.123456');" + testFoldConst("select add_time('2023-10-14 00:00:00.12', '22:35:22.123456');") + qt_sql_addtime3 "select add_time(dt, '122:35:22.123456') from ${tableName};" + qt_sql_addtime4 "select add_time(cast('822:35:22.123456' as time(6)), cast('421:01:01' as time(6)));" + qt_sql_addtime5 "select add_time(cast('-82:35:22.123456' as time(6)), cast('-421:01:01' as time(6)));" + + // test time string and datetime type in table + qt_sql_addtime6 "select add_time(datetime_val, time_str) from ${tableName} order by id;" + qt_sql_addtime7 "select add_time(cast(time_str as time), cast('02:00:00.123' as time(3))) from ${tableName} order by id;" + qt_sql_addtime8 "select add_time(datetime_val, cast('01:30:00' as time)) from ${tableName} order by id;" + + test{ + sql("select add_time('9999-12-29 00:00:00', '122:35:22.123456');") + exception "datetime value is out of range in function add_time" + } + + qt_sql_subtime1("select sub_time('2023-10-14 00:00:00', '22:35:22');") + testFoldConst("select sub_time('2023-10-14 00:00:00', '22:35:22');") + qt_sql_subtime2("select sub_time('2023-10-14 00:00:00.12', '22:35:22.123456');") + testFoldConst("select sub_time('2023-10-14 00:00:00.12', '22:35:22.123456');") + qt_sql_subtime3("select sub_time(dt, '22:35:22.123456') from ${tableName};") + qt_sql_subtime4("select sub_time('-421:01:01', '822:35:22');") + qt_sql_subtime5("select sub_time('421:01:01', '-82:35:22.123456');") + + // test time string and datetime type in table + qt_sql_subtime6("select sub_time(datetime_val, time_str) from ${tableName} order by id;") + qt_sql_subtime7("select sub_time(cast(time_str as time), cast('02:00:00.123' as time(3))) from ${tableName} order by id;") + qt_sql_subtime8("select sub_time(datetime_val, cast('01:30:00' as time)) from ${tableName} order by id;") + + test{ + sql("select sub_time('0000-01-01 00:00:00', '122:35:22.123456');") + exception "datetime value is out of range in function sub_time" + } + + //test computetimearithmetic regular + + qt_sql_addtime9 "select add_time('221222', cast('221010' as time));" + qt_sql_addtime10 "select add_time('22:12:22', '221010');" + qt_sql_addtime11 "select add_time('+22:12:22', '221010');" + + //datetime parameter + qt_sql_addtime12 "select add_time('22/12/22', '221010');" + + qt_sql_subtime9 "select sub_time('221222', cast('221010' as time));" + qt_sql_subtime10 "select sub_time('22:12:22', '221010');" + qt_sql_subtime11 "select sub_time('-22:12:22', '221010');" + //datetime parameter + qt_sql_subtime12 "select sub_time('22/12/22', '221010');" }