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

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


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 844269ac3e1 branch-4.0: [fix](func)fix for nested type in function 
param which has datetime or decimal #56625 (#56660)
844269ac3e1 is described below

commit 844269ac3e10b6cda7877de49d8378d837f91482
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Sep 30 15:42:36 2025 +0800

    branch-4.0: [fix](func)fix for nested type in function param which has 
datetime or decimal #56625 (#56660)
    
    Cherry-picked from #56625
    
    Co-authored-by: amory <[email protected]>
---
 be/src/vec/functions/function.cpp                  | 102 +++++++++++++++++----
 be/src/vec/functions/function.h                    |   9 +-
 .../sql_functions/struct_functions/sql/q02.out     |  73 +++++++++++++++
 .../sql_functions/struct_functions/sql/q02.sql     |  72 +++++++++++++++
 4 files changed, 237 insertions(+), 19 deletions(-)

diff --git a/be/src/vec/functions/function.cpp 
b/be/src/vec/functions/function.cpp
index 55a70a6e40c..17ce7b846ee 100644
--- a/be/src/vec/functions/function.cpp
+++ b/be/src/vec/functions/function.cpp
@@ -25,6 +25,7 @@
 #include <numeric>
 
 #include "common/status.h"
+#include "runtime/define_primitive_type.h"
 #include "runtime/primitive_type.h"
 #include "vec/aggregate_functions/aggregate_function.h"
 #include "vec/columns/column.h"
@@ -304,7 +305,37 @@ bool FunctionBuilderImpl::is_date_or_datetime_or_decimal(
             is_time_type(func_return_type->get_primitive_type()));
 }
 
