diff --git a/be/src/vec/functions/function_other_types_to_date.cpp b/be/src/vec/functions/function_other_types_to_date.cpp index 2c35d4138bcaeb..0d0ba8ffa2bcaa 100644 --- a/be/src/vec/functions/function_other_types_to_date.cpp +++ b/be/src/vec/functions/function_other_types_to_date.cpp @@ -44,6 +44,7 @@ #include "vec/columns/column_string.h" #include "vec/columns/column_vector.h" #include "vec/common/assert_cast.h" +#include "vec/common/pod_array.h" #include "vec/common/pod_array_fwd.h" #include "vec/common/string_ref.h" #include "vec/core/block.h" @@ -62,6 +63,7 @@ #include "vec/functions/datetime_errors.h" #include "vec/functions/function.h" #include "vec/functions/simple_function_factory.h" +#include "vec/runtime/time_value.h" #include "vec/runtime/vdatetime_value.h" #include "vec/utils/util.hpp" @@ -379,6 +381,91 @@ struct MakeDateImpl { } }; +struct MakeTimeImpl { + static constexpr auto name = "maketime"; + using DateValueType = PrimitiveTypeTraits::CppType; + using NativeType = PrimitiveTypeTraits::CppNativeType; + static bool is_variadic() { return false; } + static size_t get_number_of_arguments() { return 3; } + static DataTypes get_variadic_argument_types() { return {}; } + static DataTypePtr get_return_type_impl(const DataTypes& arguments) { + return make_nullable(std::make_shared()); + } + + static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + uint32_t result, size_t input_rows_count) { + DCHECK_EQ(arguments.size(), 3); + auto res_col = ColumnTimeV2::create(input_rows_count); + auto res_null_map = ColumnUInt8::create(input_rows_count, 0); + auto& res_data = res_col->get_data(); + + ColumnPtr arg_col[3]; + bool is_const[3]; + for (int i = 0; i < arguments.size(); ++i) { + std::tie(arg_col[i], is_const[i]) = + unpack_if_const(block.get_by_position(arguments[i]).column); + } + + const auto& hour_data = assert_cast(arg_col[0].get())->get_data(); + const auto& min_data = assert_cast(arg_col[1].get())->get_data(); + if (const auto* sec_col = check_and_get_column(arg_col[2].get())) { + for (int i = 0; i < input_rows_count; ++i) { + int64_t hour = hour_data[index_check_const(i, is_const[0])]; + int64_t minute = min_data[index_check_const(i, is_const[1])]; + double sec = sec_col->get_element(index_check_const(i, is_const[2])); + if (!check_and_set_time_value(hour, minute, sec)) { + res_data[i] = 0; + res_null_map->get_data()[i] = 1; + continue; + } + execute_single(hour, minute, sec, res_data, i); + } + } else { + const auto& sec_data = assert_cast(arg_col[2].get())->get_data(); + for (int i = 0; i < input_rows_count; ++i) { + int64_t hour = hour_data[index_check_const(i, is_const[0])]; + int64_t minute = min_data[index_check_const(i, is_const[1])]; + double sec = static_cast(sec_data[index_check_const(i, is_const[2])]); + if (!check_and_set_time_value(hour, minute, sec)) { + res_data[i] = 0; + res_null_map->get_data()[i] = 1; + continue; + } + execute_single(hour, minute, sec, res_data, i); + } + } + + block.replace_by_position( + result, ColumnNullable::create(std::move(res_col), std::move(res_null_map))); + return Status::OK(); + } + +private: + static bool check_and_set_time_value(int64_t& hour, int64_t& minute, double& sec) { + static constexpr int MAX_HOUR_FOR_MAKETIME = 838; + if (minute < 0 || minute >= 60 || sec < 0 || sec >= 60) { + return false; + } + + // Avoid overflow in `execute_single` + // the case {838, 59, 59.999999} will be slove in `TimeValue::from_double_with_limit` + if (std::abs(hour) > MAX_HOUR_FOR_MAKETIME) { + hour = hour > 0 ? MAX_HOUR_FOR_MAKETIME : -MAX_HOUR_FOR_MAKETIME; + minute = sec = 59; + } + return true; + } + + static void execute_single(int64_t hour, int64_t minute, double sec, + PaddedPODArray& res_data, size_t row) { + // Round sec to 6 decimal places (microsecond precision) + double total_sec = + std::abs(hour) * 3600 + minute * 60 + std::round(sec * 1000000.0) / 1000000.0; + + res_data[row] = TimeValue::from_double_with_limit(total_sec * (hour < 0 ? -1 : 1)); + } +}; + struct DateTruncState { using Callback_function = std::function; Callback_function callback_function; @@ -1360,6 +1447,7 @@ void register_function_timestamp(SimpleFunctionFactory& factory) { factory.register_function(); factory.register_function(); factory.register_function(); + factory.register_function>(); factory.register_function(); factory.register_function(); factory.register_function(); 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 54d40c546e5a5b..8f366d52daffbc 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 @@ -312,6 +312,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.LtrimIn; import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeDate; import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeSet; +import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsEntry; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsKey; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsValue; @@ -843,6 +844,7 @@ public class BuiltinScalarFunctions implements FunctionHelper { scalar(LtrimIn.class, "ltrim_in"), scalar(MakeDate.class, "makedate"), scalar(MakeSet.class, "make_set"), + scalar(MakeTime.class, "maketime"), scalar(MapContainsEntry.class, "map_contains_entry"), scalar(MapContainsKey.class, "map_contains_key"), scalar(MapContainsValue.class, "map_contains_value"), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java index 204ec49f6385d6..77918f35d7ab23 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java @@ -45,6 +45,7 @@ import org.apache.doris.nereids.types.DateV2Type; import org.apache.doris.nereids.types.DecimalV3Type; import org.apache.doris.nereids.types.StringType; +import org.apache.doris.nereids.types.TimeV2Type; import org.apache.doris.nereids.util.DateUtils; import org.apache.doris.qe.ConnectContext; @@ -711,6 +712,34 @@ public static Expression makeDate(IntegerLiteral year, IntegerLiteral dayOfYear) : new NullLiteral(DateV2Type.INSTANCE); } + /** + * time transformation function: maketime + */ + @ExecFunction(name = "maketime") + public static Expression makeTime(BigIntLiteral hour, BigIntLiteral minute, DoubleLiteral second) { + long hourValue = hour.getValue(); + long minuteValue = minute.getValue(); + double secondValue = second.getValue(); + + if (minuteValue < 0 || minuteValue >= 60 || secondValue < 0 || secondValue >= 60) { + return new NullLiteral(TimeV2Type.INSTANCE); + } + if (Math.abs(hourValue) > 838) { + hourValue = hourValue > 0 ? 838 : -838; + minuteValue = 59; + secondValue = 59; + } else if (Math.abs(hourValue) == 838 && secondValue > 59) { + secondValue = 59; + } + + double totalSeconds = Math.abs(hourValue) * 3600 + minuteValue * 60 + + Math.round(secondValue * 1000000.0) / 1000000.0; + if (hourValue < 0) { + totalSeconds = -totalSeconds; + } + return new TimeV2Literal(totalSeconds); + } + /** * date transformation function: str_to_date */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MakeTime.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MakeTime.java new file mode 100644 index 00000000000000..2c4e8d8038e749 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/MakeTime.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.AlwaysNullable; +import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.DoubleType; +import org.apache.doris.nereids.types.TimeV2Type; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * ScalarFunction 'maketime'. + */ +public class MakeTime extends ScalarFunction implements ExplicitlyCastableSignature, AlwaysNullable { + public static final List SIGNATURES = ImmutableList.of( + FunctionSignature.ret(TimeV2Type.INSTANCE) + .args(BigIntType.INSTANCE, BigIntType.INSTANCE, BigIntType.INSTANCE), + FunctionSignature.ret(TimeV2Type.MAX) + .args(BigIntType.INSTANCE, BigIntType.INSTANCE, DoubleType.INSTANCE) + + ); + + /** + * constructor with 2 arguments. + */ + public MakeTime(Expression arg0, Expression arg1, Expression arg2) { + super("maketime", arg0, arg1, arg2); + } + + /** constructor for withChildren and reuse signature */ + private MakeTime(ScalarFunctionParams functionParams) { + super(functionParams); + } + + /** + * withChildren. + */ + @Override + public MakeTime withChildren(List children) { + Preconditions.checkArgument(children.size() == 3); + return new MakeTime(getFunctionParams(children)); + } + + @Override + public List getSignatures() { + return SIGNATURES; + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return visitor.visitMakeTime(this, context); + } +} 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..f93bcbe7872d7b 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 @@ -317,6 +317,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.LtrimIn; import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeDate; import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeSet; +import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeTime; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsEntry; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsKey; import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsValue; @@ -2618,6 +2619,10 @@ default R visitMakeSet(MakeSet makeSet, C context) { return visitScalarFunction(makeSet, context); } + default R visitMakeTime(MakeTime makeTime, C context) { + return visitScalarFunction(makeTime, context); + } + default R visitExportSet(ExportSet exportSet, C context) { return visitScalarFunction(exportSet, context); } diff --git a/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out b/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out index bc33c29064140e..b39bec687e2bb1 100644 --- a/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out +++ b/regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out @@ -1970,3 +1970,29 @@ da fanadur -- !yearweek_5 -- 202352 +-- !maketime_test_1 -- +12:15:30.000000 +111:00:23.123457 +838:59:59.000000 +-838:59:59.000000 +\N +\N +\N +\N +\N +\N +\N + +-- !maketime_test_2 -- +12:15:25 +111:00:25 +838:59:59 +-838:59:59 +\N +14:51:25 +\N +\N +01:02:25 +\N +07:23:25 + diff --git a/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy b/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy index ac0656a15db24c..05a8576b178e33 100644 --- a/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy +++ b/regression-test/suites/doc/sql-manual/sql-functions/doc_date_functions_test.groovy @@ -35,22 +35,22 @@ suite("doc_date_functions_test") { //FIXME: make FE and BE have same range of timezone // sql "set debug_skip_fold_constant=true;" - // Test Group 1: Basic Date Functions (序号 1-12) + // Test Group 1: Basic Date Functions(1 - 12) // 1. CONVERT_TZ function tests - // 中国上海时间转换到美国洛杉矶 + // Convert China Shanghai time to America Los Angeles qt_convert_tz_1 """select CONVERT_TZ(CAST('2019-08-01 13:21:03' AS DATETIME), 'Asia/Shanghai', 'America/Los_Angeles')""" - // 将东八区(+08:00)的时间转换为美国洛杉矶 + // Convert East 8 zone (+08:00) time to America Los Angeles qt_convert_tz_2 """select CONVERT_TZ(CAST('2019-08-01 13:21:03' AS DATETIME), '+08:00', 'America/Los_Angeles')""" - // 输入为date类型,时间部分自动转换为 00:00:00 + // Input is date type, time part is automatically converted to 00:00:00 qt_convert_tz_3 """select CONVERT_TZ(CAST('2019-08-01 13:21:03' AS DATE), 'Asia/Shanghai', 'America/Los_Angeles')""" - // 转换时间为NULL,输出NULL + // Convert time is NULL, output NULL qt_convert_tz_4 """select CONVERT_TZ(NULL, 'Asia/Shanghai', 'America/New_York')""" - // 任一时区为NULL,返回NULL + // Any timezone is NULL, return NULL qt_convert_tz_5 """select CONVERT_TZ('2019-08-01 13:21:03', NULL, 'America/Los_Angeles')""" qt_convert_tz_6 """select CONVERT_TZ('2019-08-01 13:21:03', '+08:00', NULL)""" @@ -59,7 +59,7 @@ suite("doc_date_functions_test") { exception "Operation convert_tz invalid timezone" } - // 带有 scale 的时间 + // Time with scale qt_convert_tz_8 """select CONVERT_TZ('2019-08-01 13:21:03.636', '+08:00', 'America/Los_Angeles')""" // test { @@ -68,88 +68,88 @@ suite("doc_date_functions_test") { // } // 5. DATE_ADD function tests - // 添加天数 + // Add days qt_date_add_1 """select date_add(cast('2010-11-30 23:59:59' as datetime), INTERVAL 2 DAY)""" - // 添加季度 + // Add quarter qt_date_add_2 """select DATE_ADD(cast('2023-01-01' as date), INTERVAL 1 QUARTER)""" - // 添加周数 + // Add weeks qt_date_add_3 """select DATE_ADD('2023-01-01', INTERVAL 1 WEEK)""" - // 添加月数,因为2023年2月只有28天,所以1月31加一个月返回2月28 + // Add month, because February 2023 only has 28 days, so January 31 plus one month returns February 28 qt_date_add_4 """select DATE_ADD('2023-01-31', INTERVAL 1 MONTH)""" - // 负数测试 + // Negative number test qt_date_add_5 """select DATE_ADD('2019-01-01', INTERVAL -3 DAY)""" - // 跨年的小时增加 + // Cross-year hour increment qt_date_add_6 """select DATE_ADD('2023-12-31 23:00:00', INTERVAL 2 HOUR)""" - // 参数为NULL,返回NULL + // Parameter is NULL, return NULL qt_date_add_7 """select DATE_ADD(NULL, INTERVAL 1 MONTH)""" // 6. DATE_CEIL function tests - // 秒数按五秒向上取整 + // Round up seconds to five-second intervals qt_date_ceil_1 """select date_ceil(cast('2023-07-13 22:28:18' as datetime),interval 5 second)""" - // 带有 scale 的日期时间参数 + // Datetime parameter with scale qt_date_ceil_2 """select date_ceil(cast('2023-07-13 22:28:18.123' as datetime(3)),interval 5 second)""" - // 按五分钟向上取整 + // Round up to five-minute intervals qt_date_ceil_3 """select date_ceil('2023-07-13 22:28:18',interval 5 minute)""" - // 按五周向上取整 + // Round up to five-week intervals qt_date_ceil_4 """select date_ceil('2023-07-13 22:28:18',interval 5 WEEK)""" - // 按五小时向上取整 + // Round up to five-hour intervals qt_date_ceil_5 """select date_ceil('2023-07-13 22:28:18',interval 5 hour)""" - // 按五天向上取整 + // Round up to five-day intervals qt_date_ceil_6 """select date_ceil('2023-07-13 22:28:18',interval 5 day)""" - // 按五个月向上取整 + // Round up to five-month intervals qt_date_ceil_7 """select date_ceil('2023-07-13 22:28:18',interval 5 month)""" - // 按五年向上取整 + // Round up to five-year intervals qt_date_ceil_8 """select date_ceil('2023-07-13 22:28:18',interval 5 year)""" - // 任一参数为 NULL + // Any parameter is NULL qt_date_ceil_9 """select date_ceil('9900-07-13',interval NULL year)""" qt_date_ceil_10 """select date_ceil(NULL,interval 5 year)""" // 7. DATEDIFF function tests - // 两个日期相差1天(忽略时间部分) + // Two dates differ by 1 day (ignore time part) qt_datediff_1 """select datediff(CAST('2007-12-31 23:59:59' AS DATETIME), CAST('2007-12-30' AS DATETIME))""" - // 前一个日期早于后一个日期,返回负数 + // First date is earlier than second date, return negative number qt_datediff_2 """select datediff(CAST('2010-11-30 23:59:59' AS DATETIME), CAST('2010-12-31' AS DATETIME))""" - // 任一参数为 NULL + // Any parameter is NULL qt_datediff_3 """select datediff('2023-01-01', NULL)""" - // 若输入 datetime 类型,会忽略时间部分 + // If input is datetime type, time part will be ignored qt_datediff_4 """select datediff('2023-01-02 13:00:00', '2023-01-01 12:00:00')""" qt_datediff_5 """select datediff('2023-01-02 12:00:00', '2023-01-01 13:00:00')""" // 8. DATE_FLOOR function tests - // 按 5 秒向下取整 + // Round down to 5-second intervals qt_date_floor_1 """select date_floor(cast('0001-01-01 00:00:18' as datetime), INTERVAL 5 SECOND)""" - // 带有 scale 的日期时间,返回值也会带有 scale + // Datetime with scale, return value also has scale qt_date_floor_2 """select date_floor(cast('0001-01-01 00:00:18.123' as datetime), INTERVAL 5 SECOND)""" - // 输入时间恰好是 5 天周期的起点 + // Input time is exactly at the start of a 5-day cycle qt_date_floor_3 """select date_floor('2023-07-10 00:00:00', INTERVAL 5 DAY)""" - // date 类型的向下取整 + // Round down date type qt_date_floor_4 """select date_floor('2023-07-13', INTERVAL 5 YEAR)""" - // 任一参数为 NULL + // Any parameter is NULL qt_date_floor_5 """select date_floor(NULL, INTERVAL 5 HOUR)""" // 9. DATE_FORMAT function tests - // 基本格式化测试 + // Basic formatting tests qt_date_format_1 """SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y')""" qt_date_format_2 """SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s')""" qt_date_format_3 """SELECT DATE_FORMAT('1999-01-01', '%Y-%m-%d')""" @@ -187,52 +187,52 @@ suite("doc_date_functions_test") { exception "Operation date_format of 142335765945253888, %p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p is invalid" } - // 特殊格式符测试 + // Special format specifier tests qt_date_format_5 """SELECT DATE_FORMAT('2009-10-04', '%a %b %c')""" qt_date_format_6 """SELECT DATE_FORMAT('2009-10-04', '%D %e %f')""" - // 任一参数为 NULL + // Any parameter is NULL qt_date_format_7 """SELECT DATE_FORMAT(NULL, '%Y-%m-%d')""" qt_date_format_8 """SELECT DATE_FORMAT('2009-10-04', NULL)""" // 10. DATE function tests - // 从datetime中提取日期部分 + // Extract date part from datetime qt_date_1 """SELECT DATE('2003-12-31 01:02:03')""" qt_date_2 """SELECT DATE('2003-12-31')""" - // 参数为 NULL + // Parameter is NULL qt_date_3 """SELECT DATE(NULL)""" // 11. DATE_SUB function tests - // 减去天数 + // Subtract days qt_date_sub_1 """SELECT DATE_SUB('2018-05-01', INTERVAL 1 DAY)""" - // 减去月数 + // Subtract months qt_date_sub_2 """SELECT DATE_SUB('2018-05-01', INTERVAL 1 MONTH)""" - // 减去年数 + // Subtract years qt_date_sub_3 """SELECT DATE_SUB('2018-05-01', INTERVAL 1 YEAR)""" - // 减去小时 + // Subtract hours qt_date_sub_4 """SELECT DATE_SUB('2018-05-01 12:00:00', INTERVAL 2 HOUR)""" - // 参数为 NULL + // Parameter is NULL qt_date_sub_5 """SELECT DATE_SUB(NULL, INTERVAL 1 DAY)""" // 12. DATE_TRUNC function tests - // 截断到年 + // Truncate to year qt_date_trunc_1 """SELECT DATE_TRUNC('2019-05-09', 'year')""" - // 截断到月 + // Truncate to month qt_date_trunc_2 """SELECT DATE_TRUNC('2019-05-09', 'month')""" - // 截断到日 + // Truncate to day qt_date_trunc_3 """SELECT DATE_TRUNC('2019-05-09 12:30:45', 'day')""" - // 截断到小时 + // Truncate to hour qt_date_trunc_4 """SELECT DATE_TRUNC('2019-05-09 12:30:45', 'hour')""" - // 参数为 NULL + // Parameter is NULL qt_date_trunc_5 """SELECT DATE_TRUNC(NULL, 'year')""" // Group 2: Day functions and related date extraction functions @@ -1368,6 +1368,30 @@ suite("doc_date_functions_test") { qt_yearweek_4 """SELECT YEARWEEK('2023-01-02', 5) AS yearweek_mode5""" qt_yearweek_5 """SELECT YEARWEEK('2023-12-25', 1) AS date_type_mode1""" + // 100. MAKETIME function test; + sql """DROP TABLE IF EXISTS maketime_test""" + sql """CREATE TABLE maketime_test ( + `id` INT, + `hour` INT, + `minute` INT, + `sec` FLOAT + ) DUPLICATE KEY(id) + PROPERTIES ( 'replication_num' = '1' );""" + sql """ INSERT INTO maketime_test VALUES + (1, 12, 15, 30), + (2, 111, 0, 23.1234567), + (3, 1234, 11, 4), + (4, -1234, 6, 52), + (5, 20, 60, 12), + (6, 14, 51, 66), + (7, NULL, 15, 16), + (8, 7, NULL, 8), + (9, 1, 2, NULL), + (10, 123, -4, 52), + (11, 7, 23, -6);""" + qt_maketime_test_1 """SELECT MAKETIME(hour,minute,sec) FROM maketime_test ORDER BY id;""" + qt_maketime_test_2 """SELECT MAKETIME(hour, minute, 25) FROM maketime_test ORDER BY id;""" + // Test constant folding for YEARWEEK function testFoldConst("SELECT YEARWEEK('2021-01-01') AS yearweek_mode0") testFoldConst("SELECT YEARWEEK('2020-07-01', 1) AS yearweek_mode1") @@ -1985,6 +2009,19 @@ suite("doc_date_functions_test") { testFoldConst("SELECT YEARS_SUB('2022-12-25', 3) AS sub_3_year_date") testFoldConst("SELECT YEARS_SUB('2020-02-29', 1) AS leap_day_adjust_1") + // 99. MAKETIME function constant folding tests + testFoldConst("SELECT MAKETIME(12, 15, 30)") + testFoldConst("SELECT MAKETIME(111, 0, 23.1234567)") + testFoldConst("SELECT MAKETIME(1234, 11, 4)") + testFoldConst("SELECT MAKETIME(-1234, 6, 52)") + testFoldConst("SELECT MAKETIME(20, 60, 12)") + testFoldConst("SELECT MAKETIME(14, 51, 66)") + testFoldConst("SELECT MAKETIME(NULL, 15, 16)") + testFoldConst("SELECT MAKETIME(7, NULL, 8)") + testFoldConst("SELECT MAKETIME(1, 2, NULL)") + testFoldConst("SELECT MAKETIME(123, -4, 40)") + testFoldConst("SELECT MAKETIME(7, 8, -23)") + // Additional NULL parameter tests for comprehensive coverage // MINUTE functions NULL tests