1#include "duckdb/common/operator/add.hpp"
2
3#include "duckdb/common/limits.hpp"
4#include "duckdb/common/types/value.hpp"
5
6#include "duckdb/common/types/date.hpp"
7#include "duckdb/common/types/interval.hpp"
8#include "duckdb/common/types/timestamp.hpp"
9#include "duckdb/common/types/hugeint.hpp"
10
11namespace duckdb {
12
13//===--------------------------------------------------------------------===//
14// + [add]
15//===--------------------------------------------------------------------===//
16template <>
17float AddOperator::Operation(float left, float right) {
18 auto result = left + right;
19 return result;
20}
21
22template <>
23double AddOperator::Operation(double left, double right) {
24 auto result = left + right;
25 return result;
26}
27
28template <>
29interval_t AddOperator::Operation(interval_t left, interval_t right) {
30 left.months = AddOperatorOverflowCheck::Operation<int32_t, int32_t, int32_t>(left: left.months, right: right.months);
31 left.days = AddOperatorOverflowCheck::Operation<int32_t, int32_t, int32_t>(left: left.days, right: right.days);
32 left.micros = AddOperatorOverflowCheck::Operation<int64_t, int64_t, int64_t>(left: left.micros, right: right.micros);
33 return left;
34}
35
36template <>
37date_t AddOperator::Operation(date_t left, int32_t right) {
38 if (!Value::IsFinite(input: left)) {
39 return left;
40 }
41 int32_t days;
42 if (!TryAddOperator::Operation(left: left.days, right, result&: days)) {
43 throw OutOfRangeException("Date out of range");
44 }
45 date_t result(days);
46 if (!Value::IsFinite(input: result)) {
47 throw OutOfRangeException("Date out of range");
48 }
49 return result;
50}
51
52template <>
53date_t AddOperator::Operation(int32_t left, date_t right) {
54 return AddOperator::Operation<date_t, int32_t, date_t>(left: right, right: left);
55}
56
57template <>
58timestamp_t AddOperator::Operation(date_t left, dtime_t right) {
59 if (left == date_t::infinity()) {
60 return timestamp_t::infinity();
61 } else if (left == date_t::ninfinity()) {
62 return timestamp_t::ninfinity();
63 }
64 timestamp_t result;
65 if (!Timestamp::TryFromDatetime(date: left, time: right, result)) {
66 throw OutOfRangeException("Timestamp out of range");
67 }
68 return result;
69}
70
71template <>
72timestamp_t AddOperator::Operation(dtime_t left, date_t right) {
73 return AddOperator::Operation<date_t, dtime_t, timestamp_t>(left: right, right: left);
74}
75
76template <>
77date_t AddOperator::Operation(date_t left, interval_t right) {
78 return Interval::Add(left, right);
79}
80
81template <>
82date_t AddOperator::Operation(interval_t left, date_t right) {
83 return AddOperator::Operation<date_t, interval_t, date_t>(left: right, right: left);
84}
85
86template <>
87timestamp_t AddOperator::Operation(timestamp_t left, interval_t right) {
88 return Interval::Add(left, right);
89}
90
91template <>
92timestamp_t AddOperator::Operation(interval_t left, timestamp_t right) {
93 return AddOperator::Operation<timestamp_t, interval_t, timestamp_t>(left: right, right: left);
94}
95
96//===--------------------------------------------------------------------===//
97// + [add] with overflow check
98//===--------------------------------------------------------------------===//
99struct OverflowCheckedAddition {
100 template <class SRCTYPE, class UTYPE>
101 static inline bool Operation(SRCTYPE left, SRCTYPE right, SRCTYPE &result) {
102 UTYPE uresult = AddOperator::Operation<UTYPE, UTYPE, UTYPE>(UTYPE(left), UTYPE(right));
103 if (uresult < NumericLimits<SRCTYPE>::Minimum() || uresult > NumericLimits<SRCTYPE>::Maximum()) {
104 return false;
105 }
106 result = SRCTYPE(uresult);
107 return true;
108 }
109};
110
111template <>
112bool TryAddOperator::Operation(uint8_t left, uint8_t right, uint8_t &result) {
113 return OverflowCheckedAddition::Operation<uint8_t, uint16_t>(left, right, result);
114}
115template <>
116bool TryAddOperator::Operation(uint16_t left, uint16_t right, uint16_t &result) {
117 return OverflowCheckedAddition::Operation<uint16_t, uint32_t>(left, right, result);
118}
119template <>
120bool TryAddOperator::Operation(uint32_t left, uint32_t right, uint32_t &result) {
121 return OverflowCheckedAddition::Operation<uint32_t, uint64_t>(left, right, result);
122}
123
124template <>
125bool TryAddOperator::Operation(uint64_t left, uint64_t right, uint64_t &result) {
126 if (NumericLimits<uint64_t>::Maximum() - left < right) {
127 return false;
128 }
129 return OverflowCheckedAddition::Operation<uint64_t, uint64_t>(left, right, result);
130}
131
132template <>
133bool TryAddOperator::Operation(int8_t left, int8_t right, int8_t &result) {
134 return OverflowCheckedAddition::Operation<int8_t, int16_t>(left, right, result);
135}
136
137template <>
138bool TryAddOperator::Operation(int16_t left, int16_t right, int16_t &result) {
139 return OverflowCheckedAddition::Operation<int16_t, int32_t>(left, right, result);
140}
141
142template <>
143bool TryAddOperator::Operation(int32_t left, int32_t right, int32_t &result) {
144 return OverflowCheckedAddition::Operation<int32_t, int64_t>(left, right, result);
145}
146
147template <>
148bool TryAddOperator::Operation(int64_t left, int64_t right, int64_t &result) {
149#if (__GNUC__ >= 5) || defined(__clang__)
150 if (__builtin_add_overflow(left, right, &result)) {
151 return false;
152 }
153#else
154 // https://blog.regehr.org/archives/1139
155 result = int64_t((uint64_t)left + (uint64_t)right);
156 if ((left < 0 && right < 0 && result >= 0) || (left >= 0 && right >= 0 && result < 0)) {
157 return false;
158 }
159#endif
160 return true;
161}
162
163//===--------------------------------------------------------------------===//
164// add decimal with overflow check
165//===--------------------------------------------------------------------===//
166template <class T, T min, T max>
167bool TryDecimalAddTemplated(T left, T right, T &result) {
168 if (right < 0) {
169 if (min - right > left) {
170 return false;
171 }
172 } else {
173 if (max - right < left) {
174 return false;
175 }
176 }
177 result = left + right;
178 return true;
179}
180
181template <>
182bool TryDecimalAdd::Operation(int16_t left, int16_t right, int16_t &result) {
183 return TryDecimalAddTemplated<int16_t, -9999, 9999>(left, right, result);
184}
185
186template <>
187bool TryDecimalAdd::Operation(int32_t left, int32_t right, int32_t &result) {
188 return TryDecimalAddTemplated<int32_t, -999999999, 999999999>(left, right, result);
189}
190
191template <>
192bool TryDecimalAdd::Operation(int64_t left, int64_t right, int64_t &result) {
193 return TryDecimalAddTemplated<int64_t, -999999999999999999, 999999999999999999>(left, right, result);
194}
195
196template <>
197bool TryDecimalAdd::Operation(hugeint_t left, hugeint_t right, hugeint_t &result) {
198 result = left + right;
199 if (result <= -Hugeint::POWERS_OF_TEN[38] || result >= Hugeint::POWERS_OF_TEN[38]) {
200 return false;
201 }
202 return true;
203}
204
205template <>
206hugeint_t DecimalAddOverflowCheck::Operation(hugeint_t left, hugeint_t right) {
207 hugeint_t result;
208 if (!TryDecimalAdd::Operation(left, right, result)) {
209 throw OutOfRangeException("Overflow in addition of DECIMAL(38) (%s + %s);", left.ToString(), right.ToString());
210 }
211 return result;
212}
213
214//===--------------------------------------------------------------------===//
215// add time operator
216//===--------------------------------------------------------------------===//
217template <>
218dtime_t AddTimeOperator::Operation(dtime_t left, interval_t right) {
219 date_t date(0);
220 return Interval::Add(left, right, date);
221}
222
223template <>
224dtime_t AddTimeOperator::Operation(interval_t left, dtime_t right) {
225 return AddTimeOperator::Operation<dtime_t, interval_t, dtime_t>(left: right, right: left);
226}
227
228} // namespace duckdb
229