1 | #include <DataTypes/DataTypeDateTime.h> |
2 | #include <DataTypes/DataTypeDateTime64.h> |
3 | #include <DataTypes/DataTypesNumber.h> |
4 | #include <Columns/ColumnString.h> |
5 | #include <Columns/ColumnsNumber.h> |
6 | |
7 | #include <Functions/IFunctionImpl.h> |
8 | #include <Functions/FunctionHelpers.h> |
9 | #include <Functions/FunctionFactory.h> |
10 | #include <Functions/extractTimeZoneFromFunctionArguments.h> |
11 | #include <Functions/DateTimeTransforms.h> |
12 | |
13 | #include <IO/WriteHelpers.h> |
14 | |
15 | #include <common/find_symbols.h> |
16 | |
17 | #include <type_traits> |
18 | |
19 | |
20 | namespace DB |
21 | { |
22 | |
23 | namespace ErrorCodes |
24 | { |
25 | extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; |
26 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
27 | extern const int ILLEGAL_COLUMN; |
28 | extern const int BAD_ARGUMENTS; |
29 | } |
30 | |
31 | /** dateDiff('unit', t1, t2, [timezone]) |
32 | * t1 and t2 can be Date or DateTime |
33 | * |
34 | * If timezone is specified, it applied to both arguments. |
35 | * If not, timezones from datatypes t1 and t2 are used. |
36 | * If that timezones are not the same, the result is unspecified. |
37 | * |
38 | * Timezone matters because days can have different length. |
39 | */ |
40 | class FunctionDateDiff : public IFunction |
41 | { |
42 | public: |
43 | static constexpr auto name = "dateDiff" ; |
44 | static FunctionPtr create(const Context &) { return std::make_shared<FunctionDateDiff>(); } |
45 | |
46 | String getName() const override |
47 | { |
48 | return name; |
49 | } |
50 | |
51 | bool isVariadic() const override { return true; } |
52 | size_t getNumberOfArguments() const override { return 0; } |
53 | |
54 | DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override |
55 | { |
56 | if (arguments.size() != 3 && arguments.size() != 4) |
57 | throw Exception("Number of arguments for function " + getName() + " doesn't match: passed " |
58 | + toString(arguments.size()) + ", should be 3 or 4" , |
59 | ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); |
60 | |
61 | if (!isString(arguments[0])) |
62 | throw Exception("First argument for function " + getName() + " (unit) must be String" , |
63 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
64 | |
65 | if (!isDateOrDateTime(arguments[1])) |
66 | throw Exception("Second argument for function " + getName() + " must be Date or DateTime" , |
67 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
68 | |
69 | if (!isDateOrDateTime(arguments[2])) |
70 | throw Exception("Third argument for function " + getName() + " must be Date or DateTime" , |
71 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
72 | |
73 | if (arguments.size() == 4 && !isString(arguments[3])) |
74 | throw Exception("Fourth argument for function " + getName() + " (timezone) must be String" , |
75 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
76 | |
77 | return std::make_shared<DataTypeInt64>(); |
78 | } |
79 | |
80 | bool useDefaultImplementationForConstants() const override { return true; } |
81 | ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {0, 3}; } |
82 | |
83 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override |
84 | { |
85 | auto * unit_column = checkAndGetColumnConst<ColumnString>(block.getByPosition(arguments[0]).column.get()); |
86 | if (!unit_column) |
87 | throw Exception("First argument for function " + getName() + " must be constant String" , ErrorCodes::ILLEGAL_COLUMN); |
88 | |
89 | String unit = Poco::toLower(unit_column->getValue<String>()); |
90 | |
91 | const IColumn & x = *block.getByPosition(arguments[1]).column; |
92 | const IColumn & y = *block.getByPosition(arguments[2]).column; |
93 | |
94 | size_t rows = input_rows_count; |
95 | auto res = ColumnInt64::create(rows); |
96 | |
97 | const DateLUTImpl & timezone_x = extractTimeZoneFromFunctionArguments(block, arguments, 3, 1); |
98 | const DateLUTImpl & timezone_y = extractTimeZoneFromFunctionArguments(block, arguments, 3, 2); |
99 | |
100 | if (unit == "year" || unit == "yy" || unit == "yyyy" ) |
101 | dispatchForColumns<ToRelativeYearNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
102 | else if (unit == "quarter" || unit == "qq" || unit == "q" ) |
103 | dispatchForColumns<ToRelativeQuarterNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
104 | else if (unit == "month" || unit == "mm" || unit == "m" ) |
105 | dispatchForColumns<ToRelativeMonthNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
106 | else if (unit == "week" || unit == "wk" || unit == "ww" ) |
107 | dispatchForColumns<ToRelativeWeekNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
108 | else if (unit == "day" || unit == "dd" || unit == "d" ) |
109 | dispatchForColumns<ToRelativeDayNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
110 | else if (unit == "hour" || unit == "hh" ) |
111 | dispatchForColumns<ToRelativeHourNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
112 | else if (unit == "minute" || unit == "mi" || unit == "n" ) |
113 | dispatchForColumns<ToRelativeMinuteNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
114 | else if (unit == "second" || unit == "ss" || unit == "s" ) |
115 | dispatchForColumns<ToRelativeSecondNumImpl>(x, y, timezone_x, timezone_y, res->getData()); |
116 | else |
117 | throw Exception("Function " + getName() + " does not support '" + unit + "' unit" , ErrorCodes::BAD_ARGUMENTS); |
118 | |
119 | block.getByPosition(result).column = std::move(res); |
120 | } |
121 | |
122 | private: |
123 | template <typename Transform> |
124 | void dispatchForColumns( |
125 | const IColumn & x, const IColumn & y, |
126 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
127 | ColumnInt64::Container & result) |
128 | { |
129 | if (auto * x_vec_16 = checkAndGetColumn<ColumnUInt16>(&x)) |
130 | dispatchForSecondColumn<Transform>(*x_vec_16, y, timezone_x, timezone_y, result); |
131 | else if (auto * x_vec_32 = checkAndGetColumn<ColumnUInt32>(&x)) |
132 | dispatchForSecondColumn<Transform>(*x_vec_32, y, timezone_x, timezone_y, result); |
133 | else if (auto * x_const_16 = checkAndGetColumnConst<ColumnUInt16>(&x)) |
134 | dispatchConstForSecondColumn<Transform>(x_const_16->getValue<UInt16>(), y, timezone_x, timezone_y, result); |
135 | else if (auto * x_const_32 = checkAndGetColumnConst<ColumnUInt32>(&x)) |
136 | dispatchConstForSecondColumn<Transform>(x_const_32->getValue<UInt32>(), y, timezone_x, timezone_y, result); |
137 | else |
138 | throw Exception("Illegal column for first argument of function " + getName() + ", must be Date or DateTime" , ErrorCodes::ILLEGAL_COLUMN); |
139 | } |
140 | |
141 | template <typename Transform, typename T1> |
142 | void dispatchForSecondColumn( |
143 | const ColumnVector<T1> & x, const IColumn & y, |
144 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
145 | ColumnInt64::Container & result) |
146 | { |
147 | if (auto * y_vec_16 = checkAndGetColumn<ColumnUInt16>(&y)) |
148 | vector_vector<Transform>(x, *y_vec_16, timezone_x, timezone_y, result); |
149 | else if (auto * y_vec_32 = checkAndGetColumn<ColumnUInt32>(&y)) |
150 | vector_vector<Transform>(x, *y_vec_32, timezone_x, timezone_y, result); |
151 | else if (auto * y_const_16 = checkAndGetColumnConst<ColumnUInt16>(&y)) |
152 | vector_constant<Transform>(x, y_const_16->getValue<UInt16>(), timezone_x, timezone_y, result); |
153 | else if (auto * y_const_32 = checkAndGetColumnConst<ColumnUInt32>(&y)) |
154 | vector_constant<Transform>(x, y_const_32->getValue<UInt32>(), timezone_x, timezone_y, result); |
155 | else |
156 | throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime" , ErrorCodes::ILLEGAL_COLUMN); |
157 | } |
158 | |
159 | template <typename Transform, typename T1> |
160 | void dispatchConstForSecondColumn( |
161 | T1 x, const IColumn & y, |
162 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
163 | ColumnInt64::Container & result) |
164 | { |
165 | if (auto * y_vec_16 = checkAndGetColumn<ColumnUInt16>(&y)) |
166 | constant_vector<Transform>(x, *y_vec_16, timezone_x, timezone_y, result); |
167 | else if (auto * y_vec_32 = checkAndGetColumn<ColumnUInt32>(&y)) |
168 | constant_vector<Transform>(x, *y_vec_32, timezone_x, timezone_y, result); |
169 | else |
170 | throw Exception("Illegal column for second argument of function " + getName() + ", must be Date or DateTime" , ErrorCodes::ILLEGAL_COLUMN); |
171 | } |
172 | |
173 | template <typename Transform, typename T1, typename T2> |
174 | void vector_vector( |
175 | const ColumnVector<T1> & x, const ColumnVector<T2> & y, |
176 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
177 | ColumnInt64::Container & result) |
178 | { |
179 | const auto & x_data = x.getData(); |
180 | const auto & y_data = y.getData(); |
181 | for (size_t i = 0, size = x.size(); i < size; ++i) |
182 | result[i] = calculate<Transform>(x_data[i], y_data[i], timezone_x, timezone_y); |
183 | } |
184 | |
185 | template <typename Transform, typename T1, typename T2> |
186 | void vector_constant( |
187 | const ColumnVector<T1> & x, T2 y, |
188 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
189 | ColumnInt64::Container & result) |
190 | { |
191 | const auto & x_data = x.getData(); |
192 | for (size_t i = 0, size = x.size(); i < size; ++i) |
193 | result[i] = calculate<Transform>(x_data[i], y, timezone_x, timezone_y); |
194 | } |
195 | |
196 | template <typename Transform, typename T1, typename T2> |
197 | void constant_vector( |
198 | T1 x, const ColumnVector<T2> & y, |
199 | const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y, |
200 | ColumnInt64::Container & result) |
201 | { |
202 | const auto & y_data = y.getData(); |
203 | for (size_t i = 0, size = y.size(); i < size; ++i) |
204 | result[i] = calculate<Transform>(x, y_data[i], timezone_x, timezone_y); |
205 | } |
206 | |
207 | template <typename Transform, typename T1, typename T2> |
208 | Int64 calculate(T1 x, T2 y, const DateLUTImpl & timezone_x, const DateLUTImpl & timezone_y) |
209 | { |
210 | return Int64(Transform::execute(y, timezone_y)) |
211 | - Int64(Transform::execute(x, timezone_x)); |
212 | } |
213 | }; |
214 | |
215 | void registerFunctionDateDiff(FunctionFactory & factory) |
216 | { |
217 | factory.registerFunction<FunctionDateDiff>(FunctionFactory::CaseInsensitive); |
218 | } |
219 | |
220 | } |
221 | |
222 | |