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

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


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 4a375e28ec1 branch-4.1: [fix](fe) Fix deep nested complex type subtype 
validation bypass #63208 (#63222)
4a375e28ec1 is described below

commit 4a375e28ec13c72d6143400c2fdde3e0da35336b
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri May 15 16:06:24 2026 +0800

    branch-4.1: [fix](fe) Fix deep nested complex type subtype validation 
bypass #63208 (#63222)
    
    Cherry-picked from #63208
    
    Co-authored-by: morrySnow <[email protected]>
    Co-authored-by: Copilot <[email protected]>
---
 .../org/apache/doris/nereids/types/DataType.java   | 23 ++-----
 .../apache/doris/nereids/types/DataTypeTest.java   | 54 +++++++++++++++
 .../test_complex_disallowed_subtypes.groovy        | 77 ++++++++++++++++++++++
 3 files changed, 138 insertions(+), 16 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
index ab3c1718d22..8c30b835b05 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
@@ -897,22 +897,15 @@ public abstract class DataType {
         if (catalogType.isScalarType()) {
             validateScalarType((ScalarType) catalogType);
         } else if (catalogType.isComplexType()) {
-            // now we not support array / map / struct nesting complex type
             if (catalogType.isArrayType()) {
                 Type itemType = ((org.apache.doris.catalog.ArrayType) 
catalogType).getItemType();
-                if (itemType instanceof ScalarType) {
-                    validateNestedType(catalogType, (ScalarType) itemType);
-                }
+                validateNestedType(catalogType, itemType);
             }
             if (catalogType.isMapType()) {
                 org.apache.doris.catalog.MapType mt =
                         (org.apache.doris.catalog.MapType) catalogType;
-                if (mt.getKeyType() instanceof ScalarType) {
-                    validateNestedType(catalogType, (ScalarType) 
mt.getKeyType());
-                }
-                if (mt.getValueType() instanceof ScalarType) {
-                    validateNestedType(catalogType, (ScalarType) 
mt.getValueType());
-                }
+                validateNestedType(catalogType, mt.getKeyType());
+                validateNestedType(catalogType, mt.getValueType());
             }
             if (catalogType.isStructType()) {
                 ArrayList<org.apache.doris.catalog.StructField> fields =
@@ -920,12 +913,10 @@ public abstract class DataType {
                 Set<String> fieldNames = new HashSet<>();
                 for (org.apache.doris.catalog.StructField field : fields) {
                     Type fieldType = field.getType();
-                    if (fieldType instanceof ScalarType) {
-                        validateNestedType(catalogType, (ScalarType) 
fieldType);
-                        if (!fieldNames.add(field.getName())) {
-                            throw new AnalysisException("Duplicate field name 
" + field.getName()
-                                    + " in struct " + catalogType.toSql());
-                        }
+                    validateNestedType(catalogType, fieldType);
+                    if (!fieldNames.add(field.getName())) {
+                        throw new AnalysisException("Duplicate field name " + 
field.getName()
+                                + " in struct " + catalogType.toSql());
                     }
                 }
             }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java
index 3f5a4a4ca0f..59509fce805 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/types/DataTypeTest.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.types;
 
 import org.apache.doris.catalog.Type;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.types.coercion.AnyDataType;
 import org.apache.doris.nereids.types.coercion.FractionalType;
 import org.apache.doris.nereids.types.coercion.IntegralType;
@@ -600,4 +601,57 @@ public class DataTypeTest {
         Assertions.assertEquals(DateType.INSTANCE, 
DateType.INSTANCE.defaultConcreteType());
         Assertions.assertEquals(DateTimeType.INSTANCE, 
DateTimeType.INSTANCE.defaultConcreteType());
     }
+
+    // DORIS-25584: special aggregate/semi-structured types must be rejected 
as complex-type elements
+    // at any nesting depth, not only at depth 2.
+
+    @Test
+    public void testArrayMapBitmapKeyRejected() {
+        // ARRAY<MAP<BITMAP, INT>> — BITMAP as map key must be caught at depth 
3
+        DataType type = ArrayType.of(MapType.of(BitmapType.INSTANCE, 
IntegerType.INSTANCE));
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testArrayMapBitmapValueRejected() {
+        // ARRAY<MAP<STRING, BITMAP>> — BITMAP as map value must be caught at 
depth 3
+        DataType type = ArrayType.of(MapType.of(VarcharType.SYSTEM_DEFAULT, 
BitmapType.INSTANCE));
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testStructArrayHllRejected() {
+        // STRUCT<a: ARRAY<HLL>> — HLL inside array inside struct must be 
caught
+        DataType type = new StructType(
+                ImmutableList.of(new StructField("a", 
ArrayType.of(HllType.INSTANCE), true, "")));
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testMapArrayJsonbRejected() {
+        // MAP<STRING, ARRAY<JSONB>> — JSONB inside array as map value must be 
caught
+        DataType type = MapType.of(VarcharType.SYSTEM_DEFAULT, 
ArrayType.of(JsonType.INSTANCE));
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testArrayBitmapDirectlyRejected() {
+        // ARRAY<BITMAP> at depth 2 — must still be caught (regression guard)
+        DataType type = ArrayType.of(BitmapType.INSTANCE);
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testMapHllValueDirectlyRejected() {
+        // MAP<INT, HLL> at depth 2 — must still be caught (regression guard)
+        DataType type = MapType.of(IntegerType.INSTANCE, HllType.INSTANCE);
+        Assertions.assertThrows(AnalysisException.class, 
type::validateDataType);
+    }
+
+    @Test
+    public void testDeepValidComplexNestingAccepted() {
+        // ARRAY<MAP<STRING, INT>> — valid 3-level nesting must still be 
accepted
+        DataType type = ArrayType.of(MapType.of(VarcharType.SYSTEM_DEFAULT, 
IntegerType.INSTANCE));
+        Assertions.assertDoesNotThrow(type::validateDataType);
+    }
 }
diff --git 
a/regression-test/suites/datatype_p0/complex_types/test_complex_disallowed_subtypes.groovy
 
b/regression-test/suites/datatype_p0/complex_types/test_complex_disallowed_subtypes.groovy
new file mode 100644
index 00000000000..36d261e9d0a
--- /dev/null
+++ 
b/regression-test/suites/datatype_p0/complex_types/test_complex_disallowed_subtypes.groovy
@@ -0,0 +1,77 @@
+// 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.
+
+// DORIS-25584: BITMAP/HLL/JSONB must be rejected as complex-type elements at 
any nesting depth.
+// Previously depth-3 (and deeper) nesting was silently accepted; now all 
depths are validated.
+
+suite("test_complex_disallowed_subtypes") {
+    // ---- setup ----
+    sql "DROP TABLE IF EXISTS t_complex_disallowed_subtypes_1"
+    sql "DROP TABLE IF EXISTS t_complex_disallowed_subtypes_2"
+    sql "DROP TABLE IF EXISTS t_complex_disallowed_subtypes_3"
+    sql "DROP TABLE IF EXISTS t_complex_disallowed_subtypes_4"
+    sql "DROP TABLE IF EXISTS t_complex_valid"
+
+    // ---- depth-2: must still be rejected (regression guard) ----
+
+    test {
+        sql "CREATE TABLE t_complex_disallowed_subtypes_1 (k INT, v 
ARRAY<BITMAP>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    test {
+        sql "CREATE TABLE t_complex_disallowed_subtypes_2 (k INT, v MAP<INT, 
HLL>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    // ---- depth-3: were silently accepted before the fix, must now be 
rejected ----
+
+    test {
+        // ARRAY<MAP<BITMAP, INT>>
+        sql "CREATE TABLE t_complex_disallowed_subtypes_1 (k INT, v 
ARRAY<MAP<BITMAP,INT>>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    test {
+        // ARRAY<MAP<STRING, BITMAP>>
+        sql "CREATE TABLE t_complex_disallowed_subtypes_2 (k INT, v 
ARRAY<MAP<STRING,BITMAP>>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    test {
+        // STRUCT<a: ARRAY<HLL>>
+        sql "CREATE TABLE t_complex_disallowed_subtypes_3 (k INT, v 
STRUCT<a:ARRAY<HLL>>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    test {
+        // MAP<STRING, ARRAY<JSONB>>
+        sql "CREATE TABLE t_complex_disallowed_subtypes_4 (k INT, v 
MAP<STRING,ARRAY<JSON>>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 
PROPERTIES('replication_num'='1')"
+        exception "unsupported sub-type"
+    }
+
+    // ---- valid deep nesting must still be accepted ----
+
+    sql """
+        CREATE TABLE t_complex_valid (k INT, v ARRAY<MAP<STRING, INT>>)
+        DUPLICATE KEY(k)
+        DISTRIBUTED BY HASH(k) BUCKETS 1
+        PROPERTIES('replication_num'='1')
+    """
+
+    sql "DROP TABLE IF EXISTS t_complex_valid"
+}


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

Reply via email to