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]