zclllyybb commented on code in PR #49119: URL: https://github.com/apache/doris/pull/49119#discussion_r2013594169
########## fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java: ########## Review Comment: dont format other codes ########## be/test/vec/function/function_time_test.cpp: ########## @@ -1831,4 +1831,89 @@ TEST(VTimestampFunctionsTest, year_of_week_test) { } } +TEST(VTimestampFunctionsTest, next_day_test) { + std::string func_name = "next_day"; + BaseInputTypeSet input_types = {TypeIndex::DateV2, TypeIndex::String}; + { + DataSet data_set = {{{std::string("2020-01-01"), std::string("MO")}, + str_to_date_v2("2020-01-06", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("MON")}, + str_to_date_v2("2020-01-06", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("MONDAY")}, + str_to_date_v2("2020-01-06", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("TU")}, + str_to_date_v2("2020-01-07", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("TUE")}, + str_to_date_v2("2020-01-07", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("TUESDAY")}, + str_to_date_v2("2020-01-07", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("WE")}, + str_to_date_v2("2020-01-08", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("WED")}, + str_to_date_v2("2020-01-08", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("WEDNESDAY")}, + str_to_date_v2("2020-01-08", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("TH")}, + str_to_date_v2("2020-01-02", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("THU")}, + str_to_date_v2("2020-01-02", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("THURSDAY")}, + str_to_date_v2("2020-01-02", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("FR")}, + str_to_date_v2("2020-01-03", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("FRI")}, + str_to_date_v2("2020-01-03", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("FRIDAY")}, + str_to_date_v2("2020-01-03", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SA")}, + str_to_date_v2("2020-01-04", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SAT")}, + str_to_date_v2("2020-01-04", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SATURDAY")}, + str_to_date_v2("2020-01-04", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SU")}, + str_to_date_v2("2020-01-05", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SUN")}, + str_to_date_v2("2020-01-05", "%Y-%m-%d")}, + {{std::string("2020-01-01"), std::string("SUNDAY")}, + str_to_date_v2("2020-01-05", "%Y-%m-%d")}, + {{std::string(""), std::string("MON")}, Null()}, + {{Null(), std::string("MON")}, Null()}}; + static_cast<void>(check_function_all_arg_comb<DataTypeDateV2, true>(func_name, input_types, + data_set)); + } + { + DataSet data_set = {// 跨月份的日期 Review Comment: please use English comment ########## regression-test/suites/query_p0/sql_functions/string_functions/test_next_day.groovy: ########## @@ -0,0 +1,124 @@ +// 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. + +suite("test_next_day") { + sql "drop table if exists next_day_args;" + sql """ + create table next_day_args ( + k0 int, + a date not null, + b date null, + c datetime not null, + d datetime null, + e string not null, + f string null, + ) + DISTRIBUTED BY HASH(k0) + PROPERTIES + ( + "replication_num" = "1" + ); + """ + + order_qt_empty_nullable "select next_day(a, e), next_day(c, e) from next_day_args" + order_qt_empty_not_nullable "select next_day(b, f), next_day(d, f) from next_day_args" + order_qt_empty_partial_nullable "select next_day(a, f), next_day(c, f) from next_day_args" + + sql "insert into next_day_args values (1, '2025-01-01', null, '2025-01-01 00:00:00', null, 'MONDAY', null), (2, '2025-01-02', '2025-01-02', '2025-01-02 00:00:00', '2025-01-02 00:00:00', 'TUESDAY', 'TUESDAY')" + order_qt_all_null "select next_day(b, f), next_day(d, f) from next_day_args" + + sql "truncate table next_day_args" + + // 插入各种日期边界测试场景 Review Comment: dont use Chinese comment ########## be/src/vec/functions/function_date_or_datetime_computation.h: ########## @@ -998,4 +998,97 @@ class CurrentDateFunctionBuilder : public FunctionBuilderImpl { } }; +class FunctionNextDay : public IFunction { +public: + static constexpr auto name = "next_day"; + static FunctionPtr create() { return std::make_shared<FunctionNextDay>(); } + String get_name() const override { return name; } + size_t get_number_of_arguments() const override { return 2; } + DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { + return make_nullable(std::make_shared<DataTypeDateV2>()); + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + uint32_t result, size_t input_rows_count) const override { + CHECK_EQ(arguments.size(), 2); + auto res = ColumnDateV2::create(); + 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); + const auto& date_col = *assert_cast<const ColumnDateV2*>(left_col.get()); + const auto& week_col = *assert_cast<const ColumnString*>(right_col.get()); + Status status; + if (left_const && right_const) { + status = execute_vector<true, true>(input_rows_count, date_col, week_col, *res); + } else if (left_const) { + status = execute_vector<true, false>(input_rows_count, date_col, week_col, *res); + } else if (right_const) { + status = execute_vector<false, true>(input_rows_count, date_col, week_col, *res); + } else { + status = execute_vector<false, false>(input_rows_count, date_col, week_col, *res); + } + if (!status.ok()) { + return status; + } + block.replace_by_position(result, std::move(res)); + return Status::OK(); + } + +private: + static int day_of_week(const StringRef& weekday) { + static const std::unordered_map<std::string, int> weekday_map = { + {"SU", 1}, {"SUN", 1}, {"SUNDAY", 1}, {"MO", 2}, {"MON", 2}, {"MONDAY", 2}, + {"TU", 3}, {"TUE", 3}, {"TUESDAY", 3}, {"WE", 4}, {"WED", 4}, {"WEDNESDAY", 4}, + {"TH", 5}, {"THU", 5}, {"THURSDAY", 5}, {"FR", 6}, {"FRI", 6}, {"FRIDAY", 6}, + {"SA", 7}, {"SAT", 7}, {"SATURDAY", 7}, + }; + auto weekday_upper = weekday.to_string(); + std::transform(weekday_upper.begin(), weekday_upper.end(), weekday_upper.begin(), + ::toupper); + auto it = weekday_map.find(weekday_upper); + if (it == weekday_map.end()) { + return 0; + } + return it->second; + } + static Status compute_next_day(DateV2Value<DateV2ValueType>& dtv, const int week_day) { + auto days_to_add = (week_day - dtv.day_of_week() + 7) % 7; + days_to_add = days_to_add == 0 ? 7 : days_to_add; + dtv.date_add_interval<TimeUnit::DAY>(TimeInterval(TimeUnit::DAY, days_to_add, false)); + return Status::OK(); + } + + template <bool left_const, bool right_const> + static Status execute_vector(size_t input_rows_count, const ColumnDateV2& left_col, + const ColumnString& right_col, ColumnDateV2& res_col) { + DateV2Value<DateV2ValueType> dtv; + int week_day; + if constexpr (left_const) { + dtv = binary_cast<UInt32, DateV2Value<DateV2ValueType>>(left_col.get_element(0)); + } + if constexpr (right_const) { + week_day = day_of_week(right_col.get_data_at(0)); + if (week_day == 0) { + return Status::InvalidArgument("Invalid weekday: {}", right_col.get_data_at(0)); + } + } + + for (size_t i = 0; i < input_rows_count; ++i) { + if constexpr (!left_const) { + dtv = binary_cast<UInt32, DateV2Value<DateV2ValueType>>(left_col.get_element(i)); + } + if constexpr (!right_const) { + week_day = day_of_week(right_col.get_data_at(i)); + if (week_day == 0) { + return Status::InvalidArgument("Invalid weekday: {}", right_col.get_data_at(i)); Review Comment: taking function_name in errmsg would be better ########## regression-test/suites/query_p0/sql_functions/string_functions/test_next_day.groovy: ########## @@ -0,0 +1,124 @@ +// 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. + +suite("test_next_day") { + sql "drop table if exists next_day_args;" + sql """ + create table next_day_args ( + k0 int, + a date not null, + b date null, + c datetime not null, + d datetime null, + e string not null, + f string null, + ) + DISTRIBUTED BY HASH(k0) + PROPERTIES + ( + "replication_num" = "1" + ); + """ + + order_qt_empty_nullable "select next_day(a, e), next_day(c, e) from next_day_args" + order_qt_empty_not_nullable "select next_day(b, f), next_day(d, f) from next_day_args" + order_qt_empty_partial_nullable "select next_day(a, f), next_day(c, f) from next_day_args" + + sql "insert into next_day_args values (1, '2025-01-01', null, '2025-01-01 00:00:00', null, 'MONDAY', null), (2, '2025-01-02', '2025-01-02', '2025-01-02 00:00:00', '2025-01-02 00:00:00', 'TUESDAY', 'TUESDAY')" + order_qt_all_null "select next_day(b, f), next_day(d, f) from next_day_args" + + sql "truncate table next_day_args" + + // 插入各种日期边界测试场景 + sql """ + insert into next_day_args(k0, a, b, c, d, e, f) values + -- 普通日期 + (1, '2024-03-15', '2024-03-15', '2024-03-15 10:00:00', '2024-03-15 10:00:00', 'MON', 'MONDAY'), + + -- 0年第一周 + (2, '0000-01-01', '0000-01-01', '0000-01-01 00:00:00', '0000-01-01 00:00:00', 'SUN', 'SUNDAY'), + + -- 0年年末 + (3, '0000-12-31', '0000-12-31', '0000-12-31 23:59:59', '0000-12-31 23:59:59', 'FRI', 'FRIDAY'), + + -- 0年2月28 + (4, '0000-02-28', '0000-02-28', '0000-02-28 12:00:00', '0000-02-28 12:00:00', 'MO', 'MONDAY'), + + -- 闰年日期前后 + (5, '2024-02-28', '2024-02-28', '2024-02-28 00:00:00', '2024-02-28 00:00:00', 'WED', 'WEDNESDAY'), + (6, '2024-02-29', '2024-02-29', '2024-02-29 00:00:00', '2024-02-29 00:00:00', 'THU', 'THURSDAY'), + + -- 非闰年日期前后 + (7, '2023-02-28', '2023-02-28', '2023-02-28 00:00:00', '2023-02-28 00:00:00', 'TUE', 'TUESDAY'), + (8, '2023-03-01', '2023-03-01', '2023-03-01 00:00:00', '2023-03-01 00:00:00', 'WE', 'WEDNESDAY'), + + -- 1900非闰年日期前后 + (9, '1900-02-28', '1900-02-28', '1900-02-28 00:00:00', '1900-02-28 00:00:00', 'WED', 'WEDNESDAY'), + (10, '1900-03-01', '1900-03-01', '1900-03-01 00:00:00', '1900-03-01 00:00:00', 'THU', 'THURSDAY'), + + -- 9999年最后一秒 + (11, '9999-12-31', '9999-12-31', '9999-12-31 23:59:59', '9999-12-31 23:59:59', 'FRI', 'FRIDAY'), + + -- 1970年第一秒前后 + (12, '1969-12-31', '1969-12-31', '1969-12-31 23:59:59', '1969-12-31 23:59:59', 'WED', 'WEDNESDAY'), + (13, '1970-01-01', '1970-01-01', '1970-01-01 00:00:00', '1970-01-01 00:00:00', 'THU', 'THURSDAY'); + """ + + order_qt_nullable "select next_day(b, f), next_day(d, f) from next_day_args" + order_qt_not_nullable "select next_day(a, e), next_day(c, e) from next_day_args" + order_qt_partial_nullable "select next_day(a, f), next_day(c, f), next_day(b, e), next_day(d, e) from next_day_args" + order_qt_nullable_no_null "select next_day(a, nullable(e)), next_day(c, nullable(e)) from next_day_args" + + /// consts. most by BE-UT + order_qt_const_nullable "select next_day(NULL, NULL) from next_day_args" + order_qt_partial_const_nullable "select next_day(NULL, e) from next_day_args" + order_qt_const_not_nullable "select next_day('2025-01-01', 'MONDAY') from next_day_args" + order_qt_const_other_nullable "select next_day('2025-01-01', f) from next_day_args" + order_qt_const_other_not_nullable "select next_day(a, 'FRI') from next_day_args" + order_qt_const_nullable_no_null "select next_day(nullable('2025-01-01'), nullable('MON'))" + order_qt_const_nullable_no_null_multirows "select next_day(nullable(c), nullable(e)) from next_day_args" + order_qt_const_partial_nullable_no_null "select next_day('2025-01-01', nullable(e)) from next_day_args" + + /// folding + check_fold_consistency "next_day('', 'SA')" Review Comment: 常量测试补一点datetime格式的字面量吧 ########## be/src/vec/functions/function_date_or_datetime_computation.h: ########## @@ -998,4 +998,97 @@ class CurrentDateFunctionBuilder : public FunctionBuilderImpl { } }; +class FunctionNextDay : public IFunction { +public: + static constexpr auto name = "next_day"; + static FunctionPtr create() { return std::make_shared<FunctionNextDay>(); } + String get_name() const override { return name; } + size_t get_number_of_arguments() const override { return 2; } + DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { + return make_nullable(std::make_shared<DataTypeDateV2>()); + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + uint32_t result, size_t input_rows_count) const override { + CHECK_EQ(arguments.size(), 2); + auto res = ColumnDateV2::create(); + 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); + const auto& date_col = *assert_cast<const ColumnDateV2*>(left_col.get()); + const auto& week_col = *assert_cast<const ColumnString*>(right_col.get()); + Status status; + if (left_const && right_const) { + status = execute_vector<true, true>(input_rows_count, date_col, week_col, *res); + } else if (left_const) { + status = execute_vector<true, false>(input_rows_count, date_col, week_col, *res); + } else if (right_const) { + status = execute_vector<false, true>(input_rows_count, date_col, week_col, *res); + } else { + status = execute_vector<false, false>(input_rows_count, date_col, week_col, *res); + } + if (!status.ok()) { + return status; + } + block.replace_by_position(result, std::move(res)); + return Status::OK(); + } + +private: + static int day_of_week(const StringRef& weekday) { + static const std::unordered_map<std::string, int> weekday_map = { + {"SU", 1}, {"SUN", 1}, {"SUNDAY", 1}, {"MO", 2}, {"MON", 2}, {"MONDAY", 2}, + {"TU", 3}, {"TUE", 3}, {"TUESDAY", 3}, {"WE", 4}, {"WED", 4}, {"WEDNESDAY", 4}, + {"TH", 5}, {"THU", 5}, {"THURSDAY", 5}, {"FR", 6}, {"FRI", 6}, {"FRIDAY", 6}, + {"SA", 7}, {"SAT", 7}, {"SATURDAY", 7}, + }; + auto weekday_upper = weekday.to_string(); + std::transform(weekday_upper.begin(), weekday_upper.end(), weekday_upper.begin(), + ::toupper); + auto it = weekday_map.find(weekday_upper); + if (it == weekday_map.end()) { + return 0; + } + return it->second; + } + static Status compute_next_day(DateV2Value<DateV2ValueType>& dtv, const int week_day) { + auto days_to_add = (week_day - dtv.day_of_week() + 7) % 7; + days_to_add = days_to_add == 0 ? 7 : days_to_add; + dtv.date_add_interval<TimeUnit::DAY>(TimeInterval(TimeUnit::DAY, days_to_add, false)); + return Status::OK(); + } + + template <bool left_const, bool right_const> + static Status execute_vector(size_t input_rows_count, const ColumnDateV2& left_col, + const ColumnString& right_col, ColumnDateV2& res_col) { + DateV2Value<DateV2ValueType> dtv; + int week_day; + if constexpr (left_const) { + dtv = binary_cast<UInt32, DateV2Value<DateV2ValueType>>(left_col.get_element(0)); + } + if constexpr (right_const) { + week_day = day_of_week(right_col.get_data_at(0)); + if (week_day == 0) { + return Status::InvalidArgument("Invalid weekday: {}", right_col.get_data_at(0)); + } + } + + for (size_t i = 0; i < input_rows_count; ++i) { + if constexpr (!left_const) { + dtv = binary_cast<UInt32, DateV2Value<DateV2ValueType>>(left_col.get_element(i)); + } + if constexpr (!right_const) { + week_day = day_of_week(right_col.get_data_at(i)); + if (week_day == 0) { + return Status::InvalidArgument("Invalid weekday: {}", right_col.get_data_at(i)); + } + } + RETURN_IF_ERROR(compute_next_day(dtv, week_day)); + res_col.insert_value(binary_cast<DateV2Value<DateV2ValueType>, UInt32>(dtv)); Review Comment: `insert_value` may cause multi-times allocation. try to pre-set size of column and get its' PODArray from it. then just assign is ok. ########## fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java: ########## Review Comment: and maybe these codes be better in `DateTimeExtractAndTransform.java` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org