This is an automated email from the ASF dual-hosted git repository.

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new 85c82072375 branch-3.0: [function](date) Support date trunc function 
#49540 (#49660)
85c82072375 is described below

commit 85c820723754825af1010e415590a94a7e154dc4
Author: Gabriel <liwenqi...@selectdb.com>
AuthorDate: Mon Mar 31 09:54:43 2025 +0800

    branch-3.0: [function](date) Support date trunc function #49540 (#49660)
---
 be/src/vec/functions/function_timestamp.cpp        |  51 +++++++++----
 .../apache/doris/analysis/FunctionCallExpr.java    |   2 +-
 .../executable/DateTimeExtractAndTransform.java    |  20 +++++
 .../expressions/functions/scalar/DateTrunc.java    |  85 ++++++++++++++-------
 .../datetime_functions/test_date_trunc.out         | Bin 5294 -> 9785 bytes
 .../test_auto_range_partition.groovy               |   2 +-
 .../datetime_functions/test_date_trunc.groovy      |   8 ++
 7 files changed, 125 insertions(+), 43 deletions(-)

diff --git a/be/src/vec/functions/function_timestamp.cpp 
b/be/src/vec/functions/function_timestamp.cpp
index 428f8c2893f..fb3c78bbb9f 100644
--- a/be/src/vec/functions/function_timestamp.cpp
+++ b/be/src/vec/functions/function_timestamp.cpp
@@ -403,7 +403,7 @@ struct DateTruncState {
     Callback_function callback_function;
 };
 
