github-actions[bot] commented on code in PR #66761:
URL: https://github.com/apache/doris/pull/66761#discussion_r3845618683


##########
fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java:
##########
@@ -1606,6 +1806,186 @@ public static Optional<DataType> 
findWiderTypeForTwoByVariable(DataType left, Da
         }
     }
 
+    /** Find a common type for two value-producing expressions using exact 
literal conversions. */
+    public static Optional<DataType> findWiderTypeForTwoByVariable(Expression 
left, Expression right,
+            boolean overflowToDouble, boolean stringIsHighPriority) {
+        return findWiderCommonTypeForExpressionsByVariable(
+                ImmutableList.of(left, right), overflowToDouble, 
stringIsHighPriority, false);
+    }
+
+    /** Find a common type using literal values when TIMESTAMP_NS and another 
date-like type mix. */
+    public static Optional<DataType> 
findWiderCommonTypeForExpressionsByVariable(
+            List<? extends Expression> expressions, boolean overflowToDouble,
+            boolean stringIsHighPriority, boolean comparison) {
+        Optional<List<DataType>> exactTypes = 
replaceExactTimeStampNsAndDateLikeTypes(expressions);
+        if (!exactTypes.isPresent()) {
+            return Optional.empty();
+        }
+        if (GlobalVariable.enableNewTypeCoercionBehavior) {
+            return findWiderCommonType(exactTypes.get(), overflowToDouble, 
stringIsHighPriority);
+        }
+        return comparison
+                ? findWiderCommonTypeForComparison(exactTypes.get(), true)
+                : findWiderCommonTypeForCaseWhen(exactTypes.get());
+    }
+
+    /** Find an indexed-ANY common type while retaining the expression for 
each nested type. */
+    public static Optional<DataType> findWiderCommonTypeForIndexedAny(
+            List<DataType> dataTypes, List<? extends Expression> expressions) {
+        Preconditions.checkArgument(dataTypes.size() == expressions.size());
+        Optional<List<DataType>> exactTypes = 
replaceExactTimeStampNsAndDateLikeTypes(dataTypes, expressions);
+        if (!exactTypes.isPresent()) {
+            return Optional.empty();
+        }
+        return GlobalVariable.enableNewTypeCoercionBehavior
+                ? findWiderCommonType(exactTypes.get(), false, true)
+                : findWiderCommonTypeForComparison(exactTypes.get());
+    }
+
+    /** Whether the two scalar types are the mixed temporal pair with no total 
common type. */
+    public static boolean isTimeStampNsAndDateTimeV2Pair(DataType left, 
DataType right) {
+        return left instanceof TimeStampNsType && right instanceof 
DateTimeV2Type
+                || left instanceof DateTimeV2Type && right instanceof 
TimeStampNsType;
+    }
+
+    private static boolean isTimeStampNsAndDateLikePair(DataType left, 
DataType right) {
+        return (left instanceof TimeStampNsType && right.isDateLikeType()
+                && !(right instanceof TimeStampNsType))
+                || (right instanceof TimeStampNsType && left.isDateLikeType()
+                && !(left instanceof TimeStampNsType));
+    }
+
+    private static Optional<DataType> 
findExactCommonTypeForTimeStampNsAndDateLike(
+            List<? extends Expression> expressions) {
+        Optional<List<DataType>> exactTypes = 
replaceExactTimeStampNsAndDateLikeTypes(expressions);
+        if (!exactTypes.isPresent()) {
+            return Optional.empty();
+        }
+        DataType first = exactTypes.get().get(0);
+        return exactTypes.get().stream().allMatch(first::equals) ? 
Optional.of(first) : Optional.empty();
+    }
+
+    private static Optional<List<DataType>> 
replaceExactTimeStampNsAndDateLikeTypes(
+            List<? extends Expression> expressions) {
+        return replaceExactTimeStampNsAndDateLikeTypes(
+                
expressions.stream().map(Expression::getDataType).collect(Collectors.toList()), 
expressions);
+    }
+
+    private static Optional<List<DataType>> 
replaceExactTimeStampNsAndDateLikeTypes(
+            List<DataType> dataTypes, List<? extends Expression> expressions) {
+        boolean containsTimeStampNs = dataTypes.stream()

Review Comment:
   [P2] Apply exact temporal coercion inside nested result types
   
   `replaceExactTimeStampNsAndDateLikeTypes()` only recognizes scalar outer 
types. For example, a `CASE` whose branches are `array(cast('2024-01-02 
03:04:05.123456789' as timestamp_ns))` and `array(cast('2024-01-02 
03:04:05.123456' as datetimev2(6)))` reaches this helper as two `ArrayType`s, 
so it skips the value-aware conversion; recursive widening later sees only the 
leaf types, finds no total TIMESTAMP_NS/DATETIMEV2 common domain, and rejects 
an exactly representable result that the scalar CASE path accepts. This is 
independent of indexed-ANY signature resolution. Please carry the corresponding 
nested expressions while widening ARRAY/MAP/STRUCT results, with 
CASE/IF/COALESCE coverage.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/DateTimeWithPrecision.java:
##########
@@ -46,7 +50,14 @@ public FunctionSignature computeSignature(FunctionSignature 
signature) {
             // searching in FunctionSet. So we adjust the return type by hand 
here.
             if (getArgument(0) instanceof IntegerLikeLiteral) {
                 IntegerLikeLiteral integerLikeLiteral = (IntegerLikeLiteral) 
getArgument(0);
-                signature = 
signature.withReturnType(DateTimeV2Type.of(integerLikeLiteral.getIntValue()));
+                int precision = integerLikeLiteral.getIntValue();
+                if (precision < 0 || precision > TimeStampNsType.SCALE) {
+                    throw new AnalysisException("Precision of " + 
getName().toUpperCase(Locale.ROOT)
+                            + " must be between 0 and "
+                            + TimeStampNsType.SCALE + ". Precision was set to: 
" + precision);
+                }
+                signature = signature.withReturnType(precision > 
DateTimeV2Type.MAX_SCALE

Review Comment:
   [P1] Make nonliteral `now` precision obey the nanosecond contract
   
   The TIMESTAMP_NS return type and 0..9 validation run only for literals; 
`now(integer_column)` is still accepted and falls through to DATETIMEV2(6). The 
generated `N.groovy` query exercises this form, and BE dispatches by that fixed 
result carrier: row values 7-9 enter the DATETIMEV2 path, whose `from_unixtime` 
clamps the scale to 6, while values above 9 also bypass the new analysis error. 
Thus the same precision value preserves nanoseconds as `now(7)` but silently 
loses them as `now(kint)`. Please either require a constant scale as 
`utc_timestamp` does, or give this form a stable nanosecond carrier plus 
runtime range validation, with slot-value tests for 6, 7, 9, and 10.



##########
fe/fe-core/src/main/java/org/apache/doris/statistics/util/StatisticsUtil.java:
##########
@@ -313,6 +314,7 @@ public static double convertToDouble(Type type, String 
columnValue) throws Analy
                     return literal.getDouble();
 
                 case DATETIMEV2:
+                case TIMESTAMP_NS:

Review Comment:
   [P2] Use one fractional encoding for TIMESTAMP_NS statistics
   
   This arm parses TIMESTAMP_NS min/max as `DateTimeLiteral`, whose 
`getDouble()` drops the fractional second, but predicate constants are 
`TimeStampNsLiteral`s and add `nanosecond / 1e9`. For a one-value column 
containing `2024-02-29 12:34:56.123456789`, ANALYZE stores max 
`20240229123456.0`, while the matching literal is represented as 
`20240229123456.125`; `estimateColumnEqualToConstant()` therefore treats the 
literal as above max and assigns zero selectivity. Please encode collected 
bounds with the same TIMESTAMP_NS literal, or conservatively invalidate 
fractional ranges, and cover equality and range estimates for distinct 
fractions within one second.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeAcquire.java:
##########
@@ -40,26 +44,50 @@ public class DateTimeAcquire {
      */
     @ExecFunction(name = "now")
     public static Expression now() {
-        return 
DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), 
0);
+        return DateTimeV2Literal.fromJavaDateType(currentDateTime(), 0);
     }
 
     @ExecFunction(name = "now")
     public static Expression now(IntegerLiteral precision) {
-        return 
DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()),
-                precision.getValue());
+        return currentTimestamp(precision.getValue());
     }
 
     /**
      * date acquire function: current_timestamp
      */
     @ExecFunction(name = "current_timestamp")
     public static Expression currentTimestamp() {
-        return 
DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), 
0);
+        return DateTimeV2Literal.fromJavaDateType(currentDateTime(), 0);
     }
 
     @ExecFunction(name = "current_timestamp")
     public static Expression currentTimestamp(IntegerLiteral precision) {
-        return 
DateTimeV2Literal.fromJavaDateType(LocalDateTime.now(DateUtils.getTimeZone()), 
precision.getValue());
+        return currentTimestamp(precision.getValue());
+    }
+
+    private static Expression currentTimestamp(int precision) {
+        return currentTimestamp(precision, DateUtils.getTimeZone());
+    }
+
+    private static Expression currentTimestamp(int precision, ZoneId zoneId) {
+        LocalDateTime dateTime = currentDateTime(zoneId);
+        if (precision <= DateTimeV2Type.MAX_SCALE) {
+            return DateTimeV2Literal.fromJavaDateType(dateTime, precision);
+        }
+        int factor = (int) Math.pow(10, DateUtils.NANOSECOND_SCALE - 
precision);
+        return TimeStampNsLiteral.fromJavaDateType(
+                dateTime.withNano(dateTime.getNano() / factor * factor));
+    }
+
+    private static LocalDateTime currentDateTime() {
+        return currentDateTime(DateUtils.getTimeZone());
+    }
+
+    private static LocalDateTime currentDateTime(ZoneId zoneId) {
+        ConnectContext connectContext = ConnectContext.get();
+        // Executable functions are also invoked by evaluators without a 
session context.
+        Instant currentTime = connectContext == null ? Instant.now() : 
connectContext.getStartTimeInstant();

Review Comment:
   [P2] Fold every current-time sibling from the statement snapshot
   
   The new `currentDateTime` helper makes 
`now`/`current_timestamp`/`utc_timestamp` use 
`ConnectContext.getStartTimeInstant()`, but the `current_time`/`curtime` 
evaluators still call `LocalDateTime.now()`; `current_date` and no-argument 
`unix_timestamp` have the same live-clock pattern. Their BE implementations 
read the RuntimeState query globals instead. After a planning delay, FE folding 
can therefore make `select now(6), current_time(6)` observe two instants, while 
disabling folding makes both statement-stable. Please route all FE 
current-date/time/epoch acquisition through this helper and add an 
injected-start-instant fold/runtime parity test for the sibling names.



##########
fe/fe-core/src/main/java/org/apache/doris/nereids/types/TimeStampNsType.java:
##########
@@ -0,0 +1,99 @@
+// 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;
+
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
+import org.apache.doris.nereids.types.coercion.CharacterType;
+import org.apache.doris.nereids.types.coercion.DateLikeType;
+import org.apache.doris.nereids.types.coercion.ScaleTimeType;
+
+import java.time.DateTimeException;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * Timestamp represented as signed nanoseconds since the Unix epoch.
+ *
+ * <p>TIMESTAMP_NS is date-like and has a fixed scale, but it is not a 
DATETIMEV2 precision variant:
+ * the two types have different physical representations and coercion rules. 
Keeping it directly under
+ * {@link DateLikeType} prevents generic DATETIMEV2 code from treating its 
fixed nanosecond scale as a
+ * DATETIMEV2 scale.</p>
+ */
+public final class TimeStampNsType extends DateLikeType implements 
ScaleTimeType {
+    public static final int SCALE = ScalarType.TIMESTAMP_NS_SCALE;
+    public static final TimeStampNsType INSTANCE = new TimeStampNsType();
+
+    private static final int WIDTH = 8;
+
+    private TimeStampNsType() {
+    }
+
+    @Override
+    public boolean isInjectiveCastTo(DataType target) {
+        return target instanceof TimeStampNsType || target instanceof 
CharacterType;
+    }
+
+    @Override
+    public Type toCatalogDataType() {
+        return ScalarType.createTimeStampNsType();
+    }
+
+    @Override
+    public ScaleTimeType scaleTypeForType(DataType dataType) {
+        return INSTANCE;
+    }
+
+    @Override
+    public ScaleTimeType forTypeFromString(StringLikeLiteral str) {
+        return INSTANCE;
+    }
+
+    @Override
+    public int getScale() {
+        return SCALE;
+    }
+
+    @Override
+    public int width() {
+        return WIDTH;
+    }
+
+    @Override
+    public double rangeLength(double high, double low) {
+        if (high == low) {
+            return 0;
+        }
+        if (Double.isInfinite(high) || Double.isInfinite(low)) {
+            return Double.POSITIVE_INFINITY;
+        }
+        try {
+            LocalDateTime to = toLocalDateTime(high);
+            LocalDateTime from = toLocalDateTime(low);
+            return ChronoUnit.SECONDS.between(from, to);

Review Comment:
   [P2] Preserve subsecond distance in TIMESTAMP_NS range estimates
   
   `toLocalDateTime(double)` casts away the packed fractional remainder, and 
`ChronoUnit.SECONDS` then makes every pair within one second have distance 
zero. Once TIMESTAMP_NS bounds use their proper fractional encoding, a range 
`.1` to `.9` filtered at `.5` sends `0 / 0` into `FilterEstimation`; the NaN 
survives its local clamps and `Statistics.withSel()` normalizes it to 
selectivity 1.0. Please measure a subsecond distance without overflowing the 
full legal domain, or decline range scaling when the representation cannot 
distinguish the endpoints, with same-second inequality-estimation tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to