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

lijibing 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 27c6dc725fd [fix](constant fold)Make sure FE cast double to varchar 
generate identical result with BE. (#50425)
27c6dc725fd is described below

commit 27c6dc725fd44b45666182f3f037b4c7990de3c7
Author: James <lijib...@selectdb.com>
AuthorDate: Tue Apr 29 17:43:06 2025 +0800

    [fix](constant fold)Make sure FE cast double to varchar generate identical 
result with BE. (#50425)
    
    ### What problem does this PR solve?
    
    BE side use fmt::format_to to cast double to string, FE use
    Double.toString(), we need to make sure FE and BE generate identical
    cast result:
    e.g. 1E10 -> 1e+10, NaN-> nan,  Infinity -> inf
    Besides, when the double literal value is larger than 1E16 or less than
    -1E16, skip FE side constant folding because we don't know the exact
    behavior of fmt::format_to, so skip FE side constant folding and leave
    the cast operation to BE.
---
 .../expression/rules/FoldConstantRuleOnFE.java     | 23 +++++
 .../trees/expressions/literal/DoubleLiteral.java   | 30 +++++++
 .../expressions/literal/DoubleLiteralTest.java     | 49 +++++++++++
 .../fold_constant_string_arithmatic.groovy         | 98 ++++++++++++++++++++++
 4 files changed, 200 insertions(+)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
index 058fe9fa7fd..604fb007a03 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
@@ -85,6 +85,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
 import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
+import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
@@ -490,6 +491,9 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
         }
         Expression child = cast.child();
         DataType dataType = cast.getDataType();
+        if (!safeToCast(cast)) {
+            return cast;
+        }
         // todo: process other null case
         if (child.isNullLiteral()) {
             return new NullLiteral(dataType);
@@ -515,6 +519,25 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
         }
     }
 
