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
16namespace DB
17{
18
19namespace 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.
31template <typename T>
32struct 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
61struct 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
80struct 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
99struct 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
118struct 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
143struct 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
162struct 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
181struct 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
200struct 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
219template <typename Transform>
220struct 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
231struct SubtractSecondsImpl : SubtractIntervalImpl<AddSecondsImpl> { static constexpr auto name = "subtractSeconds"; };
232struct SubtractMinutesImpl : SubtractIntervalImpl<AddMinutesImpl> { static constexpr auto name = "subtractMinutes"; };
233struct SubtractHoursImpl : SubtractIntervalImpl<AddHoursImpl> { static constexpr auto name = "subtractHours"; };
234struct SubtractDaysImpl : SubtractIntervalImpl<AddDaysImpl> { static constexpr auto name = "subtractDays"; };
235struct SubtractWeeksImpl : SubtractIntervalImpl<AddWeeksImpl> { static constexpr auto name = "subtractWeeks"; };
236struct SubtractMonthsImpl : SubtractIntervalImpl<AddMonthsImpl> { static constexpr auto name = "subtractMonths"; };
237struct SubtractQuartersImpl : SubtractIntervalImpl<AddQuartersImpl> { static constexpr auto name = "subtractQuarters"; };
238struct SubtractYearsImpl : SubtractIntervalImpl<AddYearsImpl> { static constexpr auto name = "subtractYears"; };
239
240
241template <typename Transform>
242struct 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
282template <typename FromDataType, typename ToDataType, typename Transform>
283struct 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
324namespace date_and_time_type_details
325{
326// Compile-time mapping of value (DataType::FieldType) types to corresponding DataType
327template <typename FieldType> struct ResultDataTypeMap {};
328template <> struct ResultDataTypeMap<UInt16> { using ResultDataType = DataTypeDate; };
329template <> struct ResultDataTypeMap<Int16> { using ResultDataType = DataTypeDate; };
330template <> struct ResultDataTypeMap<UInt32> { using ResultDataType = DataTypeDateTime; };
331template <> struct ResultDataTypeMap<Int32> { using ResultDataType = DataTypeDateTime; };
332template <> struct ResultDataTypeMap<DateTime64> { using ResultDataType = DataTypeDateTime64; };
333}
334
335template <typename Transform>
336class FunctionDateOrDateTimeAddInterval : public IFunction
337{
338public:
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