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]