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

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


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new 06ef8b64fe2 branch-3.1: [Fix](cast) Fix lost of cast for nested type 
with precision #54824 (#55261)
06ef8b64fe2 is described below

commit 06ef8b64fe210176a48ae00a2f0d4f7b5b2bb56a
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Aug 26 16:07:51 2025 +0800

    branch-3.1: [Fix](cast) Fix lost of cast for nested type with precision 
#54824 (#55261)
    
    Cherry-picked from #54824
    
    Co-authored-by: zclllyybb <[email protected]>
---
 .../main/java/org/apache/doris/catalog/Type.java   |  38 ++++-
 .../java/org/apache/doris/catalog/TypeTest.java    | 190 +++++++++++++++++++++
 2 files changed, 226 insertions(+), 2 deletions(-)

diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java 
b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
index 83ce0fb654c..6c0af7951e5 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java
@@ -2259,8 +2259,42 @@ public abstract class Type {
     public static boolean matchExactType(Type type1, Type type2, boolean 
ignorePrecision) {
         // we should make type decide to match other for itself to impl 
matchesType instead of switch case types
         if (type1.matchesType(type2)) {
-            if 
(PrimitiveType.typeWithPrecision.contains(type2.getPrimitiveType())) {
-                // For types which has precision and scale, we also need to 
check quality between precisions and scales
+            if (type1.isArrayType()) {
+                return matchExactType(((ArrayType) type1).getItemType(), 
((ArrayType) type2).getItemType(),
+                        ignorePrecision);
+            } else if (type1.isMapType()) {
+                MapType map1 = (MapType) type1;
+                MapType map2 = (MapType) type2;
+                return matchExactType(map1.getKeyType(), map2.getKeyType(), 
ignorePrecision)
+                        && matchExactType(map1.getValueType(), 
map2.getValueType(), ignorePrecision);
+            } else if (type1.isStructType()) {
+                StructType struct1 = (StructType) type1;
+                StructType struct2 = (StructType) type2;
+                if (struct1.getFields().size() != struct2.getFields().size()) {
+                    return false;
+                }
+                for (int i = 0; i < struct1.getFields().size(); i++) {
+                    if (!matchExactType(struct1.getFields().get(i).getType(),
+                            struct2.getFields().get(i).getType(), 
ignorePrecision)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else if (type1.isVariantType()) {
+                ArrayList<VariantField> fields1 = ((VariantType) 
type1).getPredefinedFields();
+                ArrayList<VariantField> fields2 = ((VariantType) 
type2).getPredefinedFields();
+                if (fields1.size() != fields2.size()) {
+                    return false;
+                }
+                for (int i = 0; i < fields1.size(); i++) {
+                    if (!matchExactType(fields1.get(i).getType(), 
fields2.get(i).getType(), ignorePrecision)) {
+                        return false;
+                    }
+                }
+                return true;
+            } else if 
(PrimitiveType.typeWithPrecision.contains(type2.getPrimitiveType())) {
+                //FIXME: this branch will never be executed 
ScalarType.matchesType and checks below are
+                // self-contradictory. double check and remove the argument 
`ignorePrecision`.
                 if ((((ScalarType) type2).decimalPrecision()
                         == ((ScalarType) type1).decimalPrecision()) && 
(((ScalarType) type2).decimalScale()
                         == ((ScalarType) type1).decimalScale())) {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
new file mode 100644
index 00000000000..08dd895b739
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/TypeTest.java
@@ -0,0 +1,190 @@
+// 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.catalog;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class TypeTest {
+
+    // ===================== ArrayType =====================
+    @Test
+    public void testArrayOfArrayExactMatch() {
+        ArrayType a1 = new ArrayType(new ArrayType(Type.INT, true), true);
+        ArrayType a2 = new ArrayType(new ArrayType(Type.INT, true), true);
+        Assert.assertTrue(Type.matchExactType(a1, a2));
+
+        // inner type mismatch
+        ArrayType a3 = new ArrayType(new ArrayType(Type.BIGINT, true), true);
+        Assert.assertFalse(Type.matchExactType(a1, a3));
+
+        // containsNull differs -> matchesType fails
+        ArrayType a4 = new ArrayType(new ArrayType(Type.INT, true), false);
+        Assert.assertFalse(Type.matchExactType(a1, a4));
+
+        // array nested decimal test
+        ArrayType a5 = new ArrayType(new 
ArrayType(ScalarType.createDecimalV3Type(8, 2), true), true);
+        ArrayType a6 = new ArrayType(new 
ArrayType(ScalarType.createDecimalV3Type(9, 2), true), true);
+        ArrayType a7 = new ArrayType(new 
ArrayType(ScalarType.createDecimalV3Type(-1, -1), true), true);
+        Assert.assertFalse(Type.matchExactType(a5, a6, false));
+        Assert.assertFalse(Type.matchExactType(a5, a6, true));
+        Assert.assertFalse(Type.matchExactType(a6, a7, false));
+    }
+
+    // ===================== MapType =====================
+    @Test
+    public void testMapWithNestedValueExactMatch() {
+        ScalarType d10s2 = ScalarType.createDecimalV3Type(10, 2); // DECIMAL64 
range
+        ArrayType arrayOfD = new ArrayType(d10s2, true);
+        MapType m1 = new MapType(Type.INT, arrayOfD, true, true);
+        MapType m2 = new MapType(Type.INT, new 
ArrayType(ScalarType.createDecimalV3Type(10, 2), true), true, true);
+        Assert.assertTrue(Type.matchExactType(m1, m2));
+
+        // value decimal precision differs, same scale
+        MapType m3 = new MapType(Type.INT, new 
ArrayType(ScalarType.createDecimalV3Type(12, 2), true), true, true);
+        // ignorePrecision = false -> not match
+        Assert.assertFalse(Type.matchExactType(m1, m3, false));
+        Assert.assertFalse(Type.matchExactType(m1, m3, true));
+
+        // key/value containsNull differs -> doesn't matter for matching
+        MapType m4 = new MapType(Type.INT, arrayOfD, false, true);
+        Assert.assertTrue(Type.matchExactType(m1, m4));
+    }
+
+    // ===================== StructType =====================
+    @Test
+    public void testStructWithNestedFieldsExactMatch() {
+        // struct<f1:int, f2:array<int>>
+        StructType s1 = new StructType(
+                new StructField("f1", Type.INT, null, true),
+                new StructField("f2", new ArrayType(Type.INT, true), null, 
true)
+        );
+        StructType s2 = new StructType(
+                new StructField("x", Type.INT, null, true),
+                new StructField("y", new ArrayType(Type.INT, true), null, true)
+        );
+        // names are ignored by matchExactType recursion; matchesType requires 
containsNull equal
+        Assert.assertTrue(Type.matchExactType(s1, s2));
+
+        // inner element type differs
+        StructType s3 = new StructType(
+                new StructField("f1", Type.INT, null, true),
+                new StructField("f2", new ArrayType(Type.BIGINT, true), null, 
true)
+        );
+        Assert.assertFalse(Type.matchExactType(s1, s3));
+
+        // field nullability differs -> matchesType fails upfront
+        StructType s4 = new StructType(
+                new StructField("f1", Type.INT, null, false),
+                new StructField("f2", new ArrayType(Type.INT, true), null, 
true)
+        );
+        Assert.assertFalse(Type.matchExactType(s1, s4));
+    }
+
+    // ===================== VariantType =====================
+    @Test
+    public void testVariantPredefinedFieldsExactMatch() {
+        ArrayList<VariantField> fields1 = new ArrayList<>();
+        fields1.add(new VariantField("a", Type.INT, ""));
+        fields1.add(new VariantField("b", new 
ArrayType(ScalarType.createDecimalV3Type(10, 2), true), ""));
+        VariantType v1 = new VariantType(fields1);
+
+        ArrayList<VariantField> fields2 = new ArrayList<>();
+        // different names but same types and order should still match
+        fields2.add(new VariantField("x", Type.INT, ""));
+        fields2.add(new VariantField("y", new 
ArrayType(ScalarType.createDecimalV3Type(10, 2), true), ""));
+        VariantType v2 = new VariantType(fields2);
+        Assert.assertTrue(Type.matchExactType(v1, v2));
+
+        // change type of second field
+        ArrayList<VariantField> fields3 = new ArrayList<>();
+        fields3.add(new VariantField("a", Type.INT, ""));
+        fields3.add(new VariantField("b", new 
ArrayType(ScalarType.createDecimalV3Type(12, 2), true), ""));
+        VariantType v3 = new VariantType(fields3);
+        Assert.assertFalse(Type.matchExactType(v1, v3));
+
+        // same types but different order -> index-wise comparison fails
+        ArrayList<VariantField> fields4 = new ArrayList<>();
+        fields4.add(new VariantField("b", new 
ArrayType(ScalarType.createDecimalV3Type(10, 2), true), ""));
+        fields4.add(new VariantField("a", Type.INT, ""));
+        VariantType v4 = new VariantType(fields4);
+        Assert.assertFalse(Type.matchExactType(v1, v4));
+    }
+
+    // ===================== Mixed Nesting & Precision =====================
+    @Test
+    public void testArrayMapStructCombinationWithPrecision() {
+        // array<map<int, struct<c1:int, c2:array<decimal(10,2)>>>>
+        ScalarType dec10s2 = ScalarType.createDecimalV3Type(10, 2); // 
DECIMAL64 range
+        ArrayType innerArray = new ArrayType(dec10s2, true);
+        StructType innerStruct = new StructType(
+                new StructField("c1", Type.INT, null, true),
+                new StructField("c2", innerArray, null, true)
+        );
+        MapType innerMap1 = new MapType(Type.INT, innerStruct, true, true);
+        ArrayType complex1 = new ArrayType(innerMap1, true);
+
+        // Same shape but decimal precision 12 (same DECIMAL64 group), same 
scale
+        ScalarType dec12s2 = ScalarType.createDecimalV3Type(12, 2);
+        ArrayType innerArray2 = new ArrayType(dec12s2, true);
+        StructType innerStruct2 = new StructType(
+                new StructField("c1", Type.INT, null, true),
+                new StructField("c2", innerArray2, null, true)
+        );
+        MapType innerMap2 = new MapType(Type.INT, innerStruct2, true, true);
+        ArrayType complex2 = new ArrayType(innerMap2, true);
+
+        Assert.assertFalse(Type.matchExactType(complex1, complex2, false));
+    }
+
+    // ===================== Decimal/DATETIMEV2 Precision & Scale 
=====================
+    @Test
+    public void testDecimalPrecisionGroupsIgnorePrecision() {
+        // DECIMAL32 group (<=9)
+        ScalarType d8s2 = ScalarType.createDecimalV3Type(8, 2);
+        ScalarType d9s2 = ScalarType.createDecimalV3Type(9, 2);
+        Assert.assertFalse(Type.matchExactType(d8s2, d9s2, false));
+
+        // Cross group: DECIMAL32 vs DECIMAL64 -> should be false even when 
ignorePrecision
+        ScalarType d10s2 = ScalarType.createDecimalV3Type(10, 2);
+        Assert.assertFalse(Type.matchExactType(d9s2, d10s2, true));
+
+        // DECIMAL64 group (10..18)
+        ScalarType d10s3 = ScalarType.createDecimalV3Type(10, 3);
+        ScalarType d18s3 = ScalarType.createDecimalV3Type(18, 3);
+        Assert.assertFalse(Type.matchExactType(d10s3, d18s3, false));
+
+        // DECIMAL128 group (19..38)
+        ScalarType d20s1 = ScalarType.createDecimalV3Type(20, 1);
+        ScalarType d38s1 = ScalarType.createDecimalV3Type(38, 1);
+        Assert.assertFalse(Type.matchExactType(d20s1, d38s1, false));
+    }
+
+    @Test
+    public void testDatetimeV2ScaleMatching() {
+        ScalarType dtv2s3 = ScalarType.createDatetimeV2Type(3);
+        ScalarType dtv2s6 = ScalarType.createDatetimeV2Type(6);
+        // Different scales -> no match regardless of ignorePrecision
+        Assert.assertFalse(Type.matchExactType(dtv2s3, dtv2s6, false));
+        Assert.assertFalse(Type.matchExactType(dtv2s3, dtv2s6, true));
+        // Same scale -> match
+        Assert.assertTrue(Type.matchExactType(dtv2s6, 
ScalarType.createDatetimeV2Type(6)));
+    }
+}


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

Reply via email to