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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 5a484a2f884 branch-4.1: [fix](constant folding) Align FE folding with 
BE execution #64881 #65319 (#68071)
5a484a2f884 is described below

commit 5a484a2f8848815f0c32b001b53cb63fbf4a0e43
Author: morrySnow <[email protected]>
AuthorDate: Thu Sep 17 21:13:36 2026 +0800

    branch-4.1: [fix](constant folding) Align FE folding with BE execution 
#64881 #65319 (#68071)
    
    ### What problem does this PR solve?
    
    Related PRs: #64881, #65319
    
    Problem Summary:
    
    Backport the two related constant-folding fixes to branch-4.1 in one PR.
    They align FE folding with BE execution for affected string, URL,
    floating-point, date/time, decimal, and `str_to_date` expressions.
    
    Both changes are kept together because #65319 follows up on #64881,
    including the correct `FIELD` behavior for signed zero while preserving
    the existing `NaN` match behavior. The string regression assertions are
    moved from master's `test_string_all` suite into the corresponding
    `test_string_function` suite used by branch-4.1.
    
    ### Release note
    
    Fix incorrect FE constant-folded results for the affected expressions.
    
    ### Check List (For Author)
    
    - Test
        - [x] Unit Test
        - [x] Regression test coverage included
    - Behavior changed:
    - [x] Yes. FE constant folding now matches BE execution for the covered
    expressions.
    - Does this need documentation?
        - [x] No.
    
    Test details:
    
    - Targeted FE unit tests: 29 run, 0 failures, 0 errors, 2 pre-existing
    skips.
    - Full FE Maven reactor: `BUILD SUCCESS`.
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label
---
 .../executable/DateTimeExtractAndTransform.java    |  16 +-
 .../functions/executable/StringArithmetic.java     | 357 +++++++++++++--------
 .../expressions/functions/scalar/StrToDate.java    |  29 +-
 .../trees/expressions/literal/DateLiteral.java     |   2 +-
 .../trees/expressions/literal/DateTimeLiteral.java |  51 ++-
 .../expressions/literal/DateTimeV2Literal.java     |   4 +-
 .../expressions/literal/DecimalV3Literal.java      |   7 +-
 .../expressions/literal/StringLikeLiteral.java     |  68 ++--
 .../trees/expressions/literal/TimeV2Literal.java   |   5 +-
 .../expressions/literal/TimestampTzLiteral.java    |   4 +-
 .../functions/executable/StringArithmeticTest.java |  66 ++++
 .../functions/scalar/StrToDateTest.java            |  43 +++
 .../trees/expressions/literal/DateLiteralTest.java |   7 +-
 .../expressions/literal/DecimalLiteralTest.java    |  11 +
 .../expressions/literal/StringLikeLiteralTest.java |  13 +
 .../expressions/literal/TimeV2LiteralTest.java     |  33 ++
 .../suites/cast_p0/cast_to_datetime.groovy         |   7 +-
 .../fold_constant/fe_constant_cast_to_date.groovy  |  18 +-
 .../fold_constant_string_arithmatic.groovy         |  10 +-
 .../datetime_functions/test_date_function.groovy   |   1 +
 .../datetime_functions/test_func_time.groovy       |   1 +
 .../string_functions/test_string_function.groovy   |  11 +
 22 files changed, 536 insertions(+), 228 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
index 2fc25ff528f..ca55370c0d7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/DateTimeExtractAndTransform.java
@@ -747,12 +747,20 @@ public class DateTimeExtractAndTransform {
             hourValue = hourValue > 0 ? 838 : -838;
             minuteValue = 59;
             secondValue = 59;
-        } else if (Math.abs(hourValue) == 838 && secondValue > 59) {
-            secondValue = 59;
         }
 
-        return new TimeV2Literal((int) Math.abs(hourValue), (int) minuteValue, 
(int) secondValue,
-                            (int) Math.round(secondValue * 1000000) % 1000000, 
6, hourValue < 0);
+        long totalMicrosecond = Math.abs(hourValue) * 3600L * 1000000
+                + minuteValue * 60L * 1000000 + Math.round(secondValue * 
1000000);
+        long maxMicrosecond = 838L * 3600L * 1000000 + 59L * 60L * 1000000 + 
59999999L;
+        totalMicrosecond = Math.min(totalMicrosecond, maxMicrosecond);
+
+        int newHour = (int) (totalMicrosecond / 3600L / 1000000);
+        totalMicrosecond %= 3600L * 1000000;
+        int newMinute = (int) (totalMicrosecond / 60L / 1000000);
+        totalMicrosecond %= 60L * 1000000;
+        int newSecond = (int) (totalMicrosecond / 1000000);
+        int microsecond = (int) (totalMicrosecond % 1000000);
+        return new TimeV2Literal(newHour, newMinute, newSecond, microsecond, 
6, hourValue < 0);
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
index 0995c68c559..e03dab17deb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmetic.java
@@ -46,9 +46,6 @@ import com.google.common.collect.Lists;
 
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
 import java.net.URLDecoder;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
