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

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


The following commit(s) were added to refs/heads/branch-2.0 by this push:
     new f86376a2674 [branch-2.0](function) fix some date functions #26475 
#35080 #35104 (#35169)
f86376a2674 is described below

commit f86376a267445b3857d4fc74d2d55c1eff1833c5
Author: zclllyybb <[email protected]>
AuthorDate: Thu May 23 20:01:25 2024 +0800

    [branch-2.0](function) fix some date functions #26475 #35080 #35104 (#35169)
    
    Co-authored-by: zhiqiang <[email protected]>
---
 be/src/common/status.h                             |  15 ++
 .../function_date_or_datetime_computation.h        |  39 ++-
 be/src/vec/runtime/vdatetime_value.cpp             |  74 +++---
 be/src/vec/runtime/vdatetime_value.h               |  55 +++-
 .../data/correctness_p0/test_date_diff.out         |  21 ++
 .../test_date_or_datetime_computation_negative.out |  91 +++++++
 .../correctness/test_date_function_const.groovy    |   7 +-
 .../suites/correctness_p0/test_date_diff.groovy    |  32 +++
 .../datetime_functions/test_date_function.groovy   |  25 +-
 ...st_date_or_datetime_computation_negative.groovy | 276 +++++++++++++++++++++
 10 files changed, 569 insertions(+), 66 deletions(-)

diff --git a/be/src/common/status.h b/be/src/common/status.h
index 32f60e72bae..39ff9bad581 100644
--- a/be/src/common/status.h
+++ b/be/src/common/status.h
@@ -552,6 +552,21 @@ inline std::string Status::to_string() const {
         }                               \
     } while (false)
 
+#define PROPAGATE_FALSE(stmt)                     \
+    do {                                          \
+        if (UNLIKELY(!static_cast<bool>(stmt))) { \
+            return false;                         \
+        }                                         \
+    } while (false)
+
+#define THROW_IF_ERROR(stmt)            \
+    do {                                \
+        Status _status_ = (stmt);       \
+        if (UNLIKELY(!_status_.ok())) { \
+            throw Exception(_status_);  \
+        }                               \
+    } while (false)
+
 #define RETURN_ERROR_IF_NON_VEC \
     return Status::NotSupported("Non-vectorized engine is not supported since 
Doris 2.0.");
 
diff --git a/be/src/vec/functions/function_date_or_datetime_computation.h 
b/be/src/vec/functions/function_date_or_datetime_computation.h
index 609113a0bd5..b1ccce6b063 100644
--- a/be/src/vec/functions/function_date_or_datetime_computation.h
+++ b/be/src/vec/functions/function_date_or_datetime_computation.h
@@ -27,6 +27,8 @@
 #include <type_traits>
 #include <utility>
 
+#include "common/compiler_util.h"
+#include "common/exception.h"
 #include "common/logging.h"
 #include "common/status.h"
 #include "fmt/format.h"
@@ -413,7 +415,11 @@ struct DateTimeOp {
             // otherwise it will be implicitly converted to bool, causing the 
rvalue to fail to match the lvalue.
             // the same goes for the following.
             vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], 
invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, vec_from0[i], vec_from1[i]);
+            }
         }
     }
 
@@ -438,7 +444,11 @@ struct DateTimeOp {
         bool invalid = true;
         for (size_t i = 0; i < size; ++i) {
             vec_to[i] = Transform::execute(vec_from0[i], vec_from1[i], 
invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, vec_from0[i], vec_from1[i]);
+            }
         }
     }
 
@@ -462,7 +472,11 @@ struct DateTimeOp {
         bool invalid = true;
         for (size_t i = 0; i < size; ++i) {
             vec_to[i] = Transform::execute(vec_from[i], delta, invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, vec_from[i], delta);
+            }
         }
     }
 
@@ -486,7 +500,11 @@ struct DateTimeOp {
 
         for (size_t i = 0; i < size; ++i) {
             vec_to[i] = Transform::execute(vec_from[i], delta, invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, vec_from[i], delta);
+            }
         }
     }
 
@@ -510,7 +528,11 @@ struct DateTimeOp {
 
         for (size_t i = 0; i < size; ++i) {
             vec_to[i] = Transform::execute(from, delta.get_int(i), invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, from, delta.get_int(i));
+            }
         }
     }
 
