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]