@@ -56,6 +53,7 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.regex.Pattern;
 
 /**
@@ -136,11 +134,7 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "lower")
     public static Expression lowerVarchar(StringLikeLiteral first) {
-        StringBuilder result = new StringBuilder(first.getValue().length());
-        for (char c : first.getValue().toCharArray()) {
-            result.append(Character.toLowerCase(c));
-        }
-        return castStringLikeLiteral(first, result.toString());
+        return castStringLikeLiteral(first, 
first.getValue().toLowerCase(Locale.ROOT));
     }
 
     /**
@@ -148,11 +142,7 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "upper")
     public static Expression upperVarchar(StringLikeLiteral first) {
-        StringBuilder result = new StringBuilder(first.getValue().length());
-        for (char c : first.getValue().toCharArray()) {
-            result.append(Character.toUpperCase(c));
-        }
-        return castStringLikeLiteral(first, result.toString());
+        return castStringLikeLiteral(first, 
first.getValue().toUpperCase(Locale.ROOT));
     }
 
     private static String trimImpl(String first, String second, boolean left, 
boolean right) {
@@ -327,7 +317,8 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "right")
     public static Expression right(StringLikeLiteral first, IntegerLiteral 
second) {
-        int inputLength = first.getValue().codePointCount(0, 
first.getValue().length());
+        String input = first.getValue();
+        int inputLength = input.codePointCount(0, input.length());
         if (second.getValue() < (- inputLength) || Math.abs(second.getValue()) 
== 0) {
             return castStringLikeLiteral(first, "");
         } else if (second.getValue() >= inputLength) {
@@ -335,13 +326,11 @@ public class StringArithmetic {
         } else {
             // at here second can not be exceeding boundary
             if (second.getValue() >= 0) {
-                int index = first.getValue().offsetByCodePoints(0, 
second.getValue());
-                return castStringLikeLiteral(first, first.getValue().substring(
-                    inputLength - index, inputLength));
+                int index = input.offsetByCodePoints(0, inputLength - 
second.getValue());
+                return castStringLikeLiteral(first, input.substring(index));
             } else {
-                int index = first.getValue().offsetByCodePoints(0, 
Math.abs(second.getValue()) - 1);
-                return castStringLikeLiteral(first, first.getValue().substring(
-                    index, inputLength));
+                int index = input.offsetByCodePoints(0, 
Math.abs(second.getValue()) - 1);
+                return castStringLikeLiteral(first, input.substring(index));
             }
         }
     }
@@ -400,7 +389,11 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "instr")
     public static Expression instr(StringLikeLiteral first, StringLikeLiteral 
second) {
-        return new IntegerLiteral(first.getValue().indexOf(second.getValue()) 
+ 1);
+        int index = first.getValue().indexOf(second.getValue());
+        if (index < 0) {
+            return new IntegerLiteral(0);
+        }
+        return new IntegerLiteral(first.getValue().codePointCount(0, index) + 
1);
     }
 
     /**
@@ -431,13 +424,16 @@ public class StringArithmetic {
     @ExecFunction(name = "concat_ws")
     public static Expression concatWsVarcharArray(StringLikeLiteral first, 
ArrayLiteral second) {
         StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < second.getValue().size() - 1; i++) {
-            if (!(second.getValue().get(i) instanceof NullLiteral)) {
-                sb.append(second.getValue().get(i).getValue());
-                sb.append(first.getValue());
+        boolean hasValue = false;
+        for (Literal value : second.getValue()) {
+            if (!(value instanceof NullLiteral)) {
+                if (hasValue) {
+                    sb.append(first.getValue());
+                }
+                sb.append(value.getValue());
+                hasValue = true;
             }
         }
-        sb.append(second.getValue().get(second.getValue().size() - 
1).getValue());
         return castStringLikeLiteral(first, sb.toString());
     }
 
@@ -447,11 +443,12 @@ public class StringArithmetic {
     @ExecFunction(name = "concat_ws")
     public static Expression concatWsVarcharVarchar(StringLikeLiteral first, 
StringLikeLiteral... second) {
         StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < second.length - 1; i++) {
+        for (int i = 0; i < second.length; i++) {
+            if (i > 0) {
+                sb.append(first.getValue());
+            }
             sb.append(second[i].getValue());
-            sb.append(first.getValue());
         }
-        sb.append(second[second.length - 1].getValue());
         return castStringLikeLiteral(first, sb.toString());
     }
 
@@ -468,19 +465,22 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "initcap")
     public static Expression initCap(StringLikeLiteral first) {
-        StringBuilder result = new StringBuilder(first.getValue().length());
+        String lower = first.getValue().toLowerCase(Locale.ROOT);
+        StringBuilder result = new StringBuilder(lower.length());
         boolean capitalizeNext = true;
 
-        for (char c : first.getValue().toCharArray()) {
-            if (!Character.isLetterOrDigit(c)) {
-                result.append(c);
+        for (int i = 0; i < lower.length();) {
+            int codePoint = lower.codePointAt(i);
+            if (!Character.isLetterOrDigit(codePoint)) {
+                result.appendCodePoint(codePoint);
                 capitalizeNext = true;  // Next character should be capitalized
             } else if (capitalizeNext) {
-                result.append(Character.toUpperCase(c));
+                result.appendCodePoint(Character.toUpperCase(codePoint));
                 capitalizeNext = false;
             } else {
-                result.append(Character.toLowerCase(c));
+                result.appendCodePoint(codePoint);
             }
+            i += Character.charCount(codePoint);
         }
         return castStringLikeLiteral(first, result.toString());
     }
@@ -493,7 +493,7 @@ public class StringArithmetic {
         try {
             MessageDigest md = MessageDigest.getInstance("MD5");
             // Update the digest with the input bytes
-            md.update(first.getValue().getBytes());
+            md.update(first.getValue().getBytes(StandardCharsets.UTF_8));
             return castStringLikeLiteral(first, bytesToHex(md.digest()));
         } catch (NoSuchAlgorithmException e) {
             throw new RuntimeException(e);
@@ -516,7 +516,7 @@ public class StringArithmetic {
             }
 
             // Step 3: Convert the combined string to a byte array and pass it 
to the digest() method
-            byte[] messageDigest = 
md.digest(combinedInput.toString().getBytes());
+            byte[] messageDigest = 
md.digest(combinedInput.toString().getBytes(StandardCharsets.UTF_8));
 
             // Step 4: Convert the byte array into a hexadecimal string
             StringBuilder hexString = new StringBuilder();
@@ -554,6 +554,28 @@ public class StringArithmetic {
         return 0;
     }
 
+    private static int compareFloatLiteral(FloatLiteral first, FloatLiteral... 
second) {
+        float firstValue = first.getValue();
+        for (int i = 0; i < second.length; i++) {
+            float secondValue = second[i].getValue();
+            if (secondValue == firstValue || Float.isNaN(secondValue) && 
Float.isNaN(firstValue)) {
+                return i + 1;
+            }
+        }
+        return 0;
+    }
+
+    private static int compareDoubleLiteral(DoubleLiteral first, 
DoubleLiteral... second) {
+        double firstValue = first.getValue();
+        for (int i = 0; i < second.length; i++) {
+            double secondValue = second[i].getValue();
+            if (secondValue == firstValue || Double.isNaN(secondValue) && 
Double.isNaN(firstValue)) {
+                return i + 1;
+            }
+        }
+        return 0;
+    }
+
     /**
      * Executable arithmetic functions field
      */
@@ -599,7 +621,7 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "field")
     public static Expression fieldFloat(FloatLiteral first, FloatLiteral... 
second) {
-        return new IntegerLiteral(compareLiteral(first, second));
+        return new IntegerLiteral(compareFloatLiteral(first, second));
     }
 
     /**
@@ -607,7 +629,7 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "field")
     public static Expression fieldDouble(DoubleLiteral first, DoubleLiteral... 
second) {
-        return new IntegerLiteral(compareLiteral(first, second));
+        return new IntegerLiteral(compareDoubleLiteral(first, second));
     }
 
     /**
@@ -651,12 +673,23 @@ public class StringArithmetic {
     }
 
     private static int findStringInSet(String target, String input) {
-        String[] split = input.split(",", -1);
-        for (int i = 0; i < split.length; i++) {
-            if (split[i].equals(target)) {
-                return i + 1;
-            }
+        if (target.indexOf(',') >= 0) {
+            return 0;
         }
+
+        int tokenIndex = 1;
+        int start = 0;
+        do {
+            int end = start;
+            while (end < input.length() && input.charAt(end) != ',') {
+                ++end;
+            }
+            if (input.substring(start, end).equals(target)) {
+                return tokenIndex;
+            }
+            start = end + 1;
+            ++tokenIndex;
+        } while (start < input.length());
         return 0;
     }
 
@@ -832,7 +865,7 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "strcmp")
     public static Expression strcmp(StringLikeLiteral first, StringLikeLiteral 
second) {
-        int result = first.getValue().compareTo(second.getValue());
+        int result = compareUtf8Bytes(first.getValue(), second.getValue());
         if (result == 0) {
             return new TinyIntLiteral((byte) 0);
         } else if (result < 0) {
@@ -842,6 +875,19 @@ public class StringArithmetic {
         }
     }
 
+    private static int compareUtf8Bytes(String left, String right) {
+        byte[] leftBytes = left.getBytes(StandardCharsets.UTF_8);
+        byte[] rightBytes = right.getBytes(StandardCharsets.UTF_8);
+        int minLength = Math.min(leftBytes.length, rightBytes.length);
+        for (int i = 0; i < minLength; i++) {
+            int diff = Byte.toUnsignedInt(leftBytes[i]) - 
Byte.toUnsignedInt(rightBytes[i]);
+            if (diff != 0) {
+                return diff;
+            }
+        }
+        return leftBytes.length - rightBytes.length;
+    }
+
     /**
      * Executable arithmetic functions overlay
      */