+    // Check if the given literal value is safe to cast to the targetType.
+    // We need to guarantee FE cast result is identical with BE cast result.
+    // Otherwise, it's not safe.
+    protected boolean safeToCast(Cast cast) {
+        if (cast == null || cast.child() == null || cast.getDataType() == 
null) {
+            return true;
+        }
+        // Check double type.
+        if (cast.child() instanceof DoubleLiteral && 
cast.getDataType().isStringLikeType()) {
+            Double value = ((DoubleLiteral) cast.child()).getValue();
+            if (value.isInfinite() || value.isNaN()) {
+                return true;
+            }
+            return -1E16 < value && value < 1E16;
+        }
+        // Check other types if needed.
+        return true;
+    }
+
     @Override
     public Expression visitBoundFunction(BoundFunction boundFunction, 
ExpressionRewriteContext context) {
         if (!boundFunction.foldable()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java
index 939d08d1ec0..8ebdb5bd9d3 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java
@@ -56,4 +56,34 @@ public class DoubleLiteral extends FractionalLiteral {
     public LiteralExpr toLegacyLiteral() {
         return new FloatLiteral(value, Type.DOUBLE);
     }
+
+    @Override
+    public String getStringValue() {
+        Double num = getValue();
+        if (Double.isNaN(num)) {
+            return "nan";
+        } else if (Double.isInfinite(num)) {
+            return num > 0 ? "inf" : "-inf";
+        }
+
+        // Use %.17g to format the result,replace 'E' with 'e'
+        String formatted = String.format("%.17g", num).replace('E', 'e');
+
+        // Remove trailing .0 in scientific notation.
+        if (formatted.contains("e")) {
+            String[] parts = formatted.split("e");
+            String mantissa = parts[0];
+            String exponent = parts.length > 1 ? "e" + parts[1] : "";
+            mantissa = mantissa.replaceAll("\\.?0+$", "");
+            if (mantissa.isEmpty()) {
+                mantissa = "0";
+            }
+            formatted = mantissa + exponent;
+        } else if (formatted.contains(".")) {
+            // remove trailing .0 in fixed-point representation
+            formatted = formatted.replaceAll("\\.?0+$", "");
+        }
+
+        return formatted;
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteralTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteralTest.java
new file mode 100644
index 00000000000..409815d5829
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteralTest.java
@@ -0,0 +1,49 @@
+// 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.literal;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class DoubleLiteralTest {
+
+    @Test
+    public void testGetStringValue() {
+        Assertions.assertEquals("0", new DoubleLiteral(0).getStringValue());
+        Assertions.assertEquals("0", new DoubleLiteral(0.0).getStringValue());
+        Assertions.assertEquals("0", new DoubleLiteral(-0).getStringValue());
+        Assertions.assertEquals("1", new DoubleLiteral(1).getStringValue());
+        Assertions.assertEquals("1", new DoubleLiteral(1.0).getStringValue());
+        Assertions.assertEquals("-1", new DoubleLiteral(-1).getStringValue());
+        Assertions.assertEquals("1.554", new 
DoubleLiteral(1.554).getStringValue());
+        Assertions.assertEquals("0.338", new 
DoubleLiteral(0.338).getStringValue());
+        Assertions.assertEquals("-1", new 
DoubleLiteral(-1.0).getStringValue());
+        Assertions.assertEquals("1e+100", new 
DoubleLiteral(1e100).getStringValue());
+        Assertions.assertEquals("1e-100", new 
DoubleLiteral(1e-100).getStringValue());
+        Assertions.assertEquals("10000000000000000", new 
DoubleLiteral(1.0E16).getStringValue());
+        Assertions.assertEquals("-10000000000000000", new 
DoubleLiteral(-1.0E16).getStringValue());
+        Assertions.assertEquals("1e+17", new 
DoubleLiteral(1.0E17).getStringValue());
+        Assertions.assertEquals("-1e+17", new 
DoubleLiteral(-1.0E17).getStringValue());
+        Assertions.assertEquals("0.0001", new 
DoubleLiteral(0.0001).getStringValue());
+        Assertions.assertEquals("1e+308", new 
DoubleLiteral(1e308).getStringValue());
+        Assertions.assertEquals("-1e+308", new 
DoubleLiteral(-1e308).getStringValue());
+        Assertions.assertEquals("inf", new 
DoubleLiteral(Double.POSITIVE_INFINITY).getStringValue());
+        Assertions.assertEquals("-inf", new 
DoubleLiteral(Double.NEGATIVE_INFINITY).getStringValue());
+        Assertions.assertEquals("nan", new 
DoubleLiteral(Double.NaN).getStringValue());
+    }
+}
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 039cb12bc0d..bcac849c433 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
@@ -395,6 +395,8 @@ suite("fold_constant_string_arithmatic") {
     testFoldConst("select left('上海天津北京杭州', 5)")
     testFoldConst("select left('上海天津北京杭州', -5)")
     testFoldConst("select left('上海天津北京杭州', 0)")
+    testFoldConst("select left('20250409'-10000, 6)")
+
 
     // length
     testFoldConst("select length('你')")
@@ -1724,5 +1726,101 @@ suite("fold_constant_string_arithmatic") {
     testFoldConst("select split_by_string('a😁a😁a', '')")
     testFoldConst("select character_length('a😁a😁a')")
     testFoldConst("select replace_empty('a😁a😁a', '', '2')")
+
+    // cast double to string like
+    testFoldConst("select cast(cast(0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(0 as double) as string)")
+    testFoldConst("select cast(cast(0.0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(0.0 as double) as string)")
+    testFoldConst("select cast(cast(1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(1 as double) as string)")
+    testFoldConst("select cast(cast(1.0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(1.0 as double) as string)")
+    testFoldConst("select cast(cast(0.1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(0.1 as double) as string)")
+    testFoldConst("select cast(cast(1.1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(1.1 as double) as string)")
+    testFoldConst("select cast(cast(100000 as double) as string)")
+    testFoldConst("select cast(cast(1000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(10000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(100000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(1000000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(1.888 as double) as string)")
+    testFoldConst("select cast(cast(1.888777888777888 as double) as string)")
+    testFoldConst("select cast(cast(1.8887778887778887 as double) as string)")
+    testFoldConst("select cast(cast(1.888777888777888777 as double) as 
string)")
+    testFoldConst("select cast(cast(55556666.888777888777888777 as double) as 
string)")
+    testFoldConst("select cast(cast(555566667777.888777888777888777 as double) 
as string)")
+    testFoldConst("select cast(cast(5555666677778888.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(55556666777788889.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(55556666777788889999.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(0.001 as double) as string)")
+    testFoldConst("select cast(cast(0.0001 as double) as string)")
+    testFoldConst("select cast(cast(0.00001 as double) as string)")
+    testFoldConst("select cast(cast(0.000001 as double) as string)")
+    testFoldConst("select cast(cast(0.0000001 as double) as string)")
+    testFoldConst("select cast(cast(0.00000001 as double) as string)")
+    testFoldConst("select cast(cast(0.00000001 as double) as string)")
+    testFoldConst("select cast(cast(0.000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(0.0000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(0.00000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(0.000000000000000001 as double) as 
string)")
+    testFoldConst("select cast(cast(0.0000000000000000001 as double) as 
string)")
+    testFoldConst("select cast(cast(1e308 as double) as string)")
+    testFoldConst("select cast(cast(1e309 as double) as string)")
+    testFoldConst("select cast(cast(1e-308 as double) as string)")
+    testFoldConst("select cast(cast(1e-309 as double) as string)")
+    testFoldConst("select cast(cast(10000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(10000000000000010 as double) as string)")
+    testFoldConst("select cast(cast(10000000000000100 as double) as string)")
+
+    testFoldConst("select cast(cast(-0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-0 as double) as string)")
+    testFoldConst("select cast(cast(-0.0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-0.0 as double) as string)")
+    testFoldConst("select cast(cast(-1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-1 as double) as string)")
+    testFoldConst("select cast(cast(-1.0 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-1.0 as double) as string)")
+    testFoldConst("select cast(cast(-0.1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-0.1 as double) as string)")
+    testFoldConst("select cast(cast(-1.1 as double) as varchar(65533))")
+    testFoldConst("select cast(cast(-1.1 as double) as string)")
+    testFoldConst("select cast(cast(-100000 as double) as string)")
+    testFoldConst("select cast(cast(-1000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(-10000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(-100000000000000000 as double) as string)")
+    testFoldConst("select cast(cast(-1000000000000000000 as double) as 
string)")
+    testFoldConst("select cast(cast(-1.888 as double) as string)")
+    testFoldConst("select cast(cast(-1.888777888777888 as double) as string)")
+    testFoldConst("select cast(cast(-1.8887778887778887 as double) as string)")
+    testFoldConst("select cast(cast(-1.888777888777888777 as double) as 
string)")
+    testFoldConst("select cast(cast(-55556666.888777888777888777 as double) as 
string)")
+    testFoldConst("select cast(cast(-555566667777.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(-5555666677778888.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(-55556666777788889.888777888777888777 as 
double) as string)")
+    testFoldConst("select cast(cast(-55556666777788889999.888777888777888777 
as double) as string)")
+    testFoldConst("select cast(cast(-0.001 as double) as string)")
+    testFoldConst("select cast(cast(-0.0001 as double) as string)")
+    testFoldConst("select cast(cast(-0.00001 as double) as string)")
+    testFoldConst("select cast(cast(-0.000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.0000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.00000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.00000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.0000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(-0.00000000000000001 as double) as 
string)")
+    testFoldConst("select cast(cast(-0.000000000000000001 as double) as 
string)")
+    testFoldConst("select cast(cast(-0.0000000000000000001 as double) as 
string)")
+    testFoldConst("select cast(cast(-1e308 as double) as string)")
+    testFoldConst("select cast(cast(-1e309 as double) as string)")
+    testFoldConst("select cast(cast(-1e-308 as double) as string)")
+    testFoldConst("select cast(cast(-1e-309 as double) as string)")
+    testFoldConst("select cast(cast(-10000000000000001 as double) as string)")
+    testFoldConst("select cast(cast(-10000000000000010 as double) as string)")
+    testFoldConst("select cast(cast(-10000000000000100 as double) as string)")
+    testFoldConst("select cast(cast('nan' as double) as string)")
+    testFoldConst("select cast(cast('inf' as double) as string)")
+    testFoldConst("select cast(cast('-inf' as double) as string)")
 }
 


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

Reply via email to