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

Mryange 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 6bcef49165b [fix](function) Handle relative day constants and nulls 
(#68001)
6bcef49165b is described below

commit 6bcef49165be8ed5a0828dd85ef4702b07ca17fe
Author: Mryange <[email protected]>
AuthorDate: Fri Sep 18 14:52:24 2026 +0800

    [fix](function) Handle relative day constants and nulls (#68001)
    
    `next_day` and `previous_day` reused a mutated date value when the date
    argument was constant, producing cumulative cross-row results. Nullable
    inputs were also unpacked before execution, causing NULL weekday rows to
    be parsed as empty strings and fail the entire query. The functions now
    use `ColumnView` to handle constant and nullable columns directly,
    compute each row from an independent date value, propagate NULL results,
    and continue rejecting invalid non-NULL weekdays.
    
    
    ### Release note
    
    None
    
    ### Check List (For Author)
    
    - Test <!-- At least one of them must be included. -->
        - [ ] Regression test
        - [ ] Unit Test
        - [ ] Manual test (add detailed scripts or steps below)
        - [ ] No need to test or manual test. Explain why:
    - [ ] This is a refactor/code format and no logic has been changed.
            - [ ] Previous test can cover this change.
            - [ ] No code files have been changed.
            - [ ] Other reason <!-- Add your reason?  -->
    
    - Behavior changed:
        - [ ] No.
        - [ ] Yes. <!-- Explain the behavior change -->
    
    - Does this need documentation?
        - [ ] No.
    - [ ] Yes. <!-- Add document PR link here. eg:
    https://github.com/apache/doris-website/pull/1214 -->
    
    ### Check List (For Reviewer who merge this PR)
    
    - [ ] Confirm the release note
    - [ ] Confirm test cases
    - [ ] Confirm document
    - [ ] Add branch pick label <!-- Add branch pick label that this PR
    should merge into -->
---
 .../function_date_or_datetime_computation.h        | 90 ++++++++++------------
 be/test/exprs/function/function_time_test.cpp      | 18 +++++
 .../string_functions/test_next_day.out             |  8 +-
 .../string_functions/test_previous_day.out         |  4 +-
 4 files changed, 65 insertions(+), 55 deletions(-)

diff --git a/be/src/exprs/function/function_date_or_datetime_computation.h 
b/be/src/exprs/function/function_date_or_datetime_computation.h
index 15643880c0a..a056fe482e5 100644
--- a/be/src/exprs/function/function_date_or_datetime_computation.h
+++ b/be/src/exprs/function/function_date_or_datetime_computation.h
@@ -1759,33 +1759,47 @@ public:
     String get_name() const override { return name; }
     size_t get_number_of_arguments() const override { return 2; }
     DataTypePtr get_return_type_impl(const ColumnsWithTypeAndName& arguments) 
const override {
-        return std::make_shared<DataTypeDateV2>();
+        auto result = std::make_shared<DataTypeDateV2>();
+        if (arguments[0].type->is_nullable() || 
arguments[1].type->is_nullable()) {
+            return make_nullable(std::move(result));
+        }
+        return result;
     }
 
+    bool use_default_implementation_for_nulls() const override { return false; 
}
+
     Status execute_impl(FunctionContext* context, Block& block, const 
ColumnNumbers& arguments,
                         uint32_t result, size_t input_rows_count) const 
override {
         CHECK_EQ(arguments.size(), 2);
         auto res = ColumnDateV2::create();
         res->reserve(input_rows_count);
-        const auto& [left_col, left_const] =
-                unpack_if_const(block.get_by_position(arguments[0]).column);
-        const auto& [right_col, right_const] =
-                unpack_if_const(block.get_by_position(arguments[1]).column);
-        const auto& week_col = *assert_cast<const 
ColumnString*>(right_col.get());
+        const auto& date_column = block.get_by_position(arguments[0]).column;
+        const auto& week_column = block.get_by_position(arguments[1]).column;
+        const auto result_nullable = 
block.get_by_position(result).type->is_nullable();
+        ColumnUInt8::MutablePtr null_map;
+        if (result_nullable) {
+            null_map = ColumnUInt8::create(input_rows_count, 0);
+        }
         Status status;
+        auto* null_map_data = null_map ? &null_map->get_data() : nullptr;
         auto date_type = 
block.get_by_position(arguments[0]).type->get_primitive_type();
         if (date_type == TYPE_TIMESTAMP_NS) {
-            status = execute_typed<ColumnTimeStampNs>(input_rows_count, 
left_col, left_const,
-                                                      right_const, week_col, 
*res);
+            status = execute_vector<TYPE_TIMESTAMP_NS>(input_rows_count, 
date_column, week_column,
+                                                       *res, null_map_data);
         } else {
             DORIS_CHECK_EQ(date_type, TYPE_DATEV2);
-            status = execute_typed<ColumnDateV2>(input_rows_count, left_col, 
left_const,
-                                                 right_const, week_col, *res);
+            status = execute_vector<TYPE_DATEV2>(input_rows_count, 
date_column, week_column, *res,
+                                                 null_map_data);
         }
         if (!status.ok()) {
             return status;
         }
-        block.replace_by_position(result, std::move(res));
+        if (result_nullable) {
+            block.replace_by_position(result,
+                                      ColumnNullable::create(std::move(res), 
std::move(null_map)));
+        } else {
+            block.replace_by_position(result, std::move(res));
+        }
         return Status::OK();
     }
 
@@ -1813,48 +1827,26 @@ private:
         return Status::OK();
     }
 
-    template <typename DateColumn>
-    static Status execute_typed(size_t input_rows_count, const ColumnPtr& 
left_col, bool left_const,
-                                bool right_const, const ColumnString& week_col,
-                                ColumnDateV2& res_col) {
-        const auto& date_col = *assert_cast<const DateColumn*>(left_col.get());
-        if (left_const) {
-            return execute_vector<true, false>(input_rows_count, date_col, 
week_col, res_col);
-        } else if (right_const) {
-            return execute_vector<false, true>(input_rows_count, date_col, 
week_col, res_col);
-        }
-        return execute_vector<false, false>(input_rows_count, date_col, 
week_col, res_col);
-    }
-
-    template <bool left_const, bool right_const, typename DateColumn>
-    static Status execute_vector(size_t input_rows_count, const DateColumn& 
left_col,
-                                 const ColumnString& right_col, ColumnDateV2& 
res_col) {
-        DateV2Value<DateV2ValueType> dtv;
-        int week_day;
-        if constexpr (left_const) {
-            dtv = date_v2_from_date_like(left_col.get_element(0));
-        }
-        if constexpr (right_const) {
-            auto week = right_col.get_data_at(0);
-            week_day = day_of_week(week);
+    template <PrimitiveType DateType>
+    static Status execute_vector(size_t input_rows_count, const ColumnPtr& 
date_column,
+                                 const ColumnPtr& week_column, ColumnDateV2& 
res_col,
+                                 NullMap* null_map) {
+        const auto date_view = ColumnView<DateType>::create(date_column);
+        const auto week_view = ColumnView<TYPE_STRING>::create(week_column);
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            if (date_view.is_null_at(i) || week_view.is_null_at(i)) {
+                DORIS_CHECK(null_map != nullptr);
+                (*null_map)[i] = 1;
+                res_col.insert_default();
+                continue;
+            }
+            auto dtv = date_v2_from_date_like(date_view.value_at(i));
+            auto week = week_view.value_at(i);
+            auto week_day = day_of_week(week);
             if (week_day == 0) {
                 return Status::InvalidArgument("Function {} failed to parse 
weekday: {}", name,
                                                week);
             }
-        }
-
-        for (size_t i = 0; i < input_rows_count; ++i) {
-            if constexpr (!left_const) {
-                dtv = date_v2_from_date_like(left_col.get_element(i));
-            }
-            if constexpr (!right_const) {
-                auto week = right_col.get_data_at(i);
-                week_day = day_of_week(week);
-                if (week_day == 0) {
-                    return Status::InvalidArgument("Function {} failed to 
parse weekday: {}", name,
-                                                   week);
-                }
-            }
             RETURN_IF_ERROR(compute_relative_day(dtv, week_day));
             res_col.insert_value(dtv);
         }
diff --git a/be/test/exprs/function/function_time_test.cpp 
b/be/test/exprs/function/function_time_test.cpp
index 6104ebd9954..7f44e5b0904 100644
--- a/be/test/exprs/function/function_time_test.cpp
+++ b/be/test/exprs/function/function_time_test.cpp
@@ -1817,6 +1817,24 @@ TEST(VTimestampFunctionsTest, next_day_test) {
     }
 }
 
+TEST(VTimestampFunctionsTest, relative_day_nullable_test) {
+    const InputTypeSet nullable_input_types = {Nullable 
{PrimitiveType::TYPE_DATEV2},
+                                               Nullable 
{PrimitiveType::TYPE_VARCHAR}};
+    const DataSet nullable_data_set = {
+            {{std::string("2024-01-01"), std::string("MON")}, 
std::string("2024-01-08")},
+            {{Null(), Null()}, Null()},
+            {{std::string("2024-01-01"), Null()}, Null()},
+            {{Null(), std::string("MON")}, Null()}};
+    static_cast<void>(check_function<DataTypeDateV2, true>("next_day", 
nullable_input_types,
+                                                           nullable_data_set));
+    static_cast<void>(check_function<DataTypeDateV2, true>(
+            "previous_day", nullable_input_types,
+            {{{std::string("2024-01-01"), std::string("MON")}, 
std::string("2023-12-25")},
+             {{Null(), Null()}, Null()},
+             {{std::string("2024-01-01"), Null()}, Null()},
+             {{Null(), std::string("MON")}, Null()}}));
+}
+
 TEST(VTimestampFunctionsTest, from_iso8601_date) {
     std::string func_name = "from_iso8601_date";
     InputTypeSet input_types = {PrimitiveType::TYPE_VARCHAR};
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
index 8bdb8678993..ae54fc34098 100644
--- 
a/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_next_day.out
@@ -116,6 +116,8 @@
 
 -- !const_other_nullable --
 2025-01-02
+2025-01-02
+2025-01-02
 2025-01-03
 2025-01-03
 2025-01-05
@@ -126,8 +128,6 @@
 2025-01-08
 2025-01-08
 2025-01-08
-2025-01-09
-2025-01-09
 
 -- !const_other_not_nullable --
 0000-01-06
@@ -164,6 +164,8 @@
 
 -- !const_partial_nullable_no_null --
 2025-01-02
+2025-01-02
+2025-01-02
 2025-01-03
 2025-01-03
 2025-01-05
@@ -174,8 +176,6 @@
 2025-01-08
 2025-01-08
 2025-01-08
-2025-01-09
-2025-01-09
 
 -- !wrong_date --
 \N
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_previous_day.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_previous_day.out
index 61027f2cf47..874ec15d85a 100644
--- 
a/regression-test/data/query_p0/sql_functions/string_functions/test_previous_day.out
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_previous_day.out
@@ -127,7 +127,7 @@
 2024-12-30
 2024-12-25
 2024-12-26
-2024-12-24
+2024-12-31
 2024-12-25
 2024-12-25
 2024-12-26
@@ -175,7 +175,7 @@
 2024-12-30
 2024-12-25
 2024-12-26
-2024-12-24
+2024-12-31
 2024-12-25
 2024-12-25
 2024-12-26


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

Reply via email to