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 | |
13 | namespace DB |
14 | { |
15 | namespace 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 | |
24 | namespace |
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 | |
133 | class FunctionToStartOfInterval : public IFunction |
134 | { |
135 | public: |
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 | |
233 | private: |
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 | |
328 | void registerFunctionToStartOfInterval(FunctionFactory & factory) |
329 | { |
330 | factory.registerFunction<FunctionToStartOfInterval>(); |
331 | } |
332 | |
333 | } |
334 | |