This is an automated email from the ASF dual-hosted git repository. dataroaring pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new f802fc37ff add date function 'last_day' (#13609) f802fc37ff is described below commit f802fc37ff93d837f1b5ed19426123bb9414b954 Author: lsy3993 <110876560+lsy3...@users.noreply.github.com> AuthorDate: Tue Oct 25 13:46:16 2022 +0800 add date function 'last_day' (#13609) --- be/src/exprs/timestamp_functions.cpp | 31 +++++ be/src/exprs/timestamp_functions.h | 2 + be/src/vec/functions/function_timestamp.cpp | 152 +++++++++++++++++++++ .../sql-functions/date-time-functions/last_day.md | 49 +++++++ docs/sidebars.json | 1 + .../sql-functions/date-time-functions/last_day.md | 49 +++++++ gensrc/script/doris_builtins_functions.py | 12 ++ .../datetime_functions/test_date_function.out | 12 ++ .../datetime_functions/test_date_function.groovy | 48 +++++++ 9 files changed, 356 insertions(+) diff --git a/be/src/exprs/timestamp_functions.cpp b/be/src/exprs/timestamp_functions.cpp index 3df58c48da..559cfbe50e 100644 --- a/be/src/exprs/timestamp_functions.cpp +++ b/be/src/exprs/timestamp_functions.cpp @@ -707,6 +707,37 @@ DateTimeVal TimestampFunctions::from_days(FunctionContext* ctx, const IntVal& da return ts_val; } +DateTimeVal TimestampFunctions::last_day(FunctionContext* ctx, const DateTimeVal& ts_val) { + if (ts_val.is_null) { + return DateTimeVal::null(); + } + + DateTimeValue ts_value = DateTimeValue::from_datetime_val(ts_val); + + bool is_leap_year = doris::is_leap(ts_value.year()); + if (ts_value.month() == 2) { + int day = is_leap_year ? 29 : 28; + ts_value.set_time(ts_value.year(), ts_value.month(), day, 0, 0, 0, 0); + } else { + if (ts_value.month() == 1 || ts_value.month() == 3 || ts_value.month() == 5 || + ts_value.month() == 7 || ts_value.month() == 8 || ts_value.month() == 10 || + ts_value.month() == 12) { + ts_value.set_time(ts_value.year(), ts_value.month(), 31, 0, 0, 0, 0); + } else { + ts_value.set_time(ts_value.year(), ts_value.month(), 30, 0, 0, 0, 0); + } + } + + ts_value.set_type(TIME_DATE); + if (!ts_value.is_valid_date()) { + return DateTimeVal::null(); + } + + DateTimeVal result_ts_val; + ts_value.to_datetime_val(&result_ts_val); + return result_ts_val; +} + IntVal TimestampFunctions::to_days(FunctionContext* ctx, const DateTimeVal& ts_val) { if (ts_val.is_null) { return IntVal::null(); diff --git a/be/src/exprs/timestamp_functions.h b/be/src/exprs/timestamp_functions.h index 22d678cb20..2183c2c5e7 100644 --- a/be/src/exprs/timestamp_functions.h +++ b/be/src/exprs/timestamp_functions.h @@ -150,6 +150,8 @@ public: const doris_udf::StringVal& format); static doris_udf::DateTimeVal from_days(doris_udf::FunctionContext* ctx, const doris_udf::IntVal& days); + static doris_udf::DateTimeVal last_day(doris_udf::FunctionContext* ctx, + const doris_udf::DateTimeVal& ts_val); static doris_udf::IntVal to_days(doris_udf::FunctionContext* ctx, const doris_udf::DateTimeVal& ts_val); static doris_udf::DateTimeVal str_to_date(doris_udf::FunctionContext* ctx, diff --git a/be/src/vec/functions/function_timestamp.cpp b/be/src/vec/functions/function_timestamp.cpp index 461b317bf6..63fb1d900c 100644 --- a/be/src/vec/functions/function_timestamp.cpp +++ b/be/src/vec/functions/function_timestamp.cpp @@ -569,6 +569,154 @@ public: } }; +template <template <typename> class Impl, typename DateType> +class FunctionDateOrDateTimeToDate : public IFunction { +public: + static constexpr auto name = Impl<DateType>::name; + static FunctionPtr create() { + return std::make_shared<FunctionDateOrDateTimeToDate<Impl, DateType>>(); + } + + String get_name() const override { return name; } + + bool use_default_implementation_for_nulls() const override { return true; } + + bool use_default_implementation_for_constants() const override { return true; } + + size_t get_number_of_arguments() const override { return 1; } + + bool is_variadic() const override { return true; } + + // input DateTime and Date, return Date + // input DateTimeV2 and DateV2, return DateV2 + DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) const override { + if constexpr (std::is_same_v<DateType, DataTypeDateTime> || + std::is_same_v<DateType, DataTypeDate>) { + return make_nullable(std::make_shared<DataTypeDate>()); + } else { + return make_nullable(std::make_shared<DataTypeDateV2>()); + } + } + + DataTypes get_variadic_argument_types_impl() const override { + if constexpr (std::is_same_v<DateType, DataTypeDate>) { + return {std::make_shared<DataTypeDate>()}; + } else if constexpr (std::is_same_v<DateType, DataTypeDateTime>) { + return {std::make_shared<DataTypeDateTime>()}; + } else if constexpr (std::is_same_v<DateType, DataTypeDateV2>) { + return {std::make_shared<DataTypeDateV2>()}; + } else { + return {std::make_shared<DataTypeDateTimeV2>()}; + } + } + + Status execute_impl(FunctionContext* context, Block& block, const ColumnNumbers& arguments, + size_t result, size_t input_rows_count) override { + return Impl<DateType>::execute_impl(context, block, arguments, result, input_rows_count); + } +}; + +template <typename DateType> +struct LastDayImpl { + static constexpr auto name = "last_day"; + + static Status execute_impl(FunctionContext* context, Block& block, + const ColumnNumbers& arguments, size_t result, + size_t input_rows_count) { + auto null_map = ColumnUInt8::create(input_rows_count, 0); + ColumnPtr res_column; + ColumnPtr argument_column = + block.get_by_position(arguments[0]).column->convert_to_full_column_if_const(); + if constexpr (std::is_same_v<DateType, DataTypeDateTime> || + std::is_same_v<DateType, DataTypeDate>) { + auto data_col = assert_cast<const ColumnVector<Int64>*>(argument_column.get()); + res_column = ColumnInt64::create(input_rows_count); + execute_straight<VecDateTimeValue, Int64, Int64>( + input_rows_count, null_map->get_data(), data_col->get_data(), + static_cast<ColumnVector<Int64>*>(res_column->assume_mutable().get()) + ->get_data()); + + } else if constexpr (std::is_same_v<DateType, DataTypeDateV2>) { + auto data_col = assert_cast<const ColumnVector<UInt32>*>(argument_column.get()); + res_column = ColumnVector<UInt32>::create(input_rows_count); + execute_straight<DateV2Value<DateV2ValueType>, UInt32, UInt32>( + input_rows_count, null_map->get_data(), data_col->get_data(), + static_cast<ColumnVector<UInt32>*>(res_column->assume_mutable().get()) + ->get_data()); + + } else if constexpr (std::is_same_v<DateType, DataTypeDateTimeV2>) { + auto data_col = assert_cast<const ColumnVector<UInt64>*>(argument_column.get()); + res_column = ColumnVector<UInt32>::create(input_rows_count); + execute_straight<DateV2Value<DateTimeV2ValueType>, UInt32, UInt64>( + input_rows_count, null_map->get_data(), data_col->get_data(), + static_cast<ColumnVector<UInt32>*>(res_column->assume_mutable().get()) + ->get_data()); + } + + block.replace_by_position( + result, ColumnNullable::create(std::move(res_column), std::move(null_map))); + return Status::OK(); + } + + template <typename DateValueType, typename ReturnType, typename InputDateType> + static void execute_straight(size_t input_rows_count, NullMap& null_map, + const PaddedPODArray<InputDateType>& data_col, + PaddedPODArray<ReturnType>& res_data) { + for (int i = 0; i < input_rows_count; i++) { + if constexpr (std::is_same_v<DateValueType, VecDateTimeValue>) { + const auto& cur_data = data_col[i]; + auto ts_value = binary_cast<Int64, VecDateTimeValue>(cur_data); + if (!ts_value.is_valid_date()) { + null_map[i] = 1; + continue; + } + int day = get_last_month_day(ts_value.year(), ts_value.month()); + ts_value.set_time(ts_value.year(), ts_value.month(), day, 0, 0, 0); + ts_value.set_type(TIME_DATE); + res_data[i] = binary_cast<VecDateTimeValue, Int64>(ts_value); + + } else if constexpr (std::is_same_v<DateValueType, DateV2Value<DateV2ValueType>>) { + const auto& cur_data = data_col[i]; + auto ts_value = binary_cast<UInt32, DateValueType>(cur_data); + if (!ts_value.is_valid_date()) { + null_map[i] = 1; + continue; + } + int day = get_last_month_day(ts_value.year(), ts_value.month()); + ts_value.template set_time_unit<TimeUnit::DAY>(day); + res_data[i] = binary_cast<DateValueType, UInt32>(ts_value); + + } else { + const auto& cur_data = data_col[i]; + auto ts_value = binary_cast<UInt64, DateValueType>(cur_data); + if (!ts_value.is_valid_date()) { + null_map[i] = 1; + continue; + } + int day = get_last_month_day(ts_value.year(), ts_value.month()); + ts_value.template set_time_unit<TimeUnit::DAY>(day); + ts_value.set_time(ts_value.year(), ts_value.month(), day, 0, 0, 0, 0); + UInt64 cast_value = binary_cast<DateValueType, UInt64>(ts_value); + DataTypeDateTimeV2::cast_to_date_v2(cast_value, res_data[i]); + } + } + } + + static int get_last_month_day(int year, int month) { + bool is_leap_year = doris::is_leap(year); + if (month == 2) { + return is_leap_year ? 29 : 28; + } else { + if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || + month == 12) { + return 31; + } else { + return 30; + } + } + } +}; + template <typename Impl> class FunctionOtherTypesToDateType : public IFunction { public: @@ -619,6 +767,10 @@ void register_function_timestamp(SimpleFunctionFactory& factory) { factory.register_function< FunctionUnixTimestamp<UnixTimeStampDatetimeImpl<DataTypeDateTimeV2>>>(); factory.register_function<FunctionUnixTimestamp<UnixTimeStampStrImpl>>(); + factory.register_function<FunctionDateOrDateTimeToDate<LastDayImpl, DataTypeDateTime>>(); + factory.register_function<FunctionDateOrDateTimeToDate<LastDayImpl, DataTypeDate>>(); + factory.register_function<FunctionDateOrDateTimeToDate<LastDayImpl, DataTypeDateV2>>(); + factory.register_function<FunctionDateOrDateTimeToDate<LastDayImpl, DataTypeDateTimeV2>>(); } } // namespace doris::vectorized diff --git a/docs/en/docs/sql-manual/sql-functions/date-time-functions/last_day.md b/docs/en/docs/sql-manual/sql-functions/date-time-functions/last_day.md new file mode 100644 index 0000000000..1e08eb35f9 --- /dev/null +++ b/docs/en/docs/sql-manual/sql-functions/date-time-functions/last_day.md @@ -0,0 +1,49 @@ +--- +{ + "title": "last_day", + "language": "en" +} +--- + +<!-- +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. +--> + +## last_day +### Description +#### Syntax + +`DATE last_day(DATETIME date)` + +Return the last day of the month, the return day may be : +'28'(February and not a leap year), +'29'(February and a leap year), +'30'(April, June, September, November), +'31'(January, March, May, July, August, October, December) + +### example + +``` +mysql > select last_day('2000-02-03'); ++-------------------+ +| last_day('2000-02-03 00:00:00') | ++-------------------+ +| 2000-02-29 | ++-------------------+ +``` + +### keywords + LAST_DAY,DAYS diff --git a/docs/sidebars.json b/docs/sidebars.json index 918464e3b1..e76caf1d33 100644 --- a/docs/sidebars.json +++ b/docs/sidebars.json @@ -292,6 +292,7 @@ "sql-manual/sql-functions/date-time-functions/minute", "sql-manual/sql-functions/date-time-functions/second", "sql-manual/sql-functions/date-time-functions/from_days", + "sql-manual/sql-functions/date-time-functions/last_day", "sql-manual/sql-functions/date-time-functions/from_unixtime", "sql-manual/sql-functions/date-time-functions/unix_timestamp", "sql-manual/sql-functions/date-time-functions/utc_timestamp", diff --git a/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/last_day.md b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/last_day.md new file mode 100644 index 0000000000..8f7a065ff4 --- /dev/null +++ b/docs/zh-CN/docs/sql-manual/sql-functions/date-time-functions/last_day.md @@ -0,0 +1,49 @@ +--- +{ + "title": "last_day", + "language": "zh-CN" +} +--- + +<!-- +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. +--> + +## last_day +### Description +#### Syntax + +`DATE last_day(DATETIME date)` + +返回输入日期中月份的最后一天;所以返回的日期中,年和月不变,日可能是如下情况: +'28'(非闰年的二月份), +'29'(闰年的二月份), +'30'(四月,六月,九月,十一月), +'31'(一月,三月,五月,七月,八月,十月,十二月) + +### example + +``` +mysql > select last_day('2000-02-03'); ++-------------------+ +| last_day('2000-02-03 00:00:00') | ++-------------------+ +| 2000-02-29 | ++-------------------+ +``` + +### keywords + LAST_DAY,DAYS diff --git a/gensrc/script/doris_builtins_functions.py b/gensrc/script/doris_builtins_functions.py index 0976d0b284..68f1a4e48f 100755 --- a/gensrc/script/doris_builtins_functions.py +++ b/gensrc/script/doris_builtins_functions.py @@ -500,6 +500,18 @@ visible_functions = [ [['from_days'], 'DATE', ['INT'], '_ZN5doris18TimestampFunctions9from_daysEPN9doris_udf15FunctionContextERKNS1_6IntValE', '', '', 'vec', 'ALWAYS_NULLABLE'], + [['last_day'], 'DATE', ['DATETIME'], + '_ZN5doris18TimestampFunctions8last_dayEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE', + '', '', 'vec', 'ALWAYS_NULLABLE'], + [['last_day'], 'DATE', ['DATE'], + '_ZN5doris18TimestampFunctions8last_dayEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE', + '', '', 'vec', 'ALWAYS_NULLABLE'], + [['last_day'], 'DATEV2', ['DATETIMEV2'], + '_ZN5doris18TimestampFunctions8last_dayEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE', + '', '', 'vec', 'ALWAYS_NULLABLE'], + [['last_day'], 'DATEV2', ['DATEV2'], + '_ZN5doris18TimestampFunctions8last_dayEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE', + '', '', 'vec', 'ALWAYS_NULLABLE'], [['to_days'], 'INT', ['DATE'], '_ZN5doris18TimestampFunctions7to_daysEPN9doris_udf15FunctionContextERKNS1_11DateTimeValE', '', '', 'vec', 'ALWAYS_NULLABLE'], diff --git a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out index 4c8b24b8c7..b465af0658 100644 --- a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out +++ b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_function.out @@ -529,3 +529,15 @@ true -- !sql -- 2019-08-01T13:21:02.111111 +-- !sql -- +\N \N \N \N +2000-02-29 2000-02-29 2000-02-29 2000-02-29 +2022-01-31 2022-01-31 2022-01-31 2022-01-31 +2022-02-28 2022-02-28 2022-02-28 2022-02-28 + +-- !sql -- +\N \N +2000-02-29 2000-02-29 +2022-01-31 2022-01-31 +2022-02-28 2022-02-28 + 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 5250447e45..7ff926157f 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 @@ -429,4 +429,52 @@ suite("test_date_function") { qt_sql """ select minutes_sub(test_time2,1) result from ${tableName}; """ //seconds_sub qt_sql """ select seconds_sub(test_time2,1) result from ${tableName}; """ + + // test last_day for vec + sql """ SET enable_vectorized_engine = TRUE; """ + sql """ DROP TABLE IF EXISTS ${tableName}; """ + sql """ + CREATE TABLE IF NOT EXISTS ${tableName} ( + birth date, + birth1 datev2, + birth2 datetime, + birth3 datetimev2) + UNIQUE KEY(birth, birth1, birth2, birth3) + DISTRIBUTED BY HASH (birth) BUCKETS 1 + PROPERTIES( "replication_allocation" = "tag.location.default: 1"); + """ + sql """ + insert into ${tableName} values + ('2022-01-01', '2022-01-01', '2022-01-01 00:00:00', '2022-01-01 00:00:00'), + ('2000-02-01', '2000-02-01', '2000-02-01 00:00:00', '2000-02-01 00:00:00.123'), + ('2022-02-29', '2022-02-29', '2022-02-29 00:00:00', '2022-02-29 00:00:00'), + ('2022-02-28', '2022-02-28', '2022-02-28 23:59:59', '2022-02-28 23:59:59');""" + qt_sql """ + select last_day(birth), last_day(birth1), + last_day(birth2), last_day(birth3) + from ${tableName}; + """ + sql """ DROP TABLE IF EXISTS ${tableName}; """ + + // test last_day for not vec + sql """ SET enable_vectorized_engine = FALSE; """ + sql """ DROP TABLE IF EXISTS ${tableName}; """ + sql """ + CREATE TABLE IF NOT EXISTS ${tableName} ( + birth date, + birth1 datetime) + UNIQUE KEY(birth, birth1) + DISTRIBUTED BY HASH (birth) BUCKETS 1 + PROPERTIES( "replication_allocation" = "tag.location.default: 1"); + """ + sql """ + insert into ${tableName} values + ('2022-01-01', '2022-01-01 00:00:00'), + ('2000-02-01', '2000-02-01 00:00:00'), + ('2022-02-29', '2022-02-29 00:00:00'), + ('2022-02-28', '2022-02-28 23:59:59');""" + qt_sql """ + select last_day(birth), last_day(birth1) from ${tableName}; + """ + sql """ DROP TABLE IF EXISTS ${tableName}; """ } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org