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

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


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 54785062233 branch-4.2: [fix](fe) Narrow decimal256 operands of 
add/subtract/mod when enable_decimal256 is off #69056 (#70081)
54785062233 is described below

commit 54785062233b0c20aac29d73dca963bc437b8028
Author: morrySnow <[email protected]>
AuthorDate: Wed Nov 4 08:24:34 2026 +0800

    branch-4.2: [fix](fe) Narrow decimal256 operands of add/subtract/mod when 
enable_decimal256 is off #69056 (#70081)
    
    picked from master #69056
    
    Co-authored-by: Jerry Hu <[email protected]>
---
 .../functions/ComputePrecisionForSum.java          |  5 +-
 .../apache/doris/nereids/types/DecimalV3Type.java  | 21 ++++++
 .../doris/nereids/util/TypeCoercionUtils.java      | 18 +++--
 .../expressions/ViewDecimal256OperandTest.java     | 82 ++++++++++++++++++++++
 .../doris/nereids/util/TypeCoercionUtilsTest.java  | 53 ++++++++++++++
 5 files changed, 171 insertions(+), 8 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputePrecisionForSum.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputePrecisionForSum.java
index 052fddabeb7..be61d4d674a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputePrecisionForSum.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ComputePrecisionForSum.java
@@ -35,10 +35,7 @@ public interface ComputePrecisionForSum extends 
ComputePrecision {
                 enableDecimal256 = 
connectContext.getSessionVariable().isEnableDecimal256();
             }
             // child type decimalV3Type>38, but in 128 mode,need cast child 
