Skip to content

Commit ef2667b

Browse files
authored
[feat](func) Support CURTIME with precision (#56665)
### What problem does this PR solve? Issue Number: #48203 Related PR: #xxx Problem Summary: ```text mysql> select curtime(5); +----------------+ | curtime(5) | +----------------+ | 12:52:30.77164 | +----------------+ mysql> select curtime(114514); ERROR 1105 (HY000): errCode = 2, detailMessage = Can not find the compatibility function signature: current_time(INT) mysql> SELECT CURTIME(7); ERROR 1105 (HY000): errCode = 2, detailMessage = The precision must be between 0 and 6 ```
1 parent 759ed47 commit ef2667b

File tree

6 files changed

+188
-6
lines changed

6 files changed

+188
-6
lines changed

be/src/vec/functions/function_date_or_datetime_computation.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -838,10 +838,31 @@ struct CurrentTimeImpl {
838838
static Status execute(FunctionContext* context, Block& block, const ColumnNumbers& arguments,
839839
uint32_t result, size_t input_rows_count) {
840840
auto col_to = ColumnTimeV2::create();
841-
VecDateTimeValue dtv;
841+
DateV2Value<DateTimeV2ValueType> dtv;
842842
dtv.from_unixtime(context->state()->timestamp_ms() / 1000,
843843
context->state()->timezone_obj());
844-
auto time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second());
844+
double time;
845+
if (arguments.size() == 1) {
846+
// the precision must be const, which is checked in fe.
847+
const auto* col = assert_cast<const ColumnInt8*>(
848+
block.get_by_position(arguments[0]).column.get());
849+
uint8_t precision = col->get_element(0);
850+
if (precision <= 6) {
851+
dtv.from_unixtime(context->state()->timestamp_ms() / 1000,
852+
context->state()->nano_seconds(),
853+
context->state()->timezone_obj(), precision);
854+
time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second(),
855+
dtv.microsecond());
856+
} else {
857+
return Status::InvalidArgument(
858+
"The precision in function CURTIME should be between 0 and 6, but got {}",
859+
precision);
860+
}
861+
} else {
862+
dtv.from_unixtime(context->state()->timestamp_ms() / 1000,
863+
context->state()->timezone_obj());
864+
time = TimeValue::make_time(dtv.hour(), dtv.minute(), dtv.second());
865+
}
845866
col_to->insert_value(time);
846867
block.get_by_position(result).column =
847868
ColumnConst::create(std::move(col_to), input_rows_count);

be/test/vec/function/function_time_test.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,4 +1558,87 @@ TEST(VTimestampFunctionsTest, time) {
15581558
static_cast<void>(check_function<DataTypeTimeV2, true>(func_name, input_types, data_set));
15591559
}
15601560

1561+
TEST(VTimestampFunctionsTest, curtime_test) {
1562+
std::string func_name = "curtime";
1563+
1564+
// Test curtime without precision
1565+
{
1566+
InputTypeSet input_types = {};
1567+
Block block;
1568+
ColumnsWithTypeAndName arguments;
1569+
1570+
auto return_type = std::make_shared<DataTypeTimeV2>(0);
1571+
FunctionBasePtr func =
1572+
SimpleFunctionFactory::instance().get_function(func_name, arguments, return_type);
1573+
EXPECT_TRUE(func != nullptr);
1574+
1575+
auto fn_ctx_return = std::make_shared<DataTypeTimeV2>(0);
1576+
std::vector<DataTypePtr> arg_types = {};
1577+
FunctionUtils fn_utils(fn_ctx_return, arg_types, false);
1578+
auto* fn_ctx = fn_utils.get_fn_ctx();
1579+
1580+
EXPECT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
1581+
EXPECT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
1582+
1583+
block.insert({nullptr, return_type, "result"});
1584+
ColumnNumbers args;
1585+
auto st = func->execute(fn_ctx, block, args, 0, 1);
1586+
EXPECT_TRUE(st.ok());
1587+
1588+
auto result_col = block.get_by_position(0).column;
1589+
EXPECT_TRUE(result_col);
1590+
if (const auto* const_col = check_and_get_column<ColumnConst>(result_col.get())) {
1591+
auto time_value = const_col->get_field().get<double>();
1592+
EXPECT_GE(time_value, 0.0);
1593+
EXPECT_LE(time_value, 24.0 * 3600 * 1000000);
1594+
}
1595+
1596+
EXPECT_TRUE(func->close(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
1597+
EXPECT_TRUE(func->close(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
1598+
}
1599+
1600+
// Test curtime with precision
1601+
{
1602+
InputTypeSet input_types = {PrimitiveType::TYPE_TINYINT};
1603+
Block block;
1604+
1605+
auto precision_col = ColumnInt8::create();
1606+
precision_col->insert_value(3);
1607+
auto precision_type = std::make_shared<DataTypeInt8>();
1608+
block.insert({std::move(precision_col), precision_type, "precision"});
1609+
1610+
ColumnsWithTypeAndName arguments;
1611+
arguments.push_back(block.get_by_position(0));
1612+
1613+
auto return_type = std::make_shared<DataTypeTimeV2>(3);
1614+
FunctionBasePtr func =
1615+
SimpleFunctionFactory::instance().get_function(func_name, arguments, return_type);
1616+
EXPECT_TRUE(func != nullptr);
1617+
1618+
auto fn_ctx_return = std::make_shared<DataTypeTimeV2>(3);
1619+
std::vector<DataTypePtr> arg_types = {precision_type};
1620+
FunctionUtils fn_utils(fn_ctx_return, arg_types, false);
1621+
auto* fn_ctx = fn_utils.get_fn_ctx();
1622+
1623+
EXPECT_TRUE(func->open(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
1624+
EXPECT_TRUE(func->open(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
1625+
1626+
block.insert({nullptr, return_type, "result"});
1627+
ColumnNumbers args = {0};
1628+
auto st = func->execute(fn_ctx, block, args, 1, 1);
1629+
EXPECT_TRUE(st.ok());
1630+
1631+
auto result_col = block.get_by_position(1).column;
1632+
EXPECT_TRUE(result_col);
1633+
if (const auto* const_col = check_and_get_column<ColumnConst>(result_col.get())) {
1634+
auto time_value = const_col->get_field().get<double>();
1635+
EXPECT_GE(time_value, 0.0);
1636+
EXPECT_LE(time_value, 24.0 * 3600 * 1000000);
1637+
}
1638+
1639+
EXPECT_TRUE(func->close(fn_ctx, FunctionContext::THREAD_LOCAL).ok());
1640+
EXPECT_TRUE(func->close(fn_ctx, FunctionContext::FRAGMENT_LOCAL).ok());
1641+
}
1642+
}
1643+
15611644
} // namespace doris::vectorized

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
2525
import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
2626
import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal;
27+
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
2728
import org.apache.doris.nereids.util.DateUtils;
2829

2930
import java.time.LocalDateTime;
@@ -95,11 +96,21 @@ public static Expression curTime() {
9596
return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
9697
}
9798

99+
@ExecFunction(name = "curtime")
100+
public static Expression curTime(TinyIntLiteral precision) {
101+
return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), precision.getValue());
102+
}
103+
98104
@ExecFunction(name = "current_time")
99105
public static Expression currentTime() {
100106
return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()));
101107
}
102108

109+
@ExecFunction(name = "current_time")
110+
public static Expression currentTime(TinyIntLiteral precision) {
111+
return TimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), precision.getValue());
112+
}
113+
103114
/**
104115
* date transformation function: unix_timestamp
105116
*/

fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CurrentTime.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
package org.apache.doris.nereids.trees.expressions.functions.scalar;
1919

2020
import org.apache.doris.catalog.FunctionSignature;
21+
import org.apache.doris.nereids.exceptions.AnalysisException;
2122
import org.apache.doris.nereids.trees.expressions.Expression;
2223
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
23-
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
24+
import org.apache.doris.nereids.trees.expressions.functions.ComputeSignature;
25+
import org.apache.doris.nereids.trees.expressions.functions.ImplicitlyCastableSignature;
26+
import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
2427
import org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
2528
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
2629
import org.apache.doris.nereids.types.TimeV2Type;
30+
import org.apache.doris.nereids.types.TinyIntType;
2731

2832
import com.google.common.base.Preconditions;
2933
import com.google.common.collect.ImmutableList;
@@ -34,10 +38,11 @@
3438
* ScalarFunction 'current_time'. This class is generated by GenerateFunction.
3539
*/
3640
public class CurrentTime extends ScalarFunction
37-
implements LeafExpression, ExplicitlyCastableSignature, AlwaysNotNullable {
41+
implements LeafExpression, ImplicitlyCastableSignature, AlwaysNotNullable, ComputeSignature {
3842

3943
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
40-
FunctionSignature.ret(TimeV2Type.INSTANCE).args()
44+
FunctionSignature.ret(TimeV2Type.INSTANCE).args(),
45+
FunctionSignature.ret(TimeV2Type.INSTANCE).args(TinyIntType.INSTANCE)
4146
);
4247

4348
/**
@@ -47,17 +52,51 @@ public CurrentTime() {
4752
super("current_time");
4853
}
4954

55+
/**
56+
* constructor with 1 argument.
57+
*/
58+
public CurrentTime(Expression arg) {
59+
super("current_time", arg);
60+
}
61+
5062
/** constructor for withChildren and reuse signature */
5163
private CurrentTime(ScalarFunctionParams functionParams) {
5264
super(functionParams);
5365
}
5466

67+
@Override
68+
public FunctionSignature computeSignature(FunctionSignature signature) {
69+
if (arity() == 1 && child(0) instanceof TinyIntLiteral) {
70+
byte precision = ((TinyIntLiteral) child(0)).getValue();
71+
if (precision < 0 || precision > 6) {
72+
throw new IllegalArgumentException("The precision must be between 0 and 6");
73+
}
74+
return FunctionSignature.ret(TimeV2Type.of(precision)).args(TinyIntType.INSTANCE);
75+
}
76+
77+
return super.computeSignature(signature);
78+
}
79+
5580
@Override
5681
public Expression withChildren(List<Expression> children) {
57-
Preconditions.checkArgument(children.isEmpty());
82+
Preconditions.checkArgument(children.isEmpty() || children.size() == 1);
5883
return new CurrentTime(getFunctionParams(children));
5984
}
6085

86+
@Override
87+
public void checkLegalityAfterRewrite() {
88+
if (arity() == 1) {
89+
if (!child(0).isLiteral()) {
90+
throw new AnalysisException("CURTIME only accepts literal as precision.");
91+
}
92+
}
93+
}
94+
95+
@Override
96+
public void checkLegalityBeforeTypeCoercion() {
97+
checkLegalityAfterRewrite();
98+
}
99+
61100
@Override
62101
public List<FunctionSignature> getSignatures() {
63102
return SIGNATURES;

fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,18 @@ public static Expression fromJavaDateType(LocalDateTime dateTime) {
301301
return new TimeV2Literal(dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(), 0, 0, false);
302302
}
303303

304+
/**
305+
* construct with precision
306+
*/
307+
public static Expression fromJavaDateType(LocalDateTime dateTime, int precision) {
308+
if (isDateOutOfRange(dateTime)) {
309+
throw new AnalysisException("datetime out of range" + dateTime.toString());
310+
}
311+
int value = (int) Math.pow(10, TimeV2Type.MAX_SCALE - precision);
312+
return new TimeV2Literal(dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond(),
313+
(dateTime.getNano() / 1000) / value * value, precision, false);
314+
}
315+
304316
public LocalDateTime toJavaDateType() {
305317
return LocalDateTime.of(0, 1, 1, ((int) getHour()), ((int) getMinute()), ((int) getSecond()),
306318
(int) getMicroSecond() * 1000);

regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_function.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,22 @@ suite("test_date_function") {
226226
// TIME CURTIME()
227227
def curtime_result = sql """ SELECT CURTIME() """
228228
assertTrue(curtime_result[0].size() == 1)
229+
def curtime_with_arg = sql """ SELECT CAST(CURTIME(3) AS STRING) """
230+
assertTrue(curtime_with_arg[0].size() == 1)
231+
assertTrue(curtime_with_arg[0][0].contains('.'))
232+
233+
curtime_with_arg = sql """ SELECT CAST(CURTIME(0) AS STRING) """
234+
assertTrue(curtime_with_arg[0].size() == 1)
235+
assertFalse(curtime_with_arg[0][0].contains('.'))
236+
237+
test {
238+
sql """ SELECT CURTIME(114514);"""
239+
exception "Can not find the compatibility function signature: current_time(INT)"
240+
}
241+
test {
242+
sql """ SELECT CURTIME(7); """
243+
exception "The precision must be between 0 and 6"
244+
}
229245

230246
sql """ insert into ${tableName} values ("2010-11-30 23:59:59") """
231247
// DATE_ADD

0 commit comments

Comments
 (0)