Skip to content

Commit 3437cef

Browse files
committed
[Feature](func) Support function maketime (apache#57282)
```text mysql> SELECT `hour`, `minute`, `sec`, MAKETIME(`hour`, `minute`, `sec`) AS ans FROM `test_maketime`; +-------+--------+---------+-------------------+ | hour | minute | sec | ans | +-------+--------+---------+-------------------+ | 12 | 15 | 30 | 12:15:30.000000 | | 111 | 0 | 23.1235 | 111:00:23.123457 | | 1234 | 11 | 4 | 838:59:59.000000 | | -1234 | 6 | 52 | -838:59:59.000000 | | 20 | 60 | 12 | NULL | | 14 | 51 | 66 | NULL | | NULL | 15 | 16 | NULL | | 7 | NULL | 8 | NULL | | 1 | 2 | NULL | NULL | +-------+--------+---------+-------------------+ mysql> SELECT `hour`, `minute`, MAKETIME(`hour`, `minute`, 27) AS ans FROM `test_maketime`; +-------+--------+------------+ | hour | minute | ans | +-------+--------+------------+ | 12 | 15 | 12:15:27 | | 111 | 0 | 111:00:27 | | 1234 | 11 | 838:59:59 | | -1234 | 6 | -838:59:59 | | 20 | 60 | NULL | | 14 | 51 | 14:51:27 | | NULL | 15 | NULL | | 7 | NULL | NULL | | 1 | 2 | 01:02:27 | +-------+--------+------------+ ```
1 parent 85c6f70 commit 3437cef

File tree

7 files changed

+310
-47
lines changed

7 files changed

+310
-47
lines changed

be/src/vec/functions/function_other_types_to_date.cpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#include "vec/columns/column_string.h"
4545
#include "vec/columns/column_vector.h"
4646
#include "vec/common/assert_cast.h"
47+
#include "vec/common/pod_array.h"
4748
#include "vec/common/pod_array_fwd.h"
4849
#include "vec/common/string_ref.h"
4950
#include "vec/core/block.h"
@@ -62,6 +63,7 @@
6263
#include "vec/functions/datetime_errors.h"
6364
#include "vec/functions/function.h"
6465
#include "vec/functions/simple_function_factory.h"
66+
#include "vec/runtime/time_value.h"
6567
#include "vec/runtime/vdatetime_value.h"
6668
#include "vec/utils/util.hpp"
6769

@@ -379,6 +381,91 @@ struct MakeDateImpl {
379381
}
380382
};
381383