@@ -524,6 +546,7 @@ struct DateTimeOp {
             vec_to[i] = Transform::execute(from, delta[i], 
reinterpret_cast<bool&>(null_map[i]));
         }
     }
+
     static void constant_vector(const FromType1& from, PaddedPODArray<ToType>& 
vec_to,
                                 const PaddedPODArray<FromType2>& delta) {
         size_t size = delta.size();
@@ -532,7 +555,11 @@ struct DateTimeOp {
 
         for (size_t i = 0; i < size; ++i) {
             vec_to[i] = Transform::execute(from, delta[i], invalid);
-            DCHECK(!invalid);
+
+            if (UNLIKELY(invalid)) {
+                throw Exception(ErrorCode::OUT_OF_BOUND, "Operation {} {} {} 
out of range",
+                                Transform::name, from, delta[i]);
+            }
         }
     }
 };
diff --git a/be/src/vec/runtime/vdatetime_value.cpp 
b/be/src/vec/runtime/vdatetime_value.cpp
index 51cb87f100c..fa82f05c64f 100644
--- a/be/src/vec/runtime/vdatetime_value.cpp
+++ b/be/src/vec/runtime/vdatetime_value.cpp
@@ -42,8 +42,6 @@
 
 namespace doris::vectorized {
 
-static constexpr int s_days_in_month[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 
30, 31, 30, 31};
-
 static const char* s_ab_month_name[] = {"",    "Jan", "Feb", "Mar", "Apr", 
"May", "Jun",
                                         "Jul", "Aug", "Sep", "Oct", "Nov", 
"Dec", nullptr};
 
@@ -74,7 +72,7 @@ bool VecDateTimeValue::check_range(uint32_t year, uint32_t 
month, uint32_t day,
 
 bool VecDateTimeValue::check_date(uint32_t year, uint32_t month, uint32_t day) 
{
     if (month == 2 && day == 29 && doris::is_leap(year)) return false;
-    if (year > 9999 || month == 0 || month > 12 || day > 
s_days_in_month[month] || day == 0) {
+    if (year > 9999 || month == 0 || month > 12 || day > 
S_DAYS_IN_MONTH[month] || day == 0) {
         return true;
     }
     return false;
@@ -520,8 +518,8 @@ bool VecDateTimeValue::get_date_from_daynr(uint64_t daynr) {
         }
     }
     month = 1;
-    while (days_of_year > s_days_in_month[month]) {
-        days_of_year -= s_days_in_month[month];
+    while (days_of_year > S_DAYS_IN_MONTH[month]) {
+        days_of_year -= S_DAYS_IN_MONTH[month];
         month++;
     }
     day = days_of_year + leap_day;
@@ -1675,8 +1673,8 @@ bool VecDateTimeValue::date_add_interval(const 
TimeInterval& interval) {
             return false;
         }
         _month = (months % 12) + 1;
-        if (_day > s_days_in_month[_month]) {
-            _day = s_days_in_month[_month];
+        if (_day > S_DAYS_IN_MONTH[_month]) {
+            _day = S_DAYS_IN_MONTH[_month];
             if (_month == 2 && doris::is_leap(_year)) {
                 _day++;
             }
@@ -1927,11 +1925,11 @@ bool DateV2Value<T>::is_invalid(uint32_t year, uint32_t 
month, uint32_t day, uin
     if (only_time_part) {
         return false;
     }
-    if (year < MIN_YEAR || year > MAX_YEAR) {
+    if (year > MAX_YEAR) {
         return true;
     }
     if (month == 2 && day == 29 && doris::is_leap(year)) return false;
-    if (month == 0 || month > 12 || day > s_days_in_month[month] || day == 0) {
+    if (month == 0 || month > 12 || day > S_DAYS_IN_MONTH[month] || day == 0) {
         return true;
     }
     return false;
@@ -1956,7 +1954,7 @@ void DateV2Value<T>::format_datetime(uint32_t* date_val, 
bool* carry_bits) const
             date_val[1] += 1;
             carry_bits[2] = true;
         }
-    } else if (date_val[2] == s_days_in_month[date_val[1]] + 1 && 
carry_bits[3]) {
+    } else if (date_val[2] == S_DAYS_IN_MONTH[date_val[1]] + 1 && 
carry_bits[3]) {
         date_val[2] = 1;
         date_val[1] += 1;
         carry_bits[2] = true;
@@ -2925,8 +2923,8 @@ bool DateV2Value<T>::get_date_from_daynr(uint64_t daynr) {
             }
         }
         month = 1;
-        while (days_of_year > s_days_in_month[month]) {
-            days_of_year -= s_days_in_month[month];
+        while (days_of_year > S_DAYS_IN_MONTH[month]) {
+            days_of_year -= S_DAYS_IN_MONTH[month];
             month++;
         }
         day = days_of_year + leap_day;
@@ -2983,30 +2981,27 @@ bool DateV2Value<T>::date_add_interval(const 
TimeInterval& interval, DateV2Value
         to_value.set_time(seconds / 3600, (seconds / 60) % 60, seconds % 60, 
microseconds);
     } else if constexpr (unit == YEAR) {
         // This only change year information
-        to_value.template set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ + 
interval.year);
-        if (to_value.year() > 9999) {
-            return false;
-        }
+        PROPAGATE_FALSE(to_value.template 
set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ +
+                                                                        
interval.year));
         if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 &&
             !doris::is_leap(to_value.year())) {
-            to_value.template set_time_unit<TimeUnit::DAY>(28);
+            // add year. so if from Leap Year to Equal Year, use last day of 
Feb(29 to 28)
+            PROPAGATE_FALSE(to_value.template 
set_time_unit<TimeUnit::DAY>(28));
         }
     } else if constexpr (unit == QUARTER || unit == MONTH || unit == 
YEAR_MONTH) {
         // This will change month and year information, maybe date.
         int64_t months = date_v2_value_.year_ * 12 + date_v2_value_.month_ - 1 
+
                          12 * interval.year + interval.month;
-        to_value.template set_time_unit<TimeUnit::YEAR>(months / 12);
         if (months < 0) {
             return false;
         }
-        if (to_value.year() > MAX_YEAR) {
-            return false;
-        }
-        to_value.template set_time_unit<TimeUnit::MONTH>((months % 12) + 1);
-        if (date_v2_value_.day_ > s_days_in_month[to_value.month()]) {
-            date_v2_value_.day_ = s_days_in_month[to_value.month()];
+        PROPAGATE_FALSE(to_value.template set_time_unit<TimeUnit::YEAR>(months 
/ 12));
+        PROPAGATE_FALSE(to_value.template 
set_time_unit<TimeUnit::MONTH>((months % 12) + 1));
+        if (date_v2_value_.day_ > S_DAYS_IN_MONTH[to_value.month()]) {
+            date_v2_value_.day_ = S_DAYS_IN_MONTH[to_value.month()];
             if (to_value.month() == 2 && doris::is_leap(to_value.year())) {
-                to_value.template 
set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1);
+                PROPAGATE_FALSE(
+                        to_value.template 
set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1));
             }
         }
     }
@@ -3057,30 +3052,27 @@ bool DateV2Value<T>::date_add_interval(const 
TimeInterval& interval) {
         }
     } else if constexpr (unit == YEAR) {
         // This only change year information
-        this->template set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ + 
interval.year);
-        if (this->year() > 9999) {
-            return false;
-        }
+        PROPAGATE_FALSE(
+                this->template 
set_time_unit<TimeUnit::YEAR>(date_v2_value_.year_ + interval.year));
         if (date_v2_value_.month_ == 2 && date_v2_value_.day_ == 29 &&
             !doris::is_leap(this->year())) {
-            this->template set_time_unit<TimeUnit::DAY>(28);
+            // add year. so if from Leap Year to Equal Year, use last day of 
Feb(29 to 28)
+            PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::DAY>(28));
         }
     } else if constexpr (unit == QUARTER || unit == MONTH || unit == 
YEAR_MONTH) {
         // This will change month and year information, maybe date.
         int64_t months = date_v2_value_.year_ * 12 + date_v2_value_.month_ - 1 
+
                          12 * interval.year + interval.month;
-        this->template set_time_unit<TimeUnit::YEAR>(months / 12);
         if (months < 0) {
             return false;
         }
-        if (this->year() > MAX_YEAR) {
-            return false;
-        }
-        this->template set_time_unit<TimeUnit::MONTH>((months % 12) + 1);
-        if (date_v2_value_.day_ > s_days_in_month[this->month()]) {
-            date_v2_value_.day_ = s_days_in_month[this->month()];
+        PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::YEAR>(months / 
12));
+        PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::MONTH>((months 
% 12) + 1));
+        if (date_v2_value_.day_ > S_DAYS_IN_MONTH[this->month()]) {
+            date_v2_value_.day_ = S_DAYS_IN_MONTH[this->month()];
             if (this->month() == 2 && doris::is_leap(this->year())) {
-                this->template 
set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1);
+                PROPAGATE_FALSE(
+                        this->template 
set_time_unit<TimeUnit::DAY>(date_v2_value_.day_ + 1));
             }
         }
     }
