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..65c116b0cb776a 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -838,10 +838,31 @@ struct CurrentTimeImpl { static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) { auto col_to = ColumnTimeV2::create(); - VecDateTimeValue dtv; + DateV2Value dtv; dtv.from_unixtime(context->state()->timestamp_ms() / 1000, context->state()->timezone_obj()); - auto time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second()); + double time; + if (arguments.size() == 1) { + // the precision must be const, which is checked in fe. + const auto* col = assert_cast( + block.get_by_position(arguments[0]).column.get()); + uint8_t precision = col->get_element(0); + if (precision <= 6) { + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->nano_seconds(), + context->state()->timezone_obj(), precision); + time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second(), + dtv.microsecond()); + } else { + return Status::InvalidArgument( + "The precision in function CURTIME should be between 0 and 6, but got {}", + precision); + } + } else { + dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->timezone_obj()); + time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second()); + } col_to->insert_value(time); block.get_by_position(result).column = ColumnConst::create(std::move(col_to), input_rows_count); diff --git a/be/test/vec/function/function_time_test.cpp b/be/test/vec/function/function_time_test.cpp index 6368859ab554d8..7b88ecdf1b413d 100644 --- a/be/test/vec/function/function_time_test.cpp +++ b/be/test/vec/function/function_time_test.cpp @@ -1558,4 +1558,87 @@ TEST(VTimestampFunctionsTest, time) { static_cast(check_function(func_name, input_types, data_set)); } +TEST(VTimestampFunctionsTest, curtime_test) { + std::string func_name = "curtime"; + + // Test curtime without precision + { + InputTypeSet input_types = {}; + Block block; + ColumnsWithTypeAndName arguments; + + auto return_type = std::make_shared(0); + FunctionBasePtr func = + SimpleFunctionFactory::instance().get_function(func_name, arguments, return_type); + EXPECT_TRUE(func != nullptr); + + auto fn_ctx_return = std::make_shared(0); + std::vector arg_types = {}; + FunctionUtils fn_utils(fn_ctx_return, arg_types, false); + auto* fn_ctx = fn_utils.get_fn_ctx(); + + EXPECT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok()); + EXPECT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok()); + + block.insert({nullptr, return_type, "result"}); + ColumnNumbers args; + auto st = func->execute(fn_ctx, block, args, 0, 1); + EXPECT_TRUE(st.ok()); + + auto result_col = block.get_by_position(0).column; + EXPECT_TRUE(result_col); + if (const auto* const_col = check_and_get_column(result_col.get())) { + auto time_value = const_col->get_field().get(); + EXPECT_GE(time_value, 0.0); + EXPECT_LE(time_value, 24.0 * 3600 * 1000000); + } + + EXPECT_TRUE(func->close(fn_ctx, FunctionContext::THREAD_LOCAL).ok()); + EXPECT_TRUE(func->close(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok()); + } + + // Test curtime with precision + { + InputTypeSet input_types = {PrimitiveType::TYPE_TINYINT}; + Block block; + + auto precision_col = ColumnInt8::create(); + precision_col->insert_value(3); + auto precision_type = std::make_shared(); + block.insert({std::move(precision_col), precision_type, "precision"}); + + ColumnsWithTypeAndName arguments; + arguments.push_back(block.get_by_position(0)); + + auto return_type = std::make_shared(3); + FunctionBasePtr func = + SimpleFunctionFactory::instance().get_function(func_name, arguments, return_type); + EXPECT_TRUE(func != nullptr); + + auto fn_ctx_return = std::make_shared(3); + std::vector arg_types = {precision_type}; + FunctionUtils fn_utils(fn_ctx_return, arg_types, false); + auto* fn_ctx = fn_utils.get_fn_ctx(); + + EXPECT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok()); + EXPECT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok()); + + block.insert({nullptr, return_type, "result"}); + ColumnNumbers args = {0}; + auto st = func->execute(fn_ctx, block, args, 1, 1); + EXPECT_TRUE(st.ok()); + + auto result_col = block.get_by_position(1).column; + EXPECT_TRUE(result_col); + if (const auto* const_col = check_and_get_column(result_col.get())) { + auto time_value = const_col->get_field().get(); + EXPECT_GE(time_value, 0.0); + EXPECT_LE(time_value, 24.0 * 3600 * 1000000); + } + + EXPECT_TRUE(func->close(fn_ctx, FunctionContext::THREAD_LOCAL).ok()); + EXPECT_TRUE(func->close(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok()); + } +} + } // namespace doris::vectorized diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeAcquire.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeAcquire.java index cd8b2257c6e2b1..a6d12f8f015da9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeAcquire.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeAcquire.java @@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal; import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal; +import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; import org.apache.doris.nereids.util.DateUtils; import java.time.LocalDateTime; @@ -95,11 +96,21 @@ public static Expression curTime() { return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone())); } + @ExecFunction(name = "curtime") + public static Expression curTime(TinyIntLiteral precision) { + return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), precision.getValue()); + } + @ExecFunction(name = "current_time") public static Expression currentTime() { return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone())); } + @ExecFunction(name = "current_time") + public static Expression currentTime(TinyIntLiteral precision) { + return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), precision.getValue()); + } + /** * date transformation function: unix_timestamp */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CurrentTime.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CurrentTime.java index 0b640089569663..454294727aa62a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CurrentTime.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CurrentTime.java @@ -18,12 +18,16 @@ package org.apache.doris.nereids.trees.expressions.functions.scalar; import org.apache.doris.catalog.FunctionSignature; +import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable; -import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.functions.ComputeSignature; +import org.apache.doris.nereids.trees.expressions.functions.ImplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.TimeV2Type; +import org.apache.doris.nereids.types.TinyIntType; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -34,10 +38,11 @@ * ScalarFunction 'current_time'. This class is generated by GenerateFunction. */ public class CurrentTime extends ScalarFunction - implements LeafExpression, ExplicitlyCastableSignature, AlwaysNotNullable { + implements LeafExpression, ImplicitlyCastableSignature, AlwaysNotNullable, ComputeSignature { public static final List SIGNATURES = ImmutableList.of( - FunctionSignature.ret(TimeV2Type.INSTANCE).args() + FunctionSignature.ret(TimeV2Type.INSTANCE).args(), + FunctionSignature.ret(TimeV2Type.INSTANCE).args(TinyIntType.INSTANCE) ); /** @@ -47,17 +52,51 @@ public CurrentTime() { super("current_time"); } + /** + * constructor with 1 argument. + */ + public CurrentTime(Expression arg) { + super("current_time", arg); + } + /** constructor for withChildren and reuse signature */ private CurrentTime(ScalarFunctionParams functionParams) { super(functionParams); } + @Override + public FunctionSignature computeSignature(FunctionSignature signature) { + if (arity() == 1 && child(0) instanceof TinyIntLiteral) { + byte precision = ((TinyIntLiteral) child(0)).getValue(); + if (precision < 0 || precision > 6) { + throw new IllegalArgumentException("The precision must be between 0 and 6"); + } + return FunctionSignature.ret(TimeV2Type.of(precision)).args(TinyIntType.INSTANCE); + } + + return super.computeSignature(signature); + } + @Override public Expression withChildren(List children) { - Preconditions.checkArgument(children.isEmpty()); + Preconditions.checkArgument(children.isEmpty() || children.size() == 1); return new CurrentTime(getFunctionParams(children)); } + @Override + public void checkLegalityAfterRewrite() { + if (arity() == 1) { + if (!child(0).isLiteral()) { + throw new AnalysisException("CURTIME only accepts literal as precision."); + } + } + } + + @Override + public void checkLegalityBeforeTypeCoercion() { + checkLegalityAfterRewrite(); + } + @Override public List getSignatures() { return SIGNATURES; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java index 538052d9ad7f31..e697ef2b2e7235 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java @@ -301,6 +301,18 @@ public static Expression fromJavaDateType(LocalDateTime dateTime) { return new TimeV2Literal(dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), 0, 0, false); } + /** + * construct with precision + */ + public static Expression fromJavaDateType(LocalDateTime dateTime, int precision) { + if (isDateOutOfRange(dateTime)) { + throw new AnalysisException("datetime out of range" + dateTime.toString()); + } + int value = (int) Math.pow(10, TimeV2Type.MAX_SCALE - precision); + return new TimeV2Literal(dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), + (dateTime.getNano() / 1000) / value * value, precision, false); + } + public LocalDateTime toJavaDateType() { return LocalDateTime.of(0, 1, 1, ((int) getHour()), ((int) getMinute()), ((int) getSecond()), (int) getMicroSecond() * 1000); diff --git a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy index 50369ce3395953..d3920031ef0102 100644 --- a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy +++ b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy @@ -226,6 +226,22 @@ suite("test_date_function") { // TIME CURTIME() def curtime_result = sql """ SELECT CURTIME() """ assertTrue(curtime_result[0].size() == 1) + def curtime_with_arg = sql """ SELECT CAST(CURTIME(3) AS STRING) """ + assertTrue(curtime_with_arg[0].size() == 1) + assertTrue(curtime_with_arg[0][0].contains('.')) + + curtime_with_arg = sql """ SELECT CAST(CURTIME(0) AS STRING) """ + assertTrue(curtime_with_arg[0].size() == 1) + assertFalse(curtime_with_arg[0][0].contains('.')) + + test { + sql """ SELECT CURTIME(114514);""" + exception "Can not find the compatibility function signature: current_time(INT)" + } + test { + sql """ SELECT CURTIME(7); """ + exception "The precision must be between 0 and 6" + } sql """ insert into ${tableName} values ("2010-11-30 23:59:59") """ // DATE_ADD