384+
struct MakeTimeImpl {
385+
static constexpr auto name = "maketime";
386+
using DateValueType = PrimitiveTypeTraits<PrimitiveType::TYPE_TIMEV2>::CppType;
387+
using NativeType = PrimitiveTypeTraits<PrimitiveType::TYPE_TIMEV2>::CppNativeType;
388+
static bool is_variadic() { return false; }
389+
static size_t get_number_of_arguments() { return 3; }
390+
static DataTypes get_variadic_argument_types() { return {}; }
391+
static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
392+
return make_nullable(std::make_shared<DataTypeTimeV2>());
393+
}
394+
395+
static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
396+
uint32_t result, size_t input_rows_count) {
397+
DCHECK_EQ(arguments.size(), 3);
398+
auto res_col = ColumnTimeV2::create(input_rows_count);
399+
auto res_null_map = ColumnUInt8::create(input_rows_count, 0);
400+
auto& res_data = res_col->get_data();
401+
402+
ColumnPtr arg_col[3];
403+
bool is_const[3];
404+
for (int i = 0; i < arguments.size(); ++i) {
405+
std::tie(arg_col[i], is_const[i]) =
406+
unpack_if_const(block.get_by_position(arguments[i]).column);
407+
}
408+
409+
const auto& hour_data = assert_cast<const ColumnInt64*>(arg_col[0].get())->get_data();
410+
const auto& min_data = assert_cast<const ColumnInt64*>(arg_col[1].get())->get_data();
411+
if (const auto* sec_col = check_and_get_column<ColumnFloat64>(arg_col[2].get())) {
412+
for (int i = 0; i < input_rows_count; ++i) {
413+
int64_t hour = hour_data[index_check_const(i, is_const[0])];
414+
int64_t minute = min_data[index_check_const(i, is_const[1])];
415+
double sec = sec_col->get_element(index_check_const(i, is_const[2]));
416+
if (!check_and_set_time_value(hour, minute, sec)) {
417+
res_data[i] = 0;
418+
res_null_map->get_data()[i] = 1;
419+
continue;
420+
}
421+
execute_single(hour, minute, sec, res_data, i);
422+
}
423+
} else {
424+
const auto& sec_data = assert_cast<const ColumnInt64*>(arg_col[2].get())->get_data();
425+
for (int i = 0; i < input_rows_count; ++i) {
426+
int64_t hour = hour_data[index_check_const(i, is_const[0])];
427+
int64_t minute = min_data[index_check_const(i, is_const[1])];
428+
double sec = static_cast<double>(sec_data[index_check_const(i, is_const[2])]);
429+
if (!check_and_set_time_value(hour, minute, sec)) {
430+
res_data[i] = 0;
431+
res_null_map->get_data()[i] = 1;
432+
continue;
433+
}
434+
execute_single(hour, minute, sec, res_data, i);
435+
}
436+
}
437+
438+
block.replace_by_position(
439+
result, ColumnNullable::create(std::move(res_col), std::move(res_null_map)));
440+
return Status::OK();
441+
}
442+
443+
private:
444+
static bool check_and_set_time_value(int64_t& hour, int64_t& minute, double& sec) {
445+
static constexpr int MAX_HOUR_FOR_MAKETIME = 838;
446+
if (minute < 0 || minute >= 60 || sec < 0 || sec >= 60) {
447+
return false;
448+
}
449+
450+
// Avoid overflow in `execute_single`
451+
// the case {838, 59, 59.999999} will be slove in `TimeValue::from_double_with_limit`
452+
if (std::abs(hour) > MAX_HOUR_FOR_MAKETIME) {
453+
hour = hour > 0 ? MAX_HOUR_FOR_MAKETIME : -MAX_HOUR_FOR_MAKETIME;
454+
minute = sec = 59;
455+
}
456+
return true;
457+
}
458+
459+
static void execute_single(int64_t hour, int64_t minute, double sec,
460+
PaddedPODArray<double>& res_data, size_t row) {
461+
// Round sec to 6 decimal places (microsecond precision)
462+
double total_sec =
463+
std::abs(hour) * 3600 + minute * 60 + std::round(sec * 1000000.0) / 1000000.0;
464+
465+
res_data[row] = TimeValue::from_double_with_limit(total_sec * (hour < 0 ? -1 : 1));
466+
}
467+
};
468+
382469
struct DateTruncState {
383470
using Callback_function = std::function<void(const ColumnPtr&, ColumnPtr& res, size_t)>;
384471
Callback_function callback_function;
@@ -1360,6 +1447,7 @@ void register_function_timestamp(SimpleFunctionFactory& factory) {
13601447
factory.register_function<FunctionStrToDate>();
13611448
factory.register_function<FunctionStrToDatetime>();
13621449
factory.register_function<FunctionMakeDate>();
1450+
factory.register_function<FunctionOtherTypesToDateType<MakeTimeImpl>>();
13631451
factory.register_function<FromDays>();
13641452
factory.register_function<FunctionDateTruncDateV2>();
13651453
factory.register_function<FunctionDateTruncDatetimeV2>();

fe/fe-core/src/main/java/org/apache/doris/catalog/BuiltinScalarFunctions.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
import org.apache.doris.nereids.trees.expressions.functions.scalar.LtrimIn;
313313
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeDate;
314314
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeSet;
315+
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeTime;
315316
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsEntry;
316317
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsKey;
317318
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsValue;
@@ -846,6 +847,7 @@ public class BuiltinScalarFunctions implements FunctionHelper {
846847
scalar(LtrimIn.class, "ltrim_in"),
847848
scalar(MakeDate.class, "makedate"),
848849
scalar(MakeSet.class, "make_set"),
850+
scalar(MakeTime.class, "maketime"),
849851
scalar(MapContainsEntry.class, "map_contains_entry"),
850852
scalar(MapContainsKey.class, "map_contains_key"),
851853
scalar(MapContainsValue.class, "map_contains_value"),

fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.apache.doris.nereids.types.DateV2Type;
4646
import org.apache.doris.nereids.types.DecimalV3Type;
4747
import org.apache.doris.nereids.types.StringType;
48+
import org.apache.doris.nereids.types.TimeV2Type;
4849
import org.apache.doris.nereids.util.DateUtils;
4950
import org.apache.doris.qe.ConnectContext;
5051

@@ -711,6 +712,34 @@ public static Expression makeDate(IntegerLiteral year, IntegerLiteral dayOfYear)
711712
: new NullLiteral(DateV2Type.INSTANCE);
712713
}
713714

715+
/**
716+
* time transformation function: maketime
717+
*/
718+
@ExecFunction(name = "maketime")
719+
public static Expression makeTime(BigIntLiteral hour, BigIntLiteral minute, DoubleLiteral second) {
720+
long hourValue = hour.getValue();
721+
long minuteValue = minute.getValue();
722+
double secondValue = second.getValue();
723+
724+
if (minuteValue < 0 || minuteValue >= 60 || secondValue < 0 || secondValue >= 60) {
725+
return new NullLiteral(TimeV2Type.INSTANCE);
726+
}
727+
if (Math.abs(hourValue) > 838) {
728+
hourValue = hourValue > 0 ? 838 : -838;
729+
minuteValue = 59;
730+
secondValue = 59;
731+
} else if (Math.abs(hourValue) == 838 && secondValue > 59) {
732+
secondValue = 59;
733+
}
734+
735+
double totalSeconds = Math.abs(hourValue) * 3600 + minuteValue * 60
736+
+ Math.round(secondValue * 1000000.0) / 1000000.0;
737+
if (hourValue < 0) {
738+
totalSeconds = -totalSeconds;
739+
}
740+
return new TimeV2Literal(totalSeconds);
741+
}
742+
714743
/**
715744
* date transformation function: str_to_date
716745
*/
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.doris.nereids.trees.expressions.functions.scalar;
19+
20+
import org.apache.doris.catalog.FunctionSignature;
21+
import org.apache.doris.nereids.trees.expressions.Expression;
22+
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
23+
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
24+
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
25+
import org.apache.doris.nereids.types.BigIntType;
26+
import org.apache.doris.nereids.types.DoubleType;
27+
import org.apache.doris.nereids.types.TimeV2Type;
28+
29+
import com.google.common.base.Preconditions;
30+
import com.google.common.collect.ImmutableList;
31+
32+
import java.util.List;
33+
34+
/**
35+
* ScalarFunction 'maketime'.
36+
*/
37+
public class MakeTime extends ScalarFunction implements ExplicitlyCastableSignature, AlwaysNullable {
38+
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
39+
FunctionSignature.ret(TimeV2Type.INSTANCE)
40+
.args(BigIntType.INSTANCE, BigIntType.INSTANCE, BigIntType.INSTANCE),
41+
FunctionSignature.ret(TimeV2Type.MAX)
42+
.args(BigIntType.INSTANCE, BigIntType.INSTANCE, DoubleType.INSTANCE)
43+
44+
);
45+
46+
/**
47+
* constructor with 2 arguments.
48+
*/
49+
public MakeTime(Expression arg0, Expression arg1, Expression arg2) {
50+
super("maketime", arg0, arg1, arg2);
51+
}
52+
53+
/** constructor for withChildren and reuse signature */
54+
private MakeTime(ScalarFunctionParams functionParams) {
55+
super(functionParams);
56+
}
57+
58+
/**
59+
* withChildren.
60+
*/
61+
@Override
62+
public MakeTime withChildren(List<Expression> children) {
63+
Preconditions.checkArgument(children.size() == 3);
64+
return new MakeTime(getFunctionParams(children));
65+
}
66+
67+
@Override
68+
public List<FunctionSignature> getSignatures() {
69+
return SIGNATURES;
70+
}
71+
72+
@Override
73+
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
74+
return visitor.visitMakeTime(this, context);
75+
}
76+
}

fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ScalarFunctionVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@
318318
import org.apache.doris.nereids.trees.expressions.functions.scalar.LtrimIn;
319319
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeDate;
320320
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeSet;
321+
import org.apache.doris.nereids.trees.expressions.functions.scalar.MakeTime;
321322
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsEntry;
322323
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsKey;
323324
import org.apache.doris.nereids.trees.expressions.functions.scalar.MapContainsValue;
@@ -2640,6 +2641,10 @@ default R visitMakeSet(MakeSet makeSet, C context) {
26402641
return visitScalarFunction(makeSet, context);
26412642
}
26422643

2644+
default R visitMakeTime(MakeTime makeTime, C context) {
2645+
return visitScalarFunction(makeTime, context);
2646+
}
2647+
26432648
default R visitExportSet(ExportSet exportSet, C context) {
26442649
return visitScalarFunction(exportSet, context);
26452650
}

regression-test/data/doc/sql-manual/sql-functions/doc_date_functions_test.out

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,3 +1970,29 @@ da fanadur
19701970
-- !yearweek_5 --
19711971
202352
19721972

1973+
-- !maketime_test_1 --
1974+
12:15:30.000000
1975+
111:00:23.123457
1976+
838:59:59.000000
1977+
-838:59:59.000000
1978+
\N
1979+
\N
1980+
\N
1981+
\N
1982+
\N
1983+
\N
1984+
\N
1985+
1986+
-- !maketime_test_2 --
1987+
12:15:25
1988+
111:00:25
1989+
838:59:59
1990+
-838:59:59
1991+
\N
1992+
14:51:25
1993+
\N
1994+
\N
1995+
01:02:25
1996+
\N
1997+
07:23:25
1998+

0 commit comments

Comments
 (0)