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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]