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 ab20858bb65 [fix](function) Prevent cross-row rounding in 
months_between (#67935)
ab20858bb65 is described below

commit ab20858bb65d0f212b15de8eb5b1c2732080f2e4
Author: Mryange <[email protected]>
AuthorDate: Tue Sep 15 14:30:56 2026 +0800

    [fix](function) Prevent cross-row rounding in months_between (#67935)
    
    When both date arguments are constants and `round_off` varies by row, a
    rounded result was reused by later rows, causing `round_off = false`
    rows to lose precision. Use `ColumnView` for consistent constant,
    regular, and nullable column access, and calculate an independent raw
    value for every row before optional rounding.
    ### 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        | 79 ++++++----------------
 .../string_functions/test_months_between.out       | 24 +++++--
 .../string_functions/test_months_between.groovy    |  1 +
 3 files changed, 40 insertions(+), 64 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 9743ef79c55..15643880c0a 100644
--- a/be/src/exprs/function/function_date_or_datetime_computation.h
+++ b/be/src/exprs/function/function_date_or_datetime_computation.h
@@ -41,6 +41,7 @@
 #include "core/block/columns_with_type_and_name.h"
 #include "core/column/column.h"
 #include "core/column/column_const.h"
+#include "core/column/column_execute_util.h"
 #include "core/column/column_nullable.h"
 #include "core/column/column_vector.h"
 #include "core/data_type/data_type.h"
@@ -1682,26 +1683,23 @@ public:
         CHECK_EQ(arguments.size(), 3);
         auto res = ColumnFloat64::create();
 
-        bool date_consts[2];
-        date_consts[0] = 
is_column_const(*block.get_by_position(arguments[0]).column);
-        date_consts[1] = 
is_column_const(*block.get_by_position(arguments[1]).column);
-        ColumnPtr date_cols[2];
-        // convert const columns to full columns if necessary
-        default_preprocess_parameter_columns(date_cols, date_consts, {0, 1}, 
block, arguments);
-
-        const auto& [col3, col3_const] =
-                unpack_if_const(block.get_by_position(arguments[2]).column);
-        const auto& round_off_col = *assert_cast<const 
ColumnBool*>(col3.get());
-
         auto date_type = 
block.get_by_position(arguments[0]).type->get_primitive_type();
         DORIS_CHECK_EQ(date_type, 
block.get_by_position(arguments[1]).type->get_primitive_type());
+        auto round_off =
+                
ColumnView<TYPE_BOOLEAN>::create(block.get_by_position(arguments[2]).column);
         if (date_type == TYPE_TIMESTAMP_NS) {
-            execute_typed<ColumnTimeStampNs>(input_rows_count, date_cols, 
date_consts, col3_const,
-                                             round_off_col, *res);
+            auto date1 = ColumnView<TYPE_TIMESTAMP_NS>::create(
+                    block.get_by_position(arguments[0]).column);
+            auto date2 = ColumnView<TYPE_TIMESTAMP_NS>::create(
+                    block.get_by_position(arguments[1]).column);
+            execute_typed(input_rows_count, date1, date2, round_off, *res);
         } else {
             DORIS_CHECK_EQ(date_type, TYPE_DATEV2);
-            execute_typed<ColumnDateV2>(input_rows_count, date_cols, 
date_consts, col3_const,
-                                        round_off_col, *res);
+            auto date1 =
+                    
ColumnView<TYPE_DATEV2>::create(block.get_by_position(arguments[0]).column);
+            auto date2 =
+                    
ColumnView<TYPE_DATEV2>::create(block.get_by_position(arguments[1]).column);
+            execute_typed(input_rows_count, date1, date2, round_off, *res);
         }
 
         block.replace_by_position(result, std::move(res));
@@ -1709,50 +1707,15 @@ public:
     }
 
 private:
-    template <typename DateColumn>
-    static void execute_typed(size_t input_rows_count, const ColumnPtr 
(&date_cols)[2],
-                              const bool (&date_consts)[2], bool 
round_off_const,
-                              const ColumnBool& round_off_col, ColumnFloat64& 
res) {
-        const auto& date1_col = *assert_cast<const 
DateColumn*>(date_cols[0].get());
-        const auto& date2_col = *assert_cast<const 
DateColumn*>(date_cols[1].get());
-        if (date_consts[0] && date_consts[1]) {
-            execute_vector<true, false>(input_rows_count, date1_col, 
date2_col, round_off_col, res);
-        } else if (round_off_const) {
-            execute_vector<false, true>(input_rows_count, date1_col, 
date2_col, round_off_col, res);
-        } else {
-            execute_vector<false, false>(input_rows_count, date1_col, 
date2_col, round_off_col,
-                                         res);
-        }
-    }
-
-    template <bool is_date_const, bool is_round_off_const, typename DateColumn>
-    static void execute_vector(const size_t input_rows_count, const 
DateColumn& date1_col,
-                               const DateColumn& date2_col, const ColumnBool& 
round_off_col,
-                               ColumnFloat64& res) {
+    template <PrimitiveType DateType>
+    static void execute_typed(size_t input_rows_count, const 
ColumnView<DateType>& date1,
+                              const ColumnView<DateType>& date2,
+                              const ColumnView<TYPE_BOOLEAN>& round_off, 
ColumnFloat64& res) {
         res.reserve(input_rows_count);
-        double months_between;
-        bool round_off;
-
-        if constexpr (is_date_const) {
-            auto dtv1 = date_v2_from_date_like(date1_col.get_element(0));
-            auto dtv2 = date_v2_from_date_like(date2_col.get_element(0));
-            months_between = calc_months_between(dtv1, dtv2);
-        }
-
-        if constexpr (is_round_off_const) {
-            round_off = round_off_col.get_element(0);
-        }
-
-        for (int i = 0; i < input_rows_count; ++i) {
-            if constexpr (!is_date_const) {
-                auto dtv1 = date_v2_from_date_like(date1_col.get_element(i));
-                auto dtv2 = date_v2_from_date_like(date2_col.get_element(i));
-                months_between = calc_months_between(dtv1, dtv2);
-            }
-            if constexpr (!is_round_off_const) {
-                round_off = round_off_col.get_element(i);
-            }
-            if (round_off) {
+        for (size_t i = 0; i < input_rows_count; ++i) {
+            auto months_between = 
calc_months_between(date_v2_from_date_like(date1.value_at(i)),
+                                                      
date_v2_from_date_like(date2.value_at(i)));
+            if (round_off.value_at(i)) {
                 months_between = round_months_between(months_between);
             }
             res.insert_value(months_between);
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
index a2272c940b7..55f6fe782c6 100644
--- 
a/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_months_between.out
@@ -111,8 +111,8 @@
 -1     -1      -1      -1
 -1.90322581    -0.03225806     -1.90322581     -0.03225806
 -1.90322581    0.90322581      \N      \N
--12.96774194   \N      -12.96774193548387      \N
--13.87096774   0.87096774      -13.87096774193548      \N
+-12.96774194   \N      -12.967741935483872     \N
+-13.87096774   0.87096774      -13.870967741935484     \N
 -6     \N      \N      4.93548387
 
 -- !const_other_not_nullable --
@@ -122,8 +122,8 @@
 -1.4516129     -0.5483871      -1.4516129      -0.5483871
 -1.90322581    -0.03225806     -1.90322581     -0.03225806
 -1.90322581    0.90322581      -1.903225806451613      0.9032258064516129
--12.96774194   10.96774194     -12.96774193548387      10.96774193548387
--13.87096774   0.87096774      -13.87096774193548      0.8709677419354839
+-12.96774194   10.96774194     -12.967741935483872     10.967741935483872
+-13.87096774   0.87096774      -13.870967741935484     0.8709677419354839
 -6     4.93548387      -6      4.93548387
 -971.96774194  -1441   -971.96774194   -1441
 
@@ -158,8 +158,8 @@
 -1.4516129     -1.4516129
 -1.90322581    -1.903225806451613
 -1.90322581    -1.90322581
--12.96774194   -12.96774193548387
--13.87096774   -13.87096774193548
+-12.96774194   -12.967741935483872
+-13.87096774   -13.870967741935484
 -6     -6
 -971.96774194  -971.96774194
 
@@ -175,6 +175,18 @@
 -1
 -1
 
+-- !const_dates_round_off --
+1      true    2.03225806
+10     false   2.032258064516129
+2      false   2.032258064516129
+3      true    2.03225806
+4      false   2.032258064516129
+5      true    2.03225806
+6      false   2.032258064516129
+7      true    2.03225806
+8      false   2.032258064516129
+9      true    2.03225806
+
 -- !const23 --
 -0.03225806
 -0.5483871
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
index aa760d97838..937d01475e8 100644
--- 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_months_between.groovy
@@ -82,6 +82,7 @@ suite("test_months_between") {
     order_qt_const_partial_nullable_no_null "select 
months_between('2020-01-01', nullable('2020-02-01')), 
months_between(nullable('2020-01-01'), '2020-02-01'), 
months_between('2020-01-01', nullable('2020-02-01 00:00:00'), nullable(true)) 
from months_between_args"
     order_qt_const1 "select months_between('2020-01-01', date2_not_null), 
months_between('2020-01-01', date2_not_null, round_off_not_null) from 
months_between_args"
     order_qt_const12 "select months_between('2020-01-01', '2020-02-01', 
round_off_not_null) from months_between_args"
+    order_qt_const_dates_round_off "select k0, round_off_not_null, 
months_between('2020-12-26', '2020-10-25', round_off_not_null) from 
months_between_args order by k0"
     order_qt_const23 "select months_between(date1_not_null, '2020-02-01', 
true) from months_between_args"
     order_qt_const3 "select months_between(date1_not_null, date2_not_null, 
true) from months_between_args"
 


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

Reply via email to