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

yiguolei 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 60c47654435 [fix](struct)fix fe struct functions (#54978)
60c47654435 is described below

commit 60c47654435c50195224c7af37045d4c1406e57f
Author: amory <[email protected]>
AuthorDate: Mon Sep 8 15:26:23 2025 +0800

    [fix](struct)fix fe struct functions (#54978)
    
    ### What problem does this PR solve?
    fix some struct functions behavior
    doc: https://github.com/apache/doris-website/pull/2765
    Issue Number: close #xxx
    、
---
 .../trees/expressions/functions/scalar/Array.java  |  12 +++++
 .../expressions/functions/scalar/CreateMap.java    |   5 ++
 .../functions/scalar/CreateNamedStruct.java        |   9 +++-
 .../expressions/functions/scalar/CreateStruct.java |  14 ++++++
 .../functions/scalar/StructElement.java            |  14 ++++--
 .../org/apache/doris/nereids/types/StructType.java |  24 +++++-----
 .../data/doc/sql-manual/StructNullsafe.out         | Bin 0 -> 379 bytes
 .../suites/doc/sql-manual/StructNullsafe.groovy    |  53 +++++++++++++++++++++
 8 files changed, 116 insertions(+), 15 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java
index dfed9b75df6..4a1c4911e64 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
@@ -63,6 +64,17 @@ public class Array extends ScalarFunction
         super(functionParams);
     }
 
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        if (children.isEmpty()) {
+            return;
+        }
+        DataType firstChildType = children.get(0).getDataType();
+        if (firstChildType.isJsonType() || firstChildType.isVariantType()) {
+            throw new AnalysisException("array does not support jsonb/variant 
type");
+        }
+    }
+
     /**
      * withChildren.
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateMap.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateMap.java
index d44e28f473b..eed1dbebce9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateMap.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateMap.java
@@ -84,6 +84,11 @@ public class CreateMap extends ScalarFunction
         if (arity() % 2 != 0) {
             throw new AnalysisException("map can't be odd parameters, need 
even parameters " + this.toSql());
         }
+        children.forEach(child -> {
+            if (child.getDataType().isJsonType() || 
child.getDataType().isVariantType()) {
+                throw new AnalysisException("map does not support 
jsonb/variant type");
+            }
+        });
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java
index 02ea9dc240f..3b86d5a4749 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateNamedStruct.java
@@ -57,6 +57,9 @@ public class CreateNamedStruct extends ScalarFunction 
implements CustomSignature
 
     @Override
     public void checkLegalityBeforeTypeCoercion() {
+        if (arity() < 2) {
+            throw new AnalysisException("named_struct requires at least two 
arguments, like: named_struct('a', 1)");
+        }
         if (arity() % 2 != 0) {
             throw new AnalysisException("named_struct can't be odd parameters, 
need even parameters " + this.toSql());
         }
@@ -66,7 +69,7 @@ public class CreateNamedStruct extends ScalarFunction 
implements CustomSignature
                 throw new AnalysisException("named_struct only allows"
                         + " constant string parameter in odd position: " + 
this);
             } else {
-                String name = ((StringLikeLiteral) child(i)).getStringValue();
+                String name = ((StringLikeLiteral) 
child(i)).getStringValue().toLowerCase();
                 if (names.contains(name)) {
                     throw new AnalysisException("The name of the struct field 
cannot be repeated."
                             + " same name fields are " + name);
@@ -74,6 +77,10 @@ public class CreateNamedStruct extends ScalarFunction 
implements CustomSignature
                     names.add(name);
                 }
             }
+            // i+1 is value, check if it is not jsonb/variant type
+            if (child(i + 1).getDataType().isJsonType() || child(i + 
1).getDataType().isVariantType()) {
+                throw new AnalysisException("named_struct does not support 
jsonb/variant type");
+            }
         }
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateStruct.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateStruct.java
index 8e94dc690cd..9e89da0fd87 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateStruct.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CreateStruct.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
+import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
@@ -53,6 +54,19 @@ public class CreateStruct extends ScalarFunction
         super(functionParams);
     }
 
+    @Override
+    public void checkLegalityBeforeTypeCoercion() {
+        if (arity() == 0) {
+            throw new AnalysisException("struct requires at least one 
argument, like: struct(1)");
+        }
+        // for all field we do not support struct field with jsonb/variant type
+        children.forEach(child -> {
+            if (child.getDataType().isJsonType() || 
child.getDataType().isVariantType()) {
+                throw new AnalysisException("struct does not support 
jsonb/variant type");
+            }
+        });
+    }
+
     /**
      * withChildren.
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
index 39af519a617..85bb424de19 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
@@ -22,11 +22,13 @@ import 
org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
 import 
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
+import 
org.apache.doris.nereids.trees.expressions.functions.PropagateNullLiteral;
 import org.apache.doris.nereids.trees.expressions.functions.SearchSignature;
 import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.types.NullType;
 import org.apache.doris.nereids.types.StructType;
 
 import com.google.common.base.Preconditions;
@@ -38,7 +40,7 @@ import java.util.List;
  * ScalarFunction 'struct_element'.
  */
 public class StructElement extends ScalarFunction
-        implements ExplicitlyCastableSignature, AlwaysNullable {
+        implements ExplicitlyCastableSignature, AlwaysNullable, 
PropagateNullLiteral {
 
     /**
      * constructor with 0 or more arguments.
@@ -54,7 +56,8 @@ public class StructElement extends ScalarFunction
 
     @Override
     public void checkLegalityBeforeTypeCoercion() {
-        if (!(child(0).getDataType() instanceof StructType)) {
+        if (!(child(0).getDataType() instanceof StructType
+                || child(0).getDataType() instanceof NullType)) {
             SearchSignature.throwCanNotFoundFunctionException(this.getName(), 
this.getArguments());
         }
         if (!(child(1) instanceof StringLikeLiteral || child(1) instanceof 
IntegerLikeLiteral)) {
@@ -79,6 +82,11 @@ public class StructElement extends ScalarFunction
 
     @Override
     public List<FunctionSignature> getSignatures() {
+        if (child(0).getDataType() instanceof NullType) {
+            return ImmutableList.of(
+                    
FunctionSignature.ret(NullType.INSTANCE).args(NullType.INSTANCE, 
child(1).getDataType())
+            );
+        }
         StructType structArgType = (StructType) child(0).getDataType();
         DataType retType;
         if (child(1) instanceof IntegerLikeLiteral) {
@@ -90,7 +98,7 @@ public class StructElement extends ScalarFunction
             }
         } else if (child(1) instanceof StringLikeLiteral) {
             String name = ((StringLikeLiteral) child(1)).getStringValue();
-            if (!structArgType.getNameToFields().containsKey(name)) {
+            if 
(!structArgType.getNameToFields().containsKey(name.toLowerCase())) {
                 throw new AnalysisException("the specified field name " + name 
+ " was not found: " + this.toSql());
             } else {
                 retType = 
structArgType.getNameToFields().get(name).getDataType();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
index 20f0947b321..ffbff7c61e1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
@@ -22,15 +22,13 @@ import org.apache.doris.nereids.annotation.Developing;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.types.coercion.ComplexDataType;
 
-import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 /**
@@ -44,10 +42,10 @@ public class StructType extends DataType implements 
ComplexDataType {
     public static final int WIDTH = 24;
 
     private final List<StructField> fields;
-    private final Supplier<Map<String, StructField>> nameToFields;
+    private final Map<String, StructField> nameToFields;
 
     private StructType() {
-        nameToFields = Suppliers.memoize(ImmutableMap::of);
+        nameToFields = new HashMap<>();
         fields = ImmutableList.of();
     }
 
@@ -56,11 +54,15 @@ public class StructType extends DataType implements 
ComplexDataType {
      */
     public StructType(List<StructField> fields) {
         this.fields = ImmutableList.copyOf(Objects.requireNonNull(fields, 
"fields should not be null"));
-        this.nameToFields = Suppliers.memoize(() -> 
this.fields.stream().collect(ImmutableMap.toImmutableMap(
-                StructField::getName, f -> f, (f1, f2) -> {
-                    throw new AnalysisException("The name of the struct field 
cannot be repeated."
-                            + " same name fields are " + f1 + " and " + f2);
-                })));
+        // field name should be lowercase and check the same or not
+        this.nameToFields = new HashMap<>();
+        for (StructField field : this.fields) {
+            String fieldName = field.getName().toLowerCase();
+            StructField existingField = this.nameToFields.put(fieldName, 
field);
+            if (existingField != null) {
+                throw new AnalysisException("Duplicate field name found: " + 
fieldName);
+            }
+        }
     }
 
     public List<StructField> getFields() {
@@ -68,7 +70,7 @@ public class StructType extends DataType implements 
ComplexDataType {
     }
 
     public Map<String, StructField> getNameToFields() {
-        return nameToFields.get();
+        return nameToFields;
     }
 
     @Override
diff --git a/regression-test/data/doc/sql-manual/StructNullsafe.out 
b/regression-test/data/doc/sql-manual/StructNullsafe.out
new file mode 100644
index 00000000000..397ca313cef
Binary files /dev/null and 
b/regression-test/data/doc/sql-manual/StructNullsafe.out differ
diff --git a/regression-test/suites/doc/sql-manual/StructNullsafe.groovy 
b/regression-test/suites/doc/sql-manual/StructNullsafe.groovy
new file mode 100644
index 00000000000..1cac0915a78
--- /dev/null
+++ b/regression-test/suites/doc/sql-manual/StructNullsafe.groovy
@@ -0,0 +1,53 @@
+// 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.
+
+suite("nereids_scalar_fn_StructNullsafe", "p0") {
+    sql 'set enable_nereids_planner=true'
+    sql 'set enable_fallback_to_original_planner=false'
+
+    // table nullsafe array
+    sql """DROP TABLE IF EXISTS fn_test_nullsafe_struct"""
+    sql """
+        CREATE TABLE IF NOT EXISTS `fn_test_nullsafe_struct` (
+            `id` int not null,
+            `struct` struct<id: int, name: string>
+        ) engine=olap
+        DISTRIBUTED BY HASH(`id`) BUCKETS 1
+        properties("replication_num" = "1")
+    """
+    
+    // insert into fn_test_nullsafe_array with null element
+    sql """
+        INSERT INTO fn_test_nullsafe_struct VALUES 
+        (1, STRUCT(1, 'Alice')),
+        (2, STRUCT(null, 'Bob')),
+        (3, STRUCT(3, null)),
+        (4, NULL),
+        (5, STRUCT(null, null))
+    """
+
+    // test function struct 
+    qt_sql_struct_element_by_index "SELECT struct_element(struct, 1), 
struct_element(struct, 2) FROM fn_test_nullsafe_struct ORDER BY id"
+    qt_sql_struct_element_by_name "SELECT struct_element(struct, 'id'), 
struct_element(struct, 'name') FROM fn_test_nullsafe_struct ORDER BY id"
+
+    // test function named_struct
+    qt_sql_literal_named_struct "SELECT named_struct('id', 1, 'name', NULL)"
+    qt_sql_literal_struct_element "SELECT struct_element(named_struct('id', 
NULL, 'name', 'Tom'), 'id'), struct_element(named_struct('id', 2, 'name', 
NULL), 'name')"
+
+    // test function struct_element 
+    qt_sql_literal_struct "SELECT STRUCT(NULL, 'X')"
+}
\ No newline at end of file


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

Reply via email to