This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new ac3381bbfb1 [fix](fe) Reject complex types (ARRAY/MAP/STRUCT) as MAP
key types (#63621)
ac3381bbfb1 is described below
commit ac3381bbfb1a4c5b496e163b67933b61677613c5
Author: morrySnow <[email protected]>
AuthorDate: Fri Jul 3 10:34:47 2026 +0800
[fix](fe) Reject complex types (ARRAY/MAP/STRUCT) as MAP key types (#63621)
### What problem does this PR solve?
Problem Summary: `MAP<MAP/ARRAY/STRUCT, V>` was silently accepted during
table creation. The validation in `DataType.validateCatalogDataType()`
called `validateNestedType()` for both key and value of a MAP, but
`mapSubTypes` included complex types (ARRAY, MAP, STRUCT), so complex
key types passed through without error. MAP key types must be restricted
to primitive types only, as complex key types are not supported by the
execution engine.
The fix adds an explicit check before validating MAP key types: if the
key type is a complex type, an `AnalysisException` is thrown with a
clear message: `MAP key type must be a primitive type but get <type>`.
### Release note
CREATE TABLE with MAP<ARRAY<...>/MAP<.../STRUCT<...>, V> now correctly
raises an error: `MAP key type must be a primitive type`. Previously
such DDL was silently accepted and could cause undefined behavior at
runtime.
---
.../org/apache/doris/nereids/types/DataType.java | 7 +++-
.../trees/plans/CreateTableCommandTest.java | 24 ++++++++++++++
.../string_len/test_string_len_complex.out | 7 ----
.../test_complex_disallowed_subtypes.groovy | 33 +++++++++++++++++++
.../string_len/test_string_len_complex.groovy | 38 ----------------------
.../show/test_nested_complex_switch.groovy | 17 ----------
6 files changed, 63 insertions(+), 63 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 14295979dae..a47352a29e6 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
@@ -909,7 +909,12 @@ public abstract class DataType {
if (catalogType.isMapType()) {
org.apache.doris.catalog.MapType mt =
(org.apache.doris.catalog.MapType) catalogType;
- validateNestedType(catalogType, mt.getKeyType());
+ Type mapKeyType = mt.getKeyType();
+ if (mapKeyType.isComplexType()) {
+ throw new AnalysisException(
+ "MAP key type must be a primitive type but get " +
mapKeyType.toSql());
+ }
+ validateNestedType(catalogType, mapKeyType);
validateNestedType(catalogType, mt.getValueType());
}
if (catalogType.isStructType()) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java
index 3530f353619..ffa9aba39de 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/CreateTableCommandTest.java
@@ -688,6 +688,30 @@ public class CreateTableCommandTest extends
TestWithFeService {
+ "distributed by hash(k1) buckets 1
properties('replication_num' = '1');"));
}
+ @Test
+ public void testCreateTableWithComplexMapKey() {
+ // MAP<ARRAY<INT>, INT> should be rejected - array key
+ checkThrow(AnalysisException.class, "MAP key type must be a primitive
type",
+ () -> createTable("create table test.test_map_array_key(k1
INT, k2 Map<Array<int>, int>) "
+ + "duplicate key (k1) distributed by hash(k1) buckets
1 "
+ + "properties('replication_num' = '1');"));
+ // MAP<MAP<INT, INT>, INT> should be rejected - map key
+ checkThrow(AnalysisException.class, "MAP key type must be a primitive
type",
+ () -> createTable("create table test.test_map_map_key(k1 INT,
k2 Map<Map<int,int>, int>) "
+ + "duplicate key (k1) distributed by hash(k1) buckets
1 "
+ + "properties('replication_num' = '1');"));
+ // MAP<STRUCT<f1:INT>, INT> should be rejected - struct key
+ checkThrow(AnalysisException.class, "MAP key type must be a primitive
type",
+ () -> createTable("create table test.test_map_struct_key(k1
INT, k2 Map<Struct<f1:int>, int>) "
+ + "duplicate key (k1) distributed by hash(k1) buckets
1 "
+ + "properties('replication_num' = '1');"));
+ // MAP<STRING, ARRAY<INT>> should still be accepted - complex value
type is OK
+ Assertions.assertDoesNotThrow(
+ () -> createTable("create table test.test_map_complex_value(k1
INT, k2 Map<String, Array<int>>) "
+ + "duplicate key (k1) distributed by hash(k1) buckets
1 "
+ + "properties('replication_num' = '1');"));
+ }
+
@Test
public void testCreateTableWithStructType() {
Assertions.assertDoesNotThrow(
diff --git
a/regression-test/data/datatype_p0/nested_types/string_len/test_string_len_complex.out
b/regression-test/data/datatype_p0/nested_types/string_len/test_string_len_complex.out
index acc8e888691..d09fc1f00f6 100644
---
a/regression-test/data/datatype_p0/nested_types/string_len/test_string_len_complex.out
+++
b/regression-test/data/datatype_p0/nested_types/string_len/test_string_len_complex.out
@@ -34,10 +34,3 @@
-- !sql_map_all2 --
--- !sql_map_all3 --
-1 {{"01234567891":"12345678901"}:{"11234567891":"12345678901"},
{"01234567892":"12345678902"}:{"11234567892":"12345678902"}}
-2 {{"01234567891":"22345678901"}:{"11234567891":"22345678901"},
{"01234567892":"22345678902"}:{"11234567892":"22345678902"}}
-3 {{"01234567891":"32345678901"}:{"11234567891":"32345678901"},
{"01234567892":"32345678902"}:{"11234567892":"32345678902"}}
-
--- !sql_map_all4 --
-
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
index 36d261e9d0a..684c1084f83 100644
---
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
@@ -64,6 +64,31 @@ suite("test_complex_disallowed_subtypes") {
exception "unsupported sub-type"
}
+ // ---- DORIS-25583: MAP key must not be a complex type (MAP/ARRAY/STRUCT)
----
+
+ sql "DROP TABLE IF EXISTS t_complex_map_key_1"
+ sql "DROP TABLE IF EXISTS t_complex_map_key_2"
+ sql "DROP TABLE IF EXISTS t_complex_map_key_3"
+ sql "DROP TABLE IF EXISTS t_complex_map_key_valid"
+
+ test {
+ // MAP<ARRAY<INT>, INT>
+ sql "CREATE TABLE t_complex_map_key_1 (k INT, v MAP<ARRAY<INT>,INT>)
DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1
PROPERTIES('replication_num'='1')"
+ exception "MAP key type must be a primitive type"
+ }
+
+ test {
+ // MAP<MAP<STRING,INT>, INT>
+ sql "CREATE TABLE t_complex_map_key_2 (k INT, v
MAP<MAP<STRING,INT>,INT>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1
PROPERTIES('replication_num'='1')"
+ exception "MAP key type must be a primitive type"
+ }
+
+ test {
+ // MAP<STRUCT<a:INT>, INT>
+ sql "CREATE TABLE t_complex_map_key_3 (k INT, v
MAP<STRUCT<a:INT>,INT>) DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1
PROPERTIES('replication_num'='1')"
+ exception "MAP key type must be a primitive type"
+ }
+
// ---- valid deep nesting must still be accepted ----
sql """
@@ -73,5 +98,13 @@ suite("test_complex_disallowed_subtypes") {
PROPERTIES('replication_num'='1')
"""
+ sql """
+ CREATE TABLE t_complex_map_key_valid (k INT, v MAP<STRING, ARRAY<INT>>)
+ DUPLICATE KEY(k)
+ DISTRIBUTED BY HASH(k) BUCKETS 1
+ PROPERTIES('replication_num'='1')
+ """
+
sql "DROP TABLE IF EXISTS t_complex_valid"
+ sql "DROP TABLE IF EXISTS t_complex_map_key_valid"
}
diff --git
a/regression-test/suites/datatype_p0/nested_types/string_len/test_string_len_complex.groovy
b/regression-test/suites/datatype_p0/nested_types/string_len/test_string_len_complex.groovy
index 07b63914fa9..1e8ff5e4c46 100644
---
a/regression-test/suites/datatype_p0/nested_types/string_len/test_string_len_complex.groovy
+++
b/regression-test/suites/datatype_p0/nested_types/string_len/test_string_len_complex.groovy
@@ -200,42 +200,4 @@ suite("test_string_len_complex") {
}
qt_sql_map_all2 """ select * from test_string_len_map_one_level order by
id; """
- // test map two level
- def create_table_map_two_level = {
- sql """ DROP TABLE IF EXISTS test_string_len_map_two_level """
- sql """
- CREATE TABLE `test_string_len_map_two_level` (
- `id` int(11),
- `info` map<map<char(11), char(11)>, map<char(11), char(11)>>
- ) PROPERTIES ("replication_num" = "1");
- """
- }
-
- create_table_map_two_level()
- sql """
- set enable_insert_strict = false;
- set enable_strict_cast = false;
- """
- sql """
- insert into test_string_len_map_two_level values (1, {
{'01234567891':'12345678901'}:{'11234567891':'12345678901'},
{'01234567892':'12345678902'}:{'11234567892':'12345678902'} } ),
- (2, { {'01234567891':'22345678901'}:{'11234567891':'22345678901'},
{'01234567892':'22345678902'}:{'11234567892':'22345678902'} } ),
- (3, { {'01234567891':'32345678901'}:{'11234567891':'32345678901'},
{'01234567892':'32345678902x'}:{'11234567892':'32345678902x'} } );
- """
- qt_sql_map_all3 """ select * from test_string_len_map_two_level order by
id; """
-
- create_table_map_two_level()
- sql """
- set enable_insert_strict = true;
- set enable_strict_cast = true;
- """
- test {
- sql """
- insert into test_string_len_map_two_level values (1, {
{'01234567891':'12345678901'}:{'11234567891':'12345678901'},
{'01234567892':'12345678902'}:{'11234567892':'12345678902'} } ),
- (2, { {'01234567891':'22345678901'}:{'11234567891':'22345678901'},
{'01234567892':'22345678902'}:{'11234567892':'22345678902'} } ),
- (3, { {'01234567891':'32345678901'}:{'11234567891':'32345678901'},
{'01234567892':'32345678902x'}:{'11234567892':'32345678902x'} } );
- """
- exception "Insert has filtered data in strict mode"
- }
- qt_sql_map_all4 """ select * from test_string_len_map_two_level order by
id; """
-
}
\ No newline at end of file
diff --git
a/regression-test/suites/query_p0/show/test_nested_complex_switch.groovy
b/regression-test/suites/query_p0/show/test_nested_complex_switch.groovy
index 9b06a00eb42..e0175f3147a 100644
--- a/regression-test/suites/query_p0/show/test_nested_complex_switch.groovy
+++ b/regression-test/suites/query_p0/show/test_nested_complex_switch.groovy
@@ -35,19 +35,6 @@ suite("test_nested_complex_switch", "query") {
"storage_format" = "V2",
"disable_auto_compaction" = "false"
)"""
- def sql_m_a = """CREATE TABLE IF NOT EXISTS ${testTable_m} (
- `k1` INT(11) NULL,
- `k2` MAP<ARRAY<INT>, STRING>
- ) ENGINE=OLAP
- DUPLICATE KEY(`k1`)
- COMMENT 'OLAP'
- DISTRIBUTED BY HASH(`k1`) BUCKETS 1
- PROPERTIES (
- "replication_allocation" = "tag.location.default: 1",
- "in_memory" = "false",
- "storage_format" = "V2",
- "disable_auto_compaction" = "false"
- )"""
def sql_m_m = """CREATE TABLE IF NOT EXISTS ${testTable_m} (
`k1` INT(11) NULL,
@@ -145,10 +132,6 @@ suite("test_nested_complex_switch", "query") {
sql sql_m_s
}
- test {
- sql sql_m_a
- }
-
test {
sql sql_m_m
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]