-bool FunctionBuilderImpl::is_array_nested_type_date_or_datetime_or_decimal(
+bool contains_date_or_datetime_or_decimal(const DataTypePtr& type) {
+    auto type_ptr = type->is_nullable() ? 
((DataTypeNullable*)type.get())->get_nested_type() : type;
+
+    switch (type_ptr->get_primitive_type()) {
+    case TYPE_ARRAY: {
+        const auto* array_type = assert_cast<const 
DataTypeArray*>(type_ptr.get());
+        return 
contains_date_or_datetime_or_decimal(array_type->get_nested_type());
+    }
+    case TYPE_MAP: {
+        const auto* map_type = assert_cast<const DataTypeMap*>(type_ptr.get());
+        return contains_date_or_datetime_or_decimal(map_type->get_key_type()) 
||
+               
contains_date_or_datetime_or_decimal(map_type->get_value_type());
+    }
+    case TYPE_STRUCT: {
+        const auto* struct_type = assert_cast<const 
DataTypeStruct*>(type_ptr.get());
+        const auto& elements = struct_type->get_elements();
+        return std::ranges::any_of(elements, [](const DataTypePtr& element) {
+            return contains_date_or_datetime_or_decimal(element);
+        });
+    }
+    default:
+        // For scalar types, check if it's date/datetime/decimal
+        return is_date_or_datetime(type_ptr->get_primitive_type()) ||
+               is_date_v2_or_datetime_v2(type_ptr->get_primitive_type()) ||
+               is_decimal(type_ptr->get_primitive_type()) ||
+               is_time_type(type_ptr->get_primitive_type());
+    }
+}
+
+// make sure array/map/struct and nested  array/map/struct can be check
+bool FunctionBuilderImpl::is_nested_type_date_or_datetime_or_decimal(
         const DataTypePtr& return_type, const DataTypePtr& func_return_type) 
const {
     auto return_type_ptr = return_type->is_nullable()
                                    ? 
((DataTypeNullable*)return_type.get())->get_nested_type()
@@ -313,24 +344,63 @@ bool 
FunctionBuilderImpl::is_array_nested_type_date_or_datetime_or_decimal(
             func_return_type->is_nullable()
                     ? 
((DataTypeNullable*)func_return_type.get())->get_nested_type()
                     : func_return_type;
-    if (!(return_type_ptr->get_primitive_type() == TYPE_ARRAY &&
-          func_return_type_ptr->get_primitive_type() == TYPE_ARRAY)) {
+    // make sure that map/struct/array also need to check
+    if (return_type_ptr->get_primitive_type() != 
func_return_type_ptr->get_primitive_type()) {
         return false;
     }
-    auto nested_nullable_return_type_ptr =
-            (assert_cast<const 
DataTypeArray*>(return_type_ptr.get()))->get_nested_type();
-    auto nested_nullable_func_return_type =
-            (assert_cast<const 
DataTypeArray*>(func_return_type_ptr.get()))->get_nested_type();
-    // There must be nullable inside array type.
-    if (nested_nullable_return_type_ptr->is_nullable() &&
-        nested_nullable_func_return_type->is_nullable()) {
-        auto nested_return_type_ptr =
-                
((DataTypeNullable*)(nested_nullable_return_type_ptr.get()))->get_nested_type();
-        auto nested_func_return_type_ptr =
-                
((DataTypeNullable*)(nested_nullable_func_return_type.get()))->get_nested_type();
-        return is_date_or_datetime_or_decimal(nested_return_type_ptr, 
nested_func_return_type_ptr);
+
+    // Check if this type contains date/datetime/decimal types
+    if (!contains_date_or_datetime_or_decimal(return_type_ptr)) {
+        // If no date/datetime/decimal types, just pass through
+        return true;
+    }
+
+    // If contains date/datetime/decimal types, recursively check each element
+    switch (return_type_ptr->get_primitive_type()) {
+    case TYPE_ARRAY: {
+        auto nested_return_type = remove_nullable(
+                (assert_cast<const 
DataTypeArray*>(return_type_ptr.get()))->get_nested_type());
+        auto nested_func_type = remove_nullable(
+                (assert_cast<const 
DataTypeArray*>(func_return_type_ptr.get()))->get_nested_type());
+        return is_nested_type_date_or_datetime_or_decimal(nested_return_type, 
nested_func_type);
+    }
+    case TYPE_MAP: {
+        const auto* return_map = assert_cast<const 
DataTypeMap*>(return_type_ptr.get());
+        const auto* func_map = assert_cast<const 
DataTypeMap*>(func_return_type_ptr.get());
+
+        auto key_return = remove_nullable(return_map->get_key_type());
+        auto key_func = remove_nullable(func_map->get_key_type());
+        auto value_return = remove_nullable(return_map->get_value_type());
+        auto value_func = remove_nullable(func_map->get_value_type());
+
+        return is_nested_type_date_or_datetime_or_decimal(key_return, 
key_func) &&
+               is_nested_type_date_or_datetime_or_decimal(value_return, 
value_func);
+    }
+    case TYPE_STRUCT: {
+        const auto* return_struct = assert_cast<const 
DataTypeStruct*>(return_type_ptr.get());
+        const auto* func_struct = assert_cast<const 
DataTypeStruct*>(func_return_type_ptr.get());
+
+        auto return_elements = return_struct->get_elements();
+        auto func_elements = func_struct->get_elements();
+
+        if (return_elements.size() != func_elements.size()) {
+            return false;
+        }
+
+        for (size_t i = 0; i < return_elements.size(); i++) {
+            auto elem_return = remove_nullable(return_elements[i]);
+            auto elem_func = remove_nullable(func_elements[i]);
+
+            if (!is_nested_type_date_or_datetime_or_decimal(elem_return, 
elem_func)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    default:
+        return is_date_or_datetime_or_decimal(return_type_ptr, 
func_return_type_ptr);
     }
-    return false;
 }
+
 #include "common/compile_check_end.h"
 } // namespace doris::vectorized
diff --git a/be/src/vec/functions/function.h b/be/src/vec/functions/function.h
index fd0862c73d5..3dab7b782ae 100644
--- a/be/src/vec/functions/function.h
+++ b/be/src/vec/functions/function.h
@@ -39,7 +39,10 @@
 #include "vec/core/columns_with_type_and_name.h"
 #include "vec/core/types.h"
 #include "vec/data_types/data_type.h"
+#include "vec/data_types/data_type_array.h"
+#include "vec/data_types/data_type_map.h"
 #include "vec/data_types/data_type_nullable.h"
+#include "vec/data_types/data_type_struct.h"
 
 namespace doris::vectorized {
 
@@ -289,7 +292,7 @@ public:
                                ->get_nested_type()
                                ->get_primitive_type() == INVALID_TYPE) ||
               is_date_or_datetime_or_decimal(return_type, func_return_type) ||
-              is_array_nested_type_date_or_datetime_or_decimal(return_type, 
func_return_type))) {
+              is_nested_type_date_or_datetime_or_decimal(return_type, 
func_return_type))) {
             throw doris::Exception(
                     ErrorCode::INTERNAL_ERROR,
                     "function return type check failed, function_name={}, "
@@ -356,8 +359,8 @@ protected:
 private:
     bool is_date_or_datetime_or_decimal(const DataTypePtr& return_type,
                                         const DataTypePtr& func_return_type) 
const;
-    bool is_array_nested_type_date_or_datetime_or_decimal(
-            const DataTypePtr& return_type, const DataTypePtr& 
func_return_type) const;
+    bool is_nested_type_date_or_datetime_or_decimal(const DataTypePtr& 
return_type,
+                                                    const DataTypePtr& 
func_return_type) const;
 };
 
 /// Previous function interface.
diff --git 
a/regression-test/data/query_p0/sql_functions/struct_functions/sql/q02.out 
b/regression-test/data/query_p0/sql_functions/struct_functions/sql/q02.out
new file mode 100644
index 00000000000..9f2da3af2f2
--- /dev/null
+++ b/regression-test/data/query_p0/sql_functions/struct_functions/sql/q02.out
@@ -0,0 +1,73 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !q02 --
+0
+
+-- !q02_2 --
+0
+
+-- !q02_3 --
+0
+
+-- !q02_4 --
+1
+
+-- !q02_5 --
+["2023-01-01", "2023-12-31"]
+
+-- !q02_6 --
+[["2023-01-01", "2023-12-31"]]
+
+-- !q02_7 --
+["2023-01-01 10:00:00", "2023-12-31 23:59:59"]
+
+-- !q02_8 --
+[["2023-01-01 10:00:00", "2023-12-31 23:59:59"]]
+
+-- !q02_9 --
+[123.45, 678.90]
+
+-- !q02_10 --
+[[123.45, 678.90]]
+
+-- !q02_11 --
+[["2023-01-01"], ["2023-12-31"]]
+
+-- !q02_12 --
+[[["2023-01-01"], ["2023-12-31"]]]
+
+-- !q02_13 --
+{"user1":"2023-01-01", "user2":"2023-12-31"}
+
+-- !q02_14 --
+{"first":"2023-01-01", "second":"2023-12-31"}
+
+-- !q02_15 --
+{"literal":"2023-01-01", "from_table":"2023-01-01"}
+
+-- !q02_16 --
+{"2023-01-01 10:00:00":123.45, "2023-12-31 23:59:59":678.90}
+
+-- !q02_17 --
+{"first":123.45, "second":678.90}
+
+-- !q02_18 --
+{"user1":["2023-01-01", "2023-12-31"], "user2":["2023-06-15"]}
+
+-- !q02_19 --
+{"first":["2023-01-01", "2023-12-31"], "second":["2023-06-15"]}
+
+-- !q02_20 --
+{"col1":"Alice", "col2":"1990-01-01", "col3":60000.00}
+
+-- !q02_21 --
+{"col1":{"name":"Charlie", "birth_date":"1985-05-15", "salary":50000.00}}
+
+-- !q02_22 --
+{"col1":"outer", "col2":{"name":"Charlie", "birth_date":"1985-05-15", 
"salary":50000.00}, "col3":"literal", "col4":{"col1":"David", 
"col2":"1988-03-20", "col3":80000.00}}
+
+-- !q02_23 --
+{"col1":{"2023-01-01":"2023-01-01 10:00:00.123456", "2023-12-31":"2023-12-31 
23:59:59.999999"}, "col2":{"user1":"2023-01-01", "user2":"2023-12-31"}}
+
+-- !q02_24 --
+{"col1":{"user1":"2023-01-01", "user2":"2023-12-31"}, "col2":{"2023-01-01 
10:00:00":123.45, "2023-12-31 23:59:59":678.90}}
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/struct_functions/sql/q02.sql 
b/regression-test/suites/query_p0/sql_functions/struct_functions/sql/q02.sql
new file mode 100644
index 00000000000..fc1b177ef6c
--- /dev/null
+++ b/regression-test/suites/query_p0/sql_functions/struct_functions/sql/q02.sql
@@ -0,0 +1,72 @@
+use regression_test_query_p0_sql_functions_struct_functions;
+
+DROP TABLE IF EXISTS `test_nested_type_compatibility`;
+
+-- Create a simple table to test nested type compatibility
+CREATE TABLE IF NOT EXISTS `test_nested_type_compatibility` (
+  `id` int,
+  -- Test cases for ARRAY types
+  `arr_date` array<date>,
+  `arr_datetime` array<datetime>,
+  `arr_decimal` array<decimal(10,2)>,
+  `arr_nested_date` array<array<date>>,
+  
+  -- Test cases for MAP types
+  `map_string_date` map<string, date>,
+  `map_datetime_decimal` map<datetime, decimal(10,2)>,
+  `map_nested_date` map<string, array<date>>,
+  
+  -- Test cases for STRUCT types
+  `struct_mixed` struct<name:string, birth_date:date, salary:decimal(10,2)>,
+  `struct_map` struct<col: map<date, datetime(6)>, col1: map<string, date>>
+) ENGINE=OLAP
+DUPLICATE KEY(`id`)
+DISTRIBUTED BY HASH(`id`) BUCKETS 1
+PROPERTIES (
+    "replication_allocation" = "tag.location.default: 1"
+);
+
+-- Insert test data
+INSERT INTO `test_nested_type_compatibility` VALUES
+(1, 
+ -- ARRAY data
+ ['2023-01-01', '2023-12-31'], 
+ ['2023-01-01 10:00:00', '2023-12-31 23:59:59'], 
+ [123.45, 678.90], 
+ [['2023-01-01'], ['2023-12-31']],
+ 
+ -- MAP data
+ {'user1': '2023-01-01', 'user2': '2023-12-31'}, 
+ {'2023-01-01 10:00:00': 123.45, '2023-12-31 23:59:59': 678.90}, 
+ {'user1': ['2023-01-01', '2023-12-31'], 'user2': ['2023-06-15']},
+ 
+ -- STRUCT data
+ struct('Charlie', '1985-05-15', 50000.00), 
+ struct(map('2023-01-01', '2023-01-01 10:00:00.123456', '2023-12-31', 
'2023-12-31 23:59:59.999999'), map('user1', '2023-01-01', 'user2', 
'2023-12-31'))
+);
+
+-- Test ARRAY constructors with date types - should trigger compatibility check
+select array('2023-01-01', '2023-12-31') from test_nested_type_compatibility 
where id = 1;
+select array(arr_date) from test_nested_type_compatibility where id = 1;
+select array('2023-01-01 10:00:00', '2023-12-31 23:59:59') from 
test_nested_type_compatibility where id = 1;
+select array(arr_datetime) from test_nested_type_compatibility where id = 1;
+select array(123.45, 678.90) from test_nested_type_compatibility where id = 1;
+select array(arr_decimal) from test_nested_type_compatibility where id = 1;
+select array(array('2023-01-01'), array('2023-12-31')) from 
test_nested_type_compatibility where id = 1;
+select array(arr_nested_date) from test_nested_type_compatibility where id = 1;
+
+-- Test MAP constructors with date types - should trigger compatibility check
+select map('user1', '2023-01-01', 'user2', '2023-12-31') from 
test_nested_type_compatibility where id = 1;
+select map('first', map_string_date['user1'], 'second', 
map_string_date['user2']) from test_nested_type_compatibility where id = 1;
+select map('literal', '2023-01-01', 'from_table', map_string_date['user1']) 
from test_nested_type_compatibility where id = 1;
+select map('2023-01-01 10:00:00', 123.45, '2023-12-31 23:59:59', 678.90) from 
test_nested_type_compatibility where id = 1;
+select map('first', map_datetime_decimal['2023-01-01 10:00:00'], 'second', 
map_datetime_decimal['2023-12-31 23:59:59']) from 
test_nested_type_compatibility where id = 1;
+select map('user1', array('2023-01-01', '2023-12-31'), 'user2', 
array('2023-06-15')) from test_nested_type_compatibility where id = 1;
+select map('first', map_nested_date['user1'], 'second', 
map_nested_date['user2']) from test_nested_type_compatibility where id = 1;
+
+-- Test STRUCT constructors with date types - should trigger compatibility 
check
+select struct('Alice', '1990-01-01', 60000.00) from 
test_nested_type_compatibility where id = 1;
+select struct(struct_mixed) from test_nested_type_compatibility where id = 1;
+select struct('outer', struct_mixed, 'literal', struct('David', '1988-03-20', 
80000.00)) from test_nested_type_compatibility where id = 1;
+select struct(map('2023-01-01', '2023-01-01 10:00:00.123456', '2023-12-31', 
'2023-12-31 23:59:59.999999'), map('user1', '2023-01-01', 'user2', 
'2023-12-31')) from test_nested_type_compatibility where id = 1;
+select struct(map_string_date, map_datetime_decimal) from 
test_nested_type_compatibility where id = 1;


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

Reply via email to