@@ -871,93 +917,137 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "parse_url")
     public static Expression parseurl(StringLikeLiteral first, 
StringLikeLiteral second) {
-        URI uri = null;
-        try {
-            uri = new URI(first.getValue());
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e);
-        }
-        StringBuilder sb = new StringBuilder();
-        if (uri.getScheme() == null) {
+        String value = parseUrlRaw(first.getValue(), second.getValue());
+        if (value == null) {
             return new NullLiteral(first.getDataType());
         }
-        switch (second.getValue().toUpperCase()) {
+        return castStringLikeLiteral(first, value);
+    }
+
+    private static String parseUrlRaw(String url, String part) {
+        String trimmedUrl = url.trim();
+        int protocolPos = trimmedUrl.indexOf("://");
+        if (protocolPos < 0) {
+            return null;
+        }
+        String protocolEnd = trimmedUrl.substring(protocolPos + 
"://".length());
+        switch (part.toUpperCase(Locale.ROOT)) {
             case "PROTOCOL":
-                String scheme = uri.getScheme();
-                if (scheme == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(scheme); // e.g., http, https
-                break;
+                return trimmedUrl.substring(0, protocolPos);
             case "HOST":
-                String host = uri.getHost();
-                if (host == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(host);  // e.g., www.example.com
-                break;
+                return parseUrlHost(protocolEnd);
             case "PATH":
-                String path = uri.getPath();
-                if (path == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(path);  // e.g., /page
-                break;
+                return parseUrlPath(protocolEnd);
             case "REF":
-                try {
-                    String ref = uri.toURL().getRef();
-                    if (ref == null) {
-                        return new NullLiteral(first.getDataType());
-                    }
-                    sb.append(ref);  // e.g., /page
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-                break;
+                return parseUrlRef(protocolEnd);
             case "AUTHORITY":
-                String authority = uri.getAuthority();
-                if (authority == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(authority);  // e.g., param1=value1&param2=value2
-                break;
+                return parseUrlAuthority(protocolEnd);
             case "FILE":
-                try {
-                    String file = uri.toURL().getFile();
-                    if (file == null) {
-                        return new NullLiteral(first.getDataType());
-                    }
-                    sb.append(file);  // e.g., param1=value1&param2=value2
-                } catch (MalformedURLException e) {
-                    throw new RuntimeException(e);
-                }
-                break;
+                return parseUrlFile(protocolEnd);
             case "QUERY":
-                String query = uri.getQuery();
-                if (query == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(query);  // e.g., param1=value1&param2=value2
-                break;
+                return parseUrlQuery(protocolEnd);
             case "PORT":
-                int port = uri.getPort();
-                if (port == -1) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(port);
-                break;
+                return parseUrlPort(protocolEnd);
             case "USERINFO":
-                String userInfo = uri.getUserInfo();
-                if (userInfo == null) {
-                    return new NullLiteral(first.getDataType());
-                }
-                sb.append(userInfo);  // e.g., user:pass
-                break;
+                return parseUrlUserInfo(protocolEnd);
             default:
                 throw new RuntimeException("Valid URL parts are 'PROTOCOL', 
'HOST', "
                         + "'PATH', 'REF', 'AUTHORITY', 'FILE', 'USERINFO', 
'PORT' and 'QUERY'");
         }
-        return castStringLikeLiteral(first, sb.toString());
+    }
+
+    private static int firstIndexOf(String value, char first, char second) {
+        int firstIndex = value.indexOf(first);
+        int secondIndex = value.indexOf(second);
+        if (firstIndex < 0) {
+            return secondIndex;
+        }
+        if (secondIndex < 0) {
+            return firstIndex;
+        }
+        return Math.min(firstIndex, secondIndex);
+    }
+
+    private static String substringEnd(String value, int end) {
+        return end < 0 ? value : value.substring(0, end);
+    }
+
+    private static String parseUrlAuthority(String protocolEnd) {
+        return substringEnd(protocolEnd, protocolEnd.indexOf('/'));
+    }
+
+    private static String parseUrlPath(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('/');
+        if (startPos < 0) {
+            return "";
+        }
+        String pathStart = protocolEnd.substring(startPos);
+        return substringEnd(pathStart, firstIndexOf(pathStart, '?', '#'));
+    }
+
+    private static String parseUrlFile(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('/');
+        if (startPos < 0) {
+            return "";
+        }
+        String pathStart = protocolEnd.substring(startPos);
+        return substringEnd(pathStart, pathStart.indexOf('#'));
+    }
+
+    private static String parseUrlHost(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('@');
+        startPos = startPos < 0 ? 0 : startPos + 1;
+        String hostStart = protocolEnd.substring(startPos);
+        int queryStartPos = hostStart.indexOf('?');
+        if (queryStartPos > 0) {
+            hostStart = hostStart.substring(0, queryStartPos);
+        }
+        int endPos = hostStart.indexOf(':');
+        if (endPos < 0) {
+            endPos = hostStart.indexOf('/');
+        }
+        return substringEnd(hostStart, endPos);
+    }
+
+    private static String parseUrlQuery(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('?');
+        if (startPos < 0) {
+            return null;
+        }
+        String queryStart = protocolEnd.substring(startPos + 1);
+        return substringEnd(queryStart, queryStart.indexOf('#'));
+    }
+
+    private static String parseUrlRef(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('#');
+        if (startPos < 0) {
+            return null;
+        }
+        return protocolEnd.substring(startPos + 1);
+    }
+
+    private static String parseUrlUserInfo(String protocolEnd) {
+        int endPos = protocolEnd.indexOf('@');
+        if (endPos < 0) {
+            return null;
+        }
+        return protocolEnd.substring(0, endPos);
+    }
+
+    private static String parseUrlPort(String protocolEnd) {
+        int startPos = protocolEnd.indexOf('@');
+        startPos = startPos < 0 ? 0 : startPos + 1;
+        String hostStart = protocolEnd.substring(startPos);
+        int endPos = hostStart.indexOf(':');
+        if (endPos < 0) {
+            return null;
+        }
+        String portStart = hostStart.substring(endPos + 1);
+        int portEndPos = portStart.indexOf('/');
+        if (portEndPos < 0) {
+            portEndPos = portStart.indexOf('?');
+        }
+        return substringEnd(portStart, portEndPos);
     }
 
     /**
@@ -1016,25 +1106,26 @@ public class StringArithmetic {
      */
     @ExecFunction(name = "extract_url_parameter")
     public static Expression extractUrlParameter(StringLikeLiteral first, 
StringLikeLiteral second) {
-        if (first.getValue() == null || first.getValue().indexOf('?') == -1) {
+        if (second.getValue().isEmpty()) {
             return castStringLikeLiteral(first, "");
         }
-        URI uri;
-        try {
-            uri = new URI(first.getValue());
-        } catch (URISyntaxException e) {
-            throw new RuntimeException(e);
+        String trimmedUrl = first.getValue().trim();
+        int questionPos = trimmedUrl.indexOf('?');
+        if (questionPos < 0) {
+            return castStringLikeLiteral(first, "");
         }
-
-        String query = uri.getQuery();
-        if (query != null) {
-            String[] pairs = query.split("&", -1);
-
-            for (String pair : pairs) {
-                String[] keyValue = pair.split("=", -1);
-                if (second.getValue().equals(keyValue[0])) {
-                    return castStringLikeLiteral(first, keyValue[1]);
-                }
+        int hashPos = trimmedUrl.indexOf('#');
+        String subUrl = hashPos < 0
+                ? trimmedUrl.substring(questionPos + 1)
+                : trimmedUrl.substring(questionPos + 1, hashPos);
+        String[] pairs = subUrl.split("&", -1);
+        for (String pair : pairs) {
+            int eqPos = pair.indexOf('=');
+            if (eqPos < 0) {
+                continue;
+            }
+            if (second.getValue().equals(pair.substring(0, eqPos))) {
+                return castStringLikeLiteral(first, pair.substring(eqPos + 1));
             }
         }
         return castStringLikeLiteral(first, "");
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDate.java
index 6436f439ce5..31887127eb4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDate.java
@@ -19,10 +19,13 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.analysis.DateLiteral;
 import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionEvaluator;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
 import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -90,12 +93,12 @@ public class StrToDate extends ScalarFunction
          * Return type is DATETIME
          */
         DataType returnType;
-        if (getArgument(1) instanceof StringLikeLiteral) {
-            if (DateLiteral.hasTimePart(((StringLikeLiteral) 
getArgument(1)).getStringValue())) {
+        Literal formatLiteral = getConstantFormatLiteral();
+        if (formatLiteral != null) {
+            if (DateLiteral.hasTimePart(formatLiteral.getStringValue())) {
                 //FIXME: Here will pass different scale to BE with same input 
types. Need to be fixed.
                 returnType = DateTimeV2Type.SYSTEM_DEFAULT;
-                if (returnType.isDateTimeV2Type()
-                        && DateLiteral.hasMicroSecondPart(((StringLikeLiteral) 
getArgument(1)).getStringValue())) {
+                if 
(DateLiteral.hasMicroSecondPart(formatLiteral.getStringValue())) {
                     returnType = DateTimeV2Type.MAX;
                 }
             } else {
@@ -107,6 +110,24 @@ public class StrToDate extends ScalarFunction
         return signature.withReturnType(returnType);
     }
 
+    private StringLikeLiteral getConstantFormatLiteral() {
+        Expression format = getArgument(1);
+        if (!format.isConstant()) {
+            return null;
+        }
+        if (!format.getDataType().isStringLikeType()) {
+            format = new Cast(format, StringType.INSTANCE);
+        }
+        if (format instanceof StringLikeLiteral) {
+            return (StringLikeLiteral) format;
+        }
+        Expression evaluated = ExpressionEvaluator.INSTANCE.eval(format);
+        if (evaluated instanceof StringLikeLiteral) {
+            return (StringLikeLiteral) evaluated;
+        }
+        return null;
+    }
+
     /**
      * withChildren.
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
index cbec0669eb6..6ca81d5ec9a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteral.java
@@ -371,7 +371,7 @@ public class DateLiteral extends Literal implements 
ComparableLiteral {
         month = DateUtils.getOrDefault(dateTime, ChronoField.MONTH_OF_YEAR);
         day = DateUtils.getOrDefault(dateTime, ChronoField.DAY_OF_MONTH);
 
-        if (checkDatetime(dateTime) || checkRange(year, month, day) || 
checkDate(year, month, day)) {
+        if (checkRange(year, month, day) || checkDate(year, month, day)) {
             throw new AnalysisException("date/datetime literal [" + s + "] is 
out of range");
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
index 113cb7824e1..48307144532 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeLiteral.java
@@ -246,27 +246,16 @@ public class DateTimeLiteral extends DateLiteral {
         // Microseconds have 7 digits.
         long sevenDigit = microSecond % 10;
         microSecond = microSecond / 10;
-        if (sevenDigit >= 5 && (this instanceof DateTimeV2Literal || this 
instanceof TimestampTzLiteral)) {
+        if (sevenDigit >= 5) {
             DateTimeLiteral result;
-            if (this instanceof DateTimeV2Literal) {
-                result = (DateTimeV2Literal) ((DateTimeV2Literal) 
this).plusMicroSeconds(1);
-                this.second = result.second;
-                this.minute = result.minute;
-                this.hour = result.hour;
-                this.day = result.day;
-                this.month = result.month;
-                this.year = result.year;
-                this.microSecond = result.microSecond;
-            } else if (this instanceof TimestampTzLiteral) {
-                result = (TimestampTzLiteral) ((TimestampTzLiteral) 
this).plusMicroSeconds(1);
-                this.second = result.second;
-                this.minute = result.minute;
-                this.hour = result.hour;
-                this.day = result.day;
-                this.month = result.month;
-                this.year = result.year;
-                this.microSecond = result.microSecond;
-            }
+            result = this.plusMicroSeconds(1);
+            this.second = result.second;
+            this.minute = result.minute;
+            this.hour = result.hour;
+            this.day = result.day;
+            this.month = result.month;
+            this.year = result.year;
+            this.microSecond = result.microSecond;
         }
 
         if (checkRange(year, month, day) || checkDate(year, month, day)) {
@@ -274,6 +263,12 @@ public class DateTimeLiteral extends DateLiteral {
         }
     }
 
+    // When performing addition or subtraction with MicroSeconds, the 
precision must be set to 6 to display it
+    // completely. use multiplyExact to be aware of multiplication overflow 
possibility.
+    public DateTimeLiteral plusMicroSeconds(long microSeconds) {
+        return 
fromJavaDateType(toJavaDateType().plusNanos(Math.multiplyExact(microSeconds, 
1000L)), 6);
+    }
+
     private static LocalDateTime convertTimeZone(long year, long month, long 
day, long hour, long minute,
             long second, ZoneId fromZone, ZoneId toZone) {
         LocalDateTime localDateTime = LocalDateTime.of((int) year, (int) 
month, (int) day,
@@ -433,31 +428,31 @@ public class DateTimeLiteral extends DateLiteral {
     }
 
     public Expression plusDays(long days) {
-        return fromJavaDateType(toJavaDateType().plusDays(days));
+        return fromJavaDateType(toJavaDateType().plusDays(days), 0);
     }
 
     public Expression plusMonths(long months) {
-        return fromJavaDateType(toJavaDateType().plusMonths(months));
+        return fromJavaDateType(toJavaDateType().plusMonths(months), 0);
     }
 
     public Expression plusWeeks(long weeks) {
-        return fromJavaDateType(toJavaDateType().plusWeeks(weeks));
+        return fromJavaDateType(toJavaDateType().plusWeeks(weeks), 0);
     }
 
     public Expression plusYears(long years) {
-        return fromJavaDateType(toJavaDateType().plusYears(years));
+        return fromJavaDateType(toJavaDateType().plusYears(years), 0);
     }
 
     public Expression plusHours(long hours) {
-        return fromJavaDateType(toJavaDateType().plusHours(hours));
+        return fromJavaDateType(toJavaDateType().plusHours(hours), 0);
     }
 
     public Expression plusMinutes(long minutes) {
-        return fromJavaDateType(toJavaDateType().plusMinutes(minutes));
+        return fromJavaDateType(toJavaDateType().plusMinutes(minutes), 0);
     }
 
     public Expression plusSeconds(long seconds) {
-        return fromJavaDateType(toJavaDateType().plusSeconds(seconds));
+        return fromJavaDateType(toJavaDateType().plusSeconds(seconds), 0);
     }
 
     public long getHour() {
@@ -493,7 +488,7 @@ public class DateTimeLiteral extends DateLiteral {
                 ((int) getHour()), ((int) getMinute()), ((int) getSecond()), 
(int) getMicroSecond() * 1000);
     }
 
-    public static Expression fromJavaDateType(LocalDateTime dateTime) {
+    public static DateTimeLiteral fromJavaDateType(LocalDateTime dateTime, int 
precision) {
         if (isDateOutOfRange(dateTime)) {
             throw new AnalysisException("datetime out of range: " + 
dateTime.toString());
         }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
index 93f221d4d99..bfdf1e3be6e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DateTimeV2Literal.java
@@ -409,7 +409,7 @@ public class DateTimeV2Literal extends DateTimeLiteral {
 
     // When performing addition or subtraction with MicroSeconds, the 
precision must be set to 6 to display it
     // completely. use multiplyExact to be aware of multiplication overflow 
possibility.
-    public Expression plusMicroSeconds(long microSeconds) {
+    public DateTimeV2Literal plusMicroSeconds(long microSeconds) {
         return 
fromJavaDateType(toJavaDateType().plusNanos(Math.multiplyExact(microSeconds, 
1000L)), 6);
     }
 
@@ -474,7 +474,7 @@ public class DateTimeV2Literal extends DateTimeLiteral {
     /**
      * convert java LocalDateTime object to DateTimeV2Literal object.
      */
-    public static Expression fromJavaDateType(LocalDateTime dateTime, int 
precision) {
+    public static DateTimeV2Literal fromJavaDateType(LocalDateTime dateTime, 
int precision) {
         long value = (long) Math.pow(10, DateTimeV2Type.MAX_SCALE - precision);
         if (isDateOutOfRange(dateTime)) {
             throw new AnalysisException("datetime out of range" + 
dateTime.toString());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java
index c8c161b35bd..e82acd634c7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DecimalV3Literal.java
@@ -174,7 +174,12 @@ public class DecimalV3Literal extends FractionalLiteral {
 
     @Override
     public String computeToSql() {
-        return value.toPlainString();
+        return getStringValue();
+    }
+
+    @Override
+    public String getStringValue() {
+        return value.setScale(((DecimalV3Type) dataType).getScale(), 
RoundingMode.HALF_UP).toPlainString();
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
index 148c3efb852..afe473ccf75 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteral.java
@@ -23,7 +23,6 @@ import org.apache.doris.nereids.exceptions.CastException;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import 
org.apache.doris.nereids.trees.expressions.literal.format.DateTimeChecker;
 import org.apache.doris.nereids.types.DataType;
-import org.apache.doris.nereids.types.DateTimeType;
 import org.apache.doris.nereids.types.DateTimeV2Type;
 import org.apache.doris.nereids.types.TimeStampTzType;
 import org.apache.doris.nereids.types.TimeV2Type;
@@ -133,18 +132,9 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
             return castToIntegral(targetType, strictCast);
         }
         if (targetType.isDateType() || targetType.isDateV2Type()) {
-            Expression expression = castToDateTime(DateTimeV2Type.MAX, 
strictCast, false);
-            DateTimeV2Literal datetime = (DateTimeV2Literal) expression;
-            if (targetType.isDateType()) {
-                return new DateLiteral(datetime.year, datetime.month, 
datetime.day);
-            } else {
-                return new DateV2Literal(datetime.year, datetime.month, 
datetime.day);
-            }
+            return castToDateTime(targetType, strictCast);
         } else if (targetType.isDateTimeType()) {
-            Expression expression = castToDateTime(DateTimeV2Type.MAX, 
strictCast, true);
-            DateTimeV2Literal datetime = (DateTimeV2Literal) expression;
-            return new DateTimeLiteral((DateTimeType) targetType, 
datetime.year, datetime.month, datetime.day,
-                    datetime.hour, datetime.minute, datetime.second, 
datetime.microSecond);
+            return castToDateTime(targetType, strictCast);
         } else if (targetType.isTimeStampTzType()) {
             // Wildcard targets still need a concrete scale before parsing.
             TimeStampTzType timeStampTzType = (TimeStampTzType) targetType;
@@ -154,10 +144,10 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
             if (DateTimeChecker.hasTimeZone(value)) {
                 return new TimestampTzLiteral(timeStampTzType, value);
             }
-            DateTimeV2Literal datetime = castToDateTime(DateTimeV2Type.MAX, 
strictCast, true);
+            DateTimeV2Literal datetime = (DateTimeV2Literal) 
castToDateTime(DateTimeV2Type.MAX, strictCast);
             return TimestampTzLiteral.fromSessionTimeZone(timeStampTzType, 
datetime);
         } else if (targetType.isDateTimeV2Type()) {
-            return castToDateTime(targetType, strictCast, true);
+            return castToDateTime(targetType, strictCast);
         } else if (targetType.isFloatType()) {
             return castToFloat();
         } else if (targetType.isDoubleType()) {
@@ -270,7 +260,7 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
         throw new CastException(String.format("%s can't cast to decimal in 
strict mode.", value));
     }
 
-    protected DateTimeV2Literal castToDateTime(DataType targetType, boolean 
strictCast, boolean isDatetime) {
+    protected DateLiteral castToDateTime(DataType targetType, boolean 
strictCast) {
         Matcher strictMatcher = dateStrictPattern.matcher(value);
         String year;
         String month;
@@ -312,14 +302,7 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
             if (tz != null && tz.equalsIgnoreCase("CST")) {
                 tz = "+08:00";
             }
-            DateTimeV2Literal dt = getDateTimeLiteral(year, month, date, hour, 
minute, second,
-                    fraction, tz, targetType);
-            if (isDatetime) {
-                return dt;
-            } else {
-                return new 
DateTimeV2Literal(Long.parseLong(year2ToYear4(year)), Long.parseLong(month),
-                        Long.parseLong(date), 0, 0, 0);
-            }
+            return getDateTimeLiteral(year, month, date, hour, minute, second, 
fraction, tz, targetType);
         } else if (!strictCast) {
             Matcher unStrictMatcher = dateUnStrictPattern.matcher(value);
             if (unStrictMatcher.matches()) {
@@ -334,14 +317,7 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
                 if (tz != null && tz.equalsIgnoreCase("CST")) {
                     tz = "+08:00";
                 }
-                DateTimeV2Literal dt = getDateTimeLiteral(year, month, date, 
hour, minute, second,
-                        fraction, tz, targetType);
-                if (isDatetime) {
-                    return dt;
-                } else {
-                    return new 
DateTimeV2Literal(Long.parseLong(year2ToYear4(year)), Long.parseLong(month),
-                            Long.parseLong(date), 0, 0, 0);
-                }
+                return getDateTimeLiteral(year, month, date, hour, minute, 
second, fraction, tz, targetType);
             }
         }
         throw new CastException(String.format("[%s] can't cast to %s.", value, 
targetType));
@@ -355,7 +331,7 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
         return year;
     }
 
-    protected DateTimeV2Literal getDateTimeLiteral(String year, String month, 
String date, String hour, String minute,
+    protected DateLiteral getDateTimeLiteral(String year, String month, String 
date, String hour, String minute,
             String second, String fraction, String tz, DataType targetType) {
         String year4 = year2ToYear4(year);
         tz = tz == null ? "" : tz;
@@ -385,10 +361,30 @@ public abstract class StringLikeLiteral extends Literal 
implements ComparableLit
             }
         }
         String format = String.format("%s-%s-%sT%s:%s:%s%s%s", year4, month, 
date, hour, minute, second, fraction, tz);
-        try {
-            return new DateTimeV2Literal((DateTimeV2Type) targetType, format);
-        } catch (AnalysisException e) {
-            throw new CastException(e.getMessage(), e);
+        if (targetType.isDateType()) {
+            try {
+                return new DateLiteral(format);
+            } catch (AnalysisException e) {
+                throw new CastException(e.getMessage(), e);
+            }
+        } else if (targetType.isDateV2Type()) {
+            try {
+                return new DateV2Literal(format);
+            } catch (AnalysisException e) {
+                throw new CastException(e.getMessage(), e);
+            }
+        } else if (targetType.isDateTimeType()) {
+            try {
+                return new DateTimeLiteral(format);
+            } catch (AnalysisException e) {
+                throw new CastException(e.getMessage(), e);
+            }
+        } else {
+            try {
+                return new DateTimeV2Literal((DateTimeV2Type) targetType, 
format);
+            } catch (AnalysisException e) {
+                throw new CastException(e.getMessage(), e);
+            }
         }
     }
 
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 ffa3de4df14..f22e6e08965 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
@@ -302,9 +302,10 @@ public class TimeV2Literal extends Literal {
 
     @Override
     protected Expression uncheckedCastTo(DataType targetType) throws 
AnalysisException {
+        long microsecondValue = ((Double) getValue()).longValue();
         DateTimeV2Literal time = (DateTimeV2Literal) 
DateTimeV2Literal.fromJavaDateType(LocalDateTime
-                
.now(DateUtils.getTimeZone()).withHour(0).withMinute(0).withSecond(0).withNano(0).plusHours(getHour())
-                
.plusMinutes(getMinute()).plusSeconds(getSecond()).plusNanos(getMicroSecond() * 
1000),
+                
.now(DateUtils.getTimeZone()).withHour(0).withMinute(0).withSecond(0).withNano(0)
+                        .plusNanos(microsecondValue * 1000),
                 ((TimeV2Type) dataType).getScale());
         if (targetType.isDateType()) {
             return new DateLiteral(time.getYear(), time.getMonth(), 
time.getDay());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java
index ee4fb9c640f..8c801a37b13 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/TimestampTzLiteral.java
@@ -418,7 +418,7 @@ public class TimestampTzLiteral extends DateTimeLiteral {
 
     // When performing addition or subtraction with MicroSeconds, the 
precision must be set to 6 to display it
     // completely. use multiplyExact to be aware of multiplication overflow 
possibility.
-    public Expression plusMicroSeconds(long microSeconds) {
+    public TimestampTzLiteral plusMicroSeconds(long microSeconds) {
         return 
fromJavaDateType(toJavaDateType().plusNanos(Math.multiplyExact(microSeconds, 
1000L)), 6);
     }
 
@@ -511,7 +511,7 @@ public class TimestampTzLiteral extends DateTimeLiteral {
     /**
      * convert java LocalDateTime object to TimeStampTzTypeLiteral object.
      */
-    public static Expression fromJavaDateType(LocalDateTime dateTime, int 
precision) {
+    public static TimestampTzLiteral fromJavaDateType(LocalDateTime dateTime, 
int precision) {
         long value = (long) Math.pow(10, TimeStampTzType.MAX_SCALE - 
precision);
         if (isDateOutOfRange(dateTime)) {
             throw new AnalysisException("datetime out of range" + 
dateTime.toString());
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
new file mode 100644
index 00000000000..0bf26dd0da8
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/executable/StringArithmeticTest.java
@@ -0,0 +1,66 @@
+// 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.trees.expressions.functions.executable;
+
+import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.FloatLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class StringArithmeticTest {
+
+    @Test
+    void testFieldMatchesFloatNaN() {
+        IntegerLiteral result = (IntegerLiteral) 
StringArithmetic.fieldFloat(new FloatLiteral(Float.NaN),
+                new FloatLiteral(Float.NaN));
+
+        Assertions.assertEquals(1, result.getValue());
+    }
+
+    @Test
+    void testFieldMatchesDoubleNaN() {
+        IntegerLiteral result = (IntegerLiteral) 
StringArithmetic.fieldDouble(new DoubleLiteral(Double.NaN),
+                new DoubleLiteral(Double.NaN));
+
+        Assertions.assertEquals(1, result.getValue());
+    }
+
+    @Test
+    void testFieldMatchesFloatSignedZero() {
+        IntegerLiteral positiveZero = (IntegerLiteral) 
StringArithmetic.fieldFloat(new FloatLiteral(0.0f),
+                new FloatLiteral(-0.0f));
+        IntegerLiteral negativeZero = (IntegerLiteral) 
StringArithmetic.fieldFloat(new FloatLiteral(-0.0f),
+                new FloatLiteral(0.0f));
+
+        Assertions.assertEquals(1, positiveZero.getValue());
+        Assertions.assertEquals(1, negativeZero.getValue());
+    }
+
+    @Test
+    void testFieldMatchesDoubleSignedZero() {
+        IntegerLiteral positiveZero = (IntegerLiteral) 
StringArithmetic.fieldDouble(new DoubleLiteral(0.0),
+                new DoubleLiteral(-0.0));
+        IntegerLiteral negativeZero = (IntegerLiteral) 
StringArithmetic.fieldDouble(new DoubleLiteral(-0.0),
+                new DoubleLiteral(0.0));
+
+        Assertions.assertEquals(1, positiveZero.getValue());
+        Assertions.assertEquals(1, negativeZero.getValue());
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDateTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDateTest.java
new file mode 100644
index 00000000000..abb37cfc919
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StrToDateTest.java
@@ -0,0 +1,43 @@
+// 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.trees.expressions.functions.scalar;
+
+import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.DateV2Type;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class StrToDateTest {
+
+    @Test
+    void testComputeSignatureWithFoldableFormat() {
+        StrToDate dateFormat = new StrToDate(new StringLiteral("2024-01-01"),
+                new Concat(new StringLiteral("%Y-%m"), new 
StringLiteral("-%d")));
+        StrToDate dateTimeFormat = new StrToDate(new StringLiteral("2024-01-01 
12:34:56"),
+                new Concat(new StringLiteral("%Y-%m-%d"), new StringLiteral(" 
%H:%i:%s")));
+
+        FunctionSignature dateSignature = 
dateFormat.computeSignature(StrToDate.SIGNATURES.get(0));
+        FunctionSignature dateTimeSignature = 
dateTimeFormat.computeSignature(StrToDate.SIGNATURES.get(0));
+
+        Assertions.assertEquals(DateV2Type.INSTANCE, dateSignature.returnType);
+        Assertions.assertEquals(DateTimeV2Type.SYSTEM_DEFAULT, 
dateTimeSignature.returnType);
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
index cfdbb0c30cf..ea8904f4292 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DateLiteralTest.java
@@ -39,10 +39,9 @@ import java.util.function.Consumer;
 class DateLiteralTest {
     @Test
     void reject() {
-        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 01:00:00.000000"));
-        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 00:01:00.000000"));
-        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 00:00:01.000000"));
-        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 00:00:00.000001"));
+        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 24:00:00.000000"));
+        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 00:60:00.000000"));
+        Assertions.assertThrows(AnalysisException.class, () -> new 
DateLiteral("2022-01-01 00:00:60.000000"));
     }
 
     @Test
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteralTest.java
index 2a7ec959321..9000c9e5ef8 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DecimalLiteralTest.java
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.types.FloatType;
 import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.LargeIntType;
 import org.apache.doris.nereids.types.SmallIntType;
+import org.apache.doris.nereids.types.StringType;
 import org.apache.doris.nereids.types.TinyIntType;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.SessionVariable;
@@ -123,4 +124,14 @@ public class DecimalLiteralTest {
         DecimalV3Literal finalD = d1;
         Assertions.assertThrows(CastException.class, () -> 
finalD.uncheckedCastTo(DecimalV3Type.createDecimalV3Type(2, 1)));
     }
+
+    @Test
+    void testDecimalV3CastToStringUsesPlainNotation() {
+        DecimalV3Literal literal = new 
DecimalV3Literal(DecimalV3Type.createDecimalV3Type(10, 2),
+                new BigDecimal("1E+3"));
+
+        StringLiteral string = (StringLiteral) 
literal.uncheckedCastTo(StringType.INSTANCE);
+
+        Assertions.assertEquals("1000.00", string.getStringValue());
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteralTest.java
index b1b58520b20..0675a07c607 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/StringLikeLiteralTest.java
@@ -20,6 +20,8 @@ package org.apache.doris.nereids.trees.expressions.literal;
 import org.apache.doris.nereids.exceptions.CastException;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.types.BooleanType;
+import org.apache.doris.nereids.types.DateType;
+import org.apache.doris.nereids.types.DateV2Type;
 import org.apache.doris.nereids.types.DecimalV3Type;
 import org.apache.doris.nereids.types.DoubleType;
 import org.apache.doris.nereids.types.FloatType;
@@ -204,6 +206,17 @@ public class StringLikeLiteralTest {
 
     }
 
+    @Test
+    void testCastMaxDateTimeStringToDateDoesNotRoundTimePart() {
+        StringLiteral literal = new StringLiteral("9999-12-31 
23:59:59.999999");
+
+        DateLiteral date = (DateLiteral) 
literal.uncheckedCastTo(DateType.INSTANCE);
+        DateV2Literal dateV2 = (DateV2Literal) 
literal.uncheckedCastTo(DateV2Type.INSTANCE);
+
+        Assertions.assertEquals("9999-12-31", date.getStringValue());
+        Assertions.assertEquals("9999-12-31", dateV2.getStringValue());
+    }
+
     @Test
     void testUncheckedCastToTimeStampTzRejectsUnstrictNoOffsetInStrictMode() {
         try (MockedStatic<SessionVariable> mockedSessionVariable = 
Mockito.mockStatic(SessionVariable.class)) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2LiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2LiteralTest.java
index 8a79f32b8bc..96775d44bfc 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2LiteralTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/TimeV2LiteralTest.java
@@ -18,6 +18,9 @@
 package org.apache.doris.nereids.trees.expressions.literal;
 
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.types.DateTimeV2Type;
+import org.apache.doris.nereids.types.StringType;
 import org.apache.doris.nereids.types.TimeV2Type;
 
 import org.junit.jupiter.api.Assertions;
@@ -153,4 +156,34 @@ public class TimeV2LiteralTest {
         });
     }
 
+    @Test
+    public void testUncheckedCast() {
+        // to string
+        TimeV2Literal literal = new TimeV2Literal(TimeV2Type.of(0), 
"12:12:12");
+        Expression expression = literal.uncheckedCastTo(StringType.INSTANCE);
+        Assertions.assertInstanceOf(StringLiteral.class, expression);
+        Assertions.assertEquals("12:12:12", ((StringLiteral) 
expression).value);
+
+        literal = new TimeV2Literal(TimeV2Type.of(0), "0");
+        expression = literal.uncheckedCastTo(StringType.INSTANCE);
+        Assertions.assertInstanceOf(StringLiteral.class, expression);
+        Assertions.assertEquals("00:00:00", ((StringLiteral) 
expression).value);
+
+        literal = new TimeV2Literal(TimeV2Type.of(3), "0");
+        expression = literal.uncheckedCastTo(StringType.INSTANCE);
+        Assertions.assertInstanceOf(StringLiteral.class, expression);
+        Assertions.assertEquals("00:00:00.000", ((StringLiteral) 
expression).value);
+    }
+
+    @Test
+    public void testCastNegativeTimeToDateTimeV2KeepsSign() {
+        TimeV2Literal literal = new TimeV2Literal(TimeV2Type.of(0), 
"-00:00:01");
+
+        DateTimeV2Literal dateTime = (DateTimeV2Literal) 
literal.uncheckedCastTo(DateTimeV2Type.of(0));
+
+        Assertions.assertEquals(23, dateTime.getHour());
+        Assertions.assertEquals(59, dateTime.getMinute());
+        Assertions.assertEquals(59, dateTime.getSecond());
+    }
+
 }
diff --git a/regression-test/suites/cast_p0/cast_to_datetime.groovy 
b/regression-test/suites/cast_p0/cast_to_datetime.groovy
index 7a3084bd8e4..59960b54da2 100644
--- a/regression-test/suites/cast_p0/cast_to_datetime.groovy
+++ b/regression-test/suites/cast_p0/cast_to_datetime.groovy
@@ -237,6 +237,11 @@ qt_sql204 """ select cast('9999-12-31 23:59:59.999999 
+00:00' as datetime(6)) ""
 testFoldConst("select cast('9999-12-31 23:59:59.999999 +00:00' as 
datetime(6))")
 qt_sql205 """ select cast('0000-01-01 00:00:00+12:00' as datetime(6)); """
 testFoldConst("select cast('0000-01-01 00:00:00+12:00' as datetime(6));")
+testFoldConst("select cast('9999-12-31 23:59:59.999999' as date), "
+        + "cast('9999-12-31 23:59:59.999999' as datev2)")
+waitUntilSafeExecutionTime("NOT_CROSS_DAY_BOUNDARY", 2)
+testFoldConst("select cast(cast('-00:00:01' as time(0)) as datetime(0)), "
+        + "cast(cast('-00:00:01' as time(0)) as datetimev2(0))")
 
     sql "set debug_skip_fold_constant = false"
 
@@ -258,4 +263,4 @@ testFoldConst("select cast('0000-01-01 00:00:00+12:00' as 
datetime(6));")
         sql "select cast('0000-01-01 00:00:00+12:00' as datetime(6));"
         exception "out of range"
     }
-}
\ No newline at end of file
+}
diff --git 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
index 3e4c20a4954..6f9e4382b29 100644
--- 
a/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
+++ 
b/regression-test/suites/nereids_p0/expression/fold_constant/fe_constant_cast_to_date.groovy
@@ -59,36 +59,36 @@ suite("fe_constant_cast_to_date") {
 
     test {
         sql """select cast("2023-07-16T19.123+08:00" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     qt_sql """select cast("2024/05/01" as date)"""
     test {
         sql """select cast("24012" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("2411 123" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("2024-05-01 01:030:02" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("10000-01-01 00:00:00" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("2024-0131T12:00" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("2024-05-01@00:00" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("20120212051" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast("2024-05-01T00:00XYZ" as date)"""
@@ -116,7 +116,7 @@ suite("fe_constant_cast_to_date") {
     }
     test {
         sql """select cast("2024-05-01T00:00+08:25" as date)"""
-        exception "can't cast to DATETIMEV2"
+        exception "can't cast to DATEV2"
     }
     test {
         sql """select cast(1000 as date)"""
diff --git 
a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_string_arithmatic.groovy
 
b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_string_arithmatic.groovy
index 6e68587f8fe..2d4ccdbddff 100644
--- 
a/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_string_arithmatic.groovy
+++ 
b/regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_string_arithmatic.groovy
@@ -157,6 +157,15 @@ suite("fold_constant_string_arithmatic") {
     testFoldConst("select field('=', '+', '=', '=', 'こ')")
     testFoldConst("select field('==', '+', '=', '==', 'こ')")
     testFoldConst("select field('=', '+', '==', '==', 'こ')")
+    testFoldConst("select field(cast('nan' as float), cast('nan' as float)), "
+            + "field(cast('nan' as double), cast('nan' as double))")
+    testFoldConst("select field(cast(0.0 as float), cast(-0.0 as float)), "
+            + "field(cast(-0.0 as float), cast(0.0 as float)), "
+            + "field(cast(0.0 as double), cast(-0.0 as double)), "
+            + "field(cast(-0.0 as double), cast(0.0 as double))")
+
+    // cast decimalv3 to string
+    testFoldConst("select cast(cast('1E+3' as decimalv3(10, 2)) as string)")
 
     // find_in_set
     testFoldConst("select find_in_set('a', null)")
@@ -2035,4 +2044,3 @@ suite("fold_constant_string_arithmatic") {
     testFoldConst("SELECT SOUNDEX('Wang')")
     testFoldConst("SELECT SOUNDEX(NULL)")
 }
-
diff --git 
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
index b0bd531d374..fc78c9855e8 100644
--- 
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
+++ 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
@@ -350,6 +350,7 @@ suite("test_date_function") {
     check_fold_consistency("str_to_date('2026-01-28 11:32:47.1234567', 
'%Y-%m-%d %T.%f')")
     check_fold_consistency("str_to_date('2026-01-28 11:32:47.123456789', 
'%Y-%m-%d %T.%f')")
     check_fold_consistency("str_to_date('2026-01-28 11:32:47', '%Y-%m-%d %T')")
+    testFoldConst("select str_to_date('2024-01-01', concat('%Y-%m', '-%d'))")
     sql """ truncate table ${tableName} """
     sql """ insert into ${tableName} values ("2020-09-01")  """
     qt_sql """ select str_to_date(test_datetime, "%Y-%m-%d %H:%i:%s") from 
${tableName};"""
diff --git 
a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_func_time.groovy
 
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_func_time.groovy
index e1e8aeb35d8..25df49effd1 100644
--- 
a/regression-test/suites/query_p0/sql_functions/datetime_functions/test_func_time.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/datetime_functions/test_func_time.groovy
@@ -42,6 +42,7 @@ suite("test_func_time") {
     testFoldConst("select time(cast('2025-1-1 00:00:00.4321' as 
datetime(4)));")
     testFoldConst("select time(cast('2025-1-1 00:00:00.54321' as 
datetime(5)));")
     testFoldConst("select time(cast('2025-1-1 00:00:00.654321' as 
datetime(6)));")
+    testFoldConst("select maketime(1, 2, 3.9999995), maketime(1, 2, 
59.9999995);")
 
     def tableName = "test_time_function"
 
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
index 0c4a4441305..734d802695c 100644
--- 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_string_function.groovy
@@ -48,6 +48,17 @@ suite("test_string_function", "arrow_flight_sql") {
     qt_sql "select concat_ws(\"or\", [\"d\", NULL,\"is\"]);"
     qt_sql "select concat_ws(\"or\", [\"d\", \"\",\"is\"]);"
 
+    testFoldConst("SELECT CONCAT_WS(',', ['a', NULL]), CONCAT_WS(',', [NULL, 
'a', NULL]), CONCAT_WS(',', [NULL]);")
+    testFoldConst("SELECT CONCAT_WS(',', 'a', NULL), CONCAT_WS(',', NULL, 'a', 
NULL), CONCAT_WS(',', NULL);")
+    testFoldConst("SELECT RIGHT('😀a', 1), INSTR('😀a', 'a');")
+    testFoldConst("SELECT UPPER('éßi'), LOWER('ÉİA'), INITCAP('ßETA 
İSTANBUL');")
+    testFoldConst("SELECT STRCMP('😀', ''), FIND_IN_SET('', 'a,');")
+    testFoldConst("""SELECT PARSE_URL('http://h/p%20x?q=a+b%20c&k=v#r', 
'PATH'),
+        PARSE_URL('http://h/p%20x?q=a+b%20c&k=v#r', 'QUERY'),
+        EXTRACT_URL_PARAMETER('http://h/p%20x?q=a+b%20c&k=v#r', 'q');""")
+    testFoldConst("SELECT FIELD(CAST('-0.0' AS DOUBLE), CAST('0.0' AS DOUBLE), 
CAST('-0.0' AS DOUBLE));")
+    testFoldConst("SELECT MD5('doris'), MD5('ṭṛì'), MD5SUM('do', 'ris'), 
MD5SUM('ṭ', 'ṛ', 'ì');")
+
     qt_sql "select ends_with(\"Hello doris\", \"doris\");"
     qt_sql "select ends_with(\"Hello doris\", \"Hello\");"
 


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

Reply via email to