Skip to content

Commit e6fd574

Browse files
committed
Make sure FE cast double to varchar generate identical result with BE.
1 parent 8f2669f commit e6fd574

File tree

4 files changed

+203
-0
lines changed

4 files changed

+203
-0
lines changed

fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
8686
import org.apache.doris.nereids.trees.expressions.literal.DateTimeV2Literal;
8787
import org.apache.doris.nereids.trees.expressions.literal.DateV2Literal;
88+
import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
8889
import org.apache.doris.nereids.trees.expressions.literal.Literal;
8990
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
9091
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
@@ -490,6 +491,9 @@ public Expression visitCast(Cast cast, ExpressionRewriteContext context) {
490491
}
491492
Expression child = cast.child();
492493
DataType dataType = cast.getDataType();
494+
if (!safeToCast(cast)) {
495+
return cast;
496+
}
493497
// todo: process other null case
494498
if (child.isNullLiteral()) {
495499
return new NullLiteral(dataType);
@@ -515,6 +519,25 @@ public Expression visitCast(Cast cast, ExpressionRewriteContext context) {
515519
}
516520
}
517521

522+
// Check if the given literal value is safe to cast to the targetType.
523+
// We need to guarantee FE cast result is identical with BE cast result.
524+
// Otherwise, it's not safe.
525+
protected boolean safeToCast(Cast cast) {
526+
if (cast == null || cast.child() == null || cast.getDataType() == null) {
527+
return true;
528+
}
529+
// Check double type.
530+
if (cast.child() instanceof DoubleLiteral && cast.getDataType().isStringLikeType()) {
531+
Double value = ((DoubleLiteral) cast.child()).getValue();
532+
if (value.isInfinite() || value.isNaN()) {
533+
return true;
534+
}
535+
return -1E16 < value && value < 1E16;
536+
}
537+
// Check other types if needed.
538+
return true;
539+
}
540+
518541
@Override
519542
public Expression visitBoundFunction(BoundFunction boundFunction, ExpressionRewriteContext context) {
520543
if (!boundFunction.foldable()) {

fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/DoubleLiteral.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,34 @@ public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
5656
public LiteralExpr toLegacyLiteral() {
5757
return new FloatLiteral(value, Type.DOUBLE);
5858
}
59+
60+
@Override
61+
public String getStringValue() {
62+
Double num = getValue();
63+
if (Double.isNaN(num)) {
64+
return "nan";
65+
} else if (Double.isInfinite(num)) {
66+
return num > 0 ? "inf" : "-inf";
67+
}
68+
69+
// Use %.17g to format the result,replace 'E' with 'e'
70+
String formatted = String.format("%.17g", num).replace('E', 'e');
71+
72+
// Remove trailing .0 in scientific notation.
73+
if (formatted.contains("e")) {
74+
String[] parts = formatted.split("e");
75+
String mantissa = parts[0];
76+
String exponent = parts.length > 1 ? "e" + parts[1] : "";
77+
mantissa = mantissa.replaceAll("\\.?0+$", "");
78+
if (mantissa.isEmpty()) {
79+
mantissa = "0";
80+
}
81+
formatted = mantissa + exponent;
82+
} else if (formatted.contains(".")) {
83+
// remove trailing .0 in fixed-point representation
84+
formatted = formatted.replaceAll("\\.?0+$", "");
85+
}
86+
87+
return formatted;
88+
}
5989
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.doris.nereids.trees.expressions.literal;
19+
20+
import org.junit.jupiter.api.Assertions;
21+
import org.junit.jupiter.api.Test;
22+
23+
class DoubleLiteralTest {
24+
25+
@Test
26+
public void testGetStringValue() {
27+
Assertions.assertEquals("0", new DoubleLiteral(0).getStringValue());
28+
Assertions.assertEquals("0", new DoubleLiteral(0.0).getStringValue());
29+
Assertions.assertEquals("0", new DoubleLiteral(-0).getStringValue());
30+
Assertions.assertEquals("1", new DoubleLiteral(1).getStringValue());
31+
Assertions.assertEquals("1", new DoubleLiteral(1.0).getStringValue());
32+
Assertions.assertEquals("-1", new DoubleLiteral(-1).getStringValue());
33+
Assertions.assertEquals("1.554", new DoubleLiteral(1.554).getStringValue());
34+
Assertions.assertEquals("0.338", new DoubleLiteral(0.338).getStringValue());
35+
Assertions.assertEquals("-1", new DoubleLiteral(-1.0).getStringValue());
36+
Assertions.assertEquals("1e+100", new DoubleLiteral(1e100).getStringValue());
37+
Assertions.assertEquals("1e-100", new DoubleLiteral(1e-100).getStringValue());
38+
Assertions.assertEquals("10000000000000000", new DoubleLiteral(1.0E16).getStringValue());
39+
Assertions.assertEquals("-10000000000000000", new DoubleLiteral(-1.0E16).getStringValue());
40+
Assertions.assertEquals("1e+17", new DoubleLiteral(1.0E17).getStringValue());
41+
Assertions.assertEquals("-1e+17", new DoubleLiteral(-1.0E17).getStringValue());
42+
Assertions.assertEquals("0.0001", new DoubleLiteral(0.0001).getStringValue());
43+
Assertions.assertEquals("1e+308", new DoubleLiteral(1e308).getStringValue());
44+
Assertions.assertEquals("-1e+308", new DoubleLiteral(-1e308).getStringValue());
45+
Assertions.assertEquals("inf", new DoubleLiteral(Double.POSITIVE_INFINITY).getStringValue());
46+
Assertions.assertEquals("-inf", new DoubleLiteral(Double.NEGATIVE_INFINITY).getStringValue());
47+
Assertions.assertEquals("nan", new DoubleLiteral(Double.NaN).getStringValue());
48+
}
49+
}

regression-test/suites/nereids_p0/expression/fold_constant/fold_constant_string_arithmatic.groovy

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ suite("fold_constant_string_arithmatic") {
395395
testFoldConst("select left('上海天津北京杭州', 5)")
396396
testFoldConst("select left('上海天津北京杭州', -5)")
397397
testFoldConst("select left('上海天津北京杭州', 0)")
398+
testFoldConst("select left('20250409'-10000, 6)")
399+
398400

399401
// length
400402
testFoldConst("select length('你')")
@@ -1724,5 +1726,104 @@ suite("fold_constant_string_arithmatic") {
17241726
testFoldConst("select split_by_string('a😁a😁a', '')")
17251727
testFoldConst("select character_length('a😁a😁a')")
17261728
testFoldConst("select replace_empty('a😁a😁a', '', '2')")
1729+
1730+
// cast double to string like
1731+
testFoldConst("select cast(cast(0 as double) as varchar(65533))")
1732+
testFoldConst("select cast(cast(0 as double) as string)")
1733+
testFoldConst("select cast(cast(0.0 as double) as varchar(65533))")
1734+
testFoldConst("select cast(cast(0.0 as double) as string)")
1735+
testFoldConst("select cast(cast(1 as double) as varchar(65533))")
1736+
testFoldConst("select cast(cast(1 as double) as string)")
1737+
testFoldConst("select cast(cast(1.0 as double) as varchar(65533))")
1738+
testFoldConst("select cast(cast(1.0 as double) as string)")
1739+
testFoldConst("select cast(cast(0.1 as double) as varchar(65533))")
1740+
testFoldConst("select cast(cast(0.1 as double) as string)")
1741+
testFoldConst("select cast(cast(1.1 as double) as varchar(65533))")
1742+
testFoldConst("select cast(cast(1.1 as double) as string)")
1743+
testFoldConst("select cast(cast(100000 as double) as string)")
1744+
testFoldConst("select cast(cast(1000000000000000 as double) as string)")
1745+
testFoldConst("select cast(cast(10000000000000000 as double) as string)")
1746+
testFoldConst("select cast(cast(100000000000000000 as double) as string)")
1747+
testFoldConst("select cast(cast(1000000000000000000 as double) as string)")
1748+
testFoldConst("select cast(cast(1.888 as double) as string)")
1749+
testFoldConst("select cast(cast(1.888777888777888 as double) as string)")
1750+
testFoldConst("select cast(cast(1.8887778887778887 as double) as string)")
1751+
testFoldConst("select cast(cast(1.888777888777888777 as double) as string)")
1752+
testFoldConst("select cast(cast(55556666.888777888777888777 as double) as string)")
1753+
testFoldConst("select cast(cast(555566667777.888777888777888777 as double) as string)")
1754+
testFoldConst("select cast(cast(5555666677778888.888777888777888777 as double) as string)")
1755+
testFoldConst("select cast(cast(55556666777788889.888777888777888777 as double) as string)")
1756+
testFoldConst("select cast(cast(55556666777788889999.888777888777888777 as double) as string)")
1757+
testFoldConst("select cast(cast(0.001 as double) as string)")
1758+
testFoldConst("select cast(cast(0.0001 as double) as string)")
1759+
testFoldConst("select cast(cast(0.00001 as double) as string)")
1760+
testFoldConst("select cast(cast(0.000001 as double) as string)")
1761+
testFoldConst("select cast(cast(0.0000001 as double) as string)")
1762+
testFoldConst("select cast(cast(0.00000001 as double) as string)")
1763+
testFoldConst("select cast(cast(0.00000001 as double) as string)")
1764+
testFoldConst("select cast(cast(0.000000000000001 as double) as string)")
1765+
testFoldConst("select cast(cast(0.0000000000000001 as double) as string)")
1766+
testFoldConst("select cast(cast(0.00000000000000001 as double) as string)")
1767+
testFoldConst("select cast(cast(0.000000000000000001 as double) as string)")
1768+
testFoldConst("select cast(cast(0.0000000000000000001 as double) as string)")
1769+
testFoldConst("select cast(cast(1e308 as double) as string)")
1770+
testFoldConst("select cast(cast(1e309 as double) as string)")
1771+
testFoldConst("select cast(cast(1e-308 as double) as string)")
1772+
testFoldConst("select cast(cast(1e-309 as double) as string)")
1773+
testFoldConst("select cast(cast(10000000000000001 as double) as string)")
1774+
testFoldConst("select cast(cast(10000000000000010 as double) as string)")
1775+
testFoldConst("select cast(cast(10000000000000100 as double) as string)")
1776+
1777+
testFoldConst("select cast(cast(-0 as double) as varchar(65533))")
1778+
testFoldConst("select cast(cast(-0 as double) as string)")
1779+
testFoldConst("select cast(cast(-0.0 as double) as varchar(65533))")
1780+
testFoldConst("select cast(cast(-0.0 as double) as string)")
1781+
testFoldConst("select cast(cast(-1 as double) as varchar(65533))")
1782+
testFoldConst("select cast(cast(-1 as double) as string)")
1783+
testFoldConst("select cast(cast(-1.0 as double) as varchar(65533))")
1784+
testFoldConst("select cast(cast(-1.0 as double) as string)")
1785+
testFoldConst("select cast(cast(-0.1 as double) as varchar(65533))")
1786+
testFoldConst("select cast(cast(-0.1 as double) as string)")
1787+
testFoldConst("select cast(cast(-1.1 as double) as varchar(65533))")
1788+
testFoldConst("select cast(cast(-1.1 as double) as string)")
1789+
testFoldConst("select cast(cast(-100000 as double) as string)")
1790+
testFoldConst("select cast(cast(-1000000000000000 as double) as string)")
1791+
testFoldConst("select cast(cast(-10000000000000000 as double) as string)")
1792+
testFoldConst("select cast(cast(-100000000000000000 as double) as string)")
1793+
testFoldConst("select cast(cast(-1000000000000000000 as double) as string)")
1794+
testFoldConst("select cast(cast(-1.888 as double) as string)")
1795+
testFoldConst("select cast(cast(-1.888777888777888 as double) as string)")
1796+
testFoldConst("select cast(cast(-1.8887778887778887 as double) as string)")
1797+
testFoldConst("select cast(cast(-1.888777888777888777 as double) as string)")
1798+
testFoldConst("select cast(cast(-55556666.888777888777888777 as double) as string)")
1799+
testFoldConst("select cast(cast(-555566667777.888777888777888777 as double) as string)")
1800+
testFoldConst("select cast(cast(-5555666677778888.888777888777888777 as double) as string)")
1801+
testFoldConst("select cast(cast(-55556666777788889.888777888777888777 as double) as string)")
1802+
testFoldConst("select cast(cast(-55556666777788889999.888777888777888777 as double) as string)")
1803+
testFoldConst("select cast(cast(-0.001 as double) as string)")
1804+
testFoldConst("select cast(cast(-0.0001 as double) as string)")
1805+
testFoldConst("select cast(cast(-0.00001 as double) as string)")
1806+
testFoldConst("select cast(cast(-0.000001 as double) as string)")
1807+
testFoldConst("select cast(cast(-0.0000001 as double) as string)")
1808+
testFoldConst("select cast(cast(-0.00000001 as double) as string)")
1809+
testFoldConst("select cast(cast(-0.00000001 as double) as string)")
1810+
testFoldConst("select cast(cast(-0.000000000000001 as double) as string)")
1811+
testFoldConst("select cast(cast(-0.0000000000000001 as double) as string)")
1812+
testFoldConst("select cast(cast(-0.00000000000000001 as double) as string)")
1813+
testFoldConst("select cast(cast(-0.000000000000000001 as double) as string)")
1814+
testFoldConst("select cast(cast(-0.0000000000000000001 as double) as string)")
1815+
testFoldConst("select cast(cast(-1e308 as double) as string)")
1816+
testFoldConst("select cast(cast(-1e309 as double) as string)")
1817+
testFoldConst("select cast(cast(-1e-308 as double) as string)")
1818+
testFoldConst("select cast(cast(-1e-309 as double) as string)")
1819+
testFoldConst("select cast(cast(-10000000000000001 as double) as string)")
1820+
testFoldConst("select cast(cast(-10000000000000010 as double) as string)")
1821+
testFoldConst("select cast(cast(-10000000000000100 as double) as string)")
1822+
testFoldConst("select cast(cast('nan' as double) as string)")
1823+
testFoldConst("select cast(cast('NaN' as double) as string)")
1824+
testFoldConst("select cast(cast('inf' as double) as string)")
1825+
testFoldConst("select cast(cast('-inf' as double) as string)")
1826+
testFoldConst("select cast(cast('Infinity' as double) as string)")
1827+
testFoldConst("select cast(cast('-Infinity' as double) as string)")
17271828
}
17281829

0 commit comments

Comments
 (0)