-template <typename DateType>
+template <typename DateType, bool DateArgIsFirst>
 struct DateTrunc {
     static constexpr auto name = "date_trunc";
 
@@ -416,7 +416,11 @@ struct DateTrunc {
     static size_t get_number_of_arguments() { return 2; }
 
     static DataTypes get_variadic_argument_types() {
-        return {std::make_shared<DateType>(), 
std::make_shared<DataTypeString>()};
+        if constexpr (DateArgIsFirst) {
+            return {std::make_shared<DateType>(), 
std::make_shared<DataTypeString>()};
+        } else {
+            return {std::make_shared<DataTypeString>(), 
std::make_shared<DateType>()};
+        }
     }
 
     static DataTypePtr get_return_type_impl(const DataTypes& arguments) {
@@ -427,11 +431,12 @@ struct DateTrunc {
         if (scope != FunctionContext::THREAD_LOCAL) {
             return Status::OK();
         }
-        if (!context->is_col_constant(1)) {
+        if (!context->is_col_constant(DateArgIsFirst ? 1 : 0)) {
             return Status::InvalidArgument(
                     "date_trunc function of time unit argument must be 
constant.");
         }
-        const auto& data_str = 
context->get_constant_col(1)->column_ptr->get_data_at(0);
+        const auto& data_str =
+                context->get_constant_col(DateArgIsFirst ? 1 : 
0)->column_ptr->get_data_at(0);
         std::string lower_str(data_str.data, data_str.size);
         std::transform(lower_str.begin(), lower_str.end(), lower_str.begin(),
                        [](unsigned char c) { return std::tolower(c); });
@@ -467,8 +472,8 @@ struct DateTrunc {
         DCHECK_EQ(arguments.size(), 2);
 
         auto null_map = ColumnUInt8::create(input_rows_count, 0);
-        const auto& datetime_column =
-                
block.get_by_position(arguments[0]).column->convert_to_full_column_if_const();
+        const auto& datetime_column = 
block.get_by_position(arguments[DateArgIsFirst ? 0 : 1])
+                                              
.column->convert_to_full_column_if_const();
         ColumnPtr res = ColumnType::create(input_rows_count);
         auto* state = reinterpret_cast<DateTruncState*>(
                 context->get_function_state(FunctionContext::THREAD_LOCAL));
@@ -1169,10 +1174,14 @@ public:
     }
 
     Status open(FunctionContext* context, FunctionContext::FunctionStateScope 
scope) override {
-        if constexpr (std::is_same_v<Impl, DateTrunc<DataTypeDate>> ||
-                      std::is_same_v<Impl, DateTrunc<DataTypeDateV2>> ||
-                      std::is_same_v<Impl, DateTrunc<DataTypeDateTime>> ||
-                      std::is_same_v<Impl, DateTrunc<DataTypeDateTimeV2>>) {
+        if constexpr (std::is_same_v<Impl, DateTrunc<DataTypeDate, true>> ||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateV2, true>> ||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateTime, true>> 
||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateTimeV2, 
true>> ||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDate, false>> ||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateV2, false>> ||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateTime, false>> 
||
+                      std::is_same_v<Impl, DateTrunc<DataTypeDateTimeV2, 
false>>) {
             return Impl::open(context, scope);
         } else {
             return Status::OK();
@@ -1427,10 +1436,20 @@ using FunctionStrToDatetime = 
FunctionOtherTypesToDateType<StrToDate<DataTypeDat
 using FunctionStrToDateV2 = 
FunctionOtherTypesToDateType<StrToDate<DataTypeDateV2>>;
 using FunctionStrToDatetimeV2 = 
FunctionOtherTypesToDateType<StrToDate<DataTypeDateTimeV2>>;
 using FunctionMakeDate = FunctionOtherTypesToDateType<MakeDateImpl>;
-using FunctionDateTruncDate = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDate>>;
-using FunctionDateTruncDateV2 = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDateV2>>;
-using FunctionDateTruncDatetime = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTime>>;
-using FunctionDateTruncDatetimeV2 = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTimeV2>>;
+using FunctionDateTruncDate = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDate, true>>;
+using FunctionDateTruncDateV2 = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDateV2, true>>;
+using FunctionDateTruncDatetime = 
FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTime, true>>;
+using FunctionDateTruncDatetimeV2 =
+        FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTimeV2, true>>;
+
+using FunctionDateTruncDateWithCommonOrder =
+        FunctionOtherTypesToDateType<DateTrunc<DataTypeDate, false>>;
+using FunctionDateTruncDateV2WithCommonOrder =
+        FunctionOtherTypesToDateType<DateTrunc<DataTypeDateV2, false>>;
+using FunctionDateTruncDatetimeWithCommonOrder =
+        FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTime, false>>;
+using FunctionDateTruncDatetimeV2WithCommonOrder =
+        FunctionOtherTypesToDateType<DateTrunc<DataTypeDateTimeV2, false>>;
 using FunctionFromIso8601DateV2 = 
FunctionOtherTypesToDateType<FromIso8601DateV2>;
 
 void register_function_timestamp(SimpleFunctionFactory& factory) {
@@ -1444,6 +1463,10 @@ void register_function_timestamp(SimpleFunctionFactory& 
factory) {
     factory.register_function<FunctionDateTruncDateV2>();
     factory.register_function<FunctionDateTruncDatetime>();
     factory.register_function<FunctionDateTruncDatetimeV2>();
+    factory.register_function<FunctionDateTruncDateWithCommonOrder>();
+    factory.register_function<FunctionDateTruncDateV2WithCommonOrder>();
+    factory.register_function<FunctionDateTruncDatetimeWithCommonOrder>();
+    factory.register_function<FunctionDateTruncDatetimeV2WithCommonOrder>();
     factory.register_function<FunctionFromIso8601DateV2>();
 
     factory.register_function<FunctionUnixTimestamp<UnixTimeStampImpl>>();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
index 40249ae5486..03f6762b438 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/FunctionCallExpr.java
@@ -1761,7 +1761,7 @@ public class FunctionCallExpr extends Expr {
             final String constParam = ((StringLiteral) 
getChild(1)).getValue().toLowerCase();
             if (!Lists.newArrayList("year", "quarter", "month", "week", "day", 
"hour", "minute", "second")
                     .contains(constParam)) {
-                throw new AnalysisException("date_trunc function second param 
only support argument is "
+                throw new AnalysisException("date_trunc function time unit 
param only support argument is "
                         + "year|quarter|month|week|day|hour|minute|second");
             }
         }
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 0cf8e38fcbc..0be185d80f9 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
@@ -358,6 +358,26 @@ public class DateTimeExtractAndTransform {
         return 
DateV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), 
trunc.getValue()));
     }
 
+    @ExecFunction(name = "date_trunc")
+    public static Expression dateTrunc(StringLikeLiteral trunc, 
DateTimeLiteral date) {
+        return 
DateTimeLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), 
trunc.getValue()));
+    }
+
+    @ExecFunction(name = "date_trunc")
+    public static Expression dateTrunc(StringLikeLiteral trunc, 
DateTimeV2Literal date) {
+        return 
DateTimeV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), 
trunc.getValue()));
+    }
+
+    @ExecFunction(name = "date_trunc")
+    public static Expression dateTrunc(StringLikeLiteral trunc, DateLiteral 
date) {
+        return 
DateLiteral.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), 
trunc.getValue()));
+    }
+
+    @ExecFunction(name = "date_trunc")
+    public static Expression dateTrunc(StringLikeLiteral trunc, DateV2Literal 
date) {
+        return 
DateV2Literal.fromJavaDateType(dateTruncHelper(date.toJavaDateType(), 
trunc.getValue()));
+    }
+
     private static LocalDateTime dateTruncHelper(LocalDateTime dateTime, 
String trunc) {
         int year = dateTime.getYear();
         int month = dateTime.getMonthValue();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
index 2a4d838c4fd..86a6bfccf56 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/DateTrunc.java
@@ -21,20 +21,17 @@ import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 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.functions.CustomSignature;
 import org.apache.doris.nereids.trees.expressions.functions.Monotonic;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
 import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
-import org.apache.doris.nereids.types.DateTimeType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
-import org.apache.doris.nereids.types.DateType;
-import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.VarcharType;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 
 import java.util.List;
 
@@ -42,16 +39,9 @@ import java.util.List;
  * ScalarFunction 'date_trunc'. This class is generated by GenerateFunction.
  */
 public class DateTrunc extends ScalarFunction
-        implements BinaryExpression, ExplicitlyCastableSignature, 
AlwaysNullable, Monotonic {
-
-    public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
-            FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
-                    .args(DateTimeV2Type.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT),
-            
FunctionSignature.ret(DateTimeType.INSTANCE).args(DateTimeType.INSTANCE, 
VarcharType.SYSTEM_DEFAULT),
-            FunctionSignature.ret(DateV2Type.INSTANCE)
-                    .args(DateV2Type.INSTANCE, VarcharType.SYSTEM_DEFAULT),
-            FunctionSignature.ret(DateType.INSTANCE).args(DateType.INSTANCE, 
VarcharType.SYSTEM_DEFAULT)
-    );
+        implements BinaryExpression, AlwaysNullable, Monotonic, 
CustomSignature {
+    private static final List<String> LEGAL_TIME_UNIT =
+            ImmutableList.of("year", "quarter", "month", "week", "day", 
"hour", "minute", "second");
 
     /**
      * constructor with 2 arguments.
@@ -62,15 +52,27 @@ public class DateTrunc extends ScalarFunction
 
     @Override
     public void checkLegalityBeforeTypeCoercion() {
-        if (getArgument(1).isConstant() == false || !(getArgument(1) 
instanceof VarcharLiteral)) {
-            throw new AnalysisException("the second parameter of "
+        boolean firstArgIsStringLiteral =
+                getArgument(0).isConstant() && getArgument(0) instanceof 
StringLikeLiteral;
+        boolean secondArgIsStringLiteral =
+                getArgument(1).isConstant() && getArgument(1) instanceof 
StringLikeLiteral;
+        if (!firstArgIsStringLiteral && !secondArgIsStringLiteral) {
+            throw new AnalysisException("the time unit parameter of "
                     + getName() + " function must be a string constant: " + 
toSql());
-        }
-        final String constParam = ((VarcharLiteral) 
getArgument(1)).getStringValue().toLowerCase();
-        if (!Lists.newArrayList("year", "quarter", "month", "week", "day", 
"hour", "minute", "second")
-                .contains(constParam)) {
-            throw new AnalysisException("date_trunc function second param only 
support argument is "
-                    + "year|quarter|month|week|day|hour|minute|second");
+        } else if (firstArgIsStringLiteral && secondArgIsStringLiteral) {
+            if (!LEGAL_TIME_UNIT.contains(((VarcharLiteral) 
getArgument(0)).getStringValue().toLowerCase())
+                    && !LEGAL_TIME_UNIT.contains(((VarcharLiteral) 
getArgument(1))
+                    .getStringValue().toLowerCase())) {
+                throw new AnalysisException("date_trunc function time unit 
param only support argument is "
+                        + String.join("|", LEGAL_TIME_UNIT));
+            }
+        } else {
+            final String constParam = ((VarcharLiteral) 
getArgument(firstArgIsStringLiteral ? 0 : 1))
+                    .getStringValue().toLowerCase();
+            if (!LEGAL_TIME_UNIT.contains(constParam)) {
+                throw new AnalysisException("date_trunc function time unit 
param only support argument is "
+                        + String.join("|", LEGAL_TIME_UNIT));
+            }
         }
     }
 
@@ -84,8 +86,36 @@ public class DateTrunc extends ScalarFunction
     }
 
     @Override
-    public List<FunctionSignature> getSignatures() {
-        return SIGNATURES;
+    public FunctionSignature customSignature() {
+        if (getArgument(0).getDataType().isDateLikeType()) {
+            return FunctionSignature.ret(getArgument(0).getDataType())
+                    .args(getArgument(0).getDataType(), 
VarcharType.SYSTEM_DEFAULT);
+        } else if (getArgument(1).getDataType().isDateLikeType()) {
+            return FunctionSignature.ret(getArgument(1).getDataType())
+                    .args(VarcharType.SYSTEM_DEFAULT, 
getArgument(1).getDataType());
+        }
+        boolean firstArgIsStringLiteral =
+                getArgument(0).isConstant() && getArgument(0) instanceof 
VarcharLiteral;
+        boolean secondArgIsStringLiteral =
+                getArgument(1).isConstant() && getArgument(1) instanceof 
VarcharLiteral;
+        if (firstArgIsStringLiteral && !secondArgIsStringLiteral) {
+            return FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
+                    .args(VarcharType.SYSTEM_DEFAULT, 
DateTimeV2Type.SYSTEM_DEFAULT);
+        } else if (!firstArgIsStringLiteral && secondArgIsStringLiteral) {
+            return FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
+                    .args(DateTimeV2Type.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT);
+        } else if (firstArgIsStringLiteral && secondArgIsStringLiteral) {
+            boolean timeUnitIsFirst = 
LEGAL_TIME_UNIT.contains(((VarcharLiteral) getArgument(0))
+                    .getStringValue().toLowerCase());
+            return timeUnitIsFirst ? 
FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
+                    .args(VarcharType.SYSTEM_DEFAULT, 
DateTimeV2Type.SYSTEM_DEFAULT)
+                    : FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
+                            .args(DateTimeV2Type.SYSTEM_DEFAULT, 
VarcharType.SYSTEM_DEFAULT);
+        }
+        // if both of args are not constant, `checkLegalityBeforeTypeCoercion` 
will throw exception so just return
+        // a signature here.
+        return FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT)
+                .args(VarcharType.SYSTEM_DEFAULT, 
DateTimeV2Type.SYSTEM_DEFAULT);
     }
 
     @Override
@@ -100,11 +130,12 @@ public class DateTrunc extends ScalarFunction
 
     @Override
     public int getMonotonicFunctionChildIndex() {
-        return 0;
+        return getArgument(0).getDataType().isDateLikeType() ? 0 : 1;
     }
 
     @Override
     public Expression withConstantArgs(Expression literal) {
-        return new DateTrunc(literal, child(1));
+        return getArgument(0).getDataType().isDateLikeType()
+                ? new DateTrunc(literal, child(1)) : new DateTrunc(child(0), 
literal);
     }
 }
diff --git 
a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_trunc.out
 
b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_trunc.out
index a9492698e0d..642b21010ed 100644
Binary files 
a/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_trunc.out
 and 
b/regression-test/data/query_p0/sql_functions/datetime_functions/test_date_trunc.out
 differ
diff --git 
a/regression-test/suites/partition_p0/auto_partition/test_auto_range_partition.groovy
 
b/regression-test/suites/partition_p0/auto_partition/test_auto_range_partition.groovy
index 557560dfefc..f54f316b3fd 100644
--- 
a/regression-test/suites/partition_p0/auto_partition/test_auto_range_partition.groovy
+++ 
b/regression-test/suites/partition_p0/auto_partition/test_auto_range_partition.groovy
@@ -241,6 +241,6 @@ suite("test_auto_range_partition") {
     """
     test {
         sql "insert into awh_test_range_auto values (1,'20201212')"
-        exception "date_trunc function second param only support argument is"
+        exception "date_trunc function time unit param only support argument 
is"
     }
 }
diff --git 
a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_trunc.groovy
 
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_trunc.groovy
index 7ef142d410e..fff5204f623 100644
--- 
a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_trunc.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_date_trunc.groovy
@@ -59,6 +59,14 @@ suite("test_date_trunc") {
     qt_select_date_trunc_month """ SELECT k11, date_trunc(k11,'MONTH') FROM 
baseall order by k1,k2,k3;"""
     qt_select_date_trunc_quarter """ SELECT k11, date_trunc(k11,'quarter') 
FROM baseall order by k1,k2,k3;"""
     qt_select_date_trunc_year """ SELECT k11, date_trunc(k11,'YeaR') FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_second """ SELECT k11, date_trunc('SECoND', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_minute """ SELECT k11, date_trunc('MINutE', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_hour """ SELECT k11, date_trunc('Hour', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_day """ SELECT k11, date_trunc('DAY', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_week """ SELECT k11, date_trunc('Week', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_month """ SELECT k11, date_trunc('MONTH', k11) FROM 
baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_quarter """ SELECT k11, date_trunc('quarter', k11) 
FROM baseall order by k1,k2,k3;"""
+    qt_select_date_trunc_year """ SELECT k11, date_trunc('YeaR', k11) FROM 
baseall order by k1,k2,k3;"""
 
     qt_date_week """ SELECT k10, date_trunc(k10,'Week') FROM baseall order by 
k1,k2,k3;"""
     qt_date_year """ SELECT k10, date_trunc(k10,'YeaR') FROM baseall order by 
k1,k2,k3;"""


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to