type to decimal128
-            if (decimalV3Type.getPrecision() > 
DecimalV3Type.MAX_DECIMAL128_PRECISION && !enableDecimal256) {
-                int scale = Math.min(DecimalV3Type.MAX_DECIMAL128_PRECISION, 
decimalV3Type.getScale());
-                decimalV3Type = DecimalV3Type.createDecimalV3Type(38, scale);
-            }
+            decimalV3Type = 
DecimalV3Type.narrowToDecimal128IfDisabled(decimalV3Type);
 
             return signature.withArgumentType(0, decimalV3Type)
                     .withReturnType(DecimalV3Type.createDecimalV3Type(
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java
index 2a04051ffe8..f294eab23f1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DecimalV3Type.java
@@ -185,6 +185,27 @@ public class DecimalV3Type extends FractionalType {
         return new DecimalV3Type(precision, scale);
     }
 
+    /**
+     * A DecimalV3 wider than decimal128 can reach the planner while 
enable_decimal256 is off, e.g. a
+     * decimal(76, x) column or a view output analyzed with the variable on. 
In that case narrow it to
+     * decimal128 and keep as much scale as possible, so that the decimal128 
rules never see an integral
+     * part wider than 38 digits.
+     */
+    public static DecimalV3Type narrowToDecimal128IfDisabled(DecimalV3Type 
type) {
+        if (type.getPrecision() <= MAX_DECIMAL128_PRECISION) {
+            return type;
+        }
+        boolean enableDecimal256 = false;
+        ConnectContext connectContext = ConnectContext.get();
+        if (connectContext != null) {
+            enableDecimal256 = 
connectContext.getSessionVariable().isEnableDecimal256();
+        }
+        if (enableDecimal256) {
+            return type;
+        }
+        return createDecimalV3Type(MAX_DECIMAL128_PRECISION, 
Math.min(MAX_DECIMAL128_PRECISION, type.getScale()));
+    }
+
     public static DataType widerDecimalV3Type(DecimalV3Type left, 
DecimalV3Type right, boolean overflowToDouble) {
         return widerDecimalV3Type(left.getPrecision(), right.getPrecision(),
                 left.getScale(), right.getScale(), overflowToDouble);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
index 81c9708a818..3f962d19b39 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java
@@ -2551,12 +2551,15 @@ public class TypeCoercionUtils {
         DecimalV3Type dt2 =
                 
DecimalV3Type.forType(TypeCoercionUtils.getNumResultType(right.getDataType()));
 
-        // check return type whether overflow, if true, turn to double
-        DecimalV3Type retType = binaryArithmetic.getDataTypeForDecimalV3(dt1, 
dt2);
-
-        // add, subtract and mod should cast children to exactly same type as 
return type
+        // add, subtract and mod should cast children to exactly same type as 
return type.
+        // That return type is capped by enable_decimal256 and keeps the 
widest integral part of
+        // the operands, so an operand whose integral part alone exceeds the 
decimal128 cap
+        // (a decimal256 column, or a view output analyzed with the variable 
on) is narrowed first.
         if (binaryArithmetic instanceof Add || binaryArithmetic instanceof 
Subtract
                 || binaryArithmetic instanceof Mod) {
+            dt1 = narrowIfIntegralPartExceedsDecimal128(dt1);
+            dt2 = narrowIfIntegralPartExceedsDecimal128(dt2);
+            DecimalV3Type retType = 
binaryArithmetic.getDataTypeForDecimalV3(dt1, dt2);
             return castChildren(binaryArithmetic, left, right, retType);
         }
         // multiply do not need to cast children to same type
@@ -2564,6 +2567,13 @@ public class TypeCoercionUtils {
                 castIfNotSameType(right, dt2));
     }
 
+    private static DecimalV3Type 
narrowIfIntegralPartExceedsDecimal128(DecimalV3Type type) {
+        if (type.getRange() <= DecimalV3Type.MAX_DECIMAL128_PRECISION) {
+            return type;
+        }
+        return DecimalV3Type.narrowToDecimal128IfDisabled(type);
+    }
+
     /**
      * get min and max value of a data type
      *
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewDecimal256OperandTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewDecimal256OperandTest.java
new file mode 100644
index 00000000000..68f1fc85996
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/ViewDecimal256OperandTest.java
@@ -0,0 +1,82 @@
+// 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;
+
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.types.DecimalV3Type;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Arithmetic on a decimal wider than decimal128 (a decimal256 column, or the 
output of a view created with
+ * enable_decimal256 on) while enable_decimal256 is off.
+ */
+public class ViewDecimal256OperandTest extends TestWithFeService {
+
+    private static final DecimalV3Type DECIMAL128 = 
DecimalV3Type.createDecimalV3Type(38, 10);
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("view_decimal256_operand");
+        connectContext.setDatabase("view_decimal256_operand");
+        connectContext.getSessionVariable().enableDecimal256 = true;
+        createTables(
+                "CREATE TABLE IF NOT EXISTS t_view_dec256 (\n"
+                        + "    k int,\n"
+                        + "    d decimal(38, 10),\n"
+                        + "    d256 decimal(76, 10),\n"
+                        + "    m map<string, decimal(38, 10)>\n"
+                        + ")\n"
+                        + "DUPLICATE KEY(k)\n"
+                        + "DISTRIBUTED BY HASH(k) BUCKETS 1\n"
+                        + "PROPERTIES (\"replication_num\" = \"1\")\n"
+        );
+        createView("CREATE VIEW v_dec256_sum AS SELECT sum(d) AS s FROM 
t_view_dec256");
+        connectContext.getSessionVariable().enableDecimal256 = false;
+    }
+
+    private DataType outputType(String sql) {
+        return 
PlanChecker.from(connectContext).analyze(sql).getPlan().getOutput().get(0).getDataType();
+    }
+
+    @Test
+    public void testArithmeticOnWideOperandInDecimal128Mode() {
+        Assertions.assertEquals(DECIMAL128, outputType(
+                "SELECT s - cast(1 as decimal(38, 10)) FROM v_dec256_sum"));
+        Assertions.assertEquals(DECIMAL128, outputType("SELECT d256 + 1 FROM 
t_view_dec256"));
+        Assertions.assertEquals(DECIMAL128, outputType(
+                "SELECT d256 % cast(1 as decimal(38, 10)) FROM 
t_view_dec256"));
+        // the view output itself keeps the type analyzed when the view was 
created
+        Assertions.assertEquals(DecimalV3Type.createDecimalV3TypeNoCheck(76, 
10),
+                outputType("SELECT s FROM v_dec256_sum"));
+    }
+
+    @Test
+    public void testArithmeticOnWideOperandInDecimal256Mode() {
+        connectContext.getSessionVariable().enableDecimal256 = true;
+        try {
+            Assertions.assertEquals(DecimalV3Type.createDecimalV3Type(76, 10),
+                    outputType("SELECT d256 + 1 FROM t_view_dec256"));
+        } finally {
+            connectContext.getSessionVariable().enableDecimal256 = false;
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java
index 1f8306881a4..0e4da57781b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/TypeCoercionUtilsTest.java
@@ -22,6 +22,7 @@ import org.apache.doris.common.Config;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer;
 import org.apache.doris.nereids.trees.expressions.Add;
+import org.apache.doris.nereids.trees.expressions.BinaryArithmetic;
 import org.apache.doris.nereids.trees.expressions.CaseWhen;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
@@ -33,6 +34,7 @@ import 
org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.InPredicate;
 import org.apache.doris.nereids.trees.expressions.LessThan;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+import org.apache.doris.nereids.trees.expressions.Mod;
 import org.apache.doris.nereids.trees.expressions.Multiply;
 import org.apache.doris.nereids.trees.expressions.Not;
 import org.apache.doris.nereids.trees.expressions.NullSafeEqual;
@@ -451,6 +453,57 @@ public class TypeCoercionUtilsTest {
         }
     }
 
+    @Test
+    public void testDecimalArithmeticWithWiderOperandInDecimal128Mode() {
+        ConnectContext previousContext = ConnectContext.get();
+        ConnectContext connectContext = new ConnectContext();
+        connectContext.getSessionVariable().enableDecimal256 = false;
+        connectContext.setThreadLocalInfo();
+        try {
+            // a decimal256 column, or a view output analyzed with 
enable_decimal256 on
+            SlotReference wide = new SlotReference("d256", 
DecimalV3Type.createDecimalV3TypeNoCheck(76, 10));
+            DecimalV3Literal narrow = new 
DecimalV3Literal(DecimalV3Type.createDecimalV3Type(38, 10),
+                    new BigDecimal("1.0000000000"));
+            DecimalV3Type expected = DecimalV3Type.createDecimalV3Type(38, 10);
+
+            List<BinaryArithmetic> arithmetics = ImmutableList.of(new 
Add(wide, narrow), new Subtract(wide, narrow),
+                    new Mod(wide, narrow), new Add(narrow, wide), new 
Add(wide, new IntegerLiteral(1)));
+            for (BinaryArithmetic arithmetic : arithmetics) {
+                Expression coerced = 
TypeCoercionUtils.processBinaryArithmetic(arithmetic);
+                Assertions.assertEquals(expected, coerced.getDataType(), 
arithmetic.toSql());
+                Assertions.assertEquals(expected, 
coerced.child(0).getDataType(), arithmetic.toSql());
+                Assertions.assertEquals(expected, 
coerced.child(1).getDataType(), arithmetic.toSql());
+            }
+            Expression add = TypeCoercionUtils.processBinaryArithmetic(new 
Add(wide, narrow));
+            Assertions.assertInstanceOf(Cast.class, add.child(0));
+            Assertions.assertEquals(wide, add.child(0).child(0));
+
+            // an operand whose integral part fits in decimal128 keeps it, 
only the scale is reduced
+            SlotReference wideScale = new SlotReference("multi", 
DecimalV3Type.createDecimalV3TypeNoCheck(41, 11));
+            Expression addWideScale = 
TypeCoercionUtils.processBinaryArithmetic(
+                    new Add(wideScale, new IntegerLiteral(1)));
+            Assertions.assertEquals(DecimalV3Type.createDecimalV3Type(38, 8), 
addWideScale.getDataType());
+            Assertions.assertEquals(new Cast(wideScale, 
DecimalV3Type.createDecimalV3Type(38, 8)),
+                    addWideScale.child(0));
+
+            // multiply does not cast its children to the return type, so the 
wide operand is kept
+            Expression multiply = 
TypeCoercionUtils.processBinaryArithmetic(new Multiply(wide, narrow));
+            Assertions.assertEquals(wide, multiply.child(0));
+            Assertions.assertEquals(DecimalV3Type.createDecimalV3Type(38, 6), 
multiply.getDataType());
+
+            // decimal256 mode keeps the wide result type
+            connectContext.getSessionVariable().enableDecimal256 = true;
+            Expression add256 = TypeCoercionUtils.processBinaryArithmetic(new 
Add(wide, narrow));
+            Assertions.assertEquals(DecimalV3Type.createDecimalV3Type(76, 10), 
add256.getDataType());
+            Assertions.assertEquals(wide, add256.child(0));
+        } finally {
+            ConnectContext.remove();
+            if (previousContext != null) {
+                previousContext.setThreadLocalInfo();
+            }
+        }
+    }
+
     private void assertTimestampNsJoinComparisonUsesStrictCast(DataType 
temporalType, boolean temporalOnLeft) {
         SlotReference timestampNs = new SlotReference("ts", 
TimeStampNsType.INSTANCE);
         SlotReference temporal = new SlotReference("temporal", temporalType);


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

Reply via email to