ClickHouse
264 строки · 13.0 Кб
1#include <Columns/ColumnsDateTime.h>
2#include <Columns/ColumnsNumber.h>
3#include <Common/DateLUTImpl.h>
4#include <Common/IntervalKind.h>
5#include <DataTypes/DataTypeDate.h>
6#include <DataTypes/DataTypeDateTime.h>
7#include <DataTypes/DataTypeDateTime64.h>
8#include <DataTypes/DataTypeInterval.h>
9#include <Functions/DateTimeTransforms.h>
10#include <Functions/FunctionFactory.h>
11#include <Functions/IFunction.h>
12#include <IO/WriteHelpers.h>
13
14
15namespace DB
16{
17namespace ErrorCodes
18{
19extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
20extern const int ILLEGAL_COLUMN;
21extern const int ILLEGAL_TYPE_OF_ARGUMENT;
22extern const int ARGUMENT_OUT_OF_BOUND;
23}
24
25
26class FunctionToStartOfInterval : public IFunction
27{
28public:
29static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionToStartOfInterval>(); }
30
31static constexpr auto name = "toStartOfInterval";
32String getName() const override { return name; }
33bool isVariadic() const override { return true; }
34size_t getNumberOfArguments() const override { return 0; }
35bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
36bool useDefaultImplementationForConstants() const override { return true; }
37ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; }
38bool hasInformationAboutMonotonicity() const override { return true; }
39Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override { return { .is_monotonic = true, .is_always_monotonic = true }; }
40
41DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
42{
43bool value_is_date = false;
44auto check_first_argument = [&]
45{
46const DataTypePtr & type_arg1 = arguments[0].type;
47if (!isDate(type_arg1) && !isDateTime(type_arg1) && !isDateTime64(type_arg1))
48throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
49"Illegal type {} of 1st argument of function {}, expected a Date, DateTime or DateTime64",
50type_arg1->getName(), getName());
51value_is_date = isDate(type_arg1);
52};
53
54const DataTypeInterval * interval_type = nullptr;
55enum class ResultType
56{
57Date,
58DateTime,
59DateTime64
60};
61ResultType result_type;
62auto check_second_argument = [&]
63{
64const DataTypePtr & type_arg2 = arguments[1].type;
65
66interval_type = checkAndGetDataType<DataTypeInterval>(type_arg2.get());
67if (!interval_type)
68throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
69"Illegal type {} of 2nd argument of function {}, expected a time interval",
70type_arg2->getName(), getName());
71
72switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case)
73{
74case IntervalKind::Kind::Nanosecond:
75case IntervalKind::Kind::Microsecond:
76case IntervalKind::Kind::Millisecond:
77result_type = ResultType::DateTime64;
78break;
79case IntervalKind::Kind::Second:
80case IntervalKind::Kind::Minute:
81case IntervalKind::Kind::Hour:
82case IntervalKind::Kind::Day: /// weird why Day leads to DateTime but too afraid to change it
83result_type = ResultType::DateTime;
84break;
85case IntervalKind::Kind::Week:
86case IntervalKind::Kind::Month:
87case IntervalKind::Kind::Quarter:
88case IntervalKind::Kind::Year:
89result_type = ResultType::Date;
90break;
91}
92};
93
94auto check_third_argument = [&]
95{
96const DataTypePtr & type_arg3 = arguments[2].type;
97if (!isString(type_arg3))
98throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
99"Illegal type {} of 3rd argument of function {}, expected a constant timezone string",
100type_arg3->getName(), getName());
101if (value_is_date && result_type == ResultType::Date) /// weird why this is && instead of || but too afraid to change it
102throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
103"The timezone argument of function {} with interval type {} is allowed only when the 1st argument has type DateTime or DateTimt64",
104getName(), interval_type->getKind().toString());
105};
106
107if (arguments.size() == 2)
108{
109check_first_argument();
110check_second_argument();
111}
112else if (arguments.size() == 3)
113{
114check_first_argument();
115check_second_argument();
116check_third_argument();
117}
118else
119{
120throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
121"Number of arguments for function {} doesn't match: passed {}, should be 2 or 3",
122getName(), arguments.size());
123}
124
125switch (result_type)
126{
127case ResultType::Date:
128return std::make_shared<DataTypeDate>();
129case ResultType::DateTime:
130return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0, false));
131case ResultType::DateTime64:
132{
133UInt32 scale = 0;
134if (interval_type->getKind() == IntervalKind::Kind::Nanosecond)
135scale = 9;
136else if (interval_type->getKind() == IntervalKind::Kind::Microsecond)
137scale = 6;
138else if (interval_type->getKind() == IntervalKind::Kind::Millisecond)
139scale = 3;
140
141return std::make_shared<DataTypeDateTime64>(scale, extractTimeZoneNameFromFunctionArguments(arguments, 2, 0, false));
142}
143}
144
145std::unreachable();
146}
147
148ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t /* input_rows_count */) const override
149{
150const auto & time_column = arguments[0];
151const auto & interval_column = arguments[1];
152const auto & time_zone = extractTimeZoneFromFunctionArguments(arguments, 2, 0);
153auto result_column = dispatchForTimeColumn(time_column, interval_column, result_type, time_zone);
154return result_column;
155}
156
157private:
158ColumnPtr dispatchForTimeColumn(
159const ColumnWithTypeAndName & time_column, const ColumnWithTypeAndName & interval_column,
160const DataTypePtr & result_type, const DateLUTImpl & time_zone) const
161{
162const auto & time_column_type = *time_column.type.get();
163const auto & time_column_col = *time_column.column.get();
164
165if (isDateTime64(time_column_type))
166{
167const auto * time_column_vec = checkAndGetColumn<ColumnDateTime64>(time_column_col);
168auto scale = assert_cast<const DataTypeDateTime64 &>(time_column_type).getScale();
169
170if (time_column_vec)
171return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime64 &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone, scale);
172}
173else if (isDateTime(time_column_type))
174{
175const auto * time_column_vec = checkAndGetColumn<ColumnDateTime>(time_column_col);
176if (time_column_vec)
177return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone);
178}
179else if (isDate(time_column_type))
180{
181const auto * time_column_vec = checkAndGetColumn<ColumnDate>(time_column_col);
182if (time_column_vec)
183return dispatchForIntervalColumn(assert_cast<const DataTypeDate &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone);
184}
185throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column for 1st argument of function {}, expected a Date, DateTime or DateTime64", getName());
186}
187
188template <typename TimeDataType, typename TimeColumnType>
189ColumnPtr dispatchForIntervalColumn(
190const TimeDataType & time_data_type, const TimeColumnType & time_column, const ColumnWithTypeAndName & interval_column,
191const DataTypePtr & result_type, const DateLUTImpl & time_zone, UInt16 scale = 1) const
192{
193const auto * interval_type = checkAndGetDataType<DataTypeInterval>(interval_column.type.get());
194if (!interval_type)
195throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a time interval", getName());
196
197const auto * interval_column_const_int64 = checkAndGetColumnConst<ColumnInt64>(interval_column.column.get());
198if (!interval_column_const_int64)
199throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a const time interval", getName());
200
201const Int64 num_units = interval_column_const_int64->getValue<Int64>();
202if (num_units <= 0)
203throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Value for 2nd argument of function {} must be positive", getName());
204
205switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case)
206{
207case IntervalKind::Kind::Nanosecond:
208return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Nanosecond>(time_data_type, time_column, num_units, result_type, time_zone, scale);
209case IntervalKind::Kind::Microsecond:
210return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Microsecond>(time_data_type, time_column, num_units, result_type, time_zone, scale);
211case IntervalKind::Kind::Millisecond:
212return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Millisecond>(time_data_type, time_column, num_units, result_type, time_zone, scale);
213case IntervalKind::Kind::Second:
214return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Second>(time_data_type, time_column, num_units, result_type, time_zone, scale);
215case IntervalKind::Kind::Minute:
216return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Minute>(time_data_type, time_column, num_units, result_type, time_zone, scale);
217case IntervalKind::Kind::Hour:
218return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Hour>(time_data_type, time_column, num_units, result_type, time_zone, scale);
219case IntervalKind::Kind::Day:
220return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Day>(time_data_type, time_column, num_units, result_type, time_zone, scale);
221case IntervalKind::Kind::Week:
222return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Week>(time_data_type, time_column, num_units, result_type, time_zone, scale);
223case IntervalKind::Kind::Month:
224return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Month>(time_data_type, time_column, num_units, result_type, time_zone, scale);
225case IntervalKind::Kind::Quarter:
226return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Quarter>(time_data_type, time_column, num_units, result_type, time_zone, scale);
227case IntervalKind::Kind::Year:
228return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Year>(time_data_type, time_column, num_units, result_type, time_zone, scale);
229}
230
231std::unreachable();
232}
233
234template <typename TimeDataType, typename TimeColumnType, typename ResultDataType, IntervalKind::Kind unit>
235ColumnPtr execute(
236const TimeDataType &, const TimeColumnType & time_column_type, Int64 num_units,
237const DataTypePtr & result_type, const DateLUTImpl & time_zone, UInt16 scale) const
238{
239using ResultColumnType = typename ResultDataType::ColumnType;
240using ResultFieldType = typename ResultDataType::FieldType;
241
242const auto & time_data = time_column_type.getData();
243size_t size = time_data.size();
244
245auto result_col = result_type->createColumn();
246auto * col_to = assert_cast<ResultColumnType *>(result_col.get());
247auto & result_data = col_to->getData();
248result_data.resize(size);
249
250Int64 scale_multiplier = DecimalUtils::scaleMultiplier<DateTime64>(scale);
251
252for (size_t i = 0; i != size; ++i)
253result_data[i] = static_cast<ResultFieldType>(ToStartOfInterval<unit>::execute(time_data[i], num_units, time_zone, scale_multiplier));
254
255return result_col;
256}
257};
258
259REGISTER_FUNCTION(ToStartOfInterval)
260{
261factory.registerFunction<FunctionToStartOfInterval>();
262}
263
264}
265