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

zclll 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 64ca5d74e2f [Enhancement](FE) Support for time types with scale on the 
FE side (#51270)
64ca5d74e2f is described below

commit 64ca5d74e2f344886b1a1548d0114d52c670bfc3
Author: zclllyybb <[email protected]>
AuthorDate: Wed Jun 25 18:52:44 2025 +0800

    [Enhancement](FE) Support for time types with scale on the FE side (#51270)
    
    Problem Summary:
    
    based on https://github.com/apache/doris/pull/45050.
    Support time type with scale
---
 be/src/vec/functions/array/function_array_index.h  |  11 +-
 .../org/apache/doris/catalog/PrimitiveType.java    |  13 +-
 .../functions/ComputeSignatureHelper.java          | 136 ++++++++++++++-------
 .../trees/expressions/literal/TimeV2Literal.java   |  39 ++++--
 .../org/apache/doris/nereids/types/DataType.java   |   2 +
 .../apache/doris/nereids/types/DateTimeV2Type.java |  23 +++-
 .../org/apache/doris/nereids/types/TimeV2Type.java |  60 ++++++++-
 .../nereids/types/coercion/ScaleTimeType.java      |  34 ++++++
 .../doris/nereids/util/TypeCoercionUtils.java      |  14 ++-
 .../functions/ComputeSignatureHelperTest.java      | 114 +++++++++++++++++
 regression-test/data/cast_p0/cast_to_time.out      | Bin 1790 -> 1986 bytes
 regression-test/suites/cast_p0/cast_to_time.groovy |  15 ++-
 12 files changed, 384 insertions(+), 77 deletions(-)

diff --git a/be/src/vec/functions/array/function_array_index.h 
b/be/src/vec/functions/array/function_array_index.h
index 301cfcf688a..6f7229846cb 100644
--- a/be/src/vec/functions/array/function_array_index.h
+++ b/be/src/vec/functions/array/function_array_index.h
@@ -28,6 +28,7 @@
 #include "olap/column_predicate.h"
 #include "olap/rowset/segment_v2/inverted_index_query_type.h"
 #include "olap/rowset/segment_v2/inverted_index_reader.h"
+#include "runtime/define_primitive_type.h"
 #include "runtime/primitive_type.h"
 #include "vec/columns/column.h"
 #include "vec/columns/column_array.h"
@@ -481,9 +482,11 @@ private:
                 break;
             }
         } else if ((is_date_or_datetime(right_type->get_primitive_type()) ||
-                    
is_date_v2_or_datetime_v2(right_type->get_primitive_type())) &&
+                    
is_date_v2_or_datetime_v2(right_type->get_primitive_type()) ||
+                    right_type->get_primitive_type() == TYPE_TIMEV2) &&
                    
