1 | #pragma once |
2 | |
3 | #include <Common/memcmpSmall.h> |
4 | #include <Common/assert_cast.h> |
5 | |
6 | #include <Columns/ColumnsNumber.h> |
7 | #include <Columns/ColumnConst.h> |
8 | #include <Columns/ColumnDecimal.h> |
9 | #include <Columns/ColumnString.h> |
10 | #include <Columns/ColumnFixedString.h> |
11 | #include <Columns/ColumnTuple.h> |
12 | #include <Columns/ColumnArray.h> |
13 | |
14 | #include <DataTypes/DataTypesNumber.h> |
15 | #include <DataTypes/DataTypeDateTime.h> |
16 | #include <DataTypes/DataTypeDateTime64.h> |
17 | #include <DataTypes/DataTypeDate.h> |
18 | #include <DataTypes/DataTypeString.h> |
19 | #include <DataTypes/DataTypeUUID.h> |
20 | #include <DataTypes/DataTypeFixedString.h> |
21 | #include <DataTypes/DataTypeTuple.h> |
22 | #include <DataTypes/DataTypeEnum.h> |
23 | #include <DataTypes/getLeastSupertype.h> |
24 | |
25 | #include <Interpreters/castColumn.h> |
26 | |
27 | #include <Functions/FunctionsLogical.h> |
28 | #include <Functions/IFunctionAdaptors.h> |
29 | #include <Functions/FunctionHelpers.h> |
30 | |
31 | #include <Core/AccurateComparison.h> |
32 | #include <Core/DecimalComparison.h> |
33 | |
34 | #include <IO/ReadBufferFromMemory.h> |
35 | #include <IO/ReadHelpers.h> |
36 | |
37 | #include <limits> |
38 | #include <type_traits> |
39 | |
40 | |
41 | namespace DB |
42 | { |
43 | |
44 | namespace ErrorCodes |
45 | { |
46 | extern const int TOO_LARGE_STRING_SIZE; |
47 | extern const int ILLEGAL_COLUMN; |
48 | extern const int ILLEGAL_TYPE_OF_ARGUMENT; |
49 | extern const int LOGICAL_ERROR; |
50 | extern const int NOT_IMPLEMENTED; |
51 | } |
52 | |
53 | |
54 | /** Comparison functions: ==, !=, <, >, <=, >=. |
55 | * The comparison functions always return 0 or 1 (UInt8). |
56 | * |
57 | * You can compare the following types: |
58 | * - numbers and decimals; |
59 | * - strings and fixed strings; |
60 | * - dates; |
61 | * - datetimes; |
62 | * within each group, but not from different groups; |
63 | * - tuples (lexicographic comparison). |
64 | * |
65 | * Exception: You can compare the date and datetime with a constant string. Example: EventDate = '2015-01-01'. |
66 | */ |
67 | |
68 | |
69 | template <typename A, typename B, typename Op> |
70 | struct NumComparisonImpl |
71 | { |
72 | /// If you don't specify NO_INLINE, the compiler will inline this function, but we don't need this as this function contains tight loop inside. |
73 | static void NO_INLINE vector_vector(const PaddedPODArray<A> & a, const PaddedPODArray<B> & b, PaddedPODArray<UInt8> & c) |
74 | { |
75 | /** GCC 4.8.2 vectorizes a loop only if it is written in this form. |
76 | * In this case, if you loop through the array index (the code will look simpler), |
77 | * the loop will not be vectorized. |
78 | */ |
79 | |
80 | size_t size = a.size(); |
81 | const A * a_pos = a.data(); |
82 | const B * b_pos = b.data(); |
83 | UInt8 * c_pos = c.data(); |
84 | const A * a_end = a_pos + size; |
85 | |
86 | while (a_pos < a_end) |
87 | { |
88 | *c_pos = Op::apply(*a_pos, *b_pos); |
89 | ++a_pos; |
90 | ++b_pos; |
91 | ++c_pos; |
92 | } |
93 | } |
94 | |
95 | static void NO_INLINE vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<UInt8> & c) |
96 | { |
97 | size_t size = a.size(); |
98 | const A * a_pos = a.data(); |
99 | UInt8 * c_pos = c.data(); |
100 | const A * a_end = a_pos + size; |
101 | |
102 | while (a_pos < a_end) |
103 | { |
104 | *c_pos = Op::apply(*a_pos, b); |
105 | ++a_pos; |
106 | ++c_pos; |
107 | } |
108 | } |
109 | |
110 | static void constant_vector(A a, const PaddedPODArray<B> & b, PaddedPODArray<UInt8> & c) |
111 | { |
112 | NumComparisonImpl<B, A, typename Op::SymmetricOp>::vector_constant(b, a, c); |
113 | } |
114 | |
115 | static void constant_constant(A a, B b, UInt8 & c) |
116 | { |
117 | c = Op::apply(a, b); |
118 | } |
119 | }; |
120 | |
121 | |
122 | template <typename Op> |
123 | struct StringComparisonImpl |
124 | { |
125 | static void NO_INLINE string_vector_string_vector( |
126 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
127 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
128 | PaddedPODArray<UInt8> & c) |
129 | { |
130 | size_t size = a_offsets.size(); |
131 | ColumnString::Offset prev_a_offset = 0; |
132 | ColumnString::Offset prev_b_offset = 0; |
133 | |
134 | for (size_t i = 0; i < size; ++i) |
135 | { |
136 | c[i] = Op::apply(memcmpSmallAllowOverflow15( |
137 | a_data.data() + prev_a_offset, a_offsets[i] - prev_a_offset - 1, |
138 | b_data.data() + prev_b_offset, b_offsets[i] - prev_b_offset - 1), 0); |
139 | |
140 | prev_a_offset = a_offsets[i]; |
141 | prev_b_offset = b_offsets[i]; |
142 | } |
143 | } |
144 | |
145 | static void NO_INLINE string_vector_fixed_string_vector( |
146 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
147 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
148 | PaddedPODArray<UInt8> & c) |
149 | { |
150 | size_t size = a_offsets.size(); |
151 | ColumnString::Offset prev_a_offset = 0; |
152 | |
153 | for (size_t i = 0; i < size; ++i) |
154 | { |
155 | c[i] = Op::apply(memcmpSmallAllowOverflow15( |
156 | a_data.data() + prev_a_offset, a_offsets[i] - prev_a_offset - 1, |
157 | b_data.data() + i * b_n, b_n), 0); |
158 | |
159 | prev_a_offset = a_offsets[i]; |
160 | } |
161 | } |
162 | |
163 | static void NO_INLINE string_vector_constant( |
164 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
165 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
166 | PaddedPODArray<UInt8> & c) |
167 | { |
168 | size_t size = a_offsets.size(); |
169 | ColumnString::Offset prev_a_offset = 0; |
170 | |
171 | for (size_t i = 0; i < size; ++i) |
172 | { |
173 | c[i] = Op::apply(memcmpSmallAllowOverflow15( |
174 | a_data.data() + prev_a_offset, a_offsets[i] - prev_a_offset - 1, |
175 | b_data.data(), b_size), 0); |
176 | |
177 | prev_a_offset = a_offsets[i]; |
178 | } |
179 | } |
180 | |
181 | static void fixed_string_vector_string_vector( |
182 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
183 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
184 | PaddedPODArray<UInt8> & c) |
185 | { |
186 | StringComparisonImpl<typename Op::SymmetricOp>::string_vector_fixed_string_vector(b_data, b_offsets, a_data, a_n, c); |
187 | } |
188 | |
189 | static void NO_INLINE fixed_string_vector_fixed_string_vector_16( |
190 | const ColumnString::Chars & a_data, |
191 | const ColumnString::Chars & b_data, |
192 | PaddedPODArray<UInt8> & c) |
193 | { |
194 | size_t size = a_data.size(); |
195 | |
196 | for (size_t i = 0, j = 0; i < size; i += 16, ++j) |
197 | c[j] = Op::apply(memcmp16(&a_data[i], &b_data[i]), 0); |
198 | } |
199 | |
200 | static void NO_INLINE fixed_string_vector_constant_16( |
201 | const ColumnString::Chars & a_data, |
202 | const ColumnString::Chars & b_data, |
203 | PaddedPODArray<UInt8> & c) |
204 | { |
205 | size_t size = a_data.size(); |
206 | |
207 | for (size_t i = 0, j = 0; i < size; i += 16, ++j) |
208 | c[j] = Op::apply(memcmp16(&a_data[i], &b_data[0]), 0); |
209 | } |
210 | |
211 | static void NO_INLINE fixed_string_vector_fixed_string_vector( |
212 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
213 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
214 | PaddedPODArray<UInt8> & c) |
215 | { |
216 | if (a_n == 16 && b_n == 16) |
217 | { |
218 | /** Specialization if both sizes are 16. |
219 | * To more efficient comparison of IPv6 addresses stored in FixedString(16). |
220 | */ |
221 | fixed_string_vector_fixed_string_vector_16(a_data, b_data, c); |
222 | } |
223 | else if (a_n == b_n) |
224 | { |
225 | size_t size = a_data.size(); |
226 | for (size_t i = 0, j = 0; i < size; i += a_n, ++j) |
227 | c[j] = Op::apply(memcmpSmallAllowOverflow15(a_data.data() + i, b_data.data() + i, a_n), 0); |
228 | } |
229 | else |
230 | { |
231 | size_t size = a_data.size() / a_n; |
232 | |
233 | for (size_t i = 0; i < size; ++i) |
234 | c[i] = Op::apply(memcmpSmallAllowOverflow15(a_data.data() + i * a_n, a_n, b_data.data() + i * b_n, b_n), 0); |
235 | } |
236 | } |
237 | |
238 | static void NO_INLINE fixed_string_vector_constant( |
239 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
240 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
241 | PaddedPODArray<UInt8> & c) |
242 | { |
243 | if (a_n == 16 && b_size == 16) |
244 | { |
245 | fixed_string_vector_constant_16(a_data, b_data, c); |
246 | } |
247 | else if (a_n == b_size) |
248 | { |
249 | size_t size = a_data.size(); |
250 | for (size_t i = 0, j = 0; i < size; i += a_n, ++j) |
251 | c[j] = Op::apply(memcmpSmallAllowOverflow15(a_data.data() + i, b_data.data(), a_n), 0); |
252 | } |
253 | else |
254 | { |
255 | size_t size = a_data.size(); |
256 | for (size_t i = 0, j = 0; i < size; i += a_n, ++j) |
257 | c[j] = Op::apply(memcmpSmallAllowOverflow15(a_data.data() + i, a_n, b_data.data(), b_size), 0); |
258 | } |
259 | } |
260 | |
261 | static void constant_string_vector( |
262 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
263 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
264 | PaddedPODArray<UInt8> & c) |
265 | { |
266 | StringComparisonImpl<typename Op::SymmetricOp>::string_vector_constant(b_data, b_offsets, a_data, a_size, c); |
267 | } |
268 | |
269 | static void constant_fixed_string_vector( |
270 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
271 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
272 | PaddedPODArray<UInt8> & c) |
273 | { |
274 | StringComparisonImpl<typename Op::SymmetricOp>::fixed_string_vector_constant(b_data, b_n, a_data, a_size, c); |
275 | } |
276 | |
277 | static void constant_constant( |
278 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
279 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
280 | UInt8 & c) |
281 | { |
282 | c = Op::apply(memcmpSmallAllowOverflow15(a_data.data(), a_size, b_data.data(), b_size), 0); |
283 | } |
284 | }; |
285 | |
286 | |
287 | /// Comparisons for equality/inequality are implemented slightly more efficient. |
288 | template <bool positive> |
289 | struct StringEqualsImpl |
290 | { |
291 | static void NO_INLINE string_vector_string_vector( |
292 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
293 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
294 | PaddedPODArray<UInt8> & c) |
295 | { |
296 | size_t size = a_offsets.size(); |
297 | ColumnString::Offset prev_a_offset = 0; |
298 | ColumnString::Offset prev_b_offset = 0; |
299 | |
300 | for (size_t i = 0; i < size; ++i) |
301 | { |
302 | auto a_size = a_offsets[i] - prev_a_offset - 1; |
303 | auto b_size = b_offsets[i] - prev_b_offset - 1; |
304 | |
305 | c[i] = positive == memequalSmallAllowOverflow15( |
306 | a_data.data() + prev_a_offset, a_size, |
307 | b_data.data() + prev_b_offset, b_size); |
308 | |
309 | prev_a_offset = a_offsets[i]; |
310 | prev_b_offset = b_offsets[i]; |
311 | } |
312 | } |
313 | |
314 | static void NO_INLINE string_vector_fixed_string_vector( |
315 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
316 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
317 | PaddedPODArray<UInt8> & c) |
318 | { |
319 | size_t size = a_offsets.size(); |
320 | ColumnString::Offset prev_a_offset = 0; |
321 | |
322 | for (size_t i = 0; i < size; ++i) |
323 | { |
324 | auto a_size = a_offsets[i] - prev_a_offset - 1; |
325 | |
326 | c[i] = positive == memequalSmallAllowOverflow15( |
327 | a_data.data() + prev_a_offset, a_size, |
328 | b_data.data() + b_n * i, b_n); |
329 | |
330 | prev_a_offset = a_offsets[i]; |
331 | } |
332 | } |
333 | |
334 | static void NO_INLINE string_vector_constant( |
335 | const ColumnString::Chars & a_data, const ColumnString::Offsets & a_offsets, |
336 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
337 | PaddedPODArray<UInt8> & c) |
338 | { |
339 | size_t size = a_offsets.size(); |
340 | ColumnString::Offset prev_a_offset = 0; |
341 | |
342 | for (size_t i = 0; i < size; ++i) |
343 | { |
344 | auto a_size = a_offsets[i] - prev_a_offset - 1; |
345 | |
346 | c[i] = positive == memequalSmallAllowOverflow15( |
347 | a_data.data() + prev_a_offset, a_size, |
348 | b_data.data(), b_size); |
349 | |
350 | prev_a_offset = a_offsets[i]; |
351 | } |
352 | } |
353 | |
354 | static void NO_INLINE fixed_string_vector_fixed_string_vector_16( |
355 | const ColumnString::Chars & a_data, |
356 | const ColumnString::Chars & b_data, |
357 | PaddedPODArray<UInt8> & c) |
358 | { |
359 | size_t size = a_data.size() / 16; |
360 | |
361 | for (size_t i = 0; i < size; ++i) |
362 | c[i] = positive == memequal16( |
363 | a_data.data() + i * 16, |
364 | b_data.data() + i * 16); |
365 | } |
366 | |
367 | static void NO_INLINE fixed_string_vector_constant_16( |
368 | const ColumnString::Chars & a_data, |
369 | const ColumnString::Chars & b_data, |
370 | PaddedPODArray<UInt8> & c) |
371 | { |
372 | size_t size = a_data.size() / 16; |
373 | |
374 | for (size_t i = 0; i < size; ++i) |
375 | c[i] = positive == memequal16( |
376 | a_data.data() + i * 16, |
377 | b_data.data()); |
378 | } |
379 | |
380 | static void NO_INLINE fixed_string_vector_fixed_string_vector( |
381 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
382 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
383 | PaddedPODArray<UInt8> & c) |
384 | { |
385 | /** Specialization if both sizes are 16. |
386 | * To more efficient comparison of IPv6 addresses stored in FixedString(16). |
387 | */ |
388 | if (a_n == 16 && b_n == 16) |
389 | { |
390 | fixed_string_vector_fixed_string_vector_16(a_data, b_data, c); |
391 | } |
392 | else |
393 | { |
394 | size_t size = a_data.size() / a_n; |
395 | for (size_t i = 0; i < size; ++i) |
396 | c[i] = positive == memequalSmallAllowOverflow15(a_data.data() + i * a_n, a_n, b_data.data() + i * b_n, b_n); |
397 | } |
398 | } |
399 | |
400 | static void NO_INLINE fixed_string_vector_constant( |
401 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
402 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
403 | PaddedPODArray<UInt8> & c) |
404 | { |
405 | if (a_n == 16 && b_size == 16) |
406 | { |
407 | fixed_string_vector_constant_16(a_data, b_data, c); |
408 | } |
409 | else |
410 | { |
411 | size_t size = a_data.size() / a_n; |
412 | for (size_t i = 0; i < size; ++i) |
413 | c[i] = positive == memequalSmallAllowOverflow15(a_data.data() + i * a_n, a_n, b_data.data(), b_size); |
414 | } |
415 | } |
416 | |
417 | static void fixed_string_vector_string_vector( |
418 | const ColumnString::Chars & a_data, ColumnString::Offset a_n, |
419 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
420 | PaddedPODArray<UInt8> & c) |
421 | { |
422 | string_vector_fixed_string_vector(b_data, b_offsets, a_data, a_n, c); |
423 | } |
424 | |
425 | static void constant_string_vector( |
426 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
427 | const ColumnString::Chars & b_data, const ColumnString::Offsets & b_offsets, |
428 | PaddedPODArray<UInt8> & c) |
429 | { |
430 | string_vector_constant(b_data, b_offsets, a_data, a_size, c); |
431 | } |
432 | |
433 | static void constant_fixed_string_vector( |
434 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
435 | const ColumnString::Chars & b_data, ColumnString::Offset b_n, |
436 | PaddedPODArray<UInt8> & c) |
437 | { |
438 | fixed_string_vector_constant(b_data, b_n, a_data, a_size, c); |
439 | } |
440 | |
441 | static void constant_constant( |
442 | const ColumnString::Chars & a_data, ColumnString::Offset a_size, |
443 | const ColumnString::Chars & b_data, ColumnString::Offset b_size, |
444 | UInt8 & c) |
445 | { |
446 | c = positive == memequalSmallAllowOverflow15(a_data.data(), a_size, b_data.data(), b_size); |
447 | } |
448 | }; |
449 | |
450 | |
451 | template <typename A, typename B> |
452 | struct StringComparisonImpl<EqualsOp<A, B>> : StringEqualsImpl<true> {}; |
453 | |
454 | template <typename A, typename B> |
455 | struct StringComparisonImpl<NotEqualsOp<A, B>> : StringEqualsImpl<false> {}; |
456 | |
457 | |
458 | /// Generic version, implemented for columns of same type. |
459 | template <typename Op> |
460 | struct GenericComparisonImpl |
461 | { |
462 | static void NO_INLINE vector_vector(const IColumn & a, const IColumn & b, PaddedPODArray<UInt8> & c) |
463 | { |
464 | for (size_t i = 0, size = a.size(); i < size; ++i) |
465 | c[i] = Op::apply(a.compareAt(i, i, b, 1), 0); |
466 | } |
467 | |
468 | static void NO_INLINE vector_constant(const IColumn & a, const IColumn & b, PaddedPODArray<UInt8> & c) |
469 | { |
470 | auto b_materialized = b.cloneResized(1)->convertToFullColumnIfConst(); |
471 | for (size_t i = 0, size = a.size(); i < size; ++i) |
472 | c[i] = Op::apply(a.compareAt(i, 0, *b_materialized, 1), 0); |
473 | } |
474 | |
475 | static void constant_vector(const IColumn & a, const IColumn & b, PaddedPODArray<UInt8> & c) |
476 | { |
477 | GenericComparisonImpl<typename Op::SymmetricOp>::vector_constant(b, a, c); |
478 | } |
479 | |
480 | static void constant_constant(const IColumn & a, const IColumn & b, UInt8 & c) |
481 | { |
482 | c = Op::apply(a.compareAt(0, 0, b, 1), 0); |
483 | } |
484 | }; |
485 | |
486 | |
487 | #if USE_EMBEDDED_COMPILER |
488 | |
489 | template <template <typename, typename> typename Op> struct CompileOp; |
490 | |
491 | template <> struct CompileOp<EqualsOp> |
492 | { |
493 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool /*is_signed*/) |
494 | { |
495 | return x->getType()->isIntegerTy() ? b.CreateICmpEQ(x, y) : b.CreateFCmpOEQ(x, y); /// qNaNs always compare false |
496 | } |
497 | }; |
498 | |
499 | template <> struct CompileOp<NotEqualsOp> |
500 | { |
501 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool /*is_signed*/) |
502 | { |
503 | return x->getType()->isIntegerTy() ? b.CreateICmpNE(x, y) : b.CreateFCmpONE(x, y); |
504 | } |
505 | }; |
506 | |
507 | template <> struct CompileOp<LessOp> |
508 | { |
509 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) |
510 | { |
511 | return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSLT(x, y) : b.CreateICmpULT(x, y)) : b.CreateFCmpOLT(x, y); |
512 | } |
513 | }; |
514 | |
515 | template <> struct CompileOp<GreaterOp> |
516 | { |
517 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) |
518 | { |
519 | return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSGT(x, y) : b.CreateICmpUGT(x, y)) : b.CreateFCmpOGT(x, y); |
520 | } |
521 | }; |
522 | |
523 | template <> struct CompileOp<LessOrEqualsOp> |
524 | { |
525 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) |
526 | { |
527 | return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSLE(x, y) : b.CreateICmpULE(x, y)) : b.CreateFCmpOLE(x, y); |
528 | } |
529 | }; |
530 | |
531 | template <> struct CompileOp<GreaterOrEqualsOp> |
532 | { |
533 | static llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * x, llvm::Value * y, bool is_signed) |
534 | { |
535 | return x->getType()->isIntegerTy() ? (is_signed ? b.CreateICmpSGE(x, y) : b.CreateICmpUGE(x, y)) : b.CreateFCmpOGE(x, y); |
536 | } |
537 | }; |
538 | |
539 | #endif |
540 | |
541 | |
542 | struct NameEquals { static constexpr auto name = "equals" ; }; |
543 | struct NameNotEquals { static constexpr auto name = "notEquals" ; }; |
544 | struct NameLess { static constexpr auto name = "less" ; }; |
545 | struct NameGreater { static constexpr auto name = "greater" ; }; |
546 | struct NameLessOrEquals { static constexpr auto name = "lessOrEquals" ; }; |
547 | struct NameGreaterOrEquals { static constexpr auto name = "greaterOrEquals" ; }; |
548 | |
549 | |
550 | template < |
551 | template <typename, typename> class Op, |
552 | typename Name> |
553 | class FunctionComparison : public IFunction |
554 | { |
555 | public: |
556 | static constexpr auto name = Name::name; |
557 | static FunctionPtr create(const Context & context) { return std::make_shared<FunctionComparison>(context); } |
558 | |
559 | FunctionComparison(const Context & context_) |
560 | : context(context_), |
561 | check_decimal_overflow(decimalCheckComparisonOverflow(context)) |
562 | {} |
563 | |
564 | private: |
565 | const Context & context; |
566 | bool check_decimal_overflow = true; |
567 | |
568 | template <typename T0, typename T1> |
569 | bool executeNumRightType(Block & block, size_t result, const ColumnVector<T0> * col_left, const IColumn * col_right_untyped) |
570 | { |
571 | if (const ColumnVector<T1> * col_right = checkAndGetColumn<ColumnVector<T1>>(col_right_untyped)) |
572 | { |
573 | auto col_res = ColumnUInt8::create(); |
574 | |
575 | ColumnUInt8::Container & vec_res = col_res->getData(); |
576 | vec_res.resize(col_left->getData().size()); |
577 | NumComparisonImpl<T0, T1, Op<T0, T1>>::vector_vector(col_left->getData(), col_right->getData(), vec_res); |
578 | |
579 | block.getByPosition(result).column = std::move(col_res); |
580 | return true; |
581 | } |
582 | else if (auto col_right_const = checkAndGetColumnConst<ColumnVector<T1>>(col_right_untyped)) |
583 | { |
584 | auto col_res = ColumnUInt8::create(); |
585 | |
586 | ColumnUInt8::Container & vec_res = col_res->getData(); |
587 | vec_res.resize(col_left->size()); |
588 | NumComparisonImpl<T0, T1, Op<T0, T1>>::vector_constant(col_left->getData(), col_right_const->template getValue<T1>(), vec_res); |
589 | |
590 | block.getByPosition(result).column = std::move(col_res); |
591 | return true; |
592 | } |
593 | |
594 | return false; |
595 | } |
596 | |
597 | template <typename T0, typename T1> |
598 | bool executeNumConstRightType(Block & block, size_t result, const ColumnConst * col_left, const IColumn * col_right_untyped) |
599 | { |
600 | if (const ColumnVector<T1> * col_right = checkAndGetColumn<ColumnVector<T1>>(col_right_untyped)) |
601 | { |
602 | auto col_res = ColumnUInt8::create(); |
603 | |
604 | ColumnUInt8::Container & vec_res = col_res->getData(); |
605 | vec_res.resize(col_left->size()); |
606 | NumComparisonImpl<T0, T1, Op<T0, T1>>::constant_vector(col_left->template getValue<T0>(), col_right->getData(), vec_res); |
607 | |
608 | block.getByPosition(result).column = std::move(col_res); |
609 | return true; |
610 | } |
611 | else if (auto col_right_const = checkAndGetColumnConst<ColumnVector<T1>>(col_right_untyped)) |
612 | { |
613 | UInt8 res = 0; |
614 | NumComparisonImpl<T0, T1, Op<T0, T1>>::constant_constant(col_left->template getValue<T0>(), col_right_const->template getValue<T1>(), res); |
615 | |
616 | block.getByPosition(result).column = DataTypeUInt8().createColumnConst(col_left->size(), toField(res)); |
617 | return true; |
618 | } |
619 | |
620 | return false; |
621 | } |
622 | |
623 | template <typename T0> |
624 | bool executeNumLeftType(Block & block, size_t result, const IColumn * col_left_untyped, const IColumn * col_right_untyped) |
625 | { |
626 | if (const ColumnVector<T0> * col_left = checkAndGetColumn<ColumnVector<T0>>(col_left_untyped)) |
627 | { |
628 | if ( executeNumRightType<T0, UInt8>(block, result, col_left, col_right_untyped) |
629 | || executeNumRightType<T0, UInt16>(block, result, col_left, col_right_untyped) |
630 | || executeNumRightType<T0, UInt32>(block, result, col_left, col_right_untyped) |
631 | || executeNumRightType<T0, UInt64>(block, result, col_left, col_right_untyped) |
632 | || executeNumRightType<T0, UInt128>(block, result, col_left, col_right_untyped) |
633 | || executeNumRightType<T0, Int8>(block, result, col_left, col_right_untyped) |
634 | || executeNumRightType<T0, Int16>(block, result, col_left, col_right_untyped) |
635 | || executeNumRightType<T0, Int32>(block, result, col_left, col_right_untyped) |
636 | || executeNumRightType<T0, Int64>(block, result, col_left, col_right_untyped) |
637 | || executeNumRightType<T0, Int128>(block, result, col_left, col_right_untyped) |
638 | || executeNumRightType<T0, Float32>(block, result, col_left, col_right_untyped) |
639 | || executeNumRightType<T0, Float64>(block, result, col_left, col_right_untyped)) |
640 | return true; |
641 | else |
642 | throw Exception("Illegal column " + col_right_untyped->getName() |
643 | + " of second argument of function " + getName(), |
644 | ErrorCodes::ILLEGAL_COLUMN); |
645 | } |
646 | else if (auto col_left_const = checkAndGetColumnConst<ColumnVector<T0>>(col_left_untyped)) |
647 | { |
648 | if ( executeNumConstRightType<T0, UInt8>(block, result, col_left_const, col_right_untyped) |
649 | || executeNumConstRightType<T0, UInt16>(block, result, col_left_const, col_right_untyped) |
650 | || executeNumConstRightType<T0, UInt32>(block, result, col_left_const, col_right_untyped) |
651 | || executeNumConstRightType<T0, UInt64>(block, result, col_left_const, col_right_untyped) |
652 | || executeNumConstRightType<T0, UInt128>(block, result, col_left_const, col_right_untyped) |
653 | || executeNumConstRightType<T0, Int8>(block, result, col_left_const, col_right_untyped) |
654 | || executeNumConstRightType<T0, Int16>(block, result, col_left_const, col_right_untyped) |
655 | || executeNumConstRightType<T0, Int32>(block, result, col_left_const, col_right_untyped) |
656 | || executeNumConstRightType<T0, Int64>(block, result, col_left_const, col_right_untyped) |
657 | || executeNumConstRightType<T0, Int128>(block, result, col_left_const, col_right_untyped) |
658 | || executeNumConstRightType<T0, Float32>(block, result, col_left_const, col_right_untyped) |
659 | || executeNumConstRightType<T0, Float64>(block, result, col_left_const, col_right_untyped)) |
660 | return true; |
661 | else |
662 | throw Exception("Illegal column " + col_right_untyped->getName() |
663 | + " of second argument of function " + getName(), |
664 | ErrorCodes::ILLEGAL_COLUMN); |
665 | } |
666 | |
667 | return false; |
668 | } |
669 | |
670 | void executeDecimal(Block & block, size_t result, const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right) |
671 | { |
672 | TypeIndex left_number = col_left.type->getTypeId(); |
673 | TypeIndex right_number = col_right.type->getTypeId(); |
674 | |
675 | auto call = [&](const auto & types) -> bool |
676 | { |
677 | using Types = std::decay_t<decltype(types)>; |
678 | using LeftDataType = typename Types::LeftType; |
679 | using RightDataType = typename Types::RightType; |
680 | |
681 | if (check_decimal_overflow) |
682 | DecimalComparison<LeftDataType, RightDataType, Op, true>(block, result, col_left, col_right); |
683 | else |
684 | DecimalComparison<LeftDataType, RightDataType, Op, false>(block, result, col_left, col_right); |
685 | return true; |
686 | }; |
687 | |
688 | if (!callOnBasicTypes<true, false, true, true>(left_number, right_number, call)) |
689 | throw Exception("Wrong call for " + getName() + " with " + col_left.type->getName() + " and " + col_right.type->getName(), |
690 | ErrorCodes::LOGICAL_ERROR); |
691 | } |
692 | |
693 | bool executeString(Block & block, size_t result, const IColumn * c0, const IColumn * c1) |
694 | { |
695 | const ColumnString * c0_string = checkAndGetColumn<ColumnString>(c0); |
696 | const ColumnString * c1_string = checkAndGetColumn<ColumnString>(c1); |
697 | const ColumnFixedString * c0_fixed_string = checkAndGetColumn<ColumnFixedString>(c0); |
698 | const ColumnFixedString * c1_fixed_string = checkAndGetColumn<ColumnFixedString>(c1); |
699 | |
700 | const ColumnConst * c0_const = checkAndGetColumnConstStringOrFixedString(c0); |
701 | const ColumnConst * c1_const = checkAndGetColumnConstStringOrFixedString(c1); |
702 | |
703 | if (!((c0_string || c0_fixed_string || c0_const) && (c1_string || c1_fixed_string || c1_const))) |
704 | return false; |
705 | |
706 | const ColumnString::Chars * c0_const_chars = nullptr; |
707 | const ColumnString::Chars * c1_const_chars = nullptr; |
708 | ColumnString::Offset c0_const_size = 0; |
709 | ColumnString::Offset c1_const_size = 0; |
710 | |
711 | if (c0_const) |
712 | { |
713 | const ColumnString * c0_const_string = checkAndGetColumn<ColumnString>(&c0_const->getDataColumn()); |
714 | const ColumnFixedString * c0_const_fixed_string = checkAndGetColumn<ColumnFixedString>(&c0_const->getDataColumn()); |
715 | |
716 | if (c0_const_string) |
717 | { |
718 | c0_const_chars = &c0_const_string->getChars(); |
719 | c0_const_size = c0_const_string->getDataAt(0).size; |
720 | } |
721 | else if (c0_const_fixed_string) |
722 | { |
723 | c0_const_chars = &c0_const_fixed_string->getChars(); |
724 | c0_const_size = c0_const_fixed_string->getN(); |
725 | } |
726 | else |
727 | throw Exception("Logical error: ColumnConst contains not String nor FixedString column" , ErrorCodes::ILLEGAL_COLUMN); |
728 | } |
729 | |
730 | if (c1_const) |
731 | { |
732 | const ColumnString * c1_const_string = checkAndGetColumn<ColumnString>(&c1_const->getDataColumn()); |
733 | const ColumnFixedString * c1_const_fixed_string = checkAndGetColumn<ColumnFixedString>(&c1_const->getDataColumn()); |
734 | |
735 | if (c1_const_string) |
736 | { |
737 | c1_const_chars = &c1_const_string->getChars(); |
738 | c1_const_size = c1_const_string->getDataAt(0).size; |
739 | } |
740 | else if (c1_const_fixed_string) |
741 | { |
742 | c1_const_chars = &c1_const_fixed_string->getChars(); |
743 | c1_const_size = c1_const_fixed_string->getN(); |
744 | } |
745 | else |
746 | throw Exception("Logical error: ColumnConst contains not String nor FixedString column" , ErrorCodes::ILLEGAL_COLUMN); |
747 | } |
748 | |
749 | using StringImpl = StringComparisonImpl<Op<int, int>>; |
750 | |
751 | if (c0_const && c1_const) |
752 | { |
753 | UInt8 res = 0; |
754 | StringImpl::constant_constant(*c0_const_chars, c0_const_size, *c1_const_chars, c1_const_size, res); |
755 | block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(c0_const->size(), toField(res)); |
756 | return true; |
757 | } |
758 | else |
759 | { |
760 | auto c_res = ColumnUInt8::create(); |
761 | ColumnUInt8::Container & vec_res = c_res->getData(); |
762 | vec_res.resize(c0->size()); |
763 | |
764 | if (c0_string && c1_string) |
765 | StringImpl::string_vector_string_vector( |
766 | c0_string->getChars(), c0_string->getOffsets(), |
767 | c1_string->getChars(), c1_string->getOffsets(), |
768 | c_res->getData()); |
769 | else if (c0_string && c1_fixed_string) |
770 | StringImpl::string_vector_fixed_string_vector( |
771 | c0_string->getChars(), c0_string->getOffsets(), |
772 | c1_fixed_string->getChars(), c1_fixed_string->getN(), |
773 | c_res->getData()); |
774 | else if (c0_string && c1_const) |
775 | StringImpl::string_vector_constant( |
776 | c0_string->getChars(), c0_string->getOffsets(), |
777 | *c1_const_chars, c1_const_size, |
778 | c_res->getData()); |
779 | else if (c0_fixed_string && c1_string) |
780 | StringImpl::fixed_string_vector_string_vector( |
781 | c0_fixed_string->getChars(), c0_fixed_string->getN(), |
782 | c1_string->getChars(), c1_string->getOffsets(), |
783 | c_res->getData()); |
784 | else if (c0_fixed_string && c1_fixed_string) |
785 | StringImpl::fixed_string_vector_fixed_string_vector( |
786 | c0_fixed_string->getChars(), c0_fixed_string->getN(), |
787 | c1_fixed_string->getChars(), c1_fixed_string->getN(), |
788 | c_res->getData()); |
789 | else if (c0_fixed_string && c1_const) |
790 | StringImpl::fixed_string_vector_constant( |
791 | c0_fixed_string->getChars(), c0_fixed_string->getN(), |
792 | *c1_const_chars, c1_const_size, |
793 | c_res->getData()); |
794 | else if (c0_const && c1_string) |
795 | StringImpl::constant_string_vector( |
796 | *c0_const_chars, c0_const_size, |
797 | c1_string->getChars(), c1_string->getOffsets(), |
798 | c_res->getData()); |
799 | else if (c0_const && c1_fixed_string) |
800 | StringImpl::constant_fixed_string_vector( |
801 | *c0_const_chars, c0_const_size, |
802 | c1_fixed_string->getChars(), c1_fixed_string->getN(), |
803 | c_res->getData()); |
804 | else |
805 | throw Exception("Illegal columns " |
806 | + c0->getName() + " and " + c1->getName() |
807 | + " of arguments of function " + getName(), |
808 | ErrorCodes::ILLEGAL_COLUMN); |
809 | |
810 | block.getByPosition(result).column = std::move(c_res); |
811 | return true; |
812 | } |
813 | } |
814 | |
815 | bool executeDateOrDateTimeOrEnumOrUUIDWithConstString( |
816 | Block & block, size_t result, const IColumn * col_left_untyped, const IColumn * col_right_untyped, |
817 | const DataTypePtr & left_type, const DataTypePtr & right_type, bool left_is_num, size_t input_rows_count) |
818 | { |
819 | /// This is no longer very special case - comparing dates, datetimes, and enumerations with a string constant. |
820 | const IColumn * column_string_untyped = !left_is_num ? col_left_untyped : col_right_untyped; |
821 | const IColumn * column_number = left_is_num ? col_left_untyped : col_right_untyped; |
822 | const IDataType * number_type = left_is_num ? left_type.get() : right_type.get(); |
823 | |
824 | WhichDataType which(number_type); |
825 | |
826 | const bool legal_types = which.isDateOrDateTime() || which.isEnum() || which.isUUID(); |
827 | |
828 | const auto column_string = checkAndGetColumnConst<ColumnString>(column_string_untyped); |
829 | if (!column_string || !legal_types) |
830 | return false; |
831 | |
832 | StringRef string_value = column_string->getDataAt(0); |
833 | |
834 | if (which.isDate()) |
835 | { |
836 | DayNum date; |
837 | ReadBufferFromMemory in(string_value.data, string_value.size); |
838 | readDateText(date, in); |
839 | if (!in.eof()) |
840 | throw Exception("String is too long for Date: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); |
841 | |
842 | ColumnPtr parsed_const_date_holder = DataTypeDate().createColumnConst(input_rows_count, date); |
843 | const ColumnConst * parsed_const_date = assert_cast<const ColumnConst *>(parsed_const_date_holder.get()); |
844 | executeNumLeftType<DataTypeDate::FieldType>(block, result, |
845 | left_is_num ? col_left_untyped : parsed_const_date, |
846 | left_is_num ? parsed_const_date : col_right_untyped); |
847 | } |
848 | else if (which.isDateTime()) |
849 | { |
850 | time_t date_time; |
851 | ReadBufferFromMemory in(string_value.data, string_value.size); |
852 | readDateTimeText(date_time, in); |
853 | if (!in.eof()) |
854 | throw Exception("String is too long for DateTime: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); |
855 | |
856 | ColumnPtr parsed_const_date_time_holder = DataTypeDateTime().createColumnConst(input_rows_count, UInt64(date_time)); |
857 | const ColumnConst * parsed_const_date_time = assert_cast<const ColumnConst *>(parsed_const_date_time_holder.get()); |
858 | executeNumLeftType<DataTypeDateTime::FieldType>(block, result, |
859 | left_is_num ? col_left_untyped : parsed_const_date_time, |
860 | left_is_num ? parsed_const_date_time : col_right_untyped); |
861 | } |
862 | else if (which.isUUID()) |
863 | { |
864 | UUID uuid; |
865 | ReadBufferFromMemory in(string_value.data, string_value.size); |
866 | readText(uuid, in); |
867 | if (!in.eof()) |
868 | throw Exception("String is too long for UUID: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); |
869 | |
870 | ColumnPtr parsed_const_uuid_holder = DataTypeUUID().createColumnConst(input_rows_count, uuid); |
871 | const ColumnConst * parsed_const_uuid = assert_cast<const ColumnConst *>(parsed_const_uuid_holder.get()); |
872 | executeNumLeftType<DataTypeUUID::FieldType>(block, result, |
873 | left_is_num ? col_left_untyped : parsed_const_uuid, |
874 | left_is_num ? parsed_const_uuid : col_right_untyped); |
875 | } |
876 | |
877 | else if (which.isEnum8()) |
878 | executeEnumWithConstString<DataTypeEnum8>(block, result, column_number, column_string, |
879 | number_type, left_is_num, input_rows_count); |
880 | else if (which.isEnum16()) |
881 | executeEnumWithConstString<DataTypeEnum16>(block, result, column_number, column_string, |
882 | number_type, left_is_num, input_rows_count); |
883 | |
884 | return true; |
885 | } |
886 | |
887 | /// Comparison between DataTypeEnum<T> and string constant containing the name of an enum element |
888 | template <typename EnumType> |
889 | void executeEnumWithConstString( |
890 | Block & block, const size_t result, const IColumn * column_number, const ColumnConst * column_string, |
891 | const IDataType * type_untyped, const bool left_is_num, size_t input_rows_count) |
892 | { |
893 | const auto type = static_cast<const EnumType *>(type_untyped); |
894 | |
895 | const Field x = castToNearestFieldType(type->getValue(column_string->getValue<String>())); |
896 | const auto enum_col = type->createColumnConst(input_rows_count, x); |
897 | |
898 | executeNumLeftType<typename EnumType::FieldType>(block, result, |
899 | left_is_num ? column_number : enum_col.get(), |
900 | left_is_num ? enum_col.get() : column_number); |
901 | } |
902 | |
903 | void executeTuple(Block & block, size_t result, const ColumnWithTypeAndName & c0, const ColumnWithTypeAndName & c1, |
904 | size_t input_rows_count) |
905 | { |
906 | /** We will lexicographically compare the tuples. This is done as follows: |
907 | * x == y : x1 == y1 && x2 == y2 ... |
908 | * x != y : x1 != y1 || x2 != y2 ... |
909 | * |
910 | * x < y: x1 < y1 || (x1 == y1 && (x2 < y2 || (x2 == y2 ... && xn < yn)) |
911 | * x > y: x1 > y1 || (x1 == y1 && (x2 > y2 || (x2 == y2 ... && xn > yn)) |
912 | * x <= y: x1 < y1 || (x1 == y1 && (x2 < y2 || (x2 == y2 ... && xn <= yn)) |
913 | * |
914 | * Recursive form: |
915 | * x <= y: x1 < y1 || (x1 == y1 && x_tail <= y_tail) |
916 | * |
917 | * x >= y: x1 > y1 || (x1 == y1 && (x2 > y2 || (x2 == y2 ... && xn >= yn)) |
918 | */ |
919 | |
920 | const size_t tuple_size = typeid_cast<const DataTypeTuple &>(*c0.type).getElements().size(); |
921 | |
922 | if (0 == tuple_size) |
923 | throw Exception("Comparison of zero-sized tuples is not implemented." , ErrorCodes::NOT_IMPLEMENTED); |
924 | |
925 | ColumnsWithTypeAndName x(tuple_size); |
926 | ColumnsWithTypeAndName y(tuple_size); |
927 | |
928 | auto x_const = checkAndGetColumnConst<ColumnTuple>(c0.column.get()); |
929 | auto y_const = checkAndGetColumnConst<ColumnTuple>(c1.column.get()); |
930 | |
931 | Columns x_columns; |
932 | Columns y_columns; |
933 | |
934 | if (x_const) |
935 | x_columns = convertConstTupleToConstantElements(*x_const); |
936 | else |
937 | x_columns = assert_cast<const ColumnTuple &>(*c0.column).getColumnsCopy(); |
938 | |
939 | if (y_const) |
940 | y_columns = convertConstTupleToConstantElements(*y_const); |
941 | else |
942 | y_columns = assert_cast<const ColumnTuple &>(*c1.column).getColumnsCopy(); |
943 | |
944 | for (size_t i = 0; i < tuple_size; ++i) |
945 | { |
946 | x[i].type = static_cast<const DataTypeTuple &>(*c0.type).getElements()[i]; |
947 | y[i].type = static_cast<const DataTypeTuple &>(*c1.type).getElements()[i]; |
948 | |
949 | x[i].column = x_columns[i]; |
950 | y[i].column = y_columns[i]; |
951 | } |
952 | |
953 | executeTupleImpl(block, result, x, y, tuple_size, input_rows_count); |
954 | } |
955 | |
956 | void executeTupleImpl(Block & block, size_t result, const ColumnsWithTypeAndName & x, |
957 | const ColumnsWithTypeAndName & y, size_t tuple_size, |
958 | size_t input_rows_count); |
959 | |
960 | template <typename ComparisonFunction, typename ConvolutionFunction> |
961 | void executeTupleEqualityImpl(Block & block, size_t result, const ColumnsWithTypeAndName & x, const ColumnsWithTypeAndName & y, |
962 | size_t tuple_size, size_t input_rows_count) |
963 | { |
964 | if (0 == tuple_size) |
965 | throw Exception("Comparison of zero-sized tuples is not implemented." , ErrorCodes::NOT_IMPLEMENTED); |
966 | |
967 | auto func_compare = ComparisonFunction::create(context); |
968 | auto func_convolution = ConvolutionFunction::create(context); |
969 | |
970 | auto func_compare_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_compare)); |
971 | auto func_convolution_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_convolution)); |
972 | |
973 | Block tmp_block; |
974 | for (size_t i = 0; i < tuple_size; ++i) |
975 | { |
976 | tmp_block.insert(x[i]); |
977 | tmp_block.insert(y[i]); |
978 | |
979 | auto impl = func_compare_adaptor.build({x[i], y[i]}); |
980 | |
981 | /// Comparison of the elements. |
982 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
983 | impl->execute(tmp_block, {i * 3, i * 3 + 1}, i * 3 + 2, input_rows_count); |
984 | } |
985 | |
986 | if (tuple_size == 1) |
987 | { |
988 | /// Do not call AND for single-element tuple. |
989 | block.getByPosition(result).column = tmp_block.getByPosition(2).column; |
990 | return; |
991 | } |
992 | |
993 | /// Logical convolution. |
994 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
995 | |
996 | ColumnNumbers convolution_args(tuple_size); |
997 | for (size_t i = 0; i < tuple_size; ++i) |
998 | convolution_args[i] = i * 3 + 2; |
999 | |
1000 | ColumnsWithTypeAndName convolution_types(convolution_args.size(), { nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
1001 | auto impl = func_convolution_adaptor.build(convolution_types); |
1002 | |
1003 | impl->execute(tmp_block, convolution_args, tuple_size * 3, input_rows_count); |
1004 | block.getByPosition(result).column = tmp_block.getByPosition(tuple_size * 3).column; |
1005 | } |
1006 | |
1007 | template <typename HeadComparisonFunction, typename TailComparisonFunction> |
1008 | void executeTupleLessGreaterImpl(Block & block, size_t result, const ColumnsWithTypeAndName & x, |
1009 | const ColumnsWithTypeAndName & y, size_t tuple_size, size_t input_rows_count) |
1010 | { |
1011 | auto func_compare_head = HeadComparisonFunction::create(context); |
1012 | auto func_compare_tail = TailComparisonFunction::create(context); |
1013 | auto func_and = FunctionAnd::create(context); |
1014 | auto func_or = FunctionOr::create(context); |
1015 | auto func_equals = FunctionComparison<EqualsOp, NameEquals>::create(context); |
1016 | |
1017 | auto func_compare_head_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_compare_head)); |
1018 | auto func_compare_tail_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_compare_tail)); |
1019 | auto func_equals_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_equals)); |
1020 | |
1021 | ColumnsWithTypeAndName bin_args = {{ nullptr, std::make_shared<DataTypeUInt8>(), "" }, |
1022 | { nullptr, std::make_shared<DataTypeUInt8>(), "" }}; |
1023 | |
1024 | auto func_and_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_and)) |
1025 | .build(bin_args); |
1026 | |
1027 | auto func_or_adaptor = FunctionOverloadResolverAdaptor(std::make_unique<DefaultOverloadResolver>(func_or)) |
1028 | .build(bin_args); |
1029 | |
1030 | Block tmp_block; |
1031 | |
1032 | /// Pairwise comparison of the inequality of all elements; on the equality of all elements except the last. |
1033 | for (size_t i = 0; i < tuple_size; ++i) |
1034 | { |
1035 | tmp_block.insert(x[i]); |
1036 | tmp_block.insert(y[i]); |
1037 | |
1038 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
1039 | |
1040 | if (i + 1 != tuple_size) |
1041 | { |
1042 | auto impl_head = func_compare_head_adaptor.build({x[i], y[i]}); |
1043 | impl_head->execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 2, input_rows_count); |
1044 | |
1045 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
1046 | |
1047 | auto impl_equals = func_equals_adaptor.build({x[i], y[i]}); |
1048 | impl_equals->execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 3, input_rows_count); |
1049 | |
1050 | } |
1051 | else |
1052 | { |
1053 | auto impl_tail = func_compare_tail_adaptor.build({x[i], y[i]}); |
1054 | impl_tail->execute(tmp_block, {i * 4, i * 4 + 1}, i * 4 + 2, input_rows_count); |
1055 | } |
1056 | } |
1057 | |
1058 | /// Combination. Complex code - make a drawing. It can be replaced by a recursive comparison of tuples. |
1059 | size_t i = tuple_size - 1; |
1060 | while (i > 0) |
1061 | { |
1062 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
1063 | func_and_adaptor->execute(tmp_block, {tmp_block.columns() - 2, (i - 1) * 4 + 3}, tmp_block.columns() - 1, input_rows_count); |
1064 | tmp_block.insert({ nullptr, std::make_shared<DataTypeUInt8>(), "" }); |
1065 | func_or_adaptor->execute(tmp_block, {tmp_block.columns() - 2, (i - 1) * 4 + 2}, tmp_block.columns() - 1, input_rows_count); |
1066 | --i; |
1067 | } |
1068 | |
1069 | block.getByPosition(result).column = tmp_block.getByPosition(tmp_block.columns() - 1).column; |
1070 | } |
1071 | |
1072 | void executeGenericIdenticalTypes(Block & block, size_t result, const IColumn * c0, const IColumn * c1) |
1073 | { |
1074 | bool c0_const = isColumnConst(*c0); |
1075 | bool c1_const = isColumnConst(*c1); |
1076 | |
1077 | if (c0_const && c1_const) |
1078 | { |
1079 | UInt8 res = 0; |
1080 | GenericComparisonImpl<Op<int, int>>::constant_constant(*c0, *c1, res); |
1081 | block.getByPosition(result).column = DataTypeUInt8().createColumnConst(c0->size(), toField(res)); |
1082 | } |
1083 | else |
1084 | { |
1085 | auto c_res = ColumnUInt8::create(); |
1086 | ColumnUInt8::Container & vec_res = c_res->getData(); |
1087 | vec_res.resize(c0->size()); |
1088 | |
1089 | if (c0_const) |
1090 | GenericComparisonImpl<Op<int, int>>::constant_vector(*c0, *c1, vec_res); |
1091 | else if (c1_const) |
1092 | GenericComparisonImpl<Op<int, int>>::vector_constant(*c0, *c1, vec_res); |
1093 | else |
1094 | GenericComparisonImpl<Op<int, int>>::vector_vector(*c0, *c1, vec_res); |
1095 | |
1096 | block.getByPosition(result).column = std::move(c_res); |
1097 | } |
1098 | } |
1099 | |
1100 | void executeGeneric(Block & block, size_t result, const ColumnWithTypeAndName & c0, const ColumnWithTypeAndName & c1) |
1101 | { |
1102 | DataTypePtr common_type = getLeastSupertype({c0.type, c1.type}); |
1103 | |
1104 | ColumnPtr c0_converted = castColumn(c0, common_type, context); |
1105 | ColumnPtr c1_converted = castColumn(c1, common_type, context); |
1106 | |
1107 | executeGenericIdenticalTypes(block, result, c0_converted.get(), c1_converted.get()); |
1108 | } |
1109 | |
1110 | public: |
1111 | String getName() const override |
1112 | { |
1113 | return name; |
1114 | } |
1115 | |
1116 | size_t getNumberOfArguments() const override { return 2; } |
1117 | |
1118 | /// Get result types by argument types. If the function does not apply to these arguments, throw an exception. |
1119 | DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override |
1120 | { |
1121 | WhichDataType left(arguments[0].get()); |
1122 | WhichDataType right(arguments[1].get()); |
1123 | |
1124 | const DataTypeTuple * left_tuple = checkAndGetDataType<DataTypeTuple>(arguments[0].get()); |
1125 | const DataTypeTuple * right_tuple = checkAndGetDataType<DataTypeTuple>(arguments[1].get()); |
1126 | |
1127 | bool both_represented_by_number = arguments[0]->isValueRepresentedByNumber() && arguments[1]->isValueRepresentedByNumber(); |
1128 | bool has_date = left.isDate() || right.isDate(); |
1129 | |
1130 | if (!((both_represented_by_number && !has_date) /// Do not allow compare date and number. |
1131 | || (left.isStringOrFixedString() && right.isStringOrFixedString()) |
1132 | || (left.isDate() && right.isDate()) |
1133 | || (left.isDate() && right.isString()) /// You can compare the date, datetime and an enumeration with a constant string. |
1134 | || (left.isString() && right.isDate()) |
1135 | || (left.isDateTime() && right.isDateTime()) |
1136 | || (left.isDateTime() && right.isString()) |
1137 | || (left.isString() && right.isDateTime()) |
1138 | || (left.isUUID() && right.isUUID()) |
1139 | || (left.isUUID() && right.isString()) |
1140 | || (left.isString() && right.isUUID()) |
1141 | || (left.isEnum() && right.isEnum() && arguments[0]->getName() == arguments[1]->getName()) /// only equivalent enum type values can be compared against |
1142 | || (left.isEnum() && right.isString()) |
1143 | || (left.isString() && right.isEnum()) |
1144 | || (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size()) |
1145 | || (arguments[0]->equals(*arguments[1])))) |
1146 | { |
1147 | try |
1148 | { |
1149 | getLeastSupertype(arguments); |
1150 | } |
1151 | catch (const Exception &) |
1152 | { |
1153 | throw Exception("Illegal types of arguments (" + arguments[0]->getName() + ", " + arguments[1]->getName() + ")" |
1154 | " of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
1155 | } |
1156 | } |
1157 | |
1158 | if (left_tuple && right_tuple) |
1159 | { |
1160 | auto adaptor = FunctionOverloadResolverAdaptor( |
1161 | std::make_unique<DefaultOverloadResolver>(FunctionComparison<Op, Name>::create(context))); |
1162 | |
1163 | size_t size = left_tuple->getElements().size(); |
1164 | for (size_t i = 0; i < size; ++i) |
1165 | { |
1166 | ColumnsWithTypeAndName args = {{nullptr, left_tuple->getElements()[i], "" }, |
1167 | {nullptr, right_tuple->getElements()[i], "" }}; |
1168 | adaptor.build(args); |
1169 | } |
1170 | } |
1171 | |
1172 | return std::make_shared<DataTypeUInt8>(); |
1173 | } |
1174 | |
1175 | void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override |
1176 | { |
1177 | const auto & col_with_type_and_name_left = block.getByPosition(arguments[0]); |
1178 | const auto & col_with_type_and_name_right = block.getByPosition(arguments[1]); |
1179 | const IColumn * col_left_untyped = col_with_type_and_name_left.column.get(); |
1180 | const IColumn * col_right_untyped = col_with_type_and_name_right.column.get(); |
1181 | |
1182 | const DataTypePtr & left_type = col_with_type_and_name_left.type; |
1183 | const DataTypePtr & right_type = col_with_type_and_name_right.type; |
1184 | |
1185 | /// The case when arguments are the same (tautological comparison). Return constant. |
1186 | /// NOTE: Nullable types are special case. (BTW, this function use default implementation for Nullable, so Nullable types cannot be here. Check just in case.) |
1187 | /// NOTE: We consider NaN comparison to be implementation specific (and in our implementation NaNs are sometimes equal sometimes not). |
1188 | if (left_type->equals(*right_type) && !left_type->isNullable() && col_left_untyped == col_right_untyped) |
1189 | { |
1190 | /// Always true: =, <=, >= |
1191 | if constexpr (std::is_same_v<Op<int, int>, EqualsOp<int, int>> |
1192 | || std::is_same_v<Op<int, int>, LessOrEqualsOp<int, int>> |
1193 | || std::is_same_v<Op<int, int>, GreaterOrEqualsOp<int, int>>) |
1194 | { |
1195 | block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, 1u); |
1196 | return; |
1197 | } |
1198 | else |
1199 | { |
1200 | block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, 0u); |
1201 | return; |
1202 | } |
1203 | } |
1204 | |
1205 | WhichDataType which_left{left_type}; |
1206 | WhichDataType which_right{right_type}; |
1207 | |
1208 | const bool left_is_num = col_left_untyped->isNumeric(); |
1209 | const bool right_is_num = col_right_untyped->isNumeric(); |
1210 | |
1211 | bool date_and_datetime = (left_type != right_type) && |
1212 | which_left.isDateOrDateTime() && which_right.isDateOrDateTime(); |
1213 | |
1214 | if (left_is_num && right_is_num && !date_and_datetime) |
1215 | { |
1216 | if (!(executeNumLeftType<UInt8>(block, result, col_left_untyped, col_right_untyped) |
1217 | || executeNumLeftType<UInt16>(block, result, col_left_untyped, col_right_untyped) |
1218 | || executeNumLeftType<UInt32>(block, result, col_left_untyped, col_right_untyped) |
1219 | || executeNumLeftType<UInt64>(block, result, col_left_untyped, col_right_untyped) |
1220 | || executeNumLeftType<UInt128>(block, result, col_left_untyped, col_right_untyped) |
1221 | || executeNumLeftType<Int8>(block, result, col_left_untyped, col_right_untyped) |
1222 | || executeNumLeftType<Int16>(block, result, col_left_untyped, col_right_untyped) |
1223 | || executeNumLeftType<Int32>(block, result, col_left_untyped, col_right_untyped) |
1224 | || executeNumLeftType<Int64>(block, result, col_left_untyped, col_right_untyped) |
1225 | || executeNumLeftType<Int128>(block, result, col_left_untyped, col_right_untyped) |
1226 | || executeNumLeftType<Float32>(block, result, col_left_untyped, col_right_untyped) |
1227 | || executeNumLeftType<Float64>(block, result, col_left_untyped, col_right_untyped))) |
1228 | throw Exception("Illegal column " + col_left_untyped->getName() |
1229 | + " of first argument of function " + getName(), |
1230 | ErrorCodes::ILLEGAL_COLUMN); |
1231 | } |
1232 | else if (checkAndGetDataType<DataTypeTuple>(left_type.get())) |
1233 | { |
1234 | executeTuple(block, result, col_with_type_and_name_left, col_with_type_and_name_right, input_rows_count); |
1235 | } |
1236 | else if (isColumnedAsDecimal(left_type) || isColumnedAsDecimal(right_type)) |
1237 | { |
1238 | // compare |
1239 | if (!allowDecimalComparison(left_type, right_type) && !date_and_datetime) |
1240 | throw Exception("No operation " + getName() + " between " + left_type->getName() + " and " + right_type->getName(), |
1241 | ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); |
1242 | |
1243 | executeDecimal(block, result, col_with_type_and_name_left, col_with_type_and_name_right); |
1244 | } |
1245 | else if (!left_is_num && !right_is_num && executeString(block, result, col_left_untyped, col_right_untyped)) |
1246 | { |
1247 | } |
1248 | else if (left_type->equals(*right_type)) |
1249 | { |
1250 | executeGenericIdenticalTypes(block, result, col_left_untyped, col_right_untyped); |
1251 | } |
1252 | else if (executeDateOrDateTimeOrEnumOrUUIDWithConstString( |
1253 | block, result, col_left_untyped, col_right_untyped, |
1254 | left_type, right_type, |
1255 | left_is_num, input_rows_count)) |
1256 | { |
1257 | } |
1258 | else |
1259 | { |
1260 | executeGeneric(block, result, col_with_type_and_name_left, col_with_type_and_name_right); |
1261 | } |
1262 | } |
1263 | |
1264 | #if USE_EMBEDDED_COMPILER |
1265 | bool isCompilableImpl(const DataTypes & types) const override |
1266 | { |
1267 | auto isBigInteger = &typeIsEither<DataTypeInt64, DataTypeUInt64, DataTypeUUID>; |
1268 | auto isFloatingPoint = &typeIsEither<DataTypeFloat32, DataTypeFloat64>; |
1269 | if ((isBigInteger(*types[0]) && isFloatingPoint(*types[1])) |
1270 | || (isBigInteger(*types[1]) && isFloatingPoint(*types[0])) |
1271 | || (WhichDataType(types[0]).isDate() && WhichDataType(types[1]).isDateTime()) |
1272 | || (WhichDataType(types[1]).isDate() && WhichDataType(types[0]).isDateTime())) |
1273 | return false; /// TODO: implement (double, int_N where N > double's mantissa width) |
1274 | return isCompilableType(types[0]) && isCompilableType(types[1]); |
1275 | } |
1276 | |
1277 | llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override |
1278 | { |
1279 | auto & b = static_cast<llvm::IRBuilder<> &>(builder); |
1280 | auto * x = values[0](); |
1281 | auto * y = values[1](); |
1282 | if (!types[0]->equals(*types[1])) |
1283 | { |
1284 | llvm::Type * common; |
1285 | if (x->getType()->isIntegerTy() && y->getType()->isIntegerTy()) |
1286 | common = b.getIntNTy(std::max( |
1287 | /// if one integer has a sign bit, make sure the other does as well. llvm generates optimal code |
1288 | /// (e.g. uses overflow flag on x86) for (word size + 1)-bit integer operations. |
1289 | x->getType()->getIntegerBitWidth() + (!typeIsSigned(*types[0]) && typeIsSigned(*types[1])), |
1290 | y->getType()->getIntegerBitWidth() + (!typeIsSigned(*types[1]) && typeIsSigned(*types[0])))); |
1291 | else |
1292 | /// (double, float) or (double, int_N where N <= double's mantissa width) -> double |
1293 | common = b.getDoubleTy(); |
1294 | x = nativeCast(b, types[0], x, common); |
1295 | y = nativeCast(b, types[1], y, common); |
1296 | } |
1297 | auto * result = CompileOp<Op>::compile(b, x, y, typeIsSigned(*types[0]) || typeIsSigned(*types[1])); |
1298 | return b.CreateSelect(result, b.getInt8(1), b.getInt8(0)); |
1299 | } |
1300 | #endif |
1301 | }; |
1302 | |
1303 | } |
1304 | |