| 782 | template <bool is_add, typename AnyIntVal, typename Interval, |
| 783 | bool is_add_months_keep_last_day> |
| 784 | TimestampVal TimestampFunctions::AddSub(FunctionContext* context, |
| 785 | const TimestampVal& timestamp, const AnyIntVal& num_interval_units) { |
| 786 | DCHECK_EQ(Date(max_date_time).year(), MAX_YEAR); |
| 787 | DCHECK_EQ(Date(min_date_time).year(), MIN_YEAR); |
| 788 | if (timestamp.is_null || num_interval_units.is_null) return TimestampVal::null(); |
| 789 | const TimestampValue& value = TimestampValue::FromTimestampVal(timestamp); |
| 790 | if (!value.HasDate()) return TimestampVal::null(); |
| 791 | // Adding/subtracting boost::gregorian::dates can throw if the result exceeds the |
| 792 | // min/max supported dates. (Sometimes the exception is thrown lazily and calling an |
| 793 | // accessor functions is needed to trigger validation.) |
| 794 | if (UNLIKELY(IsOverMaxInterval<Interval>(num_interval_units.val))) { |
| 795 | context->AddWarning(Substitute("Cannot $0 interval $1: Interval value too large", |
| 796 | is_add ? "add" : "subtract", num_interval_units.val).c_str()); |
| 797 | return TimestampVal::null(); |
| 798 | } |
| 799 | try { |
| 800 | ptime datetime; |
| 801 | value.ToPtime(&datetime); |
| 802 | if (is_add_months_keep_last_day) { |
| 803 | AddMonths(context, is_add ? num_interval_units.val : -num_interval_units.val, true, |
| 804 | &datetime); |
| 805 | } else { |
| 806 | AddInterval<Interval>(context, |
| 807 | is_add ? num_interval_units.val : -num_interval_units.val, &datetime); |
| 808 | } |
| 809 | // Validate that the ptime is not "special" (ie not_a_date_time) and has a valid year. |
| 810 | // If validation fails, an exception is thrown. |
| 811 | datetime.date().year(); |
| 812 | const TimestampValue result_value(datetime); |
| 813 | TimestampVal result_val; |
| 814 | result_value.ToTimestampVal(&result_val); |
| 815 | DcheckAddSubResult(timestamp, result_val, is_add, num_interval_units.val); |
| 816 | return result_val; |
| 817 | } catch (const std::exception& e) { |
| 818 | context->AddWarning(Substitute("Cannot $0 interval $1: $2", |
| 819 | is_add ? "add" : "subtract", num_interval_units.val, e.what()).c_str()); |
| 820 | return TimestampVal::null(); |
| 821 | } |
| 822 | } |
| 823 | |
| 824 | IntVal TimestampFunctions::DateDiff(FunctionContext* context, |
| 825 | const TimestampVal& ts_val1, |
nothing calls this directly
no test coverage detected