1#include <common/DateLUTImpl.h>
2#include <Columns/ColumnsNumber.h>
3#include <DataTypes/DataTypeDate.h>
4#include <DataTypes/DataTypeDateTime.h>
5#include <DataTypes/DataTypeDateTime64.h>
6#include <DataTypes/DataTypeInterval.h>
7#include <Functions/DateTimeTransforms.h>
8#include <Functions/FunctionFactory.h>
9#include <Functions/IFunctionImpl.h>
10#include <IO/WriteHelpers.h>
11
12
13namespace DB
14{
15namespace ErrorCodes
16{
17 extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
18 extern const int ILLEGAL_COLUMN;
19 extern const int ILLEGAL_TYPE_OF_ARGUMENT;
20 extern const int ARGUMENT_OUT_OF_BOUND;
21}
22
23
24namespace
25{
26 static constexpr auto function_name = "toStartOfInterval";
27
28 template <IntervalKind::Kind unit>
29 struct Transform;
30
31 template <>
32 struct Transform<IntervalKind::Year>
33 {
34 static UInt16 execute(UInt16 d, UInt64 years, const DateLUTImpl & time_zone)
35 {
36 return time_zone.toStartOfYearInterval(DayNum(d), years);
37 }
38
39 static UInt16 execute(UInt32 t, UInt64 years, const DateLUTImpl & time_zone)
40 {
41 return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years);
42 }
43 };
44
45 template <>
46 struct Transform<IntervalKind::Quarter>
47 {
48 static UInt16 execute(UInt16 d, UInt64 quarters, const DateLUTImpl & time_zone)
49 {
50 return time_zone.toStartOfQuarterInterval(DayNum(d), quarters);
51 }
52
53 static UInt16 execute(UInt32 t, UInt64 quarters, const DateLUTImpl & time_zone)
54 {
55 return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters);
56 }
57 };
58
59 template <>
60 struct Transform<IntervalKind::Month>
61 {
62 static UInt16 execute(UInt16 d, UInt64 months, const DateLUTImpl & time_zone)
63 {
64 return time_zone.toStartOfMonthInterval(DayNum(d), months);
65 }
66
67 static UInt16 execute(UInt32 t, UInt64 months, const DateLUTImpl & time_zone)
68 {
69 return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months);
70 }
71 };
72
73 template <>
74 struct Transform<IntervalKind::Week>
75 {
76 static UInt16 execute(UInt16 d, UInt64 weeks, const DateLUTImpl & time_zone)
77 {
78 return time_zone.toStartOfWeekInterval(DayNum(d), weeks);
79 }
80
81 static UInt16 execute(UInt32 t, UInt64 weeks, const DateLUTImpl & time_zone)
82 {
83 return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks);
84 }
85 };
86
87 template <>
88 struct Transform<IntervalKind::Day>
89 {
90 static UInt32 execute(UInt16 d, UInt64 days, const DateLUTImpl & time_zone)
91 {
92 return time_zone.toStartOfDayInterval(DayNum(d), days);
93 }
94
95 static UInt32 execute(UInt32 t, UInt64 days, const DateLUTImpl & time_zone)
96 {
97 return time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days);
98 }
99 };
100
101 template <>
102 struct Transform<IntervalKind::Hour>
103 {
104 static UInt32 execute(UInt16, UInt64, const DateLUTImpl &) { return dateIsNotSupported(function_name); }
105
106 static UInt32 execute(UInt32 t, UInt64 hours, const DateLUTImpl & time_zone) { return time_zone.toStartOfHourInterval(t, hours); }
107 };
108
109 template <>
110 struct Transform<IntervalKind::Minute>
111 {
112 static UInt32 execute(UInt16, UInt64, const DateLUTImpl &) { return dateIsNotSupported(function_name); }
113
114 static UInt32 execute(UInt32 t, UInt64 minutes, const DateLUTImpl & time_zone)
115 {
116 return time_zone.toStartOfMinuteInterval(t, minutes);
117 }
118 };
119
120 template <>
121 struct Transform<IntervalKind::Second>
122 {
123 static UInt32 execute(UInt16, UInt64, const DateLUTImpl &) { return dateIsNotSupported(function_name); }
124
125 static UInt32 execute(UInt32 t, UInt64 seconds, const DateLUTImpl & time_zone)
126 {
127 return time_zone.toStartOfSecondInterval(t, seconds);
128 }
129 };
130}
131
132
133class FunctionToStartOfInterval : public IFunction
134{
135public:
136 static FunctionPtr create(const Context &) { return std::make_shared<FunctionToStartOfInterval>(); }
137
138 static constexpr auto name = function_name;
139 String getName() const override { return name; }
140
141 bool isVariadic() const override { return true; }
142 size_t getNumberOfArguments() const override { return 0; }
143
144 DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
145 {
146 bool first_argument_is_date = false;
147 auto check_first_argument = [&]
148 {
149 if (!isDateOrDateTime(arguments[0].type))
150 throw Exception(
151 "Illegal type " + arguments[0].type->getName() + " of argument of function " + getName()
152 + ". Should be a date or a date with time",
153 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
154 first_argument_is_date = isDate(arguments[0].type);
155 };
156
157 const DataTypeInterval * interval_type = nullptr;
158 bool result_type_is_date = false;
159 auto check_interval_argument = [&]
160 {
161 interval_type = checkAndGetDataType<DataTypeInterval>(arguments[1].type.get());
162 if (!interval_type)
163 throw Exception(
164 "Illegal type " + arguments[1].type->getName() + " of argument of function " + getName()
165 + ". Should be an interval of time",
166 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
167 result_type_is_date = (interval_type->getKind() == IntervalKind::Year)
168 || (interval_type->getKind() == IntervalKind::Quarter) || (interval_type->getKind() == IntervalKind::Month)
169 || (interval_type->getKind() == IntervalKind::Week);
170 };
171
172 auto check_timezone_argument = [&]
173 {
174 if (!WhichDataType(arguments[2].type).isString())
175 throw Exception(
176 "Illegal type " + arguments[2].type->getName() + " of argument of function " + getName()
177 + ". This argument is optional and must be a constant string with timezone name",
178 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
179 if (first_argument_is_date && result_type_is_date)
180 throw Exception(
181 "The timezone argument of function " + getName() + " with interval type " + interval_type->getKind().toString()
182 + " is allowed only when the 1st argument has the type DateTime",
183 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
184 };
185
186 if (arguments.size() == 2)
187 {
188 check_first_argument();
189 check_interval_argument();
190 }
191 else if (arguments.size() == 3)
192 {
193 check_first_argument();
194 check_interval_argument();
195 check_timezone_argument();
196 }
197 else
198 {
199 throw Exception(
200 "Number of arguments for function " + getName() + " doesn't match: passed " + toString(arguments.size())
201 + ", should be 2 or 3",
202 ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
203 }
204
205 if (result_type_is_date)
206 return std::make_shared<DataTypeDate>();
207 else
208 return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0));
209 }
210
211 bool useDefaultImplementationForConstants() const override { return true; }
212 ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; }
213
214 void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /* input_rows_count */) override
215 {
216 const auto & time_column = block.getByPosition(arguments[0]);
217 const auto & interval_column = block.getByPosition(arguments[1]);
218 const DateLUTImpl & time_zone = extractTimeZoneFromFunctionArguments(block, arguments, 2, 0);
219 auto result_column = dispatchForColumns(time_column, interval_column, time_zone);
220 block.getByPosition(result).column = std::move(result_column);
221 }
222
223 bool hasInformationAboutMonotonicity() const override
224 {
225 return true;
226 }
227
228 Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override
229 {
230 return { true, true, true };
231 }
232
233private:
234 ColumnPtr dispatchForColumns(
235 const ColumnWithTypeAndName & time_column, const ColumnWithTypeAndName & interval_column, const DateLUTImpl & time_zone)
236 {
237 const auto & from_datatype = *time_column.type.get();
238 const auto which_type = WhichDataType(from_datatype);
239 if (which_type.isDateTime())
240 {
241 const auto * time_column_vec = checkAndGetColumn<ColumnUInt32>(time_column.column.get());
242 if (time_column_vec)
243 return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime&>(from_datatype), *time_column_vec, interval_column, time_zone);
244 }
245 if (which_type.isDate())
246 {
247 const auto * time_column_vec = checkAndGetColumn<ColumnUInt16>(time_column.column.get());
248 if (time_column_vec)
249 return dispatchForIntervalColumn(assert_cast<const DataTypeDate&>(from_datatype), *time_column_vec, interval_column, time_zone);
250 }
251 if (which_type.isDateTime64())
252 {
253 const auto * time_column_vec = checkAndGetColumn<DataTypeDateTime64::ColumnType>(time_column.column.get());
254 if (time_column_vec)
255 return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime64&>(from_datatype), *time_column_vec, interval_column, time_zone);
256 }
257 throw Exception(
258 "Illegal column for first argument of function " + getName() + ". Must contain dates or dates with time",
259 ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
260 }
261
262 template <typename ColumnType, typename FromDataType>
263 ColumnPtr dispatchForIntervalColumn(
264 const FromDataType & from, const ColumnType & time_column, const ColumnWithTypeAndName & interval_column, const DateLUTImpl & time_zone)
265 {
266 const auto * interval_type = checkAndGetDataType<DataTypeInterval>(interval_column.type.get());
267 if (!interval_type)
268 throw Exception(
269 "Illegal column for second argument of function " + getName() + ", must be an interval of time.",
270 ErrorCodes::ILLEGAL_COLUMN);
271 const auto * interval_column_const_int64 = checkAndGetColumnConst<ColumnInt64>(interval_column.column.get());
272 if (!interval_column_const_int64)
273 throw Exception(
274 "Illegal column for second argument of function " + getName() + ", must be a const interval of time.", ErrorCodes::ILLEGAL_COLUMN);
275 Int64 num_units = interval_column_const_int64->getValue<Int64>();
276 if (num_units <= 0)
277 throw Exception("Value for second argument of function " + getName() + " must be positive.", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
278
279 switch (interval_type->getKind())
280 {
281 case IntervalKind::Second:
282 return execute<FromDataType, UInt32, IntervalKind::Second>(from, time_column, num_units, time_zone);
283 case IntervalKind::Minute:
284 return execute<FromDataType, UInt32, IntervalKind::Minute>(from, time_column, num_units, time_zone);
285 case IntervalKind::Hour:
286 return execute<FromDataType, UInt32, IntervalKind::Hour>(from, time_column, num_units, time_zone);
287 case IntervalKind::Day:
288 return execute<FromDataType, UInt32, IntervalKind::Day>(from, time_column, num_units, time_zone);
289 case IntervalKind::Week:
290 return execute<FromDataType, UInt16, IntervalKind::Week>(from, time_column, num_units, time_zone);
291 case IntervalKind::Month:
292 return execute<FromDataType, UInt16, IntervalKind::Month>(from, time_column, num_units, time_zone);
293 case IntervalKind::Quarter:
294 return execute<FromDataType, UInt16, IntervalKind::Quarter>(from, time_column, num_units, time_zone);
295 case IntervalKind::Year:
296 return execute<FromDataType, UInt16, IntervalKind::Year>(from, time_column, num_units, time_zone);
297 }
298
299 __builtin_unreachable();
300 }
301
302
303 template <typename FromDataType, typename ToType, IntervalKind::Kind unit, typename ColumnType>
304 ColumnPtr execute(const FromDataType & from_datatype, const ColumnType & time_column, UInt64 num_units, const DateLUTImpl & time_zone)
305 {
306 const auto & time_data = time_column.getData();
307 size_t size = time_column.size();
308 auto result = ColumnVector<ToType>::create();
309 auto & result_data = result->getData();
310 result_data.resize(size);
311
312 if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64>)
313 {
314 const auto transform = DateTime64BasicTransformWrapper<Transform<unit>>{from_datatype.getScale()};
315 for (size_t i = 0; i != size; ++i)
316 result_data[i] = transform.execute(time_data[i], num_units, time_zone);
317 }
318 else
319 {
320 for (size_t i = 0; i != size; ++i)
321 result_data[i] = Transform<unit>::execute(time_data[i], num_units, time_zone);
322 }
323 return result;
324 }
325};
326
327
328void registerFunctionToStartOfInterval(FunctionFactory & factory)
329{
330 factory.registerFunction<FunctionToStartOfInterval>();
331}
332
333}
334