@@ -3109,13 +3101,13 @@ bool DateV2Value<T>::date_set_interval(const 
TimeInterval& interval) {
         }
     } else if constexpr (unit == YEAR) {
         this->set_time(0, 1, 1, 0, 0, 0, 0);
-        this->template set_time_unit<TimeUnit::YEAR>(interval.year);
+        PROPAGATE_FALSE(this->template 
set_time_unit<TimeUnit::YEAR>(interval.year));
     } else if constexpr (unit == MONTH) {
         // This will change month and year information, maybe date.
         this->set_time(0, 1, 1, 0, 0, 0, 0);
         int64_t months = 12 * interval.year + interval.month;
-        this->template set_time_unit<TimeUnit::YEAR>(months / 12);
-        this->template set_time_unit<TimeUnit::MONTH>((months % 12) + 1);
+        PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::YEAR>(months / 
12));
+        PROPAGATE_FALSE(this->template set_time_unit<TimeUnit::MONTH>((months 
% 12) + 1));
     }
     return true;
 }
diff --git a/be/src/vec/runtime/vdatetime_value.h 
b/be/src/vec/runtime/vdatetime_value.h
index bbfbbb28226..ec4e8e11848 100644
--- a/be/src/vec/runtime/vdatetime_value.h
+++ b/be/src/vec/runtime/vdatetime_value.h
@@ -19,11 +19,11 @@
 
 #include <glog/logging.h>
 #include <re2/re2.h>
