1 | #include <common/DateLUTImpl.h> |
2 | |
3 | #include <DataTypes/DataTypeDate.h> |
4 | #include <DataTypes/DataTypeDateTime.h> |
5 | #include <DataTypes/DataTypeDateTime64.h> |
6 | |
7 | #include <Columns/ColumnVector.h> |
8 | |
9 | #include <Functions/IFunctionImpl.h> |
10 | #include <Functions/FunctionHelpers.h> |
11 | #include <Functions/extractTimeZoneFromFunctionArguments.h> |
12 | |
13 | #include <IO/WriteHelpers.h> |
14 | |
15 | |
16 | namespace DB |
17 | { |
18 | |
19 | namespace ErrorCodes |
20 | { |
21 | extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; |
22 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
23 | extern const int ILLEGAL_COLUMN; |
24 | } |
25 | |
26 | /// AddOnDateTime64DefaultImpl provides default implementation of add-X functionality for DateTime64. |
27 | /// |
28 | /// Default implementation is not to change fractional part, but only modify whole part as if it was DateTime. |
29 | /// That means large whole values (for scale less than 9) might not fit into UInt32-range, |
30 | /// and hence default implementation will produce incorrect results. |
31 | template <typename T> |
32 | struct AddOnDateTime64DefaultImpl |
33 | { |
34 | /*explicit*/ AddOnDateTime64DefaultImpl(UInt32 scale_ = 0) |
35 | : scale_multiplier(DecimalUtils::scaleMultiplier<DateTime64::NativeType>(scale_)) |
36 | {} |
37 | |
38 | // Default implementation for add/sub on DateTime64: do math on whole part (the same way as for DateTime), leave fractional as it is. |
39 | inline DateTime64 execute(const DateTime64 & t, Int64 delta, const DateLUTImpl & time_zone) const |
40 | { |
41 | const auto components = DecimalUtils::splitWithScaleMultiplier(t, scale_multiplier); |
42 | |
43 | const auto whole = static_cast<const T*>(this)->execute(static_cast<UInt32>(components.whole), delta, time_zone); |
44 | return DecimalUtils::decimalFromComponentsWithMultiplier<DateTime64>(static_cast<DateTime64::NativeType>(whole), components.fractional, scale_multiplier); |
45 | } |
46 | |
47 | UInt32 scale_multiplier = 1; |
48 | }; |
49 | |
50 | |
51 | /// Type of first argument of 'execute' function overload defines what INPUT DataType it is used for. |
52 | /// Return type defines what is the OUTPUT (return) type of the CH function. |
53 | /// Corresponding types: |
54 | /// - UInt16 => DataTypeDate |
55 | /// - UInt32 => DataTypeDateTime |
56 | /// - DateTime64 => DataTypeDateTime64 |
57 | /// Please note that INPUT and OUTPUT types may differ, e.g.: |
58 | /// - 'AddSecondsImpl::execute(UInt32, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(DateTime, ...) -> DateTime' |
59 | /// - 'AddSecondsImpl::execute(UInt16, ...) -> UInt32' is available to the ClickHouse users as 'addSeconds(Date, ...) -> DateTime' |
60 | |
61 | struct AddSecondsImpl : public AddOnDateTime64DefaultImpl<AddSecondsImpl> |
62 | { |
63 | using Base = AddOnDateTime64DefaultImpl<AddSecondsImpl>; |
64 | using Base::Base; |
65 | using Base::execute; |
66 | |
67 | static constexpr auto name = "addSeconds" ; |
68 | |
69 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) |
70 | { |
71 | return t + delta; |
72 | } |
73 | |
74 | static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
75 | { |
76 | return time_zone.fromDayNum(DayNum(d)) + delta; |
77 | } |
78 | }; |
79 | |
80 | struct AddMinutesImpl : public AddOnDateTime64DefaultImpl<AddMinutesImpl> |
81 | { |
82 | using Base = AddOnDateTime64DefaultImpl<AddMinutesImpl>; |
83 | using Base::Base; |
84 | using Base::execute; |
85 | |
86 | static constexpr auto name = "addMinutes" ; |
87 | |
88 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) |
89 | { |
90 | return t + delta * 60; |
91 | } |
92 | |
93 | static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
94 | { |
95 | return time_zone.fromDayNum(DayNum(d)) + delta * 60; |
96 | } |
97 | }; |
98 | |
99 | struct AddHoursImpl : public AddOnDateTime64DefaultImpl<AddHoursImpl> |
100 | { |
101 | using Base = AddOnDateTime64DefaultImpl<AddHoursImpl>; |
102 | using Base::Base; |
103 | using Base::execute; |
104 | |
105 | static constexpr auto name = "addHours" ; |
106 | |
107 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) |
108 | { |
109 | return t + delta * 3600; |
110 | } |
111 | |
112 | static inline UInt32 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
113 | { |
114 | return time_zone.fromDayNum(DayNum(d)) + delta * 3600; |
115 | } |
116 | }; |
117 | |
118 | struct AddDaysImpl : public AddOnDateTime64DefaultImpl<AddDaysImpl> |
119 | { |
120 | using Base = AddOnDateTime64DefaultImpl<AddDaysImpl>; |
121 | using Base::Base; |
122 | using Base::execute; |
123 | |
124 | static constexpr auto name = "addDays" ; |
125 | |
126 | // static inline UInt32 execute(UInt64 t, Int64 delta, const DateLUTImpl & time_zone) |
127 | // { |
128 | // // TODO (nemkov): LUT does not support out-of range date values for now. |
129 | // return time_zone.addDays(t, delta); |
130 | // } |
131 | |
132 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) |
133 | { |
134 | return time_zone.addDays(t, delta); |
135 | } |
136 | |
137 | static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) |
138 | { |
139 | return d + delta; |
140 | } |
141 | }; |
142 | |
143 | struct AddWeeksImpl : public AddOnDateTime64DefaultImpl<AddWeeksImpl> |
144 | { |
145 | using Base = AddOnDateTime64DefaultImpl<AddWeeksImpl>; |
146 | using Base::Base; |
147 | using Base::execute; |
148 | |
149 | static constexpr auto name = "addWeeks" ; |
150 | |
151 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) |
152 | { |
153 | return time_zone.addWeeks(t, delta); |
154 | } |
155 | |
156 | static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl &) |
157 | { |
158 | return d + delta * 7; |
159 | } |
160 | }; |
161 | |
162 | struct AddMonthsImpl : public AddOnDateTime64DefaultImpl<AddMonthsImpl> |
163 | { |
164 | using Base = AddOnDateTime64DefaultImpl<AddMonthsImpl>; |
165 | using Base::Base; |
166 | using Base::execute; |
167 | |
168 | static constexpr auto name = "addMonths" ; |
169 | |
170 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) |
171 | { |
172 | return time_zone.addMonths(t, delta); |
173 | } |
174 | |
175 | static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
176 | { |
177 | return time_zone.addMonths(DayNum(d), delta); |
178 | } |
179 | }; |
180 | |
181 | struct AddQuartersImpl : public AddOnDateTime64DefaultImpl<AddQuartersImpl> |
182 | { |
183 | using Base = AddOnDateTime64DefaultImpl<AddQuartersImpl>; |
184 | using Base::Base; |
185 | using Base::execute; |
186 | |
187 | static constexpr auto name = "addQuarters" ; |
188 | |
189 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) |
190 | { |
191 | return time_zone.addQuarters(t, delta); |
192 | } |
193 | |
194 | static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
195 | { |
196 | return time_zone.addQuarters(DayNum(d), delta); |
197 | } |
198 | }; |
199 | |
200 | struct AddYearsImpl : public AddOnDateTime64DefaultImpl<AddYearsImpl> |
201 | { |
202 | using Base = AddOnDateTime64DefaultImpl<AddYearsImpl>; |
203 | using Base::Base; |
204 | using Base::execute; |
205 | |
206 | static constexpr auto name = "addYears" ; |
207 | |
208 | static inline UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl & time_zone) |
209 | { |
210 | return time_zone.addYears(t, delta); |
211 | } |
212 | |
213 | static inline UInt16 execute(UInt16 d, Int64 delta, const DateLUTImpl & time_zone) |
214 | { |
215 | return time_zone.addYears(DayNum(d), delta); |
216 | } |
217 | }; |
218 | |
219 | template <typename Transform> |
220 | struct SubtractIntervalImpl : public Transform |
221 | { |
222 | using Transform::Transform; |
223 | |
224 | template <typename T> |
225 | inline auto execute(T t, Int64 delta, const DateLUTImpl & time_zone) const |
226 | { |
227 | return Transform::execute(t, -delta, time_zone); |
228 | } |
229 | }; |
230 | |
231 | struct SubtractSecondsImpl : SubtractIntervalImpl<AddSecondsImpl> { static constexpr auto name = "subtractSeconds" ; }; |
232 | struct SubtractMinutesImpl : SubtractIntervalImpl<AddMinutesImpl> { static constexpr auto name = "subtractMinutes" ; }; |
233 | struct SubtractHoursImpl : SubtractIntervalImpl<AddHoursImpl> { static constexpr auto name = "subtractHours" ; }; |
234 | struct SubtractDaysImpl : SubtractIntervalImpl<AddDaysImpl> { static constexpr auto name = "subtractDays" ; }; |
235 | struct SubtractWeeksImpl : SubtractIntervalImpl<AddWeeksImpl> { static constexpr auto name = "subtractWeeks" ; }; |
236 | struct SubtractMonthsImpl : SubtractIntervalImpl<AddMonthsImpl> { static constexpr auto name = "subtractMonths" ; }; |
237 | struct SubtractQuartersImpl : SubtractIntervalImpl<AddQuartersImpl> { static constexpr auto name = "subtractQuarters" ; }; |
238 | struct SubtractYearsImpl : SubtractIntervalImpl<AddYearsImpl> { static constexpr auto name = "subtractYears" ; }; |
239 | |
240 | |
241 | template <typename Transform> |
242 | struct Adder |
243 | { |
244 | const Transform transform; |
245 | |
246 | explicit Adder(Transform transform_) |
247 | : transform(std::move(transform_)) |
248 | {} |
249 | |
250 | template <typename FromVectorType, typename ToVectorType> |
251 | void vector_vector(const FromVectorType & vec_from, ToVectorType & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) const |
252 | { |
253 | size_t size = vec_from.size(); |
254 | vec_to.resize(size); |
255 | |
256 | for (size_t i = 0; i < size; ++i) |
257 | vec_to[i] = transform.execute(vec_from[i], delta.getInt(i), time_zone); |
258 | } |
259 | |
260 | template <typename FromVectorType, typename ToVectorType> |
261 | void vector_constant(const FromVectorType & vec_from, ToVectorType & vec_to, Int64 delta, const DateLUTImpl & time_zone) const |
262 | { |
263 | size_t size = vec_from.size(); |
264 | vec_to.resize(size); |
265 | |
266 | for (size_t i = 0; i < size; ++i) |
267 | vec_to[i] = transform.execute(vec_from[i], delta, time_zone); |
268 | } |
269 | |
270 | template <typename FromType, typename ToVectorType> |
271 | void constant_vector(const FromType & from, ToVectorType & vec_to, const IColumn & delta, const DateLUTImpl & time_zone) const |
272 | { |
273 | size_t size = delta.size(); |
274 | vec_to.resize(size); |
275 | |
276 | for (size_t i = 0; i < size; ++i) |
277 | vec_to[i] = transform.execute(from, delta.getInt(i), time_zone); |
278 | } |
279 | }; |
280 | |
281 | |
282 | template <typename FromDataType, typename ToDataType, typename Transform> |
283 | struct DateTimeAddIntervalImpl |
284 | { |
285 | static void execute(Transform transform, Block & block, const ColumnNumbers & arguments, size_t result) |
286 | { |
287 | using FromValueType = typename FromDataType::FieldType; |
288 | using FromColumnType = typename FromDataType::ColumnType; |
289 | using ToColumnType = typename ToDataType::ColumnType; |
290 | |
291 | auto op = Adder<Transform>{std::move(transform)}; |
292 | |
293 | const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 2, 0); |
294 | |
295 | const ColumnPtr source_col = block.getByPosition(arguments[0]).column; |
296 | |
297 | auto result_col = block.getByPosition(result).type->createColumn(); |
298 | auto col_to = assert_cast<ToColumnType *>(result_col.get()); |
299 | |
300 | if (const auto * sources = checkAndGetColumn<FromColumnType>(source_col.get())) |
301 | { |
302 | const IColumn & delta_column = *block.getByPosition(arguments[1]).column; |
303 | |
304 | if (const auto * delta_const_column = typeid_cast<const ColumnConst *>(&delta_column)) |
305 | op.vector_constant(sources->getData(), col_to->getData(), delta_const_column->getField().get<Int64>(), time_zone); |
306 | else |
307 | op.vector_vector(sources->getData(), col_to->getData(), delta_column, time_zone); |
308 | } |
309 | else if (const auto * sources_const = checkAndGetColumnConst<FromColumnType>(source_col.get())) |
310 | { |
311 | op.constant_vector(sources_const->template getValue<FromValueType>(), col_to->getData(), *block.getByPosition(arguments[1]).column, time_zone); |
312 | } |
313 | else |
314 | { |
315 | throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() |
316 | + " of first argument of function " + Transform::name, |
317 | ErrorCodes::ILLEGAL_COLUMN); |
318 | } |
319 | |
320 | block.getByPosition(result).column = std::move(result_col); |
321 | } |
322 | }; |
323 | |
324 | namespace date_and_time_type_details |
325 | { |
326 | // Compile-time mapping of value (DataType::FieldType) types to corresponding DataType |
327 | template <typename FieldType> struct ResultDataTypeMap {}; |
328 | template <> struct ResultDataTypeMap<UInt16> { using ResultDataType = DataTypeDate; }; |
329 | template <> struct ResultDataTypeMap<Int16> { using ResultDataType = DataTypeDate; }; |
330 | template <> struct ResultDataTypeMap<UInt32> { using ResultDataType = DataTypeDateTime; }; |
331 | template <> struct ResultDataTypeMap<Int32> { using ResultDataType = DataTypeDateTime; }; |
332 | template <> struct ResultDataTypeMap<DateTime64> { using ResultDataType = DataTypeDateTime64; }; |
333 | } |
334 | |
335 | template <typename Transform> |
336 | class FunctionDateOrDateTimeAddInterval : public IFunction |
337 | { |
338 | public: |
339 | static constexpr auto name = Transform::name; |
340 | static FunctionPtr create(const Context &) { return std::make_shared<FunctionDateOrDateTimeAddInterval>(); } |
341 | |
342 | String getName() const override |
343 | { |
344 | return name; |
345 | } |
346 | |
347 | bool isVariadic() const override { return true; } |
348 | size_t getNumberOfArguments() const override { return 0; } |
349 | |
350 | DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override |
351 | { |
352 | if (arguments.size() != 2 && arguments.size() != 3) |
353 | throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " |
354 | + toString(arguments.size()) + ", should be 2 or 3" , |
355 | ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); |
356 | |
357 | if (!isNativeNumber(arguments[1].type)) |
358 | throw Exception("Second argument for function " + getName() + " (delta) must be number" , |
359 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
360 | |
361 | if (arguments.size() == 2) |
362 | { |
363 | if (!isDateOrDateTime(arguments[0].type)) |
364 | throw Exception{"Illegal type " + arguments[0].type->getName() + " of argument of function " + getName() + |
365 | ". Should be a date or a date with time" , ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT}; |
366 | } |
367 | else |
368 | { |
369 | if (!WhichDataType(arguments[0].type).isDateTime() |
370 | || !WhichDataType(arguments[2].type).isString()) |
371 | throw Exception( |
372 | "Function " + getName() + " supports 2 or 3 arguments. The 1st argument " |
373 | "must be of type Date or DateTime. The 2nd argument must be number. " |
374 | "The 3rd argument (optional) must be " |
375 | "a constant string with timezone name. The timezone argument is allowed " |
376 | "only when the 1st argument has the type DateTime" , |
377 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
378 | } |
379 | |
380 | switch (arguments[0].type->getTypeId()) |
381 | { |
382 | case TypeIndex::Date: |
383 | return resolveReturnType<DataTypeDate>(arguments); |
384 | case TypeIndex::DateTime: |
385 | return resolveReturnType<DataTypeDateTime>(arguments); |
386 | case TypeIndex::DateTime64: |
387 | return resolveReturnType<DataTypeDateTime64>(arguments); |
388 | default: |
389 | { |
390 | throw Exception("Invalid type of 1st argument of function " + getName() + ": " |
391 | + arguments[0].type->getName() + ", expected: Date, DateTime or DateTime64." , |
392 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
393 | } |
394 | } |
395 | } |
396 | |
397 | // Helper templates to deduce return type based on argument type, since some overloads may promote or denote types, e.g. addSeconds(Date, 1) => DateTime |
398 | template <typename FieldType> |
399 | using TransformExecuteReturnType = decltype(std::declval<Transform>().execute(FieldType(), 0, std::declval<DateLUTImpl>())); |
400 | |
401 | // Deduces RETURN DataType from INTPUT DataType, based on return type of Transform{}.execute(INPUT_TYPE, UInt64, DateLUTImpl). |
402 | // e.g. for Transform-type that has execute()-overload with 'UInt16' input and 'UInt32' return, |
403 | // argument type is expected to be 'Date', and result type is deduced to be 'DateTime'. |
404 | template <typename FromDataType> |
405 | using TransformResultDataType = typename date_and_time_type_details::ResultDataTypeMap<TransformExecuteReturnType<typename FromDataType::FieldType>>::ResultDataType; |
406 | |
407 | template <typename FromDataType> |
408 | DataTypePtr resolveReturnType(const ColumnsWithTypeAndName & arguments) const |
409 | { |
410 | using ResultDataType = TransformResultDataType<FromDataType>; |
411 | |
412 | if constexpr (std::is_same_v<ResultDataType, DataTypeDate>) |
413 | return std::make_shared<DataTypeDate>(); |
414 | else if constexpr (std::is_same_v<ResultDataType, DataTypeDateTime>) |
415 | { |
416 | return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); |
417 | } |
418 | else if constexpr (std::is_same_v<ResultDataType, DataTypeDateTime64>) |
419 | { |
420 | // TODO (vnemkov): what if there is an overload of Transform::execute() that returns DateTime64 from DateTime or Date ? |
421 | // Shall we use the default scale or one from optional argument ? |
422 | const auto & datetime64_type = assert_cast<const DataTypeDateTime64 &>(*arguments[0].type); |
423 | return std::make_shared<DataTypeDateTime64>(datetime64_type.getScale(), extractTimeZoneNameFromFunctionArguments(arguments, 2, 0)); |
424 | } |
425 | else |
426 | { |
427 | static_assert("Failed to resolve return type." ); |
428 | } |
429 | |
430 | //to make PVS and GCC happy. |
431 | return nullptr; |
432 | } |
433 | |
434 | bool useDefaultImplementationForConstants() const override { return true; } |
435 | ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {2}; } |
436 | |
437 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override |
438 | { |
439 | const IDataType * from_type = block.getByPosition(arguments[0]).type.get(); |
440 | WhichDataType which(from_type); |
441 | |
442 | if (which.isDate()) |
443 | { |
444 | DateTimeAddIntervalImpl<DataTypeDate, TransformResultDataType<DataTypeDate>, Transform>::execute(Transform{}, block, arguments, result); |
445 | } |
446 | else if (which.isDateTime()) |
447 | { |
448 | DateTimeAddIntervalImpl<DataTypeDateTime, TransformResultDataType<DataTypeDateTime>, Transform>::execute(Transform{}, block, arguments, result); |
449 | } |
450 | else if (const auto * datetime64_type = assert_cast<const DataTypeDateTime64 *>(from_type)) |
451 | { |
452 | DateTimeAddIntervalImpl<DataTypeDateTime64, TransformResultDataType<DataTypeDateTime64>, Transform>::execute(Transform{datetime64_type->getScale()}, block, arguments, result); |
453 | } |
454 | else |
455 | throw Exception("Illegal type " + block.getByPosition(arguments[0]).type->getName() + " of argument of function " + getName(), |
456 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
457 | } |
458 | }; |
459 | |
460 | } |
461 | |
462 | |