1 | #include <Functions/FunctionFactory.h> |
2 | #include <Functions/FunctionsLogical.h> |
3 | |
4 | #include <Columns/IColumn.h> |
5 | #include <Columns/ColumnVector.h> |
6 | #include <Columns/ColumnsNumber.h> |
7 | #include <Columns/ColumnConst.h> |
8 | #include <Columns/ColumnNullable.h> |
9 | #include <Common/FieldVisitors.h> |
10 | #include <Common/typeid_cast.h> |
11 | #include <DataTypes/DataTypeNullable.h> |
12 | #include <DataTypes/DataTypesNumber.h> |
13 | #include <Functions/FunctionHelpers.h> |
14 | |
15 | #include <algorithm> |
16 | |
17 | |
18 | namespace DB |
19 | { |
20 | |
21 | void registerFunctionsLogical(FunctionFactory & factory) |
22 | { |
23 | factory.registerFunction<FunctionAnd>(); |
24 | factory.registerFunction<FunctionOr>(); |
25 | factory.registerFunction<FunctionXor>(); |
26 | factory.registerFunction<FunctionNot>(); |
27 | } |
28 | |
29 | namespace ErrorCodes |
30 | { |
31 | extern const int LOGICAL_ERROR; |
32 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
33 | extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; |
34 | extern const int ILLEGAL_COLUMN; |
35 | } |
36 | |
37 | namespace |
38 | { |
39 | using namespace FunctionsLogicalDetail; |
40 | |
41 | using UInt8Container = ColumnUInt8::Container; |
42 | using UInt8ColumnPtrs = std::vector<const ColumnUInt8 *>; |
43 | |
44 | |
45 | MutableColumnPtr convertFromTernaryData(const UInt8Container & ternary_data, const bool make_nullable) |
46 | { |
47 | const size_t rows_count = ternary_data.size(); |
48 | |
49 | auto new_column = ColumnUInt8::create(rows_count); |
50 | std::transform( |
51 | ternary_data.cbegin(), ternary_data.cend(), new_column->getData().begin(), |
52 | [](const auto x) { return x == Ternary::True; }); |
53 | |
54 | if (!make_nullable) |
55 | return new_column; |
56 | |
57 | auto null_column = ColumnUInt8::create(rows_count); |
58 | std::transform( |
59 | ternary_data.cbegin(), ternary_data.cend(), null_column->getData().begin(), |
60 | [](const auto x) { return x == Ternary::Null; }); |
61 | |
62 | return ColumnNullable::create(std::move(new_column), std::move(null_column)); |
63 | } |
64 | |
65 | template <typename T> |
66 | bool tryConvertColumnToUInt8(const IColumn * column, UInt8Container & res) |
67 | { |
68 | const auto col = checkAndGetColumn<ColumnVector<T>>(column); |
69 | if (!col) |
70 | return false; |
71 | |
72 | std::transform( |
73 | col->getData().cbegin(), col->getData().cend(), res.begin(), |
74 | [](const auto x) { return x != 0; }); |
75 | |
76 | return true; |
77 | } |
78 | |
79 | void convertColumnToUInt8(const IColumn * column, UInt8Container & res) |
80 | { |
81 | if (!tryConvertColumnToUInt8<Int8>(column, res) && |
82 | !tryConvertColumnToUInt8<Int16>(column, res) && |
83 | !tryConvertColumnToUInt8<Int32>(column, res) && |
84 | !tryConvertColumnToUInt8<Int64>(column, res) && |
85 | !tryConvertColumnToUInt8<UInt16>(column, res) && |
86 | !tryConvertColumnToUInt8<UInt32>(column, res) && |
87 | !tryConvertColumnToUInt8<UInt64>(column, res) && |
88 | !tryConvertColumnToUInt8<Float32>(column, res) && |
89 | !tryConvertColumnToUInt8<Float64>(column, res)) |
90 | throw Exception("Unexpected type of column: " + column->getName(), ErrorCodes::ILLEGAL_COLUMN); |
91 | } |
92 | |
93 | |
94 | template <class Op, typename Func> |
95 | static bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res, Func && func) |
96 | { |
97 | bool has_res = false; |
98 | |
99 | for (int i = static_cast<int>(in.size()) - 1; i >= 0; --i) |
100 | { |
101 | if (!isColumnConst(*in[i])) |
102 | continue; |
103 | |
104 | UInt8 x = func((*in[i])[0]); |
105 | if (has_res) |
106 | { |
107 | res = Op::apply(res, x); |
108 | } |
109 | else |
110 | { |
111 | res = x; |
112 | has_res = true; |
113 | } |
114 | |
115 | in.erase(in.begin() + i); |
116 | } |
117 | |
118 | return has_res; |
119 | } |
120 | |
121 | template <class Op> |
122 | inline bool extractConstColumns(ColumnRawPtrs & in, UInt8 & res) |
123 | { |
124 | return extractConstColumns<Op>( |
125 | in, res, |
126 | [](const Field & value) |
127 | { |
128 | return !value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), value); |
129 | } |
130 | ); |
131 | } |
132 | |
133 | template <class Op> |
134 | inline bool extractConstColumnsTernary(ColumnRawPtrs & in, UInt8 & res_3v) |
135 | { |
136 | return extractConstColumns<Op>( |
137 | in, res_3v, |
138 | [](const Field & value) |
139 | { |
140 | return value.isNull() |
141 | ? Ternary::makeValue(false, true) |
142 | : Ternary::makeValue(applyVisitor(FieldVisitorConvertToNumber<bool>(), value)); |
143 | } |
144 | ); |
145 | } |
146 | |
147 | |
148 | template <typename Op, size_t N> |
149 | class AssociativeApplierImpl |
150 | { |
151 | using ResultValueType = typename Op::ResultType; |
152 | |
153 | public: |
154 | /// Remembers the last N columns from `in`. |
155 | AssociativeApplierImpl(const UInt8ColumnPtrs & in) |
156 | : vec(in[in.size() - N]->getData()), next(in) {} |
157 | |
158 | /// Returns a combination of values in the i-th row of all columns stored in the constructor. |
159 | inline ResultValueType apply(const size_t i) const |
160 | { |
161 | const auto & a = vec[i]; |
162 | if constexpr (Op::isSaturable()) |
163 | return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i)); |
164 | else |
165 | return Op::apply(a, next.apply(i)); |
166 | } |
167 | |
168 | private: |
169 | const UInt8Container & vec; |
170 | const AssociativeApplierImpl<Op, N - 1> next; |
171 | }; |
172 | |
173 | template <typename Op> |
174 | class AssociativeApplierImpl<Op, 1> |
175 | { |
176 | using ResultValueType = typename Op::ResultType; |
177 | |
178 | public: |
179 | AssociativeApplierImpl(const UInt8ColumnPtrs & in) |
180 | : vec(in[in.size() - 1]->getData()) {} |
181 | |
182 | inline ResultValueType apply(const size_t i) const { return vec[i]; } |
183 | |
184 | private: |
185 | const UInt8Container & vec; |
186 | }; |
187 | |
188 | |
189 | /// A helper class used by AssociativeGenericApplierImpl |
190 | /// Allows for on-the-fly conversion of any data type into intermediate ternary representation |
191 | using ValueGetter = std::function<Ternary::ResultType (size_t)>; |
192 | |
193 | template <typename ... Types> |
194 | struct ValueGetterBuilderImpl; |
195 | |
196 | template <typename Type, typename ...Types> |
197 | struct ValueGetterBuilderImpl<Type, Types...> |
198 | { |
199 | static ValueGetter build(const IColumn * x) |
200 | { |
201 | if (const auto nullable_column = typeid_cast<const ColumnNullable *>(x)) |
202 | { |
203 | if (const auto nested_column = typeid_cast<const ColumnVector<Type> *>(nullable_column->getNestedColumnPtr().get())) |
204 | { |
205 | return [&null_data = nullable_column->getNullMapData(), &column_data = nested_column->getData()](size_t i) |
206 | { return Ternary::makeValue(column_data[i], null_data[i]); }; |
207 | } |
208 | else |
209 | return ValueGetterBuilderImpl<Types...>::build(x); |
210 | } |
211 | else if (const auto column = typeid_cast<const ColumnVector<Type> *>(x)) |
212 | return [&column_data = column->getData()](size_t i) { return Ternary::makeValue(column_data[i]); }; |
213 | else |
214 | return ValueGetterBuilderImpl<Types...>::build(x); |
215 | } |
216 | }; |
217 | |
218 | template <> |
219 | struct ValueGetterBuilderImpl<> |
220 | { |
221 | static ValueGetter build(const IColumn * x) |
222 | { |
223 | throw Exception( |
224 | std::string("Unknown numeric column of type: " ) + demangle(typeid(x).name()), |
225 | ErrorCodes::LOGICAL_ERROR); |
226 | } |
227 | }; |
228 | |
229 | using ValueGetterBuilder = |
230 | ValueGetterBuilderImpl<UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>; |
231 | |
232 | /// This class together with helper class ValueGetterBuilder can be used with columns of arbitrary data type |
233 | /// Allows for on-the-fly conversion of any type of data into intermediate ternary representation |
234 | /// and eliminates the need to materialize data columns in intermediate representation |
235 | template <typename Op, size_t N> |
236 | class AssociativeGenericApplierImpl |
237 | { |
238 | using ResultValueType = typename Op::ResultType; |
239 | |
240 | public: |
241 | /// Remembers the last N columns from `in`. |
242 | AssociativeGenericApplierImpl(const ColumnRawPtrs & in) |
243 | : val_getter{ValueGetterBuilder::build(in[in.size() - N])}, next{in} {} |
244 | |
245 | /// Returns a combination of values in the i-th row of all columns stored in the constructor. |
246 | inline ResultValueType apply(const size_t i) const |
247 | { |
248 | const auto a = val_getter(i); |
249 | if constexpr (Op::isSaturable()) |
250 | return Op::isSaturatedValue(a) ? a : Op::apply(a, next.apply(i)); |
251 | else |
252 | return Op::apply(a, next.apply(i)); |
253 | } |
254 | |
255 | private: |
256 | const ValueGetter val_getter; |
257 | const AssociativeGenericApplierImpl<Op, N - 1> next; |
258 | }; |
259 | |
260 | |
261 | template <typename Op> |
262 | class AssociativeGenericApplierImpl<Op, 1> |
263 | { |
264 | using ResultValueType = typename Op::ResultType; |
265 | |
266 | public: |
267 | /// Remembers the last N columns from `in`. |
268 | AssociativeGenericApplierImpl(const ColumnRawPtrs & in) |
269 | : val_getter{ValueGetterBuilder::build(in[in.size() - 1])} {} |
270 | |
271 | inline ResultValueType apply(const size_t i) const { return val_getter(i); } |
272 | |
273 | private: |
274 | const ValueGetter val_getter; |
275 | }; |
276 | |
277 | |
278 | /// Apply target function by feeding it "batches" of N columns |
279 | /// Combining 10 columns per pass is the fastest for large block sizes. |
280 | /// For small block sizes - more columns is faster. |
281 | template < |
282 | typename Op, template <typename, size_t> typename OperationApplierImpl, size_t N = 10> |
283 | struct OperationApplier |
284 | { |
285 | template <typename Columns, typename ResultColumn> |
286 | static void apply(Columns & in, ResultColumn & result) |
287 | { |
288 | while (in.size() > 1) |
289 | { |
290 | doBatchedApply(in, result->getData()); |
291 | in.push_back(result.get()); |
292 | } |
293 | } |
294 | |
295 | template <typename Columns, typename ResultData> |
296 | static void NO_INLINE doBatchedApply(Columns & in, ResultData & result_data) |
297 | { |
298 | if (N > in.size()) |
299 | { |
300 | OperationApplier<Op, OperationApplierImpl, N - 1>::doBatchedApply(in, result_data); |
301 | return; |
302 | } |
303 | |
304 | const OperationApplierImpl<Op, N> operationApplierImpl(in); |
305 | size_t i = 0; |
306 | for (auto & res : result_data) |
307 | res = operationApplierImpl.apply(i++); |
308 | |
309 | in.erase(in.end() - N, in.end()); |
310 | } |
311 | }; |
312 | |
313 | template < |
314 | typename Op, template <typename, size_t> typename OperationApplierImpl> |
315 | struct OperationApplier<Op, OperationApplierImpl, 1> |
316 | { |
317 | template <typename Columns, typename Result> |
318 | static void NO_INLINE doBatchedApply(Columns &, Result &) |
319 | { |
320 | throw Exception( |
321 | "OperationApplier<...>::apply(...): not enough arguments to run this method" , |
322 | ErrorCodes::LOGICAL_ERROR); |
323 | } |
324 | }; |
325 | |
326 | |
327 | template <class Op> |
328 | static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & result_info, size_t input_rows_count) |
329 | { |
330 | /// Combine all constant columns into a single constant value. |
331 | UInt8 const_3v_value = 0; |
332 | const bool has_consts = extractConstColumnsTernary<Op>(arguments, const_3v_value); |
333 | |
334 | /// If the constant value uniquely determines the result, return it. |
335 | if (has_consts && (arguments.empty() || (Op::isSaturable() && Op::isSaturatedValue(const_3v_value)))) |
336 | { |
337 | result_info.column = ColumnConst::create( |
338 | convertFromTernaryData(UInt8Container({const_3v_value}), result_info.type->isNullable()), |
339 | input_rows_count |
340 | ); |
341 | return; |
342 | } |
343 | |
344 | const auto result_column = ColumnUInt8::create(input_rows_count); |
345 | MutableColumnPtr const_column_holder; |
346 | if (has_consts) |
347 | { |
348 | const_column_holder = |
349 | convertFromTernaryData(UInt8Container(input_rows_count, const_3v_value), const_3v_value == Ternary::Null); |
350 | arguments.push_back(const_column_holder.get()); |
351 | } |
352 | |
353 | OperationApplier<Op, AssociativeGenericApplierImpl>::apply(arguments, result_column); |
354 | |
355 | result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable()); |
356 | } |
357 | |
358 | |
359 | template <typename Op, typename ... Types> |
360 | struct TypedExecutorInvoker; |
361 | |
362 | template <typename Op> |
363 | using FastApplierImpl = |
364 | TypedExecutorInvoker<Op, UInt8, UInt16, UInt32, UInt64, Int8, Int16, Int32, Int64, Float32, Float64>; |
365 | |
366 | template <typename Op, typename Type, typename ... Types> |
367 | struct TypedExecutorInvoker<Op, Type, Types ...> |
368 | { |
369 | template <typename T, typename Result> |
370 | static void apply(const ColumnVector<T> & x, const IColumn & y, Result & result) |
371 | { |
372 | if (const auto column = typeid_cast<const ColumnVector<Type> *>(&y)) |
373 | std::transform( |
374 | x.getData().cbegin(), x.getData().cend(), |
375 | column->getData().cbegin(), result.begin(), |
376 | [](const auto a, const auto b) { return Op::apply(!!a, !!b); }); |
377 | else |
378 | TypedExecutorInvoker<Op, Types ...>::template apply<T>(x, y, result); |
379 | } |
380 | |
381 | template <typename Result> |
382 | static void apply(const IColumn & x, const IColumn & y, Result & result) |
383 | { |
384 | if (const auto column = typeid_cast<const ColumnVector<Type> *>(&x)) |
385 | FastApplierImpl<Op>::template apply<Type>(*column, y, result); |
386 | else |
387 | TypedExecutorInvoker<Op, Types ...>::apply(x, y, result); |
388 | } |
389 | }; |
390 | |
391 | template <typename Op> |
392 | struct TypedExecutorInvoker<Op> |
393 | { |
394 | template <typename T, typename Result> |
395 | static void apply(const ColumnVector<T> &, const IColumn & y, Result &) |
396 | { |
397 | throw Exception(std::string("Unknown numeric column y of type: " ) + demangle(typeid(y).name()), ErrorCodes::LOGICAL_ERROR); |
398 | } |
399 | |
400 | template <typename Result> |
401 | static void apply(const IColumn & x, const IColumn &, Result &) |
402 | { |
403 | throw Exception(std::string("Unknown numeric column x of type: " ) + demangle(typeid(x).name()), ErrorCodes::LOGICAL_ERROR); |
404 | } |
405 | }; |
406 | |
407 | |
408 | template <class Op> |
409 | static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & result_info, size_t input_rows_count) |
410 | { |
411 | /// Combine all constant columns into a single constant value. |
412 | UInt8 const_val = 0; |
413 | bool has_consts = extractConstColumns<Op>(arguments, const_val); |
414 | |
415 | /// If the constant value uniquely determines the result, return it. |
416 | if (has_consts && (arguments.empty() || Op::apply(const_val, 0) == Op::apply(const_val, 1))) |
417 | { |
418 | if (!arguments.empty()) |
419 | const_val = Op::apply(const_val, 0); |
420 | result_info.column = DataTypeUInt8().createColumnConst(input_rows_count, toField(const_val)); |
421 | return; |
422 | } |
423 | |
424 | /// If the constant value is a neutral element, let's forget about it. |
425 | if (has_consts && Op::apply(const_val, 0) == 0 && Op::apply(const_val, 1) == 1) |
426 | has_consts = false; |
427 | |
428 | UInt8ColumnPtrs uint8_args; |
429 | |
430 | auto col_res = ColumnUInt8::create(); |
431 | UInt8Container & vec_res = col_res->getData(); |
432 | if (has_consts) |
433 | { |
434 | vec_res.assign(input_rows_count, const_val); |
435 | uint8_args.push_back(col_res.get()); |
436 | } |
437 | else |
438 | { |
439 | vec_res.resize(input_rows_count); |
440 | } |
441 | |
442 | /// FastPath detection goes in here |
443 | if (arguments.size() == (has_consts ? 1 : 2)) |
444 | { |
445 | if (has_consts) |
446 | FastApplierImpl<Op>::apply(*arguments[0], *col_res, col_res->getData()); |
447 | else |
448 | FastApplierImpl<Op>::apply(*arguments[0], *arguments[1], col_res->getData()); |
449 | |
450 | result_info.column = std::move(col_res); |
451 | return; |
452 | } |
453 | |
454 | /// Convert all columns to UInt8 |
455 | Columns converted_columns; |
456 | for (const IColumn * column : arguments) |
457 | { |
458 | if (auto uint8_column = checkAndGetColumn<ColumnUInt8>(column)) |
459 | uint8_args.push_back(uint8_column); |
460 | else |
461 | { |
462 | auto converted_column = ColumnUInt8::create(input_rows_count); |
463 | convertColumnToUInt8(column, converted_column->getData()); |
464 | uint8_args.push_back(converted_column.get()); |
465 | converted_columns.emplace_back(std::move(converted_column)); |
466 | } |
467 | } |
468 | |
469 | OperationApplier<Op, AssociativeApplierImpl>::apply(uint8_args, col_res); |
470 | |
471 | /// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8. |
472 | if (uint8_args[0] != col_res.get()) |
473 | vec_res.assign(uint8_args[0]->getData()); |
474 | |
475 | result_info.column = std::move(col_res); |
476 | } |
477 | |
478 | } |
479 | |
480 | template <typename Impl, typename Name> |
481 | DataTypePtr FunctionAnyArityLogical<Impl, Name>::getReturnTypeImpl(const DataTypes & arguments) const |
482 | { |
483 | if (arguments.size() < 2) |
484 | throw Exception("Number of arguments for function \"" + getName() + "\" should be at least 2: passed " |
485 | + toString(arguments.size()), |
486 | ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION); |
487 | |
488 | bool has_nullable_arguments = false; |
489 | for (size_t i = 0; i < arguments.size(); ++i) |
490 | { |
491 | const auto & arg_type = arguments[i]; |
492 | |
493 | if (!has_nullable_arguments) |
494 | { |
495 | has_nullable_arguments = arg_type->isNullable(); |
496 | if (has_nullable_arguments && !Impl::specialImplementationForNulls()) |
497 | throw Exception("Logical error: Unexpected type of argument for function \"" + getName() + "\": " |
498 | " argument " + toString(i + 1) + " is of type " + arg_type->getName(), ErrorCodes::LOGICAL_ERROR); |
499 | } |
500 | |
501 | if (!(isNativeNumber(arg_type) |
502 | || (Impl::specialImplementationForNulls() && (arg_type->onlyNull() || isNativeNumber(removeNullable(arg_type)))))) |
503 | throw Exception("Illegal type (" |
504 | + arg_type->getName() |
505 | + ") of " + toString(i + 1) + " argument of function " + getName(), |
506 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
507 | } |
508 | |
509 | auto result_type = std::make_shared<DataTypeUInt8>(); |
510 | return has_nullable_arguments |
511 | ? makeNullable(result_type) |
512 | : result_type; |
513 | } |
514 | |
515 | template <typename Impl, typename Name> |
516 | void FunctionAnyArityLogical<Impl, Name>::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result_index, size_t input_rows_count) |
517 | { |
518 | ColumnRawPtrs args_in; |
519 | for (const auto arg_index : arguments) |
520 | args_in.push_back(block.getByPosition(arg_index).column.get()); |
521 | |
522 | auto & result_info = block.getByPosition(result_index); |
523 | if (result_info.type->isNullable()) |
524 | executeForTernaryLogicImpl<Impl>(std::move(args_in), result_info, input_rows_count); |
525 | else |
526 | basicExecuteImpl<Impl>(std::move(args_in), result_info, input_rows_count); |
527 | } |
528 | |
529 | |
530 | template <typename A, typename Op> |
531 | struct UnaryOperationImpl |
532 | { |
533 | using ResultType = typename Op::ResultType; |
534 | using ArrayA = typename ColumnVector<A>::Container; |
535 | using ArrayC = typename ColumnVector<ResultType>::Container; |
536 | |
537 | static void NO_INLINE vector(const ArrayA & a, ArrayC & c) |
538 | { |
539 | std::transform( |
540 | a.cbegin(), a.cend(), c.begin(), |
541 | [](const auto x) { return Op::apply(x); }); |
542 | } |
543 | }; |
544 | |
545 | template <template <typename> class Impl, typename Name> |
546 | DataTypePtr FunctionUnaryLogical<Impl, Name>::getReturnTypeImpl(const DataTypes & arguments) const |
547 | { |
548 | if (!isNativeNumber(arguments[0])) |
549 | throw Exception("Illegal type (" |
550 | + arguments[0]->getName() |
551 | + ") of argument of function " + getName(), |
552 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
553 | |
554 | return std::make_shared<DataTypeUInt8>(); |
555 | } |
556 | |
557 | template <template <typename> class Impl, typename T> |
558 | bool functionUnaryExecuteType(Block & block, const ColumnNumbers & arguments, size_t result) |
559 | { |
560 | if (auto col = checkAndGetColumn<ColumnVector<T>>(block.getByPosition(arguments[0]).column.get())) |
561 | { |
562 | auto col_res = ColumnUInt8::create(); |
563 | |
564 | typename ColumnUInt8::Container & vec_res = col_res->getData(); |
565 | vec_res.resize(col->getData().size()); |
566 | UnaryOperationImpl<T, Impl<T>>::vector(col->getData(), vec_res); |
567 | |
568 | block.getByPosition(result).column = std::move(col_res); |
569 | return true; |
570 | } |
571 | |
572 | return false; |
573 | } |
574 | |
575 | template <template <typename> class Impl, typename Name> |
576 | void FunctionUnaryLogical<Impl, Name>::executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) |
577 | { |
578 | if (!(functionUnaryExecuteType<Impl, UInt8>(block, arguments, result) |
579 | || functionUnaryExecuteType<Impl, UInt16>(block, arguments, result) |
580 | || functionUnaryExecuteType<Impl, UInt32>(block, arguments, result) |
581 | || functionUnaryExecuteType<Impl, UInt64>(block, arguments, result) |
582 | || functionUnaryExecuteType<Impl, Int8>(block, arguments, result) |
583 | || functionUnaryExecuteType<Impl, Int16>(block, arguments, result) |
584 | || functionUnaryExecuteType<Impl, Int32>(block, arguments, result) |
585 | || functionUnaryExecuteType<Impl, Int64>(block, arguments, result) |
586 | || functionUnaryExecuteType<Impl, Float32>(block, arguments, result) |
587 | || functionUnaryExecuteType<Impl, Float64>(block, arguments, result))) |
588 | throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName() |
589 | + " of argument of function " + getName(), |
590 | ErrorCodes::ILLEGAL_COLUMN); |
591 | } |
592 | |
593 | } |
594 | |