// Copyright 2023 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { /** Comparison functions: ==, !=, <, >, <=, >=. * The comparison functions always return 0 or 1 (UInt8). * * You can compare the following types: * - numbers; * - strings and fixed strings; * - dates; * - datetimes; * within each group, but not from different groups; * - tuples (lexicographic comparison). * * Exception: You can compare the date and datetime with a constant string. Example: EventDate = '2015-01-01'. * * TODO Arrays. */ namespace ErrorCodes { extern const int ILLEGAL_TYPE_OF_ARGUMENT; } template struct NumComparisonImpl { /// 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. static void NO_INLINE vectorVector(const PaddedPODArray & a, const PaddedPODArray & b, PaddedPODArray & c) { /** GCC 4.8.2 vectorizes a loop only if it is written in this form. * In this case, if you loop through the array index (the code will look simpler), * the loop will not be vectorized. */ size_t size = a.size(); const A * __restrict a_pos = &a[0]; const B * __restrict b_pos = &b[0]; UInt8 * __restrict c_pos = &c[0]; const A * a_end = a_pos + size; while (a_pos < a_end) { *c_pos = Op::apply(*a_pos, *b_pos); ++a_pos; ++b_pos; ++c_pos; } } static void NO_INLINE vectorConstant(const PaddedPODArray & a, B b, PaddedPODArray & c) { size_t size = a.size(); const A * __restrict a_pos = &a[0]; UInt8 * __restrict c_pos = &c[0]; const A * a_end = a_pos + size; while (a_pos < a_end) { *c_pos = Op::apply(*a_pos, b); ++a_pos; ++c_pos; } } static void constantVector(A a, const PaddedPODArray & b, PaddedPODArray & c) { NumComparisonImpl::vectorConstant(b, a, c); } static void constantConstant(A a, B b, UInt8 & c) { c = Op::apply(a, b); } }; inline int memcmp16(const void * a, const void * b) { /// Assuming little endian. UInt64 a_hi = __builtin_bswap64(unalignedLoad(a)); UInt64 b_hi = __builtin_bswap64(unalignedLoad(b)); if (a_hi < b_hi) return -1; if (a_hi > b_hi) return 1; UInt64 a_lo = __builtin_bswap64(unalignedLoad(reinterpret_cast(a) + 8)); UInt64 b_lo = __builtin_bswap64(unalignedLoad(reinterpret_cast(b) + 8)); if (a_lo < b_lo) return -1; if (a_lo > b_lo) return 1; return 0; } inline time_t dateToDateTime(UInt32 date_data) { DayNum day_num(date_data); LocalDate local_date(day_num); // todo use timezone info return DateLUT::instance().makeDateTime(local_date.year(), local_date.month(), local_date.day(), 0, 0, 0); } inline std::tuple dateTimeToDate(time_t time_data) { // todo use timezone info const auto & date_lut = DateLUT::instance(); auto truncated = date_lut.toHour(time_data) != 0 || date_lut.toMinute(time_data) != 0 || date_lut.toSecond(time_data) != 0; auto values = date_lut.getValues(time_data); auto day_num = date_lut.makeDayNum(values.year, values.month, values.day_of_month); return std::make_tuple(static_cast(day_num), truncated); } template class Op, bool is_left_date> struct DateDateTimeComparisonImpl { static void NO_INLINE vectorVector(const PaddedPODArray & a, const PaddedPODArray & b, PaddedPODArray & c) { size_t size = a.size(); const A * a_pos = &a[0]; const B * b_pos = &b[0]; UInt8 * c_pos = &c[0]; const A * a_end = a_pos + size; while (a_pos < a_end) { if (is_left_date) { using OpType = B; time_t date_time = dateToDateTime(*a_pos); *c_pos = Op::apply(static_cast(date_time), *b_pos); } else { using OpType = A; time_t date_time = dateToDateTime(*b_pos); *c_pos = Op::apply(*a_pos, static_cast(date_time)); } ++a_pos; ++b_pos; ++c_pos; } } static void NO_INLINE vectorConstant(const PaddedPODArray & a, B b, PaddedPODArray & c) { if (!is_left_date) { // datetime vector with date constant using OpType = A; time_t date_time = dateToDateTime(b); NumComparisonImpl>::vectorConstant(a, static_cast(date_time), c); } else { // date vector with datetime constant // first check if datetime constant can be convert to date constant bool truncated; DayNum date_num; std::tie(date_num, truncated) = dateTimeToDate(static_cast(b)); if (!truncated) { using OpType = A; NumComparisonImpl>::vectorConstant( a, static_cast(date_num), c); } else { using OpType = B; size_t size = a.size(); const A * a_pos = &a[0]; UInt8 * c_pos = &c[0]; const A * a_end = a_pos + size; while (a_pos < a_end) { time_t date_time = dateToDateTime(*a_pos); *c_pos = Op::apply(static_cast(date_time), b); ++a_pos; ++c_pos; } } } } static void constantVector(A a, const PaddedPODArray & b, PaddedPODArray & c) { if (is_left_date) { // date constant with datetime vector using OpType = B; time_t date_time = dateToDateTime(a); NumComparisonImpl>::constantVector(static_cast(date_time), b, c); } else { // datetime constant with date vector bool truncated; DayNum date_num; std::tie(date_num, truncated) = dateTimeToDate(static_cast(a)); if (!truncated) { using OpType = B; NumComparisonImpl>::vectorConstant( static_cast(a), date_num, c); } else { using OpType = A; size_t size = b.size(); const B * b_pos = &b[0]; UInt8 * c_pos = &c[0]; const B * b_end = b_pos + size; while (b_pos < b_end) { time_t date_time = dateToDateTime(*b_pos); *c_pos = Op::apply(a, static_cast(date_time)); ++b_pos; ++c_pos; } } } } static void constantConstant(A a, B b, UInt8 & c) { if (is_left_date) { using OpType = B; time_t date_time = dateToDateTime(a); NumComparisonImpl>::constantConstant( static_cast(date_time), b, c); } else { using OpType = A; time_t date_time = dateToDateTime(b); NumComparisonImpl>::constantConstant( a, static_cast(date_time), c); } } }; template struct StringComparisonWithCollatorImpl { static void NO_INLINE stringVectorStringVector( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, const TiDB::TiDBCollatorPtr & collator, PaddedPODArray & c) { bool optimized_path = CompareStringVectorStringVector(a_data, a_offsets, b_data, b_offsets, collator, c); if (optimized_path) { return; } size_t size = a_offsets.size(); for (size_t i = 0; i < size; ++i) { size_t a_size = StringUtil::sizeAt(a_offsets, i) - 1; size_t b_size = StringUtil::sizeAt(b_offsets, i) - 1; size_t a_offset = StringUtil::offsetAt(a_offsets, i); size_t b_offset = StringUtil::offsetAt(b_offsets, i); c[i] = Op::apply( collator->compare( reinterpret_cast(&a_data[a_offset]), a_size, reinterpret_cast(&b_data[b_offset]), b_size), 0); } } static void NO_INLINE stringVectorConstant( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const std::string_view & b, const TiDB::TiDBCollatorPtr & collator, PaddedPODArray & c) { bool optimized_path = CompareStringVectorConstant(a_data, a_offsets, b, collator, c); if (optimized_path) { return; } size_t size = a_offsets.size(); ColumnString::Offset b_size = b.size(); const char * b_data = reinterpret_cast(b.data()); for (size_t i = 0; i < size; ++i) { /// Trailing zero byte of the smaller string is included in the comparison. c[i] = Op::apply( collator->compare( reinterpret_cast(&a_data[StringUtil::offsetAt(a_offsets, i)]), StringUtil::sizeAt(a_offsets, i) - 1, b_data, b_size), 0); } } static void constantStringVector( const std::string_view & a, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, const TiDB::TiDBCollatorPtr & collator, PaddedPODArray & c) { StringComparisonWithCollatorImpl::stringVectorConstant( b_data, b_offsets, a, collator, c); } static void constantConstant( const std::string_view & a, const std::string_view & b, const TiDB::TiDBCollatorPtr & collator, ResultType & c) { size_t a_n = a.size(); size_t b_n = b.size(); int res = collator->compareFastPath( reinterpret_cast(a.data()), a_n, reinterpret_cast(b.data()), b_n); c = Op::apply(res, 0); } }; template struct StringComparisonImpl { static void NO_INLINE stringVectorStringVector( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, PaddedPODArray & c) { size_t size = a_offsets.size(); for (size_t i = 0; i < size; ++i) { /// Trailing zero byte of the smaller string is included in the comparison. size_t a_size = StringUtil::sizeAt(a_offsets, i); size_t b_size = StringUtil::sizeAt(b_offsets, i); size_t a_offset = StringUtil::offsetAt(a_offsets, i); size_t b_offset = StringUtil::offsetAt(b_offsets, i); int res = memcmp(&a_data[a_offset], &b_data[b_offset], std::min(a_size, b_size)); /// if partial compare result is 0, it means the common part of the two strings are exactly the same, then need to /// further compare the string length, otherwise we can get the compare result from partial compare result. c[i] = res == 0 ? Op::apply(a_size, b_size) : Op::apply(res, 0); } } static void NO_INLINE stringVectorConstant( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const std::string & b, PaddedPODArray & c) { size_t size = a_offsets.size(); ColumnString::Offset b_size = b.size() + 1; const UInt8 * b_data = reinterpret_cast(b.data()); for (size_t i = 0; i < size; ++i) { /// Trailing zero byte of the smaller string is included in the comparison. size_t a_size = StringUtil::sizeAt(a_offsets, i); size_t a_offset = StringUtil::offsetAt(a_offsets, i); int res = memcmp(&a_data[a_offset], b_data, std::min(a_size, b_size)); c[i] = res == 0 ? Op::apply(a_size, b_size) : Op::apply(res, 0); } } static void constantStringVector( const std::string & a, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, PaddedPODArray & c) { StringComparisonImpl::stringVectorConstant(b_data, b_offsets, a, c); } static void constantConstant(const std::string & a, const std::string & b, ResultType & c) { size_t a_n = a.size(); size_t b_n = b.size(); int res = memcmp(a.data(), b.data(), std::min(a_n, b_n)); c = res == 0 ? Op::apply(a_n, b_n) : Op::apply(res, 0); } }; /// Comparisons for equality/inequality are implemented slightly more efficient. template struct StringEqualsImpl { static void NO_INLINE stringVectorStringVector( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, PaddedPODArray & c) { size_t size = a_offsets.size(); for (size_t i = 0; i < size; ++i) c[i] = positive == ((i == 0) ? (a_offsets[0] == b_offsets[0] && !memcmp(&a_data[0], &b_data[0], a_offsets[0] - 1)) : (a_offsets[i] - a_offsets[i - 1] == b_offsets[i] - b_offsets[i - 1] && !memcmp( &a_data[a_offsets[i - 1]], &b_data[b_offsets[i - 1]], a_offsets[i] - a_offsets[i - 1] - 1))); } static void NO_INLINE stringVectorConstant( const ColumnString::Chars_t & a_data, const ColumnString::Offsets & a_offsets, const std::string & b, PaddedPODArray & c) { size_t size = a_offsets.size(); ColumnString::Offset b_n = b.size(); const UInt8 * b_data = reinterpret_cast(b.data()); for (size_t i = 0; i < size; ++i) c[i] = positive == ((i == 0) ? (a_offsets[0] == b_n + 1 && !memcmp(&a_data[0], b_data, b_n)) : (a_offsets[i] - a_offsets[i - 1] == b_n + 1 && !memcmp(&a_data[a_offsets[i - 1]], b_data, b_n))); } static void constantStringVector( const std::string & a, const ColumnString::Chars_t & b_data, const ColumnString::Offsets & b_offsets, PaddedPODArray & c) { stringVectorConstant(b_data, b_offsets, a, c); } static void constantConstant(const std::string & a, const std::string & b, UInt8 & c) { c = positive == (a == b); } }; template struct StringComparisonImpl, UInt8> : StringEqualsImpl { }; template struct StringComparisonImpl, UInt8> : StringEqualsImpl { }; /// Generic version, implemented for columns of same type. template struct GenericComparisonImpl { static void NO_INLINE vectorVector(const IColumn & a, const IColumn & b, PaddedPODArray & c) { for (size_t i = 0, size = a.size(); i < size; ++i) c[i] = Op::apply(a.compareAt(i, i, b, 1), 0); } static void NO_INLINE vectorConstant(const IColumn & a, const IColumn & b, PaddedPODArray & c) { auto b_materialized = b.cloneResized(1)->convertToFullColumnIfConst(); for (size_t i = 0, size = a.size(); i < size; ++i) c[i] = Op::apply(a.compareAt(i, 0, *b_materialized, 1), 0); } static void constantVector(const IColumn & a, const IColumn & b, PaddedPODArray & c) { GenericComparisonImpl::vectorConstant(b, a, c); } static void constantConstant(const IColumn & a, const IColumn & b, UInt8 & c) { c = Op::apply(a.compareAt(0, 0, b, 1), 0); } }; struct NameEquals { static constexpr auto name = "equals"; }; struct NameNotEquals { static constexpr auto name = "notEquals"; }; struct NameLess { static constexpr auto name = "less"; }; struct NameGreater { static constexpr auto name = "greater"; }; struct NameLessOrEquals { static constexpr auto name = "lessOrEquals"; }; struct NameGreaterOrEquals { static constexpr auto name = "greaterOrEquals"; }; struct NameStrcmp { static constexpr auto name = "strcmp"; }; template