(is_date_or_datetime(left_element_type->get_primitive_type()) ||
-                    
is_date_v2_or_datetime_v2(left_element_type->get_primitive_type()))) {
+                    
is_date_v2_or_datetime_v2(left_element_type->get_primitive_type()) ||
+                    left_element_type->get_primitive_type() == TYPE_TIMEV2)) {
             if (left_element_type->get_primitive_type() == TYPE_DATE) {
                 return_column = _execute_number_expanded<ColumnDate>(
                         offsets, nested_null_map, *nested_column, 
*right_column,
@@ -500,6 +503,10 @@ private:
                 return_column = _execute_number_expanded<ColumnDateTimeV2>(
                         offsets, nested_null_map, *nested_column, 
*right_column,
                         right_nested_null_map, array_null_map);
+            } else if (left_element_type->get_primitive_type() == TYPE_TIMEV2) 
{
+                return_column = _execute_number_expanded<ColumnTimeV2>(
+                        offsets, nested_null_map, *nested_column, 
*right_column,
+                        right_nested_null_map, array_null_map);
             }
         }
 
diff --git 
a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
index 0775b524204..aa70fa2ae01 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/PrimitiveType.java
@@ -383,6 +383,7 @@ public enum PrimitiveType {
         builder.put(DATEV2, DATETIME);
         builder.put(DATEV2, DATEV2);
         builder.put(DATEV2, DATETIMEV2);
+        builder.put(DATEV2, TIMEV2);
         builder.put(DATEV2, DECIMALV2);
         builder.put(DATEV2, DECIMAL32);
         builder.put(DATEV2, DECIMAL64);
@@ -403,6 +404,7 @@ public enum PrimitiveType {
         builder.put(DATETIMEV2, DATETIME);
         builder.put(DATETIMEV2, DATEV2);
         builder.put(DATETIMEV2, DATETIMEV2);
+        builder.put(DATETIMEV2, TIMEV2);
         builder.put(DATETIMEV2, DECIMALV2);
         builder.put(DATETIMEV2, DECIMAL32);
         builder.put(DATETIMEV2, DECIMAL64);
@@ -614,17 +616,6 @@ public enum PrimitiveType {
         builder.put(AGG_STATE, AGG_STATE);
         builder.put(AGG_STATE, VARCHAR);
 
-        // TIME
-        builder.put(TIME, TIME);
-        builder.put(TIME, TIMEV2);
-        builder.put(TIME, DOUBLE);
-        builder.put(TIME, VARCHAR);
-        builder.put(TIME, STRING);
-        builder.put(TIME, DATE);
-        builder.put(TIME, DATETIME);
-        builder.put(TIME, DATEV2);
-        builder.put(TIME, DATETIMEV2);
-
         // TIMEV2
         builder.put(TIMEV2, TIME);
         builder.put(TIMEV2, TIMEV2);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
index 79ed4091cce..64836f656e1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelper.java
@@ -31,9 +31,12 @@ import org.apache.doris.nereids.types.DecimalV3Type;
 import org.apache.doris.nereids.types.MapType;
 import org.apache.doris.nereids.types.NullType;
 import org.apache.doris.nereids.types.StructType;
+import org.apache.doris.nereids.types.TimeV2Type;
 import org.apache.doris.nereids.types.coercion.AnyDataType;
+import org.apache.doris.nereids.types.coercion.ComplexDataType;
 import org.apache.doris.nereids.types.coercion.FollowToAnyDataType;
 import org.apache.doris.nereids.types.coercion.FollowToArgumentType;
+import org.apache.doris.nereids.types.coercion.ScaleTimeType;
 import org.apache.doris.nereids.util.ResponsibilityChain;
 import org.apache.doris.nereids.util.TypeCoercionUtils;
 
@@ -336,14 +339,16 @@ public class ComputeSignatureHelper {
         }
 
         boolean hasDateTimeV2Type = false;
+        boolean hasTimeV2Type = false;
         boolean hasDecimalV3Type = false;
         for (DataType argumentsType : signature.argumentsTypes) {
             hasDateTimeV2Type |= 
TypeCoercionUtils.hasDateTimeV2Type(argumentsType);
+            hasTimeV2Type |= TypeCoercionUtils.hasTimeV2Type(argumentsType);
             hasDecimalV3Type |= 
TypeCoercionUtils.hasDecimalV3Type(argumentsType);
         }
 
-        if (hasDateTimeV2Type) {
-            signature = defaultDateTimeV2PrecisionPromotion(signature, 
arguments);
+        if (hasDateTimeV2Type || hasTimeV2Type) {
+            signature = defaultTimePrecisionPromotion(signature, arguments);
         }
         if (hasDecimalV3Type) {
             // do decimal v3 precision
@@ -376,11 +381,13 @@ public class ComputeSignatureHelper {
                 ArrayType.of(arrayType.getItemType(), arrayType.containsNull() 
|| containsNull));
     }
 
-    private static FunctionSignature defaultDateTimeV2PrecisionPromotion(
-            FunctionSignature signature, List<Expression> arguments) {
-        DateTimeV2Type finalType = null;
+    // for time type with precision(now are DateTimeV2Type and TimeV2Type),
+    // we will promote the precision of the type to the maximum precision of 
all arguments
+    private static FunctionSignature 
defaultTimePrecisionPromotion(FunctionSignature signature,
+            List<Expression> arguments) {
+        int finalTypeScale = -1;
         for (int i = 0; i < arguments.size(); i++) {
-            DataType targetType;
+            DataType targetType; // type of signature_args[i]
             if (i >= signature.argumentsTypes.size()) {
                 Preconditions.checkState(signature.getVarArgType().isPresent(),
                         "argument size larger than signature");
@@ -388,47 +395,69 @@ public class ComputeSignatureHelper {
             } else {
                 targetType = signature.getArgType(i);
             }
-            List<DataType> argTypes = extractArgumentType(DateTimeV2Type.class,
-                    targetType, arguments.get(i).getDataType());
-            if (argTypes.isEmpty()) {
+            // if input type X's slot(targetType) is datetimev2/timev2 or 
complex of them, get all nested type of X.
+            List<DataType> nestedInputTypes = ImmutableList.<DataType>builder()
+                    
.addAll(extractArgumentTypeBySignature(DateTimeV2Type.class, targetType,
+                            arguments.get(i).getDataType()))
+                    .addAll(extractArgumentTypeBySignature(TimeV2Type.class, 
targetType,
+                            arguments.get(i).getDataType()))
+                    .build();
+            // there's DateTimeV2 and TimeV2 at same time, so we need get 
exact target type when we promote any slot.
+            List<DataType> nestedTargetTypes = 
ImmutableList.<DataType>builder()
+                    .addAll(extractSignatureTypes(DateTimeV2Type.class, 
targetType, arguments.get(i).getDataType()))
+                    .addAll(extractSignatureTypes(TimeV2Type.class, 
targetType, arguments.get(i).getDataType()))
+                    .build();
+            if (nestedInputTypes.isEmpty()) {
+                // if no DateTimeV2Type or TimeV2Type in the argument[i], no 
precision promotion
                 continue;
             }
 
-            for (DataType argType : argTypes) {
-                Expression arg = arguments.get(i);
-                DateTimeV2Type dateTimeV2Type;
+            // for Map or Struct, we have more than one nested type.
+            // targetType may be ScaleTimeType or comlex type(Array, Struct) 
with ScaleTimeType nested.
+            Expression arg = arguments.get(i);
+            for (int j = 0; j < nestedInputTypes.size(); j++) {
+                // inputType could be any legal input type
+                DataType inputType = nestedInputTypes.get(j);
+                // corresponding target slot type for inputType
+                DataType nestedTargetType = nestedTargetTypes.get(j);
+                int targetScale = 0;
+
+                // for string input, try to get the most suitable scale
                 if (arg instanceof StringLikeLiteral) {
-                    StringLikeLiteral str = (StringLikeLiteral) 
arguments.get(i);
-                    dateTimeV2Type = 
DateTimeV2Type.forTypeFromString(str.getStringValue());
+                    ScaleTimeType timelikeType = (ScaleTimeType) 
nestedTargetType;
+                    targetScale = 
timelikeType.forTypeFromString((StringLikeLiteral) arg).getScale();
                 } else {
-                    dateTimeV2Type = DateTimeV2Type.forType(argType);
-                }
-                if (finalType == null) {
-                    finalType = dateTimeV2Type;
-                } else {
-                    finalType = 
DateTimeV2Type.getWiderDatetimeV2Type(finalType, dateTimeV2Type);
+                    // for all other input types, get the target scale when 
cast it to targetType
+                    ScaleTimeType targetScaleType = (ScaleTimeType) 
nestedTargetType;
+                    ScaleTimeType promotedType = 
targetScaleType.scaleTypeForType(inputType);
+                    targetScale = promotedType.getScale();
                 }
+
+                finalTypeScale = Math.max(finalTypeScale, targetScale); // 
init value -1 always promotes
+                System.out.println("finalTypeScale: " + finalTypeScale
+                        + ", targetScale: " + targetScale + ", inputType: " + 
inputType
+                        + ", nestedTargetType: " + nestedTargetType);
             }
         }
-        if (finalType == null) {
+
+        // if no DateTimeV2Type or TimeV2Type in the arguments, no precision 
promotion
+        if (finalTypeScale < 0) {
             return signature;
         }
-        DateTimeV2Type argType = finalType;
-
+        // promote the precision of return type
         ImmutableList.Builder<DataType> newArgTypesBuilder = 
ImmutableList.builderWithExpectedSize(signature.arity);
-        for (DataType at : signature.argumentsTypes) {
-            
newArgTypesBuilder.add(TypeCoercionUtils.replaceDateTimeV2WithTarget(at, 
argType));
+        for (DataType signatureArgType : signature.argumentsTypes) {
+            
newArgTypesBuilder.add(TypeCoercionUtils.replaceTimesWithTargetPrecision(signatureArgType,
 finalTypeScale));
+            System.out.println("signatureArgType: " + signatureArgType
+                    + ", newArgType: " + 
TypeCoercionUtils.replaceTimesWithTargetPrecision(signatureArgType,
+                    finalTypeScale));
         }
         List<DataType> newArgTypes = newArgTypesBuilder.build();
         signature = signature.withArgumentTypes(signature.hasVarArgs, 
newArgTypes);
-        signature = signature.withArgumentTypes(signature.hasVarArgs, 
newArgTypes);
-        if (signature.returnType instanceof DateTimeV2Type) {
-            signature = signature.withReturnType(argType);
-        } else if (signature.returnType instanceof ArrayType) {
-            DataType itemType = ((ArrayType) 
signature.returnType).getItemType();
-            if (itemType instanceof DateTimeV2Type) {
-                signature = signature.withReturnType(ArrayType.of(argType));
-            }
+        if (signature.returnType instanceof DateTimeV2Type || 
signature.returnType instanceof TimeV2Type
+                || signature.returnType instanceof ComplexDataType) {
+            signature = signature.withReturnType(
+                    
TypeCoercionUtils.replaceTimesWithTargetPrecision(signature.returnType, 
finalTypeScale));
         }
         return signature;
     }
@@ -445,8 +474,8 @@ public class ComputeSignatureHelper {
             } else {
                 targetType = signature.getArgType(i);
             }
-            List<DataType> argTypes = extractArgumentType(DecimalV3Type.class,
-                    targetType, arguments.get(i).getDataType());
+            List<DataType> argTypes = 
extractArgumentTypeBySignature(DecimalV3Type.class, targetType,
+                    arguments.get(i).getDataType());
             if (argTypes.isEmpty()) {
                 continue;
             }
@@ -482,30 +511,43 @@ public class ComputeSignatureHelper {
         return signature;
     }
 
-    private static List<DataType> extractArgumentType(Class<? extends 
DataType> targetType,
+    private static List<DataType> extractArgumentTypeBySignature(Class<? 
extends DataType> targetType,
             DataType signatureType, DataType argumentType) {
+        return extractBySignature(targetType, signatureType, argumentType, 
(sig, arg) -> arg);
+    }
+
+    private static List<DataType> extractSignatureTypes(Class<? extends 
DataType> targetType, DataType signatureType,
+            DataType argumentType) {
+        return extractBySignature(targetType, signatureType, argumentType, 
(sig, arg) -> sig);
+    }
+
+    // if signatureType is a super type of targetType, then extract 
corresponding argumentType slot
+    private static List<DataType> extractBySignature(Class<? extends DataType> 
targetType,
+            DataType signatureType, DataType argumentType, 
BiFunction<DataType, DataType, DataType> pick) {
         if (targetType.isAssignableFrom(signatureType.getClass())) {
-            return Lists.newArrayList(argumentType);
+            return Lists.newArrayList(pick.apply(signatureType, argumentType));
         } else if (signatureType instanceof ArrayType) {
             if (argumentType instanceof NullType) {
-                return extractArgumentType(targetType, ((ArrayType) 
signatureType).getItemType(), argumentType);
+                return extractBySignature(targetType, ((ArrayType) 
signatureType).getItemType(),
+                        argumentType, pick);
             } else if (argumentType instanceof ArrayType) {
-                return extractArgumentType(targetType,
-                        ((ArrayType) signatureType).getItemType(), 
((ArrayType) argumentType).getItemType());
+                return extractBySignature(targetType, ((ArrayType) 
signatureType).getItemType(),
+                        ((ArrayType) argumentType).getItemType(), pick);
             } else {
                 return Lists.newArrayList();
             }
         } else if (signatureType instanceof MapType) {
             if (argumentType instanceof NullType) {
-                List<DataType> ret = extractArgumentType(targetType,
-                        ((MapType) signatureType).getKeyType(), argumentType);
-                ret.addAll(extractArgumentType(targetType, ((MapType) 
signatureType).getValueType(), argumentType));
+                List<DataType> ret = extractBySignature(targetType, ((MapType) 
signatureType).getKeyType(),
+                        argumentType, pick);
+                ret.addAll(extractBySignature(targetType, ((MapType) 
signatureType).getValueType(),
+                        argumentType, pick));
                 return ret;
             } else if (argumentType instanceof MapType) {
-                List<DataType> ret = extractArgumentType(targetType,
-                        ((MapType) signatureType).getKeyType(), ((MapType) 
argumentType).getKeyType());
-                ret.addAll(extractArgumentType(targetType,
-                        ((MapType) signatureType).getValueType(), ((MapType) 
argumentType).getValueType()));
+                List<DataType> ret = extractBySignature(targetType, ((MapType) 
signatureType).getKeyType(),
+                        ((MapType) argumentType).getKeyType(), pick);
+                ret.addAll(extractBySignature(targetType, ((MapType) 
signatureType).getValueType(),
+                        ((MapType) argumentType).getValueType(), pick));
                 return ret;
             } else {
                 return Lists.newArrayList();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java
index 33f6a1e737a..6420e22779e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2Literal.java
@@ -47,6 +47,11 @@ public class TimeV2Literal extends Literal {
         init(s);
     }
 
+    public TimeV2Literal(String s) {
+        super(TimeV2Type.forTypeFromString(s));
+        init(s);
+    }
+
     /**
      * C'tor time literal.
      */
@@ -84,16 +89,9 @@ public class TimeV2Literal extends Literal {
         }
     }
 
-    protected String normalize(String s) {
+    protected static String normalize(String s) {
         // remove suffix/prefix ' '
         s = s.trim();
-        if (s.charAt(0) == '-') {
-            s = s.substring(1);
-            negative = true;
-        } else if (s.charAt(0) == '+') {
-            s = s.substring(1);
-            negative = false;
-        }
         // just a number
         if (!s.contains(":")) {
             String tail = "";
@@ -111,6 +109,7 @@ public class TimeV2Literal extends Literal {
             } else if (len == 4) {
                 s = "00:" + s.substring(0, 2) + ":" + s.substring(2);
             } else {
+                // minute and second must be 2 digits. others put in front as 
hour
                 s = s.substring(0, len - 4) + ":" + s.substring(len - 4, len - 
2) + ":" + s.substring(len - 2);
             }
             return s + tail;
@@ -125,6 +124,12 @@ public class TimeV2Literal extends Literal {
     // should like be/src/vec/runtime/time_value.h timev2_to_double_from_str
     protected void init(String s) throws AnalysisException {
         s = normalize(s);
+        if (s.charAt(0) == '-') {
+            negative = true;
+            s = s.substring(1);
+        } else if (s.charAt(0) == '+') {
+            s = s.substring(1);
+        }
         // start parse string
         String[] parts = s.split(":");
         if (parts.length != 3) {
@@ -180,6 +185,24 @@ public class TimeV2Literal extends Literal {
                 || microsecond < 0;
     }
 
+    /**
+     * determine scale by time string. didn't check if the string is valid.
+     */
+    public static int determineScale(String s) {
+        // find point
+        s = normalize(s);
+        int pointIndex = s.indexOf('.');
+        if (pointIndex < 0) {
+            return 0; // no point, scale is 0
+        }
+        String microPart = s.substring(pointIndex + 1);
+        int len = microPart.length();
+        while (len > 0 && microPart.charAt(len - 1) == '0') {
+            len--; // remove trailing zeros
+        }
+        return Math.min(len, 6); // max scale is 6
+    }
+
     public int getHour() {
         return hour;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
index b968ea3ec88..30aa80d54e8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
@@ -85,6 +85,7 @@ public abstract class DataType {
             .add(DateType.class, () -> ImmutableList.of(
                     DateTimeType.INSTANCE, DateV2Type.INSTANCE, 
StringType.INSTANCE))
             .add(DateV2Type.class, () -> 
ImmutableList.of(DateTimeV2Type.SYSTEM_DEFAULT, StringType.INSTANCE))
+            .add(TimeV2Type.class, () -> ImmutableList.of(DateTimeV2Type.MAX, 
StringType.INSTANCE))
             .build();
 
     /**
@@ -250,6 +251,7 @@ public abstract class DataType {
                 dataType = DateV2Type.INSTANCE;
                 break;
             case "time":
+            case "timev2":
                 switch (types.size()) {
                     case 1:
                         dataType = TimeV2Type.INSTANCE;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
index f1c7462e4dc..b216b3af312 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DateTimeV2Type.java
@@ -21,9 +21,11 @@ import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import 
org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker;
 import org.apache.doris.nereids.types.coercion.DateLikeType;
 import org.apache.doris.nereids.types.coercion.IntegralType;
+import org.apache.doris.nereids.types.coercion.ScaleTimeType;
 
 import com.google.common.base.Preconditions;
 
@@ -35,7 +37,7 @@ import java.util.Objects;
 /**
  * Datetime type in Nereids.
  */
-public class DateTimeV2Type extends DateLikeType {
+public class DateTimeV2Type extends DateLikeType implements ScaleTimeType {
     public static final int MAX_SCALE = 6;
     public static final DateTimeV2Type SYSTEM_DEFAULT = new DateTimeV2Type(0);
     public static final DateTimeV2Type MAX = new DateTimeV2Type(MAX_SCALE);
@@ -80,9 +82,27 @@ public class DateTimeV2Type extends DateLikeType {
                 || dataType instanceof DateTimeType || dataType instanceof 
DateType || dataType instanceof DateV2Type) {
             return SYSTEM_DEFAULT;
         }
+        if (dataType instanceof DecimalV3Type) {
+            return DateTimeV2Type.of(Math.min(((DecimalV3Type) 
dataType).getScale(), 6));
+        }
+        if (dataType instanceof DecimalV2Type) {
+            return DateTimeV2Type.of(Math.min(((DecimalV2Type) 
dataType).getScale(), 6));
+        }
+        if (dataType instanceof TimeV2Type) {
+            return DateTimeV2Type.of(((TimeV2Type) dataType).getScale());
+        }
         return MAX;
     }
 
+    public ScaleTimeType scaleTypeForType(DataType dataType) {
+        return forType(dataType);
+    }
+
+    @Override
+    public ScaleTimeType forTypeFromString(StringLikeLiteral str) {
+        return forTypeFromString(str.getStringValue());
+    }
+
     /**
      * return proper type of datetimev2 for String
      * maybe we need to check for validity?
@@ -137,6 +157,7 @@ public class DateTimeV2Type extends DateLikeType {
         return WIDTH;
     }
 
+    @Override
     public int getScale() {
         return scale;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
index 16aace8bafa..aed3d3195e8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeV2Type.java
@@ -20,16 +20,21 @@ package org.apache.doris.nereids.types;
 import org.apache.doris.catalog.ScalarType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal;
+import org.apache.doris.nereids.types.coercion.IntegralType;
 import org.apache.doris.nereids.types.coercion.PrimitiveType;
 import org.apache.doris.nereids.types.coercion.RangeScalable;
+import org.apache.doris.nereids.types.coercion.ScaleTimeType;
 
 /**
  * Time v2 type in Nereids.
  */
-public class TimeV2Type extends PrimitiveType implements RangeScalable {
+public class TimeV2Type extends PrimitiveType implements RangeScalable, 
ScaleTimeType {
 
     public static final int MAX_SCALE = 6;
-    public static final TimeV2Type INSTANCE = new TimeV2Type();
+    public static final TimeV2Type INSTANCE = new TimeV2Type(0);
+    public static final TimeV2Type MAX = new TimeV2Type(MAX_SCALE);
 
     private static final int WIDTH = 8;
     private final int scale;
@@ -65,12 +70,63 @@ public class TimeV2Type extends PrimitiveType implements 
RangeScalable {
         return false;
     }
 
+    @Override
+    public ScaleTimeType forTypeFromString(StringLikeLiteral str) {
+        return forTypeFromString(str.getStringValue());
+    }
+
+    /**
+     * return proper type of timev2 for string
+     * if the string is not a valid timev2, return MAX
+     */
+    public static TimeV2Type forTypeFromString(String s) {
+        try {
+            new TimeV2Literal(INSTANCE, s);
+        } catch (AnalysisException e) {
+            return MAX;
+        }
+        return TimeV2Type.of(TimeV2Literal.determineScale(s));
+    }
+
+    /**
+     * return proper type of timev2 for other type
+     */
+    public static TimeV2Type forType(DataType dataType) {
+        if (dataType instanceof TimeV2Type) {
+            return (TimeV2Type) dataType;
+        }
+        if (dataType instanceof IntegralType || dataType instanceof 
BooleanType || dataType instanceof NullType
+                || dataType instanceof DateTimeType) {
+            return INSTANCE;
+        }
+        if (dataType instanceof DecimalV3Type) {
+            return TimeV2Type.of(Math.min(((DecimalV3Type) 
dataType).getScale(), 6));
+        }
+        if (dataType instanceof DecimalV2Type) {
+            return TimeV2Type.of(Math.min(((DecimalV2Type) 
dataType).getScale(), 6));
+        }
+        if (dataType instanceof DateTimeV2Type) {
+            return TimeV2Type.of(((DateTimeV2Type) dataType).getScale());
+        }
+        return MAX;
+    }
+
+    public ScaleTimeType scaleTypeForType(DataType dataType) {
+        return forType(dataType);
+    }
+
     @Override
     public int width() {
         return WIDTH;
     }
 
+    @Override
     public int getScale() {
         return scale;
     }
+
+    @Override
+    public String toSql() {
+        return super.toSql() + "(" + scale + ")";
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/ScaleTimeType.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/ScaleTimeType.java
new file mode 100644
index 00000000000..5fafef811f4
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/coercion/ScaleTimeType.java
@@ -0,0 +1,34 @@
+// 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.
+
+package org.apache.doris.nereids.types.coercion;
+
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+import org.apache.doris.nereids.types.DataType;
+
+/**
+ * ScaleTimeType is a marker interface for types that have a scale, such as 
TimeV2Type and DateTimeV2Type.
+ */
+public interface ScaleTimeType {
+
+    public int getScale();
+
+    public ScaleTimeType forTypeFromString(StringLikeLiteral str);
+
+    public ScaleTimeType scaleTypeForType(DataType dataType);
+
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
index 0502a29760e..a4cd5405b42 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
@@ -322,6 +322,10 @@ public class TypeCoercionUtils {
         return hasSpecifiedType(dataType, DateTimeV2Type.class);
     }
 
+    public static boolean hasTimeV2Type(DataType dataType) {
+        return hasSpecifiedType(dataType, TimeV2Type.class);
+    }
+
     private static boolean hasSpecifiedType(DataType dataType, Class<? extends 
DataType> specifiedType) {
         if (dataType instanceof ArrayType) {
             return hasSpecifiedType(((ArrayType) dataType).getItemType(), 
specifiedType);
@@ -354,14 +358,16 @@ public class TypeCoercionUtils {
         return replaceSpecifiedType(dataType, DecimalV3Type.class, 
DecimalV3Type.WILDCARD);
     }
 
-    public static DataType replaceDateTimeV2WithTarget(DataType dataType, 
DateTimeV2Type target) {
-        return replaceSpecifiedType(dataType, DateTimeV2Type.class, target);
-    }
-
     public static DataType replaceDateTimeV2WithMax(DataType dataType) {
         return replaceSpecifiedType(dataType, DateTimeV2Type.class, 
DateTimeV2Type.MAX);
     }
 
+    public static DataType replaceTimesWithTargetPrecision(DataType dataType, 
int targetScale) {
+        return replaceSpecifiedType(
+                replaceSpecifiedType(dataType, DateTimeV2Type.class, 
DateTimeV2Type.of(targetScale)), TimeV2Type.class,
+                TimeV2Type.of(targetScale));
+    }
+
     /**
      * replace specifiedType in dataType to newType.
      */
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelperTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelperTest.java
index ff518fb9d1f..72405959d5a 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelperTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ComputeSignatureHelperTest.java
@@ -27,6 +27,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.MapLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.TimeV2Literal;
 import org.apache.doris.nereids.types.ArrayType;
 import org.apache.doris.nereids.types.BigIntType;
 import org.apache.doris.nereids.types.BooleanType;
@@ -38,6 +39,7 @@ import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.MapType;
 import org.apache.doris.nereids.types.NullType;
 import org.apache.doris.nereids.types.SmallIntType;
+import org.apache.doris.nereids.types.TimeV2Type;
 import org.apache.doris.nereids.types.coercion.AnyDataType;
 import org.apache.doris.nereids.types.coercion.FollowToAnyDataType;
 import org.apache.doris.nereids.types.coercion.FollowToArgumentType;
@@ -404,6 +406,118 @@ public class ComputeSignatureHelperTest {
                 signature.getArgType(2));
     }
 
+    @Test
+    void testTimeV2PrecisionPromotion() {
+        FunctionSignature signature = 
FunctionSignature.ret(BooleanType.INSTANCE).args(TimeV2Type.INSTANCE,
+                        TimeV2Type.INSTANCE, TimeV2Type.INSTANCE);
+        List<Expression> arguments = Lists.newArrayList(new 
TimeV2Literal("12:34:56.12"),
+                        new TimeV2Literal("12:34:56.123"), new 
TimeV2Literal("12:34:56.1"));
+        signature = ComputeSignatureHelper.computePrecision(new 
FakeComputeSignature(), signature, arguments);
+
+        // All arguments should be promoted to the highest precision (3)
+        Assertions.assertEquals(TimeV2Type.of(3), signature.getArgType(0));
+        Assertions.assertEquals(TimeV2Type.of(3), signature.getArgType(1));
+        Assertions.assertEquals(TimeV2Type.of(3), signature.getArgType(2));
+    }
+
+    @Test
+    void testMixedDateTimeV2AndTimeV2PrecisionPromotion() {
+        FunctionSignature signature = 
FunctionSignature.ret(DateTimeV2Type.SYSTEM_DEFAULT).args(
+                        DateTimeV2Type.SYSTEM_DEFAULT, TimeV2Type.INSTANCE, 
DateTimeV2Type.SYSTEM_DEFAULT);
+        List<Expression> arguments = Lists.newArrayList(new 
DateTimeV2Literal("2020-02-02 00:00:00.12"),
+                        new TimeV2Literal("12:34:56.123"), new 
DateTimeV2Literal("2020-02-02 00:00:00.1"));
+        signature = ComputeSignatureHelper.computePrecision(new 
FakeComputeSignature(), signature, arguments);
+
+        // All arguments should be promoted to the highest precision (3)
+        Assertions.assertEquals(DateTimeV2Type.of(3), signature.getArgType(0));
+        Assertions.assertEquals(TimeV2Type.of(3), signature.getArgType(1));
+        Assertions.assertEquals(DateTimeV2Type.of(3), signature.getArgType(2));
+        // Return type should also be promoted to precision 3
+        Assertions.assertEquals(DateTimeV2Type.of(3), signature.returnType);
+    }
+
+    @Test
+    void testNestedTimeV2PrecisionPromotion() {
+        FunctionSignature signature = 
FunctionSignature.ret(ArrayType.of(TimeV2Type.INSTANCE)).args(
+                        ArrayType.of(TimeV2Type.INSTANCE),
+                        MapType.of(IntegerType.INSTANCE, TimeV2Type.INSTANCE), 
TimeV2Type.INSTANCE);
+        List<Expression> arguments = Lists.newArrayList(
+                        new ArrayLiteral(Lists.newArrayList(new 
TimeV2Literal("12:34:56.12"))),
+                        new MapLiteral(Lists.newArrayList(new 
IntegerLiteral(1)),
+                                        Lists.newArrayList(new 
TimeV2Literal("12:34:56.1234"))),
+                        new TimeV2Literal("12:34:56.123"));
+        signature = ComputeSignatureHelper.computePrecision(new 
FakeComputeSignature(), signature, arguments);
+
+        // Check array argument (precision should be 4 from the map value)
+        Assertions.assertTrue(signature.getArgType(0) instanceof ArrayType);
+        Assertions.assertEquals(TimeV2Type.of(4), ((ArrayType) 
signature.getArgType(0)).getItemType());
+
+        // Check map argument
+        Assertions.assertTrue(signature.getArgType(1) instanceof MapType);
+        Assertions.assertEquals(IntegerType.INSTANCE, ((MapType) 
signature.getArgType(1)).getKeyType());
+        Assertions.assertEquals(TimeV2Type.of(4), ((MapType) 
signature.getArgType(1)).getValueType());
+
+        // Check scalar argument
+        Assertions.assertEquals(TimeV2Type.of(4), signature.getArgType(2));
+
+        // Check return type
+        Assertions.assertTrue(signature.returnType instanceof ArrayType);
+        Assertions.assertEquals(TimeV2Type.of(4), ((ArrayType) 
signature.returnType).getItemType());
+    }
+
+    @Test
+    void testComplexNestedMixedTimePrecisionPromotion() {
+        // Create a complex nested structure with both DateTimeV2 and TimeV2 
types
+        FunctionSignature signature = FunctionSignature
+                        .ret(MapType.of(DateTimeV2Type.SYSTEM_DEFAULT, 
ArrayType.of(TimeV2Type.INSTANCE)))
+                        .args(MapType.of(DateTimeV2Type.SYSTEM_DEFAULT, 
ArrayType.of(TimeV2Type.INSTANCE)),
+                                        
ArrayType.of(MapType.of(TimeV2Type.INSTANCE,
+                                                        
DateTimeV2Type.SYSTEM_DEFAULT)),
+                                        DateTimeV2Type.SYSTEM_DEFAULT);
+
+        // Create complex arguments with different precisions
+        List<Expression> arguments = Lists.newArrayList(
+                        // Map(DateTimeV2(2) -> Array(TimeV2(1)))
+                        new MapLiteral(Lists.newArrayList(new 
DateTimeV2Literal("2020-02-02 00:00:00.12")),
+                                        Lists.newArrayList(new ArrayLiteral(
+                                                        Lists.newArrayList(new 
TimeV2Literal("12:34:56.1"))))),
+                        // Array(Map(TimeV2(3) -> DateTimeV2(0)))
+                        new ArrayLiteral(Lists.newArrayList(new MapLiteral(
+                                        Lists.newArrayList(new 
TimeV2Literal("12:34:56.123")),
+                                        Lists.newArrayList(new 
DateTimeV2Literal("2020-02-02 00:00:00"))))),
+                        // DateTimeV2(4)
+                        new DateTimeV2Literal("2020-02-02 00:00:00.1234"));
+
+        signature = ComputeSignatureHelper.computePrecision(new 
FakeComputeSignature(), signature, arguments);
+
+        // All time types should be promoted to precision 4
+
+        // Check first argument: Map(DateTimeV2 -> Array(TimeV2))
+        Assertions.assertTrue(signature.getArgType(0) instanceof MapType);
+        Assertions.assertEquals(DateTimeV2Type.of(4), ((MapType) 
signature.getArgType(0)).getKeyType());
+        Assertions.assertTrue(((MapType) 
signature.getArgType(0)).getValueType() instanceof ArrayType);
+        Assertions.assertEquals(TimeV2Type.of(4),
+                        ((ArrayType) ((MapType) 
signature.getArgType(0)).getValueType()).getItemType());
+
+        // Check second argument: Array(Map(TimeV2 -> DateTimeV2))
+        Assertions.assertTrue(signature.getArgType(1) instanceof ArrayType);
+        Assertions.assertTrue(((ArrayType) 
signature.getArgType(1)).getItemType() instanceof MapType);
+        Assertions.assertEquals(TimeV2Type.of(4),
+                        ((MapType) ((ArrayType) 
signature.getArgType(1)).getItemType()).getKeyType());
+        Assertions.assertEquals(DateTimeV2Type.of(4),
+                        ((MapType) ((ArrayType) 
signature.getArgType(1)).getItemType()).getValueType());
+
+        // Check third argument: DateTimeV2
+        Assertions.assertEquals(DateTimeV2Type.of(4), signature.getArgType(2));
+
+        // Check return type: Map(DateTimeV2 -> Array(TimeV2))
+        Assertions.assertTrue(signature.returnType instanceof MapType);
+        Assertions.assertEquals(DateTimeV2Type.of(4), ((MapType) 
signature.returnType).getKeyType());
+        Assertions.assertTrue(((MapType) signature.returnType).getValueType() 
instanceof ArrayType);
+        Assertions.assertEquals(TimeV2Type.of(4),
+                        ((ArrayType) ((MapType) 
signature.returnType).getValueType()).getItemType());
+    }
+
     private static class FakeComputeSignature implements ComputeSignature {
         @Override
         public List<Expression> children() {
diff --git a/regression-test/data/cast_p0/cast_to_time.out 
b/regression-test/data/cast_p0/cast_to_time.out
index 67fb5ae4d28..4ba5b9c4d41 100644
Binary files a/regression-test/data/cast_p0/cast_to_time.out and 
b/regression-test/data/cast_p0/cast_to_time.out differ
diff --git a/regression-test/suites/cast_p0/cast_to_time.groovy 
b/regression-test/suites/cast_p0/cast_to_time.groovy
index fe06dbf1f35..54ffebcb176 100644
--- a/regression-test/suites/cast_p0/cast_to_time.groovy
+++ b/regression-test/suites/cast_p0/cast_to_time.groovy
@@ -17,7 +17,7 @@
 
 suite("cast_to_time") {
     sql "set debug_skip_fold_constant = true"
-
+//TODO: after we finished cast refactor, we can parse microseconds
 qt_sql """ select cast("1" as time(6)) """
 qt_sql """ select cast("123" as time(6)) """
 qt_sql """ select cast("2005959.12" as time(6)) """
@@ -119,4 +119,15 @@ qt_sql """ select cast(cast(67 as double) as time(3)) """
 // qt_sql """ select cast(cast(67 as decimal(27, 9)) as time(3)) """
 
 // qt_sql """ select cast(cast("2012-02-05 12:12:12.123456" as datetime(6)) as 
time(4)) """
-}
\ No newline at end of file
+
+qt_sql "select cast('11:12:13.123456' as time) = cast('11:12:13.12' as time)"
+
+    sql "set debug_skip_fold_constant = false"
+qt_sql """ select cast("100:10:10.123456" as time(3)) """
+qt_sql """ select cast("-100:10:10.123456" as time(3)) """
+qt_sql """ select cast("100:10:10.12345699999" as time(3)) """
+qt_sql """ select cast("100:10:10.12345699999" as time(6)) """
+qt_sql """ select cast("100:10:10.9999999999" as time(6)) """
+qt_sql """ select cast("x:10:10.123456" as time(3)) """
+qt_sql """ select cast("-900:10:10.123456" as time(3)) """
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to