-#include <stdint.h>
-#include <string.h>
 
 #include <algorithm>
 #include <cstddef>
+#include <cstdint>
+#include <cstring>
 #include <iostream>
 #include <iterator>
 #include <string>
@@ -162,6 +162,8 @@ constexpr int HOUR_PER_DAY = 24;
 constexpr int64_t SECOND_PER_HOUR = 3600;
 constexpr int64_t SECOND_PER_MINUTE = 60;
 
+inline constexpr int S_DAYS_IN_MONTH[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 
30, 31, 30, 31};
+
 constexpr size_t const_length(const char* str) {
     return (str == nullptr || *str == 0) ? 0 : const_length(str + 1) + 1;
 }
@@ -195,7 +197,11 @@ static constexpr uint64_t MAX_DATETIME_V2 = 
((uint64_t)MAX_DATE_V2 << TIME_PART_
 static constexpr uint64_t MIN_DATETIME_V2 = (uint64_t)MIN_DATE_V2 << 
TIME_PART_LENGTH;
 
 static constexpr uint32_t MAX_YEAR = 9999;
-static constexpr uint32_t MIN_YEAR = 0;
+static constexpr uint32_t MAX_MONTH = 12;
+static constexpr uint32_t MAX_HOUR = 23;
+static constexpr uint32_t MAX_MINUTE = 59;
+static constexpr uint32_t MAX_SECOND = 59;
+static constexpr uint32_t MAX_MICROSECOND = 999999;
 
 static constexpr uint32_t DATEV2_YEAR_WIDTH = 23;
 static constexpr uint32_t DATETIMEV2_YEAR_WIDTH = 18;
@@ -1150,31 +1156,58 @@ public:
 
     bool get_date_from_daynr(uint64_t);
 
+    // should do check
     template <TimeUnit unit>
-    void set_time_unit(uint32_t val) {
+    bool set_time_unit(uint32_t val) {
+        // is uint so need check upper bound only
         if constexpr (unit == TimeUnit::YEAR) {
+            if (val > MAX_YEAR) [[unlikely]] {
+                return false;
+            }
             date_v2_value_.year_ = val;
         } else if constexpr (unit == TimeUnit::MONTH) {
+            if (val > MAX_MONTH) [[unlikely]] {
+                return false;
+            }
             date_v2_value_.month_ = val;
         } else if constexpr (unit == TimeUnit::DAY) {
+            DCHECK(date_v2_value_.month_ <= MAX_MONTH);
+            DCHECK(date_v2_value_.month_ != 0);
+            if (val > S_DAYS_IN_MONTH[date_v2_value_.month_] &&
+                !(is_leap(date_v2_value_.year_) && date_v2_value_.month_ == 2 
&& val == 29)) {
+                return false;
+            }
             date_v2_value_.day_ = val;
         } else if constexpr (unit == TimeUnit::HOUR) {
             if constexpr (is_datetime) {
+                if (val > MAX_HOUR) [[unlikely]] {
+                    return false;
+                }
                 date_v2_value_.hour_ = val;
             }
         } else if constexpr (unit == TimeUnit::MINUTE) {
             if constexpr (is_datetime) {
+                if (val > MAX_MINUTE) [[unlikely]] {
+                    return false;
+                }
                 date_v2_value_.minute_ = val;
             }
         } else if constexpr (unit == TimeUnit::SECOND) {
             if constexpr (is_datetime) {
+                if (val > MAX_SECOND) [[unlikely]] {
+                    return false;
+                }
                 date_v2_value_.second_ = val;
             }
-        } else if constexpr (unit == TimeUnit::SECOND_MICROSECOND) {
+        } else if constexpr (unit == TimeUnit::MICROSECOND) {
             if constexpr (is_datetime) {
+                if (val > MAX_MICROSECOND) [[unlikely]] {
+                    return false;
+                }
                 date_v2_value_.microsecond_ = val;
             }
         }
+        return true;
     }
     operator int64_t() const { return to_int64(); }
 
@@ -1397,10 +1430,22 @@ int64_t datetime_diff(const DateV2Value<T0>& ts_value1, 
const DateV2Value<T1>& t
     }
     case WEEK: {
         int day = ts_value2.daynr() - ts_value1.daynr();
+        int64_t ms_diff = ts_value2.time_part_diff_microsecond(ts_value1);
+        if (day > 0 && ms_diff < 0) {
+            day--;
+        } else if (day < 0 && ms_diff > 0) {
+            day++;
+        }
         return day / 7;
     }
     case DAY: {
         int day = ts_value2.daynr() - ts_value1.daynr();
+        int64_t ms_diff = ts_value2.time_part_diff_microsecond(ts_value1);
+        if (day > 0 && ms_diff < 0) {
+            day--;
+        } else if (day < 0 && ms_diff > 0) {
+            day++;
+        }
         return day;
     }
     case HOUR: {
diff --git a/regression-test/data/correctness_p0/test_date_diff.out 
b/regression-test/data/correctness_p0/test_date_diff.out
new file mode 100644
index 00000000000..4f2470fe4ee
--- /dev/null
+++ b/regression-test/data/correctness_p0/test_date_diff.out
@@ -0,0 +1,21 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !days --
+0
+0
+1
+2
+3
+4
+5
+6
+
+-- !weeks --
+0
+0
+0
+0
+0
+0
+0
+0
+
diff --git 
a/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.out
 
b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.out
new file mode 100644
index 00000000000..9a3dd1db9cc
--- /dev/null
+++ 
b/regression-test/data/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.out
@@ -0,0 +1,91 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !select_nullable_1 --
+\N     \N      \N
+\N     \N      \N
+9998-12-31     9998-12-31      9998-12-31T23:59:59
+\N     \N      \N
+
+-- !select_nullable_2 --
+\N     \N      \N
+\N     \N      \N
+9999-11-30     9999-11-30      9999-11-30T23:59:59
+\N     \N      \N
+
+-- !select_nullable_3 --
+\N     \N      \N
+\N     \N      \N
+9999-12-24     9999-12-24      9999-12-24T23:59:59
+\N     \N      \N
+
+-- !select_nullable_4 --
+\N     \N      \N
+\N     \N      \N
+9999-12-30     9999-12-30      9999-12-30T23:59:59
+\N     \N      \N
+
+-- !select_nullable_5 --
+\N     \N      \N
+\N     \N      \N
+9999-12-30T23:00       9999-12-30T23:00        9999-12-31T22:59:59
+\N     \N      \N
+
+-- !select_nullable_6 --
+\N     \N      \N
+\N     \N      \N
+9999-12-30T23:59       9999-12-30T23:59        9999-12-31T23:58:59
+\N     \N      \N
+
+-- !select_nullable_7 --
+\N     \N      \N
+\N     \N      \N
+9999-12-30T23:59:59    9999-12-30T23:59:59     9999-12-31T23:59:58
+\N     \N      \N
+
+-- !select_nullable_8 --
+0001-01-01     0001-01-01      0001-01-01T00:00
+\N     \N      \N
+\N     \N      \N
+\N     \N      \N
+
+-- !select_nullable_9 --
+0000-02-01     0000-02-01      0000-02-01T00:00
+\N     \N      \N
+\N     \N      \N
+\N     \N      \N
+
+-- !select_nullable_10 --
+0000-01-08     0000-01-08      0000-01-08T00:00
+\N     \N      \N
+\N     \N      \N
+\N     \N      \N
+
+-- !select_nullable_11 --
+0000-01-02     0000-01-02      0000-01-02T00:00
+\N     \N      \N
+\N     \N      \N
+\N     \N      \N
+
+-- !select_nullable_12 --
+0000-01-01T01:00       0000-01-01T01:00        0000-01-01T01:00
+\N     \N      \N
+9999-12-31T01:00       9999-12-31T01:00        \N
+\N     \N      \N
+
+-- !select_nullable_13 --
+0000-01-01T00:01       0000-01-01T00:01        0000-01-01T00:01
+\N     \N      \N
+9999-12-31T00:01       9999-12-31T00:01        \N
+\N     \N      \N
+
+-- !select_nullable_14 --
+0000-01-01T00:00:01    0000-01-01T00:00:01     0000-01-01T00:00:01
+\N     \N      \N
+9999-12-31T00:00:01    9999-12-31T00:00:01     \N
+\N     \N      \N
+
+-- !select_nullable_15 --
+0000-01-02T00:00       0000-01-02T00:00        0000-01-02T00:00
+\N     \N      \N
+\N     \N      \N
+\N     \N      \N
+
diff --git a/regression-test/suites/correctness/test_date_function_const.groovy 
b/regression-test/suites/correctness/test_date_function_const.groovy
index 2b423e4e933..53c80ed0e32 100644
--- a/regression-test/suites/correctness/test_date_function_const.groovy
+++ b/regression-test/suites/correctness/test_date_function_const.groovy
@@ -59,4 +59,9 @@ suite("test_date_function_const") {
         sql("""select date_add(CURRENT_DATE(),-2);""")
         notContains("00:00:00")
     }
-}
\ No newline at end of file
+
+    test {
+        sql """select date_add("1900-01-01 12:00:00.123456", interval 
10000000000 month);"""
+        exception "Operation months_add 133705200962757184 1410065408 out of 
range"
+    }
+}
diff --git a/regression-test/suites/correctness_p0/test_date_diff.groovy 
b/regression-test/suites/correctness_p0/test_date_diff.groovy
new file mode 100644
index 00000000000..91e9cef6eb3
--- /dev/null
+++ b/regression-test/suites/correctness_p0/test_date_diff.groovy
@@ -0,0 +1,32 @@
+// 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("test_date_diff") {
+    sql """
+        create table dt6(
+            k0 datetime(6) null
+        )
+        DISTRIBUTED BY HASH(`k0`) BUCKETS auto
+        properties("replication_num" = "1");
+    """
+    sql """
+    insert into dt6 values ("0000-01-01 12:00:00"), ("0000-01-02 12:00:00"), 
("0000-01-03 12:00:00"), ("0000-01-04 12:00:00"),
+        ("0000-01-05 12:00:00"), ("0000-01-06 12:00:00"), ("0000-01-07 
12:00:00"), ("0000-01-08 12:00:00");
+    """
+    qt_days """ select days_diff(k0, '0000-01-01 13:00:00') from dt6 order by 
k0; """
+    qt_weeks """ select weeks_diff(k0, '0000-01-01 13:00:00') from dt6 order 
by k0; """
+}
\ No newline at end of file
diff --git 
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
index 9db6c65547e..9a66b90aa35 100644
--- 
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
+++ 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_function.groovy
@@ -594,31 +594,30 @@ suite("test_date_function") {
     qt_sql """ select seconds_sub(test_time2,1) result from ${tableName}; """
 
     // test last_day for vec
-    sql """ DROP TABLE IF EXISTS ${tableName}; """
+    sql """ DROP TABLE IF EXISTS test_time_add_sub_function; """
     sql """
-            CREATE TABLE IF NOT EXISTS ${tableName} (
-                birth date,    
-                birth1 datev2, 
-                birth2 datetime, 
+            CREATE TABLE IF NOT EXISTS test_time_add_sub_function (
+                birth date,
+                birth1 datev2,
+                birth2 datetime,
                 birth3 datetimev2)
             UNIQUE KEY(birth, birth1, birth2, birth3)
             DISTRIBUTED BY HASH (birth) BUCKETS 1 
             PROPERTIES( "replication_allocation" = "tag.location.default: 1");
         """
     sql """
-        insert into ${tableName} values 
-        ('2022-01-01', '2022-01-01', '2022-01-01 00:00:00', '2022-01-01 
00:00:00'), 
-        ('2000-02-01', '2000-02-01', '2000-02-01 00:00:00', '2000-02-01 
00:00:00.123'), 
+        insert into test_time_add_sub_function values
+        ('2022-01-01', '2022-01-01', '2022-01-01 00:00:00', '2022-01-01 
00:00:00'),
+        ('2000-02-01', '2000-02-01', '2000-02-01 00:00:00', '2000-02-01 
00:00:00.123'),
         ('2022-02-27', '2022-02-27', '2022-02-27 00:00:00', '2022-02-27 
00:00:00'),
         ('2022-02-28', '2022-02-28', '2022-02-28 23:59:59', '2022-02-28 
23:59:59');"""
     qt_sql """
-        select last_day(birth), last_day(birth1), 
-                last_day(birth2), last_day(birth3) 
-                from ${tableName};
+        select last_day(birth), last_day(birth1),
+                last_day(birth2), last_day(birth3)
+                from test_time_add_sub_function;
     """
-    sql """ DROP TABLE IF EXISTS ${tableName}; """
 
-    sql """ DROP TABLE IF EXISTS ${tableName}; """
+    sql """ DROP TABLE IF EXISTS test_time_add_sub_function; """
     sql """
             CREATE TABLE IF NOT EXISTS ${tableName} (
                 birth date,    
diff --git 
a/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy
 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy
new file mode 100644
index 00000000000..83431ede6ab
--- /dev/null
+++ 
b/regression-test/suites/nereids_p0/sql_functions/datetime_functions/test_date_or_datetime_computation_negative.groovy
@@ -0,0 +1,276 @@
+// 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.
+test_date_or_datetime_computation_negative
+suite("test_date_or_datetime_computation_negative") {
+    sql """ CREATE TABLE IF NOT EXISTS 
test_date_or_datetime_computation_negative (
+                `row_id` LARGEINT NOT NULL,
+                `date` DATE NOT NULL,
+                `date_null` DATE NULL,
+                `dateV2` DATEV2 NOT NULL,
+                `dateV2_null` DATEV2 NULL,
+                `datetime` DATETIME NOT NULL,
+                `datetime_null` DATETIME NULL, )
+            DUPLICATE KEY(`row_id`)
+            DISTRIBUTED BY HASH(`row_id`) BUCKETS 1
+            PROPERTIES (
+                "replication_allocation" = "tag.location.default: 1"
+            );"""
+
+    sql "set enable_insert_strict = false;"
+    sql "set parallel_fragment_exec_instance_num = 3;"
+    sql "set enable_nereids_planner = true;"
+
+    sql """INSERT INTO test_date_or_datetime_computation_negative VALUES (1, 
'0000-01-01', '0000-01-01', '0000-01-01', '0000-01-01', '0000-01-01 00:00:00', 
'0000-01-01 00:00:00');"""
+    sql """INSERT INTO test_date_or_datetime_computation_negative VALUES (2, 
'0000-01-01', NULL, '0000-01-01', NULL, '0000-01-01 00:00:00', NULL);"""
+    sql """INSERT INTO test_date_or_datetime_computation_negative VALUES (3, 
'9999-12-31', '9999-12-31', '9999-12-31', '9999-12-31', '9999-12-31 23:59:59', 
'9999-12-31 23:59:59');"""
+    sql """INSERT INTO test_date_or_datetime_computation_negative VALUES (4, 
'9999-12-31', NULL, '9999-12-31', NULL, '9999-12-31 23:59:59', NULL);"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_1 """SELECT date_sub(date_null, interval 1 year), 
date_sub(dateV2_null, interval 1 year), date_sub(datetime_null, interval 1 
year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_2 """SELECT date_sub(date_null, interval 1 month), 
date_sub(dateV2_null, interval 1 month), date_sub(datetime_null, interval 1 
month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """ SELECT date_sub(date, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """ SELECT date_sub(dateV2, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """ SELECT date_sub(datetime, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1; """
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+
+    qt_select_nullable_3 """SELECT date_sub(date_null, interval 1 week), 
date_sub(dateV2_null, interval 1 week), date_sub(datetime_null, interval 1 
week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+
+    qt_select_nullable_4 """SELECT date_sub(date_null, interval 1 day), 
date_sub(dateV2_null, interval 1 day), date_sub(datetime_null, interval 1 day) 
FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_5 """ SELECT date_sub(date_null, interval 1 hour), 
date_sub(dateV2_null, interval 1 hour), date_sub(datetime_null, interval 1 
hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_6 """SELECT date_sub(date_null, interval 1 minute), 
date_sub(dateV2_null, interval 1 minute), date_sub(datetime_null, interval 1 
minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_sub(date, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(dateV2, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_sub(datetime, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=1;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_7 """SELECT date_sub(date_null, interval 1 second), 
date_sub(dateV2_null, interval 1 second), date_sub(datetime_null, interval 1 
second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+
+    test {
+        sql """SELECT date_add(date, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 year) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_8 """SELECT date_add(date_null, interval 1 year), 
date_add(dateV2_null, interval 1 year), date_add(datetime_null, interval 1 
year) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_add(date, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 month) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_9 """SELECT date_add(date_null, interval 1 month), 
date_add(dateV2_null, interval 1 month), date_add(datetime_null, interval 1 
month) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """ SELECT date_add(date, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """ SELECT date_add(dateV2, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """ SELECT date_add(datetime, interval 1 week) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3; """
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+
+    qt_select_nullable_10 """SELECT date_add(date_null, interval 1 week), 
date_add(dateV2_null, interval 1 week), date_add(datetime_null, interval 1 
week) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_add(date, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 day) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+
+    qt_select_nullable_11 """SELECT date_add(date_null, interval 1 day), 
date_add(dateV2_null, interval 1 day), date_add(datetime_null, interval 1 day) 
FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_add(date, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 hour) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_12 """ SELECT date_add(date_null, interval 1 hour), 
date_add(dateV2_null, interval 1 hour), date_add(datetime_null, interval 1 
hour) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_add(date, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 minute) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_13 """SELECT date_add(date_null, interval 1 minute), 
date_add(dateV2_null, interval 1 minute), date_add(datetime_null, interval 1 
minute) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    test {
+        sql """SELECT date_add(date, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(dateV2, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT date_add(datetime, interval 1 second) FROM 
test_date_or_datetime_computation_negative WHERE row_id=3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_14 """SELECT date_add(date_null, interval 1 second), 
date_add(dateV2_null, interval 1 second), date_add(datetime_null, interval 1 
second) FROM test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    // TODO:
+    // nagetive test for 
microseconds_add/milliseconds_add/seconds_add/minutes_add/hours_add/days_add/weeks_add/months_add/years_add
+
+    test {
+        sql """SELECT hours_add(date, 24) FROM 
test_date_or_datetime_computation_negative WHERE row_id = 3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT hours_add(dateV2, 24) FROM 
test_date_or_datetime_computation_negative WHERE row_id = 3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+
+        sql """SELECT hours_add(datetime, 24) FROM 
test_date_or_datetime_computation_negative WHERE row_id = 3;"""
+        check {result, exception, startTime, endTime ->
+                assertTrue (exception != null)}
+    }
+    qt_select_nullable_15 """SELECT hours_add(date_null, 24), 
hours_add(dateV2_null, 24), hours_add(datetime_null, 24) FROM 
test_date_or_datetime_computation_negative ORDER BY row_id;"""
+
+    sql "DROP TABLE test_date_or_datetime_computation_negative"
+}


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


Reply via email to