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]