branch-2.1: [fix](constant fold)Make sure FE cast double to varchar generate identical result with BE. #50425 (#50548)

Cherry-picked from #50425

Co-authored-by: James <lijibing@selectdb.com>
This commit is contained in:
github-actions[bot]
2025-04-30 09:17:47 +08:00
committed by GitHub
parent dfc1dbfb59
commit 98be2cedcf
4 changed files with 200 additions and 0 deletions

View File

@ -78,6 +78,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;
@ -448,6 +449,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);
@ -473,6 +477,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()) {

View File

@ -49,4 +49,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;
}
}

View File

@ -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());
}
}