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 f9f2db932b7224..3fe9a7b007117f 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.cpp +++ b/be/src/vec/functions/function_date_or_datetime_computation.cpp @@ -17,6 +17,7 @@ #include "vec/functions/function_date_or_datetime_computation.h" +#include "runtime/define_primitive_type.h" #include "vec/functions/simple_function_factory.h" namespace doris::vectorized { @@ -58,7 +59,9 @@ struct CurTimeFunctionName { }; using FunctionCurTime = FunctionCurrentDateOrDateTime>; -using FunctionUtcTimeStamp = FunctionCurrentDateOrDateTime; +using FunctionUtcTimeStamp = FunctionCurrentDateOrDateTime>; +using FunctionUtcDate = FunctionCurrentDateOrDateTime>; +using FunctionUtcTime = FunctionCurrentDateOrDateTime>; using FunctionTimeToSec = FunctionCurrentDateOrDateTime; using FunctionSecToTime = FunctionCurrentDateOrDateTime; using FunctionMicroSecToDateTime = TimestampToDateTime; @@ -86,6 +89,8 @@ void register_function_date_time_computation(SimpleFunctionFactory& factory) { factory.register_function(CurDateFunctionName::name, &createCurDateFunctionBuilderFunction); factory.register_function(); factory.register_function(); + factory.register_function(); + factory.register_function(); factory.register_function(); factory.register_function(); factory.register_function(); 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 cc22393a49e63d..2a63b3750d0372 100644 --- a/be/src/vec/functions/function_date_or_datetime_computation.h +++ b/be/src/vec/functions/function_date_or_datetime_computation.h @@ -971,18 +971,50 @@ struct TimestampToDateTime : IFunction { } }; -struct UtcTimestampImpl { - static constexpr PrimitiveType ReturnType = TYPE_DATETIME; - static constexpr auto name = "utc_timestamp"; +template +struct UtcImpl { + static constexpr PrimitiveType ReturnType = UTCType; + + static constexpr const char* get_function_name() { + if constexpr (ReturnType == TYPE_DATETIMEV2 || ReturnType == TYPE_DATETIME) { + return "utc_timestamp"; + } else if constexpr (ReturnType == TYPE_DATEV2 || ReturnType == TYPE_DATE) { + return "utc_date"; + } else if constexpr (ReturnType == TYPE_TIMEV2) { + return "utc_time"; + } + } + + static constexpr auto name = get_function_name(); + static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, uint32_t result, size_t input_rows_count) { - auto col_to = ColumnDateTimeV2::create(); + int scale = 0; + 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()); + scale = col->get_element(0); + } + auto col_to = PrimitiveTypeTraits::ColumnType::create(); DateV2Value dtv; - if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, "+00:00")) { - auto date_packed_int = binary_cast, UInt64>(dtv); - col_to->insert_data(reinterpret_cast(&date_packed_int), 0); + if (dtv.from_unixtime(context->state()->timestamp_ms() / 1000, + context->state()->nano_seconds(), "+00:00", scale)) { + if constexpr (ReturnType == TYPE_DATETIMEV2) { + auto date_packed_int = binary_cast, UInt64>(dtv); + col_to->insert_data(reinterpret_cast(&date_packed_int), 0); + } else if constexpr (ReturnType == TYPE_DATEV2) { + DateV2Value dv; + dv.assign_from(dtv); + auto date_packed_int = binary_cast, UInt32>(dv); + col_to->insert_data(reinterpret_cast(&date_packed_int), 0); + } else if constexpr (ReturnType == TYPE_TIMEV2) { + double time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second(), + dtv.microsecond()); + col_to->insert_data(reinterpret_cast(&time), 0); + } } else { - uint64_t invalid_val = 0; + typename PrimitiveTypeTraits::ColumnItemType invalid_val = 0; col_to->insert_data(reinterpret_cast(&invalid_val), 0); } block.get_by_position(result).column = diff --git a/be/test/testutil/function_utils.cpp b/be/test/testutil/function_utils.cpp index 2b88bca480f76c..b82c86ed5b3b09 100644 --- a/be/test/testutil/function_utils.cpp +++ b/be/test/testutil/function_utils.cpp @@ -34,6 +34,7 @@ FunctionUtils::FunctionUtils(const vectorized::DataTypePtr& return_type, globals.__set_now_string("2019-08-06 01:38:57"); globals.__set_timestamp_ms(1565026737805); globals.__set_time_zone("Asia/Shanghai"); + globals.__set_nano_seconds(805000000); _state = std::make_unique(globals); _fn_ctx = FunctionContext::create_context(_state.get(), return_type, arg_types); diff --git a/be/test/vec/function/function_time_test.cpp b/be/test/vec/function/function_time_test.cpp index 7b88ecdf1b413d..d51c45513c3567 100644 --- a/be/test/vec/function/function_time_test.cpp +++ b/be/test/vec/function/function_time_test.cpp @@ -29,6 +29,7 @@ #include "vec/data_types/data_type_number.h" #include "vec/data_types/data_type_string.h" #include "vec/data_types/data_type_time.h" +#include "vec/functions/function_date_or_datetime_computation.h" #include "vec/runtime/time_value.h" #include "vec/runtime/vdatetime_value.h" @@ -1641,4 +1642,73 @@ TEST(VTimestampFunctionsTest, curtime_test) { } } +// Test UTC functions +// FunctionUtils sets fixed time: 2019-08-06 01:38:57.805000 Asia/Shanghai (UTC+8) +// Corresponding UTC time: 2019-08-05 17:38:57.805 +TEST(VTimestampFunctionsTest, utc_timestamp_test) { + std::string func_name = "utc_timestamp"; + TimezoneUtils::load_timezones_to_cache(); + + { + InputTypeSet input_types = {}; + DataSet data_set = { + {{}, std::string("2019-08-05 17:38:57")}, + }; + static_cast(check_function(func_name, input_types, data_set)); + } + + { + InputTypeSet input_types = {PrimitiveType::TYPE_INT}; + DataSet data_set = { + {{int32_t(3)}, std::string("2019-08-05 17:38:57.805")}, + }; + static_cast( + check_function(func_name, input_types, data_set, 3)); + } +} + +TEST(VTimestampFunctionsTest, utc_date_test) { + std::string func_name = "utc_date"; + TimezoneUtils::load_timezones_to_cache(); + + { + InputTypeSet input_types = {}; + DataSet data_set = { + {{}, std::string("2019-08-05")}, + }; + static_cast(check_function(func_name, input_types, data_set)); + } +} + +TEST(VTimestampFunctionsTest, utc_time_test) { + std::string func_name = "utc_time"; + TimezoneUtils::load_timezones_to_cache(); + + { + InputTypeSet input_types = {}; + DataSet data_set = { + {{}, std::string("17:38:57")}, + }; + static_cast(check_function(func_name, input_types, data_set)); + } + + { + InputTypeSet input_types = {PrimitiveType::TYPE_INT}; + DataSet data_set = { + {{int32_t(3)}, std::string("17:38:57.805")}, + }; + static_cast( + check_function(func_name, input_types, data_set, 3)); + } +} + +TEST(VTimestampFunctionsTest, utc_impl_function_name_test) { + EXPECT_STREQ("utc_timestamp", UtcImpl::get_function_name()); + EXPECT_STREQ("utc_date", UtcImpl::get_function_name()); + EXPECT_STREQ("utc_time", UtcImpl::get_function_name()); + EXPECT_STREQ("utc_timestamp", UtcImpl::name); + EXPECT_STREQ("utc_date", UtcImpl::name); + EXPECT_STREQ("utc_time", UtcImpl::name); +} + } // namespace doris::vectorized diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcDate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcDate.java new file mode 100644 index 00000000000000..a5465c6890381d --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcDate.java @@ -0,0 +1,75 @@ +// 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.AlwaysNotNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DateV2Type; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'utc_date'. This class is generated by GenerateFunction. + */ +public class UtcDate extends ScalarFunction + implements LeafExpression, ExplicitlyCastableSignature, AlwaysNotNullable { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(DateV2Type.INSTANCE).args() + ); + + /** + * constructor with 0 argument. + */ + public UtcDate() { + super("utc_date"); + } + + /** constructor for withChildren and reuse signature */ + private UtcDate(ScalarFunctionParams functionParams) { + super(functionParams); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public Expression withChildren(List children) { + Preconditions.checkArgument(children.isEmpty()); + return new UtcDate(getFunctionParams(children)); + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitUtcDate(this, context); + } + + @Override + public boolean isDeterministic() { + return false; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTime.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTime.java new file mode 100644 index 00000000000000..85c3a4bbb990bc --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTime.java @@ -0,0 +1,112 @@ +// 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.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.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.TimeV2Type; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'utc_time'. This class is generated by GenerateFunction. + */ +public class UtcTime extends ScalarFunction + implements LeafExpression, ExplicitlyCastableSignature, AlwaysNotNullable { + + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(TimeV2Type.INSTANCE).args(), + FunctionSignature.ret(TimeV2Type.INSTANCE).args(IntegerType.INSTANCE) + ); + + /** + * constructor with 0 argument. + */ + public UtcTime() { + super("utc_time"); + } + + /** + * constructor with 1 argument. + */ + public UtcTime(Expression arg) { + super("utc_time", arg); + } + + /** constructor for withChildren and reuse signature */ + private UtcTime(ScalarFunctionParams functionParams) { + super(functionParams); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public FunctionSignature computeSignature(FunctionSignature signature) { + signature = super.computeSignature(signature); + if (arity() == 1 && getArgument(0) instanceof IntegerLiteral) { + int scale = ((IntegerLiteral) getArgument(0)).getValue(); + if (scale < 0 || scale > 6) { + throw new AnalysisException("scale must be between 0 and 6"); + } + return signature.withReturnType(TimeV2Type.of(scale)); + } + + return signature; + } + + @Override + public void checkLegalityAfterRewrite() { + if (arity() == 1 && !child(0).isLiteral()) { + throw new AnalysisException("UTC_TIME scale argument must be a constant literal."); + } + } + + @Override + public void checkLegalityBeforeTypeCoercion() { + checkLegalityAfterRewrite(); + } + + @Override + public Expression withChildren(List children) { + Preconditions.checkArgument(children.isEmpty() || arity() == 1); + return new UtcTime(getFunctionParams(children)); + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitUtcTime(this, context); + } + + @Override + public boolean isDeterministic() { + return false; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTimestamp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTimestamp.java index 5bc9ad7c675e3d..62965878289a64 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTimestamp.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/UtcTimestamp.java @@ -18,12 +18,15 @@ 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.literal.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.shape.LeafExpression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DateTimeV2Type; +import org.apache.doris.nereids.types.IntegerType; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -37,7 +40,8 @@ public class UtcTimestamp extends ScalarFunction implements LeafExpression, ExplicitlyCastableSignature, AlwaysNotNullable { public static final List SIGNATURES = ImmutableList.of( - FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args() + FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args(), + FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args(IntegerType.INSTANCE) ); /** @@ -47,11 +51,44 @@ public UtcTimestamp() { super("utc_timestamp"); } + /** + * constructor with 1 argument. + */ + public UtcTimestamp(Expression arg) { + super("utc_timestamp", arg); + } + /** constructor for withChildren and reuse signature */ private UtcTimestamp(ScalarFunctionParams functionParams) { super(functionParams); } + @Override + public FunctionSignature computeSignature(FunctionSignature signature) { + signature = super.computeSignature(signature); + if (arity() == 1 && getArgument(0) instanceof IntegerLiteral) { + int scale = ((IntegerLiteral) getArgument(0)).getValue(); + if (scale < 0 || scale > 6) { + throw new AnalysisException("scale must be between 0 and 6"); + } + return signature.withReturnType(DateTimeV2Type.of(scale)); + } + + return signature; + } + + @Override + public void checkLegalityAfterRewrite() { + if (arity() == 1 && !child(0).isLiteral()) { + throw new AnalysisException("UTC_TIMESTAMP scale argument must be a constant literal."); + } + } + + @Override + public void checkLegalityBeforeTypeCoercion() { + checkLegalityAfterRewrite(); + } + @Override public List getSignatures() { return SIGNATURES; @@ -59,7 +96,7 @@ public List getSignatures() { @Override public Expression withChildren(List children) { - Preconditions.checkArgument(children.isEmpty()); + Preconditions.checkArgument(children.isEmpty() || arity() == 1); return new UtcTimestamp(getFunctionParams(children)); } 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 7f823c53fadf36..ad68b1c87d4c28 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 @@ -508,6 +508,8 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.UrlDecode; import org.apache.doris.nereids.trees.expressions.functions.scalar.UrlEncode; import org.apache.doris.nereids.trees.expressions.functions.scalar.User; +import org.apache.doris.nereids.trees.expressions.functions.scalar.UtcDate; +import org.apache.doris.nereids.trees.expressions.functions.scalar.UtcTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.UtcTimestamp; import org.apache.doris.nereids.trees.expressions.functions.scalar.Uuid; import org.apache.doris.nereids.trees.expressions.functions.scalar.UuidNumeric; @@ -2385,6 +2387,14 @@ default R visitSessionUser(SessionUser user, C context) { return visitScalarFunction(user, context); } + default R visitUtcDate(UtcDate utcDate, C context) { + return visitScalarFunction(utcDate, context); + } + + default R visitUtcTime(UtcTime utcTime, C context) { + return visitScalarFunction(utcTime, context); + } + default R visitUtcTimestamp(UtcTimestamp utcTimestamp, C context) { return visitScalarFunction(utcTimestamp, context); } 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 d3920031ef0102..0278e85119e106 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 @@ -493,6 +493,25 @@ suite("test_date_function") { // UTC_TIMESTAMP def utc_timestamp_str = sql """ select utc_timestamp(),utc_timestamp() + 1 """ assertTrue(utc_timestamp_str[0].size() == 2) + utc_timestamp_str = sql """ select utc_timestamp(6), utc_timestamp(6) + 1 """ + assertTrue(utc_timestamp_str[0].size() == 2) + test { + sql """ select utc_timestamp(7) """ + exception "scale must be between 0 and 6" + } + + def utc_time_str = sql """ select utc_time(),utc_time() + 1 """ + assertTrue(utc_time_str[0].size() == 2) + utc_time_str = sql """ select utc_time(6), utc_time(6) + 1 """ + assertTrue(utc_time_str[0].size() == 2) + test { + sql """ select utc_time(7) """ + exception "scale must be between 0 and 6" + } + + def utc_date_str = sql """ select utc_date(),utc_date() + 1 """ + assertTrue(utc_date_str[0].size() == 2) + // WEEK qt_sql """ select week('2020-1-1') """ qt_sql """ select week('2020-7-1',1) """